compose-8.x-1.x-dev/src/Plugin/Field/FieldWidget/ComposeWidget.php

src/Plugin/Field/FieldWidget/ComposeWidget.php
<?php

namespace Drupal\compose\Plugin\Field\FieldWidget;

use Drupal\Core\Render\Element;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\inline_entity_form\Plugin\Field\FieldWidget\InlineEntityFormComplex;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\InsertCommand;
use Drupal\Core\Ajax\AppendCommand;
use Drupal\Core\Ajax\CloseDialogCommand;
use Drupal\compose\Ajax\ComposeOpenModalDialog;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Ajax\PrependCommand;

/**
 * Plugin implementation of the 'compose_widget' widget.
 *
 * @FieldWidget(
 *   id = "compose_widget",
 *   label = @Translation("Compose Widget"),
 *   field_types = {
 *     "entity_reference",
 *     "entity_reference_revisions",
 *   },
 *   multiple_values = true
 * )
 */
class ComposeWidget extends InlineEntityFormComplex implements ContainerFactoryPluginInterface {
  use StringTranslationTrait;

  /**
   * {@inheritdoc}
   */
  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
    $element = parent::formElement($items, $delta, $element, $form, $form_state);
    $element['#type'] = 'container';
    $element['_compose_field_title'] = [
      '#theme' => 'form_element_label',
      '#weight' => -99,
    ] + array_intersect_key($element, array_flip([
      '#id',
      '#required',
      '#title',
      '#title_display',
    ]));
    $settings = $this->getSettings();
    $ief_id = $element['#ief_id'];
    $field_name = $this->fieldDefinition->getName();
    $parents = array_merge($form['#parents'], [$field_name, 'form']);
    $labels = $element['#ief_labels'] = $this->getEntityTypeLabels();

    // @todo Add bundle labels once added, hopefully in 8.8.x
    // @see https://www.drupal.org/project/drupal/issues/2765065
    // Get the target bundles that can be referenced from this field.
    // $bundles = $items->getFieldDefinition()->getSetting('handler_settings')['target_bundles']);
    // $entity = $items->getEntity();
    //
    // if (count($bundles) === 1) {
    //   $labels = [
    //     'singular' => $entity->getSingularLabel(),
    //     'plural' => $entity->getPluralLabel()
    //   ];
    // }

    // Pass along the IEF form id, field name and entity type.
    $element['entities']['#form_id'] = $ief_id;
    $element['entities']['#field_name'] = $items->getName();
    // $element['entities']['#entity_type'] = $items->getEntity()->getEntityTypeId();

    // Use our own #theme implementation.
    $element['entities']['#theme'] = 'compose_entity_widget';
    $element['#attached']['library'][] = 'compose/compose-entity-widget';

    // Add a wrapper to target for the widget level dialog content and container
    $dialog_container_selector = 'compose-widget-dialog-container-' . $ief_id;
    $dialog_content_selector = 'compose-widget-dialog-content-' . $ief_id;

    $element['compose_dialog_container'] = [
      '#type' => 'html_tag',
      '#tag' => 'div',
      '#attributes' => [
        "id" => $dialog_container_selector,
        'class' => [$dialog_container_selector],
      ],
      '#value' => '',
    ];
    // Make sure form gets output as a container if needed
    if (!isset($element['form'])) {
      $element['form'] = [
        '#type' => 'html_tag',
        '#tag' => 'div',
        '#attributes' => [
          "id" => $dialog_content_selector,
        ],
        '#value' => '',
      ];
    }
    else {
      $element['form']['#attributes']['id'] = $dialog_content_selector;
    }

    // Start "Add New" form
    //show button with custom callback and submit
    $element['actions']['#attributes']['class'] = ['compose-actions'];
    $element['actions']['compose_add'] = [
      '#type' => 'submit',
      '#value' => new FormattableMarkup('Add @label', ['@label' => $labels['singular']]),
      '#name' => 'compose-' . $ief_id . '-add',
      '#limit_validation_errors' => [array_merge($parents, ['actions'])],
      '#attributes' => [
        'class' => ['compose-add-button'],
      ],
      '#element_validate' => [[$this, 'addNewEntityValidate']],
      '#ajax' => [
        'callback' => [$this, 'openDialogCallback'],
        'wrapper' => $dialog_content_selector,
      ],
      '#submit' => [[$this, 'addNewEntitySubmit']],
      '#ief_id' => $ief_id,
      '#ief_labels' => $labels,
    ];

