class ParagraphsClassicAsymmetricWidget in Paragraphs asymmetric translation widgets 8
Plugin implementation of the 'paragraphs_classic_asymmetric' widget.
Plugin annotation
@FieldWidget(
id = "paragraphs_classic_asymmetric",
label = @Translation("Paragraphs Classic Asymmetric"),
description = @Translation("A paragraphs inline form widget that supports asymmetric translations."),
field_types = {
"entity_reference_revisions"
}
)
Hierarchy
- class \Drupal\Component\Plugin\PluginBase implements DerivativeInspectionInterface, PluginInspectionInterface
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, MessengerTrait, StringTranslationTrait
- class \Drupal\Core\Field\PluginSettingsBase implements DependentPluginInterface, PluginSettingsInterface
- class \Drupal\Core\Field\WidgetBase implements WidgetInterface, ContainerFactoryPluginInterface uses AllowedTagsXssTrait
- class \Drupal\paragraphs\Plugin\Field\FieldWidget\InlineParagraphsWidget
- class \Drupal\paragraphs_asymmetric_translation_widgets\Plugin\Field\FieldWidget\ParagraphsClassicAsymmetricWidget
- class \Drupal\paragraphs\Plugin\Field\FieldWidget\InlineParagraphsWidget
- class \Drupal\Core\Field\WidgetBase implements WidgetInterface, ContainerFactoryPluginInterface uses AllowedTagsXssTrait
- class \Drupal\Core\Field\PluginSettingsBase implements DependentPluginInterface, PluginSettingsInterface
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, MessengerTrait, StringTranslationTrait
Expanded class hierarchy of ParagraphsClassicAsymmetricWidget
File
- src/
Plugin/ Field/ FieldWidget/ ParagraphsClassicAsymmetricWidget.php, line 31
Namespace
Drupal\paragraphs_asymmetric_translation_widgets\Plugin\Field\FieldWidgetView source
class ParagraphsClassicAsymmetricWidget extends InlineParagraphsWidget {
/**
* {@inheritdoc}
*
* @see \Drupal\content_translation\Controller\ContentTranslationController::prepareTranslation()
* Uses a similar approach to populate a new translation.
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$field_name = $this->fieldDefinition
->getName();
$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);
$entity_manager = \Drupal::entityTypeManager();
$target_type = $this
->getFieldSetting('target_type');
$item_mode = isset($widget_state['paragraphs'][$delta]['mode']) ? $widget_state['paragraphs'][$delta]['mode'] : 'edit';
$default_edit_mode = $this
->getSetting('edit_mode');
$show_must_be_saved_warning = !empty($widget_state['paragraphs'][$delta]['show_warning']);
if (isset($widget_state['paragraphs'][$delta]['entity'])) {
$paragraphs_entity = $widget_state['paragraphs'][$delta]['entity'];
}
elseif (isset($items[$delta]->entity)) {
$paragraphs_entity = $items[$delta]->entity;
// We don't have a widget state yet, get from selector settings.
if (!isset($widget_state['paragraphs'][$delta]['mode'])) {
if ($default_edit_mode == 'open') {
$item_mode = 'edit';
}
elseif ($default_edit_mode == 'closed') {
$item_mode = 'closed';
}
elseif ($default_edit_mode == 'preview') {
$item_mode = 'preview';
}
}
}
elseif (isset($widget_state['selected_bundle'])) {
$entity_type = $entity_manager
->getDefinition($target_type);
$bundle_key = $entity_type
->getKey('bundle');
$paragraphs_entity = $entity_manager
->getStorage($target_type)
->create(array(
$bundle_key => $widget_state['selected_bundle'],
));
$paragraphs_entity
->setParentEntity($items
->getEntity(), $field_name);
$item_mode = 'edit';
}
if ($item_mode == 'collapsed') {
$item_mode = $default_edit_mode;
}
if ($item_mode == 'closed') {
// Validate closed paragraphs and expand if needed.
// @todo Consider recursion.
$violations = $paragraphs_entity
->validate();
$violations
->filterByFieldAccess();
if (count($violations) > 0) {
$item_mode = 'edit';
$messages = [];
foreach ($violations as $violation) {
$messages[] = $violation
->getMessage();
}
$info['validation_error'] = array(
'#type' => 'container',
'#markup' => $this
->t('@messages', [
'@messages' => strip_tags(implode('\\n', $messages)),
]),
'#attributes' => [
'class' => [
'messages',
'messages--warning',
],
],
);
}
}
if ($paragraphs_entity) {
// Detect if we are translating.
$this
->initIsTranslating($form_state, $host);
$langcode = $form_state
->get('langcode');
if (!$this->isTranslating) {
// Set the langcode if we are not translating.
$langcode_key = $paragraphs_entity
->getEntityType()
->getKey('langcode');
if ($paragraphs_entity
->get($langcode_key)->value != $langcode) {
// If a translation in the given language already exists, switch to
// that. If there is none yet, update the language.
if ($paragraphs_entity
->hasTranslation($langcode)) {
$paragraphs_entity = $paragraphs_entity
->getTranslation($langcode);
}
else {
$paragraphs_entity
->set($langcode_key, $langcode);
}
}
}
elseif ($items
->getFieldDefinition()
->isTranslatable()) {
// If the field is translatable, host entity translation should refer to
// different paragraph entities. So we clone the paragraph.
if (!empty($form_state
->get('content_translation'))) {
$paragraphs_entity = $this
->createDuplicateWithSingleLanguage($paragraphs_entity, $langcode);
}
}
else {
// Add translation if missing for the target language.
if (!$paragraphs_entity
->hasTranslation($langcode)) {
// Get the selected translation of the paragraph entity.
$entity_langcode = $paragraphs_entity
->language()
->getId();
$source = $form_state
->get([
'content_translation',
'source',
]);
$source_langcode = $source ? $source
->getId() : $entity_langcode;
// Make sure the source language version is used if available. It is a
// valid scenario to have no paragraphs items in the source version of
// the host and fetching the translation without this check could lead
// to an exception.
if ($paragraphs_entity
->hasTranslation($source_langcode)) {
$paragraphs_entity = $paragraphs_entity
->getTranslation($source_langcode);
}
// The paragraphs entity has no content translation source field if
// no paragraph entity field is translatable, even if the host is.
if ($paragraphs_entity
->hasField('content_translation_source')) {
// Initialise the translation with source language values.
$paragraphs_entity
->addTranslation($langcode, $paragraphs_entity
->toArray());
$translation = $paragraphs_entity
->getTranslation($langcode);
$manager = \Drupal::service('content_translation.manager');
$manager
->getTranslationMetadata($translation)
->setSource($paragraphs_entity
->language()
->getId());
}
}
// If any paragraphs type is translatable do not switch.
if ($paragraphs_entity
->hasField('content_translation_source')) {
// Switch the paragraph to the translation.
$paragraphs_entity = $paragraphs_entity
->getTranslation($langcode);
}
}
$element_parents = $parents;
$element_parents[] = $field_name;
$element_parents[] = $delta;
$element_parents[] = 'subform';
$id_prefix = implode('-', array_merge($parents, array(
$field_name,
$delta,
)));
$wrapper_id = Html::getUniqueId($id_prefix . '-item-wrapper');
$element += array(
'#type' => 'container',
'#element_validate' => array(
array(
$this,
'elementValidate',
),
),
'#paragraph_type' => $paragraphs_entity
->bundle(),
'subform' => array(
'#type' => 'container',
'#parents' => $element_parents,
),
);
$element['#prefix'] = '<div id="' . $wrapper_id . '">';
$element['#suffix'] = '</div>';
$item_bundles = \Drupal::service('entity_type.bundle.info')
->getBundleInfo($target_type);
if (isset($item_bundles[$paragraphs_entity
->bundle()])) {
$bundle_info = $item_bundles[$paragraphs_entity
->bundle()];
$element['top'] = array(
'#type' => 'container',
'#weight' => -1000,
'#attributes' => array(
'class' => array(
'paragraph-type-top',
),
),
);
$element['top']['paragraph_type_title'] = array(
'#type' => 'container',
'#weight' => 0,
'#attributes' => array(
'class' => array(
'paragraph-type-title',
),
),
);
$element['top']['paragraph_type_title']['info'] = array(
'#markup' => $bundle_info['label'],
);
$actions = array();
$links = array();
// Hide the button when translating.
$button_access = $paragraphs_entity
->access('delete') && (!$this->isTranslating || $items
->getFieldDefinition()
->isTranslatable());
if ($item_mode != 'remove') {
$links['remove_button'] = [
'#type' => 'submit',
'#value' => $this
->t('Remove'),
'#name' => strtr($id_prefix, '-', '_') . '_remove',
'#weight' => 501,
'#submit' => [
[
get_class($this),
'paragraphsItemSubmit',
],
],
'#limit_validation_errors' => [
array_merge($parents, [
$field_name,
'add_more',
]),
],
'#delta' => $delta,
'#ajax' => [
'callback' => [
get_class($this),
'itemAjax',
],
'wrapper' => $widget_state['ajax_wrapper_id'],
'effect' => 'fade',
],
'#access' => $button_access,
'#prefix' => '<li class="remove">',
'#suffix' => '</li>',
'#paragraphs_mode' => 'remove',
];
}
if ($item_mode == 'edit') {
if (isset($items[$delta]->entity) && ($default_edit_mode == 'preview' || $default_edit_mode == 'closed')) {
$links['collapse_button'] = array(
'#type' => 'submit',
'#value' => $this
->t('Collapse'),
'#name' => strtr($id_prefix, '-', '_') . '_collapse',
'#weight' => 499,
'#submit' => array(
array(
get_class($this),
'paragraphsItemSubmit',
),
),
'#delta' => $delta,
'#limit_validation_errors' => [
array_merge($parents, [
$field_name,
'add_more',
]),
],
'#ajax' => array(
'callback' => array(
get_class($this),
'itemAjax',
),
'wrapper' => $widget_state['ajax_wrapper_id'],
'effect' => 'fade',
),
'#access' => $paragraphs_entity
->access('update'),
'#prefix' => '<li class="collapse">',
'#suffix' => '</li>',
'#paragraphs_mode' => 'collapsed',
'#paragraphs_show_warning' => TRUE,
);
}
// Hide the button when translating.
$button_access = $paragraphs_entity
->access('delete') && !$this->isTranslating;
$info['edit_button_info'] = array(
'#type' => 'container',
'#markup' => $this
->t('You are not allowed to edit this @title.', array(
'@title' => $this
->getSetting('title'),
)),
'#attributes' => [
'class' => [
'messages',
'messages--warning',
],
],
'#access' => !$paragraphs_entity
->access('update') && $paragraphs_entity
->access('delete'),
);
$info['remove_button_info'] = array(
'#type' => 'container',
'#markup' => $this
->t('You are not allowed to remove this @title.', array(
'@title' => $this
->getSetting('title'),
)),
'#attributes' => [
'class' => [
'messages',
'messages--warning',
],
],
'#access' => !$paragraphs_entity
->access('delete') && $paragraphs_entity
->access('update'),
);
$info['edit_remove_button_info'] = array(
'#type' => 'container',
'#markup' => $this
->t('You are not allowed to edit or remove this @title.', array(
'@title' => $this
->getSetting('title'),
)),
'#attributes' => [
'class' => [
'messages',
'messages--warning',
],
],
'#access' => !$paragraphs_entity
->access('update') && !$paragraphs_entity
->access('delete'),
);
}
elseif ($item_mode == 'preview' || $item_mode == 'closed') {
$links['edit_button'] = array(
'#type' => 'submit',
'#value' => $this
->t('Edit'),
'#name' => strtr($id_prefix, '-', '_') . '_edit',
'#weight' => 500,
'#submit' => array(
array(
get_class($this),
'paragraphsItemSubmit',
),
),
'#limit_validation_errors' => array(
array_merge($parents, array(
$field_name,
'add_more',
)),
),
'#delta' => $delta,
'#ajax' => array(
'callback' => array(
get_class($this),
'itemAjax',
),
'wrapper' => $widget_state['ajax_wrapper_id'],
'effect' => 'fade',
),
'#access' => $paragraphs_entity
->access('update'),
'#prefix' => '<li class="edit">',
'#suffix' => '</li>',
'#paragraphs_mode' => 'edit',
);
if ($show_must_be_saved_warning) {
$info['must_be_saved_info'] = array(
'#type' => 'container',
'#markup' => $this
->t('You have unsaved changes on this @title item.', array(
'@title' => $this
->getSetting('title'),
)),
'#attributes' => [
'class' => [
'messages',
'messages--warning',
],
],
);
}
$info['preview_info'] = array(
'#type' => 'container',
'#markup' => $this
->t('You are not allowed to view this @title.', array(
'@title' => $this
->getSetting('title'),
)),
'#attributes' => [
'class' => [
'messages',
'messages--warning',
],
],
'#access' => !$paragraphs_entity
->access('view'),
);
$info['edit_button_info'] = array(
'#type' => 'container',
'#markup' => $this
->t('You are not allowed to edit this @title.', array(
'@title' => $this
->getSetting('title'),
)),
'#attributes' => [
'class' => [
'messages',
'messages--warning',
],
],
'#access' => !$paragraphs_entity
->access('update') && $paragraphs_entity
->access('delete'),
);
$info['remove_button_info'] = array(
'#type' => 'container',
'#markup' => $this
->t('You are not allowed to remove this @title.', array(
'@title' => $this
->getSetting('title'),
)),
'#attributes' => [
'class' => [
'messages',
'messages--warning',
],
],
'#access' => !$paragraphs_entity
->access('delete') && $paragraphs_entity
->access('update'),
);
$info['edit_remove_button_info'] = array(
'#type' => 'container',
'#markup' => $this
->t('You are not allowed to edit or remove this @title.', array(
'@title' => $this
->getSetting('title'),
)),
'#attributes' => [
'class' => [
'messages',
'messages--warning',
],
],
'#access' => !$paragraphs_entity
->access('update') && !$paragraphs_entity
->access('delete'),
);
}
elseif ($item_mode == 'remove') {
$element['top']['paragraph_type_title']['info'] = [
'#markup' => $this
->t('Deleted @title: %type', [
'@title' => $this
->getSetting('title'),
'%type' => $bundle_info['label'],
]),
];
$links['confirm_remove_button'] = [
'#type' => 'submit',
'#value' => $this
->t('Confirm removal'),
'#name' => strtr($id_prefix, '-', '_') . '_confirm_remove',
'#weight' => 503,
'#submit' => [
[
get_class($this),
'paragraphsItemSubmit',
],
],
'#limit_validation_errors' => [
array_merge($parents, [
$field_name,
'add_more',
]),
],
'#delta' => $delta,
'#ajax' => [
'callback' => [
get_class($this),
'itemAjax',
],
'wrapper' => $widget_state['ajax_wrapper_id'],
'effect' => 'fade',
],
'#prefix' => '<li class="confirm-remove">',
'#suffix' => '</li>',
'#paragraphs_mode' => 'removed',
];
$links['restore_button'] = [
'#type' => 'submit',
'#value' => $this
->t('Restore'),
'#name' => strtr($id_prefix, '-', '_') . '_restore',
'#weight' => 504,
'#submit' => [
[
get_class($this),
'paragraphsItemSubmit',
],
],
'#limit_validation_errors' => [
array_merge($parents, [
$field_name,
'add_more',
]),
],
'#delta' => $delta,
'#ajax' => [
'callback' => [
get_class($this),
'itemAjax',
],
'wrapper' => $widget_state['ajax_wrapper_id'],
'effect' => 'fade',
],
'#prefix' => '<li class="restore">',
'#suffix' => '</li>',
'#paragraphs_mode' => 'edit',
];
}
if (count($links)) {
$show_links = 0;
foreach ($links as $link_item) {
if (!isset($link_item['#access']) || $link_item['#access']) {
$show_links++;
}
}
if ($show_links > 0) {
$element['top']['links'] = $links;
if ($show_links > 1) {
$element['top']['links']['#theme_wrappers'] = array(
'dropbutton_wrapper',
'paragraphs_dropbutton_wrapper',
);
$element['top']['links']['prefix'] = array(
'#markup' => '<ul class="dropbutton">',
'#weight' => -999,
);
$element['top']['links']['suffix'] = array(
'#markup' => '</li>',
'#weight' => 999,
);
}
else {
$element['top']['links']['#theme_wrappers'] = array(
'paragraphs_dropbutton_wrapper',
);
foreach ($links as $key => $link_item) {
unset($element['top']['links'][$key]['#prefix']);
unset($element['top']['links'][$key]['#suffix']);
}
}
$element['top']['links']['#weight'] = 2;
}
}
if (count($info)) {
$show_info = FALSE;
foreach ($info as $info_item) {
if (!isset($info_item['#access']) || $info_item['#access']) {
$show_info = TRUE;
break;
}
}
if ($show_info) {
$element['info'] = $info;
$element['info']['#weight'] = 998;
}
}
if (count($actions)) {
$show_actions = FALSE;
foreach ($actions as $action_item) {
if (!isset($action_item['#access']) || $action_item['#access']) {
$show_actions = TRUE;
break;
}
}
if ($show_actions) {
$element['actions'] = $actions;
$element['actions']['#type'] = 'actions';
$element['actions']['#weight'] = 999;
}
}
}
$display = EntityFormDisplay::collectRenderDisplay($paragraphs_entity, $this
->getSetting('form_display_mode'));
// @todo Remove as part of https://www.drupal.org/node/2640056
if (\Drupal::moduleHandler()
->moduleExists('field_group')) {
$context = [
'entity_type' => $paragraphs_entity
->getEntityTypeId(),
'bundle' => $paragraphs_entity
->bundle(),
'entity' => $paragraphs_entity,
'context' => 'form',
'display_context' => 'form',
'mode' => $display
->getMode(),
];
field_group_attach_groups($element['subform'], $context);
if (method_exists(FormatterHelper::class, 'formProcess')) {
$element['subform']['#process'][] = [
FormatterHelper::class,
'formProcess',
];
}
elseif (function_exists('field_group_form_pre_render')) {
$element['subform']['#pre_render'][] = 'field_group_form_pre_render';
}
elseif (function_exists('field_group_form_process')) {
$element['subform']['#process'][] = 'field_group_form_process';
}
}
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();
// 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',
];
}
}
}
}
}
elseif ($item_mode == 'preview') {
$element['subform'] = array();
$element['behavior_plugins'] = [];
$render_controller = \Drupal::entityTypeManager()
->getViewBuilder($paragraphs_entity
->getEntityTypeId());
$element['preview'] = $render_controller
->view($paragraphs_entity, 'preview', $paragraphs_entity
->language()
->getId());
$element['preview']['#access'] = $paragraphs_entity
->access('view');
}
elseif ($item_mode == 'closed') {
$element['subform'] = array();
$element['behavior_plugins'] = [];
if ($paragraphs_entity) {
$summary = $paragraphs_entity
->getSummary();
$element['top']['paragraph_summary']['fields_info'] = [
'#markup' => $summary,
'#prefix' => '<div class="paragraphs-collapsed-description">',
'#suffix' => '</div>',
];
}
}
else {
$element['subform'] = array();
}
$element['subform']['#attributes']['class'][] = 'paragraphs-subform';
$element['subform']['#access'] = $paragraphs_entity
->access('update');
if ($item_mode == 'removed') {
$element['#access'] = FALSE;
}
$widget_state['paragraphs'][$delta]['entity'] = $paragraphs_entity;
$widget_state['paragraphs'][$delta]['display'] = $display;
$widget_state['paragraphs'][$delta]['mode'] = $item_mode;
static::setWidgetState($parents, $field_name, $form_state, $widget_state);
}
else {
$element['#access'] = FALSE;
}
return $element;
}
public function formMultipleElements(FieldItemListInterface $items, array &$form, FormStateInterface $form_state) {
$field_name = $this->fieldDefinition
->getName();
$cardinality = $this->fieldDefinition
->getFieldStorageDefinition()
->getCardinality();
$this->fieldParents = $form['#parents'];
$field_state = static::getWidgetState($this->fieldParents, $field_name, $form_state);
$max = $field_state['items_count'];
$entity_type_manager = \Drupal::entityTypeManager();
// Consider adding a default paragraph for new host entities.
if ($max == 0 && $items
->getEntity()
->isNew()) {
$default_type = $this
->getDefaultParagraphTypeMachineName();
// Checking if default_type is not none and if is allowed.
if ($default_type) {
// Place the default paragraph.
$target_type = $this
->getFieldSetting('target_type');
$paragraphs_entity = $entity_type_manager
->getStorage($target_type)
->create([
'type' => $default_type,
]);
$paragraphs_entity
->setParentEntity($items
->getEntity(), $field_name);
$field_state['selected_bundle'] = $default_type;
$display = EntityFormDisplay::collectRenderDisplay($paragraphs_entity, $this
->getSetting('form_display_mode'));
$field_state['paragraphs'][0] = [
'entity' => $paragraphs_entity,
'display' => $display,
'mode' => 'edit',
'original_delta' => 1,
];
$max = 1;
$field_state['items_count'] = $max;
}
}
$this->realItemCount = $max;
$is_multiple = $this->fieldDefinition
->getFieldStorageDefinition()
->isMultiple();
$title = $this->fieldDefinition
->getLabel();
$description = FieldFilteredMarkup::create(\Drupal::token()
->replace($this->fieldDefinition
->getDescription()));
$elements = array();
$this->fieldIdPrefix = implode('-', array_merge($this->fieldParents, array(
$field_name,
)));
$this->fieldWrapperId = Html::getUniqueId($this->fieldIdPrefix . '-add-more-wrapper');
$elements['#prefix'] = '<div id="' . $this->fieldWrapperId . '">';
$elements['#suffix'] = '</div>';
$field_state['ajax_wrapper_id'] = $this->fieldWrapperId;
// Persist the widget state so formElement() can access it.
static::setWidgetState($this->fieldParents, $field_name, $form_state, $field_state);
if ($max > 0) {
for ($delta = 0; $delta < $max; $delta++) {
// Add a new empty item if it doesn't exist yet at this delta.
if (!isset($items[$delta])) {
$items
->appendItem();
}
// For multiple fields, title and description are handled by the wrapping
// table.
$element = array(
'#title' => $is_multiple ? '' : $title,
'#description' => $is_multiple ? '' : $description,
);
$element = $this
->formSingleElement($items, $delta, $element, $form, $form_state);
if ($element) {
// Input field for the delta (drag-n-drop reordering).
if ($is_multiple) {
// We name the element '_weight' to avoid clashing with elements
// defined by widget.
$element['_weight'] = array(
'#type' => 'weight',
'#title' => $this
->t('Weight for row @number', array(
'@number' => $delta + 1,
)),
'#title_display' => 'invisible',
// Note: this 'delta' is the FAPI #type 'weight' element's property.
'#delta' => $max,
'#default_value' => $items[$delta]->_weight ?: $delta,
'#weight' => 100,
);
}
// Access for the top element is set to FALSE only when the paragraph
// was removed. A paragraphs that a user can not edit has access on
// lower level.
if (isset($element['#access']) && !$element['#access']) {
$this->realItemCount--;
}
else {
$elements[$delta] = $element;
}
}
}
}
$field_state = static::getWidgetState($this->fieldParents, $field_name, $form_state);
$field_state['real_item_count'] = $this->realItemCount;
static::setWidgetState($this->fieldParents, $field_name, $form_state, $field_state);
$elements += [
'#element_validate' => [
[
$this,
'multipleElementValidate',
],
],
'#required' => $this->fieldDefinition
->isRequired(),
'#field_name' => $field_name,
'#cardinality' => $cardinality,
'#max_delta' => $max - 1,
];
if ($this->realItemCount > 0) {
$elements += array(
'#theme' => 'field_multiple_value_form',
'#cardinality_multiple' => $is_multiple,
'#title' => $title,
'#description' => $description,
);
}
else {
$classes = $this->fieldDefinition
->isRequired() ? [
'form-required',
] : [];
$elements += [
'#type' => 'container',
'#theme_wrappers' => [
'container',
],
'#cardinality_multiple' => TRUE,
'title' => [
'#type' => 'html_tag',
'#tag' => 'strong',
'#value' => $title,
'#attributes' => [
'class' => $classes,
],
],
'text' => [
'#type' => 'container',
'value' => [
'#markup' => $this
->t('No @title added yet.', [
'@title' => $this
->getSetting('title'),
]),
'#prefix' => '<em>',
'#suffix' => '</em>',
],
],
];
if ($this->fieldDefinition
->isRequired()) {
$elements['title']['#attributes']['class'][] = 'form-required';
}
if ($description) {
$elements['description'] = [
'#type' => 'container',
'value' => [
'#markup' => $description,
],
'#attributes' => [
'class' => [
'description',
],
],
];
}
}
$host = $items
->getEntity();
$this
->initIsTranslating($form_state, $host);
if (($this->realItemCount < $cardinality || $cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) && !$form_state
->isProgrammed() && (!$this->isTranslating || $this->fieldDefinition
->isTranslatable())) {
$elements['add_more'] = $this
->buildAddActions();
}
$elements['#attached']['library'][] = 'paragraphs_asymmetric_translation_widgets/drupal.paragraphs_asymmetric_translation_widgets.widget';
return $elements;
}
/**
* Clones a paragraph recursively.
*
* Also, in case of a translatable paragraph, updates its original language
* and removes all other translations.
*
* @param \Drupal\paragraphs\ParagraphInterface $paragraph
* The paragraph entity to clone.
* @param string $langcode
* Language code for all the clone entities created.
*
* @return \Drupal\paragraphs\ParagraphInterface
* New paragraph object with the data from the original paragraph. Not
* saved. All sub-paragraphs are clones as well.
*/
protected function createDuplicateWithSingleLanguage(ParagraphInterface $paragraph, $langcode) {
$duplicate = $paragraph
->createDuplicate();
// Clone all sub-paragraphs recursively.
foreach ($duplicate
->getFields(FALSE) as $field) {
// @todo: should we support field collections as well?
if ($field
->getFieldDefinition()
->getType() == 'entity_reference_revisions' && $field
->getFieldDefinition()
->getTargetEntityTypeId() == 'paragraph') {
foreach ($field as $item) {
$item->entity = $this
->createDuplicateWithSingleLanguage($item->entity, $langcode);
}
}
}
// Change the original language and remove possible translations.
if ($duplicate
->isTranslatable()) {
$duplicate
->set('langcode', $langcode);
foreach ($duplicate
->getTranslationLanguages(FALSE) as $language) {
try {
$duplicate
->removeTranslation($language
->getId());
} catch (\InvalidArgumentException $e) {
// Should never happen.
}
}
}
return $duplicate;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
AllowedTagsXssTrait:: |
public | function | Returns a list of tags allowed by AllowedTagsXssTrait::fieldFilterXss(). | |
AllowedTagsXssTrait:: |
public | function | Returns a human-readable list of allowed tags for display in help texts. | |
AllowedTagsXssTrait:: |
public | function | Filters an HTML string to prevent XSS vulnerabilities. | |
DependencySerializationTrait:: |
protected | property | An array of entity type IDs keyed by the property name of their storages. | |
DependencySerializationTrait:: |
protected | property | An array of service IDs keyed by property name used for serialization. | |
DependencySerializationTrait:: |
public | function | 1 | |
DependencySerializationTrait:: |
public | function | 2 | |
InlineParagraphsWidget:: |
protected | property | Accessible paragraphs types. | |
InlineParagraphsWidget:: |
protected | property | Id to name ajax buttons that includes field parents and field name. | |
InlineParagraphsWidget:: |
protected | property | Parents for the current paragraph. | |
InlineParagraphsWidget:: |
protected | property | Wrapper id to identify the paragraphs. | |
InlineParagraphsWidget:: |
protected | property | Indicates whether the current widget instance is in translation. | |
InlineParagraphsWidget:: |
protected | property | Number of paragraphs item on form. | |
InlineParagraphsWidget:: |
public static | function |
Ajax callback for the "Add another item" button. Overrides WidgetBase:: |
|
InlineParagraphsWidget:: |
public static | function |
Submission handler for the "Add another item" button. Overrides WidgetBase:: |
|
InlineParagraphsWidget:: |
public static | function | After-build callback for adding the translatability clue from the widget. | |
InlineParagraphsWidget:: |
protected | function | Add 'add more' button, if not working with a programmed form. | |
InlineParagraphsWidget:: |
protected | function | Builds dropdown button for adding new paragraph. | |
InlineParagraphsWidget:: |
protected | function | Builds list of actions based on paragraphs type. | |
InlineParagraphsWidget:: |
public static | function |
Defines the default settings for this plugin. Overrides PluginSettingsBase:: |
|
InlineParagraphsWidget:: |
public | function | ||
InlineParagraphsWidget:: |
public | function |
Assigns a field-level validation error to the right widget sub-element. Overrides WidgetBase:: |
|
InlineParagraphsWidget:: |
public | function |
Extracts field values from submitted form values. Overrides WidgetBase:: |
|
InlineParagraphsWidget:: |
public | function |
Creates a form element for a field. Overrides WidgetBase:: |
|
InlineParagraphsWidget:: |
protected | function | Returns the available paragraphs type. | |
InlineParagraphsWidget:: |
public | function | Returns the sorted allowed types for a entity reference field. | |
InlineParagraphsWidget:: |
protected | function | Gets current language code from the form state or item. | |
InlineParagraphsWidget:: |
protected | function | Returns the default paragraph type. | |
InlineParagraphsWidget:: |
protected | function | Returns the machine name for default paragraph type. | |
InlineParagraphsWidget:: |
protected | function | Counts the number of paragraphs in a certain mode in a form substructure. | |
InlineParagraphsWidget:: |
protected | function | Returns the value of a setting for the entity reference selection handler. | |
InlineParagraphsWidget:: |
protected | function | Initializes the translation form state. | |
InlineParagraphsWidget:: |
public static | function |
Returns if the widget can be used for the provided field. Overrides WidgetBase:: |
|
InlineParagraphsWidget:: |
protected | function | Checks whether a content entity is referenced. | |
InlineParagraphsWidget:: |
public static | function | ||
InlineParagraphsWidget:: |
public | function |
Massages the form values into the format expected for field values. Overrides WidgetBase:: |
|
InlineParagraphsWidget:: |
public | function | Special handling to validate form elements with multiple values. | |
InlineParagraphsWidget:: |
public static | function | ||
InlineParagraphsWidget:: |
public | function |
Returns a form to configure settings for the widget. Overrides WidgetBase:: |
|
InlineParagraphsWidget:: |
public | function |
Returns a short summary for the current widget settings. Overrides WidgetBase:: |
|
MessengerTrait:: |
protected | property | The messenger. | 29 |
MessengerTrait:: |
public | function | Gets the messenger. | 29 |
MessengerTrait:: |
public | function | Sets the messenger. | |
ParagraphsClassicAsymmetricWidget:: |
protected | function | Clones a paragraph recursively. | |
ParagraphsClassicAsymmetricWidget:: |
public | function |
Uses a similar approach to populate a new translation. Overrides InlineParagraphsWidget:: |
|
ParagraphsClassicAsymmetricWidget:: |
public | function |
Special handling to create form elements for multiple values. Overrides InlineParagraphsWidget:: |
|
PluginBase:: |
protected | property | Configuration information passed into the plugin. | 1 |
PluginBase:: |
protected | property | The plugin implementation definition. | 1 |
PluginBase:: |
protected | property | The plugin_id. | |
PluginBase:: |
constant | A string which is used to separate base plugin IDs from the derivative ID. | ||
PluginBase:: |
public | function |
Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the definition of the plugin implementation. Overrides PluginInspectionInterface:: |
3 |
PluginBase:: |
public | function |
Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface:: |
|
PluginBase:: |
public | function | Determines if the plugin is configurable. | |
PluginSettingsBase:: |
protected | property | Whether default settings have been merged into the current $settings. | |
PluginSettingsBase:: |
protected | property | The plugin settings injected by third party modules. | |
PluginSettingsBase:: |
public | function |
Calculates dependencies for the configured plugin. Overrides DependentPluginInterface:: |
6 |
PluginSettingsBase:: |
public | function |
Returns the value of a setting, or its default value if absent. Overrides PluginSettingsInterface:: |
|
PluginSettingsBase:: |
public | function |
Returns the array of settings, including defaults for missing settings. Overrides PluginSettingsInterface:: |
|
PluginSettingsBase:: |
public | function |
Gets the list of third parties that store information. Overrides ThirdPartySettingsInterface:: |
|
PluginSettingsBase:: |
public | function |
Gets the value of a third-party setting. Overrides ThirdPartySettingsInterface:: |
|
PluginSettingsBase:: |
public | function |
Gets all third-party settings of a given module. Overrides ThirdPartySettingsInterface:: |
|
PluginSettingsBase:: |
protected | function | Merges default settings values into $settings. | |
PluginSettingsBase:: |
public | function |
Informs the plugin that some configuration it depends on will be deleted. Overrides PluginSettingsInterface:: |
3 |
PluginSettingsBase:: |
public | function |
Sets the value of a setting for the plugin. Overrides PluginSettingsInterface:: |
|
PluginSettingsBase:: |
public | function |
Sets the settings for the plugin. Overrides PluginSettingsInterface:: |
|
PluginSettingsBase:: |
public | function |
Sets the value of a third-party setting. Overrides ThirdPartySettingsInterface:: |
|
PluginSettingsBase:: |
public | function |
Unsets a third-party setting. Overrides ThirdPartySettingsInterface:: |
|
StringTranslationTrait:: |
protected | property | The string translation service. | 1 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. | |
WidgetBase:: |
protected | property | The field definition. | |
WidgetBase:: |
protected | property |
The widget settings. Overrides PluginSettingsBase:: |
|
WidgetBase:: |
public static | function | After-build handler for field elements in a form. | |
WidgetBase:: |
public static | function |
Creates an instance of the plugin. Overrides ContainerFactoryPluginInterface:: |
5 |
WidgetBase:: |
public | function |
Reports field-level validation errors against actual form elements. Overrides WidgetBaseInterface:: |
2 |
WidgetBase:: |
protected | function | Generates the form element for a single copy of the widget. | |
WidgetBase:: |
protected | function | Returns the value of a field setting. | |
WidgetBase:: |
protected | function | Returns the array of field settings. | |
WidgetBase:: |
protected | function | Returns the filtered field description. | |
WidgetBase:: |
public static | function |
Retrieves processing information about the widget from $form_state. Overrides WidgetBaseInterface:: |
|
WidgetBase:: |
protected static | function | Returns the location of processing information within $form_state. | |
WidgetBase:: |
protected | function | Returns whether the widget handles multiple values. | |
WidgetBase:: |
protected | function | Returns whether the widget used for default value form. | |
WidgetBase:: |
public static | function |
Stores processing information about the widget in $form_state. Overrides WidgetBaseInterface:: |
|
WidgetBase:: |
public | function |
Constructs a WidgetBase object. Overrides PluginBase:: |
5 |