366 lines
15 KiB
PHP
366 lines
15 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @file
|
|
* The content translation administration forms.
|
|
*/
|
|
|
|
use Drupal\Core\Entity\EntityInterface;
|
|
use Drupal\Core\Field\FieldDefinitionInterface;
|
|
use Drupal\Core\Language\Language;
|
|
use Drupal\field\Field as FieldService;
|
|
|
|
/**
|
|
* Returns a form element to configure field synchronization.
|
|
*
|
|
* @param \Drupal\Core\Field\FieldDefinitionInterface $field
|
|
* A field definition object.
|
|
*
|
|
* @return array
|
|
* A form element to configure field synchronization.
|
|
*/
|
|
function content_translation_field_sync_widget(FieldDefinitionInterface $field) {
|
|
$element = array();
|
|
|
|
$column_groups = $field->getSetting('column_groups');
|
|
if (!empty($column_groups) && count($column_groups) > 1) {
|
|
$options = array();
|
|
$default = array();
|
|
|
|
foreach ($column_groups as $group => $info) {
|
|
$options[$group] = $info['label'];
|
|
$default[$group] = !empty($info['translatable']) ? $group : FALSE;
|
|
}
|
|
|
|
$settings = array('dependent_selectors' => array('instance[settings][translation_sync]' => array('file')));
|
|
|
|
$translation_sync = $field->getSetting('translation_sync');
|
|
$element = array(
|
|
'#type' => 'checkboxes',
|
|
'#title' => t('Translatable elements'),
|
|
'#options' => $options,
|
|
'#default_value' => !empty($translation_sync) ? $translation_sync : $default,
|
|
'#attached' => array(
|
|
'library' => array(
|
|
array('content_translation', 'drupal.content_translation.admin'),
|
|
),
|
|
'js' => array(
|
|
array('data' => array('contentTranslationDependentOptions' => $settings), 'type' => 'setting'),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
return $element;
|
|
}
|
|
|
|
/**
|
|
* (proxied) Implements hook_form_FORM_ID_alter().
|
|
*/
|
|
function _content_translation_form_language_content_settings_form_alter(array &$form, array &$form_state) {
|
|
// Inject into the content language settings the translation settings if the
|
|
// user has the required permission.
|
|
if (!user_access('administer content translation')) {
|
|
return;
|
|
}
|
|
|
|
$default = $form['entity_types']['#default_value'];
|
|
foreach ($default as $entity_type => $enabled) {
|
|
$default[$entity_type] = $enabled || content_translation_enabled($entity_type) ? $entity_type : FALSE;
|
|
}
|
|
$form['entity_types']['#default_value'] = $default;
|
|
|
|
$form['#attached']['library'][] = array('content_translation', 'drupal.content_translation.admin');
|
|
$form['#attached']['js'][] = array('data' => drupal_get_path('module', 'content_translation') . '/content_translation.admin.js', 'type' => 'file');
|
|
|
|
$dependent_options_settings = array();
|
|
$entity_manager = Drupal::entityManager();
|
|
foreach ($form['#labels'] as $entity_type => $label) {
|
|
$entity_info = \Drupal::entityManager()->getDefinition($entity_type);
|
|
foreach (entity_get_bundles($entity_type) 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.
|
|
$form['settings'][$entity_type][$bundle]['settings']['language']['#content_translation_skip_alter'] = TRUE;
|
|
|
|
// Only show the checkbox to enable translation if the bundles in the
|
|
// entity might have fields and if there are fields to translate.
|
|
if ($entity_info->isFieldable()) {
|
|
$fields = $entity_manager->getFieldDefinitions($entity_type, $bundle);
|
|
if ($fields) {
|
|
$form['settings'][$entity_type][$bundle]['translatable'] = array(
|
|
'#type' => 'checkbox',
|
|
'#default_value' => content_translation_enabled($entity_type, $bundle),
|
|
);
|
|
|
|
$field_settings = content_translation_get_config($entity_type, $bundle, 'fields');
|
|
foreach ($fields as $field_name => $definition) {
|
|
$translatable = !empty($field_settings[$field_name]);
|
|
|
|
// We special case Field API fields as they always natively support
|
|
// translation.
|
|
// @todo Remove this special casing as soon as configurable and
|
|
// base field definitions are "unified".
|
|
if ($definition->isConfigurable() && ($field = FieldService::fieldInfo()->getField($entity_type, $field_name))) {
|
|
$instance = FieldService::fieldInfo()->getInstance($entity_type, $bundle, $field_name);
|
|
$form['settings'][$entity_type][$bundle]['fields'][$field_name] = array(
|
|
'#label' => $instance->getLabel(),
|
|
'#type' => 'checkbox',
|
|
'#default_value' => $translatable,
|
|
);
|
|
$column_element = content_translation_field_sync_widget($instance);
|
|
if ($column_element) {
|
|
$form['settings'][$entity_type][$bundle]['columns'][$field_name] = $column_element;
|
|
|
|
// @todo This should not concern only files.
|
|
if (isset($column_element['#options']['file'])) {
|
|
$dependent_options_settings["settings[{$entity_type}][{$bundle}][columns][{$field_name}]"] = array('file');
|
|
}
|
|
}
|
|
}
|
|
// Instead we need to rely on field definitions to determine whether
|
|
// fields support translation. Whether they are actually enabled is
|
|
// determined through our settings. As a consequence only fields
|
|
// that support translation can be enabled or disabled.
|
|
elseif (isset($field_settings[$field_name]) || $definition->isTranslatable()) {
|
|
$form['settings'][$entity_type][$bundle]['fields'][$field_name] = array(
|
|
'#label' => $definition->getLabel(),
|
|
'#type' => 'checkbox',
|
|
'#default_value' => $translatable,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
$settings = array('dependent_selectors' => $dependent_options_settings);
|
|
$form['#attached']['js'][] = array('data' => array('contentTranslationDependentOptions' => $settings), 'type' => 'setting');
|
|
$form['#validate'][] = 'content_translation_form_language_content_settings_validate';
|
|
$form['#submit'][] = 'content_translation_form_language_content_settings_submit';
|
|
}
|
|
|
|
/**
|
|
* (proxied) Implements hook_preprocess_HOOK();
|
|
*/
|
|
function _content_translation_preprocess_language_content_settings_table(&$variables) {
|
|
// Alter the 'build' variable injecting the translation settings if the user
|
|
// has the required permission.
|
|
if (!user_access('administer content translation')) {
|
|
return;
|
|
}
|
|
|
|
$element = $variables['element'];
|
|
$build = &$variables['build'];
|
|
|
|
array_unshift($build['#header'], array('data' => t('Translatable'), 'class' => array('translatable')));
|
|
$rows = array();
|
|
|
|
foreach (element_children($element) as $bundle) {
|
|
$field_names = !empty($element[$bundle]['fields']) ? element_children($element[$bundle]['fields']) : array();
|
|
if (!empty($element[$bundle]['translatable'])) {
|
|
$checkbox_id = $element[$bundle]['translatable']['#id'];
|
|
}
|
|
$rows[$bundle] = $build['#rows'][$bundle];
|
|
|
|
if (!empty($element[$bundle]['translatable'])) {
|
|
$translatable = array(
|
|
'data' => $element[$bundle]['translatable'],
|
|
'class' => array('translatable'),
|
|
);
|
|
array_unshift($rows[$bundle]['data'], $translatable);
|
|
|
|
$rows[$bundle]['data'][1]['data']['#prefix'] = '<label for="' . $checkbox_id . '">';
|
|
}
|
|
else {
|
|
$translatable = array(
|
|
'class' => array('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(
|
|
'data' => drupal_render($field_element),
|
|
'class' => array('translatable'),
|
|
),
|
|
array(
|
|
'data' => array(
|
|
'#prefix' => '<label for="' . $field_element['#id'] . '">',
|
|
'#suffix' => '</label>',
|
|
'bundle' => array(
|
|
'#prefix' => '<span class="visually-hidden">',
|
|
'#suffix' => '</span> ',
|
|
'#markup' => check_plain($element[$bundle]['settings']['#label']),
|
|
),
|
|
'field' => array(
|
|
'#markup' => check_plain($field_element['#label']),
|
|
),
|
|
),
|
|
'class' => array('field'),
|
|
),
|
|
array(
|
|
'data' => '',
|
|
'class' => array('operations'),
|
|
),
|
|
),
|
|
'class' => array('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(
|
|
'data' => drupal_render($column_element[$key]),
|
|
'class' => array('translatable'),
|
|
),
|
|
array(
|
|
'data' => array(
|
|
'#prefix' => '<label for="' . $column_element[$key]['#id'] . '">',
|
|
'#suffix' => '</label>',
|
|
'bundle' => array(
|
|
'#prefix' => '<span class="visually-hidden">',
|
|
'#suffix' => '</span> ',
|
|
'#markup' => check_plain($element[$bundle]['settings']['#label']),
|
|
),
|
|
'field' => array(
|
|
'#prefix' => '<span class="visually-hidden">',
|
|
'#suffix' => '</span> ',
|
|
'#markup' => check_plain($field_element['#label']),
|
|
),
|
|
'columns' => array(
|
|
'#markup' => check_plain($column_label),
|
|
),
|
|
),
|
|
'class' => array('column'),
|
|
),
|
|
array(
|
|
'data' => '',
|
|
'class' => array('operations'),
|
|
),
|
|
),
|
|
'class' => array('column-settings'),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$build['#rows'] = $rows;
|
|
}
|
|
|
|
/**
|
|
* Form validation handler for content_translation_admin_settings_form().
|
|
*
|
|
* @see content_translation_admin_settings_form_submit()
|
|
*/
|
|
function content_translation_form_language_content_settings_validate(array $form, array &$form_state) {
|
|
$settings = &$form_state['values']['settings'];
|
|
foreach ($settings as $entity_type => $entity_settings) {
|
|
foreach ($entity_settings as $bundle => $bundle_settings) {
|
|
if (!empty($bundle_settings['translatable'])) {
|
|
$name = "settings][$entity_type][$bundle][translatable";
|
|
|
|
$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']);
|
|
form_set_error($name, $form_state, t('At least one field needs to be translatable to enable %bundle for translation.', $t_args));
|
|
}
|
|
|
|
$values = $bundle_settings['settings']['language'];
|
|
if (empty($values['language_show']) && \Drupal::languageManager()->isLanguageLocked($values['langcode'])) {
|
|
foreach (language_list(Language::STATE_LOCKED) as $language) {
|
|
$locked_languages[] = $language->name;
|
|
}
|
|
form_set_error($name, $form_state, t('Translation is not supported if language is always one of: @locked_languages', array('@locked_languages' => implode(', ', $locked_languages))));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Form submission handler for content_translation_admin_settings_form().
|
|
*
|
|
* @see content_translation_admin_settings_form_validate()
|
|
*/
|
|
function content_translation_form_language_content_settings_submit(array $form, array &$form_state) {
|
|
$entity_types = $form_state['values']['entity_types'];
|
|
$settings = &$form_state['values']['settings'];
|
|
|
|
// If an entity type is not translatable all its bundles and fields must be
|
|
// marked as non-translatable. Similarly, if a bundle is made non-translatable
|
|
// all of its fields will be not translatable.
|
|
foreach ($settings as $entity_type => &$entity_settings) {
|
|
foreach ($entity_settings as &$bundle_settings) {
|
|
if (!empty($bundle_settings['translatable'])) {
|
|
$bundle_settings['translatable'] = $bundle_settings['translatable'] && $entity_types[$entity_type];
|
|
}
|
|
if (!empty($bundle_settings['fields'])) {
|
|
foreach ($bundle_settings['fields'] as $field_name => $translatable) {
|
|
$bundle_settings['fields'][$field_name] = $translatable && $bundle_settings['translatable'];
|
|
// If we have column settings and no column is translatable, no point
|
|
// in making the field translatable.
|
|
if (isset($bundle_settings['columns'][$field_name]) && !array_filter($bundle_settings['columns'][$field_name])) {
|
|
$bundle_settings['fields'][$field_name] = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
_content_translation_update_field_translatability($settings);
|
|
drupal_set_message(t('Settings successfully updated.'));
|
|
}
|
|
|
|
/**
|
|
* Stores content translation settings.
|
|
*
|
|
* @param array $settings
|
|
* An associative array of settings keyed by entity type and bundle. At bundle
|
|
* level the following keys are available:
|
|
* - translatable: The bundle translatability status, which is a bool.
|
|
* - settings: An array of language configuration settings as defined by
|
|
* language_save_default_configuration().
|
|
* - fields: An associative array with field names as keys and a boolean as
|
|
* value, indicating field translatability.
|
|
*/
|
|
function _content_translation_update_field_translatability($settings) {
|
|
// Update translatability for configurable fields.
|
|
// @todo Remove this once configurable fields rely on entity field info to
|
|
// track translatability. See https://drupal.org/node/2018685.
|
|
foreach ($settings as $entity_type => $entity_settings) {
|
|
$fields = array();
|
|
foreach ($entity_settings as $bundle_settings) {
|
|
// Collapse field settings since here we have per instance settings, but
|
|
// translatability has per-field scope. We assume that all the field
|
|
// instances have the same value.
|
|
if (!empty($bundle_settings['fields'])) {
|
|
foreach ($bundle_settings['fields'] as $field_name => $translatable) {
|
|
// If translatability changes for at least one field instance we need
|
|
// to switch field translatability.
|
|
$field = FieldService::fieldInfo()->getField($entity_type, $field_name);
|
|
if ($field && $field->isTranslatable() !== $translatable) {
|
|
$fields[$field_name] = $translatable;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Store updated fields.
|
|
foreach ($fields as $field_name => $translatable) {
|
|
$field = FieldService::fieldInfo()->getField($entity_type, $field_name);
|
|
$field->translatable = $translatable;
|
|
$field->save();
|
|
}
|
|
}
|
|
|
|
content_translation_save_settings($settings);
|
|
}
|