g2-8.x-1.x-dev/src/Plugin/Filter/Automatic.php

src/Plugin/Filter/Automatic.php
<?php

declare(strict_types=1);

namespace Drupal\g2\Plugin\Filter;

use Drupal\Component\Utility\Unicode;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\filter\Attribute\Filter;
use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;
use Drupal\filter\Plugin\FilterInterface;
use Drupal\g2\G2;
use Drupal\g2\Matcher;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides a 'Automatic' filter.
 *
 * @Filter(
 *   id = "g2_automatic",
 *   title = @Translation("Automatically wrap G2 entries found in content with <code>&lt;dfn/&gt;</code> elements"),
 *   type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE,
 *   settings = {
 *     "stop_list" = "",
 *   },
 *   description=@Translation("Automatically wrap recognized G2 entries in <code>&lt;dfn/&gt;</code> elements, except for those in your configured stop list. This filter is only useful along with the <code>&lt;dfn/&gt;</code> conversion filter, and <em>must</em> be applied before it."),
 *   weight = -1,
 * )
 *
 * @phpstan-consistent-constructor
 */
#[Filter(
  id: "g2_automatic",
  title: new TranslatableMarkup("G2 Automatic definitions"),
  type: FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE,
  description: new TranslatableMarkup("Automatically wrap G2 entries found in content with <code>&lt;dfn/&gt;</code> elements"),
  weight: -1,
  settings: [
    "stop_list" => "",
  ]
)]
class Automatic extends FilterBase implements ContainerFactoryPluginInterface {

  /**
   * The name of the single settings for this filter.
   */
  const STOP = 'stop_list';

  /**
   * The logger.channel.g2 service.
   *
   * @var \Psr\Log\LoggerInterface
   */
  protected LoggerInterface $logger;

  /**
   * The g2.matcher service.
   *
   * @var \Drupal\g2\Matcher
   */
  protected Matcher $matcher;

  /**
   * Constructor.
   *
   * @param array<string,mixed> $configuration
   *   The plugin configuration.
   * @param string $plugin_id
   *   The plugin ID.
   * @param array<string,mixed> $plugin_definition
   *   The plugin definition.
   * @param \Drupal\g2\Matcher $matcher
   *   The g2.matcher service.
   * @param \Psr\Log\LoggerInterface $logger
   *   The logger.channel.g2 service.
   */
  public function __construct(
    array $configuration,
    string $plugin_id,
    array $plugin_definition,
    Matcher $matcher,
    LoggerInterface $logger,
  ) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->logger = $logger;
    $this->matcher = $matcher;
  }

  /**
   * Static factory.
   *
   * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
   *   The container.
   * @param array<string,mixed> $configuration
   *   The plugin configuration.
   * @param string $plugin_id
   *   The plugin ID.
   * @param array<string,mixed> $plugin_definition
   *   The plugin definition.
   *
   * @return static
   *   The plugin instance.
   */
  public static function create(
    ContainerInterface $container,
    array $configuration,
    $plugin_id,
    $plugin_definition,
  ): static {
    $logger = $container->get(G2::SVC_LOGGER);
    $matcher = $container->get(G2::SVC_MATCHER);

    return new static($configuration, $plugin_id, $plugin_definition, $matcher, $logger);
  }

  /**
   * {@inheritDoc}
   *
   * @throws \DOMException
   */
  public function prepare($text, $langcode): string {
    if (!Unicode::validateUtf8($text)) {
      $this->logger
        ->error('The text is apparently not valid UTF-8 charset: @text.', [
          '@text' => $text,
        ]);
      return $text;
    }
    $stopList = $this->getConfiguration()['settings'][self::STOP] ?? [];
    $stopList = $this->getNormalizedStopList($stopList);
    return Matcher::handleSource($text,
      $this->matcher->getMultiStringMatcher(),
      $stopList);
  }

  /**
   * {@inheritdoc}
   */
  public function process($text, $langcode) {
    return new FilterProcessResult($text);
  }

  /**
   * {@inheritdoc}
   */
  public function tips($long = FALSE): ?TranslatableMarkup {
    return $long
      ? $this->t('Glossary entries which are not in the glossary stop list will be automatically wrapped in a &lt;dfn&gt; element during rendering, just as if you had tagged them yourself.')
      : $this->t('Glossary entries are automatically wrapped in &lt;dfn&gt; to generate links.');
  }

  /**
   * Return a deduplicated, trimmed, sorted array version of the stop list.
   *
   * @param mixed $stopList
   *   The stop list to normalize. May be null, string, or array.
   *
   * @return string[]
   *   The normalized version of the same stop list.
   */
  protected function getNormalizedStopList(mixed $stopList = NULL): array {
    // No existing config or submitted a NULL.
    if ($stopList === NULL) {
      $stopList = $this->settings[static::STOP] ?? [];
    }

    // Handle legacy string configuration.
    if (is_string($stopList)) {
      // Web submissions add CR, just ignore them.
      $stopList = str_replace("\r", "", $stopList);
      $stopList = explode("\n", $stopList);
    }

    // At this point, we know we have a well-formed array. Let's clean it.
    $stopList = array_map(fn($item) => trim($item), $stopList);
    $stopList = array_filter($stopList);
    sort($stopList);
    $stopList = array_unique($stopList);

    return $stopList;
  }

  /**
   * Builder for the plugin settings subform.
   *
   * @param array<string,mixed> $form
   *   The original form.
   * @param \Drupal\Core\Form\FormStateInterface $formState
   *   The form state.
   *
   * @return array<string,mixed>
   *   The modified form.
   */
  public function settingsForm(array $form, FormStateInterface $formState) {
    $form[self::STOP] = [
      '#type' => 'textarea',
      '#title' => $this->t('Stop list'),
      '#default_value' => implode("\n", $this->getNormalizedStopList()),
      '#description' => $this->t('Enter G2 entries that must never be automatically wrapped in a &lt;dfn/&gt; element, one per line. You will still be able to define them by adding the &lt;dfn/&gt; element manually.'),
      '#element_validate' => [[$this, 'validateStopList']],
    ];
    return $form;
  }

  /**
   * Form element validation handler.
   *
   * Deduplicates and sorts entries.
   *
   * @param array<string,mixed> $element
   *   The stop_list form element.
   * @param \Drupal\Core\Form\FormStateInterface $formState
   *   The form state.
   */
  public function validateStopList(array &$element, FormStateInterface $formState): void {
    $rawValue = $element['#value'] ?? '';
    $stopList = $this->getNormalizedStopList($rawValue);
    $value = implode("\r\n", $stopList);
    $formState->setValueForElement($element, $stopList);
    if ($value !== $rawValue) {
      $this->messenger()
        ->addStatus("Your G2 stop list has been filtered and reordered.");
    }
  }

}

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

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