diff --git a/paragraphs.module b/paragraphs.module index e5af4a6..8545404 100644 --- a/paragraphs.module +++ b/paragraphs.module @@ -242,6 +242,17 @@ function paragraphs_form_language_content_settings_form_alter(&$form, \Drupal\Co } } } + + // Update the description on the hide untranslatable fields checkbox. + if (isset($form['settings']['paragraph'])) { + $paragraph_untranslatable_hide_description = t('Paragraph types that are used in moderated content requires non-translatable fields to be edited in the original language form and this must be checked.'); + foreach (Element::children($form['settings']['paragraph']) as $bundle) { + if (!empty($form['settings']['paragraph'][$bundle]['settings']['content_translation']['untranslatable_fields_hide'])) { + $form['settings']['paragraph'][$bundle]['settings']['content_translation']['untranslatable_fields_hide']['#description'] = $paragraph_untranslatable_hide_description; + } + } + } + $form['settings']['paragraphs_message'] = array( '#type' => 'container', '#markup' => $message_text, diff --git a/src/Plugin/Field/FieldWidget/InlineParagraphsWidget.php b/src/Plugin/Field/FieldWidget/InlineParagraphsWidget.php index 681182f..8ffb05f 100644 --- a/src/Plugin/Field/FieldWidget/InlineParagraphsWidget.php +++ b/src/Plugin/Field/FieldWidget/InlineParagraphsWidget.php @@ -230,6 +230,7 @@ class InlineParagraphsWidget extends WidgetBase { $parents = $element['#field_parents']; $info = []; + /** @var \Drupal\paragraphs\ParagraphInterface $paragraphs_entity */ $paragraphs_entity = NULL; $host = $items->getEntity(); $widget_state = static::getWidgetState($parents, $field_name, $form_state); @@ -654,14 +655,38 @@ class InlineParagraphsWidget extends WidgetBase { if ($item_mode == 'edit') { $display->buildForm($paragraphs_entity, $element['subform'], $form_state); + $hide_untranslatable_fields = $paragraphs_entity->isDefaultTranslationAffectedOnly(); foreach (Element::children($element['subform']) as $field) { if ($paragraphs_entity->hasField($field)) { + $field_definition = $paragraphs_entity->getFieldDefinition($field); $translatable = $paragraphs_entity->{$field}->getFieldDefinition()->isTranslatable(); - if ($translatable) { - $element['subform'][$field]['widget']['#after_build'][] = [ - static::class, - 'removeTranslatabilityClue' - ]; + + // Do a check if we have to add a class to the form element. We need + // those classes (paragraphs-content and paragraphs-behavior) to show + // and hide elements, depending of the active perspective. + // We need them to filter out entity reference revisions fields that + // reference paragraphs, cause otherwise we have problems with showing + // and hiding the right fields in nested paragraphs. + $is_paragraph_field = FALSE; + if ($field_definition->getType() == 'entity_reference_revisions') { + // Check if we are referencing paragraphs. + if ($field_definition->getSetting('target_type') == 'paragraph') { + $is_paragraph_field = TRUE; + } + } + + // Hide untranslatable fields when configured to do so except + // paragraph fields. + if (!$translatable && $this->isTranslating && !$is_paragraph_field) { + if ($hide_untranslatable_fields) { + $element['subform'][$field]['#access'] = FALSE; + } + else { + $element['subform'][$field]['widget']['#after_build'][] = [ + static::class, + 'addTranslatabilityClue' + ]; + } } } } @@ -906,6 +931,11 @@ class InlineParagraphsWidget extends WidgetBase { $elements['#attached']['library'][] = 'paragraphs/drupal.paragraphs.admin'; + // Signal to content_translation that this field should be treated as + // multilingual and not be hidden, see + // \Drupal\content_translation\ContentTranslationHandler::entityFormSharedElements(). + $elements['#multilingual'] = TRUE; + return $elements; } @@ -1349,38 +1379,46 @@ class InlineParagraphsWidget extends WidgetBase { } /** - * After-build callback for removing the translatability clue from the widget. + * After-build callback for adding the translatability clue from the widget. * - * If the fields on the paragraph type are translatable, - * ContentTranslationHandler::addTranslatabilityClue()adds an - * "(all languages)" suffix to the widget title. That suffix is incorrect and - * is being removed by this method using a #after_build on the field widget. + * ContentTranslationHandler::addTranslatabilityClue() adds an + * "(all languages)" suffix to the widget title, replicate that here. * * @param array $element * @param \Drupal\Core\Form\FormStateInterface $form_state * * @return array */ - public static function removeTranslatabilityClue(array $element, FormStateInterface $form_state) { + public static function addTranslatabilityClue(array $element, FormStateInterface $form_state) { + static $suffix, $fapi_title_elements; + // Widgets could have multiple elements with their own titles, so remove the // suffix if it exists, do not recurse lower than this to avoid going into // nested paragraphs or similar nested field types. - $suffix = ' (' . t('all languages') . ')'; - if (isset($element['#title']) && strpos($element['#title'], $suffix)) { - $element['#title'] = str_replace($suffix, '', $element['#title']); + // Elements which can have a #title attribute according to FAPI Reference. + if (!isset($suffix)) { + $suffix = ' (' . t('all languages') . ')'; + $fapi_title_elements = array_flip(['checkbox', 'checkboxes', 'date', 'details', 'fieldset', 'file', 'item', 'password', 'password_confirm', 'radio', 'radios', 'select', 'textarea', 'textfield', 'weight']); } - // Loop over all widget deltas. - foreach (Element::children($element) as $delta) { - if (isset($element[$delta]['#title']) && strpos($element[$delta]['#title'], $suffix)) { - $element[$delta]['#title'] = str_replace($suffix, '', $element[$delta]['#title']); - } - // Loop over all form elements within the current delta. - foreach (Element::children($element[$delta]) as $field) { - if (isset($element[$delta][$field]['#title']) && strpos($element[$delta][$field]['#title'], $suffix)) { - $element[$delta][$field]['#title'] = str_replace($suffix, '', $element[$delta][$field]['#title']); - } + + // Update #title attribute for all elements that are allowed to have a + // #title attribute according to the Form API Reference. The reason for this + // check is because some elements have a #title attribute even though it is + // not rendered; for instance, field containers. + if (isset($element['#type']) && isset($fapi_title_elements[$element['#type']]) && isset($element['#title'])) { + $element['#title'] .= $suffix; + } + // If the current element does not have a (valid) title, try child elements. + elseif ($children = Element::children($element)) { + foreach ($children as $delta) { + $element[$delta] = static::addTranslatabilityClue($element[$delta], $form_state); } } + // If there are no children, fall back to the current #title attribute if it + // exists. + elseif (isset($element['#title'])) { + $element['#title'] .= $suffix; + } return $element; } diff --git a/src/Plugin/Field/FieldWidget/ParagraphsWidget.php b/src/Plugin/Field/FieldWidget/ParagraphsWidget.php index 82b9872..e89b40e 100644 --- a/src/Plugin/Field/FieldWidget/ParagraphsWidget.php +++ b/src/Plugin/Field/FieldWidget/ParagraphsWidget.php @@ -715,16 +715,18 @@ class ParagraphsWidget extends WidgetBase { $element['subform'][$field]['#attributes']['class'][] = 'paragraphs-content'; } $translatable = $field_definition->isTranslatable(); - if ($translatable) { - $element['subform'][$field]['widget']['#after_build'][] = [ - static::class, - 'removeTranslatabilityClue', - ]; - } // Hide untranslatable fields when configured to do so except // paragraph fields. - elseif ($hide_untranslatable_fields && $this->isTranslating && !$is_paragraph_field) { - $element['subform'][$field]['#access'] = FALSE; + if (!$translatable && $this->isTranslating && !$is_paragraph_field) { + if ($hide_untranslatable_fields) { + $element['subform'][$field]['#access'] = FALSE; + } + else { + $element['subform'][$field]['widget']['#after_build'][] = [ + static::class, + 'addTranslatabilityClue' + ]; + } } } } @@ -2229,38 +2231,46 @@ class ParagraphsWidget extends WidgetBase { } /** - * After-build callback for removing the translatability clue from the widget. + * After-build callback for adding the translatability clue from the widget. * - * If the fields on the paragraph type are translatable, - * ContentTranslationHandler::addTranslatabilityClue()adds an - * "(all languages)" suffix to the widget title. That suffix is incorrect and - * is being removed by this method using a #after_build on the field widget. + * ContentTranslationHandler::addTranslatabilityClue() adds an + * "(all languages)" suffix to the widget title, replicate that here. * * @param array $element * @param \Drupal\Core\Form\FormStateInterface $form_state * * @return array */ - public static function removeTranslatabilityClue(array $element, FormStateInterface $form_state) { + public static function addTranslatabilityClue(array $element, FormStateInterface $form_state) { + static $suffix, $fapi_title_elements; + // Widgets could have multiple elements with their own titles, so remove the // suffix if it exists, do not recurse lower than this to avoid going into // nested paragraphs or similar nested field types. - $suffix = ' (' . t('all languages') . ')'; - if (isset($element['#title']) && strpos($element['#title'], $suffix)) { - $element['#title'] = str_replace($suffix, '', $element['#title']); + // Elements which can have a #title attribute according to FAPI Reference. + if (!isset($suffix)) { + $suffix = ' (' . t('all languages') . ')'; + $fapi_title_elements = array_flip(['checkbox', 'checkboxes', 'date', 'details', 'fieldset', 'file', 'item', 'password', 'password_confirm', 'radio', 'radios', 'select', 'textarea', 'textfield', 'weight']); } - // Loop over all widget deltas. - foreach (Element::children($element) as $delta) { - if (isset($element[$delta]['#title']) && strpos($element[$delta]['#title'], $suffix)) { - $element[$delta]['#title'] = str_replace($suffix, '', $element[$delta]['#title']); - } - // Loop over all form elements within the current delta. - foreach (Element::children($element[$delta]) as $field) { - if (isset($element[$delta][$field]['#title']) && strpos($element[$delta][$field]['#title'], $suffix)) { - $element[$delta][$field]['#title'] = str_replace($suffix, '', $element[$delta][$field]['#title']); - } + + // Update #title attribute for all elements that are allowed to have a + // #title attribute according to the Form API Reference. The reason for this + // check is because some elements have a #title attribute even though it is + // not rendered; for instance, field containers. + if (isset($element['#type']) && isset($fapi_title_elements[$element['#type']]) && isset($element['#title'])) { + $element['#title'] .= $suffix; + } + // If the current element does not have a (valid) title, try child elements. + elseif ($children = Element::children($element)) { + foreach ($children as $delta) { + $element[$delta] = static::addTranslatabilityClue($element[$delta], $form_state); } } + // If there are no children, fall back to the current #title attribute if it + // exists. + elseif (isset($element['#title'])) { + $element['#title'] .= $suffix; + } return $element; } diff --git a/tests/src/Functional/ParagraphsContentModerationTranslationsTest.php b/tests/src/Functional/ParagraphsContentModerationTranslationsTest.php index 7894d1e..2c4a1c6 100644 --- a/tests/src/Functional/ParagraphsContentModerationTranslationsTest.php +++ b/tests/src/Functional/ParagraphsContentModerationTranslationsTest.php @@ -108,6 +108,8 @@ class ParagraphsContentModerationTranslationsTest extends BrowserTestBase { $this->addParagraphsField('container', 'field_paragraphs', 'paragraph'); + $this->drupalGet('admin/config/regional/content-language'); + $this->assertSession()->pageTextContains('Paragraph types that are used in moderated content requires non-translatable fields to be edited in the original language form and this must be checked.'); $edit = [ 'entity_types[paragraph]' => TRUE, 'entity_types[node]' => TRUE, @@ -118,14 +120,13 @@ class ParagraphsContentModerationTranslationsTest extends BrowserTestBase { 'settings[paragraph][container][translatable]' => TRUE, // Because the paragraph entity itself is not directly enabled in the // workflow, these options must be enabled manually. - // @todo Improve this. 'settings[paragraph][text][settings][content_translation][untranslatable_fields_hide]' => TRUE, 'settings[paragraph][container][settings][content_translation][untranslatable_fields_hide]' => TRUE, 'settings[paragraph][text][fields][field_text]' => TRUE, 'settings[paragraph][text][fields][field_untranslatable]' => FALSE, 'settings[paragraph][container][fields][field_paragraphs]' => FALSE, ]; - $this->drupalPostForm('admin/config/regional/content-language', $edit, t('Save configuration')); + $this->drupalPostForm(NULL, $edit, t('Save configuration')); } /**