You are here

public function LayoutParagraphsWidget::entityForm in Layout Paragraphs 1.0.x

Builds an entity form for a paragraph item.

Add form components in this rendering order: 1. Layout selection, if this is a layout paragraph. 2. The entity's fields from the form display. 3. The layout plugin config form, if exists. 4. The paragraph behaviors plugins form, if any exist.

Parameters

array $element: The form element.

\Drupal\Core\Form\FormStateInterface $form_state: The form_state object.

array $form: The form array.

Return value

array The entity form element.

1 call to LayoutParagraphsWidget::entityForm()
LayoutParagraphsWidget::formMultipleElements in src/Plugin/Field/FieldWidget/LayoutParagraphsWidget.php
Builds the main widget form array container/wrapper.

File

src/Plugin/Field/FieldWidget/LayoutParagraphsWidget.php, line 952

Class

LayoutParagraphsWidget
Entity Reference with Layout field widget.

Namespace

Drupal\layout_paragraphs\Plugin\Field\FieldWidget

Code

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'];

  /** @var \Drupal\paragraphs\Entity\Paragraph $entity */
  $entity = $widget_state['items'][$delta]['entity'];

  // Set correct default language for the 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,
        ]),
      ],
    ],
  ];

  // Support for Field Group module based on Paragraphs module.
  // @todo Remove as part of https://www.drupal.org/node/2640056
  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);

  // Add layout selection form if "paragraphs layout" behavior is enabled.
  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,
      ],
    ];

    // Switching layouts should change the layout plugin options form
    // with Ajax for users with adequate permissions.
    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'),
    ];

    // Add the layout configuration form if applicable.
    $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 the user selects a new layout,
        // we provide a way for them to choose
        // what to do with items from regions
        // that no longer exist.
        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);
      }
    }
  }

  // Add behavior forms if applicable.
  $paragraphs_type = $entity
    ->getParagraphType();

  // @todo: Check translation functionality.
  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']);
    }
  }

  // Add save, cancel, etc.
  $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)) {

      /** @var \Drupal\Core\Field\FieldDefinitionInterface $field_definition */
      $field_definition = $entity
        ->get($field)
        ->getFieldDefinition();
      $translatable = $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;
        }
      }
      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',
          ];
        }
      }
    }
  }

  // Add compatibility for Inline Entity Form module.
  // See https://www.drupal.org/project/inline_entity_form/issues/3160809
  // and https://www.drupal.org/project/layout_paragraphs/issues/3160806
  $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);
  }

  // Allow others modules to adjust the Layout Paragraph Element Dialog Form.
  $this->moduleHandler
    ->alter('layout_paragraph_element_form', $element['entity_form'], $form_state, $form);
  return $element;
}