You are here

protected function Internal::generateACFSettings in Drupal 10

Same name and namespace in other branches
  1. 8 core/modules/ckeditor/src/Plugin/CKEditorPlugin/Internal.php \Drupal\ckeditor\Plugin\CKEditorPlugin\Internal::generateACFSettings()
  2. 9 core/modules/ckeditor/src/Plugin/CKEditorPlugin/Internal.php \Drupal\ckeditor\Plugin\CKEditorPlugin\Internal::generateACFSettings()

Builds the ACF part of the CKEditor JS settings.

This ensures that CKEditor obeys the HTML restrictions defined by Drupal's filter system, by enabling CKEditor's Advanced Content Filter (ACF) functionality: http://ckeditor.com/blog/CKEditor-4.1-RC-Released.

Parameters

\Drupal\editor\Entity\Editor $editor: A configured text editor object.

Return value

array An array with two values:

  • the first value is the "allowedContent" setting: a well-formatted array or TRUE. The latter indicates that anything is allowed.
  • the second value is the "disallowedContent" setting: a well-formatted array or FALSE. The latter indicates that nothing is disallowed.

See also

getConfig()

File

core/modules/ckeditor/src/Plugin/CKEditorPlugin/Internal.php, line 409

Class

Internal
Defines the "internal" plugin (i.e. core plugins part of our CKEditor build).

Namespace

Drupal\ckeditor\Plugin\CKEditorPlugin

Code

