panopoly_magic-8.x-2.x-dev/src/Form/LayoutBuilderBlockFormTrait.php

src/Form/LayoutBuilderBlockFormTrait.php
<?php

namespace Drupal\panopoly_magic\Form;

use Drupal\block_content\Access\RefinableDependentAccessInterface;
use Drupal\block_content\Plugin\Block\BlockContentBlock;
use Drupal\Component\Plugin\Exception\ContextException;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\SubformState;
use Drupal\Core\Plugin\ContextAwarePluginInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Render\PreviewFallbackInterface;
use Drupal\layout_builder\Access\LayoutPreviewAccessAllowed;
use Drupal\panopoly_magic\Alterations\ReusableBlocks;
use Drupal\panopoly_magic\Event\PanopolyMagicLivePreviewEvent;
use Drupal\panopoly_magic\PanopolyMagicEvents;
use Drupal\views\Plugin\Block\ViewsBlock;

/**
 * Trait with methods for the add and update forms in layout builder.
 */
trait LayoutBuilderBlockFormTrait {

  /**
   * Gets the configured live preview mode.
   *
   * @return string
   *   The live preview mode: automatic, manual or disabled.
   */
  protected function getLivePreviewMode() {
    return \Drupal::config('panopoly_magic.settings')->get('live_preview');
  }

  /**
   * Gets the event dispatcher.
   *
   * @return \Symfony\Component\EventDispatcher\EventDispatcher
   *   The event dispatcher.
   */
  protected function getEventDispatcher() {
    return \Drupal::service('event_dispatcher');
  }

  /**
   * Gets the plugin definition for the block we are configuring.
   *
   * This will get any layout_builder-specific customizations included.
   *
   * @return array
   *   The plugin definition.
   */
  protected function getBlockPluginDefinition() {
    // First, get the definition as filtered for layout builder, so we can get
    // any customizations.
    $plugin_id = $this->block->getPluginId();
    $definitions = $this->blockManager->getFilteredDefinitions('layout_builder', [], ['list' => 'inline_blocks']);
    if (isset($definitions[$plugin_id])) {
      return $definitions[$plugin_id];
    }

    // But, if it doesn't exist (because it was hidden, possibly via Panopoly
    // Admin), then use the upstream definition.
    return $this->block->getPluginDefinition();
  }

  /**
   * Alters the form to add the preview elements.
   *
   * @param array $form
   *   The form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  protected function alterFormForPreview(array &$form, FormStateInterface $form_state) {
    if ($this->getLivePreviewMode() === 'disabled') {
      return;
    }

    // Put the action buttons in the bottom of the dialog.
    $form['actions']['#type'] = 'container';
    $form['actions']['#attributes']['class'][] = 'form-actions';

    // Add the preview button.
    $form['actions']['preview'] = [
      '#type' => 'button',
      '#value' => $this->t('Preview'),
      '#attributes' => [
        'class' => [
          'panopoly-magic-live-preview',
        ],
      ],
      '#ajax' => [
        'callback' => '::ajaxSubmit',
        'disable-refocus' => TRUE,
      ],
    ];

    // Add a special class to all the buttons so ONLY they'll get moved to the
    // bottom of the dialog.
    foreach (Element::children($form['actions']) as $name) {
      $form['actions'][$name]['#attributes']['class'][] = 'js-panopoly-magic-live-preview-button';
    }
  }

  /**
   * Suppresses form validate for preview.
   *
   * @param array $form
   *   The form.
   * @param Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  protected function suppressValidationForPreview(array &$form, FormStateInterface $form_state) {
    // Suppress form validation errors when using automatic preview, allowing
    // partial previews and removing error messages when a block has multiple
    // required fields.
    $submit_button_name = end($form_state->getTriggeringElement()['#parents']);
    if ($submit_button_name == 'preview' && $this->getLivePreviewMode() === "automatic") {
      // Suppress all future validation errors from parent::validateForm().
      $form_state->setLimitValidationErrors([]);
      // Capture any errors so we can show them to the user.
      $form_state->setTemporaryValue('panopoly_magic_preview_errors', $form_state->getErrors());
      // Clear any existing validation errors from the Field API.
      $form_state->clearErrors();
      // Prevent caching the form from preview.
      $form_state->disableCache();
    }
  }

  /**
   * Creates AJAX responses to rebuild the preview.
   *
   * @param array $form
   *   The form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @return \Drupal\Core\Ajax\AjaxResponse
   *   An AJAX response to rebuild the preview.
   */
  protected function rebuildPreview(array &$form, FormStateInterface $form_state) {
    $response = new AjaxResponse();

    // If there's form validation errors, show them instead of the preview.
    if ($form_state->hasTemporaryValue('panopoly_magic_preview_errors')) {
      $errors = $form_state->getTemporaryValue('panopoly_magic_preview_errors');
      if (!empty($errors)) {
        $response->addCommand(new ReplaceCommand('#panopoly-magic-preview', $this->buildPreviewError($errors)));
        return $response;
      }
    }

    $subform_state = SubformState::createForSubform($form['settings'], $form, $form_state);
    $subform_obj = $this->getPluginForm($this->block);

    // Call the plugin validation handler (includes entity validation when
    // placing content blocks).
    $subform_state->setValidationComplete(FALSE);
    $subform_obj->validateConfigurationForm($form['settings'], $subform_state);
    $subform_state->setValidationComplete(TRUE);

    // If there's plugin validation errors, show them instead of the preview.
    if ($subform_state->hasAnyErrors()) {
      $response->addCommand(new ReplaceCommand('#panopoly-magic-preview', $this->buildPreviewError($subform_state->getErrors())));
      return $response;
    }

    // Call the plugin submit handler.
    if ($subform_state->hasTemporaryValue('block_form_parents')) {
      // Adjust a temporary value set by the block form so it can work correctly
      // as a sub-form.
      $block_form_parents = $subform_state->getTemporaryValue('block_form_parents');
      if ($block_form_parents[0] === 'settings') {
        array_shift($block_form_parents);
      }
      $subform_state->setTemporaryValue('block_form_parents', $block_form_parents);
    }
    $subform_obj->submitConfigurationForm($form['settings'], $subform_state);

    // If this block is context-aware, set the context mapping.
    if ($this->block instanceof ContextAwarePluginInterface) {
      $this->block->setContextMapping($subform_state->getValue('context_mapping', []));
    }

    // If this is a content block, then we need to also validate and submit the
    // content entity's form (but not save it).
    if ($this->block instanceof BlockContentBlock && !empty($form['block_form'])) {
      ReusableBlocks::blockContentValidate($form, $form_state);
      ReusableBlocks::blockContentSubmit($form, $form_state, FALSE);
    }

    $response->addCommand(new ReplaceCommand('#panopoly-magic-preview', $this->buildPreview($form_state->getValues())));
    return $response;
  }

  /**
   * Builds the preview render array for the current block.
   */
  public function buildPreview(array $form_values = []) {
    if ($this->block instanceof BlockContentBlock) {
      // For content blocks, we need to reuse the same instance of the block
      // because it'll have updated content entity on it.
      $block = $this->block;
    }
    else {
      // Create a fresh instance of all other blocks, because there may be some
      // lingering state on the old block instance due to creating and
      // submitting the form.
      $block = $this->blockManager->createInstance($this->block->getPluginId(), $this->block->getConfiguration());
    }

    if ($block instanceof RefinableDependentAccessInterface) {
      $block->setAccessDependency(new LayoutPreviewAccessAllowed());
    }

    if ($block instanceof ViewsBlock) {
      $block->getViewExecutable()->setShowAdminLinks(FALSE);
    }

    if ($block instanceof ContextAwarePluginInterface) {
      $storage_contexts = $this->sectionStorage->getContexts();
      foreach ($storage_contexts as $context_name => $context) {
        $block->setContext($context_name, $context);
      }
    }

    try {
      $content = $block->build();
      unset($content['#contextual_links']);
    }
    catch (\Exception $e) {
      $content = [];
    }

    $cache = new CacheableMetadata();
    $cache->addCacheableDependency(AccessResult::allowed()->setCacheMaxAge(0));
    $cache->addCacheableDependency(CacheableMetadata::createFromRenderArray($content));

    if (Element::isEmpty($content)) {
      $fallback_string = '';
      if ($block instanceof PreviewFallbackInterface) {
        try {
          $fallback_string = $block->getPreviewFallbackString();
        }
        catch (ContextException $e) {
          // Leave empty. We'll assign a default below.
        }
      }
      if (!$fallback_string) {
        $fallback_string = $this->t("Preview unavailable.");
      }
      $content = [
        '#markup' => $fallback_string,
      ];
    }
    else {
      $content = [
        '#theme' => 'block',
        '#attributes' => [],
        '#configuration' => $block->getConfiguration(),
        '#plugin_id' => $block->getPluginId(),
        '#base_plugin_id' => $block->getBaseId(),
        '#derivative_plugin_id' => $block->getDerivativeId(),
        'content' => $content,
      ];
    }

    // Dispatch event so other modules can modify the preview.
    $event = new PanopolyMagicLivePreviewEvent($block->getPluginId(), $block->getConfiguration(), $content, $this->getCurrentComponent(), $form_values);
    /** @var \Symfony\Component\EventDispatcher\EventDispatcher $event_dispatcher */
    $event_dispatcher = $this->getEventDispatcher();
    $event_dispatcher->dispatch($event, PanopolyMagicEvents::LIVE_PREVIEW_EVENT);

    $preview = [
      '#theme' => 'panopoly_magic_preview',
      '#title' => $this->t("Preview"),
      '#attributes' => [
        'id' => 'panopoly-magic-preview',
      ],
      '#weight' => -100,
      'preview' => $event->getPreview(),
    ];

    if ($this->getLivePreviewMode() === 'automatic') {
      $preview['#attached']['library'][] = 'panopoly_magic/preview.live.automatic';
    }
    else {
      $preview['#attached']['library'][] = 'panopoly_magic/preview.live.manual';
    }

    $cache->applyTo($preview);

    return $preview;
  }

  /**
   * Builds preview render array with error messages.
   *
   * @param string[] $errors
   *   Array of error messages to show.
   */
  protected function buildPreviewError(array $errors) {
    $preview = [
      '#theme' => 'panopoly_magic_preview',
      '#title' => $this->t("Preview"),
      '#attributes' => [
        'id' => 'panopoly-magic-preview',
      ],
      '#cache' => [
        'max-age' => 0,
      ],
      'preview' => [
        '#theme' => 'status_messages',
        '#message_list' => [
          'error' => $errors,
        ],
      ],
    ];
    return $preview;
  }

}

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

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