diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module index a6f7987..10f6a92 100644 --- a/core/modules/content_translation/content_translation.module +++ b/core/modules/content_translation/content_translation.module @@ -60,6 +60,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; } } @@ -325,6 +335,82 @@ 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. + if ($entity instanceof ContentEntityInterface) { + $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($entity->entityType(), $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 aa1b1f3..c363233 100644 --- a/core/modules/content_translation/content_translation.pages.inc +++ b/core/modules/content_translation/content_translation.pages.inc @@ -86,6 +86,7 @@ function content_translation_overview(EntityInterface $entity) { // language we point the link to the translation form. if ($entity->access('update')) { $links['edit'] = isset($edit_links->links[$langcode]['href']) ? $edit_links->links[$langcode] : array('href' => $rel['edit-form']['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); @@ -187,14 +188,8 @@ function _content_translation_get_switch_links($path) { * @deprecated Use \Drupal\content_translation\Controller\ContentTranslationController::add() */ 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); - $form_state['langcode'] = $target->id; - $form_state['content_translation']['source'] = $source; - $form_state['content_translation']['target'] = $target; - $form_state['content_translation']['translation_form'] = !$entity->access('update'); + $form_state = array(); + content_translation_prepare_translation_form($entity, $source->id, $target->id, $form_state); return \Drupal::entityManager()->getForm($entity, 'default', $form_state); } @@ -213,25 +208,50 @@ function content_translation_add_page(EntityInterface $entity, Language $source * @deprecated Use \Drupal\content_translation\Controller\ContentTranslationController::edit() */ function content_translation_edit_page(EntityInterface $entity, Language $language = NULL) { - $language = !empty($language) ? $language : language(Language::TYPE_CONTENT); - $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, 'default', $form_state); } /** - * Populates target values with the source values. + * Form constructor for the translation deletion confirmation. * - * @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. + * @deprecated Use \Drupal\content_translation\Form\ContentTranslationForm::deleteTranslation() + */ +function content_translation_delete_confirm(array $form, array $form_state, EntityInterface $entity, Language $language) { + $controller = content_translation_controller($entity->entityType()); + $uri = $entity->uri('drupal:content-translation-overview'); + + return confirm_form( + $form, + t('Are you sure you want to delete the @language translation of %label?', array('@language' => $language->name, '%label' => $entity->label())), + $uri['path'], + t('This action cannot be undone.'), + t('Delete'), + t('Cancel') + ); +} + +/** + * Form submission handler for content_translation_delete_confirm(). */ -function content_translation_prepare_translation(EntityInterface $entity, Language $source, Language $target) { - if ($entity instanceof ContentEntityInterface) { - $source_translation = $entity->getTranslation($source->id); - $entity->addTranslation($target->id, $source_translation->getPropertyValues()); +function content_translation_delete_confirm_submit(array $form, array &$form_state) { + list($entity, $language) = $form_state['build_info']['args']; + $controller = content_translation_controller($entity->entityType()); + + // Remove the translated values. + $entity->removeTranslation($language->id); + $entity->save(); + + // Remove any existing path alias for the removed translation. + // @todo This should be taken care of by the Path module. + if (\Drupal::moduleHandler()->moduleExists('path')) { + $uri = $entity->uri(); + $conditions = array('source' => $uri['path'], 'langcode' => $language->id); + \Drupal::service('path.crud')->delete($conditions); } + + $uri = $entity->uri('drupal:content-translation-overview'); + $form_state['redirect'] = $uri['path']; } 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 e99c9b0..e98c89f 100644 --- a/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationController.php +++ b/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationController.php @@ -71,7 +71,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 8a1b198..62a0e51 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->doTestBasicTranslation(); + $this->assertFormLanguage(); $this->doTestTranslationOverview(); $this->doTestOutdatedStatus(); $this->doTestPublishedStatus(); @@ -218,6 +219,24 @@ protected function doTestAuthoringInfo() { } /** + * Tests the form language switch functionality. + */ + protected function assertFormLanguage() { + $entity = $this->container + ->get('plugin.manager.entity') + ->getStorageController($this->entityType) + ->load($this->entityId); + + $uri = $entity->uri('edit-form'); + $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($uri['path'], $options); + $this->assertRaw($entity->getTranslation($langcode)->{$this->fieldName}->value, format_string($message, array('@langcode' => $langcode))); + } + } + + /** * Tests translation deletion. */ protected function doTestTranslationDeletion() { 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 0b3da77..ea36b69 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 @@ -127,7 +127,10 @@ protected function assertWorkflows(UserInterface $user, $expected_status) { // Check whether the user is allowed to access the entity form in edit mode. $uri = $this->entity->uri('edit-form'); $edit_path = $uri['path']; - $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)); @@ -166,7 +169,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');