You are here

function insert_allowed_html_validate in Insert 8

Additional validation for the filter format edit form. This function is supposed to alter the allowed HTML filter tags and attributes settings as to what is required for the Insert module to work properly. To prevent confusion, this should be done minimally invasive. The tag an attribute detection logic is copied over from \Drupal\filter\Plugin\Filter\FilterHtml. A cleaner, though rather less usable, method would be an individual Filter extending FilterHtml overwriting FilterHtml::getHtmlRestrictions with adding necessary tags and attributes to $restrictions['allowed'].

Parameters

array $element:

\Drupal\Core\Form\FormState $form_state:

See also

\Drupal\filter\Plugin\Filter\FilterHtml::prepareAttributeValues

1 string reference to 'insert_allowed_html_validate'
insert_form_alter in ./insert.module
Implements hook_form_alter().

File

./insert.module, line 70

Code

function insert_allowed_html_validate($element, FormState &$form_state) {
  $value = $element['#value'];
  $tags = [
    'img' => null,
    'a' => null,
    'span' => null,
  ];
  $attributes = [
    'img' => [
      'class' => null,
      'src' => null,
      'width' => null,
      'height' => null,
      'alt' => null,
      'title' => null,
    ],
    'a' => [
      'class' => null,
      'title' => null,
      'type' => null,
    ],
    'span' => [
      'class' => null,
    ],
  ];

  // see \Drupal\filter\Plugin\Filter\FilterHtml::prepareAttributeValues
  $html = str_replace('>', ' />', $value);
  $star_protector = '__zqh6vxfbk3cg__';
  $html = str_replace('*', $star_protector, $html);
  $body_child_nodes = Html::load($html)
    ->getElementsByTagName('body')
    ->item(0)->childNodes;

  // Detect which tags an attributes are allowed already.
  foreach ($body_child_nodes as $node) {
    if ($node->nodeType !== XML_ELEMENT_NODE) {
      continue;
    }
    $tag = $node->tagName;
    if (array_key_exists($tag, $tags)) {
      $tags[$tag] = TRUE;
    }
    else {
      continue;
    }

    /** @var DOMNode $node */
    if ($node
      ->hasAttributes()) {
      foreach ($node->attributes as $name => $attribute) {

        // see \Drupal\filter\Plugin\Filter\FilterHtml::prepareAttributeValues
        $name = str_replace($star_protector, '*', $name);
        $allowed_attribute_values = preg_split('/\\s+/', str_replace($star_protector, '*', $attribute->value), -1, PREG_SPLIT_NO_EMPTY);
        $allowed_attribute_values = array_filter($allowed_attribute_values, function ($value) use ($star_protector) {
          return $value !== '*';
        });

        // $allowed_attribute_values needs to be empty to allow all values.
        if (array_key_exists($name, $attributes[$tag])) {
          $attributes[$tag][$name] = empty($allowed_attribute_values);
        }
      }
    }
  }

  // Add missing tags and attributes required by the Insert module. This is done
  // using string parsing as the actually saved string should be altered as
  // minimally as possible.
  foreach ($tags as $tag => $found_tag) {
    if (!$found_tag) {
      $value .= ' <' . $tag . '>';
    }
    foreach ($attributes[$tag] as $name => $found_attribute) {
      if ($found_attribute === TRUE) {

        // The attribute is set already and allows all values.
        continue;
      }
      elseif ($found_attribute === null) {

        // The attribute is not yet set, just add it.
        $value = preg_replace('/<' . $tag . '/', '<' . $tag . ' ' . $name, $value);
      }
      else {

        // The attribute is set but limited to particular values; Remove that
        // limitation.
        $value = preg_replace('/(<' . $tag . '[^>]+' . $name . ')(=("|\')[^"\']+("|\'))/', '$1', $value);
      }
    }
  }
  $form_state
    ->setValueForElement($element, $value);
}