diff -Nur --exclude '*.svn' entity/entity.test entity.svn/entity.test --- entity/entity.test 2011-07-25 20:08:24.035573549 -0400 +++ entity.svn/entity.test 2011-07-21 16:26:19.069141709 -0400 @@ -60,6 +60,56 @@ } /** + * Tests CRUD for entities support revisions. + */ + function testCRUDRevisisions() { + module_enable(array('entity_feature')); + + $user1 = $this->drupalCreateUser(); + $user2 = $this->drupalCreateUser(); + + // Create a test entity. + $entity_initial = entity_create('entity_test_revision', array('name' => 'test', 'uid' => $user1->uid)); + $entity_initial->save(); + + $entities = array_values(entity_load('entity_test_revision', FALSE)); + $this->assertEqual(count($entities), 1, 'Entity created.'); + + // Saving the entity in revision mode should create a new revision. + $entity_bis = clone $entity_initial; + $entity_bis->uid = $user2->uid; + $entity_bis->revision = TRUE; + $entity_bis->save(); + $this->assertNotEqual($entity_bis->rid, $entity_initial->rid, 'Saving an entity in revision mode creates a revision.'); + + // Check the saved entity. + $entities = entity_load('entity_test_revision', array($entity_initial->pid)); + $entity = reset($entities); + $this->assertEqual($entity->uid, $user2->uid, 'Modifications to the entity were saved.'); + + // Check the initial revision. + $entities = entity_load('entity_test_revision', array($entity_initial->pid), array('rid' => $entity_initial->rid)); + $entity = reset($entities); + $this->assertEqual($entity->uid, $user1->uid, 'Modifications to the entity have not affected the initial revision.'); + + // Saving the entity normally should not create a new revision. + $entity_ter = clone $entity_bis; + $entity_ter->uid = $user1->uid; + $entity_ter->save(); + $this->assertEqual($entity_ter->rid, $entity_bis->rid, 'Saving an entity does not create a revision.'); + + // Check the saved entity. + $entities = entity_load('entity_test_revision', array($entity_initial->pid)); + $entity = reset($entities); + $this->assertEqual($entity->uid, $user1->uid, 'Modifications to the entity were saved.'); + + // Delete the entity. + $entity_initial->delete(); + $entities = array_values(entity_load('entity_test_revision', FALSE)); + $this->assertEqual(count($entities), 0, 'Entity successfully deleted.'); + } + + /** * Tests CRUD API functions: entity_(create|delete|save) */ function testCRUDAPIfunctions() { diff -Nur --exclude '*.svn' entity/includes/entity.controller.inc entity.svn/includes/entity.controller.inc --- entity/includes/entity.controller.inc 2011-07-25 20:08:23.995573496 -0400 +++ entity.svn/includes/entity.controller.inc 2011-07-25 19:17:04.911057439 -0400 @@ -316,6 +316,12 @@ db_delete($this->entityInfo['base table']) ->condition($this->idKey, $ids, 'IN') ->execute(); + + if (isset($this->entityInfo['revision table'])) { + db_delete($this->entityInfo['revision table']) + ->condition($this->idKey, $ids, 'IN') + ->execute(); + } // Reset the cache as soon as the changes have been applied. $this->resetCache($ids); @@ -353,19 +359,48 @@ $this->invoke('presave', $entity); - if (!empty($entity->{$this->idKey}) && empty($entity->is_new)) { + // When saving a new revision, unset any existing revision ID 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 (!empty($this->revisionKey) && empty($entity->is_new) && !empty($entity->revision) && !empty($entity->{$this->revisionKey})) { + $entity->old_revision_id = $entity->{$this->revisionKey}; + unset($entity->{$this->revisionKey}); + } + + if (empty($entity->{$this->idKey}) || !empty($entity->is_new)) { + // For new entities, create the row in the base table, then save the + // revision. + $return = drupal_write_record($this->entityInfo['base table'], $entity); + if (!empty($this->revisionKey)) { + $this->saveRevision($entity); + $update_base_table = TRUE; + } + $this->invoke('insert', $entity); + } + else { $return = drupal_write_record($this->entityInfo['base table'], $entity, $this->idKey); + + if (!empty($this->revisionKey)) { + $update_base_table = $this->saveRevision($entity); + } + $this->resetCache(array($entity->{$this->idKey})); $this->invoke('update', $entity); } - else { - $return = drupal_write_record($this->entityInfo['base table'], $entity); - $this->invoke('insert', $entity); + + if (!empty($update_base_table)) { + // Go back to the base table and update the pointer to the revision ID. + db_update($this->entityInfo['base table']) + ->fields(array($this->revisionKey => $entity->{$this->revisionKey})) + ->condition($this->idKey, $entity->{$this->idKey}) + ->execute(); } + // Ignore slave server temporarily. db_ignore_slave(); unset($entity->is_new); unset($entity->original); + unset($entity->revision); return $return; } @@ -376,6 +411,16 @@ } } + protected function saveRevision($entity) { + if (!empty($entity->revision) || (isset($entity->is_new) && $entity->is_new)) { + drupal_write_record($this->entityInfo['revision table'], $entity); + return TRUE; + } + else { + drupal_write_record($this->entityInfo['revision table'], $entity, $this->revisionKey); + } + } + /** * Implements EntityAPIControllerInterface. */ diff -Nur --exclude '*.svn' entity/tests/entity_feature.module entity.svn/tests/entity_feature.module --- entity/tests/entity_feature.module 2011-07-25 20:08:24.045573562 -0400 +++ entity.svn/tests/entity_feature.module 2011-07-21 16:26:19.069141709 -0400 @@ -30,3 +30,29 @@ return $types; } + +/** + * Implements hook_default_entity_test_revision_type(). + */ +function entity_feature_default_entity_test_revision_type() { + $types['main'] = entity_create('entity_test_revision_type', array( + 'name' => 'main', + 'label' => t('Main test type'), + 'weight' => 0, + 'locked' => TRUE, + )); + + // Types used during CRUD testing. + $types['test'] = entity_create('entity_test_revision_type', array( + 'name' => 'test', + 'label' => 'label', + 'weight' => 0, + )); + $types['test2'] = entity_create('entity_test_revision_type', array( + 'name' => 'test2', + 'label' => 'label2', + 'weight' => 2, + )); + + return $types; +} diff -Nur --exclude '*.svn' entity/tests/entity_test.install entity.svn/tests/entity_test.install --- entity/tests/entity_test.install 2011-07-25 20:08:24.045573562 -0400 +++ entity.svn/tests/entity_test.install 2011-07-21 16:26:19.069141709 -0400 @@ -133,5 +133,32 @@ 'name' => array('name'), ), ); + + $schema['entity_test_revision'] = $schema['entity_test']; + $schema['entity_test_revision']['fields']['rid'] = array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'default' => NULL, + 'description' => "The current revision ID of the entity.", + ); + + $schema['entity_test_revision_revision'] = $schema['entity_test']; + $schema['entity_test_revision_revision']['fields']['rid'] = array( + 'type' => 'serial', + 'not null' => TRUE, + 'description' => 'Primary Key: Unique revision ID.', + ); + $schema['entity_test_revision_revision']['fields']['pid'] = array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'default' => NULL, + 'description' => "The ID of the attached entity.", + ); + $schema['entity_test_revision_revision']['primary key'] = array('rid'); + + $schema['entity_test_revision_type'] = $schema['entity_test_type']; + return $schema; } diff -Nur --exclude '*.svn' entity/tests/entity_test.module entity.svn/tests/entity_test.module --- entity/tests/entity_test.module 2011-07-25 20:08:24.045573562 -0400 +++ entity.svn/tests/entity_test.module 2011-07-21 16:26:19.069141709 -0400 @@ -41,6 +41,40 @@ 'name' => 'name', ), ), + + 'entity_test_revision' => array( + 'label' => t('Test Entity (revision support)'), + 'entity class' => 'EntityClassRevision', + 'controller class' => 'EntityAPIController', + 'base table' => 'entity_test_revision', + 'revision table' => 'entity_test_revision_revision', + 'fieldable' => TRUE, + 'entity keys' => array( + 'id' => 'pid', + 'revision' => 'rid', + 'bundle' => 'name', + ), + // Make use the class' label() and uri() implementation by default. + 'label callback' => 'entity_class_label', + 'uri callback' => 'entity_class_uri', + 'bundles' => array(), + 'bundle keys' => array( + 'bundle' => 'name', + ), + ), + 'entity_test_revision_type' => array( + 'label' => t('Test entity type'), + 'entity class' => 'Entity', + 'controller class' => 'EntityAPIController', + 'base table' => 'entity_test_revision_type', + 'fieldable' => FALSE, + 'bundle of' => 'entity_test_revision', + 'exportable' => TRUE, + 'entity keys' => array( + 'id' => 'id', + 'name' => 'name', + ), + ), ); return $return; } @@ -132,6 +166,16 @@ } } +/** + * Main class for test entities (with revision support). + */ +class EntityClassRevision extends EntityClass { + + public function __construct(array $values = array(), $entityType = NULL) { + Entity::__construct($values, 'entity_test_revision'); + } + +} /** *