Issue #2940204 by plach, Wim Leers, effulgentsia, matsbla, Gábor Hojtsy, tacituseu: Translatable fields with synchronization enabled should behave as untranslatable fields with respect to pending revisions
parent
5978c5f3e6
commit
7e343df734
|
@ -163,6 +163,8 @@ function content_translation_entity_type_alter(array &$entity_types) {
|
|||
}
|
||||
$entity_type->set('translation', $translation);
|
||||
}
|
||||
|
||||
$entity_type->addConstraint('ContentTranslationSynchronizedFields');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
services:
|
||||
content_translation.synchronizer:
|
||||
class: Drupal\content_translation\FieldTranslationSynchronizer
|
||||
arguments: ['@entity.manager']
|
||||
arguments: ['@entity.manager', '@plugin.manager.field.field_type']
|
||||
|
||||
content_translation.subscriber:
|
||||
class: Drupal\content_translation\Routing\ContentTranslationRouteSubscriber
|
||||
|
|
|
@ -5,6 +5,8 @@ namespace Drupal\content_translation;
|
|||
use Drupal\Core\Config\Entity\ThirdPartySettingsInterface;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\FieldTypePluginManagerInterface;
|
||||
|
||||
/**
|
||||
* Provides field translation synchronization capabilities.
|
||||
|
@ -18,14 +20,57 @@ class FieldTranslationSynchronizer implements FieldTranslationSynchronizerInterf
|
|||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* The field type plugin manager.
|
||||
*
|
||||
* @var \Drupal\Core\Field\FieldTypePluginManagerInterface
|
||||
*/
|
||||
protected $fieldTypeManager;
|
||||
|
||||
/**
|
||||
* Constructs a FieldTranslationSynchronizer object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entityManager
|
||||
* The entity manager.
|
||||
* @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager
|
||||
* The field type plugin manager.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entityManager) {
|
||||
public function __construct(EntityManagerInterface $entityManager, FieldTypePluginManagerInterface $field_type_manager) {
|
||||
$this->entityManager = $entityManager;
|
||||
$this->fieldTypeManager = $field_type_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldSynchronizedProperties(FieldDefinitionInterface $field_definition) {
|
||||
$properties = [];
|
||||
$settings = $this->getFieldSynchronizationSettings($field_definition);
|
||||
foreach ($settings as $group => $translatable) {
|
||||
if (!$translatable) {
|
||||
$field_type_definition = $this->fieldTypeManager->getDefinition($field_definition->getType());
|
||||
if (!empty($field_type_definition['column_groups'][$group]['columns'])) {
|
||||
$properties = array_merge($properties, $field_type_definition['column_groups'][$group]['columns']);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the synchronization settings for the specified field.
|
||||
*
|
||||
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
|
||||
* A field definition.
|
||||
*
|
||||
* @return string[]
|
||||
* An array of synchronized field property names.
|
||||
*/
|
||||
protected function getFieldSynchronizationSettings(FieldDefinitionInterface $field_definition) {
|
||||
if ($field_definition instanceof ThirdPartySettingsInterface && $field_definition->isTranslatable()) {
|
||||
return $field_definition->getThirdPartySetting('content_translation', 'translation_sync', []);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -33,7 +78,6 @@ class FieldTranslationSynchronizer implements FieldTranslationSynchronizerInterf
|
|||
*/
|
||||
public function synchronizeFields(ContentEntityInterface $entity, $sync_langcode, $original_langcode = NULL) {
|
||||
$translations = $entity->getTranslationLanguages();
|
||||
$field_type_manager = \Drupal::service('plugin.manager.field.field_type');
|
||||
|
||||
// If we have no information about what to sync to, if we are creating a new
|
||||
// entity, if we have no translations for the current entity and we are not
|
||||
|
@ -43,21 +87,55 @@ class FieldTranslationSynchronizer implements FieldTranslationSynchronizerInterf
|
|||
}
|
||||
|
||||
// If the entity language is being changed there is nothing to synchronize.
|
||||
$entity_type = $entity->getEntityTypeId();
|
||||
$entity_unchanged = isset($entity->original) ? $entity->original : $this->entityManager->getStorage($entity_type)->loadUnchanged($entity->id());
|
||||
$entity_unchanged = $this->getOriginalEntity($entity);
|
||||
if ($entity->getUntranslated()->language()->getId() != $entity_unchanged->getUntranslated()->language()->getId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($entity->isNewRevision()) {
|
||||
if ($entity->isDefaultTranslationAffectedOnly()) {
|
||||
// If changes to untranslatable fields are configured to affect only the
|
||||
// default translation, we need to skip synchronization in pending
|
||||
// revisions, otherwise multiple translations would be affected.
|
||||
if (!$entity->isDefaultRevision()) {
|
||||
return;
|
||||
}
|
||||
// When this mode is enabled, changes to synchronized properties are
|
||||
// allowed only in the default translation, thus we need to make sure this
|
||||
// is always used as source for the synchronization process.
|
||||
else {
|
||||
$sync_langcode = $entity->getUntranslated()->language()->getId();
|
||||
}
|
||||
}
|
||||
elseif ($entity->isDefaultRevision()) {
|
||||
// If a new default revision is being saved, but a newer default
|
||||
// revision was created meanwhile, use any other translation as source
|
||||
// for synchronization, since that will have been merged from the
|
||||
// default revision. In this case the actual language does not matter as
|
||||
// synchronized properties are the same for all the translations in the
|
||||
// default revision.
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $default_revision */
|
||||
$default_revision = $this->entityManager
|
||||
->getStorage($entity->getEntityTypeId())
|
||||
->load($entity->id());
|
||||
if ($default_revision->getLoadedRevisionId() !== $entity->getLoadedRevisionId()) {
|
||||
$other_langcodes = array_diff_key($default_revision->getTranslationLanguages(), [$sync_langcode => FALSE]);
|
||||
if ($other_langcodes) {
|
||||
$sync_langcode = key($other_langcodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @var \Drupal\Core\Field\FieldItemListInterface $items */
|
||||
foreach ($entity as $field_name => $items) {
|
||||
$field_definition = $items->getFieldDefinition();
|
||||
$field_type_definition = $field_type_manager->getDefinition($field_definition->getType());
|
||||
$field_type_definition = $this->fieldTypeManager->getDefinition($field_definition->getType());
|
||||
$column_groups = $field_type_definition['column_groups'];
|
||||
|
||||
// Sync if the field is translatable, not empty, and the synchronization
|
||||
// setting is enabled.
|
||||
if ($field_definition instanceof ThirdPartySettingsInterface && $field_definition->isTranslatable() && !$items->isEmpty() && $translation_sync = $field_definition->getThirdPartySetting('content_translation', 'translation_sync')) {
|
||||
if (($translation_sync = $this->getFieldSynchronizationSettings($field_definition)) && !$items->isEmpty()) {
|
||||
// Retrieve all the untranslatable column groups and merge them into
|
||||
// single list.
|
||||
$groups = array_keys(array_diff($translation_sync, array_filter($translation_sync)));
|
||||
|
@ -101,6 +179,26 @@ class FieldTranslationSynchronizer implements FieldTranslationSynchronizerInterf
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the original unchanged entity to be used to detect changes.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
|
||||
* The entity being changed.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\ContentEntityInterface
|
||||
* The unchanged entity.
|
||||
*/
|
||||
protected function getOriginalEntity(ContentEntityInterface $entity) {
|
||||
if (!isset($entity->original)) {
|
||||
$storage = $this->entityManager->getStorage($entity->getEntityTypeId());
|
||||
$original = $entity->isDefaultRevision() ? $storage->loadUnchanged($entity->id()) : $storage->loadRevision($entity->getLoadedRevisionId());
|
||||
}
|
||||
else {
|
||||
$original = $entity->original;
|
||||
}
|
||||
return $original;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -174,9 +272,7 @@ class FieldTranslationSynchronizer implements FieldTranslationSynchronizerInterf
|
|||
// items and the other columns from the existing values. This only
|
||||
// works if the delta exists in the language.
|
||||
elseif ($created && !empty($original_field_values[$langcode][$delta])) {
|
||||
$item_columns_to_sync = array_intersect_key($source_items[$delta], array_flip($columns));
|
||||
$item_columns_to_keep = array_diff_key($original_field_values[$langcode][$delta], array_flip($columns));
|
||||
$values[$langcode][$delta] = $item_columns_to_sync + $item_columns_to_keep;
|
||||
$values[$langcode][$delta] = $this->createMergedItem($source_items[$delta], $original_field_values[$langcode][$delta], $columns);
|
||||
}
|
||||
// If the delta doesn't exist, copy from the source language.
|
||||
elseif ($created) {
|
||||
|
@ -190,13 +286,37 @@ class FieldTranslationSynchronizer implements FieldTranslationSynchronizerInterf
|
|||
// If the value has only been reordered we just move the old one in
|
||||
// the new position.
|
||||
$item = isset($original_field_values[$langcode][$old_delta]) ? $original_field_values[$langcode][$old_delta] : $source_items[$new_delta];
|
||||
$values[$langcode][$new_delta] = $item;
|
||||
// When saving a default revision starting from a pending revision,
|
||||
// we may have desynchronized field values, so we make sure that
|
||||
// untranslatable properties are synchronized, even if in any other
|
||||
// situation this would not be necessary.
|
||||
$values[$langcode][$new_delta] = $this->createMergedItem($source_items[$new_delta], $item, $columns);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a merged item.
|
||||
*
|
||||
* @param array $source_item
|
||||
* An item containing the untranslatable properties to be synchronized.
|
||||
* @param array $target_item
|
||||
* An item containing the translatable properties to be kept.
|
||||
* @param string[] $properties
|
||||
* An array of properties to be synchronized.
|
||||
*
|
||||
* @return array
|
||||
* A merged item array.
|
||||
*/
|
||||
protected function createMergedItem(array $source_item, array $target_item, array $properties) {
|
||||
$property_keys = array_flip($properties);
|
||||
$item_properties_to_sync = array_intersect_key($source_item, $property_keys);
|
||||
$item_properties_to_keep = array_diff_key($target_item, $property_keys);
|
||||
return $item_properties_to_sync + $item_properties_to_keep;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a hash code for the specified item.
|
||||
*
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Drupal\content_translation;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
|
||||
/**
|
||||
* Provides field translation synchronization capabilities.
|
||||
|
@ -54,4 +55,15 @@ interface FieldTranslationSynchronizerInterface {
|
|||
*/
|
||||
public function synchronizeItems(array &$field_values, array $unchanged_items, $sync_langcode, array $translations, array $columns);
|
||||
|
||||
/**
|
||||
* Returns the synchronized properties for the specified field definition.
|
||||
*
|
||||
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
|
||||
* A field definition.
|
||||
*
|
||||
* @return string[]
|
||||
* An array of synchronized field property names.
|
||||
*/
|
||||
public function getFieldSynchronizedProperties(FieldDefinitionInterface $field_definition);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\content_translation\Plugin\Validation\Constraint;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
/**
|
||||
* Validation constraint for the entity changed timestamp.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @Constraint(
|
||||
* id = "ContentTranslationSynchronizedFields",
|
||||
* label = @Translation("Content translation synchronized fields", context = "Validation"),
|
||||
* type = {"entity"}
|
||||
* )
|
||||
*/
|
||||
class ContentTranslationSynchronizedFieldsConstraint extends Constraint {
|
||||
|
||||
// In this case "elements" refers to "field properties", in fact it is what we
|
||||
// are using in the UI elsewhere.
|
||||
public $defaultRevisionMessage = 'Non-translatable field elements can only be changed when updating the current revision.';
|
||||
public $defaultTranslationMessage = 'Non-translatable field elements can only be changed when updating the original language.';
|
||||
|
||||
}
|
|
@ -0,0 +1,226 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\content_translation\Plugin\Validation\Constraint;
|
||||
|
||||
use Drupal\content_translation\ContentTranslationManagerInterface;
|
||||
use Drupal\content_translation\FieldTranslationSynchronizerInterface;
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
|
||||
/**
|
||||
* Checks that synchronized fields are handled correctly in pending revisions.
|
||||
*
|
||||
* As for untranslatable fields, two modes are supported:
|
||||
* - When changes to untranslatable fields are configured to affect all revision
|
||||
* translations, synchronized field properties can be changed only in default
|
||||
* revisions.
|
||||
* - When changes to untranslatable fields affect are configured to affect only
|
||||
* the revision's default translation, synchronized field properties can be
|
||||
* changed only when editing the default translation. This may lead to
|
||||
* temporarily desynchronized values, when saving a pending revision for the
|
||||
* default translation that changes a synchronized property. These are
|
||||
* actually synchronized when saving changes to the default translation as a
|
||||
* new default revision.
|
||||
*
|
||||
* @see \Drupal\content_translation\Plugin\Validation\Constraint\ContentTranslationSynchronizedFieldsConstraint
|
||||
* @see \Drupal\Core\Entity\Plugin\Validation\Constraint\EntityUntranslatableFieldsConstraintValidator
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ContentTranslationSynchronizedFieldsConstraintValidator extends ConstraintValidator implements ContainerInjectionInterface {
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* The content translation manager.
|
||||
*
|
||||
* @var \Drupal\content_translation\ContentTranslationManagerInterface
|
||||
*/
|
||||
protected $contentTranslationManager;
|
||||
|
||||
/**
|
||||
* The field translation synchronizer.
|
||||
*
|
||||
* @var \Drupal\content_translation\FieldTranslationSynchronizerInterface
|
||||
*/
|
||||
protected $synchronizer;
|
||||
|
||||
/**
|
||||
* ContentTranslationSynchronizedFieldsConstraintValidator constructor.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity type manager.
|
||||
* @param \Drupal\content_translation\ContentTranslationManagerInterface $content_translation_manager
|
||||
* The content translation manager.
|
||||
* @param \Drupal\content_translation\FieldTranslationSynchronizerInterface $synchronizer
|
||||
* The field translation synchronizer.
|
||||
*/
|
||||
public function __construct(EntityTypeManagerInterface $entity_type_manager, ContentTranslationManagerInterface $content_translation_manager, FieldTranslationSynchronizerInterface $synchronizer) {
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->contentTranslationManager = $content_translation_manager;
|
||||
$this->synchronizer = $synchronizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* [@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('content_translation.manager'),
|
||||
$container->get('content_translation.synchronizer')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate($value, Constraint $constraint) {
|
||||
/** @var \Drupal\content_translation\Plugin\Validation\Constraint\ContentTranslationSynchronizedFieldsConstraint $constraint */
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
|
||||
$entity = $value;
|
||||
if ($entity->isNew() || !$entity->getEntityType()->isRevisionable()) {
|
||||
return;
|
||||
}
|
||||
// When changes to untranslatable fields are configured to affect all
|
||||
// revision translations, we always allow changes in default revisions.
|
||||
if ($entity->isDefaultRevision() && !$entity->isDefaultTranslationAffectedOnly()) {
|
||||
return;
|
||||
}
|
||||
$entity_type_id = $entity->getEntityTypeId();
|
||||
if (!$this->contentTranslationManager->isEnabled($entity_type_id, $entity->bundle())) {
|
||||
return;
|
||||
}
|
||||
$synchronized_properties = $this->getSynchronizedPropertiesByField($entity->getFieldDefinitions());
|
||||
if (!$synchronized_properties) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $original */
|
||||
$original = $this->getOriginalEntity($entity);
|
||||
$original_translation = $this->getOriginalTranslation($entity, $original);
|
||||
if ($this->hasSynchronizedPropertyChanges($entity, $original_translation, $synchronized_properties)) {
|
||||
if ($entity->isDefaultTranslationAffectedOnly()) {
|
||||
foreach ($entity->getTranslationLanguages(FALSE) as $langcode => $language) {
|
||||
if ($entity->getTranslation($langcode)->hasTranslationChanges()) {
|
||||
$this->context->addViolation($constraint->defaultTranslationMessage);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->context->addViolation($constraint->defaultRevisionMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether any synchronized property has changes.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
|
||||
* The entity being validated.
|
||||
* @param \Drupal\Core\Entity\ContentEntityInterface $original
|
||||
* The original unchanged entity.
|
||||
* @param string[][] $synchronized_properties
|
||||
* An associative array of arrays of synchronized field properties keyed by
|
||||
* field name.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if changes in synchronized properties were detected, FALSE
|
||||
* otherwise.
|
||||
*/
|
||||
protected function hasSynchronizedPropertyChanges(ContentEntityInterface $entity, ContentEntityInterface $original, array $synchronized_properties) {
|
||||
foreach ($synchronized_properties as $field_name => $properties) {
|
||||
foreach ($properties as $property) {
|
||||
$items = $entity->get($field_name)->getValue();
|
||||
$original_items = $original->get($field_name)->getValue();
|
||||
if (count($items) !== count($original_items)) {
|
||||
return TRUE;
|
||||
}
|
||||
foreach ($items as $delta => $item) {
|
||||
// @todo This loose comparison is not fully reliable. Revisit this
|
||||
// after https://www.drupal.org/project/drupal/issues/2941092.
|
||||
if ($items[$delta][$property] != $original_items[$delta][$property]) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the original unchanged entity to be used to detect changes.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
|
||||
* The entity being changed.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\ContentEntityInterface
|
||||
* The unchanged entity.
|
||||
*/
|
||||
protected function getOriginalEntity(ContentEntityInterface $entity) {
|
||||
if (!isset($entity->original)) {
|
||||
$storage = $this->entityTypeManager->getStorage($entity->getEntityTypeId());
|
||||
$original = $entity->isDefaultRevision() ? $storage->loadUnchanged($entity->id()) : $storage->loadRevision($entity->getLoadedRevisionId());
|
||||
}
|
||||
else {
|
||||
$original = $entity->original;
|
||||
}
|
||||
return $original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the original translation.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
|
||||
* The entity being validated.
|
||||
* @param \Drupal\Core\Entity\ContentEntityInterface $original
|
||||
* The original entity.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\ContentEntityInterface
|
||||
* The original entity translation object.
|
||||
*/
|
||||
protected function getOriginalTranslation(ContentEntityInterface $entity, ContentEntityInterface $original) {
|
||||
$langcode = $entity->language()->getId();
|
||||
if ($original->hasTranslation($langcode)) {
|
||||
$original_langcode = $langcode;
|
||||
}
|
||||
else {
|
||||
$metadata = $this->contentTranslationManager->getTranslationMetadata($entity);
|
||||
$original_langcode = $metadata->getSource();
|
||||
}
|
||||
return $original->getTranslation($original_langcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the synchronized properties for every specified field.
|
||||
*
|
||||
* @param \Drupal\Core\Field\FieldDefinitionInterface[] $field_definitions
|
||||
* An array of field definitions.
|
||||
*
|
||||
* @return string[][]
|
||||
* An associative array of arrays of field property names keyed by field
|
||||
* name.
|
||||
*/
|
||||
public function getSynchronizedPropertiesByField(array $field_definitions) {
|
||||
$synchronizer = $this->synchronizer;
|
||||
$synchronized_properties = array_filter(array_map(
|
||||
function (FieldDefinitionInterface $field_definition) use ($synchronizer) {
|
||||
return $synchronizer->getFieldSynchronizedProperties($field_definition);
|
||||
},
|
||||
$field_definitions
|
||||
));
|
||||
return $synchronized_properties;
|
||||
}
|
||||
|
||||
}
|
|
@ -5,7 +5,10 @@
|
|||
* Helper module for the Content Translation tests.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Implements hook_entity_bundle_info_alter().
|
||||
|
@ -23,6 +26,19 @@ function content_translation_test_entity_bundle_info_alter(&$bundles) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_access().
|
||||
*/
|
||||
function content_translation_test_entity_access(EntityInterface $entity, $operation, AccountInterface $account) {
|
||||
$access = \Drupal::state()->get('content_translation.entity_access.' . $entity->getEntityTypeId());
|
||||
if (!empty($access[$operation])) {
|
||||
return AccessResult::allowed();
|
||||
}
|
||||
else {
|
||||
return AccessResult::neutral();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_BASE_FORM_ID_alter().
|
||||
*
|
||||
|
|
|
@ -0,0 +1,482 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_translation\Kernel;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\EntityConstraintViolationListInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\entity_test\Entity\EntityTestMulRev;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Tests the field synchronization logic when revisions are involved.
|
||||
*
|
||||
* @group content_translation
|
||||
*/
|
||||
class ContentTranslationFieldSyncRevisionTest extends EntityKernelTestBase {
|
||||
|
||||
use TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['file', 'image', 'language', 'content_translation', 'simpletest', 'content_translation_test'];
|
||||
|
||||
/**
|
||||
* The synchronized field name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName = 'sync_field';
|
||||
|
||||
/**
|
||||
* The content translation manager.
|
||||
*
|
||||
* @var \Drupal\content_translation\ContentTranslationManagerInterface|\Drupal\content_translation\BundleTranslationSettingsInterface
|
||||
*/
|
||||
protected $contentTranslationManager;
|
||||
|
||||
/**
|
||||
* The test entity storage.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\ContentEntityStorageInterface
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$entity_type_id = 'entity_test_mulrev';
|
||||
$this->installEntitySchema($entity_type_id);
|
||||
$this->installEntitySchema('file');
|
||||
$this->installSchema('file', ['file_usage']);
|
||||
|
||||
ConfigurableLanguage::createFromLangcode('it')->save();
|
||||
ConfigurableLanguage::createFromLangcode('fr')->save();
|
||||
|
||||
/** @var \Drupal\field\Entity\FieldStorageConfig $field_storage */
|
||||
$field_storage_config = FieldStorageConfig::create([
|
||||
'field_name' => $this->fieldName,
|
||||
'type' => 'image',
|
||||
'entity_type' => $entity_type_id,
|
||||
'cardinality' => 1,
|
||||
'translatable' => 1,
|
||||
]);
|
||||
$field_storage_config->save();
|
||||
|
||||
$field_config = FieldConfig::create([
|
||||
'entity_type' => $entity_type_id,
|
||||
'field_name' => $this->fieldName,
|
||||
'bundle' => $entity_type_id,
|
||||
'label' => 'Synchronized field',
|
||||
'translatable' => 1,
|
||||
]);
|
||||
$field_config->save();
|
||||
|
||||
$property_settings = [
|
||||
'alt' => 'alt',
|
||||
'title' => 'title',
|
||||
'file' => 0,
|
||||
];
|
||||
$field_config->setThirdPartySetting('content_translation', 'translation_sync', $property_settings);
|
||||
$field_config->save();
|
||||
|
||||
$this->entityManager->clearCachedDefinitions();
|
||||
|
||||
$this->contentTranslationManager = $this->container->get('content_translation.manager');
|
||||
$this->contentTranslationManager->setEnabled($entity_type_id, $entity_type_id, TRUE);
|
||||
|
||||
$this->storage = $this->entityManager->getStorage($entity_type_id);
|
||||
|
||||
foreach ($this->getTestFiles('image') as $file) {
|
||||
$entity = File::create((array) $file + ['status' => 1]);
|
||||
$entity->save();
|
||||
}
|
||||
|
||||
$this->state->set('content_translation.entity_access.file', ['view' => TRUE]);
|
||||
|
||||
$account = User::create([
|
||||
'name' => $this->randomMachineName(),
|
||||
'status' => 1,
|
||||
]);
|
||||
$account->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that field synchronization works as expected with revisions.
|
||||
*
|
||||
* @covers \Drupal\content_translation\Plugin\Validation\Constraint\ContentTranslationSynchronizedFieldsConstraintValidator::create
|
||||
* @covers \Drupal\content_translation\Plugin\Validation\Constraint\ContentTranslationSynchronizedFieldsConstraintValidator::validate
|
||||
* @covers \Drupal\content_translation\Plugin\Validation\Constraint\ContentTranslationSynchronizedFieldsConstraintValidator::hasSynchronizedPropertyChanges
|
||||
* @covers \Drupal\content_translation\FieldTranslationSynchronizer::getFieldSynchronizedProperties
|
||||
* @covers \Drupal\content_translation\FieldTranslationSynchronizer::synchronizeFields
|
||||
* @covers \Drupal\content_translation\FieldTranslationSynchronizer::synchronizeItems
|
||||
*/
|
||||
public function testFieldSynchronizationAndValidation() {
|
||||
// Test that when untranslatable field widgets are displayed, synchronized
|
||||
// field properties can be changed only in default revisions.
|
||||
$this->setUntranslatableFieldWidgetsDisplay(TRUE);
|
||||
$entity = $this->saveNewEntity();
|
||||
$entity_id = $entity->id();
|
||||
$this->assertLatestRevisionFieldValues($entity_id, [1, 1, 1, 'Alt 1 EN']);
|
||||
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $en_revision */
|
||||
$en_revision = $this->createRevision($entity, FALSE);
|
||||
$en_revision->get($this->fieldName)->target_id = 2;
|
||||
$violations = $en_revision->validate();
|
||||
$this->assertViolations($violations);
|
||||
|
||||
$it_translation = $entity->addTranslation('it', $entity->toArray());
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $it_revision */
|
||||
$it_revision = $this->createRevision($it_translation, FALSE);
|
||||
$metadata = $this->contentTranslationManager->getTranslationMetadata($it_revision);
|
||||
$metadata->setSource('en');
|
||||
$it_revision->get($this->fieldName)->target_id = 2;
|
||||
$it_revision->get($this->fieldName)->alt = 'Alt 2 IT';
|
||||
$violations = $it_revision->validate();
|
||||
$this->assertViolations($violations);
|
||||
$it_revision->isDefaultRevision(TRUE);
|
||||
$violations = $it_revision->validate();
|
||||
$this->assertEmpty($violations);
|
||||
$this->storage->save($it_revision);
|
||||
$this->assertLatestRevisionFieldValues($entity_id, [2, 2, 2, 'Alt 1 EN', 'Alt 2 IT']);
|
||||
|
||||
$en_revision = $this->createRevision($en_revision, FALSE);
|
||||
$en_revision->get($this->fieldName)->alt = 'Alt 3 EN';
|
||||
$violations = $en_revision->validate();
|
||||
$this->assertEmpty($violations);
|
||||
$this->storage->save($en_revision);
|
||||
$this->assertLatestRevisionFieldValues($entity_id, [3, 2, 2, 'Alt 3 EN', 'Alt 2 IT']);
|
||||
|
||||
$it_revision = $this->createRevision($it_revision, FALSE);
|
||||
$it_revision->get($this->fieldName)->alt = 'Alt 4 IT';
|
||||
$violations = $it_revision->validate();
|
||||
$this->assertEmpty($violations);
|
||||
$this->storage->save($it_revision);
|
||||
$this->assertLatestRevisionFieldValues($entity_id, [4, 2, 2, 'Alt 1 EN', 'Alt 4 IT']);
|
||||
|
||||
$en_revision = $this->createRevision($en_revision);
|
||||
$en_revision->get($this->fieldName)->alt = 'Alt 5 EN';
|
||||
$violations = $en_revision->validate();
|
||||
$this->assertEmpty($violations);
|
||||
$this->storage->save($en_revision);
|
||||
$this->assertLatestRevisionFieldValues($entity_id, [5, 2, 2, 'Alt 5 EN', 'Alt 2 IT']);
|
||||
|
||||
$en_revision = $this->createRevision($en_revision);
|
||||
$en_revision->get($this->fieldName)->target_id = 6;
|
||||
$en_revision->get($this->fieldName)->alt = 'Alt 6 EN';
|
||||
$violations = $en_revision->validate();
|
||||
$this->assertEmpty($violations);
|
||||
$this->storage->save($en_revision);
|
||||
$this->assertLatestRevisionFieldValues($entity_id, [6, 6, 6, 'Alt 6 EN', 'Alt 2 IT']);
|
||||
|
||||
$it_revision = $this->createRevision($it_revision);
|
||||
$it_revision->get($this->fieldName)->alt = 'Alt 7 IT';
|
||||
$violations = $it_revision->validate();
|
||||
$this->assertEmpty($violations);
|
||||
$this->storage->save($it_revision);
|
||||
$this->assertLatestRevisionFieldValues($entity_id, [7, 6, 6, 'Alt 6 EN', 'Alt 7 IT']);
|
||||
|
||||
// Test that when untranslatable field widgets are hidden, synchronized
|
||||
// field properties can be changed only when editing the default
|
||||
// translation. This may lead to temporarily desynchronized values, when
|
||||
// saving a pending revision for the default translation that changes a
|
||||
// synchronized property (see revision 11).
|
||||
$this->setUntranslatableFieldWidgetsDisplay(FALSE);
|
||||
$entity = $this->saveNewEntity();
|
||||
$entity_id = $entity->id();
|
||||
$this->assertLatestRevisionFieldValues($entity_id, [8, 1, 1, 'Alt 1 EN']);
|
||||
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $en_revision */
|
||||
$en_revision = $this->createRevision($entity, FALSE);
|
||||
$en_revision->get($this->fieldName)->target_id = 2;
|
||||
$en_revision->get($this->fieldName)->alt = 'Alt 2 EN';
|
||||
$violations = $en_revision->validate();
|
||||
$this->assertEmpty($violations);
|
||||
$this->storage->save($en_revision);
|
||||
$this->assertLatestRevisionFieldValues($entity_id, [9, 2, 2, 'Alt 2 EN']);
|
||||
|
||||
$it_translation = $entity->addTranslation('it', $entity->toArray());
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $it_revision */
|
||||
$it_revision = $this->createRevision($it_translation, FALSE);
|
||||
$metadata = $this->contentTranslationManager->getTranslationMetadata($it_revision);
|
||||
$metadata->setSource('en');
|
||||
$it_revision->get($this->fieldName)->target_id = 3;
|
||||
$violations = $it_revision->validate();
|
||||
$this->assertViolations($violations);
|
||||
$it_revision->isDefaultRevision(TRUE);
|
||||
$violations = $it_revision->validate();
|
||||
$this->assertViolations($violations);
|
||||
|
||||
$it_revision = $this->createRevision($it_translation);
|
||||
$metadata = $this->contentTranslationManager->getTranslationMetadata($it_revision);
|
||||
$metadata->setSource('en');
|
||||
$it_revision->get($this->fieldName)->alt = 'Alt 3 IT';
|
||||
$violations = $it_revision->validate();
|
||||
$this->assertEmpty($violations);
|
||||
$this->storage->save($it_revision);
|
||||
$this->assertLatestRevisionFieldValues($entity_id, [10, 1, 1, 'Alt 1 EN', 'Alt 3 IT']);
|
||||
|
||||
$en_revision = $this->createRevision($en_revision, FALSE);
|
||||
$en_revision->get($this->fieldName)->alt = 'Alt 4 EN';
|
||||
$violations = $en_revision->validate();
|
||||
$this->assertEmpty($violations);
|
||||
$this->storage->save($en_revision);
|
||||
$this->assertLatestRevisionFieldValues($entity_id, [11, 2, 1, 'Alt 4 EN', 'Alt 3 IT']);
|
||||
|
||||
$it_revision = $this->createRevision($it_revision, FALSE);
|
||||
$it_revision->get($this->fieldName)->alt = 'Alt 5 IT';
|
||||
$violations = $it_revision->validate();
|
||||
$this->assertEmpty($violations);
|
||||
$this->storage->save($it_revision);
|
||||
$this->assertLatestRevisionFieldValues($entity_id, [12, 1, 1, 'Alt 1 EN', 'Alt 5 IT']);
|
||||
|
||||
$en_revision = $this->createRevision($en_revision);
|
||||
$en_revision->get($this->fieldName)->target_id = 6;
|
||||
$en_revision->get($this->fieldName)->alt = 'Alt 6 EN';
|
||||
$violations = $en_revision->validate();
|
||||
$this->assertEmpty($violations);
|
||||
$this->storage->save($en_revision);
|
||||
$this->assertLatestRevisionFieldValues($entity_id, [13, 6, 6, 'Alt 6 EN', 'Alt 3 IT']);
|
||||
|
||||
$it_revision = $this->createRevision($it_revision);
|
||||
$it_revision->get($this->fieldName)->target_id = 7;
|
||||
$violations = $it_revision->validate();
|
||||
$this->assertViolations($violations);
|
||||
|
||||
$it_revision = $this->createRevision($it_revision);
|
||||
$it_revision->get($this->fieldName)->alt = 'Alt 7 IT';
|
||||
$violations = $it_revision->validate();
|
||||
$this->assertEmpty($violations);
|
||||
$this->storage->save($it_revision);
|
||||
$this->assertLatestRevisionFieldValues($entity_id, [14, 6, 6, 'Alt 6 EN', 'Alt 7 IT']);
|
||||
|
||||
// Test that creating a default revision starting from a pending revision
|
||||
// having changes to synchronized properties, without introducing new
|
||||
// changes works properly.
|
||||
$this->setUntranslatableFieldWidgetsDisplay(FALSE);
|
||||
$entity = $this->saveNewEntity();
|
||||
$entity_id = $entity->id();
|
||||
$this->assertLatestRevisionFieldValues($entity_id, [15, 1, 1, 'Alt 1 EN']);
|
||||
|
||||
$it_translation = $entity->addTranslation('it', $entity->toArray());
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $it_revision */
|
||||
$it_revision = $this->createRevision($it_translation);
|
||||
$metadata = $this->contentTranslationManager->getTranslationMetadata($it_revision);
|
||||
$metadata->setSource('en');
|
||||
$it_revision->get($this->fieldName)->alt = 'Alt 2 IT';
|
||||
$violations = $it_revision->validate();
|
||||
$this->assertEmpty($violations);
|
||||
$this->storage->save($it_revision);
|
||||
$this->assertLatestRevisionFieldValues($entity_id, [16, 1, 1, 'Alt 1 EN', 'Alt 2 IT']);
|
||||
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $en_revision */
|
||||
$en_revision = $this->createRevision($entity);
|
||||
$en_revision->get($this->fieldName)->target_id = 3;
|
||||
$en_revision->get($this->fieldName)->alt = 'Alt 3 EN';
|
||||
$violations = $en_revision->validate();
|
||||
$this->assertEmpty($violations);
|
||||
$this->storage->save($en_revision);
|
||||
$this->assertLatestRevisionFieldValues($entity_id, [17, 3, 3, 'Alt 3 EN', 'Alt 2 IT']);
|
||||
|
||||
$en_revision = $this->createRevision($entity, FALSE);
|
||||
$en_revision->get($this->fieldName)->target_id = 4;
|
||||
$en_revision->get($this->fieldName)->alt = 'Alt 4 EN';
|
||||
$violations = $en_revision->validate();
|
||||
$this->assertEmpty($violations);
|
||||
$this->storage->save($en_revision);
|
||||
$this->assertLatestRevisionFieldValues($entity_id, [18, 4, 3, 'Alt 4 EN', 'Alt 2 IT']);
|
||||
|
||||
$en_revision = $this->createRevision($entity);
|
||||
$violations = $en_revision->validate();
|
||||
$this->assertEmpty($violations);
|
||||
$this->storage->save($en_revision);
|
||||
$this->assertLatestRevisionFieldValues($entity_id, [19, 4, 4, 'Alt 4 EN', 'Alt 2 IT']);
|
||||
|
||||
$it_revision = $this->createRevision($it_revision);
|
||||
$it_revision->get($this->fieldName)->alt = 'Alt 6 IT';
|
||||
$violations = $it_revision->validate();
|
||||
$this->assertEmpty($violations);
|
||||
$this->storage->save($it_revision);
|
||||
$this->assertLatestRevisionFieldValues($entity_id, [20, 4, 4, 'Alt 4 EN', 'Alt 6 IT']);
|
||||
|
||||
// Check that we are not allowed to perform changes to multiple translations
|
||||
// in pending revisions when synchronized properties are involved.
|
||||
$this->setUntranslatableFieldWidgetsDisplay(FALSE);
|
||||
$entity = $this->saveNewEntity();
|
||||
$entity_id = $entity->id();
|
||||
$this->assertLatestRevisionFieldValues($entity_id, [21, 1, 1, 'Alt 1 EN']);
|
||||
|
||||
$it_translation = $entity->addTranslation('it', $entity->toArray());
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $it_revision */
|
||||
$it_revision = $this->createRevision($it_translation);
|
||||
$metadata = $this->contentTranslationManager->getTranslationMetadata($it_revision);
|
||||
$metadata->setSource('en');
|
||||
$it_revision->get($this->fieldName)->alt = 'Alt 2 IT';
|
||||
$violations = $it_revision->validate();
|
||||
$this->assertEmpty($violations);
|
||||
$this->storage->save($it_revision);
|
||||
$this->assertLatestRevisionFieldValues($entity_id, [22, 1, 1, 'Alt 1 EN', 'Alt 2 IT']);
|
||||
|
||||
$en_revision = $this->createRevision($entity, FALSE);
|
||||
$en_revision->get($this->fieldName)->target_id = 2;
|
||||
$en_revision->getTranslation('it')->get($this->fieldName)->alt = 'Alt 3 IT';
|
||||
$violations = $en_revision->validate();
|
||||
$this->assertViolations($violations);
|
||||
|
||||
// Test that when saving a new default revision starting from a pending
|
||||
// revision, outdated synchronized properties do not override more recent
|
||||
// ones.
|
||||
$this->setUntranslatableFieldWidgetsDisplay(TRUE);
|
||||
$entity = $this->saveNewEntity();
|
||||
$entity_id = $entity->id();
|
||||
$this->assertLatestRevisionFieldValues($entity_id, [23, 1, 1, 'Alt 1 EN']);
|
||||
|
||||
$it_translation = $entity->addTranslation('it', $entity->toArray());
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $it_revision */
|
||||
$it_revision = $this->createRevision($it_translation, FALSE);
|
||||
$metadata = $this->contentTranslationManager->getTranslationMetadata($it_revision);
|
||||
$metadata->setSource('en');
|
||||
$it_revision->get($this->fieldName)->alt = 'Alt 2 IT';
|
||||
$violations = $it_revision->validate();
|
||||
$this->assertEmpty($violations);
|
||||
$this->storage->save($it_revision);
|
||||
$this->assertLatestRevisionFieldValues($entity_id, [24, 1, 1, 'Alt 1 EN', 'Alt 2 IT']);
|
||||
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $en_revision */
|
||||
$en_revision = $this->createRevision($entity);
|
||||
$en_revision->get($this->fieldName)->target_id = 3;
|
||||
$en_revision->get($this->fieldName)->alt = 'Alt 3 EN';
|
||||
$violations = $en_revision->validate();
|
||||
$this->assertEmpty($violations);
|
||||
$this->storage->save($en_revision);
|
||||
$this->assertLatestRevisionFieldValues($entity_id, [25, 3, 3, 'Alt 3 EN', 'Alt 2 IT']);
|
||||
|
||||
$it_revision = $this->createRevision($it_revision);
|
||||
$it_revision->get($this->fieldName)->alt = 'Alt 4 IT';
|
||||
$violations = $it_revision->validate();
|
||||
$this->assertEmpty($violations);
|
||||
$this->storage->save($it_revision);
|
||||
$this->assertLatestRevisionFieldValues($entity_id, [26, 3, 3, 'Alt 3 EN', 'Alt 4 IT']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets untranslatable field widgets' display status.
|
||||
*
|
||||
* @param bool $display
|
||||
* Whether untranslatable field widgets should be displayed.
|
||||
*/
|
||||
protected function setUntranslatableFieldWidgetsDisplay($display) {
|
||||
$entity_type_id = $this->storage->getEntityTypeId();
|
||||
$settings = ['untranslatable_fields_hide' => !$display];
|
||||
$this->contentTranslationManager->setBundleTranslationSettings($entity_type_id, $entity_type_id, $settings);
|
||||
/** @var \Drupal\Core\Entity\EntityTypeBundleInfo $bundle_info */
|
||||
$bundle_info = $this->container->get('entity_type.bundle.info');
|
||||
$bundle_info->clearCachedBundles();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Drupal\Core\Entity\ContentEntityInterface
|
||||
*/
|
||||
protected function saveNewEntity() {
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
|
||||
$entity = EntityTestMulRev::create([
|
||||
'uid' => 1,
|
||||
'langcode' => 'en',
|
||||
$this->fieldName => [
|
||||
'target_id' => 1,
|
||||
'alt' => 'Alt 1 EN',
|
||||
],
|
||||
]);
|
||||
$metadata = $this->contentTranslationManager->getTranslationMetadata($entity);
|
||||
$metadata->setSource(LanguageInterface::LANGCODE_NOT_SPECIFIED);
|
||||
$violations = $entity->validate();
|
||||
$this->assertEmpty($violations);
|
||||
$this->storage->save($entity);
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new revision starting from the latest translation-affecting one.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\ContentEntityInterface $translation
|
||||
* The translation to be revisioned.
|
||||
* @param bool $default
|
||||
* (optional) Whether the new revision should be marked as default. Defaults
|
||||
* to TRUE.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\ContentEntityInterface
|
||||
* An entity revision object.
|
||||
*/
|
||||
protected function createRevision(ContentEntityInterface $translation, $default = TRUE) {
|
||||
if (!$translation->isNewTranslation()) {
|
||||
$langcode = $translation->language()->getId();
|
||||
$revision_id = $this->storage->getLatestTranslationAffectedRevisionId($translation->id(), $langcode);
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $revision */
|
||||
$revision = $this->storage->loadRevision($revision_id);
|
||||
$translation = $revision->getTranslation($langcode);
|
||||
}
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $revision */
|
||||
$revision = $this->storage->createRevision($translation, $default);
|
||||
return $revision;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the expected violations were found.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityConstraintViolationListInterface $violations
|
||||
* A list of violations.
|
||||
*/
|
||||
protected function assertViolations(EntityConstraintViolationListInterface $violations) {
|
||||
$entity_type_id = $this->storage->getEntityTypeId();
|
||||
$settings = $this->contentTranslationManager->getBundleTranslationSettings($entity_type_id, $entity_type_id);
|
||||
$message = !empty($settings['untranslatable_fields_hide']) ?
|
||||
'Non-translatable field elements can only be changed when updating the original language.' :
|
||||
'Non-translatable field elements can only be changed when updating the current revision.';
|
||||
|
||||
$list = [];
|
||||
foreach ($violations as $violation) {
|
||||
if ((string) $violation->getMessage() === $message) {
|
||||
$list[] = $violation;
|
||||
}
|
||||
}
|
||||
$this->assertCount(1, $list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the latest revision has the expected field values.
|
||||
*
|
||||
* @param $entity_id
|
||||
* The entity ID.
|
||||
* @param array $expected_values
|
||||
* An array of expected values in the following order:
|
||||
* - revision ID
|
||||
* - target ID (en)
|
||||
* - target ID (it)
|
||||
* - alt (en)
|
||||
* - alt (it)
|
||||
*/
|
||||
protected function assertLatestRevisionFieldValues($entity_id, array $expected_values) {
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
|
||||
$entity = $this->storage->loadRevision($this->storage->getLatestRevisionId($entity_id));
|
||||
@list($revision_id, $target_id_en, $target_id_it, $alt_en, $alt_it) = $expected_values;
|
||||
$this->assertEquals($revision_id, $entity->getRevisionId());
|
||||
$this->assertEquals($target_id_en, $entity->get($this->fieldName)->target_id);
|
||||
$this->assertEquals($alt_en, $entity->get($this->fieldName)->alt);
|
||||
if ($entity->hasTranslation('it')) {
|
||||
$it_translation = $entity->getTranslation('it');
|
||||
$this->assertEquals($target_id_it, $it_translation->get($this->fieldName)->target_id);
|
||||
$this->assertEquals($alt_it, $it_translation->get($this->fieldName)->alt);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -59,7 +59,7 @@ class ContentTranslationSyncUnitTest extends KernelTestBase {
|
|||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->synchronizer = new FieldTranslationSynchronizer($this->container->get('entity.manager'));
|
||||
$this->synchronizer = new FieldTranslationSynchronizer($this->container->get('entity.manager'), $this->container->get('plugin.manager.field.field_type'));
|
||||
$this->synchronized = ['sync1', 'sync2'];
|
||||
$this->columns = array_merge($this->synchronized, ['var1', 'var2']);
|
||||
$this->langcodes = ['en', 'it', 'fr', 'de', 'es'];
|
||||
|
|
Loading…
Reference in New Issue