updated core and modules

This commit is contained in:
Bachir Soussi Chiadmi
2017-09-09 16:27:51 +02:00
parent 027aa99b32
commit 41863f872c
7810 changed files with 318922 additions and 83631 deletions
@@ -29,10 +29,10 @@ use Drupal\Core\Render\Element;
function content_translation_field_sync_widget(FieldDefinitionInterface $field, $element_name = 'third_party_settings[content_translation][translation_sync]') {
// No way to store field sync information on this field.
if (!($field instanceof ThirdPartySettingsInterface)) {
return array();
return [];
}
$element = array();
$element = [];
$definition = \Drupal::service('plugin.manager.field.field_type')->getDefinition($field->getType());
$column_groups = $definition['column_groups'];
if (!empty($column_groups) && count($column_groups) > 1) {
@@ -50,12 +50,12 @@ function content_translation_field_sync_widget(FieldDefinitionInterface $field,
$default = $field->getThirdPartySetting('content_translation', 'translation_sync', $default);
$element = array(
$element = [
'#type' => 'checkboxes',
'#title' => t('Translatable elements'),
'#options' => $options,
'#default_value' => $default,
);
];
if ($require_all_groups_for_translation) {
// The actual checkboxes are sometimes rendered separately and the parent
@@ -93,31 +93,32 @@ function _content_translation_form_language_content_settings_form_alter(array &$
$form['#attached']['library'][] = 'content_translation/drupal.content_translation.admin';
$entity_manager = Drupal::entityManager();
$bundle_info_service = \Drupal::service('entity_type.bundle.info');
foreach ($form['#labels'] as $entity_type_id => $label) {
$entity_type = $entity_manager->getDefinition($entity_type_id);
$storage_definitions = $entity_type instanceof ContentEntityTypeInterface ? $entity_manager->getFieldStorageDefinitions($entity_type_id) : array();
$storage_definitions = $entity_type instanceof ContentEntityTypeInterface ? $entity_manager->getFieldStorageDefinitions($entity_type_id) : [];
$entity_type_translatable = $content_translation_manager->isSupported($entity_type_id);
foreach (entity_get_bundles($entity_type_id) as $bundle => $bundle_info) {
foreach ($bundle_info_service->getBundleInfo($entity_type_id) as $bundle => $bundle_info) {
// Here we do not want the widget to be altered and hold also the "Enable
// translation" checkbox, which would be redundant. Hence we add this key
// to be able to skip alterations. Alter the title and display the message
// about UI integration.
$form['settings'][$entity_type_id][$bundle]['settings']['language']['#content_translation_skip_alter'] = TRUE;
if (!$entity_type_translatable) {
$form['settings'][$entity_type_id]['#title'] = t('@label (Translation is not supported).', array('@label' => $entity_type->getLabel()));
$form['settings'][$entity_type_id]['#title'] = t('@label (Translation is not supported).', ['@label' => $entity_type->getLabel()]);
continue;
}
$fields = $entity_manager->getFieldDefinitions($entity_type_id, $bundle);
if ($fields) {
foreach ($fields as $field_name => $definition) {
if (!empty($storage_definitions[$field_name]) && _content_translation_is_field_translatability_configurable($entity_type, $storage_definitions[$field_name])) {
$form['settings'][$entity_type_id][$bundle]['fields'][$field_name] = array(
if ($definition->isComputed() || (!empty($storage_definitions[$field_name]) && _content_translation_is_field_translatability_configurable($entity_type, $storage_definitions[$field_name]))) {
$form['settings'][$entity_type_id][$bundle]['fields'][$field_name] = [
'#label' => $definition->getLabel(),
'#type' => 'checkbox',
'#default_value' => $definition->isTranslatable(),
);
];
// Display the column translatability configuration widget.
$column_element = content_translation_field_sync_widget($definition, "settings[{$entity_type_id}][{$bundle}][columns][{$field_name}]");
if ($column_element) {
@@ -128,10 +129,10 @@ function _content_translation_form_language_content_settings_form_alter(array &$
if (!empty($form['settings'][$entity_type_id][$bundle]['fields'])) {
// Only show the checkbox to enable translation if the bundles in the
// entity might have fields and if there are fields to translate.
$form['settings'][$entity_type_id][$bundle]['translatable'] = array(
$form['settings'][$entity_type_id][$bundle]['translatable'] = [
'#type' => 'checkbox',
'#default_value' => $content_translation_manager->isEnabled($entity_type_id, $bundle),
);
];
}
}
}
@@ -176,102 +177,102 @@ function _content_translation_preprocess_language_content_settings_table(&$varia
$element = $variables['element'];
$build = &$variables['build'];
array_unshift($build['#header'], array('data' => t('Translatable'), 'class' => array('translatable')));
$rows = array();
array_unshift($build['#header'], ['data' => t('Translatable'), 'class' => ['translatable']]);
$rows = [];
foreach (Element::children($element) as $bundle) {
$field_names = !empty($element[$bundle]['fields']) ? Element::children($element[$bundle]['fields']) : array();
$field_names = !empty($element[$bundle]['fields']) ? Element::children($element[$bundle]['fields']) : [];
if (!empty($element[$bundle]['translatable'])) {
$checkbox_id = $element[$bundle]['translatable']['#id'];
}
$rows[$bundle] = $build['#rows'][$bundle];
if (!empty($element[$bundle]['translatable'])) {
$translatable = array(
$translatable = [
'data' => $element[$bundle]['translatable'],
'class' => array('translatable'),
);
'class' => ['translatable'],
];
array_unshift($rows[$bundle]['data'], $translatable);
$rows[$bundle]['data'][1]['data']['#prefix'] = '<label for="' . $checkbox_id . '">';
}
else {
$translatable = array(
$translatable = [
'data' => t('N/A'),
'class' => array('untranslatable'),
);
'class' => ['untranslatable'],
];
array_unshift($rows[$bundle]['data'], $translatable);
}
foreach ($field_names as $field_name) {
$field_element = &$element[$bundle]['fields'][$field_name];
$rows[] = array(
'data' => array(
array(
$rows[] = [
'data' => [
[
'data' => drupal_render($field_element),
'class' => array('translatable'),
),
array(
'data' => array(
'class' => ['translatable'],
],
[
'data' => [
'#prefix' => '<label for="' . $field_element['#id'] . '">',
'#suffix' => '</label>',
'bundle' => array(
'bundle' => [
'#prefix' => '<span class="visually-hidden">',
'#suffix' => '</span> ',
'#plain_text' => $element[$bundle]['settings']['#label'],
),
'field' => array(
],
'field' => [
'#plain_text' => $field_element['#label'],
),
),
'class' => array('field'),
),
array(
],
],
'class' => ['field'],
],
[
'data' => '',
'class' => array('operations'),
),
),
'class' => array('field-settings'),
);
'class' => ['operations'],
],
],
'class' => ['field-settings'],
];
if (!empty($element[$bundle]['columns'][$field_name])) {
$column_element = &$element[$bundle]['columns'][$field_name];
foreach (Element::children($column_element) as $key) {
$column_label = $column_element[$key]['#title'];
unset($column_element[$key]['#title']);
$rows[] = array(
'data' => array(
array(
$rows[] = [
'data' => [
[
'data' => drupal_render($column_element[$key]),
'class' => array('translatable'),
),
array(
'data' => array(
'class' => ['translatable'],
],
[
'data' => [
'#prefix' => '<label for="' . $column_element[$key]['#id'] . '">',
'#suffix' => '</label>',
'bundle' => array(
'bundle' => [
'#prefix' => '<span class="visually-hidden">',
'#suffix' => '</span> ',
'#plain_text' => $element[$bundle]['settings']['#label'],
),
'field' => array(
],
'field' => [
'#prefix' => '<span class="visually-hidden">',
'#suffix' => '</span> ',
'#plain_text' => $field_element['#label'],
),
'columns' => array(
],
'columns' => [
'#plain_text' => $column_label,
),
),
'class' => array('column'),
),
array(
],
],
'class' => ['column'],
],
[
'data' => '',
'class' => array('operations'),
),
),
'class' => array('column-settings'),
);
'class' => ['operations'],
],
],
'class' => ['column-settings'],
];
}
}
}
@@ -294,7 +295,7 @@ function content_translation_form_language_content_settings_validate(array $form
$translatable_fields = isset($settings[$entity_type][$bundle]['fields']) ? array_filter($settings[$entity_type][$bundle]['fields']) : FALSE;
if (empty($translatable_fields)) {
$t_args = array('%bundle' => $form['settings'][$entity_type][$bundle]['settings']['#label']);
$t_args = ['%bundle' => $form['settings'][$entity_type][$bundle]['settings']['#label']];
$form_state->setErrorByName($name, t('At least one field needs to be translatable to enable %bundle for translation.', $t_args));
}
@@ -303,7 +304,7 @@ function content_translation_form_language_content_settings_validate(array $form
foreach (\Drupal::languageManager()->getLanguages(LanguageInterface::STATE_LOCKED) as $language) {
$locked_languages[] = $language->getName();
}
$form_state->setErrorByName($name, t('Translation is not supported if language is always one of: @locked_languages', array('@locked_languages' => implode(', ', $locked_languages))));
$form_state->setErrorByName($name, t('Translation is not supported if language is always one of: @locked_languages', ['@locked_languages' => implode(', ', $locked_languages)]));
}
}
}
@@ -8,8 +8,8 @@ package: Multilingual
# core: 8.x
configure: language.content_settings_page
# Information added by Drupal.org packaging script on 2016-08-03
version: '8.1.8'
# Information added by Drupal.org packaging script on 2017-08-16
version: '8.3.7'
core: '8.x'
project: 'drupal'
datestamp: 1470233792
datestamp: 1502903957
@@ -31,11 +31,6 @@ function content_translation_install() {
drupal_set_message($message, 'warning');
}
/**
* @addtogroup updates-8.0.0-rc
* @{
*/
/**
* Rebuild the routes as the content translation routes have now new names.
*/
@@ -43,22 +38,9 @@ function content_translation_update_8001() {
\Drupal::service('router.builder')->rebuild();
}
/**
* @} End of "addtogroup updates-8.0.0-rc".
*/
/**
* @addtogroup updates-8.0.x
* @{
*/
/**
* Clear field type plugin caches to fix image field translatability.
*/
function content_translation_update_8002() {
\Drupal::service('plugin.manager.field.field_type')->clearCachedDefinitions();
}
/**
* @} End of "addtogroup updates-8.0.x".
*/
@@ -23,11 +23,11 @@ function content_translation_help($route_name, RouteMatchInterface $route_match)
case 'help.page.content_translation':
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('The Content Translation module allows you to translate content, comments, custom blocks, taxonomy terms, users and other <a href=":field_help" title="Field module help, with background on content entities">content entities</a>. Together with the modules <a href=":language">Language</a>, <a href=":config-trans">Configuration Translation</a>, and <a href=":locale">Interface Translation</a>, it allows you to build multilingual websites. For more information, see the <a href=":translation-entity">online documentation for the Content Translation module</a>.', array(':locale' => (\Drupal::moduleHandler()->moduleExists('locale')) ? \Drupal::url('help.page', array('name' => 'locale')) : '#', ':config-trans' => (\Drupal::moduleHandler()->moduleExists('config_translation')) ? \Drupal::url('help.page', array('name' => 'config_translation')) : '#', ':language' => \Drupal::url('help.page', array('name' => 'language')), ':translation-entity' => 'https://www.drupal.org/documentation/modules/translation', ':field_help' => \Drupal::url('help.page', array('name' => 'field')))) . '</p>';
$output .= '<p>' . t('The Content Translation module allows you to translate content, comments, custom blocks, taxonomy terms, users and other <a href=":field_help" title="Field module help, with background on content entities">content entities</a>. Together with the modules <a href=":language">Language</a>, <a href=":config-trans">Configuration Translation</a>, and <a href=":locale">Interface Translation</a>, it allows you to build multilingual websites. For more information, see the <a href=":translation-entity">online documentation for the Content Translation module</a>.', [':locale' => (\Drupal::moduleHandler()->moduleExists('locale')) ? \Drupal::url('help.page', ['name' => 'locale']) : '#', ':config-trans' => (\Drupal::moduleHandler()->moduleExists('config_translation')) ? \Drupal::url('help.page', ['name' => 'config_translation']) : '#', ':language' => \Drupal::url('help.page', ['name' => 'language']), ':translation-entity' => 'https://www.drupal.org/documentation/modules/translation', ':field_help' => \Drupal::url('help.page', ['name' => 'field'])]) . '</p>';
$output .= '<h3>' . t('Uses') . '</h3>';
$output .= '<dl>';
$output .= '<dt>' . t('Enabling translation') . '</dt>';
$output .= '<dd>' . t('In order to translate content, the website must have at least two <a href=":url">languages</a>. When that is the case, you can enable translation for the desired content entities on the <a href=":translation-entity">Content language</a> page. When enabling translation you can choose the default language for content and decide whether to show the language selection field on the content editing forms.', array(':url' => \Drupal::url('entity.configurable_language.collection'), ':translation-entity' => \Drupal::url('language.content_settings_page'), ':language-help' => \Drupal::url('help.page', array('name' => 'language')))) . '</dd>';
$output .= '<dd>' . t('In order to translate content, the website must have at least two <a href=":url">languages</a>. When that is the case, you can enable translation for the desired content entities on the <a href=":translation-entity">Content language</a> page. When enabling translation you can choose the default language for content and decide whether to show the language selection field on the content editing forms.', [':url' => \Drupal::url('entity.configurable_language.collection'), ':translation-entity' => \Drupal::url('language.content_settings_page'), ':language-help' => \Drupal::url('help.page', ['name' => 'language'])]) . '</dd>';
$output .= '<dt>' . t('Enabling field translation') . '</dt>';
$output .= '<dd>' . t('You can define which fields of a content entity can be translated. For example, you might want to translate the title and body field while leaving the image field untranslated. If you exclude a field from being translated, it will still show up in the content editing form, but any changes made to that field will be applied to <em>all</em> translations of that content.') . '</dd>';
$output .= '<dt>' . t('Translating content') . '</dt>';
@@ -42,7 +42,7 @@ function content_translation_help($route_name, RouteMatchInterface $route_match)
case 'language.content_settings_page':
$output = '';
if (!\Drupal::languageManager()->isMultilingual()) {
$output .= '<br/>' . t('Before you can translate content, there must be at least two languages added on the <a href=":url">languages administration</a> page.', array(':url' => \Drupal::url('entity.configurable_language.collection')));
$output .= '<p>' . t('Before you can translate content, there must be at least two languages added on the <a href=":url">languages administration</a> page.', [':url' => \Drupal::url('entity.configurable_language.collection')]) . '</p>';
}
return $output;
}
@@ -134,7 +134,7 @@ function content_translation_entity_type_alter(array &$entity_types) {
$translation = $entity_type->get('translation');
if (!$translation || !isset($translation['content_translation'])) {
$translation['content_translation'] = array();
$translation['content_translation'] = [];
}
if ($entity_type->hasLinkTemplate('canonical')) {
@@ -148,9 +148,9 @@ function content_translation_entity_type_alter(array &$entity_types) {
}
// @todo Remove this as soon as menu access checks rely on the
// controller. See https://www.drupal.org/node/2155787.
$translation['content_translation'] += array(
$translation['content_translation'] += [
'access_callback' => 'content_translation_translate_access',
);
];
}
$entity_type->set('translation', $translation);
}
@@ -215,7 +215,7 @@ function content_translation_field_info_alter(&$info) {
foreach ($info as $key => $settings) {
// Supply the column_groups key if it's not there.
if (empty($settings['column_groups'])) {
$info[$key]['column_groups'] = array();
$info[$key]['column_groups'] = [];
}
}
}
@@ -224,13 +224,13 @@ function content_translation_field_info_alter(&$info) {
* Implements hook_entity_operation().
*/
function content_translation_entity_operation(EntityInterface $entity) {
$operations = array();
$operations = [];
if ($entity->hasLinkTemplate('drupal:content-translation-overview') && content_translation_translate_access($entity)->isAllowed()) {
$operations['translate'] = array(
$operations['translate'] = [
'title' => t('Translate'),
'url' => $entity->urlInfo('drupal:content-translation-overview'),
'weight' => 50,
);
];
}
return $operations;
}
@@ -296,8 +296,10 @@ function content_translation_form_alter(array &$form, FormStateInterface $form_s
$entity = $form_object->getEntity();
$op = $form_object->getOperation();
// Let the content translation handler alter the content entity edit form.
if ($entity instanceof ContentEntityInterface && $entity->isTranslatable() && count($entity->getTranslationLanguages()) > 1 && ($op == 'edit' || $op == 'default')) {
// Let the content translation handler alter the content entity form. This can
// be the 'add' or 'edit' form. It also tries a 'default' form in case neither
// of the aforementioned forms are defined.
if ($entity instanceof ContentEntityInterface && $entity->isTranslatable() && count($entity->getTranslationLanguages()) > 1 && in_array($op, ['edit', 'add', 'default'], TRUE)) {
$controller = \Drupal::entityManager()->getHandler($entity->getEntityTypeId(), 'translation');
$controller->entityFormAlter($form, $form_state, $entity);
@@ -350,16 +352,16 @@ function content_translation_language_fallback_candidates_entity_view_alter(&$ca
* Implements hook_entity_extra_field_info().
*/
function content_translation_entity_extra_field_info() {
$extra = array();
$extra = [];
$bundle_info_service = \Drupal::service('entity_type.bundle.info');
foreach (\Drupal::entityManager()->getDefinitions() as $entity_type => $info) {
foreach (entity_get_bundles($entity_type) as $bundle => $bundle_info) {
foreach ($bundle_info_service->getBundleInfo($entity_type) as $bundle => $bundle_info) {
if (\Drupal::service('content_translation.manager')->isEnabled($entity_type, $bundle)) {
$extra[$entity_type][$bundle]['form']['translation'] = array(
$extra[$entity_type][$bundle]['form']['translation'] = [
'label' => t('Translation'),
'description' => t('Translation settings'),
'weight' => 10,
);
];
}
}
}
@@ -374,23 +376,23 @@ function content_translation_form_field_config_edit_form_alter(array &$form, For
$field = $form_state->getFormObject()->getEntity();
$bundle_is_translatable = \Drupal::service('content_translation.manager')->isEnabled($field->getTargetEntityTypeId(), $field->getTargetBundle());
$form['translatable'] = array(
$form['translatable'] = [
'#type' => 'checkbox',
'#title' => t('Users may translate this field'),
'#default_value' => $field->isTranslatable(),
'#weight' => -1,
'#disabled' => !$bundle_is_translatable,
'#access' => $field->getFieldStorageDefinition()->isTranslatable(),
);
];
// Provide helpful pointers for administrators.
if (\Drupal::currentUser()->hasPermission('administer content translation') && !$bundle_is_translatable) {
$toggle_url = \Drupal::url('language.content_settings_page', array(), array(
$toggle_url = \Drupal::url('language.content_settings_page', [], [
'query' => \Drupal::destination()->getAsArray(),
));
$form['translatable']['#description'] = t('To configure translation for this field, <a href=":language-settings-url">enable language support</a> for this type.', array(
]);
$form['translatable']['#description'] = t('To configure translation for this field, <a href=":language-settings-url">enable language support</a> for this type.', [
':language-settings-url' => $toggle_url,
));
]);
}
if ($field->isTranslatable()) {
@@ -412,7 +414,8 @@ function content_translation_entity_presave(EntityInterface $entity) {
// as original language, since source values are the only ones available to
// compare against.
if (!isset($entity->original)) {
$entity->original = entity_load_unchanged($entity->entityType(), $entity->id());
$entity->original = \Drupal::entityTypeManager()
->getStorage($entity->entityType())->loadUnchanged($entity->id());
}
$langcode = $entity->language()->getId();
/** @var \Drupal\content_translation\ContentTranslationManagerInterface $manager */
@@ -453,7 +456,7 @@ function content_translation_enable_widget($entity_type, $bundle, array &$form,
$context = $form_state->get(['language', $key]) ?: [];
$context += ['entity_type' => $entity_type, 'bundle' => $bundle];
$form_state->set(['language', $key], $context);
$element = content_translation_language_configuration_element_process(array('#name' => $key), $form_state, $form);
$element = content_translation_language_configuration_element_process(['#name' => $key], $form_state, $form);
unset($element['content_translation']['#element_validate']);
return $element;
}
@@ -473,14 +476,14 @@ function content_translation_language_configuration_element_process(array $eleme
$form_state->set(['content_translation', 'key'], $key);
$context = $form_state->get(['language', $key]);
$element['content_translation'] = array(
$element['content_translation'] = [
'#type' => 'checkbox',
'#title' => t('Enable translation'),
// For new bundle, we don't know the bundle name yet,
// default to no translatability.
'#default_value' => $context['bundle'] ? \Drupal::service('content_translation.manager')->isEnabled($context['entity_type'], $context['bundle']) : FALSE,
'#element_validate' => array('content_translation_language_configuration_element_validate'),
);
'#element_validate' => ['content_translation_language_configuration_element_validate'],
];
$submit_name = isset($form['actions']['save_continue']) ? 'save_continue' : 'submit';
// Only add the submit handler on the submit button if the #submit property
@@ -514,7 +517,7 @@ function content_translation_language_configuration_element_validate($element, F
// @todo Set the correct form element name as soon as the element parents
// are correctly set. We should be using NestedArray::getValue() but for
// now we cannot.
$form_state->setErrorByName('', t('"Show language selector" is not compatible with translating content that has default language: %choice. Either do not hide the language selector or pick a specific language.', array('%choice' => $locked_languages[$values['langcode']])));
$form_state->setErrorByName('', t('"Show language selector" is not compatible with translating content that has default language: %choice. Either do not hide the language selector or pick a specific language.', ['%choice' => $locked_languages[$values['langcode']]]));
}
}
@@ -528,7 +531,7 @@ function content_translation_language_configuration_element_validate($element, F
function content_translation_language_configuration_element_submit(array $form, FormStateInterface $form_state) {
$key = $form_state->get(['content_translation', 'key']);
$context = $form_state->get(['language', $key]);
$enabled = $form_state->getValue(array($key, 'content_translation'));
$enabled = $form_state->getValue([$key, 'content_translation']);
if (\Drupal::service('content_translation.manager')->isEnabled($context['entity_type'], $context['bundle']) != $enabled) {
\Drupal::service('content_translation.manager')->setEnabled($context['entity_type'], $context['bundle'], $enabled);
@@ -571,21 +574,21 @@ function content_translation_page_attachments(&$page) {
}
$entity = $route_match->getParameter($name);
if ($entity instanceof ContentEntityInterface) {
if ($entity instanceof ContentEntityInterface && $entity->hasLinkTemplate('canonical')) {
// Current route represents a content entity. Build hreflang links.
foreach ($entity->getTranslationLanguages() as $language) {
$url = $entity->urlInfo()
$url = $entity->toUrl('canonical')
->setOption('language', $language)
->setAbsolute()
->toString();
$page['#attached']['html_head_link'][] = array(
array(
$page['#attached']['html_head_link'][] = [
[
'rel' => 'alternate',
'hreflang' => $language->getId(),
'href' => $url,
),
],
TRUE,
);
];
}
}
// Since entity was found, no need to iterate further.
@@ -1,6 +1,5 @@
administer content translation:
title: 'Administer translation settings'
description: 'Configure translatability of entities and fields.'
create content translations:
title: 'Create translations'
update content translations:
@@ -4,6 +4,7 @@ namespace Drupal\content_translation;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\Entity\EntityChangedInterface;
use Drupal\Core\Entity\EntityHandlerInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityManagerInterface;
@@ -15,6 +16,7 @@ use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\Entity\User;
use Drupal\user\EntityOwnerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@@ -108,7 +110,7 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
* {@inheritdoc}
*/
public function getFieldDefinitions() {
$definitions = array();
$definitions = [];
$definitions['content_translation_source'] = BaseFieldDefinition::create('language')
->setLabel(t('Translation source'))
@@ -174,7 +176,7 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
// EntityOwnerInterface. This helps to exclude cases, where the uid is
// defined as field name, but is not meant to be an owner field; for
// instance, the User entity.
return $this->entityType->isSubclassOf('\Drupal\user\EntityOwnerInterface') && $this->checkFieldStorageDefinitionTranslatability('uid');
return $this->entityType->entityClassImplements(EntityOwnerInterface::class) && $this->checkFieldStorageDefinitionTranslatability('uid');
}
/**
@@ -194,7 +196,7 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
* TRUE if metadata is natively supported, FALSE otherwise.
*/
protected function hasChangedTime() {
return $this->entityType->isSubclassOf('Drupal\Core\Entity\EntityChangedInterface') && $this->checkFieldStorageDefinitionTranslatability('changed');
return $this->entityType->entityClassImplements(EntityChangedInterface::class) && $this->checkFieldStorageDefinitionTranslatability('changed');
}
/**
@@ -284,8 +286,8 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
if (isset($languages[$form_langcode]) && ($has_translations || $new_translation)) {
$title = $this->entityFormTitle($entity);
// When editing the original values display just the entity label.
if ($form_langcode != $entity_langcode) {
$t_args = array('%language' => $languages[$form_langcode]->getName(), '%title' => $entity->label(), '@title' => $title);
if ($is_translation) {
$t_args = ['%language' => $languages[$form_langcode]->getName(), '%title' => $entity->label(), '@title' => $title];
$title = empty($source_langcode) ? t('@title [%language translation]', $t_args) : t('Create %language translation of %title', $t_args);
}
$form['#title'] = $title;
@@ -294,25 +296,25 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
// Display source language selector only if we are creating a new
// translation and there are at least two translations available.
if ($has_translations && $new_translation) {
$form['source_langcode'] = array(
$form['source_langcode'] = [
'#type' => 'details',
'#title' => t('Source language: @language', array('@language' => $languages[$source_langcode]->getName())),
'#title' => t('Source language: @language', ['@language' => $languages[$source_langcode]->getName()]),
'#tree' => TRUE,
'#weight' => -100,
'#multilingual' => TRUE,
'source' => array(
'source' => [
'#title' => t('Select source language'),
'#title_display' => 'invisible',
'#type' => 'select',
'#default_value' => $source_langcode,
'#options' => array(),
),
'submit' => array(
'#options' => [],
],
'submit' => [
'#type' => 'submit',
'#value' => t('Change'),
'#submit' => array(array($this, 'entityFormSourceChange')),
),
);
'#submit' => [[$this, 'entityFormSourceChange']],
],
];
foreach ($this->languageManager->getLanguages() as $language) {
if (isset($translations[$language->getId()])) {
$form['source_langcode']['source']['#options'][$language->getId()] = $language->getName();
@@ -333,7 +335,7 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
if (isset($language_widget['widget'][0]['value']) && !$is_translation && $has_translations) {
$language_select = &$language_widget['widget'][0]['value'];
if ($language_select['#type'] == 'language_select') {
$options = array();
$options = [];
foreach ($this->languageManager->getLanguages() as $language) {
// Show the current language, and the languages for which no
// translation already exists.
@@ -352,20 +354,20 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
// Replace the delete button with the delete translation one.
if (!$new_translation) {
$weight = 100;
foreach (array('delete', 'submit') as $key) {
foreach (['delete', 'submit'] as $key) {
if (isset($form['actions'][$key]['weight'])) {
$weight = $form['actions'][$key]['weight'];
break;
}
}
$access = $this->getTranslationAccess($entity, 'delete')->isAllowed() || ($entity->access('delete') && $this->entityType->hasLinkTemplate('delete-form'));
$form['actions']['delete_translation'] = array(
$form['actions']['delete_translation'] = [
'#type' => 'submit',
'#value' => t('Delete translation'),
'#weight' => $weight,
'#submit' => array(array($this, 'entityFormDeleteTranslation')),
'#submit' => [[$this, 'entityFormDeleteTranslation']],
'#access' => $access,
);
];
}
// Always remove the delete button on translation forms.
@@ -375,14 +377,24 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
// We need to display the translation tab only when there is at least one
// translation available or a new one is about to be created.
if ($new_translation || $has_translations) {
$form['content_translation'] = array(
$form['content_translation'] = [
'#type' => 'details',
'#title' => t('Translation'),
'#tree' => TRUE,
'#weight' => 10,
'#access' => $this->getTranslationAccess($entity, $source_langcode ? 'create' : 'update')->isAllowed(),
'#multilingual' => TRUE,
);
];
if (isset($form['advanced'])) {
$form['content_translation'] += [
'#group' => 'advanced',
'#weight' => 100,
'#attributes' => [
'class' => ['entity-translation-options'],
],
];
}
// A new translation is enabled by default.
$metadata = $this->manager->getTranslationMetadata($entity);
@@ -402,30 +414,30 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
t('An unpublished translation will not be visible without translation permissions.') :
t('Only this translation is published. You must publish at least one more translation to unpublish this one.');
$form['content_translation']['status'] = array(
$form['content_translation']['status'] = [
'#type' => 'checkbox',
'#title' => t('This translation is published'),
'#default_value' => $status,
'#description' => $description,
'#disabled' => !$enabled,
);
];
$translate = !$new_translation && $metadata->isOutdated();
if (!$translate) {
$form['content_translation']['retranslate'] = array(
$form['content_translation']['retranslate'] = [
'#type' => 'checkbox',
'#title' => t('Flag other translations as outdated'),
'#default_value' => FALSE,
'#description' => t('If you made a significant change, which means the other translations should be updated, you can flag all translations of this content as outdated. This will not change any other property of them, like whether they are published or not.'),
);
];
}
else {
$form['content_translation']['outdated'] = array(
$form['content_translation']['outdated'] = [
'#type' => 'checkbox',
'#title' => t('This translation needs to be updated'),
'#default_value' => $translate,
'#description' => t('When this option is checked, this translation needs to be updated. Uncheck when the translation is up to date again.'),
);
];
$form['content_translation']['#open'] = TRUE;
}
@@ -437,7 +449,7 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
elseif (($account = $metadata->getAuthor()) && $account->id()) {
$uid = $account->id();
}
$form['content_translation']['uid'] = array(
$form['content_translation']['uid'] = [
'#type' => 'entity_autocomplete',
'#title' => t('Authored by'),
'#target_type' => 'user',
@@ -445,34 +457,34 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
// Validation is done by static::entityFormValidate().
'#validate_reference' => FALSE,
'#maxlength' => 60,
'#description' => t('Leave blank for %anonymous.', array('%anonymous' => \Drupal::config('user.settings')->get('anonymous'))),
);
'#description' => t('Leave blank for %anonymous.', ['%anonymous' => \Drupal::config('user.settings')->get('anonymous')]),
];
$date = $new_translation ? REQUEST_TIME : $metadata->getCreatedTime();
$form['content_translation']['created'] = array(
$form['content_translation']['created'] = [
'#type' => 'textfield',
'#title' => t('Authored on'),
'#maxlength' => 25,
'#description' => t('Format: %time. The date format is YYYY-MM-DD and %timezone is the time zone offset from UTC. Leave blank to use the time of form submission.', array('%time' => format_date(REQUEST_TIME, 'custom', 'Y-m-d H:i:s O'), '%timezone' => format_date(REQUEST_TIME, 'custom', 'O'))),
'#description' => t('Format: %time. The date format is YYYY-MM-DD and %timezone is the time zone offset from UTC. Leave blank to use the time of form submission.', ['%time' => format_date(REQUEST_TIME, 'custom', 'Y-m-d H:i:s O'), '%timezone' => format_date(REQUEST_TIME, 'custom', 'O')]),
'#default_value' => $new_translation || !$date ? '' : format_date($date, 'custom', 'Y-m-d H:i:s O'),
);
];
if (isset($language_widget)) {
$language_widget['#multilingual'] = TRUE;
}
$form['#process'][] = array($this, 'entityFormSharedElements');
$form['#process'][] = [$this, 'entityFormSharedElements'];
}
// Process the submitted values before they are stored.
$form['#entity_builders'][] = array($this, 'entityFormEntityBuild');
$form['#entity_builders'][] = [$this, 'entityFormEntityBuild'];
// Handle entity validation.
$form['#validate'][] = array($this, 'entityFormValidate');
$form['#validate'][] = [$this, 'entityFormValidate'];
// Handle entity deletion.
if (isset($form['actions']['delete'])) {
$form['actions']['delete']['#submit'][] = array($this, 'entityFormDelete');
$form['actions']['delete']['#submit'][] = [$this, 'entityFormDelete'];
}
// Handle entity form submission before the entity has been saved.
@@ -494,7 +506,7 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
// @todo Find a more reliable way to determine if a form element concerns a
// multilingual value.
if (!isset($ignored_types)) {
$ignored_types = array_flip(array('actions', 'value', 'hidden', 'vertical_tabs', 'token', 'details'));
$ignored_types = array_flip(['actions', 'value', 'hidden', 'vertical_tabs', 'token', 'details']);
}
foreach (Element::children($element) as $key) {
@@ -539,7 +551,7 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
// Elements which can have a #title attribute according to FAPI Reference.
if (!isset($suffix)) {
$suffix = ' <span class="translation-entity-all-languages">(' . t('all languages') . ')</span>';
$fapi_title_elements = array_flip(array('checkbox', 'checkboxes', 'date', 'details', 'fieldset', 'file', 'item', 'password', 'password_confirm', 'radio', 'radios', 'select', 'text_format', 'textarea', 'textfield', 'weight'));
$fapi_title_elements = array_flip(['checkbox', 'checkboxes', 'date', 'details', 'fieldset', 'file', 'item', 'password', 'password_confirm', 'radio', 'radios', 'select', 'text_format', 'textarea', 'textfield', 'weight']);
}
// Update #title attribute for all elements that are allowed to have a
@@ -575,7 +587,7 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
public function entityFormEntityBuild($entity_type, EntityInterface $entity, array $form, FormStateInterface $form_state) {
$form_object = $form_state->getFormObject();
$form_langcode = $form_object->getFormLangcode($form_state);
$values = &$form_state->getValue('content_translation', array());
$values = &$form_state->getValue('content_translation', []);
$metadata = $this->manager->getTranslationMetadata($entity);
$metadata->setAuthor(!empty($values['uid']) ? User::load($values['uid']) : User::load(0));
@@ -598,12 +610,12 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
*
* Validates the submitted content translation metadata.
*/
function entityFormValidate($form, FormStateInterface $form_state) {
public function entityFormValidate($form, FormStateInterface $form_state) {
if (!$form_state->isValueEmpty('content_translation')) {
$translation = $form_state->getValue('content_translation');
// Validate the "authored by" field.
if (!empty($translation['uid']) && !($account = User::load($translation['uid']))) {
$form_state->setErrorByName('content_translation][uid', t('The translation authoring username %name does not exist.', array('%name' => $account->getUsername())));
$form_state->setErrorByName('content_translation][uid', t('The translation authoring username %name does not exist.', ['%name' => $account->getUsername()]));
}
// Validate the "authored on" field.
if (!empty($translation['created']) && strtotime($translation['created']) === FALSE) {
@@ -618,7 +630,7 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
* Updates metadata fields, which should be updated only after the validation
* has run and before the entity is saved.
*/
function entityFormSubmit($form, FormStateInterface $form_state) {
public function entityFormSubmit($form, FormStateInterface $form_state) {
/** @var \Drupal\Core\Entity\ContentEntityFormInterface $form_object */
$form_object = $form_state->getFormObject();
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
@@ -644,16 +656,16 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
public function entityFormSourceChange($form, FormStateInterface $form_state) {
$form_object = $form_state->getFormObject();
$entity = $form_object->getEntity();
$source = $form_state->getValue(array('source_langcode', 'source'));
$source = $form_state->getValue(['source_langcode', 'source']);
$entity_type_id = $entity->getEntityTypeId();
$form_state->setRedirect("entity.$entity_type_id.content_translation_add", array(
$form_state->setRedirect("entity.$entity_type_id.content_translation_add", [
$entity_type_id => $entity->id(),
'source' => $source,
'target' => $form_object->getFormLangcode($form_state),
));
]);
$languages = $this->languageManager->getLanguages();
drupal_set_message(t('Source language set to: %language', array('%language' => $languages[$source]->getName())));
drupal_set_message(t('Source language set to: %language', ['%language' => $languages[$source]->getName()]));
}
/**
@@ -661,11 +673,11 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
*
* Takes care of entity deletion.
*/
function entityFormDelete($form, FormStateInterface $form_state) {
public function entityFormDelete($form, FormStateInterface $form_state) {
$form_object = $form_state->getFormObject()->getEntity();
$entity = $form_object->getEntity();
if (count($entity->getTranslationLanguages()) > 1) {
drupal_set_message(t('This will delete all the translations of %label.', array('%label' => $entity->label())), 'warning');
drupal_set_message(t('This will delete all the translations of %label.', ['%label' => $entity->label()]), 'warning');
}
}
@@ -674,7 +686,7 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
*
* Takes care of content translation deletion.
*/
function entityFormDeleteTranslation($form, FormStateInterface $form_state) {
public function entityFormDeleteTranslation($form, FormStateInterface $form_state) {
/** @var \Drupal\Core\Entity\ContentEntityFormInterface $form_object */
$form_object = $form_state->getFormObject();
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
@@ -40,7 +40,7 @@ class ContentTranslationManager implements ContentTranslationManagerInterface {
/**
* {@inheritdoc}
*/
function getTranslationHandler($entity_type_id) {
public function getTranslationHandler($entity_type_id) {
return $this->entityManager->getHandler($entity_type_id, 'translation');
}
@@ -66,7 +66,7 @@ class ContentTranslationManager implements ContentTranslationManagerInterface {
* {@inheritdoc}
*/
public function getSupportedEntityTypes() {
$supported_types = array();
$supported_types = [];
foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) {
if ($this->isSupported($entity_type_id)) {
$supported_types[$entity_type_id] = $entity_type;
@@ -82,7 +82,7 @@ class ContentTranslationManager implements ContentTranslationManagerInterface {
$config = $this->loadContentLanguageSettings($entity_type_id, $bundle);
$config->setThirdPartySetting('content_translation', 'enabled', $value)->save();
$entity_type = $this->entityManager->getDefinition($entity_type_id);
$this->updatesManager->updateDefinitions(array($entity_type_id => $entity_type));
$this->updatesManager->updateDefinitions([$entity_type_id => $entity_type]);
}
/**
@@ -92,7 +92,7 @@ class ContentTranslationManager implements ContentTranslationManagerInterface {
$enabled = FALSE;
if ($this->isSupported($entity_type_id)) {
$bundles = !empty($bundle) ? array($bundle) : array_keys($this->entityManager->getBundleInfo($entity_type_id));
$bundles = !empty($bundle) ? [$bundle] : array_keys($this->entityManager->getBundleInfo($entity_type_id));
foreach ($bundles as $bundle) {
$config = $this->loadContentLanguageSettings($entity_type_id, $bundle);
if ($config->getThirdPartySetting('content_translation', 'enabled', FALSE)) {
@@ -26,7 +26,7 @@ class ContentTranslationController extends ControllerBase {
/**
* Initializes a content translation controller.
*
* @param \Drupal\content_translation\ContentTranslationManagerInterface
* @param \Drupal\content_translation\ContentTranslationManagerInterface $manager
* A content translation manager instance.
*/
public function __construct(ContentTranslationManagerInterface $manager) {
@@ -97,7 +97,7 @@ class ContentTranslationController extends ControllerBase {
$translations = $entity->getTranslationLanguages();
$field_ui = $this->moduleHandler()->moduleExists('field_ui') && $account->hasPermission('administer ' . $entity_type_id . ' fields');
$rows = array();
$rows = [];
$show_source_column = FALSE;
if ($this->languageManager()->isMultilingual()) {
@@ -123,41 +123,41 @@ class ContentTranslationController extends ControllerBase {
$add_url = new Url(
"entity.$entity_type_id.content_translation_add",
array(
[
'source' => $original,
'target' => $language->getId(),
$entity_type_id => $entity->id(),
),
array(
],
[
'language' => $language,
)
]
);
$edit_url = new Url(
"entity.$entity_type_id.content_translation_edit",
array(
[
'language' => $language->getId(),
$entity_type_id => $entity->id(),
),
array(
],
[
'language' => $language,
)
]
);
$delete_url = new Url(
"entity.$entity_type_id.content_translation_delete",
array(
[
'language' => $language->getId(),
$entity_type_id => $entity->id(),
),
array(
],
[
'language' => $language,
)
]
);
$operations = array(
'data' => array(
$operations = [
'data' => [
'#type' => 'operations',
'#links' => array(),
),
);
'#links' => [],
],
];
$links = &$operations['data']['#links'];
if (array_key_exists($langcode, $translations)) {
@@ -167,7 +167,7 @@ class ContentTranslationController extends ControllerBase {
$source = $metadata->getSource() ?: LanguageInterface::LANGCODE_NOT_SPECIFIED;
$is_original = $langcode == $original;
$label = $entity->getTranslation($langcode)->label();
$link = isset($links->links[$langcode]['url']) ? $links->links[$langcode] : array('url' => $entity->urlInfo());
$link = isset($links->links[$langcode]['url']) ? $links->links[$langcode] : ['url' => $entity->urlInfo()];
if (!empty($link['url'])) {
$link['url']->setOption('language', $language);
$row_title = $this->l($label, $link['url']);
@@ -196,17 +196,17 @@ class ContentTranslationController extends ControllerBase {
if (isset($links['edit'])) {
$links['edit']['title'] = $this->t('Edit');
}
$status = array('data' => array(
$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' => array(
'#context' => [
'status' => $metadata->isPublished(),
'outdated' => $metadata->isOutdated(),
),
));
],
]];
if ($is_original) {
$language_name = $this->t('<strong>@language_name (Original language)</strong>', array('@language_name' => $language_name));
$language_name = $this->t('<strong>@language_name (Original language)</strong>', ['@language_name' => $language_name]);
$source_name = $this->t('n/a');
}
else {
@@ -217,17 +217,17 @@ class ContentTranslationController extends ControllerBase {
->merge(CacheableMetadata::createFromObject($delete_access))
->merge(CacheableMetadata::createFromObject($translation_access));
if ($entity->access('delete') && $entity_type->hasLinkTemplate('delete-form')) {
$links['delete'] = array(
$links['delete'] = [
'title' => $this->t('Delete'),
'url' => $entity->urlInfo('delete-form'),
'language' => $language,
);
];
}
elseif ($translation_access->isAllowed()) {
$links['delete'] = array(
$links['delete'] = [
'title' => $this->t('Delete'),
'url' => $delete_url,
);
];
}
}
}
@@ -241,58 +241,58 @@ class ContentTranslationController extends ControllerBase {
->merge(CacheableMetadata::createFromObject($create_translation_access));
if ($source != $langcode && $create_translation_access->isAllowed()) {
if ($translatable) {
$links['add'] = array(
$links['add'] = [
'title' => $this->t('Add'),
'url' => $add_url,
);
];
}
elseif ($field_ui) {
$url = new Url('language.content_settings_page');
// Link directly to the fields tab to make it easier to find the
// setting to enable translation on fields.
$links['nofields'] = array(
$links['nofields'] = [
'title' => $this->t('No translatable fields'),
'url' => $url,
);
];
}
}
$status = $this->t('Not translated');
}
if ($show_source_column) {
$rows[] = array(
$rows[] = [
$language_name,
$row_title,
$source_name,
$status,
$operations,
);
];
}
else {
$rows[] = array($language_name, $row_title, $status, $operations);
$rows[] = [$language_name, $row_title, $status, $operations];
}
}
}
if ($show_source_column) {
$header = array(
$header = [
$this->t('Language'),
$this->t('Translation'),
$this->t('Source language'),
$this->t('Status'),
$this->t('Operations'),
);
];
}
else {
$header = array(
$header = [
$this->t('Language'),
$this->t('Translation'),
$this->t('Status'),
$this->t('Operations'),
);
];
}
$build['#title'] = $this->t('Translations of %label', array('%label' => $entity->label()));
$build['#title'] = $this->t('Translations of %label', ['%label' => $entity->label()]);
// Add metadata to the build render array to let other modules know about
// which entity this is.
@@ -301,11 +301,11 @@ class ContentTranslationController extends ControllerBase {
->addCacheTags($entity->getCacheTags())
->applyTo($build);
$build['content_translation_overview'] = array(
$build['content_translation_overview'] = [
'#theme' => 'table',
'#header' => $header,
'#rows' => $rows,
);
];
return $build;
}
@@ -319,7 +319,7 @@ class ContentTranslationController extends ControllerBase {
* @param \Drupal\Core\Language\LanguageInterface $target
* The language of the translated values. Defaults to the current content
* language.
* @param \Drupal\Core\Routing\RouteMatchInterface
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match object from which to extract the entity type.
* @param string $entity_type_id
* (optional) The entity type ID.
@@ -337,9 +337,11 @@ class ContentTranslationController extends ControllerBase {
// @todo Provide a way to figure out the default form operation. Maybe like
// $operation = isset($info['default_operation']) ? $info['default_operation'] : 'default';
// See https://www.drupal.org/node/2006348.
$operation = 'default';
$form_state_additions = array();
// Use the add form handler, if available, otherwise default.
$operation = $entity->getEntityType()->hasHandlerClass('form', 'add') ? 'add' : 'default';
$form_state_additions = [];
$form_state_additions['langcode'] = $target->getId();
$form_state_additions['content_translation']['source'] = $source;
$form_state_additions['content_translation']['target'] = $target;
@@ -354,7 +356,7 @@ class ContentTranslationController extends ControllerBase {
* @param \Drupal\Core\Language\LanguageInterface $language
* The language of the translated values. Defaults to the current content
* language.
* @param \Drupal\Core\Routing\RouteMatchInterface
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match object from which to extract the entity type.
* @param string $entity_type_id
* (optional) The entity type ID.
@@ -368,9 +370,11 @@ class ContentTranslationController extends ControllerBase {
// @todo Provide a way to figure out the default form operation. Maybe like
// $operation = isset($info['default_operation']) ? $info['default_operation'] : 'default';
// See https://www.drupal.org/node/2006348.
$operation = 'default';
$form_state_additions = array();
// Use the edit form handler, if available, otherwise default.
$operation = $entity->getEntityType()->hasHandlerClass('form', 'edit') ? 'edit' : 'default';
$form_state_additions = [];
$form_state_additions['langcode'] = $language->getId();
$form_state_additions['content_translation']['translation_form'] = TRUE;
@@ -73,14 +73,14 @@ class FieldTranslationSynchronizer implements FieldTranslationSynchronizerInterf
}
}
if (!empty($groups)) {
$columns = array();
$columns = [];
foreach ($groups as $group) {
$info = $column_groups[$group];
// A missing 'columns' key indicates we have a single-column group.
$columns = array_merge($columns, isset($info['columns']) ? $info['columns'] : array($group));
$columns = array_merge($columns, isset($info['columns']) ? $info['columns'] : [$group]);
}
if (!empty($columns)) {
$values = array();
$values = [];
foreach ($translations as $langcode => $language) {
$values[$langcode] = $entity->getTranslation($langcode)->get($field_name)->getValue();
}
@@ -108,18 +108,18 @@ class FieldTranslationSynchronizer implements FieldTranslationSynchronizerInterf
$source_items = $values[$sync_langcode];
// Make sure we can detect any change in the source items.
$change_map = array();
$change_map = [];
// By picking the maximum size between updated and unchanged items, we make
// sure to process also removed items.
$total = max(array(count($source_items), count($unchanged_items)));
$total = max([count($source_items), count($unchanged_items)]);
// As a first step we build a map of the deltas corresponding to the column
// values to be synchronized. Recording both the old values and the new
// values will allow us to detect any change in the order of the new items
// for each column.
for ($delta = 0; $delta < $total; $delta++) {
foreach (array('old' => $unchanged_items, 'new' => $source_items) as $key => $items) {
foreach (['old' => $unchanged_items, 'new' => $source_items] as $key => $items) {
if ($item_id = $this->itemHash($items, $delta, $columns)) {
$change_map[$item_id][$key][] = $delta;
}
@@ -132,7 +132,7 @@ class FieldTranslationSynchronizer implements FieldTranslationSynchronizerInterf
// Reset field values so that no spurious one is stored. Source values must
// be preserved in any case.
$values = array($sync_langcode => $source_items);
$values = [$sync_langcode => $source_items];
// Update field translations.
foreach ($translations as $langcode) {
@@ -211,7 +211,7 @@ class FieldTranslationSynchronizer implements FieldTranslationSynchronizerInterf
* A hash code that can be used to identify the item.
*/
protected function itemHash(array $items, $delta, array $columns) {
$values = array();
$values = [];
if (isset($items[$delta])) {
foreach ($columns as $column) {
@@ -66,12 +66,12 @@ class ContentTranslationLocalTasks extends DeriverBase implements ContainerDeriv
$translation_route_name = "entity.$entity_type_id.content_translation_overview";
$base_route_name = "entity.$entity_type_id.canonical";
$this->derivatives[$translation_route_name] = array(
$this->derivatives[$translation_route_name] = [
'entity_type' => $entity_type_id,
'title' => $this->t('Translate'),
'route_name' => $translation_route_name,
'base_route' => $base_route_name,
) + $base_plugin_definition;
] + $base_plugin_definition;
}
return parent::getDerivativeDefinitions($base_plugin_definition);
}
@@ -58,104 +58,104 @@ class ContentTranslationRouteSubscriber extends RouteSubscriberBase {
$route = new Route(
$path,
array(
[
'_controller' => '\Drupal\content_translation\Controller\ContentTranslationController::overview',
'entity_type_id' => $entity_type_id,
),
array(
],
[
'_entity_access' => $entity_type_id . '.view',
'_access_content_translation_overview' => $entity_type_id,
),
array(
'parameters' => array(
$entity_type_id => array(
],
[
'parameters' => [
$entity_type_id => [
'type' => 'entity:' . $entity_type_id,
),
),
],
],
'_admin_route' => $is_admin,
)
]
);
$route_name = "entity.$entity_type_id.content_translation_overview";
$collection->add($route_name, $route);
$route = new Route(
$path . '/add/{source}/{target}',
array(
[
'_controller' => '\Drupal\content_translation\Controller\ContentTranslationController::add',
'source' => NULL,
'target' => NULL,
'_title' => 'Add',
'entity_type_id' => $entity_type_id,
),
array(
],
[
'_entity_access' => $entity_type_id . '.view',
'_access_content_translation_manage' => 'create',
),
array(
'parameters' => array(
'source' => array(
],
[
'parameters' => [
'source' => [
'type' => 'language',
),
'target' => array(
],
'target' => [
'type' => 'language',
),
$entity_type_id => array(
],
$entity_type_id => [
'type' => 'entity:' . $entity_type_id,
),
),
],
],
'_admin_route' => $is_admin,
)
]
);
$collection->add("entity.$entity_type_id.content_translation_add", $route);
$route = new Route(
$path . '/edit/{language}',
array(
[
'_controller' => '\Drupal\content_translation\Controller\ContentTranslationController::edit',
'language' => NULL,
'_title' => 'Edit',
'entity_type_id' => $entity_type_id,
),
array(
],
[
'_access_content_translation_manage' => 'update',
),
array(
'parameters' => array(
'language' => array(
],
[
'parameters' => [
'language' => [
'type' => 'language',
),
$entity_type_id => array(
],
$entity_type_id => [
'type' => 'entity:' . $entity_type_id,
),
),
],
],
'_admin_route' => $is_admin,
)
]
);
$collection->add("entity.$entity_type_id.content_translation_edit", $route);
$route = new Route(
$path . '/delete/{language}',
array(
[
'_entity_form' => $entity_type_id . '.content_translation_deletion',
'language' => NULL,
'_title' => 'Delete',
'entity_type_id' => $entity_type_id,
),
array(
],
[
'_access_content_translation_manage' => 'delete',
),
array(
'parameters' => array(
'language' => array(
],
[
'parameters' => [
'language' => [
'type' => 'language',
),
$entity_type_id => array(
],
$entity_type_id => [
'type' => 'entity:' . $entity_type_id,
),
),
],
],
'_admin_route' => $is_admin,
)
]
);
$collection->add("entity.$entity_type_id.content_translation_delete", $route);
}
@@ -168,7 +168,7 @@ class ContentTranslationRouteSubscriber extends RouteSubscriberBase {
$events = parent::getSubscribedEvents();
// Should run after AdminRouteSubscriber so the routes can inherit admin
// status of the edit routes on entities. Therefore priority -210.
$events[RoutingEvents::ALTER] = array('onAlterRoutes', -210);
$events[RoutingEvents::ALTER] = ['onAlterRoutes', -210];
return $events;
}
@@ -48,7 +48,7 @@ class ContentTranslationContextualLinksTest extends WebTestBase {
*
* @var array
*/
public static $modules = array('content_translation', 'contextual', 'node');
public static $modules = ['content_translation', 'contextual', 'node'];
/**
* The profile to install as a basis for testing.
@@ -60,20 +60,20 @@ class ContentTranslationContextualLinksTest extends WebTestBase {
protected function setUp() {
parent::setUp();
// Set up an additional language.
$this->langcodes = array(\Drupal::languageManager()->getDefaultLanguage()->getId(), 'es');
$this->langcodes = [\Drupal::languageManager()->getDefaultLanguage()->getId(), 'es'];
ConfigurableLanguage::createFromLangcode('es')->save();
// Create a content type.
$this->bundle = $this->randomMachineName();
$this->contentType = $this->drupalCreateContentType(array('type' => $this->bundle));
$this->contentType = $this->drupalCreateContentType(['type' => $this->bundle]);
// Add a field to the content type. The field is not yet translatable.
FieldStorageConfig::create(array(
FieldStorageConfig::create([
'field_name' => 'field_test_text',
'entity_type' => 'node',
'type' => 'text',
'cardinality' => 1,
))->save();
])->save();
FieldConfig::create([
'entity_type' => 'node',
'field_name' => 'field_test_text',
@@ -81,19 +81,19 @@ class ContentTranslationContextualLinksTest extends WebTestBase {
'label' => 'Test text-field',
])->save();
entity_get_form_display('node', $this->bundle, 'default')
->setComponent('field_test_text', array(
->setComponent('field_test_text', [
'type' => 'text_textfield',
'weight' => 0,
))
])
->save();
// Create a translator user.
$permissions = array(
$permissions = [
'access contextual links',
'administer nodes',
"edit any $this->bundle content",
'translate any entity',
);
];
$this->translator = $this->drupalCreateUser($permissions);
}
@@ -103,18 +103,18 @@ class ContentTranslationContextualLinksTest extends WebTestBase {
public function testContentTranslationContextualLinks() {
// Create a node.
$title = $this->randomString();
$this->drupalCreateNode(array('type' => $this->bundle, 'title' => $title, 'langcode' => 'en'));
$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 = array(
$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();
@@ -122,7 +122,7 @@ class ContentTranslationContextualLinksTest extends WebTestBase {
$this->drupalLogin($this->translator);
$translate_link = 'node/' . $node->id() . '/translations';
$response = $this->renderContextualLinks(array('node:node=1:'), 'node/' . $node->id());
$response = $this->renderContextualLinks(['node:node=1:'], 'node/' . $node->id());
$this->assertResponse(200);
$json = Json::decode($response);
$this->setRawContent($json['node:node=1:']);
@@ -130,7 +130,7 @@ class ContentTranslationContextualLinksTest extends WebTestBase {
// Check that the link leads to the translate page.
$this->drupalGet($translate_link);
$this->assertRaw(t('Translations of %label', array('%label' => $node->label())), 'The contextual link leads to the translate page.');
$this->assertRaw(t('Translations of %label', ['%label' => $node->label()]), 'The contextual link leads to the translate page.');
}
/**
@@ -148,7 +148,7 @@ class ContentTranslationContextualLinksTest extends WebTestBase {
*/
protected function renderContextualLinks($ids, $current_path) {
// Build POST values.
$post = array();
$post = [];
for ($i = 0; $i < count($ids); $i++) {
$post['ids[' . $i . ']'] = $ids[$i];
}
@@ -163,15 +163,15 @@ class ContentTranslationContextualLinksTest extends WebTestBase {
$post = implode('&', $post);
// Perform HTTP request.
return $this->curlExec(array(
CURLOPT_URL => \Drupal::url('contextual.render', array(), array('absolute' => TRUE, 'query' => array('destination' => $current_path))),
return $this->curlExec([
CURLOPT_URL => \Drupal::url('contextual.render', [], ['absolute' => TRUE, 'query' => ['destination' => $current_path]]),
CURLOPT_POST => TRUE,
CURLOPT_POSTFIELDS => $post,
CURLOPT_HTTPHEADER => array(
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Content-Type: application/x-www-form-urlencoded',
),
));
],
]);
}
}
@@ -10,6 +10,9 @@ use Drupal\field\Entity\FieldStorageConfig;
/**
* Base class for content translation tests.
*
* @deprecated Scheduled for removal in Drupal 9.0.0.
* Use \Drupal\Tests\content_translation\Functional\ContentTranslationTestBase instead.
*/
abstract class ContentTranslationTestBase extends WebTestBase {
@@ -18,7 +21,7 @@ abstract class ContentTranslationTestBase extends WebTestBase {
*
* @var array
*/
public static $modules = array('text');
public static $modules = ['text'];
/**
* The entity type being tested.
@@ -102,7 +105,7 @@ abstract class ContentTranslationTestBase extends WebTestBase {
* Adds additional languages.
*/
protected function setupLanguages() {
$this->langcodes = array('it', 'fr');
$this->langcodes = ['it', 'fr'];
foreach ($this->langcodes as $langcode) {
ConfigurableLanguage::createFromLangcode($langcode)->save();
}
@@ -113,7 +116,7 @@ abstract class ContentTranslationTestBase extends WebTestBase {
* Returns an array of permissions needed for the translator.
*/
protected function getTranslatorPermissions() {
return array_filter(array($this->getTranslatePermission(), 'create content translations', 'update content translations', 'delete content translations'));
return array_filter([$this->getTranslatePermission(), 'create content translations', 'update content translations', 'delete content translations']);
}
/**
@@ -131,14 +134,14 @@ abstract class ContentTranslationTestBase extends WebTestBase {
*/
protected function getEditorPermissions() {
// Every entity-type-specific test needs to define these.
return array();
return [];
}
/**
* Returns an array of permissions needed for the administrator.
*/
protected function getAdministratorPermissions() {
return array_merge($this->getEditorPermissions(), $this->getTranslatorPermissions(), array('administer content translation'));
return array_merge($this->getEditorPermissions(), $this->getTranslatorPermissions(), ['administer content translation']);
}
/**
@@ -180,12 +183,12 @@ abstract class ContentTranslationTestBase extends WebTestBase {
if (empty($this->fieldName)) {
$this->fieldName = 'field_test_et_ui_test';
}
FieldStorageConfig::create(array(
FieldStorageConfig::create([
'field_name' => $this->fieldName,
'type' => 'string',
'entity_type' => $this->entityTypeId,
'cardinality' => 1,
))->save();
])->save();
FieldConfig::create([
'entity_type' => $this->entityTypeId,
'field_name' => $this->fieldName,
@@ -193,10 +196,10 @@ abstract class ContentTranslationTestBase extends WebTestBase {
'label' => 'Test translatable text-field',
])->save();
entity_get_form_display($this->entityTypeId, $this->bundle, 'default')
->setComponent($this->fieldName, array(
->setComponent($this->fieldName, [
'type' => 'string_textfield',
'weight' => 0,
))
])
->save();
}
@@ -225,7 +228,7 @@ abstract class ContentTranslationTestBase extends WebTestBase {
if (!($controller instanceof SqlContentEntityStorage)) {
foreach ($values as $property => $value) {
if (is_array($value)) {
$entity_values[$property] = array($langcode => $value);
$entity_values[$property] = [$langcode => $value];
}
}
}
@@ -3,6 +3,7 @@
namespace Drupal\content_translation\Tests;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\EntityChangedInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Language\Language;
use Drupal\Core\Language\LanguageInterface;
@@ -13,6 +14,9 @@ use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
/**
* Tests the Content Translation UI.
*
* @deprecated Scheduled for removal in Drupal 9.0.0.
* Use \Drupal\Tests\content_translation\Functional\ContentTranslationUITestBase instead.
*/
abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
@@ -52,7 +56,7 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
/**
* Tests the basic translation UI.
*/
function testTranslationUI() {
public function testTranslationUI() {
$this->doTestBasicTranslation();
$this->doTestTranslationOverview();
$this->doTestOutdatedStatus();
@@ -77,7 +81,10 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
$this->drupalLogin($this->editor);
$this->entityId = $this->createEntity($values[$default_langcode], $default_langcode);
$this->drupalLogin($this->translator);
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$this->assertTrue($entity, 'Entity found in the database.');
$this->drupalGet($entity->urlInfo());
$this->assertResponse(200, 'Entity URL is valid.');
@@ -93,7 +100,7 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
foreach ($values[$default_langcode] as $property => $value) {
$stored_value = $this->getValue($translation, $property, $default_langcode);
$value = is_array($value) ? $value[0]['value'] : $value;
$message = format_string('@property correctly stored in the default language.', array('@property' => $property));
$message = format_string('@property correctly stored in the default language.', ['@property' => $property]);
$this->assertEqual($stored_value, $value, $message);
}
@@ -107,7 +114,7 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
$entity->getEntityTypeId() => $entity->id(),
'source' => $default_langcode,
'target' => $langcode
], array('language' => $language));
], ['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
@@ -119,7 +126,10 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
// Ensure that the content language cache context is not yet added to the
// page.
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$this->drupalGet($entity->urlInfo());
$this->assertCacheContexts(Cache::mergeContexts(['languages:language_content'], $this->defaultCacheContexts));
@@ -131,11 +141,11 @@ 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.', array('@langcode' => $langcode)));
SafeMarkup::format('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.',
array('@target' => $langcode, '@source' => $default_langcode)));
['@target' => $langcode, '@source' => $default_langcode]));
}
else {
$this->assertEqual($metadata_target_translation->getAuthor()->id(), $this->editor->id(), 'Author of the entity remained untouched after translation for non translatable owner field.');
@@ -145,7 +155,7 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
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.',
array('@target' => $langcode, '@source' => $default_langcode)));
['@target' => $langcode, '@source' => $default_langcode]));
}
else {
$this->assertEqual($metadata_target_translation->getCreatedTime(), $metadata_source_translation->getCreatedTime(), 'Creation timestamp of the entity remained untouched after translation for non translatable created field.');
@@ -154,7 +164,8 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
if ($this->testLanguageSelector) {
$this->assertNoFieldByXPath('//select[@id="edit-langcode-0-value"]', NULL, 'Language selector correctly disabled on translations.');
}
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$this->drupalGet($entity->urlInfo('drupal:content-translation-overview'));
$this->assertNoText('Source language', 'Source language column correctly hidden.');
@@ -162,13 +173,13 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
$langcode = 'fr';
$language = ConfigurableLanguage::load($langcode);
$source_langcode = 'it';
$edit = array('source_langcode[source]' => $source_langcode);
$edit = ['source_langcode[source]' => $source_langcode];
$entity_type_id = $entity->getEntityTypeId();
$add_url = Url::fromRoute("entity.$entity_type_id.content_translation_add", [
$entity->getEntityTypeId() => $entity->id(),
'source' => $default_langcode,
'target' => $langcode
], array('language' => $language));
], ['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.
$this->drupalPostForm($add_url, $edit, t('Change'));
@@ -176,15 +187,16 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
// Add another translation and mark the other ones as outdated.
$values[$langcode] = $this->getNewEntityValues($langcode);
$edit = $this->getEditValues($values, $langcode) + array('content_translation[retranslate]' => TRUE);
$edit = $this->getEditValues($values, $langcode) + ['content_translation[retranslate]' => TRUE];
$entity_type_id = $entity->getEntityTypeId();
$add_url = Url::fromRoute("entity.$entity_type_id.content_translation_add", [
$entity->getEntityTypeId() => $entity->id(),
'source' => $source_langcode,
'target' => $langcode
], array('language' => $language));
], ['language' => $language]);
$this->drupalPostForm($add_url, $edit, $this->getFormSubmitActionForNewTranslation($entity, $langcode));
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$this->drupalGet($entity->urlInfo('drupal:content-translation-overview'));
$this->assertText('Source language', 'Source language column correctly shown.');
@@ -194,7 +206,7 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
foreach ($property_values as $property => $value) {
$stored_value = $this->getValue($translation, $property, $langcode);
$value = is_array($value) ? $value[0]['value'] : $value;
$message = format_string('%property correctly stored with language %language.', array('%property' => $property, '%language' => $langcode));
$message = format_string('%property correctly stored with language %language.', ['%property' => $property, '%language' => $langcode]);
$this->assertEqual($stored_value, $value, $message);
}
}
@@ -204,20 +216,23 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
* Tests that the translation overview shows the correct values.
*/
protected function doTestTranslationOverview() {
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$translate_url = $entity->urlInfo('drupal:content-translation-overview');
$this->drupalGet($translate_url);
$translate_url->setAbsolute(FALSE);
foreach ($this->langcodes as $langcode) {
if ($entity->hasTranslation($langcode)) {
$language = new Language(array('id' => $langcode));
$language = new Language(['id' => $langcode]);
$view_url = $entity->url('canonical', ['language' => $language]);
$elements = $this->xpath('//table//a[@href=:href]', [':href' => $view_url]);
$this->assertEqual((string) $elements[0], $entity->getTranslation($langcode)->label(), format_string('Label correctly shown for %language translation.', array('%language' => $langcode)));
$edit_path = $entity->url('edit-form', array('language' => $language));
$elements = $this->xpath('//table//ul[@class="dropbutton"]/li/a[@href=:href]', array(':href' => $edit_path));
$this->assertEqual((string) $elements[0], t('Edit'), format_string('Edit link correct for %language translation.', array('%language' => $langcode)));
$this->assertEqual((string) $elements[0], $entity->getTranslation($langcode)->label(), format_string('Label correctly shown for %language translation.', ['%language' => $langcode]));
$edit_path = $entity->url('edit-form', ['language' => $language]);
$elements = $this->xpath('//table//ul[@class="dropbutton"]/li/a[@href=:href]', [':href' => $edit_path]);
$this->assertEqual((string) $elements[0], t('Edit'), format_string('Edit link correct for %language translation.', ['%language' => $langcode]));
}
}
}
@@ -226,20 +241,24 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
* Tests up-to-date status tracking.
*/
protected function doTestOutdatedStatus() {
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$langcode = 'fr';
$languages = \Drupal::languageManager()->getLanguages();
// Mark translations as outdated.
$edit = array('content_translation[retranslate]' => TRUE);
$edit_path = $entity->urlInfo('edit-form', array('language' => $languages[$langcode]));
$edit = ['content_translation[retranslate]' => TRUE];
$edit_path = $entity->urlInfo('edit-form', ['language' => $languages[$langcode]]);
$this->drupalPostForm($edit_path, $edit, $this->getFormSubmitAction($entity, $langcode));
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
// Check that every translation has the correct "outdated" status, and that
// the Translation fieldset is open if the translation is "outdated".
foreach ($this->langcodes as $added_langcode) {
$url = $entity->urlInfo('edit-form', array('language' => ConfigurableLanguage::load($added_langcode)));
$url = $entity->urlInfo('edit-form', ['language' => ConfigurableLanguage::load($added_langcode)]);
$this->drupalGet($url);
if ($added_langcode == $langcode) {
$this->assertFieldByXPath('//input[@name="content_translation[retranslate]"]', FALSE, 'The retranslate flag is not checked by default.');
@@ -248,11 +267,14 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
else {
$this->assertFieldByXPath('//input[@name="content_translation[outdated]"]', TRUE, 'The translate flag is checked by default.');
$this->assertTrue($this->xpath('//details[@id="edit-content-translation" and @open="open"]'), 'The translation tab is correctly expanded when the translation is outdated.');
$edit = array('content_translation[outdated]' => FALSE);
$edit = ['content_translation[outdated]' => FALSE];
$this->drupalPostForm($url, $edit, $this->getFormSubmitAction($entity, $added_langcode));
$this->drupalGet($url);
$this->assertFieldByXPath('//input[@name="content_translation[retranslate]"]', FALSE, 'The retranslate flag is now shown.');
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$this->assertFalse($this->manager->getTranslationMetadata($entity->getTranslation($added_langcode))->isOutdated(), 'The "outdated" status has been correctly stored.');
}
}
@@ -262,15 +284,21 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
* Tests the translation publishing status.
*/
protected function doTestPublishedStatus() {
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
// Unpublish translations.
foreach ($this->langcodes as $index => $langcode) {
if ($index > 0) {
$url = $entity->urlInfo('edit-form', array('language' => ConfigurableLanguage::load($langcode)));
$edit = array('content_translation[status]' => FALSE);
$url = $entity->urlInfo('edit-form', ['language' => ConfigurableLanguage::load($langcode)]);
$edit = ['content_translation[status]' => FALSE];
$this->drupalPostForm($url, $edit, $this->getFormSubmitAction($entity, $langcode));
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$this->assertFalse($this->manager->getTranslationMetadata($entity->getTranslation($langcode))->isPublished(), 'The translation has been correctly unpublished.');
}
}
@@ -284,25 +312,31 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
* Tests the translation authoring information.
*/
protected function doTestAuthoringInfo() {
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
$values = array();
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$values = [];
// Post different authoring information for each translation.
foreach ($this->langcodes as $index => $langcode) {
$user = $this->drupalCreateUser();
$values[$langcode] = array(
$values[$langcode] = [
'uid' => $user->id(),
'created' => REQUEST_TIME - mt_rand(0, 1000),
);
$edit = array(
];
$edit = [
'content_translation[uid]' => $user->getUsername(),
'content_translation[created]' => format_date($values[$langcode]['created'], 'custom', 'Y-m-d H:i:s O'),
);
$url = $entity->urlInfo('edit-form', array('language' => ConfigurableLanguage::load($langcode)));
];
$url = $entity->urlInfo('edit-form', ['language' => ConfigurableLanguage::load($langcode)]);
$this->drupalPostForm($url, $edit, $this->getFormSubmitAction($entity, $langcode));
}
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
foreach ($this->langcodes as $langcode) {
$metadata = $this->manager->getTranslationMetadata($entity->getTranslation($langcode));
$this->assertEqual($metadata->getAuthor()->id(), $values[$langcode]['uid'], 'Translation author correctly stored.');
@@ -311,11 +345,11 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
// Try to post non valid values and check that they are rejected.
$langcode = 'en';
$edit = array(
$edit = [
// User names have by default length 8.
'content_translation[uid]' => $this->randomMachineName(12),
'content_translation[created]' => '19/11/1978',
);
];
$this->drupalPostForm($entity->urlInfo('edit-form'), $edit, $this->getFormSubmitAction($entity, $langcode));
$this->assertTrue($this->xpath('//div[contains(@class, "error")]//ul'), 'Invalid values generate a list of form errors.');
$metadata = $this->manager->getTranslationMetadata($entity->getTranslation($langcode));
@@ -330,12 +364,16 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
// Confirm and delete a translation.
$this->drupalLogin($this->translator);
$langcode = 'fr';
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$language = ConfigurableLanguage::load($langcode);
$url = $entity->urlInfo('edit-form', array('language' => $language));
$this->drupalPostForm($url, array(), t('Delete translation'));
$this->drupalPostForm(NULL, array(), t('Delete @language translation', array('@language' => $language->getName())));
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
$url = $entity->urlInfo('edit-form', ['language' => $language]);
$this->drupalPostForm($url, [], t('Delete translation'));
$this->drupalPostForm(NULL, [], t('Delete @language translation', ['@language' => $language->getName()]));
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId, TRUE);
if ($this->assertTrue(is_object($entity), 'Entity found')) {
$translations = $entity->getTranslationLanguages();
$this->assertTrue(count($translations) == 2 && empty($translations[$langcode]), 'Translation successfully deleted.');
@@ -351,7 +389,7 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
* Returns an array of entity field values to be tested.
*/
protected function getNewEntityValues($langcode) {
return array($this->fieldName => array(array('value' => $this->randomMachineName(16))));
return [$this->fieldName => [['value' => $this->randomMachineName(16)]]];
}
/**
@@ -465,13 +503,16 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
* Tests edit content translation.
*/
protected function doTestTranslationEdit() {
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$languages = $this->container->get('language_manager')->getLanguages();
foreach ($this->langcodes as $langcode) {
// We only want to test the title for non-english translations.
if ($langcode != 'en') {
$options = array('language' => $languages[$langcode]);
$options = ['language' => $languages[$langcode]];
$url = $entity->urlInfo('edit-form', $options);
$this->drupalGet($url);
@@ -484,7 +525,10 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
* Tests the basic translation workflow.
*/
protected function doTestTranslationChanged() {
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$changed_field_name = $this->getChangedFieldName($entity);
$definition = $entity->getFieldDefinition($changed_field_name);
$config = $definition->getConfig($entity->bundle());
@@ -511,20 +555,23 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
$langcode = $language->getId();
$edit = array(
$edit = [
$this->fieldName . '[0][value]' => $this->randomString(),
);
$edit_path = $entity->urlInfo('edit-form', array('language' => $language));
];
$edit_path = $entity->urlInfo('edit-form', ['language' => $language]);
$this->drupalPostForm($edit_path, $edit, $this->getFormSubmitAction($entity, $langcode));
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$this->assertEqual(
$entity->getChangedTimeAcrossTranslations(), $entity->getTranslation($langcode)->getChangedTime(),
format_string('Changed time for language %language is the latest change over all languages.', array('%language' => $language->getName()))
format_string('Changed time for language %language is the latest change over all languages.', ['%language' => $language->getName()])
);
}
$timestamps = array();
$timestamps = [];
foreach ($entity->getTranslationLanguages() as $language) {
$next_timestamp = $entity->getTranslation($language->getId())->getChangedTime();
if (!in_array($next_timestamp, $timestamps)) {
@@ -551,13 +598,19 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
* Test the changed time after API and FORM save without changes.
*/
public function doTestChangedTimeAfterSaveWithoutChanges() {
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
// Test only entities, which implement the EntityChangedInterface.
if ($entity->getEntityType()->isSubclassOf('Drupal\Core\Entity\EntityChangedInterface')) {
if ($entity instanceof EntityChangedInterface) {
$changed_timestamp = $entity->getChangedTime();
$entity->save();
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$this->assertEqual($changed_timestamp, $entity->getChangedTime(), 'The entity\'s changed time wasn\'t updated after API save without changes.');
// Ensure different save timestamps.
@@ -565,10 +618,11 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
// Save the entity on the regular edit form.
$language = $entity->language();
$edit_path = $entity->urlInfo('edit-form', array('language' => $language));
$edit_path = $entity->urlInfo('edit-form', ['language' => $language]);
$this->drupalPostForm($edit_path, [], $this->getFormSubmitAction($entity, $language->getId()));
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$this->assertNotEqual($changed_timestamp, $entity->getChangedTime(), 'The entity\'s changed time was updated after form save without changes.');
}
}
@@ -26,7 +26,7 @@ class ContentTranslationWorkflowsTest extends ContentTranslationTestBase {
*
* @var array
*/
public static $modules = array('language', 'content_translation', 'entity_test');
public static $modules = ['language', 'content_translation', 'entity_test'];
protected function setUp() {
parent::setUp();
@@ -47,7 +47,7 @@ class ContentTranslationWorkflowsTest extends ContentTranslationTestBase {
* {@inheritdoc}
*/
protected function getEditorPermissions() {
return array('administer entity_test content');
return ['administer entity_test content'];
}
/**
@@ -58,25 +58,28 @@ class ContentTranslationWorkflowsTest extends ContentTranslationTestBase {
// Create a test entity.
$user = $this->drupalCreateUser();
$values = array(
$values = [
'name' => $this->randomMachineName(),
'user_id' => $user->id(),
$this->fieldName => array(array('value' => $this->randomMachineName(16))),
);
$this->fieldName => [['value' => $this->randomMachineName(16)]],
];
$id = $this->createEntity($values, $default_langcode);
$this->entity = entity_load($this->entityTypeId, $id, TRUE);
$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, array(), t('Save'));
$this->drupalPostForm($add_translation_url, [], t('Save'));
$this->rebuildContainer();
}
/**
* Test simple and editorial translation workflows.
*/
function testWorkflows() {
public function testWorkflows() {
// Test workflows for the editor.
$expected_status = [
'edit' => 200,
@@ -111,10 +114,10 @@ class ContentTranslationWorkflowsTest extends ContentTranslationTestBase {
$this->doTestWorkflows($this->administrator, $expected_status);
// Check that translation permissions allow the associated operations.
$ops = array('create' => t('Add'), 'update' => t('Edit'), 'delete' => t('Delete'));
$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(array($this->getTranslatePermission(), "$current_op content translations", 'view test entity'));
$user = $this->drupalCreateUser([$this->getTranslatePermission(), "$current_op content translations", 'view test entity']);
$this->drupalLogin($user);
$this->drupalGet($translations_url);
@@ -127,10 +130,10 @@ class ContentTranslationWorkflowsTest extends ContentTranslationTestBase {
foreach ($ops as $op => $label) {
if ($op != $current_op) {
$this->assertNoLink($label, format_string('No %op link found.', array('%op' => $label)));
$this->assertNoLink($label, format_string('No %op link found.', ['%op' => $label]));
}
else {
$this->assertLink($label, 0, format_string('%op link found.', array('%op' => $label)));
$this->assertLink($label, 0, format_string('%op link found.', ['%op' => $label]));
}
}
}
@@ -9,8 +9,8 @@ dependencies:
- language
- entity_test
# Information added by Drupal.org packaging script on 2016-08-03
version: '8.1.8'
# Information added by Drupal.org packaging script on 2017-08-16
version: '8.3.7'
core: '8.x'
project: 'drupal'
datestamp: 1470233792
datestamp: 1502903957
@@ -0,0 +1,38 @@
<?php
/**
* @file
* Helper module for the Content Translation tests.
*/
use \Drupal\Core\Form\FormStateInterface;
/**
* Implements hook_form_BASE_FORM_ID_alter().
*
* Adds a textfield to node forms based on a request parameter.
*/
function content_translation_test_form_node_form_alter(&$form, FormStateInterface $form_state, $form_id) {
$langcode = $form_state->getFormObject()->getFormLangcode($form_state);
if (in_array($langcode, ['en', 'fr']) && \Drupal::request()->get('test_field_only_en_fr')) {
$form['test_field_only_en_fr'] = [
'#type' => 'textfield',
'#title' => 'Field only available on the english and french form',
];
foreach (array_keys($form['actions']) as $action) {
if ($action != 'preview' && isset($form['actions'][$action]['#type']) && $form['actions'][$action]['#type'] === 'submit') {
$form['actions'][$action]['#submit'][] = 'content_translation_test_form_node_form_submit';
}
}
}
}
/**
* Form submission handler for custom field added based on a request parameter.
*
* @see content_translation_test_form_node_article_form_alter()
*/
function content_translation_test_form_node_form_submit($form, FormStateInterface $form_state) {
\Drupal::state()->set('test_field_only_en_fr', $form_state->getValue('test_field_only_en_fr'));
}
@@ -10,6 +10,14 @@ use Drupal\entity_test\Entity\EntityTest;
* @ContentEntityType(
* id = "entity_test_translatable_no_skip",
* label = @Translation("Test entity - Translatable check UI"),
* handlers = {
* "form" = {
* "default" = "Drupal\entity_test\EntityTestForm",
* },
* "route_provider" = {
* "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
* },
* },
* base_table = "entity_test_mul",
* data_table = "entity_test_mul_property_data",
* entity_keys = {
@@ -20,6 +28,10 @@ use Drupal\entity_test\Entity\EntityTest;
* "langcode" = "langcode",
* },
* translatable = TRUE,
* admin_permission = "administer entity_test content",
* links = {
* "edit-form" = "/entity_test_translatable_no_skip/{entity_test_translatable_no_skip}/edit",
* },
* )
*/
class EntityTestTranslatableNoUISkip extends EntityTest {
@@ -8,8 +8,8 @@ dependencies:
- content_translation
- views
# Information added by Drupal.org packaging script on 2016-08-03
version: '8.1.8'
# Information added by Drupal.org packaging script on 2017-08-16
version: '8.3.7'
core: '8.x'
project: 'drupal'
datestamp: 1470233792
datestamp: 1502903957
@@ -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']);
}
}
@@ -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);
}
}
@@ -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]');
}
}
@@ -0,0 +1,51 @@
<?php
namespace Drupal\Tests\content_translation\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Tests the content translation behaviours on entity bundle UI.
*
* @group content_translation
*/
class ContentTranslationEntityBundleUITest extends BrowserTestBase {
public static $modules = ['language', 'content_translation', 'node', 'comment', 'field_ui'];
protected function setUp() {
parent::setUp();
$user = $this->drupalCreateUser(['access administration pages', 'administer languages', 'administer content translation', 'administer content types']);
$this->drupalLogin($user);
}
/**
* Tests content types default translation behaviour.
*/
public function testContentTypeUI() {
// Create first content type.
$this->drupalCreateContentType(['type' => 'article']);
// Enable content translation.
$edit = ['language_configuration[content_translation]' => TRUE];
$this->drupalPostForm('admin/structure/types/manage/article', $edit, 'Save content type');
// Make sure add page does not inherit translation configuration from first
// content type.
$this->drupalGet('admin/structure/types/add');
$this->assertNoFieldChecked('edit-language-configuration-content-translation');
// Create second content type and set content translation.
$edit = [
'name' => 'Page',
'type' => 'page',
'language_configuration[content_translation]' => TRUE,
];
$this->drupalPostForm('admin/structure/types/add', $edit, 'Save and manage fields');
// Make sure the settings are saved when creating the content type.
$this->drupalGet('admin/structure/types/manage/page');
$this->assertFieldChecked('edit-language-configuration-content-translation');
}
}
@@ -0,0 +1,149 @@
<?php
namespace Drupal\Tests\content_translation\Functional;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\node\Tests\NodeTestBase;
/**
* Tests the content translation language that is set.
*
* @group content_translation
*/
class ContentTranslationLanguageChangeTest extends NodeTestBase {
/**
* 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 and publish'));
// Create a translation in French.
$this->clickLink('Translate');
$this->clickLink('Add');
$this->drupalPostForm(NULL, [], t('Save and keep published (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 and keep published (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 and publish'));
$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->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 and keep published (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));
}
}
@@ -0,0 +1,137 @@
<?php
namespace Drupal\Tests\content_translation\Functional;
use Drupal\Tests\BrowserTestBase;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\entity_test\Entity\EntityTestMul;
use Drupal\content_translation_test\Entity\EntityTestTranslatableNoUISkip;
/**
* Tests whether canonical link tags are present for content entities.
*
* @group content_translation
*/
class ContentTranslationLinkTagTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['entity_test', 'content_translation', 'content_translation_test', 'language'];
/**
* The added languages.
*
* @var string[]
*/
protected $langcodes;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Set up user.
$user = $this->drupalCreateUser([
'view test entity',
'view test entity translations',
'administer entity_test content',
]);
$this->drupalLogin($user);
// Add additional languages.
$this->langcodes = ['it', 'fr'];
foreach ($this->langcodes as $langcode) {
ConfigurableLanguage::createFromLangcode($langcode)->save();
}
// Rebuild the container so that the new languages are picked up by services
// that hold a list of languages.
$this->rebuildContainer();
}
/**
* Create a test entity with translations.
*
* @return \Drupal\Core\Entity\EntityInterface
* An entity with translations.
*/
protected function createTranslatableEntity() {
$entity = EntityTestMul::create(['label' => $this->randomString()]);
// Create translations for non default languages.
foreach ($this->langcodes as $langcode) {
$entity->addTranslation($langcode, ['label' => $this->randomString()]);
}
$entity->save();
return $entity;
}
/**
* Tests alternate link tag found for entity types with canonical links.
*/
public function testCanonicalAlternateTags() {
/** @var \Drupal\Core\Language\LanguageManagerInterface $languageManager */
$languageManager = $this->container->get('language_manager');
/** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager */
$entityTypeManager = $this->container->get('entity_type.manager');
$definition = $entityTypeManager->getDefinition('entity_test_mul');
$this->assertTrue($definition->hasLinkTemplate('canonical'), 'Canonical link template found for entity_test.');
$entity = $this->createTranslatableEntity();
$url_base = $entity->toUrl('canonical')
->setAbsolute();
$langcodes_all = $this->langcodes;
$langcodes_all[] = $languageManager
->getDefaultLanguage()
->getId();
/** @var \Drupal\Core\Url[] $urls */
$urls = array_map(
function ($langcode) use ($url_base, $languageManager) {
$url = clone $url_base;
return $url
->setOption('language', $languageManager->getLanguage($langcode));
},
array_combine($langcodes_all, $langcodes_all)
);
// Ensure link tags for all languages are found on each language variation
// page of an entity.
foreach ($urls as $langcode => $url) {
$this->drupalGet($url);
foreach ($urls as $langcode_alternate => $url_alternate) {
$args = [':href' => $url_alternate->toString(), ':hreflang' => $langcode_alternate];
$links = $this->xpath('head/link[@rel = "alternate" and @href = :href and @hreflang = :hreflang]', $args);
$message = sprintf('The "%s" translation has the correct alternate hreflang link for "%s": %s.', $langcode, $langcode_alternate, $url->toString());
$this->assertTrue(isset($links[0]), $message);
}
}
}
/**
* Tests alternate link tag missing for entity types without canonical links.
*/
public function testCanonicalAlternateTagsMissing() {
/** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager */
$entityTypeManager = $this->container->get('entity_type.manager');
$definition = $entityTypeManager->getDefinition('entity_test_translatable_no_skip');
// Ensure 'canonical' link template does not exist, in case it is added in
// the future.
$this->assertFalse($definition->hasLinkTemplate('canonical'), 'Canonical link template does not exist for entity_test_translatable_no_skip entity.');
$entity = EntityTestTranslatableNoUISkip::create();
$entity->save();
$this->drupalGet($entity->toUrl('edit-form'));
$this->assertSession()->statusCodeEquals(200);
$result = $this->xpath('//link[@rel="alternate" and @hreflang]');
$this->assertFalse($result, 'No alternate link tag found.');
}
}
@@ -0,0 +1,144 @@
<?php
namespace Drupal\Tests\content_translation\Functional;
/**
* Tests the Content Translation metadata fields handling.
*
* @group content_translation
*/
class ContentTranslationMetadataFieldsTest extends ContentTranslationTestBase {
/**
* The entity type being tested.
*
* @var string
*/
protected $entityTypeId = 'node';
/**
* The bundle being tested.
*
* @var string
*/
protected $bundle = 'article';
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['language', 'content_translation', 'node'];
/**
* The profile to install as a basis for testing.
*
* @var string
*/
protected $profile = 'standard';
/**
* Tests skipping setting non translatable metadata fields.
*/
public function testSkipUntranslatable() {
$this->drupalLogin($this->translator);
$entity_manager = \Drupal::entityManager();
$fields = $entity_manager->getFieldDefinitions($this->entityTypeId, $this->bundle);
// Turn off translatability for the metadata fields on the current bundle.
$metadata_fields = ['created', 'changed', 'uid', 'status'];
foreach ($metadata_fields as $field_name) {
$fields[$field_name]
->getConfig($this->bundle)
->setTranslatable(FALSE)
->save();
}
// Create a new test entity with original values in the default language.
$default_langcode = $this->langcodes[0];
$entity_id = $this->createEntity(['title' => $this->randomString()], $default_langcode);
$storage = $entity_manager->getStorage($this->entityTypeId);
$storage->resetCache();
$entity = $storage->load($entity_id);
// Add a content translation.
$langcode = 'it';
$values = $entity->toArray();
// Apply a default value for the metadata fields.
foreach ($metadata_fields as $field_name) {
unset($values[$field_name]);
}
$entity->addTranslation($langcode, $values);
$metadata_source_translation = $this->manager->getTranslationMetadata($entity->getTranslation($default_langcode));
$metadata_target_translation = $this->manager->getTranslationMetadata($entity->getTranslation($langcode));
$created_time = $metadata_source_translation->getCreatedTime();
$changed_time = $metadata_source_translation->getChangedTime();
$published = $metadata_source_translation->isPublished();
$author = $metadata_source_translation->getAuthor();
$this->assertEqual($created_time, $metadata_target_translation->getCreatedTime(), 'Metadata created field has the same value for both translations.');
$this->assertEqual($changed_time, $metadata_target_translation->getChangedTime(), 'Metadata changed field has the same value for both translations.');
$this->assertEqual($published, $metadata_target_translation->isPublished(), 'Metadata published field has the same value for both translations.');
$this->assertEqual($author->id(), $metadata_target_translation->getAuthor()->id(), 'Metadata author field has the same value for both translations.');
$metadata_target_translation->setCreatedTime(time() + 50);
$metadata_target_translation->setChangedTime(time() + 50);
$metadata_target_translation->setPublished(TRUE);
$metadata_target_translation->setAuthor($this->editor);
$this->assertEqual($created_time, $metadata_target_translation->getCreatedTime(), 'Metadata created field correctly not updated');
$this->assertEqual($changed_time, $metadata_target_translation->getChangedTime(), 'Metadata changed field correctly not updated');
$this->assertEqual($published, $metadata_target_translation->isPublished(), 'Metadata published field correctly not updated');
$this->assertEqual($author->id(), $metadata_target_translation->getAuthor()->id(), 'Metadata author field correctly not updated');
}
/**
* Tests setting translatable metadata fields.
*/
public function testSetTranslatable() {
$this->drupalLogin($this->translator);
$entity_manager = \Drupal::entityManager();
$fields = $entity_manager->getFieldDefinitions($this->entityTypeId, $this->bundle);
// Turn off translatability for the metadata fields on the current bundle.
$metadata_fields = ['created', 'changed', 'uid', 'status'];
foreach ($metadata_fields as $field_name) {
$fields[$field_name]
->getConfig($this->bundle)
->setTranslatable(TRUE)
->save();
}
// Create a new test entity with original values in the default language.
$default_langcode = $this->langcodes[0];
$entity_id = $this->createEntity(['title' => $this->randomString(), 'status' => FALSE], $default_langcode);
$storage = $entity_manager->getStorage($this->entityTypeId);
$storage->resetCache();
$entity = $storage->load($entity_id);
// Add a content translation.
$langcode = 'it';
$values = $entity->toArray();
// Apply a default value for the metadata fields.
foreach ($metadata_fields as $field_name) {
unset($values[$field_name]);
}
$entity->addTranslation($langcode, $values);
$metadata_source_translation = $this->manager->getTranslationMetadata($entity->getTranslation($default_langcode));
$metadata_target_translation = $this->manager->getTranslationMetadata($entity->getTranslation($langcode));
$metadata_target_translation->setCreatedTime(time() + 50);
$metadata_target_translation->setChangedTime(time() + 50);
$metadata_target_translation->setPublished(TRUE);
$metadata_target_translation->setAuthor($this->editor);
$this->assertNotEqual($metadata_source_translation->getCreatedTime(), $metadata_target_translation->getCreatedTime(), 'Metadata created field correctly different on both translations.');
$this->assertNotEqual($metadata_source_translation->getChangedTime(), $metadata_target_translation->getChangedTime(), 'Metadata changed field correctly different on both translations.');
$this->assertNotEqual($metadata_source_translation->isPublished(), $metadata_target_translation->isPublished(), 'Metadata published field correctly different on both translations.');
$this->assertNotEqual($metadata_source_translation->getAuthor()->id(), $metadata_target_translation->getAuthor()->id(), 'Metadata author field correctly different on both translations.');
}
}
@@ -0,0 +1,157 @@
<?php
namespace Drupal\Tests\content_translation\Functional;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\Tests\node\Functional\NodeTestBase;
use Drupal\user\Entity\Role;
/**
* Tests the content translation operations available in the content listing.
*
* @group content_translation
*/
class ContentTranslationOperationsTest extends NodeTestBase {
/**
* A base user.
*
* @var \Drupal\user\Entity\User|false
*/
protected $baseUser1;
/**
* A base user.
*
* @var \Drupal\user\Entity\User|false
*/
protected $baseUser2;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['language', 'content_translation', 'node', 'views', 'block'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Enable additional languages.
$langcodes = ['es', 'ast'];
foreach ($langcodes as $langcode) {
ConfigurableLanguage::createFromLangcode($langcode)->save();
}
// Enable translation for the current entity type and ensure the change is
// picked up.
\Drupal::service('content_translation.manager')->setEnabled('node', 'article', TRUE);
drupal_static_reset();
\Drupal::entityManager()->clearCachedDefinitions();
\Drupal::service('router.builder')->rebuild();
\Drupal::service('entity.definition_update_manager')->applyUpdates();
$this->baseUser1 = $this->drupalCreateUser(['access content overview']);
$this->baseUser2 = $this->drupalCreateUser(['access content overview', 'create content translations', 'update content translations', 'delete content translations']);
}
/**
* Test that the operation "Translate" is displayed in the content listing.
*/
public function testOperationTranslateLink() {
$node = $this->drupalCreateNode(['type' => 'article', 'langcode' => 'es']);
// Verify no translation operation links are displayed for users without
// permission.
$this->drupalLogin($this->baseUser1);
$this->drupalGet('admin/content');
$this->assertNoLinkByHref('node/' . $node->id() . '/translations');
$this->drupalLogout();
// Verify there's a translation operation link for users with enough
// permissions.
$this->drupalLogin($this->baseUser2);
$this->drupalGet('admin/content');
$this->assertLinkByHref('node/' . $node->id() . '/translations');
// Ensure that an unintended misconfiguration of permissions does not open
// access to the translation form, see https://www.drupal.org/node/2558905.
$this->drupalLogout();
user_role_change_permissions(
Role::AUTHENTICATED_ID,
[
'create content translations' => TRUE,
'access content' => FALSE,
]
);
$this->drupalLogin($this->baseUser1);
$this->drupalGet($node->urlInfo('drupal:content-translation-overview'));
$this->assertResponse(403);
// Ensure that the translation overview is also not accessible when the user
// has 'access content', but the node is not published.
user_role_change_permissions(
Role::AUTHENTICATED_ID,
[
'create content translations' => TRUE,
'access content' => TRUE,
]
);
$node->setPublished(FALSE)->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();
user_role_change_permissions(
Role::AUTHENTICATED_ID,
[
'administer content translation' => TRUE,
'administer languages' => TRUE,
]
);
$this->drupalPlaceBlock('local_tasks_block');
$this->drupalLogin($this->baseUser2);
$this->drupalGet('node/' . $node->id());
$this->assertLinkByHref('node/' . $node->id() . '/translations');
$this->drupalPostForm('admin/config/regional/content-language', ['settings[node][article][translatable]' => FALSE], t('Save configuration'));
$this->drupalGet('node/' . $node->id());
$this->assertNoLinkByHref('node/' . $node->id() . '/translations');
}
/**
* Tests the access to the overview page for translations.
*
* @see content_translation_translate_access()
*/
public function testContentTranslationOverviewAccess() {
$access_control_handler = \Drupal::entityManager()->getAccessControlHandler('node');
$user = $this->createUser(['create content translations', 'access content']);
$this->drupalLogin($user);
$node = $this->drupalCreateNode(['status' => FALSE, 'type' => 'article']);
$this->assertFalse(content_translation_translate_access($node)->isAllowed());
$access_control_handler->resetCache();
$node->setPublished(TRUE);
$node->save();
$this->assertTrue(content_translation_translate_access($node)->isAllowed());
$access_control_handler->resetCache();
user_role_change_permissions(
Role::AUTHENTICATED_ID,
[
'access content' => FALSE,
]
);
$user = $this->createUser(['create content translations']);
$this->drupalLogin($user);
$this->assertFalse(content_translation_translate_access($node)->isAllowed());
$access_control_handler->resetCache();
}
}
@@ -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');
}
}
@@ -0,0 +1,84 @@
<?php
namespace Drupal\Tests\content_translation\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Tests the Content translation settings using the standard profile.
*
* @group content_translation
*/
class ContentTranslationStandardFieldsTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = [
'language',
'content_translation',
'node',
'comment',
'field_ui',
'entity_test',
];
/**
* {@inheritdoc}
*/
protected $profile = 'standard';
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$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',
]);
$this->drupalLogin($admin_user);
}
/**
* Tests that translatable fields are being rendered.
*/
public function testFieldTranslatableArticle() {
$path = 'admin/config/regional/content-language';
$this->drupalGet($path);
// Check content block fields.
$this->assertFieldByXPath("//input[@id='edit-settings-block-content-basic-fields-body' and @checked='checked']");
// Check comment fields.
$this->assertFieldByXPath("//input[@id='edit-settings-comment-comment-fields-comment-body' and @checked='checked']");
// Check node fields.
$this->assertFieldByXPath("//input[@id='edit-settings-node-article-fields-comment' and @checked='checked']");
$this->assertFieldByXPath("//input[@id='edit-settings-node-article-fields-field-image' and @checked='checked']");
$this->assertFieldByXPath("//input[@id='edit-settings-node-article-fields-field-tags' and @checked='checked']");
// Check user fields.
$this->assertFieldByXPath("//input[@id='edit-settings-user-user-fields-user-picture' and @checked='checked']");
}
/**
* Test that revision_log is not translatable.
*/
public function testRevisionLogNotTranslatable() {
$path = 'admin/config/regional/content-language';
$this->drupalGet($path);
$this->assertNoFieldByXPath("//input[@id='edit-settings-node-article-fields-revision-log']");
}
}
@@ -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;
}
}
@@ -0,0 +1,239 @@
<?php
namespace Drupal\Tests\content_translation\Functional;
use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
use Drupal\field\Entity\FieldConfig;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\Tests\BrowserTestBase;
use Drupal\field\Entity\FieldStorageConfig;
/**
* Base class for content translation tests.
*/
abstract class ContentTranslationTestBase extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['text'];
/**
* The entity type being tested.
*
* @var string
*/
protected $entityTypeId = 'entity_test_mul';
/**
* The bundle being tested.
*
* @var string
*/
protected $bundle;
/**
* The added languages.
*
* @var array
*/
protected $langcodes;
/**
* The account to be used to test translation operations.
*
* @var \Drupal\user\UserInterface
*/
protected $translator;
/**
* The account to be used to test multilingual entity editing.
*
* @var \Drupal\user\UserInterface
*/
protected $editor;
/**
* The account to be used to test access to both workflows.
*
* @var \Drupal\user\UserInterface
*/
protected $administrator;
/**
* The name of the field used to test translation.
*
* @var string
*/
protected $fieldName;
/**
* The translation controller for the current entity type.
*
* @var \Drupal\content_translation\ContentTranslationHandlerInterface
*/
protected $controller;
/**
* @var \Drupal\content_translation\ContentTranslationManagerInterface
*/
protected $manager;
protected function setUp() {
parent::setUp();
$this->setupLanguages();
$this->setupBundle();
$this->enableTranslation();
$this->setupUsers();
$this->setupTestFields();
$this->manager = $this->container->get('content_translation.manager');
$this->controller = $this->manager->getTranslationHandler($this->entityTypeId);
// Rebuild the container so that the new languages are picked up by services
// that hold a list of languages.
$this->rebuildContainer();
}
/**
* Adds additional languages.
*/
protected function setupLanguages() {
$this->langcodes = ['it', 'fr'];
foreach ($this->langcodes as $langcode) {
ConfigurableLanguage::createFromLangcode($langcode)->save();
}
array_unshift($this->langcodes, \Drupal::languageManager()->getDefaultLanguage()->getId());
}
/**
* Returns an array of permissions needed for the translator.
*/
protected function getTranslatorPermissions() {
return array_filter([$this->getTranslatePermission(), 'create content translations', 'update content translations', 'delete content translations']);
}
/**
* Returns the translate permissions for the current entity and bundle.
*/
protected function getTranslatePermission() {
$entity_type = \Drupal::entityManager()->getDefinition($this->entityTypeId);
if ($permission_granularity = $entity_type->getPermissionGranularity()) {
return $permission_granularity == 'bundle' ? "translate {$this->bundle} {$this->entityTypeId}" : "translate {$this->entityTypeId}";
}
}
/**
* Returns an array of permissions needed for the editor.
*/
protected function getEditorPermissions() {
// Every entity-type-specific test needs to define these.
return [];
}
/**
* Returns an array of permissions needed for the administrator.
*/
protected function getAdministratorPermissions() {
return array_merge($this->getEditorPermissions(), $this->getTranslatorPermissions(), ['administer content translation']);
}
/**
* Creates and activates translator, editor and admin users.
*/
protected function setupUsers() {
$this->translator = $this->drupalCreateUser($this->getTranslatorPermissions(), 'translator');
$this->editor = $this->drupalCreateUser($this->getEditorPermissions(), 'editor');
$this->administrator = $this->drupalCreateUser($this->getAdministratorPermissions(), 'administrator');
$this->drupalLogin($this->translator);
}
/**
* Creates or initializes the bundle date if needed.
*/
protected function setupBundle() {
if (empty($this->bundle)) {
$this->bundle = $this->entityTypeId;
}
}
/**
* Enables translation for the current entity type and bundle.
*/
protected function enableTranslation() {
// Enable translation for the current entity type and ensure the change is
// picked up.
\Drupal::service('content_translation.manager')->setEnabled($this->entityTypeId, $this->bundle, TRUE);
drupal_static_reset();
\Drupal::entityManager()->clearCachedDefinitions();
\Drupal::service('router.builder')->rebuild();
\Drupal::service('entity.definition_update_manager')->applyUpdates();
}
/**
* Creates the test fields.
*/
protected function setupTestFields() {
if (empty($this->fieldName)) {
$this->fieldName = 'field_test_et_ui_test';
}
FieldStorageConfig::create([
'field_name' => $this->fieldName,
'type' => 'string',
'entity_type' => $this->entityTypeId,
'cardinality' => 1,
])->save();
FieldConfig::create([
'entity_type' => $this->entityTypeId,
'field_name' => $this->fieldName,
'bundle' => $this->bundle,
'label' => 'Test translatable text-field',
])->save();
entity_get_form_display($this->entityTypeId, $this->bundle, 'default')
->setComponent($this->fieldName, [
'type' => 'string_textfield',
'weight' => 0,
])
->save();
}
/**
* Creates the entity to be translated.
*
* @param array $values
* An array of initial values for the entity.
* @param string $langcode
* The initial language code of the entity.
* @param string $bundle_name
* (optional) The entity bundle, if the entity uses bundles. Defaults to
* NULL. If left NULL, $this->bundle will be used.
*
* @return string
* The entity id.
*/
protected function createEntity($values, $langcode, $bundle_name = NULL) {
$entity_values = $values;
$entity_values['langcode'] = $langcode;
$entity_type = \Drupal::entityManager()->getDefinition($this->entityTypeId);
if ($bundle_key = $entity_type->getKey('bundle')) {
$entity_values[$bundle_key] = $bundle_name ?: $this->bundle;
}
$controller = $this->container->get('entity.manager')->getStorage($this->entityTypeId);
if (!($controller instanceof SqlContentEntityStorage)) {
foreach ($values as $property => $value) {
if (is_array($value)) {
$entity_values[$property] = [$langcode => $value];
}
}
}
$entity = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId)
->create($entity_values);
$entity->save();
return $entity->id();
}
}
@@ -0,0 +1,39 @@
<?php
namespace Drupal\Tests\content_translation\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Tests the content translation UI check skip.
*
* @group content_translation
*/
class ContentTranslationUISkipTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['content_translation_test', 'user', 'node'];
/**
* Tests the content_translation_ui_skip key functionality.
*/
public function testUICheckSkip() {
$admin_user = $this->drupalCreateUser([
'translate any entity',
'administer content translation',
'administer languages'
]);
$this->drupalLogin($admin_user);
// Visit the content translation.
$this->drupalGet('admin/config/regional/content-language');
// Check the message regarding UI integration.
$this->assertText('Test entity - Translatable skip UI check');
$this->assertText('Test entity - Translatable check UI (Translation is not supported)');
}
}
@@ -0,0 +1,628 @@
<?php
namespace Drupal\Tests\content_translation\Functional;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\EntityChangedInterface;
use Drupal\Core\Entity\EntityInterface;
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;
/**
* Tests the Content Translation UI.
*/
abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
use AssertPageCacheContextsAndTagsTrait;
/**
* The id of the entity being translated.
*
* @var mixed
*/
protected $entityId;
/**
* Whether the behavior of the language selector should be tested.
*
* @var bool
*/
protected $testLanguageSelector = TRUE;
/**
* Flag that tells whether the HTML escaping of all languages works or not
* after SafeMarkup change.
*
* @var bool
*/
protected $testHTMLEscapeForAllLanguages = FALSE;
/**
* Default cache contexts expected on a non-translated entity.
*
* Cache contexts will not be checked if this list is empty.
*
* @var string[]
*/
protected $defaultCacheContexts = ['languages:language_interface', 'theme', 'url.query_args:_wrapper_format', 'user.permissions'];
/**
* Tests the basic translation UI.
*/
public function testTranslationUI() {
$this->doTestBasicTranslation();
$this->doTestTranslationOverview();
$this->doTestOutdatedStatus();
$this->doTestPublishedStatus();
$this->doTestAuthoringInfo();
$this->doTestTranslationEdit();
$this->doTestTranslationChanged();
$this->doTestChangedTimeAfterSaveWithoutChanges();
$this->doTestTranslationDeletion();
}
/**
* Tests the basic translation workflow.
*/
protected function doTestBasicTranslation() {
// Create a new test entity with original values in the default language.
$default_langcode = $this->langcodes[0];
$values[$default_langcode] = $this->getNewEntityValues($default_langcode);
// Create the entity with the editor as owner, so that afterwards a new
// translation is created by the translator and the translation author is
// tested.
$this->drupalLogin($this->editor);
$this->entityId = $this->createEntity($values[$default_langcode], $default_langcode);
$this->drupalLogin($this->translator);
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$this->assertTrue($entity, 'Entity found in the database.');
$this->drupalGet($entity->urlInfo());
$this->assertResponse(200, 'Entity URL is valid.');
// Ensure that the content language cache context is not yet added to the
// page.
$this->assertCacheContexts($this->defaultCacheContexts);
$this->drupalGet($entity->urlInfo('drupal:content-translation-overview'));
$this->assertNoText('Source language', 'Source language column correctly hidden.');
$translation = $this->getTranslation($entity, $default_langcode);
foreach ($values[$default_langcode] as $property => $value) {
$stored_value = $this->getValue($translation, $property, $default_langcode);
$value = is_array($value) ? $value[0]['value'] : $value;
$message = format_string('@property correctly stored in the default language.', ['@property' => $property]);
$this->assertEqual($stored_value, $value, $message);
}
// Add a content translation.
$langcode = 'it';
$language = ConfigurableLanguage::load($langcode);
$values[$langcode] = $this->getNewEntityValues($langcode);
$entity_type_id = $entity->getEntityTypeId();
$add_url = Url::fromRoute("entity.$entity_type_id.content_translation_add", [
$entity->getEntityTypeId() => $entity->id(),
'source' => $default_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.
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>');
}
// Ensure that the content language cache context is not yet added to the
// page.
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$this->drupalGet($entity->urlInfo());
$this->assertCacheContexts(Cache::mergeContexts(['languages:language_content'], $this->defaultCacheContexts));
// Reset the cache of the entity, so that the new translation gets the
// updated values.
$metadata_source_translation = $this->manager->getTranslationMetadata($entity->getTranslation($default_langcode));
$metadata_target_translation = $this->manager->getTranslationMetadata($entity->getTranslation($langcode));
$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]));
$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.',
['@target' => $langcode, '@source' => $default_langcode]));
}
else {
$this->assertEqual($metadata_target_translation->getAuthor()->id(), $this->editor->id(), 'Author of the entity remained untouched after translation for non translatable owner field.');
}
$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.',
['@target' => $langcode, '@source' => $default_langcode]));
}
else {
$this->assertEqual($metadata_target_translation->getCreatedTime(), $metadata_source_translation->getCreatedTime(), 'Creation timestamp of the entity remained untouched after translation for non translatable created field.');
}
if ($this->testLanguageSelector) {
$this->assertNoFieldByXPath('//select[@id="edit-langcode-0-value"]', NULL, 'Language selector correctly disabled on translations.');
}
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$this->drupalGet($entity->urlInfo('drupal:content-translation-overview'));
$this->assertNoText('Source language', 'Source language column correctly hidden.');
// Switch the source language.
$langcode = 'fr';
$language = ConfigurableLanguage::load($langcode);
$source_langcode = 'it';
$edit = ['source_langcode[source]' => $source_langcode];
$entity_type_id = $entity->getEntityTypeId();
$add_url = Url::fromRoute("entity.$entity_type_id.content_translation_add", [
$entity->getEntityTypeId() => $entity->id(),
'source' => $default_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.
$this->drupalPostForm($add_url, $edit, t('Change'));
$this->assertFieldByXPath("//input[@name=\"{$this->fieldName}[0][value]\"]", $values[$source_langcode][$this->fieldName][0]['value'], 'Source language correctly switched.');
// Add another translation and mark the other ones as outdated.
$values[$langcode] = $this->getNewEntityValues($langcode);
$edit = $this->getEditValues($values, $langcode) + ['content_translation[retranslate]' => TRUE];
$entity_type_id = $entity->getEntityTypeId();
$add_url = Url::fromRoute("entity.$entity_type_id.content_translation_add", [
$entity->getEntityTypeId() => $entity->id(),
'source' => $source_langcode,
'target' => $langcode
], ['language' => $language]);
$this->drupalPostForm($add_url, $edit, $this->getFormSubmitActionForNewTranslation($entity, $langcode));
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$this->drupalGet($entity->urlInfo('drupal:content-translation-overview'));
$this->assertText('Source language', 'Source language column correctly shown.');
// Check that the entered values have been correctly stored.
foreach ($values as $langcode => $property_values) {
$translation = $this->getTranslation($entity, $langcode);
foreach ($property_values as $property => $value) {
$stored_value = $this->getValue($translation, $property, $langcode);
$value = is_array($value) ? $value[0]['value'] : $value;
$message = format_string('%property correctly stored with language %language.', ['%property' => $property, '%language' => $langcode]);
$this->assertEqual($stored_value, $value, $message);
}
}
}
/**
* Tests that the translation overview shows the correct values.
*/
protected function doTestTranslationOverview() {
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$translate_url = $entity->urlInfo('drupal:content-translation-overview');
$this->drupalGet($translate_url);
$translate_url->setAbsolute(FALSE);
foreach ($this->langcodes as $langcode) {
if ($entity->hasTranslation($langcode)) {
$language = new Language(['id' => $langcode]);
$view_url = $entity->url('canonical', ['language' => $language]);
$elements = $this->xpath('//table//a[@href=:href]', [':href' => $view_url]);
$this->assertEqual($elements[0]->getText(), $entity->getTranslation($langcode)->label(), new FormattableMarkup('Label correctly shown for %language translation.', ['%language' => $langcode]));
$edit_path = $entity->url('edit-form', ['language' => $language]);
$elements = $this->xpath('//table//ul[@class="dropbutton"]/li/a[@href=:href]', [':href' => $edit_path]);
$this->assertEqual($elements[0]->getText(), t('Edit'), new FormattableMarkup('Edit link correct for %language translation.', ['%language' => $langcode]));
}
}
}
/**
* Tests up-to-date status tracking.
*/
protected function doTestOutdatedStatus() {
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$langcode = 'fr';
$languages = \Drupal::languageManager()->getLanguages();
// Mark translations as outdated.
$edit = ['content_translation[retranslate]' => TRUE];
$edit_path = $entity->urlInfo('edit-form', ['language' => $languages[$langcode]]);
$this->drupalPostForm($edit_path, $edit, $this->getFormSubmitAction($entity, $langcode));
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
// Check that every translation has the correct "outdated" status, and that
// the Translation fieldset is open if the translation is "outdated".
foreach ($this->langcodes as $added_langcode) {
$url = $entity->urlInfo('edit-form', ['language' => ConfigurableLanguage::load($added_langcode)]);
$this->drupalGet($url);
if ($added_langcode == $langcode) {
$this->assertFieldByXPath('//input[@name="content_translation[retranslate]"]', FALSE, 'The retranslate flag is not checked by default.');
$this->assertFalse($this->xpath('//details[@id="edit-content-translation" and @open="open"]'), 'The translation tab should be collapsed by default.');
}
else {
$this->assertFieldByXPath('//input[@name="content_translation[outdated]"]', TRUE, 'The translate flag is checked by default.');
$this->assertTrue($this->xpath('//details[@id="edit-content-translation" and @open="open"]'), 'The translation tab is correctly expanded when the translation is outdated.');
$edit = ['content_translation[outdated]' => FALSE];
$this->drupalPostForm($url, $edit, $this->getFormSubmitAction($entity, $added_langcode));
$this->drupalGet($url);
$this->assertFieldByXPath('//input[@name="content_translation[retranslate]"]', FALSE, 'The retranslate flag is now shown.');
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$this->assertFalse($this->manager->getTranslationMetadata($entity->getTranslation($added_langcode))->isOutdated(), 'The "outdated" status has been correctly stored.');
}
}
}
/**
* Tests the translation publishing status.
*/
protected function doTestPublishedStatus() {
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
// Unpublish translations.
foreach ($this->langcodes as $index => $langcode) {
if ($index > 0) {
$url = $entity->urlInfo('edit-form', ['language' => ConfigurableLanguage::load($langcode)]);
$edit = ['content_translation[status]' => FALSE];
$this->drupalPostForm($url, $edit, $this->getFormSubmitAction($entity, $langcode));
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$this->assertFalse($this->manager->getTranslationMetadata($entity->getTranslation($langcode))->isPublished(), 'The translation has been correctly unpublished.');
}
}
// Check that the last published translation cannot be unpublished.
$this->drupalGet($entity->urlInfo('edit-form'));
$this->assertFieldByXPath('//input[@name="content_translation[status]" and @disabled="disabled"]', TRUE, 'The last translation is published and cannot be unpublished.');
}
/**
* Tests the translation authoring information.
*/
protected function doTestAuthoringInfo() {
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$values = [];
// Post different authoring information for each translation.
foreach ($this->langcodes as $index => $langcode) {
$user = $this->drupalCreateUser();
$values[$langcode] = [
'uid' => $user->id(),
'created' => REQUEST_TIME - mt_rand(0, 1000),
];
$edit = [
'content_translation[uid]' => $user->getUsername(),
'content_translation[created]' => format_date($values[$langcode]['created'], 'custom', 'Y-m-d H:i:s O'),
];
$url = $entity->urlInfo('edit-form', ['language' => ConfigurableLanguage::load($langcode)]);
$this->drupalPostForm($url, $edit, $this->getFormSubmitAction($entity, $langcode));
}
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
foreach ($this->langcodes as $langcode) {
$metadata = $this->manager->getTranslationMetadata($entity->getTranslation($langcode));
$this->assertEqual($metadata->getAuthor()->id(), $values[$langcode]['uid'], 'Translation author correctly stored.');
$this->assertEqual($metadata->getCreatedTime(), $values[$langcode]['created'], 'Translation date correctly stored.');
}
// Try to post non valid values and check that they are rejected.
$langcode = 'en';
$edit = [
// User names have by default length 8.
'content_translation[uid]' => $this->randomMachineName(12),
'content_translation[created]' => '19/11/1978',
];
$this->drupalPostForm($entity->urlInfo('edit-form'), $edit, $this->getFormSubmitAction($entity, $langcode));
$this->assertTrue($this->xpath('//div[contains(@class, "error")]//ul'), 'Invalid values generate a list of form errors.');
$metadata = $this->manager->getTranslationMetadata($entity->getTranslation($langcode));
$this->assertEqual($metadata->getAuthor()->id(), $values[$langcode]['uid'], 'Translation author correctly kept.');
$this->assertEqual($metadata->getCreatedTime(), $values[$langcode]['created'], 'Translation date correctly kept.');
}
/**
* Tests translation deletion.
*/
protected function doTestTranslationDeletion() {
// Confirm and delete a translation.
$this->drupalLogin($this->translator);
$langcode = 'fr';
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$language = ConfigurableLanguage::load($langcode);
$url = $entity->urlInfo('edit-form', ['language' => $language]);
$this->drupalPostForm($url, [], t('Delete translation'));
$this->drupalPostForm(NULL, [], t('Delete @language translation', ['@language' => $language->getName()]));
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId, TRUE);
if ($this->assertTrue(is_object($entity), 'Entity found')) {
$translations = $entity->getTranslationLanguages();
$this->assertTrue(count($translations) == 2 && empty($translations[$langcode]), 'Translation successfully deleted.');
}
// Check that the translator cannot delete the original translation.
$args = [$this->entityTypeId => $entity->id(), 'language' => 'en'];
$this->drupalGet(Url::fromRoute("entity.$this->entityTypeId.content_translation_delete", $args));
$this->assertResponse(403);
}
/**
* Returns an array of entity field values to be tested.
*/
protected function getNewEntityValues($langcode) {
return [$this->fieldName => [['value' => $this->randomMachineName(16)]]];
}
/**
* Returns an edit array containing the values to be posted.
*/
protected function getEditValues($values, $langcode, $new = FALSE) {
$edit = $values[$langcode];
$langcode = $new ? LanguageInterface::LANGCODE_NOT_SPECIFIED : $langcode;
foreach ($values[$langcode] as $property => $value) {
if (is_array($value)) {
$edit["{$property}[0][value]"] = $value[0]['value'];
unset($edit[$property]);
}
}
return $edit;
}
/**
* Returns the form action value when submitting a new translation.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity being tested.
* @param string $langcode
* Language code for the form.
*
* @return string
* Name of the button to hit.
*/
protected function getFormSubmitActionForNewTranslation(EntityInterface $entity, $langcode) {
$entity->addTranslation($langcode, $entity->toArray());
return $this->getFormSubmitAction($entity, $langcode);
}
/**
* Returns the form action value to be used to submit the entity form.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity being tested.
* @param string $langcode
* Language code for the form.
*
* @return string
* Name of the button to hit.
*/
protected function getFormSubmitAction(EntityInterface $entity, $langcode) {
return t('Save') . $this->getFormSubmitSuffix($entity, $langcode);
}
/**
* Returns appropriate submit button suffix based on translatability.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity being tested.
* @param string $langcode
* Language code for the form.
*
* @return string
* Submit button suffix based on translatability.
*/
protected function getFormSubmitSuffix(EntityInterface $entity, $langcode) {
return '';
}
/**
* Returns the translation object to use to retrieve the translated values.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity being tested.
* @param string $langcode
* The language code identifying the translation to be retrieved.
*
* @return \Drupal\Core\TypedData\TranslatableInterface
* The translation object to act on.
*/
protected function getTranslation(EntityInterface $entity, $langcode) {
return $entity->getTranslation($langcode);
}
/**
* Returns the value for the specified property in the given language.
*
* @param \Drupal\Core\Entity\EntityInterface $translation
* The translation object the property value should be retrieved from.
* @param string $property
* The property name.
* @param string $langcode
* The property value.
*
* @return
* The property value.
*/
protected function getValue(EntityInterface $translation, $property, $langcode) {
$key = $property == 'user_id' ? 'target_id' : 'value';
return $translation->get($property)->{$key};
}
/**
* Returns the name of the field that implements the changed timestamp.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity being tested.
*
* @return string
* The field name.
*/
protected function getChangedFieldName($entity) {
return $entity->hasField('content_translation_changed') ? 'content_translation_changed' : 'changed';
}
/**
* Tests edit content translation.
*/
protected function doTestTranslationEdit() {
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$languages = $this->container->get('language_manager')->getLanguages();
foreach ($this->langcodes as $langcode) {
// We only want to test the title for non-english translations.
if ($langcode != 'en') {
$options = ['language' => $languages[$langcode]];
$url = $entity->urlInfo('edit-form', $options);
$this->drupalGet($url);
$this->assertRaw($entity->getTranslation($langcode)->label());
}
}
}
/**
* Tests the basic translation workflow.
*/
protected function doTestTranslationChanged() {
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$changed_field_name = $this->getChangedFieldName($entity);
$definition = $entity->getFieldDefinition($changed_field_name);
$config = $definition->getConfig($entity->bundle());
foreach ([FALSE, TRUE] as $translatable_changed_field) {
if ($definition->isTranslatable()) {
// For entities defining a translatable changed field we want to test
// the correct behavior of that field even if the translatability is
// revoked. In that case the changed timestamp should be synchronized
// across all translations.
$config->setTranslatable($translatable_changed_field);
$config->save();
}
elseif ($translatable_changed_field) {
// For entities defining a non-translatable changed field we cannot
// declare the field as translatable on the fly by modifying its config
// because the schema doesn't support this.
break;
}
foreach ($entity->getTranslationLanguages() as $language) {
// Ensure different timestamps.
sleep(1);
$langcode = $language->getId();
$edit = [
$this->fieldName . '[0][value]' => $this->randomString(),
];
$edit_path = $entity->urlInfo('edit-form', ['language' => $language]);
$this->drupalPostForm($edit_path, $edit, $this->getFormSubmitAction($entity, $langcode));
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$this->assertEqual(
$entity->getChangedTimeAcrossTranslations(), $entity->getTranslation($langcode)->getChangedTime(),
format_string('Changed time for language %language is the latest change over all languages.', ['%language' => $language->getName()])
);
}
$timestamps = [];
foreach ($entity->getTranslationLanguages() as $language) {
$next_timestamp = $entity->getTranslation($language->getId())->getChangedTime();
if (!in_array($next_timestamp, $timestamps)) {
$timestamps[] = $next_timestamp;
}
}
if ($translatable_changed_field) {
$this->assertEqual(
count($timestamps), count($entity->getTranslationLanguages()),
'All timestamps from all languages are different.'
);
}
else {
$this->assertEqual(
count($timestamps), 1,
'All timestamps from all languages are identical.'
);
}
}
}
/**
* Test the changed time after API and FORM save without changes.
*/
public function doTestChangedTimeAfterSaveWithoutChanges() {
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
// Test only entities, which implement the EntityChangedInterface.
if ($entity instanceof EntityChangedInterface) {
$changed_timestamp = $entity->getChangedTime();
$entity->save();
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$this->assertEqual($changed_timestamp, $entity->getChangedTime(), 'The entity\'s changed time wasn\'t updated after API save without changes.');
// Ensure different save timestamps.
sleep(1);
// Save the entity on the regular edit form.
$language = $entity->language();
$edit_path = $entity->urlInfo('edit-form', ['language' => $language]);
$this->drupalPostForm($edit_path, [], $this->getFormSubmitAction($entity, $language->getId()));
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$this->assertNotEqual($changed_timestamp, $entity->getChangedTime(), 'The entity\'s changed time was updated after form save without changes.');
}
}
}
@@ -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')]));
}
}
@@ -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.');
}
}
@@ -25,7 +25,7 @@ class ContentTranslationConfigImportTest extends KernelTestBase {
*
* @var array
*/
public static $modules = array('system', 'user', 'entity_test', 'language', 'content_translation');
public static $modules = ['system', 'user', 'entity_test', 'language', 'content_translation'];
/**
* {@inheritdoc}
@@ -58,7 +58,7 @@ class ContentTranslationConfigImportTest extends KernelTestBase {
/**
* Tests config import updates.
*/
function testConfigImportUpdates() {
public function testConfigImportUpdates() {
$entity_type_id = 'entity_test_mul';
$config_id = $entity_type_id . '.' . $entity_type_id;
$config_name = 'language.content_settings.' . $config_id;
@@ -69,22 +69,22 @@ class ContentTranslationConfigImportTest extends KernelTestBase {
$this->assertIdentical($storage->exists($config_name), FALSE, $config_name . ' not found.');
// Create new config entity.
$data = array(
$data = [
'uuid' => 'a019d89b-c4d9-4ed4-b859-894e4e2e93cf',
'langcode' => 'en',
'status' => TRUE,
'dependencies' => array(
'module' => array('content_translation')
),
'dependencies' => [
'module' => ['content_translation']
],
'id' => $config_id,
'target_entity_type_id' => 'entity_test_mul',
'target_bundle' => 'entity_test_mul',
'default_langcode' => 'site_default',
'language_alterable' => FALSE,
'third_party_settings' => array(
'content_translation' => array('enabled' => TRUE),
),
);
'third_party_settings' => [
'content_translation' => ['enabled' => TRUE],
],
];
$sync->write($config_name, $data);
$this->assertIdentical($sync->exists($config_name), TRUE, $config_name . ' found.');
@@ -16,7 +16,7 @@ class ContentTranslationSettingsApiTest extends KernelTestBase {
*
* @var array
*/
public static $modules = array('language', 'content_translation', 'user', 'entity_test');
public static $modules = ['language', 'content_translation', 'user', 'entity_test'];
/**
* {@inheritdoc}
@@ -29,7 +29,7 @@ class ContentTranslationSettingsApiTest extends KernelTestBase {
/**
* Tests that enabling translation via the API triggers schema updates.
*/
function testSettingsApi() {
public function testSettingsApi() {
$this->container->get('content_translation.manager')->setEnabled('entity_test_mul', 'entity_test_mul', TRUE);
$result =
db_field_exists('entity_test_mul_property_data', 'content_translation_source') &&
@@ -54,17 +54,17 @@ class ContentTranslationSyncUnitTest extends KernelTestBase {
*/
protected $unchangedFieldValues;
public static $modules = array('language', 'content_translation');
public static $modules = ['language', 'content_translation'];
protected function setUp() {
parent::setUp();
$this->synchronizer = new FieldTranslationSynchronizer($this->container->get('entity.manager'));
$this->synchronized = array('sync1', 'sync2');
$this->columns = array_merge($this->synchronized, array('var1', 'var2'));
$this->langcodes = array('en', 'it', 'fr', 'de', 'es');
$this->synchronized = ['sync1', 'sync2'];
$this->columns = array_merge($this->synchronized, ['var1', 'var2']);
$this->langcodes = ['en', 'it', 'fr', 'de', 'es'];
$this->cardinality = 4;
$this->unchangedFieldValues = array();
$this->unchangedFieldValues = [];
// Set up an initial set of values in the correct state, that is with
// "synchronized" values being equal.
@@ -88,7 +88,7 @@ class ContentTranslationSyncUnitTest extends KernelTestBase {
$sync_langcode = $this->langcodes[2];
$unchanged_items = $this->unchangedFieldValues[$sync_langcode];
$field_values = $this->unchangedFieldValues;
$item = array();
$item = [];
foreach ($this->columns as $column) {
$item[$column] = $this->randomMachineName();
}
@@ -140,7 +140,7 @@ class ContentTranslationSyncUnitTest extends KernelTestBase {
$sync_langcode = $this->langcodes[3];
$unchanged_items = $this->unchangedFieldValues[$sync_langcode];
$field_values = $this->unchangedFieldValues;
$field_values[$sync_langcode] = array();
$field_values[$sync_langcode] = [];
// Scramble the items.
foreach ($unchanged_items as $delta => $item) {
$new_delta = ($delta + 1) % $this->cardinality;
@@ -179,7 +179,7 @@ class ContentTranslationSyncUnitTest extends KernelTestBase {
// Determine whether the unchanged values should be altered depending on
// their delta.
$delta_callbacks = array(
$delta_callbacks = [
// Continuous field values: all values are equal.
function($delta) { return TRUE; },
// Alternated field values: only the even ones are equal.
@@ -188,7 +188,7 @@ class ContentTranslationSyncUnitTest extends KernelTestBase {
function($delta) { return $delta === 1 || $delta === 2; },
// Sparse field values: only the "extreme" ones are equal.
function($delta) { return $delta === 0 || $delta === 3; },
);
];
foreach ($delta_callbacks as $delta_callback) {
$field_values = $this->unchangedFieldValues;
@@ -69,18 +69,18 @@ class ContentTranslationManageAccessCheckTest extends UnitTestCase {
$language_manager->expects($this->at(0))
->method('getLanguage')
->with($this->equalTo($source))
->will($this->returnValue(new Language(array('id' => 'en'))));
->will($this->returnValue(new Language(['id' => 'en'])));
$language_manager->expects($this->at(1))
->method('getLanguages')
->will($this->returnValue(array('en' => array(), 'it' => array())));
->will($this->returnValue(['en' => [], 'it' => []]));
$language_manager->expects($this->at(2))
->method('getLanguage')
->with($this->equalTo($source))
->will($this->returnValue(new Language(array('id' => 'en'))));
->will($this->returnValue(new Language(['id' => 'en'])));
$language_manager->expects($this->at(3))
->method('getLanguage')
->with($this->equalTo($target))
->will($this->returnValue(new Language(array('id' => 'it'))));
->will($this->returnValue(new Language(['id' => 'it'])));
// Set the mock entity. We need to use ContentEntityBase for mocking due to
// issues with phpunit and multiple interfaces.
@@ -92,7 +92,7 @@ class ContentTranslationManageAccessCheckTest extends UnitTestCase {
$entity->expects($this->once())
->method('getTranslationLanguages')
->with()
->will($this->returnValue(array()));
->will($this->returnValue([]));
$entity->expects($this->once())
->method('getCacheContexts')
->willReturn([]);
@@ -101,10 +101,10 @@ class ContentTranslationManageAccessCheckTest extends UnitTestCase {
->willReturn(Cache::PERMANENT);
$entity->expects($this->once())
->method('getCacheTags')
->will($this->returnValue(array('node:1337')));
->will($this->returnValue(['node:1337']));
$entity->expects($this->once())
->method('getCacheContexts')
->willReturn(array());
->willReturn([]);
// Set the route requirements.
$route = new Route('test_route');
@@ -12,25 +12,25 @@ use Drupal\Tests\Core\Menu\LocalTaskIntegrationTestBase;
class ContentTranslationLocalTasksTest extends LocalTaskIntegrationTestBase {
protected function setUp() {
$this->directoryList = array(
$this->directoryList = [
'content_translation' => 'core/modules/content_translation',
'node' => 'core/modules/node',
);
];
parent::setUp();
$entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
$entity_type->expects($this->any())
->method('getLinkTemplate')
->will($this->returnValueMap(array(
array('canonical', 'entity.node.canonical'),
array('drupal:content-translation-overview', 'entity.node.content_translation_overview'),
)));
->will($this->returnValueMap([
['canonical', 'entity.node.canonical'],
['drupal:content-translation-overview', 'entity.node.content_translation_overview'],
]));
$content_translation_manager = $this->getMock('Drupal\content_translation\ContentTranslationManagerInterface');
$content_translation_manager->expects($this->any())
->method('getSupportedEntityTypes')
->will($this->returnValue(array(
->will($this->returnValue([
'node' => $entity_type,
)));
]));
\Drupal::getContainer()->set('content_translation.manager', $content_translation_manager);
\Drupal::getContainer()->set('string_translation', $this->getStringTranslationStub());
}
@@ -48,22 +48,22 @@ class ContentTranslationLocalTasksTest extends LocalTaskIntegrationTestBase {
* Provides a list of routes to test.
*/
public function providerTestBlockAdminDisplay() {
return array(
array('entity.node.canonical', array(array(
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',
))),
array('entity.node.content_translation_overview', array(array(
]]],
['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',
))),
);
]]],
];
}
}