Issue #2949710 by plach, catch: Pending revisions may become unavailable when untranslatable fields affect all translations
parent
787ca247df
commit
5fd118611a
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
use Drupal\content_translation\BundleTranslationSettingsInterface;
|
||||
use Drupal\content_translation\ContentTranslationManager;
|
||||
use Drupal\Core\Config\Entity\ThirdPartySettingsInterface;
|
||||
use Drupal\Core\Entity\ContentEntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
|
@ -115,10 +116,13 @@ function _content_translation_form_language_content_settings_form_alter(array &$
|
|||
// Displayed the "shared fields widgets" toggle.
|
||||
if ($content_translation_manager instanceof BundleTranslationSettingsInterface) {
|
||||
$settings = $content_translation_manager->getBundleTranslationSettings($entity_type_id, $bundle);
|
||||
$force_hidden = ContentTranslationManager::isPendingRevisionSupportEnabled($entity_type_id, $bundle);
|
||||
$form['settings'][$entity_type_id][$bundle]['settings']['content_translation']['untranslatable_fields_hide'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Hide non translatable fields on translation forms'),
|
||||
'#default_value' => !empty($settings['untranslatable_fields_hide']),
|
||||
'#default_value' => $force_hidden || !empty($settings['untranslatable_fields_hide']),
|
||||
'#disabled' => $force_hidden,
|
||||
'#description' => $force_hidden ? t('Moderated content requires non-translatable fields to be edited in the original language form.') : '',
|
||||
'#states' => [
|
||||
'visible' => [
|
||||
':input[name="settings[' . $entity_type_id . '][' . $bundle . '][translatable]"]' => [
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
use Drupal\content_translation\BundleTranslationSettingsInterface;
|
||||
use Drupal\content_translation\ContentTranslationManager;
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Entity\ContentEntityFormInterface;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
|
@ -177,9 +178,13 @@ function content_translation_entity_bundle_info_alter(&$bundles) {
|
|||
foreach ($bundles as $entity_type_id => &$info) {
|
||||
foreach ($info as $bundle => &$bundle_info) {
|
||||
$bundle_info['translatable'] = $content_translation_manager->isEnabled($entity_type_id, $bundle);
|
||||
if ($content_translation_manager instanceof BundleTranslationSettingsInterface) {
|
||||
if ($bundle_info['translatable'] && $content_translation_manager instanceof BundleTranslationSettingsInterface) {
|
||||
$settings = $content_translation_manager->getBundleTranslationSettings($entity_type_id, $bundle);
|
||||
$bundle_info['untranslatable_fields.default_translation_affected'] = !empty($settings['untranslatable_fields_hide']);
|
||||
// If pending revision support is enabled for this bundle, we need to
|
||||
// hide untranslatable field widgets, otherwise changes in pending
|
||||
// revisions might be overridden by changes in later default revisions.
|
||||
$bundle_info['untranslatable_fields.default_translation_affected'] =
|
||||
!empty($settings['untranslatable_fields_hide']) || ContentTranslationManager::isPendingRevisionSupportEnabled($entity_type_id, $bundle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace Drupal\content_translation;
|
|||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
|
||||
/**
|
||||
* Provides common functionality for content translation.
|
||||
|
@ -148,6 +149,11 @@ class ContentTranslationManager implements ContentTranslationManagerInterface, B
|
|||
/**
|
||||
* Checks whether support for pending revisions should be enabled.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* The ID of the entity type to be checked.
|
||||
* @param string $bundle_id
|
||||
* (optional) The ID of the bundle to be checked. Defaults to none.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if pending revisions should be enabled, FALSE otherwise.
|
||||
*
|
||||
|
@ -158,8 +164,29 @@ class ContentTranslationManager implements ContentTranslationManagerInterface, B
|
|||
*
|
||||
* @see https://www.drupal.org/node/2940575
|
||||
*/
|
||||
public static function isPendingRevisionSupportEnabled() {
|
||||
return \Drupal::moduleHandler()->moduleExists('content_moderation');
|
||||
public static function isPendingRevisionSupportEnabled($entity_type_id, $bundle_id = NULL) {
|
||||
if (!\Drupal::moduleHandler()->moduleExists('content_moderation')) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
foreach (Workflow::loadMultipleByType('content_moderation') as $workflow) {
|
||||
/** @var \Drupal\content_moderation\Plugin\WorkflowType\ContentModeration $plugin */
|
||||
$plugin = $workflow->getTypePlugin();
|
||||
$entity_type_ids = array_flip($plugin->getEntityTypes());
|
||||
if (isset($entity_type_ids[$entity_type_id])) {
|
||||
if (!isset($bundle_id)) {
|
||||
return TRUE;
|
||||
}
|
||||
else {
|
||||
$bundle_ids = array_flip($plugin->getBundlesForEntityType($entity_type_id));
|
||||
if (isset($bundle_ids[$bundle_id])) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -88,7 +88,7 @@ class ContentTranslationController extends ControllerBase {
|
|||
$handler = $this->entityManager()->getHandler($entity_type_id, 'translation');
|
||||
$manager = $this->manager;
|
||||
$entity_type = $entity->getEntityType();
|
||||
$use_latest_revisions = $entity_type->isRevisionable() && ContentTranslationManager::isPendingRevisionSupportEnabled();
|
||||
$use_latest_revisions = $entity_type->isRevisionable() && ContentTranslationManager::isPendingRevisionSupportEnabled($entity_type_id, $entity->bundle());
|
||||
|
||||
// Start collecting the cacheability metadata, starting with the entity and
|
||||
// later merge in the access result cacheability metadata.
|
||||
|
@ -351,7 +351,7 @@ class ContentTranslationController extends ControllerBase {
|
|||
// 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()) {
|
||||
if (!$entity->isDefaultRevision() && ContentTranslationManager::isPendingRevisionSupportEnabled($entity_type_id, $entity->bundle())) {
|
||||
/** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
|
||||
$storage = $this->entityTypeManager()->getStorage($entity->getEntityTypeId());
|
||||
$revision_id = $storage->getLatestTranslationAffectedRevisionId($entity->id(), $source->getId());
|
||||
|
|
|
@ -56,7 +56,7 @@ class ContentTranslationRouteSubscriber extends RouteSubscriberBase {
|
|||
}
|
||||
|
||||
$path = $base_path . '/translations';
|
||||
$load_latest_revision = ContentTranslationManager::isPendingRevisionSupportEnabled();
|
||||
$load_latest_revision = ContentTranslationManager::isPendingRevisionSupportEnabled($entity_type_id);
|
||||
|
||||
$route = new Route(
|
||||
$path,
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_translation\Functional;
|
||||
|
||||
use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
|
||||
|
||||
/**
|
||||
* Base class for pending revision translation tests.
|
||||
*/
|
||||
abstract class ContentTranslationPendingRevisionTestBase extends ContentTranslationTestBase {
|
||||
|
||||
use ContentTypeCreationTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['language', 'content_translation', 'content_moderation', 'node'];
|
||||
|
||||
/**
|
||||
* The entity storage.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\ContentEntityStorageInterface
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
/**
|
||||
* Permissions common to all test accounts.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $commonPermissions;
|
||||
|
||||
/**
|
||||
* The current test account.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $currentAccount;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
$this->entityTypeId = 'node';
|
||||
$this->bundle = 'article';
|
||||
|
||||
$this->commonPermissions = [
|
||||
'view any unpublished content',
|
||||
"translate {$this->bundle} {$this->entityTypeId}",
|
||||
"create content translations",
|
||||
'use editorial transition create_new_draft',
|
||||
'use editorial transition publish',
|
||||
'use editorial transition archive',
|
||||
'use editorial transition archived_draft',
|
||||
'use editorial transition archived_published',
|
||||
];
|
||||
|
||||
parent::setUp();
|
||||
|
||||
/** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager */
|
||||
$entity_type_manager = $this->container->get('entity_type.manager');
|
||||
$this->storage = $entity_type_manager->getStorage($this->entityTypeId);
|
||||
|
||||
// @todo Remove this line once https://www.drupal.org/node/2945928 is fixed.
|
||||
$this->config('node.settings')->set('use_admin_theme', '1')->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables content moderation for the test entity type and bundle.
|
||||
*/
|
||||
protected function enableContentModeration() {
|
||||
$this->drupalLogin($this->rootUser);
|
||||
$workflow_id = 'editorial';
|
||||
$this->drupalGet('/admin/config/workflow/workflows');
|
||||
$edit['bundles[' . $this->bundle . ']'] = TRUE;
|
||||
$this->drupalPostForm('admin/config/workflow/workflows/manage/' . $workflow_id . '/type/' . $this->entityTypeId, $edit, t('Save'));
|
||||
// Ensure the parent environment is up-to-date.
|
||||
// @see content_moderation_workflow_insert()
|
||||
\Drupal::service('entity_type.bundle.info')->clearCachedBundles();
|
||||
\Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
|
||||
/** @var \Drupal\Core\Routing\RouteBuilderInterface $router_builder */
|
||||
$router_builder = $this->container->get('router.builder');
|
||||
$router_builder->rebuildIfNeeded();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEditorPermissions() {
|
||||
$editor_permissions = [
|
||||
"edit any {$this->bundle} content",
|
||||
"delete any {$this->bundle} content",
|
||||
"view {$this->bundle} revisions",
|
||||
"delete {$this->bundle} revisions",
|
||||
];
|
||||
return array_merge($editor_permissions, $this->commonPermissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getTranslatorPermissions() {
|
||||
return array_merge(parent::getTranslatorPermissions(), $this->commonPermissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setupBundle() {
|
||||
parent::setupBundle();
|
||||
$this->createContentType(['type' => $this->bundle]);
|
||||
}
|
||||
|
||||
}
|
|
@ -10,14 +10,7 @@ use Drupal\language\Entity\ConfigurableLanguage;
|
|||
*
|
||||
* @group content_translation
|
||||
*/
|
||||
class ContentTranslationUntranslatableFieldsTest extends ContentTranslationTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['language', 'content_translation', 'entity_test'];
|
||||
class ContentTranslationUntranslatableFieldsTest extends ContentTranslationPendingRevisionTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
@ -39,20 +32,13 @@ class ContentTranslationUntranslatableFieldsTest extends ContentTranslationTestB
|
|||
$this->assertFalse($definitions[$this->fieldName]->isTranslatable());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEditorPermissions() {
|
||||
return array_merge(parent::getTranslatorPermissions(), ['administer entity_test content', 'view test entity']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that hiding untranslatable field widgets works correctly.
|
||||
*/
|
||||
public function testHiddenWidgets() {
|
||||
/** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager */
|
||||
$entity_type_manager = $this->container->get('entity_type.manager');
|
||||
$id = $this->createEntity([], 'en');
|
||||
$id = $this->createEntity(['title' => $this->randomString()], 'en');
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
|
||||
$entity = $entity_type_manager
|
||||
->getStorage($this->entityTypeId)
|
||||
|
@ -60,7 +46,8 @@ class ContentTranslationUntranslatableFieldsTest extends ContentTranslationTestB
|
|||
|
||||
// Check that the untranslatable field widget is displayed on the edit form
|
||||
// and no translatability clue is displayed yet.
|
||||
$this->drupalGet($entity->toUrl('edit-form'));
|
||||
$en_edit_url = $entity->toUrl('edit-form');
|
||||
$this->drupalGet($en_edit_url);
|
||||
$field_xpath = '//input[@name="' . $this->fieldName . '[0][value]"]';
|
||||
$this->assertNotEmpty($this->xpath($field_xpath));
|
||||
$clue_xpath = '//label[@for="edit-' . strtr($this->fieldName, '_', '-') . '-0-value"]/span[text()="(all languages)"]';
|
||||
|
@ -81,30 +68,29 @@ class ContentTranslationUntranslatableFieldsTest extends ContentTranslationTestB
|
|||
|
||||
// Check that the widget is displayed along with its clue in the edit form
|
||||
// for both languages.
|
||||
$this->drupalGet($entity->toUrl('edit-form'));
|
||||
$this->drupalGet($en_edit_url);
|
||||
$this->assertNotEmpty($this->xpath($field_xpath));
|
||||
$this->assertNotEmpty($this->xpath($clue_xpath));
|
||||
$it_language = ConfigurableLanguage::load('it');
|
||||
$this->drupalGet($entity->toUrl('edit-form', ['language' => $it_language]));
|
||||
$it_edit_url = $entity->toUrl('edit-form', ['language' => ConfigurableLanguage::load('it')]);
|
||||
$this->drupalGet($it_edit_url);
|
||||
$this->assertNotEmpty($this->xpath($field_xpath));
|
||||
$this->assertNotEmpty($this->xpath($clue_xpath));
|
||||
|
||||
// Configure untranslatable field widgets to be hidden on non-default
|
||||
// language edit forms.
|
||||
$edit = [
|
||||
'settings[' . $this->entityTypeId . '][' . $this->bundle . '][settings][content_translation][untranslatable_fields_hide]' => 1,
|
||||
];
|
||||
$this->drupalPostForm('admin/config/regional/content-language', $edit, 'Save configuration');
|
||||
$settings_key = 'settings[' . $this->entityTypeId . '][' . $this->bundle . '][settings][content_translation][untranslatable_fields_hide]';
|
||||
$settings_url = 'admin/config/regional/content-language';
|
||||
$this->drupalPostForm($settings_url, [$settings_key => 1], 'Save configuration');
|
||||
|
||||
// Verify that the widget is displayed in the default language edit form,
|
||||
// but no clue is displayed.
|
||||
$this->drupalGet($entity->toUrl('edit-form'));
|
||||
$this->drupalGet($en_edit_url);
|
||||
$field_xpath = '//input[@name="' . $this->fieldName . '[0][value]"]';
|
||||
$this->assertNotEmpty($this->xpath($field_xpath));
|
||||
$this->assertEmpty($this->xpath($clue_xpath));
|
||||
|
||||
// Verify no widget is displayed on the non-default language edit form.
|
||||
$this->drupalGet($entity->toUrl('edit-form', ['language' => $it_language]));
|
||||
$this->drupalGet($it_edit_url);
|
||||
$this->assertEmpty($this->xpath($field_xpath));
|
||||
$this->assertEmpty($this->xpath($clue_xpath));
|
||||
|
||||
|
@ -114,6 +100,41 @@ class ContentTranslationUntranslatableFieldsTest extends ContentTranslationTestB
|
|||
$link_xpath = '//a[@href=:edit_path and text()="Edit them on the original language form"]';
|
||||
$elements = $this->xpath($link_xpath, [':edit_path' => $edit_path]);
|
||||
$this->assertNotEmpty($elements);
|
||||
|
||||
// Configure untranslatable field widgets to be displayed on non-default
|
||||
// language edit forms.
|
||||
$this->drupalPostForm($settings_url, [$settings_key => 0], 'Save configuration');
|
||||
|
||||
// Check that the widget is displayed along with its clue in the edit form
|
||||
// for both languages.
|
||||
$this->drupalGet($en_edit_url);
|
||||
$this->assertNotEmpty($this->xpath($field_xpath));
|
||||
$this->assertNotEmpty($this->xpath($clue_xpath));
|
||||
$this->drupalGet($it_edit_url);
|
||||
$this->assertNotEmpty($this->xpath($field_xpath));
|
||||
$this->assertNotEmpty($this->xpath($clue_xpath));
|
||||
|
||||
// Enable content moderation and verify that widgets are hidden despite them
|
||||
// being configured to be displayed.
|
||||
$this->enableContentModeration();
|
||||
$this->drupalGet($it_edit_url);
|
||||
$this->assertEmpty($this->xpath($field_xpath));
|
||||
$this->assertEmpty($this->xpath($clue_xpath));
|
||||
|
||||
// Verify a warning is displayed.
|
||||
$this->assertSession()->pageTextContains('Fields that apply to all languages are hidden to avoid conflicting changes.');
|
||||
$elements = $this->xpath($link_xpath, [':edit_path' => $edit_path]);
|
||||
$this->assertNotEmpty($elements);
|
||||
|
||||
// Verify that checkboxes on the language content settings page are checked
|
||||
// and disabled for moderated bundles.
|
||||
$this->drupalGet($settings_url);
|
||||
$input_xpath = '//input[@name="settings[' . $this->entityTypeId . '][' . $this->bundle . '][settings][content_translation][untranslatable_fields_hide]" and @value=1 and @disabled="disabled"]';
|
||||
$elements = $this->xpath($input_xpath);
|
||||
$this->assertNotEmpty($elements);
|
||||
$this->drupalPostForm(NULL, [$settings_key => 0], 'Save configuration');
|
||||
$elements = $this->xpath($input_xpath);
|
||||
$this->assertNotEmpty($elements);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue