upgrades core to 8.4.2
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* @file
|
||||
* Content Translation admin behaviors.
|
||||
*/
|
||||
|
||||
(function ($, Drupal, drupalSettings) {
|
||||
/**
|
||||
* Forces applicable options to be checked as translatable.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*
|
||||
* @prop {Drupal~behaviorAttach} attach
|
||||
* Attaches content translation dependent options to the UI.
|
||||
*/
|
||||
Drupal.behaviors.contentTranslationDependentOptions = {
|
||||
attach(context) {
|
||||
const $context = $(context);
|
||||
const options = drupalSettings.contentTranslationDependentOptions;
|
||||
let $fields;
|
||||
let dependent_columns;
|
||||
|
||||
function fieldsChangeHandler($fields, dependent_columns) {
|
||||
return function (e) {
|
||||
Drupal.behaviors.contentTranslationDependentOptions.check($fields, dependent_columns, $(e.target));
|
||||
};
|
||||
}
|
||||
|
||||
// We're given a generic name to look for so we find all inputs containing
|
||||
// that name and copy over the input values that require all columns to be
|
||||
// translatable.
|
||||
if (options && options.dependent_selectors) {
|
||||
for (const field in options.dependent_selectors) {
|
||||
if (options.dependent_selectors.hasOwnProperty(field)) {
|
||||
$fields = $context.find(`input[name^="${field}"]`);
|
||||
dependent_columns = options.dependent_selectors[field];
|
||||
|
||||
$fields.on('change', fieldsChangeHandler($fields, dependent_columns));
|
||||
Drupal.behaviors.contentTranslationDependentOptions.check($fields, dependent_columns);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
check($fields, dependent_columns, $changed) {
|
||||
let $element = $changed;
|
||||
let column;
|
||||
|
||||
function filterFieldsList(index, field) {
|
||||
return $(field).val() === column;
|
||||
}
|
||||
|
||||
// A field that has many different translatable parts can also define one
|
||||
// or more columns that require all columns to be translatable.
|
||||
for (const index in dependent_columns) {
|
||||
if (dependent_columns.hasOwnProperty(index)) {
|
||||
column = dependent_columns[index];
|
||||
|
||||
if (!$changed) {
|
||||
$element = $fields.filter(filterFieldsList);
|
||||
}
|
||||
|
||||
if ($element.is(`input[value="${column}"]:checked`)) {
|
||||
$fields.prop('checked', true)
|
||||
.not($element).prop('disabled', true);
|
||||
}
|
||||
else {
|
||||
$fields.prop('disabled', false);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Makes field translatability inherit bundle translatability.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*
|
||||
* @prop {Drupal~behaviorAttach} attach
|
||||
* Attaches content translation behavior.
|
||||
*/
|
||||
Drupal.behaviors.contentTranslation = {
|
||||
attach(context) {
|
||||
// Initially hide all field rows for non translatable bundles and all
|
||||
// column rows for non translatable fields.
|
||||
$(context).find('table .bundle-settings .translatable :input').once('translation-entity-admin-hide').each(function () {
|
||||
const $input = $(this);
|
||||
const $bundleSettings = $input.closest('.bundle-settings');
|
||||
if (!$input.is(':checked')) {
|
||||
$bundleSettings.nextUntil('.bundle-settings').hide();
|
||||
}
|
||||
else {
|
||||
$bundleSettings.nextUntil('.bundle-settings', '.field-settings').find('.translatable :input:not(:checked)').closest('.field-settings').nextUntil(':not(.column-settings)').hide();
|
||||
}
|
||||
});
|
||||
|
||||
// When a bundle is made translatable all of its fields should inherit
|
||||
// this setting. Instead when it is made non translatable its fields are
|
||||
// hidden, since their translatability no longer matters.
|
||||
$('body').once('translation-entity-admin-bind').on('click', 'table .bundle-settings .translatable :input', (e) => {
|
||||
const $target = $(e.target);
|
||||
const $bundleSettings = $target.closest('.bundle-settings');
|
||||
const $settings = $bundleSettings.nextUntil('.bundle-settings');
|
||||
const $fieldSettings = $settings.filter('.field-settings');
|
||||
if ($target.is(':checked')) {
|
||||
$bundleSettings.find('.operations :input[name$="[language_alterable]"]').prop('checked', true);
|
||||
$fieldSettings.find('.translatable :input').prop('checked', true);
|
||||
$settings.show();
|
||||
}
|
||||
else {
|
||||
$settings.hide();
|
||||
}
|
||||
})
|
||||
.on('click', 'table .field-settings .translatable :input', (e) => {
|
||||
const $target = $(e.target);
|
||||
const $fieldSettings = $target.closest('.field-settings');
|
||||
const $columnSettings = $fieldSettings.nextUntil('.field-settings, .bundle-settings');
|
||||
if ($target.is(':checked')) {
|
||||
$columnSettings.show();
|
||||
}
|
||||
else {
|
||||
$columnSettings.hide();
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
}(jQuery, Drupal, drupalSettings));
|
||||
@@ -209,7 +209,7 @@ function _content_translation_preprocess_language_content_settings_table(&$varia
|
||||
$rows[] = [
|
||||
'data' => [
|
||||
[
|
||||
'data' => drupal_render($field_element),
|
||||
'data' => \Drupal::service('renderer')->render($field_element),
|
||||
'class' => ['translatable'],
|
||||
],
|
||||
[
|
||||
@@ -243,7 +243,7 @@ function _content_translation_preprocess_language_content_settings_table(&$varia
|
||||
$rows[] = [
|
||||
'data' => [
|
||||
[
|
||||
'data' => drupal_render($column_element[$key]),
|
||||
'data' => \Drupal::service('renderer')->render($column_element[$key]),
|
||||
'class' => ['translatable'],
|
||||
],
|
||||
[
|
||||
|
||||
@@ -1,26 +1,17 @@
|
||||
/**
|
||||
* @file
|
||||
* Content Translation admin behaviors.
|
||||
*/
|
||||
* DO NOT EDIT THIS FILE.
|
||||
* See the following change record for more information,
|
||||
* https://www.drupal.org/node/2815083
|
||||
* @preserve
|
||||
**/
|
||||
|
||||
(function ($, Drupal, drupalSettings) {
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Forces applicable options to be checked as translatable.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*
|
||||
* @prop {Drupal~behaviorAttach} attach
|
||||
* Attaches content translation dependent options to the UI.
|
||||
*/
|
||||
Drupal.behaviors.contentTranslationDependentOptions = {
|
||||
attach: function (context) {
|
||||
attach: function attach(context) {
|
||||
var $context = $(context);
|
||||
var options = drupalSettings.contentTranslationDependentOptions;
|
||||
var $fields;
|
||||
var dependent_columns;
|
||||
var $fields = void 0;
|
||||
var dependent_columns = void 0;
|
||||
|
||||
function fieldsChangeHandler($fields, dependent_columns) {
|
||||
return function (e) {
|
||||
@@ -28,9 +19,6 @@
|
||||
};
|
||||
}
|
||||
|
||||
// We're given a generic name to look for so we find all inputs containing
|
||||
// that name and copy over the input values that require all columns to be
|
||||
// translatable.
|
||||
if (options && options.dependent_selectors) {
|
||||
for (var field in options.dependent_selectors) {
|
||||
if (options.dependent_selectors.hasOwnProperty(field)) {
|
||||
@@ -43,16 +31,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
check: function ($fields, dependent_columns, $changed) {
|
||||
check: function check($fields, dependent_columns, $changed) {
|
||||
var $element = $changed;
|
||||
var column;
|
||||
var column = void 0;
|
||||
|
||||
function filterFieldsList(index, field) {
|
||||
return $(field).val() === column;
|
||||
}
|
||||
|
||||
// A field that has many different translatable parts can also define one
|
||||
// or more columns that require all columns to be translatable.
|
||||
for (var index in dependent_columns) {
|
||||
if (dependent_columns.hasOwnProperty(index)) {
|
||||
column = dependent_columns[index];
|
||||
@@ -62,44 +48,27 @@
|
||||
}
|
||||
|
||||
if ($element.is('input[value="' + column + '"]:checked')) {
|
||||
$fields.prop('checked', true)
|
||||
.not($element).prop('disabled', true);
|
||||
}
|
||||
else {
|
||||
$fields.prop('checked', true).not($element).prop('disabled', true);
|
||||
} else {
|
||||
$fields.prop('disabled', false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Makes field translatability inherit bundle translatability.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*
|
||||
* @prop {Drupal~behaviorAttach} attach
|
||||
* Attaches content translation behavior.
|
||||
*/
|
||||
Drupal.behaviors.contentTranslation = {
|
||||
attach: function (context) {
|
||||
// Initially hide all field rows for non translatable bundles and all
|
||||
// column rows for non translatable fields.
|
||||
attach: function attach(context) {
|
||||
$(context).find('table .bundle-settings .translatable :input').once('translation-entity-admin-hide').each(function () {
|
||||
var $input = $(this);
|
||||
var $bundleSettings = $input.closest('.bundle-settings');
|
||||
if (!$input.is(':checked')) {
|
||||
$bundleSettings.nextUntil('.bundle-settings').hide();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$bundleSettings.nextUntil('.bundle-settings', '.field-settings').find('.translatable :input:not(:checked)').closest('.field-settings').nextUntil(':not(.column-settings)').hide();
|
||||
}
|
||||
});
|
||||
|
||||
// When a bundle is made translatable all of its fields should inherit
|
||||
// this setting. Instead when it is made non translatable its fields are
|
||||
// hidden, since their translatability no longer matters.
|
||||
$('body').once('translation-entity-admin-bind').on('click', 'table .bundle-settings .translatable :input', function (e) {
|
||||
var $target = $(e.target);
|
||||
var $bundleSettings = $target.closest('.bundle-settings');
|
||||
@@ -109,23 +78,19 @@
|
||||
$bundleSettings.find('.operations :input[name$="[language_alterable]"]').prop('checked', true);
|
||||
$fieldSettings.find('.translatable :input').prop('checked', true);
|
||||
$settings.show();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$settings.hide();
|
||||
}
|
||||
})
|
||||
.on('click', 'table .field-settings .translatable :input', function (e) {
|
||||
var $target = $(e.target);
|
||||
var $fieldSettings = $target.closest('.field-settings');
|
||||
var $columnSettings = $fieldSettings.nextUntil('.field-settings, .bundle-settings');
|
||||
if ($target.is(':checked')) {
|
||||
$columnSettings.show();
|
||||
}
|
||||
else {
|
||||
$columnSettings.hide();
|
||||
}
|
||||
});
|
||||
}).on('click', 'table .field-settings .translatable :input', function (e) {
|
||||
var $target = $(e.target);
|
||||
var $fieldSettings = $target.closest('.field-settings');
|
||||
var $columnSettings = $fieldSettings.nextUntil('.field-settings, .bundle-settings');
|
||||
if ($target.is(':checked')) {
|
||||
$columnSettings.show();
|
||||
} else {
|
||||
$columnSettings.hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery, Drupal, drupalSettings);
|
||||
})(jQuery, Drupal, drupalSettings);
|
||||
@@ -8,8 +8,8 @@ package: Multilingual
|
||||
# core: 8.x
|
||||
configure: language.content_settings_page
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-08-16
|
||||
version: '8.3.7'
|
||||
# Information added by Drupal.org packaging script on 2017-11-03
|
||||
version: '8.4.2'
|
||||
core: '8.x'
|
||||
project: 'drupal'
|
||||
datestamp: 1502903957
|
||||
datestamp: 1509719929
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
* Installation functions for Content Translation module.
|
||||
*/
|
||||
|
||||
use \Drupal\Core\Url;
|
||||
use Drupal\Core\Entity\Sql\SqlEntityStorageInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Implements hook_install().
|
||||
@@ -44,3 +46,57 @@ function content_translation_update_8001() {
|
||||
function content_translation_update_8002() {
|
||||
\Drupal::service('plugin.manager.field.field_type')->clearCachedDefinitions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix the initial values for content translation metadata fields.
|
||||
*/
|
||||
function content_translation_update_8400() {
|
||||
$database = \Drupal::database();
|
||||
/** @var \Drupal\content_translation\ContentTranslationManagerInterface $content_translation_manager */
|
||||
$content_translation_manager = \Drupal::service('content_translation.manager');
|
||||
/** @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface $last_installed_schema_repository */
|
||||
$last_installed_schema_repository = \Drupal::service('entity.last_installed_schema.repository');
|
||||
$entity_type_manager = \Drupal::entityTypeManager();
|
||||
$entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager();
|
||||
|
||||
foreach ($content_translation_manager->getSupportedEntityTypes() as $entity_type_id => $entity_type_definition) {
|
||||
$storage = $entity_type_manager->getStorage($entity_type_id);
|
||||
if ($storage instanceof SqlEntityStorageInterface) {
|
||||
$entity_type = $entity_definition_update_manager->getEntityType($entity_type_id);
|
||||
$storage_definitions = $last_installed_schema_repository->getLastInstalledFieldStorageDefinitions($entity_type_id);
|
||||
|
||||
// Since the entity type is managed by Content Translation, we can assume
|
||||
// that it is translatable, so we use the data and revision data tables.
|
||||
$tables_to_update = [$entity_type->getDataTable()];
|
||||
if ($entity_type->isRevisionable()) {
|
||||
$tables_to_update += [$entity_type->getRevisionDataTable()];
|
||||
}
|
||||
|
||||
foreach ($tables_to_update as $table_name) {
|
||||
// Fix the values of the 'content_translation_source' field.
|
||||
if (isset($storage_definitions['content_translation_source'])) {
|
||||
$database->update($table_name)
|
||||
->fields(['content_translation_source' => LanguageInterface::LANGCODE_NOT_SPECIFIED])
|
||||
->isNull('content_translation_source')
|
||||
->execute();
|
||||
}
|
||||
|
||||
// Fix the values of the 'content_translation_outdated' field.
|
||||
if (isset($storage_definitions['content_translation_outdated'])) {
|
||||
$database->update($table_name)
|
||||
->fields(['content_translation_outdated' => 0])
|
||||
->isNull('content_translation_outdated')
|
||||
->execute();
|
||||
}
|
||||
|
||||
// Fix the values of the 'content_translation_status' field.
|
||||
if (isset($storage_definitions['content_translation_status'])) {
|
||||
$database->update($table_name)
|
||||
->fields(['content_translation_status' => 1])
|
||||
->isNull('content_translation_status')
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,6 +116,7 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
|
||||
->setLabel(t('Translation source'))
|
||||
->setDescription(t('The source language from which this translation was created.'))
|
||||
->setDefaultValue(LanguageInterface::LANGCODE_NOT_SPECIFIED)
|
||||
->setInitialValue(LanguageInterface::LANGCODE_NOT_SPECIFIED)
|
||||
->setRevisionable(TRUE)
|
||||
->setTranslatable(TRUE);
|
||||
|
||||
@@ -123,6 +124,7 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
|
||||
->setLabel(t('Translation outdated'))
|
||||
->setDescription(t('A boolean indicating whether this translation needs to be updated.'))
|
||||
->setDefaultValue(FALSE)
|
||||
->setInitialValue(FALSE)
|
||||
->setRevisionable(TRUE)
|
||||
->setTranslatable(TRUE);
|
||||
|
||||
@@ -142,6 +144,7 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
|
||||
->setLabel(t('Translation status'))
|
||||
->setDescription(t('A boolean indicating whether the translation is visible to non-translators.'))
|
||||
->setDefaultValue(TRUE)
|
||||
->setInitialValue(TRUE)
|
||||
->setRevisionable(TRUE)
|
||||
->setTranslatable(TRUE);
|
||||
}
|
||||
|
||||
@@ -196,14 +196,16 @@ class ContentTranslationController extends ControllerBase {
|
||||
if (isset($links['edit'])) {
|
||||
$links['edit']['title'] = $this->t('Edit');
|
||||
}
|
||||
$status = ['data' => [
|
||||
'#type' => 'inline_template',
|
||||
'#template' => '<span class="status">{% if status %}{{ "Published"|t }}{% else %}{{ "Not published"|t }}{% endif %}</span>{% if outdated %} <span class="marker">{{ "outdated"|t }}</span>{% endif %}',
|
||||
'#context' => [
|
||||
'status' => $metadata->isPublished(),
|
||||
'outdated' => $metadata->isOutdated(),
|
||||
$status = [
|
||||
'data' => [
|
||||
'#type' => 'inline_template',
|
||||
'#template' => '<span class="status">{% if status %}{{ "Published"|t }}{% else %}{{ "Not published"|t }}{% endif %}</span>{% if outdated %} <span class="marker">{{ "outdated"|t }}</span>{% endif %}',
|
||||
'#context' => [
|
||||
'status' => $metadata->isPublished(),
|
||||
'outdated' => $metadata->isOutdated(),
|
||||
],
|
||||
],
|
||||
]];
|
||||
];
|
||||
|
||||
if ($is_original) {
|
||||
$language_name = $this->t('<strong>@language_name (Original language)</strong>', ['@language_name' => $language_name]);
|
||||
|
||||
+3
-3
@@ -9,8 +9,8 @@ dependencies:
|
||||
- language
|
||||
- entity_test
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-08-16
|
||||
version: '8.3.7'
|
||||
# Information added by Drupal.org packaging script on 2017-11-03
|
||||
version: '8.4.2'
|
||||
core: '8.x'
|
||||
project: 'drupal'
|
||||
datestamp: 1502903957
|
||||
datestamp: 1509719929
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* Helper module for the Content Translation tests.
|
||||
*/
|
||||
|
||||
use \Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Implements hook_form_BASE_FORM_ID_alter().
|
||||
|
||||
+3
-3
@@ -8,8 +8,8 @@ dependencies:
|
||||
- content_translation
|
||||
- views
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-08-16
|
||||
version: '8.3.7'
|
||||
# Information added by Drupal.org packaging script on 2017-11-03
|
||||
version: '8.4.2'
|
||||
core: '8.x'
|
||||
project: 'drupal'
|
||||
datestamp: 1502903957
|
||||
datestamp: 1509719929
|
||||
|
||||
+127
@@ -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.');
|
||||
}
|
||||
|
||||
}
|
||||
+6
-6
@@ -73,12 +73,12 @@ class ContentTranslationLanguageChangeTest extends NodeTestBase {
|
||||
$edit = [
|
||||
'title[0][value]' => 'english_title',
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save and publish'));
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
|
||||
// Create a translation in French.
|
||||
$this->clickLink('Translate');
|
||||
$this->clickLink('Add');
|
||||
$this->drupalPostForm(NULL, [], t('Save and keep published (this translation)'));
|
||||
$this->drupalPostForm(NULL, [], t('Save (this translation)'));
|
||||
$this->clickLink('Translate');
|
||||
|
||||
// Edit English translation.
|
||||
@@ -90,7 +90,7 @@ class ContentTranslationLanguageChangeTest extends NodeTestBase {
|
||||
'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 and keep published (this translation)'));
|
||||
$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');
|
||||
@@ -109,13 +109,13 @@ class ContentTranslationLanguageChangeTest extends NodeTestBase {
|
||||
'title[0][value]' => 'english_title',
|
||||
'test_field_only_en_fr' => 'node created',
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save and publish'));
|
||||
$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 and keep published (this translation)'));
|
||||
$this->drupalPostForm(NULL, [], t('Save (this translation)'));
|
||||
$this->clickLink('Translate');
|
||||
|
||||
// Edit English translation.
|
||||
@@ -137,7 +137,7 @@ class ContentTranslationLanguageChangeTest extends NodeTestBase {
|
||||
'langcode[0][value]' => 'en',
|
||||
'field_image_field[0][alt]' => 'alternative_text'
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save and keep published (this translation)'));
|
||||
$this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
|
||||
|
||||
// Check that the translation languages are correct.
|
||||
$node = $this->getNodeByTitle('english_title');
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ 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.
|
||||
|
||||
+259
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_translation\Functional;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
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'], SafeMarkup::format('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'], SafeMarkup::format('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'], SafeMarkup::format('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'], SafeMarkup::format('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'], SafeMarkup::format('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'], SafeMarkup::format('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.');
|
||||
}
|
||||
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_translation\Functional\Update;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
|
||||
use Drupal\system\Tests\Entity\EntityDefinitionTestTrait;
|
||||
|
||||
/**
|
||||
* Tests the upgrade path for the Content Translation module.
|
||||
*
|
||||
* @group Update
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_translation\FunctionalJavascript;
|
||||
|
||||
use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
|
||||
/**
|
||||
* Tests that contextual links are available for content translation.
|
||||
*
|
||||
* @group content_translation
|
||||
*/
|
||||
class ContentTranslationContextualLinksTest extends JavascriptTestBase {
|
||||
|
||||
/**
|
||||
* 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'));
|
||||
}
|
||||
|
||||
}
|
||||
+12
-4
@@ -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) {
|
||||
|
||||
+23
-13
@@ -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',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user