Commit b8c40441 authored by catch's avatar catch
Browse files

Issue #3231336 by Wim Leers, lauriii, bnjmnm: Simplify HtmlRestrictions and...

Issue #3231336 by Wim Leers, lauriii, bnjmnm: Simplify HtmlRestrictions and FundamentalCompatibilityConstraintValidator now that "forbidden tags" are deprecated
parent 9d3ad415
Loading
Loading
Loading
Loading
+5 −17
Original line number Diff line number Diff line
@@ -38,9 +38,6 @@
 *
 * @see ::WILDCARD_ELEMENT_METHODS
 *
 * NOTE: Currently only supports the 'allowed' portion.
 * @todo Add support for "forbidden" tags in https://www.drupal.org/project/drupal/issues/3231336
 *
 * @internal
 */
final class HTMLRestrictions {
@@ -308,7 +305,6 @@ public static function fromTextFormat(FilterFormatInterface $text_format): HTMLR
   * @return \Drupal\ckeditor5\HTMLRestrictions
   */
  private static function unrestricted(): self {
    // @todo Refine in https://www.drupal.org/project/drupal/issues/3231336, including adding support for all operations.
    $restrictions = HTMLRestrictions::emptySet();
    $restrictions->unrestricted = TRUE;
    return $restrictions;
@@ -337,28 +333,20 @@ private static function fromObjectWithHtmlRestrictions(object $object): HTMLRest
      throw new \InvalidArgumentException();
    }

    if ($object->getHtmlRestrictions() === FALSE) {
      // @todo Refine in https://www.drupal.org/project/drupal/issues/3231336
      return self::unrestricted();
    }

    $restrictions = $object->getHTMLRestrictions();
    if (!isset($restrictions['allowed'])) {
      // @todo Handle HTML restrictor filters that only set forbidden_tags
      //   https://www.drupal.org/project/ckeditor5/issues/3231336.
      throw new \DomainException('text formats with only filters that forbid tags rather than allowing tags are not yet supported.');
    if ($restrictions === FALSE || $restrictions === []) {
      return self::unrestricted();
    }

    // When allowing all tags on an attribute, transform FilterHtml output from
    // ['tag' => ['*'=> TRUE]] to ['tag' => TRUE]
    foreach ($restrictions['allowed'] as $element => $attributes) {
    $allowed = $restrictions['allowed'];
    foreach ($allowed as $element => $attributes) {
      if (is_array($attributes) && isset($attributes['*']) && $attributes['*'] === TRUE) {
        $restrictions['allowed'][$element] = TRUE;
        $allowed[$element] = TRUE;
      }
    }

    $allowed = $restrictions['allowed'];

    return new self($allowed);
  }

+0 −7
Original line number Diff line number Diff line
@@ -25,13 +25,6 @@ class FundamentalCompatibilityConstraint extends Constraint {
   */
  public $noMarkupFiltersMessage = 'CKEditor 5 only works with HTML-based text formats. The "%filter_label" (%filter_plugin_id) filter implies this text format is not HTML anymore.';

  /**
   * The violation message when fundamental HTML elements are forbidden.
   *
   * @var string
   */
  public $forbiddenElementsMessage = 'CKEditor 5 needs at least the <p> and <br> tags to be allowed to be able to function. They are forbidden by the "%filter_label" (%filter_plugin_id) filter.';

  /**
   * The violation message when fundamental HTML elements are not allowed.
   *
+5 −60
Original line number Diff line number Diff line
@@ -119,27 +119,13 @@ private function checkNoMarkupFilters(FilterFormatInterface $text_format, Fundam
   *   The constraint to validate.
   */
  private function checkHtmlRestrictionsAreCompatible(FilterFormatInterface $text_format, FundamentalCompatibilityConstraint $constraint): void {
    $fundamental = new HTMLRestrictions($this->pluginManager->getProvidedElements(self::FUNDAMENTAL_CKEDITOR5_PLUGINS));

    // @todo Remove in favor of HTMLRestrictions::diff() in https://www.drupal.org/project/drupal/issues/3231336
    $html_restrictions = $text_format->getHtmlRestrictions();
    $minimum_tags = array_keys($fundamental->getAllowedElements());
    $forbidden_minimum_tags = isset($html_restrictions['forbidden_tags'])
      ? array_diff($minimum_tags, $html_restrictions['forbidden_tags'])
      : [];
    if (!empty($forbidden_minimum_tags)) {
      $offending_filter = static::findHtmlRestrictorFilterForbiddingTags($text_format, $minimum_tags);
      $this->context->buildViolation($constraint->forbiddenElementsMessage)
        ->setParameter('%filter_label', (string) $offending_filter->getLabel())
        ->setParameter('%filter_plugin_id', $offending_filter->getPluginId())
        ->addViolation();
    }

    // @todo Remove early return in https://www.drupal.org/project/drupal/issues/3231336
    if (!isset($html_restrictions['allowed'])) {
    $html_restrictions = HTMLRestrictions::fromTextFormat($text_format);
    if ($html_restrictions->isUnrestricted()) {
      return;
    }
    if (!$fundamental->diff(HTMLRestrictions::fromTextFormat($text_format))->allowsNothing()) {

    $fundamental = new HTMLRestrictions($this->pluginManager->getProvidedElements(self::FUNDAMENTAL_CKEDITOR5_PLUGINS));
    if (!$fundamental->diff($html_restrictions)->allowsNothing()) {
      $offending_filter = static::findHtmlRestrictorFilterNotAllowingTags($text_format, $fundamental);
      $this->context->buildViolation($constraint->nonAllowedElementsMessage)
        ->setParameter('%filter_label', (string) $offending_filter->getLabel())
@@ -287,47 +273,6 @@ private static function getFiltersInFormatOfType(FilterFormatInterface $text_for
    }
  }

  /**
   * Analyzes a text format to find the filter not allowing required tags.
   *
   * @param \Drupal\filter\FilterFormatInterface $text_format
   *   A text format whose filters to check for compatibility.
   * @param string[] $required_tags
   *   A list of HTML tags that are required.
   *
   * @return \Drupal\filter\Plugin\FilterInterface
   *   The filter plugin instance not allowing the required tags.
   *
   * @throws \InvalidArgumentException
   */
  private static function findHtmlRestrictorFilterForbiddingTags(FilterFormatInterface $text_format, array $required_tags): FilterInterface {
    // Get HTML restrictor filters that actually restrict HTML.
    $filters = static::getFiltersInFormatOfType(
      $text_format,
      FilterInterface::TYPE_HTML_RESTRICTOR,
      function (FilterInterface $filter) {
        return $filter->getHTMLRestrictions() !== FALSE;
      }
    );

    foreach ($filters as $filter) {
      $restrictions = $filter->getHTMLRestrictions();

      // @todo Fix
      //   \Drupal\filter_test\Plugin\Filter\FilterTestRestrictTagsAndAttributes::getHTMLRestrictions(),
      //   whose computed value for forbidden_tags does not comply with the API
      //   https://www.drupal.org/project/drupal/issues/3231331.
      if (array_keys($restrictions['forbidden_tags']) != range(0, count($restrictions['forbidden_tags']))) {
        $restrictions['forbidden_tags'] = array_keys($restrictions['forbidden_tags']);
      }
      if (isset($restrictions['forbidden_tags']) && !empty(array_intersect($required_tags, $restrictions['forbidden_tags']))) {
        return $filter;
      }
    }

    throw new \InvalidArgumentException('This text format does not have a "tags forbidden" restriction that includes the required tags.');
  }

  /**
   * Analyzes a text format to find the filter not allowing required tags.
   *