content_translation.admin.inc 14.6 KB
Newer Older
1 2 3 4
<?php

/**
 * @file
5
 * The content translation administration forms.
6 7
 */

8
use Drupal\Component\Utility\SafeMarkup;
9
use Drupal\Core\Config\Entity\ThirdPartySettingsInterface;
10
use Drupal\Core\Entity\ContentEntityTypeInterface;
11
use Drupal\Core\Field\FieldDefinitionInterface;
12
use Drupal\Core\Form\FormStateInterface;
13
use Drupal\Core\Language\LanguageInterface;
14
use Drupal\Core\Render\Element;
15

16
/**
17
 * Returns a form element to configure field synchronization.
18
 *
19
 * @param \Drupal\Core\Field\FieldDefinitionInterface $field
20
 *   A field definition object.
21 22 23 24
 *
 * @return array
 *   A form element to configure field synchronization.
 */
25
function content_translation_field_sync_widget(FieldDefinitionInterface $field) {
26 27 28 29
  // No way to store field sync information on this field.
  if (!($field instanceof ThirdPartySettingsInterface)) {
    return array();
  }
30

31
  $element = array();
32 33
  $definition = \Drupal::service('plugin.manager.field.field_type')->getDefinition($field->getType());
  $column_groups = $definition['column_groups'];
34
  if (!empty($column_groups) && count($column_groups) > 1) {
35 36 37
    $options = array();
    $default = array();

38
    foreach ($column_groups as $group => $info) {
39 40 41 42
      $options[$group] = $info['label'];
      $default[$group] = !empty($info['translatable']) ? $group : FALSE;
    }

43 44
    $settings = array('dependent_selectors' => array('instance[third_party_settings][content_translation][translation_sync]' => array('file')));
    $default = $field->getThirdPartySetting('content_translation', 'translation_sync', $default);
45

46 47 48 49
    $element = array(
      '#type' => 'checkboxes',
      '#title' => t('Translatable elements'),
      '#options' => $options,
50
      '#default_value' => $default,
51
      '#attached' => array(
52
        'library' => array(
53
          'content_translation/drupal.content_translation.admin',
54
        ),
55 56 57
        'drupalSettings' => [
          'contentTranslationDependentOptions' => $settings,
        ],
58
      ),
59 60 61 62 63 64 65 66
    );
  }

  return $element;
}

/**
 * (proxied) Implements hook_form_FORM_ID_alter().
67
 */
68
function _content_translation_form_language_content_settings_form_alter(array &$form, FormStateInterface $form_state) {
69 70
  // Inject into the content language settings the translation settings if the
  // user has the required permission.
71
  if (!\Drupal::currentUser()->hasPermission('administer content translation')) {
72 73 74
    return;
  }

75
  $content_translation_manager = \Drupal::service('content_translation.manager');
76
  $default = $form['entity_types']['#default_value'];
77
  foreach ($default as $entity_type_id => $enabled) {
78
    $default[$entity_type_id] = $enabled || $content_translation_manager->isEnabled($entity_type_id) ? $entity_type_id : FALSE;
79 80 81
  }
  $form['entity_types']['#default_value'] = $default;

82
  $form['#attached']['library'][] = 'content_translation/drupal.content_translation.admin';
83

84
  $dependent_options_settings = array();
85
  $entity_manager = Drupal::entityManager();
86
  foreach ($form['#labels'] as $entity_type_id => $label) {
87 88 89
    $entity_type = $entity_manager->getDefinition($entity_type_id);
    $storage_definitions = $entity_type instanceof ContentEntityTypeInterface ? $entity_manager->getFieldStorageDefinitions($entity_type_id) : array();

90
    $entity_type_translatable = $content_translation_manager->isSupported($entity_type_id);
91
    foreach (entity_get_bundles($entity_type_id) as $bundle => $bundle_info) {
92 93
      // 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
94 95
      // to be able to skip alterations. Alter the title and display the message
      // about UI integration.
96
      $form['settings'][$entity_type_id][$bundle]['settings']['language']['#content_translation_skip_alter'] = TRUE;
97
      if (!$entity_type_translatable) {
98
        $form['settings'][$entity_type_id]['#title'] = t('@label (Translation is not supported).', array('@label' => $entity_type->getLabel()));
99 100
        continue;
      }
101

102 103 104 105
      $fields = $entity_manager->getFieldDefinitions($entity_type_id, $bundle);
      if ($fields) {
        foreach ($fields as $field_name => $definition) {
          // Allow to configure only fields supporting multilingual storage.
106
          // We skip our own fields as they are always translatable.
107
          if (!empty($storage_definitions[$field_name]) && $storage_definitions[$field_name]->isTranslatable() && $storage_definitions[$field_name]->getProvider() != 'content_translation' && $field_name != $entity_type->getKey('langcode') && $field_name != $entity_type->getKey('default_langcode')) {
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
            $form['settings'][$entity_type_id][$bundle]['fields'][$field_name] = array(
              '#label' => $definition->getLabel(),
              '#type' => 'checkbox',
              '#default_value' => $definition->isTranslatable(),
            );
            // Display the column translatability configuration widget.
            $column_element = content_translation_field_sync_widget($definition);
            if ($column_element) {
              $form['settings'][$entity_type_id][$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_id}][{$bundle}][columns][{$field_name}]"] = array('file');
              }
            }
          }
        }
        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.
127
          $form['settings'][$entity_type_id][$bundle]['translatable'] = array(
128
            '#type' => 'checkbox',
129
            '#default_value' => $content_translation_manager->isEnabled($entity_type_id, $bundle),
130
          );
131
        }
132 133 134
      }
    }
  }
