Commit a2616738 authored by effulgentsia's avatar effulgentsia

Issue #2860097 by plach, timmillwood, Sam152, Wim Leers, hchonov, catch,...

Issue #2860097 by plach, timmillwood, Sam152, Wim Leers, hchonov, catch, amateescu, matsbla: Ensure that content translations can be moderated independently
parent dd993b38
......@@ -127,6 +127,15 @@ function content_moderation_entity_translation_delete(EntityInterface $translati
->entityTranslationDelete($translation);
}
/**
* Implements hook_entity_prepare_form().
*/
function content_moderation_entity_prepare_form(EntityInterface $entity, $operation, FormStateInterface $form_state) {
\Drupal::service('class_resolver')
->getInstanceFromDefinition(EntityTypeInfo::class)
->entityPrepareForm($entity, $operation, $form_state);
}
/**
* Implements hook_form_alter().
*/
......@@ -252,6 +261,7 @@ function content_moderation_action_info_alter(&$definitions) {
* Implements hook_entity_bundle_info_alter().
*/
function content_moderation_entity_bundle_info_alter(&$bundles) {
$translatable = FALSE;
/** @var \Drupal\workflows\WorkflowInterface $workflow */
foreach (Workflow::loadMultipleByType('content_moderation') as $workflow) {
/** @var \Drupal\content_moderation\Plugin\WorkflowType\ContentModeration $plugin */
......@@ -260,10 +270,18 @@ function content_moderation_entity_bundle_info_alter(&$bundles) {
foreach ($plugin->getBundlesForEntityType($entity_type_id) as $bundle_id) {
if (isset($bundles[$entity_type_id][$bundle_id])) {
$bundles[$entity_type_id][$bundle_id]['workflow'] = $workflow->id();
// If we have even one moderation-enabled translatable bundle, we need
// to make the moderation state bundle translatable as well, to enable
// the revision translation merge logic also for content moderation
// state revisions.
if (!empty($bundles[$entity_type_id][$bundle_id]['translatable'])) {
$translatable = TRUE;
}
}
}
}
}
$bundles['content_moderation_state']['content_moderation_state']['translatable'] = $translatable;
}
/**
......
......@@ -221,4 +221,16 @@ protected function realSave() {
return parent::save();
}
/**
* {@inheritdoc}
*/
protected function getFieldsToSkipFromTranslationChangesCheck() {
$field_names = parent::getFieldsToSkipFromTranslationChangesCheck();
// We need to skip the parent entity revision ID, since that will always
// change on every save, otherwise every translation would be marked as
// affected regardless of actual changes.
$field_names[] = 'content_entity_revision_id';
return $field_names;
}
}
......@@ -35,11 +35,6 @@ public function onPresave(ContentEntityInterface $entity, $default_revision, $pu
// This is probably not necessary if configuration is setup correctly.
$entity->setNewRevision(TRUE);
$entity->isDefaultRevision($default_revision);
if ($entity->hasField('revision_translation_affected')) {
// @todo remove this when revision and translation issues have been
// resolved. https://www.drupal.org/node/2860097
$entity->set('revision_translation_affected', TRUE);
}
// Update publishing status if it can be updated and if it needs updating.
if (($entity instanceof EntityPublishedInterface) && $entity->isPublished() !== $published_state) {
......
......@@ -98,10 +98,9 @@ public function entityPresave(EntityInterface $entity) {
$current_state = $workflow->getTypePlugin()
->getState($entity->moderation_state->value);
// This entity is default if it is new, a new translation, the default
// revision, or the default revision is not published.
// This entity is default if it is new, the default revision, or the
// default revision is not published.
$update_default_revision = $entity->isNew()
|| $entity->isNewTranslation()
|| $current_state->isDefaultRevisionState()
|| !$this->moderationInfo->isDefaultRevisionPublished($entity);
......@@ -247,27 +246,28 @@ public function entityTranslationDelete(EntityInterface $translation) {
* @see EntityFieldManagerInterface::getExtraFields()
*/
public function entityView(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
if (!$this->moderationInfo->isModeratedEntity($entity)) {
return;
}
if (!$this->moderationInfo->isLatestRevision($entity)) {
// If the component is not defined for this display, we have nothing to do.
if (!$display->getComponent('content_moderation_control')) {
return;
}
if ($this->moderationInfo->isLiveRevision($entity)) {
// The moderation form should be displayed only when viewing the latest
// (translation-affecting) revision, unless it was created as published
// default revision.
if (!$entity->isLatestRevision() && !$entity->isLatestTranslationAffectedRevision()) {
return;
}
// Don't display the moderation form when when:
// - The revision is not translation affected.
// - There are more than one translation languages.
// - The entity has pending revisions.
if (!$this->moderationInfo->isPendingRevisionAllowed($entity)) {
return;
if (($entity->isDefaultRevision() || $entity->wasDefaultRevision()) && ($moderation_state = $entity->get('moderation_state')->value)) {
$workflow = $this->moderationInfo->getWorkflowForEntity($entity);
if ($workflow->getTypePlugin()->getState($moderation_state)->isPublishedState()) {
return;
}
}
$component = $display->getComponent('content_moderation_control');
if ($component) {
$build['content_moderation_control'] = $this->formBuilder->getForm(EntityModerationForm::class, $entity);
}
$build['content_moderation_control'] = $this->formBuilder->getForm(EntityModerationForm::class, $entity);
}
}
......@@ -7,10 +7,12 @@
use Drupal\Core\Entity\ContentEntityFormInterface;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\ContentEntityTypeInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Form\FormInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
......@@ -268,6 +270,40 @@ public function entityBaseFieldInfo(EntityTypeInterface $entity_type) {
return $fields;
}
/**
* Replaces the entity form entity object with a proper revision object.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity being edited.
* @param string $operation
* The entity form operation.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*
* @see hook_entity_prepare_form()
*/
public function entityPrepareForm(EntityInterface $entity, $operation, FormStateInterface $form_state) {
/** @var \Drupal\Core\Entity\EntityFormInterface $form_object */
$form_object = $form_state->getFormObject();
if ($this->isModeratedEntityEditForm($form_object) && !$entity->isNew()) {
// Generate a proper revision object for the current entity. This allows
// to correctly handle translatable entities having pending revisions.
/** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
$storage = $this->entityTypeManager->getStorage($entity->getEntityTypeId());
/** @var \Drupal\Core\Entity\ContentEntityInterface $new_revision */
$new_revision = $storage->createRevision($entity, FALSE);
// Restore the revision ID as other modules may expect to find it still
// populated. This will reset the "new revision" flag, however the entity
// object will be marked as a new revision again on submit.
// @see \Drupal\Core\Entity\ContentEntityForm::buildEntity()
$revision_key = $new_revision->getEntityType()->getKey('revision');
$new_revision->set($revision_key, $new_revision->getLoadedRevisionId());
$form_object->setEntity($new_revision);
}
}
/**
* Alters bundle forms to enforce revision handling.
*
......@@ -291,57 +327,15 @@ public function formAlter(array &$form, FormStateInterface $form_state, $form_id
$this->entityTypeManager->getHandler($config_entity_type->getBundleOf(), 'moderation')->enforceRevisionsBundleFormAlter($form, $form_state, $form_id);
}
}
elseif ($form_object instanceof ContentEntityFormInterface && in_array($form_object->getOperation(), ['edit', 'default'])) {
elseif ($this->isModeratedEntityEditForm($form_object)) {
/** @var \Drupal\Core\Entity\ContentEntityFormInterface $form_object */
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
$entity = $form_object->getEntity();
if ($this->moderationInfo->isModeratedEntity($entity)) {
$this->entityTypeManager
->getHandler($entity->getEntityTypeId(), 'moderation')
->enforceRevisionsEntityFormAlter($form, $form_state, $form_id);
if (!$this->moderationInfo->isPendingRevisionAllowed($entity)) {
$latest_revision = $this->moderationInfo->getLatestRevision($entity->getEntityTypeId(), $entity->id());
if ($entity->bundle()) {
$bundle_type_id = $entity->getEntityType()->getBundleEntityType();
$bundle = $this->entityTypeManager->getStorage($bundle_type_id)->load($entity->bundle());
$type_label = $bundle->label();
}
else {
$type_label = $entity->getEntityType()->getLabel();
}
$translation = $this->moderationInfo->getAffectedRevisionTranslation($latest_revision);
$args = [
'@type_label' => $type_label,
'@latest_revision_edit_url' => $translation->toUrl('edit-form', ['language' => $translation->language()])->toString(),
'@latest_revision_delete_url' => $translation->toUrl('delete-form', ['language' => $translation->language()])->toString(),
];
$label = $this->t('Unable to save this @type_label.', $args);
$message = $this->t('<a href="@latest_revision_edit_url">Publish</a> or <a href="@latest_revision_delete_url">delete</a> the latest revision to allow all workflow transitions.', $args);
$full_message = $this->t('Unable to save this @type_label. <a href="@latest_revision_edit_url">Publish</a> or <a href="@latest_revision_delete_url">delete</a> the latest revision to allow all workflow transitions.', $args);
drupal_set_message($full_message, 'error');
$form['moderation_state']['#access'] = FALSE;
$form['actions']['#access'] = FALSE;
$form['invalid_transitions'] = [
'label' => [
'#type' => 'item',
'#prefix' => '<strong class="label">',
'#markup' => $label,
'#suffix' => '</strong>',
],
'message' => [
'#type' => 'item',
'#markup' => $message,
],
'#weight' => 999,
'#no_valid_transitions' => TRUE,
];
if ($form['footer']) {
$form['invalid_transitions']['#group'] = 'footer';
}
}
// Submit handler to redirect to the latest version, if available.
$form['actions']['submit']['#submit'][] = [EntityTypeInfo::class, 'bundleFormRedirect'];
......@@ -360,6 +354,21 @@ public function formAlter(array &$form, FormStateInterface $form_state, $form_id
}
}
/**
* Checks whether the specified form allows to edit a moderated entity.
*
* @param \Drupal\Core\Form\FormInterface $form_object
* The form object.
*
* @return bool
* TRUE if the form should get form moderation, FALSE otherwise.
*/
protected function isModeratedEntityEditForm(FormInterface $form_object) {
return $form_object instanceof ContentEntityFormInterface &&
in_array($form_object->getOperation(), ['edit', 'default'], TRUE) &&
$this->moderationInfo->isModeratedEntity($form_object->getEntity());
}
/**
* Redirect content entity edit forms on save, if there is a pending revision.
*
......
......@@ -138,6 +138,9 @@ public function buildForm(array $form, FormStateInterface $form_state, ContentEn
public function submitForm(array &$form, FormStateInterface $form_state) {
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
$entity = $form_state->get('entity');
/** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
$storage = \Drupal::entityTypeManager()->getStorage($entity->getEntityTypeId());
$entity = $storage->createRevision($entity, $entity->isDefaultRevision());
$new_state = $form_state->getValue('new_state');
......
......@@ -127,13 +127,6 @@ public function getAffectedRevisionTranslation(ContentEntityInterface $entity) {
}
}
/**
* {@inheritdoc}
*/
public function isPendingRevisionAllowed(ContentEntityInterface $entity) {
return !(!$entity->isRevisionTranslationAffected() && count($entity->getTranslationLanguages()) > 1 && $this->hasPendingRevision($entity));
}
/**
* {@inheritdoc}
*/
......@@ -145,8 +138,20 @@ public function isLatestRevision(ContentEntityInterface $entity) {
* {@inheritdoc}
*/
public function hasPendingRevision(ContentEntityInterface $entity) {
return $this->isModeratedEntity($entity)
&& !($this->getLatestRevisionId($entity->getEntityTypeId(), $entity->id()) == $this->getDefaultRevisionId($entity->getEntityTypeId(), $entity->id()));
$result = FALSE;
if ($this->isModeratedEntity($entity)) {
/** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
$storage = $this->entityTypeManager->getStorage($entity->getEntityTypeId());
$latest_revision_id = $storage->getLatestTranslationAffectedRevisionId($entity->id(), $entity->language()->getId());
$default_revision_id = $entity->isDefaultRevision() && !$entity->isNewRevision() && ($revision_id = $entity->getRevisionId()) ?
$revision_id : $this->getDefaultRevisionId($entity->getEntityTypeId(), $entity->id());
if ($latest_revision_id != $default_revision_id) {
/** @var \Drupal\Core\Entity\ContentEntityInterface $latest_revision */
$latest_revision = $storage->loadRevision($latest_revision_id);
$result = !$latest_revision->wasDefaultRevision();
}
}
return $result;
}
/**
......@@ -172,9 +177,15 @@ public function isDefaultRevisionPublished(ContentEntityInterface $entity) {
// Loop through each language that has a translation.
foreach ($default_revision->getTranslationLanguages() as $language) {
// Load the translated revision.
$language_revision = $default_revision->getTranslation($language->getId());
$translation = $default_revision->getTranslation($language->getId());
// If the moderation state is empty, it was not stored yet so no point
// in doing further work.
$moderation_state = $translation->moderation_state->value;
if (!$moderation_state) {
continue;
}
// Return TRUE if a translation with a published state is found.
if ($workflow->getTypePlugin()->getState($language_revision->moderation_state->value)->isPublishedState()) {
if ($workflow->getTypePlugin()->getState($moderation_state)->isPublishedState()) {
return TRUE;
}
}
......
......@@ -100,19 +100,6 @@ public function getDefaultRevisionId($entity_type_id, $entity_id);
*/
public function getAffectedRevisionTranslation(ContentEntityInterface $entity);
/**
* Determines if pending revisions are allowed.
*
* @internal
*
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* The content entity.
*
* @return bool
* If pending revisions are allowed.
*/
public function isPendingRevisionAllowed(ContentEntityInterface $entity);
/**
* Determines if an entity is a latest revision.
*
......
......@@ -166,10 +166,9 @@ protected function updateModeratedEntity($moderation_state_id) {
/** @var \Drupal\content_moderation\ContentModerationState $current_state */
$current_state = $workflow->getTypePlugin()->getState($moderation_state_id);
// This entity is default if it is new, a new translation, the default
// revision state, or the default revision is not published.
// This entity is default if it is new, the default revision state, or the
// default revision is not published.
$update_default_revision = $entity->isNew()
|| $entity->isNewTranslation()
|| $current_state->isDefaultRevisionState()
|| !$content_moderation_info->isDefaultRevisionPublished($entity);
......
......@@ -176,8 +176,8 @@ protected function opSimple() {
$entity_base_table_alias = $this->table;
// The bundle field of an entity type is not revisionable so we need to
// join the data table.
$entity_base_table = $entity_type->isTranslatable() ? $entity_type->getDataTable() : $entity_type->getBaseTable();
// join the base table.
$entity_base_table = $entity_type->getBaseTable();
$entity_revision_base_table = $entity_type->isTranslatable() ? $entity_type->getRevisionDataTable() : $entity_type->getRevisionTable();
if ($this->table === $entity_revision_base_table) {
$configuration = [
......@@ -187,12 +187,6 @@ protected function opSimple() {
'left_field' => $entity_type->getKey('id'),
'type' => 'INNER',
];
if ($entity_type->isTranslatable()) {
$configuration['extra'][] = [
'field' => $entity_type->getKey('langcode'),
'left_field' => $entity_type->getKey('langcode'),
];
}
$join = Views::pluginManager('join')->createInstance('standard', $configuration);
$entity_base_table_alias = $this->query->addRelationship($entity_base_table, $join, $entity_revision_base_table);
......
......@@ -296,14 +296,6 @@ public function testContentTranslationNodeForm() {
$this->drupalGet($latest_version_path, ['language' => $french]);
$this->assertTrue($this->xpath('//ul[@class="entity-moderation-form"]'));
// It should not be possible to add a new english revision.
$this->drupalGet($edit_path);
$this->assertSession()->fieldNotExists('moderation_state[0][state]');
$this->assertSession()->pageTextContains('Unable to save this Moderated content.');
$this->clickLink('Publish');
$this->assertSession()->fieldValueEquals('body[0][value]', 'Third version of the content.');
$this->drupalGet($edit_path);
$this->clickLink('Delete');
$this->assertSession()->buttonExists('Delete');
......@@ -324,7 +316,7 @@ public function testContentTranslationNodeForm() {
$this->drupalGet($latest_version_path, ['language' => $french]);
$this->assertFalse($this->xpath('//ul[@class="entity-moderation-form"]'));
// Now we can publish the english (revision 5).
// Publish the English pending revision (revision 5).
$this->drupalGet($edit_path);
$this->assertSession()->optionExists('moderation_state[0][state]', 'draft');
$this->assertSession()->optionExists('moderation_state[0][state]', 'published');
......@@ -337,13 +329,13 @@ public function testContentTranslationNodeForm() {
$this->drupalGet($latest_version_path);
$this->assertFalse($this->xpath('//ul[@class="entity-moderation-form"]'));
// Make sure we're allowed to create a pending french revision.
// Make sure we are allowed to create a pending French revision.
$this->drupalGet($edit_path, ['language' => $french]);
$this->assertSession()->optionExists('moderation_state[0][state]', 'draft');
$this->assertSession()->optionExists('moderation_state[0][state]', 'published');
$this->assertSession()->optionExists('moderation_state[0][state]', 'archived');
// Add a english pending revision (revision 6).
// Add an English pending revision (revision 6).
$this->drupalGet($edit_path);
$this->assertSession()->optionExists('moderation_state[0][state]', 'draft');
$this->assertSession()->optionExists('moderation_state[0][state]', 'published');
......@@ -355,16 +347,10 @@ public function testContentTranslationNodeForm() {
$this->drupalGet($latest_version_path);
$this->assertTrue($this->xpath('//ul[@class="entity-moderation-form"]'));
// Make sure we're not allowed to create a pending french revision.
$this->drupalGet($edit_path, ['language' => $french]);
$this->assertSession()->fieldNotExists('moderation_state[0][state]');
$this->assertSession()->pageTextContains('Unable to save this Moderated content.');
$this->drupalGet($latest_version_path, ['language' => $french]);
$this->assertFalse($this->xpath('//ul[@class="entity-moderation-form"]'));
// We should be able to publish the english pending revision (revision 7)
// Publish the English pending revision (revision 7)
$this->drupalGet($edit_path);
$this->assertSession()->optionExists('moderation_state[0][state]', 'draft');
$this->assertSession()->optionExists('moderation_state[0][state]', 'published');
......@@ -377,44 +363,17 @@ public function testContentTranslationNodeForm() {
$this->drupalGet($latest_version_path);
$this->assertFalse($this->xpath('//ul[@class="entity-moderation-form"]'));
// Make sure we're allowed to create a pending french revision.
// Make sure we are allowed to create a pending French revision.
$this->drupalGet($edit_path, ['language' => $french]);
$this->assertSession()->optionExists('moderation_state[0][state]', 'draft');
$this->assertSession()->optionExists('moderation_state[0][state]', 'published');
$this->assertSession()->optionExists('moderation_state[0][state]', 'archived');
// Make sure we're allowed to create a pending english revision.
$this->drupalGet($edit_path);
$this->assertSession()->optionExists('moderation_state[0][state]', 'draft');
$this->assertSession()->optionExists('moderation_state[0][state]', 'published');
$this->assertSession()->optionExists('moderation_state[0][state]', 'archived');
// Create new moderated content. (revision 1).
$this->drupalPostForm('node/add/moderated_content', [
'title[0][value]' => 'Second moderated content',
'body[0][value]' => 'First version of the content.',
'moderation_state[0][state]' => 'published',
], t('Save'));
$node = $this->drupalGetNodeByTitle('Second moderated content');
$this->assertTrue($node->language(), 'en');
$edit_path = sprintf('node/%d/edit', $node->id());
$translate_path = sprintf('node/%d/translations/add/en/fr', $node->id());
// Add a pending revision (revision 2).
// Make sure we are allowed to create a pending English revision.
$this->drupalGet($edit_path);
$this->assertSession()->optionExists('moderation_state[0][state]', 'draft');
$this->assertSession()->optionExists('moderation_state[0][state]', 'published');
$this->assertSession()->optionExists('moderation_state[0][state]', 'archived');
$this->drupalPostForm(NULL, [
'body[0][value]' => 'Second version of the content.',
'moderation_state[0][state]' => 'draft',
], t('Save'));
// It shouldn't be possible to translate as we have a pending revision.
$this->drupalGet($translate_path);
$this->assertSession()->fieldNotExists('moderation_state[0][state]');
$this->assertSession()->pageTextContains('Unable to save this Moderated content.');
// Create new moderated content (revision 1).
$this->drupalPostForm('node/add/moderated_content', [
......@@ -445,11 +404,6 @@ public function testContentTranslationNodeForm() {
'moderation_state[0][state]' => 'draft',
], t('Save (this translation)'));
// Editing the original translation should not be possible.
$this->drupalGet($edit_path);
$this->assertSession()->fieldNotExists('moderation_state[0][state]');
$this->assertSession()->pageTextContains('Unable to save this Moderated content.');
// Updating and publishing the french translation is still possible.
$this->drupalGet($edit_path, ['language' => $french]);
$this->assertSession()->optionExists('moderation_state[0][state]', 'draft');
......
......@@ -121,10 +121,10 @@ public function testStateFilterViewsRelationship() {
$translated_forward_revision->moderation_state = 'translated_draft';
$translated_forward_revision->save();
// Four revisions for the nodes when no filter.
$this->assertNodesWithFilters([$node, $second_node, $third_node, $third_node], []);
// The three default revisions are listed when no filter is specified.
$this->assertNodesWithFilters([$node, $second_node, $third_node], []);
// The default revision of node one and three is published.
// The default revision of node one and three are published.
$this->assertNodesWithFilters([$node, $third_node], [
'default_revision_state' => 'editorial-published',
]);
......
......@@ -145,4 +145,21 @@ protected function loadContentLanguageSettings($entity_type_id, $bundle) {
return $config;
}
/**
* Checks whether support for pending revisions should be enabled.
*
* @return bool
* TRUE if pending revisions should be enabled, FALSE otherwise.
*
* @internal
* There is ongoing discussion about how pending revisions should behave.
* The logic enabling pending revision support is likely to change once a
* decision is made.
*
* @see https://www.drupal.org/node/2940575
*/
public static function isPendingRevisionSupportEnabled() {
return \Drupal::moduleHandler()->moduleExists('content_moderation');
}
}
......@@ -2,6 +2,7 @@
namespace Drupal\content_translation\Controller;
use Drupal\content_translation\ContentTranslationManager;
use Drupal\content_translation\ContentTranslationManagerInterface;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Controller\ControllerBase;
......@@ -87,6 +88,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();
$use_latest_revisions = $entity_type->isRevisionable() && ContentTranslationManager::isPendingRevisionSupportEnabled();
// Start collecting the cacheability metadata, starting with the entity and
// later merge in the access result cacheability metadata.
......@@ -99,6 +101,9 @@ public function overview(RouteMatchInterface $route_match, $entity_type_id = NUL
$rows = [];
$show_source_column = FALSE;
$default_revision = $entity;
/** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
$storage = $this->entityTypeManager()->getStorage($entity_type_id);
if ($this->languageManager()->isMultilingual()) {
// Determine whether the current entity is translatable.
......@@ -121,6 +126,16 @@ public function overview(RouteMatchInterface $route_match, $entity_type_id = NUL
$language_name = $language->getName();
$langcode = $language->getId();
// If the entity type is revisionable, we may have pending revisions
// with translations not available yet in the default revision. Thus we
// need to load the latest translation-affecting revision for each
// language to be sure we are listing all available translations.
if ($use_latest_revisions) {
$latest_revision_id = $storage->getLatestTranslationAffectedRevisionId($entity->id(), $langcode);
$entity = $latest_revision_id ? $storage->loadRevision($latest_revision_id) : $default_revision;
$translations = $entity->getTranslationLanguages();
}
$add_url = new Url(
"entity.$entity_type_id.content_translation_add",
[
......@@ -330,8 +345,21 @@ public function overview(RouteMatchInterface $route_match, $entity_type_id = NUL
* A processed form array ready to be rendered.
*/
public function add(LanguageInterface $source, LanguageInterface $target, RouteMatchInterface $route_match, $entity_type_id = NULL) {
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
$entity = $route_match->getParameter($entity_type_id);
// In case of a pending revision, make sure we load the latest
// translation-affecting revision for the source language, otherwise the
// initial form values may not be up-to-date.
if (!$entity->isDefaultRevision() && ContentTranslationManager::isPendingRevisionSupportEnabled()) {
/** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
$storage = $this->entityTypeManager()->getStorage($entity->getEntityTypeId());
$revision_id = $storage->getLatestTranslationAffectedRevisionId($entity->id(), $source->getId());
if ($revision_id != $entity->getRevisionId()) {
$entity = $storage->loadRevision($revision_id);
}
}
// @todo Exploit the upcoming hook_entity_prepare() when available.
// See https://www.drupal.org/node/1810394.
$this->prepareTranslation($entity, $source, $target);
......
......@@ -2,6 +2,7 @@
namespace Drupal\content_translation\Routing;
use Drupal\content_translation\ContentTranslationManager;
use Drupal\content_translation\ContentTranslationManagerInterface;
use Drupal\Core\Routing\RouteSubscriberBase;
use Drupal\Core\Routing\RoutingEvents;
......@@ -55,6 +56,7 @@ protected function alterRoutes(RouteCollection $collection) {
}
$path = $base_path . '/translations';
$load_latest_revision = ContentTranslationManager::isPendingRevisionSupportEnabled();
$route = new Route(
$path,
......@@ -70,6 +72,7 @@ protected function alterRoutes(RouteCollection $collection) {
'parameters' => [
$entity_type_id => [
'type' => 'entity:' . $entity_type_id,
'load_latest_revision' => $load_latest_revision,
],
],
'_admin_route' => $is_admin,
......@@ -102,6 +105,7 @@ protected function alterRoutes(RouteCollection $collection) {
],
$entity_type_id => [
'type' => 'entity:' . $entity_type_id,
'load_latest_revision' => $load_latest_revision,
],
],
'_admin_route' => $is_admin,
......@@ -127,6 +131,7 @@ protected function alterRoutes(RouteCollection $collection) {
],
$entity_type_id => [
'type' => 'entity:' . $entity_type_id,
'load_latest_revision' => $load_latest_revision,
],
],
'_admin_route' => $is_admin,
......@@ -152,6 +157,7 @@ protected function alterRoutes(RouteCollection $collection) {
],
$entity_type_id => [
'type' => 'entity:' . $entity_type_id,
'load_latest_revision' => $load_latest_revision,
],
],
'_admin_route' => $is_admin,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment