You are here

class ParserConfigurationForm in Markdown 8.2

Form for modifying parser configuration.

Hierarchy

Expanded class hierarchy of ParserConfigurationForm

1 file declares its use of ParserConfigurationForm
FilterMarkdown.php in src/Plugin/Filter/FilterMarkdown.php
1 string reference to 'ParserConfigurationForm'
markdown.routing.yml in ./markdown.routing.yml
markdown.routing.yml

File

src/Form/ParserConfigurationForm.php, line 40

Namespace

Drupal\markdown\Form
View source
class ParserConfigurationForm extends FormBase implements FilterAwareInterface, ParserAwareInterface {
  use FilterAwareTrait;
  use FormTrait;
  use MoreInfoTrait;
  use ParserAwareTrait;
  use PluginDependencyTrait;

  /**
   * The Cache Tags Invalidator service.
   *
   * @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface
   */
  protected $cacheTagsInvalidator;

  /**
   * The Element Info Plugin Manager service.
   *
   * @var \Drupal\Core\Render\ElementInfoManagerInterface
   */
  protected $elementInfo;

  /**
   * The Markdown Parser Plugin Manager service.
   *
   * @var \Drupal\markdown\PluginManager\ParserManagerInterface
   */
  protected $parserManager;

  /***
   * The typed config manager.
   *
   * @var \Drupal\Core\Config\TypedConfigManagerInterface
   */
  protected $typedConfigManager;

  /**
   * ParserConfigurationForm constructor.
   *
   * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
   *   The Config Factory service.
   * @param \Drupal\Core\Config\TypedConfigManagerInterface $typedConfigManager
   *   The Typed Config Manager service.
   * @param \Drupal\Core\Cache\CacheTagsInvalidatorInterface $cacheTagsInvalidator
   *   The Cache Tags Invalidator service.
   * @param \Drupal\Core\Render\ElementInfoManagerInterface $elementInfo
   *   The Element Info Plugin Manager service.
   * @param \Drupal\markdown\PluginManager\ParserManagerInterface $parserManager
   *   The Markdown Parser Plugin Manager service.
   */
  public function __construct(ConfigFactoryInterface $configFactory, TypedConfigManagerInterface $typedConfigManager, CacheTagsInvalidatorInterface $cacheTagsInvalidator, ElementInfoManagerInterface $elementInfo, ParserManagerInterface $parserManager) {
    $this->configFactory = $configFactory;
    $this->cacheTagsInvalidator = $cacheTagsInvalidator;
    $this->elementInfo = $elementInfo;
    $this->parserManager = $parserManager;
    $this->typedConfigManager = $typedConfigManager;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container = NULL) {
    if (!$container) {
      $container = \Drupal::getContainer();
    }
    return new static($container
      ->get('config.factory'), $container
      ->get('config.typed'), $container
      ->get('cache_tags.invalidator'), $container
      ->get('plugin.manager.element_info'), $container
      ->get('plugin.manager.markdown.parser'));
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'markdown_parser_configuration';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state, ParserInterface $parser = NULL) {

    // Set the parser.
    $this
      ->setParser($parser);
    $form += [
      '#parents' => [],
      '#title' => $this
        ->t('Configure @parser', [
        '@parser' => $parser
          ->getLabel(FALSE),
      ]),
    ];
    $form['actions']['#type'] = 'actions';
    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this
        ->t('Save configuration'),
      '#button_type' => 'primary',
    ];

    // By default, render the form using system-config-form.html.twig.
    $form['#theme'] = 'system_config_form';

    // Due to the way the Form API works, any time a property is explicitly
    // specified, the default property values are not included. It must be
    // manually retrieved and set here.
    $form['#process'] = $this->elementInfo
      ->getInfoProperty('form', '#process', []);

    // Build the subform via a #process callback.
    $form['#process'][] = [
      $this,
      'processSubform',
    ];
    return $form;
  }

  /**
   * Process callback for constructing markdown settings for a parser.
   *
   * @param array $element
   *   The element being processed.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current form state.
   * @param array $complete_form
   *   The complete form, passed by reference.
   *
   * @return array
   *   The processed element.
   *
   * @throws \Drupal\Core\Form\EnforcedResponseException
   *   When an invalid parser or no parser is provided.
   */
  public function processSubform(array &$element, FormStateInterface $form_state, array &$complete_form) {

    // Keep track of subform parents for the validation and submit handlers.
    $form_state
      ->set('markdownSubformParents', $parents = isset($element['#parents']) ? $element['#parents'] : []);
    $form_state
      ->set('markdownSubformArrayParents', $element['#array_parents']);

    // Add the markdown/admin library to update summaries in vertical tabs.
    $element['#attached']['library'][] = 'markdown/admin';

    // Check for installed parsers.
    if (!$this->parserManager
      ->installedDefinitions()) {
      $error = $this
        ->t('No markdown parsers installed.');
    }
    elseif (!($parser = $this
      ->getParser())) {
      $error = $this
        ->t('No markdown parser has been set. Unable to create the parser form.');
    }
    elseif ($parser
      ->getPluginId() === $this->parserManager
      ->getFallbackPluginId()) {
      $error = $this
        ->t('Unknown parser: %parser_id.', [
        '%parser_id' => $parser
          ->getOriginalPluginId(),
      ]);
    }

    // Add #validate and #submit handlers. These help validate and submit
    // the various markdown plugin forms for parsers and extensions.
    if ($validationHandlers = $form_state
      ->getValidateHandlers()) {
      if (!in_array([
        $this,
        'validateSubform',
      ], $validationHandlers)) {
        array_unshift($validationHandlers, [
          $this,
          'validateSubform',
        ]);
        $form_state
          ->setValidateHandlers($validationHandlers);
      }
    }
    else {
      $complete_form['#validate'][] = [
        $this,
        'validateSubform',
      ];
    }

    // Build a wrapper for the ajax response.
    $form_state
      ->set('markdownAjaxId', $markdownAjaxId = Html::getUniqueId('markdown-parser-ajax'));
    $element['ajax'] = static::createElement([
      '#type' => 'container',
      '#id' => $markdownAjaxId,
      '#attributes' => [
        'data' => [
          'markdownElement' => 'wrapper',
        ],
      ],
    ]);

    // Build vertical tabs that parser and extensions will go into.
    $element['ajax']['vertical_tabs'] = [
      '#type' => 'vertical_tabs',
      '#parents' => array_merge($parents, [
        'vertical_tabs',
      ]),
    ];

    // Determine the group that details should be referencing for vertical tabs.
    $form_state
      ->set('markdownGroup', $group = implode('][', array_merge($parents, [
      'vertical_tabs',
    ])));

    // Create a subform state.
    $subform_state = SubformState::createForSubform($element, $complete_form, $form_state);

    // Build the parser form.
    $element = $this
      ->buildParser($element, $subform_state);
    if (isset($error)) {
      if (($markdownOverview = Url::fromRoute('markdown.overview', [], [
        'absolute' => TRUE,
      ])) && $markdownOverview
        ->access()) {
        $error = $this
          ->t('@error Visit the <a href=":markdown.overview" target="_blank">Markdown Overview</a> page for more details.', [
          '@error' => $error,
          ':markdown.overview' => $markdownOverview
            ->toString(),
        ]);
      }
      else {
        $error = $this
          ->t('@error Ask your site administrator to install a <a href=":supported_parsers" target="_blank">supported markdown parser</a>.', [
          '@error' => $error,
          ':supported_parsers' => Markdown::DOCUMENTATION_URL . '/parsers',
        ]);
      }

      // If there's no filter associated, show the error after the redirect.
      if (!$this
        ->getFilter()) {
        $this
          ->messenger()
          ->addError($error);
      }
      throw new EnforcedResponseException($this
        ->redirect('markdown.overview'), $error);
    }
    return $element;
  }

  /**
   * Builds the parser form elements.
   *
   * @param array $element
   *   An element in a render array.
   * @param \Drupal\markdown\Form\SubformStateInterface $form_state
   *   The form state.
   *
   * @return array
   *   The $element passed, modified to include the parser element.
   */
  protected function buildParser(array $element, SubformStateInterface $form_state) {
    $parser = $this
      ->getParser();
    $parserId = $parser
      ->getPluginId();
    $markdownGroup = $form_state
      ->get('markdownGroup');
    $markdownParents = $form_state
      ->get('markdownSubformParents');
    $element['parser'] = [
      '#weight' => -20,
      '#type' => 'details',
      '#title' => $this
        ->t('Parser'),
      '#tree' => TRUE,
      '#parents' => $markdownParents,
      '#group' => $markdownGroup,
    ];
    $parserElement =& $element['parser'];
    $parserSubform = SubformState::createForSubform($parserElement, $element, $form_state);
    $parserElement['id'] = static::createElement([
      '#type' => 'hidden',
      '#default_value' => $parserId,
      '#attributes' => [
        'data' => [
          'markdownSummary' => 'parser',
          'markdownSummaryValue' => $parser
            ->getLabel(),
          'markdownId' => $parserId,
        ],
      ],
    ]);

    // Build render strategy.
    $parserElement = $this
      ->buildRenderStrategy($parser, $parserElement, $parserSubform);

    // Build parser settings.
    $parserElement = $this
      ->buildParserSettings($parser, $parserElement, $parserSubform);

    // Build parser extensions.
    $parserElement = $this
      ->buildParserExtensions($parser, $parserElement, $parserSubform);
    return $element;
  }

  /**
   * Builds the settings for a specific parser.
   *
   * @param \Drupal\markdown\Plugin\Markdown\ParserInterface $parser
   *   The parser.
   * @param array $element
   *   An element in a render array.
   * @param \Drupal\markdown\Form\SubformStateInterface $form_state
   *   The form state.
   *
   * @return array
   *   The $element passed, modified to include the parser settings element.
   */
  protected function buildParserSettings(ParserInterface $parser, array $element, SubformStateInterface $form_state) {
    $element['settings'] = [
      '#type' => 'details',
      '#title' => $this
        ->t('Settings'),
      '#open' => TRUE,
    ];
    if ($parser instanceof PluginFormInterface) {
      $parserSettingsSubform = SubformState::createForSubform($element['settings'], $element, $form_state);
      $element['settings'] = $parser
        ->buildConfigurationForm($element['settings'], $parserSettingsSubform);
    }

    // If there are no visible settings, add a description so the user knows
    // that is the case and not left with an empty container.
    if (!Element::getVisibleChildren($element['settings'])) {
      $element['settings']['#description'] = $this
        ->t('This parser has no settings to configure.');
    }
    return $element;
  }

  /**
   * Builds the extension settings for a specific parser.
   *
   * @param \Drupal\markdown\Plugin\Markdown\ParserInterface $parser
   *   The parser.
   * @param array $element
   *   An element in a render array.
   * @param \Drupal\markdown\Form\SubformStateInterface $form_state
   *   The form state.
   *
   * @return array
   *   The $element passed, modified to include the parser extension elements.
   */
  protected function buildParserExtensions(ParserInterface $parser, array $element, SubformStateInterface $form_state) {

    // Immediately return if parser isn't extensible.
    if (!$parser instanceof ExtensibleParserInterface) {
      return $element;
    }
    $markdownGroup = $form_state
      ->get('markdownGroup');
    $extensions = $parser
      ->extensions();
    if (!$extensions) {
      return $element;
    }
    $parents = $element['#parents'];
    $element['extensions'] = [
      '#type' => 'container',
    ];

    // Add any specific extension settings.
    foreach ($extensions as $extensionId => $extension) {
      $label = $extension
        ->getLabel(FALSE);
      $url = $extension
        ->getUrl();
      $element['extensions'][$extensionId] = [
        '#type' => 'details',
        '#title' => $label,
        '#group' => $markdownGroup,
        '#parents' => array_merge($parents, [
          'extensions',
          $extensionId,
        ]),
      ];

      /* @var array $extensionElement */
      $extensionElement =& $element['extensions'][$extensionId];
      $extensionSubform = SubformState::createForSubform($extensionElement, $element, $form_state);
      $bundled = in_array($extensionId, $parser
        ->getBundledExtensionIds(), TRUE);
      $installed = $extension
        ->isInstalled();
      $enabled = $extensionSubform
        ->getValue('enabled', $extension
        ->isEnabled());
      if ($experimental = $extension
        ->getExperimental()) {
        $extensionElement['experimental'] = static::createInlineMessage([
          'info' => [
            $experimental === TRUE ? $this
              ->t('This is an experimental extension. Not all features or functionality may work.') : $experimental,
          ],
        ]);
      }
      $extensionElement['libraries'] = $extension
        ->buildStatus(!$installed);

      // Extension enabled checkbox.
      $extensionElement['enabled'] = static::createElement([
        '#type' => 'checkbox',
        '#title' => $this
          ->t('Enable'),
        '#description' => $this
          ->moreInfo($extension
          ->getDescription(), $url),
        '#attributes' => [
          'data' => [
            'markdownElement' => 'extension',
            'markdownSummary' => 'extension',
            'markdownId' => $extensionId,
            'markdownLabel' => $label,
            'markdownInstalled' => $installed,
            'markdownBundle' => $bundled ? $parser
              ->getLabel(FALSE) : FALSE,
            'markdownRequires' => $extension
              ->requires(),
            'markdownRequiredBy' => $extension
              ->requiredBy(),
          ],
        ],
        '#default_value' => $bundled || $enabled,
        '#disabled' => $bundled || !$installed,
      ]);

      // Installed extension settings.
      if ($installed && $extension instanceof PluginFormInterface) {
        $extensionElement['settings'] = [
          '#type' => 'details',
          '#title' => $this
            ->t('Settings'),
          '#open' => TRUE,
        ];
        $extensionSettingsElement =& $extensionElement['settings'];
        $extensionSettingsSubform = SubformState::createForSubform($extensionSettingsElement, $extensionElement, $extensionSubform);
        $extensionSubform
          ->addElementState($extensionSettingsElement, 'visible', 'enabled', [
          'checked' => TRUE,
        ]);
        $extensionSettingsElement = $extension
          ->buildConfigurationForm($extensionSettingsElement, $extensionSettingsSubform);
        $extensionSettingsElement['#access'] = !!Element::getVisibleChildren($extensionSettingsElement);
      }
    }

    // Only show extensions if there are extensions.
    $element['extensions']['#access'] = !!Element::getVisibleChildren($element['extensions']);
    return $element;
  }

  /**
   * Builds the render strategy for a specific parser.
   *
   * @param \Drupal\markdown\Plugin\Markdown\ParserInterface $parser
   *   The parser.
   * @param array $element
   *   An element in a render array.
   * @param \Drupal\markdown\Form\SubformStateInterface $form_state
   *   The form state.
   * @param bool $siteWide
   *   Flag indicating whether the parser is the site-wide parser.
   *
   * @return array
   *   The $element passed, modified to include the render strategy elements.
   */
  protected function buildRenderStrategy(ParserInterface $parser, array $element, SubformStateInterface $form_state, $siteWide = FALSE) {
    $element['render_strategy'] = [
      '#weight' => -10,
      '#type' => 'details',
      '#title' => $this
        ->t('Render Strategy'),
      '#group' => $form_state
        ->get('markdownGroup'),
    ];
    $renderStrategySubform =& $element['render_strategy'];
    $renderStrategySubformState = SubformState::createForSubform($renderStrategySubform, $element, $form_state);
    $renderStrategySubform['type'] = [
      '#weight' => -10,
      '#type' => 'select',
      '#description' => $this
        ->t('Determines the strategy to use when dealing with user provided HTML markup.'),
      '#default_value' => $renderStrategySubformState
        ->getValue('type', $parser
        ->getRenderStrategy()),
      '#attributes' => [
        'data-markdown-element' => 'render_strategy',
        'data-markdown-summary' => 'render_strategy',
      ],
      '#options' => [
        RenderStrategyInterface::FILTER_OUTPUT => $this
          ->t('Filter Output'),
        RenderStrategyInterface::ESCAPE_INPUT => $this
          ->t('Escape Input'),
        RenderStrategyInterface::STRIP_INPUT => $this
          ->t('Strip Input'),
        RenderStrategyInterface::NONE => $this
          ->t('None'),
      ],
    ];
    $renderStrategySubform['type']['#description'] = $this
      ->moreInfo($renderStrategySubform['type']['#description'], RenderStrategyInterface::DOCUMENTATION_URL . '#xss');

    // Build allowed HTML plugins.
    $renderStrategySubform['plugins'] = [
      '#weight' => -10,
      '#type' => 'item',
      '#input' => FALSE,
      '#title' => $this
        ->t('Allowed HTML'),
      '#description_display' => 'before',
      '#description' => $this
        ->t('The following are registered <code>@MarkdownAllowedHtml</code> plugins that allow HTML tags and attributes based on configuration. These are typically provided by the parser itself, any of its enabled extensions that convert additional HTML tag and potentially various Drupal filters, modules or themes (if supported).'),
    ];
    $renderStrategySubform['plugins']['#description'] = $this
      ->moreInfo($renderStrategySubform['plugins']['#description'], RenderStrategyInterface::DOCUMENTATION_URL);
    $renderStrategySubformState
      ->addElementState($renderStrategySubform['plugins'], 'visible', 'type', [
      'value' => RenderStrategyInterface::FILTER_OUTPUT,
    ]);
    $allowedHtmlManager = AllowedHtmlManager::create();
    foreach ($allowedHtmlManager
      ->appliesTo($parser) as $plugin_id => $allowedHtml) {
      $pluginDefinition = $allowedHtml
        ->getPluginDefinition();
      $label = isset($pluginDefinition['label']) ? $pluginDefinition['label'] : $plugin_id;
      $description = isset($pluginDefinition['description']) ? $pluginDefinition['description'] : '';
      $type = isset($pluginDefinition['type']) ? $pluginDefinition['type'] : 'other';
      if (!isset($renderStrategySubform['plugins'][$type])) {
        $renderStrategySubform['plugins'][$type] = [
          '#type' => 'details',
          '#open' => TRUE,
          '#title' => $this
            ->t(ucfirst($type) . 's'),
          //phpcs:ignore
          '#parents' => $renderStrategySubformState
            ->createParents([
            'plugins',
          ]),
        ];
        if ($type === 'module') {
          $renderStrategySubform['plugins'][$type]['#weight'] = -10;
        }
        if ($type === 'filter') {
          $renderStrategySubform['plugins'][$type]['#weight'] = -9;
          $renderStrategySubform['plugins'][$type]['#description'] = $this
            ->t('NOTE: these will only be applied when the filter it represents is actually enabled.');
          $renderStrategySubform['plugins'][$type]['#description_display'] = 'before';
        }
        if ($type === 'parser') {
          $renderStrategySubform['plugins'][$type]['#weight'] = -8;
        }
        if ($type === 'extension') {
          $renderStrategySubform['plugins'][$type]['#weight'] = -7;
          $renderStrategySubform['plugins'][$type]['#title'] = $this
            ->t('Extensions');
          $renderStrategySubform['plugins'][$type]['#description'] = $this
            ->t('NOTE: these will only be applied when the parser extension it represents is actually enabled.');
          $renderStrategySubform['plugins'][$type]['#description_display'] = 'before';
        }
        if ($type === 'theme') {
          $renderStrategySubform['plugins'][$type]['#weight'] = -6;
          $renderStrategySubform['plugins'][$type]['#description'] = $this
            ->t('NOTE: these will only be applied when the theme that provides the plugin is the active theme or is a descendant of the active theme.');
          $renderStrategySubform['plugins'][$type]['#description_display'] = 'before';
        }
      }
      $allowedHtmlTags = $allowedHtml
        ->allowedHtmlTags($parser);
      $allowedHtmlPlugins = $parser
        ->getAllowedHtmlPlugins();

      // Determine the default value.
      $defaultValue = NULL;
      if ($allowedHtmlTags) {

        // Setting value.
        if (!isset($defaultValue) && isset($allowedHtmlPlugins[$plugin_id])) {
          $defaultValue = $allowedHtmlPlugins[$plugin_id];
        }
        if (!isset($defaultValue)) {
          if ($type === 'filter' && ($filter = $this
            ->getFilter()) && $filter instanceof FilterFormatAwareInterface && ($format = $filter
            ->getFilterFormat())) {
            $definition = $allowedHtml
              ->getPluginDefinition();
            $filterId = isset($definition['requiresFilter']) ? $definition['requiresFilter'] : $plugin_id;
            $defaultValue = $format
              ->filters()
              ->has($filterId) ? !!$format
              ->filters($filterId)->status : FALSE;
          }
          elseif ($type === 'extension' && $parser instanceof ExtensibleParserInterface && $parser
            ->extensions()
            ->has($plugin_id)) {
            $defaultValue = $parser
              ->extension($plugin_id)
              ->isEnabled();
          }
          else {
            $defaultValue = TRUE;
          }
        }
      }
      $renderStrategySubform['plugins'][$type][$plugin_id] = [
        '#type' => 'checkbox',
        '#title' => $label,
        '#disabled' => !$allowedHtmlTags,
        '#description' => Markup::create(sprintf('%s<pre><code>%s</code></pre>', $description, $allowedHtmlTags ? htmlentities(FilterHtml::tagsToString($allowedHtmlTags)) : $this
          ->t('No HTML tags provided.'))),
        '#default_value' => $renderStrategySubformState
          ->getValue([
          'plugins',
          $plugin_id,
        ], $defaultValue),
        '#attributes' => [
          'data-markdown-default-value' => $renderStrategySubformState
            ->getValue([
            'plugins',
            $plugin_id,
          ], $defaultValue) ? 'true' : 'false',
        ],
      ];
      if ($plugin_id === 'markdown') {
        $renderStrategySubform['plugins'][$type][$plugin_id]['#weight'] = -10;
      }
      if (!$allowedHtmlTags) {
        continue;
      }

      // Filters should only show based on whether they're enabled.
      if ($type === 'extension') {

        // If using the site-wide parser, then allowed HTML plugins that
        // reference disabled extensions there cannot be enable here.
        if ($siteWide) {
          $extensionDisabled = $defaultValue !== TRUE;
          $renderStrategySubform['plugins'][$type][$plugin_id]['#disabled'] = $extensionDisabled;
          if ($extensionDisabled) {
            $renderStrategySubform['plugins'][$type][$plugin_id]['#title'] = new FormattableMarkup('@title @disabled', [
              '@title' => $renderStrategySubform['plugins'][$type][$plugin_id]['#title'],
              '@disabled' => $this
                ->t('(extension disabled)'),
            ]);
          }
        }
        else {
          $parents = array_merge(array_slice($renderStrategySubformState
            ->createParents(), 0, -1), [
            'extensions',
            $plugin_id,
            'enabled',
          ]);
          $selector = ':input[name="' . array_shift($parents) . '[' . implode('][', $parents) . ']"]';
          $renderStrategySubform['plugins'][$type][$plugin_id]['#title'] = new FormattableMarkup('@title @disabled', [
            '@title' => $renderStrategySubform['plugins'][$type][$plugin_id]['#title'],
            '@disabled' => $renderStrategySubformState
              ->conditionalElement([
              '#value' => $this
                ->t('(extension disabled)'),
            ], 'visible', $selector, [
              'checked' => FALSE,
            ]),
          ]);
          $renderStrategySubform['plugins'][$type][$plugin_id]['#states'] = [
            '!checked' => [
              $selector => [
                'checked' => FALSE,
              ],
            ],
            'disabled' => [
              $selector => [
                'checked' => FALSE,
              ],
            ],
          ];
        }
      }
      elseif ($type === 'filter') {
        $selector = ':input[name="filters[' . $plugin_id . '][status]"]';
        $renderStrategySubform['plugins'][$type][$plugin_id]['#title'] = new FormattableMarkup('@title @disabled', [
          '@title' => $renderStrategySubform['plugins'][$type][$plugin_id]['#title'],
          '@disabled' => $renderStrategySubformState
            ->conditionalElement([
            '#value' => $this
              ->t('(filter disabled)'),
          ], 'visible', $selector, [
            'checked' => FALSE,
          ]),
        ]);
        $renderStrategySubform['plugins'][$type][$plugin_id]['#states'] = [
          '!checked' => [
            $selector => [
              'checked' => FALSE,
            ],
          ],
          'disabled' => [
            $selector => [
              'checked' => FALSE,
            ],
          ],
        ];
      }
    }
    $renderStrategySubform['plugins']['#access'] = !!Element::getVisibleChildren($renderStrategySubform['plugins']);
    $renderStrategySubform['custom_allowed_html'] = [
      '#weight' => -10,
      '#type' => 'textarea',
      '#title' => $this
        ->t('Custom Allowed HTML'),
      '#description' => $this
        ->t('A list of additional custom allowed HTML tags that can be used. This follows the same rules as above; use cautiously and sparingly.'),
      '#default_value' => $renderStrategySubformState
        ->getValue('custom_allowed_html', $parser
        ->getCustomAllowedHtml()),
      '#attributes' => [
        'data-markdown-element' => 'custom_allowed_html',
      ],
    ];
    $renderStrategySubform['custom_allowed_html']['#description'] = $this
      ->moreInfo($renderStrategySubform['custom_allowed_html']['#description'], RenderStrategyInterface::DOCUMENTATION_URL);
    FormTrait::resetToDefault($renderStrategySubform['custom_allowed_html'], 'custom_allowed_html', '', $renderStrategySubformState);
    $renderStrategySubformState
      ->addElementState($renderStrategySubform['custom_allowed_html'], 'visible', 'type', [
      'value' => RenderStrategyInterface::FILTER_OUTPUT,
    ]);
    return $element;
  }

  /**
   * Retrieves configuration from an array of values.
   *
   * @param string $name
   *   The config name to use.
   * @param array $values
   *   An array of values.
   *
   * @return \Drupal\Core\Config\Config
   *   A Config object.
   */
  public function getConfigFromValues($name, array $values) {
    $config = $this->configFactory
      ->getEditable($name);

    // Some older 8.x-2.x code used to have the parser value as a string.
    // @todo Remove after 8.x-2.0 release.
    if (isset($values['parser']) && is_string($values['parser'])) {
      $values['id'] = $values['parser'];
      unset($values['parser']);
    }
    elseif (isset($values['parser']) && is_array($values['parser'])) {
      $values += $values['parser'];
    }

    // Load the parser with the values so it can construct the proper config.
    $parserId = isset($values['id']) ? (string) $values['id'] : '';
    $parser = $this->parserManager
      ->createInstance($parserId, $values);

    // Sort $configuration by using the $defaults keys. This ensures there
    // is a consistent order when saving the config.
    $configuration = $parser
      ->getSortedConfiguration();
    $config
      ->setData($configuration);
    return $config;
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {

    // Extract the values from the form.
    $values = $form_state
      ->cleanValues()
      ->getValues();

    // Determine the parser identifier.
    $parserId = isset($values['id']) ? (string) $values['id'] : $this
      ->getParser()
      ->getOriginalPluginId();

    // Normalize parser values into config data.
    $config = $this
      ->getConfigFromValues("markdown.parser.{$parserId}", $values);

    // Save the config.
    $config
      ->save();

    // Invalidate any tags associated with the parser.
    $this->cacheTagsInvalidator
      ->invalidateTags([
      "markdown.parser.{$parserId}",
    ]);
    drupal_set_message($this
      ->t('The configuration options have been saved.'));
  }

  /**
   * Subform submission handler.
   *
   * @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.
   */
  public function submitSubform(array &$form, FormStateInterface $form_state) {

    // Immediately return if no subform parents or form hasn't submitted.
    if (!($arrayParents = $form_state
      ->get('markdownSubformArrayParents')) || !$form_state
      ->isSubmitted()) {
      return;
    }
    $subform =& NestedArray::getValue($form, $arrayParents);
    $subformState = SubformState::createForSubform($subform, $form, $form_state);
    $parserId = $subformState
      ->getValue('id');
    if ($parserId && $this->parserManager
      ->hasDefinition($parserId)) {
      $parser = $this->parserManager
        ->createInstance($parserId, $subformState
        ->getValues());
      if ($parser instanceof SettingsInterface && $parser instanceof PluginFormInterface && !empty($subform['parser']['settings'])) {
        $parser
          ->submitConfigurationForm($subform['parser']['settings'], SubformState::createForSubform($subform['parser']['settings'], $subform, $subformState));
      }
      if ($parser instanceof ExtensibleParserInterface && !empty($subform['parser']['extensions'])) {
        foreach ($parser
          ->extensions() as $extensionId => $extension) {
          if ($extension instanceof SettingsInterface && $extension instanceof PluginFormInterface && isset($subform['parser']['extensions'][$extensionId]['settings'])) {
            $parser
              ->submitConfigurationForm($subform['parser']['extensions'][$extensionId]['settings'], SubformState::createForSubform($subform['parser']['extensions'][$extensionId]['settings'], $subform, $subformState));
          }
        }
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {
    parent::validateForm($form, $form_state);

    // Extract the values from the form.
    $values = $form_state
      ->cleanValues()
      ->getValues();

    // Determine the parser identifier.
    $parserId = isset($values['id']) ? (string) $values['id'] : $this
      ->getParser()
      ->getOriginalPluginId();

    // Normalize parser values into config data.
    $config = $this
      ->getConfigFromValues("markdown.parser.{$parserId}", $values);
    $typed_config = $this->typedConfigManager
      ->createFromNameAndData("markdown.parser.{$parserId}", $config
      ->get());
    $violations = $typed_config
      ->validate();
    foreach ($violations as $violation) {
      $form_state
        ->setErrorByName(static::mapViolationPropertyPathsToFormNames($violation
        ->getPropertyPath()), $violation
        ->getMessage());
    }
  }
  protected static function mapViolationPropertyPathsToFormNames($property_path) {
    return str_replace('.', '][', $property_path);
  }

  /**
   * Subform validation handler.
   *
   * @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.
   */
  public function validateSubform(array &$form, FormStateInterface $form_state) {

    // Immediately return if no subform parents or form hasn't submitted.
    if (!($arrayParents = $form_state
      ->get('markdownSubformArrayParents')) || !$form_state
      ->isSubmitted()) {
      return;
    }

    // Submit handlers aren't necessarily known until a user has started the.
    // process of submitting the form. The triggering element might have
    // specific submit handlers that needs to be intercepted and the only place
    // that this can be done is during the validation phase.
    if ($submitHandlers = $form_state
      ->getSubmitHandlers()) {
      if (!in_array([
        $this,
        'submitSubform',
      ], $submitHandlers)) {
        array_unshift($submitHandlers, [
          $this,
          'submitSubform',
        ]);
        $form_state
          ->setSubmitHandlers($submitHandlers);
      }
    }
    else {
      $complete_form =& $form_state
        ->getCompleteForm();
      $complete_form['#submit'][] = [
        $this,
        'submitSubform',
      ];
    }
    $subform =& NestedArray::getValue($form, $arrayParents);
    $subformState = SubformState::createForSubform($subform, $form, $form_state);
    $parserId = $subformState
      ->getValue('id');
    if ($parserId && $this->parserManager
      ->hasDefinition($parserId)) {
      $parser = $this->parserManager
        ->createInstance($parserId, $subformState
        ->getValues());
      if ($parser instanceof SettingsInterface && $parser instanceof PluginFormInterface && !empty($subform['parser']['settings'])) {
        $parser
          ->validateConfigurationForm($subform['parser']['settings'], SubformState::createForSubform($subform['parser']['settings'], $subform, $subformState));
      }
      if ($parser instanceof ExtensibleParserInterface && !empty($subform['parser']['extensions'])) {
        foreach ($parser
          ->extensions() as $extensionId => $extension) {
          if ($extension instanceof SettingsInterface && $extension instanceof PluginFormInterface && isset($subform['parser']['extensions'][$extensionId]['settings'])) {
            $extension
              ->validateConfigurationForm($subform['parser']['extensions'][$extensionId]['settings'], SubformState::createForSubform($subform['parser']['extensions'][$extensionId]['settings'], $subform, $subformState));
          }
        }
      }
    }
  }

}

Members

Namesort descending Modifiers Type Description Overrides
DependencySerializationTrait::$_entityStorages protected property An array of entity type IDs keyed by the property name of their storages.
DependencySerializationTrait::$_serviceIds protected property An array of service IDs keyed by property name used for serialization.
DependencySerializationTrait::__sleep public function 1
DependencySerializationTrait::__wakeup public function 2
DependencyTrait::$dependencies protected property The object's dependencies.
DependencyTrait::addDependencies protected function Adds multiple dependencies.
DependencyTrait::addDependency protected function Adds a dependency.
FilterAwareTrait::$filter protected property A Filter plugin.
FilterAwareTrait::getFilter public function
FilterAwareTrait::setFilter public function
FormBase::$configFactory protected property The config factory. 1
FormBase::$requestStack protected property The request stack. 1
FormBase::$routeMatch protected property The route match.
FormBase::config protected function Retrieves a configuration object.
FormBase::configFactory protected function Gets the config factory for this form. 1
FormBase::container private function Returns the service container.
FormBase::currentUser protected function Gets the current user.
FormBase::getRequest protected function Gets the request object.
FormBase::getRouteMatch protected function Gets the route match.
FormBase::logger protected function Gets the logger for a specific channel.
FormBase::redirect protected function Returns a redirect response object for the specified route. Overrides UrlGeneratorTrait::redirect
FormBase::resetConfigFactory public function Resets the configuration factory.
FormBase::setConfigFactory public function Sets the config factory for this form.
FormBase::setRequestStack public function Sets the request stack object to use.
FormTrait::$tokenModuleExists protected static property Flag indicating whether the token module exists.
FormTrait::addDataAttribute public static function Adds a data attribute to an element.
FormTrait::addDataAttributes public static function Adds multiple data attributes to an element.
FormTrait::addElementState public static function Adds a #states selector to an element.
FormTrait::createElement public static function Creates an element, adding data attributes to it if necessary.
FormTrait::createInlineMessage public static function Creates an inline status message to be used in a render array.
FormTrait::createTokenBrowser public static function Creates a Token browser element for use when dealing with tokens.
FormTrait::getElementSelector public static function Retrieves the selector for an element.
FormTrait::resetToDefault public static function Allows a form element to be reset to its default value.
LinkGeneratorTrait::$linkGenerator protected property The link generator. 1
LinkGeneratorTrait::getLinkGenerator Deprecated protected function Returns the link generator.
LinkGeneratorTrait::l Deprecated protected function Renders a link to a route given a route name and its parameters.
LinkGeneratorTrait::setLinkGenerator Deprecated public function Sets the link generator service.
LoggerChannelTrait::$loggerFactory protected property The logger channel factory service.
LoggerChannelTrait::getLogger protected function Gets the logger for a specific channel.
LoggerChannelTrait::setLoggerFactory public function Injects the logger channel factory.
MessengerTrait::$messenger protected property The messenger. 29
MessengerTrait::messenger public function Gets the messenger. 29
MessengerTrait::setMessenger public function Sets the messenger.
MoreInfoTrait::moreInfo protected function Appends existing content with a "More Info" link.
ParserAwareTrait::$parser protected property A Markdown Parser instance.
ParserAwareTrait::getParser public function 1
ParserAwareTrait::setParser public function
ParserConfigurationForm::$cacheTagsInvalidator protected property The Cache Tags Invalidator service.
ParserConfigurationForm::$elementInfo protected property The Element Info Plugin Manager service.
ParserConfigurationForm::$parserManager protected property The Markdown Parser Plugin Manager service.
ParserConfigurationForm::$typedConfigManager protected property
ParserConfigurationForm::buildForm public function Form constructor. Overrides FormInterface::buildForm
ParserConfigurationForm::buildParser protected function Builds the parser form elements.
ParserConfigurationForm::buildParserExtensions protected function Builds the extension settings for a specific parser.
ParserConfigurationForm::buildParserSettings protected function Builds the settings for a specific parser.
ParserConfigurationForm::buildRenderStrategy protected function Builds the render strategy for a specific parser.
ParserConfigurationForm::create public static function Instantiates a new instance of this class. Overrides FormBase::create 1
ParserConfigurationForm::getConfigFromValues public function Retrieves configuration from an array of values.
ParserConfigurationForm::getFormId public function Returns a unique string identifying the form. Overrides FormInterface::getFormId 1
ParserConfigurationForm::mapViolationPropertyPathsToFormNames protected static function
ParserConfigurationForm::processSubform public function Process callback for constructing markdown settings for a parser.
ParserConfigurationForm::submitForm public function Form submission handler. Overrides FormInterface::submitForm
ParserConfigurationForm::submitSubform public function Subform submission handler.
ParserConfigurationForm::validateForm public function Form validation handler. Overrides FormBase::validateForm
ParserConfigurationForm::validateSubform public function Subform validation handler.
ParserConfigurationForm::__construct public function ParserConfigurationForm constructor. 1
PluginDependencyTrait::calculatePluginDependencies protected function Calculates and adds dependencies of a specific plugin instance. 1
PluginDependencyTrait::getPluginDependencies protected function Calculates and returns dependencies of a specific plugin instance.
PluginDependencyTrait::moduleHandler protected function Wraps the module handler. 1
PluginDependencyTrait::themeHandler protected function Wraps the theme handler. 1
RedirectDestinationTrait::$redirectDestination protected property The redirect destination service. 1
RedirectDestinationTrait::getDestinationArray protected function Prepares a 'destination' URL query parameter for use with \Drupal\Core\Url.
RedirectDestinationTrait::getRedirectDestination protected function Returns the redirect destination service.
RedirectDestinationTrait::setRedirectDestination public function Sets the redirect destination service.
RendererTrait::$renderer protected static property The Renderer service.
RendererTrait::renderer protected function Retrieves the Renderer service.
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.
UrlGeneratorTrait::$urlGenerator protected property The url generator.
UrlGeneratorTrait::getUrlGenerator Deprecated protected function Returns the URL generator service.
UrlGeneratorTrait::setUrlGenerator Deprecated public function Sets the URL generator service.
UrlGeneratorTrait::url Deprecated protected function Generates a URL or path for a specific route based on the given parameters.