View source
<?php
namespace Drupal\markdown\Plugin\Markdown;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Component\Utility\Html;
use Drupal\Core\Cache\RefinableCacheableDependencyTrait;
use Drupal\Core\Config\Schema\Mapping;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Plugin\PluginFormInterface;
use Drupal\filter\Entity\FilterFormat;
use Drupal\filter\Plugin\FilterInterface;
use Drupal\markdown\PluginManager\ParserManager;
use Drupal\markdown\Render\ParsedMarkdown;
use Drupal\markdown\Traits\EnabledPluginTrait;
use Drupal\markdown\Traits\FilterAwareTrait;
use Drupal\markdown\Traits\SettingsTrait;
use Drupal\markdown\Util\FilterAwareInterface;
use Drupal\markdown\Util\FilterFormatAwareInterface;
use Drupal\markdown\Util\FilterHtml;
use Drupal\markdown\Util\ParserAwareInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
abstract class BaseParser extends InstallablePluginBase implements FilterAwareInterface, ParserInterface, PluginFormInterface {
use EnabledPluginTrait;
use FilterAwareTrait;
use RefinableCacheableDependencyTrait;
use SettingsTrait;
protected $enabled = TRUE;
public static function validateSettings(array $settings, ExecutionContextInterface $context) {
try {
$object = $context
->getObject();
$parent = $object instanceof Mapping ? $object
->getParent() : NULL;
$parserId = $parent instanceof Mapping && ($id = $parent
->get('id')) ? $id
->getValue() : NULL;
$parserManager = ParserManager::create();
if (!$parserId || !$parserManager
->hasDefinition($parserId)) {
throw new \RuntimeException(sprintf('Unknown markdown parser: "%s"', $parserId));
}
$parser = $parserManager
->createInstance($parserId);
if (!$parser instanceof SettingsInterface) {
return;
}
$defaultSettings = $parser::defaultSettings($parser
->getPluginDefinition());
$unknownSettings = array_keys(array_diff_key($settings, $defaultSettings));
if ($unknownSettings) {
throw new \RuntimeException(sprintf('Unknown parser settings: %s', implode(', ', $unknownSettings)));
}
} catch (\RuntimeException $exception) {
$context
->addViolation($exception
->getMessage());
}
}
protected abstract function convertToHtml($markdown, LanguageInterface $language = NULL);
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
return $form;
}
public function getAllowedHtml() {
return $this
->getCustomAllowedHtml();
}
public function getAllowedHtmlPlugins() {
return $this
->config()
->get('render_strategy.plugins') ?: [];
}
public function getConfiguration() {
$configuration = parent::getConfiguration();
$configuration['render_strategy'] = [
'type' => $this
->getRenderStrategy(),
'custom_allowed_html' => $this
->getCustomAllowedHtml(),
'plugins' => $this
->getAllowedHtmlPlugins(),
];
ksort($configuration['render_strategy']['plugins']);
return $configuration;
}
protected function getConfigurationSortOrder() {
return [
'render_strategy' => -11,
] + parent::getConfigurationSortOrder();
}
protected function getContext(array $context = []) {
$parser = NULL;
if ($this instanceof ParserAwareInterface) {
$parser = $this
->getParser();
}
elseif ($this instanceof ParserInterface) {
$parser = $this;
}
$filter = NULL;
if ($this instanceof FilterAwareInterface) {
$filter = $this
->getFilter();
}
elseif ($parser instanceof FilterAwareInterface) {
$filter = $parser
->getFilter();
}
elseif ($this instanceof FilterInterface) {
$filter = $this;
}
$format = NULL;
if ($this instanceof FilterFormatAwareInterface) {
$format = $this
->getFilterFormat();
}
elseif ($parser instanceof FilterFormatAwareInterface) {
$format = $parser
->getFilterFormat();
}
elseif ($filter instanceof FilterFormatAwareInterface) {
$format = $filter
->getFilterFormat();
}
elseif ($this instanceof FilterFormat) {
$format = $this;
}
return [
'parser' => $parser,
'filter' => $filter,
'format' => $format,
] + $context;
}
public function getCustomAllowedHtml() {
return $this
->config()
->get('render_strategy.custom_allowed_html');
}
public function getRenderStrategy() {
return $this
->config()
->get('render_strategy.type') ?: static::FILTER_OUTPUT;
}
public function parse($markdown, LanguageInterface $language = NULL) {
$moduleHandler = \Drupal::moduleHandler();
$renderStrategy = $this
->getRenderStrategy();
if ($renderStrategy === static::ESCAPE_INPUT) {
$markdown = Html::escape($markdown);
}
elseif ($renderStrategy === static::STRIP_INPUT) {
$markdown = strip_tags($markdown);
}
$context = $this
->getContext([
'language' => $language,
]);
$moduleHandler
->alter('markdown', $markdown, $context);
$html = $this
->convertToHtml($markdown, $language);
$context['markdown'] = $markdown;
$moduleHandler
->alter('markdown_html', $html, $context);
if ($renderStrategy === static::FILTER_OUTPUT) {
$html = (string) FilterHtml::fromParser($this)
->process($html, $language ? $language
->getId() : NULL);
}
return ParsedMarkdown::create($markdown, $html, $language)
->addCacheableDependency($this);
}
protected function renderStrategyDisabledSetting(FormStateInterface $form_state) {
$markdownParents = $form_state
->get('markdownSubformParents');
$parents = array_merge($markdownParents, [
'render_strategy',
'type',
]);
$selector = ':input[name="' . array_shift($parents) . '[' . implode('][', $parents) . ']"]';
return new FormattableMarkup('@disabled@warning', [
'@disabled' => $form_state
->conditionalElement([
'#type' => 'container',
'#attributes' => [
'class' => [
'form-item--description',
'is-disabled',
],
],
[
'#markup' => $this
->moreInfo($this
->t('<strong>NOTE:</strong> This setting is disabled when a render strategy is being used.'), RenderStrategyInterface::DOCUMENTATION_URL),
],
], 'visible', $selector, [
'!value' => static::NONE,
]),
'@warning' => $form_state
->conditionalElement([
'#type' => 'container',
'#theme_wrappers' => [
'container__markdown_disabled_setting__render_strategy__warning',
],
'#attributes' => [
'class' => [
'form-item__error-message',
'form-item--error-message',
],
],
[
'#markup' => $this
->moreInfo($this
->t('<strong>WARNING:</strong> This setting does not guarantee protection against malicious JavaScript from being injected. It is recommended to use the "Filter Output" render strategy.'), RenderStrategyInterface::DOCUMENTATION_URL),
],
], 'visible', $selector, [
'value' => static::NONE,
]),
]);
}
protected function renderStrategyDisabledSettingState(FormStateInterface $form_state, array &$element, $state = 'disabled', array $conditions = [
'!value' => self::NONE,
]) {
$markdownParents = $form_state
->get('markdownSubformParents');
$parents = array_merge($markdownParents, [
'render_strategy',
'type',
]);
$selector = ':input[name="' . array_shift($parents) . '[' . implode('][', $parents) . ']"]';
$states = (array) $state;
foreach ($states as $state) {
$form_state
->addElementState($element, $state, $selector, $conditions);
}
if (!isset($element['#description'])) {
$element['#description'] = $this
->renderStrategyDisabledSetting($form_state);
}
else {
$element['#description'] = new FormattableMarkup('@description @renderStrategyDisabledSetting', [
'@description' => $element['#description'],
'@renderStrategyDisabledSetting' => $this
->renderStrategyDisabledSetting($form_state),
]);
}
}
}