summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwebchick2012-10-14 05:44:26 (GMT)
committerwebchick2012-10-14 05:44:26 (GMT)
commitca64740b0b0254a5573174b47ec6928657a56cd5 (patch)
tree755443de00cd1eea9915099ca7417c55f5e43b97
parentbbdc5256eb633ea455e9f96f3bcb59b059f42fec (diff)
Issue #1723892 by Berdir, Pancho, das-peter, fago: Support for revisions for entity save and delete operations.
-rw-r--r--core/CHANGELOG.txt1
-rw-r--r--core/includes/entity.inc12
-rw-r--r--core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php7
-rw-r--r--core/lib/Drupal/Core/Entity/DatabaseStorageController.php94
-rw-r--r--core/lib/Drupal/Core/Entity/Entity.php22
-rw-r--r--core/lib/Drupal/Core/Entity/EntityInterface.php20
-rw-r--r--core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php10
-rw-r--r--core/modules/book/book.admin.inc2
-rw-r--r--core/modules/book/book.module2
-rw-r--r--core/modules/field/lib/Drupal/field/Tests/BulkDeleteTest.php16
-rw-r--r--core/modules/field/modules/options/lib/Drupal/options/Tests/OptionsSelectDynamicValuesTest.php4
-rw-r--r--core/modules/field/modules/options/tests/options_test.module7
-rw-r--r--core/modules/field/tests/modules/field_test/field_test.entity.inc4
-rw-r--r--core/modules/field/tests/modules/field_test/lib/Drupal/field_test/TestEntity.php7
-rw-r--r--core/modules/field/tests/modules/field_test/lib/Drupal/field_test/TestEntityController.php34
-rw-r--r--core/modules/file/file.field.inc2
-rw-r--r--core/modules/forum/forum.module4
-rw-r--r--core/modules/node/lib/Drupal/node/NodeFormController.php17
-rw-r--r--core/modules/node/lib/Drupal/node/NodeStorageController.php199
-rw-r--r--core/modules/node/lib/Drupal/node/Tests/NodeRevisionPermissionsTest.php2
-rw-r--r--core/modules/node/lib/Drupal/node/Tests/NodeRevisionsTest.php6
-rw-r--r--core/modules/node/node.module24
-rw-r--r--core/modules/node/node.pages.inc4
-rw-r--r--core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php4
-rw-r--r--core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldQueryTest.php15
25 files changed, 261 insertions, 258 deletions
diff --git a/core/CHANGELOG.txt b/core/CHANGELOG.txt
index e8eec3c..85556b8 100644
--- a/core/CHANGELOG.txt
+++ b/core/CHANGELOG.txt
@@ -7,6 +7,7 @@ Drupal 8.0, xxxx-xx-xx (development version)
* 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.
+ * All entity types, not just nodes, now have support for revisions.
- Replaced the core routing system with one built on the Symfony2 framework.
- Configuration:
* Added a centralized file-based configuration system.
diff --git a/core/includes/entity.inc b/core/includes/entity.inc
index 75c3386..cb0efe0 100644
--- a/core/includes/entity.inc
+++ b/core/includes/entity.inc
@@ -150,6 +150,18 @@ function entity_revision_load($entity_type, $revision_id) {
}
/**
+ * Deletes a node revision.
+ *
+ * @param string $entity_type
+ * The entity type to load, e.g. node or user.
+ * @param $revision_id
+ * The revision ID to delete.
+ */
+function entity_revision_delete($entity_type, $revision_id) {
+ entity_get_controller($entity_type)->deleteRevision($revision_id);
+}
+
+/**
* Loads an entity by UUID.
*
* Note that some entity types may not support UUIDs.
diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php b/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
index e82eb7f..07849c6 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
@@ -122,6 +122,13 @@ class ConfigStorageController implements EntityStorageControllerInterface {
}
/**
+ * Implements Drupal\Core\Entity\EntityStorageControllerInterface::deleteRevision().
+ */
+ public function deleteRevision($revision_id) {
+ return NULL;
+ }
+
+ /**
* Implements Drupal\Core\Entity\EntityStorageControllerInterface::loadByProperties().
*/
public function loadByProperties(array $values = array()) {
diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
index 1fa59ec..6342502 100644
--- a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
+++ b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
@@ -243,6 +243,23 @@ class DatabaseStorageController implements EntityStorageControllerInterface {
}
/**
+ * Implements Drupal\Core\Entity\EntityStorageControllerInterface::deleteRevision().
+ */
+ public function deleteRevision($revision_id) {
+ if ($revision = $this->loadRevision($revision_id)) {
+ // Prevent deletion if this is the default revision.
+ if ($revision->isDefaultRevision()) {
+ throw new EntityStorageException('Default revision can not be deleted');
+ }
+
+ db_delete($this->revisionTable)
+ ->condition($this->revisionKey, $revision->getRevisionId())
+ ->execute();
+ $this->invokeHook('revision_delete', $revision);
+ }
+ }
+
+ /**
* Implements Drupal\Core\Entity\EntityStorageControllerInterface::loadByProperties().
*/
public function loadByProperties(array $values = array()) {
@@ -446,6 +463,13 @@ class DatabaseStorageController implements EntityStorageControllerInterface {
db_delete($this->entityInfo['base table'])
->condition($this->idKey, $ids, 'IN')
->execute();
+
+ if ($this->revisionKey) {
+ db_delete($this->revisionTable)
+ ->condition($this->idKey, $ids, 'IN')
+ ->execute();
+ }
+
// Reset the cache as soon as the changes have been applied.
$this->resetCache($ids);
@@ -478,13 +502,26 @@ class DatabaseStorageController implements EntityStorageControllerInterface {
$this->invokeHook('presave', $entity);
if (!$entity->isNew()) {
- $return = drupal_write_record($this->entityInfo['base table'], $entity, $this->idKey);
+ 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) {
+ $this->saveRevision($entity);
+ }
$this->resetCache(array($entity->id()));
$this->postSave($entity, TRUE);
$this->invokeHook('update', $entity);
}
else {
$return = drupal_write_record($this->entityInfo['base table'], $entity);
+ if ($this->revisionKey) {
+ $this->saveRevision($entity);
+ }
// Reset general caches, but keep caches specific to certain entities.
$this->resetCache(array());
@@ -507,6 +544,43 @@ class DatabaseStorageController implements EntityStorageControllerInterface {
}
/**
+ * Saves an entity revision.
+ *
+ * @param Drupal\Core\Entity\EntityInterface $entity
+ * The entity object.
+ */
+ protected function saveRevision(EntityInterface $entity) {
+ // Convert the entity into an array as it might not have the same properties
+ // as the entity, it is just a raw structure.
+ $record = (array) $entity;
+
+ // When saving a new revision, set any existing revision ID to NULL so as to
+ // ensure that a new revision will actually be created, then store the old
+ // revision ID in a separate property for use by hook implementations.
+ if ($entity->isNewRevision() && $record[$this->revisionKey]) {
+ $record[$this->revisionKey] = NULL;
+ }
+
+ $this->preSaveRevision($record, $entity);
+
+ if ($entity->isNewRevision()) {
+ drupal_write_record($this->revisionTable, $record);
+ if ($entity->isDefaultRevision()) {
+ db_update($this->entityInfo['base table'])
+ ->fields(array($this->revisionKey => $record[$this->revisionKey]))
+ ->condition($this->idKey, $entity->id())
+ ->execute();
+ }
+ $entity->setNewRevision(FALSE);
+ }
+ 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];
+ }
+
+ /**
* Acts on an entity before the presave hook is invoked.
*
* Used before the entity is saved and before invoking the presave hook.
@@ -540,6 +614,16 @@ class DatabaseStorageController implements EntityStorageControllerInterface {
protected function postDelete($entities) { }
/**
+ * Act on a revision before being saved.
+ *
+ * @param array $record
+ * The revision array.
+ * @param Drupal\Core\Entity\EntityInterface $entity
+ * The entity object.
+ */
+ protected function preSaveRevision(array &$record, EntityInterface $entity) { }
+
+ /**
* Invokes a hook on behalf of the entity.
*
* @param $hook
@@ -548,7 +632,13 @@ class DatabaseStorageController implements EntityStorageControllerInterface {
* The entity object.
*/
protected function invokeHook($hook, EntityInterface $entity) {
- if (!empty($this->entityInfo['fieldable']) && function_exists($function = 'field_attach_' . $hook)) {
+ $function = 'field_attach_' . $hook;
+ // @todo: field_attach_delete_revision() is named the wrong way round,
+ // consider renaming it.
+ if ($function == 'field_attach_revision_delete') {
+ $function = 'field_attach_delete_revision';
+ }
+ if (!empty($this->entityInfo['fieldable']) && function_exists($function)) {
$function($this->entityType, $entity);
}
// Invoke the hook.
diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php
index 325ce6d..3d65b5e 100644
--- a/core/lib/Drupal/Core/Entity/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -43,6 +43,13 @@ class Entity implements IteratorAggregate, EntityInterface {
protected $enforceIsNew;
/**
+ * Boolean indicating whether a new revision should be created on save.
+ *
+ * @var bool
+ */
+ protected $newRevision = FALSE;
+
+ /**
* Indicates whether this is the default revision.
*
* @var bool
@@ -82,6 +89,14 @@ class Entity implements IteratorAggregate, EntityInterface {
}
/**
+ * Implements EntityInterface::isNewRevision().
+ */
+ public function isNewRevision() {
+ $info = $this->entityInfo();
+ return $this->newRevision || (!empty($info['entity keys']['revision']) && !$this->getRevisionId());
+ }
+
+ /**
* Implements EntityInterface::enforceIsNew().
*/
public function enforceIsNew($value = TRUE) {
@@ -89,6 +104,13 @@ class Entity implements IteratorAggregate, EntityInterface {
}
/**
+ * Implements EntityInterface::setNewRevision().
+ */
+ public function setNewRevision($value = TRUE) {
+ $this->newRevision = $value;
+ }
+
+ /**
* Implements EntityInterface::entityType().
*/
public function entityType() {
diff --git a/core/lib/Drupal/Core/Entity/EntityInterface.php b/core/lib/Drupal/Core/Entity/EntityInterface.php
index a6d21bb..7d2151c 100644
--- a/core/lib/Drupal/Core/Entity/EntityInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityInterface.php
@@ -64,6 +64,26 @@ interface EntityInterface extends ComplexDataInterface, AccessibleInterface, Tra
public function isNew();
/**
+ * Returns whether a new revision should be created on save.
+ *
+ * @return bool
+ * TRUE if a new revision should be created.
+ *
+ * @see Drupal\Core\Entity\EntityInterface::setNewRevision()
+ */
+ public function isNewRevision();
+
+ /**
+ * Enforces an entity to be saved as a new revision.
+ *
+ * @param bool $value
+ * (optional) Whether a new revision should be saved.
+ *
+ * @see Drupal\Core\Entity\EntityInterface::isNewRevision()
+ */
+ public function setNewRevision($value = TRUE);
+
+ /**
* Enforces an entity to be new.
*
* Allows migrations to create entities with pre-defined IDs by forcing the
diff --git a/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php b/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php
index b0267e5..153cd80 100644
--- a/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php
@@ -60,6 +60,16 @@ interface EntityStorageControllerInterface {
public function loadRevision($revision_id);
/**
+ * Delete a specific entity revision.
+ *
+ * A revision can only be deleted if it's not the currently active one.
+ *
+ * @param int $revision_id
+ * The revision id.
+ */
+ public function deleteRevision($revision_id);
+
+ /**
* Load entities by their property values.
*
* @param array $values
diff --git a/core/modules/book/book.admin.inc b/core/modules/book/book.admin.inc
index d4d6f0b..fbd8dda 100644
--- a/core/modules/book/book.admin.inc
+++ b/core/modules/book/book.admin.inc
@@ -162,7 +162,7 @@ function book_admin_edit_submit($form, &$form_state) {
$langcode = LANGUAGE_NOT_SPECIFIED;
$node->title = $values['title'];
$node->book['link_title'] = $values['title'];
- $node->revision = 1;
+ $node->setNewRevision();
$node->log = t('Title changed from %original to %current.', array('%original' => $node->title, '%current' => $values['title']));
$node->save();
diff --git a/core/modules/book/book.module b/core/modules/book/book.module
index fd4fac9..7c43e28 100644
--- a/core/modules/book/book.module
+++ b/core/modules/book/book.module
@@ -927,7 +927,7 @@ function book_page_alter(&$page) {
function book_node_presave(Node $node) {
// Always save a revision for non-administrators.
if (!empty($node->book['bid']) && !user_access('administer nodes')) {
- $node->revision = 1;
+ $node->setNewRevision();
}
// Make sure a new node gets a new menu link.
if (empty($node->nid)) {
diff --git a/core/modules/field/lib/Drupal/field/Tests/BulkDeleteTest.php b/core/modules/field/lib/Drupal/field/Tests/BulkDeleteTest.php
index 84b6f06..8211ce8 100644
--- a/core/modules/field/lib/Drupal/field/Tests/BulkDeleteTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/BulkDeleteTest.php
@@ -48,13 +48,15 @@ class BulkDeleteTest extends FieldTestBase {
protected function convertToPartialEntities($entities, $field_name) {
$partial_entities = array();
foreach ($entities as $id => $entity) {
- // Re-create the entity with only the required keys, remove label as that
- // is not present when using _field_create_entity_from_ids().
- $partial_entities[$id] = field_test_create_entity($entity->ftid, $entity->ftvid, $entity->fttype, $entity->ftlabel);
- // Remove the label and set enforceIsNew to NULL to make sure that the
- // entity classes match the actual arguments.
- unset($partial_entities[$id]->ftlabel);
- $partial_entities[$id]->enforceIsNew(NULL);
+ // Re-create the entity to match what is expected
+ // _field_create_entity_from_ids().
+ $ids = (object) array(
+ 'entity_id' => $entity->ftid,
+ 'revision_id' => $entity->ftvid,
+ 'bundle' => $entity->fttype,
+ 'entity_type' => 'test_entity',
+ );
+ $partial_entities[$id] = _field_create_entity_from_ids($ids);
$partial_entities[$id]->$field_name = $entity->$field_name;
}
return $partial_entities;
diff --git a/core/modules/field/modules/options/lib/Drupal/options/Tests/OptionsSelectDynamicValuesTest.php b/core/modules/field/modules/options/lib/Drupal/options/Tests/OptionsSelectDynamicValuesTest.php
index 0a612e8..4344d21 100644
--- a/core/modules/field/modules/options/lib/Drupal/options/Tests/OptionsSelectDynamicValuesTest.php
+++ b/core/modules/field/modules/options/lib/Drupal/options/Tests/OptionsSelectDynamicValuesTest.php
@@ -24,8 +24,8 @@ class OptionsSelectDynamicValuesTest extends OptionsDynamicValuesTest {
*/
function testSelectListDynamic() {
// Create an entity.
- $this->entity->is_new = TRUE;
- field_test_entity_save($this->entity);
+ $this->entity->save();
+
// Create a web user.
$web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content'));
$this->drupalLogin($web_user);
diff --git a/core/modules/field/modules/options/tests/options_test.module b/core/modules/field/modules/options/tests/options_test.module
index 604205b..2f92a66 100644
--- a/core/modules/field/modules/options/tests/options_test.module
+++ b/core/modules/field/modules/options/tests/options_test.module
@@ -28,5 +28,10 @@ function options_test_allowed_values_callback($field, $instance, $entity_type, $
function options_test_dynamic_values_callback($field, $instance, $entity_type, $entity, &$cacheable) {
$cacheable = FALSE;
// We need the values of the entity as keys.
- return drupal_map_assoc(array_merge(array($entity->ftlabel), array($entity->id(), $entity->getRevisionId(), $entity->bundle())));
+ return drupal_map_assoc(array(
+ $entity->ftlabel,
+ $entity->id(),
+ $entity->getRevisionId(),
+ $entity->bundle(),
+ ));
}
diff --git a/core/modules/field/tests/modules/field_test/field_test.entity.inc b/core/modules/field/tests/modules/field_test/field_test.entity.inc
index 7acb4bb..431ac11 100644
--- a/core/modules/field/tests/modules/field_test/field_test.entity.inc
+++ b/core/modules/field/tests/modules/field_test/field_test.entity.inc
@@ -237,6 +237,8 @@ function field_test_create_entity($id = 1, $vid = 1, $bundle = 'test_bundle', $l
}
if (isset($vid)) {
$entity->ftvid = $vid;
+ // Flag to make sure that the provided vid is used for a new revision.
+ $entity->use_provided_revision_id = $vid;
}
$entity->fttype = $bundle;
@@ -244,6 +246,7 @@ function field_test_create_entity($id = 1, $vid = 1, $bundle = 'test_bundle', $l
$entity->ftlabel = $label;
// Make sure the entity will saved even if a primary key is provided.
$entity->enforceIsNew();
+ $entity->setNewRevision();
return $entity;
}
@@ -293,7 +296,6 @@ function field_test_entity_edit(TestEntity $entity) {
return entity_get_form($entity);
}
-
/**
* Form combining two separate entities.
*/
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/TestEntity.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/TestEntity.php
index 18cfff5..25898c8 100644
--- a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/TestEntity.php
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/TestEntity.php
@@ -36,6 +36,13 @@ class TestEntity extends Entity {
public $fttype;
/**
+ * Label property
+ *
+ * @var string
+ */
+ public $ftlabel;
+
+ /**
* Overrides Drupal\Core\Entity\Entity::id().
*/
public function id() {
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/TestEntityController.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/TestEntityController.php
index 16f66df..ab62a9a 100644
--- a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/TestEntityController.php
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/TestEntityController.php
@@ -16,36 +16,12 @@ use Drupal\Core\Entity\EntityInterface;
class TestEntityController extends DatabaseStorageController {
/**
- * Overrides Drupal\Core\Entity\DatabaseStorageController::preSave().
+ * Overrides Drupal\Core\Entity\DatabaseStorageController::preSaveRevision().
*/
- public function preSave(EntityInterface $entity) {
- // Prepare for a new revision.
- if (!$entity->isNew() && !empty($entity->revision)) {
- $entity->old_ftvid = $entity->ftvid;
- $entity->ftvid = NULL;
- }
- }
-
- /**
- * Overrides Drupal\Core\Entity\DatabaseStorageController::postSave().
- */
- public function postSave(EntityInterface $entity, $update) {
- // Only the test_entity entity type has revisions.
- if ($entity->entityType() == 'test_entity') {
- $update_entity = TRUE;
- if (!$update || !empty($entity->revision)) {
- drupal_write_record('test_entity_revision', $entity);
- }
- else {
- drupal_write_record('test_entity_revision', $entity, 'ftvid');
- $update_entity = FALSE;
- }
- if ($update_entity) {
- db_update('test_entity')
- ->fields(array('ftvid' => $entity->ftvid))
- ->condition('ftid', $entity->ftid)
- ->execute();
- }
+ public function preSaveRevision(array &$record, EntityInterface $entity) {
+ // Allow for predefined revision ids.
+ if (!empty($record['use_provided_revision_id'])) {
+ $record['ftvid'] = $record['use_provided_revision_id'];
}
}
diff --git a/core/modules/file/file.field.inc b/core/modules/file/file.field.inc
index cbc2246..187d57c 100644
--- a/core/modules/file/file.field.inc
+++ b/core/modules/file/file.field.inc
@@ -231,7 +231,7 @@ function file_field_insert($entity_type, $entity, $field, $instance, $langcode,
function file_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) {
// On new revisions, all files are considered to be a new usage and no
// deletion of previous file usages are necessary.
- if (!empty($entity->revision)) {
+ if (!empty($entity->original) && $entity->getRevisionId() != $entity->original->getRevisionId()) {
foreach ($items as $item) {
file_usage_add(file_load($item['fid']), 'file', $entity_type, $entity->id());
}
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index f365491..af2daa9 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -346,7 +346,9 @@ function forum_node_presave(Node $node) {
*/
function forum_node_update(Node $node) {
if (_forum_node_check_node_type($node)) {
- if (empty($node->revision) && db_query('SELECT tid FROM {forum} WHERE nid=:nid', array(':nid' => $node->nid))->fetchField()) {
+ // If this is not a new revision and does exist, update the forum record,
+ // otherwise insert a new one.
+ if ($node->getRevisionId() == $node->original->getRevisionId() && db_query('SELECT tid FROM {forum} WHERE nid=:nid', array(':nid' => $node->nid))->fetchField()) {
if (!empty($node->forum_tid)) {
db_update('forum')
->fields(array('tid' => $node->forum_tid))
diff --git a/core/modules/node/lib/Drupal/node/NodeFormController.php b/core/modules/node/lib/Drupal/node/NodeFormController.php
index b9bf7eb..dedf805 100644
--- a/core/modules/node/lib/Drupal/node/NodeFormController.php
+++ b/core/modules/node/lib/Drupal/node/NodeFormController.php
@@ -44,7 +44,7 @@ class NodeFormController extends EntityFormController {
$node->log = NULL;
}
// Always use the default revision setting.
- $node->revision = in_array('revision', $node_options);
+ $node->setNewRevision(in_array('revision', $node_options));
node_invoke($node, 'prepare');
module_invoke_all('node_prepare', $node);
@@ -117,8 +117,8 @@ class NodeFormController extends EntityFormController {
'#type' => 'fieldset',
'#title' => t('Revision information'),
'#collapsible' => TRUE,
- // Collapsed by default when "Create new revision" is unchecked
- '#collapsed' => !$node->revision,
+ // Collapsed by default when "Create new revision" is unchecked.
+ '#collapsed' => !$node->isNewRevision(),
'#group' => 'additional_settings',
'#attributes' => array(
'class' => array('node-form-revision-information'),
@@ -127,20 +127,20 @@ class NodeFormController extends EntityFormController {
'js' => array(drupal_get_path('module', 'node') . '/node.js'),
),
'#weight' => 20,
- '#access' => $node->revision || user_access('administer nodes'),
+ '#access' => $node->isNewRevision() || user_access('administer nodes'),
);
$form['revision_information']['revision'] = array(
'#type' => 'checkbox',
'#title' => t('Create new revision'),
- '#default_value' => $node->revision,
+ '#default_value' => $node->isNewRevision(),
'#access' => user_access('administer nodes'),
);
// Check the revision log checkbox when the log textarea is filled in.
// This must not happen if "Create new revision" is enabled by default,
// since the state would auto-disable the checkbox otherwise.
- if (!$node->revision) {
+ if (!$node->isNewRevision()) {
$form['revision_information']['revision']['#states'] = array(
'checked' => array(
'textarea[name="log"]' => array('empty' => FALSE),
@@ -321,6 +321,11 @@ class NodeFormController extends EntityFormController {
// Build the node object from the submitted values.
$node = parent::submit($form, $form_state);
+ // Save as a new revision if requested to do so.
+ if (!empty($form_state['values']['revision'])) {
+ $node->setNewRevision();
+ }
+
node_submit($node);
foreach (module_implements('node_submit') as $module) {
$function = $module . '_node_submit';
diff --git a/core/modules/node/lib/Drupal/node/NodeStorageController.php b/core/modules/node/lib/Drupal/node/NodeStorageController.php
index 02e694e..3993d24 100644
--- a/core/modules/node/lib/Drupal/node/NodeStorageController.php
+++ b/core/modules/node/lib/Drupal/node/NodeStorageController.php
@@ -9,8 +9,6 @@ namespace Drupal\node;
use Drupal\Core\Entity\DatabaseStorageController;
use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Entity\EntityStorageException;
-use Exception;
/**
* Controller class for nodes.
@@ -35,135 +33,6 @@ class NodeStorageController extends DatabaseStorageController {
}
/**
- * Overrides Drupal\Core\Entity\DatabaseStorageController::delete().
- */
- public function delete($ids) {
- $entities = $ids ? $this->load($ids) : FALSE;
- if (!$entities) {
- // If no IDs or invalid IDs were passed, do nothing.
- return;
- }
- $transaction = db_transaction();
-
- try {
- $this->preDelete($entities);
- foreach ($entities as $id => $entity) {
- $this->invokeHook('predelete', $entity);
- }
- $ids = array_keys($entities);
-
- db_delete($this->entityInfo['base table'])
- ->condition($this->idKey, $ids, 'IN')
- ->execute();
-
- if ($this->revisionKey) {
- db_delete($this->revisionTable)
- ->condition($this->idKey, $ids, 'IN')
- ->execute();
- }
-
- // Reset the cache as soon as the changes have been applied.
- $this->resetCache($ids);
-
- $this->postDelete($entities);
- foreach ($entities as $id => $entity) {
- $this->invokeHook('delete', $entity);
- }
- // Ignore slave server temporarily.
- db_ignore_slave();
- }
- catch (Exception $e) {
- $transaction->rollback();
- watchdog_exception($this->entityType, $e);
- throw new EntityStorageException($e->getMessage, $e->getCode, $e);
- }
- }
-
- /**
- * Overrides Drupal\Core\Entity\DatabaseStorageController::save().
- */
- public function save(EntityInterface $entity) {
- $transaction = db_transaction();
- try {
- // Load the stored entity, if any.
- if (!$entity->isNew() && !isset($entity->original)) {
- $entity->original = entity_load_unchanged($this->entityType, $entity->id());
- }
-
- $this->preSave($entity);
- $this->invokeHook('presave', $entity);
-
- if ($entity->isNew()) {
- $op = 'insert';
- $return = drupal_write_record($this->entityInfo['base table'], $entity);
- $entity->enforceIsNew(FALSE);
- }
- else {
- $op = 'update';
- // 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) {
- $this->saveRevision($entity);
- }
-
- // Reset general caches, but keep caches specific to certain entities.
- $this->resetCache($op == 'update' ? array($entity->{$this->idKey}): array());
-
- $this->postSave($entity, $op == 'update');
- $this->invokeHook($op, $entity);
-
- // Ignore slave server temporarily.
- db_ignore_slave();
- unset($entity->original);
-
- return $return;
- }
- catch (Exception $e) {
- $transaction->rollback();
- watchdog_exception($this->entityType, $e);
- throw new EntityStorageException($e->getMessage(), $e->getCode(), $e);
- }
- }
-
- /**
- * Saves a node revision.
- *
- * @param Drupal\Core\Entity\EntityInterface $node
- * The node entity.
- */
- protected function saveRevision(EntityInterface $entity) {
- $record = clone $entity;
- $record->uid = $entity->revision_uid;
- $record->timestamp = $entity->revision_timestamp;
-
- if (empty($entity->{$this->revisionKey}) || !empty($entity->revision)) {
- drupal_write_record($this->revisionTable, $record);
- // 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};
- }
-
- /**
* Overrides Drupal\Core\Entity\DatabaseStorageController::attachLoad().
*/
protected function attachLoad(&$nodes, $load_revision = FALSE) {
@@ -218,39 +87,6 @@ class NodeStorageController extends DatabaseStorageController {
}
parent::invokeHook($hook, $node);
-
- if ($hook == 'presave') {
- if ($node->isNew() || !empty($node->revision)) {
- // When inserting either a new node or a new node revision, $node->log
- // must be set because {node_revision}.log is a text column and therefore
- // cannot have a default value. However, it might not be set at this
- // point (for example, if the user submitting a node form does not have
- // permission to create revisions), so we ensure that it is at least an
- // empty string in that case.
- // @todo: Make the {node_revision}.log column nullable so that we can
- // remove this check.
- if (!isset($node->log)) {
- $node->log = '';
- }
- }
- elseif (!isset($node->log) || $node->log === '') {
- // If we are updating an existing node without adding a new revision, we
- // need to make sure $node->log is unset whenever it is empty. As long as
- // $node->log is unset, drupal_write_record() will not attempt to update
- // the existing database column when re-saving the revision; therefore,
- // this code allows us to avoid clobbering an existing log entry with an
- // empty one.
- unset($node->log);
- }
-
- // When saving a new node revision, unset any existing $node->vid so as to
- // ensure that a new revision will actually be created, then store the old
- // revision ID in a separate property for use by node hook implementations.
- if (!$node->isNew() && !empty($node->revision) && $node->vid) {
- $node->old_vid = $node->vid;
- $node->vid = NULL;
- }
- }
}
/**
@@ -259,14 +95,39 @@ class NodeStorageController extends DatabaseStorageController {
protected function preSave(EntityInterface $node) {
// Before saving the node, set changed and revision times.
$node->changed = REQUEST_TIME;
+ }
- if ($this->revisionKey && !empty($node->revision)) {
- $node->revision_timestamp = REQUEST_TIME;
-
- if (!isset($node->revision_uid)) {
- $node->revision_uid = $GLOBALS['user']->uid;
+ /**
+ * Overrides Drupal\Core\Entity\DatabaseStorageController::preSaveRevision().
+ */
+ protected function preSaveRevision(array &$record, EntityInterface $entity) {
+ if ($entity->isNewRevision()) {
+ // When inserting either a new node or a new node revision, $node->log
+ // must be set because {node_revision}.log is a text column and therefore
+ // cannot have a default value. However, it might not be set at this
+ // point (for example, if the user submitting a node form does not have
+ // permission to create revisions), so we ensure that it is at least an
+ // empty string in that case.
+ // @todo: Make the {node_revision}.log column nullable so that we can
+ // remove this check.
+ if (!isset($record['log'])) {
+ $record['log'] = '';
}
}
+ elseif (!isset($record['log']) || $record['log'] === '') {
+ // If we are updating an existing node without adding a new revision, we
+ // need to make sure $node->log is unset whenever it is empty. As long as
+ // $node->log is unset, drupal_write_record() will not attempt to update
+ // the existing database column when re-saving the revision; therefore,
+ // this code allows us to avoid clobbering an existing log entry with an
+ // empty one.
+ unset($record['log']);
+ }
+
+ if ($entity->isNewRevision()) {
+ $record['timestamp'] = REQUEST_TIME;
+ $record['uid'] = isset($record['revision_uid']) ? $record['revision_uid'] : $GLOBALS['user']->uid;
+ }
}
/**
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeRevisionPermissionsTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeRevisionPermissionsTest.php
index 9a5bb56..7af3429 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeRevisionPermissionsTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeRevisionPermissionsTest.php
@@ -39,7 +39,7 @@ class NodeRevisionPermissionsTest extends NodeTestBase {
for ($i = 0; $i < 3; $i++) {
// Create a revision for the same nid and settings with a random log.
$revision = clone $node;
- $revision->revision = 1;
+ $revision->setNewRevision();
$revision->log = $this->randomName(32);
node_save($revision);
$this->node_revisions[] = $revision;
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeRevisionsTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeRevisionsTest.php
index 5a40510..7404685 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeRevisionsTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeRevisionsTest.php
@@ -121,7 +121,7 @@ class NodeRevisionsTest extends NodeTestBase {
$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->setNewRevision();
$new_node_revision->isDefaultRevision = FALSE;
node_save($new_node_revision);
@@ -160,7 +160,7 @@ class NodeRevisionsTest extends NodeTestBase {
$node = clone $node;
$node->title = $new_title;
$node->log = '';
- $node->revision = FALSE;
+ $node->setNewRevision(FALSE);
$node->save();
$this->drupalGet('node/' . $node->nid);
@@ -177,7 +177,7 @@ class NodeRevisionsTest extends NodeTestBase {
$node = clone $node;
$node->title = $new_title;
- $node->revision = TRUE;
+ $node->setNewRevision();
$node->log = NULL;
$node->save();
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 2dd1b57..8d5803d 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -1082,13 +1082,13 @@ function node_revision_load($vid = NULL) {
/**
* Prepares a node for saving by populating the author and creation date.
*
- * @param object $node
+ * @param Drupal\node\Node $node
* A node object.
*
- * @return object
+ * @return Drupal\node\Node
* An updated node object.
*/
-function node_submit($node) {
+function node_submit(Node $node) {
global $user;
// A user might assign the node author by entering a user name in the node
@@ -1103,7 +1103,7 @@ function node_submit($node) {
}
// If a new revision is created, save the current user as revision author.
- if (!empty($node->revision)) {
+ if ($node->isNewRevision()) {
$node->revision_uid = $user->uid;
}
@@ -1157,21 +1157,7 @@ function node_delete_multiple($nids) {
* TRUE if the revision deletion was successful; otherwise, FALSE.
*/
function node_revision_delete($revision_id) {
- if ($revision = node_revision_load($revision_id)) {
- // Prevent deleting the default revision.
- if ($revision->isDefaultRevision()) {
- return FALSE;
- }
-
- db_delete('node_revision')
- ->condition('nid', $revision->nid)
- ->condition('vid', $revision->vid)
- ->execute();
- module_invoke_all('node_revision_delete', $revision);
- field_attach_delete_revision('node', $revision);
- return TRUE;
- }
- return FALSE;
+ entity_revision_delete('node', $revision_id);
}
/**
diff --git a/core/modules/node/node.pages.inc b/core/modules/node/node.pages.inc
index 570ae88..041dd35 100644
--- a/core/modules/node/node.pages.inc
+++ b/core/modules/node/node.pages.inc
@@ -333,9 +333,9 @@ function node_revision_revert_confirm($form, $form_state, $node_revision) {
*/
function node_revision_revert_confirm_submit($form, &$form_state) {
$node_revision = $form['#node_revision'];
- $node_revision->revision = 1;
+ $node_revision->setNewRevision();
// Make this the new default revision for the node.
- $node_revision->isDefaultRevision = TRUE;
+ $node_revision->isDefaultRevision(TRUE);
// The revision timestamp will be updated when the revision is saved. Keep the
// original one for the confirmation message.
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
index 400d885..8ad58ef 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
@@ -206,7 +206,6 @@ abstract class WebTestBase extends TestBase {
'status' => 1,
'sticky' => 0,
'type' => 'page',
- 'revisions' => NULL,
'langcode' => LANGUAGE_NOT_SPECIFIED,
);
@@ -235,6 +234,9 @@ abstract class WebTestBase extends TestBase {
$settings['body'][$settings['langcode']][0] += $body;
$node = entity_create('node', $settings);
+ if (!empty($settings['revision'])) {
+ $node->setNewRevision();
+ }
$node->save();
// Small hack to link revisions to our test user.
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldQueryTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldQueryTest.php
index 305c298..fa0f4b2 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldQueryTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldQueryTest.php
@@ -148,19 +148,12 @@ class EntityFieldQueryTest extends WebTestBase {
// Add two revisions to an entity.
for ($i = 100; $i < 102; $i++) {
- $entity = field_test_create_entity(4);
$entity->ftvid = $i;
+ // Flag to make sure that the provided vid is used for a new revision.
+ $entity->use_provided_revision_id = $i;
$entity->{$this->field_names[0]}[LANGUAGE_NOT_SPECIFIED][0]['value'] = $i;
-
- drupal_write_record('test_entity', $entity, 'ftid');
- drupal_write_record('test_entity_revision', $entity);
-
- db_update('test_entity')
- ->fields(array('ftvid' => $entity->ftvid))
- ->condition('ftid', $entity->ftid)
- ->execute();
-
- field_attach_update('test_entity', $entity);
+ $entity->setNewRevision();
+ $entity->save();
}
}