Issue #218755 by jstoller, Gábor Hojtsy, stevector, mradcliffe, agentrickard, catch, Crell: Added Support revisions in different states.
parent
5f4440fa7d
commit
e203b73b62
|
@ -4,6 +4,9 @@ Drupal 8.0, xxxx-xx-xx (development version)
|
||||||
- Improved entity system.
|
- Improved entity system.
|
||||||
* Added support for saving and deleting entities through the controller.
|
* Added support for saving and deleting entities through the controller.
|
||||||
* Entities are now classed objects, implementing EntityInterface.
|
* Entities are now classed objects, implementing EntityInterface.
|
||||||
|
* Drupal now understands the concept of a "default" revision, tracked
|
||||||
|
independently from the latest revision, allowing for the creation of
|
||||||
|
drafts while the current revision stays published.
|
||||||
- Replaced the core routing system with one built on the Symfony2 framework.
|
- Replaced the core routing system with one built on the Symfony2 framework.
|
||||||
- Configuration:
|
- Configuration:
|
||||||
* Added a centralized file-based configuration system.
|
* Added a centralized file-based configuration system.
|
||||||
|
|
|
@ -318,8 +318,8 @@ class DatabaseStorageController implements EntityStorageControllerInterface {
|
||||||
$query->fields('revision', $entity_revision_fields);
|
$query->fields('revision', $entity_revision_fields);
|
||||||
|
|
||||||
// Compare revision id of the base and revision table, if equal then this
|
// Compare revision id of the base and revision table, if equal then this
|
||||||
// is the current revision.
|
// is the default revision.
|
||||||
$query->addExpression('base.' . $this->revisionKey . ' = revision.' . $this->revisionKey, 'isCurrentRevision');
|
$query->addExpression('base.' . $this->revisionKey . ' = revision.' . $this->revisionKey, 'isDefaultRevision');
|
||||||
}
|
}
|
||||||
|
|
||||||
$query->fields('base', $entity_fields);
|
$query->fields('base', $entity_fields);
|
||||||
|
|
|
@ -42,11 +42,11 @@ class Entity implements EntityInterface {
|
||||||
protected $enforceIsNew;
|
protected $enforceIsNew;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates whether this is the current revision.
|
* Indicates whether this is the default revision.
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
protected $isCurrentRevision = TRUE;
|
protected $isDefaultRevision = TRUE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new entity object.
|
* Constructs a new entity object.
|
||||||
|
@ -277,12 +277,12 @@ class Entity implements EntityInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements Drupal\entity\EntityInterface::isCurrentRevision().
|
* Implements Drupal\entity\EntityInterface::isDefaultRevision().
|
||||||
*/
|
*/
|
||||||
public function isCurrentRevision($new_value = NULL) {
|
public function isDefaultRevision($new_value = NULL) {
|
||||||
$return = $this->isCurrentRevision;
|
$return = $this->isDefaultRevision;
|
||||||
if (isset($new_value)) {
|
if (isset($new_value)) {
|
||||||
$this->isCurrentRevision = (bool) $new_value;
|
$this->isDefaultRevision = (bool) $new_value;
|
||||||
}
|
}
|
||||||
return $return;
|
return $return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -209,14 +209,14 @@ interface EntityInterface {
|
||||||
public function getRevisionId();
|
public function getRevisionId();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if this entity is the current revision.
|
* Checks if this entity is the default revision.
|
||||||
*
|
*
|
||||||
* @param bool $new_value
|
* @param bool $new_value
|
||||||
* (optional) A Boolean to (re)set the isCurrentRevision flag.
|
* (optional) A Boolean to (re)set the isDefaultRevision flag.
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
* TRUE if the entity is the current revision, FALSE otherwise. If
|
* TRUE if the entity is the default revision, FALSE otherwise. If
|
||||||
* $new_value was passed, the previous value is returned.
|
* $new_value was passed, the previous value is returned.
|
||||||
*/
|
*/
|
||||||
public function isCurrentRevision($new_value = NULL);
|
public function isDefaultRevision($new_value = NULL);
|
||||||
}
|
}
|
||||||
|
|
|
@ -400,11 +400,15 @@ function field_sql_storage_field_storage_write($entity_type, $entity, $op, $fiel
|
||||||
// Delete all language codes if $entity->$field_name is empty.
|
// Delete all language codes if $entity->$field_name is empty.
|
||||||
$langcodes = !empty($entity->$field_name) ? $field_langcodes : $all_langcodes;
|
$langcodes = !empty($entity->$field_name) ? $field_langcodes : $all_langcodes;
|
||||||
if ($langcodes) {
|
if ($langcodes) {
|
||||||
db_delete($table_name)
|
// Only overwrite the field's base table if saving the default revision
|
||||||
->condition('entity_type', $entity_type)
|
// of an entity.
|
||||||
->condition('entity_id', $id)
|
if ($entity->isDefaultRevision()) {
|
||||||
->condition('langcode', $langcodes, 'IN')
|
db_delete($table_name)
|
||||||
->execute();
|
->condition('entity_type', $entity_type)
|
||||||
|
->condition('entity_id', $id)
|
||||||
|
->condition('langcode', $langcodes, 'IN')
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
db_delete($revision_name)
|
db_delete($revision_name)
|
||||||
->condition('entity_type', $entity_type)
|
->condition('entity_type', $entity_type)
|
||||||
->condition('entity_id', $id)
|
->condition('entity_id', $id)
|
||||||
|
@ -453,7 +457,11 @@ function field_sql_storage_field_storage_write($entity_type, $entity, $op, $fiel
|
||||||
|
|
||||||
// Execute the query if we have values to insert.
|
// Execute the query if we have values to insert.
|
||||||
if ($do_insert) {
|
if ($do_insert) {
|
||||||
$query->execute();
|
// Only overwrite the field's base table if saving the default revision
|
||||||
|
// of an entity.
|
||||||
|
if ($entity->isDefaultRevision()) {
|
||||||
|
$query->execute();
|
||||||
|
}
|
||||||
$revision_query->execute();
|
$revision_query->execute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,16 @@ class Node extends Entity implements ContentEntityInterface {
|
||||||
*/
|
*/
|
||||||
public $vid;
|
public $vid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether this is the default node revision.
|
||||||
|
*
|
||||||
|
* The default revision of a node is the one loaded when no specific revision
|
||||||
|
* has been specified. Only default revisions are saved to the node table.
|
||||||
|
*
|
||||||
|
* @var boolean
|
||||||
|
*/
|
||||||
|
public $isDefaultRevision = TRUE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The node UUID.
|
* The node UUID.
|
||||||
*
|
*
|
||||||
|
|
|
@ -100,7 +100,16 @@ class NodeStorageController extends DatabaseStorageController {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$op = 'update';
|
$op = 'update';
|
||||||
$return = drupal_write_record($this->entityInfo['base table'], $entity, $this->idKey);
|
// Update the base node table, but only if this revision is marked as
|
||||||
|
// the default.
|
||||||
|
if ($entity->isDefaultRevision()) {
|
||||||
|
$return = drupal_write_record($this->entityInfo['base table'], $entity, $this->idKey);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// @todo, should a different value be returned when saving an entity
|
||||||
|
// with $isDefaultRevision = FALSE?
|
||||||
|
$return = FALSE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->revisionKey) {
|
if ($this->revisionKey) {
|
||||||
|
@ -139,19 +148,19 @@ class NodeStorageController extends DatabaseStorageController {
|
||||||
|
|
||||||
if (empty($entity->{$this->revisionKey}) || !empty($entity->revision)) {
|
if (empty($entity->{$this->revisionKey}) || !empty($entity->revision)) {
|
||||||
drupal_write_record($this->revisionTable, $record);
|
drupal_write_record($this->revisionTable, $record);
|
||||||
db_update($this->entityInfo['base table'])
|
// Only update the base node table if this revision is the default.
|
||||||
->fields(array($this->revisionKey => $record->{$this->revisionKey}))
|
if ($entity->isDefaultRevision()) {
|
||||||
->condition($this->idKey, $entity->{$this->idKey})
|
db_update($this->entityInfo['base table'])
|
||||||
->execute();
|
->fields(array($this->revisionKey => $record->{$this->revisionKey}))
|
||||||
|
->condition($this->idKey, $entity->{$this->idKey})
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
drupal_write_record($this->revisionTable, $record, $this->revisionKey);
|
drupal_write_record($this->revisionTable, $record, $this->revisionKey);
|
||||||
}
|
}
|
||||||
// Make sure to update the new revision key for the entity.
|
// Make sure to update the new revision key for the entity.
|
||||||
$entity->{$this->revisionKey} = $record->{$this->revisionKey};
|
$entity->{$this->revisionKey} = $record->{$this->revisionKey};
|
||||||
|
|
||||||
// Mark this revision as the current one.
|
|
||||||
$entity->isCurrentRevision(TRUE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -263,9 +272,13 @@ class NodeStorageController extends DatabaseStorageController {
|
||||||
* Overrides Drupal\entity\DatabaseStorageController::postSave().
|
* Overrides Drupal\entity\DatabaseStorageController::postSave().
|
||||||
*/
|
*/
|
||||||
function postSave(EntityInterface $node, $update) {
|
function postSave(EntityInterface $node, $update) {
|
||||||
node_access_acquire_grants($node, $update);
|
// Update the node access table for this node, but only if it is the
|
||||||
|
// default revision. There's no need to delete existing records if the node
|
||||||
|
// is new.
|
||||||
|
if ($node->isDefaultRevision()) {
|
||||||
|
node_access_acquire_grants($node, $update);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overrides Drupal\entity\DatabaseStorageController::preDelete().
|
* Overrides Drupal\entity\DatabaseStorageController::preDelete().
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -24,13 +24,14 @@ class NodeRevisionsTest extends NodeTestBase {
|
||||||
|
|
||||||
// Create and login user.
|
// Create and login user.
|
||||||
$web_user = $this->drupalCreateUser(array('view revisions', 'revert revisions', 'edit any page content',
|
$web_user = $this->drupalCreateUser(array('view revisions', 'revert revisions', 'edit any page content',
|
||||||
'delete revisions', 'delete any page content'));
|
'delete revisions', 'delete any page content', 'administer nodes'));
|
||||||
$this->drupalLogin($web_user);
|
$this->drupalLogin($web_user);
|
||||||
|
|
||||||
// Create initial node.
|
// Create initial node.
|
||||||
$node = $this->drupalCreateNode();
|
$node = $this->drupalCreateNode();
|
||||||
$settings = get_object_vars($node);
|
$settings = get_object_vars($node);
|
||||||
$settings['revision'] = 1;
|
$settings['revision'] = 1;
|
||||||
|
$settings['isDefaultRevision'] = TRUE;
|
||||||
|
|
||||||
$nodes = array();
|
$nodes = array();
|
||||||
$logs = array();
|
$logs = array();
|
||||||
|
@ -47,6 +48,7 @@ class NodeRevisionsTest extends NodeTestBase {
|
||||||
$this->drupalCreateNode($settings);
|
$this->drupalCreateNode($settings);
|
||||||
$node = node_load($node->nid); // Make sure we get revision information.
|
$node = node_load($node->nid); // Make sure we get revision information.
|
||||||
$settings = get_object_vars($node);
|
$settings = get_object_vars($node);
|
||||||
|
$settings['isDefaultRevision'] = TRUE;
|
||||||
|
|
||||||
$nodes[] = $node;
|
$nodes[] = $node;
|
||||||
}
|
}
|
||||||
|
@ -75,8 +77,8 @@ class NodeRevisionsTest extends NodeTestBase {
|
||||||
$this->assertText($log, t('Log message found.'));
|
$this->assertText($log, t('Log message found.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Confirm that this is the current revision.
|
// Confirm that this is the default revision.
|
||||||
$this->assertTrue($node->isCurrentRevision(), 'Third node revision is the current one.');
|
$this->assertTrue($node->isDefaultRevision(), 'Third node revision is the default one.');
|
||||||
|
|
||||||
// Confirm that revisions revert properly.
|
// Confirm that revisions revert properly.
|
||||||
$this->drupalPost("node/$node->nid/revisions/{$nodes[1]->vid}/revert", array(), t('Revert'));
|
$this->drupalPost("node/$node->nid/revisions/{$nodes[1]->vid}/revert", array(), t('Revert'));
|
||||||
|
@ -86,9 +88,9 @@ class NodeRevisionsTest extends NodeTestBase {
|
||||||
$reverted_node = node_load($node->nid);
|
$reverted_node = node_load($node->nid);
|
||||||
$this->assertTrue(($nodes[1]->body[LANGUAGE_NOT_SPECIFIED][0]['value'] == $reverted_node->body[LANGUAGE_NOT_SPECIFIED][0]['value']), t('Node reverted correctly.'));
|
$this->assertTrue(($nodes[1]->body[LANGUAGE_NOT_SPECIFIED][0]['value'] == $reverted_node->body[LANGUAGE_NOT_SPECIFIED][0]['value']), t('Node reverted correctly.'));
|
||||||
|
|
||||||
// Confirm that this is not the current version.
|
// Confirm that this is not the default version.
|
||||||
$node = node_revision_load($node->vid);
|
$node = node_revision_load($node->vid);
|
||||||
$this->assertFalse($node->isCurrentRevision(), 'Third node revision is not the current one.');
|
$this->assertFalse($node->isDefaultRevision(), 'Third node revision is not the default one.');
|
||||||
|
|
||||||
// Confirm revisions delete properly.
|
// Confirm revisions delete properly.
|
||||||
$this->drupalPost("node/$node->nid/revisions/{$nodes[1]->vid}/delete", array(), t('Delete'));
|
$this->drupalPost("node/$node->nid/revisions/{$nodes[1]->vid}/delete", array(), t('Delete'));
|
||||||
|
@ -112,6 +114,33 @@ class NodeRevisionsTest extends NodeTestBase {
|
||||||
'%title' => $nodes[2]->label(),
|
'%title' => $nodes[2]->label(),
|
||||||
'%revision-date' => format_date($old_revision_date),
|
'%revision-date' => format_date($old_revision_date),
|
||||||
)));
|
)));
|
||||||
|
|
||||||
|
// Make a new revision and set it to not be default.
|
||||||
|
// This will create a new revision that is not "front facing".
|
||||||
|
$new_node_revision = clone $node;
|
||||||
|
$new_body = $this->randomName();
|
||||||
|
$new_node_revision->body[LANGUAGE_NOT_SPECIFIED][0]['value'] = $new_body;
|
||||||
|
// Save this as a non-default revision.
|
||||||
|
$new_node_revision->revision = TRUE;
|
||||||
|
$new_node_revision->isDefaultRevision = FALSE;
|
||||||
|
node_save($new_node_revision);
|
||||||
|
|
||||||
|
$this->drupalGet("node/$node->nid");
|
||||||
|
$this->assertNoText($new_body, t('Revision body text is not present on default version of node.'));
|
||||||
|
|
||||||
|
// Verify that the new body text is present on the revision.
|
||||||
|
$this->drupalGet("node/$node->nid/revisions/" . $new_node_revision->vid . "/view");
|
||||||
|
$this->assertText($new_body, t('Revision body text is present when loading specific revision.'));
|
||||||
|
|
||||||
|
// Verify that the non-default revision vid is greater than the default
|
||||||
|
// revision vid.
|
||||||
|
$default_revision = db_select('node', 'n')
|
||||||
|
->fields('n', array('vid'))
|
||||||
|
->condition('nid', $node->nid)
|
||||||
|
->execute()
|
||||||
|
->fetchCol();
|
||||||
|
$default_revision_vid = $default_revision[0];
|
||||||
|
$this->assertTrue($new_node_revision->vid > $default_revision_vid, 'Revision vid is greater than default revision vid.');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1129,8 +1129,8 @@ function node_delete_multiple($nids) {
|
||||||
*/
|
*/
|
||||||
function node_revision_delete($revision_id) {
|
function node_revision_delete($revision_id) {
|
||||||
if ($revision = node_revision_load($revision_id)) {
|
if ($revision = node_revision_load($revision_id)) {
|
||||||
// Prevent deleting the current revision.
|
// Prevent deleting the default revision.
|
||||||
if ($revision->isCurrentRevision()) {
|
if ($revision->isDefaultRevision()) {
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1795,20 +1795,20 @@ function _node_revision_access(Node $node, $op = 'view', $account = NULL, $langc
|
||||||
}
|
}
|
||||||
|
|
||||||
// There should be at least two revisions. If the vid of the given node
|
// There should be at least two revisions. If the vid of the given node
|
||||||
// and the vid of the current revision differ, then we already have two
|
// and the vid of the default revision differ, then we already have two
|
||||||
// different revisions so there is no need for a separate database check.
|
// different revisions so there is no need for a separate database check.
|
||||||
// Also, if you try to revert to or delete the current revision, that's
|
// Also, if you try to revert to or delete the default revision, that's
|
||||||
// not good.
|
// not good.
|
||||||
if ($node->isCurrentRevision() && (db_query('SELECT COUNT(vid) FROM {node_revision} WHERE nid = :nid', array(':nid' => $node->nid))->fetchField() == 1 || $op == 'update' || $op == 'delete')) {
|
if ($node->isDefaultRevision() && (db_query('SELECT COUNT(vid) FROM {node_revision} WHERE nid = :nid', array(':nid' => $node->nid))->fetchField() == 1 || $op == 'update' || $op == 'delete')) {
|
||||||
$access[$cid] = FALSE;
|
$access[$cid] = FALSE;
|
||||||
}
|
}
|
||||||
elseif (user_access('administer nodes', $account)) {
|
elseif (user_access('administer nodes', $account)) {
|
||||||
$access[$cid] = TRUE;
|
$access[$cid] = TRUE;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// First check the access to the current revision and finally, if the
|
// First check the access to the default revision and finally, if the
|
||||||
// node passed in is not the current revision then access to that, too.
|
// node passed in is not the default revision then access to that, too.
|
||||||
$access[$cid] = node_access($op, node_load($node->nid), $account, $langcode) && ($node->isCurrentRevision() || node_access($op, $node, $account, $langcode));
|
$access[$cid] = node_access($op, node_load($node->nid), $account, $langcode) && ($node->isDefaultRevision() || node_access($op, $node, $account, $langcode));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -291,6 +291,8 @@ function node_revision_revert_confirm($form, $form_state, $node_revision) {
|
||||||
function node_revision_revert_confirm_submit($form, &$form_state) {
|
function node_revision_revert_confirm_submit($form, &$form_state) {
|
||||||
$node_revision = $form['#node_revision'];
|
$node_revision = $form['#node_revision'];
|
||||||
$node_revision->revision = 1;
|
$node_revision->revision = 1;
|
||||||
|
// Make this the new default revision for the node.
|
||||||
|
$node_revision->isDefaultRevision = TRUE;
|
||||||
|
|
||||||
// The revision timestamp will be updated when the revision is saved. Keep the
|
// The revision timestamp will be updated when the revision is saved. Keep the
|
||||||
// original one for the confirmation message.
|
// original one for the confirmation message.
|
||||||
|
|
|
@ -1665,7 +1665,7 @@ function taxonomy_build_node_index($node) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// We only maintain the taxonomy index for published nodes.
|
// We only maintain the taxonomy index for published nodes.
|
||||||
if ($status) {
|
if ($status && $node->isDefaultRevision()) {
|
||||||
// Collect a unique list of all the term IDs from all node fields.
|
// Collect a unique list of all the term IDs from all node fields.
|
||||||
$tid_all = array();
|
$tid_all = array();
|
||||||
foreach (field_info_instances('node', $node->type) as $instance) {
|
foreach (field_info_instances('node', $node->type) as $instance) {
|
||||||
|
|
Loading…
Reference in New Issue