You are here

class HashAutolinker in Markdown 3.0.x

Plugin annotation


@MarkdownExtension(
  id = "hash_autolinker",
  label = @Translation("# Autolinker"),
  installed = TRUE,
  description = @Translation("Automatically link commonly used references that come after a hash character (#) without having to use the link syntax."),
  parsers = {"thephpleague/commonmark", "thephpleague/commonmark-gfm"},
)

Hierarchy

Expanded class hierarchy of HashAutolinker

File

src/Plugin/Markdown/Extension/HashAutolinker.php, line 23

Namespace

Drupal\markdown\Plugin\Markdown\Extension
View source
class HashAutolinker extends CommonMarkExtension implements InlineParserInterface, MarkdownGuidelinesAlterInterface {

  /**
   * {@inheritdoc}
   */
  public function alterGuidelines(array &$guides = []) {
    if ($this
      ->getSetting('type') === 'node') {
      $description = [
        t('Text that starts with hash symbol (#) followed by numbers will be automatically be linked to a node on this site.'),
      ];
      if ($this
        ->getSetting('node_title')) {
        $description[] = t('The node title will be used in place the text.');
      }
      $description[] = t('If the node does not exist, it will not automatically link.');
      $guides['links']['items'][] = [
        'title' => t('# Autolinker'),
        'description' => $description,
      ];
    }
    elseif ($this
      ->getSetting('type') === 'url') {
      $description = [
        t('Text that starts with a hash symbol (#) followed by any character other than a space will automatically be linked to the following URL: <code>@url</code>', [
          '@url' => $this
            ->getSetting('url'),
        ]),
      ];
      if ($this
        ->getSetting('url_title')) {
        $description[] = t('The URL title will be used in place of the original text.');
      }
      $guides['links']['items'][] = [
        'title' => t('@ Autolinker'),
        'description' => $description,
        'tags' => [
          'a' => [
            '#3060',
            '#2562913',
            '#259843',
          ],
        ],
      ];
    }
  }

  /**
   * {@inheritdoc}
   */
  public function defaultSettings() {
    return [
      'type' => 'node',
      'node_title' => TRUE,
      'url' => 'https://www.drupal.org/node/[text]',
      'url_title' => TRUE,
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function getCharacters() : array {
    return [
      '#',
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function parse(InlineParserContext $inline_context) : bool {
    $cursor = $inline_context
      ->getCursor();

    // The # symbol must not have any other characters immediately prior.
    $previous_char = $cursor
      ->peek(-1);
    if ($previous_char !== NULL && $previous_char !== ' ' && $previous_char !== '[') {

      // peek() doesn't modify the cursor, so no need to restore state first.
      return FALSE;
    }

    // Save the cursor state in case we need to rewind and bail.
    $previous_state = $cursor
      ->saveState();

    // Advance past the # symbol to keep parsing simpler.
    $cursor
      ->advance();

    // Parse the handle.
    $text = $cursor
      ->match('/^[^\\s\\]]+/');
    $url = FALSE;
    $title = FALSE;
    $type = $this
      ->getSetting('type');

    // @todo Make entity type abstract and comment aware.
    if ($type === 'node' && is_numeric($text) && ($node = \Drupal::entityTypeManager()
      ->getStorage('node')
      ->load($text))) {
      $url = $node
        ->toUrl('canonical', [
        'absolute' => TRUE,
      ])
        ->toString();
      if ($this
        ->getSetting('node_title') && ($title = $node
        ->label())) {
        $text = $title;
      }
      else {
        $text = "#{$text}";
      }
    }
    elseif ($type === 'url' && ($url = $this
      ->getSetting('url')) && strpos($url, '[text]') !== FALSE) {
      $url = str_replace('[text]', $text, $url);
      if ($this
        ->getSetting('url_title') && ($title = $this
        ->getUrlTitle($url))) {
        $text = $title;
        $title = FALSE;
      }
    }
    else {
      $text = FALSE;
    }

    // Regex failed to match; this isn't a valid @ handle.
    if (empty($text) || empty($url)) {
      $cursor
        ->restoreState($previous_state);
      return FALSE;
    }
    $inline_context
      ->getContainer()
      ->appendChild(new Link($url, $text, $title));
    return TRUE;
  }

  /**
   * Retrieves a URL page title.
   *
   * @param string $url
   *   The URL to retrieve the title from.
   *
   * @return string|false
   *   The URL title or FALSE if it could not be retrieved.
   */
  protected function getUrlTitle($url) {
    $response = \Drupal::httpClient()
      ->get($url);
    if ($response
      ->getStatusCode() >= 200 && $response
      ->getStatusCode() < 400) {
      $dom = new \DOMDocument();
      @$dom
        ->loadHTML($response
        ->getBody()
        ->getContents());
      if (($title = $dom
        ->getElementsByTagName('title')) && $title->length) {
        return Html::escape(trim(preg_replace('/\\s+/', ' ', $title
          ->item(0)->textContent)));
      }
    }
    return FALSE;
  }

  /**
   * Retrieves an Entity object for the current route.
   *
   * @return \Drupal\Core\Entity\EntityInterface|null
   *   An Entity object or NULL if none could be found.
   */
  protected function currentRouteEntity() {
    $route_match = \Drupal::routeMatch();
    foreach ($route_match
      ->getParameters()
      ->all() as $item) {
      if ($item instanceof EntityInterface) {
        return $item;
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function settingsForm(array $element, FormStateInterface $formState, MarkdownFilterInterface $filter) {
    $element = parent::settingsForm($element, $formState, $filter);
    $selector = $this
      ->getSatesSelector($this
      ->getElementParents($element, [
      $this
        ->getPluginId(),
    ]), 'type');
    $element['type'] = [
      '#type' => 'select',
      '#title' => $this
        ->t('Map text to'),
      '#default_value' => $this
        ->getSetting('type'),
      '#options' => [
        'node' => $this
          ->t('Node'),
        'url' => $this
          ->t('URL'),
      ],
    ];
    $element['node_title'] = [
      '#type' => 'checkbox',
      '#title' => $this
        ->t('Replace text with title of node'),
      '#description' => $this
        ->t('If enabled, it will replace the matched text with the title of the node.'),
      '#default_value' => $this
        ->getSetting('node_title'),
      '#states' => [
        'visible' => [
          $selector => [
            'value' => 'node',
          ],
        ],
      ],
    ];
    $element['url'] = [
      '#type' => 'textfield',
      '#title' => $this
        ->t('URL'),
      '#description' => $this
        ->t('A URL to format text with. Use the token "[text]" where it is needed. If you need to include the #, use the URL encoded equivalent: <code>%23</code>. Example: <code>https://twitter.com/search?q=%23[text]</code>.'),
      '#default_value' => $this
        ->getSetting('url'),
      '#states' => [
        'visible' => [
          $selector => [
            'value' => 'url',
          ],
        ],
      ],
    ];
    $element['url_title'] = [
      '#type' => 'checkbox',
      '#title' => $this
        ->t('Replace text with title of URL'),
      '#description' => $this
        ->t('If enabled, it will replace the matched text with the title of the URL.'),
      '#default_value' => $this
        ->getSetting('url_title'),
      '#states' => [
        'visible' => [
          $selector => [
            'value' => 'url',
          ],
        ],
      ],
    ];
    return $element;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
BaseExtension::baseConfigurationDefaults protected function Returns generic default configuration for markdown extension plugins.
BaseExtension::calculateDependencies public function
BaseExtension::defaultConfiguration public function
BaseExtension::getConfiguration public function
BaseExtension::getDescription public function Retrieves the description of the plugin, if set. Overrides MarkdownInstallablePluginInterface::getDescription
BaseExtension::getLabel public function Displays the human-readable label of the plugin. Overrides MarkdownInstallablePluginInterface::getLabel
BaseExtension::getSetting public function Retrieves a setting. Overrides MarkdownExtensionInterface::getSetting
BaseExtension::getSettings public function Retrieves the current settings. Overrides MarkdownExtensionInterface::getSettings
BaseExtension::getUrl public function Retrieves the URL of the plugin, if set. Overrides MarkdownInstallablePluginInterface::getUrl
BaseExtension::getVersion public function The current version of the parser. Overrides MarkdownInstallablePluginInterface::getVersion
BaseExtension::installed public static function Indicates whether the parser is installed. Overrides MarkdownInstallablePluginInterface::installed
BaseExtension::isEnabled public function Indicates whether the extension is being used. Overrides MarkdownExtensionInterface::isEnabled
BaseExtension::isInstalled public function Indicates whether the parser is installed. Overrides MarkdownInstallablePluginInterface::isInstalled
BaseExtension::label public function
BaseExtension::setConfiguration public function
BaseExtension::setSetting public function Sets a specific setting. Overrides MarkdownExtensionInterface::setSetting
BaseExtension::setSettings public function Provides settings to an extension. Overrides MarkdownExtensionInterface::setSettings
BaseExtension::version public static function Retrieves the version of the installed parser. Overrides MarkdownInstallablePluginInterface::version
BaseExtension::__construct public function Constructs a \Drupal\Component\Plugin\PluginBase object. Overrides PluginBase::__construct
CommonMarkExtension::getName public function Retrieves the name of the extension. Overrides CommonMarkExtensionInterface::getName
DependencySerializationTrait::$_entityStorages protected property
DependencySerializationTrait::$_serviceIds protected property
DependencySerializationTrait::__sleep public function 2
DependencySerializationTrait::__wakeup public function 2
HashAutolinker::alterGuidelines public function Alters existing guides on how to use the Markdown Parser. Overrides MarkdownGuidelinesAlterInterface::alterGuidelines
HashAutolinker::currentRouteEntity protected function Retrieves an Entity object for the current route.
HashAutolinker::defaultSettings public function Retrieves the default settings. Overrides BaseExtension::defaultSettings
HashAutolinker::getCharacters public function
HashAutolinker::getUrlTitle protected function Retrieves a URL page title.
HashAutolinker::parse public function
HashAutolinker::settingsForm public function Returns the configuration form elements specific to this plugin. Overrides BaseExtension::settingsForm
MarkdownStatesTrait::getElementParents protected static function Retrieves the ancestry of the extension in a form/render array.
MarkdownStatesTrait::getSatesSelector protected static function Retrieves a states selector to use based on the form/render array parents.
MessengerTrait::$messenger protected property The messenger. 27
MessengerTrait::messenger public function Gets the messenger. 27
MessengerTrait::setMessenger public function Sets the messenger.
PluginBase::$configuration protected property Configuration information passed into the plugin. 1
PluginBase::$pluginDefinition protected property The plugin implementation definition. 1
PluginBase::$pluginId protected property The plugin_id.
PluginBase::DERIVATIVE_SEPARATOR constant A string which is used to separate base plugin IDs from the derivative ID.
PluginBase::getBaseId public function Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface::getBaseId
PluginBase::getDerivativeId public function Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface::getDerivativeId
PluginBase::getPluginDefinition public function Gets the definition of the plugin implementation. Overrides PluginInspectionInterface::getPluginDefinition 2
PluginBase::getPluginId public function Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface::getPluginId
PluginBase::isConfigurable public function Determines if the plugin is configurable.
StringTranslationTrait::$stringTranslation protected property The string translation service. 4
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.