layout_builder_ipe-1.0.x-dev/src/LayoutBuilder/LayoutBuilderConfirmForm.php

src/LayoutBuilder/LayoutBuilderConfirmForm.php
<?php

namespace Drupal\layout_builder_ipe\LayoutBuilder;

use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\CloseDialogCommand;
use Drupal\Core\Ajax\OpenDialogCommand;
use Drupal\Core\Ajax\RedirectCommand;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Drupal\layout_builder\Form\DiscardLayoutChangesForm;
use Drupal\layout_builder\Form\RevertOverridesForm;
use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage;
use Drupal\layout_builder\SectionStorageInterface;
use Drupal\layout_builder_ipe\LayoutBuilderIpeService;
use Drupal\layout_builder_ipe\Traits\ConfirmDialogTrait;
use Drupal\layout_builder_ipe\Traits\RedirectUriTrait;
use Drupal\layout_builder_ipe\Traits\SectionStorageTrait;

/**
 * Service class for helping with altering the behavior of confirm forms.
 *
 * In the context of IPE, the default behavior of the confirmation forms for
 * "Discard changes" and "Revert to defaults" doesn't make much sense. Instead
 * of redirecting to the confirmation pages for those forms, where the cancel
 * action then redirects to the canonical URL with a deactivated IPE, we want
 * them to be displayed in a modal, that can simply be closed for canceling the
 * action, so that the users stays in context.
 */
class LayoutBuilderConfirmForm {

  use StringTranslationTrait;
  use SectionStorageTrait;
  use RedirectUriTrait;
  use ConfirmDialogTrait;

  /**
   * The entity decorator service.
   *
   * @var \Drupal\layout_builder_ipe\LayoutBuilderIpeService
   */
  protected $layoutBuilderIpe;

  /**
   * Creates an LayoutBuilderConfirmForm.
   *
   * @param \Drupal\layout_builder_ipe\LayoutBuilderIpeService $layout_builder_ipe
   *   The layout builder IPE service.
   */
  public function __construct(LayoutBuilderIpeService $layout_builder_ipe) {
    $this->layoutBuilderIpe = $layout_builder_ipe;
  }

  /**
   * Get the confirm form class that is responsible to build the actual form.
   *
   * @param string $action
   *   The action. This is basically the buttons element key in the form array.
   *
   * @return \Drupal\Core\Form\ConfirmFormInterface|null
   *   An instance of the confirm class.
   */
  private static function getConfirmFormClass($action) {
    switch ($action) {
      case 'discard_changes':
        return DiscardLayoutChangesForm::create(\Drupal::getContainer());

      case 'revert':
        return RevertOverridesForm::create(\Drupal::getContainer());
    }
  }

  /**
   * Add our own handler for the given button in the LB interface.
   *
   * @param array $form
   *   An associative array containing the structure of the form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   * @param string $button_key
   *   The key of the button in the layout builder form structure.
   */
  public function setConfirmButtonHandler(array &$form, FormStateInterface $form_state, $button_key) {
    if (!array_key_exists($button_key, $form['actions'])) {
      return;
    }
    $submit_handlers = $form['actions'][$button_key]['#submit'];
    if (count($submit_handlers) != 1 || $submit_handlers[0] != '::redirectOnSubmit') {
      return;
    }

    $section_storage = self::getSectionStorageFromFormState($form_state);
    if (!$section_storage instanceof OverridesSectionStorage && $section_storage->getPluginId() != 'page_manager') {
      return;
    }
    $entity = $this->layoutBuilderIpe->getEntityFromSectionStorage($section_storage);

    // Ok, this is the default submit handler that simply redirects to the
    // confirm form, so we are save to proceed.
    $form['actions'][$button_key]['#ajax'] = [
      'event' => 'click',
      'callback' => [static::class, 'handleConfirmableFormSubmit'],
      // Setting the route here is important, otherwise the form in the modal
      // inherits the main URL outside the modal, which in our case is the
      // layout_builder_ipe edit route. Confirming that will obviously fail, so
      // we set the URL manually here and also mark this request as coming from
      // AJAX.
      'url' => Url::fromRoute('layout_builder.' . $section_storage->getStorageType() . '.' . $entity->getEntityTypeId() . '.' . $button_key, [
        $entity->getEntityTypeId() => $entity->id(),
      ]),
      'options' => [
        'query' => array_filter([
          FormBuilderInterface::AJAX_FORM_REQUEST => TRUE,
          'destination' => $this->getRedirectUri($form_state),
        ]),
      ],
    ];
    $form['#attached']['library'][] = 'core/drupal.dialog.ajax';
  }

  /**
   * Handle a form submit that will lead to a confirmable form.
   *
   * @param array $form
   *   An associative array containing the structure of the form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   *
   * @return \Drupal\Core\Ajax\AjaxResponse
   *   An AJAX response object.
   */
  public static function handleConfirmableFormSubmit(array &$form, FormStateInterface $form_state) {
    $action = end($form_state->getTriggeringElement()['#parents']);
    $confirm_form_class = self::getConfirmFormClass($action);
    if (!$confirm_form_class) {
      return NULL;
    }
    $section_storage = self::getSectionStorageFromFormState($form_state);
    $confirm_form = \Drupal::formBuilder()->getForm($confirm_form_class, $section_storage);

    $ajax_response = new AjaxResponse();
    $ajax_response->addCommand(new OpenDialogCommand('#layout-builder-modal', $confirm_form_class->getQuestion(), $confirm_form, self::getDialogOptions()));
    return $ajax_response;
  }

  /**
   * Alter the confirmation form for the "Revert to defaults" button.
   *
   * @param array $form
   *   An associative array containing the structure of the form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   * @param string $form_id
   *   The id of the form.
   */
  public function alterConfirmationForm(array &$form, FormStateInterface $form_state, $form_id) {
    $section_storage = self::getSectionStorageFromFormState($form_state);
    if (!$section_storage instanceof OverridesSectionStorage && !$section_storage->getPluginId() == 'page_manager') {
      return;
    }

    $entity = $this->layoutBuilderIpe->getEntityFromSectionStorage($section_storage);
    if (!$this->layoutBuilderIpe->ipeEnabled($entity)) {
      return;
    }

    // Check this is one of the confirmation forms that we want to handle.
    $confirm_forms = [
      'layout_builder_discard_changes' => [
        'callback' => 'discardChangesConfirm',
        'route_fragment' => 'discard_changes',
      ],
      'layout_builder_revert_overrides' => [
        'callback' => 'revertConfirm',
        'route_fragment' => 'revert',
      ],
    ];
    $callback = $confirm_forms[$form_id]['callback'] ?? NULL;
    $route_fragment = $confirm_forms[$form_id]['route_fragment'] ?? NULL;
    if (!$callback || !$route_fragment) {
      // Something is fishy, better bail out.
      return;
    }

    $entity = $section_storage->getContextValue('entity');
    $section_storage_identifier = $section_storage->getPluginId() . ($section_storage->getPluginId() != 'page_manager' ? '.' . $entity->getEntityTypeId() : '');

    $redirect_url = $this->getRedirectUri($form_state);
    if (!$redirect_url && $section_storage->getPluginId() == 'page_manager') {
      $redirect_url = $this->layoutBuilderIpe->getCurrentEditPath();
    }
    $form['#submit'] = [[static::class, $callback]];
    $form['actions']['submit']['#ajax'] = [
      'event' => 'click',
      'callback' => [static::class, $callback],
      // Setting the route here is important, otherwise the form in the modal
      // inherits the main URL outside the modal, which in our case is the
      // layout_builder_ipe edit route. Confirming that will obviously fail, so
      // we set the URL manually here and also mark this request as coming from
      // AJAX.
      'url' => Url::fromRoute('layout_builder.' . $section_storage_identifier . '.' . $route_fragment, [
        $entity->getEntityTypeId() => $entity->id(),
      ]),
      'options' => [
        'query' => array_filter([
          FormBuilderInterface::AJAX_FORM_REQUEST => TRUE,
          'destination' => $redirect_url,
        ]),
      ],
    ];
    $form['actions']['cancel']['#attributes']['class'][] = 'dialog-cancel';
  }

  /**
   * Create a redirect AJAX response for the given section storage.
   *
   * @param \Drupal\layout_builder\SectionStorageInterface $section_storage
   *   The section storage to get the redirect URL.
   * @param string $redirect_uri
   *   An optional redirect uri.
   *
   * @return \Drupal\Core\Ajax\AjaxResponse
   *   An AJAX response object.
   */
  private static function redirectAjaxResponse(SectionStorageInterface $section_storage, $redirect_uri = NULL) {
    $ajax_response = new AjaxResponse();
    $ajax_response->addCommand(new CloseDialogCommand('#layout-builder-modal'));
    $ajax_response->addCommand(new RedirectCommand($redirect_uri ?? $section_storage->getRedirectUrl()->toString()));
    return $ajax_response;
  }

  /**
   * Handle the confirm action of the discard changes confirmation form.
   *
   * @param array $form
   *   An associative array containing the structure of the form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   *
   * @return \Drupal\Core\Ajax\AjaxResponse
   *   An AJAX response object.
   */
  public static function discardChangesConfirm(array $form, FormStateInterface $form_state) {
    $section_storage = self::getSectionStorageFromFormState($form_state);
    /** @var \Drupal\layout_builder\LayoutTempstoreRepositoryInterface $layout_tempstore_repository */
    $layout_tempstore_repository = \Drupal::service('layout_builder.tempstore_repository');
    $layout_tempstore_repository->delete($section_storage);

    \Drupal::messenger()->addMessage(t('The changes to the layout have been discarded.'));
    return self::redirectAjaxResponse($section_storage, self::getRedirectUri($form_state));
  }

  /**
   * Handle the confirm action of the revert confirmation form.
   *
   * @param array $form
   *   An associative array containing the structure of the form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   *
   * @return \Drupal\Core\Ajax\AjaxResponse
   *   An AJAX response object.
   */
  public static function revertConfirm(array $form, FormStateInterface $form_state) {
    $section_storage = self::getSectionStorageFromFormState($form_state);
    $section_storage->removeAllSections()->save();
    /** @var \Drupal\layout_builder\LayoutTempstoreRepositoryInterface $layout_tempstore_repository */
    $layout_tempstore_repository = \Drupal::service('layout_builder.tempstore_repository');
    $layout_tempstore_repository->delete($section_storage);

    \Drupal::messenger()->addMessage(t('The layout has been reverted back to defaults.'));
    return self::redirectAjaxResponse($section_storage);
  }

}

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

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