freelinking-8.x-3.x-dev/src/Plugin/Filter/Freelinking.php

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

namespace Drupal\freelinking\Plugin\Filter;

use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Session\AccountProxyInterface;
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\freelinking\FreelinkingManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Freelinking input filter plugin.
 */
#[Filter(
  id: 'freelinking',
  title: new TranslatableMarkup('Freelinking'),
  description: new TranslatableMarkup('Provides a flexible format for linking content.'),
  type: FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE,
  status: FALSE,
  settings: [
    'default' => 'nodetitle',
    'global_options' => [
      'ignore_upi' => FALSE,
    ],
    'plugins' => [],
  ],
  weight: 0,
)]
class Freelinking extends FilterBase implements ContainerFactoryPluginInterface {

  /**
   * Freelinking plugin manager.
   *
   * @var \Drupal\freelinking\FreelinkingManagerInterface
   */
  protected $freelinkingManager;

  /**
   * Current user account.
   *
   * @var \Drupal\Core\Session\AccountProxyInterface
   */
  protected $currentUser;

  /**
   * Initialize method.
   *
   * @param array $configuration
   *   The plugin configuration.
   * @param string $plugin_id
   *   The plugin ID.
   * @param array $plugin_definition
   *   The plugin definition array.
   * @param \Drupal\freelinking\FreelinkingManagerInterface $freelinkingManager
   *   The Freelinking plugin manager.
   * @param \Drupal\Core\Session\AccountProxyInterface $current_user
   *   The current user account.
   */
  public function __construct(array $configuration, $plugin_id, array $plugin_definition, FreelinkingManagerInterface $freelinkingManager, AccountProxyInterface $current_user) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);

    $this->freelinkingManager = $freelinkingManager;
    $this->currentUser = $current_user;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('freelinking.manager'),
      $container->get('current_user')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function settingsForm(array $form, FormStateInterface $form_state) {
    $plugins = $this->freelinkingManager->getDefinitions();

    $form['default'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Default plugin'),
      '#description' => $this->t('Default plugin to use when no indicator is specified. “Nodetitle” mimics previous versions of Freelinking.'),
      '#default_value' => $this->settings['default'],
      '#required' => TRUE,
    ];

    $form['global_options'] = [
      '#tree' => TRUE,
      'ignore_upi' => [
        '#type' => 'select',
        '#title' => $this->t('Ignore Unknown Plugin Indicators (UPI)'),
        '#description' => $this->t('Choose whether to ignore markup that may look like freelinking or does not have a valid plugin.'),
        '#options' => [
          0 => $this->t('Display an error'),
          1 => $this->t('Ignore indicators'),
        ],
        '#required' => TRUE,
        '#default_value' => $this->settings['global_options']['ignore_upi'],
      ],
    ];

    $form['plugins'] = [
      '#tree' => TRUE,
    ];

    foreach ($plugins as $plugin_name => $plugin_definition) {
      $config = $this->extractPluginSettings($plugin_name, $this->settings['plugins']);
      $plugin_settings = $config['settings'] ?? [];
      $plugin = $this->freelinkingManager->createInstance(
        $plugin_name,
        ['settings' => $plugin_settings]
      );

      $form['plugins'][$plugin_name] = [
        '#tree' => TRUE,
        '#type' => 'fieldset',
        '#collapsible' => FALSE,
        '#title' => $plugin_definition['title'],
        'plugin' => [
          '#type' => 'value',
          '#value' => $plugin_name,
        ],
        'enabled' => [
          '#type' => 'checkbox',
          '#title' => $this->t('Enable'),
          '#default_value' => $config['enabled'] ?? FALSE,
        ],
        'settings' => [
          '#tree' => TRUE,
          '#type' => 'container',
        ] + $plugin->settingsForm($form, $form_state),
      ];

      // Hide the enabled checkbox if the plugin is always enabled.
      if ($plugin->isHidden()) {
        $form['plugins'][$plugin_name]['enabled']['#disabled'] = TRUE;
        $form['plugins'][$plugin_name]['enabled']['#default_value'] = TRUE;
      }
    }

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function tips($long = FALSE) {
    $text = $this->t('Freelinking helps you easily create HTML links. Links take the form of <code>[[indicator:target|Title]].</code>');

    if (!$long) {
      if (isset($this->settings['default']) &&
          'NONE' !== $this->settings['default']) {
        $plugin = $this->freelinkingManager->createInstance($this->settings['default']);
        $text .= ' ' . $plugin->getTip();
      }
      return $text;
    }

    $content = <<<EOF
<p>Freelinking helps you easily create HTML links. Links take the form of <code>[[indicator:target|Title]].</code><br />
Below is a list of available types of freelinks you may use, organized as <strong>Plugin Name</strong>: [<em>indicator</em>].</p>
<ul>
EOF;

    // Assemble tips for each allowed plugin.
    $allowed_plugins = $this->extractAllowedPlugins($this->settings['plugins']);
    foreach ($allowed_plugins as $plugin_name => $plugin_info) {
      $configuration = [
        'settings' => $plugin_info['settings'] ?? [],
      ];
      $plugin = $this->freelinkingManager->createInstance($plugin_info['plugin'], $configuration);
      $content .= '<li><strong>' . $plugin->getPluginDefinition()['title'] . '</strong> [<em>' . $plugin->getIndicator() . '</em>]: ' . $plugin->getTip() . '</li>';
    }
    $content .= '</ul>';

    // Ignore phpcs warning because tips are dynamically generated based on
    // plugins. This follows the pattern in core.
    // @see \Drupal\filter\Plugin\Filter\FilterAlign::tips().
    return $this->t($content);
  }

  /**
   * {@inheritdoc}
   */
  public function process($text, $langcode) {
    $defaultplugin = $this->settings['default'];
    // Ignore the node language if the current user's preferred language is
    // different so that links can be generated in the user's native language.
    $user_langcode = $langcode <> $this->currentUser->getPreferredLangcode() ? $this->currentUser->getPreferredLangcode() : $langcode;

    $result = new FilterProcessResult($text);

    // $pattern = '/(\[\[([a-z0-9\_]+):([^\]]+)\]\])/';.
    // @todo why not go back to preg_match_all for double square bracket syntax?
    // @see https://www.drupal.org/node/647940
    $start = 0;
    $remain = $text;
    $newtext = '';

    // Begin an indefinite loop until it is manually broken.
    while (TRUE) {
      $offset = 0;

      // Break when there is no remaining text to process.
      if (empty($remain)) {
        break;
      }

      // Begin freelinking parse or reset.
      if ('[' === $remain[0] && '[' === $remain[1]) {
        $infreelinkp = TRUE;
        $delim = ']]';
      }
      else {
        $infreelinkp = FALSE;
        $delim = '[[';
      }

      // Break out of loop if cannot find anything in remaining text.
      $pos = strpos($remain, $delim);
      if (FALSE === $pos) {
        break;
      }

      // Get the next chunk of text until the position of the delimiter above,
      // and if in freelinking, start processing, otherwise set remaining text
      // to the next chunk from the beginning delimiter.
      $chunk_all = substr($remain, $start, $pos);
      if ($infreelinkp) {
        $chunk_stripped = substr($chunk_all, 2);

        // Find the indicator (plugin) from the first set of characters up until
        // the colon, or use the default plugin.
        $indicatorPosition = strpos($chunk_stripped, ':');
        if (FALSE === $indicatorPosition) {
          $indicator = $defaultplugin;
          $target = $chunk_stripped;
        }
        else {
          $indicator = substr($chunk_stripped, 0, $indicatorPosition);
          $target = substr($chunk_stripped, $indicatorPosition + 1);
        }

        // Load the current plugin from the indicator and available plugins.
        $current_plugin = $this->freelinkingManager->getPluginFromIndicator(
          $indicator,
          $this->extractAllowedPlugins($this->settings['plugins']),
          $this->settings
        );

        if (!$this->settings['global_options']['ignore_upi'] || $current_plugin) {
          // Lazy Builder callback and context must be scalar.
          if (!$current_plugin) {
            $link = $result->createPlaceholder('freelinking.manager:createErrorElement', [$indicator]);
          }
          else {
            // Serialize plugin settings as a string.
            $plugin_settings = self::extractPluginSettings($current_plugin->getPluginId(), $this->settings['plugins']);

            // Failover plugin settings need to be extracted here and passed in
            // to the placeholder as well.
            if ($current_plugin->getFailoverPluginId()) {
              $failover_settings = self::extractPluginSettings($current_plugin->getFailoverPluginId(), $this->settings['plugins']);
            }
            else {
              $failover_settings = [];
            }

            $link = $result->createPlaceholder(
              'freelinking.manager:createFreelinkElement',
              [
                $current_plugin->getPluginId(),
                $target,
                $indicator,
                $user_langcode,
                serialize($plugin_settings),
                serialize($failover_settings),
              ]
            );
          }

          if ($link) {
            $chunk_all = $link;
            $offset = 2;
          }
        }
        $remain = substr($remain, $pos + $offset);
      }
      else {
        $remain = substr($remain, $pos);
      }
      $newtext .= $chunk_all;
    }
    $newtext .= $remain;

    $result->setProcessedText($newtext);

    return $result;
  }

  /**
   * Extract plugin information from freelinking plugin settings.
   *
   * @param string $plugin_name
   *   The plugin ID.
   * @param array $plugins
   *   The plugin array with settings.
   *
   * @return array
   *   The plugin information.
   */
  public static function extractPluginSettings($plugin_name, array $plugins) {
    return array_reduce($plugins, function ($result, $info) use ($plugin_name) {
      if ($info['plugin'] === $plugin_name) {
        $result = $info;
      }
      return $result;
    }, []);
  }

  /**
   * Extract plugin names that are enabled from configuration.
   *
   * @param array $plugins
   *   The array of plugin information from the settings.
   *
   * @return array
   *   An indexed array of allowed plugin information.
   */
  protected function extractAllowedPlugins(array $plugins) {
    return array_reduce($plugins, function ($result, $info) {
      if ($info['enabled']) {
        $result[$info['plugin']] = $info;
      }
      return $result;
    }, []);
  }

}

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

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