CKEditor 5 architecture
Same name in other branches
- 9 core/modules/ckeditor5/ckeditor5.api.php \ckeditor5_architecture
- 10 core/modules/ckeditor5/ckeditor5.api.php \ckeditor5_architecture
Overview
The CKEditor 5 module integrates CKEditor 5 with Drupal's filtering and text editor APIs.
Where possible, it uses upstream CKEditor plugins, but it also relies on Drupal-specific CKEditor plugins to ensure a consistent user experience.
Data models
Drupal and CKEditor 5 have very different data models.
Drupal stores blobs of HTML that remains manageable thanks to the use of filters and granular HTML restrictions — crucially this remains manageable thanks to those restrictions but also because Drupal does not need to process, render, understand or otherwise interact with it.
CKEditor 5 Plugins
CKEditor 5 plugins may use either YAML or a PHP attribute for their definitions. A PHP class does not need an attribute if it is defined in yml.
To be discovered, YAML definition files must be named {module_name}.ckeditor5.yml.
# In the MODULE_NAME.ckeditor5.yml file.
MODULE_NAME_marquee:
ckeditor5:
plugins: [PACKAGE.CLASS]
drupal:
label: Marquee
library: MODULE_NAME/ckeditor5.marquee
elements:
- <marquee>
- <marquee behavior>
Declared as an Attribute:
use Drupal\ckeditor5\Attribute\CKEditor5AspectsOfCKEditor5Plugin;
use Drupal\ckeditor5\Attribute\CKEditor5Plugin;
use Drupal\ckeditor5\Attribute\DrupalAspectsOfCKEditor5Plugin;
use Drupal\Core\StringTranslation\TranslatableMarkup;
#[CKEditor5Plugin(
id: 'MODULE_NAME_marquee',
ckeditor5: new CKEditor5AspectsOfCKEditor5Plugin(
plugins: ['PACKAGE.CLASS'],
),
drupal: new DrupalAspectsOfCKEditor5Plugin(
label: new TranslatableMarkup('Marquee'),
library: 'MODULE_NAME/ckeditor5.marquee',
elements: ['<marquee>', '<marquee behavior>'],
),
)]
The metadata relating strictly to the CKEditor 5 plugin's JS code is stored in the 'ckeditor5' key; all other metadata is stored in the 'drupal' key.
If the plugin has a dependency on another module, adding the 'provider' key will prevent the plugin from being loaded if that module is not installed.
All of these can be defined in YAML or attributes. A given plugin should choose one or the other, as a definition can't parse both at once.
Overview of all available plugin definition properties:
- provider: Allows a plugin to have a dependency on another module. If it has a value, a module with a machine name matching that value must be installed for the configured plugin to load.
- ckeditor5.plugins: A list CKEditor 5 JavaScript plugins to load, as '{package.Class}' , such as 'drupalMedia.DrupalMedia'.
- ckeditor5.config: A keyed array of additional values for the constructor of the CKEditor 5 JavaScript plugins being loaded. i.e. this becomes the CKEditor 5 plugin configuration settings (see https://ckeditor.com/docs/ckeditor5/latest/builds/guides/integration/co…) for a given plugin.
- drupal.label: Human-readable name of the CKEditor 5 plugin.
- drupal.library: A Drupal asset library to load with the plugin.
- drupal.admin_library: A Drupal asset library that will load in the text format admin UI when the plugin is available.
- drupal.class: Optional PHP class that makes it possible for the plugin to provide dynamic values, or a configuration UI. The value should be formatted as '\Drupal\{module_name}\Plugin\CKEditor5Plugin\{class_name}' to 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: '<$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. Note that while the syntax is the same, some extra nuance is needed: although this syntax can be used to create an attribute on an element, f.e. (['<marquee behavior>']) creating the `behavior` attribute on `<marquee>`, the tag itself must be creatable as well (['<marquee>']). If a plugin wants the tag and attribute to be created, list both: (['<marquee>', '<marquee behavior>']). Validation logic ensures that a plugin supporting only the creation of attributes cannot be enabled if the tag cannot be created via itself or through another CKEditor 5 plugin.
- drupal.toolbar_items: List of toolbar items the plugin provides. Keyed by a machine name and the value being a pair defining the label:
toolbar_items:
indent:
label: Indent
outdent:
label: Outdent
@encode
- drupal.conditions: Conditions required for the plugin to load (other than
module dependencies, which are defined by the 'provider' property).
Conditions can check for five different things:
- 'toolbarItem': a toolbar item that must be enabled
- 'filter': a filter that must be enabled
- 'imageUploadStatus': TRUE if image upload must be enabled, FALSE if it
must not be enabled
- 'requiresConfiguration': a subset of the configuration for this plugin
that must match (exactly)
- 'plugins': a list of CKEditor 5 Drupal plugin IDs that must be enabled
Plugins requiring more complex conditions, such as requiring multiple
toolbar items or multiple filters, have not yet been identified. If this
need arises, see
https://www.drupal.org/docs/drupal-apis/ckeditor-5-api/overview#conditions.
All of these can be defined in YAML or attributes. A given plugin should
choose one or the other, as a definition can't parse both at once.
If the CKEditor 5 plugin contains translation they can be automatically
loaded by Drupal by adding the dependency to the core/ckeditor5.translations
library to the CKEditor 5 plugin library definition:
@code
# In the MODULE_NAME.libraries.yml file.
marquee:
js:
assets/ckeditor5/marquee/marquee.js: { minified: true }
dependencies:
- core/ckeditor5
- core/ckeditor5.translations
The translations for CKEditor 5 are located in a translations/ subdirectory, Drupal will load the corresponding translation when necessary, located in assets/ckeditor5/marquee/translations/* in this example.
Public API
The CKEditor 5 module provides no public API, other than:
- the attributes and interfaces mentioned above;
- to help implement CKEditor 5 plugins: \Drupal\ckeditor5\Plugin\CKEditor5PluginConfigurableTrait and \Drupal\ckeditor5\Plugin\CKEditor5PluginDefault;
- \Drupal\ckeditor5\Plugin\CKEditor5PluginDefinition, which is used to interact with plugin definitions in hook_ckeditor5_plugin_info_alter();
- to help contributed modules write tests: \Drupal\Tests\ckeditor5\Kernel\CKEditor5ValidationTestTrait and \Drupal\Tests\ckeditor5\Traits\CKEditor5TestTrait;
- to help contributed modules write configuration schemas for configurable plugins, the data types in config/schema/ckeditor5.data_types.yml are likely to be useful. They automatically get validation constraints applied;
- to help contributed modules write validation constraints for configurable plugins, it is strongly recommended to subclass \Drupal\Tests\ckeditor5\Kernel\ValidatorsTest. For very complex validation constraints that need to access text editor and/or format, use \Drupal\ckeditor5\Plugin\Validation\Constraint\TextEditorObjectDependentValidatorTrait.
See also
https://ckeditor.com/ckeditor-5/
\Drupal\text\Plugin\Field\FieldType\TextItemBase
\Drupal\filter\Plugin\Filter\FilterInterface::getHTMLRestrictions()
On the other hand, CKEditor 5 must not only be able to render these blobs, but also allow editing and creating it. This requires a much deeper understanding of that HTML.
CKEditor 5 (in contrast with CKEditor 4) therefore has its own data model to represent this information — that data model is explicitly not HTML.
Therefore all interactions between Drupal and CKEditor 5 need to translate between these different data models.
https://ckeditor.com/docs/ckeditor5/latest/framework/guides/architectur…
ckeditor5.ckeditor5.yml for many Examples of CKEditor 5 plugin configuration as YAML.
The minimally required metadata: the CKEditor 5 plugins to load, the label and the HTML elements it can generate — here's an example for a module providing a Marquee plugin, both in yml or Annotation form:
Declared in the yml File interface:
\Drupal\ckeditor5\Attribute\CKEditor5Plugin
\Drupal\ckeditor5\Attribute\CKEditor5AspectsOfCKEditor5Plugin
\Drupal\ckeditor5\Attribute\DrupalAspectsOfCKEditor5Plugin
File
-
core/
modules/ ckeditor5/ ckeditor5.api.php, line 10
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.