View source
<?php
namespace Drupal\markdown\Plugin\Markdown\Extension;
use Drupal\Component\Utility\Html;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\markdown\Plugin\Filter\MarkdownFilterInterface;
use Drupal\markdown\Plugin\Markdown\MarkdownGuidelinesAlterInterface;
use League\CommonMark\Inline\Element\Link;
use League\CommonMark\Inline\Parser\InlineParserInterface;
use League\CommonMark\InlineParserContext;
class HashAutolinker extends CommonMarkExtension implements InlineParserInterface, MarkdownGuidelinesAlterInterface {
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',
],
],
];
}
}
public function defaultSettings() {
return [
'type' => 'node',
'node_title' => TRUE,
'url' => 'https://www.drupal.org/node/[text]',
'url_title' => TRUE,
];
}
public function getCharacters() : array {
return [
'#',
];
}
public function parse(InlineParserContext $inline_context) : bool {
$cursor = $inline_context
->getCursor();
$previous_char = $cursor
->peek(-1);
if ($previous_char !== NULL && $previous_char !== ' ' && $previous_char !== '[') {
return FALSE;
}
$previous_state = $cursor
->saveState();
$cursor
->advance();
$text = $cursor
->match('/^[^\\s\\]]+/');
$url = FALSE;
$title = FALSE;
$type = $this
->getSetting('type');
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;
}
if (empty($text) || empty($url)) {
$cursor
->restoreState($previous_state);
return FALSE;
}
$inline_context
->getContainer()
->appendChild(new Link($url, $text, $title));
return TRUE;
}
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;
}
protected function currentRouteEntity() {
$route_match = \Drupal::routeMatch();
foreach ($route_match
->getParameters()
->all() as $item) {
if ($item instanceof EntityInterface) {
return $item;
}
}
}
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;
}
}