diff --git a/revisioning.info b/revisioning.info index 45565f4..68c1cd2 100644 --- a/revisioning.info +++ b/revisioning.info @@ -14,6 +14,8 @@ files[] = views/revisioning_handler_filter_node_state.inc files[] = views/revisioning_handler_filter_revision_state.inc files[] = views/revisioning_handler_filter_revision_latest.inc files[] = views/revisioning_handler_filter_revision_latest_published.inc +files[] = revisioning.translation_handler.inc +files[] = revisioning.test ; Information added by Drupal.org packaging script on 2014-06-26 version = "7.x-1.9" diff --git a/revisioning.module b/revisioning.module index 326357d..d0fb758 100644 --- a/revisioning.module +++ b/revisioning.module @@ -424,6 +424,24 @@ function revisioning_node_load($nodes, $types) { } /** + * Implements hook_entity_info_alter(). + */ +function revisioning_entity_info_alter(&$entity_info) { + // If the Entity Translation module exists, register our own path scheme. This + // will allow the default revision form to contain the correct translation + // data. Override the default class with our own, so the class can tell the + // system to use the correct path scheme. + if (module_exists('entity_translation')) { + $et_info = &$entity_info['node']['translation']['entity_translation']; + $et_info['class'] = 'EntityTranslationNodeRevisioningHandler'; + $et_info['path schemes']['revisioning'] = array( + 'view path' => 'node/%node/revisions/%vid/view', + 'edit path' => 'node/%node/revisions/%vid/edit', + ); + } +} + +/** * Implements hook_entity_prepare_view(). * * First of the dont_display hooks. diff --git a/revisioning.pages.inc b/revisioning.pages.inc index cc92630..6cfdb53 100644 --- a/revisioning.pages.inc +++ b/revisioning.pages.inc @@ -19,8 +19,20 @@ function revisioning_form_alter(&$form, &$form_state, $form_id) { $is_moderated_content = isset($node->revision_moderation) ? $node->revision_moderation : revisioning_content_is_moderated($form['type']['#value'], $node); + // Make sure we are not adding a new translation, in which case we do not + // create a new revision. + $et_add_translation = FALSE; + if (module_exists('entity_translatsion') && !empty($node->nid) && entity_translation_enabled('node', $node)) { + $et_add_translation = !!preg_match('@edit/add/.+?/.+?$@', $form['#action']); + + // Disable revision if adding a new translation. + if ($et_add_translation && !empty($node->is_current)) { + $node->revision = FALSE; + } + } + // Alter the Create/Edit content form, if subject to moderation. - if ($is_moderated_content) { + if ($is_moderated_content && !$et_add_translation) { // "Create new revision" must be set when the node is to be moderated. $node->revision = TRUE; // Next line is not essential, just ensures form options are @@ -60,7 +72,9 @@ function revisioning_form_alter(&$form, &$form_state, $form_id) { '#type' => 'radios', '#options' => $options, '#default_value' => isset($node->nid) - ? (int) $node->revision + (int) $is_moderated_content + ? (int) $node->revision + (int) $is_moderated_content - ( + $et_add_translation ? (int) $node->is_current : 0 + ) : ($is_moderated_content ? REVISIONING_NEW_REVISION_WITH_MODERATION : REVISIONING_NEW_REVISION_NO_MODERATION), ); unset($form['revision_information']['revision']); diff --git a/revisioning.test b/revisioning.test new file mode 100644 index 0000000..f0acdc4 --- /dev/null +++ b/revisioning.test @@ -0,0 +1,117 @@ + "Revisioning Entity translation integration", + 'description' => "Test the revisioning logic integration with the Entity translation module.", + 'group' => "Revisioning", + ); + } + + public function setUp() { + parent::setUp('revisioning'); + if (module_enable(array('entity_translation'), TRUE)) { + // Enable another language. + $info = language_negotiation_info(); + locale_add_language('fr'); + language_negotiation_set('language_content', array( + 'locale-url' => $info['locale-url'], + 'language-default' => $info['language-default'], + )); + + // Create a content type with a translatable field. Make sure we enable + // translations for it. + node_type_save((object) array( + 'type' => 'test_ct', + 'name' => 'Test content type', + 'base' => 'node_content', + )); + field_create_field(array( + 'field_name' => 'test_field', + 'cardinality' => 1, + 'type' => 'text', + 'translatable' => 1, + )); + field_create_instance(array( + 'entity_type' => 'node', + 'bundle' => 'test_ct', + 'field_name' => 'test_field', + 'label' => 'Test field', + )); + module_load_include('inc', 'entity_translation', 'entity_translation.node'); + variable_set('language_content_type_test_ct', ENTITY_TRANSLATION_ENABLED); + variable_set('entity_translation_settings_node__test_ct', array( + 'default_language' => 'en', + 'hide_language_selector' => 0, + 'exclude_language_none' => 1, + 'lock_language' => 0, + 'shared_fields_original_only' => 1, + )); + + // Activate revisioning. + variable_set('node_options_test_ct', array('revision_moderation', 'revision')); + + // Clear all caches. + $this->resetAll(); + + // Create a new admin user and log in. + $admin = $this->drupalCreateUser(array_keys(module_invoke_all('permission'))); + $this->drupalLogin($admin); + } + else { + $this->fail('This test requires the Entity Translation module to run.'); + } + } + + public function testET() { + // Create a node. + $node = $this->drupalCreateNode(array( + 'type' => 'test_ct', + 'language' => 'en', + 'test_field' => array( + 'en' => array( + array('value' => $this->randomName(20) . '--en'), + ), + ), + // This is to prevent a Fatal Error. It is fixed in the latest dev release + // of Drupal 7 (not yet published in Drupal 7.44). + 'body' => array( + LANGUAGE_NONE => array(array()), + 'en' => array(array()), + ), + )); + + // Publish the current revision, and create a new one, pending. + _revisioning_publish_latest_revision($node); + $published_vid = $node->vid; + $node->title = $this->randomName(8); + node_save($node); + $this->assertEqual(2, revisioning_get_number_of_revisions($node->nid)); + + // Go to the translation tab. Should show an "add" link. Add a new + // translation. + $this->drupalGet("node/{$node->nid}/translate"); + $this->assertLink('add'); + $this->clickLink('add'); + $edit = array( + 'test_field[fr][0][value]' => $this->randomName(8) . '--fr', + ); + $this->drupalPost(NULL, $edit, 'Save'); + $this->drupalGet("fr/node/{$node->nid}/revisions/{$node->vid}/view"); + $this->assertText('--fr'); + $this->drupalGet("node/{$node->nid}/revisions/{$node->vid}/view"); + $this->assertText('--en'); + + // Should have registered the translation, and there should be no more "add" + // link. It should not have created a new revision either, and should not + // have published the currently pending revision. + $this->drupalGet("node/{$node->nid}/translate"); + $this->assertNoLink('add'); + $this->assertEqual(2, revisioning_get_number_of_revisions($node->nid)); + $this->assertEqual($published_vid, revisioning_get_current_node_revision_id($node->nid)); + } +} diff --git a/revisioning.translation_handler.inc b/revisioning.translation_handler.inc new file mode 100644 index 0000000..f6827a6 --- /dev/null +++ b/revisioning.translation_handler.inc @@ -0,0 +1,89 @@ +isModerated = revisioning_content_is_moderated($this->bundle, $entity)) { + $this->refreshEntity(); + $this->setPathScheme(self::REVISIONING_SCHEME); + } + } + + public function initPathScheme($path = NULL) { + if ($this->isModerated) { + $this->setPathScheme(self::REVISIONING_SCHEME); + return self::REVISIONING_SCHEME; + } + else { + return parent::initPathScheme($path); + } + } + + public function getTranslations() { + $this->refreshEntity(); + return parent::getTranslations(); + } + + protected function getPathInstance($path) { + if (!empty($this->pathScheme) && $this->pathScheme == self::REVISIONING_SCHEME) { + $path_segments = explode('/', $path); + foreach ($path_segments as $index => $segment) { + if ($segment == '%node') { + $path_segments[$index] = $this->getEntityId(); + } + elseif ($segment == '%vid') { + $path_segments[$index] = $this->revisionId; + } + elseif ($segment{0} == '%' && isset($this->routerMap[$index])) { + $path_segments[$index] = $this->routerMap[$index]; + } + } + + return implode('/', $path_segments); + } + else { + return parent::getPathInstance($path); + } + } + + public function setEntity($entity) { + // Sometimes this method is called right after the constructor, effectively + // resetting our node to an older revision. Allow it to be stored again, but + // call our refresh method right after it, to be sure we have the right + // data. + parent::setEntity($entity); + $this->refreshEntity(); + } + + protected function refreshEntity() { + // @todo Always load latest revision?? At least we are sure it contains + // translation data; for the last published revision, we cannot be sure... + $latest_revision_id = revisioning_get_latest_revision_id($this->getEntityId()); + if ($latest_revision_id !== $this->revisionId) { + $old_revision_id = $this->revisionId; + parent::setEntity(node_load($this->getEntityId(), $latest_revision_id)); + + // If we needed to refresh, it is possible we have a sibling floating + // around which contains the actual entity form. This can happen in our + // case, as the correct revision is not always loaded in each context (for + // example, a different node could be loaded for the access callback, + // meaning the entity_translation_get_handler() function will return a new + // handler, different from our current one. In that case, check if the + // factory contains another instance for the same node, but for the + // latest revision ID. If so, check if we are in an entity form context. + // This is necessary to get the language tabs right. + if (!empty($this->factory)) { + $handler = $this->factory->getHandler('node', $this->entity); + if ($handler != $this) { + if ($handler->isEntityForm()) { + $this->entityForm = TRUE; + } + } + } + } + } +} diff --git a/views/revisioning_handler_filter_revision_state.inc b/views/revisioning_handler_filter_revision_state.inc index a38e920..b05a10c 100644 --- a/views/revisioning_handler_filter_revision_state.inc +++ b/views/revisioning_handler_filter_revision_state.inc @@ -47,7 +47,7 @@ class revisioning_handler_filter_revision_state extends views_handler_filter_in_ $subclauses[] = "($revisions_table.vid>$node_table.vid OR ($node_table.status=0 AND (SELECT COUNT(vid) FROM {" . $revisions_table . "} WHERE nid=$node_table.nid)=1))"; // Only add this join if there is not already a 'Revision NID of the // content revision' contextual filter relationship. - if (empty($this->query->relationships[$this->relationship])) { + if (empty($this->query->relationships[$node_table])) { // Make sure UNIQUE is set! $this->query->table_queue[$revisions_table]['join'] = new views_join(); $this->query->table_queue[$revisions_table]['join']->construct($node_table, $revisions_table, 'nid', 'nid');