diff --git a/core/modules/content_translation/content_translation.install b/core/modules/content_translation/content_translation.install index a8d4c49..c253bbe 100644 --- a/core/modules/content_translation/content_translation.install +++ b/core/modules/content_translation/content_translation.install @@ -5,9 +5,6 @@ * Installation functions for Content Translation module. */ -use Drupal\Core\Language\LanguageInterface; -use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl; - /** * Implements hook_install(). */ @@ -15,6 +12,11 @@ function content_translation_install() { // Assign a fairly low weight to ensure our implementation of // hook_module_implements_alter() is run among the last ones. module_set_weight('content_translation', 10); + + // Initialize the Content Translation form language type configuration. + /** @var \Drupal\language\LanguageNegotiatorInterface $negotiator */ + $negotiator = \Drupal::service('language_negotiator'); + $negotiator->updateConfiguration([]); } /** diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module index 3b29ecc..a6a2a5e 100644 --- a/core/modules/content_translation/content_translation.module +++ b/core/modules/content_translation/content_translation.module @@ -13,6 +13,7 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Routing\RouteMatchInterface; +use Drupal\content_translation\Plugin\LanguageNegotiation\ContentTranslationFormLanguage; /** * Implements hook_help(). @@ -52,16 +53,37 @@ function content_translation_help($route_name, RouteMatchInterface $route_match) */ function content_translation_module_implements_alter(&$implementations, $hook) { switch ($hook) { - // Move some of our hook implementations to the end of the list. + // Move our entity_type_alter hook implementation to the end of the list. case 'entity_type_alter': $group = $implementations['content_translation']; unset($implementations['content_translation']); $implementations['content_translation'] = $group; break; + // Move our entity_prepare_form hook implementation to the beginning of the + // list, so that the entity in the form object is exchanged with its + // requested translation before any other hook implementations have had + // access on it. + case 'entity_prepare_form': + $group = $implementations['content_translation']; + unset($implementations['content_translation']); + $implementations = array_merge(array('content_translation' => $group), $implementations); + break; } } /** + * Implements hook_language_types_info(). + */ +function content_translation_language_types_info() { + return [ + ContentTranslationFormLanguage::TYPE => array( + 'fixed' => [ContentTranslationFormLanguage::METHOD_ID], + 'locked' => TRUE, + ), + ]; +} + +/** * Implements hook_language_type_info_alter(). */ function content_translation_language_types_info_alter(array &$language_types) { @@ -307,6 +329,32 @@ function content_translation_form_alter(array &$form, FormStateInterface $form_s } /** + * Implements hook_entity_prepare_form(). + */ +function content_translation_entity_prepare_form(EntityInterface $entity, $operation, FormStateInterface $form_state) { + // If the Content Translation form language differs from the current form + // language and we are editing an entity translation, we need to update the + // form language to match the specified value. + $langcode = \Drupal::languageManager()->getCurrentLanguage(ContentTranslationFormLanguage::TYPE)->getId(); + $prepare_translation = + $entity instanceof ContentEntityInterface && + in_array($operation, ['default', 'edit']) && + count($entity->getTranslationLanguages()) > 1 && + !$form_state->get(['content_translation', 'source']) && + !$form_state->get(['content_translation', 'translation_form']) && + $entity->language()->getId() != $langcode && + $entity->hasTranslation($langcode); + + if ($prepare_translation) { + /** @var \Drupal\Core\Entity\EntityFormInterface $form_object*/ + $form_object = $form_state->getFormObject(); + $translation = $entity->getTranslation($langcode); + $form_object->setEntity($translation); + $form_state->set('langcode', $langcode); + } +} + +/** * Implements hook_language_fallback_candidates_OPERATION_alter(). * * Performs language fallback for inaccessible translations. diff --git a/core/modules/content_translation/src/Controller/ContentTranslationController.php b/core/modules/content_translation/src/Controller/ContentTranslationController.php index 9c6879a..83432f3 100644 --- a/core/modules/content_translation/src/Controller/ContentTranslationController.php +++ b/core/modules/content_translation/src/Controller/ContentTranslationController.php @@ -8,6 +8,7 @@ namespace Drupal\content_translation\Controller; use Drupal\content_translation\ContentTranslationManagerInterface; +use Drupal\content_translation\Plugin\LanguageNegotiation\ContentTranslationFormLanguage; use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Entity\ContentEntityInterface; @@ -82,8 +83,9 @@ public function prepareTranslation(ContentEntityInterface $entity, LanguageInter * The route match. * @param string $entity_type_id * (optional) The entity type ID. + * * @return array Array of page elements to render. - * Array of page elements to render. + * Array of page elements to render. */ public function overview(RouteMatchInterface $route_match, $entity_type_id = NULL) { /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ @@ -92,6 +94,7 @@ public function overview(RouteMatchInterface $route_match, $entity_type_id = NUL $handler = $this->entityManager()->getHandler($entity_type_id, 'translation'); $manager = $this->manager; $entity_type = $entity->getEntityType(); + $destination = $this->getDestinationArray(); // Start collecting the cacheability metadata, starting with the entity and // later merge in the access result cacheability metadata. @@ -191,16 +194,17 @@ public function overview(RouteMatchInterface $route_match, $entity_type_id = NUL ->merge(CacheableMetadata::createFromObject($update_access)) ->merge(CacheableMetadata::createFromObject($translation_access)); if ($update_access->isAllowed() && $entity_type->hasLinkTemplate('edit-form')) { + $url = $entity->urlInfo('edit-form'); + $url->setOption('query', [ContentTranslationFormLanguage::QUERY_PARAMETER => $langcode] + $destination); $links['edit']['url'] = $entity->urlInfo('edit-form'); - $links['edit']['language'] = $language; } elseif (!$is_original && $translation_access->isAllowed()) { $links['edit']['url'] = $edit_url; } - if (isset($links['edit'])) { $links['edit']['title'] = $this->t('Edit'); } + $status = array('data' => array( '#type' => 'inline_template', '#template' => '{% if status %}{{ "Published"|t }}{% else %}{{ "Not published"|t }}{% endif %}{% if outdated %} {{ "outdated"|t }}{% endif %}', @@ -221,18 +225,17 @@ public function overview(RouteMatchInterface $route_match, $entity_type_id = NUL $cacheability = $cacheability ->merge(CacheableMetadata::createFromObject($delete_access)) ->merge(CacheableMetadata::createFromObject($translation_access)); + if ($entity->access('delete') && $entity_type->hasLinkTemplate('delete-form')) { - $links['delete'] = array( - 'title' => $this->t('Delete'), - 'url' => $entity->urlInfo('delete-form'), - 'language' => $language, - ); + $url = $entity->urlInfo('delete-form'); + $url->setOption('query', [ContentTranslationFormLanguage::QUERY_PARAMETER => $langcode] + $destination); + $links['delete']['url'] = $url; } elseif ($translation_access->isAllowed()) { - $links['delete'] = array( - 'title' => $this->t('Delete'), - 'url' => $delete_url, - ); + $links['delete']['url'] = $delete_url; + } + if (isset($links['delete'])) { + $links['delete']['title'] = $this->t('Delete'); } } } diff --git a/core/modules/content_translation/src/Plugin/LanguageNegotiation/ContentTranslationFormLanguage.php b/core/modules/content_translation/src/Plugin/LanguageNegotiation/ContentTranslationFormLanguage.php new file mode 100644 index 0000000..4e77d96 --- /dev/null +++ b/core/modules/content_translation/src/Plugin/LanguageNegotiation/ContentTranslationFormLanguage.php @@ -0,0 +1,50 @@ +get(static::QUERY_PARAMETER); + return $langcode ?: $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId(); + } + +} diff --git a/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php b/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php index 5faa85c..82bb1a0 100644 --- a/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php +++ b/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php @@ -7,6 +7,7 @@ namespace Drupal\content_translation\Tests; +use Drupal\content_translation\Plugin\LanguageNegotiation\ContentTranslationFormLanguage; use Drupal\Core\Cache\Cache; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Language\Language; @@ -56,6 +57,7 @@ function testTranslationUI() { $this->doTestPublishedStatus(); $this->doTestAuthoringInfo(); $this->doTestTranslationEdit(); + $this->doTestFormLanguageSwitch(); $this->doTestTranslationChanged(); $this->doTestTranslationDeletion(); } @@ -191,16 +193,19 @@ protected function doTestBasicTranslation() { */ protected function doTestTranslationOverview() { $entity = entity_load($this->entityTypeId, $this->entityId, TRUE); - $this->drupalGet($entity->urlInfo('drupal:content-translation-overview')); + $translate_url = $entity->urlInfo('drupal:content-translation-overview'); + $this->drupalGet($translate_url); + $translate_url->setAbsolute(FALSE); foreach ($this->langcodes as $langcode) { if ($entity->hasTranslation($langcode)) { $language = new Language(array('id' => $langcode)); - $view_path = $entity->url('canonical', array('language' => $language)); - $elements = $this->xpath('//table//a[@href=:href]', array(':href' => $view_path)); + $view_url = $entity->url('canonical', ['language' => $language]); + $elements = $this->xpath('//table//a[@href=:href]', array(':href' => $view_url)); $this->assertEqual((string) $elements[0], $entity->getTranslation($langcode)->label(), format_string('Label correctly shown for %language translation.', array('%language' => $langcode))); - $edit_path = $entity->url('edit-form', array('language' => $language)); - $elements = $this->xpath('//table//ul[@class="dropbutton"]/li/a[@href=:href]', array(':href' => $edit_path)); + $query = [ContentTranslationFormLanguage::QUERY_PARAMETER => $langcode, 'destination' => $translate_url->toString()]; + $edit_url = $entity->url('edit-form', ['query' => $query]); + $elements = $this->xpath('//table//ul[@class="dropbutton"]/li/a[@href=:href]', array(':href' => $edit_url)); $this->assertEqual((string) $elements[0], t('Edit'), format_string('Edit link correct for %language translation.', array('%language' => $langcode))); } } @@ -308,6 +313,20 @@ protected function doTestAuthoringInfo() { } /** + * Tests the form language switch functionality. + */ + protected function doTestFormLanguageSwitch() { + $entity = entity_load($this->entityTypeId, $this->entityId, TRUE); + + $message = 'The form language can be switched to @langcode through a query string parameter'; + foreach ($entity->getTranslationLanguages() as $langcode => $language) { + $url = $entity->urlInfo('edit-form', ['query' => ['content_translation_target' => $langcode]]); + $this->drupalGet($url); + $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/src/Tests/ContentTranslationWorkflowsTest.php b/core/modules/content_translation/src/Tests/ContentTranslationWorkflowsTest.php index 223bb81..f07f2ae 100644 --- a/core/modules/content_translation/src/Tests/ContentTranslationWorkflowsTest.php +++ b/core/modules/content_translation/src/Tests/ContentTranslationWorkflowsTest.php @@ -7,6 +7,7 @@ namespace Drupal\content_translation\Tests; +use Drupal\content_translation\Plugin\LanguageNegotiation\ContentTranslationFormLanguage; use Drupal\Component\Utility\SafeMarkup; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Url; @@ -192,8 +193,10 @@ protected function doTestWorkflows(UserInterface $user, $expected_status) { $this->clickLink('Edit', 2); // An editor should be pointed to the entity form in multilingual mode. // We need a new expected edit path with a new language. - $expected_edit_path = $this->entity->url('edit-form', $options); - $this->assertUrl($expected_edit_path, [], 'The translation overview points to the edit form for editors when editing translations.'); + $translate_url = $this->entity->url('drupal:content-translation-overview', ['language' => $languages[$this->langcodes[1]], 'absolute' => FALSE]); + $options = ['query' => [ContentTranslationFormLanguage::QUERY_PARAMETER => $langcode, 'destination' => $translate_url]]; + $expected_edit_url = $this->entity->url('edit-form', $options); + $this->assertUrl($expected_edit_url, [], 'The translation overview points to the edit form for editors when editing translations.'); } else { $this->clickLink('Edit');