Problem/Motivation

When using modules that provides CKEditor plugins, such as https://www.drupal.org/project/ckeditor_font, the dependency on the module providing the plugin is not automatically added to the the editor format configuration, such as editor.editor.full_html.yml

langcode: fr
status: true
dependencies:
  config:
    - filter.format.full_html
  module:
    - ckeditor

Should be:

langcode: fr
status: true
dependencies:
  config:
    - filter.format.full_html
  module:
    - ckeditor
    - ckeditor_font
    - colorbutton

Proposed resolution

Add automatically a dependency on modules providing enabled CKEditor plugins.

Remaining tasks

  • Waiting feedback from core maintainers
  • Agree on solution plan
  • Write a patch
  • Review the patch
  • Write automated tests?
  • Merge the patch

User interface changes

None

API changes

None

Data model changes

None

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Grimreaper created an issue. See original summary.

Wim Leers’s picture

Version: 8.4.3 » 8.5.x-dev
Issue summary: View changes
Issue tags: +missing config dependencies

Woah, great find! 👌 Thanks for reporting it so very clearly!

Of course I completely agree with the proposed resolution:

Add automatically a dependency on modules providing enabled CKEditor plugins.

I'd love to review a patch that fixes this!

Grimreaper’s picture

Issue summary: View changes
Status: Active » Needs review
FileSize
4.66 KB

Hi Wim Leers,

Thanks for your reply :).

Here is a patch that add a new method to the EditorPluginInterface so that it can be triggered to calculate the dependencies.

Currently no automated test. Waiting for the manual patch to be reviewed ok.

I found the code confusing, you constantly need to ask what "editor" means. Is it the config entity or the "editor plugin" (CKEditor, TinyMCE, etc.)

Thanks for the review.

Status: Needs review » Needs work

The last submitted patch, 3: drupal-ckeditor_config_dependencies-2950795-3.patch, failed testing. View results

Grimreaper’s picture

Assigned: Unassigned » Grimreaper
Grimreaper’s picture

Assigned: Grimreaper » Unassigned
Status: Needs work » Needs review
Related issues: +#2962981: File upload plugin can't be disabled
FileSize
5.47 KB
773 bytes

Fixing Prophecy mock.

Also during my tests I have noticed some strange behavior with #2962981: File upload plugin can't be disabled, when removing buttons, config remains.

msankhala’s picture

Excellent work @Grimreaper. Patch looks good to me. Waiting for a review from Drupal core committer.

Wim Leers’s picture

Status: Needs review » Needs work

Adding a new method to an interface unfortunately is a BC break: https://www.drupal.org/core/d8-bc-policy#interfaces

Interfaces tagged @api can be safely used as type hints and implemented. No methods will be added, removed or changed in a breaking way.

Could you create a new EditorPluginWithCalculatableDependenciesInterface interface instead? That interface should be marked @deprecated from the start, and should have a @todo Move into \Drupal\editor\Plugin\EditorPluginInterface in Drupal 9.

Wim Leers’s picture

BTW: the patch looks great! 👌 Well done :)

Wim Leers’s picture

Component: ckeditor.module » editor.module
+++ b/core/modules/editor/src/Entity/Editor.php
@@ -105,6 +105,10 @@ public function calculateDependencies() {
+    // Allow the editor to add its dependencies.
+    $this->addDependencies($this->editorPluginManager()->createInstance($this->editor)->calculateEditorDependencies($this));

This means it's a change in editor.module, not ckeditor.module.

Wim Leers’s picture

Patch review:

  1. +++ b/core/modules/editor/src/Plugin/EditorPluginInterface.php
    @@ -72,4 +72,37 @@ public function getJSSettings(Editor $editor);
    +   * Similar to
    +   * \Drupal\Component\Plugin\DependentPluginInterface::calculateDependencies(),
    +   * but we need to pass the editor as parameter to know which plugins are
    +   * enabled.
    

    👍

    Just one nit: "pass the editor" -> "pass the text editor config entity"

  2. +++ b/core/modules/editor/src/Plugin/EditorPluginInterface.php
    @@ -72,4 +72,37 @@ public function getJSSettings(Editor $editor);
    +   * determine configuration synchronization order. For example, if the plugin
    

    s/plugin/text editor plugin/

  3. +++ b/core/modules/editor/src/Plugin/EditorPluginInterface.php
    @@ -72,4 +72,37 @@ public function getJSSettings(Editor $editor);
    +   * dependencies listing the specified roles.
    

    s/roles/role config entities/

  4. +++ b/core/modules/editor/src/Plugin/EditorPluginInterface.php
    @@ -72,4 +72,37 @@ public function getJSSettings(Editor $editor);
    +   *     'config' => array('user.role.anonymous', 'user.role.authenticated'),
    

    Nit: use short array notation.

Wim Leers’s picture

Issue tags: +Needs change record
+++ b/core/modules/editor/src/Plugin/EditorBase.php
@@ -78,4 +78,11 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s
+  public function calculateEditorDependencies(Editor $editor) {
+    return [];
+  }

Hm … on second thought, this actually may make the interface change okay. Because the handful of \Drupal\editor\Plugin\EditorPluginInterface implementations likely all use this base class, which means disruption is super minimal.

So … ignore #8!

Once the nits in #11 are addressed, I'll RTBC this, and we'll see what a core committer has to say.

yogeshmpawar’s picture

Assigned: Unassigned » yogeshmpawar
yogeshmpawar’s picture

Assigned: yogeshmpawar » Unassigned
Status: Needs work » Needs review
FileSize
4.84 KB
2.03 KB

@Wim Leers - all suggested changes addressed mentioned in comment #11 & also added an interdiff.

Wim Leers’s picture

Status: Needs review » Reviewed & tested by the community

This should be reviewed by @alexpott, who is both core committer and maintainer of the configuration API. If he's okay with this, I'm okay with this.

A CR is still needed, but perhaps it makes more sense for Alex to +1 or -1 the direction before creating the CR.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs tests

I think the plugin should implement DependentPluginInterface no?

  1. +++ b/core/modules/ckeditor/src/Plugin/Editor/CKEditor.php
    @@ -429,4 +429,27 @@ public function buildContentsCssJSSetting(Editor $editor) {
    +  public function calculateEditorDependencies(Editor $editor) {
    +    $dependencies = [];
    +
    +    // Add the module provider of each enabled CKEditor plugins.
    +    $enabled_plugins = array_keys($this->ckeditorPluginManager->getEnabledPluginFiles($editor));
    +    if (!empty($enabled_plugins)) {
    +      $dependencies['module'] = [];
    +
    +      foreach ($enabled_plugins as $plugin_id) {
    +        $ckeditor_plugin_definition = $this->ckeditorPluginManager->getDefinition($plugin_id);
    +        $provider = $ckeditor_plugin_definition['provider'];
    +        if (!in_array($provider, $dependencies['module'])) {
    +          $dependencies['module'][] = $provider;
    +        }
    +      }
    +    }
    +
    +    return $dependencies;
    +  }
    

    I'm a bit concerned that this method doesn't really have much to do with the CkEditor plugin itself and seems to be reaching out for things it itself has not configured.

  2. +++ b/core/modules/editor/src/Entity/Editor.php
    @@ -105,6 +105,10 @@ public function calculateDependencies() {
    +    // Allow the editor to add its dependencies.
    +    $this->addDependencies($this->editorPluginManager()->createInstance($this->editor)->calculateEditorDependencies($this));
    

    This all feels quite convoluted :(

    The approach seems okay but it's an API break. We should add calculateEditorDependencies to a new interface and make CkEditor implement that. And check for the interface in Editor::calculateDependenices. This way other editors won't break and get to opt in to the new functionality.

  3. We need tests of this actually working
Wim Leers’s picture

Thanks for providing clear direction, @alexpott! 🙏

Grimreaper’s picture

Assigned: Unassigned » Grimreaper

Hello,

Thanks for the review.

I will update the patch with @alexpott suggestions.

Grimreaper’s picture

Here is an updated patch.

Thanks for the review.

@alexpott:

I think the plugin should implement DependentPluginInterface no?

The problem is that we need the Editor object in arguments to get the configuration of the Editor plugin.

alexpott’s picture

+++ b/core/modules/editor/src/Entity/Editor.php
@@ -101,10 +102,17 @@ public function calculateDependencies() {
+      $this->addDependencies($editor_plugin_instance->calculateEditorDependencies($this));

This is still looking really odd to me. Passing an instance of the configuration entity to the plugin is kinda the wrong direction. Plugins really should have no knowledge of a configuration entity. Looking at the code in CkEditor::calculateDependencies it is hard to reason why this goes there. However the Editor configuration entity is being passed all over the place through the editor and ckeditor plugin system so it's hard to roll that back now.

In an ideal world the Editor configuration entity is used to configure the Editor and other plugins but the plugins don't know anything about the Editor config entity this separation of concerns means that the plugins can stand on their own feet with the config system.

Atm I can't see any other way of doing this without considerable refactoring and API breaks :(

Grimreaper’s picture

@alexpott, I completly agree with you and I had to follow the same reasonning.

Status: Needs review » Needs work
Grimreaper’s picture

Hum, in my local environment I am using PHPUnit 6 and I don't have the error mentioned in comment 22.

So I don't know how to debug this failure without downgrading PHPUnit.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.6 was released on August 1, 2018 and is the final bugfix release for the Drupal 8.5.x series. Drupal 8.5.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.6.0 on September 5, 2018. (Drupal 8.6.0-rc1 is available for testing.)

Bug reports should be targeted against the 8.6.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.8.x-dev

Drupal 8.6.x will not receive any further development aside from security fixes. Bug reports should be targeted against the 8.8.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.9.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.7 was released on June 3, 2020 and is the final full bugfix release for the Drupal 8.8.x series. Drupal 8.8.x will not receive any further development aside from security fixes. Sites should prepare to update to Drupal 8.9.0 or Drupal 9.0.0 for ongoing support.

Bug reports should be targeted against the 8.9.x-dev branch from now on, and new development or disruptive changes should be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.2.x-dev

Drupal 8 is end-of-life as of November 17, 2021. There will not be further changes made to Drupal 8. Bugfixes are now made to the 9.3.x and higher branches only. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.2.x-dev » 9.3.x-dev
Wim Leers’s picture

Title: CKEditor plugin module dependency not added to text format configuration » CKEditor 4+5 plugin module dependency not added to text format configuration
Related issues: +#2080823: Create API to discover config entities' soft dependencies and use this to present a confirm form on module uninstall

Because this is a problem/oversight in the Editor config entity, the same problem still exists for CKEditor 5 too. Discovered this while responding to #3245720-50: [drupalMedia] Support choosing a view mode for <drupal-media>.

When I started implementing this independently, I ended up doing something very similar to @Grimreaper because AFAICT there's no other way.

This is still looking really odd to me. Passing an instance of the configuration entity to the plugin is kinda the wrong direction. Plugins really should have no knowledge of a configuration entity.

Each text editor config entity has its own configuration: which buttons are enabled, which settings are applied, and so on. So I don't see how this could work any other way? 🤔

In an ideal world the Editor configuration entity is used to configure the Editor and other plugins but the plugins don't know anything about the Editor config entity this separation of concerns means that the plugins can stand on their own feet with the config system.

I don't think this is possible for text editor config entities because the set of plugins to choose from is itself dependent on the text editor plugin being used!

IOW: there's two layers of config dependencies here:

  1. \Drupal\editor\Entity\Editor has a \Drupal\editor\Plugin\EditorPluginInterface plugin configured — this is stored in the top-level editor key in the Editor config entity. In core right now this usually means the ckeditor plugin (\Drupal\ckeditor\Plugin\Editor\CKEditor) is configured. In the future, it'll mean that usually the ckeditor5 plugin (\Drupal\ckeditor5\Plugin\Editor\CKEditor5) is configured.
  2. Each of those two can have plugins configured for them too: for ckeditor it's @CKEditorPlugin plugins that implement \Drupal\ckeditor\CKEditorPluginInterface; for ckeditor5 it's @CKEditor5Plugin plugins that implement \Drupal\ckeditor5\Plugin\CKEditor5PluginInterface.

    Plugins for one text editor plugin do not work for another.

I don't think anything else (in Drupal core) has this multi-level plugin architecture.

I propose we generalize @Grimreaper's DependentEditorPluginInterface proposal into something that isn't Editor-specific. It's conceivable there are other things that need this.

This is also why this comment that was added in 2014 in #2080823: Create API to discover config entities' soft dependencies and use this to present a confirm form on module uninstall is wrong:

    // @todo use EntityWithPluginCollectionInterface so configuration between
    //   config entity and dependency on provider is managed automatically.

The Editor entity does not have a plugin collection directly. It has a single plugin (\Drupal\editor\Plugin\EditorPluginInterface) which may have a plugin collection. (Not every text editor plugin is required to support plugins — a simple one with far less configurability and extensibility would not need it.)

Wim Leers’s picture

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.15 was released on June 1st, 2022 and is the final full bugfix release for the Drupal 9.3.x series. Drupal 9.3.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.4.x-dev branch from now on, and new development or disruptive changes should be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.9 was released on December 7, 2022 and is the final full bugfix release for the Drupal 9.4.x series. Drupal 9.4.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.5.x-dev branch from now on, and new development or disruptive changes should be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Wim Leers credited gordon.

Wim Leers’s picture

This caused problems once again — this time a detailed report by @gordon at #3335155-8: CKEditor 5 toolbar configuration: no admin UI visible, only JSON in a <textarea>.

Wim Leers’s picture

Title: CKEditor 4+5 plugin module dependency not added to text format configuration » CKEditor 5 plugin module dependency not added to text format configuration

CKEditor 4 is gone. But this problem is not.