Update Composer, update everything

This commit is contained in:
Oliver Davies 2018-11-23 12:29:20 +00:00
parent ea3e94409f
commit dda5c284b6
19527 changed files with 1135420 additions and 351004 deletions

View file

@ -5,6 +5,6 @@ package: Testing
version: VERSION
core: 8.x
dependencies:
- content_translation
- language
- entity_test
- drupal:content_translation
- drupal:language
- drupal:entity_test

View file

@ -5,7 +5,39 @@
* Helper module for the Content Translation tests.
*/
use \Drupal\Core\Form\FormStateInterface;
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().
*/
function content_translation_test_entity_bundle_info_alter(&$bundles) {
// Store the initial status of the "translatable" property for the
// "entity_test_mul" bundle.
$translatable = !empty($bundles['entity_test_mul']['entity_test_mul']['translatable']);
\Drupal::state()->set('content_translation_test.translatable', $translatable);
// Make it translatable if Content Translation did not. This will make the
// entity object translatable even if it is disabled in Content Translation
// settings.
if (!$translatable) {
$bundles['entity_test_mul']['entity_test_mul']['translatable'] = TRUE;
}
}
/**
* 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().

View file

@ -5,5 +5,5 @@ package: Testing
version: VERSION
core: 8.x
dependencies:
- content_translation
- views
- drupal:content_translation
- drupal:views

View file

@ -0,0 +1,40 @@
<?php
namespace Drupal\Tests\content_translation\Functional;
/**
* Tests the test content translation UI with the test entity.
*
* @group content_translation
*/
class ContentTestTranslationUITest extends ContentTranslationUITestBase {
/**
* {@inheritdoc}
*/
protected $testHTMLEscapeForAllLanguages = TRUE;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['language', 'content_translation', 'entity_test'];
/**
* {@inheritdoc}
*/
protected function setUp() {
// Use the entity_test_mul as this has multilingual property support.
$this->entityTypeId = 'entity_test_mul_changed';
parent::setUp();
}
/**
* {@inheritdoc}
*/
protected function getTranslatorPermissions() {
return array_merge(parent::getTranslatorPermissions(), ['administer entity_test content', 'view test entity']);
}
}

View file

@ -0,0 +1,127 @@
<?php
namespace Drupal\Tests\content_translation\Functional;
use Drupal\field\Entity\FieldConfig;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\Tests\BrowserTestBase;
/**
* Tests that contextual links are available for content translation.
*
* @group content_translation
*/
class ContentTranslationContextualLinksTest extends BrowserTestBase {
/**
* The bundle being tested.
*
* @var string
*/
protected $bundle;
/**
* The content type being tested.
*
* @var \Drupal\node\Entity\NodeType
*/
protected $contentType;
/**
* The 'translator' user to use during testing.
*
* @var \Drupal\user\UserInterface
*/
protected $translator;
/**
* The enabled languages.
*
* @var array
*/
protected $langcodes;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['content_translation', 'contextual', 'node'];
/**
* The profile to install as a basis for testing.
*
* @var string
*/
protected $profile = 'testing';
protected function setUp() {
parent::setUp();
// Set up an additional language.
$this->langcodes = [\Drupal::languageManager()->getDefaultLanguage()->getId(), 'es'];
ConfigurableLanguage::createFromLangcode('es')->save();
// Create a content type.
$this->bundle = $this->randomMachineName();
$this->contentType = $this->drupalCreateContentType(['type' => $this->bundle]);
// Add a field to the content type. The field is not yet translatable.
FieldStorageConfig::create([
'field_name' => 'field_test_text',
'entity_type' => 'node',
'type' => 'text',
'cardinality' => 1,
])->save();
FieldConfig::create([
'entity_type' => 'node',
'field_name' => 'field_test_text',
'bundle' => $this->bundle,
'label' => 'Test text-field',
])->save();
entity_get_form_display('node', $this->bundle, 'default')
->setComponent('field_test_text', [
'type' => 'text_textfield',
'weight' => 0,
])
->save();
// Create a translator user.
$permissions = [
'access contextual links',
'administer nodes',
"edit any $this->bundle content",
'translate any entity',
];
$this->translator = $this->drupalCreateUser($permissions);
}
/**
* Tests that a contextual link is available for translating a node.
*/
public function testContentTranslationContextualLinks() {
// Create a node.
$title = $this->randomString();
$this->drupalCreateNode(['type' => $this->bundle, 'title' => $title, 'langcode' => 'en']);
$node = $this->drupalGetNodeByTitle($title);
// Use a UI form submission to make the node type and field translatable.
// This tests that caches are properly invalidated.
$this->drupalLogin($this->rootUser);
$edit = [
'entity_types[node]' => TRUE,
'settings[node][' . $this->bundle . '][settings][language][language_alterable]' => TRUE,
'settings[node][' . $this->bundle . '][translatable]' => TRUE,
'settings[node][' . $this->bundle . '][fields][field_test_text]' => TRUE,
];
$this->drupalPostForm('admin/config/regional/content-language', $edit, t('Save configuration'));
$this->drupalLogout();
// Check that the link leads to the translate page.
$this->drupalLogin($this->translator);
$translate_link = 'node/' . $node->id() . '/translations';
$this->drupalGet($translate_link);
$this->assertRaw(t('Translations of %label', ['%label' => $node->label()]), 'The contextual link leads to the translate page.');
}
}

View file

@ -0,0 +1,69 @@
<?php
namespace Drupal\Tests\content_translation\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Test disabling content translation module.
*
* @group content_translation
*/
class ContentTranslationDisableSettingTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'content_translation',
'menu_link_content',
'language',
];
/**
* Tests that entity schemas are up-to-date after enabling translation.
*/
public function testDisableSetting() {
// Define selectors.
$group_checkbox = 'entity_types[menu_link_content]';
$translatable_checkbox = 'settings[menu_link_content][menu_link_content][translatable]';
$language_alterable = 'settings[menu_link_content][menu_link_content][settings][language][language_alterable]';
$user = $this->drupalCreateUser([
'administer site configuration',
'administer content translation',
'create content translations',
'administer languages',
]);
$this->drupalLogin($user);
$assert = $this->assertSession();
$this->drupalGet('admin/config/regional/content-language');
$assert->checkboxNotChecked('entity_types[menu_link_content]');
$edit = [
$group_checkbox => TRUE,
$translatable_checkbox => TRUE,
$language_alterable => TRUE,
];
$this->submitForm($edit, t('Save configuration'));
$assert->pageTextContains(t('Settings successfully updated.'));
$assert->checkboxChecked($group_checkbox);
$edit = [
$group_checkbox => FALSE,
$translatable_checkbox => TRUE,
$language_alterable => TRUE,
];
$this->submitForm($edit, t('Save configuration'));
$assert->pageTextContains(t('Settings successfully updated.'));
$assert->checkboxNotChecked($group_checkbox);
}
}

View file

@ -0,0 +1,71 @@
<?php
namespace Drupal\Tests\content_translation\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Test enabling content translation module.
*
* @group content_translation
*/
class ContentTranslationEnableTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['entity_test', 'menu_link_content', 'node'];
/**
* Tests that entity schemas are up-to-date after enabling translation.
*/
public function testEnable() {
$this->drupalLogin($this->rootUser);
// Enable modules and make sure the related config entity type definitions
// are installed.
$edit = [
'modules[content_translation][enable]' => TRUE,
'modules[language][enable]' => TRUE,
];
$this->drupalPostForm('admin/modules', $edit, t('Install'));
// Status messages are shown.
$this->assertText(t('This site has only a single language enabled. Add at least one more language in order to translate content.'));
$this->assertText(t('Enable translation for content types, taxonomy vocabularies, accounts, or any other element you wish to translate.'));
// No pending updates should be available.
$this->drupalGet('admin/reports/status');
$requirement_value = $this->cssSelect("details.system-status-report__entry summary:contains('Entity/field definitions') + div");
$this->assertEqual(t('Up to date'), trim($requirement_value[0]->getText()));
$this->drupalGet('admin/config/regional/content-language');
// The node entity type should not be an option because it has no bundles.
$this->assertNoRaw('entity_types[node]');
// Enable content translation on entity types that have will have a
// content_translation_uid.
$edit = [
'entity_types[menu_link_content]' => TRUE,
'settings[menu_link_content][menu_link_content][translatable]' => TRUE,
'entity_types[entity_test_mul]' => TRUE,
'settings[entity_test_mul][entity_test_mul][translatable]' => TRUE,
];
$this->drupalPostForm(NULL, $edit, t('Save configuration'));
// No pending updates should be available.
$this->drupalGet('admin/reports/status');
$requirement_value = $this->cssSelect("details.system-status-report__entry summary:contains('Entity/field definitions') + div");
$this->assertEqual(t('Up to date'), trim($requirement_value[0]->getText()));
// Create a node type and check the content translation settings are now
// available for nodes.
$edit = [
'name' => 'foo',
'title_label' => 'title for foo',
'type' => 'foo',
];
$this->drupalPostForm('admin/structure/types/add', $edit, t('Save content type'));
$this->drupalGet('admin/config/regional/content-language');
$this->assertRaw('entity_types[node]');
}
}

View file

@ -0,0 +1,154 @@
<?php
namespace Drupal\Tests\content_translation\Functional;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\Tests\node\Functional\NodeTestBase;
use Drupal\Tests\TestFileCreationTrait;
/**
* Tests the content translation language that is set.
*
* @group content_translation
*/
class ContentTranslationLanguageChangeTest extends NodeTestBase {
use TestFileCreationTrait {
getTestFiles as drupalGetTestFiles;
}
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['language', 'content_translation', 'content_translation_test', 'node', 'block', 'field_ui', 'image'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$langcodes = ['de', 'fr'];
foreach ($langcodes as $langcode) {
ConfigurableLanguage::createFromLangcode($langcode)->save();
}
$this->drupalPlaceBlock('local_tasks_block');
$user = $this->drupalCreateUser([
'administer site configuration',
'administer nodes',
'create article content',
'edit any article content',
'delete any article content',
'administer content translation',
'translate any entity',
'create content translations',
'administer languages',
'administer content types',
'administer node fields',
]);
$this->drupalLogin($user);
// Enable translation for article.
$edit = [
'entity_types[node]' => TRUE,
'settings[node][article][translatable]' => TRUE,
'settings[node][article][settings][language][language_alterable]' => TRUE,
];
$this->drupalPostForm('admin/config/regional/content-language', $edit, t('Save configuration'));
// Add an image field.
$this->drupalGet('admin/structure/types/manage/article/fields/add-field');
$edit = [
'new_storage_type' => 'image',
'field_name' => 'image_field',
'label' => 'image_field',
];
$this->drupalPostForm(NULL, $edit, t('Save and continue'));
$this->drupalPostForm(NULL, [], t('Save field settings'));
$this->drupalPostForm(NULL, [], t('Save settings'));
}
/**
* Test that the source language is properly set when changing.
*/
public function testLanguageChange() {
// Create a node in English.
$this->drupalGet('node/add/article');
$edit = [
'title[0][value]' => 'english_title',
];
$this->drupalPostForm(NULL, $edit, t('Save'));
// Create a translation in French.
$this->clickLink('Translate');
$this->clickLink('Add');
$this->drupalPostForm(NULL, [], t('Save (this translation)'));
$this->clickLink('Translate');
// Edit English translation.
$this->clickLink('Edit');
// Upload and image after changing the node language.
$images = $this->drupalGetTestFiles('image')[1];
$edit = [
'langcode[0][value]' => 'de',
'files[field_image_field_0]' => $images->uri,
];
$this->drupalPostForm(NULL, $edit, t('Upload'));
$this->drupalPostForm(NULL, ['field_image_field[0][alt]' => 'alternative_text'], t('Save (this translation)'));
// Check that the translation languages are correct.
$node = $this->getNodeByTitle('english_title');
$translation_languages = array_keys($node->getTranslationLanguages());
$this->assertTrue(in_array('fr', $translation_languages));
$this->assertTrue(in_array('de', $translation_languages));
}
/**
* Test that title does not change on ajax call with new language value.
*/
public function testTitleDoesNotChangesOnChangingLanguageWidgetAndTriggeringAjaxCall() {
// Create a node in English.
$this->drupalGet('node/add/article', ['query' => ['test_field_only_en_fr' => 1]]);
$edit = [
'title[0][value]' => 'english_title',
'test_field_only_en_fr' => 'node created',
];
$this->drupalPostForm(NULL, $edit, t('Save'));
$this->assertEqual('node created', \Drupal::state()->get('test_field_only_en_fr'));
// Create a translation in French.
$this->clickLink('Translate');
$this->clickLink('Add');
$this->drupalPostForm(NULL, [], t('Save (this translation)'));
$this->clickLink('Translate');
// Edit English translation.
$node = $this->getNodeByTitle('english_title');
$this->drupalGet('node/' . $node->id() . '/edit');
// Test the expected title when loading the form.
$this->assertRaw('<title>Edit Article english_title | Drupal</title>');
// Upload and image after changing the node language.
$images = $this->drupalGetTestFiles('image')[1];
$edit = [
'langcode[0][value]' => 'de',
'files[field_image_field_0]' => $images->uri,
];
$this->drupalPostForm(NULL, $edit, t('Upload'));
// Test the expected title after triggering an ajax call with a new
// language selected.
$this->assertRaw('<title>Edit Article english_title | Drupal</title>');
$edit = [
'langcode[0][value]' => 'en',
'field_image_field[0][alt]' => 'alternative_text',
];
$this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
// Check that the translation languages are correct.
$node = $this->getNodeByTitle('english_title');
$translation_languages = array_keys($node->getTranslationLanguages());
$this->assertTrue(in_array('fr', $translation_languages));
$this->assertTrue(!in_array('de', $translation_languages));
}
}

View file

@ -98,14 +98,14 @@ class ContentTranslationOperationsTest extends NodeTestBase {
'access content' => TRUE,
]
);
$node->setPublished(FALSE)->save();
$node->setUnpublished()->save();
$this->drupalGet($node->urlInfo('drupal:content-translation-overview'));
$this->assertResponse(403);
$this->drupalLogout();
// Ensure the 'Translate' local task does not show up anymore when disabling
// translations for a content type.
$node->setPublished(TRUE)->save();
$node->setPublished()->save();
user_role_change_permissions(
Role::AUTHENTICATED_ID,
[
@ -136,7 +136,7 @@ class ContentTranslationOperationsTest extends NodeTestBase {
$this->assertFalse(content_translation_translate_access($node)->isAllowed());
$access_control_handler->resetCache();
$node->setPublished(TRUE);
$node->setPublished();
$node->save();
$this->assertTrue(content_translation_translate_access($node)->isAllowed());
$access_control_handler->resetCache();

View file

@ -0,0 +1,89 @@
<?php
namespace Drupal\Tests\content_translation\Functional;
use Drupal\Core\Url;
use Drupal\language\Entity\ConfigurableLanguage;
/**
* Tests the "Flag as outdated" functionality with revision translations.
*
* @group content_translation
*/
class ContentTranslationOutdatedRevisionTranslationTest extends ContentTranslationPendingRevisionTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->enableContentModeration();
}
/**
* Tests that outdated revision translations work correctly.
*/
public function testFlagAsOutdatedHidden() {
// Create a test node.
$values = [
'title' => 'Test 1.1 EN',
'moderation_state' => 'published',
];
$id = $this->createEntity($values, 'en');
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
$entity = $this->storage->load($id);
// Add a published Italian translation.
$add_translation_url = Url::fromRoute("entity.{$this->entityTypeId}.content_translation_add", [
$entity->getEntityTypeId() => $id,
'source' => 'en',
'target' => 'it',
],
[
'language' => ConfigurableLanguage::load('it'),
'absolute' => FALSE,
]
);
$this->drupalGet($add_translation_url);
$this->assertFlagWidget();
$edit = [
'title[0][value]' => 'Test 1.2 IT',
'moderation_state[0][state]' => 'published',
];
$this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
// Add a published French translation.
$add_translation_url = Url::fromRoute("entity.{$this->entityTypeId}.content_translation_add", [
$entity->getEntityTypeId() => $id,
'source' => 'en',
'target' => 'fr',
],
[
'language' => ConfigurableLanguage::load('fr'),
'absolute' => FALSE,
]
);
$this->drupalGet($add_translation_url);
$this->assertFlagWidget();
$edit = [
'title[0][value]' => 'Test 1.3 FR',
'moderation_state[0][state]' => 'published',
];
$this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
// Create an English draft.
$entity = $this->storage->loadUnchanged($id);
$en_edit_url = $this->getEditUrl($entity);
$this->drupalGet($en_edit_url);
$this->assertFlagWidget();
}
/**
* Checks whether the flag widget is displayed.
*/
protected function assertFlagWidget() {
$this->assertSession()->pageTextNotContains('Flag other translations as outdated');
$this->assertSession()->pageTextContains('Translations cannot be flagged as outdated when content is moderated.');
}
}

View file

@ -0,0 +1,179 @@
<?php
namespace Drupal\Tests\content_translation\Functional;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Tests\content_moderation\Traits\ContentModerationTestTrait;
use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
/**
* Base class for pending revision translation tests.
*/
abstract class ContentTranslationPendingRevisionTestBase extends ContentTranslationTestBase {
use ContentTypeCreationTrait;
use ContentModerationTestTrait;
/**
* {@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]);
$this->createEditorialWorkflow();
}
/**
* Loads the active revision translation for the specified entity.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* The entity being edited.
* @param string $langcode
* The translation language code.
*
* @return \Drupal\Core\Entity\ContentEntityInterface|null
* The active revision translation or NULL if none could be identified.
*/
protected function loadRevisionTranslation(ContentEntityInterface $entity, $langcode) {
// Explicitly invalidate the cache for that node, as the call below is
// statically cached.
$this->storage->resetCache([$entity->id()]);
$revision_id = $this->storage->getLatestTranslationAffectedRevisionId($entity->id(), $langcode);
/** @var \Drupal\Core\Entity\ContentEntityInterface $revision */
$revision = $revision_id ? $this->storage->loadRevision($revision_id) : NULL;
return $revision && $revision->hasTranslation($langcode) ? $revision->getTranslation($langcode) : NULL;
}
/**
* Returns the edit URL for the specified entity.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* The entity being edited.
*
* @return \Drupal\Core\Url
* The edit URL.
*/
protected function getEditUrl(ContentEntityInterface $entity) {
if ($entity->access('update', $this->loggedInUser)) {
$url = $entity->toUrl('edit-form');
}
else {
$url = $entity->toUrl('drupal:content-translation-edit');
$url->setRouteParameter('language', $entity->language()->getId());
}
return $url;
}
/**
* Returns the delete translation URL for the specified entity.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* The entity being edited.
*
* @return \Drupal\Core\Url
* The delete translation URL.
*/
protected function getDeleteUrl(ContentEntityInterface $entity) {
if ($entity->access('delete', $this->loggedInUser)) {
$url = $entity->toUrl('delete-form');
}
else {
$url = $entity->toUrl('drupal:content-translation-delete');
$url->setRouteParameter('language', $entity->language()->getId());
}
return $url;
}
}

View file

@ -0,0 +1,214 @@
<?php
namespace Drupal\Tests\content_translation\Functional;
use Drupal\Core\Url;
use Drupal\language\Entity\ConfigurableLanguage;
/**
* Tests that revision translation deletion is handled correctly.
*
* @group content_translation
*/
class ContentTranslationRevisionTranslationDeletionTest extends ContentTranslationPendingRevisionTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->enableContentModeration();
}
/**
* Tests that translation overview handles pending revisions correctly.
*/
public function testOverview() {
$index = 1;
$accounts = [
$this->rootUser,
$this->editor,
$this->translator,
];
foreach ($accounts as $account) {
$this->currentAccount = $account;
$this->doTestOverview($index++);
}
}
/**
* Performs a test run.
*
* @param int $index
* The test run index.
*/
public function doTestOverview($index) {
$this->drupalLogin($this->currentAccount);
// Create a test node.
$values = [
'title' => "Test $index.1 EN",
'moderation_state' => 'published',
];
$id = $this->createEntity($values, 'en');
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
$entity = $this->storage->load($id);
// Add a draft translation and check that it is available only in the latest
// revision.
$add_translation_url = Url::fromRoute("entity.{$this->entityTypeId}.content_translation_add", [
$entity->getEntityTypeId() => $id,
'source' => 'en',
'target' => 'it',
],
[
'language' => ConfigurableLanguage::load('it'),
'absolute' => FALSE,
]
);
$add_translation_href = $add_translation_url->toString();
$this->drupalGet($add_translation_url);
$edit = [
'title[0][value]' => "Test $index.2 IT",
'moderation_state[0][state]' => 'draft',
];
$this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
$entity = $this->storage->loadUnchanged($id);
$this->assertFalse($entity->hasTranslation('it'));
$it_revision = $this->loadRevisionTranslation($entity, 'it');
$this->assertTrue($it_revision->hasTranslation('it'));
// Check that translations cannot be deleted in drafts.
$overview_url = $entity->toUrl('drupal:content-translation-overview');
$this->drupalGet($overview_url);
$it_delete_url = $this->getDeleteUrl($it_revision);
$it_delete_href = $it_delete_url->toString();
$this->assertSession()->linkByHrefNotExists($it_delete_href);
$warning = 'The "Delete translation" action is only available for published translations.';
$this->assertSession()->pageTextContains($warning);
$this->drupalGet($this->getEditUrl($it_revision));
$this->assertSession()->buttonNotExists('Delete translation');
// Publish the translation and verify it can be deleted.
$edit = [
'title[0][value]' => "Test $index.3 IT",
'moderation_state[0][state]' => 'published',
];
$this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
$entity = $this->storage->loadUnchanged($id);
$this->assertTrue($entity->hasTranslation('it'));
$it_revision = $this->loadRevisionTranslation($entity, 'it');
$this->assertTrue($it_revision->hasTranslation('it'));
$this->drupalGet($overview_url);
$this->assertSession()->linkByHrefExists($it_delete_href);
$this->assertSession()->pageTextNotContains($warning);
$this->drupalGet($this->getEditUrl($it_revision));
$this->assertSession()->buttonExists('Delete translation');
// Create an English draft and verify the published translation was
// preserved.
$this->drupalLogin($this->editor);
$en_revision = $this->loadRevisionTranslation($entity, 'en');
$this->drupalGet($this->getEditUrl($en_revision));
$edit = [
'title[0][value]' => "Test $index.4 EN",
'moderation_state[0][state]' => 'draft',
];
$this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
$entity = $this->storage->loadUnchanged($id);
$this->assertTrue($entity->hasTranslation('it'));
$en_revision = $this->loadRevisionTranslation($entity, 'en');
$this->assertTrue($en_revision->hasTranslation('it'));
$this->drupalLogin($this->currentAccount);
// Delete the translation and verify that it is actually gone and that it is
// possible to create it again.
$this->drupalGet($it_delete_url);
$this->drupalPostForm(NULL, [], 'Delete Italian translation');
$entity = $this->storage->loadUnchanged($id);
$this->assertFalse($entity->hasTranslation('it'));
$it_revision = $this->loadRevisionTranslation($entity, 'it');
$this->assertTrue($it_revision->wasDefaultRevision());
$this->assertTrue($it_revision->hasTranslation('it'));
$this->assertTrue($it_revision->getRevisionId() < $entity->getRevisionId());
$this->drupalGet($overview_url);
$this->assertSession()->linkByHrefNotExists($this->getEditUrl($it_revision)->toString());
$this->assertSession()->linkByHrefExists($add_translation_href);
// Publish the English draft and verify the translation is not accidentally
// restored.
$this->drupalLogin($this->editor);
$en_revision = $this->loadRevisionTranslation($entity, 'en');
$this->drupalGet($this->getEditUrl($en_revision));
$edit = [
'title[0][value]' => "Test $index.6 EN",
'moderation_state[0][state]' => 'published',
];
$this->drupalPostForm(NULL, $edit, t('Save'));
$entity = $this->storage->loadUnchanged($id);
$this->assertFalse($entity->hasTranslation('it'));
$this->drupalLogin($this->currentAccount);
// Create a published translation again and verify it could be deleted.
$this->drupalGet($add_translation_url);
$edit = [
'title[0][value]' => "Test $index.7 IT",
'moderation_state[0][state]' => 'published',
];
$this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
$entity = $this->storage->loadUnchanged($id);
$this->assertTrue($entity->hasTranslation('it'));
$it_revision = $this->loadRevisionTranslation($entity, 'it');
$this->assertTrue($it_revision->hasTranslation('it'));
$this->drupalGet($overview_url);
$this->assertSession()->linkByHrefExists($it_delete_href);
// Create a translation draft again and verify it cannot be deleted.
$this->drupalGet($this->getEditUrl($it_revision));
$edit = [
'title[0][value]' => "Test $index.8 IT",
'moderation_state[0][state]' => 'draft',
];
$this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
$entity = $this->storage->loadUnchanged($id);
$this->assertTrue($entity->hasTranslation('it'));
$it_revision = $this->loadRevisionTranslation($entity, 'it');
$this->assertTrue($it_revision->hasTranslation('it'));
$this->drupalGet($overview_url);
$this->assertSession()->linkByHrefNotExists($it_delete_href);
// Delete the translation draft and verify the translation can be deleted
// again, since the active revision is now a default revision.
$this->drupalLogin($this->editor);
$this->drupalGet($it_revision->toUrl('version-history'));
$revision_deletion_url = Url::fromRoute('node.revision_delete_confirm', [
'node' => $id,
'node_revision' => $it_revision->getRevisionId(),
],
[
'language' => ConfigurableLanguage::load('it'),
'absolute' => FALSE,
]
);
$revision_deletion_href = $revision_deletion_url->toString();
$this->getSession()->getDriver()->click("//a[@href='$revision_deletion_href']");
$this->drupalPostForm(NULL, [], 'Delete');
$this->drupalLogin($this->currentAccount);
$this->drupalGet($overview_url);
$this->assertSession()->linkByHrefExists($it_delete_href);
// Verify that now the translation can be deleted.
$this->drupalGet($it_delete_url);
$this->drupalPostForm(NULL, [], 'Delete Italian translation');
$entity = $this->storage->loadUnchanged($id);
$this->assertFalse($entity->hasTranslation('it'));
$it_revision = $this->loadRevisionTranslation($entity, 'it');
$this->assertTrue($it_revision->wasDefaultRevision());
$this->assertTrue($it_revision->hasTranslation('it'));
$this->assertTrue($it_revision->getRevisionId() < $entity->getRevisionId());
$this->drupalGet($overview_url);
$this->assertSession()->linkByHrefNotExists($this->getEditUrl($it_revision)->toString());
$this->assertSession()->linkByHrefExists($add_translation_href);
}
}

View file

@ -0,0 +1,298 @@
<?php
namespace Drupal\Tests\content_translation\Functional;
use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
use Drupal\comment\Tests\CommentTestTrait;
use Drupal\Core\Field\Entity\BaseFieldOverride;
use Drupal\Core\Language\Language;
use Drupal\field\Entity\FieldConfig;
use Drupal\language\Entity\ContentLanguageSettings;
use Drupal\Tests\BrowserTestBase;
/**
* Tests the content translation settings UI.
*
* @group content_translation
*/
class ContentTranslationSettingsTest extends BrowserTestBase {
use CommentTestTrait;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['language', 'content_translation', 'node', 'comment', 'field_ui', 'entity_test'];
protected function setUp() {
parent::setUp();
// Set up two content types to test fields shared between different
// bundles.
$this->drupalCreateContentType(['type' => 'article']);
$this->drupalCreateContentType(['type' => 'page']);
$this->addDefaultCommentField('node', 'article', 'comment_article', CommentItemInterface::OPEN, 'comment_article');
$this->addDefaultCommentField('node', 'page', 'comment_page');
$admin_user = $this->drupalCreateUser(['access administration pages', 'administer languages', 'administer content translation', 'administer content types', 'administer node fields', 'administer comment fields', 'administer comments', 'administer comment types', 'administer account settings']);
$this->drupalLogin($admin_user);
}
/**
* Tests that the settings UI works as expected.
*/
public function testSettingsUI() {
// Check for the content_translation_menu_links_discovered_alter() changes.
$this->drupalGet('admin/config');
$this->assertLink('Content language and translation');
$this->assertText('Configure language and translation support for content.');
// Test that the translation settings are ignored if the bundle is marked
// translatable but the entity type is not.
$edit = ['settings[comment][comment_article][translatable]' => TRUE];
$this->assertSettings('comment', NULL, FALSE, $edit);
// Test that the translation settings are ignored if only a field is marked
// as translatable and not the related entity type and bundle.
$edit = ['settings[comment][comment_article][fields][comment_body]' => TRUE];
$this->assertSettings('comment', NULL, FALSE, $edit);
// Test that the translation settings are not stored if an entity type and
// bundle are marked as translatable but no field is.
$edit = [
'entity_types[comment]' => TRUE,
'settings[comment][comment_article][translatable]' => TRUE,
// Base fields are translatable by default.
'settings[comment][comment_article][fields][changed]' => FALSE,
'settings[comment][comment_article][fields][created]' => FALSE,
'settings[comment][comment_article][fields][homepage]' => FALSE,
'settings[comment][comment_article][fields][hostname]' => FALSE,
'settings[comment][comment_article][fields][mail]' => FALSE,
'settings[comment][comment_article][fields][name]' => FALSE,
'settings[comment][comment_article][fields][status]' => FALSE,
'settings[comment][comment_article][fields][subject]' => FALSE,
'settings[comment][comment_article][fields][uid]' => FALSE,
];
$this->assertSettings('comment', 'comment_article', FALSE, $edit);
$xpath_err = '//div[contains(@class, "error")]';
$this->assertTrue($this->xpath($xpath_err), 'Enabling translation only for entity bundles generates a form error.');
// Test that the translation settings are not stored if a non-configurable
// language is set as default and the language selector is hidden.
$edit = [
'entity_types[comment]' => TRUE,
'settings[comment][comment_article][settings][language][langcode]' => Language::LANGCODE_NOT_SPECIFIED,
'settings[comment][comment_article][settings][language][language_alterable]' => FALSE,
'settings[comment][comment_article][translatable]' => TRUE,
'settings[comment][comment_article][fields][comment_body]' => TRUE,
];
$this->assertSettings('comment', 'comment_article', FALSE, $edit);
$this->assertTrue($this->xpath($xpath_err), 'Enabling translation with a fixed non-configurable language generates a form error.');
// Test that a field shared among different bundles can be enabled without
// needing to make all the related bundles translatable.
$edit = [
'entity_types[comment]' => TRUE,
'settings[comment][comment_article][settings][language][langcode]' => 'current_interface',
'settings[comment][comment_article][settings][language][language_alterable]' => TRUE,
'settings[comment][comment_article][translatable]' => TRUE,
'settings[comment][comment_article][fields][comment_body]' => TRUE,
// Override both comment subject fields to untranslatable.
'settings[comment][comment_article][fields][subject]' => FALSE,
'settings[comment][comment][fields][subject]' => FALSE,
];
$this->assertSettings('comment', 'comment_article', TRUE, $edit);
$definition = $this->entityManager()->getFieldDefinitions('comment', 'comment_article')['comment_body'];
$this->assertTrue($definition->isTranslatable(), 'Article comment body is translatable.');
$definition = $this->entityManager()->getFieldDefinitions('comment', 'comment_article')['subject'];
$this->assertFalse($definition->isTranslatable(), 'Article comment subject is not translatable.');
$definition = $this->entityManager()->getFieldDefinitions('comment', 'comment')['comment_body'];
$this->assertFalse($definition->isTranslatable(), 'Page comment body is not translatable.');
$definition = $this->entityManager()->getFieldDefinitions('comment', 'comment')['subject'];
$this->assertFalse($definition->isTranslatable(), 'Page comment subject is not translatable.');
// Test that translation can be enabled for base fields.
$edit = [
'entity_types[entity_test_mul]' => TRUE,
'settings[entity_test_mul][entity_test_mul][translatable]' => TRUE,
'settings[entity_test_mul][entity_test_mul][fields][name]' => TRUE,
'settings[entity_test_mul][entity_test_mul][fields][user_id]' => FALSE,
];
$this->assertSettings('entity_test_mul', 'entity_test_mul', TRUE, $edit);
$field_override = BaseFieldOverride::loadByName('entity_test_mul', 'entity_test_mul', 'name');
$this->assertTrue($field_override->isTranslatable(), 'Base fields can be overridden with a base field bundle override entity.');
$definitions = $this->entityManager()->getFieldDefinitions('entity_test_mul', 'entity_test_mul');
$this->assertTrue($definitions['name']->isTranslatable() && !$definitions['user_id']->isTranslatable(), 'Base field bundle overrides were correctly altered.');
// Test that language settings are correctly stored.
$language_configuration = ContentLanguageSettings::loadByEntityTypeBundle('comment', 'comment_article');
$this->assertEqual($language_configuration->getDefaultLangcode(), 'current_interface', 'The default language for article comments is set to the interface text language selected for page.');
$this->assertTrue($language_configuration->isLanguageAlterable(), 'The language selector for article comments is shown.');
// Verify language widget appears on comment type form.
$this->drupalGet('admin/structure/comment/manage/comment_article');
$this->assertField('language_configuration[content_translation]');
$this->assertFieldChecked('edit-language-configuration-content-translation');
// Verify that translation may be enabled for the article content type.
$edit = [
'language_configuration[content_translation]' => TRUE,
];
// Make sure the checkbox is available and not checked by default.
$this->drupalGet('admin/structure/types/manage/article');
$this->assertField('language_configuration[content_translation]');
$this->assertNoFieldChecked('edit-language-configuration-content-translation');
$this->drupalPostForm('admin/structure/types/manage/article', $edit, t('Save content type'));
$this->drupalGet('admin/structure/types/manage/article');
$this->assertFieldChecked('edit-language-configuration-content-translation');
// Test that the title field of nodes is available in the settings form.
$edit = [
'entity_types[node]' => TRUE,
'settings[node][article][settings][language][langcode]' => 'current_interface',
'settings[node][article][settings][language][language_alterable]' => TRUE,
'settings[node][article][translatable]' => TRUE,
'settings[node][article][fields][title]' => TRUE,
];
$this->assertSettings('node', NULL, TRUE, $edit);
foreach ([TRUE, FALSE] as $translatable) {
// Test that configurable field translatability is correctly switched.
$edit = ['settings[node][article][fields][body]' => $translatable];
$this->assertSettings('node', 'article', TRUE, $edit);
$field = FieldConfig::loadByName('node', 'article', 'body');
$definitions = \Drupal::entityManager()->getFieldDefinitions('node', 'article');
$this->assertEqual($definitions['body']->isTranslatable(), $translatable, 'Field translatability correctly switched.');
$this->assertEqual($field->isTranslatable(), $definitions['body']->isTranslatable(), 'Configurable field translatability correctly switched.');
// Test that also the Field UI form behaves correctly.
$translatable = !$translatable;
$edit = ['translatable' => $translatable];
$this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.body', $edit, t('Save settings'));
\Drupal::entityManager()->clearCachedFieldDefinitions();
$field = FieldConfig::loadByName('node', 'article', 'body');
$definitions = \Drupal::entityManager()->getFieldDefinitions('node', 'article');
$this->assertEqual($definitions['body']->isTranslatable(), $translatable, 'Field translatability correctly switched.');
$this->assertEqual($field->isTranslatable(), $definitions['body']->isTranslatable(), 'Configurable field translatability correctly switched.');
}
// Test that the order of the language list is similar to other language
// lists, such as in Views UI.
$this->drupalGet('admin/config/regional/content-language');
$expected_elements = [
'site_default',
'current_interface',
'authors_default',
'en',
'und',
'zxx',
];
$elements = $this->xpath('//select[@id="edit-settings-node-article-settings-language-langcode"]/option');
// Compare values inside the option elements with expected values.
for ($i = 0; $i < count($elements); $i++) {
$this->assertEqual($elements[$i]->getValue(), $expected_elements[$i]);
}
}
/**
* Tests the language settings checkbox on account settings page.
*/
public function testAccountLanguageSettingsUI() {
// Make sure the checkbox is available and not checked by default.
$this->drupalGet('admin/config/people/accounts');
$this->assertField('language[content_translation]');
$this->assertNoFieldChecked('edit-language-content-translation');
$edit = [
'language[content_translation]' => TRUE,
];
$this->drupalPostForm('admin/config/people/accounts', $edit, t('Save configuration'));
$this->drupalGet('admin/config/people/accounts');
$this->assertFieldChecked('edit-language-content-translation');
// Make sure account settings can be saved.
$this->drupalPostForm('admin/config/people/accounts', ['anonymous' => 'Save me please!'], 'Save configuration');
$this->assertFieldByName('anonymous', 'Save me please!', 'Anonymous name has been changed.');
$this->assertText('The configuration options have been saved.');
}
/**
* Asserts that translatability has the expected value for the given bundle.
*
* @param string $entity_type
* The entity type for which to check translatability.
* @param string $bundle
* The bundle for which to check translatability.
* @param bool $enabled
* TRUE if translatability should be enabled, FALSE otherwise.
* @param array $edit
* An array of values to submit to the content translation settings page.
*
* @return bool
* TRUE if the assertion succeeded, FALSE otherwise.
*/
protected function assertSettings($entity_type, $bundle, $enabled, $edit) {
$this->drupalPostForm('admin/config/regional/content-language', $edit, t('Save configuration'));
$args = ['@entity_type' => $entity_type, '@bundle' => $bundle, '@enabled' => $enabled ? 'enabled' : 'disabled'];
$message = format_string('Translation for entity @entity_type (@bundle) is @enabled.', $args);
\Drupal::entityManager()->clearCachedDefinitions();
return $this->assertEqual(\Drupal::service('content_translation.manager')->isEnabled($entity_type, $bundle), $enabled, $message);
}
/**
* Tests that field setting depends on bundle translatability.
*/
public function testFieldTranslatableSettingsUI() {
// At least one field needs to be translatable to enable article for
// translation. Create an extra field to be used for this purpose. We use
// the UI to test our form alterations.
$edit = [
'new_storage_type' => 'text',
'label' => 'Test',
'field_name' => 'article_text',
];
$this->drupalPostForm('admin/structure/types/manage/article/fields/add-field', $edit, 'Save and continue');
// Tests that field doesn't have translatable setting if bundle is not
// translatable.
$path = 'admin/structure/types/manage/article/fields/node.article.field_article_text';
$this->drupalGet($path);
$this->assertFieldByXPath('//input[@id="edit-translatable" and @disabled="disabled"]');
$this->assertText('To configure translation for this field, enable language support for this type.', 'No translatable setting for field.');
// Tests that field has translatable setting if bundle is translatable.
// Note: this field is not translatable when enable bundle translatability.
$edit = [
'entity_types[node]' => TRUE,
'settings[node][article][settings][language][language_alterable]' => TRUE,
'settings[node][article][translatable]' => TRUE,
'settings[node][article][fields][field_article_text]' => TRUE,
];
$this->assertSettings('node', 'article', TRUE, $edit);
$this->drupalGet($path);
$this->assertFieldByXPath('//input[@id="edit-translatable" and not(@disabled) and @checked="checked"]');
$this->assertNoText('To enable translation of this field, enable language support for this type.', 'Translatable setting for field available.');
}
/**
* Tests the translatable settings checkbox for untranslatable entities.
*/
public function testNonTranslatableTranslationSettingsUI() {
$this->drupalGet('admin/config/regional/content-language');
$this->assertNoField('settings[entity_test][entity_test][translatable]');
}
/**
* Returns the entity manager.
*
* @return \Drupal\Core\Entity\EntityManagerInterface
* The entity manager;
*/
protected function entityManager() {
return $this->container->get('entity.manager');
}
}

View file

@ -0,0 +1,253 @@
<?php
namespace Drupal\Tests\content_translation\Functional;
use Drupal\Core\Entity\EntityInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\file\Entity\File;
use Drupal\Tests\TestFileCreationTrait;
/**
* Tests the field synchronization behavior for the image field.
*
* @group content_translation
*/
class ContentTranslationSyncImageTest extends ContentTranslationTestBase {
use TestFileCreationTrait {
getTestFiles as drupalGetTestFiles;
}
/**
* The cardinality of the image field.
*
* @var int
*/
protected $cardinality;
/**
* The test image files.
*
* @var array
*/
protected $files;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['language', 'content_translation', 'entity_test', 'image', 'field_ui'];
protected function setUp() {
parent::setUp();
$this->files = $this->drupalGetTestFiles('image');
}
/**
* Creates the test image field.
*/
protected function setupTestFields() {
$this->fieldName = 'field_test_et_ui_image';
$this->cardinality = 3;
FieldStorageConfig::create([
'field_name' => $this->fieldName,
'entity_type' => $this->entityTypeId,
'type' => 'image',
'cardinality' => $this->cardinality,
])->save();
FieldConfig::create([
'entity_type' => $this->entityTypeId,
'field_name' => $this->fieldName,
'bundle' => $this->entityTypeId,
'label' => 'Test translatable image field',
'third_party_settings' => [
'content_translation' => [
'translation_sync' => [
'file' => FALSE,
'alt' => 'alt',
'title' => 'title',
],
],
],
])->save();
}
/**
* {@inheritdoc}
*/
protected function getEditorPermissions() {
// Every entity-type-specific test needs to define these.
return ['administer entity_test_mul fields', 'administer languages', 'administer content translation'];
}
/**
* Tests image field field synchronization.
*/
public function testImageFieldSync() {
// Check that the alt and title fields are enabled for the image field.
$this->drupalLogin($this->editor);
$this->drupalGet('entity_test_mul/structure/' . $this->entityTypeId . '/fields/' . $this->entityTypeId . '.' . $this->entityTypeId . '.' . $this->fieldName);
$this->assertFieldChecked('edit-third-party-settings-content-translation-translation-sync-alt');
$this->assertFieldChecked('edit-third-party-settings-content-translation-translation-sync-title');
$edit = [
'third_party_settings[content_translation][translation_sync][alt]' => FALSE,
'third_party_settings[content_translation][translation_sync][title]' => FALSE,
];
$this->drupalPostForm(NULL, $edit, t('Save settings'));
// Check that the content translation settings page reflects the changes
// performed in the field edit page.
$this->drupalGet('admin/config/regional/content-language');
$this->assertNoFieldChecked('edit-settings-entity-test-mul-entity-test-mul-columns-field-test-et-ui-image-alt');
$this->assertNoFieldChecked('edit-settings-entity-test-mul-entity-test-mul-columns-field-test-et-ui-image-title');
$edit = [
'settings[entity_test_mul][entity_test_mul][fields][field_test_et_ui_image]' => TRUE,
'settings[entity_test_mul][entity_test_mul][columns][field_test_et_ui_image][alt]' => TRUE,
'settings[entity_test_mul][entity_test_mul][columns][field_test_et_ui_image][title]' => TRUE,
];
$this->drupalPostForm('admin/config/regional/content-language', $edit, t('Save configuration'));
$errors = $this->xpath('//div[contains(@class, "messages--error")]');
$this->assertFalse($errors, 'Settings correctly stored.');
$this->assertFieldChecked('edit-settings-entity-test-mul-entity-test-mul-columns-field-test-et-ui-image-alt');
$this->assertFieldChecked('edit-settings-entity-test-mul-entity-test-mul-columns-field-test-et-ui-image-title');
$this->drupalLogin($this->translator);
$default_langcode = $this->langcodes[0];
$langcode = $this->langcodes[1];
// Populate the test entity with some random initial values.
$values = [
'name' => $this->randomMachineName(),
'user_id' => mt_rand(1, 128),
'langcode' => $default_langcode,
];
$entity = entity_create($this->entityTypeId, $values);
// Create some file entities from the generated test files and store them.
$values = [];
for ($delta = 0; $delta < $this->cardinality; $delta++) {
// For the default language use the same order for files and field items.
$index = $delta;
// Create the file entity for the image being processed and record its
// identifier.
$field_values = [
'uri' => $this->files[$index]->uri,
'uid' => \Drupal::currentUser()->id(),
'status' => FILE_STATUS_PERMANENT,
];
$file = File::create($field_values);
$file->save();
$fid = $file->id();
$this->files[$index]->fid = $fid;
// Generate the item for the current image file entity and attach it to
// the entity.
$item = [
'target_id' => $fid,
'alt' => $default_langcode . '_' . $fid . '_' . $this->randomMachineName(),
'title' => $default_langcode . '_' . $fid . '_' . $this->randomMachineName(),
];
$entity->{$this->fieldName}[] = $item;
// Store the generated values keying them by fid for easier lookup.
$values[$default_langcode][$fid] = $item;
}
$entity = $this->saveEntity($entity);
// Create some field translations for the test image field. The translated
// items will be one less than the original values to check that only the
// translated ones will be preserved. In fact we want the same fids and
// items order for both languages.
$translation = $entity->addTranslation($langcode);
for ($delta = 0; $delta < $this->cardinality - 1; $delta++) {
// Simulate a field reordering: items are shifted of one position ahead.
// The modulo operator ensures we start from the beginning after reaching
// the maximum allowed delta.
$index = ($delta + 1) % $this->cardinality;
// Generate the item for the current image file entity and attach it to
// the entity.
$fid = $this->files[$index]->fid;
$item = [
'target_id' => $fid,
'alt' => $langcode . '_' . $fid . '_' . $this->randomMachineName(),
'title' => $langcode . '_' . $fid . '_' . $this->randomMachineName(),
];
$translation->{$this->fieldName}[] = $item;
// Again store the generated values keying them by fid for easier lookup.
$values[$langcode][$fid] = $item;
}
// Perform synchronization: the translation language is used as source,
// while the default language is used as target.
$this->manager->getTranslationMetadata($translation)->setSource($default_langcode);
$entity = $this->saveEntity($translation);
$translation = $entity->getTranslation($langcode);
// Check that one value has been dropped from the original values.
$assert = count($entity->{$this->fieldName}) == 2;
$this->assertTrue($assert, 'One item correctly removed from the synchronized field values.');
// Check that fids have been synchronized and translatable column values
// have been retained.
$fids = [];
foreach ($entity->{$this->fieldName} as $delta => $item) {
$value = $values[$default_langcode][$item->target_id];
$source_item = $translation->{$this->fieldName}->get($delta);
$assert = $item->target_id == $source_item->target_id && $item->alt == $value['alt'] && $item->title == $value['title'];
$this->assertTrue($assert, format_string('Field item @fid has been successfully synchronized.', ['@fid' => $item->target_id]));
$fids[$item->target_id] = TRUE;
}
// Check that the dropped value is the right one.
$removed_fid = $this->files[0]->fid;
$this->assertTrue(!isset($fids[$removed_fid]), format_string('Field item @fid has been correctly removed.', ['@fid' => $removed_fid]));
// Add back an item for the dropped value and perform synchronization again.
$values[$langcode][$removed_fid] = [
'target_id' => $removed_fid,
'alt' => $langcode . '_' . $removed_fid . '_' . $this->randomMachineName(),
'title' => $langcode . '_' . $removed_fid . '_' . $this->randomMachineName(),
];
$translation->{$this->fieldName}->setValue(array_values($values[$langcode]));
$entity = $this->saveEntity($translation);
$translation = $entity->getTranslation($langcode);
// Check that the value has been added to the default language.
$assert = count($entity->{$this->fieldName}->getValue()) == 3;
$this->assertTrue($assert, 'One item correctly added to the synchronized field values.');
foreach ($entity->{$this->fieldName} as $delta => $item) {
// When adding an item its value is copied over all the target languages,
// thus in this case the source language needs to be used to check the
// values instead of the target one.
$fid_langcode = $item->target_id != $removed_fid ? $default_langcode : $langcode;
$value = $values[$fid_langcode][$item->target_id];
$source_item = $translation->{$this->fieldName}->get($delta);
$assert = $item->target_id == $source_item->target_id && $item->alt == $value['alt'] && $item->title == $value['title'];
$this->assertTrue($assert, format_string('Field item @fid has been successfully synchronized.', ['@fid' => $item->target_id]));
}
}
/**
* Saves the passed entity and reloads it, enabling compatibility mode.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity to be saved.
*
* @return \Drupal\Core\Entity\EntityInterface
* The saved entity.
*/
protected function saveEntity(EntityInterface $entity) {
$entity->save();
$entity = entity_test_mul_load($entity->id(), TRUE);
return $entity;
}
}

View file

@ -2,6 +2,7 @@
namespace Drupal\Tests\content_translation\Functional;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
use Drupal\field\Entity\FieldConfig;
use Drupal\language\Entity\ConfigurableLanguage;
@ -138,7 +139,7 @@ abstract class ContentTranslationTestBase extends BrowserTestBase {
* Returns an array of permissions needed for the administrator.
*/
protected function getAdministratorPermissions() {
return array_merge($this->getEditorPermissions(), $this->getTranslatorPermissions(), ['administer content translation']);
return array_merge($this->getEditorPermissions(), $this->getTranslatorPermissions(), ['administer languages', 'administer content translation']);
}
/**
@ -236,4 +237,24 @@ abstract class ContentTranslationTestBase extends BrowserTestBase {
return $entity->id();
}
/**
* Returns the edit URL for the specified entity.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* The entity being edited.
*
* @return \Drupal\Core\Url
* The edit URL.
*/
protected function getEditUrl(ContentEntityInterface $entity) {
if ($entity->access('update', $this->loggedInUser)) {
$url = $entity->toUrl('edit-form');
}
else {
$url = $entity->toUrl('drupal:content-translation-edit');
$url->setRouteParameter('language', $entity->language()->getId());
}
return $url;
}
}

View file

@ -25,7 +25,7 @@ class ContentTranslationUISkipTest extends BrowserTestBase {
$admin_user = $this->drupalCreateUser([
'translate any entity',
'administer content translation',
'administer languages'
'administer languages',
]);
$this->drupalLogin($admin_user);
// Visit the content translation.

View file

@ -10,8 +10,7 @@ use Drupal\Core\Language\Language;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Url;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
use Drupal\Tests\system\Functional\Cache\AssertPageCacheContextsAndTagsTrait;
/**
* Tests the Content Translation UI.
@ -35,8 +34,7 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
protected $testLanguageSelector = TRUE;
/**
* Flag that tells whether the HTML escaping of all languages works or not
* after SafeMarkup change.
* Flag to determine if "all languages" rendering is tested.
*
* @var bool
*/
@ -111,12 +109,11 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
$add_url = Url::fromRoute("entity.$entity_type_id.content_translation_add", [
$entity->getEntityTypeId() => $entity->id(),
'source' => $default_langcode,
'target' => $langcode
'target' => $langcode,
], ['language' => $language]);
$this->drupalPostForm($add_url, $this->getEditValues($values, $langcode), $this->getFormSubmitActionForNewTranslation($entity, $langcode));
// Assert that HTML is escaped in "all languages" in UI after SafeMarkup
// change.
// Assert that HTML is not escaped unexpectedly.
if ($this->testHTMLEscapeForAllLanguages) {
$this->assertNoRaw('&lt;span class=&quot;translation-entity-all-languages&quot;&gt;(all languages)&lt;/span&gt;');
$this->assertRaw('<span class="translation-entity-all-languages">(all languages)</span>');
@ -139,10 +136,10 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
$author_field_name = $entity->hasField('content_translation_uid') ? 'content_translation_uid' : 'uid';
if ($entity->getFieldDefinition($author_field_name)->isTranslatable()) {
$this->assertEqual($metadata_target_translation->getAuthor()->id(), $this->translator->id(),
SafeMarkup::format('Author of the target translation @langcode correctly stored for translatable owner field.', ['@langcode' => $langcode]));
new FormattableMarkup('Author of the target translation @langcode correctly stored for translatable owner field.', ['@langcode' => $langcode]));
$this->assertNotEqual($metadata_target_translation->getAuthor()->id(), $metadata_source_translation->getAuthor()->id(),
SafeMarkup::format('Author of the target translation @target different from the author of the source translation @source for translatable owner field.',
new FormattableMarkup('Author of the target translation @target different from the author of the source translation @source for translatable owner field.',
['@target' => $langcode, '@source' => $default_langcode]));
}
else {
@ -152,7 +149,7 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
$created_field_name = $entity->hasField('content_translation_created') ? 'content_translation_created' : 'created';
if ($entity->getFieldDefinition($created_field_name)->isTranslatable()) {
$this->assertTrue($metadata_target_translation->getCreatedTime() > $metadata_source_translation->getCreatedTime(),
SafeMarkup::format('Translation creation timestamp of the target translation @target is newer than the creation timestamp of the source translation @source for translatable created field.',
new FormattableMarkup('Translation creation timestamp of the target translation @target is newer than the creation timestamp of the source translation @source for translatable created field.',
['@target' => $langcode, '@source' => $default_langcode]));
}
else {
@ -176,7 +173,7 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
$add_url = Url::fromRoute("entity.$entity_type_id.content_translation_add", [
$entity->getEntityTypeId() => $entity->id(),
'source' => $default_langcode,
'target' => $langcode
'target' => $langcode,
], ['language' => $language]);
// This does not save anything, it merely reloads the form and fills in the
// fields with the values from the different source language.
@ -190,7 +187,7 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
$add_url = Url::fromRoute("entity.$entity_type_id.content_translation_add", [
$entity->getEntityTypeId() => $entity->id(),
'source' => $source_langcode,
'target' => $langcode
'target' => $langcode,
], ['language' => $language]);
$this->drupalPostForm($add_url, $edit, $this->getFormSubmitActionForNewTranslation($entity, $langcode));
$storage->resetCache([$this->entityId]);

View file

@ -0,0 +1,177 @@
<?php
namespace Drupal\Tests\content_translation\Functional;
use Drupal\Core\Url;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\language\Entity\ConfigurableLanguage;
/**
* Tests the untranslatable fields behaviors.
*
* @group content_translation
*/
class ContentTranslationUntranslatableFieldsTest extends ContentTranslationPendingRevisionTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['field_test'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Configure one field as untranslatable.
$this->drupalLogin($this->administrator);
$edit = [
'settings[' . $this->entityTypeId . '][' . $this->bundle . '][fields][' . $this->fieldName . ']' => 0,
];
$this->drupalPostForm('admin/config/regional/content-language', $edit, 'Save configuration');
/** @var \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager */
$entity_field_manager = $this->container->get('entity_field.manager');
$entity_field_manager->clearCachedFieldDefinitions();
$definitions = $entity_field_manager->getFieldDefinitions($this->entityTypeId, $this->bundle);
$this->assertFalse($definitions[$this->fieldName]->isTranslatable());
}
/**
* {@inheritdoc}
*/
protected function setupTestFields() {
parent::setupTestFields();
$field_storage = FieldStorageConfig::create([
'field_name' => 'field_multilingual',
'type' => 'test_field',
'entity_type' => $this->entityTypeId,
'cardinality' => 1,
]);
$field_storage->save();
FieldConfig::create([
'field_storage' => $field_storage,
'bundle' => $this->bundle,
'label' => 'Untranslatable-but-visible test field',
'translatable' => FALSE,
])->save();
entity_get_form_display($this->entityTypeId, $this->bundle, 'default')
->setComponent('field_multilingual', [
'type' => 'test_field_widget_multilingual',
])
->save();
}
/**
* 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(['title' => $this->randomString()], 'en');
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
$entity = $entity_type_manager
->getStorage($this->entityTypeId)
->load($id);
// Check that the untranslatable field widget is displayed on the edit form
// and no translatability clue is displayed yet.
$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)"]';
$this->assertEmpty($this->xpath($clue_xpath));
$this->assertSession()->pageTextContains('Untranslatable-but-visible test field');
// Add a translation and check that the untranslatable field widget is
// displayed on the translation and edit forms along with translatability
// clues.
$add_url = Url::fromRoute("entity.{$this->entityTypeId}.content_translation_add", [
$entity->getEntityTypeId() => $entity->id(),
'source' => 'en',
'target' => 'it',
]);
$this->drupalGet($add_url);
$this->assertNotEmpty($this->xpath($field_xpath));
$this->assertNotEmpty($this->xpath($clue_xpath));
$this->assertSession()->pageTextContains('Untranslatable-but-visible test field');
$this->drupalPostForm(NULL, [], 'Save');
// 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));
$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.
$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($en_edit_url);
$field_xpath = '//input[@name="' . $this->fieldName . '[0][value]"]';
$this->assertNotEmpty($this->xpath($field_xpath));
$this->assertEmpty($this->xpath($clue_xpath));
$this->assertSession()->pageTextContains('Untranslatable-but-visible test field');
// Verify no widget is displayed on the non-default language edit form.
$this->drupalGet($it_edit_url);
$this->assertEmpty($this->xpath($field_xpath));
$this->assertEmpty($this->xpath($clue_xpath));
$this->assertSession()->pageTextContains('Untranslatable-but-visible test field');
// Verify a warning is displayed.
$this->assertSession()->pageTextContains('Fields that apply to all languages are hidden to avoid conflicting changes.');
$edit_path = $entity->toUrl('edit-form')->toString();
$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);
}
}

View file

@ -0,0 +1,259 @@
<?php
namespace Drupal\Tests\content_translation\Functional;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Url;
use Drupal\Tests\system\Functional\Cache\AssertPageCacheContextsAndTagsTrait;
use Drupal\user\UserInterface;
/**
* Tests the content translation workflows for the test entity.
*
* @group content_translation
*/
class ContentTranslationWorkflowsTest extends ContentTranslationTestBase {
use AssertPageCacheContextsAndTagsTrait;
/**
* The entity used for testing.
*
* @var \Drupal\Core\Entity\EntityInterface
*/
protected $entity;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['language', 'content_translation', 'entity_test'];
protected function setUp() {
parent::setUp();
$this->setupEntity();
}
/**
* {@inheritdoc}
*/
protected function getTranslatorPermissions() {
$permissions = parent::getTranslatorPermissions();
$permissions[] = 'view test entity';
return $permissions;
}
/**
* {@inheritdoc}
*/
protected function getEditorPermissions() {
return ['administer entity_test content'];
}
/**
* Creates a test entity and translate it.
*/
protected function setupEntity() {
$default_langcode = $this->langcodes[0];
// Create a test entity.
$user = $this->drupalCreateUser();
$values = [
'name' => $this->randomMachineName(),
'user_id' => $user->id(),
$this->fieldName => [['value' => $this->randomMachineName(16)]],
];
$id = $this->createEntity($values, $default_langcode);
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$id]);
$this->entity = $storage->load($id);
// Create a translation.
$this->drupalLogin($this->translator);
$add_translation_url = Url::fromRoute("entity.$this->entityTypeId.content_translation_add", [$this->entityTypeId => $this->entity->id(), 'source' => $default_langcode, 'target' => $this->langcodes[2]]);
$this->drupalPostForm($add_translation_url, [], t('Save'));
$this->rebuildContainer();
}
/**
* Test simple and editorial translation workflows.
*/
public function testWorkflows() {
// Test workflows for the editor.
$expected_status = [
'edit' => 200,
'delete' => 200,
'overview' => 403,
'add_translation' => 403,
'edit_translation' => 403,
'delete_translation' => 403,
];
$this->doTestWorkflows($this->editor, $expected_status);
// Test workflows for the translator.
$expected_status = [
'edit' => 403,
'delete' => 403,
'overview' => 200,
'add_translation' => 200,
'edit_translation' => 200,
'delete_translation' => 200,
];
$this->doTestWorkflows($this->translator, $expected_status);
// Test workflows for the admin.
$expected_status = [
'edit' => 200,
'delete' => 200,
'overview' => 200,
'add_translation' => 200,
'edit_translation' => 403,
'delete_translation' => 403,
];
$this->doTestWorkflows($this->administrator, $expected_status);
// Check that translation permissions allow the associated operations.
$ops = ['create' => t('Add'), 'update' => t('Edit'), 'delete' => t('Delete')];
$translations_url = $this->entity->urlInfo('drupal:content-translation-overview');
foreach ($ops as $current_op => $item) {
$user = $this->drupalCreateUser([$this->getTranslatePermission(), "$current_op content translations", 'view test entity']);
$this->drupalLogin($user);
$this->drupalGet($translations_url);
// Make sure that the user.permissions cache context and the cache tags
// for the entity are present.
$this->assertCacheContext('user.permissions');
foreach ($this->entity->getCacheTags() as $cache_tag) {
$this->assertCacheTag($cache_tag);
}
foreach ($ops as $op => $label) {
if ($op != $current_op) {
$this->assertNoLink($label, format_string('No %op link found.', ['%op' => $label]));
}
else {
$this->assertLink($label, 0, format_string('%op link found.', ['%op' => $label]));
}
}
}
}
/**
* Checks that workflows have the expected behaviors for the given user.
*
* @param \Drupal\user\UserInterface $user
* The user to test the workflow behavior against.
* @param array $expected_status
* The an associative array with the operation name as key and the expected
* status as value.
*/
protected function doTestWorkflows(UserInterface $user, $expected_status) {
$default_langcode = $this->langcodes[0];
$languages = $this->container->get('language_manager')->getLanguages();
$args = ['@user_label' => $user->getUsername()];
$options = ['language' => $languages[$default_langcode], 'absolute' => TRUE];
$this->drupalLogin($user);
// Check whether the user is allowed to access the entity form in edit mode.
$edit_url = $this->entity->urlInfo('edit-form', $options);
$this->drupalGet($edit_url, $options);
$this->assertResponse($expected_status['edit'], new FormattableMarkup('The @user_label has the expected edit access.', $args));
// Check whether the user is allowed to access the entity delete form.
$delete_url = $this->entity->urlInfo('delete-form', $options);
$this->drupalGet($delete_url, $options);
$this->assertResponse($expected_status['delete'], new FormattableMarkup('The @user_label has the expected delete access.', $args));
// Check whether the user is allowed to access the translation overview.
$langcode = $this->langcodes[1];
$options['language'] = $languages[$langcode];
$translations_url = $this->entity->url('drupal:content-translation-overview', $options);
$this->drupalGet($translations_url);
$this->assertResponse($expected_status['overview'], new FormattableMarkup('The @user_label has the expected translation overview access.', $args));
// Check whether the user is allowed to create a translation.
$add_translation_url = Url::fromRoute("entity.$this->entityTypeId.content_translation_add", [$this->entityTypeId => $this->entity->id(), 'source' => $default_langcode, 'target' => $langcode], $options);
if ($expected_status['add_translation'] == 200) {
$this->clickLink('Add');
$this->assertUrl($add_translation_url->toString(), [], 'The translation overview points to the translation form when creating translations.');
// Check that the translation form does not contain shared elements for
// translators.
if ($expected_status['edit'] == 403) {
$this->assertNoSharedElements();
}
}
else {
$this->drupalGet($add_translation_url);
}
$this->assertResponse($expected_status['add_translation'], new FormattableMarkup('The @user_label has the expected translation creation access.', $args));
// Check whether the user is allowed to edit a translation.
$langcode = $this->langcodes[2];
$options['language'] = $languages[$langcode];
$edit_translation_url = Url::fromRoute("entity.$this->entityTypeId.content_translation_edit", [$this->entityTypeId => $this->entity->id(), 'language' => $langcode], $options);
if ($expected_status['edit_translation'] == 200) {
$this->drupalGet($translations_url);
$editor = $expected_status['edit'] == 200;
if ($editor) {
$this->clickLink('Edit', 2);
// An editor should be pointed to the entity form in multilingual mode.
// We need a new expected edit path with a new language.
$expected_edit_path = $this->entity->url('edit-form', $options);
$this->assertUrl($expected_edit_path, [], 'The translation overview points to the edit form for editors when editing translations.');
}
else {
$this->clickLink('Edit');
// While a translator should be pointed to the translation form.
$this->assertUrl($edit_translation_url->toString(), [], 'The translation overview points to the translation form for translators when editing translations.');
// Check that the translation form does not contain shared elements.
$this->assertNoSharedElements();
}
}
else {
$this->drupalGet($edit_translation_url);
}
$this->assertResponse($expected_status['edit_translation'], new FormattableMarkup('The @user_label has the expected translation edit access.', $args));
// Check whether the user is allowed to delete a translation.
$langcode = $this->langcodes[2];
$options['language'] = $languages[$langcode];
$delete_translation_url = Url::fromRoute("entity.$this->entityTypeId.content_translation_delete", [$this->entityTypeId => $this->entity->id(), 'language' => $langcode], $options);
if ($expected_status['delete_translation'] == 200) {
$this->drupalGet($translations_url);
$editor = $expected_status['delete'] == 200;
if ($editor) {
$this->clickLink('Delete', 2);
// An editor should be pointed to the entity deletion form in
// multilingual mode. We need a new expected delete path with a new
// language.
$expected_delete_path = $this->entity->url('delete-form', $options);
$this->assertUrl($expected_delete_path, [], 'The translation overview points to the delete form for editors when deleting translations.');
}
else {
$this->clickLink('Delete');
// While a translator should be pointed to the translation deletion
// form.
$this->assertUrl($delete_translation_url->toString(), [], 'The translation overview points to the translation deletion form for translators when deleting translations.');
}
}
else {
$this->drupalGet($delete_translation_url);
}
$this->assertResponse($expected_status['delete_translation'], new FormattableMarkup('The @user_label has the expected translation deletion access.', $args));
}
/**
* Assert that the current page does not contain shared form elements.
*/
protected function assertNoSharedElements() {
$language_none = LanguageInterface::LANGCODE_NOT_SPECIFIED;
return $this->assertNoFieldByXPath("//input[@name='field_test_text[$language_none][0][value]']", NULL, 'Shared elements are not available on the translation form.');
}
}

View file

@ -0,0 +1,101 @@
<?php
namespace Drupal\Tests\content_translation\Functional\Update;
use Drupal\Core\Language\LanguageInterface;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
use Drupal\Tests\system\Functional\Entity\Traits\EntityDefinitionTestTrait;
/**
* Tests the upgrade path for the Content Translation module.
*
* @group Update
* @group legacy
*/
class ContentTranslationUpdateTest extends UpdatePathTestBase {
use EntityDefinitionTestTrait;
/**
* The database connection used.
*
* @var \Drupal\Core\Database\Connection
*/
protected $database;
/**
* The entity definition update manager.
*
* @var \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface
*/
protected $entityDefinitionUpdateManager;
/**
* The entity manager service.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
protected $entityManager;
/**
* The state service.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->database = \Drupal::database();
$this->entityDefinitionUpdateManager = \Drupal::entityDefinitionUpdateManager();
$this->entityManager = \Drupal::entityManager();
$this->state = \Drupal::state();
}
/**
* {@inheritdoc}
*/
public function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.0.0-rc1-filled.standard.entity_test_update_mul.php.gz',
];
}
/**
* Tests that initial values for metadata fields are populated correctly.
*/
public function testContentTranslationUpdate8400() {
$this->updateEntityTypeToTranslatable();
// The test database dump contains NULL values for
// 'content_translation_source', 'content_translation_outdated' and
// 'content_translation_status' for the first 50 test entities.
// @see _entity_test_update_create_test_entities()
$first_entity_record = $this->database->select('entity_test_update_data', 'etud')
->fields('etud')
->condition('etud.id', 1)
->execute()
->fetchAllAssoc('id');
$this->assertNull($first_entity_record[1]->content_translation_source);
$this->assertNull($first_entity_record[1]->content_translation_outdated);
$this->assertNull($first_entity_record[1]->content_translation_status);
$this->runUpdates();
// After running the updates, all those fields should be populated with
// their default values.
$first_entity_record = $this->database->select('entity_test_update_data', 'etud')
->fields('etud')
->condition('etud.id', 1)
->execute()
->fetchAllAssoc('id');
$this->assertEqual(LanguageInterface::LANGCODE_NOT_SPECIFIED, $first_entity_record[1]->content_translation_source);
$this->assertEqual(0, $first_entity_record[1]->content_translation_outdated);
$this->assertEqual(1, $first_entity_record[1]->content_translation_status);
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace Drupal\Tests\content_translation\Functional\Views;
use Drupal\Tests\views_ui\Functional\UITestBase;
/**
* Tests the views UI when content_translation is enabled.
*
* @group content_translation
*/
class ContentTranslationViewsUITest extends UITestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_view'];
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['content_translation'];
/**
* Tests the views UI.
*/
public function testViewsUI() {
$this->drupalGet('admin/structure/views/view/test_view/edit');
$this->assertTitle(t('@label (@table) | @site-name', ['@label' => 'Test view', '@table' => 'Views test data', '@site-name' => $this->config('system.site')->get('name')]));
}
}

View file

@ -0,0 +1,69 @@
<?php
namespace Drupal\Tests\content_translation\Functional\Views;
use Drupal\Tests\content_translation\Functional\ContentTranslationTestBase;
use Drupal\views\Tests\ViewTestData;
use Drupal\Core\Language\Language;
use Drupal\user\Entity\User;
/**
* Tests the content translation overview link field handler.
*
* @group content_translation
* @see \Drupal\content_translation\Plugin\views\field\TranslationLink
*/
class TranslationLinkTest extends ContentTranslationTestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_entity_translations_link'];
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['content_translation_test_views'];
protected function setUp() {
// @todo Use entity_type once it is has multilingual Views integration.
$this->entityTypeId = 'user';
parent::setUp();
// Assign user 1 a language code so that the entity can be translated.
$user = User::load(1);
$user->langcode = 'en';
$user->save();
// Assign user 2 LANGCODE_NOT_SPECIFIED code so entity can't be translated.
$user = User::load(2);
$user->langcode = Language::LANGCODE_NOT_SPECIFIED;
$user->save();
ViewTestData::createTestViews(get_class($this), ['content_translation_test_views']);
}
/**
* {@inheritdoc}
*/
protected function getTranslatorPermissions() {
$permissions = parent::getTranslatorPermissions();
$permissions[] = 'access user profiles';
return $permissions;
}
/**
* Tests the content translation overview link field handler.
*/
public function testTranslationLink() {
$this->drupalGet('test-entity-translations-link');
$this->assertLinkByHref('user/1/translations');
$this->assertNoLinkByHref('user/2/translations', 'The translations link is not present when content_translation_translate_access() is FALSE.');
}
}

View file

@ -0,0 +1,72 @@
<?php
namespace Drupal\Tests\content_translation\FunctionalJavascript;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
use Drupal\language\Entity\ConfigurableLanguage;
/**
* Tests that contextual links are available for content translation.
*
* @group content_translation
*/
class ContentTranslationContextualLinksTest extends WebDriverTestBase {
/**
* The 'translator' user to use during testing.
*
* @var \Drupal\user\UserInterface
*/
protected $translator;
/**
* {@inheritdoc}
*/
public static $modules = ['content_translation', 'contextual', 'node'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Set up an additional language.
ConfigurableLanguage::createFromLangcode('es')->save();
// Create a content type.
$this->drupalCreateContentType(['type' => 'page']);
// Enable content translation.
$this->drupalLogin($this->rootUser);
$this->drupalGet('admin/config/regional/content-language');
$edit = [
'entity_types[node]' => TRUE,
'settings[node][page][translatable]' => TRUE,
];
$this->drupalPostForm(NULL, $edit, t('Save configuration'));
$this->drupalLogout();
// Create a translator user.
$permissions = [
'access contextual links',
'administer nodes',
'edit any page content',
'translate any entity',
];
$this->translator = $this->drupalCreateUser($permissions);
}
/**
* Tests that a contextual link is available for translating a node.
*/
public function testContentTranslationContextualLinks() {
$node = $this->drupalCreateNode(['type' => 'page', 'title' => 'Test']);
// Check that the translate link appears on the node page.
$this->drupalLogin($this->translator);
$this->drupalGet('node/' . $node->id());
$link = $this->assertSession()->waitForElement('css', '[data-contextual-id^="node:node=1"] .contextual-links a:contains("Translate")');
$this->assertContains('node/1/translations', $link->getAttribute('href'));
}
}

View file

@ -33,6 +33,7 @@ class ContentTranslationConfigImportTest extends KernelTestBase {
protected function setUp() {
parent::setUp();
$this->installConfig(['system']);
$this->installEntitySchema('entity_test_mul');
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync'));
@ -74,7 +75,7 @@ class ContentTranslationConfigImportTest extends KernelTestBase {
'langcode' => 'en',
'status' => TRUE,
'dependencies' => [
'module' => ['content_translation']
'module' => ['content_translation'],
],
'id' => $config_id,
'target_entity_type_id' => 'entity_test_mul',

View file

@ -0,0 +1,90 @@
<?php
namespace Drupal\Tests\content_translation\Kernel;
use Drupal\KernelTests\KernelTestBase;
use Drupal\entity_test\Entity\EntityTestMul;
use Drupal\language\Entity\ConfigurableLanguage;
/**
* Tests the Content Translation bundle info logic.
*
* @group content_translation
*/
class ContentTranslationEntityBundleInfoTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['user', 'language', 'content_translation_test', 'content_translation', 'entity_test'];
/**
* The content translation manager.
*
* @var \Drupal\content_translation\ContentTranslationManagerInterface
*/
protected $contentTranslationManager;
/**
* The bundle info service.
*
* @var \Drupal\Core\Entity\EntityTypeBundleInfo
*/
protected $bundleInfo;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->contentTranslationManager = $this->container->get('content_translation.manager');
$this->bundleInfo = $this->container->get('entity_type.bundle.info');
$this->installEntitySchema('entity_test_mul');
ConfigurableLanguage::createFromLangcode('it')->save();
}
/**
* Tests that modules can know whether bundles are translatable.
*/
public function testHookInvocationOrder() {
$this->contentTranslationManager->setEnabled('entity_test_mul', 'entity_test_mul', TRUE);
$this->bundleInfo->clearCachedBundles();
$this->bundleInfo->getAllBundleInfo();
// Verify that the test module comes first in the module list, which would
// normally make its hook implementation to be invoked first.
/** @var \Drupal\Core\Extension\ModuleHandlerInterface $module_handler */
$module_handler = $this->container->get('module_handler');
$module_list = $module_handler->getModuleList();
$expected_modules = [
'content_translation_test',
'content_translation',
];
$actual_modules = array_keys(array_intersect_key($module_list, array_flip($expected_modules)));
$this->assertEquals($expected_modules, $actual_modules);
// Check that the "content_translation_test" hook implementation has access
// to the "translatable" bundle info property.
/** @var \Drupal\Core\State\StateInterface $state */
$state = $this->container->get('state');
$this->assertTrue($state->get('content_translation_test.translatable'));
}
/**
* Tests that field synchronization is skipped for disabled bundles.
*/
public function testFieldSynchronizationWithDisabledBundle() {
$entity = EntityTestMul::create();
$entity->save();
/** @var \Drupal\Core\Entity\ContentEntityInterface $translation */
$translation = $entity->addTranslation('it');
$translation->save();
$this->assertTrue($entity->isTranslatable());
}
}

View file

@ -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);
}
}
}

View file

@ -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'];
@ -181,13 +181,21 @@ class ContentTranslationSyncUnitTest extends KernelTestBase {
// their delta.
$delta_callbacks = [
// Continuous field values: all values are equal.
function($delta) { return TRUE; },
function ($delta) {
return TRUE;
},
// Alternated field values: only the even ones are equal.
function($delta) { return $delta % 2 !== 0; },
function ($delta) {
return $delta % 2 !== 0;
},
// Sparse field values: only the "middle" ones are equal.
function($delta) { return $delta === 1 || $delta === 2; },
function ($delta) {
return $delta === 1 || $delta === 2;
},
// Sparse field values: only the "extreme" ones are equal.
function($delta) { return $delta === 0 || $delta === 3; },
function ($delta) {
return $delta === 0 || $delta === 3;
},
];
foreach ($delta_callbacks as $delta_callback) {
@ -241,7 +249,7 @@ class ContentTranslationSyncUnitTest extends KernelTestBase {
for ($delta = 0; $delta < $this->cardinality; $delta++) {
foreach ($this->columns as $column) {
// If the column is synchronized, the value should have been synced,
// for unsychronized columns, the value must not change.
// for unsynchronized columns, the value must not change.
$expected_value = in_array($column, $this->synchronized) ? $changed_items[$delta][$column] : $this->unchangedFieldValues[$langcode][$delta][$column];
$this->assertEqual($field_values[$langcode][$delta][$column], $expected_value, "Differing Item $delta column $column for langcode $langcode synced correctly");
}

View file

@ -0,0 +1,127 @@
<?php
namespace Drupal\Tests\content_translation\Kernel\Migrate\d6;
use Drupal\taxonomy\Entity\Term;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
use Drupal\taxonomy\TermInterface;
/**
* Test migration of translated taxonomy terms.
*
* @group migrate_drupal_6
*/
class MigrateTaxonomyTermTranslationTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'content_translation',
'language',
'menu_ui',
// Required for translation migrations.
'migrate_drupal_multilingual',
'node',
'taxonomy',
];
/**
* The cached taxonomy tree items, keyed by vid and tid.
*
* @var array
*/
protected $treeData = [];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installEntitySchema('taxonomy_term');
$this->installConfig(static::$modules);
$this->executeMigrations([
'd6_node_type',
'd6_field',
'd6_taxonomy_vocabulary',
'd6_field_instance',
'd6_taxonomy_term',
'd6_taxonomy_term_translation',
]);
}
/**
* Validate a migrated term contains the expected values.
*
* @param int $id
* Entity ID to load and check.
* @param string $expected_language
* The language code for this term.
* @param string $expected_label
* The label the migrated entity should have.
* @param string $expected_vid
* The parent vocabulary the migrated entity should have.
* @param string $expected_description
* The description the migrated entity should have.
* @param string $expected_format
* The format the migrated entity should have.
* @param int $expected_weight
* The weight the migrated entity should have.
* @param array $expected_parents
* The parent terms the migrated entity should have.
* @param int $expected_field_integer_value
* The value the migrated entity field should have.
* @param int $expected_term_reference_tid
* The term reference ID the migrated entity field should have.
*/
protected function assertEntity($id, $expected_language, $expected_label, $expected_vid, $expected_description = '', $expected_format = NULL, $expected_weight = 0, $expected_parents = [], $expected_field_integer_value = NULL, $expected_term_reference_tid = NULL) {
/** @var \Drupal\taxonomy\TermInterface $entity */
$entity = Term::load($id);
$this->assertInstanceOf(TermInterface::class, $entity);
$this->assertSame($expected_language, $entity->language()->getId());
$this->assertSame($expected_label, $entity->label());
$this->assertSame($expected_vid, $entity->bundle());
$this->assertSame($expected_description, $entity->getDescription());
$this->assertSame($expected_format, $entity->getFormat());
$this->assertSame($expected_weight, $entity->getWeight());
$this->assertHierarchy($expected_vid, $id, $expected_parents);
}
/**
* Assert that a term is present in the tree storage, with the right parents.
*
* @param string $vid
* Vocabulary ID.
* @param int $tid
* ID of the term to check.
* @param array $parent_ids
* The expected parent term IDs.
*/
protected function assertHierarchy($vid, $tid, array $parent_ids) {
if (!isset($this->treeData[$vid])) {
$tree = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadTree($vid);
$this->treeData[$vid] = [];
foreach ($tree as $item) {
$this->treeData[$vid][$item->tid] = $item;
}
}
$this->assertArrayHasKey($tid, $this->treeData[$vid], "Term $tid exists in taxonomy tree");
$term = $this->treeData[$vid][$tid];
$this->assertEquals($parent_ids, array_filter($term->parents), "Term $tid has correct parents in taxonomy tree");
}
/**
* Tests the Drupal 6 i18n taxonomy term to Drupal 8 migration.
*/
public function testTranslatedTaxonomyTerms() {
$this->assertEntity(1, 'zu', 'zu - term 1 of vocabulary 1', 'vocabulary_1_i_0_', 'zu - description of term 1 of vocabulary 1', NULL, '0', []);
$this->assertEntity(2, 'fr', 'fr - term 2 of vocabulary 2', 'vocabulary_2_i_1_', 'fr - description of term 2 of vocabulary 2', NULL, '3', []);
$this->assertEntity(3, 'fr', 'fr - term 3 of vocabulary 2', 'vocabulary_2_i_1_', 'fr - description of term 3 of vocabulary 2', NULL, '4', ['2']);
$this->assertEntity(4, 'en', 'term 4 of vocabulary 3', 'vocabulary_3_i_2_', 'description of term 4 of vocabulary 3', NULL, '6', []);
$this->assertEntity(5, 'en', 'term 5 of vocabulary 3', 'vocabulary_3_i_2_', 'description of term 5 of vocabulary 3', NULL, '7', ['4']);
$this->assertEntity(6, 'en', 'term 6 of vocabulary 3', 'vocabulary_3_i_2_', 'description of term 6 of vocabulary 3', NULL, '8', ['4', '5']);
$this->assertEntity(7, 'fr', 'fr - term 2 of vocabulary 1', 'vocabulary_1_i_0_', 'fr - desc of term 2 vocab 1', NULL, '0', []);
}
}

View file

@ -0,0 +1,99 @@
<?php
namespace Drupal\Tests\content_translation\Kernel\Migrate\d7;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
/**
* Tests the migration of entity translation settings.
*
* @group migrate_drupal_7
*/
class MigrateEntityTranslationSettingsTest extends MigrateDrupal7TestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'comment',
'content_translation',
'language',
'menu_ui',
// Required for translation migrations.
'migrate_drupal_multilingual',
'node',
'taxonomy',
'text',
'user',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installConfig([
'comment',
'content_translation',
'node',
'taxonomy',
'user',
]);
$this->installEntitySchema('comment');
$this->installEntitySchema('node');
$this->installEntitySchema('taxonomy_term');
$this->installEntitySchema('user');
$this->executeMigrations([
'd7_comment_type',
'd7_node_type',
'd7_taxonomy_vocabulary',
'd7_entity_translation_settings',
]);
}
/**
* Tests entity translation settings migration.
*/
public function testEntityTranslationSettingsMigration() {
// Tests 'comment_node_test_content_type' entity translation settings.
$config = $this->config('language.content_settings.comment.comment_node_test_content_type');
$this->assertSame($config->get('target_entity_type_id'), 'comment');
$this->assertSame($config->get('target_bundle'), 'comment_node_test_content_type');
$this->assertSame($config->get('default_langcode'), 'current_interface');
$this->assertFalse((bool) $config->get('language_alterable'));
$this->assertTrue((bool) $config->get('third_party_settings.content_translation.enabled'));
$this->assertFalse((bool) $config->get('third_party_settings.content_translation.bundle_settings.untranslatable_fields_hide'));
// Tests 'test_content_type' entity translation settings.
$config = $this->config('language.content_settings.node.test_content_type');
$this->assertSame($config->get('target_entity_type_id'), 'node');
$this->assertSame($config->get('target_bundle'), 'test_content_type');
$this->assertSame($config->get('default_langcode'), LanguageInterface::LANGCODE_NOT_SPECIFIED);
$this->assertTrue((bool) $config->get('language_alterable'));
$this->assertTrue((bool) $config->get('third_party_settings.content_translation.enabled'));
$this->assertFalse((bool) $config->get('third_party_settings.content_translation.bundle_settings.untranslatable_fields_hide'));
// Tests 'test_vocabulary' entity translation settings.
$config = $this->config('language.content_settings.taxonomy_term.test_vocabulary');
$this->assertSame($config->get('target_entity_type_id'), 'taxonomy_term');
$this->assertSame($config->get('target_bundle'), 'test_vocabulary');
$this->assertSame($config->get('default_langcode'), LanguageInterface::LANGCODE_SITE_DEFAULT);
$this->assertFalse((bool) $config->get('language_alterable'));
$this->assertTrue((bool) $config->get('third_party_settings.content_translation.enabled'));
$this->assertFalse((bool) $config->get('third_party_settings.content_translation.bundle_settings.untranslatable_fields_hide'));
// Tests 'user' entity translation settings.
$config = $this->config('language.content_settings.user.user');
$this->assertSame($config->get('target_entity_type_id'), 'user');
$this->assertSame($config->get('target_bundle'), 'user');
$this->assertSame($config->get('default_langcode'), LanguageInterface::LANGCODE_SITE_DEFAULT);
$this->assertFalse((bool) $config->get('language_alterable'));
$this->assertTrue((bool) $config->get('third_party_settings.content_translation.enabled'));
$this->assertFalse((bool) $config->get('third_party_settings.content_translation.bundle_settings.untranslatable_fields_hide'));
}
}

