Problem/Motivation

@nod_ found a hard blocker in #3263384: Add ckeditor5-code-block package and CodeBlock plugin: the CKE4 Format toolbar button (dropdown, really) is already mapped to CKE5's heading. This means it's impossible to also map it to codeBlock.

Worse, blindly mapping Format to heading is wrong too: if no <h*> tags are allowed in a text format, then it also does not appear in the Format dropdown:

Compare this with the case where <h*> and <pre> are allowed:

Steps to reproduce

See above.

Proposed resolution

TBD

Remaining tasks

TBD

User interface changes

None.

API changes

TBD

Data model changes

None.

Release notes snippet

One of the methods on the CKEditor 4-to-5 upgrade plugin interface was made more capable: CKEditor4To5UpgradePluginInterface::mapCKEditor4ToolbarButtonToCKEditor5ToolbarItem(). In doing so, a small backwards compatibility break was introduced. This was considered acceptable given zero real-world usages of this (very new) interface exist.

Issue fork drupal-3268174

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Wim Leers created an issue. See original summary.

Wim Leers’s picture

Assigned: Unassigned » Wim Leers

Wim Leers’s picture

Assigned: Wim Leers » Unassigned
Status: Active » Needs review

nod_ made their first commit to this issue’s fork.

nod_’s picture

Status: Needs review » Reviewed & tested by the community

RTBC, just a very minor readability concern

Wim Leers’s picture

Wim Leers’s picture

Ugh — PhpStorm is making my computer lag so much that both the commit and the #7 patch are missing a semi-colon…

lauriii’s picture

