diff --git a/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php b/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php index 699742d..585264c 100644 --- a/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php +++ b/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php @@ -166,10 +166,10 @@ public function buildForm(array $form, FormStateInterface $form_state, Request $ $form['#attached']['library'][] = 'config_translation/drupal.config_translation.admin'; - $form['config_names'] = array( - '#type' => 'container', - '#tree' => TRUE, - ); + // Even though this is a nested form, we do not set #tree to TRUE because + // the form value structure is generated by using #parents for each element. + // @see \Drupal\config_translation\FormElement\FormElementBase::getElements() + $form['config_names'] = array('#type' => 'container'); foreach ($this->mapper->getConfigNames() as $name) { $form['config_names'][$name] = array('#type' => 'container'); $form['config_names'][$name] += $this->buildConfigForm($name, $this->typedConfigManager->get($name), $this->baseConfigData[$name], $config_factory->get($name)->get()); @@ -256,16 +256,7 @@ protected function buildConfigForm($name, ArrayElement $schema, $source_config, if ($form_element = $this->createFormElement($schema)) { $parents = array('config_names', $name); - $key = ''; - $build = $form_element->getElements($this->sourceLanguage, $this->language, $source_config, $translation_config, $key, $parents); - } - else { - $definition = $schema->getDefinition(); - $this->logger('config_translation')->notice("No form element class exists for config schema definition '@name'", array('@name', $definition->getLabel())); - } - - if ($build) { - $build[$key]['#open'] = TRUE; + $build = $form_element->getElements($this->sourceLanguage, $this->language, $source_config, $translation_config, $parents); } return $build; @@ -277,19 +268,19 @@ protected function buildConfigForm($name, ArrayElement $schema, $source_config, * @param \Drupal\Core\TypedData\TypedDataInterface $schema * Schema definition of configuration. * - * @return \Drupal\config_translation\FormElement\ElementInterface|NULL + * @return \Drupal\config_translation\FormElement\ElementInterface|null * The element builder object if possible. */ public static function createFormElement(TypedDataInterface $schema) { $definition = $schema->getDataDefinition(); - if (!empty($definition['translatable']) || $schema instanceof ArrayElement) { + // Form element classes can be specified even for non-translatable elements + // such as the ListElement form element which used for for Mapping and List + // schema elements. + if (isset($definition['form_element_class'])) { if (!$definition->getLabel()) { $definition->setLabel(t('n/a')); } - if (isset($definition['form_element_class'])) { - /** @var \Drupal\config_translation\FormElement\ElementInterface $form_element */ - return new $definition['form_element_class']($schema); - } + return new $definition['form_element_class']($schema); } } diff --git a/core/modules/config_translation/src/FormElement/DateFormat.php b/core/modules/config_translation/src/FormElement/DateFormat.php index 49e0c12..729afee 100644 --- a/core/modules/config_translation/src/FormElement/DateFormat.php +++ b/core/modules/config_translation/src/FormElement/DateFormat.php @@ -27,7 +27,6 @@ public function getTranslationElement(LanguageInterface $translation_language, $ return array( '#type' => 'textfield', - '#title' => $this->t($definition->getLabel()) . ' (' . $language->name . ')', '#description' => $description, '#field_suffix' => '
' . $format . '
', '#ajax' => array( diff --git a/core/modules/config_translation/src/FormElement/ElementInterface.php b/core/modules/config_translation/src/FormElement/ElementInterface.php index 47cd387..d0df738 100644 --- a/core/modules/config_translation/src/FormElement/ElementInterface.php +++ b/core/modules/config_translation/src/FormElement/ElementInterface.php @@ -28,15 +28,16 @@ * The configuration value of the element in the source language. * @param mixed $translation_config * The configuration value of the element in the language to translate to. - * @param string $base_key - * Base key to be used for the elements in the form. * @param array $parents * Parents array for the element in the form. + * @param string $base_key + * (optional) Base key to be used for the elements in the form. NULL for + * top-level form elements. * * @return array * A render array for the source value. */ - public function getElements(LanguageInterface $source_language, LanguageInterface $translation_language, $source_config, $translation_config, $base_key, $parents); + public function getElements(LanguageInterface $source_language, LanguageInterface $translation_language, $source_config, $translation_config, $parents, $base_key = NULL); /** * Sets configuration based on a nested form value array. diff --git a/core/modules/config_translation/src/FormElement/FormElementBase.php b/core/modules/config_translation/src/FormElement/FormElementBase.php index 3881346..9caa7f7 100644 --- a/core/modules/config_translation/src/FormElement/FormElementBase.php +++ b/core/modules/config_translation/src/FormElement/FormElementBase.php @@ -50,8 +50,8 @@ public function __construct(TypedDataInterface $element) { /** * {@inheritdoc} */ - public function getElements(LanguageInterface $source_language, LanguageInterface $translation_language, $source_config, $translation_config, $base_key, $parents) { - $build[$base_key]['#theme'] = 'config_translation_manage_form_element'; + public function getElements(LanguageInterface $source_language, LanguageInterface $translation_language, $source_config, $translation_config, $parents, $base_key = NULL) { + $build['#theme'] = 'config_translation_manage_form_element'; // For accessibility we make source and translation appear next to each // other in the source for each element, which is why we utilize the @@ -61,11 +61,11 @@ public function getElements(LanguageInterface $source_language, LanguageInterfac // configuration values in ConfigTranslationFormBase::setConfig(). // Therefore we make the 'source' and 'translation' keys the top-level // keys in $form_state['values']. - $build[$base_key]['source'] = $this->getSourceElement($source_language, $source_config); - $build[$base_key]['translation'] = $this->getTranslationElement($translation_language, $source_config, $translation_config); + $build['source'] = $this->getSourceElement($source_language, $source_config); + $build['translation'] = $this->getTranslationElement($translation_language, $source_config, $translation_config); - $build[$base_key]['source']['#parents'] = array_merge(array('source'), $parents); - $build[$base_key]['translation']['#parents'] = array_merge(array('translation'), $parents); + $build['source']['#parents'] = array_merge(array('source'), $parents); + $build['translation']['#parents'] = array_merge(array('translation'), $parents); return $build; } diff --git a/core/modules/config_translation/src/FormElement/ListElement.php b/core/modules/config_translation/src/FormElement/ListElement.php index 0314a3c..18c0687 100644 --- a/core/modules/config_translation/src/FormElement/ListElement.php +++ b/core/modules/config_translation/src/FormElement/ListElement.php @@ -8,17 +8,19 @@ namespace Drupal\config_translation\FormElement; use Drupal\Core\Config\Config; -use Drupal\Core\Config\Schema\Element; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Config\Schema\ArrayElement; use Drupal\config_translation\Form\ConfigTranslationFormBase; -use Drupal\Core\TypedData\TypedDataInterface; +use Drupal\Core\StringTranslation\StringTranslationTrait; +use Drupal\Core\TypedData\DataDefinitionInterface; use Drupal\language\Config\LanguageConfigOverride; /** * Defines the list element for the configuration translation interface. */ -class ListElement extends FormElementBase { +class ListElement implements ElementInterface { + + use StringTranslationTrait; /** * The schema element this form is for. @@ -28,19 +30,27 @@ class ListElement extends FormElementBase { protected $element; /** + * The data definition of the element this form element is for. + * + * @var \Drupal\Core\TypedData\ListDataDefinitionInterface + */ + protected $definition; + + /** * Constructs a FormElementBase. * * @param \Drupal\Core\Config\Schema\ArrayElement $element * The schema element this form element is for. */ public function __construct(ArrayElement $element) { - parent::__construct($element); + $this->element = $element; + $this->definition = $element->getDataDefinition(); } /** * {@inheritdoc} */ - public function getElements(LanguageInterface $source_language, LanguageInterface $translation_language, $source_config, $translation_config, $base_key, $parents) { + public function getElements(LanguageInterface $source_language, LanguageInterface $translation_language, $source_config, $translation_config, $parents, $base_key = NULL) { $build = array(); foreach ($this->element as $key => $element) { $sub_build = array(); @@ -67,39 +77,23 @@ public function getElements(LanguageInterface $source_language, LanguageInterfac // Therefore we make the 'source' and 'translation' keys the top-level // keys in $form_state['values']. $element_parents = array_merge($parents, array($key)); - $sub_build += $form_element->getElements($source_language, $translation_language, $source_config[$key], $translation_config[$key], $element_key, $element_parents); + $sub_build += $form_element->getElements($source_language, $translation_language, $source_config[$key], $translation_config[$key], $element_parents, $element_key); + + if (empty($sub_build)) { + continue; + } + // Build sub-structure and include it with a wrapper in the form // if there are any translatable elements there. - if (!empty($sub_build) && $element instanceof ArrayElement) { - // For some configuration elements the same element structure can - // repeat multiple times, (like views displays, filters, etc.). - // So try to find a more usable title for the details summary. First - // check if there is an element which is called title or label, then - // check if there is an element which contains these words. - $title = ''; - if (isset($sub_build['title']['source'])) { - $title = $sub_build['title']['source']['#markup']; - } - elseif (isset($sub_build['label']['source'])) { - $title = $sub_build['label']['source']['#markup']; - } - else { - foreach (array_keys($sub_build) as $title_key) { - if (isset($sub_build[$title_key]['source']) && (strpos($title_key, 'title') !== FALSE || strpos($title_key, 'label') !== FALSE)) { - $title = $sub_build[$title_key]['source']['#markup']; - break; - } - } - } - $build[$base_key] = array( + $build[$key] = array(); + if ($element instanceof ArrayElement) { + $build[$key] = array( '#type' => 'details', - '#title' => (!empty($title) ? (strip_tags($title) . ' ') : '') . $this->t($definition['label']), - '#open' => FALSE, - ) + $sub_build; - } - else { - $build += $sub_build; + '#title' => $this->getGroupTitle($definition, $sub_build), + '#open' => empty($base_key), + ); } + $build[$key] += $sub_build; } } @@ -126,4 +120,41 @@ public function setConfig(Config $base_config, LanguageConfigOverride $config_tr } } + /** + * Returns the title for the 'details' element of a group of schema elements. + * + * For some configuration elements the same element structure can be repeated + * multiple times (for example views displays, filters, etc.). Thus, we try to + * find a more usable title for the details summary. First check if there is + * an element which is called title or label and use its value, then check if + * there is an element which contains these words and use those. Fall back + * to the generic definition label if no such element is found. + * + * @param \Drupal\Core\TypedData\DataDefinitionInterface $definition + * The defintion of the schema element. + * @param $group_build + * The renderable array for the group of schema elements. + * + * @return string + * The title for the group of schema elements. + */ + protected function getGroupTitle(DataDefinitionInterface $definition, $group_build) { + $title = ''; + if (isset($group_build['title']['source'])) { + $title = $group_build['title']['source']['#markup']; + } + elseif (isset($group_build['label']['source'])) { + $title = $group_build['label']['source']['#markup']; + } + else { + foreach (array_keys($group_build) as $title_key) { + if (isset($group_build[$title_key]['source']) && (strpos($title_key, 'title') !== FALSE || strpos($title_key, 'label') !== FALSE)) { + $title = $group_build[$title_key]['source']['#markup']; + break; + } + } + } + return (!empty($title) ? (strip_tags($title) . ' ') : '') . $this->t($definition['label']); + } + } diff --git a/core/modules/config_translation/src/Tests/ConfigTranslationUiTest.php b/core/modules/config_translation/src/Tests/ConfigTranslationUiTest.php index 6c29709..d33a565 100644 --- a/core/modules/config_translation/src/Tests/ConfigTranslationUiTest.php +++ b/core/modules/config_translation/src/Tests/ConfigTranslationUiTest.php @@ -671,7 +671,7 @@ public function testTextFormatTranslation() { $config_factory = $this->container->get('config.factory'); $expected = array( - 'value' => 'Hello World', + 'value' => '

Hello World

', 'format' => 'plain_text', ); $actual = $config_factory @@ -695,7 +695,7 @@ public function testTextFormatTranslation() { // Update translatable fields. $edit = array( - 'translation[config_names][config_translation_test.content][content][value]' => 'Hello World - FR', + 'translation[config_names][config_translation_test.content][content][value]' => '

Hello World - FR

', ); // Save language specific version of form. @@ -703,7 +703,7 @@ public function testTextFormatTranslation() { // Get translation and check we've got the right value. $expected = array( - 'value' => 'Hello World - FR', + 'value' => '

Hello World - FR

', 'format' => 'plain_text', ); $this->container->get('language.config_factory_override') @@ -751,7 +751,7 @@ public function testTextFormatTranslation() { ); $this->drupalPostForm($translation_page_url, $edit, t('Save translation')); $expected = array( - 'value' => 'Hello World - FR', + 'value' => '

Hello World - FR

', 'format' => 'full_html', ); $actual = $config_factory