135

136
  $settings = array('dependent_selectors' => $dependent_options_settings);
137
  $form['#attached']['drupalSettings']['contentTranslationDependentOptions'] = $settings;
138 139
  $form['#validate'][] = 'content_translation_form_language_content_settings_validate';
  $form['#submit'][] = 'content_translation_form_language_content_settings_submit';
140 141
}

142 143 144
/**
 * (proxied) Implements hook_preprocess_HOOK();
 */
145
function _content_translation_preprocess_language_content_settings_table(&$variables) {
146 147
  // Alter the 'build' variable injecting the translation settings if the user
  // has the required permission.
148
  if (!\Drupal::currentUser()->hasPermission('administer content translation')) {
149 150 151 152 153 154 155 156 157
    return;
  }

  $element = $variables['element'];
  $build = &$variables['build'];

  array_unshift($build['#header'], array('data' => t('Translatable'), 'class' => array('translatable')));
  $rows = array();

158 159
  foreach (Element::children($element) as $bundle) {
    $field_names = !empty($element[$bundle]['fields']) ? Element::children($element[$bundle]['fields']) : array();
160 161 162
    if (!empty($element[$bundle]['translatable'])) {
      $checkbox_id = $element[$bundle]['translatable']['#id'];
    }
163 164
    $rows[$bundle] = $build['#rows'][$bundle];

165 166 167 168 169 170
    if (!empty($element[$bundle]['translatable'])) {
      $translatable = array(
        'data' => $element[$bundle]['translatable'],
        'class' => array('translatable'),
      );
      array_unshift($rows[$bundle]['data'], $translatable);
171

172 173 174 175
      $rows[$bundle]['data'][1]['data']['#prefix'] = '<label for="' . $checkbox_id . '">';
    }
    else {
      $translatable = array(
176
        'data' => t('N/A'),
177 178 179 180
        'class' => array('untranslatable'),
      );
      array_unshift($rows[$bundle]['data'], $translatable);
    }
181 182 183 184 185 186 187 188 189 190 191 192 193 194

    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(
195
                '#prefix' => '<span class="visually-hidden">',
196
                '#suffix' => '</span> ',
197
                '#markup' => SafeMarkup::checkPlain($element[$bundle]['settings']['#label']),
198 199
              ),
              'field' => array(
200
                '#markup' => SafeMarkup::checkPlain($field_element['#label']),
201 202 203 204 205 206 207 208 209 210 211 212 213 214
              ),
            ),
            '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];
215
        foreach (Element::children($column_element) as $key) {
216 217 218 219 220 221 222 223 224 225 226 227 228
          $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(
229
                    '#prefix' => '<span class="visually-hidden">',
230
                    '#suffix' => '</span> ',
231
                    '#markup' => SafeMarkup::checkPlain($element[$bundle]['settings']['#label']),
232 233
                  ),
                  'field' => array(
234
                    '#prefix' => '<span class="visually-hidden">',
235
                    '#suffix' => '</span> ',
236
                    '#markup' => SafeMarkup::checkPlain($field_element['#label']),
237 238
                  ),
                  'columns' => array(
239
                    '#markup' => SafeMarkup::checkPlain($column_label),
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
                  ),
                ),
                'class' => array('column'),
              ),
              array(
                'data' => '',
                'class' => array('operations'),
              ),
            ),
            'class' => array('column-settings'),
          );
        }
      }
    }
  }

  $build['#rows'] = $rows;
}

