diff --git a/core/modules/ckeditor/tests/modules/src/Kernel/CKEditorTest.php b/core/modules/ckeditor/tests/modules/src/Kernel/CKEditorTest.php index 4fbead9..625e28c 100644 --- a/core/modules/ckeditor/tests/modules/src/Kernel/CKEditorTest.php +++ b/core/modules/ckeditor/tests/modules/src/Kernel/CKEditorTest.php @@ -49,7 +49,22 @@ protected function setUp() { 'filter_html' => [ 'status' => 1, 'settings' => [ - 'allowed_html' => '


', + 'allowed_html' => [ + 'id' => [ + 'id' => '*', + ], + 'h3' => [], + 'h4' => [], + 'h5' => [], + 'h6' => [], + 'p' => [], + 'br' => [], + 'strong' => [], + 'a' => [ + 'href' => '*', + 'hreflang' => '*', + ], + ], ] ], ], diff --git a/core/modules/ckeditor/tests/src/Kernel/Plugin/CKEditorPlugin/InternalTest.php b/core/modules/ckeditor/tests/src/Kernel/Plugin/CKEditorPlugin/InternalTest.php index aecc228..87f8dc4 100644 --- a/core/modules/ckeditor/tests/src/Kernel/Plugin/CKEditorPlugin/InternalTest.php +++ b/core/modules/ckeditor/tests/src/Kernel/Plugin/CKEditorPlugin/InternalTest.php @@ -115,7 +115,10 @@ public function formatTagsSettingsTestCases() { 'filter_html' => [ 'status' => 1, 'settings' => [ - 'allowed_html' => '

', + 'allowed_html' => [ + 'h1' => [], + 'h2' => [], + ], 'filter_html_help' => 1, 'filter_html_nofollow' => 0, ], diff --git a/core/modules/editor/src/Tests/EditorSecurityTest.php b/core/modules/editor/src/Tests/EditorSecurityTest.php index d00fe0e..f1991d9 100644 --- a/core/modules/editor/src/Tests/EditorSecurityTest.php +++ b/core/modules/editor/src/Tests/EditorSecurityTest.php @@ -92,7 +92,17 @@ protected function setUp() { 'filter_html' => [ 'status' => 1, 'settings' => [ - 'allowed_html' => '


', + 'allowed_html' => [ + 'h2' => [], + 'h3' => [], + 'h4' => [], + 'h5' => [], + 'h6' => [], + 'p' => [], + 'br' => [], + 'strong' => [], + 'a' => [], + ], ] ], ], @@ -107,7 +117,17 @@ protected function setUp() { 'filter_html' => [ 'status' => 1, 'settings' => [ - 'allowed_html' => '


', + 'allowed_html' => [ + 'h2' => [], + 'h3' => [], + 'h4' => [], + 'h5' => [], + 'h6' => [], + 'p' => [], + 'br' => [], + 'strong' => [], + 'a' => [], + ], ] ], ], @@ -127,7 +147,18 @@ protected function setUp() { 'filter_html' => [ 'status' => 1, 'settings' => [ - 'allowed_html' => '


', + 'allowed_html' => [ + 'h2' => [], + 'h3' => [], + 'h4' => [], + 'h5' => [], + 'h6' => [], + 'p' => [], + 'br' => [], + 'strong' => [], + 'a' => [], + 'embed' => [], + ], ] ], ], diff --git a/core/modules/filter/filter.install b/core/modules/filter/filter.install index bfc0466..9c73649 100644 --- a/core/modules/filter/filter.install +++ b/core/modules/filter/filter.install @@ -5,7 +5,7 @@ * Install, update and uninstall functions for the filter module. */ -use Drupal\filter\Plugin\Filter\FilterHtml; +use Drupal\filter\FilterHtmlAllowedMarkupSchema; /** * Update the filter_html filter format schema. @@ -16,10 +16,7 @@ function filter_update_8001() { $filter = $config_factory->getEditable($name); $allowed_html = $filter->get('filters.filter_html.settings.allowed_html'); if ($allowed_html !== NULL) { - $plugin_class = new FilterHtml([], '', ['provider' => 'foo']); - $generate_method = new ReflectionMethod(FilterHtml::class, 'generateAllowedHtmlSettings'); - $generate_method->setAccessible(TRUE); - $updated_allowed_html = $generate_method->invoke($plugin_class, $allowed_html); + $updated_allowed_html = FilterHtmlAllowedMarkupSchema::generateSettings($allowed_html); $filter->set('filters.filter_html.settings.allowed_html', $updated_allowed_html); $filter->save(); } diff --git a/core/modules/filter/src/FilterHtmlAllowedMarkupSchema.php b/core/modules/filter/src/FilterHtmlAllowedMarkupSchema.php new file mode 100644 index 0000000..0ee5823 --- /dev/null +++ b/core/modules/filter/src/FilterHtmlAllowedMarkupSchema.php @@ -0,0 +1,78 @@ +', ' />', $allowed_html_string); + // Protect any trailing * characters in attribute names, since DomDocument + // strips them as invalid. + $star_protector = '__zqh6vxfbk3cg__'; + $html = str_replace('*', $star_protector, $html); + $body_child_nodes = Html::load($html) + ->getElementsByTagName('body') + ->item(0)->childNodes; + + foreach ($body_child_nodes as $node) { + if ($node->nodeType !== XML_ELEMENT_NODE) { + // Skip the empty text nodes inside tags. + continue; + } + $tag = $node->tagName; + $allowed_html_settings[$tag] = []; + if ($node->hasAttributes()) { + // Iterate over any attributes, and mark them as allowed. + foreach ($node->attributes as $name => $attribute) { + // Put back any trailing * on wildcard attribute name. + $name = str_replace($star_protector, '*', $name); + $value = str_replace($star_protector, '*', $attribute->value); + if (empty($value)) { + $value = '*'; + } + $allowed_html_settings[$tag][$name] = $value; + } + } + } + + return $allowed_html_settings; + } + + /** + * Generate a string to represent the allowed HTML. + * + * @param array $allowed_html_settings + * The allowed HTML + * @return string + */ + public static function generateString($allowed_html_settings) { + $allowed_html_tags = []; + foreach ($allowed_html_settings as $tag => $attributes) { + $attribute_strings = []; + foreach ($attributes as $attribute => $value) { + $attribute_strings[] = $value === '*' ? $attribute : sprintf('%s="%s"', $attribute, $value); + } + $allowed_html_tags[] = sprintf('<%s%s%s>', $tag, count($attribute_strings) > 0 ? ' ' : '', implode(' ', $attribute_strings)); + } + return implode(' ', $allowed_html_tags); + } + +} diff --git a/core/modules/filter/src/Plugin/Filter/FilterHtml.php b/core/modules/filter/src/Plugin/Filter/FilterHtml.php index 76a240d..175bfc6 100644 --- a/core/modules/filter/src/Plugin/Filter/FilterHtml.php +++ b/core/modules/filter/src/Plugin/Filter/FilterHtml.php @@ -5,6 +5,7 @@ use Drupal\Component\Utility\Xss; use Drupal\Core\Form\FormStateInterface; use Drupal\Component\Utility\Html; +use Drupal\filter\FilterHtmlAllowedMarkupSchema; use Drupal\filter\FilterProcessResult; use Drupal\filter\Plugin\FilterBase; @@ -74,84 +75,18 @@ public function settingsForm(array $form, FormStateInterface $form_state) { * Value callback for the allowed_html field. */ public function allowedHtmlValueCallback($element, $input, FormStateInterface $form_state) { - return $input === FALSE ? $element['#default_value'] : $this->generateAllowedHtmlSettings($input); + return $input === FALSE ? $element['#default_value'] : FilterHtmlAllowedMarkupSchema::generateString($input); } /** * Pre render callback for the allowed_html element. */ public function preRenderAllowedHtml($element) { - $element['#value'] = $this->generateAllowedHtmlString($element['#value']); + $element['#value'] = FilterHtmlAllowedMarkupSchema::generateString($element['#value']); return $element; } /** - * Generate an array to represent the allowed HTML settings. - * - * @param string $allowed_html_string - * A string to parse into an allowed HTML settings array. - * - * @return array - * An array representing the allowed HTML settings. - */ - protected function generateAllowedHtmlSettings($allowed_html_string) { - $allowed_html_settings = []; - - // Make all the tags self-closing, so they will be parsed into direct - // children of the body tag in the DomDocument. - $html = str_replace('>', ' />', $allowed_html_string); - // Protect any trailing * characters in attribute names, since DomDocument - // strips them as invalid. - $star_protector = '__zqh6vxfbk3cg__'; - $html = str_replace('*', $star_protector, $html); - $body_child_nodes = Html::load($html) - ->getElementsByTagName('body') - ->item(0)->childNodes; - - foreach ($body_child_nodes as $node) { - if ($node->nodeType !== XML_ELEMENT_NODE) { - // Skip the empty text nodes inside tags. - continue; - } - $tag = $node->tagName; - $allowed_html_settings[$tag] = []; - if ($node->hasAttributes()) { - // Iterate over any attributes, and mark them as allowed. - foreach ($node->attributes as $name => $attribute) { - // Put back any trailing * on wildcard attribute name. - $name = str_replace($star_protector, '*', $name); - $value = str_replace($star_protector, '*', $attribute->value); - if (empty($value)) { - $value = '*'; - } - $allowed_html_settings[$tag][$name] = $value; - } - } - } - - return $allowed_html_settings; - } - - /** - * Generate a string to represent the allowed HTML. - * - * @param array $allowed_html_settings - * The allowed HTML - * @return string - */ - protected function generateAllowedHtmlString($allowed_html_settings) { - $allowed_html_tags = []; - foreach ($allowed_html_settings as $tag => $attributes) { - $attribute_strings = []; - foreach ($attributes as $attribute => $value) { - $attribute_strings[] = $value === '*' ? $attribute : sprintf('%s="%s"', $attribute, $value); - } - $allowed_html_tags[] = sprintf('<%s%s%s>', $tag, count($attribute_strings) > 0 ? ' ' : '', implode(' ', $attribute_strings)); - } - return implode(' ', $allowed_html_tags); - } - - /** * {@inheritdoc} */ public function setConfiguration(array $configuration) { @@ -399,7 +334,7 @@ public function tips($long = FALSE) { if (!($allowed_html = $this->settings['allowed_html'])) { return; } - $output = $this->t('Allowed HTML tags: @tags', ['@tags' => $this->generateAllowedHtmlString($allowed_html)]); + $output = $this->t('Allowed HTML tags: @tags', ['@tags' => FilterHtmlAllowedMarkupSchema::generateString($allowed_html)]); if (!$long) { return $output; } diff --git a/core/modules/filter/src/Plugin/migrate/process/FilterSettings.php b/core/modules/filter/src/Plugin/migrate/process/FilterSettings.php index 2350426..bfd0d90 100644 --- a/core/modules/filter/src/Plugin/migrate/process/FilterSettings.php +++ b/core/modules/filter/src/Plugin/migrate/process/FilterSettings.php @@ -2,6 +2,7 @@ namespace Drupal\filter\Plugin\migrate\process; +use Drupal\filter\FilterHtmlAllowedMarkupSchema; use Drupal\migrate\ProcessPluginBase; use Drupal\migrate\MigrateExecutableInterface; use Drupal\migrate\Row; @@ -43,6 +44,7 @@ public function transform($value, MigrateExecutableInterface $migrate_executable // Only the filter_html filter's settings have a changed format. if ($row->getDestinationProperty('id') === 'filter_html') { $value['allowed_html'] = str_replace(array_keys($this->allowedHtmlDefaultAttributes), array_values($this->allowedHtmlDefaultAttributes), $value['allowed_html']); + $value['allowed_html'] = FilterHtmlAllowedMarkupSchema::generateSettings($value['allowed_html']); } return $value; } diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Editor/EditorResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Editor/EditorResourceTestBase.php index 4eb4a6e..d19c703 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/Editor/EditorResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/Editor/EditorResourceTestBase.php @@ -48,7 +48,12 @@ protected function createEntity() { 'filter_html' => [ 'status' => TRUE, 'settings' => [ - 'allowed_html' => '

', + 'allowed_html' => [ + 'p' => [], + 'a' => [], + 'b' => [], + 'lo' => [], + ], ], ], ], diff --git a/core/modules/rest/tests/src/Functional/EntityResource/FilterFormat/FilterFormatResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/FilterFormat/FilterFormatResourceTestBase.php index 4d65e00..5772343 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/FilterFormat/FilterFormatResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/FilterFormat/FilterFormatResourceTestBase.php @@ -41,7 +41,12 @@ protected function createEntity() { 'filter_html' => [ 'status' => TRUE, 'settings' => [ - 'allowed_html' => '

', + 'allowed_html' => [ + 'p' => [], + 'a' => [], + 'b' => [], + 'lo' => [], + ], ], ], ], @@ -63,7 +68,12 @@ protected function getExpectedNormalizedEntity() { 'status' => TRUE, 'weight' => -10, 'settings' => [ - 'allowed_html' => '

', + 'allowed_html' => [ + 'p' => [], + 'a' => [], + 'b' => [], + 'lo' => [], + ], 'filter_html_help' => TRUE, 'filter_html_nofollow' => FALSE, ],