Loading core/modules/ckeditor5/ckeditor5.api.php +6 −5 Original line number Diff line number Diff line Loading @@ -119,11 +119,12 @@ * make it discoverable. * - drupal.elements: A list of elements and attributes the plugin allows use of * within CKEditor 5. This uses the same syntax as the 'filter_html' plugin * with an additional special keyword: '<$block>' . Using * '<$block [attribute(s)]>` will permit the provided attributes in all block * level tags that are explicitly enabled in any plugin. i.e. if only '<p>', * '<h3>' and '<h2>' tags are allowed, then '<$block data-something>' will * allow the 'data-something' attribute for '<p>', '<h3>' and '<h2>' tags. * with an additional special keyword: '<$text-container>' . Using * '<$text-container [attribute(s)]>` will permit the provided * attributes in all CKEditor 5's `$block` text container tags that are * explicitly enabled in any plugin. i.e. if only '<p>', '<h3>' and '<h2>' * tags are allowed, then '<$text-container data-something>' will allow the * 'data-something' attribute for '<p>', '<h3>' and '<h2>' tags. * - drupal.toolbar_items: List of toolbar items the plugin provides. Keyed by a * machine name and the value being a pair defining the label: * @code Loading core/modules/ckeditor5/ckeditor5.ckeditor5.yml +5 −5 Original line number Diff line number Diff line Loading @@ -332,7 +332,7 @@ ckeditor5_alignment: alignment: label: Text alignment elements: - <$block class="text-align-left text-align-center text-align-right text-align-justify"> - <$text-container class="text-align-left text-align-center text-align-right text-align-justify"> ckeditor5_alignment.left: ckeditor5: *alignment_ckeditor5_section Loading @@ -342,7 +342,7 @@ ckeditor5_alignment.left: "alignment:left": label: Align left elements: - <$block class="text-align-left"> - <$text-container class="text-align-left"> <<: *alignment_drupal_section ckeditor5_alignment.center: Loading @@ -353,7 +353,7 @@ ckeditor5_alignment.center: "alignment:center": label: Align center elements: - <$block class="text-align-center"> - <$text-container class="text-align-center"> <<: *alignment_drupal_section ckeditor5_alignment.right: Loading @@ -364,7 +364,7 @@ ckeditor5_alignment.right: "alignment:right": label: Align right elements: - <$block class="text-align-right"> - <$text-container class="text-align-right"> <<: *alignment_drupal_section ckeditor5_alignment.justify: Loading @@ -375,7 +375,7 @@ ckeditor5_alignment.justify: "alignment:justify": label: Justify elements: - <$block class="text-align-justify"> - <$text-container class="text-align-justify"> <<: *alignment_drupal_section ckeditor5_removeFormat: Loading core/modules/ckeditor5/src/HTMLRestrictions.php +49 −27 Original line number Diff line number Diff line Loading @@ -9,7 +9,6 @@ use Drupal\filter\FilterFormatInterface; use Drupal\filter\Plugin\Filter\FilterHtml; use Drupal\filter\Plugin\FilterInterface; use Masterminds\HTML5\Elements; /** * Represents a set of HTML restrictions. Loading Loading @@ -70,7 +69,7 @@ final class HTMLRestrictions { * @var string[] */ private const WILDCARD_ELEMENT_METHODS = [ '$block' => 'getBlockElementList', '$text-container' => 'getTextContainerElementList', ]; /** Loading Loading @@ -338,8 +337,8 @@ private static function fromObjectWithHtmlRestrictions(object $object): HTMLRest * @see ::toCKEditor5ElementsArray() */ public static function fromString(string $elements_string): HTMLRestrictions { // Preprocess wildcard tags: convert `<$block>` to // `<__preprocessed-wildcard-block__>`. // Preprocess wildcard tags: convert `<$text-container>` to // `<__preprocessed-wildcard-text-container__>`. // Note: unknown wildcard tags will trigger a validation error in // ::validateAllowedRestrictionsPhase1(). $replaced_wildcard_tags = []; Loading @@ -361,8 +360,8 @@ public static function fromString(string $elements_string): HTMLRestrictions { // @todo remove this in https://www.drupal.org/project/drupal/issues/3226368 unset($allowed_elements['__zqh6vxfbk3cg__']); // Postprocess tag wildcards: convert `<__preprocessed-wildcard-block__>` to // `<$block>`. // Postprocess tag wildcards: convert // `<__preprocessed-wildcard-text-container__>` to `<$text-container>`. foreach ($replaced_wildcard_tags as $processed => $original) { if (isset($allowed_elements[$processed])) { $allowed_elements[$original] = $allowed_elements[$processed]; Loading Loading @@ -772,7 +771,7 @@ public function merge(HTMLRestrictions $other): HTMLRestrictions { */ private static function applyOperation(HTMLRestrictions $a, HTMLRestrictions $b, string $operation_method_name): HTMLRestrictions { // 1. Operation applied to wildcard tags that exist in both operands. // For example: <$block id> in both operands. // For example: <$text-container id> in both operands. $a_wildcard = $a->getWildcardSubset(); $b_wildcard = $b->getWildcardSubset(); $wildcard_op_result = $a_wildcard->$operation_method_name($b_wildcard); Loading @@ -784,7 +783,8 @@ private static function applyOperation(HTMLRestrictions $a, HTMLRestrictions $b, // 2. Operation applied with wildcard tags resolved into concrete tags. // For example: <p class="text-align-center"> in the first operand and // <$block class="text-align-center"> in the second operand. // <$text-container class="text-align-center"> in the second // operand. $a_concrete = self::resolveWildcards($a); $b_concrete = self::resolveWildcards($b); $concrete_op_result = $a_concrete->$operation_method_name($b_concrete); Loading Loading @@ -901,15 +901,16 @@ private static function resolveWildcards(HTMLRestrictions $r): HTMLRestrictions // let ::merge() pick the most permissive one. // This is necessary because resolving wildcards may result in concrete tags // becoming either more permissive: // - if $r is `<p> <$block class="foo">` // - if $r is `<p> <$text-container class="foo">` // - then $naive will be `<p class="foo">` // - merging them yields `<p class="foo"> <$block class="foo">` // - merging them yields `<p class="foo"> <$text-container class="foo">` // - diffing the wildcard subsets yields just `<p class="foo">` // Or it could result in concrete tags being unaffected by the resolved // wildcards: // - if $r is `<p class> <$block class="foo">` // - if $r is `<p class> <$text-container class="foo">` // - then $naive will be `<p class="foo">` // - merging them yields `<p class> <$block class="foo">` again // - merging them yields `<p class> <$text-container class="foo">` // again // - diffing the wildcard subsets yields just `<p class>` return $r->merge($naive_resolution)->doDiff($r->getWildcardSubset()); } Loading Loading @@ -996,12 +997,7 @@ public function toGeneralHtmlSupportConfig(): array { $allowed = []; // Resolve any remaining wildcards based on Drupal's assumptions on // wildcards to ensure all HTML tags that Drupal thinks are supported are // truly supported by CKEditor 5. For example: the <$block> wildcard does // NOT correspond to block-level HTML tags, but to CKEditor 5 elements that // behave like blocks. Knowing the list of concrete HTML tags this maps to // is impossible without executing JavaScript, which PHP cannot do. By // generating this GHS configuration, we can guarantee that Drupal's only // possible interpretation also actually works. // truly supported by CKEditor 5. $elements = self::resolveWildcards($this)->getAllowedElements(); foreach ($elements as $tag => $attributes) { $to_allow = ['name' => $tag]; Loading Loading @@ -1048,25 +1044,51 @@ public function toGeneralHtmlSupportConfig(): array { } /** * Gets a list of block-level elements. * Gets a list of CKEditor 5's `$block` text container elements. * * This is a hard coded list of known elements that CKEditor 5 uses as * `$block` text container elements. The elements listed here are registered * with `inheritAllFrom: "$block"` to the CKEditor 5 schema. This list * corresponds to the `$text-container` wildcard in Drupal configuration. * * * This group of elements is special because they allow text as an immediate * child node. These elements are also allowed to be used for text styles that * must be applied to the wrapper instead of inline to the text, such as text * alignment. * * This list is highly opinionated. It is based on decisions made upstream in * CKEditor 5. For example, `<blockquote>` is not considered as a `$block` * text container, meaning that text inside `<blockquote>` needs to always be * wrapped by an element that is `$block` text container such as `<p>`. This * list also excludes some special case text container elements like * `<caption>` that allow containing text directly inside the element, yet do * not fully implement the `$block` text container interface. * * It is acceptable to list the elements here because the list of elements is * not likely to change often. If the list changed, an upgrade path would be * required anyway. In most cases, missing elements would only impact new * functionality shipped in upstream. * * @see https://ckeditor.com/docs/ckeditor5/latest/framework/guides/deep-dive/schema.html#generic-items * * @return string[] * An array of block-level element tags. */ private static function getBlockElementList(): array { return array_filter(array_keys(Elements::$html5), function (string $element): bool { return Elements::isA($element, Elements::BLOCK_TAG); }); private static function getTextContainerElementList(): array { return [ 'div', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'pre', 'li', ]; } /** * Computes the tags that match the provided wildcard. * * A wildcard tag in element config is a way of representing multiple tags * with a single item, such as `<$block>` to represent all block tags. Each * wildcard should have a corresponding callback method listed in * WILDCARD_ELEMENT_METHODS that returns the set of tags represented by the * wildcard. * with a single item, such as `<$text-container>` to represent CKEditor 5's * `$block` text container tags. Each wildcard should have a corresponding * callback method listed in WILDCARD_ELEMENT_METHODS that returns the set of * tags represented by the wildcard. * * @param string $wildcard * The wildcard that represents multiple tags. Loading core/modules/ckeditor5/src/Plugin/CKEditor5PluginManager.php +2 −2 Original line number Diff line number Diff line Loading @@ -179,8 +179,8 @@ public function getEnabledDefinitions(EditorInterface $editor): array { $restrictions = new HTMLRestrictions($this->getProvidedElements(array_keys($definitions), $editor, FALSE)); if ($restrictions->getWildcardSubset()->isEmpty()) { // This is only reached if arbitrary HTML is not enabled. If wildcard // tags (such as $block) are present, they need to be resolved via the // wildcardHtmlSupport plugin. // tags (such as $text-container) are present, they need to // be resolved via the wildcardHtmlSupport plugin. // @see \Drupal\ckeditor5\Plugin\CKEditor5PluginManager::getCKEditor5PluginConfig() unset($definitions['ckeditor5_wildcardHtmlSupport']); } Loading core/modules/ckeditor5/tests/modules/ckeditor5_plugin_elements_test/ckeditor5_plugin_elements_test.ckeditor5.yml +4 −4 Original line number Diff line number Diff line # cspell:ignore everyblock justblockquote ckeditor5_plugin_elements_test_blockquoteCombo: # cspell:ignore everytextcontainer justheading ckeditor5_plugin_elements_test_headingCombo: ckeditor5: plugins: [] drupal: label: TEST — block quote combo elements: - <blockquote data-justblockquote> - <$block data-everyblock> - <h1 data-justheading> - <$text-container data-everytextcontainer> ckeditor5_plugin_elements_test_headingsWithOtherAttributes: ckeditor5: Loading Loading
core/modules/ckeditor5/ckeditor5.api.php +6 −5 Original line number Diff line number Diff line Loading @@ -119,11 +119,12 @@ * make it discoverable. * - drupal.elements: A list of elements and attributes the plugin allows use of * within CKEditor 5. This uses the same syntax as the 'filter_html' plugin * with an additional special keyword: '<$block>' . Using * '<$block [attribute(s)]>` will permit the provided attributes in all block * level tags that are explicitly enabled in any plugin. i.e. if only '<p>', * '<h3>' and '<h2>' tags are allowed, then '<$block data-something>' will * allow the 'data-something' attribute for '<p>', '<h3>' and '<h2>' tags. * with an additional special keyword: '<$text-container>' . Using * '<$text-container [attribute(s)]>` will permit the provided * attributes in all CKEditor 5's `$block` text container tags that are * explicitly enabled in any plugin. i.e. if only '<p>', '<h3>' and '<h2>' * tags are allowed, then '<$text-container data-something>' will allow the * 'data-something' attribute for '<p>', '<h3>' and '<h2>' tags. * - drupal.toolbar_items: List of toolbar items the plugin provides. Keyed by a * machine name and the value being a pair defining the label: * @code Loading
core/modules/ckeditor5/ckeditor5.ckeditor5.yml +5 −5 Original line number Diff line number Diff line Loading @@ -332,7 +332,7 @@ ckeditor5_alignment: alignment: label: Text alignment elements: - <$block class="text-align-left text-align-center text-align-right text-align-justify"> - <$text-container class="text-align-left text-align-center text-align-right text-align-justify"> ckeditor5_alignment.left: ckeditor5: *alignment_ckeditor5_section Loading @@ -342,7 +342,7 @@ ckeditor5_alignment.left: "alignment:left": label: Align left elements: - <$block class="text-align-left"> - <$text-container class="text-align-left"> <<: *alignment_drupal_section ckeditor5_alignment.center: Loading @@ -353,7 +353,7 @@ ckeditor5_alignment.center: "alignment:center": label: Align center elements: - <$block class="text-align-center"> - <$text-container class="text-align-center"> <<: *alignment_drupal_section ckeditor5_alignment.right: Loading @@ -364,7 +364,7 @@ ckeditor5_alignment.right: "alignment:right": label: Align right elements: - <$block class="text-align-right"> - <$text-container class="text-align-right"> <<: *alignment_drupal_section ckeditor5_alignment.justify: Loading @@ -375,7 +375,7 @@ ckeditor5_alignment.justify: "alignment:justify": label: Justify elements: - <$block class="text-align-justify"> - <$text-container class="text-align-justify"> <<: *alignment_drupal_section ckeditor5_removeFormat: Loading
core/modules/ckeditor5/src/HTMLRestrictions.php +49 −27 Original line number Diff line number Diff line Loading @@ -9,7 +9,6 @@ use Drupal\filter\FilterFormatInterface; use Drupal\filter\Plugin\Filter\FilterHtml; use Drupal\filter\Plugin\FilterInterface; use Masterminds\HTML5\Elements; /** * Represents a set of HTML restrictions. Loading Loading @@ -70,7 +69,7 @@ final class HTMLRestrictions { * @var string[] */ private const WILDCARD_ELEMENT_METHODS = [ '$block' => 'getBlockElementList', '$text-container' => 'getTextContainerElementList', ]; /** Loading Loading @@ -338,8 +337,8 @@ private static function fromObjectWithHtmlRestrictions(object $object): HTMLRest * @see ::toCKEditor5ElementsArray() */ public static function fromString(string $elements_string): HTMLRestrictions { // Preprocess wildcard tags: convert `<$block>` to // `<__preprocessed-wildcard-block__>`. // Preprocess wildcard tags: convert `<$text-container>` to // `<__preprocessed-wildcard-text-container__>`. // Note: unknown wildcard tags will trigger a validation error in // ::validateAllowedRestrictionsPhase1(). $replaced_wildcard_tags = []; Loading @@ -361,8 +360,8 @@ public static function fromString(string $elements_string): HTMLRestrictions { // @todo remove this in https://www.drupal.org/project/drupal/issues/3226368 unset($allowed_elements['__zqh6vxfbk3cg__']); // Postprocess tag wildcards: convert `<__preprocessed-wildcard-block__>` to // `<$block>`. // Postprocess tag wildcards: convert // `<__preprocessed-wildcard-text-container__>` to `<$text-container>`. foreach ($replaced_wildcard_tags as $processed => $original) { if (isset($allowed_elements[$processed])) { $allowed_elements[$original] = $allowed_elements[$processed]; Loading Loading @@ -772,7 +771,7 @@ public function merge(HTMLRestrictions $other): HTMLRestrictions { */ private static function applyOperation(HTMLRestrictions $a, HTMLRestrictions $b, string $operation_method_name): HTMLRestrictions { // 1. Operation applied to wildcard tags that exist in both operands. // For example: <$block id> in both operands. // For example: <$text-container id> in both operands. $a_wildcard = $a->getWildcardSubset(); $b_wildcard = $b->getWildcardSubset(); $wildcard_op_result = $a_wildcard->$operation_method_name($b_wildcard); Loading @@ -784,7 +783,8 @@ private static function applyOperation(HTMLRestrictions $a, HTMLRestrictions $b, // 2. Operation applied with wildcard tags resolved into concrete tags. // For example: <p class="text-align-center"> in the first operand and // <$block class="text-align-center"> in the second operand. // <$text-container class="text-align-center"> in the second // operand. $a_concrete = self::resolveWildcards($a); $b_concrete = self::resolveWildcards($b); $concrete_op_result = $a_concrete->$operation_method_name($b_concrete); Loading Loading @@ -901,15 +901,16 @@ private static function resolveWildcards(HTMLRestrictions $r): HTMLRestrictions // let ::merge() pick the most permissive one. // This is necessary because resolving wildcards may result in concrete tags // becoming either more permissive: // - if $r is `<p> <$block class="foo">` // - if $r is `<p> <$text-container class="foo">` // - then $naive will be `<p class="foo">` // - merging them yields `<p class="foo"> <$block class="foo">` // - merging them yields `<p class="foo"> <$text-container class="foo">` // - diffing the wildcard subsets yields just `<p class="foo">` // Or it could result in concrete tags being unaffected by the resolved // wildcards: // - if $r is `<p class> <$block class="foo">` // - if $r is `<p class> <$text-container class="foo">` // - then $naive will be `<p class="foo">` // - merging them yields `<p class> <$block class="foo">` again // - merging them yields `<p class> <$text-container class="foo">` // again // - diffing the wildcard subsets yields just `<p class>` return $r->merge($naive_resolution)->doDiff($r->getWildcardSubset()); } Loading Loading @@ -996,12 +997,7 @@ public function toGeneralHtmlSupportConfig(): array { $allowed = []; // Resolve any remaining wildcards based on Drupal's assumptions on // wildcards to ensure all HTML tags that Drupal thinks are supported are // truly supported by CKEditor 5. For example: the <$block> wildcard does // NOT correspond to block-level HTML tags, but to CKEditor 5 elements that // behave like blocks. Knowing the list of concrete HTML tags this maps to // is impossible without executing JavaScript, which PHP cannot do. By // generating this GHS configuration, we can guarantee that Drupal's only // possible interpretation also actually works. // truly supported by CKEditor 5. $elements = self::resolveWildcards($this)->getAllowedElements(); foreach ($elements as $tag => $attributes) { $to_allow = ['name' => $tag]; Loading Loading @@ -1048,25 +1044,51 @@ public function toGeneralHtmlSupportConfig(): array { } /** * Gets a list of block-level elements. * Gets a list of CKEditor 5's `$block` text container elements. * * This is a hard coded list of known elements that CKEditor 5 uses as * `$block` text container elements. The elements listed here are registered * with `inheritAllFrom: "$block"` to the CKEditor 5 schema. This list * corresponds to the `$text-container` wildcard in Drupal configuration. * * * This group of elements is special because they allow text as an immediate * child node. These elements are also allowed to be used for text styles that * must be applied to the wrapper instead of inline to the text, such as text * alignment. * * This list is highly opinionated. It is based on decisions made upstream in * CKEditor 5. For example, `<blockquote>` is not considered as a `$block` * text container, meaning that text inside `<blockquote>` needs to always be * wrapped by an element that is `$block` text container such as `<p>`. This * list also excludes some special case text container elements like * `<caption>` that allow containing text directly inside the element, yet do * not fully implement the `$block` text container interface. * * It is acceptable to list the elements here because the list of elements is * not likely to change often. If the list changed, an upgrade path would be * required anyway. In most cases, missing elements would only impact new * functionality shipped in upstream. * * @see https://ckeditor.com/docs/ckeditor5/latest/framework/guides/deep-dive/schema.html#generic-items * * @return string[] * An array of block-level element tags. */ private static function getBlockElementList(): array { return array_filter(array_keys(Elements::$html5), function (string $element): bool { return Elements::isA($element, Elements::BLOCK_TAG); }); private static function getTextContainerElementList(): array { return [ 'div', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'pre', 'li', ]; } /** * Computes the tags that match the provided wildcard. * * A wildcard tag in element config is a way of representing multiple tags * with a single item, such as `<$block>` to represent all block tags. Each * wildcard should have a corresponding callback method listed in * WILDCARD_ELEMENT_METHODS that returns the set of tags represented by the * wildcard. * with a single item, such as `<$text-container>` to represent CKEditor 5's * `$block` text container tags. Each wildcard should have a corresponding * callback method listed in WILDCARD_ELEMENT_METHODS that returns the set of * tags represented by the wildcard. * * @param string $wildcard * The wildcard that represents multiple tags. Loading
core/modules/ckeditor5/src/Plugin/CKEditor5PluginManager.php +2 −2 Original line number Diff line number Diff line Loading @@ -179,8 +179,8 @@ public function getEnabledDefinitions(EditorInterface $editor): array { $restrictions = new HTMLRestrictions($this->getProvidedElements(array_keys($definitions), $editor, FALSE)); if ($restrictions->getWildcardSubset()->isEmpty()) { // This is only reached if arbitrary HTML is not enabled. If wildcard // tags (such as $block) are present, they need to be resolved via the // wildcardHtmlSupport plugin. // tags (such as $text-container) are present, they need to // be resolved via the wildcardHtmlSupport plugin. // @see \Drupal\ckeditor5\Plugin\CKEditor5PluginManager::getCKEditor5PluginConfig() unset($definitions['ckeditor5_wildcardHtmlSupport']); } Loading
core/modules/ckeditor5/tests/modules/ckeditor5_plugin_elements_test/ckeditor5_plugin_elements_test.ckeditor5.yml +4 −4 Original line number Diff line number Diff line # cspell:ignore everyblock justblockquote ckeditor5_plugin_elements_test_blockquoteCombo: # cspell:ignore everytextcontainer justheading ckeditor5_plugin_elements_test_headingCombo: ckeditor5: plugins: [] drupal: label: TEST — block quote combo elements: - <blockquote data-justblockquote> - <$block data-everyblock> - <h1 data-justheading> - <$text-container data-everytextcontainer> ckeditor5_plugin_elements_test_headingsWithOtherAttributes: ckeditor5: Loading