View source
<?php
namespace Drupal\layout_paragraphs\Plugin\Field\FieldWidget;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Layout\LayoutPluginManager;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Render\Renderer;
use Drupal\field_group\FormatterHelper;
use Drupal\paragraphs\Plugin\EntityReferenceSelection\ParagraphSelection;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Plugin\PluginFormInterface;
use Drupal\Core\Plugin\PluginWithFormsInterface;
use Drupal\Core\Plugin\PluginFormFactoryInterface;
use Drupal\Core\Layout\LayoutInterface;
use Drupal\Core\Form\SubformState;
use Drupal\Component\Utility\NestedArray;
use Drupal\Component\Utility\Html;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Language\LanguageManager;
use Drupal\Core\Field\FieldFilteredMarkup;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\OpenDialogCommand;
use Drupal\Core\Ajax\AppendCommand;
use Drupal\Core\Ajax\RemoveCommand;
use Drupal\Core\Ajax\CloseDialogCommand;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Ajax\InvokeCommand;
use Drupal\Core\Ajax\SettingsCommand;
use Drupal\paragraphs\ParagraphInterface;
use Drupal\paragraphs\ParagraphsTypeInterface;
use Drupal\layout_paragraphs\Ajax\LayoutParagraphsStateResetCommand;
use Drupal\layout_paragraphs\Ajax\LayoutParagraphsInsertCommand;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\inline_entity_form\ElementSubmit;
use Drupal\inline_entity_form\WidgetSubmit;
use Drupal\Component\Render\FormattableMarkup;
class LayoutParagraphsWidget extends WidgetBase implements ContainerFactoryPluginInterface {
protected $renderer;
protected $entityTypeManager;
protected $layoutPluginManager;
protected $pluginFormFactory;
protected $host;
protected $fieldName;
protected $wrapperId;
protected $itemFormWrapperId;
protected $languageManager;
protected $entityTypeBundleInfo;
protected $currentUser;
protected $entityDisplayRepository;
protected $config;
protected $moduleHandler;
protected $isTranslating;
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, Renderer $renderer, EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info, LayoutPluginManager $layout_plugin_manager, PluginFormFactoryInterface $plugin_form_manager, LanguageManager $language_manager, AccountProxyInterface $current_user, EntityDisplayRepositoryInterface $entity_display_repository, ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
$this->renderer = $renderer;
$this->entityTypeManager = $entity_type_manager;
$this->entityTypeBundleInfo = $entity_type_bundle_info;
$this->layoutPluginManager = $layout_plugin_manager;
$this->pluginFormFactory = $plugin_form_manager;
$this->fieldName = $this->fieldDefinition
->getName();
$this->languageManager = $language_manager;
$this->currentUser = $current_user;
$this->entityDisplayRepository = $entity_display_repository;
$this->config = $config_factory;
$this->moduleHandler = $module_handler;
}
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($plugin_id, $plugin_definition, $configuration['field_definition'], $configuration['settings'], $configuration['third_party_settings'], $container
->get('renderer'), $container
->get('entity_type.manager'), $container
->get('entity_type.bundle.info'), $container
->get('plugin.manager.core.layout'), $container
->get('plugin_form.factory'), $container
->get('language_manager'), $container
->get('current_user'), $container
->get('entity_display.repository'), $container
->get('config.factory'), $container
->get('module_handler'));
}
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$parents = $form['#parents'];
$widget_state = static::getWidgetState($parents, $this->fieldName, $form_state);
if (isset($widget_state['items'][intval($delta)])) {
$widget_state_item = $widget_state['items'][intval($delta)];
}
else {
return [];
}
if (!isset($widget_state_item['entity'])) {
return [];
}
if (isset($widget_state_item['is_new'])) {
return [];
}
$entity = $widget_state_item['entity'];
$layout_settings = $this
->getLayoutSettings($entity);
$layout = $layout_settings['layout'] ?? '';
$config = $layout_settings['config'] ?? [];
$region = $this
->extractInput($form, $form_state, $delta, 'region', $layout_settings['region']);
$this
->setLayoutSetting($widget_state['items'][$delta]['entity'], 'region', $region);
$parent_uuid = $this
->extractInput($form, $form_state, $delta, 'parent_uuid', $layout_settings['parent_uuid']);
$this
->setLayoutSetting($widget_state['items'][$delta]['entity'], 'parent_uuid', $parent_uuid);
$weight = $this
->extractInput($form, $form_state, $delta, '_weight', $widget_state_item['weight']);
$layout_instance = $layout ? $this->layoutPluginManager
->createInstance($layout, $config) : FALSE;
$preview = [];
if (isset($entity)) {
$preview_view_mode = $this
->getSetting('preview_view_mode');
$view_builder = $this->entityTypeManager
->getViewBuilder($entity
->getEntityTypeId());
$preview = $view_builder
->view($entity, $preview_view_mode, $entity
->language()
->getId());
$preview['#cache']['max-age'] = 0;
$preview['#attributes']['class'][] = Html::cleanCssIdentifier($entity
->uuid() . '-preview');
}
$show_paragraphs_labels = $this->config
->get('layout_paragraphs.settings')
->get('show_paragraph_labels');
$show_layout_labels = $this->config
->get('layout_paragraphs.settings')
->get('show_layout_labels');
$element = [
'#widget_item' => TRUE,
'#type' => 'container',
'#delta' => $delta,
'#entity' => $entity,
'#layout' => $layout,
'#region' => $region,
'#parent_uuid' => $parent_uuid,
'#weight' => $weight,
'#attributes' => [
'class' => [
'layout-paragraphs-item',
'paragraph-' . $entity
->uuid(),
],
'id' => [
$this->wrapperId . '--item-' . $delta,
],
],
'_weight' => [
'#type' => 'hidden',
'#default_value' => $weight,
'#weight' => -1000,
'#attributes' => [
'class' => [
'layout-paragraphs-weight',
],
],
],
'preview' => $preview,
'region' => [
'#type' => 'hidden',
'#attributes' => [
'class' => [
'layout-paragraphs-region',
],
],
'#default_value' => $region,
],
'uuid' => [
'#type' => 'hidden',
'#attributes' => [
'class' => [
'layout-paragraphs-uuid',
],
],
'#value' => $entity
->uuid(),
'#weight' => -999,
],
'parent_uuid' => [
'#type' => 'hidden',
'#attributes' => [
'class' => [
'layout-paragraphs-parent-uuid',
],
],
'#default_value' => $parent_uuid,
],
'entity' => [
'#type' => 'value',
'#value' => $entity,
],
'actions' => [
'#type' => 'container',
'#weight' => -1000,
'#attributes' => [
'class' => [
'layout-paragraphs-actions',
],
],
'edit' => [
'#type' => 'submit',
'#name' => 'edit_' . $this->wrapperId . '_' . $delta,
'#value' => $this
->t('Edit'),
'#attributes' => [
'class' => [
'layout-paragraphs-edit',
],
],
'#limit_validation_errors' => [],
'#submit' => [
[
$this,
'editItemSubmit',
],
],
'#delta' => $delta,
'#ajax' => [
'callback' => [
$this,
'editItemAjax',
],
'progress' => 'none',
],
'#element_parents' => $parents,
],
'remove' => [
'#type' => 'submit',
'#name' => 'remove_' . $this->wrapperId . '_' . $delta,
'#value' => $this
->t('Remove'),
'#attributes' => [
'class' => [
'layout-paragraphs-remove',
],
],
'#limit_validation_errors' => [],
'#submit' => [
[
$this,
'removeItemSubmit',
],
],
'#delta' => $delta,
'#ajax' => [
'callback' => [
$this,
'removeItemAjax',
],
'progress' => 'none',
],
'#element_parents' => $parents,
],
],
'label' => $show_paragraphs_labels ? [
'#type' => 'label',
'#title' => $entity
->getParagraphType()->label,
'#attributes' => [
'class' => [
'paragraph-type--label',
],
],
] : [],
];
if ($layout_instance) {
$element['#layout_instance'] = $layout_instance;
$element['#attributes']['class'][] = 'layout-paragraphs-layout';
if ($show_layout_labels) {
$element_label = $element['label']['#title'] ?? '';
$label = $layout_instance
->getPluginDefinition() ? $layout_instance
->getPluginDefinition()
->getLabel()
->__toString() : [];
if (!empty($label) && !empty($element_label)) {
$label = $element_label . ' - ' . $label;
}
$element['label'] = [
'#type' => 'label',
'#title' => $label,
'#title_display' => $label,
'#attributes' => [
'class' => [
'paragraph-layout--label',
],
],
];
}
foreach ($layout_instance
->getPluginDefinition()
->getRegionNames() as $region_name) {
$element['preview']['regions'][$region_name] = [
'#attributes' => [
'class' => [
'layout-paragraphs-layout-region',
'layout-paragraphs-layout-region--' . $region_name,
$entity
->uuid() . '-layout-region--' . $region_name,
],
'id' => [
$this->wrapperId . '--item-' . $delta . '-' . $region_name,
],
],
];
}
}
if (!empty($widget_state['items'][$delta]['is_new'])) {
$element['#is_new'] = TRUE;
}
else {
$element['#is_new'] = FALSE;
}
return $element;
}
public function getAllowedTypes(FieldDefinitionInterface $field_definition = NULL) {
$return_bundles = [];
$selection_manager = \Drupal::service('plugin.manager.entity_reference_selection');
$handler = $selection_manager
->getSelectionHandler($field_definition ?: $this->fieldDefinition);
if ($handler instanceof ParagraphSelection) {
$return_bundles = $handler
->getSortedAllowedTypes();
}
else {
$bundles = \Drupal::service('entity_type.bundle.info')
->getBundleInfo($field_definition ? $field_definition
->getSetting('target_type') : $this->fieldDefinition
->getSetting('target_type'));
$weight = 0;
foreach ($bundles as $machine_name => $bundle) {
if (empty($this
->getSelectionHandlerSetting('target_bundles')) || in_array($machine_name, $this
->getSelectionHandlerSetting('target_bundles'))) {
$return_bundles[$machine_name] = [
'label' => $bundle['label'],
'weight' => $weight,
];
$weight++;
}
}
}
return $return_bundles;
}
protected function formMultipleElements(FieldItemListInterface $items, array &$form, FormStateInterface $form_state) {
$parents = $form['#parents'];
$widget_state = static::getWidgetState($parents, $this->fieldName, $form_state);
$this->wrapperId = trim(Html::getId(implode('-', $parents) . '-' . $this->fieldName . '-wrapper'), '-');
$this->itemFormWrapperId = trim(Html::getId(implode('-', $parents) . '-' . $this->fieldName . '-form'), '-');
$target_bundles = array_keys($this
->getAllowedTypes());
$title = $this->fieldDefinition
->getLabel();
$description = FieldFilteredMarkup::create(\Drupal::token()
->replace($this->fieldDefinition
->getDescription()));
$host = $items
->getEntity();
$this
->initIsTranslating($form_state, $host);
if (!isset($widget_state['items'])) {
$widget_state['items'] = [];
$widget_state['open_form'] = FALSE;
$widget_state['remove_item'] = FALSE;
foreach ($items as $delta => $item) {
if ($paragraph = $item->entity) {
if ($item->entity instanceof ParagraphInterface) {
$langcode = $form_state
->get('langcode');
if (!$this->isTranslating) {
$langcode_key = $item->entity
->getEntityType()
->getKey('langcode');
if ($item->entity
->get($langcode_key)->value != $langcode) {
if ($item->entity
->hasTranslation($langcode)) {
$item->entity = $item->entity
->getTranslation($langcode);
}
else {
$item->entity
->set($langcode_key, $langcode);
}
}
}
else {
if (!$item->entity
->hasTranslation($langcode)) {
$entity_langcode = $item->entity
->language()
->getId();
$source = $form_state
->get([
'content_translation',
'source',
]);
$source_langcode = $source ? $source
->getId() : $entity_langcode;
if ($item->entity
->hasTranslation($source_langcode)) {
$entity = $item->entity
->getTranslation($source_langcode);
}
if ($item->entity
->hasField('content_translation_source')) {
$item->entity
->addTranslation($langcode, $entity
->toArray());
$translation = $item->entity
->getTranslation($langcode);
$manager = \Drupal::service('content_translation.manager');
$manager
->getTranslationMetadata($translation)
->setSource($item->entity
->language()
->getId());
}
}
if ($item->entity
->hasField('content_translation_source')) {
$item->entity = $item->entity
->getTranslation($langcode);
}
}
}
$widget_state['items'][$delta] = [
'entity' => $paragraph,
'weight' => $delta,
];
}
}
}
if ($items
->getFieldDefinition()
->isTranslatable()) {
$langcode = $this->languageManager
->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)
->getId();
foreach ($widget_state['items'] as $delta => $item) {
if (empty($item['entity']) || $item['entity']
->get('langcode')->value == $langcode) {
continue;
}
$duplicate = $item['entity']
->createDuplicate();
$duplicate
->set('langcode', $langcode);
$widget_state['items'][$delta]['entity'] = $duplicate;
}
}
static::setWidgetState($parents, $this->fieldName, $form_state, $widget_state);
$elements = [
'#field_name' => $this->fieldName,
'#required' => $this->fieldDefinition
->isRequired(),
'#title' => $title,
'#description' => $description,
'#attributes' => [
'class' => [
'layout-paragraphs-field',
],
],
'#type' => 'fieldset',
'#parents' => $form['#parents'],
'#id' => $this->wrapperId,
];
for ($delta = 0; $delta < $widget_state['items_count']; $delta++) {
$elements[$delta] = $this
->formSingleElement($items, $delta, [], $form, $form_state);
}
$elements['#after_build'][] = [
$this,
'buildLayouts',
];
if ($this
->allowReferenceChanges()) {
$elements['add_more'] = [
'actions' => [
'#attributes' => [
'class' => [
'js-hide',
],
],
'#type' => 'container',
],
];
$bundle_info = $this->entityTypeBundleInfo
->getBundleInfo('paragraph');
$options = [];
$types = [
'layout' => [],
'content' => [],
];
$bundle_ids = $target_bundles;
$target_type = $items
->getSetting('target_type');
$definition = $this->entityTypeManager
->getDefinition($target_type);
$storage = $this->entityTypeManager
->getStorage($definition
->getBundleEntityType());
foreach ($bundle_ids as $bundle_id) {
$type = $storage
->load($bundle_id);
$has_layout = count($this
->getAvailableLayoutsByType($type)) > 0;
$path = '';
if (method_exists($type, 'getIconUrl')) {
$path = $type
->getIconUrl();
}
$options[$bundle_id] = $bundle_info[$bundle_id]['label'];
$types[$has_layout ? 'layout' : 'content'][] = [
'id' => $bundle_id,
'name' => $bundle_info[$bundle_id]['label'],
'image' => $path,
'title' => $this
->t('Create new @name', [
'@name' => $bundle_info[$bundle_id]['label'],
]),
];
}
$elements['add_more']['actions']['type'] = [
'#title' => $this
->t('Choose type'),
'#type' => 'select',
'#options' => $options,
'#attributes' => [
'class' => [
'layout-paragraphs-item-type',
],
],
];
$elements['add_more']['actions']['item'] = [
'#type' => 'submit',
'#host' => $items
->getEntity(),
'#value' => $this
->t('Create New'),
'#submit' => [
[
$this,
'newItemSubmit',
],
],
'#element_validate' => [
[
$this,
'newItemValidate',
],
],
'#limit_validation_errors' => [
array_merge($parents, [
$this->fieldName,
'add_more',
]),
],
'#attributes' => [
'class' => [
'layout-paragraphs-add-item',
],
],
'#ajax' => [
'callback' => [
$this,
'editItemAjax',
],
],
'#name' => trim(implode('_', $parents) . '_' . $this->fieldName . '_add_item', '_'),
'#element_parents' => $parents,
];
$elements['add_more']['actions']['dom_id'] = [
'#type' => 'hidden',
'#attributes' => [
'class' => [
'dom-id',
],
],
];
$elements['add_more']['actions']['insert_method'] = [
'#type' => 'hidden',
'#attributes' => [
'class' => [
'insert-method',
],
],
];
$elements['add_more']['menu'] = [
'#type' => 'inline_template',
'#template' => '
<div class="layout-paragraphs-add-more-menu hidden">
<h4 class="visually-hidden">Add Item</h4>
<div class="layout-paragraphs-add-more-menu__search hidden">
<input type="text" placeholder="{{ search_text }}" />
</div>
<div class="layout-paragraphs-add-more-menu__group">
{% if types.layout %}
<div class="layout-paragraphs-add-more-menu__group--layout">
{% endif %}
{% for type in types.layout %}
<div class="layout-paragraphs-add-more-menu__item paragraph-type-{{type.id}} layout-paragraph">
<a data-type="{{ type.id }}" href="#{{ type.id }}" title="{{ type.title }}">
{% if type.image %}
<img src="{{ type.image }}" alt ="" />
{% endif %}
<div>{{ type.name }}</div>
</a>
</div>
{% endfor %}
{% if types.layout %}
</div>
{% endif %}
{% if types.content %}
<div class="layout-paragraphs-add-more-menu__group--content">
{% endif %}
{% for type in types.content %}
<div class="layout-paragraphs-add-more-menu__item paragraph-type-{{type.id}}">
<a data-type="{{ type.id }}" href="#{{ type.id }}" title="{{ type.title }}">
{% if type.image %}
<img src="{{ type.image }}" alt ="" />
{% endif %}
<div>{{ type.name }}</div>
</a>
</div>
{% endfor %}
{% if types.content %}
</div>
{% endif %}
</div>
</div>',
'#context' => [
'types' => $types,
'search_text' => $this
->t('Search'),
],
];
}
else {
$elements['is_translating_warning'] = [
'#type' => "html_tag",
'#tag' => 'div',
'#value' => t("This is Translation Context (editing a version not in the original language). <b>No new Layout Sections and Paragraphs can be added</b>."),
'#weight' => -1100,
'#attributes' => [
'class' => [
'is_translating_warning',
],
],
];
$elements['add_more'] = [
'actions' => [
'#isTranslating' => TRUE,
],
];
}
if ($widget_state['open_form'] !== FALSE) {
$this
->entityForm($elements, $form_state, $form);
}
if ($widget_state['remove_item'] !== FALSE) {
$this
->removeForm($elements, $form_state, $form);
}
$elements['disabled'] = [
'#type' => 'fieldset',
'#attributes' => [
'class' => [
'layout-paragraphs-disabled-items',
],
],
'#weight' => 999,
'#title' => $this
->t('Disabled Items'),
'description' => [
'#markup' => '<div class="layout-paragraphs-disabled-items__description">' . $this
->t('Drop items here that you want to keep disabled / hidden, without removing them permanently.') . '</div>',
],
'items' => [
'#type' => 'container',
'#attributes' => [
'class' => [
'layout-paragraphs-disabled-items__items',
],
],
],
];
$elements['#attached']['drupalSettings']['layoutParagraphsWidgets'][$this->wrapperId] = [
'wrapperId' => $this->wrapperId,
'maxDepth' => $this
->getSetting('nesting_depth'),
'requireLayouts' => $this
->getSetting('require_layouts'),
'isTranslating' => $elements["add_more"]["actions"]["#isTranslating"] ?? NULL,
'cardinality' => $this->fieldDefinition
->getFieldStorageDefinition()
->getCardinality(),
'itemsCount' => $this
->activeItemsCount($widget_state['items']),
];
$elements['#attached']['library'][] = 'layout_paragraphs/layout_paragraphs_widget';
return $elements;
}
public function form(FieldItemListInterface $items, array &$form, FormStateInterface $form_state, $get_delta = NULL) {
$elements = parent::form($items, $form, $form_state, $get_delta);
$elements['#multilingual'] = TRUE;
return $elements;
}
public function buildLayouts(array $elements, FormStateInterface $form_state) {
$tree = [
'#parents' => [],
];
$paragraph_elements = [];
$elements['#items'] = [];
foreach (Element::children($elements) as $index) {
$element = $elements[$index];
if (!empty($element['#widget_item'])) {
$paragraph_elements[$element['#entity']
->uuid()] = $element;
$elements['#items'][$element['#entity']
->uuid()] = $element;
unset($elements[$index]);
}
}
foreach ($paragraph_elements as $index => $element) {
$paragraph = $element['#entity'];
$layout_settings = $this
->getLayoutSettings($paragraph);
$parent_uuid = $layout_settings['parent_uuid'];
if ($parent_uuid && !isset($paragraph_elements[$parent_uuid])) {
$paragraph_elements[$index]['#region'] = '_disabled';
}
}
uasort($paragraph_elements, function ($a, $b) {
if ($a['#weight'] == $b['#weight']) {
return 0;
}
return $a['#weight'] < $b['#weight'] ? -1 : 1;
});
while ($element = array_shift($paragraph_elements)) {
$entity = $element['#entity'];
if ($element['#layout']) {
$tree[$entity
->uuid()] = $this
->buildLayout($element, $paragraph_elements, $elements['disabled']['items']);
}
else {
$tree[$entity
->uuid()] = $element;
}
if ($element['#region'] == '_disabled') {
$elements['disabled']['items'][$entity
->uuid()] = $tree[$entity
->uuid()];
unset($tree[$entity
->uuid()]);
}
}
$elements['active_items'] = [
'#type' => 'container',
'#attributes' => [
'class' => [
'active-items',
],
'id' => $this->wrapperId . "--active-items",
],
'items' => $tree,
'#parents' => [],
];
return $elements;
}
public function buildLayout($layout_element, &$elements, &$disabled_items) {
$entity = $layout_element['#entity'];
$uuid = $entity
->uuid();
$layout_element['preview']['regions'] = [
'#weight' => 100,
] + $layout_element['#layout_instance']
->build($layout_element['preview']['regions']);
$layout_element['preview']['regions']['#parents'] = $layout_element['#parents'];
foreach ($elements as $index => $element) {
if (!empty($element['#region']) && $element['#parent_uuid'] == $uuid) {
$child_entity = $element['#entity'];
$child_uuid = $child_entity
->uuid();
$region = $element['#region'];
if ($element['#layout']) {
$sub_element = $this
->buildLayout($element, $elements, $disabled_items);
}
else {
$sub_element = $element;
}
unset($elements[$index]);
if (isset($layout_element['preview']['regions'][$region])) {
$layout_element['preview']['regions'][$region][$child_uuid] = $sub_element;
}
else {
$disabled_items[$child_uuid] = $sub_element;
}
}
}
return $layout_element;
}
private function layoutLabels(array $layout_groups) {
$layouts = $this->layoutPluginManager
->getSortedDefinitions();
$layout_info = [];
foreach ($layout_groups as $group) {
foreach ($group as $layout_id => $layout_name) {
$layout_info[$layout_id] = $layouts[$layout_id]
->getLabel();
}
}
return $layout_info;
}
public function entityForm(array &$element, FormStateInterface $form_state, array &$form) {
$parents = $element['#parents'];
$widget_state = static::getWidgetState($parents, $this->fieldName, $form_state);
$delta = $widget_state['open_form'];
$entity = $widget_state['items'][$delta]['entity'];
if ($this->isTranslating && ($language = $form_state
->get('langcode'))) {
$entity = $entity
->getTranslation($language);
}
$display = EntityFormDisplay::collectRenderDisplay($entity, 'default');
$bundle_label = $entity->type->entity
->label();
$element['entity_form'] = [
'#entity' => $entity,
'#prefix' => '<div class="layout-paragraphs-form entity-type-' . $entity
->bundle() . '">',
'#suffix' => '</div>',
'#type' => 'container',
'#parents' => array_merge($parents, [
$this->fieldName,
'entity_form',
$delta,
]),
'#weight' => 1000,
'#delta' => $delta,
'#display' => $display,
'#attributes' => [
'data-dialog-title' => [
$entity
->id() ? $this
->t('Edit @type', [
'@type' => $bundle_label,
]) : $this
->t('Create new @type', [
'@type' => $bundle_label,
]),
],
],
];
if ($this->moduleHandler
->moduleExists('field_group')) {
$context = [
'entity_type' => $entity
->getEntityTypeId(),
'bundle' => $entity
->bundle(),
'entity' => $entity,
'context' => 'form',
'display_context' => 'form',
'mode' => $display
->getMode(),
];
field_group_attach_groups($element['entity_form'], $context);
if (method_exists(FormatterHelper::class, 'formProcess')) {
$element['entity_form']['#process'][] = [
FormatterHelper::class,
'formProcess',
];
}
elseif (function_exists('field_group_form_pre_render')) {
$element['entity_form']['#pre_render'][] = 'field_group_form_pre_render';
}
elseif (function_exists('field_group_form_process')) {
$element['entity_form']['#process'][] = 'field_group_form_process';
}
}
$display
->buildForm($entity, $element['entity_form'], $form_state);
if ($this
->isLayoutParagraph($entity)) {
$available_layouts = $this
->getAvailableLayouts($entity);
$layout_settings = $this
->getLayoutSettings($entity);
$layout = !empty($layout_settings['layout']) ? $layout_settings['layout'] : key($available_layouts);
$layout_plugin_config = $layout_settings['config'] ?? [];
$element['entity_form']['layout_selection'] = [
'#type' => 'container',
'#weight' => -1000,
'layout' => [
'#type' => 'radios',
'#title' => $this
->t('Select a layout:'),
'#options' => $available_layouts,
'#default_value' => $layout,
'#attributes' => [
'class' => [
'layout-paragraphs-layout-select',
],
],
'#required' => TRUE,
'#after_build' => [
[
$this,
'buildLayoutRadios',
],
],
],
'update' => [
'#type' => 'button',
'#value' => $this
->t('Update'),
'#name' => 'update_layout',
'#delta' => $delta,
'#limit_validation_errors' => [
array_merge($parents, [
$this->fieldName,
'entity_form',
$delta,
'layout_selection',
]),
],
'#attributes' => [
'class' => [
'js-hide',
],
],
'#element_parents' => $parents,
],
];
if ($this->currentUser
->hasPermission('edit layout paragraphs plugin config')) {
$element['entity_form']['layout_selection']['layout']['#ajax'] = [
'event' => 'change',
'callback' => [
$this,
'buildLayoutConfigurationFormAjax',
],
'trigger_as' => [
'name' => 'update_layout',
],
'wrapper' => 'layout-config',
'progress' => 'none',
];
$element['entity_form']['layout_selection']['update']['#ajax'] = [
'callback' => [
$this,
'buildLayoutConfigurationFormAjax',
],
'wrapper' => 'layout-config',
'progress' => 'none',
];
}
$element['entity_form']['layout_plugin_form'] = [
'#prefix' => '<div id="layout-config">',
'#suffix' => '</div>',
'#access' => $this->currentUser
->hasPermission('edit layout paragraphs plugin config'),
];
$layout_select_parents = array_merge($parents, [
$this->fieldName,
'entity_form',
$delta,
'layout_selection',
'layout',
]);
$updated_layout = $form_state
->getValue($layout_select_parents) ?? $layout;
if (!empty($updated_layout)) {
try {
$updated_layout_instance = $this->layoutPluginManager
->createInstance($updated_layout, $layout_plugin_config);
if ($layout && $updated_layout != $layout) {
$move_items = [];
$original_layout = $this->layoutPluginManager
->createInstance($layout);
$original_definition = $original_layout
->getPluginDefinition();
$original_regions = $original_definition
->getRegions();
$updated_layout_definition = $updated_layout_instance
->getPluginDefinition();
$updated_regions = $updated_layout_definition
->getRegions();
$updated_regions_options = [];
foreach ($updated_regions as $region_name => $region) {
$updated_regions_options[$region_name] = $region['label'];
}
$updated_regions_options['_disabled'] = $this
->t('Disabled');
foreach ($original_regions as $region_name => $region) {
if (!isset($updated_regions[$region_name]) && $this
->hasChildren($entity, $widget_state['items'], $region_name)) {
$move_items[$region_name] = [
'#type' => 'select',
'#wrapper_attributes' => [
'class' => [
'container-inline',
],
],
'#title' => $this
->t('Move items from the "@region" region to', [
'@region' => $region['label'],
]),
'#options' => $updated_regions_options,
];
}
}
if (count($move_items)) {
$element['entity_form']['layout_selection']['move_items'] = [
'#type' => 'fieldset',
'#title' => $this
->t('Move orphaned items'),
'#description' => $this
->t('The layout you selected has different regions than the previous one.'),
'items' => $move_items,
];
}
}
if ($layout_plugin = $this
->getLayoutPluginForm($updated_layout_instance)) {
$element['entity_form']['layout_plugin_form'] += [
'#type' => 'details',
'#title' => $this
->t('Layout Configuration'),
'#weight' => 999,
];
$element['entity_form']['layout_plugin_form'] += $layout_plugin
->buildConfigurationForm([], $form_state);
}
} catch (\Exception $e) {
watchdog_exception('Layout Paragraphs, updating_layout', $e);
}
}
}
$paragraphs_type = $entity
->getParagraphType();
if ($paragraphs_type && $this->currentUser
->hasPermission('edit behavior plugin settings') && (!$this->isTranslating || !$entity
->isDefaultTranslationAffectedOnly()) && ($behavior_plugins = $paragraphs_type
->getEnabledBehaviorPlugins())) {
$has_behavior_plugin_form = FALSE;
$element['entity_form']['behavior_plugins'] = [
'#type' => 'details',
'#title' => $this
->t('Behaviors'),
'#element_validate' => [
[
$this,
'validateBehaviors',
],
],
'#entity' => $entity,
'#weight' => -99,
];
foreach ($behavior_plugins as $plugin_id => $plugin) {
$element['entity_form']['behavior_plugins'][$plugin_id] = [
'#type' => 'container',
];
$subform_state = SubformState::createForSubform($element['entity_form']['behavior_plugins'][$plugin_id], $form, $form_state);
$plugin_form = $plugin
->buildBehaviorForm($entity, $element['entity_form']['behavior_plugins'][$plugin_id], $subform_state);
if (!empty(Element::children($plugin_form))) {
$element['entity_form']['behavior_plugins'][$plugin_id] = $plugin_form;
$has_behavior_plugin_form = TRUE;
}
}
if (!$has_behavior_plugin_form) {
unset($element['entity_form']['behavior_plugins']);
}
}
$element['entity_form'] += [
'actions' => [
'#weight' => 1000,
'#type' => 'actions',
'#attributes' => [
'class' => [
'layout-paragraphs-item-form-actions',
],
],
'submit' => [
'#type' => 'submit',
'#name' => 'save',
'#value' => $this
->t('Save'),
'#delta' => $delta,
'#uuid' => $entity
->uuid(),
'#limit_validation_errors' => [
array_merge($parents, [
$this->fieldName,
'entity_form',
$delta,
]),
],
'#submit' => [
[
$this,
'saveItemSubmit',
],
],
'#ajax' => [
'callback' => [
$this,
'saveItemAjax',
],
'progress' => 'none',
],
'#element_parents' => $parents,
],
'cancel' => [
'#type' => 'submit',
'#name' => 'cancel',
'#value' => $this
->t('Cancel'),
'#limit_validation_errors' => [],
'#delta' => $delta,
'#submit' => [
[
$this,
'cancelItemSubmit',
],
],
'#attributes' => [
'class' => [
'layout-paragraphs-cancel',
'button--danger',
],
],
'#ajax' => [
'callback' => [
$this,
'closeDialogAjax',
],
'progress' => 'none',
],
'#element_parents' => $parents,
],
],
];
$hide_untranslatable_fields = $entity
->isDefaultTranslationAffectedOnly();
foreach (Element::children($element['entity_form']) as $field) {
if ($entity
->hasField($field)) {
$field_definition = $entity
->get($field)
->getFieldDefinition();
$translatable = $entity->{$field}
->getFieldDefinition()
->isTranslatable();
$is_paragraph_field = FALSE;
if ($field_definition
->getType() == 'entity_reference_revisions') {
if ($field_definition
->getSetting('target_type') == 'paragraph') {
$is_paragraph_field = TRUE;
}
}
if (!$translatable && $this->isTranslating && !$is_paragraph_field) {
if ($hide_untranslatable_fields) {
$element['entity_form'][$field]['#access'] = FALSE;
}
else {
$element['entity_form'][$field]['widget']['#after_build'][] = [
static::class,
'addTranslatabilityClue',
];
}
}
}
}
$ief_widget_state = $form_state
->get('inline_entity_form');
if (!is_null($ief_widget_state)) {
ElementSubmit::attach($element['entity_form'], $form_state);
WidgetSubmit::attach($element['entity_form'], $form_state);
}
$this->moduleHandler
->alter('layout_paragraph_element_form', $element['entity_form'], $form_state, $form);
return $element;
}
public function removeForm(array &$element, FormStateInterface $form_state, array &$form) {
$parents = $element['#parents'];
$widget_state = static::getWidgetState($parents, $this->fieldName, $form_state);
$delta = $widget_state['remove_item'];
$entity = $widget_state['items'][$delta]['entity'];
$element['remove_form'] = [
'#prefix' => '<div class="layout-paragraphs-form">',
'#suffix' => '</div>',
'#type' => 'container',
'#entity' => $entity,
'#attributes' => [
'data-dialog-title' => [
$this
->t('Confirm removal'),
],
],
'message' => [
'#type' => 'markup',
'#markup' => $this
->t('Are you sure you want to permanently remove this <b>@type?</b><br />This action cannot be undone.', [
'@type' => $entity->type->entity
->label(),
]),
],
'actions' => [
'#type' => 'actions',
'#attributes' => [
'class' => [
'layout-paragraphs-item-form-actions',
],
],
'confirm' => [
'#type' => 'submit',
'#value' => $this
->t('Remove'),
'#delta' => $delta,
'#uuid' => $entity
->uuid(),
'#submit' => [
[
$this,
'removeItemConfirmSubmit',
],
],
'#ajax' => [
'callback' => [
$this,
'removeItemConfirmAjax',
],
'progress' => 'none',
],
'#limit_validation_errors' => [
array_merge($parents, [
$this->fieldName,
'remove_form',
]),
],
'#element_parents' => $parents,
],
'cancel' => [
'#type' => 'submit',
'#value' => $this
->t('Cancel'),
'#delta' => $delta,
'#submit' => [
[
$this,
'removeItemCancelSubmit',
],
],
'#attributes' => [
'class' => [
'layout-paragraphs-cancel',
'button--danger',
],
],
'#ajax' => [
'callback' => [
$this,
'closeDialogAjax',
],
'progress' => 'none',
],
'#element_parents' => $parents,
'#limit_validation_errors' => [],
],
],
'#delta' => $delta,
];
if ($this
->hasChildren($entity, $widget_state['items'])) {
$element['remove_form']['nested_items'] = [
'#type' => 'radios',
'#title' => $this
->t('This layout has nested items.'),
'#options' => [
'disable' => $this
->t('Disable nested items'),
'remove' => $this
->t('Permanently remove nested items'),
],
'disable' => [
'#description' => $this
->t('Nested items will be moved to the <b>Disabled Items</b> section and can be editied or restored.'),
],
'remove' => [
'#description' => $this
->t('Nested items will be permanenty removed.'),
],
'#default_value' => 'disable',
];
}
}
public function buildLayoutRadios($element) {
foreach (Element::children($element) as $key) {
$layout_name = $key;
$definition = $this->layoutPluginManager
->getDefinition($layout_name);
$icon = $definition
->getIcon(40, 60, 1, 0);
$rendered_icon = $this->renderer
->render($icon);
$title = new FormattableMarkup('<span class="layout-select__item-icon">@icon</span><span class="layout-select__item-title">@title</span>', [
'@title' => $element[$key]['#title'],
'@icon' => $rendered_icon,
]);
$element[$key]['#title'] = $title;
$element[$key]['#wrapper_attributes']['class'][] = 'layout-select__item';
}
$element['#wrapper_attributes'] = [
'class' => [
'layout-select',
],
];
return $element;
}
protected function extractInput(array $form, FormStateInterface $form_state, int $delta, $element_name, $default_value = '') {
$input = $form_state
->getUserInput();
$parents = $form['#parents'];
$key_exists = NULL;
if (is_array($element_name)) {
$element_path = array_merge($parents, [
$this->fieldName,
$delta,
], $element_name);
}
else {
$element_path = array_merge($parents, [
$this->fieldName,
$delta,
$element_name,
]);
}
$val = NestedArray::getValue($input, $element_path, $key_exists);
if ($key_exists) {
return Html::escape($val);
}
return $default_value;
}
protected function setUserInput(array $form, FormStateInterface &$form_state, int $delta, $element_name, $value) {
$input = $form_state
->getUserInput();
$parents = $form['#parents'];
if (is_array($element_name)) {
$element_path = array_merge($parents, [
$this->fieldName,
$delta,
], $element_name);
}
else {
$element_path = array_merge($parents, [
$this->fieldName,
$delta,
$element_name,
]);
}
NestedArray::setValue($input, $element_path, $value);
$form_state
->setUserInput($input);
}
public function validateBehaviors(array $element, FormStateInterface $form_state, array $form) {
$entity = $element['#entity'];
$paragraphs_type = $entity
->getParagraphType();
if ($this->currentUser
->hasPermission('edit behavior plugin settings')) {
foreach ($paragraphs_type
->getEnabledBehaviorPlugins() as $plugin_id => $plugin_values) {
if (!empty($element[$plugin_id])) {
$subform_state = SubformState::createForSubform($element[$plugin_id], $form_state
->getCompleteForm(), $form_state);
$plugin_values
->validateBehaviorForm($entity, $element[$plugin_id], $subform_state);
}
}
}
}
public function newItemValidate(array $element, FormStateInterface $form_state) {
$triggering_element = $form_state
->getTriggeringElement();
if ($triggering_element['#array_parents'] == $element['#array_parents']) {
$parents = $element['#element_parents'];
$widget_state = static::getWidgetState($parents, $this->fieldName, $form_state);
$count = $this
->activeItemsCount($widget_state['items']);
$cardinality = $this->fieldDefinition
->getFieldStorageDefinition()
->getCardinality();
if ($cardinality != -1 && $count >= $cardinality) {
$form_state
->setError($element, $this
->t('You can only add @cardinality or fewer items.', [
'@cardinality' => $cardinality,
]));
}
}
}
public function newItemSubmit(array $form, FormStateInterface $form_state) {
$element = $form_state
->getTriggeringElement();
$parents = $element['#element_parents'];
$widget_state = static::getWidgetState($parents, $this->fieldName, $form_state);
if (!empty($element['#bundle_id'])) {
$bundle_id = $element['#bundle_id'];
}
else {
$element_parents = $element['#parents'];
array_splice($element_parents, -1, 1, 'type');
$bundle_id = $form_state
->getValue($element_parents);
}
try {
$entity_type = $this->entityTypeManager
->getDefinition('paragraph');
$bundle_key = $entity_type
->getKey('bundle');
$paragraph_entity = $this->entityTypeManager
->getStorage('paragraph')
->create([
$bundle_key => $bundle_id,
]);
$paragraph_entity
->setParentEntity($element['#host'], $this->fieldDefinition
->getName());
$widget_state['items'][] = [
'entity' => $paragraph_entity,
'is_new' => TRUE,
'weight' => count($widget_state['items']),
];
$widget_state['open_form'] = $widget_state['items_count'];
$widget_state['items_count'] = count($widget_state['items']);
static::setWidgetState($parents, $this->fieldName, $form_state, $widget_state);
$form_state
->setRebuild();
} catch (\Exception $e) {
watchdog_exception('Layout Paragraphs, new Item Submit', $e);
}
}
public function editItemSubmit(array $form, FormStateInterface $form_state) {
$element = $form_state
->getTriggeringElement();
$parents = $element['#element_parents'];
$delta = $element['#delta'];
$widget_state = static::getWidgetState($parents, $this->fieldName, $form_state);
$widget_state['open_form'] = $delta;
static::setWidgetState($parents, $this->fieldName, $form_state, $widget_state);
$form_state
->setRebuild();
}
public function removeItemSubmit(array $form, FormStateInterface $form_state) {
$element = $form_state
->getTriggeringElement();
$parents = $element['#element_parents'];
$delta = $element['#delta'];
$widget_state = static::getWidgetState($parents, $this->fieldName, $form_state);
$widget_state['remove_item'] = $delta;
static::setWidgetState($parents, $this->fieldName, $form_state, $widget_state);
$form_state
->setRebuild();
}
public function removeItemConfirmSubmit(array $form, FormStateInterface $form_state) {
$element = $form_state
->getTriggeringElement();
$element_parents = $element['#parents'];
$remove_form_parents = array_splice($element_parents, 0, -2);
$nested_items_path = array_merge($remove_form_parents, [
'nested_items',
]);
$nested_items_value = $form_state
->getValue($nested_items_path);
$uuid = $element['#uuid'];
$parents = $element['#element_parents'];
$delta = $element['#delta'];
$widget_state = static::getWidgetState($parents, $this->fieldName, $form_state);
if ($nested_items_value == 'remove') {
$this
->removeChildren($widget_state['items'], $uuid);
}
unset($widget_state['items'][$delta]['entity']);
$widget_state['remove_item'] = FALSE;
static::setWidgetState($parents, $this->fieldName, $form_state, $widget_state);
$form_state
->setRebuild();
}
public function removeItemCancelSubmit(array $form, FormStateInterface $form_state) {
$element = $form_state
->getTriggeringElement();
$parents = $element['#element_parents'];
$widget_state = static::getWidgetState($parents, $this->fieldName, $form_state);
$widget_state['remove_item'] = FALSE;
static::setWidgetState($parents, $this->fieldName, $form_state, $widget_state);
$form_state
->setRebuild();
}
public function saveItemSubmit(array $form, FormStateInterface $form_state) {
$element = $form_state
->getTriggeringElement();
$parents = $element['#element_parents'];
$delta = $element['#delta'];
$element_array_parents = $element['#array_parents'];
$item_array_parents = array_splice($element_array_parents, 0, -2);
$item_form = NestedArray::getValue($form, $item_array_parents);
$display = $item_form['#display'];
$widget_state = static::getWidgetState($parents, $this->fieldName, $form_state);
unset($widget_state['items'][$delta]['is_new']);
$paragraph = $widget_state['items'][$delta]['entity'];
if ($this->isTranslating && ($language = $form_state
->get('langcode'))) {
$paragraph = $paragraph
->getTranslation($language);
}
$display
->extractFormValues($paragraph, $item_form, $form_state);
$paragraphs_type = $paragraph
->getParagraphType();
if ($this->currentUser
->hasPermission('edit behavior plugin settings')) {
foreach ($paragraphs_type
->getEnabledBehaviorPlugins() as $plugin_id => $plugin_values) {
$plugin_form = isset($item_form['behavior_plugins']) ? $item_form['behavior_plugins'][$plugin_id] : [];
if (!empty($plugin_form) && !empty(Element::children($plugin_form))) {
$subform_state = SubformState::createForSubform($item_form['behavior_plugins'][$plugin_id], $form_state
->getCompleteForm(), $form_state);
$plugin_values
->submitBehaviorForm($paragraph, $item_form['behavior_plugins'][$plugin_id], $subform_state);
}
}
}
$widget_state['items'][$delta]['entity'] = $paragraph;
if (!empty($item_form['layout_selection']['layout'])) {
$layout_settings = $this
->getLayoutSettings($paragraph);
$layout = $form_state
->getValue($item_form['layout_selection']['layout']['#parents']);
$layout_settings['layout'] = $layout;
if (!empty($item_form['layout_plugin_form'])) {
try {
$layout_instance = $this->layoutPluginManager
->createInstance($layout);
if ($this
->getLayoutPluginForm($layout_instance)) {
$subform_state = SubformState::createForSubform($item_form['layout_plugin_form'], $form_state
->getCompleteForm(), $form_state);
$layout_instance
->submitConfigurationForm($item_form['layout_plugin_form'], $subform_state);
$layout_settings['config'] = $layout_instance
->getConfiguration();
}
$this
->setLayoutSettings($paragraph, $layout_settings);
} catch (\Exception $e) {
watchdog_exception('Layout Paragraphs, Layout Instance generation', $e);
}
}
if (isset($item_form['layout_selection']['move_items'])) {
$move_items = $form_state
->getValue($item_form['layout_selection']['move_items']['#parents']);
if ($move_items && isset($move_items['items'])) {
$parent_uuid = $paragraph
->uuid();
foreach ($move_items['items'] as $from_region => $to_region) {
foreach ($widget_state['items'] as $delta => $item) {
$layout_settings = $this
->getLayoutSettings($item['entity']);
if ($layout_settings['parent_uuid'] == $parent_uuid && $layout_settings['region'] == $from_region) {
$this
->setLayoutSetting($widget_state['items'][$delta]['entity'], 'region', $to_region);
$path = array_merge($parents, [
$this->fieldName,
$delta,
'region',
]);
$input = $form_state
->getUserInput();
NestedArray::setValue($input, $path, $to_region);
$form_state
->setUserInput($input);
}
}
}
}
}
}
$widget_state['open_form'] = FALSE;
static::setWidgetState($parents, $this->fieldName, $form_state, $widget_state);
$form_state
->setRebuild();
}
public function cancelItemSubmit(array $form, FormStateInterface $form_state) {
$element = $form_state
->getTriggeringElement();
$parents = $element['#element_parents'];
$delta = $element['#delta'];
$widget_state = static::getWidgetState($parents, $this->fieldName, $form_state);
if (!empty($widget_state['items'][$delta]['is_new'])) {
unset($widget_state['items'][$delta]['entity']);
}
$widget_state['open_form'] = FALSE;
static::setWidgetState($parents, $this->fieldName, $form_state, $widget_state);
$form_state
->setRebuild();
}
public function elementAjax(array $form, FormStateInterface $form_state) {
$element = $form_state
->getTriggeringElement();
$parents = $element['#element_parents'];
$field_state = static::getWidgetState($parents, $this->fieldName, $form_state);
$widget_field = NestedArray::getValue($form, $field_state['array_parents']);
$html_id = $this
->entityFormHtmlId($field_state);
$response = new AjaxResponse();
$response
->addCommand(new CloseDialogCommand('#' . $html_id));
$response
->addCommand(new ReplaceCommand('#' . $this->wrapperId, $widget_field));
$response
->addCommand(new LayoutParagraphsStateResetCommand('#' . $this->wrapperId));
return $response;
}
public function saveItemAjax(array $form, FormStateInterface $form_state) {
$triggering_element = $form_state
->getTriggeringElement();
$uuid = $triggering_element['#uuid'];
$delta = $triggering_element['#delta'];
$parents = $triggering_element['#element_parents'];
$field_state = static::getWidgetState($parents, $this->fieldName, $form_state);
$widget_field = NestedArray::getValue($form, $field_state['array_parents']);
$html_id = $this
->entityFormHtmlId($field_state);
$response = new AjaxResponse();
if ($form_state
->hasAnyErrors()) {
$entity_form = $widget_field['entity_form'];
$selector = '#' . $this->wrapperId . ' .layout-paragraphs-form';
$entity_form['status'] = [
'#weight' => -100,
'#type' => 'status_messages',
];
$response
->addCommand(new ReplaceCommand($selector, $entity_form));
}
else {
$element = static::findElementByUuid($widget_field, $uuid);
$path = array_merge($widget_field['#parents'], [
'add_more',
'actions',
]);
$target_id = $form_state
->getValue(array_merge($path, [
'dom_id',
]));
$insert_method = $form_state
->getValue(array_merge($path, [
'insert_method',
]));
$settings = [
'target_id' => $target_id,
'insert_method' => $insert_method,
'element_id' => $this->wrapperId . '--item-' . $delta,
];
$response
->addCommand(new LayoutParagraphsInsertCommand($settings, $element));
$response
->addCommand(new CloseDialogCommand('#' . $html_id));
$response
->addCommand(new SettingsCommand([
'layoutParagraphsWidgets' => [
$this->wrapperId => [
'itemsCount' => $this
->activeItemsCount($field_state['items']),
],
],
], TRUE));
}
$disabled_bin = $widget_field['disabled'];
$response
->addCommand(new ReplaceCommand('#' . $this->wrapperId . ' .layout-paragraphs-disabled-items', $disabled_bin));
return $response;
}
public function editItemAjax(array $form, FormStateInterface $form_state) {
$element = $form_state
->getTriggeringElement();
$parents = $element['#element_parents'];
$field_state = static::getWidgetState($parents, $this->fieldName, $form_state);
$widget_field = NestedArray::getValue($form, $field_state['array_parents']);
$entity_form = $widget_field['entity_form'];
$entity = $entity_form['#entity'];
$type = $entity
->getParagraphType();
$html_id = $this
->entityFormHtmlId($field_state);
$dialog_options = [
'dialogClass' => 'layout-paragraphs-dialog',
'modal' => TRUE,
'appendTo' => '#' . $this->wrapperId,
'width' => 800,
'drupalAutoButtons' => FALSE,
];
$response = new AjaxResponse();
if ($form_state
->hasAnyErrors()) {
$content = [
'status' => [
'#weight' => -100,
'#type' => 'status_messages',
],
];
$response
->addCommand(new OpenDialogCommand('#' . $html_id, $this
->t('Unexpected Error'), $content, $dialog_options));
$response
->addCommand(new LayoutParagraphsStateResetCommand('#' . $this->wrapperId));
}
else {
$response
->addCommand(new AppendCommand('#' . $this->wrapperId, '<div id="' . $html_id . '"></div>'));
$response
->addCommand(new OpenDialogCommand('#' . $html_id, $this
->t('Edit @type', [
'@type' => $type
->label(),
]), $entity_form, $dialog_options));
$response
->addCommand(new LayoutParagraphsStateResetCommand('#' . $this->wrapperId));
}
return $response;
}
public function removeItemAjax(array $form, FormStateInterface $form_state) {
$element = $form_state
->getTriggeringElement();
$parents = $element['#element_parents'];
$field_state = static::getWidgetState($parents, $this->fieldName, $form_state);
$widget_field = NestedArray::getValue($form, $field_state['array_parents']);
$entity_form = $widget_field['remove_form'];
$entity = $entity_form['#entity'];
$type = $entity
->getParagraphType();
$html_id = $this
->entityFormHtmlId($field_state);
$dialog_options = [
'dialogClass' => 'layout-paragraphs-dialog',
'modal' => TRUE,
'appendTo' => '#' . $this->wrapperId,
'width' => 800,
'drupalAutoButtons' => FALSE,
];
$response = new AjaxResponse();
$response
->addCommand(new AppendCommand('#' . $this->wrapperId, '<div id="' . $html_id . '"></div>'));
$response
->addCommand(new OpenDialogCommand('#' . $html_id, $this
->t('Remove @type', [
'@type' => $type
->label(),
]), $entity_form, $dialog_options));
$response
->addCommand(new LayoutParagraphsStateResetCommand('#' . $this->wrapperId));
return $response;
}
public function removeItemConfirmAjax(array $form, FormStateInterface $form_state) {
$element = $form_state
->getTriggeringElement();
$uuid = $element['#uuid'];
$parents = $element['#element_parents'];
$field_state = static::getWidgetState($parents, $this->fieldName, $form_state);
$html_id = $this
->entityFormHtmlId($field_state);
$widget_field = NestedArray::getValue($form, $field_state['array_parents']);
$disabled_bin = $widget_field['disabled'];
$response = new AjaxResponse();
$response
->addCommand(new RemoveCommand('.paragraph-' . $uuid));
$response
->addCommand(new CloseDialogCommand('#' . $html_id));
$response
->addCommand(new ReplaceCommand('#' . $this->wrapperId . ' .layout-paragraphs-disabled-items', $disabled_bin));
$response
->addCommand(new SettingsCommand([
'layoutParagraphsWidgets' => [
$this->wrapperId => [
'itemsCount' => $this
->activeItemsCount($field_state['items']),
],
],
], TRUE));
return $response;
}
public function closeDialogAjax(array $form, FormStateInterface $form_state) {
$element = $form_state
->getTriggeringElement();
$parents = $element['#element_parents'];
$field_state = static::getWidgetState($parents, $this->fieldName, $form_state);
$html_id = $this
->entityFormHtmlId($field_state);
$response = new AjaxResponse();
$response
->addCommand(new CloseDialogCommand('#' . $html_id));
return $response;
}
public function buildLayoutConfigurationFormAjax(array $form, FormStateInterface $form_state) {
$element = $form_state
->getTriggeringElement();
$parents = $element['#array_parents'];
$parents = array_splice($parents, 0, -2);
if ($entity_form = NestedArray::getValue($form, $parents)) {
$response = new AjaxResponse();
$response
->addCommand(new ReplaceCommand('#' . $this->wrapperId . ' .layout-paragraphs-form', $entity_form));
$response
->addCommand(new InvokeCommand('#' . $this->wrapperId . ' .layout-paragraphs-form input:checked', 'focus'));
$response
->addCommand(new LayoutParagraphsStateResetCommand('#' . $this->wrapperId));
return $response;
}
else {
return [];
}
}
protected function activeItemsCount(array $items) {
return array_reduce($items, function ($count, $item) {
return isset($item['entity']) ? $count + 1 : $count;
}, 0);
}
public static function findElementByUuid(array $array, string $uuid) {
$element = FALSE;
if (isset($array['active_items']['items']) && isset($array['disabled']['items'])) {
return static::findElementByUuid($array['active_items']['items'] + $array['disabled']['items'], $uuid);
}
foreach ($array as $key => $item) {
if (is_array($item)) {
if (isset($item['#entity'])) {
if ($item['#entity']
->uuid() == $uuid) {
return $item;
}
}
if (isset($item['preview']['regions'])) {
foreach (Element::children($item['preview']['regions']) as $region_name) {
if ($element = static::findElementByUuid($item['preview']['regions'][$region_name], $uuid)) {
return $element;
}
}
}
}
}
return $element;
}
public function hasChildren(ParagraphInterface $parent, array $items, string $region = '') {
$uuid = $parent
->uuid();
foreach ($items as $item) {
if (isset($item['entity'])) {
$layout_settings = $this
->getLayoutSettings($item['entity']);
if ($region) {
if ($layout_settings['region'] == $region && $layout_settings['parent_uuid'] == $uuid) {
return TRUE;
}
}
elseif ($layout_settings['parent_uuid'] == $uuid) {
return TRUE;
}
}
}
return FALSE;
}
protected function removeChildren(array &$items, string $uuid) {
foreach ($items as $index => $item) {
if (isset($item['entity'])) {
$paragraph = $item['entity'];
$layout_settings = $this
->getLayoutSettings($paragraph);
if ($layout_settings['parent_uuid'] == $uuid) {
unset($items[$index]['entity']);
$this
->removeChildren($items, $paragraph
->uuid());
}
}
}
}
private function entityFormHtmlId(array $field_state) {
return trim(Html::getId(implode('-', $field_state['array_parents']) . '-entity-form'), '-');
}
protected function getSelectionHandlerSetting($setting_name) {
$settings = $this
->getFieldSetting('handler_settings');
return isset($settings[$setting_name]) ? $settings[$setting_name] : NULL;
}
protected function getLayoutSettings(ParagraphInterface $paragraph) {
$defaults = [
'parent_uuid' => '',
'layout' => '',
'region' => '',
'config' => [],
];
$behavior_settings = $paragraph
->getAllBehaviorSettings();
return ($behavior_settings['layout_paragraphs'] ?? []) + $defaults;
}
protected function setLayoutSetting(ParagraphInterface &$paragraph, string $name, $value) {
$settings = $this
->getLayoutSettings($paragraph);
$settings[$name] = $value;
$this
->setLayoutSettings($paragraph, $settings);
}
protected function setLayoutSettings(ParagraphInterface &$paragraph, array $layout_settings) {
$paragraph
->setBehaviorSettings('layout_paragraphs', $layout_settings);
}
protected function isLayoutParagraph(ParagraphInterface &$paragraph) {
$available_layouts = $this
->getAvailableLayouts($paragraph);
return count($available_layouts) > 0;
}
protected function getAvailableLayouts(ParagraphInterface &$paragraph) {
$paragraphs_type = $paragraph
->getParagraphType();
return $this
->getAvailableLayoutsByType($paragraphs_type);
}
protected function getAvailableLayoutsByType(ParagraphsTypeInterface $paragraphs_type) {
$plugins = $paragraphs_type
->getEnabledBehaviorPlugins();
if (isset($plugins['layout_paragraphs'])) {
$layout_paragraphs_plugin = $paragraphs_type
->getBehaviorPlugin('layout_paragraphs');
$config = $layout_paragraphs_plugin
->getConfiguration();
return $config['available_layouts'] ?? [];
}
return [];
}
public function settingsForm(array $form, FormStateInterface $form_state) {
$entity_type_id = $this
->getFieldSetting('target_type');
$element = parent::settingsForm($form, $form_state);
$element['preview_view_mode'] = [
'#type' => 'select',
'#title' => $this
->t('Preview view mode'),
'#default_value' => $this
->getSetting('preview_view_mode'),
'#options' => $this->entityDisplayRepository
->getViewModeOptions($entity_type_id),
'#description' => $this
->t('View mode for the referenced entity preview on the edit form. Automatically falls back to "default", if it is not enabled in the referenced entity type displays.'),
];
$element['nesting_depth'] = [
'#type' => 'select',
'#title' => $this
->t('Maximum nesting depth'),
'#options' => range(0, 10),
'#default_value' => $this
->getSetting('nesting_depth'),
'#description' => $this
->t('Choosing 0 will prevent nesting layouts within other layouts.'),
];
$element['require_layouts'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Require paragraphs to be added inside a layout'),
'#default_value' => $this
->getSetting('require_layouts'),
];
return $element;
}
public function settingsSummary() {
$entity_type = $this
->getFieldSetting('target_type');
$target_bundles = array_keys($this
->getAllowedTypes());
$definition = $this->entityTypeManager
->getDefinition($entity_type);
$storage = $this->entityTypeManager
->getStorage($definition
->getBundleEntityType());
$has_layout = FALSE;
try {
if (!empty($target_bundles)) {
foreach ($target_bundles as $target_bundle) {
$type = $storage
->load($target_bundle);
if (count($this
->getAvailableLayoutsByType($type)) > 0) {
$has_layout = TRUE;
break;
}
}
}
} catch (\Exception $e) {
watchdog_exception('paragraphs layout widget, behaviour plugin', $e);
}
if (!$has_layout) {
$field_name = $this->fieldDefinition
->getLabel();
$message = $this
->t('To use layouts with the "@field_name" field, make sure you have enabled the "Paragraphs Layout" behavior for at least one target paragraph type.', [
'@field_name' => $field_name,
]);
$this
->messenger()
->addMessage($message, $this
->messenger()::TYPE_WARNING);
}
$summary = parent::settingsSummary();
$summary[] = $this
->t('Preview view mode: @preview_view_mode', [
'@preview_view_mode' => $this
->getSetting('preview_view_mode'),
]);
$summary[] = $this
->t('Maximum nesting depth: @max_depth', [
'@max_depth' => $this
->getSetting('nesting_depth'),
]);
if ($this
->getSetting('require_layouts')) {
$summary[] = $this
->t('Paragraphs <b>must be</b> added within layouts.');
}
else {
$summary[] = $this
->t('Layouts are optional.');
}
$summary[] = $this
->t('Maximum nesting depth: @max_depth', [
'@max_depth' => $this
->getSetting('nesting_depth'),
]);
return $summary;
}
public static function defaultSettings() {
$defaults = parent::defaultSettings();
$defaults += [
'preview_view_mode' => 'default',
'nesting_depth' => 0,
'require_layouts' => 0,
];
return $defaults;
}
public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
foreach ($values as $delta => &$item) {
unset($values[$delta]['actions']);
if (isset($item['entity']) && $item['entity'] instanceof ParagraphInterface) {
$paragraph_entity = $item['entity'];
$behavior_settings = $this
->getLayoutSettings($paragraph_entity);
$new_behavior_settings = [
'region' => $item['region'],
'parent_uuid' => $item['parent_uuid'],
];
$this
->setLayoutSettings($paragraph_entity, $new_behavior_settings + $behavior_settings);
$paragraph_entity
->setNeedsSave(TRUE);
$item['target_id'] = $paragraph_entity
->id();
$item['target_revision_id'] = $paragraph_entity
->getRevisionId();
}
}
return $values;
}
protected function getLayoutPluginForm(LayoutInterface $layout) {
if ($layout instanceof PluginWithFormsInterface) {
try {
return $this->pluginFormFactory
->createInstance($layout, 'configure');
} catch (\Exception $e) {
watchdog_exception('Erl, Layout Configuration', $e);
}
}
if ($layout instanceof PluginFormInterface) {
return $layout;
}
return NULL;
}
protected function initIsTranslating(FormStateInterface $form_state, ContentEntityInterface $host) {
if ($this->isTranslating != NULL) {
return;
}
$this->isTranslating = FALSE;
if (!$host
->isTranslatable()) {
return;
}
if (!$host
->getEntityType()
->hasKey('default_langcode')) {
return;
}
$default_langcode_key = $host
->getEntityType()
->getKey('default_langcode');
if (!$host
->hasField($default_langcode_key)) {
return;
}
if (!empty($form_state
->get('content_translation'))) {
$this->isTranslating = TRUE;
}
$langcode = $form_state
->get('langcode');
if ($host
->hasTranslation($langcode) && $host
->getTranslation($langcode)
->get($default_langcode_key)->value == 0) {
$this->isTranslating = TRUE;
}
}
protected function allowReferenceChanges() {
return !$this->isTranslating;
}
public static function addTranslatabilityClue(array $element, FormStateInterface $form_state) {
static $suffix, $fapi_title_elements;
if (!isset($suffix)) {
$suffix = ' <span class="translation-entity-all-languages">(' . t('all languages') . ')</span>';
$fapi_title_elements = array_flip([
'checkbox',
'checkboxes',
'date',
'details',
'fieldset',
'file',
'item',
'password',
'password_confirm',
'radio',
'radios',
'select',
'textarea',
'textfield',
'weight',
]);
}
if (isset($element['#type']) && isset($fapi_title_elements[$element['#type']]) && isset($element['#title'])) {
$element['#title'] .= $suffix;
}
elseif ($children = Element::children($element)) {
foreach ($children as $delta) {
$element[$delta] = static::addTranslatabilityClue($element[$delta], $form_state);
}
}
elseif (isset($element['#title'])) {
$element['#title'] .= $suffix;
}
return $element;
}
}