View source
<?php
declare (strict_types=1);
namespace Drupal\ckeditor5\Plugin\Validation\Constraint;
use Drupal\ckeditor5\HTMLRestrictions;
use Drupal\ckeditor5\Plugin\CKEditor5Plugin\Style;
use Drupal\ckeditor5\Plugin\CKEditor5PluginDefinition;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
class StyleSensibleElementConstraintValidator extends ConstraintValidator implements ContainerInjectionInterface {
use PrecedingConstraintAwareValidatorTrait;
use PluginManagerDependentValidatorTrait;
use TextEditorObjectDependentValidatorTrait;
public function validate($element, Constraint $constraint) {
if (!$constraint instanceof StyleSensibleElementConstraint) {
throw new UnexpectedTypeException($constraint, StyleSensibleElementConstraint::class);
}
if ($this
->hasViolationsForPrecedingConstraints($constraint)) {
return;
}
$text_editor = $this
->createTextEditorObjectFromContext();
$style_element = HTMLRestrictions::fromString($element);
assert(count($style_element
->getAllowedElements()) === 1);
[
$tag,
$classes,
] = Style::getTagAndClasses($style_element);
$superset = HTMLRestrictions::fromString('<$any-html5-element class>');
$supported_range = $superset
->merge($style_element
->extractPlainTagsSubset());
if (!$style_element
->diff($supported_range)
->allowsNothing()) {
$this->context
->buildViolation($constraint->nonHtml5TagMessage)
->setParameter('@tag', sprintf("<%s>", $tag))
->addViolation();
return;
}
$other_enabled_plugins = $this
->getOtherEnabledPlugins($text_editor, 'ckeditor5_style');
$enableable_disabled_plugins = $this
->getEnableableDisabledPlugins($text_editor);
$other_enabled_plugin_elements = new HTMLRestrictions($this->pluginManager
->getProvidedElements(array_keys($other_enabled_plugins), $text_editor, FALSE));
$disabled_plugin_elements = new HTMLRestrictions($this->pluginManager
->getProvidedElements(array_keys($enableable_disabled_plugins), $text_editor, FALSE));
if (self::intersectionWithClasses($style_element, $other_enabled_plugin_elements)) {
$this->context
->buildViolation($constraint->conflictingEnabledPluginMessage)
->setParameter('@tag', sprintf("<%s>", $tag))
->setParameter('@classes', implode(", ", $classes))
->setParameter('%plugin', $this
->findStyleConflictingPluginLabel($style_element))
->addViolation();
}
elseif (self::intersectionWithClasses($style_element, $disabled_plugin_elements)) {
$this->context
->buildViolation($constraint->conflictingDisabledPluginMessage)
->setParameter('@tag', sprintf("<%s>", $tag))
->setParameter('@classes', implode(", ", $classes))
->setParameter('%plugin', $this
->findStyleConflictingPluginLabel($style_element))
->addViolation();
}
}
private static function intersectionWithClasses(HTMLRestrictions $a, HTMLRestrictions $b) : bool {
$tags_from_a = array_diff(array_keys($a
->getConcreteSubset()
->getAllowedElements()), [
'*',
]);
$tags_from_b = array_diff(array_keys($b
->getConcreteSubset()
->getAllowedElements()), [
'*',
]);
$a = $a
->merge(new HTMLRestrictions(array_fill_keys($tags_from_b, FALSE)));
$b = $b
->merge(new HTMLRestrictions(array_fill_keys($tags_from_a, FALSE)));
$b_without_class_wildcard = $b
->getAllowedElements();
foreach ($b_without_class_wildcard as $allowedElement => $config) {
if (!empty($config['class']) && $config['class'] === TRUE) {
unset($b_without_class_wildcard[$allowedElement]['class']);
}
if (empty($b_without_class_wildcard[$allowedElement])) {
unset($b_without_class_wildcard[$allowedElement]);
}
}
$intersection = $a
->intersect(new HTMLRestrictions($b_without_class_wildcard));
$intersection_as_ghs_config = $intersection
->toGeneralHtmlSupportConfig();
$ghs_config_classes = array_column($intersection_as_ghs_config, 'classes');
return !empty($ghs_config_classes);
}
private function findStyleConflictingPluginLabel(HTMLRestrictions $needle) : TranslatableMarkup {
foreach ($this->pluginManager
->getDefinitions() as $id => $definition) {
if ($id === 'ckeditor5_style') {
continue;
}
assert($definition instanceof CKEditor5PluginDefinition);
if (!$definition
->hasElements()) {
continue;
}
$haystack = HTMLRestrictions::fromString(implode($definition
->getElements()));
if ($id === 'ckeditor5_sourceEditing') {
$text_editor = $this
->createTextEditorObjectFromContext();
$editor_plugins = $text_editor
->getSettings()['plugins'];
if (!empty($editor_plugins['ckeditor5_sourceEditing'])) {
$source_tags = $editor_plugins['ckeditor5_sourceEditing']['allowed_tags'];
$haystack = HTMLRestrictions::fromString(implode($source_tags));
}
}
if (self::intersectionWithClasses($needle, $haystack)) {
return $definition
->label();
}
}
throw new \OutOfBoundsException();
}
}