diff --git a/core/modules/entity/lib/Drupal/entity/DatabaseStorageController.php b/core/modules/entity/lib/Drupal/entity/DatabaseStorageController.php index 0942f292a7493059b389a44702491b3f304d79cf..1ca440eb945f47d6ce7fa3f7f8a9e04ee501f73d 100644 --- a/core/modules/entity/lib/Drupal/entity/DatabaseStorageController.php +++ b/core/modules/entity/lib/Drupal/entity/DatabaseStorageController.php @@ -318,8 +318,8 @@ class DatabaseStorageController implements EntityStorageControllerInterface { $query->fields('revision', $entity_revision_fields); // Compare revision id of the base and revision table, if equal then this - // is the current revision. - $query->addExpression('base.' . $this->revisionKey . ' = revision.' . $this->revisionKey, 'isCurrentRevision'); + // is the default revision. + $query->addExpression('base.' . $this->revisionKey . ' = revision.' . $this->revisionKey, 'isDefaultRevision'); } $query->fields('base', $entity_fields); diff --git a/core/modules/entity/lib/Drupal/entity/Entity.php b/core/modules/entity/lib/Drupal/entity/Entity.php index 2d6ac5c54163ad6c2f2c93ab401f83b052366af6..abab6512ac5ff528b0f2b9bdff1bc07a9178be6b 100644 --- a/core/modules/entity/lib/Drupal/entity/Entity.php +++ b/core/modules/entity/lib/Drupal/entity/Entity.php @@ -41,11 +41,11 @@ class Entity implements EntityInterface { protected $enforceIsNew; /** - * Indicates whether this is the current revision. + * Indicates whether this is the default revision. * * @var bool */ - public $isCurrentRevision = TRUE; + public $isDefaultRevision = TRUE; /** * Constructs a new entity object. @@ -278,10 +278,10 @@ class Entity implements EntityInterface { } /** - * Implements Drupal\entity\EntityInterface::isCurrentRevision(). + * Implements Drupal\entity\EntityInterface::isDefaultRevision(). */ - public function isCurrentRevision() { - return $this->isCurrentRevision; + public function isDefaultRevision() { + return $this->isDefaultRevision; } } diff --git a/core/modules/entity/lib/Drupal/entity/EntityInterface.php b/core/modules/entity/lib/Drupal/entity/EntityInterface.php index a111d55ba63b842dba0b523c13b4a156b8ccfc7a..395256caafa91c9a53cd00b52aeebfd1940ed727 100644 --- a/core/modules/entity/lib/Drupal/entity/EntityInterface.php +++ b/core/modules/entity/lib/Drupal/entity/EntityInterface.php @@ -209,10 +209,10 @@ interface EntityInterface { public function getRevisionId(); /** - * Checks if this entity is the current revision. + * Checks if this entity is the default revision. * * @return bool - * TRUE if the entity is the current revision, FALSE otherwise. + * TRUE if the entity is the default revision, FALSE otherwise. */ - public function isCurrentRevision(); + public function isDefaultRevision(); } diff --git a/core/modules/field/modules/field_sql_storage/field_sql_storage.module b/core/modules/field/modules/field_sql_storage/field_sql_storage.module index 69e8bda6e000ca3ccf14e9f09bcc58aa1f691d8e..91956adbd78ab602411cf8648eed00c78a018533 100644 --- a/core/modules/field/modules/field_sql_storage/field_sql_storage.module +++ b/core/modules/field/modules/field_sql_storage/field_sql_storage.module @@ -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. $langcodes = !empty($entity->$field_name) ? $field_langcodes : $all_langcodes; if ($langcodes) { - db_delete($table_name) - ->condition('entity_type', $entity_type) - ->condition('entity_id', $id) - ->condition('langcode', $langcodes, 'IN') - ->execute(); + // Only overwrite the field's base table if saving the default revision + // of an entity. + if ($entity->isDefaultRevision()) { + db_delete($table_name) + ->condition('entity_type', $entity_type) + ->condition('entity_id', $id) + ->condition('langcode', $langcodes, 'IN') + ->execute(); + } db_delete($revision_name) ->condition('entity_type', $entity_type) ->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. 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(); } } diff --git a/core/modules/node/lib/Drupal/node/Node.php b/core/modules/node/lib/Drupal/node/Node.php index a96f309fe8e9bc1fd3e5a20e8ad7b8139248cb48..51077c71b3cdab9acd9258fb0ba1e798141e66d2 100644 --- a/core/modules/node/lib/Drupal/node/Node.php +++ b/core/modules/node/lib/Drupal/node/Node.php @@ -30,6 +30,16 @@ class Node extends Entity implements ContentEntityInterface { 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. * * @var string diff --git a/core/modules/node/lib/Drupal/node/NodeFormController.php b/core/modules/node/lib/Drupal/node/NodeFormController.php index b99547e6bf2fd18d185a975d24460621048df812..5fefafdc74b9fa37d08e842a0200394b7ad37c2b 100644 --- a/core/modules/node/lib/Drupal/node/NodeFormController.php +++ b/core/modules/node/lib/Drupal/node/NodeFormController.php @@ -148,6 +148,18 @@ class NodeFormController extends EntityFormController { ); } + $form['revision_information']['isDefaultRevision'] = array( + '#type' => 'checkbox', + '#title' => t('Set as default revision'), + '#default_value' => TRUE, + '#access' => user_access('administer nodes'), + '#states' => array( + 'visible' => array( + ':input[name="revision"]' => array('checked' => TRUE), + ), + ), + ); + $form['revision_information']['log'] = array( '#type' => 'textarea', '#title' => t('Revision log message'), diff --git a/core/modules/node/lib/Drupal/node/NodeStorageController.php b/core/modules/node/lib/Drupal/node/NodeStorageController.php index 898aa71fa3bc4cbe2a1e89d4ca7dfeab3675bf26..d82a454667ff42c7c57a02ef3e4e676cc68ecc80 100644 --- a/core/modules/node/lib/Drupal/node/NodeStorageController.php +++ b/core/modules/node/lib/Drupal/node/NodeStorageController.php @@ -100,7 +100,16 @@ class NodeStorageController extends DatabaseStorageController { } else { $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) { @@ -139,19 +148,19 @@ class NodeStorageController extends DatabaseStorageController { if (empty($entity->{$this->revisionKey}) || !empty($entity->revision)) { drupal_write_record($this->revisionTable, $record); - db_update($this->entityInfo['base table']) - ->fields(array($this->revisionKey => $record->{$this->revisionKey})) - ->condition($this->idKey, $entity->{$this->idKey}) - ->execute(); + // Only update the base node table if this revision is the default. + if ($entity->isDefaultRevision()) { + db_update($this->entityInfo['base table']) + ->fields(array($this->revisionKey => $record->{$this->revisionKey})) + ->condition($this->idKey, $entity->{$this->idKey}) + ->execute(); + } } else { drupal_write_record($this->revisionTable, $record, $this->revisionKey); } // Make sure to update the new revision key for the entity. $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(). */ 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(). */ diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeRevisionsTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeRevisionsTest.php index 6b1b14112353eb071880419a3b1fb8c2966dd8e3..760b55b81fed9d373b6cc46dd016102d7e8ccc67 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeRevisionsTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeRevisionsTest.php @@ -24,13 +24,14 @@ class NodeRevisionsTest extends NodeTestBase { // Create and login user. $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); // Create initial node. $node = $this->drupalCreateNode(); $settings = get_object_vars($node); $settings['revision'] = 1; + $settings['isDefaultRevision'] = TRUE; $nodes = array(); $logs = array(); @@ -47,6 +48,7 @@ class NodeRevisionsTest extends NodeTestBase { $this->drupalCreateNode($settings); $node = node_load($node->nid); // Make sure we get revision information. $settings = get_object_vars($node); + $settings['isDefaultRevision'] = TRUE; $nodes[] = $node; } @@ -75,8 +77,8 @@ class NodeRevisionsTest extends NodeTestBase { $this->assertText($log, t('Log message found.')); } - // Confirm that this is the current revision. - $this->assertTrue($node->isCurrentRevision(), 'Third node revision is the current one.'); + // Confirm that this is the default revision. + $this->assertTrue($node->isDefaultRevision(), 'Third node revision is the default one.'); // Confirm that revisions revert properly. $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); $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); - $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. $this->drupalPost("node/$node->nid/revisions/{$nodes[1]->vid}/delete", array(), t('Delete')); @@ -112,6 +114,44 @@ class NodeRevisionsTest extends NodeTestBase { '%title' => $nodes[2]->label(), '%revision-date' => format_date($old_revision_date), ))); + + // Make a new revision and set it to not be default. + // This will create a "forward revision." + $edit = array(); + $langcode = LANGUAGE_NOT_SPECIFIED; + $new_body = $this->randomName(); + $edit["body[$langcode][0][value]"] = $new_body; + // Save this as a forward revision. + $edit["revision"] = TRUE; + $edit["isDefaultRevision"] = FALSE; + $this->drupalPost("node/$node->nid/edit", $edit, t('Save')); + $this->drupalGet("node/$node->nid"); + $this->assertNoText($new_body, t('Forward revision body text is not present on default version of node.')); + + // Get the highest vid for this node. + $versions = db_select('node_revision', 'nr') + ->fields('nr', array('vid')) + ->condition('nid', $node->nid) + ->orderBy('nr.vid', 'DESC') + ->range(0,1) + ->execute() + ->fetchCol(); + + $forward_revision_vid = $versions[0]; + + // Verify that the new body text is present on the forward revision. + $this->drupalGet("node/$node->nid/revisions/" . $forward_revision_vid . "/view"); + $this->assertText($new_body, t('Forward revision body text is present on forward version of node.')); + + // Verify that the forward 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($forward_revision_vid > $default_revision_vid, 'Forward revision vid is greater than default revision vid.'); } /** diff --git a/core/modules/node/node.module b/core/modules/node/node.module index 7ebb1dcee5082998fc6a56bcc948c8f803abec11..2a34790698f6c23db306eae4be0980f14f98ea0e 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -1105,8 +1105,8 @@ function node_delete_multiple($nids) { */ function node_revision_delete($revision_id) { if ($revision = node_revision_load($revision_id)) { - // Prevent deleting the current revision. - if ($revision->isCurrentRevision()) { + // Prevent deleting the default revision. + if ($revision->isDefaultRevision()) { return FALSE; } @@ -1771,20 +1771,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 - // 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. - // 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. - 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; } elseif (user_access('administer nodes', $account)) { $access[$cid] = TRUE; } else { - // First check the access to the current revision and finally, if the - // node passed in is not the current 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)); + // First check the access to the default revision and finally, if the + // 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->isDefaultRevision() || node_access($op, $node, $account, $langcode)); } } diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module index 76fbae4538f06f6e367c5cf44c4b57d8ef2a6af8..3f1fc55eaed478acc34c02e359f82aa4550467f2 100644 --- a/core/modules/taxonomy/taxonomy.module +++ b/core/modules/taxonomy/taxonomy.module @@ -1581,7 +1581,7 @@ function taxonomy_build_node_index($node) { } } // 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. $tid_all = array(); foreach (field_info_instances('node', $node->type) as $instance) {