View file

@ -0,0 +1,254 @@
<?php
namespace Drupal\Tests\content_translation\Kernel\Plugin\migrate\source\d7;
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
/**
* Tests entity translation settings source plugin.
*
* @covers \Drupal\content_translation\Plugin\migrate\source\d7\EntityTranslationSettings
*
* @group content_translation
*/
class EntityTranslationSettingsTest extends MigrateSqlSourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'content_translation',
'language',
'migrate_drupal',
];
/**
* {@inheritdoc}
*/
public function providerSource() {
$tests = [];
// Source data when there's no entity type that uses entity translation.
$tests[0]['source_data']['variable'] = [
[
'name' => 'entity_translation_entity_types',
'value' => 'a:4:{s:7:"comment";i:0;s:4:"node";i:0;s:13:"taxonomy_term";i:0;s:4:"user";i:0;}',
],
];
// Source data when there's no bundle settings variables.
$tests[1]['source_data']['variable'] = [
[
'name' => 'entity_translation_entity_types',
'value' => 'a:4:{s:7:"comment";s:7:"comment";s:4:"node";s:4:"node";s:13:"taxonomy_term";s:13:"taxonomy_term";s:4:"user";s:4:"user";}',
],
[
'name' => 'entity_translation_taxonomy',
'value' => 'a:3:{s:6:"forums";b:1;s:4:"tags";b:1;s:4:"test";b:0;}',
],
[
'name' => 'language_content_type_article',
'value' => 's:1:"2";',
],
[
'name' => 'language_content_type_forum',
'value' => 's:1:"4";',
],
[
'name' => 'language_content_type_page',
'value' => 's:1:"4";',
],
];
// Source data when there's bundle settings variables.
$tests[2]['source_data']['variable'] = [
[
'name' => 'entity_translation_entity_types',
'value' => 'a:4:{s:7:"comment";s:7:"comment";s:4:"node";s:4:"node";s:13:"taxonomy_term";s:13:"taxonomy_term";s:4:"user";s:4:"user";}',
],
[
'name' => 'entity_translation_settings_comment__comment_node_forum',
'value' => 'a:5:{s:16:"default_language";s:12:"xx-et-author";s:22:"hide_language_selector";i:1;s:21:"exclude_language_none";i:0;s:13:"lock_language";i:0;s:27:"shared_fields_original_only";i:0;}',
],
[
'name' => 'entity_translation_settings_comment__comment_node_page',
'value' => 'a:5:{s:16:"default_language";s:12:"xx-et-author";s:22:"hide_language_selector";i:0;s:21:"exclude_language_none";i:0;s:13:"lock_language";i:0;s:27:"shared_fields_original_only";i:1;}',
],
[
'name' => 'entity_translation_settings_node__forum',
'value' => 'a:5:{s:16:"default_language";s:12:"xx-et-author";s:22:"hide_language_selector";i:0;s:21:"exclude_language_none";i:0;s:13:"lock_language";i:0;s:27:"shared_fields_original_only";i:0;}',
],
[
'name' => 'entity_translation_settings_node__page',
'value' => 'a:5:{s:16:"default_language";s:13:"xx-et-default";s:22:"hide_language_selector";i:1;s:21:"exclude_language_none";i:0;s:13:"lock_language";i:0;s:27:"shared_fields_original_only";i:1;}',
],
[
'name' => 'entity_translation_settings_taxonomy_term__forums',
'value' => 'a:5:{s:16:"default_language";s:13:"xx-et-current";s:22:"hide_language_selector";i:0;s:21:"exclude_language_none";i:0;s:13:"lock_language";i:0;s:27:"shared_fields_original_only";i:1;}',
],
[
'name' => 'entity_translation_settings_taxonomy_term__tags',
'value' => 'a:5:{s:16:"default_language";s:13:"xx-et-current";s:22:"hide_language_selector";i:1;s:21:"exclude_language_none";i:0;s:13:"lock_language";i:0;s:27:"shared_fields_original_only";i:0;}',
],
[
'name' => 'entity_translation_settings_user__user',
'value' => 'a:5:{s:16:"default_language";s:12:"xx-et-author";s:22:"hide_language_selector";i:1;s:21:"exclude_language_none";i:0;s:13:"lock_language";i:0;s:27:"shared_fields_original_only";i:1;}',
],
[
'name' => 'entity_translation_taxonomy',
'value' => 'a:3:{s:6:"forums";b:1;s:4:"tags";b:1;s:4:"test";b:0;}',
],
[
'name' => 'language_content_type_article',
'value' => 's:1:"2";',
],
[
'name' => 'language_content_type_forum',
'value' => 's:1:"4";',
],
[
'name' => 'language_content_type_page',
'value' => 's:1:"4";',
],
];
// Source data when taxonomy terms are translatable but the
// 'entity_translation_taxonomy' variable is not set.
$tests[3]['source_data']['variable'] = [
[
'name' => 'entity_translation_entity_types',
'value' => 'a:4:{s:7:"comment";i:0;s:4:"node";i:0;s:13:"taxonomy_term";i:1;s:4:"user";i:0;}',
],
];
// Expected data when there's no entity type that uses entity translation.
$tests[0]['expected_data'] = [];
// Expected data when there's no bundle settings variables.
$tests[1]['expected_data'] = [
[
'id' => 'node.forum',
'target_entity_type_id' => 'node',
'target_bundle' => 'forum',
'default_langcode' => 'und',
'language_alterable' => TRUE,
'untranslatable_fields_hide' => FALSE,
],
[
'id' => 'node.page',
'target_entity_type_id' => 'node',
'target_bundle' => 'page',
'default_langcode' => 'und',
'language_alterable' => TRUE,
'untranslatable_fields_hide' => FALSE,
],
[
'id' => 'comment.comment_forum',
'target_entity_type_id' => 'comment',
'target_bundle' => 'comment_forum',
'default_langcode' => 'xx-et-current',
'language_alterable' => FALSE,
'untranslatable_fields_hide' => FALSE,
],
[
'id' => 'comment.comment_node_page',
'target_entity_type_id' => 'comment',
'target_bundle' => 'comment_node_page',
'default_langcode' => 'xx-et-current',
'language_alterable' => FALSE,
'untranslatable_fields_hide' => FALSE,
],
[
'id' => 'taxonomy_term.forums',
'target_entity_type_id' => 'taxonomy_term',
'target_bundle' => 'forums',
'default_langcode' => 'xx-et-default',
'language_alterable' => FALSE,
'untranslatable_fields_hide' => FALSE,
],
[
'id' => 'taxonomy_term.tags',
'target_entity_type_id' => 'taxonomy_term',
'target_bundle' => 'tags',
'default_langcode' => 'xx-et-default',
'language_alterable' => FALSE,
'untranslatable_fields_hide' => FALSE,
],
[
'id' => 'user.user',
'target_entity_type_id' => 'user',
'target_bundle' => 'user',
'default_langcode' => 'xx-et-default',
'language_alterable' => FALSE,
'untranslatable_fields_hide' => FALSE,
],
];
// Expected data when there's bundle settings variables.
$tests[2]['expected_data'] = [
[
'id' => 'node.forum',
'target_entity_type_id' => 'node',
'target_bundle' => 'forum',
'default_langcode' => 'xx-et-author',
'language_alterable' => TRUE,
'untranslatable_fields_hide' => FALSE,
],
[
'id' => 'node.page',
'target_entity_type_id' => 'node',
'target_bundle' => 'page',
'default_langcode' => 'xx-et-default',
'language_alterable' => FALSE,
'untranslatable_fields_hide' => TRUE,
],
[
'id' => 'comment.comment_forum',
'target_entity_type_id' => 'comment',
'target_bundle' => 'comment_forum',
'default_langcode' => 'xx-et-author',
'language_alterable' => FALSE,
'untranslatable_fields_hide' => FALSE,
],
[
'id' => 'comment.comment_node_page',
'target_entity_type_id' => 'comment',
'target_bundle' => 'comment_node_page',
'default_langcode' => 'xx-et-author',
'language_alterable' => TRUE,
'untranslatable_fields_hide' => TRUE,
],
[
'id' => 'taxonomy_term.forums',
'target_entity_type_id' => 'taxonomy_term',
'target_bundle' => 'forums',
'default_langcode' => 'xx-et-current',
'language_alterable' => TRUE,
'untranslatable_fields_hide' => TRUE,
],
[
'id' => 'taxonomy_term.tags',
'target_entity_type_id' => 'taxonomy_term',
'target_bundle' => 'tags',
'default_langcode' => 'xx-et-current',
'language_alterable' => FALSE,
'untranslatable_fields_hide' => FALSE,
],
[
'id' => 'user.user',
'target_entity_type_id' => 'user',
'target_bundle' => 'user',
'default_langcode' => 'xx-et-author',
'language_alterable' => FALSE,
'untranslatable_fields_hide' => TRUE,
],
];
// Expected data when taxonomy terms are translatable but the
// 'entity_translation_taxonomy' variable is not set.
$tests[3]['expected_data'] = [];
return $tests;
}
}

View file

@ -49,20 +49,30 @@ class ContentTranslationLocalTasksTest extends LocalTaskIntegrationTestBase {
*/
public function providerTestBlockAdminDisplay() {
return [
['entity.node.canonical', [[
'content_translation.local_tasks:entity.node.content_translation_overview',
[
'entity.node.canonical',
'entity.node.edit_form',
'entity.node.delete_form',
'entity.node.version_history',
]]],
['entity.node.content_translation_overview', [[
'content_translation.local_tasks:entity.node.content_translation_overview',
'entity.node.canonical',
'entity.node.edit_form',
'entity.node.delete_form',
'entity.node.version_history',
]]],
[
[
'content_translation.local_tasks:entity.node.content_translation_overview',
'entity.node.canonical',
'entity.node.edit_form',
'entity.node.delete_form',
'entity.node.version_history',
],
],
],
[
'entity.node.content_translation_overview',
[
[
'content_translation.local_tasks:entity.node.content_translation_overview',
'entity.node.canonical',
'entity.node.edit_form',
'entity.node.delete_form',
'entity.node.version_history',
],
],
],
];
}