Status: Reviewed & tested by the community » Needs review
  1. +++ b/core/modules/ckeditor5/src/HTMLRestrictions.php
    @@ -214,6 +221,15 @@ public static function emptySet(): HTMLRestrictions {
    +  public function isUnrestricted(): bool {
    

    Seems fine to add this to the API since this is something is useful to be able to check 👍

  2. +++ b/core/modules/ckeditor5/src/HTMLRestrictions.php
    @@ -249,6 +265,18 @@ public static function fromTextFormat(FilterFormatInterface $text_format): HTMLR
    +    // @todo Refine in https://www.drupal.org/project/drupal/issues/3231336, including adding support for all operations.
    +    $restrictions = HTMLRestrictions::emptySet();
    +    $restrictions->unrestricted = TRUE;
    

    Seems fine since this is all internal. 👍

  3. +++ b/core/modules/ckeditor5/src/Plugin/CKEditor4To5Upgrade/Core.php
    @@ -115,7 +120,21 @@ public function mapCKEditor4ToolbarButtonToCKEditor5ToolbarItem(string $cke4_but
    +        $intersect = array_intersect(['h2', 'h3', 'h4', 'h5', 'h6'], array_keys($allowed_elements));
    

    Could there be wildcards like <h*>?

  4. +++ b/core/modules/ckeditor5/src/Plugin/CKEditor4To5Upgrade/Core.php
    --- a/core/modules/ckeditor5/src/Plugin/CKEditor4To5UpgradePluginInterface.php
    +++ b/core/modules/ckeditor5/src/Plugin/CKEditor4To5UpgradePluginInterface.php
    
    +++ b/core/modules/ckeditor5/src/Plugin/CKEditor4To5UpgradePluginInterface.php
    @@ -35,7 +42,7 @@ interface CKEditor4To5UpgradePluginInterface extends PluginInspectionInterface {
    +  public function mapCKEditor4ToolbarButtonToCKEditor5ToolbarItem(string $cke4_button, HTMLRestrictions $text_format_html_restrictions = NULL): ?string;
    

    Wondering about the new argument being optional. Since it's required for some plugins, shouldn't we have it as a required argument even though that would be a BC break?

  5. +++ b/core/modules/ckeditor5/src/Plugin/CKEditor4To5Upgrade/Core.php
    @@ -69,7 +70,11 @@ class Core extends PluginBase implements CKEditor4To5UpgradePluginInterface {
    +      throw new \LogicException('This @CKEditor4To5Upgrade plugin requires the optional $text_format_html_restrictions argument.');
    

    Ahh I see, it is marked as required here 🤔 I guess this is better for BC reasons + this should be only called by \Drupal\ckeditor5\Plugin\CKEditor4To5UpgradePluginManager.

  6. +++ b/core/modules/ckeditor5/src/Plugin/CKEditor4To5UpgradePluginInterface.php
    @@ -24,10 +25,16 @@ interface CKEditor4To5UpgradePluginInterface extends PluginInspectionInterface {
    +   *   In rare situations, a single toolbar button in CKEditor 4 is mapped to
    +   *   multiple toolbar items in CKEditor 5. In this case, a string is still
    +   *   returned, but it may contain multiple toolbar items separated by commas.
    

    🥲

nod_’s picture

wildcards are only for attributes names or values no? using them in tag names triggers a JS error at least on 10.x so not sure it's supposed to work

nod_’s picture

Status: Needs review » Reviewed & tested by the community

Looking at the php doesn't seem like <h*> is a valid config.

Wim Leers’s picture

Status: Reviewed & tested by the community » Needs review

#9.5: yep, exactly! I wanted to verify that this actually works though:

<?php

interface HTMLRestrictions {}

interface Test {
  public function doThing(string $cke4_button, HTMLRestrictions $optional_new = NULL): ?string;
}

class PreExisting implements Test {
  public function doThing(string $cke4_button): ?string {
      return NULL;
  }
}

class NewlyPossible implements Test {
  public function doThing(string $cke4_button, HTMLRestrictions $optional_new = NULL): ?string {
      return NULL;
  }
}

and that fails like this:

// Output for 8.0.1 - 8.0.16, 8.1.0 - 8.1.3
Fatal error: Declaration of PreExisting::doThing(string $cke4_button): ?string must be compatible with Test::doThing(string $cke4_button, ?HTMLRestrictions $optional_new = null): ?string in /in/CUJ0U on line 10

// Output for 7.4.0 - 7.4.28
Fatal error: Declaration of PreExisting::doThing(string $cke4_button): ?string must be compatible with Test::doThing(string $cke4_button, ?HTMLRestrictions $optional_new = NULL): ?string in /in/CUJ0U on line 10

(See https://3v4l.org/trPZF.)

Looks like we have to rethink this. I don't see how we can do this without breaking BC? OTOH, we are HIGHLY confident that nobody in the world has implemented CKEditor 5 upgrade path plugins yet. So … I think a BC break may be acceptable in this one case?

catch’s picture

If we're confident that no-one is implementing the interface yet, I think we should just go ahead and break it (with a release note and CR) before anyone does. ckeditor5 is still experimental and this is required to fix a critical bug.

Wim Leers’s picture

@catch: Alright, thank you! 👍

FYI: @nod_ also double-checked the Drupal contrib scanning site and found zero implementations of this 👍

Here's then a reroll that fixes the problem described in #12 and technically introduces a BC break. Created a change record too: https://www.drupal.org/node/3269438

nod_’s picture

Status: Needs review » Reviewed & tested by the community

code-wise it's good with me. I'll let framework managers greenlight it, or not :D

lauriii’s picture

Status: Reviewed & tested by the community » Needs review
+++ b/core/modules/ckeditor5/src/Plugin/CKEditor4To5UpgradePluginInterface.php
@@ -24,10 +25,16 @@ interface CKEditor4To5UpgradePluginInterface extends PluginInspectionInterface {
    * @return string|null
...
+   *   In rare situations, a single toolbar button in CKEditor 4 is mapped to
+   *   multiple toolbar items in CKEditor 5. In this case, a string is still
+   *   returned, but it may contain multiple toolbar items separated by commas.

Since we are introducing changes to the interface, should we change the return type to an array to properly support this use case?

Wim Leers’s picture

Status: Needs review » Reviewed & tested by the community
FileSize
9.65 KB
21.54 KB

#16: I think that makes sense! (And coincidentally it makes all CKEditor4To5UpgradePluginInterface methods consistent in their return type.)

CR updated too.

catch’s picture

Still needs a release note. Also tagging for all three branches.

Wim Leers’s picture

Issue summary: View changes

Proposed release note snippet added.

catch’s picture

Status: Reviewed & tested by the community » Needs review
+++ b/core/modules/ckeditor5/src/HTMLRestrictions.php
@@ -53,6 +53,13 @@ final class HTMLRestrictions {
 
+  /**
+   * Whether this set of restrictions is unrestricted.
+   *
+   * @var bool
+   */
+  private $unrestricted = FALSE;
+

This is confusing with the double negative, could it use isEmpty or isEmptySet or similar instead?

lauriii’s picture

Status: Needs review » Reviewed & tested by the community

I personally think unrestricted makes the most sense it's a term that's already being used in Editor and CKEditor 5. This is specifically trying to explain why the restriction set is empty - is it because nothing is allowed or because everything is allowed.

catch’s picture

Status: Reviewed & tested by the community » Needs work

I think we need something better than "Whether this set of restrictions is unrestricted." for the comment then.

Wim Leers’s picture

Status: Needs work » Reviewed & tested by the community
FileSize
853 bytes
21.76 KB

Improved comment:

   /**
-   * Whether this set of restrictions is unrestricted.
+   * Whether unrestricted, in other words: arbitrary HTML allowed.
+   *
+   * Used for when FilterFormatInterface::getHTMLRestrictions() returns `FALSE`,
+   * f.e. in case of the default "Full HTML" text format.
    *
    * @var bool
+   * @see \Drupal\filter\Plugin\FilterInterface::getHTMLRestrictions()
    */
   private $unrestricted = FALSE;
 

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 23: 3268174-23.patch, failed testing. View results

Wim Leers’s picture

Status: Needs work » Reviewed & tested by the community

Random unrelated failure on 10.0.x branch only:

1) Drupal\Tests\media\FunctionalJavascript\CKEditorIntegrationTest::testLinkability with data set "linkability when `drupalimage` is disabled" (false)

Re-testing, re-RTBC'ing.

  • catch committed 196b68e on 10.0.x
    Issue #3268174 by Wim Leers, nod_, catch, lauriii: Bug in CKE 4 → 5...

  • catch committed ef310ce on 9.4.x
    Issue #3268174 by Wim Leers, nod_, catch, lauriii: Bug in CKE 4 → 5...

  • catch committed e3ae54a on 9.3.x
    Issue #3268174 by Wim Leers, nod_, catch, lauriii: Bug in CKE 4 → 5...
catch’s picture

Version: 10.0.x-dev » 9.3.x-dev
Status: Reviewed & tested by the community » Fixed

OK that reads a lot better, I changed 'f.e.' to 'e.g.' on commit.

Committed/pushed to 10.0.x, 9.4.x, and 9.3.x, thanks!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

xjm’s picture

Given that this was backported to 9.3.x for a release note about a technical BC break, I don't think we don't need to mention it in the 10.0 and 9.4 release notes. It's a very small change from 10.0.0-alpha2 compared to the many significant BC breaks from major dependency updates and deprecation removals for things that were not deprecated until this month, and 9.4.x has never had a tagged release.