diff --git a/entity_translation.install b/entity_translation.install index 8b39013..1a82a2e 100644 --- a/entity_translation.install +++ b/entity_translation.install @@ -9,6 +9,8 @@ * Implements hook_schema(). */ function entity_translation_schema() { + $schema = array(); + $schema['entity_translation'] = array( 'description' => 'Table to track entity translations', 'fields' => array( @@ -25,7 +27,12 @@ function entity_translation_schema() { 'not null' => TRUE, 'description' => 'The entity id this translation relates to', ), - // @todo: Consider an integer field for 'language'. + 'revision_id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'The entity revision id this translation relates to', + ), 'language' => array( 'type' => 'varchar', 'length' => 32, @@ -73,6 +80,78 @@ function entity_translation_schema() { ), 'primary key' => array('entity_type', 'entity_id', 'language'), ); + + $schema['entity_translation_revision'] = array( + 'description' => 'Table to track entity translation revisions', + 'fields' => array( + 'entity_type' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The entity type this translation revision relates to', + ), + 'entity_id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'The entity id this translation revision relates to', + ), + 'revision_id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'The entity revision id this translation relates to', + ), + 'language' => array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The target language for this translation revision.', + ), + 'source' => array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The source language from which this translation revision was created.', + ), + 'uid' => array( + 'description' => 'The author of this translation revision.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'status' => array( + 'description' => 'Boolean indicating whether the translation revision is published (visible to non-administrators).', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 1, + ), + 'translate' => array( + 'description' => 'A boolean indicating whether this translation revision needs to be updated.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'created' => array( + 'description' => 'The Unix timestamp when the translation revision was created.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'changed' => array( + 'description' => 'The Unix timestamp when the translation revision was most recently saved.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'primary key' => array('entity_type', 'revision_id', 'language'), + 'indexes'=> array('revision_id' => array('revision_id')), + ); + return $schema; } @@ -261,3 +340,94 @@ function entity_translation_update_7004() { function entity_translation_update_7005() { registry_rebuild(); } + +/** + * Add revision schema for entity translation metadata. + */ +function entity_translation_update_7006() { + // Disable translation revisions on existing sites to avoid upgrade issues. It + // will have to be enabled/upgraded manually case by case. + variable_set('entity_translation_revision_enabled', FALSE); + + // Create revision id column. + $spec = array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'The entity revision id this translation relates to', + ); + db_add_field('entity_translation', 'revision_id', $spec); + + // Create the entity translation revision schema. + $table = array( + 'description' => 'Table to track entity translation revisions', + 'fields' => array( + 'entity_type' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The entity type this translation revision relates to', + ), + 'entity_id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'The entity id this translation revision relates to', + ), + 'revision_id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'The entity revision id this translation relates to', + ), + 'language' => array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The target language for this translation revision.', + ), + 'source' => array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The source language from which this translation revision was created.', + ), + 'uid' => array( + 'description' => 'The author of this translation revision.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'status' => array( + 'description' => 'Boolean indicating whether the translation revision is published (visible to non-administrators).', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 1, + ), + 'translate' => array( + 'description' => 'A boolean indicating whether this translation revision needs to be updated.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'created' => array( + 'description' => 'The Unix timestamp when the translation revision was created.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'changed' => array( + 'description' => 'The Unix timestamp when the translation revision was most recently saved.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'primary key' => array('entity_type', 'revision_id', 'language'), + 'indexes'=> array('revision_id' => array('revision_id')), + ); + db_create_table('entity_translation_revision', $table); +} diff --git a/includes/translation.handler.inc b/includes/translation.handler.inc index c8aa1bf..b0e0d49 100644 --- a/includes/translation.handler.inc +++ b/includes/translation.handler.inc @@ -136,6 +136,11 @@ interface EntityTranslationHandlerInterface { public function isRevision(); /** + * Return TRUE if the entity type supports revisions. + */ + public function isRevisionable(); + + /** * Replaces the wrapped entity. * * @param $entity @@ -338,6 +343,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa protected $entityInfo; protected $entityId; protected $bundle; + protected $revisionable; /** * The translation handler factory. @@ -408,14 +414,28 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa return; } + $revisionable = self::isEntityTypeRevisionable($entity_type); + $revisions_ids = array(); foreach ($entities as $id => $entity) { $entities[$id]->{$translations_key} = self::emptyTranslations(); + if ($revisionable) { + list(, $revisions_id,) = entity_extract_ids($entity_type, $entity); + $revisions_ids[$id] = $revisions_id; + } } - $results = db_select('entity_translation', 'et') + $query = db_select('entity_translation_revision', 'et') ->fields('et') - ->condition('entity_type', $entity_type) - ->condition('entity_id', array_keys($entities), 'IN') + ->condition('entity_type', $entity_type); + + if (!$revisionable) { + $query->condition('entity_id', array_keys($entities), 'IN'); + } + else { + $query->condition('revision_id', $revisions_ids, 'IN'); + } + + $results = $query ->orderBy('entity_id') ->orderBy('created') ->execute(); @@ -501,43 +521,14 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa * @see EntityTranslationHandlerInterface::saveTranslations() */ public function saveTranslations() { - // Delete and insert, rather than update, in case a value was added. - db_delete('entity_translation') - ->condition('entity_type', $this->entityType) - ->condition('entity_id', $this->entityId) - ->execute(); - $translations = $this->getTranslations(); - if (count($translations->data)) { - global $user; - - $columns = array('entity_type', 'entity_id', 'language', 'source', 'uid', 'status', 'translate', 'created', 'changed'); - $query = db_insert('entity_translation')->fields($columns); + // Save current values. + $this->doSaveTranslations($translations, 'entity_translation'); - // These values should overridde the translation ones as they are not - // supposed to change. - $overrides = array( - 'entity_id' => $this->entityId, - 'entity_type' => $this->entityType, - ); - - // These instead are just defaults. - $defaults = array( - 'source' => '', - 'uid' => $user->uid, - 'translate' => 0, - 'status' => 0, - 'created' => REQUEST_TIME, - 'changed' => REQUEST_TIME, - ); - - foreach ($translations->data as $langcode => $translation) { - $translation = $overrides + $translation + $defaults; - $query->values($translation); - } - - $query->execute(); + // Save revision values. + if ($this->isRevisionable()) { + $this->doSaveTranslations($translations, 'entity_translation_revision', TRUE); } // The translation handler interface decouples operations on translations at @@ -568,6 +559,54 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa } /** + * Saves entity translation records to the storage. + */ + protected function doSaveTranslations($translations, $table, $revision = FALSE) { + // Delete and insert, rather than update, in case a value was added. + $query = db_delete($table) + ->condition('entity_type', $this->entityType) + ->condition('entity_id', $this->entityId); + // If we are storing translations for the current revision or we are + // deleting the entity we should remove all translation data. + $langcode = $translations->original; + $hook = isset($translations->hook) ? $translations->hook : array(); + if ($revision && $this->isRevisionable() && (empty($hook[$langcode]['hook']) || $hook[$langcode]['hook'] != 'delete')) { + $query->condition('revision_id', $this->revisionId); + } + $query->execute(); + + if (count($translations->data)) { + $columns = array('entity_type', 'entity_id', 'revision_id', 'language', 'source', 'uid', 'status', 'translate', 'created', 'changed'); + $query = db_insert($table)->fields($columns); + + // These values should override the translation ones as they are not + // supposed to change. + $overrides = array( + 'entity_type' => $this->entityType, + 'entity_id' => $this->entityId, + 'revision_id' => $this->isRevisionable() ? $this->revisionId : $this->entityId, + ); + + // These instead are just defaults. + $defaults = array( + 'source' => '', + 'uid' => $GLOBALS['user']->uid, + 'translate' => 0, + 'status' => 0, + 'created' => REQUEST_TIME, + 'changed' => REQUEST_TIME, + ); + + foreach ($translations->data as $translation) { + $translation = $overrides + $translation + $defaults; + $query->values($translation); + } + + $query->execute(); + } + } + + /** * @see EntityTranslationHandlerInterface::getTranslations() */ public function getTranslations() { @@ -831,6 +870,25 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa } /** + * @see EntityTranslationHandlerInterface::isRevisionable() + */ + public function isRevisionable() { + $result = FALSE; + if (!isset($this->revisionable)) { + $result = self::isEntityTypeRevisionable($this->entityType); + } + return $result; + } + + /** + * Returns whether the entity type is revisionable. + */ + public static function isEntityTypeRevisionable($entity_type) { + $entity_info = entity_get_info($entity_type); + return variable_get('entity_translation_revision_enabled', TRUE) && !empty($entity_info['entity keys']['revision']); + } + + /** * @see EntityTranslationHandlerInterface::setEntity() */ public function setEntity($entity) { @@ -842,8 +900,8 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa $this->entity->{$translations_key} = self::emptyTranslations(); } - // Update bundle and entity id properties. - list($this->entityId, , $this->bundle) = entity_extract_ids($this->entityType, $this->entity); + // Update entity properties. + list($this->entityId, $this->revisionId, $this->bundle) = entity_extract_ids($this->entityType, $this->entity); // Initialize the handler id if needed. if (!empty($this->factory)) {