    // Remove actions from parent
    foreach (['ief_add', 'bundle', 'ief_add_existing'] as $action) {
      if (isset($element['actions'][$action])) {
        unset($element['actions'][$action]);
      }
    }

    $ief_form_action = $form_state->get(['inline_entity_form', $this->getIefId(), 'form']);

    // Avoid ugly fieldset for ief add form
    if ($ief_form_action == 'add') {
      $element['form']['#type'] = 'container';
    }

    if ($ief_form_action == 'compose_add') {
      
      $element['form']['compose_add_options'] = $this->buildAddNewEntityForm($parents);
      $element['form']['compose_add_options']['type_filter'] = [
        '#type' => 'textfield',
        '#title' => 'Filter type list',
        '#weight' => -100
      ];
      if ($settings['allow_existing']) {
        $element['form']['tabs'] =[
          '#type' => 'html_tag',
          '#tag' => 'ul',
          '#attributes' => [
            'class' => ['compose-tabs'],
          ],
          '#weight' => -99,
          'new' => [
            '#type' => 'html_tag',
            '#tag' => 'li',
            'link' => [
              '#type' => 'html_tag',
              '#tag' => 'a',
              '#attributes' => [
                'href' => '#compose-widget-add-new-' . $ief_id,
              ],
              '#value' => $this->t('New'),
            ]
          ],
          'existing' => [
            '#type' => 'html_tag',
            '#tag' => 'li',
            'link' => [
              '#type' => 'html_tag',
              '#tag' => 'a',
              '#attributes' => [
                'href' => '#compose-widget-add-existing-' . $ief_id,
              ],
              '#value' => $this->t('Existing'),
            ]
          ],
        ];
        $element['form']['#attributes']['class'][] = 'compose-dialog-tabs';
        $element['form']['compose_add_existing'] = $this->buildAddExistingEntityForm($form_state, $parents);
      }
    }

    // Edit, Duplicate, Remove
    // $entities = $form_state->get(['inline_entity_form', $this->getIefId(), 'entities']);
    foreach (Element::children($element['entities']) as $key) {
      $set_attributes = FALSE;
      foreach (['edit', 'duplicate', 'remove'] as $op) {
        if (isset($element['entities'][$key]['actions']["ief_entity_$op"])) {
          $element['entities'][$key]['actions']["ief_entity_$op"]['#ajax']['callback'] = [$this, 'openEntityDialogCallback'];
          if (!$set_attributes) {
            $element['entities'][$key]['#attributes']['data-compose-row-delta'] = $element['entities'][$key]['actions']["ief_entity_$op"]['#ief_row_delta'];
            $element['entities'][$key]['#attributes']['data-compose-ief-id'] = $ief_id;
            $set_attributes = TRUE;
          }
        }
      }

      if (isset($element['entities'][$key]['form']['inline_entity_form']['#ief_row_delta'])) {
        $element['entities'][$key]['#attributes']['data-compose-row-delta'] = $element['entities'][$key]['form']['inline_entity_form']['#ief_row_delta'];
        $element['entities'][$key]['#attributes']['data-compose-ief-id'] = $ief_id;
      }

      $element['entities'][$key]['compose_dialog_container'] = [
        '#type' => 'html_tag',
        '#tag' => 'div',
        '#attributes' => [
          'id' => 'compose-dialog-container-' . $ief_id . '-' . $key,
          'class' => ['compose-dialog-container'],
        ],
        '#value' => '',
        '#weight' => 9999,
      ];
    }

    return $element;
  }

  /**
   * Validation callback for adding a new entity.
   *
   * @param array $form
   *   The full form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  public function addNewEntityValidate($form, FormStateInterface $form_state) {
    $target_type = $this->getFieldSetting('target_type');
    $bundle_info = $this->entityTypeBundleInfo->getBundleInfo($target_type);
    if (!$bundle_info || empty($bundle_info)){
      //bundled_entity_has_no_bundles
      $form_state->setError($form['actions']['compose_add'], 'Please add a bundle.');
    }
  }

  /**
   * Submit handler to open the compose add (new or existing) dialog.
   *
   * @param array $form
   *   The full form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  public function addNewEntitySubmit($form, FormStateInterface $form_state) {
    $trigger = $form_state->getTriggeringElement();
    $ief_id = $trigger['#ief_id'];
    $target_type = $this->getFieldSetting('target_type');
    $bundle_info = $this->entityTypeBundleInfo->getBundleInfo($target_type);
    $create_bundles = $this->getCreateBundles();
    $bundles = [];

    foreach ($bundle_info as $bundle_name => $bundle_label) {
      if (in_array($bundle_name, $create_bundles)) {
        $bundles[$bundle_name] = $bundle_label['label'];
      }
    }
    if (count($bundles) > 1) {
      //bundled_entity_has_multiple_bundles
      $form_state->set(['inline_entity_form', $ief_id, 'form'], 'compose_add');
    }
    else if (count($bundles) == 1) {
      //bundled_entity_has_one_bundle
      $form_state->set(['inline_entity_form', $ief_id, 'form'], 'add');
      $form_state->set(['inline_entity_form', $ief_id, 'form settings'], [
        'bundle' => array_keys($bundles),
      ]);
    }

    $form_state->setRebuild();
  }

  /**
   * Submit handler for most cancel actions.
   *
   * @param array $form
   *   The full form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  public static function composeCancelSubmit($form, FormStateInterface $form_state) {
    $trigger = $form_state->getTriggeringElement();
    $ief_id = $trigger['#ief_id'];
    $form_state->set(['inline_entity_form', $ief_id, 'form'], NULL);
    $form_state->setRebuild();
  }

  /**
   * Submit handler for selecting a bundle from the add new entity dialog.
   *
   * @param array $form
   *   The full form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  public static function addNewEntityRowSubmit($form, FormStateInterface $form_state) {
    $element = inline_entity_form_get_element($form, $form_state);
    $trigger = $form_state->getTriggeringElement();
    $ief_id = $element['#ief_id'];
    $form_state->set(['inline_entity_form', $ief_id, 'form'], 'add');
    $form_state->set(['inline_entity_form', $ief_id, 'form settings'], [
      'bundle' => $trigger['#compose_bundle'],
      'bundle_label' => $trigger['#compose_bundle_label'],
    ]);
    $form_state->setRebuild();
  }

  /**
   * Ajax callback for after a new entity is added.
   *
   * @param array $form
   *   The full form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @return \Drupal\Core\Ajax\AjaxResponse
   *   An ajax-renderable response.
   */
  public static function addNewEntityDialogCallback($form, FormStateInterface $form_state) {
    $element = inline_entity_form_get_element($form, $form_state);
    $ief_id = $element['#ief_id'];
    $container = '#compose-widget-dialog-container-' . $ief_id;
    $selector = '#compose-widget-dialog-content-' . $ief_id;
    $classes = [];
    $settings = $form_state->get(['inline_entity_form', $ief_id, 'form settings']);
    $title = t('Add new @bundle', ['@bundle' => $settings['bundle_label']]);
    $classes = ['compose-entity-dialog'];

    $response = new AjaxResponse();
    $response->addCommand(new CloseDialogCommand($container));
    $response->addCommand(new InsertCommand(NULL, $element));
    $response->addCommand(new ComposeOpenModalDialog($selector, $title, $container, NULL, $classes));

    return $response;
  }

  /**
   * #ajax callback to close the 'Add existing' form/modal dialog.
   *
   * @param array $form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @return \Drupal\Core\Ajax\AjaxResponse
   *   An ajax-renderable response.
   */
  public static function closeAddExistingDialogCallback(array $form, FormStateInterface $form_state) {
    $element = inline_entity_form_get_element($form, $form_state);
    $ief_id = $element['#ief_id'];
    $container = '#compose-widget-dialog-container-' . $ief_id;
    $selector = '#compose-widget-dialog-content-' . $ief_id;

    $response = new AjaxResponse();
    $response->addCommand(new CloseDialogCommand($container));
    $response->addCommand(new InsertCommand(NULL, $element));

    return $response;
  }

  /**
   * Opens the widget-level dialog.
   *
   * @param array $form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @returns \Drupal\Core\Ajax\AjaxResponse
   *   An ajax-renderable response.
   */
  public static function openDialogCallback(array $form, FormStateInterface $form_state) {
    $element = inline_entity_form_get_element($form, $form_state);
    $ief_id = $element['#ief_id'];
    $container = '#compose-widget-dialog-container-' . $ief_id;
    $selector = '#compose-widget-dialog-content-' . $ief_id;
    $append = '#inline-entity-form-' . $ief_id;
    list($title, $insert_element) = self::getRenderableForm($element, $ief_id, $form_state);

    $response = new AjaxResponse();
    $response->addCommand(new InsertCommand(NULL, $insert_element));
    $response->addCommand(new ComposeOpenModalDialog($selector, $title, $container, $append, ['compose-entity-dialog']));

    return $response;
  }

  /**
   * Opens an entity row for dialog.
   *
   * @param array $form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @returns \Drupal\Core\Ajax\AjaxResponse
   *   An ajax-renderable response.
   */
  public function openEntityDialogCallback($form, FormStateInterface $form_state) {
    $element = inline_entity_form_get_element($form, $form_state);
    $trigger = $form_state->getTriggeringElement();
    $ief_id = $element['#ief_id'];
    $ief_row_delta = $trigger['#ief_row_delta'];
    $append = sprintf('[data-compose-row-delta="%s"][data-compose-ief-id="%s"]', $ief_row_delta, $ief_id);
    $selector = $append . ' > .ief-form';
    $container = $append . ' > .compose-dialog-container';
    $label = $form_state->get(['inline_entity_form', $ief_id, 'entities'])[$ief_row_delta]['entity']->label();
    $classes = [];
    switch ($trigger['#ief_row_form']) {
      case 'edit':
        $title = $this->t('Edit %label', ['%label' => $label]);
        $classes = ['compose-entity-dialog'];
        break;

      case 'remove':
        $title = $this->t('Remove %label', ['%label' => $label]);
        break;
    }
    $insert_element = $element['entities'][$ief_row_delta]['form'];

    $response = new AjaxResponse();
    $response->addCommand(new AppendCommand($append, $insert_element));
    $response->addCommand(new ComposeOpenModalDialog($selector, $title, $container, $append, $classes));

    return $response;
  }

  /**
   * Gets the tile and renderable form from the IEF element based on the
   * current action.
   *
   * @TODO: probably needs to be refactored out of existence now.
   *
   * @param array $element
   *   The IEF element as a renderable array.
   * @param string $ief_id
   *   The IEF ID
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @return array
   *   An an array with two items: the title of the dialog and the renderable
   *   content.
   */
  public static function getRenderableForm($element, $ief_id, FormStateInterface $form_state) {
    $trigger = $form_state->getTriggeringElement();
    $entity_label = $trigger['#ief_labels']['singular'];

    switch ($form_state->get(['inline_entity_form', $ief_id, 'form'])) {
      case 'add':
        $title = 'Add New ' . $entity_label;
        break;
      case 'ief_add_existing':
        $title = 'Add Existing ' . $entity_label;
        break;
      case 'compose_add':
        $title = 'Add ' . $entity_label;
        break;
    }

    $insert_element = $element['form'];

    return [$title, $insert_element];
  }

  /**
   * Opens an entity row for dialog.
   *
   * @param array $form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  public static function closeDialogCallback($form, FormStateInterface $form_state) {
    $trigger = $form_state->getTriggeringElement();
    $element = inline_entity_form_get_element($form, $form_state);
    $ief_id = $element['#ief_id'];
    $selector = isset($trigger['#ief_row_delta']) ?
      sprintf('[data-compose-row-delta="%s"][data-compose-ief-id="%s"] > .compose-dialog-container', $trigger['#ief_row_delta'], $ief_id) :
      '#compose-widget-dialog-container-' . $ief_id;

    $response = new AjaxResponse();
    $response->addCommand(new CloseDialogCommand($selector, TRUE));
    $response->addCommand(new InsertCommand(NULL, $element));

    return $response;
  }

  /**
   * Callback to handle the save button on entity forms (widget or row).
   *
   * @param array $form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @returns \Drupal\Core\Ajax\AjaxResponse
   *   An ajax-renderable response.
   */
  public static function saveCallback($form, FormStateInterface $form_state) {
    $element = inline_entity_form_get_element($form, $form_state);
    $trigger = $form_state->getTriggeringElement();
    $ief_id = $element['#ief_id'];

    if ($form_state->getErrors()) {
      if (isset($trigger['#ief_row_delta'])) {
        $append = sprintf('[data-compose-row-delta="%s"][data-compose-ief-id="%s"]', $trigger['#ief_row_delta'], $ief_id);
        $selector = $append . ' > .ief-form';
        $container = $append . ' > .compose-dialog-container';
      }
      else {
        $selector = '#compose-widget-dialog-content-' . $ief_id;
        $container = '#compose-widget-dialog-container-' . $ief_id;
      }
      $title = "Edit " . $element['#ief_labels']['singular'];
      $messages = ['messages' => ['#type'=> 'status_messages']];

      # This is an ugly brute force alternative to refreshing the modal content of the dialog.
      $response = new AjaxResponse();
      $response->addCommand(new CloseDialogCommand($container));
      $response->addCommand(new InsertCommand('#inline-entity-form-' . $ief_id, $element));
      $response->addCommand(new PrependCommand($selector, \Drupal::service('renderer')->renderRoot($messages)));
      $response->addCommand(new ComposeOpenModalDialog($selector, $title, $container, $append, ['compose-entity-dialog']));
      return $response;
    }
    else {
      return self::closeDialogCallback($form, $form_state);
    }
  }

  /**
   * Callback to handle cancel.
   *
   * @param array $form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @returns \Drupal\Core\Ajax\AjaxResponse
   *   An ajax-renderable response.
   */
  public static function cancelCallback($form, FormStateInterface $form_state) {
    return self::closeDialogCallback($form, $form_state);
  }

  /**
   * {@inheritdoc}
   */
  public static function buildEntityFormActions($element) {
    $element = parent::buildEntityFormActions($element);
    $element['actions']['#type'] = 'actions';

    // Cancel
    $element['actions']['ief_' . $element['#op'] . '_cancel']['#ajax']['callback'] = [get_called_class(), 'cancelCallback'];
    $element['actions']['ief_' . $element['#op'] . '_cancel']['#ajax']['progress'] = 'fullscreen';
    $element['actions']['ief_' . $element['#op'] . '_cancel']['#attributes']['data-compose-cancel'] = '';
    // Save
    $element['actions']['ief_' . $element['#op'] . '_save']['#ajax']['callback'] = [get_called_class(), 'saveCallback'];
    $element['actions']['ief_' . $element['#op'] . '_save']['#ajax']['progress'] = 'fullscreen';

    return $element;
  }

  /**
   * {@inheritdoc}
   */
  protected function buildRemoveForm(&$form) {
    parent::buildRemoveForm($form);
    $form['actions']['#type'] = 'actions';

    // Cancel
    $form['actions']['ief_remove_cancel']['#ajax']['callback'] = [get_called_class(), 'cancelCallback'];
    $form['actions']['ief_remove_cancel']['#ajax']['progress'] = 'fullscreen';
    $form['actions']['ief_remove_cancel']['#attributes']['data-compose-cancel'] = '';
    // Remove
    $form['actions']['ief_remove_confirm']['#ajax']['callback'] = [get_called_class(), 'saveCallback'];
  }

  /**
   * Builds the add new entity screen.
   *
   * @param array $parents
   *   The form array parents for where this item will reside.
   *
   * @return array
   *   The add new entity form as a renderable array.
   */
  public function buildAddNewEntityForm($parents) {
    $ief_id = $this->getIefId();
    $labels = $this->getEntityTypeLabels();
    $form = [
      '#type' => 'container',
      '#attributes' => [
        'class' => ['add-new-container'],
        'id' => 'compose-widget-add-new-' . $ief_id
      ],
    ];

    // Get bundles.
    $bundles = [];
    $target_type = $this->getFieldSetting('target_type');
    $create_bundles = $this->getCreateBundles();
    foreach ($this->entityTypeBundleInfo->getBundleInfo($target_type) as $bundle_name => $bundle_info) {
      if (in_array($bundle_name, $create_bundles)) {
        $bundles[$bundle_name] = $bundle_info['label'];
      }
    }
    asort($bundles);

    // Loop through bundles and build Add New Entity rows
    foreach ($bundles as $bundle_id => $bundle_label) {
      $handler = \Drupal::entityTypeManager()->getHandler($target_type, 'compose_widget');
      $add = $handler->addNew($bundle_id);
      $button = [
        '#type' => 'submit',
        '#value' => 'Select',
        '#name' => 'compose-' . $ief_id . '-add-' . $bundle_id,
        '#limit_validation_errors' => [$parents],
        '#ajax' => [
          'callback' => [$this, 'addNewEntityDialogCallback'],
          'wrapper' => 'inline-entity-form-' . $ief_id,
          'progress' => 'fullscreen',
        ],
        '#submit' => [[$this, 'addNewEntityRowSubmit']],
        '#compose_bundle' => $bundle_id,
        '#compose_bundle_label' => $bundle_label,
        '#attributes' => [
          'class' => ['btn-primary'],
        ],
        '#ief_labels' => $labels,
      ];
      $handler->addNewSelectButton($add, $button);
      $form[] = $add;
    } // End "Add New" form

    // Cancel
    $form['actions'] = [
      '#type' => 'actions',
      '#weight' => 100,
    ];
    $form['actions']['cancel'] = [
      '#type' => 'submit',
      '#value' => 'Cancel',
      '#name' => 'compose-add-new-entity-cancel-' . $ief_id,
      '#limit_validation_errors' => [],
      '#ajax' => [
        'callback' => [$this, 'closeDialogCallback'],
        'wrapper' => 'inline-entity-form-' . $ief_id,
        'progress' => 'fullscreen',
      ],
      '#submit' => [[$this, 'composeCancelSubmit']],
      '#attributes' => [
        'data-compose-cancel' => '',
      ],
      '#ief_id' => $ief_id,
    ];

    return $form;
  }

  /**
   * Creates the content for the Add Existing Entity form.
   *
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state of the parent form.
   * @param array $parents
   *   The array parents of this IEF for convenience.
   *
   * @return array
   *   A form array for the add existing form.
   */
  public function buildAddExistingEntityForm($form_state, $parents) {
    $ief_id = $this->getIefId();
    $entity_type = $this->getFieldSetting('target_type');
    $handler = $this->entityTypeManager->getHandler($entity_type, 'compose_widget');
    $target_bundles = $this->getTargetBundles();
    $labels = $this->getEntityTypeLabels();
    $limit = 10;
    $form = [
      '#type' => 'container',
      '#attributes' => [
        'class' => ['add-existing-container'],
        'id' => 'compose-widget-add-existing-' . $ief_id
      ],
      "content" => [
        '#type' => 'container',
        // '#tree' => FALSE,
        '#attributes' => [
          'class' => ['add-existing-container-content'],
          'id' => 'compose-widget-add-existing-content-' . $ief_id
        ],
      ]
    ];

    // Add filter elements to the form.
    $filters = $handler->getAddExistingFilters($target_bundles);
    if ($filters) {
      $form['content']['filters'] = [
        '#type' => 'container',
        '#attributes' => [
          'class' => ['form--inline', 'form-inline', 'compose-filters'],
        ],
      ];
      foreach ($filters as $filter => $info) {
        $form['content']['filters'][$filter] = [
          '#type' => isset($info['options']) ? 'select' : 'textfield',
          '#title' => $info['label'],
        ];
        if (isset($info['options'])) {
          $form['content']['filters'][$filter]['#options'] = array_merge(['' => $this->t('- Any -')], $info['options']);
        }
      }

      $form['content']['filters']['actions'] = [
        '#type' => 'container',
        '#attributes' => [
          'class' => ['compose-filter-actions']
        ],
      ];
      $form['content']['filters']['actions']['filter_submit'] = [
        '#type' => 'submit',
        '#value' => $this->t('Apply'),
        '#limit_validation_errors' => [ array_merge($parents, ['compose_add_existing','content','filters']) ],
        '#submit' => [[$this, 'rebuildSubmit']],
        '#ajax' => [
          'callback' => [$this, 'addExistingContentCallback'],
          'wrapper' => 'compose-widget-add-existing-content-' . $ief_id,
          'progress' => 'fullscreen',
        ],
      ];

    }

    // Set the current page from form state
    \Drupal::request()->query->set('page', $form_state->getValue(array_merge($parents, ['compose_add_existing', 'content', 'page']), 0) . ',0');
    $query = \Drupal::entityQuery($entity_type);
    $query->condition('type', $target_bundles, 'IN');
    if ($existing_ids = array_filter(array_map(
      function ($entity) {
        return $entity['entity']->id();
      },
      $form_state->get(['inline_entity_form', $ief_id, 'entities'])
    ))) {
      $query->condition('id', $existing_ids, 'NOT IN');
    }

    // Add filters to the query.
    foreach ($filters as $filter => $info) {
      $value = $form_state->getValue(array_merge($parents, ['compose_add_existing', 'content', 'filters', $filter]));
      if ($value) {
        $query->condition($info['field'], $value, $info['operator']);
      }
    }

    $query = $handler->getAddExistingSort($query);
    $query->pager($limit, 0);
    $entity_ids = $query->execute();

    $results = $this->entityTypeManager
      ->getStorage($entity_type)
      ->loadMultiple($entity_ids);

    $header = [
      t('Title'),
      t('Type'),
    ];
    $types = \Drupal::moduleHandler()->invokeAll('compose_add_existing_row_data');
    foreach ($types as $type => $data) {
      if ($type == $entity_type) {
        foreach ($data as $value) {
          $header[] = $value['label'];
        }
      }
    }
    $header[] = '';


    $form['content']['entity_table'] = [
      '#type' => 'table',
      '#header' => $header,
      '#sticky' => FALSE,
      '#responsive' => TRUE,
      '#empty' => $this->t('There are no available @labels to add.', ['@labels' => $labels['plural']]),
    ];

    foreach ($results as $entity_id => $result) {
      $row = $handler->addExistingRow($result);
      $row['button'] = [
        '#type' => 'submit',
        '#value' => t('Select'),
        '#name' => 'ief-reference-add-existing-' . $entity_id,
        '#compose_parents' => $parents,
        '#limit_validation_errors' => [$parents],
        '#id' => 'entity-id-' . $entity_id,
        '#attributes' => [
          'class' => ['ief-entity-submit'],
          'data-entity-id' => [$entity_id],
        ],
        '#ief_entity_id' => $entity_id,
        '#ajax' => [
          'callback' => [$this, 'closeDialogCallback'],
          'wrapper' => 'inline-entity-form-' . $ief_id,
          'event' => 'click',
          'progress' => [
            'type' => 'throbber',
            'message' => NULL,
          ],
        ],
      ];

      InlineEntityFormComplex::addSubmitCallbacks($row['button']);
      $row['button']['#submit'][] = [$this, 'addExistingSubmit'];
      $form['content']['entity_table'][] = $row;
    }

    $form['content']['pager'] = [
      '#type' => 'pager',
      '#element' => 0,
      '#prefix' => '<div data-compose-pager data-compose-ief-id="' . $ief_id . '">',
      '#suffix' => '</div>',
    ];
    $form['content']['page'] = [
      '#type' => 'textfield',
      '#default_value' => 0,
      '#label' => 'Page',
      '#attributes' => [
        'data-compose-pager-update' => '',
        'data-compose-ief-id' => $ief_id,
        'class' => ['visually-hidden'],
      ],
    ];
    $form['content']['update_page'] = [
      '#type' => 'submit',
      '#value' => t('Change page'),
      '#submit' => [[$this, 'rebuildSubmit']],
      '#limit_validation_errors' => [array_merge($parents, ['compose_add_existing','content','update_page']), array_merge($parents, ['compose_add_existing','content','page']), array_merge($parents, ['compose_add_existing','content','filters'])],
      '#ajax' => [
        'callback' => [$this, 'addExistingContentCallback'],
        'wrapper' => 'compose-widget-add-existing-content-' . $ief_id,
        'progress' => 'fullscreen',
      ],
      '#attributes' => [
        'data-compose-pager-submit' => '',
        'data-compose-ief-id' => $ief_id,
        'class' => ['visually-hidden'],
      ],
    ];

    $form['#attached']['library'][] = 'core/jquery.form';
    $form['#attached']['library'][] = 'core/drupal.ajax';

    return $form;
  }


  /**
   * Validates the form for adding existing entities.
   *
   * @param array &$element
   *   The element being validated.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state of the parent form.
   */
  public static function addExistingValidate(&$form, FormStateInterface $form_state) {
    $trigger = $form_state->getTriggeringElement();
    $entity_id = $trigger['#ief_entity_id'];
    $form_state->setValue(array_merge($trigger['#compose_parents'], ['entity_id']), $entity_id);

  }

  /**
   * Button #submit callback: Add existing Compose buttons.
   *
   * @param $form
   *   The complete parent form.
   * @param $form_state
   *   The form state of the parent form.
   */
  public function addExistingSubmit($form, FormStateInterface $form_state) {
    $element = inline_entity_form_get_element($form, $form_state);
    $ief_id = $element['#ief_id'];
    $trigger = $form_state->getTriggeringElement();
    $entity_id = $trigger['#ief_entity_id'];
    $instance = $form_state->get(['inline_entity_form', $ief_id, 'instance']);
    $entity_type = $instance->getSetting('target_type');

    if ($entity_id !== NULL && $entity_type !== NULL) {
      $storage = \Drupal::entityTypeManager()->getStorage($entity_type);
      $entity = $storage->load($entity_id);
      $entities = &$form_state->get(['inline_entity_form', $ief_id, 'entities']);
      // Determine the correct weight of the new element.
      $weight = 0;
      if ($entities) {
        $weight = max(array_keys($entities)) + 1;
      }

      $entities[] = [
        'attributes' => [
          'data-compose-ief-id' => $ief_id,
        ],
        'entity' => $entity,
        'weight' => $weight,
        'form' => NULL,
        'needs_save' => FALSE,
      ];
    }
  }

  /**
   * AJAX Callback for the add existing Select buttons.
   *
   * @param $form
   *   The complete parent form.
   * @param $form_state
   *   The form state of the parent form.
   */
  public static function addExistingCallback(&$form, FormStateInterface $form_state) {
    $element = inline_entity_form_get_element($form, $form_state);
    $ief_id = $element['#ief_id'];
    $trigger = $form_state->getTriggeringElement();
    $entity_id = $trigger['#ief_entity_id'];
    $instance = $form_state->get(['inline_entity_form', $ief_id, 'instance']);
    $entity_type = $instance->getSetting('target_type');

    if ($entity_id !== NULL && $entity_type !== NULL) {
      $storage = \Drupal::entityTypeManager()->getStorage($entity_type);
      $entity = $storage->load($entity_id);
      $entities = $form_state->get(['inline_entity_form', $ief_id, 'entities']);
      // Determine the correct weight of the new element.
      $weight = 0;
      if ($entities) {
        $weight = max(array_keys($entities)) + 1;
      }

      $element['entities'][] = [
        '#entity' => $entity,
        '#label' => $entity->label(),
        '#weight' => $weight,
        'form' => NULL,
        'actions' => NULL,
        '#needs_save' => FALSE,
      ];

      $form['#entity'] = $entity;

      // Pass along the IEF form id, field name and entity type.
      $element['entities']['#form_id'] = $ief_id;
    }

    return $element;
  }

  /**
   * Submit handler that just sets the form to rebuild.
   *
   * @param $form
   *   The complete parent form.
   * @param $form_state
   *   The form state of the parent form.
   */
  public function rebuildSubmit($form, FormStateInterface $form_state) {
    $form_state->setRebuild();
  }

  /**
   * AJAX callback for returning the content of the add existing dialog after
   * a pager or filter submit.
   *
   * @param $form
   *   The complete parent form.
   * @param $form_state
   *   The form state of the parent form.
   */
  public function addExistingContentCallback(&$form, FormStateInterface $form_state) {
    $element = inline_entity_form_get_element($form, $form_state);
    return $element['form']['compose_add_existing']['content'];
  }
}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc