component_library-1.0.x-dev/src/Form/ComponentOverrideForm.php

src/Form/ComponentOverrideForm.php
<?php

declare(strict_types=1);

namespace Drupal\component_library\Form;

use Drupal\component_library\ComponentOverrideManager;
use Drupal\component_library\Entity\ComponentOverride;
use Drupal\component_library\Event\PrepareOverrideEvent;
use Drupal\component_library\OverrideMode;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\HtmlCommand;
use Drupal\Core\Ajax\PrependCommand;
use Drupal\Core\Ajax\RedirectCommand;
use Drupal\Core\Ajax\RemoveCommand;
use Drupal\Core\Asset\LibraryDiscoveryInterface;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Extension\ExtensionList;
use Drupal\Core\Extension\ModuleExtensionList;
use Drupal\Core\Extension\ProfileExtensionList;
use Drupal\Core\Extension\ThemeExtensionList;
use Drupal\Core\Extension\ThemeHandlerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\SubformState;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Template\TwigEnvironment;
use Drupal\Core\TempStore\PrivateTempStore;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Twig\Error\Error;

/**
 * Component override form.
 */
final class ComponentOverrideForm extends EntityForm {

  use ComponentOverrideFormTrait;

  /**
   * Cached ID for libraries gathered from all available/installed extensions.
   */
  public const COLLECTED_LIBRARIES_CID = 'component_library:component_override_form:collected_libraries';

  /**
   * Theme.
   *
   * @var string
   *   The theme this Component Override applies to.
   */
  protected string $theme;
  protected TwigEnvironment $twig;
  protected PrivateTempStore $cache;
  protected OverrideMode $overrideMode;
  protected RendererInterface $renderer;
  protected ThemeHandlerInterface $themeHandler;
  protected CacheBackendInterface $cacheDefault;
  protected ExtensionList $profileExtensionList;
  protected EventDispatcherInterface $dispatcher;
  protected ThemeExtensionList $themeExtensionList;
  private PrepareOverrideEvent $prepareOverrideEvent;
  protected ModuleExtensionList $moduleExtensionList;
  protected ComponentOverrideManager $overrideManager;
  protected LibraryDiscoveryInterface $libraryDiscovery;

  public function __construct(ThemeHandlerInterface $theme_handler, CacheBackendInterface $cache_default, ComponentOverrideManager $override_manager, LibraryDiscoveryInterface $library_discovery, ModuleExtensionList $module_list, ProfileExtensionList $profile_list, ThemeExtensionList $theme_list, TwigEnvironment $twig, EventDispatcherInterface $event_dispatcher, RendererInterface $renderer, PrivateTempStoreFactory $temp_store, OverrideMode $override_mode, RequestStack $request_stack) {
    $this->twig = $twig;
    $this->renderer = $renderer;
    $this->overrideMode = $override_mode;
    $this->themeHandler = $theme_handler;
    $this->cacheDefault = $cache_default;
    $this->dispatcher = $event_dispatcher;
    $this->themeExtensionList = $theme_list;
    $this->moduleExtensionList = $module_list;
    $this->overrideManager = $override_manager;
    $this->libraryDiscovery = $library_discovery;
    $this->profileExtensionList = $profile_list;
    $this->cache = $temp_store->get('component_overrides');
    $this->requestStack = $request_stack;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container): self {
    return new self(
      $container->get('theme_handler'),
      $container->get('cache.default'),
      $container->get('plugin.manager.component_override'),
      $container->get('library.discovery'),
      $container->get('extension.list.module'),
      $container->get('extension.list.profile'),
      $container->get('extension.list.theme'),
      $container->get('twig'),
      $container->get('event_dispatcher'),
      $container->get('renderer'),
      $container->get('tempstore.private'),
      $container->get('component_library.override_mode'),
      $container->get('request_stack')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state, $theme = '') {
    $form_state->set('workspace_safe', TRUE);
    // Ensure $theme is a valid theme.
    if ($theme) {
      if (!$this->themeHandler->hasUi($theme)) {
        throw new NotFoundHttpException();
      }
      $this->entity->set('theme', $theme);
      $this->theme = $theme;
    }
    elseif ($this->getFormId() === 'component_override_edit_form') {
      $this->theme = $this->getEntity()->get('theme');
    }
    $this->prepareOverrideEvent = $this->dispatcher->dispatch(new PrepareOverrideEvent());

    return parent::buildForm($form, $form_state);
  }

  /**
   * {@inheritdoc}
   */
  public function form(array $form, FormStateInterface $form_state, $theme = NULL): array {
    $form = parent::form($form, $form_state);

    $form['configuration_container'] = [
      '#type' => 'container',
    ];
    $form['label'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Label'),
      '#maxlength' => 255,
      '#default_value' => $this->entity->label(),
      '#required' => TRUE,
      '#group' => 'configuration_container',
    ];

    $form['id'] = [
      '#type' => 'machine_name',
      '#default_value' => $this->entity->id(),
      '#machine_name' => [
        'exists' => \sprintf('%s::load', ComponentOverride::class),
      ],
      '#disabled' => !$this->entity->isNew(),
      '#group' => 'configuration_container',
    ];

    $plugin_options = $this->overrideManager->getPluginOptions();
    $plugin = $this->getPlugin($form_state);
    if ($plugin === NULL) {
      unset($plugin_options['override_mode']);
    }
    $form['plugin_container'] = [
      '#type' => 'container',
      '#prefix' => '<div id="override-plugin-container">',
      '#suffix' => '</div>',
      '#group' => 'configuration_container',
      '#parents' => ['plugin_container'],
      '#tree' => TRUE,
      '#attributes' => ['class' => ['flex']],
      'plugin' => [
        '#title' => $this->t('Plugin'),
        '#type' => 'select',
        '#empty_option' => $this->t('- Select -'),
        '#empty_value' => '',
        '#options' => $plugin_options,
        '#required' => TRUE,
        '#default_value' => $plugin,
        '#disabled' => !$this->getEntity()->isNew(),
        '#ajax' => [
          'callback' => [$this, 'loadPluginForm'],
          'wrapper' => 'override-plugin-container',
          'effect' => 'fade',
        ],
      ],
    ];
    if (!$form_state->getValue('plugin_container')) {
      // Give plugins a place to stash their values on initial load.
      $form_state->setValue('plugin_container', []);
    }

    /** @var \Drupal\component_library\Plugin\ComponentOverride\ComponentOverrideInterface $override_plugin */
    $override_plugin = NULL;
    if ($plugin) {
      $override_plugin = $this->entity->getPlugin($this->prepareOverrideEvent);
      $form['#parents'] = [];
      $subform_state = SubformState::createForSubform($form['plugin_container'], $form, $form_state);
      $form['plugin_container'] += $override_plugin->buildConfigurationForm([], $subform_state);

      // Populate the template when an override is initially selected.
      if (\array_key_exists('override', $form['plugin_container'])) {
        $form['plugin_container']['override']['#ajax'] = [
          'callback' => [$this, 'loadDefaultTemplate'],
          'wrapper' => 'dynamic-elements-container',
          'effect' => 'fade',
        ];
      }
    }
    $override = $this->getOverride($form_state);
    $template = $form_state->getValue('template') ?? $this->entity->get('template') ?? '';
    $trigger = $form_state->getTriggeringElement();
    $input = $form_state->getUserInput();
    if (
      // The override field was selected.
      (\is_array($trigger) && \array_key_exists('#name', $trigger) && $trigger['#name'] === 'plugin_container[override]') ||
      // Override Mode passed in the override.
      ((empty($input['template']) && $this->prepareOverrideEvent->isPrepopulated())
        && $override)
    ) {
      $template = $this->getTemplate($override, TRUE);
      $input['template'] = $template;
      $form_state->setUserInput($input);
    }

    $form['dynamic_elements_container'] = [
      '#type' => 'container',
      '#tree' => FALSE,
      '#prefix' => '<div id="dynamic-elements-container">',
      '#suffix' => '</div>',
    ];

    $form['dynamic_elements_container']['template'] = [
      '#type' => 'codemirror',
      '#codemirror' => [
        'autoCloseTags' => TRUE,
        'buttons' => [
          'undo',
          'redo',
          'enlarge',
          'shrink',
        ],
        'foldGutter' => TRUE,
        'lineNumbers' => TRUE,
        'lineWrapping' => TRUE,
        'styleActiveLine' => TRUE,
        'toolbar' => TRUE,
        'lineSeparator' => "\n",
        'mode' => 'html_twig',
        'viewportMargin' => 'Infinity',
      ],
      '#title' => $this->t('Template'),
      '#default_value' => $template,
      '#element_validate' => [[$this, 'twigValidate']],
      '#suffix' => '<output id="override-preview"></output>',
    ];

    if ($override && empty($form_state->get('selected_libraries')[$override])) {
      $selected_libraries = [];
      $selected_libraries[$override] = $form_state->getValue('selected_libraries') ?? $this->entity->get('libraries') ?? [];

      $selected_libraries[$override] += $this->parseAttachedLibraries(
        $this->getTemplate($override, FALSE)
      );
      $form_state->set('selected_libraries', $selected_libraries);
    }
    $attached_libraries = NULL;
    if ($override) {
      $attached_libraries = $form_state->get('selected_libraries')[$override];
      $available_libraries = $this->getAvailableLibraries();
      $form['dynamic_elements_container']['libraries_wrapper'] = [
        '#type' => 'details',
        '#prefix' => '<div id="libraries-wrapper">',
        '#suffix' => '</div>',
        '#open' => FALSE,
        '#title' => $this->t('Libraries'),
        'libraries_selector' => [
          '#type' => 'select',
          '#title' => $this->t('Add library'),
          '#empty_option' => $this->t('- Select -'),
          '#options' => $available_libraries,
          '#ajax' => [
            'callback' => [$this, 'ajaxUpdateLibraries'],
            'wrapper' => 'libraries-wrapper',
            'effect' => 'fade',
          ],
        ],
        'libraries_list' => [
          '#prefix' => '<h6>' . $this->t('Attached libraries') . '</h6>',
          '#type' => 'table',
          '#header' => [$this->t('Library'), $this->t('Remove')],
          '#empty' => $this->t('No libraries are attached.'),
        ],
        'libraries' => [
          '#type' => 'value',
          '#value' => $attached_libraries ?? [],
        ],
      ];
      if ($attached_libraries) {
        $this->buildLibrariesList($attached_libraries, $form);
      }
    }

    $form['#prefix'] = '<div id="status-messages"></div>';
    $form['#attached']['library'][] = 'component_library/component_override';
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state): void {
    // Validate attached libraries.
    $override = $form_state->getValue('override') ?? $this->entity->get('override');
    $libraries = $form_state->get('selected_libraries');
    $trigger = $form_state->getTriggeringElement();
    if ((isset($trigger['#name']) && $trigger['#name'] === 'override') && $override) {
      $theme_registry = $this->overrideManager->getThemeRegistry()[$form_state->getValue('override')];
      $template_path = \sprintf('%s/%s.html.twig', $theme_registry['path'], $theme_registry['template']);
      $template = \file_get_contents($template_path);
      $libraries[$override] ??= $this->parseAttachedLibraries($template);
    }

    if ((isset($trigger['#name']) && $trigger['#name'] === 'libraries_selector')) {
      $added_library = $form_state->getValue('libraries_selector');
      if (!empty($added_library) && !\in_array($added_library, $libraries, TRUE)) {
        $libraries[$override][] = $added_library;
      }
    }

    if (isset($trigger['#parents'][1]) && isset($trigger['#name']) && \strpos($trigger['#name'], 'library_remove') !== FALSE) {
      \array_splice($libraries[$override], $trigger['#parents'][1], 1);
    }
    $form_state->set('selected_libraries', $libraries);

    // Validate the plugin.
    $plugin_values = $form_state->getValue('plugin_container');
    if (!empty($plugin_values['plugin'])) {
      $override = $this->entity->getPlugin();
      $form_state->setTemporaryValue('override_plugin', $override);
      $subform_state = SubformState::createForSubform($form['plugin_container'], $form, $form_state);
      $override->validateConfigurationForm($form, $subform_state);
    }
  }

  /**
   * Use element validator to make sure that Twig is valid.
   */
  public function twigValidate(array $element, FormStateInterface $form_state): void {
    $template = $form_state->getValue($element['#parents']);
    $context = $this->getVariables();

    $markup = [
      '#type' => 'inline_template',
      '#template' => $template,
      '#context' => $context,
    ];
    try {
      $this->renderer->render($markup);
    }
    catch (Error $exception) {
      $form_state->setError($element, new TranslatableMarkup('Twig error: %error', ['%error' => $exception->getMessage()]));
    }
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state): void {
    if ($override = $form_state->getTemporaryValue('override_plugin')) {
      $subform_state = SubformState::createForSubform($form['plugin_container'], $form, $form_state);
      $override->submitConfigurationForm($form, $subform_state);
    }
    parent::submitForm($form, $form_state);
  }

  /**
   * {@inheritdoc}
   */
  public function save(array $form, FormStateInterface $form_state): int {
    $result = parent::save($form, $form_state);
    $message_args = [
      '@url' => Url::fromRoute(
        'system.theme_settings_theme',
        ['theme' => $this->entity->get('theme')],
      )->toString(),
      '%label' => $this->entity->label(),
    ];
    $message = $result == \SAVED_NEW
      ? $this->t('Created new %label <a href="@url">component override</a>.', $message_args)
      : $this->t('Updated %label <a href="@url">component override</a>.', $message_args);
    $this->messenger()->addStatus($message);

    $this->cache->delete('variables');

    return $result;
  }

  /**
   * {@inheritdoc}
   */
  protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state): void {
    $value = \implode("\n", \preg_split('(\r\n|\r)', $form_state->getValue('template')));
    $form_state->setValue('template', $value);

    $plugin_values = $form_state->getValue('plugin_container');
    if (isset($plugin_values['plugin'])) {
      $entity->set('plugin', $plugin_values['plugin']);
    }
    if (isset($plugin_values['override'])) {
      $entity->set('override', $plugin_values['override']);
    }
    $variables = $entity->getVariables();
    if ($variables === NULL) {
      $variables = $this->cache->get('variables');
      $entity->set('variables', \serialize($variables));
    }

    parent::copyFormValuesToEntity($entity, $form, $form_state);
  }

  /**
   * {@inheritdoc}
   */
  protected function actions(array $form, FormStateInterface $form_state) {
    $actions = parent::actions($form, $form_state);

    $actions['submit']['#ajax'] = [
      'callback' => [$this, 'ajaxSubmit'],
      'event' => 'click',
    ];

    return $actions;
  }

  /**
   * AJAX callback to load the plugin form.
   */
  public function loadPluginForm(array $form, FormStateInterface $form_state): array {
    return $form['plugin_container'];
  }

  /**
   * AJAX callback to load the plugin form.
   */
  public function loadDefaultTemplate(array $form, FormStateInterface $form_state): array {
    return $form['dynamic_elements_container'];
  }

  /**
   * AJAX callback to submit the override form.
   */
  public function ajaxSubmit(array $form, FormStateInterface $form_state): AjaxResponse {
    if ($error_messages = $this->checkForErrorMessages()) {
      return $error_messages;
    }

    $response = new AjaxResponse();
    $url = $this->getRedirectDestination()->get();
    $query = $this->getRequest()->query->all();
    if ($query && !array_key_exists('destination', $query)) {
      $url = Url::fromRoute(
        'system.theme_settings_theme',
        ['theme' => $this->entity->get('theme')],
      )->toString();
    }
    $response->addCommand(new RedirectCommand($url));
    return $response;
  }

  /**
   * Ajax callback to preview the template.
   */
  public function previewCallback(array $form, FormStateInterface $form_state): AjaxResponse {
    if ($error_messages = $this->checkForErrorMessages()) {
      return $error_messages;
    }
    $markup = [
      '#type' => 'container',
      '#attributes' => ['id' => 'override-preview-context'],
      'label' => [
        '#markup' => $this->t('<h2>Preview @label</h2>', [
          '@label' => $form_state->getValue('plugin_container')['override'] ?? '',
        ]),
      ],
      'preview' => [
        '#type' => 'inline_template',
        '#template' => $form_state->getValue('template'),
        '#context' => $this->getVariables(),
      ],
    ];

    try {
      $rendered = $this->renderer->render($markup);
    }
    catch (Error $exception) {
      $rendered = new TranslatableMarkup('Twig error: %error', ['%error' => $exception->getMessage()]);
    }
    $response = new AjaxResponse();
    $response->addCommand(new RemoveCommand('[data-drupal-messages]'));
    $response->addCommand(new HtmlCommand('#override-preview', $rendered));
    return $response;
  }

  /**
   * Check for error messages.
   *
   * @return \Drupal\Core\Ajax\AjaxResponse|null
   *   Renders the status messages.
   */
  private function checkForErrorMessages(): ?AjaxResponse {
    $all_messages = $this->messenger()->all();
    if (!empty($all_messages['error'])) {
      $response = new AjaxResponse();
      $status_messages = ['#type' => 'status_messages'];
      $messages = $this->renderer->renderRoot($status_messages);
      if (!empty($messages)) {
        $response->addCommand(new RemoveCommand('[data-drupal-messages]'));
        $response->addCommand(new PrependCommand('#status-messages', $messages));
      }
      return $response;
    }
    return NULL;
  }

  /**
   * AJAX callback to load the libraries list.
   */
  public function ajaxUpdateLibraries(array &$form, FormStateInterface $form_state): array {
    return $form['dynamic_elements_container']['libraries_wrapper'];
  }

  /**
   * Builds the rows of the libraries list widget.
   *
   * @param array $selected_libraries
   *   The currently selected libraries.
   * @param array $form
   *   The form array.
   */
  private function buildLibrariesList(array $selected_libraries, array &$form): void {
    $row = 0;
    foreach ($selected_libraries as $key => $library) {
      $form['dynamic_elements_container']['libraries_wrapper']['libraries_list'][$row]['library'] = [
        '#markup' => $library,
      ];
      $button_key = 'library_remove' . $key;
      $form['dynamic_elements_container']['libraries_wrapper']['libraries_list'][$row][$button_key] = [
        '#type' => 'submit',
        '#value' => $this->t('Remove'),
        '#name' => $button_key,
        '#limit_validation_errors' => TRUE,
        '#executes_submit_callback' => FALSE,
        '#ajax' => [
          'callback' => [$this, 'ajaxUpdateLibraries'],
          'wrapper' => 'libraries-wrapper',
          'effect' => 'fade',
        ],
      ];
      $row++;
    }
  }

  /**
   * Parses libraries from a given template string.
   *
   * @param string $template
   *   The template to parse from.
   *
   * @return array
   *   List of libraries.
   */
  private function parseAttachedLibraries($template): array {
    $matches = [];
    $libraries = [];
    \preg_match_all('/attach_library\(.+\)/', $template, $matches);
    if (!empty($matches[0])) {
      foreach ($matches[0] as $attach_statement) {
        $libraries[] = \mb_substr(
          $attach_statement,
          \mb_strlen("attach_library('"),
          \mb_strlen($attach_statement) - \mb_strlen("attach_library('") - \mb_strlen("')")
        );
      }
    }
    return $libraries;
  }

  /**
   * Gathers libraries from core, profiles, installed modules and themes.
   *
   * @return array
   *   Nested array of libraries in the form of extension => [libraries].
   */
  private function getAvailableLibraries(): array {
    $cache = $this->cacheDefault->get(self::COLLECTED_LIBRARIES_CID);
    if (!empty($cache->data)) {
      return $cache->data;
    }
    else {
      $available_libraries = [];
      $modules = $this->getInstalledList($this->moduleExtensionList);
      $profiles = $this->getInstalledList($this->profileExtensionList);
      $themes = $this->getInstalledList($this->themeExtensionList);
      $core_libraries = $this->libraryDiscovery->getLibrariesByExtension('core');
      foreach ($core_libraries as $library_name => $library) {
        $available_libraries['core']['core/' . $library_name] = 'core/' . $library_name;
      }
      /** @var \Drupal\Core\Extension\Extension $extension */
      foreach (\array_merge($modules, $profiles, $themes) as $extension_name => $extension) {
        $extension_info = (array) $extension;
        if ($extension->getType() == 'profile'|| !empty($extension_info['status'])) {
          $extension_libraries = $this->libraryDiscovery->getLibrariesByExtension($extension_name);
          $libraries_list = [];
          foreach ($extension_libraries as $library_name => $library) {
            $libraries_list[$extension_name][$extension_name . '/' . $library_name] = $extension_name . '/' . $library_name;
          }
          $available_libraries = \array_merge($available_libraries, $libraries_list);
        }
      }
      $component_library_libraries = $available_libraries['component_library'];
      // Move Component Libraries to the front of the line.
      unset($available_libraries['component_library']);
      $available_libraries = ['component_library' => $component_library_libraries] + $available_libraries;

      $this->cacheDefault->set(
        self::COLLECTED_LIBRARIES_CID,
        $available_libraries,
        Cache::PERMANENT,
        [
          'config:core.extension',
          'library_info',
          'component_library_asset_list',
        ]
      );
      return $available_libraries;
    }
  }

  /**
   * Get installed list.
   *
   * @param \Drupal\Core\Extension\ExtensionList $extension_list
   *   An extension list.
   *
   * @return array
   *   An array of installed extensions for the given list.
   */
  private function getInstalledList(ExtensionList $extension_list): array {
    $list = [];
    foreach ($extension_list->getAllInstalledInfo() as $name => $info) {
      $list[$name] = $extension_list->get($name);
    }
    return $list;
  }

  /**
   * Get template.
   *
   * Gets the most relevant template as a starting point for users to alter.
   *
   * @param string $override
   *   The theme suggestion being overridden.
   * @param bool $strip_libraries
   *   Whether to strip attach_library twig statements from the template.
   *
   * @return string
   *   The file contents of the template.
   *
   * @throws \Twig\Error\LoaderError
   */
  private function getTemplate(string $override, $strip_libraries): string {
    $registry = $this->overrideManager->getThemeRegistry();
    $pieces = \explode('__', $override);
    for ($i = \count($pieces); $i > 0; $i--) {
      $possible_theme_suggestion = \implode('__', \array_slice($pieces, 0, $i));
      if (!empty($registry[$possible_theme_suggestion])) {
        $path = \sprintf('%s/%s.html.twig', $registry[$possible_theme_suggestion]['path'], $registry[$possible_theme_suggestion]['template']);
        $template = $this->twig->getLoader()->getSourceContext($path)->getCode();
        if ($strip_libraries) {
          // Strip attach_library statements as libraries get displayed in
          // dedicated field.
          $template = \preg_replace('/\{\{ attach_library\(.+\) \}\}[\r\n|\r]/', '', $template);
          $template = \preg_replace('/\{\{attach_library\(.+\)\}\}[\r\n|\r]/', '', $template);
        }
        return $template;
      }
    }
    return '';
  }

  /**
   * Get variables.
   *
   * @return array
   *   The preprocessed variables.
   */
  private function getVariables(): array {
    $variables = $this->getEntity()->getVariables() ?? $this->cache->get('variables') ?? [];
    $variables = $this->overrideMode->replacePlaceholders($variables);
    return $variables;
  }

  /**
   * Get plugin.
   *
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @return string|null
   *   The plugin ID.
   */
  private function getPlugin(FormStateInterface $form_state): ?string {
    $plugin = $this->getPluginContainerValue('plugin', $form_state);
    if ($plugin === NULL) {
      $plugin = $this->getValue('plugin', $this->getEntity(), $form_state);
    }
    if ($plugin) {
      $this->entity->set('plugin', $plugin);
      $this->prepareOverrideEvent->setPlugin($plugin);
    }
    return $plugin;
  }

  /**
   * Get override.
   *
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @return string|null
   *   The override.
   */
  private function getOverride(FormStateInterface $form_state): ?string {
    $override = $this->getPluginContainerValue('override', $form_state);
    if ($override === NULL) {
      $override = $this->getValue('override', $this->getEntity(), $form_state);
    }
    return $override;
  }

  /**
   * Get plugin container value.
   *
   * @param string $form_item
   *   The nested form item key to retrieve.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @return string|null
   *   The value.
   */
  private function getPluginContainerValue(string $form_item, FormStateInterface $form_state): ?string {
    $value = NULL;
    $plugin_container = $form_state->getValue('plugin_container');
    if ($plugin_container && !empty($plugin_container[$form_item])) {
      $value = $plugin_container[$form_item];
    }
    return $value;
  }

}

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

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