259
/**
260
 * Form validation handler for content_translation_admin_settings_form().
261
 *
262
 * @see content_translation_admin_settings_form_submit()
263
 */
264
function content_translation_form_language_content_settings_validate(array $form, FormStateInterface $form_state) {
265
  $settings = &$form_state->getValue('settings');
266 267 268 269 270 271 272 273
  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']);
274
          $form_state->setErrorByName($name, t('At least one field needs to be translatable to enable %bundle for translation.', $t_args));
275 276 277
        }

        $values = $bundle_settings['settings']['language'];
278
        if (empty($values['language_alterable']) && \Drupal::languageManager()->isLanguageLocked($values['langcode'])) {
279
          foreach (\Drupal::languageManager()->getLanguages(LanguageInterface::STATE_LOCKED) as $language) {
280
            $locked_languages[] = $language->getName();
281
          }
282
          $form_state->setErrorByName($name, t('Translation is not supported if language is always one of: @locked_languages', array('@locked_languages' => implode(', ', $locked_languages))));
283 284 285 286 287 288 289
        }
      }
    }
  }
}

/**
290
 * Form submission handler for content_translation_admin_settings_form().
291
 *
292
 * @see content_translation_admin_settings_form_validate()
293
 */
294
function content_translation_form_language_content_settings_submit(array $form, FormStateInterface $form_state) {
295 296
  $entity_types = $form_state->getValue('entity_types');
  $settings = &$form_state->getValue('settings');
297 298 299 300

  // 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.
301 302
  foreach ($settings as $entity_type_id => &$entity_settings) {
    foreach ($entity_settings as $bundle => &$bundle_settings) {
303
      $fields = \Drupal::entityManager()->getFieldDefinitions($entity_type_id, $bundle);
304
      if (!empty($bundle_settings['translatable'])) {
305
        $bundle_settings['translatable'] = $bundle_settings['translatable'] && $entity_types[$entity_type_id];
306
      }
307 308
      if (!empty($bundle_settings['fields'])) {
        foreach ($bundle_settings['fields'] as $field_name => $translatable) {
309
          $translatable = $translatable && $bundle_settings['translatable'];
310 311 312
          // 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])) {
313
            $translatable = FALSE;
314
          }
315 316 317 318 319
          $field_config = $fields[$field_name]->getConfig($bundle);
          if ($field_config->isTranslatable() != $translatable) {
            $field_config
              ->setTranslatable($translatable)
              ->save();
320
          }
321 322
        }
      }
323 324
      if (isset($bundle_settings['translatable'])) {
        // Store whether a bundle has translation enabled or not.
325
        \Drupal::service('content_translation.manager')->setEnabled($entity_type_id, $bundle, $bundle_settings['translatable']);
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342

        // Save translation_sync settings.
        if (!empty($bundle_settings['columns'])) {
          foreach ($bundle_settings['columns'] as $field_name => $column_settings) {
            $field_config = $fields[$field_name]->getConfig($bundle);
            if ($field_config->isTranslatable()) {
              $field_config->setThirdPartySetting('content_translation', 'translation_sync', $column_settings);
            }
            // If the field does not have translatable enabled we need to reset
            // the sync settings to their defaults.
            else {
              $field_config->unsetThirdPartySetting('content_translation', 'translation_sync');
            }
            $field_config->save();
          }
        }
      }
343 344
    }
  }
345 346 347 348
  // Ensure entity and menu router information are correctly rebuilt.
  \Drupal::entityManager()->clearCachedDefinitions();
  \Drupal::service('router.builder')->setRebuildNeeded();

349
}