diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module index a6988cf..67476c6 100644 --- a/core/modules/content_translation/content_translation.module +++ b/core/modules/content_translation/content_translation.module @@ -57,6 +57,16 @@ function content_translation_module_implements_alter(&$implementations, $hook) { unset($implementations['content_translation']); $implementations['content_translation'] = $group; break; + + // Since in this hook implementation we are changing the form language, by + // acting first we minimize the risk of having inconsistent behaviors due to + // different hook_entity_prepare_form() implementations assuming different + // form languages. + case 'entity_prepare_form': + $group = $implementations['content_translation']; + unset($implementations['content_translation']); + $implementations = array('content_translation' => $group) + $implementations; + break; } } @@ -270,6 +280,83 @@ function _content_translation_menu_strip_loaders($path) { } /** + * Implements hook_entity_prepare_form(). + */ +function content_translation_entity_prepare_form(EntityInterface $entity, $form_display, $operation, array &$form_state) { + $languages = language_list(); + $source = Drupal::request()->get('content_translation_source'); + $target = Drupal::request()->get('content_translation_target'); + content_translation_prepare_translation_form($entity, isset($languages[$source]) ? $source : FALSE, isset($languages[$target]) ? $target : NULL, $form_state); +} + +/** + * Prepares the entity and the form state to display a translation form. + * + * @param EntityInterface $entity + * The entity being translated. + * @param string $source + * The language code to be used as source. + * @param string $target + * The language code to be used as target. + * @param array $form_state + * An associative array representing the state of the form. + */ +function content_translation_prepare_translation_form(EntityInterface $entity, $source, $target, array &$form_state) { + if (empty($form_state['content_translation']['prepared']) && $entity->isTranslatable() && content_translation_access($entity, $source ? 'create' : 'update')) { + // Avoid preparing the entity form twice. + $form_state['content_translation']['prepared'] = TRUE; + + // If no valid translation language is specified, we just let the entity + // form controller determine most appropriate form language based on the + // entity data. + if (isset($target) && ($source || ($translations = $entity->getTranslationLanguages()) && isset($translations[$target]))) { + $form_state['langcode'] = $target; + $form_state['content_translation']['target'] = $target; + } + $form_state['content_translation']['source'] = $source; + + // Translators do not see the full entity form, just the translatable bits. + $form_state['content_translation']['translation_form'] = !$entity->access('update'); + + // If we have a source language defined we are creating a new translation + // for which we need to prepare the initial values. + if ($source) { + content_translation_prepare_translation($entity, $source, $target); + } + } +} + +/** + * Populates target values with the source values. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity being translated. + * @param string $source + * The language code to be used as source. + * @param string $target + * The language code to be used as target. + */ +function content_translation_prepare_translation(EntityInterface $entity, $source, $target) { + // @todo Unify field and property handling. + $entity = $entity->getNGEntity(); + if ($entity instanceof EntityNG) { + $source_translation = $entity->getTranslation($source); + $entity->addTranslation($target, $source_translation->getPropertyValues()); + } + else { + $instances = field_info_instances($entity->entityType(), $entity->bundle()); + foreach ($instances as $field_name => $instance) { + $field = field_info_field($field_name); + if (!empty($field['translatable'])) { + $value = $entity->get($field_name); + $value[$target] = isset($value[$source]) ? $value[$source] : array(); + $entity->set($field_name, $value); + } + } + } +} + +/** * Access callback for the translation overview page. * * @param \Drupal\Core\Entity\EntityInterface $entity diff --git a/core/modules/content_translation/content_translation.pages.inc b/core/modules/content_translation/content_translation.pages.inc index e277791..2f078ed 100644 --- a/core/modules/content_translation/content_translation.pages.inc +++ b/core/modules/content_translation/content_translation.pages.inc @@ -88,6 +88,7 @@ function content_translation_overview(EntityInterface $entity) { // language we point the link to the translation form. if ($edit_path && $entity->access('update')) { $links['edit'] = isset($edit_links->links[$langcode]['href']) ? $edit_links->links[$langcode] : array('href' => $edit_path, 'language' => $language); + $links['edit']['query']['content_translation_target'] = $langcode; } elseif (!$is_original && $controller->getTranslationAccess($entity, 'update')) { $links['edit'] = isset($translate_links->links[$langcode]['href']) ? $translate_links->links[$langcode] : array('href' => $translate_path, 'language' => $language); @@ -191,17 +192,10 @@ function _content_translation_get_switch_links($path) { * A processed form array ready to be rendered. */ function content_translation_add_page(EntityInterface $entity, Language $source = NULL, Language $target = NULL) { - $source = !empty($source) ? $source : $entity->language(); - $target = !empty($target) ? $target : language(Language::TYPE_CONTENT); - // @todo Exploit the upcoming hook_entity_prepare() when available. - content_translation_prepare_translation($entity, $source, $target); - $info = $entity->entityInfo(); $operation = isset($info['default_operation']) ? $info['default_operation'] : 'default'; - $form_state['langcode'] = $target->id; - $form_state['content_translation']['source'] = $source; - $form_state['content_translation']['target'] = $target; - $controller = content_translation_controller($entity->entityType()); - $form_state['content_translation']['translation_form'] = !$entity->access('update'); + $form_state = array(); + content_translation_prepare_translation_form($entity, $source->id, $target->id, $form_state); + $info = $entity->entityInfo(); return Drupal::entityManager()->getForm($entity, $operation, $form_state); } @@ -218,45 +212,15 @@ function content_translation_add_page(EntityInterface $entity, Language $source * A processed form array ready to be rendered. */ function content_translation_edit_page(EntityInterface $entity, Language $language = NULL) { - $language = !empty($language) ? $language : language(Language::TYPE_CONTENT); $info = $entity->entityInfo(); $operation = isset($info['default_operation']) ? $info['default_operation'] : 'default'; - $form_state['langcode'] = $language->id; + $form_state = array(); + content_translation_prepare_translation_form($entity, FALSE, $language->id, $form_state); $form_state['content_translation']['translation_form'] = TRUE; return Drupal::entityManager()->getForm($entity, $operation, $form_state); } /** - * Populates target values with the source values. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entitiy being translated. - * @param \Drupal\Core\Language\Language $source - * The language to be used as source. - * @param \Drupal\Core\Language\Language $target - * The language to be used as target. - */ -function content_translation_prepare_translation(EntityInterface $entity, Language $source, Language $target) { - // @todo Unify field and property handling. - $entity = $entity->getNGEntity(); - if ($entity instanceof EntityNG) { - $source_translation = $entity->getTranslation($source->id); - $entity->addTranslation($target->id, $source_translation->getPropertyValues()); - } - else { - $instances = field_info_instances($entity->entityType(), $entity->bundle()); - foreach ($instances as $field_name => $instance) { - $field = field_info_field($field_name); - if (!empty($field['translatable'])) { - $value = $entity->get($field_name); - $value[$target->id] = isset($value[$source->id]) ? $value[$source->id] : array(); - $entity->set($field_name, $value); - } - } - } -} - -/** * Form constructor for the translation deletion confirmation. */ function content_translation_delete_confirm(array $form, array $form_state, EntityInterface $entity, Language $language) { diff --git a/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationController.php b/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationController.php index 6948642..9a1976b 100644 --- a/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationController.php +++ b/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationController.php @@ -110,7 +110,7 @@ public function getTranslationAccess(EntityInterface $entity, $op) { * Implements ContentTranslationControllerInterface::getSourceLanguage(). */ public function getSourceLangcode(array $form_state) { - return isset($form_state['content_translation']['source']) ? $form_state['content_translation']['source']->id : FALSE; + return !empty($form_state['content_translation']['source']) ? $form_state['content_translation']['source'] : FALSE; } /** diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationUITest.php b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationUITest.php index 1e21d53..3f5e3c4 100644 --- a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationUITest.php +++ b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationUITest.php @@ -35,6 +35,7 @@ */ function testTranslationUI() { $this->assertBasicTranslation(); + $this->assertFormLanguage(); $this->assertOutdatedStatus(); $this->assertPublishedStatus(); $this->assertAuthoringInfo(); @@ -196,6 +197,24 @@ protected function assertAuthoringInfo() { } /** + * Tests the form language switch functionality. + */ + protected function assertFormLanguage() { + $entity = $this->container + ->get('plugin.manager.entity') + ->getStorageController($this->entityType) + ->load($this->entityId); + + $path = $this->controller->getEditPath($entity); + $message = 'The form language can be switched to @langcode through a query string parameter'; + foreach ($entity->getTranslationLanguages() as $langcode => $language) { + $options = array('query' => array('content_translation_target' => $langcode)); + $this->drupalGet($path, $options); + $this->assertRaw($entity->getTranslation($langcode)->{$this->fieldName}->value, format_string($message, array('@langcode' => $langcode))); + } + } + + /** * Tests translation deletion. */ protected function assertTranslationDeletion() { diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationWorkflowsTest.php b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationWorkflowsTest.php index 4930283..879815a 100644 --- a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationWorkflowsTest.php +++ b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationWorkflowsTest.php @@ -123,7 +123,10 @@ protected function assertWorkflows(UserInterface $user, $expected_status) { // Check whether the user is allowed to access the entity form in edit mode. $edit_path = $this->controller->getEditPath($this->entity); - $options = array('language' => $languages[$default_langcode]); + $options = array( + 'language' => $languages[$default_langcode], + 'query' => array('content_translation_target' => $default_langcode), + ); $this->drupalGet($edit_path, $options); $this->assertResponse($expected_status['edit'], format_string('The @user_label has the expected edit access.', $args)); @@ -161,7 +164,8 @@ protected function assertWorkflows(UserInterface $user, $expected_status) { if ($editor) { $this->clickLink('Edit', 2); // An editor should be pointed to the entity form in multilingual mode. - $this->assertUrl($edit_path, $options, 'The translation overview points to the edit form for editors when editing translations.'); + $query = array('query' => array('content_translation_target' => $langcode)); + $this->assertUrl($edit_path, $options + $query, 'The translation overview points to the edit form for editors when editing translations.'); } else { $this->clickLink('Edit');