(deprecated) CKEditor 4 API (Drupal 8 and 9)

Body

Documentation for the deprecated CKEditor 4 API in Drupal 8 and 9. For Drupal 10, refer to the CKEditor 5 API documentation.

The CKEditor 5 API is new in Drupal 9.4.

Note this is only covering the CKEditor Drupal 9 module API, not the CKEditor JavaScript library API — for that, see https://ckeditor.com/docs/ckeditor5/latest/index.html.

For high-level information on what this module does, see the Text Editor module documentation.

API features

Ordered by most to least frequently used APIs:

CKEditor 5 CSS

CKEditor 5 does not run in an iframe, unlike CKEditor 4. This means that any CSS file loaded on a page can in theory affect a CKEditor 5 instance. No worries though: CKEditor 5 itself has plenty of CSS resets to prevent unintentional overrides.

For intentional overrides, for example to make the content edited with CKEditor 5 match the styling of your front-end theme, the ckeditor5-stylesheets property can be specified in THEMENAME.info.yml. See the change record for details.

CKEditor 5 Plugin plugins

Add more functionality to CKEditor!

\Drupal\ckeditor5\Plugin\CKEditor5PluginInterface: Drupal plugins that correspond 1:1 to CKEditor 5 plugins, to make Drupal aware of the available CKEditor plugins. Hence the — at first sight — confusing name: CKEditor 5 Plugin plugins, but it actually makes sense!

For CKEditor 4, it was required to create a CKEditor 4 plugin definition: a PHP file. No longer with CKEditor 5. With CKEditor 5, all you need is a MODULENAME.ckeditor5.yml file, which contains CKEditor 5 plugin definitions. It can be as simple as:
 

ckeditor5_code:
  ckeditor5:
    plugins: [basicStyles.Code]
  drupal:
    label: Code
    library: core/ckeditor5.basic
    admin_library: ckeditor5/admin.basic
    toolbar_items:
      code:
        label: Code
    elements:
      - <code>

You can distinguish two clear sections: ckeditor5 (which contains the CKEditor 5 JS plugin-specific aspects) and drupal (which contains the CKEditor 5 Drupal plugin-specific aspects).

See "CKEditor 5 architecture" for more details.

A more complex example would be:

ckeditor5_imageCaption:
  ckeditor5:
    plugins:
      - image.ImageCaption
    config:
      image:
        toolbar: [toggleImageCaption]
  drupal:
    label: Image caption
    elements:
      - <img data-caption>
    conditions:
      toolbarItem: uploadImage
      filter: filter_caption

Here, we can see more nuance for both aspects:

  1. config key-value pair in the ckeditor5 key, to pass on configuration to the CKEditor 5 JS plugin
  2. conditions key-value pair in the drupal key, to indicate to Drupal which conditions have to be met for this CKEditor 5 plugin to be loaded. Also note the absence of a toolbar item! That's because this CKEditor 5 plugin provides a UX that enhances the image functionality in CKEditor 5 whenever the filter_caption filter is enabled (and the uploadImage toolbar item is enabled). This removes the need for complex configuration forms, and allows the CKEditor 5 plugins to detail when exactly they should be enabled.

    If the currently supported conditions are inadequate, please create a new Drupal core issue against the ckeditor5.module component and tag it Contributed project blocker.

Debugging

Drupal 9 includes a customized, optimized build of CKEditor. See "CKEditor 5 development" for how to swap this for a build that is optimized for development and debugging.

Plugin definition updates: post-update hook

When plugin definitions are updated, it's important to understand when which aspects of plugin definitions are evaluated:

  1. Changes to conditions automatically get re-evaluated, because these are evaluated at runtime. For example, a CKEditor 5 plugin that was previously explicitly enabled thanks to a toolbar item that has been enabled will not show up anymore if the plugin definition was modified to also have a filter condition and that filter is not enabled.
  2. Changes to elements do not get automatically synced to filter_html's allowed HTML tags, because that requires changes to the corresponding FilterFormat config entity's data.
  3. Generally speaking all plugin definition changes are applied immediately, with the sole exception of elements, because that requires those same elements to also be allowed by filter_html (if enabled), and that cannot be automatically updated.

You can ensure that elements changes are propagated automatically to each text format/editor that uses this CKEditor 5 plugin: it's possible to update filter_html automatically. This can be done through a MODULENAME.post_update.php file that looks like this:

<?php

use Drupal\editor\Entity\Editor;

/**
 * Your description here.
 */
function MYMODULE_post_update_DESCRIPTION() {
  _ckeditor5_plugin_supports_more_elements_append_to_filter_html_settings('mymodule_myplugin', '<span data-test>');
}

/**
 * Expands filter_html allowed tags for CKE5 plugin that supports more HTML.
 *
 * @param string $cke5_plugin_id
 *   The CKEditor 5 plugin ID which supports more HTML after an update.
 * @param string $allowed_html_to_append
 *   The string to append to `filter_html`'s `allowed_html` setting.
 */
function _ckeditor5_plugin_supports_more_elements_append_to_filter_html_settings(string $cke5_plugin_id, string $allowed_html_to_append) {
  $cke5_plugin_manager = \Drupal::service('plugin.manager.ckeditor5.plugin');
  assert($cke5_plugin_manager instanceof \Drupal\ckeditor5\Plugin\CKEditor5PluginManagerInterface);

  // 1. Determine which text editors use the updated CKEditor 5 plugin.
  $affected_editors = [];
  foreach (Editor::loadMultiple() as $editor) {
    // Text editors not using CKEditor 5 cannot be affected.
    if ($editor->getEditor() !== 'ckeditor5') {
      continue;
    }
    // Ask the plugin manager which CKEditor 5 plugins are enabled; this works
    // for every plugin, no matter if they have toolbar items or not, conditions
    // or not, et cetera.
    $enabled_cke5_plugin_ids = array_keys($cke5_plugin_manager->getEnabledDefinitions($editor));
    if (in_array($cke5_plugin_id, $enabled_cke5_plugin_ids, TRUE)) {
      $affected_editors[] = $editor;
    }
  }

  // 2. Update the corresponding text formats' `filter_html` configuration, if
  // they are using that filter plugin.
  foreach ($affected_editors as $editor) {
    $format = $editor->getFilterFormat();
    // Text formats not using `filter_html` filter do not need to be updated.
    if (!$format->filters('filter_html')->status) {
      continue;
    }
    // Append to "Allowed HTML tags" setting.
    $filter_html_config = $format->filters('filter_html')->getConfiguration();
    $filter_html_config['settings']['allowed_html'] .= ' ' . trim($allowed_html_to_append);
    $format->setFilterConfig('filter_html', $filter_html_config);
    // Save updated text format.
    $format->save();
  }
}
 

The _ckeditor5_plugin_supports_more_elements_append_to_filter_html_settings() helper does all the work for you. All you should need to do is update MYMODULE_post_update_DESCRIPTION() to match your module name, plugin ID, etc.

Knowledge Category