function 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()

1 call to Internal::generateACFSettings()
Internal::getConfig in core/modules/ckeditor/src/Plugin/CKEditorPlugin/Internal.php
Returns the additions to CKEDITOR.config for a specific CKEditor instance.

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,
    ];
  }
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.