protected function generateACFSettings(Editor $editor) {

  // When no text format is associated yet, assume nothing is disallowed, so
  // set allowedContent to true.
  if (!$editor
    ->hasAssociatedFilterFormat()) {
    return TRUE;
  }
  $format = $editor
    ->getFilterFormat();
  $filter_types = $format
    ->getFilterTypes();

  // When nothing is disallowed, set allowedContent to true.
  if (!in_array(FilterInterface::TYPE_HTML_RESTRICTOR, $filter_types)) {
    return [
      TRUE,
      FALSE,
    ];
  }
  else {
    $get_attribute_values = function ($attribute_values, $allowed_values) {
      $values = array_keys(array_filter($attribute_values, function ($value) use ($allowed_values) {
        if ($allowed_values) {
          return $value !== FALSE;
        }
        else {
          return $value === FALSE;
        }
      }));
      if (count($values)) {
        return implode(',', $values);
      }
      else {
        return NULL;
      }
    };
    $html_restrictions = $format
      ->getHtmlRestrictions();

    // When all HTML is allowed, also set allowedContent to true and
    // disallowedContent to false.
    if ($html_restrictions === FALSE) {
      return [
        TRUE,
        FALSE,
      ];
    }
    $allowed = [];
    $disallowed = [];
    if (isset($html_restrictions['forbidden_tags'])) {
      foreach ($html_restrictions['forbidden_tags'] as $tag) {
        $disallowed[$tag] = TRUE;
      }
    }
    foreach ($html_restrictions['allowed'] as $tag => $attributes) {

      // Tell CKEditor the tag is allowed, but no attributes.
      if ($attributes === FALSE) {
        $allowed[$tag] = [
          'attributes' => FALSE,
          'styles' => FALSE,
          'classes' => FALSE,
        ];
      }
      elseif ($attributes === TRUE) {
        $allowed[$tag] = [
          'attributes' => TRUE,
          'styles' => TRUE,
          'classes' => TRUE,
        ];

        // We've just marked that any value for the "style" and "class"
        // attributes is allowed. However, that may not be the case: the "*"
        // tag may still apply restrictions.
        // Since CKEditor's ACF follows the following principle:
        // - Once validated, an element or its property cannot be
        //   invalidated by another rule.
        // That means that the most permissive setting wins. Which means that
        // it will still be allowed by CKEditor, for instance, to define any
        // style, no matter what the "*" tag's restrictions may be. If there
        // is a setting for either the "style" or "class" attribute, it cannot
        // possibly be more permissive than what was set above. Hence, inherit
        // from the "*" tag where possible.
        if (isset($html_restrictions['allowed']['*'])) {
          $wildcard = $html_restrictions['allowed']['*'];
          if (isset($wildcard['style'])) {
            if (!is_array($wildcard['style'])) {
              $allowed[$tag]['styles'] = $wildcard['style'];
            }
            else {
              $allowed_styles = $get_attribute_values($wildcard['style'], TRUE);
              if (isset($allowed_styles)) {
                $allowed[$tag]['styles'] = $allowed_styles;
              }
              else {
                unset($allowed[$tag]['styles']);
              }
            }
          }
          if (isset($wildcard['class'])) {
            if (!is_array($wildcard['class'])) {
              $allowed[$tag]['classes'] = $wildcard['class'];
            }
            else {
              $allowed_classes = $get_attribute_values($wildcard['class'], TRUE);
              if (isset($allowed_classes)) {
                $allowed[$tag]['classes'] = $allowed_classes;
              }
              else {
                unset($allowed[$tag]['classes']);
              }
            }
          }
        }
      }
      elseif (is_array($attributes)) {

        // Set defaults (these will be overridden below if more specific
        // values are present).
        $allowed[$tag] = [
          'attributes' => FALSE,
          'styles' => FALSE,
          'classes' => FALSE,
        ];

        // Configure allowed attributes, allowed "style" attribute values and
        // allowed "class" attribute values.
        // CKEditor only allows specific values for the "class" and "style"
        // attributes; so ignore restrictions on other attributes, which
        // Drupal filters may provide.
        // NOTE: A Drupal contrib module can subclass this class, override the
        // getConfig() method, and override the JavaScript at
        // Drupal.editors.ckeditor to somehow make validation of values for
        // attributes other than "class" and "style" work.
        $allowed_attributes = array_filter($attributes, function ($value) {
          return $value !== FALSE;
        });
        if (count($allowed_attributes)) {
          $allowed[$tag]['attributes'] = implode(',', array_keys($allowed_attributes));
        }
        if (isset($allowed_attributes['style'])) {
          if (is_bool($allowed_attributes['style'])) {
            $allowed[$tag]['styles'] = $allowed_attributes['style'];
          }
          elseif (is_array($allowed_attributes['style'])) {
            $allowed_classes = $get_attribute_values($allowed_attributes['style'], TRUE);
            if (isset($allowed_classes)) {
              $allowed[$tag]['styles'] = $allowed_classes;
            }
          }
        }
        if (isset($allowed_attributes['class'])) {
          if (is_bool($allowed_attributes['class'])) {
            $allowed[$tag]['classes'] = $allowed_attributes['class'];
          }
          elseif (is_array($allowed_attributes['class'])) {
            $allowed_classes = $get_attribute_values($allowed_attributes['class'], TRUE);
            if (isset($allowed_classes)) {
              $allowed[$tag]['classes'] = $allowed_classes;
            }
          }
        }

        // Handle disallowed attributes analogously. However, to handle *dis-
        // allowed* attribute values, we must look at *allowed* attributes'
        // disallowed attribute values! After all, a disallowed attribute
        // implies that all of its possible attribute values are disallowed,
        // thus we must look at the disallowed attribute values on allowed
        // attributes.
        $disallowed_attributes = array_filter($attributes, function ($value) {
          return $value === FALSE;
        });
        if (count($disallowed_attributes)) {

          // No need to blacklist the 'class' or 'style' attributes; CKEditor
          // handles them separately (if no specific class or style attribute
          // values are allowed, then those attributes are disallowed).
          if (isset($disallowed_attributes['class'])) {
            unset($disallowed_attributes['class']);
          }
          if (isset($disallowed_attributes['style'])) {
            unset($disallowed_attributes['style']);
          }
          $disallowed[$tag]['attributes'] = implode(',', array_keys($disallowed_attributes));
        }
        if (isset($allowed_attributes['style']) && is_array($allowed_attributes['style'])) {
          $disallowed_styles = $get_attribute_values($allowed_attributes['style'], FALSE);
          if (isset($disallowed_styles)) {
            $disallowed[$tag]['styles'] = $disallowed_styles;
          }
        }
        if (isset($allowed_attributes['class']) && is_array($allowed_attributes['class'])) {
          $disallowed_classes = $get_attribute_values($allowed_attributes['class'], FALSE);
          if (isset($disallowed_classes)) {
            $disallowed[$tag]['classes'] = $disallowed_classes;
          }
        }
      }
    }
    ksort($allowed);
    ksort($disallowed);
    return [
      $allowed,
      $disallowed,
    ];
  }
}