summaryrefslogtreecommitdiffstatshomepage
path: root/core/modules
diff options
context:
space:
mode:
Diffstat (limited to 'core/modules')
-rw-r--r--core/modules/ckeditor/ckeditor.admin.inc143
-rw-r--r--core/modules/ckeditor/ckeditor.api.php61
-rw-r--r--core/modules/ckeditor/ckeditor.info.yml7
-rw-r--r--core/modules/ckeditor/ckeditor.libraries.yml99
-rw-r--r--core/modules/ckeditor/ckeditor.module292
-rw-r--r--core/modules/ckeditor/ckeditor.post_update.php15
-rw-r--r--core/modules/ckeditor/ckeditor.services.yml4
-rw-r--r--core/modules/ckeditor/config/schema/ckeditor.schema.yml52
-rw-r--r--core/modules/ckeditor/css/ckeditor-iframe.css25
-rw-r--r--core/modules/ckeditor/css/ckeditor.admin.css334
-rw-r--r--core/modules/ckeditor/css/ckeditor.css31
-rw-r--r--core/modules/ckeditor/css/plugins/drupalimagecaption/ckeditor.drupalimagecaption.css21
-rw-r--r--core/modules/ckeditor/css/plugins/drupalmedia/ckeditor.drupalmedia.css41
-rw-r--r--core/modules/ckeditor/css/plugins/language/ckeditor.language.css19
-rw-r--r--core/modules/ckeditor/js/ckeditor.admin.es6.js565
-rw-r--r--core/modules/ckeditor/js/ckeditor.admin.js289
-rw-r--r--core/modules/ckeditor/js/ckeditor.drupalimage.admin.es6.js58
-rw-r--r--core/modules/ckeditor/js/ckeditor.drupalimage.admin.js40
-rw-r--r--core/modules/ckeditor/js/ckeditor.drupalmedia.theme.es6.js17
-rw-r--r--core/modules/ckeditor/js/ckeditor.drupalmedia.theme.js10
-rw-r--r--core/modules/ckeditor/js/ckeditor.es6.js392
-rw-r--r--core/modules/ckeditor/js/ckeditor.js212
-rw-r--r--core/modules/ckeditor/js/ckeditor.language.admin.es6.js20
-rw-r--r--core/modules/ckeditor/js/ckeditor.language.admin.js23
-rw-r--r--core/modules/ckeditor/js/ckeditor.off-canvas-css-reset.es6.js112
-rw-r--r--core/modules/ckeditor/js/ckeditor.off-canvas-css-reset.js58
-rw-r--r--core/modules/ckeditor/js/ckeditor.stylescombo.admin.es6.js134
-rw-r--r--core/modules/ckeditor/js/ckeditor.stylescombo.admin.js81
-rw-r--r--core/modules/ckeditor/js/models/Model.es6.js73
-rw-r--r--core/modules/ckeditor/js/models/Model.js25
-rw-r--r--core/modules/ckeditor/js/plugins/drupalimage/icons/drupalimage.pngbin470 -> 0 bytes
-rw-r--r--core/modules/ckeditor/js/plugins/drupalimage/icons/hidpi/drupalimage.pngbin1415 -> 0 bytes
-rw-r--r--core/modules/ckeditor/js/plugins/drupalimage/plugin.es6.js400
-rw-r--r--core/modules/ckeditor/js/plugins/drupalimage/plugin.js249
-rw-r--r--core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.es6.js353
-rw-r--r--core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.js214
-rw-r--r--core/modules/ckeditor/js/plugins/drupallink/icons/drupallink.pngbin328 -> 0 bytes
-rw-r--r--core/modules/ckeditor/js/plugins/drupallink/icons/drupalunlink.pngbin312 -> 0 bytes
-rw-r--r--core/modules/ckeditor/js/plugins/drupallink/icons/hidpi/drupallink.pngbin1231 -> 0 bytes
-rw-r--r--core/modules/ckeditor/js/plugins/drupallink/icons/hidpi/drupalunlink.pngbin1656 -> 0 bytes
-rw-r--r--core/modules/ckeditor/js/plugins/drupallink/plugin.es6.js364
-rw-r--r--core/modules/ckeditor/js/plugins/drupallink/plugin.js272
-rw-r--r--core/modules/ckeditor/js/plugins/drupalmedia/plugin.es6.js499
-rw-r--r--core/modules/ckeditor/js/plugins/drupalmedia/plugin.js345
-rw-r--r--core/modules/ckeditor/js/plugins/drupalmedialibrary/icons/drupalmedialibrary.pngbin283 -> 0 bytes
-rw-r--r--core/modules/ckeditor/js/plugins/drupalmedialibrary/icons/hidpi/drupalmedialibrary.pngbin620 -> 0 bytes
-rw-r--r--core/modules/ckeditor/js/plugins/drupalmedialibrary/plugin.es6.js76
-rw-r--r--core/modules/ckeditor/js/plugins/drupalmedialibrary/plugin.js68
-rw-r--r--core/modules/ckeditor/js/views/AuralView.es6.js266
-rw-r--r--core/modules/ckeditor/js/views/AuralView.js170
-rw-r--r--core/modules/ckeditor/js/views/ControllerView.es6.js419
-rw-r--r--core/modules/ckeditor/js/views/ControllerView.js236
-rw-r--r--core/modules/ckeditor/js/views/KeyboardView.es6.js307
-rw-r--r--core/modules/ckeditor/js/views/KeyboardView.js152
-rw-r--r--core/modules/ckeditor/js/views/VisualView.es6.js302
-rw-r--r--core/modules/ckeditor/js/views/VisualView.js152
-rw-r--r--core/modules/ckeditor/src/Ajax/AddStyleSheetCommand.php64
-rw-r--r--core/modules/ckeditor/src/Annotation/CKEditorPlugin.php44
-rw-r--r--core/modules/ckeditor/src/CKEditorPluginBase.php93
-rw-r--r--core/modules/ckeditor/src/CKEditorPluginButtonsInterface.php58
-rw-r--r--core/modules/ckeditor/src/CKEditorPluginConfigurableInterface.php46
-rw-r--r--core/modules/ckeditor/src/CKEditorPluginContextualInterface.php42
-rw-r--r--core/modules/ckeditor/src/CKEditorPluginCssInterface.php39
-rw-r--r--core/modules/ckeditor/src/CKEditorPluginInterface.php102
-rw-r--r--core/modules/ckeditor/src/CKEditorPluginManager.php222
-rw-r--r--core/modules/ckeditor/src/Plugin/CKEditorPlugin/DrupalImage.php88
-rw-r--r--core/modules/ckeditor/src/Plugin/CKEditorPlugin/DrupalImageCaption.php129
-rw-r--r--core/modules/ckeditor/src/Plugin/CKEditorPlugin/DrupalLink.php62
-rw-r--r--core/modules/ckeditor/src/Plugin/CKEditorPlugin/DrupalMedia.php136
-rw-r--r--core/modules/ckeditor/src/Plugin/CKEditorPlugin/DrupalMediaLibrary.php171
-rw-r--r--core/modules/ckeditor/src/Plugin/CKEditorPlugin/Internal.php609
-rw-r--r--core/modules/ckeditor/src/Plugin/CKEditorPlugin/Language.php136
-rw-r--r--core/modules/ckeditor/src/Plugin/CKEditorPlugin/StylesCombo.php169
-rw-r--r--core/modules/ckeditor/src/Plugin/Editor/CKEditor.php491
-rw-r--r--core/modules/ckeditor/templates/ckeditor-settings-toolbar.html.twig75
-rw-r--r--core/modules/ckeditor/tests/modules/ckeditor_test.info.yml5
-rw-r--r--core/modules/ckeditor/tests/modules/ckeditor_test.libraries.yml5
-rw-r--r--core/modules/ckeditor/tests/modules/ckeditor_test.module15
-rw-r--r--core/modules/ckeditor/tests/modules/ckeditor_test.routing.yml14
-rw-r--r--core/modules/ckeditor/tests/modules/config/schema/ckeditor_test.schema.yml16
-rw-r--r--core/modules/ckeditor/tests/modules/css/test.css3
-rw-r--r--core/modules/ckeditor/tests/modules/js/ajax-css.es6.js19
-rw-r--r--core/modules/ckeditor/tests/modules/js/ajax-css.js17
-rw-r--r--core/modules/ckeditor/tests/modules/src/CkeditorOffCanvasTestController.php33
-rw-r--r--core/modules/ckeditor/tests/modules/src/Form/AjaxCssForm.php138
-rw-r--r--core/modules/ckeditor/tests/modules/src/Plugin/CKEditorPlugin/Llama.php86
-rw-r--r--core/modules/ckeditor/tests/modules/src/Plugin/CKEditorPlugin/LlamaButton.php35
-rw-r--r--core/modules/ckeditor/tests/modules/src/Plugin/CKEditorPlugin/LlamaContextual.php41
-rw-r--r--core/modules/ckeditor/tests/modules/src/Plugin/CKEditorPlugin/LlamaContextualAndButton.php76
-rw-r--r--core/modules/ckeditor/tests/modules/src/Plugin/CKEditorPlugin/LlamaCss.php46
-rw-r--r--core/modules/ckeditor/tests/modules/src/Plugin/Filter/TestAttributeFilter.php38
-rw-r--r--core/modules/ckeditor/tests/src/Functional/CKEditorAdminTest.php306
-rw-r--r--core/modules/ckeditor/tests/src/Functional/CKEditorLoadingTest.php233
-rw-r--r--core/modules/ckeditor/tests/src/Functional/CKEditorStylesComboAdminTest.php114
-rw-r--r--core/modules/ckeditor/tests/src/Functional/CKEditorStylesComboTranslationTest.php96
-rw-r--r--core/modules/ckeditor/tests/src/Functional/CKEditorToolbarButtonTest.php93
-rw-r--r--core/modules/ckeditor/tests/src/FunctionalJavascript/AjaxCssTest.php128
-rw-r--r--core/modules/ckeditor/tests/src/FunctionalJavascript/BigPipeRegressionTest.php119
-rw-r--r--core/modules/ckeditor/tests/src/FunctionalJavascript/CKEditor5CKEditor4Compatibility.php139
-rw-r--r--core/modules/ckeditor/tests/src/FunctionalJavascript/CKEditorIntegrationTest.php244
-rw-r--r--core/modules/ckeditor/tests/src/FunctionalJavascript/FormErrorHandlerCKEditorTest.php120
-rw-r--r--core/modules/ckeditor/tests/src/FunctionalJavascript/MediaLibraryTest.php327
-rw-r--r--core/modules/ckeditor/tests/src/FunctionalJavascript/MediaTest.php1603
-rw-r--r--core/modules/ckeditor/tests/src/Kernel/CKEditorPluginManagerTest.php165
-rw-r--r--core/modules/ckeditor/tests/src/Kernel/CKEditorTest.php562
-rw-r--r--core/modules/ckeditor/tests/src/Kernel/Plugin/CKEditorPlugin/InternalTest.php140
-rw-r--r--core/modules/ckeditor/tests/src/Traits/CKEditorAdminSortTrait.php34
-rw-r--r--core/modules/ckeditor/tests/src/Traits/CKEditorTestTrait.php111
-rw-r--r--core/modules/ckeditor/tests/src/Unit/CKEditorPluginManagerTest.php97
-rw-r--r--core/modules/ckeditor/tests/src/Unit/Plugin/CKEditorPlugin/LanguageTest.php65
-rw-r--r--core/modules/ckeditor5/tests/src/Kernel/CKEditor4to5UpgradeCompletenessTest.php275
-rw-r--r--core/modules/editor/editor.admin.inc3
-rw-r--r--core/modules/editor/editor.module4
-rw-r--r--core/modules/editor/src/Annotation/Editor.php2
-rw-r--r--core/modules/system/tests/themes/test_ckeditor_stylesheets_drupal_root/test_ckeditor_stylesheets_drupal_root.info.yml2
115 files changed, 4 insertions, 16438 deletions
diff --git a/core/modules/ckeditor/ckeditor.admin.inc b/core/modules/ckeditor/ckeditor.admin.inc
deleted file mode 100644
index b344edff9d4d..000000000000
--- a/core/modules/ckeditor/ckeditor.admin.inc
+++ /dev/null
@@ -1,143 +0,0 @@
-<?php
-
-/**
- * @file
- * Callbacks and theming for the CKEditor toolbar configuration UI.
- */
-
-use Drupal\Component\Utility\Html;
-use Drupal\Core\Template\Attribute;
-use Drupal\Core\Language\LanguageInterface;
-
-/**
- * Prepares variables for CKEditor settings toolbar templates.
- *
- * Default template: ckeditor-settings-toolbar.html.twig.
- *
- * @param array $variables
- * An associative array containing:
- * - editor: An editor object.
- * - plugins: A list of plugins.
- * - active_buttons: A list of disabled buttons.
- * - disabled_buttons: A list of disabled buttons.
- * - multiple_buttons: A list of multiple buttons that may be added multiple
- * times.
- */
-function template_preprocess_ckeditor_settings_toolbar(&$variables) {
- $language_interface = \Drupal::languageManager()->getCurrentLanguage();
-
- // Create lists of active and disabled buttons.
- $editor = $variables['editor'];
- $plugins = $variables['plugins'];
- $buttons = [];
- $multiple_buttons = [];
- foreach ($plugins as $plugin_buttons) {
- foreach ($plugin_buttons as $button_name => $button) {
- $button['name'] = $button_name;
- if (!empty($button['multiple'])) {
- $multiple_buttons[$button_name] = $button;
- }
- $buttons[$button_name] = $button;
- }
- }
- $button_groups = [];
- $active_buttons = [];
- $settings = $editor->getSettings();
- foreach ($settings['toolbar']['rows'] as $row_number => $row) {
- $button_groups[$row_number] = [];
- foreach ($row as $group) {
- foreach ($group['items'] as $button_name) {
- if (isset($buttons[$button_name])) {
- // Save a reference to the button's configured toolbar group.
- $buttons[$button_name]['group'] = $group['name'];
- $active_buttons[$row_number][] = $buttons[$button_name];
- if (empty($buttons[$button_name]['multiple'])) {
- unset($buttons[$button_name]);
- }
- // Create a list of all the toolbar button groups.
- if (!in_array($group['name'], $button_groups[$row_number])) {
- array_push($button_groups[$row_number], $group['name']);
- }
- }
- }
- }
- }
- $disabled_buttons = array_diff_key($buttons, $multiple_buttons);
-
- $rtl = $language_interface->getDirection() === LanguageInterface::DIRECTION_RTL ? '_rtl' : '';
-
- $build_button_item = function ($button, $rtl) {
- // Value of the button item.
- if (isset($button['image_alternative' . $rtl])) {
- $value = $button['image_alternative' . $rtl];
- }
- elseif (isset($button['image_alternative'])) {
- $value = $button['image_alternative'];
- }
- elseif (isset($button['image']) || isset($button['image' . $rtl])) {
- $value = [
- '#theme' => 'image',
- '#uri' => $button['image' . $rtl] ?? $button['image'],
- '#title' => $button['label'],
- '#prefix' => '<a href="#" role="button" title="' . $button['label'] . '" aria-label="' . $button['label'] . '"><span class="cke_button_icon">',
- '#suffix' => '</span></a>',
- ];
- }
- else {
- $value = '?';
- }
-
- // Build the button attributes.
- $attributes = [
- 'data-drupal-ckeditor-button-name' => $button['name'],
- ];
- if (!empty($button['attributes'])) {
- $attributes = array_merge($attributes, $button['attributes']);
- }
-
- // Build the button item.
- $button_item = [
- 'value' => $value,
- 'attributes' => new Attribute($attributes),
- ];
- // If this button has group information, add it to the attributes.
- if (!empty($button['group'])) {
- $button_item['group'] = $button['group'];
- }
-
- // Set additional flag on the button if it can occur multiple times.
- if (!empty($button['multiple'])) {
- $button_item['multiple'] = TRUE;
- }
-
- return $button_item;
- };
-
- // Assemble list of disabled buttons (which are always a single row).
- $variables['active_buttons'] = [];
- foreach ($active_buttons as $row_number => $button_row) {
- foreach ($button_groups[$row_number] as $group_name) {
- $group_name = (string) $group_name;
- $variables['active_buttons'][$row_number][$group_name] = [
- 'group_name_class' => Html::getClass($group_name),
- 'buttons' => [],
- ];
- $buttons = array_filter($button_row, function ($button) use ($group_name) {
- return (string) $button['group'] === $group_name;
- });
- foreach ($buttons as $button) {
- $variables['active_buttons'][$row_number][$group_name]['buttons'][] = $build_button_item($button, $rtl);
- }
- }
- }
- // Assemble list of disabled buttons (which are always a single row).
- $variables['disabled_buttons'] = [];
- foreach ($disabled_buttons as $button) {
- $variables['disabled_buttons'][] = $build_button_item($button, $rtl);
- }
- // Assemble list of multiple buttons that may be added multiple times.
- $variables['multiple_buttons'] = [];
- foreach ($multiple_buttons as $button) {
- $variables['multiple_buttons'][] = $build_button_item($button, $rtl);
- }
-}
diff --git a/core/modules/ckeditor/ckeditor.api.php b/core/modules/ckeditor/ckeditor.api.php
deleted file mode 100644
index 657482b1362b..000000000000
--- a/core/modules/ckeditor/ckeditor.api.php
+++ /dev/null
@@ -1,61 +0,0 @@
-<?php
-
-/**
- * @file
- * Documentation for CKEditor module APIs.
- */
-
-use Drupal\editor\Entity\Editor;
-
-/**
- * @addtogroup hooks
- * @{
- */
-
-/**
- * Modify the list of available CKEditor plugins.
- *
- * This hook may be used to modify plugin properties after they have been
- * specified by other modules.
- *
- * @param $plugins
- * An array of all the existing plugin definitions, passed by reference.
- *
- * @see \Drupal\ckeditor\CKEditorPluginManager
- */
-function hook_ckeditor_plugin_info_alter(array &$plugins) {
- $plugins['someplugin']['label'] = t('Better name');
-}
-
-/**
- * Modify the list of CSS files that will be added to a CKEditor instance.
- *
- * Modules may use this hook to provide their own custom CSS file without
- * providing a CKEditor plugin. This list of CSS files is only used in the
- * iframe versions of CKEditor.
- *
- * Front-end themes (and base themes) can easily specify CSS files to be used in
- * iframe instances of CKEditor through an entry in their .info.yml file:
- *
- * @code
- * ckeditor_stylesheets:
- * - css/ckeditor-iframe.css
- * @endcode
- *
- * @param array &$css
- * An array of CSS files, passed by reference. This is a flat list of file
- * paths which can be either relative to the Drupal root or external URLs.
- * @param $editor
- * The text editor object as returned by editor_load(), for which these files
- * are being loaded. Based on this information, it is possible to load the
- * corresponding text format object.
- *
- * @see _ckeditor_theme_css()
- */
-function hook_ckeditor_css_alter(array &$css, Editor $editor) {
- $css[] = \Drupal::service('extension.list.module')->getPath('mymodule') . '/css/mymodule-ckeditor.css';
-}
-
-/**
- * @} End of "addtogroup hooks".
- */
diff --git a/core/modules/ckeditor/ckeditor.info.yml b/core/modules/ckeditor/ckeditor.info.yml
deleted file mode 100644
index f915250a81f0..000000000000
--- a/core/modules/ckeditor/ckeditor.info.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-name: CKEditor
-type: module
-description: "WYSIWYG editing for rich text fields using CKEditor."
-package: Core
-version: VERSION
-dependencies:
- - drupal:editor
diff --git a/core/modules/ckeditor/ckeditor.libraries.yml b/core/modules/ckeditor/ckeditor.libraries.yml
deleted file mode 100644
index f71b90c611df..000000000000
--- a/core/modules/ckeditor/ckeditor.libraries.yml
+++ /dev/null
@@ -1,99 +0,0 @@
-drupal.ckeditor:
- version: VERSION
- js:
- js/ckeditor.js: {}
- js/ckeditor.off-canvas-css-reset.js: {}
- css:
- state:
- css/ckeditor.css: {}
- dependencies:
- - core/jquery
- - core/drupal
- - core/drupalSettings
- - core/drupal.debounce
- - core/drupal.displace
- - core/ckeditor
- - editor/drupal.editor
-
-drupal.ckeditor.plugins.drupalimagecaption:
- version: VERSION
- css:
- component:
- css/plugins/drupalimagecaption/ckeditor.drupalimagecaption.css: {}
- dependencies:
- - filter/caption
-
-drupal.ckeditor.plugins.language:
- version: VERSION
- css:
- component:
- css/plugins/language/ckeditor.language.css: {}
-
-drupal.ckeditor.admin:
- version: VERSION
- js:
- # Core.
- js/ckeditor.admin.js: {}
- # Models.
- js/models/Model.js: {}
- # Views.
- js/views/AuralView.js: {}
- js/views/KeyboardView.js: {}
- js/views/ControllerView.js: {}
- js/views/VisualView.js: {}
- css:
- theme:
- css/ckeditor.admin.css: {}
- /core/assets/vendor/ckeditor/skins/moono-lisa/editor.css: {}
- dependencies:
- - core/jquery
- - core/drupal
- - core/drupalSettings
- - core/once
- - core/internal.backbone
- - core/drupal.dialog
- - core/drupal.announce
- - core/ckeditor
- - core/sortable
- - editor/drupal.editor.admin
- # Ensure to run after core/drupal.vertical-tabs.
- - core/drupal.vertical-tabs
-
-drupal.ckeditor.drupalimage.admin:
- version: VERSION
- js:
- js/ckeditor.drupalimage.admin.js: {}
- dependencies:
- - core/jquery
- - core/drupal
- - core/once
- - core/drupal.vertical-tabs
- - core/drupalSettings
-
-drupal.ckeditor.stylescombo.admin:
- version: VERSION
- js:
- js/ckeditor.stylescombo.admin.js: {}
- dependencies:
- - core/jquery
- - core/drupal
- - core/drupal.vertical-tabs
- - core/drupalSettings
- # Ensure to run after ckeditor/drupal.ckeditor.admin.
- - ckeditor/drupal.ckeditor.admin
-
-drupal.ckeditor.language.admin:
- version: VERSION
- js:
- js/ckeditor.language.admin.js: {}
- dependencies:
- - core/jquery
- - core/drupal
- - core/drupal.vertical-tabs
-
-drupal.ckeditor.plugins.drupalmedia:
- version: VERSION
- js:
- js/ckeditor.drupalmedia.theme.js: {}
- dependencies:
- - core/drupal
diff --git a/core/modules/ckeditor/ckeditor.module b/core/modules/ckeditor/ckeditor.module
deleted file mode 100644
index babe27555e1a..000000000000
--- a/core/modules/ckeditor/ckeditor.module
+++ /dev/null
@@ -1,292 +0,0 @@
-<?php
-
-/**
- * @file
- * Provides integration with the CKEditor WYSIWYG editor.
- */
-
-use Drupal\Core\Url;
-use Drupal\Component\Serialization\Json;
-use Drupal\Component\Utility\UrlHelper;
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Routing\RouteMatchInterface;
-use Drupal\ckeditor\CKEditorPluginButtonsInterface;
-use Drupal\ckeditor\CKEditorPluginContextualInterface;
-use Drupal\Core\StringTranslation\TranslatableMarkup;
-use Drupal\editor\Entity\Editor;
-use Drupal\editor\EditorInterface;
-
-/**
- * Implements hook_help().
- */
-function ckeditor_help($route_name, RouteMatchInterface $route_match) {
- switch ($route_name) {
- case 'help.page.ckeditor':
- $output = '';
- $output .= '<h3>' . t('About') . '</h3>';
- $output .= '<p>' . t('The CKEditor module provides a highly-accessible, highly-usable visual text editor and adds a toolbar to text fields. Users can use buttons to format content and to create semantically correct and valid HTML. The CKEditor module uses the framework provided by the <a href=":text_editor">Text Editor module</a>. It requires JavaScript to be enabled in the browser. For more information, see the <a href=":doc_url">online documentation for the CKEditor module</a> and the <a href=":cke_url">CKEditor website</a>.', [':doc_url' => 'https://www.drupal.org/documentation/modules/ckeditor', ':cke_url' => 'http://ckeditor.com', ':text_editor' => Url::fromRoute('help.page', ['name' => 'editor'])->toString()]) . '</p>';
- $output .= '<h3>' . t('Uses') . '</h3>';
- $output .= '<dl>';
- $output .= '<dt>' . t('Enabling CKEditor for individual text formats') . '</dt>';
- $output .= '<dd>' . t('CKEditor has to be enabled and configured separately for individual text formats from the <a href=":formats">Text formats and editors page</a> because the filter settings for each text format can be different. For more information, see the <a href=":text_editor">Text Editor help page</a> and <a href=":filter">Filter help page</a>.', [':formats' => Url::fromRoute('filter.admin_overview')->toString(), ':text_editor' => Url::fromRoute('help.page', ['name' => 'editor'])->toString(), ':filter' => Url::fromRoute('help.page', ['name' => 'filter'])->toString()]) . '</dd>';
- $output .= '<dt>' . t('Configuring the toolbar') . '</dt>';
- $output .= '<dd>' . t('When CKEditor is chosen from the <em>Text editor</em> drop-down menu, its toolbar configuration is displayed. You can add and remove buttons from the <em>Active toolbar</em> by dragging and dropping them, and additional rows can be added to organize the buttons.') . '</dd>';
- $output .= '<dt>' . t('Formatting content') . '</dt>';
- $output .= '<dd>' . t('CKEditor only allow users to format content in accordance with the filter configuration of the specific text format. If a text format excludes certain HTML tags, the corresponding toolbar buttons are not displayed to users when they edit a text field in this format. For more information see the <a href=":filter">Filter help page</a>.', [':filter' => Url::fromRoute('help.page', ['name' => 'filter'])->toString()]) . '</dd>';
- $output .= '<dt>' . t('Toggling between formatted text and HTML source') . '</dt>';
- $output .= '<dd>' . t('If the <em>Source</em> button is available in the toolbar, users can click this button to disable the visual editor and edit the HTML source directly. After toggling back, the visual editor uses the allowed HTML tags to format the text — independent of whether buttons for these tags are available in the toolbar. If the text format is set to <em>limit the use of HTML tags</em>, then all excluded tags will be stripped out of the HTML source when the user toggles back to the text editor.') . '</dd>';
- $output .= '<dt>' . t('Check my spelling as I type') . '</dt>';
- $output .= '<dd>' . t("By default, CKEditor is configured to leverage your browser's spell check capability. Make sure your browser's spell checker is enabled in your browser's settings. To access suggested corrections for misspelled words, it may be necessary to hold the <em>Control</em> or <em>command</em> (Mac) key while right-clicking the misspelling.") . '</dd>';
- $output .= '<dt>' . t('Accessibility features') . '</dt>';
- $output .= '<dd>' . t('The built in WYSIWYG editor (CKEditor) comes with a number of <a href=":features">accessibility features</a>. CKEditor comes with built in <a href=":shortcuts">keyboard shortcuts</a>, which can be beneficial for both power users and keyboard only users.', [':features' => 'http://docs.ckeditor.com/#!/guide/dev_a11y', ':shortcuts' => 'http://docs.ckeditor.com/#!/guide/dev_shortcuts']) . '</dd>';
- $output .= '<dt>' . t('Generating accessible content') . '</dt>';
- $output .= '<dd>';
- $output .= '<ul>';
- $output .= '<li>' . t('HTML tables can be created with table headers and caption/summary elements.') . '</li>';
- $output .= '<li>' . t('Alt text is required by default on images added through CKEditor (note that this can be overridden).') . '</li>';
- $output .= '<li>' . t('Semantic HTML5 figure/figcaption are available to add captions to images.') . '</li>';
- $output .= '<li>' . t('To support multilingual page content, CKEditor can be configured to include a language button in the toolbar.') . '</li>';
- $output .= '</ul>';
- $output .= '</dd>';
- $output .= '</dl>';
- return $output;
- }
-}
-
-/**
- * Implements hook_theme().
- */
-function ckeditor_theme() {
- return [
- 'ckeditor_settings_toolbar' => [
- 'file' => 'ckeditor.admin.inc',
- 'variables' => ['editor' => NULL, 'plugins' => NULL],
- ],
- ];
-}
-
-/**
- * Implements hook_ckeditor_css_alter().
- */
-function ckeditor_ckeditor_css_alter(array &$css, Editor $editor) {
- if (!$editor->hasAssociatedFilterFormat()) {
- return;
- }
-
- // Add the filter caption CSS if the text format associated with this text
- // editor uses the filter_caption filter. This is used by the included
- // CKEditor DrupalImageCaption plugin.
- if ($editor->getFilterFormat()->filters('filter_caption')->status) {
- $css[] = \Drupal::service('extension.list.module')->getPath('filter') . '/css/filter.caption.css';
- }
-}
-
-/**
- * Retrieves the default theme's CKEditor stylesheets.
- *
- * Themes may specify iframe-specific CSS files for use with CKEditor by
- * including a "ckeditor_stylesheets" key in their .info.yml file.
- *
- * @code
- * ckeditor_stylesheets:
- * - css/ckeditor-iframe.css
- * @endcode
- */
-function _ckeditor_theme_css($theme = NULL) {
- $css = [];
- if (!isset($theme)) {
- $theme = \Drupal::config('system.theme')->get('default');
- }
- /** @var \Drupal\Core\Extension\ThemeExtensionList $theme_list */
- $theme_list = \Drupal::service('extension.list.theme');
- if (isset($theme) && $theme_path = $theme_list->getPath($theme)) {
- $info = $theme_list->getExtensionInfo($theme);
- if (isset($info['ckeditor_stylesheets'])) {
- $css = $info['ckeditor_stylesheets'];
- foreach ($css as $key => $url) {
- // CSS url is external.
- if (UrlHelper::isExternal($url)) {
- $css[$key] = $url;
- }
- // CSS url is relative to Drupal root.
- elseif ($url[0] === '/') {
- $css[$key] = substr($url, 1);
- }
- // CSS url is relative to theme.
- else {
- $css[$key] = $theme_path . '/' . $url;
- }
- }
- }
- if (isset($info['base theme'])) {
- $css = array_merge(_ckeditor_theme_css($info['base theme']), $css);
- }
- }
- return $css;
-}
-
-/**
- * Gets all enabled CKEditor 4 plugins.
- *
- * @param \Drupal\editor\EditorInterface $editor
- * A text editor config entity configured to use CKEditor 4.
- *
- * @return string[]
- * The enabled CKEditor 4 plugin IDs.
- *
- * @internal
- */
-function _ckeditor_get_enabled_plugins(EditorInterface $editor): array {
- assert($editor->getEditor() === 'ckeditor');
-
- $cke4_plugin_manager = \Drupal::service('plugin.manager.ckeditor.plugin');
-
- // This is largely copied from the CKEditor 4 plugin manager, because it
- // unfortunately does not provide the API this needs.
- // @see \Drupal\ckeditor\CKEditorPluginManager::getEnabledPluginFiles()
- $plugins = array_keys($cke4_plugin_manager->getDefinitions());
- $toolbar_buttons = $cke4_plugin_manager->getEnabledButtons($editor);
- $enabled_plugins = [];
- $additional_plugins = [];
- foreach ($plugins as $plugin_id) {
- $plugin = $cke4_plugin_manager->createInstance($plugin_id);
-
- $enabled = FALSE;
- // Plugin is enabled if it provides a button that has been enabled.
- if ($plugin instanceof CKEditorPluginButtonsInterface) {
- $plugin_buttons = array_keys($plugin->getButtons());
- $enabled = (count(array_intersect($toolbar_buttons, $plugin_buttons)) > 0);
- }
- // Otherwise plugin is enabled if it declares itself as enabled.
- if (!$enabled && $plugin instanceof CKEditorPluginContextualInterface) {
- $enabled = $plugin->isEnabled($editor);
- }
-
- if ($enabled) {
- $enabled_plugins[$plugin_id] = $plugin_id;
- // Check if this plugin has dependencies that should be considered
- // enabled.
- $additional_plugins = array_merge($additional_plugins, array_diff($plugin->getDependencies($editor), $additional_plugins));
- }
- }
-
- // Add the list of dependent plugins.
- foreach ($additional_plugins as $plugin_id) {
- $enabled_plugins[$plugin_id] = $plugin_id;
- }
-
- return $enabled_plugins;
-}
-
-/**
- * Implements hook_library_info_alter().
- */
-function ckeditor_library_info_alter(&$libraries, $extension) {
- // Pass Drupal's JS cache-busting string via settings along to CKEditor.
- // @see http://docs.ckeditor.com/#!/api/CKEDITOR-property-timestamp
- if ($extension === 'ckeditor' && isset($libraries['drupal.ckeditor'])) {
- $query_string = \Drupal::state()->get('system.css_js_query_string', '0');
- $libraries['drupal.ckeditor']['drupalSettings']['ckeditor']['timestamp'] = $query_string;
- }
-}
-
-/**
- * Implements hook_form_FORM_ID_alter().
- */
-function ckeditor_form_filter_format_edit_form_alter(array &$form, FormStateInterface $form_state, $form_id) {
- // Add an additional validate callback so we can ensure the media_embed filter
- // is enabled when the DrupalMediaLibrary button is enabled.
- $form['#validate'][] = 'ckeditor_filter_format_edit_form_validate';
-}
-
-/**
- * Implements hook_form_FORM_ID_alter().
- */
-function ckeditor_form_filter_format_add_form_alter(array &$form, FormStateInterface $form_state, $form_id) {
- // Add an additional validate callback so we can ensure the media_embed filter
- // is enabled when the DrupalMediaLibrary button is enabled.
- $form['#validate'][] = 'ckeditor_filter_format_edit_form_validate';
-}
-
-/**
- * Validate callback to ensure the DrupalMediaLibrary button can work correctly.
- */
-function ckeditor_filter_format_edit_form_validate($form, FormStateInterface $form_state) {
- if ($form_state->getTriggeringElement()['#name'] !== 'op') {
- return;
- }
-
- // The "DrupalMediaLibrary" button is for the CKEditor text editor.
- if ($form_state->getValue(['editor', 'editor']) !== 'ckeditor') {
- return;
- }
-
- $button_group_path = [
- 'editor',
- 'settings',
- 'toolbar',
- 'button_groups',
- ];
-
- if ($button_groups = $form_state->getValue($button_group_path)) {
- $buttons = [];
- $button_groups = Json::decode($button_groups);
-
- foreach ($button_groups as $button_row) {
- foreach ($button_row as $button_group) {
- $buttons = array_merge($buttons, array_values($button_group['items']));
- }
- }
-
- $get_filter_label = function ($filter_plugin_id) use ($form) {
- return (string) $form['filters']['order'][$filter_plugin_id]['filter']['#markup'];
- };
-
- if (in_array('DrupalMediaLibrary', $buttons, TRUE)) {
- $media_embed_enabled = $form_state->getValue([
- 'filters',
- 'media_embed',
- 'status',
- ]);
-
- if (!$media_embed_enabled) {
- $error_message = new TranslatableMarkup('The %media-embed-filter-label filter must be enabled to use the %drupal-media-library-button button.', [
- '%media-embed-filter-label' => $get_filter_label('media_embed'),
- '%drupal-media-library-button' => new TranslatableMarkup('Insert from Media Library'),
- ]);
- $form_state->setErrorByName('filters', $error_message);
- }
- }
- }
-}
-
-/**
- * Implements hook_ENTITY_TYPE_presave().
- */
-function ckeditor_editor_presave(EditorInterface $editor) {
- // Only try to update editors using CKEditor 4.
- if ($editor->getEditor() !== 'ckeditor') {
- return FALSE;
- }
-
- $enabled_plugins = _ckeditor_get_enabled_plugins($editor);
-
- // Only update if the editor has plugin settings for disabled plugins.
- $needs_update = FALSE;
- $settings = $editor->getSettings();
-
- // Updates are not needed if plugin settings are not defined for the editor.
- if (!isset($settings['plugins'])) {
- return;
- }
-
- foreach (array_keys($settings['plugins']) as $plugin_id) {
- if (!in_array($plugin_id, $enabled_plugins, TRUE)) {
- unset($settings['plugins'][$plugin_id]);
- $needs_update = TRUE;
- }
- }
- if ($needs_update) {
- $editor->setSettings($settings);
- }
-}
diff --git a/core/modules/ckeditor/ckeditor.post_update.php b/core/modules/ckeditor/ckeditor.post_update.php
deleted file mode 100644
index 6839bd430d4b..000000000000
--- a/core/modules/ckeditor/ckeditor.post_update.php
+++ /dev/null
@@ -1,15 +0,0 @@
-<?php
-
-/**
- * @file
- * Post update functions for CKEditor.
- */
-
-/**
- * Implements hook_removed_post_updates().
- */
-function ckeditor_removed_post_updates() {
- return [
- 'ckeditor_post_update_omit_settings_for_disabled_plugins' => '10.0.0',
- ];
-}
diff --git a/core/modules/ckeditor/ckeditor.services.yml b/core/modules/ckeditor/ckeditor.services.yml
deleted file mode 100644
index a905f782ad33..000000000000
--- a/core/modules/ckeditor/ckeditor.services.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-services:
- plugin.manager.ckeditor.plugin:
- class: Drupal\ckeditor\CKEditorPluginManager
- parent: default_plugin_manager
diff --git a/core/modules/ckeditor/config/schema/ckeditor.schema.yml b/core/modules/ckeditor/config/schema/ckeditor.schema.yml
deleted file mode 100644
index 528919d41f0a..000000000000
--- a/core/modules/ckeditor/config/schema/ckeditor.schema.yml
+++ /dev/null
@@ -1,52 +0,0 @@
-# Schema for the configuration files of the CKEditor module.
-
-editor.settings.ckeditor:
- type: mapping
- label: 'CKEditor settings'
- mapping:
- toolbar:
- type: mapping
- label: 'Toolbar configuration'
- mapping:
- rows:
- type: sequence
- label: 'Rows'
- sequence:
- type: sequence
- label: 'Button groups'
- sequence:
- type: mapping
- label: 'Button group'
- mapping:
- name:
- type: string
- label: 'Button group name'
- items:
- type: sequence
- label: 'Buttons'
- sequence:
- type: string
- label: 'Button'
- plugins:
- type: sequence
- label: 'Plugins'
- sequence:
- type: ckeditor.plugin.[%key]
-
-# Plugin \Drupal\ckeditor\Plugin\CKEditorPlugin\Language
-ckeditor.plugin.language:
- type: mapping
- label: 'Language'
- mapping:
- language_list:
- type: string
- label: 'Language list ID'
-
-# Plugin \Drupal\ckeditor\Plugin\ckeditor\plugin\StylesCombo
-ckeditor.plugin.stylescombo:
- type: mapping
- label: 'Styles dropdown'
- mapping:
- styles:
- type: text
- label: 'List of styles'
diff --git a/core/modules/ckeditor/css/ckeditor-iframe.css b/core/modules/ckeditor/css/ckeditor-iframe.css
deleted file mode 100644
index aafa5f205536..000000000000
--- a/core/modules/ckeditor/css/ckeditor-iframe.css
+++ /dev/null
@@ -1,25 +0,0 @@
-/**
- * CSS added to iframe-based instances only.
- */
-body {
- margin: 8px;
- color: #222;
- background-color: #fff;
- font-family: Arial, Verdana, sans-serif;
- font-size: 15px;
-}
-
-@media screen and (max-width: 600px) {
- /* A font-size of 16px prevents iOS from zooming. */
- body {
- font-size: 16px;
- }
-}
-
-ol,
-ul,
-dl {
- /* Preserved spaces for list items with text direction other than the list.
- * (CKEditor issues #6249,#8049) */
- padding: 0 40px;
-}
diff --git a/core/modules/ckeditor/css/ckeditor.admin.css b/core/modules/ckeditor/css/ckeditor.admin.css
deleted file mode 100644
index 51429c0f0256..000000000000
--- a/core/modules/ckeditor/css/ckeditor.admin.css
+++ /dev/null
@@ -1,334 +0,0 @@
-/**
- * @file
- * Styles for configuration of CKEditor module.
- *
- * Many of these styles are adapted directly from the default CKEditor theme
- * "moono".
- */
-
-.ckeditor-toolbar {
- margin: 5px 0;
- padding: 0.1667em 0.1667em 0.08em;
- /* Disallow any user selections in the drag-and-drop toolbar config UI. */
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
- border: 1px solid #b6b6b6;
- background: #cfd1cf;
- background-image: -webkit-linear-gradient(top, whiteSmoke, #cfd1cf);
- background-image: linear-gradient(top, whiteSmoke, #cfd1cf);
- box-shadow: 0 1px 0 white inset;
-}
-.ckeditor-toolbar-active {
- margin-top: 0.25em;
-}
-.ckeditor-toolbar-disabled {
- margin-bottom: 0.5em;
-}
-.ckeditor-toolbar ul,
-.ckeditor-toolbar-disabled ul {
- margin: 0;
- padding: 0;
- list-style: none;
-}
-/* This is required to win over specificity of [dir="rtl"] ul */
-[dir="rtl"] .ckeditor-toolbar ul,
-[dir="rtl"] .ckeditor-toolbar-disabled ul {
- margin-right: 0;
-}
-
-.ckeditor-row {
- padding: 2px 0 3px;
- border-radius: 3px;
-}
-.ckeditor-group-names-are-visible .ckeditor-row {
- border: 1px solid whitesmoke;
-}
-.ckeditor-row + .ckeditor-row {
- margin-top: 0.25em;
-}
-.ckeditor-toolbar-group,
-.ckeditor-toolbar-group-placeholder,
-.ckeditor-add-new-group {
- float: left; /* LTR */
-}
-[dir="rtl"] .ckeditor-toolbar-group,
-[dir="rtl"] .ckeditor-toolbar-group-placeholder,
-[dir="rtl"] .ckeditor-add-new-group {
- float: right;
-}
-.ckeditor-toolbar-groups {
- min-height: 2em;
-}
-.ckeditor-toolbar-group {
- margin: 0 0.3333em;
- cursor: move;
-}
-.ckeditor-group-names-are-visible .ckeditor-toolbar-group,
-.ckeditor-add-new-group {
- padding: 0.2em 0.4em;
- border: 1px dotted #a6a6a6;
- border-radius: 3px;
-}
-.ckeditor-toolbar-group.placeholder,
-.ckeditor-toolbar-group.placeholder .ckeditor-toolbar-group-name {
- cursor: not-allowed;
-}
-.ckeditor-toolbar-group.placeholder .ckeditor-toolbar-group-name {
- font-style: italic;
-}
-.ckeditor-toolbar-group-name {
- display: none;
- margin: 0.25em 0;
- font-size: 1em;
- font-weight: normal;
-}
-.ckeditor-group-names-are-visible .ckeditor-toolbar-group-name {
- display: block;
- cursor: pointer;
-}
-.ckeditor-toolbar-active .placeholder,
-.ckeditor-toolbar-active .ckeditor-add-new-group {
- display: none;
-}
-.ckeditor-group-names-are-visible .placeholder,
-.ckeditor-group-names-are-visible .ckeditor-add-new-group {
- display: block;
-}
-.ckeditor-toolbar-group-buttons {
- float: left; /* LTR */
-}
-[dir="rtl"] .ckeditor-toolbar-group-buttons {
- float: right;
-}
-.ckeditor-groupnames-toggle {
- float: right; /* LTR */
- cursor: pointer;
-}
-[dir="rtl"] .ckeditor-groupnames-toggle {
- float: left;
-}
-.ckeditor-toolbar .ckeditor-toolbar-group > li {
- margin: 3px 6px;
- padding: 3px;
- border: 1px solid white;
- border-radius: 5px;
- background-image: -webkit-linear-gradient(transparent 60%, rgba(0, 0, 0, 0.1));
- background-image: linear-gradient(transparent 60%, rgba(0, 0, 0, 0.1));
-}
-.ckeditor-toolbar-configuration .fieldset-description {
- margin-bottom: 1em;
-}
-.ckeditor-toolbar-disabled .ckeditor-toolbar-available,
-.ckeditor-toolbar-disabled .ckeditor-toolbar-dividers {
- box-sizing: border-box;
-}
-.ckeditor-toolbar-disabled .ckeditor-toolbar-available {
- float: left; /* LTR */
- width: 80%;
-}
-[dir="rtl"] .ckeditor-toolbar-disabled .ckeditor-toolbar-available {
- float: right;
-}
-.ckeditor-toolbar-disabled .ckeditor-toolbar-dividers {
- float: right; /* LTR */
- width: 20%;
-}
-[dir="rtl"] .ckeditor-toolbar-disabled .ckeditor-toolbar-dividers {
- float: left;
-}
-.ckeditor-toolbar-disabled .ckeditor-buttons li a,
-.ckeditor-toolbar .ckeditor-buttons,
-.ckeditor-add-new-group button {
- border: 1px solid #a6a6a6;
- border-bottom-color: #979797;
- border-radius: 3px;
- box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5), 0 0 2px rgba(255, 255, 255, 0.15) inset, 0 1px 0 rgba(255, 255, 255, 0.15) inset;
-}
-.ckeditor-toolbar-disabled .ckeditor-buttons {
- border: 0;
-}
-.ckeditor-toolbar-disabled .ckeditor-buttons li {
- margin: 2px;
-}
-.ckeditor-buttons {
- min-width: 26px;
- min-height: 26px;
-}
-.ckeditor-buttons li {
- float: left; /* LTR */
- margin: 0;
- padding: 0;
-}
-[dir="rtl"] .ckeditor-buttons li {
- float: right;
-}
-.ckeditor-buttons li a,
-.ckeditor-add-new-group button {
- color: #474747;
- background: #e4e4e4;
- background-image: -webkit-linear-gradient(top, white, #e4e4e4);
- background-image: linear-gradient(top, white, #e4e4e4);
-}
-.ckeditor-buttons li a {
- position: relative;
- display: block;
- min-height: 18px;
- padding: 4px 6px;
- cursor: move;
- white-space: nowrap;
- text-decoration: none;
- border: 0;
- text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
- line-height: 1.4;
-}
-.ckeditor-toolbar-dividers {
- float: right; /* LTR */
-}
-[dir="rtl"] .ckeditor-toolbar-dividers {
- float: left;
-}
-.ckeditor-buttons li .cke-icon-only {
- /* Firefox includes the offscreen text in the focus indicator, resulting in a
- far too wide focus indicator. This fixes that. */
- overflow: hidden;
- width: 16px;
- text-indent: -9999px;
-}
-.ckeditor-buttons .cke_button_icon img {
- width: 16px;
- height: 16px;
-}
-.ckeditor-buttons li .cke_ltr {
- direction: ltr;
-}
-.ckeditor-buttons li .cke_rtl {
- direction: rtl;
-}
-.ckeditor-buttons li a:focus,
-.ckeditor-buttons li a:active,
-.ckeditor-multiple-buttons li a:focus {
- z-index: 11; /* Ensure focused buttons show their outline on all sides. */
- outline: 1px dotted #212121;
- outline: 5px auto Highlight;
- outline: 5px auto -webkit-focus-ring-color;
-}
-.ckeditor-buttons li:first-child a {
- border-top-left-radius: 2px; /* LTR */
- border-bottom-left-radius: 2px; /* LTR */
-}
-[dir="rtl"] .ckeditor-buttons li:first-child a {
- border-top-right-radius: 2px;
- border-bottom-right-radius: 2px;
-}
-.ckeditor-buttons li:last-child a {
- border-top-right-radius: 2px; /* LTR */
- border-bottom-right-radius: 2px; /* LTR */
-}
-[dir="rtl"] .ckeditor-buttons li:last-child a {
- border-top-left-radius: 2px;
- border-bottom-left-radius: 2px;
-}
-.ckeditor-button-placeholder,
-.ckeditor-buttons .ckeditor-button-placeholder a,
-.ckeditor-toolbar-group-placeholder {
- background: #9dcae7;
-}
-.ckeditor-toolbar-group-placeholder {
- border-radius: 4px;
-}
-.ckeditor-multiple-buttons {
- float: left; /* LTR */
- margin: 5px;
- padding: 1px 2px;
- list-style: none;
-}
-[dir="rtl"] .ckeditor-multiple-buttons {
- float: right;
-}
-.ckeditor-multiple-buttons li {
- float: left; /* LTR */
- margin: 0;
- padding: 0;
-}
-[dir="rtl"] .ckeditor-multiple-buttons li {
- float: right;
-}
-.ckeditor-multiple-buttons li a {
- display: inline-block;
- min-height: 18px;
- margin: 0;
- padding: 2px 0;
- cursor: move;
- line-height: 1.4;
-}
-.ckeditor-buttons .ckeditor-group-button-separator,
-.ckeditor-multiple-buttons .ckeditor-group-button-separator {
- margin: -1px -3px -2px;
-}
-.ckeditor-buttons .ckeditor-group-button-separator a,
-.ckeditor-multiple-buttons .ckeditor-group-button-separator a {
- position: relative;
- z-index: 10;
- width: 13px;
- height: 29px;
- padding: 0;
- background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAAdCAMAAABG4xbVAAAAhFBMVEUAAACmpqampqampqb////l5eX////5+fmmpqatra2urq6vr6+1tbW2tra4uLi6urq8vLzb29ve3t7i4uLl5eXn5+fo6Ojp6enq6urr6+vs7Ozt7e3u7u7v7+/w8PDx8fHy8vLz8/P09PT19fX29vb39/f4+Pj5+fn6+vr7+/v8/Pz+/v7qIQO+AAAACHRSTlMATVmAi8XM29MuWToAAABjSURBVBiVrc5BCoAwDETRMKhtRBduev9LKm1xjItWRBBE6Nt9QkIwOTcUzk0Imi8aoMssxbgoTHMtqsFMLta0vPh2N49HyfdelPg6k9uvX/a+Bmggt1qJRNzQFVgjEnkUZDoBmH57VSypjg4AAAAASUVORK5CYII=) no-repeat center center;
-}
-ul.ckeditor-buttons li.ckeditor-button-separator a {
- position: relative;
- z-index: 10;
- width: 1px;
- height: 24px;
- margin: 1px 0 0;
- padding: 0;
- background: #e4e4e4;
- background-image: -webkit-linear-gradient(#e4e4e4, #b4b4b4);
- background-image: linear-gradient(#e4e4e4, #b4b4b4);
-}
-.ckeditor-multiple-buttons .ckeditor-button-separator a {
- width: 2px;
- height: 26px;
- margin: 0 10px;
- padding: 0;
-}
-.ckeditor-separator {
- display: block;
- width: 1px;
- height: 18px;
- margin: 5px 0;
- background-color: silver;
- background-color: rgba(0, 0, 0, 0.2);
- box-shadow: 1px 0 1px rgba(255, 255, 255, 0.5);
-}
-.ckeditor-button-arrow {
- display: inline-block;
- width: 0;
- margin: 0 4px 2px;
- text-align: center;
- border-top: 3px solid #333;
- border-right: 3px solid transparent;
- border-left: 3px solid transparent;
-}
-.ckeditor-row-controls {
- float: right; /* LTR */
- width: 40px;
- text-align: right; /* LTR */
- font-size: 18px;
-}
-[dir="rtl"] .ckeditor-row-controls {
- float: left;
- text-align: left;
-}
-.ckeditor-row-controls a {
- display: inline-block;
- box-sizing: border-box;
- width: 20px;
- height: 28px;
- padding: 6px 2px;
- text-decoration: none;
- color: #333;
- font-weight: bold;
- line-height: 0.9;
-}
diff --git a/core/modules/ckeditor/css/ckeditor.css b/core/modules/ckeditor/css/ckeditor.css
deleted file mode 100644
index 567d35a03354..000000000000
--- a/core/modules/ckeditor/css/ckeditor.css
+++ /dev/null
@@ -1,31 +0,0 @@
-.ckeditor-dialog-loading {
- position: absolute;
- top: 0;
- width: 100%;
- text-align: center;
-}
-
-.ckeditor-dialog-loading-link {
- position: relative;
- top: 0;
- display: inline-block;
- padding: 3px 10px;
- -webkit-user-select: none;
- -khtml-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
- border: 1px solid #b6b6b6;
- border-top: none;
- border-radius: 0 0 5px 5px;
- background: white;
- box-shadow: 0 0 10px -3px #000;
- font-size: 14px;
- -webkit-touch-callout: none;
-}
-
-.cke.cke_chrome.cke_focus {
- outline: 1px dotted #212121;
- outline: 5px auto Highlight;
- outline: 5px auto -webkit-focus-ring-color;
-}
diff --git a/core/modules/ckeditor/css/plugins/drupalimagecaption/ckeditor.drupalimagecaption.css b/core/modules/ckeditor/css/plugins/drupalimagecaption/ckeditor.drupalimagecaption.css
deleted file mode 100644
index 29f6a490e5c3..000000000000
--- a/core/modules/ckeditor/css/plugins/drupalimagecaption/ckeditor.drupalimagecaption.css
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- * @file
- * Image Caption: overrides to make centered alignment work inside CKEditor.
- */
-
-/**
- * Since .align-center is set on the non-captioned image's parent block element
- * in CKEditor, the image must be centered separately.
- */
-p[data-widget="image"].align-center {
- text-align: center;
-}
-
-/**
- * Since .align-center is set on captioned widget's wrapper element in CKEditor,
- * the alignment of internals must be set separately.
- */
-div[data-cke-widget-wrapper].align-center > figure[data-widget="image"] {
- margin-right: auto;
- margin-left: auto;
-}
diff --git a/core/modules/ckeditor/css/plugins/drupalmedia/ckeditor.drupalmedia.css b/core/modules/ckeditor/css/plugins/drupalmedia/ckeditor.drupalmedia.css
deleted file mode 100644
index 82923ff63e96..000000000000
--- a/core/modules/ckeditor/css/plugins/drupalmedia/ckeditor.drupalmedia.css
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
- * @file
- * Media embed: overrides to make focus styles and alignment work in CKEditor.
- */
-
-/**
- * Allow the drupal-media element's width to collapse to the size of its
- * contents so that the outline has no extra white space (margin). This
- * emulates the image2 plugin's styles inherited by the drupallink CKEditor
- * plugin.
- */
-drupal-media {
- display: inline-block;
-}
-
-/**
- * For center alignment, take advantage of drupal-media's inline-block
- * display and center it as if it were text.
- */
-.cke_widget_drupalmedia.align-center {
- text-align: center;
-}
-
-/**
- * Fix positioning without delete button. Can be removed with this issue:
- * @see https://www.drupal.org/project/drupal/issues/3074859
- */
-drupal-media .media-library-item__edit {
- right: 10px;
-}
-
-/**
- * Allow alignment to display in CKEditor.
- */
-drupal-media[data-align=left],
-drupal-media[data-align=right] {
- display: inline;
-}
-drupal-media[data-align=center] {
- display: flex;
-}
diff --git a/core/modules/ckeditor/css/plugins/language/ckeditor.language.css b/core/modules/ckeditor/css/plugins/language/ckeditor.language.css
deleted file mode 100644
index 41b75699523f..000000000000
--- a/core/modules/ckeditor/css/plugins/language/ckeditor.language.css
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * @file
- * Language: add styling for elements that have a language attribute.
- */
-
-/**
- * Show the user that a 'lang' tag has been applied by adding a thin dotted
- * border. We also append the value of the tag between brackets, for example:
- * '(en)'. Since the html element has a 'lang' attribute too we only target
- * elements within the html scope.
- */
-html [lang] {
- outline: 1px dotted gray;
-}
-html [lang]:after {
- content: " ("attr(lang)")";
- color: #666;
- font-size: 10px;
-}
diff --git a/core/modules/ckeditor/js/ckeditor.admin.es6.js b/core/modules/ckeditor/js/ckeditor.admin.es6.js
deleted file mode 100644
index 77462288a9a8..000000000000
--- a/core/modules/ckeditor/js/ckeditor.admin.es6.js
+++ /dev/null
@@ -1,565 +0,0 @@
-/**
- * @file
- * CKEditor button and group configuration user interface.
- */
-
-(function ($, Drupal, drupalSettings, _) {
- Drupal.ckeditor = Drupal.ckeditor || {};
-
- /**
- * Sets config behavior and creates config views for the CKEditor toolbar.
- *
- * @type {Drupal~behavior}
- *
- * @prop {Drupal~behaviorAttach} attach
- * Attaches admin behavior to the CKEditor buttons.
- * @prop {Drupal~behaviorDetach} detach
- * Detaches admin behavior from the CKEditor buttons on 'unload'.
- */
- Drupal.behaviors.ckeditorAdmin = {
- attach(context) {
- // Process the CKEditor configuration fragment once.
- const configurationForm = once(
- 'ckeditor-configuration',
- '.ckeditor-toolbar-configuration',
- context,
- );
- if (configurationForm.length) {
- const $configurationForm = $(configurationForm);
- const $textarea = $configurationForm
- // Hide the textarea that contains the serialized representation of the
- // CKEditor configuration.
- .find('.js-form-item-editor-settings-toolbar-button-groups')
- .hide()
- // Return the textarea child node from this expression.
- .find('textarea');
-
- // The HTML for the CKEditor configuration is assembled on the server
- // and sent to the client as a serialized DOM fragment.
- $configurationForm.append(drupalSettings.ckeditor.toolbarAdmin);
-
- // Create a configuration model.
- Drupal.ckeditor.models.Model = new Drupal.ckeditor.Model({
- $textarea,
- activeEditorConfig: JSON.parse($textarea[0].value),
- hiddenEditorConfig: drupalSettings.ckeditor.hiddenCKEditorConfig,
- });
-
- // Create the configuration Views.
- const viewDefaults = {
- model: Drupal.ckeditor.models.Model,
- el: $('.ckeditor-toolbar-configuration'),
- };
- Drupal.ckeditor.views = {
- controller: new Drupal.ckeditor.ControllerView(viewDefaults),
- visualView: new Drupal.ckeditor.VisualView(viewDefaults),
- keyboardView: new Drupal.ckeditor.KeyboardView(viewDefaults),
- auralView: new Drupal.ckeditor.AuralView(viewDefaults),
- };
- }
- },
- detach(context, settings, trigger) {
- // Early-return if the trigger for detachment is something else than
- // unload.
- if (trigger !== 'unload') {
- return;
- }
-
- // We're detaching because CKEditor as text editor has been disabled; this
- // really means that all CKEditor toolbar buttons have been removed.
- // Hence,all editor features will be removed, so any reactions from
- // filters will be undone.
- const configurationForm = once.filter(
- 'ckeditor-configuration',
- '.ckeditor-toolbar-configuration',
- context,
- );
- if (
- configurationForm.length &&
- Drupal.ckeditor.models &&
- Drupal.ckeditor.models.Model
- ) {
- const config = Drupal.ckeditor.models.Model.toJSON().activeEditorConfig;
- const buttons = Drupal.ckeditor.views.controller.getButtonList(config);
- const $activeToolbar = $('.ckeditor-toolbar-configuration').find(
- '.ckeditor-toolbar-active',
- );
- for (let i = 0; i < buttons.length; i++) {
- $activeToolbar.trigger('CKEditorToolbarChanged', [
- 'removed',
- buttons[i],
- ]);
- }
- }
- },
- };
-
- /**
- * CKEditor configuration UI methods of Backbone objects.
- *
- * @namespace
- */
- Drupal.ckeditor = {
- /**
- * A hash of View instances.
- *
- * @type {object}
- */
- views: {},
-
- /**
- * A hash of Model instances.
- *
- * @type {object}
- */
- models: {},
-
- /**
- * Translates changes in CKEditor config DOM structure to the config model.
- *
- * If the button is moved within an existing group, the DOM structure is
- * simply translated to a configuration model. If the button is moved into a
- * new group placeholder, then a process is launched to name that group
- * before the button move is translated into configuration.
- *
- * @param {Backbone.View} view
- * The Backbone View that invoked this function.
- * @param {jQuery} $button
- * A jQuery set that contains an li element that wraps a button element.
- * @param {function} callback
- * A callback to invoke after the button group naming modal dialog has
- * been closed.
- *
- */
- registerButtonMove(view, $button, callback) {
- const $group = $button.closest('.ckeditor-toolbar-group');
-
- // If dropped in a placeholder button group, the user must name it.
- if ($group.hasClass('placeholder')) {
- if (view.isProcessing) {
- return;
- }
- view.isProcessing = true;
-
- Drupal.ckeditor.openGroupNameDialog(view, $group, callback);
- } else {
- view.model.set('isDirty', true);
- callback(true);
- }
- },
-
- /**
- * Translates changes in CKEditor config DOM structure to the config model.
- *
- * Each row has a placeholder group at the end of the row. A user may not
- * move an existing button group past the placeholder group at the end of a
- * row.
- *
- * @param {Backbone.View} view
- * The Backbone View that invoked this function.
- * @param {jQuery} $group
- * A jQuery set that contains an li element that wraps a group of buttons.
- */
- registerGroupMove(view, $group) {
- // Remove placeholder classes if necessary.
- let $row = $group.closest('.ckeditor-row');
- if ($row.hasClass('placeholder')) {
- $row.removeClass('placeholder');
- }
- // If there are any rows with just a placeholder group, mark the row as a
- // placeholder.
- $row
- .parent()
- .children()
- .each(function () {
- $row = $(this);
- if (
- $row.find('.ckeditor-toolbar-group').not('.placeholder').length ===
- 0
- ) {
- $row.addClass('placeholder');
- }
- });
- view.model.set('isDirty', true);
- },
-
- /**
- * Opens a dialog with a form for changing the title of a button group.
- *
- * @param {Backbone.View} view
- * The Backbone View that invoked this function.
- * @param {jQuery} $group
- * A jQuery set that contains an li element that wraps a group of buttons.
- * @param {function} callback
- * A callback to invoke after the button group naming modal dialog has
- * been closed.
- */
- openGroupNameDialog(view, $group, callback) {
- callback = callback || function () {};
-
- /**
- * Validates the string provided as a button group title.
- *
- * @param {HTMLElement} form
- * The form DOM element that contains the input with the new button
- * group title string.
- *
- * @return {bool}
- * Returns true when an error exists, otherwise returns false.
- */
- function validateForm(form) {
- if (form.elements[0].value.length === 0) {
- const $form = $(form);
- if (!$form.hasClass('errors')) {
- $form
- .addClass('errors')
- .find('input')
- .addClass('error')
- .attr('aria-invalid', 'true');
- $(
- `<div class="description" >${Drupal.t(
- 'Please provide a name for the button group.',
- )}</div>`,
- ).insertAfter(form.elements[0]);
- }
- return true;
- }
- return false;
- }
-
- /**
- * Attempts to close the dialog; Validates user input.
- *
- * @param {string} action
- * The dialog action chosen by the user: 'apply' or 'cancel'.
- * @param {HTMLElement} form
- * The form DOM element that contains the input with the new button
- * group title string.
- */
- function closeDialog(action, form) {
- /**
- * Closes the dialog when the user cancels or supplies valid data.
- */
- function shutdown() {
- // eslint-disable-next-line no-use-before-define
- dialog.close(action);
-
- // The processing marker can be deleted since the dialog has been
- // closed.
- delete view.isProcessing;
- }
-
- /**
- * Applies a string as the name of a CKEditor button group.
- *
- * @param {jQuery} $group
- * A jQuery set that contains an li element that wraps a group of
- * buttons.
- * @param {string} name
- * The new name of the CKEditor button group.
- */
- function namePlaceholderGroup($group, name) {
- // If it's currently still a placeholder, then that means we're
- // creating a new group, and we must do some extra work.
- if ($group.hasClass('placeholder')) {
- // Remove all whitespace from the name, lowercase it and ensure
- // HTML-safe encoding, then use this as the group ID for CKEditor
- // configuration UI accessibility purposes only.
- const groupID = `ckeditor-toolbar-group-aria-label-for-${Drupal.checkPlain(
- name.toLowerCase().replace(/\s/g, '-'),
- )}`;
- $group
- // Update the group container.
- .removeAttr('aria-label')
- .attr('data-drupal-ckeditor-type', 'group')
- .attr('tabindex', 0)
- // Update the group heading.
- .children('.ckeditor-toolbar-group-name')
- .attr('id', groupID)
- .end()
- // Update the group items.
- .children('.ckeditor-toolbar-group-buttons')
- .attr('aria-labelledby', groupID);
- }
-
- $group
- .attr('data-drupal-ckeditor-toolbar-group-name', name)
- .children('.ckeditor-toolbar-group-name')
- .each(function () {
- this.textContent = name;
- });
- }
-
- // Invoke a user-provided callback and indicate failure.
- if (action === 'cancel') {
- shutdown();
- callback(false, $group);
- return;
- }
-
- // Validate that a group name was provided.
- if (form && validateForm(form)) {
- return;
- }
-
- // React to application of a valid group name.
- if (action === 'apply') {
- shutdown();
- // Apply the provided name to the button group label.
- namePlaceholderGroup(
- $group,
- Drupal.checkPlain(form.elements[0].value),
- );
- // Remove placeholder classes so that new placeholders will be
- // inserted.
- $group
- .closest('.ckeditor-row.placeholder')
- .addBack()
- .removeClass('placeholder');
-
- // Invoke a user-provided callback and indicate success.
- callback(true, $group);
-
- // Signal that the active toolbar DOM structure has changed.
- view.model.set('isDirty', true);
- }
- }
-
- // Create a Drupal dialog that will get a button group name from the user.
- const $ckeditorButtonGroupNameForm = $(
- Drupal.theme('ckeditorButtonGroupNameForm'),
- );
- const dialog = Drupal.dialog($ckeditorButtonGroupNameForm.get(0), {
- title: Drupal.t('Button group name'),
- dialogClass: 'ckeditor-name-toolbar-group',
- resizable: false,
- buttons: [
- {
- text: Drupal.t('Apply'),
- click() {
- closeDialog('apply', this);
- },
- primary: true,
- },
- {
- text: Drupal.t('Cancel'),
- click() {
- closeDialog('cancel');
- },
- },
- ],
- open() {
- const form = this;
- const $form = $(this);
- const $widget = $form.parent();
- $widget.find('.ui-dialog-titlebar-close').remove();
- // Set a click handler on the input and button in the form.
- $widget.on('keypress.ckeditor', 'input, button', (event) => {
- // React to enter key press.
- if (event.keyCode === 13) {
- const $target = $(event.currentTarget);
- const data = $target.data('ui-button');
- let action = 'apply';
- // Assume 'apply', but take into account that the user might have
- // pressed the enter key on the dialog buttons.
- if (data && data.options && data.options.label) {
- action = data.options.label.toLowerCase();
- }
- closeDialog(action, form);
- event.stopPropagation();
- event.stopImmediatePropagation();
- event.preventDefault();
- }
- });
- // Announce to the user that a modal dialog is open.
- let text = Drupal.t(
- 'Editing the name of the new button group in a dialog.',
- );
- if (
- typeof $group.attr('data-drupal-ckeditor-toolbar-group-name') !==
- 'undefined'
- ) {
- text = Drupal.t(
- 'Editing the name of the "@groupName" button group in a dialog.',
- {
- '@groupName': $group.attr(
- 'data-drupal-ckeditor-toolbar-group-name',
- ),
- },
- );
- }
- Drupal.announce(text);
- },
- close(event) {
- // Automatically destroy the DOM element that was used for the dialog.
- $(event.target).remove();
- },
- });
-
- // A modal dialog is used because the user must provide a button group
- // name or cancel the button placement before taking any other action.
- dialog.showModal();
-
- $(
- document
- .querySelector('.ckeditor-name-toolbar-group')
- .querySelector('input'),
- )
- // When editing, set the "group name" input in the form to the current
- // value.
- .attr('value', $group.attr('data-drupal-ckeditor-toolbar-group-name'))
- // Focus on the "group name" input in the form.
- .trigger('focus');
- },
- };
-
- /**
- * Automatically shows/hides settings of buttons-only CKEditor plugins.
- *
- * @type {Drupal~behavior}
- *
- * @prop {Drupal~behaviorAttach} attach
- * Attaches show/hide behavior to Plugin Settings buttons.
- */
- Drupal.behaviors.ckeditorAdminButtonPluginSettings = {
- attach(context) {
- const $context = $(context);
- const ckeditorPluginSettings = once(
- 'ckeditor-plugin-settings',
- '#ckeditor-plugin-settings',
- context,
- );
- if (ckeditorPluginSettings.length) {
- const $ckeditorPluginSettings = $(ckeditorPluginSettings);
- // Hide all button-dependent plugin settings initially.
- $ckeditorPluginSettings
- .find('[data-ckeditor-buttons]')
- .each(function () {
- const $this = $(this);
- if ($this.data('verticalTab')) {
- $this.data('verticalTab').tabHide();
- } else {
- // On very narrow viewports, Vertical Tabs are disabled.
- $this.hide();
- }
- $this.data('ckeditorButtonPluginSettingsActiveButtons', []);
- });
-
- // Whenever a button is added or removed, check if we should show or
- // hide the corresponding plugin settings. (Note that upon
- // initialization, each button that already is part of the toolbar still
- // is considered "added", hence it also works correctly for buttons that
- // were added previously.)
- $context
- .find('.ckeditor-toolbar-active')
- .off('CKEditorToolbarChanged.ckeditorAdminPluginSettings')
- .on(
- 'CKEditorToolbarChanged.ckeditorAdminPluginSettings',
- (event, action, button) => {
- const $pluginSettings = $ckeditorPluginSettings.find(
- `[data-ckeditor-buttons~=${button}]`,
- );
-
- // No settings for this button.
- if ($pluginSettings.length === 0) {
- return;
- }
-
- const verticalTab = $pluginSettings.data('verticalTab');
- const activeButtons = $pluginSettings.data(
- 'ckeditorButtonPluginSettingsActiveButtons',
- );
- if (action === 'added') {
- activeButtons.push(button);
- // Show this plugin's settings if >=1 of its buttons are active.
- if (verticalTab) {
- verticalTab.tabShow();
- } else {
- // On very narrow viewports, Vertical Tabs remain fieldsets.
- $pluginSettings.show();
- }
- } else {
- // Remove this button from the list of active buttons.
- activeButtons.splice(activeButtons.indexOf(button), 1);
- // Show this plugin's settings 0 of its buttons are active.
- if (activeButtons.length === 0) {
- if (verticalTab) {
- verticalTab.tabHide();
- } else {
- // On very narrow viewports, Vertical Tabs are disabled.
- $pluginSettings.hide();
- }
- }
- }
- $pluginSettings.data(
- 'ckeditorButtonPluginSettingsActiveButtons',
- activeButtons,
- );
- },
- );
- }
- },
- };
-
- /**
- * Themes a blank CKEditor row.
- *
- * @return {string}
- * A HTML string for a CKEditor row.
- */
- Drupal.theme.ckeditorRow = function () {
- return '<li class="ckeditor-row placeholder" role="group"><ul class="ckeditor-toolbar-groups clearfix"></ul></li>';
- };
-
- /**
- * Themes a blank CKEditor button group.
- *
- * @return {string}
- * A HTML string for a CKEditor button group.
- */
- Drupal.theme.ckeditorToolbarGroup = function () {
- let group = '';
- group += `<li class="ckeditor-toolbar-group placeholder" role="presentation" aria-label="${Drupal.t(
- 'Place a button to create a new button group.',
- )}">`;
- group += `<h3 class="ckeditor-toolbar-group-name">${Drupal.t(
- 'New group',
- )}</h3>`;
- group +=
- '<ul class="ckeditor-buttons ckeditor-toolbar-group-buttons" role="toolbar" data-drupal-ckeditor-button-sorting="target"></ul>';
- group += '</li>';
- return group;
- };
-
- /**
- * Themes a form for changing the title of a CKEditor button group.
- *
- * @return {string}
- * A HTML string for the form for the title of a CKEditor button group.
- */
- Drupal.theme.ckeditorButtonGroupNameForm = function () {
- return '<form><input name="group-name" required="required"></form>';
- };
-
- /**
- * Themes a button that will toggle the button group names in active config.
- *
- * @return {string}
- * A HTML string for the button to toggle group names.
- */
- Drupal.theme.ckeditorButtonGroupNamesToggle = function () {
- return '<button class="link ckeditor-groupnames-toggle" aria-pressed="false"></button>';
- };
-
- /**
- * Themes a button that will prompt the user to name a new button group.
- *
- * @return {string}
- * A HTML string for the button to create a name for a new button group.
- */
- Drupal.theme.ckeditorNewButtonGroup = function () {
- return `<li class="ckeditor-add-new-group"><button aria-label="${Drupal.t(
- 'Add a CKEditor button group to the end of this row.',
- )}">${Drupal.t('Add group')}</button></li>`;
- };
-})(jQuery, Drupal, drupalSettings, _);
diff --git a/core/modules/ckeditor/js/ckeditor.admin.js b/core/modules/ckeditor/js/ckeditor.admin.js
deleted file mode 100644
index 835bd01c2c0d..000000000000
--- a/core/modules/ckeditor/js/ckeditor.admin.js
+++ /dev/null
@@ -1,289 +0,0 @@
-/**
-* DO NOT EDIT THIS FILE.
-* See the following change record for more information,
-* https://www.drupal.org/node/2815083
-* @preserve
-**/
-
-(function ($, Drupal, drupalSettings, _) {
- Drupal.ckeditor = Drupal.ckeditor || {};
- Drupal.behaviors.ckeditorAdmin = {
- attach(context) {
- const configurationForm = once('ckeditor-configuration', '.ckeditor-toolbar-configuration', context);
-
- if (configurationForm.length) {
- const $configurationForm = $(configurationForm);
- const $textarea = $configurationForm.find('.js-form-item-editor-settings-toolbar-button-groups').hide().find('textarea');
- $configurationForm.append(drupalSettings.ckeditor.toolbarAdmin);
- Drupal.ckeditor.models.Model = new Drupal.ckeditor.Model({
- $textarea,
- activeEditorConfig: JSON.parse($textarea[0].value),
- hiddenEditorConfig: drupalSettings.ckeditor.hiddenCKEditorConfig
- });
- const viewDefaults = {
- model: Drupal.ckeditor.models.Model,
- el: $('.ckeditor-toolbar-configuration')
- };
- Drupal.ckeditor.views = {
- controller: new Drupal.ckeditor.ControllerView(viewDefaults),
- visualView: new Drupal.ckeditor.VisualView(viewDefaults),
- keyboardView: new Drupal.ckeditor.KeyboardView(viewDefaults),
- auralView: new Drupal.ckeditor.AuralView(viewDefaults)
- };
- }
- },
-
- detach(context, settings, trigger) {
- if (trigger !== 'unload') {
- return;
- }
-
- const configurationForm = once.filter('ckeditor-configuration', '.ckeditor-toolbar-configuration', context);
-
- if (configurationForm.length && Drupal.ckeditor.models && Drupal.ckeditor.models.Model) {
- const config = Drupal.ckeditor.models.Model.toJSON().activeEditorConfig;
- const buttons = Drupal.ckeditor.views.controller.getButtonList(config);
- const $activeToolbar = $('.ckeditor-toolbar-configuration').find('.ckeditor-toolbar-active');
-
- for (let i = 0; i < buttons.length; i++) {
- $activeToolbar.trigger('CKEditorToolbarChanged', ['removed', buttons[i]]);
- }
- }
- }
-
- };
- Drupal.ckeditor = {
- views: {},
- models: {},
-
- registerButtonMove(view, $button, callback) {
- const $group = $button.closest('.ckeditor-toolbar-group');
-
- if ($group.hasClass('placeholder')) {
- if (view.isProcessing) {
- return;
- }
-
- view.isProcessing = true;
- Drupal.ckeditor.openGroupNameDialog(view, $group, callback);
- } else {
- view.model.set('isDirty', true);
- callback(true);
- }
- },
-
- registerGroupMove(view, $group) {
- let $row = $group.closest('.ckeditor-row');
-
- if ($row.hasClass('placeholder')) {
- $row.removeClass('placeholder');
- }
-
- $row.parent().children().each(function () {
- $row = $(this);
-
- if ($row.find('.ckeditor-toolbar-group').not('.placeholder').length === 0) {
- $row.addClass('placeholder');
- }
- });
- view.model.set('isDirty', true);
- },
-
- openGroupNameDialog(view, $group, callback) {
- callback = callback || function () {};
-
- function validateForm(form) {
- if (form.elements[0].value.length === 0) {
- const $form = $(form);
-
- if (!$form.hasClass('errors')) {
- $form.addClass('errors').find('input').addClass('error').attr('aria-invalid', 'true');
- $(`<div class="description" >${Drupal.t('Please provide a name for the button group.')}</div>`).insertAfter(form.elements[0]);
- }
-
- return true;
- }
-
- return false;
- }
-
- function closeDialog(action, form) {
- function shutdown() {
- dialog.close(action);
- delete view.isProcessing;
- }
-
- function namePlaceholderGroup($group, name) {
- if ($group.hasClass('placeholder')) {
- const groupID = `ckeditor-toolbar-group-aria-label-for-${Drupal.checkPlain(name.toLowerCase().replace(/\s/g, '-'))}`;
- $group.removeAttr('aria-label').attr('data-drupal-ckeditor-type', 'group').attr('tabindex', 0).children('.ckeditor-toolbar-group-name').attr('id', groupID).end().children('.ckeditor-toolbar-group-buttons').attr('aria-labelledby', groupID);
- }
-
- $group.attr('data-drupal-ckeditor-toolbar-group-name', name).children('.ckeditor-toolbar-group-name').each(function () {
- this.textContent = name;
- });
- }
-
- if (action === 'cancel') {
- shutdown();
- callback(false, $group);
- return;
- }
-
- if (form && validateForm(form)) {
- return;
- }
-
- if (action === 'apply') {
- shutdown();
- namePlaceholderGroup($group, Drupal.checkPlain(form.elements[0].value));
- $group.closest('.ckeditor-row.placeholder').addBack().removeClass('placeholder');
- callback(true, $group);
- view.model.set('isDirty', true);
- }
- }
-
- const $ckeditorButtonGroupNameForm = $(Drupal.theme('ckeditorButtonGroupNameForm'));
- const dialog = Drupal.dialog($ckeditorButtonGroupNameForm.get(0), {
- title: Drupal.t('Button group name'),
- dialogClass: 'ckeditor-name-toolbar-group',
- resizable: false,
- buttons: [{
- text: Drupal.t('Apply'),
-
- click() {
- closeDialog('apply', this);
- },
-
- primary: true
- }, {
- text: Drupal.t('Cancel'),
-
- click() {
- closeDialog('cancel');
- }
-
- }],
-
- open() {
- const form = this;
- const $form = $(this);
- const $widget = $form.parent();
- $widget.find('.ui-dialog-titlebar-close').remove();
- $widget.on('keypress.ckeditor', 'input, button', event => {
- if (event.keyCode === 13) {
- const $target = $(event.currentTarget);
- const data = $target.data('ui-button');
- let action = 'apply';
-
- if (data && data.options && data.options.label) {
- action = data.options.label.toLowerCase();
- }
-
- closeDialog(action, form);
- event.stopPropagation();
- event.stopImmediatePropagation();
- event.preventDefault();
- }
- });
- let text = Drupal.t('Editing the name of the new button group in a dialog.');
-
- if (typeof $group.attr('data-drupal-ckeditor-toolbar-group-name') !== 'undefined') {
- text = Drupal.t('Editing the name of the "@groupName" button group in a dialog.', {
- '@groupName': $group.attr('data-drupal-ckeditor-toolbar-group-name')
- });
- }
-
- Drupal.announce(text);
- },
-
- close(event) {
- $(event.target).remove();
- }
-
- });
- dialog.showModal();
- $(document.querySelector('.ckeditor-name-toolbar-group').querySelector('input')).attr('value', $group.attr('data-drupal-ckeditor-toolbar-group-name')).trigger('focus');
- }
-
- };
- Drupal.behaviors.ckeditorAdminButtonPluginSettings = {
- attach(context) {
- const $context = $(context);
- const ckeditorPluginSettings = once('ckeditor-plugin-settings', '#ckeditor-plugin-settings', context);
-
- if (ckeditorPluginSettings.length) {
- const $ckeditorPluginSettings = $(ckeditorPluginSettings);
- $ckeditorPluginSettings.find('[data-ckeditor-buttons]').each(function () {
- const $this = $(this);
-
- if ($this.data('verticalTab')) {
- $this.data('verticalTab').tabHide();
- } else {
- $this.hide();
- }
-
- $this.data('ckeditorButtonPluginSettingsActiveButtons', []);
- });
- $context.find('.ckeditor-toolbar-active').off('CKEditorToolbarChanged.ckeditorAdminPluginSettings').on('CKEditorToolbarChanged.ckeditorAdminPluginSettings', (event, action, button) => {
- const $pluginSettings = $ckeditorPluginSettings.find(`[data-ckeditor-buttons~=${button}]`);
-
- if ($pluginSettings.length === 0) {
- return;
- }
-
- const verticalTab = $pluginSettings.data('verticalTab');
- const activeButtons = $pluginSettings.data('ckeditorButtonPluginSettingsActiveButtons');
-
- if (action === 'added') {
- activeButtons.push(button);
-
- if (verticalTab) {
- verticalTab.tabShow();
- } else {
- $pluginSettings.show();
- }
- } else {
- activeButtons.splice(activeButtons.indexOf(button), 1);
-
- if (activeButtons.length === 0) {
- if (verticalTab) {
- verticalTab.tabHide();
- } else {
- $pluginSettings.hide();
- }
- }
- }
-
- $pluginSettings.data('ckeditorButtonPluginSettingsActiveButtons', activeButtons);
- });
- }
- }
-
- };
-
- Drupal.theme.ckeditorRow = function () {
- return '<li class="ckeditor-row placeholder" role="group"><ul class="ckeditor-toolbar-groups clearfix"></ul></li>';
- };
-
- Drupal.theme.ckeditorToolbarGroup = function () {
- let group = '';
- group += `<li class="ckeditor-toolbar-group placeholder" role="presentation" aria-label="${Drupal.t('Place a button to create a new button group.')}">`;
- group += `<h3 class="ckeditor-toolbar-group-name">${Drupal.t('New group')}</h3>`;
- group += '<ul class="ckeditor-buttons ckeditor-toolbar-group-buttons" role="toolbar" data-drupal-ckeditor-button-sorting="target"></ul>';
- group += '</li>';
- return group;
- };
-
- Drupal.theme.ckeditorButtonGroupNameForm = function () {
- return '<form><input name="group-name" required="required"></form>';
- };
-
- Drupal.theme.ckeditorButtonGroupNamesToggle = function () {
- return '<button class="link ckeditor-groupnames-toggle" aria-pressed="false"></button>';
- };
-
- Drupal.theme.ckeditorNewButtonGroup = function () {
- return `<li class="ckeditor-add-new-group"><button aria-label="${Drupal.t('Add a CKEditor button group to the end of this row.')}">${Drupal.t('Add group')}</button></li>`;
- };
-})(jQuery, Drupal, drupalSettings, _); \ No newline at end of file
diff --git a/core/modules/ckeditor/js/ckeditor.drupalimage.admin.es6.js b/core/modules/ckeditor/js/ckeditor.drupalimage.admin.es6.js
deleted file mode 100644
index f473b957487f..000000000000
--- a/core/modules/ckeditor/js/ckeditor.drupalimage.admin.es6.js
+++ /dev/null
@@ -1,58 +0,0 @@
-/**
- * @file
- * CKEditor 'drupalimage' plugin admin behavior.
- */
-
-(function ($, Drupal, drupalSettings) {
- /**
- * Provides the summary for the "drupalimage" plugin settings vertical tab.
- *
- * @type {Drupal~behavior}
- *
- * @prop {Drupal~behaviorAttach} attach
- * Attaches summary behavior to the "drupalimage" settings vertical tab.
- */
- Drupal.behaviors.ckeditorDrupalImageSettingsSummary = {
- attach() {
- $('[data-ckeditor-plugin-id="drupalimage"]').drupalSetSummary(
- (context) => {
- const root =
- 'input[name="editor[settings][plugins][drupalimage][image_upload]';
- const $status = $(`${root}[status]"]`);
- const maxFileSizeElement = document.querySelector(
- `${root}[max_size]"]`,
- );
- const maxWidth = document.querySelector(
- `${root}[max_dimensions][width]"]`,
- );
- const maxHeight = document.querySelector(
- `${root}[max_dimensions][height]"]`,
- );
- const $scheme = $(`${root}[scheme]"]:checked`);
-
- const maxFileSize = maxFileSizeElement.value
- ? maxFileSizeElement.value
- : maxFileSizeElement.getAttribute('placeholder');
- const maxDimensions =
- maxWidth.value && maxHeight.value
- ? `(${maxWidth.value}x${maxHeight.value})`
- : '';
-
- if (!$status.is(':checked')) {
- return Drupal.t('Uploads disabled');
- }
-
- let output = '';
- output += Drupal.t('Uploads enabled, max size: @size @dimensions', {
- '@size': maxFileSize,
- '@dimensions': maxDimensions,
- });
- if ($scheme.length) {
- output += `<br />${$scheme.attr('data-label')}`;
- }
- return output;
- },
- );
- },
- };
-})(jQuery, Drupal, drupalSettings);
diff --git a/core/modules/ckeditor/js/ckeditor.drupalimage.admin.js b/core/modules/ckeditor/js/ckeditor.drupalimage.admin.js
deleted file mode 100644
index 56ba642f5538..000000000000
--- a/core/modules/ckeditor/js/ckeditor.drupalimage.admin.js
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
-* DO NOT EDIT THIS FILE.
-* See the following change record for more information,
-* https://www.drupal.org/node/2815083
-* @preserve
-**/
-
-(function ($, Drupal, drupalSettings) {
- Drupal.behaviors.ckeditorDrupalImageSettingsSummary = {
- attach() {
- $('[data-ckeditor-plugin-id="drupalimage"]').drupalSetSummary(context => {
- const root = 'input[name="editor[settings][plugins][drupalimage][image_upload]';
- const $status = $(`${root}[status]"]`);
- const maxFileSizeElement = document.querySelector(`${root}[max_size]"]`);
- const maxWidth = document.querySelector(`${root}[max_dimensions][width]"]`);
- const maxHeight = document.querySelector(`${root}[max_dimensions][height]"]`);
- const $scheme = $(`${root}[scheme]"]:checked`);
- const maxFileSize = maxFileSizeElement.value ? maxFileSizeElement.value : maxFileSizeElement.getAttribute('placeholder');
- const maxDimensions = maxWidth.value && maxHeight.value ? `(${maxWidth.value}x${maxHeight.value})` : '';
-
- if (!$status.is(':checked')) {
- return Drupal.t('Uploads disabled');
- }
-
- let output = '';
- output += Drupal.t('Uploads enabled, max size: @size @dimensions', {
- '@size': maxFileSize,
- '@dimensions': maxDimensions
- });
-
- if ($scheme.length) {
- output += `<br />${$scheme.attr('data-label')}`;
- }
-
- return output;
- });
- }
-
- };
-})(jQuery, Drupal, drupalSettings); \ No newline at end of file
diff --git a/core/modules/ckeditor/js/ckeditor.drupalmedia.theme.es6.js b/core/modules/ckeditor/js/ckeditor.drupalmedia.theme.es6.js
deleted file mode 100644
index fbc93079c2ac..000000000000
--- a/core/modules/ckeditor/js/ckeditor.drupalmedia.theme.es6.js
+++ /dev/null
@@ -1,17 +0,0 @@
-/**
- * @file
- * Theme elements for the Media Embed CKEditor plugin.
- */
-
-((Drupal) => {
- /**
- * Themes the edit button for a media embed.
- *
- * @return {string}
- * An HTML string to insert in the CKEditor.
- */
- Drupal.theme.mediaEmbedEditButton = () =>
- `<button class="media-library-item__edit">${Drupal.t(
- 'Edit media',
- )}</button>`;
-})(Drupal);
diff --git a/core/modules/ckeditor/js/ckeditor.drupalmedia.theme.js b/core/modules/ckeditor/js/ckeditor.drupalmedia.theme.js
deleted file mode 100644
index 629422479f0f..000000000000
--- a/core/modules/ckeditor/js/ckeditor.drupalmedia.theme.js
+++ /dev/null
@@ -1,10 +0,0 @@
-/**
-* DO NOT EDIT THIS FILE.
-* See the following change record for more information,
-* https://www.drupal.org/node/2815083
-* @preserve
-**/
-
-(Drupal => {
- Drupal.theme.mediaEmbedEditButton = () => `<button class="media-library-item__edit">${Drupal.t('Edit media')}</button>`;
-})(Drupal); \ No newline at end of file
diff --git a/core/modules/ckeditor/js/ckeditor.es6.js b/core/modules/ckeditor/js/ckeditor.es6.js
deleted file mode 100644
index fe0ebb940ffb..000000000000
--- a/core/modules/ckeditor/js/ckeditor.es6.js
+++ /dev/null
@@ -1,392 +0,0 @@
-/**
- * @file
- * CKEditor implementation of {@link Drupal.editors} API.
- */
-
-(function (Drupal, debounce, CKEDITOR, $, displace, AjaxCommands) {
- /**
- * @namespace
- */
- Drupal.editors.ckeditor = {
- /**
- * Editor attach callback.
- *
- * @param {HTMLElement} element
- * The element to attach the editor to.
- * @param {string} format
- * The text format for the editor.
- *
- * @return {bool}
- * Whether the call to `CKEDITOR.replace()` created an editor or not.
- */
- attach(element, format) {
- this._loadExternalPlugins(format);
- // Also pass settings that are Drupal-specific.
- format.editorSettings.drupal = {
- format: format.format,
- };
-
- // Set a title on the CKEditor instance that includes the text field's
- // label so that screen readers say something that is understandable
- // for end users.
- const label = $(`label[for=${element.getAttribute('id')}]`).html();
- format.editorSettings.title = Drupal.t('Rich Text Editor, !label field', {
- '!label': label,
- });
-
- return !!CKEDITOR.replace(element, format.editorSettings);
- },
-
- /**
- * Editor detach callback.
- *
- * @param {HTMLElement} element
- * The element to detach the editor from.
- * @param {string} format
- * The text format used for the editor.
- * @param {string} trigger
- * The event trigger for the detach.
- *
- * @return {bool}
- * Whether the call to `CKEDITOR.dom.element.get(element).getEditor()`
- * found an editor or not.
- */
- detach(element, format, trigger) {
- const editor = CKEDITOR.dom.element.get(element).getEditor();
- if (editor) {
- if (trigger === 'serialize') {
- editor.updateElement();
- } else {
- editor.destroy();
- element.removeAttribute('contentEditable');
- }
- }
- return !!editor;
- },
-
- /**
- * Reacts on a change in the editor element.
- *
- * @param {HTMLElement} element
- * The element where the change occurred.
- * @param {function} callback
- * Callback called with the value of the editor.
- *
- * @return {bool}
- * Whether the call to `CKEDITOR.dom.element.get(element).getEditor()`
- * found an editor or not.
- */
- onChange(element, callback) {
- const editor = CKEDITOR.dom.element.get(element).getEditor();
- if (editor) {
- editor.on(
- 'change',
- debounce(() => {
- callback(editor.getData());
- }, 400),
- );
-
- // A temporary workaround to control scrollbar appearance when using
- // autoGrow event to control editor's height.
- // @todo Remove when http://dev.ckeditor.com/ticket/12120 is fixed.
- editor.on('mode', () => {
- const editable = editor.editable();
- if (!editable.isInline()) {
- editor.on(
- 'autoGrow',
- (evt) => {
- const doc = evt.editor.document;
- const scrollable = CKEDITOR.env.quirks
- ? doc.getBody()
- : doc.getDocumentElement();
-
- if (scrollable.$.scrollHeight < scrollable.$.clientHeight) {
- scrollable.setStyle('overflow-y', 'hidden');
- } else {
- scrollable.removeStyle('overflow-y');
- }
- },
- null,
- null,
- 10000,
- );
- }
- });
- }
- return !!editor;
- },
-
- /**
- * Attaches an inline editor to a DOM element.
- *
- * @param {HTMLElement} element
- * The element to attach the editor to.
- * @param {object} format
- * The text format used in the editor.
- * @param {string} [mainToolbarId]
- * The id attribute for the main editor toolbar, if any.
- * @param {string} [floatedToolbarId]
- * The id attribute for the floated editor toolbar, if any.
- *
- * @return {bool}
- * Whether the call to `CKEDITOR.replace()` created an editor or not.
- */
- attachInlineEditor(element, format, mainToolbarId, floatedToolbarId) {
- this._loadExternalPlugins(format);
- // Also pass settings that are Drupal-specific.
- format.editorSettings.drupal = {
- format: format.format,
- };
-
- const settings = $.extend(true, {}, format.editorSettings);
-
- // If a toolbar is already provided for "true WYSIWYG" (in-place editing),
- // then use that toolbar instead: override the default settings to render
- // CKEditor UI's top toolbar into mainToolbar, and don't render the bottom
- // toolbar at all. (CKEditor doesn't need a floated toolbar.)
- if (mainToolbarId) {
- const settingsOverride = {
- extraPlugins: 'sharedspace',
- removePlugins: 'floatingspace,elementspath',
- sharedSpaces: {
- top: mainToolbarId,
- },
- };
-
- // Find the "Source" button, if any, and replace it with "Sourcedialog".
- // (The 'sourcearea' plugin only works in CKEditor's iframe mode.)
- let sourceButtonFound = false;
- for (
- let i = 0;
- !sourceButtonFound && i < settings.toolbar.length;
- i++
- ) {
- if (settings.toolbar[i] !== '/') {
- for (
- let j = 0;
- !sourceButtonFound && j < settings.toolbar[i].items.length;
- j++
- ) {
- if (settings.toolbar[i].items[j] === 'Source') {
- sourceButtonFound = true;
- // Swap sourcearea's "Source" button for sourcedialog's.
- settings.toolbar[i].items[j] = 'Sourcedialog';
- settingsOverride.extraPlugins += ',sourcedialog';
- settingsOverride.removePlugins += ',sourcearea';
- }
- }
- }
- }
-
- settings.extraPlugins += `,${settingsOverride.extraPlugins}`;
- settings.removePlugins += `,${settingsOverride.removePlugins}`;
- settings.sharedSpaces = settingsOverride.sharedSpaces;
- }
-
- // CKEditor requires an element to already have the contentEditable
- // attribute set to "true", otherwise it won't attach an inline editor.
- element.setAttribute('contentEditable', 'true');
-
- return !!CKEDITOR.inline(element, settings);
- },
-
- /**
- * Loads the required external plugins for the editor.
- *
- * @param {object} format
- * The text format used in the editor.
- */
- _loadExternalPlugins(format) {
- const externalPlugins = format.editorSettings.drupalExternalPlugins;
- // Register and load additional CKEditor plugins as necessary.
- if (externalPlugins) {
- Object.keys(externalPlugins || {}).forEach((pluginName) => {
- CKEDITOR.plugins.addExternal(
- pluginName,
- externalPlugins[pluginName],
- '',
- );
- });
- delete format.editorSettings.drupalExternalPlugins;
- }
- },
- };
-
- Drupal.ckeditor = {
- /**
- * Variable storing the current dialog's save callback.
- *
- * @type {?function}
- */
- saveCallback: null,
-
- /**
- * Open a dialog for a Drupal-based plugin.
- *
- * This dynamically loads jQuery UI (if necessary) using the Drupal AJAX
- * framework, then opens a dialog at the specified Drupal path.
- *
- * @param {CKEditor} editor
- * The CKEditor instance that is opening the dialog.
- * @param {string} url
- * The URL that contains the contents of the dialog.
- * @param {object} existingValues
- * Existing values that will be sent via POST to the url for the dialog
- * contents.
- * @param {function} saveCallback
- * A function to be called upon saving the dialog.
- * @param {object} dialogSettings
- * An object containing settings to be passed to the jQuery UI.
- */
- openDialog(editor, url, existingValues, saveCallback, dialogSettings) {
- // Locate a suitable place to display our loading indicator.
- let $target = $(editor.container.$);
- if (editor.elementMode === CKEDITOR.ELEMENT_MODE_REPLACE) {
- $target = $target.find('.cke_contents');
- }
-
- // Remove any previous loading indicator.
- $target
- .css('position', 'relative')
- .find('.ckeditor-dialog-loading')
- .remove();
-
- // Add a consistent dialog class.
- const classes = dialogSettings.dialogClass
- ? dialogSettings.dialogClass.split(' ')
- : [];
- classes.push('ui-dialog--narrow');
- dialogSettings.dialogClass = classes.join(' ');
- dialogSettings.autoResize =
- window.matchMedia('(min-width: 600px)').matches;
- dialogSettings.width = 'auto';
-
- // Add a "Loading…" message, hide it underneath the CKEditor toolbar,
- // create a Drupal.Ajax instance to load the dialog and trigger it.
- const $content = $(
- `<div class="ckeditor-dialog-loading"><span style="top: -40px;" class="ckeditor-dialog-loading-link">${Drupal.t(
- 'Loading...',
- )}</span></div>`,
- );
- $content.appendTo($target);
-
- const ckeditorAjaxDialog = Drupal.ajax({
- dialog: dialogSettings,
- dialogType: 'modal',
- selector: '.ckeditor-dialog-loading-link',
- url,
- progress: { type: 'throbber' },
- submit: {
- editor_object: existingValues,
- },
- });
- ckeditorAjaxDialog.execute();
-
- // After a short delay, show "Loading…" message.
- window.setTimeout(() => {
- $content.find('span').animate({ top: '0px' });
- }, 1000);
-
- // Store the save callback to be executed when this dialog is closed.
- Drupal.ckeditor.saveCallback = saveCallback;
- },
- };
-
- // Moves the dialog to the top of the CKEDITOR stack.
- $(window).on('dialogcreate', (e, dialog, $element, settings) => {
- $('.ui-dialog--narrow').css('zIndex', CKEDITOR.config.baseFloatZIndex + 1);
- });
-
- // Respond to new dialogs that are opened by CKEditor, closing the AJAX loader.
- $(window).on('dialog:beforecreate', (e, dialog, $element, settings) => {
- $('.ckeditor-dialog-loading').animate({ top: '-40px' }, function () {
- $(this).remove();
- });
- });
-
- // Respond to dialogs that are saved, sending data back to CKEditor.
- $(window).on('editor:dialogsave', (e, values) => {
- if (Drupal.ckeditor.saveCallback) {
- Drupal.ckeditor.saveCallback(values);
- }
- });
-
- // Respond to dialogs that are closed, removing the current save handler.
- $(window).on('dialog:afterclose', (e, dialog, $element) => {
- if (Drupal.ckeditor.saveCallback) {
- Drupal.ckeditor.saveCallback = null;
- }
- });
-
- // Formulate a default formula for the maximum autoGrow height.
- $(document).on('drupalViewportOffsetChange', () => {
- CKEDITOR.config.autoGrow_maxHeight =
- 0.7 *
- (window.innerHeight - displace.offsets.top - displace.offsets.bottom);
- });
-
- // Redirect on hash change when the original hash has an associated CKEditor.
- function redirectTextareaFragmentToCKEditorInstance() {
- const hash = window.location.hash.substr(1);
- const element = document.getElementById(hash);
- if (element) {
- const editor = CKEDITOR.dom.element.get(element).getEditor();
- if (editor) {
- const id = editor.container.getAttribute('id');
- window.location.replace(`#${id}`);
- }
- }
- }
- $(window).on(
- 'hashchange.ckeditor',
- redirectTextareaFragmentToCKEditorInstance,
- );
-
- // Set autoGrow to make the editor grow the moment it is created.
- CKEDITOR.config.autoGrow_onStartup = true;
-
- // Default max height. Will be updated as the viewport changes.
- CKEDITOR.config.autoGrow_maxHeight = 0.7 * window.innerHeight;
-
- // Set the CKEditor cache-busting string to the same value as Drupal.
- CKEDITOR.timestamp = drupalSettings.ckeditor.timestamp;
-
- if (AjaxCommands) {
- /**
- * Command to add style sheets to a CKEditor instance.
- *
- * Works for both iframe and inline CKEditor instances.
- *
- * @param {Drupal.Ajax} [ajax]
- * {@link Drupal.Ajax} object created by {@link Drupal.ajax}.
- * @param {object} response
- * The response from the Ajax request.
- * @param {string} response.editor_id
- * The CKEditor instance ID.
- * @param {number} [status]
- * The XMLHttpRequest status.
- *
- * @see http://docs.ckeditor.com/#!/api/CKEDITOR.dom.document
- */
- AjaxCommands.prototype.ckeditor_add_stylesheet = function (
- ajax,
- response,
- status,
- ) {
- const editor = CKEDITOR.instances[response.editor_id];
-
- if (editor) {
- response.stylesheets.forEach((url) => {
- editor.document.appendStyleSheet(url);
- });
- }
- };
- }
-})(
- Drupal,
- Drupal.debounce,
- CKEDITOR,
- jQuery,
- Drupal.displace,
- Drupal.AjaxCommands,
-);
diff --git a/core/modules/ckeditor/js/ckeditor.js b/core/modules/ckeditor/js/ckeditor.js
deleted file mode 100644
index e9431d793068..000000000000
--- a/core/modules/ckeditor/js/ckeditor.js
+++ /dev/null
@@ -1,212 +0,0 @@
-/**
-* DO NOT EDIT THIS FILE.
-* See the following change record for more information,
-* https://www.drupal.org/node/2815083
-* @preserve
-**/
-
-(function (Drupal, debounce, CKEDITOR, $, displace, AjaxCommands) {
- Drupal.editors.ckeditor = {
- attach(element, format) {
- this._loadExternalPlugins(format);
-
- format.editorSettings.drupal = {
- format: format.format
- };
- const label = $(`label[for=${element.getAttribute('id')}]`).html();
- format.editorSettings.title = Drupal.t('Rich Text Editor, !label field', {
- '!label': label
- });
- return !!CKEDITOR.replace(element, format.editorSettings);
- },
-
- detach(element, format, trigger) {
- const editor = CKEDITOR.dom.element.get(element).getEditor();
-
- if (editor) {
- if (trigger === 'serialize') {
- editor.updateElement();
- } else {
- editor.destroy();
- element.removeAttribute('contentEditable');
- }
- }
-
- return !!editor;
- },
-
- onChange(element, callback) {
- const editor = CKEDITOR.dom.element.get(element).getEditor();
-
- if (editor) {
- editor.on('change', debounce(() => {
- callback(editor.getData());
- }, 400));
- editor.on('mode', () => {
- const editable = editor.editable();
-
- if (!editable.isInline()) {
- editor.on('autoGrow', evt => {
- const doc = evt.editor.document;
- const scrollable = CKEDITOR.env.quirks ? doc.getBody() : doc.getDocumentElement();
-
- if (scrollable.$.scrollHeight < scrollable.$.clientHeight) {
- scrollable.setStyle('overflow-y', 'hidden');
- } else {
- scrollable.removeStyle('overflow-y');
- }
- }, null, null, 10000);
- }
- });
- }
-
- return !!editor;
- },
-
- attachInlineEditor(element, format, mainToolbarId, floatedToolbarId) {
- this._loadExternalPlugins(format);
-
- format.editorSettings.drupal = {
- format: format.format
- };
- const settings = $.extend(true, {}, format.editorSettings);
-
- if (mainToolbarId) {
- const settingsOverride = {
- extraPlugins: 'sharedspace',
- removePlugins: 'floatingspace,elementspath',
- sharedSpaces: {
- top: mainToolbarId
- }
- };
- let sourceButtonFound = false;
-
- for (let i = 0; !sourceButtonFound && i < settings.toolbar.length; i++) {
- if (settings.toolbar[i] !== '/') {
- for (let j = 0; !sourceButtonFound && j < settings.toolbar[i].items.length; j++) {
- if (settings.toolbar[i].items[j] === 'Source') {
- sourceButtonFound = true;
- settings.toolbar[i].items[j] = 'Sourcedialog';
- settingsOverride.extraPlugins += ',sourcedialog';
- settingsOverride.removePlugins += ',sourcearea';
- }
- }
- }
- }
-
- settings.extraPlugins += `,${settingsOverride.extraPlugins}`;
- settings.removePlugins += `,${settingsOverride.removePlugins}`;
- settings.sharedSpaces = settingsOverride.sharedSpaces;
- }
-
- element.setAttribute('contentEditable', 'true');
- return !!CKEDITOR.inline(element, settings);
- },
-
- _loadExternalPlugins(format) {
- const externalPlugins = format.editorSettings.drupalExternalPlugins;
-
- if (externalPlugins) {
- Object.keys(externalPlugins || {}).forEach(pluginName => {
- CKEDITOR.plugins.addExternal(pluginName, externalPlugins[pluginName], '');
- });
- delete format.editorSettings.drupalExternalPlugins;
- }
- }
-
- };
- Drupal.ckeditor = {
- saveCallback: null,
-
- openDialog(editor, url, existingValues, saveCallback, dialogSettings) {
- let $target = $(editor.container.$);
-
- if (editor.elementMode === CKEDITOR.ELEMENT_MODE_REPLACE) {
- $target = $target.find('.cke_contents');
- }
-
- $target.css('position', 'relative').find('.ckeditor-dialog-loading').remove();
- const classes = dialogSettings.dialogClass ? dialogSettings.dialogClass.split(' ') : [];
- classes.push('ui-dialog--narrow');
- dialogSettings.dialogClass = classes.join(' ');
- dialogSettings.autoResize = window.matchMedia('(min-width: 600px)').matches;
- dialogSettings.width = 'auto';
- const $content = $(`<div class="ckeditor-dialog-loading"><span style="top: -40px;" class="ckeditor-dialog-loading-link">${Drupal.t('Loading...')}</span></div>`);
- $content.appendTo($target);
- const ckeditorAjaxDialog = Drupal.ajax({
- dialog: dialogSettings,
- dialogType: 'modal',
- selector: '.ckeditor-dialog-loading-link',
- url,
- progress: {
- type: 'throbber'
- },
- submit: {
- editor_object: existingValues
- }
- });
- ckeditorAjaxDialog.execute();
- window.setTimeout(() => {
- $content.find('span').animate({
- top: '0px'
- });
- }, 1000);
- Drupal.ckeditor.saveCallback = saveCallback;
- }
-
- };
- $(window).on('dialogcreate', (e, dialog, $element, settings) => {
- $('.ui-dialog--narrow').css('zIndex', CKEDITOR.config.baseFloatZIndex + 1);
- });
- $(window).on('dialog:beforecreate', (e, dialog, $element, settings) => {
- $('.ckeditor-dialog-loading').animate({
- top: '-40px'
- }, function () {
- $(this).remove();
- });
- });
- $(window).on('editor:dialogsave', (e, values) => {
- if (Drupal.ckeditor.saveCallback) {
- Drupal.ckeditor.saveCallback(values);
- }
- });
- $(window).on('dialog:afterclose', (e, dialog, $element) => {
- if (Drupal.ckeditor.saveCallback) {
- Drupal.ckeditor.saveCallback = null;
- }
- });
- $(document).on('drupalViewportOffsetChange', () => {
- CKEDITOR.config.autoGrow_maxHeight = 0.7 * (window.innerHeight - displace.offsets.top - displace.offsets.bottom);
- });
-
- function redirectTextareaFragmentToCKEditorInstance() {
- const hash = window.location.hash.substr(1);
- const element = document.getElementById(hash);
-
- if (element) {
- const editor = CKEDITOR.dom.element.get(element).getEditor();
-
- if (editor) {
- const id = editor.container.getAttribute('id');
- window.location.replace(`#${id}`);
- }
- }
- }
-
- $(window).on('hashchange.ckeditor', redirectTextareaFragmentToCKEditorInstance);
- CKEDITOR.config.autoGrow_onStartup = true;
- CKEDITOR.config.autoGrow_maxHeight = 0.7 * window.innerHeight;
- CKEDITOR.timestamp = drupalSettings.ckeditor.timestamp;
-
- if (AjaxCommands) {
- AjaxCommands.prototype.ckeditor_add_stylesheet = function (ajax, response, status) {
- const editor = CKEDITOR.instances[response.editor_id];
-
- if (editor) {
- response.stylesheets.forEach(url => {
- editor.document.appendStyleSheet(url);
- });
- }
- };
- }
-})(Drupal, Drupal.debounce, CKEDITOR, jQuery, Drupal.displace, Drupal.AjaxCommands); \ No newline at end of file
diff --git a/core/modules/ckeditor/js/ckeditor.language.admin.es6.js b/core/modules/ckeditor/js/ckeditor.language.admin.es6.js
deleted file mode 100644
index 2ec193afccb6..000000000000
--- a/core/modules/ckeditor/js/ckeditor.language.admin.es6.js
+++ /dev/null
@@ -1,20 +0,0 @@
-(function ($, Drupal) {
- /**
- * Provides the summary for the "language" plugin settings vertical tab.
- */
- Drupal.behaviors.ckeditorLanguageSettingsSummary = {
- attach() {
- $('#edit-editor-settings-plugins-language').drupalSetSummary(
- (context) => {
- const $selected = $(
- '#edit-editor-settings-plugins-language-language-list-type option:selected',
- );
- if ($selected.length) {
- return $selected[0].textContent;
- }
- return '';
- },
- );
- },
- };
-})(jQuery, Drupal);
diff --git a/core/modules/ckeditor/js/ckeditor.language.admin.js b/core/modules/ckeditor/js/ckeditor.language.admin.js
deleted file mode 100644
index 8d5941930cf4..000000000000
--- a/core/modules/ckeditor/js/ckeditor.language.admin.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/**
-* DO NOT EDIT THIS FILE.
-* See the following change record for more information,
-* https://www.drupal.org/node/2815083
-* @preserve
-**/
-
-(function ($, Drupal) {
- Drupal.behaviors.ckeditorLanguageSettingsSummary = {
- attach() {
- $('#edit-editor-settings-plugins-language').drupalSetSummary(context => {
- const $selected = $('#edit-editor-settings-plugins-language-language-list-type option:selected');
-
- if ($selected.length) {
- return $selected[0].textContent;
- }
-
- return '';
- });
- }
-
- };
-})(jQuery, Drupal); \ No newline at end of file
diff --git a/core/modules/ckeditor/js/ckeditor.off-canvas-css-reset.es6.js b/core/modules/ckeditor/js/ckeditor.off-canvas-css-reset.es6.js
deleted file mode 100644
index 0ccc378bda70..000000000000
--- a/core/modules/ckeditor/js/ckeditor.off-canvas-css-reset.es6.js
+++ /dev/null
@@ -1,112 +0,0 @@
-/**
- * @file
- * Provides styles for CKEditor inside off-canvas dialogs.
- */
-
-(($, CKEDITOR) => {
- /**
- * Takes a string of CKEditor CSS and modifies it for use in off-canvas.
- *
- * @param {string} originalCss
- * The CSS rules from CKEditor.
- * @return {string}
- * The rules from originalCss with extra specificity for off-canvas.
- */
- const convertToOffCanvasCss = (originalCss) => {
- const selectorPrefix = '#drupal-off-canvas ';
- const skinPath = `${CKEDITOR.basePath}${CKEDITOR.skinName}/`;
- const css = originalCss
- .substring(originalCss.indexOf('*/') + 2)
- .trim()
- .replace(/}/g, `}${selectorPrefix}`)
- .replace(/,/g, `,${selectorPrefix}`)
- .replace(/url\(/g, skinPath);
- return `${selectorPrefix}${css}`;
- };
-
- /**
- * Inserts CSS rules into DOM.
- *
- * @param {string} cssToInsert
- * CSS rules to be inserted
- */
- const insertCss = (cssToInsert) => {
- const offCanvasCss = document.createElement('style');
- offCanvasCss.innerHTML = cssToInsert;
- offCanvasCss.setAttribute('id', 'ckeditor-off-canvas-reset');
- document.body.appendChild(offCanvasCss);
- };
-
- /**
- * Adds CSS so CKEditor is styled properly in off-canvas.
- */
- const addCkeditorOffCanvasCss = () => {
- // If #ckeditor-off-canvas-reset exists, this has already run.
- if (document.getElementById('ckeditor-off-canvas-reset')) {
- return;
- }
- // CKEDITOR.skin.getPath() requires the CKEDITOR.skinName property.
- // @see https://stackoverflow.com/a/17336982
- CKEDITOR.skinName = CKEDITOR.skin.name;
-
- // Get the paths to the css CKEditor is using.
- const editorCssPath = CKEDITOR.skin.getPath('editor');
- const dialogCssPath = CKEDITOR.skin.getPath('dialog');
-
- // The key for cached CSS in localStorage is based on the CSS paths.
- const storedOffCanvasCss = window.localStorage.getItem(
- `Drupal.off-canvas.css.${editorCssPath}${dialogCssPath}`,
- );
-
- // See if CSS is cached in localStorage, and use that when available.
- if (storedOffCanvasCss) {
- insertCss(storedOffCanvasCss);
- return;
- }
-
- // If CSS unavailable in localStorage, get the files via AJAX and parse.
- $.when($.get(editorCssPath), $.get(dialogCssPath)).done(
- (editorCss, dialogCss) => {
- const offCanvasEditorCss = convertToOffCanvasCss(editorCss[0]);
- const offCanvasDialogCss = convertToOffCanvasCss(dialogCss[0]);
- const cssToInsert = `#drupal-off-canvas .cke_inner * {background: transparent;}
- ${offCanvasEditorCss}
- ${offCanvasDialogCss}`;
- insertCss(cssToInsert);
-
- // The localStorage key for accessing the cached CSS is based on the
- // paths of the CKEditor CSS files. This prevents localStorage from
- // providing outdated CSS. If new files are used due to using a new
- // skin, a new localStorage key is created.
- //
- // The CSS paths also include the cache-busting query string that is
- // stored in state and CKEDITOR.timestamp. This query string changes on
- // update and cache clear and prevents localStorage from providing
- // stale CKEditor CSS.
- //
- // Before adding the CSS rules to localStorage, there is a check that
- // confirms the cache-busting query (CKEDITOR.timestamp) is in the CSS
- // paths. This prevents localStorage from caching something unbustable.
- //
- // @see ckeditor_library_info_alter()
- if (
- CKEDITOR.timestamp &&
- editorCssPath.indexOf(CKEDITOR.timestamp) !== -1 &&
- dialogCssPath.indexOf(CKEDITOR.timestamp) !== -1
- ) {
- Object.keys(window.localStorage).forEach((key) => {
- if (key.indexOf('Drupal.off-canvas.css.') === 0) {
- window.localStorage.removeItem(key);
- }
- });
- window.localStorage.setItem(
- `Drupal.off-canvas.css.${editorCssPath}${dialogCssPath}`,
- cssToInsert,
- );
- }
- },
- );
- };
-
- addCkeditorOffCanvasCss();
-})(jQuery, CKEDITOR);
diff --git a/core/modules/ckeditor/js/ckeditor.off-canvas-css-reset.js b/core/modules/ckeditor/js/ckeditor.off-canvas-css-reset.js
deleted file mode 100644
index e0b9dbf9a322..000000000000
--- a/core/modules/ckeditor/js/ckeditor.off-canvas-css-reset.js
+++ /dev/null
@@ -1,58 +0,0 @@
-/**
-* DO NOT EDIT THIS FILE.
-* See the following change record for more information,
-* https://www.drupal.org/node/2815083
-* @preserve
-**/
-
-(($, CKEDITOR) => {
- const convertToOffCanvasCss = originalCss => {
- const selectorPrefix = '#drupal-off-canvas ';
- const skinPath = `${CKEDITOR.basePath}${CKEDITOR.skinName}/`;
- const css = originalCss.substring(originalCss.indexOf('*/') + 2).trim().replace(/}/g, `}${selectorPrefix}`).replace(/,/g, `,${selectorPrefix}`).replace(/url\(/g, skinPath);
- return `${selectorPrefix}${css}`;
- };
-
- const insertCss = cssToInsert => {
- const offCanvasCss = document.createElement('style');
- offCanvasCss.innerHTML = cssToInsert;
- offCanvasCss.setAttribute('id', 'ckeditor-off-canvas-reset');
- document.body.appendChild(offCanvasCss);
- };
-
- const addCkeditorOffCanvasCss = () => {
- if (document.getElementById('ckeditor-off-canvas-reset')) {
- return;
- }
-
- CKEDITOR.skinName = CKEDITOR.skin.name;
- const editorCssPath = CKEDITOR.skin.getPath('editor');
- const dialogCssPath = CKEDITOR.skin.getPath('dialog');
- const storedOffCanvasCss = window.localStorage.getItem(`Drupal.off-canvas.css.${editorCssPath}${dialogCssPath}`);
-
- if (storedOffCanvasCss) {
- insertCss(storedOffCanvasCss);
- return;
- }
-
- $.when($.get(editorCssPath), $.get(dialogCssPath)).done((editorCss, dialogCss) => {
- const offCanvasEditorCss = convertToOffCanvasCss(editorCss[0]);
- const offCanvasDialogCss = convertToOffCanvasCss(dialogCss[0]);
- const cssToInsert = `#drupal-off-canvas .cke_inner * {background: transparent;}
- ${offCanvasEditorCss}
- ${offCanvasDialogCss}`;
- insertCss(cssToInsert);
-
- if (CKEDITOR.timestamp && editorCssPath.indexOf(CKEDITOR.timestamp) !== -1 && dialogCssPath.indexOf(CKEDITOR.timestamp) !== -1) {
- Object.keys(window.localStorage).forEach(key => {
- if (key.indexOf('Drupal.off-canvas.css.') === 0) {
- window.localStorage.removeItem(key);
- }
- });
- window.localStorage.setItem(`Drupal.off-canvas.css.${editorCssPath}${dialogCssPath}`, cssToInsert);
- }
- });
- };
-
- addCkeditorOffCanvasCss();
-})(jQuery, CKEDITOR); \ No newline at end of file
diff --git a/core/modules/ckeditor/js/ckeditor.stylescombo.admin.es6.js b/core/modules/ckeditor/js/ckeditor.stylescombo.admin.es6.js
deleted file mode 100644
index 6717a65433ea..000000000000
--- a/core/modules/ckeditor/js/ckeditor.stylescombo.admin.es6.js
+++ /dev/null
@@ -1,134 +0,0 @@
-/**
- * @file
- * CKEditor StylesCombo admin behavior.
- */
-
-(function ($, Drupal, drupalSettings, _) {
- /**
- * Ensures that the "stylescombo" button's metadata remains up-to-date.
- *
- * Triggers the CKEditorPluginSettingsChanged event whenever the "stylescombo"
- * plugin settings change, to ensure that the corresponding feature metadata
- * is immediately updated — i.e. ensure that HTML tags and classes entered
- * here are known to be "required", which may affect filter settings.
- *
- * @type {Drupal~behavior}
- *
- * @prop {Drupal~behaviorAttach} attach
- * Attaches admin behavior to the "stylescombo" button.
- */
- Drupal.behaviors.ckeditorStylesComboSettings = {
- attach(context) {
- const $context = $(context);
-
- // React to changes in the list of user-defined styles: calculate the new
- // stylesSet setting up to 2 times per second, and if it is different,
- // fire the CKEditorPluginSettingsChanged event with the updated parts of
- // the CKEditor configuration. (This will, in turn, cause the hidden
- // CKEditor instance to be updated and a drupalEditorFeatureModified event
- // to fire.)
- const $ckeditorActiveToolbar = $context
- .find('.ckeditor-toolbar-configuration')
- .find('.ckeditor-toolbar-active');
- let previousStylesSet =
- drupalSettings.ckeditor.hiddenCKEditorConfig.stylesSet;
- const that = this;
- $context
- .find('[name="editor[settings][plugins][stylescombo][styles]"]')
- .on('blur.ckeditorStylesComboSettings', function () {
- const styles = this.value.trim();
- const stylesSet = that._generateStylesSetSetting(styles);
- if (!_.isEqual(previousStylesSet, stylesSet)) {
- previousStylesSet = stylesSet;
- $ckeditorActiveToolbar.trigger('CKEditorPluginSettingsChanged', [
- { stylesSet },
- ]);
- }
- });
- },
-
- /**
- * Builds the "stylesSet" configuration part of the CKEditor JS settings.
- *
- * @see \Drupal\ckeditor\Plugin\ckeditor\plugin\StylesCombo::generateStylesSetSetting()
- *
- * Note that this is a more forgiving implementation than the PHP version:
- * the parsing works identically, but instead of failing on invalid styles,
- * we just ignore those.
- *
- * @param {string} styles
- * The "styles" setting.
- *
- * @return {Array}
- * An array containing the "stylesSet" configuration.
- */
- _generateStylesSetSetting(styles) {
- const stylesSet = [];
-
- styles = styles.replace(/\r/g, '\n');
- const lines = styles.split('\n');
- for (let i = 0; i < lines.length; i++) {
- const style = lines[i].trim();
-
- // Ignore empty lines in between non-empty lines.
- if (style.length === 0) {
- continue;
- }
-
- // Validate syntax: element[.class...]|label pattern expected.
- if (
- style.match(/^ *[a-zA-Z0-9]+ *(\.[a-zA-Z0-9_-]+ *)*\| *.+ *$/) ===
- null
- ) {
- // Instead of failing, we just ignore any invalid styles.
- continue;
- }
-
- // Parse.
- const parts = style.split('|');
- const selector = parts[0];
- const label = parts[1];
- const classes = selector.split('.');
- const element = classes.shift();
-
- // Build the data structure CKEditor's stylescombo plugin expects.
- // @see https://ckeditor.com/docs/ckeditor4/latest/guide/dev_howtos_styles.html
- stylesSet.push({
- attributes: { class: classes.join(' ') },
- element,
- name: label,
- });
- }
-
- return stylesSet;
- },
- };
-
- /**
- * Provides the summary for the "stylescombo" plugin settings vertical tab.
- *
- * @type {Drupal~behavior}
- *
- * @prop {Drupal~behaviorAttach} attach
- * Attaches summary behavior to the plugin settings vertical tab.
- */
- Drupal.behaviors.ckeditorStylesComboSettingsSummary = {
- attach() {
- $('[data-ckeditor-plugin-id="stylescombo"]').drupalSetSummary(
- (context) => {
- const stylesElement = document.querySelector(
- '[data-drupal-selector="edit-editor-settings-plugins-stylescombo-styles"]',
- );
- const styles = stylesElement ? stylesElement.value.trim() : '';
-
- if (styles.length === 0) {
- return Drupal.t('No styles configured');
- }
-
- const count = styles.split('\n').length;
- return Drupal.t('@count styles configured', { '@count': count });
- },
- );
- },
- };
-})(jQuery, Drupal, drupalSettings, _);
diff --git a/core/modules/ckeditor/js/ckeditor.stylescombo.admin.js b/core/modules/ckeditor/js/ckeditor.stylescombo.admin.js
deleted file mode 100644
index bd51791e1b95..000000000000
--- a/core/modules/ckeditor/js/ckeditor.stylescombo.admin.js
+++ /dev/null
@@ -1,81 +0,0 @@
-/**
-* DO NOT EDIT THIS FILE.
-* See the following change record for more information,
-* https://www.drupal.org/node/2815083
-* @preserve
-**/
-
-(function ($, Drupal, drupalSettings, _) {
- Drupal.behaviors.ckeditorStylesComboSettings = {
- attach(context) {
- const $context = $(context);
- const $ckeditorActiveToolbar = $context.find('.ckeditor-toolbar-configuration').find('.ckeditor-toolbar-active');
- let previousStylesSet = drupalSettings.ckeditor.hiddenCKEditorConfig.stylesSet;
- const that = this;
- $context.find('[name="editor[settings][plugins][stylescombo][styles]"]').on('blur.ckeditorStylesComboSettings', function () {
- const styles = this.value.trim();
-
- const stylesSet = that._generateStylesSetSetting(styles);
-
- if (!_.isEqual(previousStylesSet, stylesSet)) {
- previousStylesSet = stylesSet;
- $ckeditorActiveToolbar.trigger('CKEditorPluginSettingsChanged', [{
- stylesSet
- }]);
- }
- });
- },
-
- _generateStylesSetSetting(styles) {
- const stylesSet = [];
- styles = styles.replace(/\r/g, '\n');
- const lines = styles.split('\n');
-
- for (let i = 0; i < lines.length; i++) {
- const style = lines[i].trim();
-
- if (style.length === 0) {
- continue;
- }
-
- if (style.match(/^ *[a-zA-Z0-9]+ *(\.[a-zA-Z0-9_-]+ *)*\| *.+ *$/) === null) {
- continue;
- }
-
- const parts = style.split('|');
- const selector = parts[0];
- const label = parts[1];
- const classes = selector.split('.');
- const element = classes.shift();
- stylesSet.push({
- attributes: {
- class: classes.join(' ')
- },
- element,
- name: label
- });
- }
-
- return stylesSet;
- }
-
- };
- Drupal.behaviors.ckeditorStylesComboSettingsSummary = {
- attach() {
- $('[data-ckeditor-plugin-id="stylescombo"]').drupalSetSummary(context => {
- const stylesElement = document.querySelector('[data-drupal-selector="edit-editor-settings-plugins-stylescombo-styles"]');
- const styles = stylesElement ? stylesElement.value.trim() : '';
-
- if (styles.length === 0) {
- return Drupal.t('No styles configured');
- }
-
- const count = styles.split('\n').length;
- return Drupal.t('@count styles configured', {
- '@count': count
- });
- });
- }
-
- };
-})(jQuery, Drupal, drupalSettings, _); \ No newline at end of file
diff --git a/core/modules/ckeditor/js/models/Model.es6.js b/core/modules/ckeditor/js/models/Model.es6.js
deleted file mode 100644
index 08aaf7f7eb9d..000000000000
--- a/core/modules/ckeditor/js/models/Model.es6.js
+++ /dev/null
@@ -1,73 +0,0 @@
-/**
- * @file
- * A Backbone Model for the state of a CKEditor toolbar configuration .
- */
-
-(function (Drupal, Backbone) {
- /**
- * Backbone model for the CKEditor toolbar configuration state.
- *
- * @constructor
- *
- * @augments Backbone.Model
- */
- Drupal.ckeditor.Model = Backbone.Model.extend(
- /** @lends Drupal.ckeditor.Model# */ {
- /**
- * Default values.
- *
- * @type {object}
- */
- defaults: /** @lends Drupal.ckeditor.Model# */ {
- /**
- * The CKEditor configuration that is being manipulated through the UI.
- */
- activeEditorConfig: null,
-
- /**
- * The textarea that contains the serialized representation of the active
- * CKEditor configuration.
- */
- $textarea: null,
-
- /**
- * Tracks whether the active toolbar DOM structure has been changed. When
- * true, activeEditorConfig needs to be updated, and when that is updated,
- * $textarea will also be updated.
- */
- isDirty: false,
-
- /**
- * The configuration for the hidden CKEditor instance that is used to
- * build the features metadata.
- */
- hiddenEditorConfig: null,
-
- /**
- * A hash that maps buttons to features.
- */
- buttonsToFeatures: null,
-
- /**
- * A hash, keyed by a feature name, that details CKEditor plugin features.
- */
- featuresMetadata: null,
-
- /**
- * Whether the button group names are currently visible.
- */
- groupNamesVisible: false,
- },
-
- /**
- * @method
- */
- sync() {
- // Push the settings into the textarea.
- this.get('$textarea').val(
- JSON.stringify(this.get('activeEditorConfig')),
- );
- },
- },
- );
-})(Drupal, Backbone);
diff --git a/core/modules/ckeditor/js/models/Model.js b/core/modules/ckeditor/js/models/Model.js
deleted file mode 100644
index c3379ead9924..000000000000
--- a/core/modules/ckeditor/js/models/Model.js
+++ /dev/null
@@ -1,25 +0,0 @@
-/**
-* DO NOT EDIT THIS FILE.
-* See the following change record for more information,
-* https://www.drupal.org/node/2815083
-* @preserve
-**/
-
-(function (Drupal, Backbone) {
- Drupal.ckeditor.Model = Backbone.Model.extend({
- defaults: {
- activeEditorConfig: null,
- $textarea: null,
- isDirty: false,
- hiddenEditorConfig: null,
- buttonsToFeatures: null,
- featuresMetadata: null,
- groupNamesVisible: false
- },
-
- sync() {
- this.get('$textarea').val(JSON.stringify(this.get('activeEditorConfig')));
- }
-
- });
-})(Drupal, Backbone); \ No newline at end of file
diff --git a/core/modules/ckeditor/js/plugins/drupalimage/icons/drupalimage.png b/core/modules/ckeditor/js/plugins/drupalimage/icons/drupalimage.png
deleted file mode 100644
index 83cd553d9bb8..000000000000
--- a/core/modules/ckeditor/js/plugins/drupalimage/icons/drupalimage.png
+++ /dev/null
Binary files differ
diff --git a/core/modules/ckeditor/js/plugins/drupalimage/icons/hidpi/drupalimage.png b/core/modules/ckeditor/js/plugins/drupalimage/icons/hidpi/drupalimage.png
deleted file mode 100644
index 63e115dfdb08..000000000000
--- a/core/modules/ckeditor/js/plugins/drupalimage/icons/hidpi/drupalimage.png
+++ /dev/null
Binary files differ
diff --git a/core/modules/ckeditor/js/plugins/drupalimage/plugin.es6.js b/core/modules/ckeditor/js/plugins/drupalimage/plugin.es6.js
deleted file mode 100644
index fda04cbff42e..000000000000
--- a/core/modules/ckeditor/js/plugins/drupalimage/plugin.es6.js
+++ /dev/null
@@ -1,400 +0,0 @@
-/**
- * @file
- * Drupal Image plugin.
- *
- * This alters the existing CKEditor image2 widget plugin to:
- * - require a data-entity-type and a data-entity-uuid attribute (which Drupal
- * uses to track where images are being used)
- * - use a Drupal-native dialog (that is in fact just an alterable Drupal form
- * like any other) instead of CKEditor's own dialogs.
- *
- * @see \Drupal\editor\Form\EditorImageDialog
- *
- * @ignore
- */
-
-(function ($, Drupal, CKEDITOR) {
- /**
- * Gets the focused widget, if of the type specific for this plugin.
- *
- * @param {CKEDITOR.editor} editor
- * A CKEditor instance.
- *
- * @return {?CKEDITOR.plugins.widget}
- * The focused image2 widget instance, or null.
- */
- function getFocusedWidget(editor) {
- const widget = editor.widgets.focused;
-
- if (widget && widget.name === 'image') {
- return widget;
- }
-
- return null;
- }
-
- /**
- * Integrates the drupalimage widget with the drupallink plugin.
- *
- * Makes images linkable.
- *
- * @param {CKEDITOR.editor} editor
- * A CKEditor instance.
- */
- function linkCommandIntegrator(editor) {
- // Nothing to integrate with if the drupallink plugin is not loaded.
- if (!editor.plugins.drupallink) {
- return;
- }
-
- CKEDITOR.plugins.drupallink.registerLinkableWidget('image');
-
- // Override default behavior of 'drupalunlink' command.
- editor.getCommand('drupalunlink').on('exec', function (evt) {
- const widget = getFocusedWidget(editor);
-
- // Override 'drupalunlink' only when link truly belongs to the widget. If
- // wrapped inline widget in a link, let default unlink work.
- // @see https://dev.ckeditor.com/ticket/11814
- if (!widget || !widget.parts.link) {
- return;
- }
-
- widget.setData('link', null);
-
- // Selection (which is fake) may not change if unlinked image in focused
- // widget, i.e. if captioned image. Let's refresh command state manually
- // here.
- this.refresh(editor, editor.elementPath());
-
- evt.cancel();
- });
-
- // Override default refresh of 'drupalunlink' command.
- editor.getCommand('drupalunlink').on('refresh', function (evt) {
- const widget = getFocusedWidget(editor);
-
- if (!widget) {
- return;
- }
-
- // Note that widget may be wrapped in a link, which
- // does not belong to that widget (#11814).
- this.setState(
- widget.data.link || widget.wrapper.getAscendant('a')
- ? CKEDITOR.TRISTATE_OFF
- : CKEDITOR.TRISTATE_DISABLED,
- );
-
- evt.cancel();
- });
- }
-
- CKEDITOR.plugins.add('drupalimage', {
- requires: 'image2',
- icons: 'drupalimage',
- hidpi: true,
-
- beforeInit(editor) {
- // Override the image2 widget definition to require and handle the
- // additional data-entity-type and data-entity-uuid attributes.
- editor.on('widgetDefinition', (event) => {
- const widgetDefinition = event.data;
- if (widgetDefinition.name !== 'image') {
- return;
- }
-
- // First, convert requiredContent & allowedContent from the string
- // format that image2 uses for both to formats that are better suited
- // for extending, so that both this basic drupalimage plugin and Drupal
- // modules can easily extend it.
- // @see http://docs.ckeditor.com/#!/api/CKEDITOR.filter.allowedContentRules
- // Mapped from image2's allowedContent. Unlike image2, we don't allow
- // <figure>, <figcaption>, <div> or <p> in our downcast, so we omit
- // those. For the <img> tag, we list all attributes it lists, but omit
- // the classes, because the listed classes are for alignment, and for
- // alignment we use the data-align attribute.
- widgetDefinition.allowedContent = {
- img: {
- attributes: {
- '!src': true,
- '!alt': true,
- width: true,
- height: true,
- },
- classes: {},
- },
- };
- // Mapped from image2's requiredContent: "img[src,alt]". This does not
- // use the object format unlike above, but a CKEDITOR.style instance,
- // because requiredContent does not support the object format.
- // @see https://www.drupal.org/node/2585173#comment-10456981
- widgetDefinition.requiredContent = new CKEDITOR.style({
- element: 'img',
- attributes: {
- src: '',
- alt: '',
- },
- });
-
- // Extend requiredContent & allowedContent.
- // CKEDITOR.style is an immutable object: we cannot modify its
- // definition to extend requiredContent. Hence we get the definition,
- // modify it, and pass it to a new CKEDITOR.style instance.
- const requiredContent =
- widgetDefinition.requiredContent.getDefinition();
- requiredContent.attributes['data-entity-type'] = '';
- requiredContent.attributes['data-entity-uuid'] = '';
- widgetDefinition.requiredContent = new CKEDITOR.style(requiredContent);
- widgetDefinition.allowedContent.img.attributes[
- '!data-entity-type'
- ] = true;
- widgetDefinition.allowedContent.img.attributes[
- '!data-entity-uuid'
- ] = true;
-
- // Override downcast(): since we only accept <img> in our upcast method,
- // the element is already correct. We only need to update the element's
- // data-entity-uuid attribute.
- widgetDefinition.downcast = function (element) {
- element.attributes['data-entity-type'] =
- this.data['data-entity-type'];
- element.attributes['data-entity-uuid'] =
- this.data['data-entity-uuid'];
- };
-
- // We want to upcast <img> elements to a DOM structure required by the
- // image2 widget; we only accept an <img> tag, and that <img> tag MAY
- // have a data-entity-type and a data-entity-uuid attribute.
- widgetDefinition.upcast = function (element, data) {
- if (element.name !== 'img') {
- return;
- }
- // Don't initialize on pasted fake objects.
- if (element.attributes['data-cke-realelement']) {
- return;
- }
-
- // Parse the data-entity-type attribute.
- data['data-entity-type'] = element.attributes['data-entity-type'];
- // Parse the data-entity-uuid attribute.
- data['data-entity-uuid'] = element.attributes['data-entity-uuid'];
-
- return element;
- };
-
- // Overrides default implementation. Used to populate the "classes"
- // property of the widget's "data" property, which is used for the
- // "widget styles" functionality
- // (http://docs.ckeditor.com/#!/guide/dev_styles-section-widget-styles).
- // Is applied to whatever the main element of the widget is (<figure> or
- // <img>). The classes in image2_captionedClass are always added due to
- // a bug in CKEditor. In the case of drupalimage, we don't ever want to
- // add that class, because the widget template already contains it.
- // @see http://dev.ckeditor.com/ticket/13888
- // @see https://www.drupal.org/node/2268941
- const originalGetClasses = widgetDefinition.getClasses;
- widgetDefinition.getClasses = function () {
- const classes = originalGetClasses.call(this);
- const captionedClasses = (
- this.editor.config.image2_captionedClass || ''
- ).split(/\s+/);
-
- if (captionedClasses.length && classes) {
- for (let i = 0; i < captionedClasses.length; i++) {
- if (captionedClasses[i] in classes) {
- delete classes[captionedClasses[i]];
- }
- }
- }
-
- return classes;
- };
-
- // Protected; keys of the widget data to be sent to the Drupal dialog.
- // Keys in the hash are the keys for image2's data, values are the keys
- // that the Drupal dialog uses.
- widgetDefinition._mapDataToDialog = {
- src: 'src',
- alt: 'alt',
- width: 'width',
- height: 'height',
- 'data-entity-type': 'data-entity-type',
- 'data-entity-uuid': 'data-entity-uuid',
- };
-
- // Protected; transforms widget's data object to the format used by the
- // \Drupal\editor\Form\EditorImageDialog dialog, keeping only the data
- // listed in widgetDefinition._dataForDialog.
- widgetDefinition._dataToDialogValues = function (data) {
- const dialogValues = {};
- const map = widgetDefinition._mapDataToDialog;
- Object.keys(widgetDefinition._mapDataToDialog).forEach((key) => {
- dialogValues[map[key]] = data[key];
- });
- return dialogValues;
- };
-
- // Protected; the inverse of _dataToDialogValues.
- widgetDefinition._dialogValuesToData = function (dialogReturnValues) {
- const data = {};
- const map = widgetDefinition._mapDataToDialog;
- Object.keys(widgetDefinition._mapDataToDialog).forEach((key) => {
- if (dialogReturnValues.hasOwnProperty(map[key])) {
- data[key] = dialogReturnValues[map[key]];
- }
- });
- return data;
- };
-
- // Protected; creates Drupal dialog save callback.
- widgetDefinition._createDialogSaveCallback = function (editor, widget) {
- return function (dialogReturnValues) {
- const firstEdit = !widget.ready;
-
- // Dialog may have blurred the widget. Re-focus it first.
- if (!firstEdit) {
- widget.focus();
- }
-
- editor.fire('saveSnapshot');
-
- // Pass `true` so DocumentFragment will also be returned.
- const container = widget.wrapper.getParent(true);
- const image = widget.parts.image;
-
- // Set the updated widget data, after the necessary conversions from
- // the dialog's return values.
- // Note: on widget#setData this widget instance might be destroyed.
- const data = widgetDefinition._dialogValuesToData(
- dialogReturnValues.attributes,
- );
- widget.setData(data);
-
- // Retrieve the widget once again. It could've been destroyed
- // when shifting state, so might deal with a new instance.
- widget = editor.widgets.getByElement(image);
-
- // It's first edit, just after widget instance creation, but before
- // it was inserted into DOM. So we need to retrieve the widget
- // wrapper from inside the DocumentFragment which we cached above
- // and finalize other things (like ready event and flag).
- if (firstEdit) {
- editor.widgets.finalizeCreation(container);
- }
-
- setTimeout(() => {
- // (Re-)focus the widget.
- widget.focus();
- // Save snapshot for undo support.
- editor.fire('saveSnapshot');
- });
-
- return widget;
- };
- };
-
- const originalInit = widgetDefinition.init;
- widgetDefinition.init = function () {
- originalInit.call(this);
-
- // Update data.link object with attributes if the link has been
- // discovered.
- // @see plugins/image2/plugin.js/init() in CKEditor; this is similar.
- if (this.parts.link) {
- this.setData(
- 'link',
- CKEDITOR.plugins.image2.getLinkAttributesParser()(
- editor,
- this.parts.link,
- ),
- );
- }
- };
- });
-
- // Add a widget#edit listener to every instance of image2 widget in order
- // to handle its editing with a Drupal-native dialog.
- // This includes also a case just after the image was created
- // and dialog should be opened for it for the first time.
- editor.widgets.on('instanceCreated', (event) => {
- const widget = event.data;
-
- if (widget.name !== 'image') {
- return;
- }
-
- widget.on('edit', (event) => {
- // Cancel edit event to break image2's dialog binding
- // (and also to prevent automatic insertion before opening dialog).
- event.cancel();
-
- // Open drupalimage dialog.
- editor.execCommand('editdrupalimage', {
- existingValues: widget.definition._dataToDialogValues(widget.data),
- saveCallback: widget.definition._createDialogSaveCallback(
- editor,
- widget,
- ),
- // Drupal.t() will not work inside CKEditor plugins because CKEditor
- // loads the JavaScript file instead of Drupal. Pull translated
- // strings from the plugin settings that are translated server-side.
- dialogTitle: widget.data.src
- ? editor.config.drupalImage_dialogTitleEdit
- : editor.config.drupalImage_dialogTitleAdd,
- });
- });
- });
-
- // Register the "editdrupalimage" command, which essentially just replaces
- // the "image" command's CKEditor dialog with a Drupal-native dialog.
- editor.addCommand('editdrupalimage', {
- allowedContent:
- 'img[alt,!src,width,height,!data-entity-type,!data-entity-uuid]',
- requiredContent: 'img[alt,src,data-entity-type,data-entity-uuid]',
- modes: { wysiwyg: 1 },
- canUndo: true,
- exec(editor, data) {
- const dialogSettings = {
- title: data.dialogTitle,
- dialogClass: 'editor-image-dialog',
- };
- Drupal.ckeditor.openDialog(
- editor,
- Drupal.url(`editor/dialog/image/${editor.config.drupal.format}`),
- data.existingValues,
- data.saveCallback,
- dialogSettings,
- );
- },
- });
-
- // Register the toolbar button.
- if (editor.ui.addButton) {
- editor.ui.addButton('DrupalImage', {
- label: Drupal.t('Image'),
- // Note that we use the original image2 command!
- command: 'image',
- });
- }
- },
-
- afterInit(editor) {
- linkCommandIntegrator(editor);
- },
- });
-
- // Override image2's integration with the official CKEditor link plugin:
- // integrate with the drupallink plugin instead.
- CKEDITOR.plugins.image2.getLinkAttributesParser = function () {
- return CKEDITOR.plugins.drupallink.parseLinkAttributes;
- };
- CKEDITOR.plugins.image2.getLinkAttributesGetter = function () {
- return CKEDITOR.plugins.drupallink.getLinkAttributes;
- };
-
- // Expose an API for other plugins to interact with drupalimage widgets.
- CKEDITOR.plugins.drupalimage = {
- getFocusedWidget,
- };
-})(jQuery, Drupal, CKEDITOR);
diff --git a/core/modules/ckeditor/js/plugins/drupalimage/plugin.js b/core/modules/ckeditor/js/plugins/drupalimage/plugin.js
deleted file mode 100644
index 570efab2815c..000000000000
--- a/core/modules/ckeditor/js/plugins/drupalimage/plugin.js
+++ /dev/null
@@ -1,249 +0,0 @@
-/**
-* DO NOT EDIT THIS FILE.
-* See the following change record for more information,
-* https://www.drupal.org/node/2815083
-* @preserve
-**/
-
-(function ($, Drupal, CKEDITOR) {
- function getFocusedWidget(editor) {
- const widget = editor.widgets.focused;
-
- if (widget && widget.name === 'image') {
- return widget;
- }
-
- return null;
- }
-
- function linkCommandIntegrator(editor) {
- if (!editor.plugins.drupallink) {
- return;
- }
-
- CKEDITOR.plugins.drupallink.registerLinkableWidget('image');
- editor.getCommand('drupalunlink').on('exec', function (evt) {
- const widget = getFocusedWidget(editor);
-
- if (!widget || !widget.parts.link) {
- return;
- }
-
- widget.setData('link', null);
- this.refresh(editor, editor.elementPath());
- evt.cancel();
- });
- editor.getCommand('drupalunlink').on('refresh', function (evt) {
- const widget = getFocusedWidget(editor);
-
- if (!widget) {
- return;
- }
-
- this.setState(widget.data.link || widget.wrapper.getAscendant('a') ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED);
- evt.cancel();
- });
- }
-
- CKEDITOR.plugins.add('drupalimage', {
- requires: 'image2',
- icons: 'drupalimage',
- hidpi: true,
-
- beforeInit(editor) {
- editor.on('widgetDefinition', event => {
- const widgetDefinition = event.data;
-
- if (widgetDefinition.name !== 'image') {
- return;
- }
-
- widgetDefinition.allowedContent = {
- img: {
- attributes: {
- '!src': true,
- '!alt': true,
- width: true,
- height: true
- },
- classes: {}
- }
- };
- widgetDefinition.requiredContent = new CKEDITOR.style({
- element: 'img',
- attributes: {
- src: '',
- alt: ''
- }
- });
- const requiredContent = widgetDefinition.requiredContent.getDefinition();
- requiredContent.attributes['data-entity-type'] = '';
- requiredContent.attributes['data-entity-uuid'] = '';
- widgetDefinition.requiredContent = new CKEDITOR.style(requiredContent);
- widgetDefinition.allowedContent.img.attributes['!data-entity-type'] = true;
- widgetDefinition.allowedContent.img.attributes['!data-entity-uuid'] = true;
-
- widgetDefinition.downcast = function (element) {
- element.attributes['data-entity-type'] = this.data['data-entity-type'];
- element.attributes['data-entity-uuid'] = this.data['data-entity-uuid'];
- };
-
- widgetDefinition.upcast = function (element, data) {
- if (element.name !== 'img') {
- return;
- }
-
- if (element.attributes['data-cke-realelement']) {
- return;
- }
-
- data['data-entity-type'] = element.attributes['data-entity-type'];
- data['data-entity-uuid'] = element.attributes['data-entity-uuid'];
- return element;
- };
-
- const originalGetClasses = widgetDefinition.getClasses;
-
- widgetDefinition.getClasses = function () {
- const classes = originalGetClasses.call(this);
- const captionedClasses = (this.editor.config.image2_captionedClass || '').split(/\s+/);
-
- if (captionedClasses.length && classes) {
- for (let i = 0; i < captionedClasses.length; i++) {
- if (captionedClasses[i] in classes) {
- delete classes[captionedClasses[i]];
- }
- }
- }
-
- return classes;
- };
-
- widgetDefinition._mapDataToDialog = {
- src: 'src',
- alt: 'alt',
- width: 'width',
- height: 'height',
- 'data-entity-type': 'data-entity-type',
- 'data-entity-uuid': 'data-entity-uuid'
- };
-
- widgetDefinition._dataToDialogValues = function (data) {
- const dialogValues = {};
- const map = widgetDefinition._mapDataToDialog;
- Object.keys(widgetDefinition._mapDataToDialog).forEach(key => {
- dialogValues[map[key]] = data[key];
- });
- return dialogValues;
- };
-
- widgetDefinition._dialogValuesToData = function (dialogReturnValues) {
- const data = {};
- const map = widgetDefinition._mapDataToDialog;
- Object.keys(widgetDefinition._mapDataToDialog).forEach(key => {
- if (dialogReturnValues.hasOwnProperty(map[key])) {
- data[key] = dialogReturnValues[map[key]];
- }
- });
- return data;
- };
-
- widgetDefinition._createDialogSaveCallback = function (editor, widget) {
- return function (dialogReturnValues) {
- const firstEdit = !widget.ready;
-
- if (!firstEdit) {
- widget.focus();
- }
-
- editor.fire('saveSnapshot');
- const container = widget.wrapper.getParent(true);
- const image = widget.parts.image;
-
- const data = widgetDefinition._dialogValuesToData(dialogReturnValues.attributes);
-
- widget.setData(data);
- widget = editor.widgets.getByElement(image);
-
- if (firstEdit) {
- editor.widgets.finalizeCreation(container);
- }
-
- setTimeout(() => {
- widget.focus();
- editor.fire('saveSnapshot');
- });
- return widget;
- };
- };
-
- const originalInit = widgetDefinition.init;
-
- widgetDefinition.init = function () {
- originalInit.call(this);
-
- if (this.parts.link) {
- this.setData('link', CKEDITOR.plugins.image2.getLinkAttributesParser()(editor, this.parts.link));
- }
- };
- });
- editor.widgets.on('instanceCreated', event => {
- const widget = event.data;
-
- if (widget.name !== 'image') {
- return;
- }
-
- widget.on('edit', event => {
- event.cancel();
- editor.execCommand('editdrupalimage', {
- existingValues: widget.definition._dataToDialogValues(widget.data),
- saveCallback: widget.definition._createDialogSaveCallback(editor, widget),
- dialogTitle: widget.data.src ? editor.config.drupalImage_dialogTitleEdit : editor.config.drupalImage_dialogTitleAdd
- });
- });
- });
- editor.addCommand('editdrupalimage', {
- allowedContent: 'img[alt,!src,width,height,!data-entity-type,!data-entity-uuid]',
- requiredContent: 'img[alt,src,data-entity-type,data-entity-uuid]',
- modes: {
- wysiwyg: 1
- },
- canUndo: true,
-
- exec(editor, data) {
- const dialogSettings = {
- title: data.dialogTitle,
- dialogClass: 'editor-image-dialog'
- };
- Drupal.ckeditor.openDialog(editor, Drupal.url(`editor/dialog/image/${editor.config.drupal.format}`), data.existingValues, data.saveCallback, dialogSettings);
- }
-
- });
-
- if (editor.ui.addButton) {
- editor.ui.addButton('DrupalImage', {
- label: Drupal.t('Image'),
- command: 'image'
- });
- }
- },
-
- afterInit(editor) {
- linkCommandIntegrator(editor);
- }
-
- });
-
- CKEDITOR.plugins.image2.getLinkAttributesParser = function () {
- return CKEDITOR.plugins.drupallink.parseLinkAttributes;
- };
-
- CKEDITOR.plugins.image2.getLinkAttributesGetter = function () {
- return CKEDITOR.plugins.drupallink.getLinkAttributes;
- };
-
- CKEDITOR.plugins.drupalimage = {
- getFocusedWidget
- };
-})(jQuery, Drupal, CKEDITOR); \ No newline at end of file
diff --git a/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.es6.js b/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.es6.js
deleted file mode 100644
index 733e7652129e..000000000000
--- a/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.es6.js
+++ /dev/null
@@ -1,353 +0,0 @@
-/**
- * @file
- * Drupal Image Caption plugin.
- *
- * This alters the existing CKEditor image2 widget plugin, which is already
- * altered by the Drupal Image plugin, to:
- * - allow for the data-caption and data-align attributes to be set
- * - mimic the upcasting behavior of the caption_filter filter.
- *
- * @ignore
- */
-
-(function (CKEDITOR) {
- /**
- * Finds an element by its name.
- *
- * Function will check first the passed element itself and then all its
- * children in DFS order.
- *
- * @param {CKEDITOR.htmlParser.element} element
- * The element to search.
- * @param {string} name
- * The element name to search for.
- *
- * @return {?CKEDITOR.htmlParser.element}
- * The found element, or null.
- */
- function findElementByName(element, name) {
- if (element.name === name) {
- return element;
- }
-
- let found = null;
- element.forEach((el) => {
- if (el.name === name) {
- found = el;
- // Stop here.
- return false;
- }
- }, CKEDITOR.NODE_ELEMENT);
- return found;
- }
-
- CKEDITOR.plugins.add('drupalimagecaption', {
- requires: 'drupalimage',
-
- beforeInit(editor) {
- // Disable default placeholder text that comes with CKEditor's image2
- // plugin: it has an inferior UX (it requires the user to manually delete
- // the place holder text).
- editor.lang.image2.captionPlaceholder = '';
-
- // Drupal.t() will not work inside CKEditor plugins because CKEditor loads
- // the JavaScript file instead of Drupal. Pull translated strings from the
- // plugin settings that are translated server-side.
- const placeholderText =
- editor.config.drupalImageCaption_captionPlaceholderText;
-
- // Override the image2 widget definition to handle the additional
- // data-align and data-caption attributes.
- editor.on(
- 'widgetDefinition',
- (event) => {
- const widgetDefinition = event.data;
- if (widgetDefinition.name !== 'image') {
- return;
- }
-
- // Only perform the downcasting/upcasting for to the enabled filters.
- const captionFilterEnabled =
- editor.config.drupalImageCaption_captionFilterEnabled;
- const alignFilterEnabled =
- editor.config.drupalImageCaption_alignFilterEnabled;
-
- // Override default features definitions for drupalimagecaption.
- CKEDITOR.tools.extend(
- widgetDefinition.features,
- {
- caption: {
- requiredContent: 'img[data-caption]',
- },
- align: {
- requiredContent: 'img[data-align]',
- },
- },
- true,
- );
-
- // Extend requiredContent & allowedContent.
- // CKEDITOR.style is an immutable object: we cannot modify its
- // definition to extend requiredContent. Hence we get the definition,
- // modify it, and pass it to a new CKEDITOR.style instance.
- const requiredContent =
- widgetDefinition.requiredContent.getDefinition();
- requiredContent.attributes['data-align'] = '';
- requiredContent.attributes['data-caption'] = '';
- widgetDefinition.requiredContent = new CKEDITOR.style(
- requiredContent,
- );
- widgetDefinition.allowedContent.img.attributes['!data-align'] = true;
- widgetDefinition.allowedContent.img.attributes[
- '!data-caption'
- ] = true;
-
- // Override allowedContent setting for the 'caption' nested editable.
- // This must match what caption_filter enforces.
- // @see \Drupal\filter\Plugin\Filter\FilterCaption::process()
- // @see \Drupal\Component\Utility\Xss::filter()
- widgetDefinition.editables.caption.allowedContent =
- 'a[!href]; em strong cite code br';
-
- // Override downcast(): ensure we *only* output <img>, but also ensure
- // we include the data-entity-type, data-entity-uuid, data-align and
- // data-caption attributes.
- const originalDowncast = widgetDefinition.downcast;
- widgetDefinition.downcast = function (element) {
- const img = findElementByName(element, 'img');
- originalDowncast.call(this, img);
-
- const caption = this.editables.caption;
- const captionHtml = caption && caption.getData();
- const attrs = img.attributes;
-
- if (captionFilterEnabled) {
- // If image contains a non-empty caption, serialize caption to the
- // data-caption attribute.
- if (captionHtml) {
- attrs['data-caption'] = captionHtml;
- }
- }
- if (alignFilterEnabled) {
- if (this.data.align !== 'none') {
- attrs['data-align'] = this.data.align;
- }
- }
-
- // If img is wrapped with a link, we want to return that link.
- if (img.parent.name === 'a') {
- return img.parent;
- }
-
- return img;
- };
-
- // We want to upcast <img> elements to a DOM structure required by the
- // image2 widget. Depending on a case it may be:
- // - just an <img> tag (non-captioned, not-centered image),
- // - <img> tag in a paragraph (non-captioned, centered image),
- // - <figure> tag (captioned image).
- // We take the same attributes into account as downcast() does.
- const originalUpcast = widgetDefinition.upcast;
- widgetDefinition.upcast = function (element, data) {
- if (
- element.name !== 'img' ||
- !element.attributes['data-entity-type'] ||
- !element.attributes['data-entity-uuid']
- ) {
- return;
- }
- // Don't initialize on pasted fake objects.
- if (element.attributes['data-cke-realelement']) {
- return;
- }
-
- element = originalUpcast.call(this, element, data);
- const attrs = element.attributes;
-
- if (element.parent.name === 'a') {
- element = element.parent;
- }
-
- let retElement = element;
- let caption;
-
- // We won't need the attributes during editing: we'll use widget.data
- // to store them (except the caption, which is stored in the DOM).
- if (captionFilterEnabled) {
- caption = attrs['data-caption'];
- delete attrs['data-caption'];
- }
- if (alignFilterEnabled) {
- data.align = attrs['data-align'];
- delete attrs['data-align'];
- }
- data['data-entity-type'] = attrs['data-entity-type'];
- delete attrs['data-entity-type'];
- data['data-entity-uuid'] = attrs['data-entity-uuid'];
- delete attrs['data-entity-uuid'];
-
- if (captionFilterEnabled) {
- // Unwrap from <p> wrapper created by HTML parser for a captioned
- // image. The captioned image will be transformed to <figure>, so we
- // don't want the <p> anymore.
- if (element.parent.name === 'p' && caption) {
- let index = element.getIndex();
- const splitBefore = index > 0;
- const splitAfter = index + 1 < element.parent.children.length;
-
- if (splitBefore) {
- element.parent.split(index);
- }
- index = element.getIndex();
- if (splitAfter) {
- element.parent.split(index + 1);
- }
-
- element.parent.replaceWith(element);
- retElement = element;
- }
-
- // If this image has a caption, create a full <figure> structure.
- if (caption) {
- const figure = new CKEDITOR.htmlParser.element('figure');
- caption = new CKEDITOR.htmlParser.fragment.fromHtml(
- caption,
- 'figcaption',
- );
-
- const captionFilter = new CKEDITOR.filter(
- widgetDefinition.editables.caption.allowedContent,
- );
- captionFilter.applyTo(caption);
-
- // Use Drupal's data-placeholder attribute to insert a CSS-based,
- // translation-ready placeholder for empty captions. Note that it
- // also must to be done for new instances (see
- // widgetDefinition._createDialogSaveCallback).
- caption.attributes['data-placeholder'] = placeholderText;
-
- element.replaceWith(figure);
- figure.add(element);
- figure.add(caption);
- figure.attributes.class = editor.config.image2_captionedClass;
- retElement = figure;
- }
- }
-
- if (alignFilterEnabled) {
- // If this image doesn't have a caption (or the caption filter is
- // disabled), but it is centered, make sure that it's wrapped with
- // <p>, which will become a part of the widget.
- if (
- data.align === 'center' &&
- (!captionFilterEnabled || !caption)
- ) {
- const p = new CKEDITOR.htmlParser.element('p');
- element.replaceWith(p);
- p.add(element);
- // Apply the class for centered images.
- p.addClass(editor.config.image2_alignClasses[1]);
- retElement = p;
- }
- }
-
- // Return the upcasted element (<img>, <figure> or <p>).
- return retElement;
- };
-
- // Protected; keys of the widget data to be sent to the Drupal dialog.
- // Append to the values defined by the drupalimage plugin.
- // @see core/modules/ckeditor/js/plugins/drupalimage/plugin.js
- CKEDITOR.tools.extend(widgetDefinition._mapDataToDialog, {
- align: 'data-align',
- 'data-caption': 'data-caption',
- hasCaption: 'hasCaption',
- });
-
- // Override Drupal dialog save callback.
- const originalCreateDialogSaveCallback =
- widgetDefinition._createDialogSaveCallback;
- widgetDefinition._createDialogSaveCallback = function (
- editor,
- widget,
- ) {
- const saveCallback = originalCreateDialogSaveCallback.call(
- this,
- editor,
- widget,
- );
-
- return function (dialogReturnValues) {
- // Ensure hasCaption is a boolean. image2 assumes it always works
- // with booleans; if this is not the case, then
- // CKEDITOR.plugins.image2.stateShifter() will incorrectly mark
- // widget.data.hasCaption as "changed" (e.g. when hasCaption === 0
- // instead of hasCaption === false). This causes image2's "state
- // shifter" to enter the wrong branch of the algorithm and blow up.
- dialogReturnValues.attributes.hasCaption =
- !!dialogReturnValues.attributes.hasCaption;
-
- const actualWidget = saveCallback(dialogReturnValues);
-
- // By default, the template of captioned widget has no
- // data-placeholder attribute. Note that it also must be done when
- // upcasting existing elements (see widgetDefinition.upcast).
- if (dialogReturnValues.attributes.hasCaption) {
- actualWidget.editables.caption.setAttribute(
- 'data-placeholder',
- placeholderText,
- );
-
- // Some browsers will add a <br> tag to a newly created DOM
- // element with no content. Remove this <br> if it is the only
- // thing in the caption. Our placeholder support requires the
- // element be entirely empty. See filter-caption.css.
- const captionElement = actualWidget.editables.caption.$;
- if (
- captionElement.childNodes.length === 1 &&
- captionElement.childNodes.item(0).nodeName === 'BR'
- ) {
- captionElement.removeChild(captionElement.childNodes.item(0));
- }
- }
- };
- };
- // Low priority to ensure drupalimage's event handler runs first.
- },
- null,
- null,
- 20,
- );
- },
-
- afterInit(editor) {
- const disableButtonIfOnWidget = function (evt) {
- const widget = editor.widgets.focused;
- if (widget && widget.name === 'image') {
- this.setState(CKEDITOR.TRISTATE_DISABLED);
- evt.cancel();
- }
- };
-
- // Disable alignment buttons if the align filter is not enabled.
- if (
- editor.plugins.justify &&
- !editor.config.drupalImageCaption_alignFilterEnabled
- ) {
- let cmd;
- const commands = [
- 'justifyleft',
- 'justifycenter',
- 'justifyright',
- 'justifyblock',
- ];
- for (let n = 0; n < commands.length; n++) {
- cmd = editor.getCommand(commands[n]);
- cmd.contextSensitive = 1;
- cmd.on('refresh', disableButtonIfOnWidget, null, null, 4);
- }
- }
- },
- });
-})(CKEDITOR);
diff --git a/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.js b/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.js
deleted file mode 100644
index 4efdff440a06..000000000000
--- a/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.js
+++ /dev/null
@@ -1,214 +0,0 @@
-/**
-* DO NOT EDIT THIS FILE.
-* See the following change record for more information,
-* https://www.drupal.org/node/2815083
-* @preserve
-**/
-
-(function (CKEDITOR) {
- function findElementByName(element, name) {
- if (element.name === name) {
- return element;
- }
-
- let found = null;
- element.forEach(el => {
- if (el.name === name) {
- found = el;
- return false;
- }
- }, CKEDITOR.NODE_ELEMENT);
- return found;
- }
-
- CKEDITOR.plugins.add('drupalimagecaption', {
- requires: 'drupalimage',
-
- beforeInit(editor) {
- editor.lang.image2.captionPlaceholder = '';
- const placeholderText = editor.config.drupalImageCaption_captionPlaceholderText;
- editor.on('widgetDefinition', event => {
- const widgetDefinition = event.data;
-
- if (widgetDefinition.name !== 'image') {
- return;
- }
-
- const captionFilterEnabled = editor.config.drupalImageCaption_captionFilterEnabled;
- const alignFilterEnabled = editor.config.drupalImageCaption_alignFilterEnabled;
- CKEDITOR.tools.extend(widgetDefinition.features, {
- caption: {
- requiredContent: 'img[data-caption]'
- },
- align: {
- requiredContent: 'img[data-align]'
- }
- }, true);
- const requiredContent = widgetDefinition.requiredContent.getDefinition();
- requiredContent.attributes['data-align'] = '';
- requiredContent.attributes['data-caption'] = '';
- widgetDefinition.requiredContent = new CKEDITOR.style(requiredContent);
- widgetDefinition.allowedContent.img.attributes['!data-align'] = true;
- widgetDefinition.allowedContent.img.attributes['!data-caption'] = true;
- widgetDefinition.editables.caption.allowedContent = 'a[!href]; em strong cite code br';
- const originalDowncast = widgetDefinition.downcast;
-
- widgetDefinition.downcast = function (element) {
- const img = findElementByName(element, 'img');
- originalDowncast.call(this, img);
- const caption = this.editables.caption;
- const captionHtml = caption && caption.getData();
- const attrs = img.attributes;
-
- if (captionFilterEnabled) {
- if (captionHtml) {
- attrs['data-caption'] = captionHtml;
- }
- }
-
- if (alignFilterEnabled) {
- if (this.data.align !== 'none') {
- attrs['data-align'] = this.data.align;
- }
- }
-
- if (img.parent.name === 'a') {
- return img.parent;
- }
-
- return img;
- };
-
- const originalUpcast = widgetDefinition.upcast;
-
- widgetDefinition.upcast = function (element, data) {
- if (element.name !== 'img' || !element.attributes['data-entity-type'] || !element.attributes['data-entity-uuid']) {
- return;
- }
-
- if (element.attributes['data-cke-realelement']) {
- return;
- }
-
- element = originalUpcast.call(this, element, data);
- const attrs = element.attributes;
-
- if (element.parent.name === 'a') {
- element = element.parent;
- }
-
- let retElement = element;
- let caption;
-
- if (captionFilterEnabled) {
- caption = attrs['data-caption'];
- delete attrs['data-caption'];
- }
-
- if (alignFilterEnabled) {
- data.align = attrs['data-align'];
- delete attrs['data-align'];
- }
-
- data['data-entity-type'] = attrs['data-entity-type'];
- delete attrs['data-entity-type'];
- data['data-entity-uuid'] = attrs['data-entity-uuid'];
- delete attrs['data-entity-uuid'];
-
- if (captionFilterEnabled) {
- if (element.parent.name === 'p' && caption) {
- let index = element.getIndex();
- const splitBefore = index > 0;
- const splitAfter = index + 1 < element.parent.children.length;
-
- if (splitBefore) {
- element.parent.split(index);
- }
-
- index = element.getIndex();
-
- if (splitAfter) {
- element.parent.split(index + 1);
- }
-
- element.parent.replaceWith(element);
- retElement = element;
- }
-
- if (caption) {
- const figure = new CKEDITOR.htmlParser.element('figure');
- caption = new CKEDITOR.htmlParser.fragment.fromHtml(caption, 'figcaption');
- const captionFilter = new CKEDITOR.filter(widgetDefinition.editables.caption.allowedContent);
- captionFilter.applyTo(caption);
- caption.attributes['data-placeholder'] = placeholderText;
- element.replaceWith(figure);
- figure.add(element);
- figure.add(caption);
- figure.attributes.class = editor.config.image2_captionedClass;
- retElement = figure;
- }
- }
-
- if (alignFilterEnabled) {
- if (data.align === 'center' && (!captionFilterEnabled || !caption)) {
- const p = new CKEDITOR.htmlParser.element('p');
- element.replaceWith(p);
- p.add(element);
- p.addClass(editor.config.image2_alignClasses[1]);
- retElement = p;
- }
- }
-
- return retElement;
- };
-
- CKEDITOR.tools.extend(widgetDefinition._mapDataToDialog, {
- align: 'data-align',
- 'data-caption': 'data-caption',
- hasCaption: 'hasCaption'
- });
- const originalCreateDialogSaveCallback = widgetDefinition._createDialogSaveCallback;
-
- widgetDefinition._createDialogSaveCallback = function (editor, widget) {
- const saveCallback = originalCreateDialogSaveCallback.call(this, editor, widget);
- return function (dialogReturnValues) {
- dialogReturnValues.attributes.hasCaption = !!dialogReturnValues.attributes.hasCaption;
- const actualWidget = saveCallback(dialogReturnValues);
-
- if (dialogReturnValues.attributes.hasCaption) {
- actualWidget.editables.caption.setAttribute('data-placeholder', placeholderText);
- const captionElement = actualWidget.editables.caption.$;
-
- if (captionElement.childNodes.length === 1 && captionElement.childNodes.item(0).nodeName === 'BR') {
- captionElement.removeChild(captionElement.childNodes.item(0));
- }
- }
- };
- };
- }, null, null, 20);
- },
-
- afterInit(editor) {
- const disableButtonIfOnWidget = function (evt) {
- const widget = editor.widgets.focused;
-
- if (widget && widget.name === 'image') {
- this.setState(CKEDITOR.TRISTATE_DISABLED);
- evt.cancel();
- }
- };
-
- if (editor.plugins.justify && !editor.config.drupalImageCaption_alignFilterEnabled) {
- let cmd;
- const commands = ['justifyleft', 'justifycenter', 'justifyright', 'justifyblock'];
-
- for (let n = 0; n < commands.length; n++) {
- cmd = editor.getCommand(commands[n]);
- cmd.contextSensitive = 1;
- cmd.on('refresh', disableButtonIfOnWidget, null, null, 4);
- }
- }
- }
-
- });
-})(CKEDITOR); \ No newline at end of file
diff --git a/core/modules/ckeditor/js/plugins/drupallink/icons/drupallink.png b/core/modules/ckeditor/js/plugins/drupallink/icons/drupallink.png
deleted file mode 100644
index 54e506a3d824..000000000000
--- a/core/modules/ckeditor/js/plugins/drupallink/icons/drupallink.png
+++ /dev/null
Binary files differ
diff --git a/core/modules/ckeditor/js/plugins/drupallink/icons/drupalunlink.png b/core/modules/ckeditor/js/plugins/drupallink/icons/drupalunlink.png
deleted file mode 100644
index 64056ad6d89b..000000000000
--- a/core/modules/ckeditor/js/plugins/drupallink/icons/drupalunlink.png
+++ /dev/null
Binary files differ
diff --git a/core/modules/ckeditor/js/plugins/drupallink/icons/hidpi/drupallink.png b/core/modules/ckeditor/js/plugins/drupallink/icons/hidpi/drupallink.png
deleted file mode 100644
index e9f60c572ac6..000000000000
--- a/core/modules/ckeditor/js/plugins/drupallink/icons/hidpi/drupallink.png
+++ /dev/null
Binary files differ
diff --git a/core/modules/ckeditor/js/plugins/drupallink/icons/hidpi/drupalunlink.png b/core/modules/ckeditor/js/plugins/drupallink/icons/hidpi/drupalunlink.png
deleted file mode 100644
index a000713e6e21..000000000000
--- a/core/modules/ckeditor/js/plugins/drupallink/icons/hidpi/drupalunlink.png
+++ /dev/null
Binary files differ
diff --git a/core/modules/ckeditor/js/plugins/drupallink/plugin.es6.js b/core/modules/ckeditor/js/plugins/drupallink/plugin.es6.js
deleted file mode 100644
index 5293f183214e..000000000000
--- a/core/modules/ckeditor/js/plugins/drupallink/plugin.es6.js
+++ /dev/null
@@ -1,364 +0,0 @@
-/**
- * @file
- * Drupal Link plugin.
- *
- * @ignore
- */
-
-(function ($, Drupal, drupalSettings, CKEDITOR) {
- function parseAttributes(editor, element) {
- const parsedAttributes = {};
-
- const domElement = element.$;
- let attribute;
- let attributeName;
- for (
- let attrIndex = 0;
- attrIndex < domElement.attributes.length;
- attrIndex++
- ) {
- attribute = domElement.attributes.item(attrIndex);
- attributeName = attribute.nodeName.toLowerCase();
- // Ignore data-cke-* attributes; they're CKEditor internals.
- if (attributeName.indexOf('data-cke-') === 0) {
- continue;
- }
- // Store the value for this attribute, unless there's a data-cke-saved-
- // alternative for it, which will contain the quirk-free, original value.
- parsedAttributes[attributeName] =
- element.data(`cke-saved-${attributeName}`) || attribute.nodeValue;
- }
-
- // Remove any cke_* classes.
- if (parsedAttributes.class) {
- parsedAttributes.class = CKEDITOR.tools.trim(
- parsedAttributes.class.replace(/cke_\S+/, ''),
- );
- }
-
- return parsedAttributes;
- }
-
- function getAttributes(editor, data) {
- const set = {};
- Object.keys(data || {}).forEach((attributeName) => {
- set[attributeName] = data[attributeName];
- });
-
- // CKEditor tracks the *actual* saved href in a data-cke-saved-* attribute
- // to work around browser quirks. We need to update it.
- set['data-cke-saved-href'] = set.href;
-
- // Remove all attributes which are not currently set.
- const removed = {};
- Object.keys(set).forEach((s) => {
- delete removed[s];
- });
-
- return {
- set,
- removed: CKEDITOR.tools.objectKeys(removed),
- };
- }
-
- const registeredLinkableWidgets = [];
-
- /**
- * Registers a widget name as linkable.
- *
- * @param {string} widgetName
- * The name of the widget to register as linkable.
- */
- function registerLinkableWidget(widgetName) {
- registeredLinkableWidgets.push(widgetName);
- }
-
- /**
- * Gets the focused widget, if one of the registered linkable widget names.
- *
- * @param {CKEDITOR.editor} editor
- * A CKEditor instance.
- *
- * @return {?CKEDITOR.plugins.widget}
- * The focused linkable widget instance, or null.
- */
- function getFocusedLinkableWidget(editor) {
- const widget = editor.widgets.focused;
- if (widget && registeredLinkableWidgets.indexOf(widget.name) !== -1) {
- return widget;
- }
- return null;
- }
-
- /**
- * Get the surrounding link element of current selection.
- *
- * The following selection will all return the link element.
- *
- * @example
- * <a href="#">li^nk</a>
- * <a href="#">[link]</a>
- * text[<a href="#">link]</a>
- * <a href="#">li[nk</a>]
- * [<b><a href="#">li]nk</a></b>]
- * [<a href="#"><b>li]nk</b></a>
- *
- * @param {CKEDITOR.editor} editor
- * The CKEditor editor object
- *
- * @return {?HTMLElement}
- * The selected link element, or null.
- *
- */
- function getSelectedLink(editor) {
- const selection = editor.getSelection();
- const selectedElement = selection.getSelectedElement();
- if (selectedElement && selectedElement.is('a')) {
- return selectedElement;
- }
-
- const range = selection.getRanges(true)[0];
-
- if (range) {
- range.shrink(CKEDITOR.SHRINK_TEXT);
- return editor.elementPath(range.getCommonAncestor()).contains('a', 1);
- }
- return null;
- }
-
- CKEDITOR.plugins.add('drupallink', {
- icons: 'drupallink,drupalunlink',
- hidpi: true,
-
- init(editor) {
- // Add the commands for link and unlink.
- editor.addCommand('drupallink', {
- allowedContent: {
- a: {
- attributes: {
- '!href': true,
- },
- classes: {},
- },
- },
- requiredContent: new CKEDITOR.style({
- element: 'a',
- attributes: {
- href: '',
- },
- }),
- modes: { wysiwyg: 1 },
- canUndo: true,
- exec(editor) {
- const focusedLinkableWidget = getFocusedLinkableWidget(editor);
- let linkElement = getSelectedLink(editor);
-
- // Set existing values based on selected element.
- let existingValues = {};
- if (linkElement && linkElement.$) {
- existingValues = parseAttributes(editor, linkElement);
- }
- // Or, if an image widget is focused, we're editing a link wrapping
- // an image widget.
- else if (focusedLinkableWidget && focusedLinkableWidget.data.link) {
- existingValues = CKEDITOR.tools.clone(
- focusedLinkableWidget.data.link,
- );
- }
-
- // Prepare a save callback to be used upon saving the dialog.
- const saveCallback = function (returnValues) {
- // If an image widget is focused, we're not editing an independent
- // link, but we're wrapping an image widget in a link.
- if (focusedLinkableWidget) {
- focusedLinkableWidget.setData(
- 'link',
- CKEDITOR.tools.extend(
- returnValues.attributes,
- focusedLinkableWidget.data.link,
- ),
- );
- editor.fire('saveSnapshot');
- return;
- }
-
- editor.fire('saveSnapshot');
-
- // Create a new link element if needed.
- if (!linkElement && returnValues.attributes.href) {
- const selection = editor.getSelection();
- const range = selection.getRanges(1)[0];
-
- // Use link URL as text with a collapsed cursor.
- if (range.collapsed) {
- // Shorten mailto URLs to just the email address.
- const text = new CKEDITOR.dom.text(
- returnValues.attributes.href.replace(/^mailto:/, ''),
- editor.document,
- );
- range.insertNode(text);
- range.selectNodeContents(text);
- }
-
- // Create the new link by applying a style to the new text.
- const style = new CKEDITOR.style({
- element: 'a',
- attributes: returnValues.attributes,
- });
- style.type = CKEDITOR.STYLE_INLINE;
- style.applyToRange(range);
- range.select();
-
- // Set the link so individual properties may be set below.
- linkElement = getSelectedLink(editor);
- }
- // Update the link properties.
- else if (linkElement) {
- Object.keys(returnValues.attributes || {}).forEach((attrName) => {
- // Update the property if a value is specified.
- if (returnValues.attributes[attrName].length > 0) {
- const value = returnValues.attributes[attrName];
- linkElement.data(`cke-saved-${attrName}`, value);
- linkElement.setAttribute(attrName, value);
- }
- // Delete the property if set to an empty string.
- else {
- linkElement.removeAttribute(attrName);
- }
- });
- }
-
- // Save snapshot for undo support.
- editor.fire('saveSnapshot');
- };
- // Drupal.t() will not work inside CKEditor plugins because CKEditor
- // loads the JavaScript file instead of Drupal. Pull translated
- // strings from the plugin settings that are translated server-side.
- const dialogSettings = {
- title: linkElement
- ? editor.config.drupalLink_dialogTitleEdit
- : editor.config.drupalLink_dialogTitleAdd,
- dialogClass: 'editor-link-dialog',
- };
-
- // Open the dialog for the edit form.
- Drupal.ckeditor.openDialog(
- editor,
- Drupal.url(`editor/dialog/link/${editor.config.drupal.format}`),
- existingValues,
- saveCallback,
- dialogSettings,
- );
- },
- });
- editor.addCommand('drupalunlink', {
- contextSensitive: 1,
- startDisabled: 1,
- requiredContent: new CKEDITOR.style({
- element: 'a',
- attributes: {
- href: '',
- },
- }),
- exec(editor) {
- const style = new CKEDITOR.style({
- element: 'a',
- type: CKEDITOR.STYLE_INLINE,
- alwaysRemoveElement: 1,
- });
- editor.removeStyle(style);
- },
- refresh(editor, path) {
- const element =
- path.lastElement && path.lastElement.getAscendant('a', true);
- if (
- element &&
- element.getName() === 'a' &&
- element.getAttribute('href') &&
- element.getChildCount()
- ) {
- this.setState(CKEDITOR.TRISTATE_OFF);
- } else {
- this.setState(CKEDITOR.TRISTATE_DISABLED);
- }
- },
- });
-
- // CTRL + K.
- editor.setKeystroke(CKEDITOR.CTRL + 75, 'drupallink');
-
- // Add buttons for link and unlink.
- if (editor.ui.addButton) {
- editor.ui.addButton('DrupalLink', {
- label: Drupal.t('Link'),
- command: 'drupallink',
- });
- editor.ui.addButton('DrupalUnlink', {
- label: Drupal.t('Unlink'),
- command: 'drupalunlink',
- });
- }
-
- editor.on('doubleclick', (evt) => {
- const element = getSelectedLink(editor) || evt.data.element;
-
- if (!element.isReadOnly()) {
- if (element.is('a')) {
- editor.getSelection().selectElement(element);
- editor.getCommand('drupallink').exec();
- }
- }
- });
-
- // If the "menu" plugin is loaded, register the menu items.
- if (editor.addMenuItems) {
- editor.addMenuItems({
- link: {
- label: Drupal.t('Edit Link'),
- command: 'drupallink',
- group: 'link',
- order: 1,
- },
-
- unlink: {
- label: Drupal.t('Unlink'),
- command: 'drupalunlink',
- group: 'link',
- order: 5,
- },
- });
- }
-
- // If the "contextmenu" plugin is loaded, register the listeners.
- if (editor.contextMenu) {
- editor.contextMenu.addListener((element, selection) => {
- if (!element || element.isReadOnly()) {
- return null;
- }
- const anchor = getSelectedLink(editor);
- if (!anchor) {
- return null;
- }
-
- let menu = {};
- if (anchor.getAttribute('href') && anchor.getChildCount()) {
- menu = {
- link: CKEDITOR.TRISTATE_OFF,
- unlink: CKEDITOR.TRISTATE_OFF,
- };
- }
- return menu;
- });
- }
- },
- });
-
- // Expose an API for other plugins to interact with drupallink widgets.
- // (Compatible with the official CKEditor link plugin's API:
- // http://dev.ckeditor.com/ticket/13885.)
- CKEDITOR.plugins.drupallink = {
- parseLinkAttributes: parseAttributes,
- getLinkAttributes: getAttributes,
- registerLinkableWidget,
- };
-})(jQuery, Drupal, drupalSettings, CKEDITOR);
diff --git a/core/modules/ckeditor/js/plugins/drupallink/plugin.js b/core/modules/ckeditor/js/plugins/drupallink/plugin.js
deleted file mode 100644
index 6dff582a8d82..000000000000
--- a/core/modules/ckeditor/js/plugins/drupallink/plugin.js
+++ /dev/null
@@ -1,272 +0,0 @@
-/**
-* DO NOT EDIT THIS FILE.
-* See the following change record for more information,
-* https://www.drupal.org/node/2815083
-* @preserve
-**/
-
-(function ($, Drupal, drupalSettings, CKEDITOR) {
- function parseAttributes(editor, element) {
- const parsedAttributes = {};
- const domElement = element.$;
- let attribute;
- let attributeName;
-
- for (let attrIndex = 0; attrIndex < domElement.attributes.length; attrIndex++) {
- attribute = domElement.attributes.item(attrIndex);
- attributeName = attribute.nodeName.toLowerCase();
-
- if (attributeName.indexOf('data-cke-') === 0) {
- continue;
- }
-
- parsedAttributes[attributeName] = element.data(`cke-saved-${attributeName}`) || attribute.nodeValue;
- }
-
- if (parsedAttributes.class) {
- parsedAttributes.class = CKEDITOR.tools.trim(parsedAttributes.class.replace(/cke_\S+/, ''));
- }
-
- return parsedAttributes;
- }
-
- function getAttributes(editor, data) {
- const set = {};
- Object.keys(data || {}).forEach(attributeName => {
- set[attributeName] = data[attributeName];
- });
- set['data-cke-saved-href'] = set.href;
- const removed = {};
- Object.keys(set).forEach(s => {
- delete removed[s];
- });
- return {
- set,
- removed: CKEDITOR.tools.objectKeys(removed)
- };
- }
-
- const registeredLinkableWidgets = [];
-
- function registerLinkableWidget(widgetName) {
- registeredLinkableWidgets.push(widgetName);
- }
-
- function getFocusedLinkableWidget(editor) {
- const widget = editor.widgets.focused;
-
- if (widget && registeredLinkableWidgets.indexOf(widget.name) !== -1) {
- return widget;
- }
-
- return null;
- }
-
- function getSelectedLink(editor) {
- const selection = editor.getSelection();
- const selectedElement = selection.getSelectedElement();
-
- if (selectedElement && selectedElement.is('a')) {
- return selectedElement;
- }
-
- const range = selection.getRanges(true)[0];
-
- if (range) {
- range.shrink(CKEDITOR.SHRINK_TEXT);
- return editor.elementPath(range.getCommonAncestor()).contains('a', 1);
- }
-
- return null;
- }
-
- CKEDITOR.plugins.add('drupallink', {
- icons: 'drupallink,drupalunlink',
- hidpi: true,
-
- init(editor) {
- editor.addCommand('drupallink', {
- allowedContent: {
- a: {
- attributes: {
- '!href': true
- },
- classes: {}
- }
- },
- requiredContent: new CKEDITOR.style({
- element: 'a',
- attributes: {
- href: ''
- }
- }),
- modes: {
- wysiwyg: 1
- },
- canUndo: true,
-
- exec(editor) {
- const focusedLinkableWidget = getFocusedLinkableWidget(editor);
- let linkElement = getSelectedLink(editor);
- let existingValues = {};
-
- if (linkElement && linkElement.$) {
- existingValues = parseAttributes(editor, linkElement);
- } else if (focusedLinkableWidget && focusedLinkableWidget.data.link) {
- existingValues = CKEDITOR.tools.clone(focusedLinkableWidget.data.link);
- }
-
- const saveCallback = function (returnValues) {
- if (focusedLinkableWidget) {
- focusedLinkableWidget.setData('link', CKEDITOR.tools.extend(returnValues.attributes, focusedLinkableWidget.data.link));
- editor.fire('saveSnapshot');
- return;
- }
-
- editor.fire('saveSnapshot');
-
- if (!linkElement && returnValues.attributes.href) {
- const selection = editor.getSelection();
- const range = selection.getRanges(1)[0];
-
- if (range.collapsed) {
- const text = new CKEDITOR.dom.text(returnValues.attributes.href.replace(/^mailto:/, ''), editor.document);
- range.insertNode(text);
- range.selectNodeContents(text);
- }
-
- const style = new CKEDITOR.style({
- element: 'a',
- attributes: returnValues.attributes
- });
- style.type = CKEDITOR.STYLE_INLINE;
- style.applyToRange(range);
- range.select();
- linkElement = getSelectedLink(editor);
- } else if (linkElement) {
- Object.keys(returnValues.attributes || {}).forEach(attrName => {
- if (returnValues.attributes[attrName].length > 0) {
- const value = returnValues.attributes[attrName];
- linkElement.data(`cke-saved-${attrName}`, value);
- linkElement.setAttribute(attrName, value);
- } else {
- linkElement.removeAttribute(attrName);
- }
- });
- }
-
- editor.fire('saveSnapshot');
- };
-
- const dialogSettings = {
- title: linkElement ? editor.config.drupalLink_dialogTitleEdit : editor.config.drupalLink_dialogTitleAdd,
- dialogClass: 'editor-link-dialog'
- };
- Drupal.ckeditor.openDialog(editor, Drupal.url(`editor/dialog/link/${editor.config.drupal.format}`), existingValues, saveCallback, dialogSettings);
- }
-
- });
- editor.addCommand('drupalunlink', {
- contextSensitive: 1,
- startDisabled: 1,
- requiredContent: new CKEDITOR.style({
- element: 'a',
- attributes: {
- href: ''
- }
- }),
-
- exec(editor) {
- const style = new CKEDITOR.style({
- element: 'a',
- type: CKEDITOR.STYLE_INLINE,
- alwaysRemoveElement: 1
- });
- editor.removeStyle(style);
- },
-
- refresh(editor, path) {
- const element = path.lastElement && path.lastElement.getAscendant('a', true);
-
- if (element && element.getName() === 'a' && element.getAttribute('href') && element.getChildCount()) {
- this.setState(CKEDITOR.TRISTATE_OFF);
- } else {
- this.setState(CKEDITOR.TRISTATE_DISABLED);
- }
- }
-
- });
- editor.setKeystroke(CKEDITOR.CTRL + 75, 'drupallink');
-
- if (editor.ui.addButton) {
- editor.ui.addButton('DrupalLink', {
- label: Drupal.t('Link'),
- command: 'drupallink'
- });
- editor.ui.addButton('DrupalUnlink', {
- label: Drupal.t('Unlink'),
- command: 'drupalunlink'
- });
- }
-
- editor.on('doubleclick', evt => {
- const element = getSelectedLink(editor) || evt.data.element;
-
- if (!element.isReadOnly()) {
- if (element.is('a')) {
- editor.getSelection().selectElement(element);
- editor.getCommand('drupallink').exec();
- }
- }
- });
-
- if (editor.addMenuItems) {
- editor.addMenuItems({
- link: {
- label: Drupal.t('Edit Link'),
- command: 'drupallink',
- group: 'link',
- order: 1
- },
- unlink: {
- label: Drupal.t('Unlink'),
- command: 'drupalunlink',
- group: 'link',
- order: 5
- }
- });
- }
-
- if (editor.contextMenu) {
- editor.contextMenu.addListener((element, selection) => {
- if (!element || element.isReadOnly()) {
- return null;
- }
-
- const anchor = getSelectedLink(editor);
-
- if (!anchor) {
- return null;
- }
-
- let menu = {};
-
- if (anchor.getAttribute('href') && anchor.getChildCount()) {
- menu = {
- link: CKEDITOR.TRISTATE_OFF,
- unlink: CKEDITOR.TRISTATE_OFF
- };
- }
-
- return menu;
- });
- }
- }
-
- });
- CKEDITOR.plugins.drupallink = {
- parseLinkAttributes: parseAttributes,
- getLinkAttributes: getAttributes,
- registerLinkableWidget
- };
-})(jQuery, Drupal, drupalSettings, CKEDITOR); \ No newline at end of file
diff --git a/core/modules/ckeditor/js/plugins/drupalmedia/plugin.es6.js b/core/modules/ckeditor/js/plugins/drupalmedia/plugin.es6.js
deleted file mode 100644
index 7a0c5a4213ec..000000000000
--- a/core/modules/ckeditor/js/plugins/drupalmedia/plugin.es6.js
+++ /dev/null
@@ -1,499 +0,0 @@
-/**
- * @file
- * Drupal Media embed plugin.
- */
-
-(function (jQuery, Drupal, CKEDITOR) {
- /**
- * Gets the focused widget, if of the type specific for this plugin.
- *
- * @param {CKEDITOR.editor} editor
- * A CKEditor instance.
- *
- * @return {?CKEDITOR.plugins.widget}
- * The focused drupalmedia widget instance, or null.
- */
- function getFocusedWidget(editor) {
- const widget = editor.widgets.focused;
-
- if (widget && widget.name === 'drupalmedia') {
- return widget;
- }
- return null;
- }
-
- /**
- * Makes embedded items linkable by integrating with the drupallink plugin.
- *
- * @param {CKEDITOR.editor} editor
- * A CKEditor instance.
- */
- function linkCommandIntegrator(editor) {
- if (!editor.plugins.drupallink) {
- return;
- }
-
- CKEDITOR.plugins.drupallink.registerLinkableWidget('drupalmedia');
-
- editor.getCommand('drupalunlink').on('exec', function (evt) {
- const widget = getFocusedWidget(editor);
-
- if (!widget) {
- return;
- }
-
- widget.setData('link', null);
-
- this.refresh(editor, editor.elementPath());
-
- evt.cancel();
- });
-
- editor.getCommand('drupalunlink').on('refresh', function (evt) {
- const widget = getFocusedWidget(editor);
-
- if (!widget) {
- return;
- }
-
- this.setState(
- widget.data.link ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
- );
-
- evt.cancel();
- });
-
- // Register context menu items for editing link.
- if (editor.contextMenu) {
- editor.contextMenu.addListener(() => {
- const widget = getFocusedWidget(editor);
-
- if (!widget) {
- return;
- }
-
- if (widget.data.link) {
- return {
- link: CKEDITOR.TRISTATE_OFF,
- unlink: CKEDITOR.TRISTATE_OFF,
- };
- }
- return {};
- });
- }
- }
-
- CKEDITOR.plugins.add('drupalmedia', {
- requires: 'widget',
-
- beforeInit(editor) {
- // Configure CKEditor DTD for custom drupal-media element.
- // @see https://www.drupal.org/node/2448449#comment-9717735
- const { dtd } = CKEDITOR;
- // Allow text within the drupal-media tag.
- dtd['drupal-media'] = { '#': 1 };
- // Register drupal-media element as an allowed child in each tag that can
- // contain a div element and as an allowed child of the a tag.
- Object.keys(dtd).forEach((tagName) => {
- if (dtd[tagName].div) {
- dtd[tagName]['drupal-media'] = 1;
- }
- });
- dtd.a['drupal-media'] = 1;
-
- editor.widgets.add('drupalmedia', {
- allowedContent: {
- 'drupal-media': {
- attributes: {
- '!data-entity-type': true,
- '!data-entity-uuid': true,
- 'data-align': true,
- 'data-caption': true,
- alt: true,
- title: true,
- },
- classes: {},
- },
- },
- // Minimum HTML which is required by this widget to work.
- // This does not use the object format used above, but a
- // CKEDITOR.style instance, because requiredContent does not support
- // the object format.
- // @see https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_filter_contentRule.html
- requiredContent: new CKEDITOR.style({
- element: 'drupal-media',
- attributes: {
- 'data-entity-type': '',
- 'data-entity-uuid': '',
- },
- }),
-
- pathName: Drupal.t('Embedded media'),
-
- editables: {
- caption: {
- selector: 'figcaption',
- allowedContent: 'a[!href]; em strong cite code br',
- pathName: Drupal.t('Caption'),
- },
- },
-
- getLabel() {
- if (this.data.label) {
- return this.data.label;
- }
- return Drupal.t('Embedded media');
- },
-
- upcast(element, data) {
- const { attributes } = element;
- // This matches the behavior of the corresponding server-side text filter plugin.
- if (
- element.name !== 'drupal-media' ||
- attributes['data-entity-type'] !== 'media' ||
- attributes['data-entity-uuid'] === undefined
- ) {
- return;
- }
- data.attributes = CKEDITOR.tools.copy(attributes);
- data.hasCaption = data.attributes.hasOwnProperty('data-caption');
- // Add space to the empty caption to allow the server-side text
- // filter to render a caption, allowing the placeholder-rendering
- // CSS to work.
- if (data.hasCaption && data.attributes['data-caption'] === '') {
- data.attributes['data-caption'] = ' ';
- }
- data.label = null;
- data.link = null;
- if (element.parent.name === 'a') {
- data.link = CKEDITOR.tools.copy(element.parent.attributes);
- // Omit CKEditor-internal attributes.
- Object.keys(element.parent.attributes).forEach((attrName) => {
- if (attrName.indexOf('data-cke-') !== -1) {
- delete data.link[attrName];
- }
- });
- }
- // @see media_field_widget_single_element_form_alter()
- const hostEntityLangcode = document
- .getElementById(editor.name)
- .getAttribute('data-media-embed-host-entity-langcode');
- if (hostEntityLangcode) {
- data.hostEntityLangcode = hostEntityLangcode;
- }
- return element;
- },
-
- destroy() {
- this._tearDownDynamicEditables();
- },
-
- data(event) {
- // Only run during changes.
- if (this.oldData) {
- // The server-side text filter plugin treats both an empty
- // `data-caption` attribute and a non-existing one the same: it
- // does not render a caption. But in the CKEditor Widget, we need
- // to be able to show an empty caption with placeholder text using
- // CSS even when technically there is no `data-caption` attribute
- // value yet. That's why this CKEditor Widget has an independent
- // `hasCaption` boolean (which is not an attribute) to know when
- // to generate a non-empty `data-caption` attribute when the
- // content creator has enabled caption: this makes the server-side
- // text filter render a caption, allowing the placeholder-rendering
- // CSS to work.
- // @see core/modules/filter/css/filter.caption.css
- // @see ckeditor_ckeditor_css_alter()
- if (!this.data.hasCaption && this.oldData.hasCaption) {
- delete this.data.attributes['data-caption'];
- } else if (
- this.data.hasCaption &&
- !this.data.attributes['data-caption']
- ) {
- this.data.attributes['data-caption'] = ' ';
- }
- }
-
- if (this._previewNeedsServerSideUpdate()) {
- editor.fire('lockSnapshot');
- this._tearDownDynamicEditables();
-
- this._loadPreview((widget) => {
- widget._setUpDynamicEditables();
- widget._setUpEditButton();
- editor.fire('unlockSnapshot');
- });
- }
-
- // Remove old attributes from drupal-media element within the widget.
- if (this.oldData) {
- Object.keys(this.oldData.attributes).forEach((attrName) => {
- this.element.removeAttribute(attrName);
- });
- }
- // Add attributes to drupal-media element within the widget.
- this.element.setAttributes(this.data.attributes);
-
- // Track the previous state to allow checking if preview needs
- // server side update.
- this.oldData = CKEDITOR.tools.clone(this.data);
- },
-
- downcast() {
- const downcastElement = new CKEDITOR.htmlParser.element(
- 'drupal-media',
- this.data.attributes,
- );
- if (this.data.link) {
- const link = new CKEDITOR.htmlParser.element('a', this.data.link);
- link.add(downcastElement);
- return link;
- }
- return downcastElement;
- },
-
- _setUpDynamicEditables() {
- // Now that the caption is available in the DOM, make it editable.
- if (this.initEditable('caption', this.definition.editables.caption)) {
- const captionEditable = this.editables.caption;
- // @see core/modules/filter/css/filter.caption.css
- // @see ckeditor_ckeditor_css_alter()
- captionEditable.setAttribute(
- 'data-placeholder',
- Drupal.t('Enter caption here'),
- );
- // Ensure that any changes made to the caption are persisted in the
- // widget's data-caption attribute.
- this.captionObserver = new MutationObserver(() => {
- const mediaAttributes = CKEDITOR.tools.clone(
- this.data.attributes,
- );
- mediaAttributes['data-caption'] = captionEditable.getData();
- this.setData('attributes', mediaAttributes);
- });
- this.captionObserver.observe(captionEditable.$, {
- characterData: true,
- attributes: true,
- childList: true,
- subtree: true,
- });
- // Some browsers will add a <br> tag to a newly created DOM element
- // with no content. Remove this <br> if it is the only thing in the
- // caption. Our placeholder support requires the element to be
- // entirely empty.
- // @see core/modules/filter/css/filter.caption.css
- // @see core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.es6.js
- if (
- captionEditable.$.childNodes.length === 1 &&
- captionEditable.$.childNodes.item(0).nodeName === 'BR'
- ) {
- captionEditable.$.removeChild(
- captionEditable.$.childNodes.item(0),
- );
- }
- }
- },
-
- /**
- * Injects HTML for edit button into the preview that was just loaded.
- */
- _setUpEditButton() {
- // No buttons for missing media.
- if (this.element.findOne('.media-embed-error')) {
- return;
- }
-
- /**
- * Determines if a node is an element node.
- *
- * @param {CKEDITOR.dom.node} n
- * A DOM node to evaluate.
- *
- * @return {bool}
- * Returns true if node is an element node and not a non-element
- * node (such as NODE_TEXT, NODE_COMMENT, NODE_DOCUMENT or
- * NODE_DOCUMENT_FRAGMENT).
- *
- * @see https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR.html#property-NODE_ELEMENT
- */
- const isElementNode = function (n) {
- return n.type === CKEDITOR.NODE_ELEMENT;
- };
-
- // Find the actual embedded media in the DOM.
- const embeddedMediaContainer = this.data.hasCaption
- ? this.element.findOne('figure')
- : this.element;
- let embeddedMedia = embeddedMediaContainer.getFirst(isElementNode);
- // If there is a link, the top-level element is the `a` tag, and the
- // embedded media will be within the `a` tag.
- if (this.data.link) {
- embeddedMedia = embeddedMedia.getFirst(isElementNode);
- }
- // To allow the edit button to be absolutely positioned, the parent
- // element must be positioned relative.
- embeddedMedia.setStyle('position', 'relative');
-
- const editButton = CKEDITOR.dom.element.createFromHtml(
- Drupal.theme('mediaEmbedEditButton'),
- );
- embeddedMedia.getFirst().insertBeforeMe(editButton);
-
- // Make the edit button do things.
- const widget = this;
- this.element
- .findOne('.media-library-item__edit')
- .on('click', (event) => {
- const saveCallback = function (values) {
- event.cancel();
- editor.fire('saveSnapshot');
- if (values.hasOwnProperty('attributes')) {
- // Combine the dialog attributes with the widget attributes.
- // This copies the properties from widget.data.attributes to
- // values.attributes. (Properties already present
- // in values.attributes are not overwritten.)
- CKEDITOR.tools.extend(
- values.attributes,
- widget.data.attributes,
- );
- // Allow the dialog to delete attributes by setting them
- // to `false` or `none`. For example: `alt`.
- Object.keys(values.attributes).forEach((prop) => {
- if (
- values.attributes[prop] === false ||
- (prop === 'data-align' &&
- values.attributes[prop] === 'none')
- ) {
- delete values.attributes[prop];
- }
- });
- }
- widget.setData({
- attributes: values.attributes,
- hasCaption: !!values.hasCaption,
- });
- editor.fire('saveSnapshot');
- };
-
- Drupal.ckeditor.openDialog(
- editor,
- Drupal.url(
- `editor/dialog/media/${editor.config.drupal.format}`,
- ),
- widget.data,
- saveCallback,
- {},
- );
- });
-
- // Allow opening the dialog with the return key or the space bar
- // by triggering a click event when a keydown event occurs on
- // the edit button.
- this.element
- .findOne('.media-library-item__edit')
- .on('keydown', (event) => {
- // The character code for the return key.
- const returnKey = 13;
- // The character code for the space bar.
- const spaceBar = 32;
- if (typeof event.data !== 'undefined') {
- const keypress = event.data.getKey();
- if (keypress === returnKey || keypress === spaceBar) {
- // Clicks the edit button that triggered the 'keydown'
- // event.
- event.sender.$.click();
- }
- // Stop propagation to keep the return key from
- // adding a line break.
- event.data.$.stopPropagation();
- event.data.$.stopImmediatePropagation();
- }
- });
- },
-
- _tearDownDynamicEditables() {
- // If we are watching for changes to the caption, stop doing that.
- if (this.captionObserver) {
- this.captionObserver.disconnect();
- }
- },
-
- /**
- * Determines if the preview needs to be re-rendered by the server.
- *
- * @return {boolean}
- * Returns true if the data hashes differ.
- */
- _previewNeedsServerSideUpdate() {
- // When the widget is first loading, it of course needs to still get a preview!
- if (!this.ready) {
- return true;
- }
-
- return this._hashData(this.oldData) !== this._hashData(this.data);
- },
-
- /**
- * Computes a hash of the data that can only be previewed by the server.
- *
- * @return {string}
- */
- _hashData(data) {
- const dataToHash = CKEDITOR.tools.clone(data);
- // The caption does not need rendering.
- delete dataToHash.attributes['data-caption'];
- // The media entity's label is server-side data and cannot be
- // modified by the content author.
- delete dataToHash.label;
- // Changed link destinations do not affect the visual preview.
- if (dataToHash.link) {
- delete dataToHash.link.href;
- }
- return JSON.stringify(dataToHash);
- },
-
- /**
- * Loads an media embed preview and runs a callback after insertion.
- *
- * Note the absence of caching, that's because this uses a GET request (which is cacheable) and the server takes
- * special care to make the responses privately cacheable (i.e. per session) in the browser.
- *
- * @see \Drupal\media\Controller\MediaFilterController::preview()
- *
- * @param {function} callback
- * A callback function that will be called after the preview has
- * loaded. Receives the widget instance.
- */
- _loadPreview(callback) {
- jQuery.get({
- url: Drupal.url(`media/${editor.config.drupal.format}/preview`),
- data: {
- text: this.downcast().getOuterHtml(),
- uuid: this.data.attributes['data-entity-uuid'],
- },
- dataType: 'html',
- headers: {
- 'X-Drupal-MediaPreview-CSRF-Token':
- editor.config.drupalMedia_previewCsrfToken,
- },
- success: (previewHtml, textStatus, jqXhr) => {
- this.element.setHtml(previewHtml);
- this.setData(
- 'label',
- jqXhr.getResponseHeader('Drupal-Media-Label'),
- );
- callback(this);
- },
- error: () => {
- this.element.setHtml(Drupal.theme('mediaEmbedPreviewError'));
- },
- });
- },
- });
- },
-
- afterInit(editor) {
- linkCommandIntegrator(editor);
- },
- });
-})(jQuery, Drupal, CKEDITOR);
diff --git a/core/modules/ckeditor/js/plugins/drupalmedia/plugin.js b/core/modules/ckeditor/js/plugins/drupalmedia/plugin.js
deleted file mode 100644
index 51e1da502e3e..000000000000
--- a/core/modules/ckeditor/js/plugins/drupalmedia/plugin.js
+++ /dev/null
@@ -1,345 +0,0 @@
-/**
-* DO NOT EDIT THIS FILE.
-* See the following change record for more information,
-* https://www.drupal.org/node/2815083
-* @preserve
-**/
-
-(function (jQuery, Drupal, CKEDITOR) {
- function getFocusedWidget(editor) {
- const widget = editor.widgets.focused;
-
- if (widget && widget.name === 'drupalmedia') {
- return widget;
- }
-
- return null;
- }
-
- function linkCommandIntegrator(editor) {
- if (!editor.plugins.drupallink) {
- return;
- }
-
- CKEDITOR.plugins.drupallink.registerLinkableWidget('drupalmedia');
- editor.getCommand('drupalunlink').on('exec', function (evt) {
- const widget = getFocusedWidget(editor);
-
- if (!widget) {
- return;
- }
-
- widget.setData('link', null);
- this.refresh(editor, editor.elementPath());
- evt.cancel();
- });
- editor.getCommand('drupalunlink').on('refresh', function (evt) {
- const widget = getFocusedWidget(editor);
-
- if (!widget) {
- return;
- }
-
- this.setState(widget.data.link ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED);
- evt.cancel();
- });
-
- if (editor.contextMenu) {
- editor.contextMenu.addListener(() => {
- const widget = getFocusedWidget(editor);
-
- if (!widget) {
- return;
- }
-
- if (widget.data.link) {
- return {
- link: CKEDITOR.TRISTATE_OFF,
- unlink: CKEDITOR.TRISTATE_OFF
- };
- }
-
- return {};
- });
- }
- }
-
- CKEDITOR.plugins.add('drupalmedia', {
- requires: 'widget',
-
- beforeInit(editor) {
- const {
- dtd
- } = CKEDITOR;
- dtd['drupal-media'] = {
- '#': 1
- };
- Object.keys(dtd).forEach(tagName => {
- if (dtd[tagName].div) {
- dtd[tagName]['drupal-media'] = 1;
- }
- });
- dtd.a['drupal-media'] = 1;
- editor.widgets.add('drupalmedia', {
- allowedContent: {
- 'drupal-media': {
- attributes: {
- '!data-entity-type': true,
- '!data-entity-uuid': true,
- 'data-align': true,
- 'data-caption': true,
- alt: true,
- title: true
- },
- classes: {}
- }
- },
- requiredContent: new CKEDITOR.style({
- element: 'drupal-media',
- attributes: {
- 'data-entity-type': '',
- 'data-entity-uuid': ''
- }
- }),
- pathName: Drupal.t('Embedded media'),
- editables: {
- caption: {
- selector: 'figcaption',
- allowedContent: 'a[!href]; em strong cite code br',
- pathName: Drupal.t('Caption')
- }
- },
-
- getLabel() {
- if (this.data.label) {
- return this.data.label;
- }
-
- return Drupal.t('Embedded media');
- },
-
- upcast(element, data) {
- const {
- attributes
- } = element;
-
- if (element.name !== 'drupal-media' || attributes['data-entity-type'] !== 'media' || attributes['data-entity-uuid'] === undefined) {
- return;
- }
-
- data.attributes = CKEDITOR.tools.copy(attributes);
- data.hasCaption = data.attributes.hasOwnProperty('data-caption');
-
- if (data.hasCaption && data.attributes['data-caption'] === '') {
- data.attributes['data-caption'] = ' ';
- }
-
- data.label = null;
- data.link = null;
-
- if (element.parent.name === 'a') {
- data.link = CKEDITOR.tools.copy(element.parent.attributes);
- Object.keys(element.parent.attributes).forEach(attrName => {
- if (attrName.indexOf('data-cke-') !== -1) {
- delete data.link[attrName];
- }
- });
- }
-
- const hostEntityLangcode = document.getElementById(editor.name).getAttribute('data-media-embed-host-entity-langcode');
-
- if (hostEntityLangcode) {
- data.hostEntityLangcode = hostEntityLangcode;
- }
-
- return element;
- },
-
- destroy() {
- this._tearDownDynamicEditables();
- },
-
- data(event) {
- if (this.oldData) {
- if (!this.data.hasCaption && this.oldData.hasCaption) {
- delete this.data.attributes['data-caption'];
- } else if (this.data.hasCaption && !this.data.attributes['data-caption']) {
- this.data.attributes['data-caption'] = ' ';
- }
- }
-
- if (this._previewNeedsServerSideUpdate()) {
- editor.fire('lockSnapshot');
-
- this._tearDownDynamicEditables();
-
- this._loadPreview(widget => {
- widget._setUpDynamicEditables();
-
- widget._setUpEditButton();
-
- editor.fire('unlockSnapshot');
- });
- }
-
- if (this.oldData) {
- Object.keys(this.oldData.attributes).forEach(attrName => {
- this.element.removeAttribute(attrName);
- });
- }
-
- this.element.setAttributes(this.data.attributes);
- this.oldData = CKEDITOR.tools.clone(this.data);
- },
-
- downcast() {
- const downcastElement = new CKEDITOR.htmlParser.element('drupal-media', this.data.attributes);
-
- if (this.data.link) {
- const link = new CKEDITOR.htmlParser.element('a', this.data.link);
- link.add(downcastElement);
- return link;
- }
-
- return downcastElement;
- },
-
- _setUpDynamicEditables() {
- if (this.initEditable('caption', this.definition.editables.caption)) {
- const captionEditable = this.editables.caption;
- captionEditable.setAttribute('data-placeholder', Drupal.t('Enter caption here'));
- this.captionObserver = new MutationObserver(() => {
- const mediaAttributes = CKEDITOR.tools.clone(this.data.attributes);
- mediaAttributes['data-caption'] = captionEditable.getData();
- this.setData('attributes', mediaAttributes);
- });
- this.captionObserver.observe(captionEditable.$, {
- characterData: true,
- attributes: true,
- childList: true,
- subtree: true
- });
-
- if (captionEditable.$.childNodes.length === 1 && captionEditable.$.childNodes.item(0).nodeName === 'BR') {
- captionEditable.$.removeChild(captionEditable.$.childNodes.item(0));
- }
- }
- },
-
- _setUpEditButton() {
- if (this.element.findOne('.media-embed-error')) {
- return;
- }
-
- const isElementNode = function (n) {
- return n.type === CKEDITOR.NODE_ELEMENT;
- };
-
- const embeddedMediaContainer = this.data.hasCaption ? this.element.findOne('figure') : this.element;
- let embeddedMedia = embeddedMediaContainer.getFirst(isElementNode);
-
- if (this.data.link) {
- embeddedMedia = embeddedMedia.getFirst(isElementNode);
- }
-
- embeddedMedia.setStyle('position', 'relative');
- const editButton = CKEDITOR.dom.element.createFromHtml(Drupal.theme('mediaEmbedEditButton'));
- embeddedMedia.getFirst().insertBeforeMe(editButton);
- const widget = this;
- this.element.findOne('.media-library-item__edit').on('click', event => {
- const saveCallback = function (values) {
- event.cancel();
- editor.fire('saveSnapshot');
-
- if (values.hasOwnProperty('attributes')) {
- CKEDITOR.tools.extend(values.attributes, widget.data.attributes);
- Object.keys(values.attributes).forEach(prop => {
- if (values.attributes[prop] === false || prop === 'data-align' && values.attributes[prop] === 'none') {
- delete values.attributes[prop];
- }
- });
- }
-
- widget.setData({
- attributes: values.attributes,
- hasCaption: !!values.hasCaption
- });
- editor.fire('saveSnapshot');
- };
-
- Drupal.ckeditor.openDialog(editor, Drupal.url(`editor/dialog/media/${editor.config.drupal.format}`), widget.data, saveCallback, {});
- });
- this.element.findOne('.media-library-item__edit').on('keydown', event => {
- const returnKey = 13;
- const spaceBar = 32;
-
- if (typeof event.data !== 'undefined') {
- const keypress = event.data.getKey();
-
- if (keypress === returnKey || keypress === spaceBar) {
- event.sender.$.click();
- }
-
- event.data.$.stopPropagation();
- event.data.$.stopImmediatePropagation();
- }
- });
- },
-
- _tearDownDynamicEditables() {
- if (this.captionObserver) {
- this.captionObserver.disconnect();
- }
- },
-
- _previewNeedsServerSideUpdate() {
- if (!this.ready) {
- return true;
- }
-
- return this._hashData(this.oldData) !== this._hashData(this.data);
- },
-
- _hashData(data) {
- const dataToHash = CKEDITOR.tools.clone(data);
- delete dataToHash.attributes['data-caption'];
- delete dataToHash.label;
-
- if (dataToHash.link) {
- delete dataToHash.link.href;
- }
-
- return JSON.stringify(dataToHash);
- },
-
- _loadPreview(callback) {
- jQuery.get({
- url: Drupal.url(`media/${editor.config.drupal.format}/preview`),
- data: {
- text: this.downcast().getOuterHtml(),
- uuid: this.data.attributes['data-entity-uuid']
- },
- dataType: 'html',
- headers: {
- 'X-Drupal-MediaPreview-CSRF-Token': editor.config.drupalMedia_previewCsrfToken
- },
- success: (previewHtml, textStatus, jqXhr) => {
- this.element.setHtml(previewHtml);
- this.setData('label', jqXhr.getResponseHeader('Drupal-Media-Label'));
- callback(this);
- },
- error: () => {
- this.element.setHtml(Drupal.theme('mediaEmbedPreviewError'));
- }
- });
- }
-
- });
- },
-
- afterInit(editor) {
- linkCommandIntegrator(editor);
- }
-
- });
-})(jQuery, Drupal, CKEDITOR); \ No newline at end of file
diff --git a/core/modules/ckeditor/js/plugins/drupalmedialibrary/icons/drupalmedialibrary.png b/core/modules/ckeditor/js/plugins/drupalmedialibrary/icons/drupalmedialibrary.png
deleted file mode 100644
index bcd36d441320..000000000000
--- a/core/modules/ckeditor/js/plugins/drupalmedialibrary/icons/drupalmedialibrary.png
+++ /dev/null
Binary files differ
diff --git a/core/modules/ckeditor/js/plugins/drupalmedialibrary/icons/hidpi/drupalmedialibrary.png b/core/modules/ckeditor/js/plugins/drupalmedialibrary/icons/hidpi/drupalmedialibrary.png
deleted file mode 100644
index 6d5f1405cf21..000000000000
--- a/core/modules/ckeditor/js/plugins/drupalmedialibrary/icons/hidpi/drupalmedialibrary.png
+++ /dev/null
Binary files differ
diff --git a/core/modules/ckeditor/js/plugins/drupalmedialibrary/plugin.es6.js b/core/modules/ckeditor/js/plugins/drupalmedialibrary/plugin.es6.js
deleted file mode 100644
index 1e270a66b79e..000000000000
--- a/core/modules/ckeditor/js/plugins/drupalmedialibrary/plugin.es6.js
+++ /dev/null
@@ -1,76 +0,0 @@
-/**
- * @file
- * Drupal Media Library plugin.
- */
-
-(function (Drupal, CKEDITOR) {
- CKEDITOR.plugins.add('drupalmedialibrary', {
- requires: 'drupalmedia',
- icons: 'drupalmedialibrary',
- hidpi: true,
- beforeInit(editor) {
- editor.addCommand('drupalmedialibrary', {
- allowedContent: {
- 'drupal-media': {
- attributes: {
- '!data-entity-type': true,
- '!data-entity-uuid': true,
- '!data-view-mode': true,
- '!data-align': true,
- '!data-caption': true,
- '!alt': true,
- '!title': true,
- },
- classes: {},
- },
- },
- // This does not use the object format used above, but a
- // CKEDITOR.style instance, because requiredContent does not support
- // the object format.
- // @see https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_filter_contentRule.html
- // eslint-disable-next-line new-cap
- requiredContent: new CKEDITOR.style({
- element: 'drupal-media',
- attributes: {
- 'data-entity-type': '',
- 'data-entity-uuid': '',
- },
- }),
- modes: { wysiwyg: 1 },
- // There is an edge case related to the undo functionality that will
- // be resolved in https://www.drupal.org/project/drupal/issues/3073294.
- canUndo: true,
- // eslint-disable-next-line no-shadow
- exec(editor) {
- const saveCallback = function (values) {
- editor.fire('saveSnapshot');
- const mediaElement = editor.document.createElement('drupal-media');
- // eslint-disable-next-line prefer-destructuring
- const attributes = values.attributes;
- Object.keys(attributes).forEach((key) => {
- mediaElement.setAttribute(key, attributes[key]);
- });
- editor.insertHtml(mediaElement.getOuterHtml());
- editor.fire('saveSnapshot');
- };
-
- // @see \Drupal\media_library\MediaLibraryUiBuilder::dialogOptions()
- Drupal.ckeditor.openDialog(
- editor,
- editor.config.DrupalMediaLibrary_url,
- {},
- saveCallback,
- editor.config.DrupalMediaLibrary_dialogOptions,
- );
- },
- });
-
- if (editor.ui.addButton) {
- editor.ui.addButton('DrupalMediaLibrary', {
- label: Drupal.t('Insert from Media Library'),
- command: 'drupalmedialibrary',
- });
- }
- },
- });
-})(Drupal, CKEDITOR);
diff --git a/core/modules/ckeditor/js/plugins/drupalmedialibrary/plugin.js b/core/modules/ckeditor/js/plugins/drupalmedialibrary/plugin.js
deleted file mode 100644
index 84cf9e7cca0f..000000000000
--- a/core/modules/ckeditor/js/plugins/drupalmedialibrary/plugin.js
+++ /dev/null
@@ -1,68 +0,0 @@
-/**
-* DO NOT EDIT THIS FILE.
-* See the following change record for more information,
-* https://www.drupal.org/node/2815083
-* @preserve
-**/
-
-(function (Drupal, CKEDITOR) {
- CKEDITOR.plugins.add('drupalmedialibrary', {
- requires: 'drupalmedia',
- icons: 'drupalmedialibrary',
- hidpi: true,
-
- beforeInit(editor) {
- editor.addCommand('drupalmedialibrary', {
- allowedContent: {
- 'drupal-media': {
- attributes: {
- '!data-entity-type': true,
- '!data-entity-uuid': true,
- '!data-view-mode': true,
- '!data-align': true,
- '!data-caption': true,
- '!alt': true,
- '!title': true
- },
- classes: {}
- }
- },
- requiredContent: new CKEDITOR.style({
- element: 'drupal-media',
- attributes: {
- 'data-entity-type': '',
- 'data-entity-uuid': ''
- }
- }),
- modes: {
- wysiwyg: 1
- },
- canUndo: true,
-
- exec(editor) {
- const saveCallback = function (values) {
- editor.fire('saveSnapshot');
- const mediaElement = editor.document.createElement('drupal-media');
- const attributes = values.attributes;
- Object.keys(attributes).forEach(key => {
- mediaElement.setAttribute(key, attributes[key]);
- });
- editor.insertHtml(mediaElement.getOuterHtml());
- editor.fire('saveSnapshot');
- };
-
- Drupal.ckeditor.openDialog(editor, editor.config.DrupalMediaLibrary_url, {}, saveCallback, editor.config.DrupalMediaLibrary_dialogOptions);
- }
-
- });
-
- if (editor.ui.addButton) {
- editor.ui.addButton('DrupalMediaLibrary', {
- label: Drupal.t('Insert from Media Library'),
- command: 'drupalmedialibrary'
- });
- }
- }
-
- });
-})(Drupal, CKEDITOR); \ No newline at end of file
diff --git a/core/modules/ckeditor/js/views/AuralView.es6.js b/core/modules/ckeditor/js/views/AuralView.es6.js
deleted file mode 100644
index e3d015166928..000000000000
--- a/core/modules/ckeditor/js/views/AuralView.es6.js
+++ /dev/null
@@ -1,266 +0,0 @@
-/**
- * @file
- * A Backbone View that provides the aural view of CKEditor toolbar
- * configuration.
- */
-
-(function (Drupal, Backbone, $) {
- Drupal.ckeditor.AuralView = Backbone.View.extend(
- /** @lends Drupal.ckeditor.AuralView# */ {
- /**
- * @type {object}
- */
- events: {
- 'click .ckeditor-buttons a': 'announceButtonHelp',
- 'click .ckeditor-multiple-buttons a': 'announceSeparatorHelp',
- 'focus .ckeditor-button a': 'onFocus',
- 'focus .ckeditor-button-separator a': 'onFocus',
- 'focus .ckeditor-toolbar-group': 'onFocus',
- },
-
- /**
- * Backbone View for CKEditor toolbar configuration; aural UX (output only).
- *
- * @constructs
- *
- * @augments Backbone.View
- */
- initialize() {
- // Announce the button and group positions when the model is no longer
- // dirty.
- this.listenTo(this.model, 'change:isDirty', this.announceMove);
- },
-
- /**
- * Calls announce on buttons and groups when their position is changed.
- *
- * @param {Drupal.ckeditor.ConfigurationModel} model
- * The ckeditor configuration model.
- * @param {bool} isDirty
- * A model attribute that indicates if the changed toolbar configuration
- * has been stored or not.
- */
- announceMove(model, isDirty) {
- // Announce the position of a button or group after the model has been
- // updated.
- if (!isDirty) {
- const item = document.activeElement || null;
- if (item) {
- const $item = $(item);
- if ($item.hasClass('ckeditor-toolbar-group')) {
- this.announceButtonGroupPosition($item);
- } else if ($item.parent().hasClass('ckeditor-button')) {
- this.announceButtonPosition($item.parent());
- }
- }
- }
- },
-
- /**
- * Handles the focus event of elements in the active and available toolbars.
- *
- * @param {jQuery.Event} event
- * The focus event that was triggered.
- */
- onFocus(event) {
- event.stopPropagation();
-
- const $originalTarget = $(event.target);
- const $currentTarget = $(event.currentTarget);
- const $parent = $currentTarget.parent();
- if (
- $parent.hasClass('ckeditor-button') ||
- $parent.hasClass('ckeditor-button-separator')
- ) {
- this.announceButtonPosition($currentTarget.parent());
- } else if (
- $originalTarget.attr('role') !== 'button' &&
- $currentTarget.hasClass('ckeditor-toolbar-group')
- ) {
- this.announceButtonGroupPosition($currentTarget);
- }
- },
-
- /**
- * Announces the current position of a button group.
- *
- * @param {jQuery} $group
- * A jQuery set that contains an li element that wraps a group of buttons.
- */
- announceButtonGroupPosition($group) {
- const $groups = $group.parent().children();
- const $row = $group.closest('.ckeditor-row');
- const $rows = $row.parent().children();
- const position = $groups.index($group) + 1;
- const positionCount = $groups.not('.placeholder').length;
- const row = $rows.index($row) + 1;
- const rowCount = $rows.not('.placeholder').length;
- let text = Drupal.t(
- '@groupName button group in position @position of @positionCount in row @row of @rowCount.',
- {
- '@groupName': $group.attr(
- 'data-drupal-ckeditor-toolbar-group-name',
- ),
- '@position': position,
- '@positionCount': positionCount,
- '@row': row,
- '@rowCount': rowCount,
- },
- );
- // If this position is the first in the last row then tell the user that
- // pressing the down arrow key will create a new row.
- if (position === 1 && row === rowCount) {
- text += '\n';
- text += Drupal.t('Press the down arrow key to create a new row.');
- }
- Drupal.announce(text, 'assertive');
- },
-
- /**
- * Announces current button position.
- *
- * @param {jQuery} $button
- * A jQuery set that contains an li element that wraps a button.
- */
- announceButtonPosition($button) {
- const $row = $button.closest('.ckeditor-row');
- const $rows = $row.parent().children();
- const $buttons = $button.closest('.ckeditor-buttons').children();
- const $group = $button.closest('.ckeditor-toolbar-group');
- const $groups = $group.parent().children();
- const groupPosition = $groups.index($group) + 1;
- const groupPositionCount = $groups.not('.placeholder').length;
- const position = $buttons.index($button) + 1;
- const positionCount = $buttons.length;
- const row = $rows.index($row) + 1;
- const rowCount = $rows.not('.placeholder').length;
- // The name of the button separator is 'button separator' and its type
- // is 'separator', so we do not want to print the type of this item,
- // otherwise the UA will speak 'button separator separator'.
- const type =
- $button.attr('data-drupal-ckeditor-type') === 'separator'
- ? ''
- : Drupal.t('button');
- let text;
- // The button is located in the available button set.
- if ($button.closest('.ckeditor-toolbar-disabled').length > 0) {
- text = Drupal.t('@name @type.', {
- '@name': $button.children().attr('aria-label'),
- '@type': type,
- });
- text += `\n${Drupal.t('Press the down arrow key to activate.')}`;
-
- Drupal.announce(text, 'assertive');
- }
- // The button is in the active toolbar.
- else if ($group.not('.placeholder').length === 1) {
- text = Drupal.t(
- '@name @type in position @position of @positionCount in @groupName button group in row @row of @rowCount.',
- {
- '@name': $button.children().attr('aria-label'),
- '@type': type,
- '@position': position,
- '@positionCount': positionCount,
- '@groupName': $group.attr(
- 'data-drupal-ckeditor-toolbar-group-name',
- ),
- '@row': row,
- '@rowCount': rowCount,
- },
- );
- // If this position is the first in the last row then tell the user that
- // pressing the down arrow key will create a new row.
- if (groupPosition === 1 && position === 1 && row === rowCount) {
- text += '\n';
- text += Drupal.t(
- 'Press the down arrow key to create a new button group in a new row.',
- );
- }
- // If this position is the last one in this row then tell the user that
- // moving the button to the next group will create a new group.
- if (
- groupPosition === groupPositionCount &&
- position === positionCount
- ) {
- text += '\n';
- text += Drupal.t(
- 'This is the last group. Move the button forward to create a new group.',
- );
- }
- Drupal.announce(text, 'assertive');
- }
- },
-
- /**
- * Provides help information when a button is clicked.
- *
- * @param {jQuery.Event} event
- * The click event for the button click.
- */
- announceButtonHelp(event) {
- const $link = $(event.currentTarget);
- const $button = $link.parent();
- const enabled = $button.closest('.ckeditor-toolbar-active').length > 0;
- let message;
-
- if (enabled) {
- message = Drupal.t('The "@name" button is currently enabled.', {
- '@name': $link.attr('aria-label'),
- });
- message += `\n${Drupal.t(
- 'Use the keyboard arrow keys to change the position of this button.',
- )}`;
- message += `\n${Drupal.t(
- 'Press the up arrow key on the top row to disable the button.',
- )}`;
- } else {
- message = Drupal.t('The "@name" button is currently disabled.', {
- '@name': $link.attr('aria-label'),
- });
- message += `\n${Drupal.t(
- 'Use the down arrow key to move this button into the active toolbar.',
- )}`;
- }
- Drupal.announce(message);
- event.preventDefault();
- },
-
- /**
- * Provides help information when a separator is clicked.
- *
- * @param {jQuery.Event} event
- * The click event for the separator click.
- */
- announceSeparatorHelp(event) {
- const $link = $(event.currentTarget);
- const $button = $link.parent();
- const enabled = $button.closest('.ckeditor-toolbar-active').length > 0;
- let message;
-
- if (enabled) {
- message = Drupal.t('This @name is currently enabled.', {
- '@name': $link.attr('aria-label'),
- });
- message += `\n${Drupal.t(
- 'Use the keyboard arrow keys to change the position of this separator.',
- )}`;
- } else {
- message = Drupal.t(
- 'Separators are used to visually split individual buttons.',
- );
- message += `\n${Drupal.t('This @name is currently disabled.', {
- '@name': $link.attr('aria-label'),
- })}`;
- message += `\n${Drupal.t(
- 'Use the down arrow key to move this separator into the active toolbar.',
- )}`;
- message += `\n${Drupal.t(
- 'You may add multiple separators to each button group.',
- )}`;
- }
- Drupal.announce(message);
- event.preventDefault();
- },
- },
- );
-})(Drupal, Backbone, jQuery);
diff --git a/core/modules/ckeditor/js/views/AuralView.js b/core/modules/ckeditor/js/views/AuralView.js
deleted file mode 100644
index e0fae0c6e153..000000000000
--- a/core/modules/ckeditor/js/views/AuralView.js
+++ /dev/null
@@ -1,170 +0,0 @@
-/**
-* DO NOT EDIT THIS FILE.
-* See the following change record for more information,
-* https://www.drupal.org/node/2815083
-* @preserve
-**/
-
-(function (Drupal, Backbone, $) {
- Drupal.ckeditor.AuralView = Backbone.View.extend({
- events: {
- 'click .ckeditor-buttons a': 'announceButtonHelp',
- 'click .ckeditor-multiple-buttons a': 'announceSeparatorHelp',
- 'focus .ckeditor-button a': 'onFocus',
- 'focus .ckeditor-button-separator a': 'onFocus',
- 'focus .ckeditor-toolbar-group': 'onFocus'
- },
-
- initialize() {
- this.listenTo(this.model, 'change:isDirty', this.announceMove);
- },
-
- announceMove(model, isDirty) {
- if (!isDirty) {
- const item = document.activeElement || null;
-
- if (item) {
- const $item = $(item);
-
- if ($item.hasClass('ckeditor-toolbar-group')) {
- this.announceButtonGroupPosition($item);
- } else if ($item.parent().hasClass('ckeditor-button')) {
- this.announceButtonPosition($item.parent());
- }
- }
- }
- },
-
- onFocus(event) {
- event.stopPropagation();
- const $originalTarget = $(event.target);
- const $currentTarget = $(event.currentTarget);
- const $parent = $currentTarget.parent();
-
- if ($parent.hasClass('ckeditor-button') || $parent.hasClass('ckeditor-button-separator')) {
- this.announceButtonPosition($currentTarget.parent());
- } else if ($originalTarget.attr('role') !== 'button' && $currentTarget.hasClass('ckeditor-toolbar-group')) {
- this.announceButtonGroupPosition($currentTarget);
- }
- },
-
- announceButtonGroupPosition($group) {
- const $groups = $group.parent().children();
- const $row = $group.closest('.ckeditor-row');
- const $rows = $row.parent().children();
- const position = $groups.index($group) + 1;
- const positionCount = $groups.not('.placeholder').length;
- const row = $rows.index($row) + 1;
- const rowCount = $rows.not('.placeholder').length;
- let text = Drupal.t('@groupName button group in position @position of @positionCount in row @row of @rowCount.', {
- '@groupName': $group.attr('data-drupal-ckeditor-toolbar-group-name'),
- '@position': position,
- '@positionCount': positionCount,
- '@row': row,
- '@rowCount': rowCount
- });
-
- if (position === 1 && row === rowCount) {
- text += '\n';
- text += Drupal.t('Press the down arrow key to create a new row.');
- }
-
- Drupal.announce(text, 'assertive');
- },
-
- announceButtonPosition($button) {
- const $row = $button.closest('.ckeditor-row');
- const $rows = $row.parent().children();
- const $buttons = $button.closest('.ckeditor-buttons').children();
- const $group = $button.closest('.ckeditor-toolbar-group');
- const $groups = $group.parent().children();
- const groupPosition = $groups.index($group) + 1;
- const groupPositionCount = $groups.not('.placeholder').length;
- const position = $buttons.index($button) + 1;
- const positionCount = $buttons.length;
- const row = $rows.index($row) + 1;
- const rowCount = $rows.not('.placeholder').length;
- const type = $button.attr('data-drupal-ckeditor-type') === 'separator' ? '' : Drupal.t('button');
- let text;
-
- if ($button.closest('.ckeditor-toolbar-disabled').length > 0) {
- text = Drupal.t('@name @type.', {
- '@name': $button.children().attr('aria-label'),
- '@type': type
- });
- text += `\n${Drupal.t('Press the down arrow key to activate.')}`;
- Drupal.announce(text, 'assertive');
- } else if ($group.not('.placeholder').length === 1) {
- text = Drupal.t('@name @type in position @position of @positionCount in @groupName button group in row @row of @rowCount.', {
- '@name': $button.children().attr('aria-label'),
- '@type': type,
- '@position': position,
- '@positionCount': positionCount,
- '@groupName': $group.attr('data-drupal-ckeditor-toolbar-group-name'),
- '@row': row,
- '@rowCount': rowCount
- });
-
- if (groupPosition === 1 && position === 1 && row === rowCount) {
- text += '\n';
- text += Drupal.t('Press the down arrow key to create a new button group in a new row.');
- }
-
- if (groupPosition === groupPositionCount && position === positionCount) {
- text += '\n';
- text += Drupal.t('This is the last group. Move the button forward to create a new group.');
- }
-
- Drupal.announce(text, 'assertive');
- }
- },
-
- announceButtonHelp(event) {
- const $link = $(event.currentTarget);
- const $button = $link.parent();
- const enabled = $button.closest('.ckeditor-toolbar-active').length > 0;
- let message;
-
- if (enabled) {
- message = Drupal.t('The "@name" button is currently enabled.', {
- '@name': $link.attr('aria-label')
- });
- message += `\n${Drupal.t('Use the keyboard arrow keys to change the position of this button.')}`;
- message += `\n${Drupal.t('Press the up arrow key on the top row to disable the button.')}`;
- } else {
- message = Drupal.t('The "@name" button is currently disabled.', {
- '@name': $link.attr('aria-label')
- });
- message += `\n${Drupal.t('Use the down arrow key to move this button into the active toolbar.')}`;
- }
-
- Drupal.announce(message);
- event.preventDefault();
- },
-
- announceSeparatorHelp(event) {
- const $link = $(event.currentTarget);
- const $button = $link.parent();
- const enabled = $button.closest('.ckeditor-toolbar-active').length > 0;
- let message;
-
- if (enabled) {
- message = Drupal.t('This @name is currently enabled.', {
- '@name': $link.attr('aria-label')
- });
- message += `\n${Drupal.t('Use the keyboard arrow keys to change the position of this separator.')}`;
- } else {
- message = Drupal.t('Separators are used to visually split individual buttons.');
- message += `\n${Drupal.t('This @name is currently disabled.', {
- '@name': $link.attr('aria-label')
- })}`;
- message += `\n${Drupal.t('Use the down arrow key to move this separator into the active toolbar.')}`;
- message += `\n${Drupal.t('You may add multiple separators to each button group.')}`;
- }
-
- Drupal.announce(message);
- event.preventDefault();
- }
-
- });
-})(Drupal, Backbone, jQuery); \ No newline at end of file
diff --git a/core/modules/ckeditor/js/views/ControllerView.es6.js b/core/modules/ckeditor/js/views/ControllerView.es6.js
deleted file mode 100644
index 270442ee8300..000000000000
--- a/core/modules/ckeditor/js/views/ControllerView.es6.js
+++ /dev/null
@@ -1,419 +0,0 @@
-/**
- * @file
- * A Backbone View acting as a controller for CKEditor toolbar configuration.
- */
-
-(function ($, Drupal, Backbone, CKEDITOR, _) {
- Drupal.ckeditor.ControllerView = Backbone.View.extend(
- /** @lends Drupal.ckeditor.ControllerView# */ {
- /**
- * @type {object}
- */
- events: {},
-
- /**
- * Backbone View acting as a controller for CKEditor toolbar configuration.
- *
- * @constructs
- *
- * @augments Backbone.View
- */
- initialize() {
- this.getCKEditorFeatures(
- this.model.get('hiddenEditorConfig'),
- this.disableFeaturesDisallowedByFilters.bind(this),
- );
-
- // Push the active editor configuration to the textarea.
- this.model.listenTo(
- this.model,
- 'change:activeEditorConfig',
- this.model.sync,
- );
- this.listenTo(this.model, 'change:isDirty', this.parseEditorDOM);
- },
-
- /**
- * Converts the active toolbar DOM structure to an object representation.
- *
- * @param {Drupal.ckeditor.ConfigurationModel} model
- * The state model for the CKEditor configuration.
- * @param {bool} isDirty
- * Tracks whether the active toolbar DOM structure has been changed.
- * isDirty is toggled back to false in this method.
- * @param {object} options
- * An object that includes:
- * @param {bool} [options.broadcast]
- * A flag that controls whether a CKEditorToolbarChanged event should be
- * fired for configuration changes.
- *
- * @fires event:CKEditorToolbarChanged
- */
- parseEditorDOM(model, isDirty, options) {
- if (isDirty) {
- const currentConfig = this.model.get('activeEditorConfig');
-
- // Process the rows.
- const rows = [];
- this.$el
- .find('.ckeditor-active-toolbar-configuration')
- .children('.ckeditor-row')
- .each(function () {
- const groups = [];
- // Process the button groups.
- $(this)
- .find('.ckeditor-toolbar-group')
- .each(function () {
- const $group = $(this);
- const $buttons = $group.find('.ckeditor-button');
- if ($buttons.length) {
- const group = {
- name: $group.attr(
- 'data-drupal-ckeditor-toolbar-group-name',
- ),
- items: [],
- };
- $group
- .find('.ckeditor-button, .ckeditor-multiple-button')
- .each(function () {
- group.items.push(
- $(this).attr('data-drupal-ckeditor-button-name'),
- );
- });
- groups.push(group);
- }
- });
- if (groups.length) {
- rows.push(groups);
- }
- });
- this.model.set('activeEditorConfig', rows);
- // Mark the model as clean. Whether or not the sync to the textfield
- // occurs depends on the activeEditorConfig attribute firing a change
- // event. The DOM has at least been processed and posted, so as far as
- // the model is concerned, it is clean.
- this.model.set('isDirty', false);
-
- // Determine whether we should trigger an event.
- if (options.broadcast !== false) {
- const prev = this.getButtonList(currentConfig);
- const next = this.getButtonList(rows);
- if (prev.length !== next.length) {
- this.$el
- .find('.ckeditor-toolbar-active')
- .trigger('CKEditorToolbarChanged', [
- prev.length < next.length ? 'added' : 'removed',
- _.difference(
- _.union(prev, next),
- _.intersection(prev, next),
- )[0],
- ]);
- }
- }
- }
- },
-
- /**
- * Asynchronously retrieve the metadata for all available CKEditor features.
- *
- * In order to get a list of all features needed by CKEditor, we create a
- * hidden CKEditor instance, then check the CKEditor's "allowedContent"
- * filter settings. Because creating an instance is expensive, a callback
- * must be provided that will receive a hash of {@link Drupal.EditorFeature}
- * features keyed by feature (button) name.
- *
- * @param {object} CKEditorConfig
- * An object that represents the configuration settings for a CKEditor
- * editor component.
- * @param {function} callback
- * A function to invoke when the instanceReady event is fired by the
- * CKEditor object.
- */
- getCKEditorFeatures(CKEditorConfig, callback) {
- const getProperties = function (CKEPropertiesList) {
- return _.isObject(CKEPropertiesList) ? _.keys(CKEPropertiesList) : [];
- };
-
- const convertCKERulesToEditorFeature = function (
- feature,
- CKEFeatureRules,
- ) {
- for (let i = 0; i < CKEFeatureRules.length; i++) {
- const CKERule = CKEFeatureRules[i];
- const rule = new Drupal.EditorFeatureHTMLRule();
-
- // Tags.
- const tags = getProperties(CKERule.elements);
- rule.required.tags = CKERule.propertiesOnly ? [] : tags;
- rule.allowed.tags = tags;
- // Attributes.
- rule.required.attributes = getProperties(
- CKERule.requiredAttributes,
- );
- rule.allowed.attributes = getProperties(CKERule.attributes);
- // Styles.
- rule.required.styles = getProperties(CKERule.requiredStyles);
- rule.allowed.styles = getProperties(CKERule.styles);
- // Classes.
- rule.required.classes = getProperties(CKERule.requiredClasses);
- rule.allowed.classes = getProperties(CKERule.classes);
- // Raw.
- rule.raw = CKERule;
-
- feature.addHTMLRule(rule);
- }
- };
-
- // Create hidden CKEditor with all features enabled, retrieve metadata.
- // @see \Drupal\ckeditor\Plugin\Editor\CKEditor::buildConfigurationForm().
- const hiddenCKEditorID = 'ckeditor-hidden';
- if (CKEDITOR.instances[hiddenCKEditorID]) {
- CKEDITOR.instances[hiddenCKEditorID].destroy(true);
- }
- // Load external plugins, if any.
- const hiddenEditorConfig = this.model.get('hiddenEditorConfig');
- if (hiddenEditorConfig.drupalExternalPlugins) {
- const externalPlugins = hiddenEditorConfig.drupalExternalPlugins;
- Object.keys(externalPlugins || {}).forEach((pluginName) => {
- CKEDITOR.plugins.addExternal(
- pluginName,
- externalPlugins[pluginName],
- '',
- );
- });
- }
- CKEDITOR.inline($(`#${hiddenCKEditorID}`).get(0), CKEditorConfig);
-
- // Once the instance is ready, retrieve the allowedContent filter rules
- // and convert them to Drupal.EditorFeature objects.
- CKEDITOR.once('instanceReady', (e) => {
- if (e.editor.name === hiddenCKEditorID) {
- // First collect all CKEditor allowedContent rules.
- const CKEFeatureRulesMap = {};
- const rules = e.editor.filter.allowedContent;
- let rule;
- let name;
- for (let i = 0; i < rules.length; i++) {
- rule = rules[i];
- name = rule.featureName || ':(';
- if (!CKEFeatureRulesMap[name]) {
- CKEFeatureRulesMap[name] = [];
- }
- CKEFeatureRulesMap[name].push(rule);
- }
-
- // Now convert these to Drupal.EditorFeature objects. And track which
- // buttons are mapped to which features.
- // @see getFeatureForButton()
- const features = {};
- const buttonsToFeatures = {};
- Object.keys(CKEFeatureRulesMap).forEach((featureName) => {
- const feature = new Drupal.EditorFeature(featureName);
- convertCKERulesToEditorFeature(
- feature,
- CKEFeatureRulesMap[featureName],
- );
- features[featureName] = feature;
- const command = e.editor.getCommand(featureName);
- if (command) {
- buttonsToFeatures[command.uiItems[0].name] = featureName;
- }
- });
-
- callback(features, buttonsToFeatures);
- }
- });
- },
-
- /**
- * Retrieves the feature for a given button from featuresMetadata. Returns
- * false if the given button is in fact a divider.
- *
- * @param {string} button
- * The name of a CKEditor button.
- *
- * @return {object}
- * The feature metadata object for a button.
- */
- getFeatureForButton(button) {
- // Return false if the button being added is a divider.
- if (button === '-') {
- return false;
- }
-
- // Get a Drupal.editorFeature object that contains all metadata for
- // the feature that was just added or removed. Not every feature has
- // such metadata.
- let featureName =
- this.model.get('buttonsToFeatures')[button.toLowerCase()];
- // Features without an associated command do not have a 'feature name' by
- // default, so we use the lowercased button name instead.
- if (!featureName) {
- featureName = button.toLowerCase();
- }
- const featuresMetadata = this.model.get('featuresMetadata');
- if (!featuresMetadata[featureName]) {
- featuresMetadata[featureName] = new Drupal.EditorFeature(featureName);
- this.model.set('featuresMetadata', featuresMetadata);
- }
- return featuresMetadata[featureName];
- },
-
- /**
- * Checks buttons against filter settings; disables disallowed buttons.
- *
- * @param {object} features
- * A map of {@link Drupal.EditorFeature} objects.
- * @param {object} buttonsToFeatures
- * Object containing the button-to-feature mapping.
- *
- * @see Drupal.ckeditor.ControllerView#getFeatureForButton
- */
- disableFeaturesDisallowedByFilters(features, buttonsToFeatures) {
- this.model.set('featuresMetadata', features);
- // Store the button-to-feature mapping. Needs to happen only once, because
- // the same buttons continue to have the same features; only the rules for
- // specific features may change.
- // @see getFeatureForButton()
- this.model.set('buttonsToFeatures', buttonsToFeatures);
-
- // Ensure that toolbar configuration changes are broadcast.
- this.broadcastConfigurationChanges(this.$el);
-
- // Initialization: not all of the default toolbar buttons may be allowed
- // by the current filter settings. Remove any of the default toolbar
- // buttons that require more permissive filter settings. The remaining
- // default toolbar buttons are marked as "added".
- let existingButtons = [];
- // Loop through each button group after flattening the groups from the
- // toolbar row arrays.
- const buttonGroups = _.flatten(this.model.get('activeEditorConfig'));
- for (let i = 0; i < buttonGroups.length; i++) {
- // Pull the button names from each toolbar button group.
- const buttons = buttonGroups[i].items;
- for (let k = 0; k < buttons.length; k++) {
- existingButtons.push(buttons[k]);
- }
- }
- // Remove duplicate buttons.
- existingButtons = _.unique(existingButtons);
- // Prepare the active toolbar and available-button toolbars.
- for (let n = 0; n < existingButtons.length; n++) {
- const button = existingButtons[n];
- const feature = this.getFeatureForButton(button);
- // Skip dividers.
- if (feature === false) {
- continue;
- }
-
- if (Drupal.editorConfiguration.featureIsAllowedByFilters(feature)) {
- // Existing toolbar buttons are in fact "added features".
- this.$el
- .find('.ckeditor-toolbar-active')
- .trigger('CKEditorToolbarChanged', ['added', existingButtons[n]]);
- } else {
- // Move the button element from the active the active toolbar to the
- // list of available buttons.
- $(
- `.ckeditor-toolbar-active li[data-drupal-ckeditor-button-name="${button}"]`,
- )
- .detach()
- .appendTo(
- '.ckeditor-toolbar-disabled > .ckeditor-toolbar-available > ul',
- );
- // Update the toolbar value field.
- this.model.set({ isDirty: true }, { broadcast: false });
- }
- }
- },
-
- /**
- * Sets up broadcasting of CKEditor toolbar configuration changes.
- *
- * @param {jQuery} $ckeditorToolbar
- * The active toolbar DOM element wrapped in jQuery.
- */
- broadcastConfigurationChanges($ckeditorToolbar) {
- const view = this;
- const hiddenEditorConfig = this.model.get('hiddenEditorConfig');
- const getFeatureForButton = this.getFeatureForButton.bind(this);
- const getCKEditorFeatures = this.getCKEditorFeatures.bind(this);
- $ckeditorToolbar
- .find('.ckeditor-toolbar-active')
- // Listen for CKEditor toolbar configuration changes. When a button is
- // added/removed, call an appropriate Drupal.editorConfiguration method.
- .on(
- 'CKEditorToolbarChanged.ckeditorAdmin',
- (event, action, button) => {
- const feature = getFeatureForButton(button);
-
- // Early-return if the button being added is a divider.
- if (feature === false) {
- return;
- }
-
- // Trigger a standardized text editor configuration event to indicate
- // whether a feature was added or removed, so that filters can react.
- const configEvent =
- action === 'added' ? 'addedFeature' : 'removedFeature';
- Drupal.editorConfiguration[configEvent](feature);
- },
- )
- // Listen for CKEditor plugin settings changes. When a plugin setting is
- // changed, rebuild the CKEditor features metadata.
- .on(
- 'CKEditorPluginSettingsChanged.ckeditorAdmin',
- (event, settingsChanges) => {
- // Update hidden CKEditor configuration.
- Object.keys(settingsChanges || {}).forEach((key) => {
- hiddenEditorConfig[key] = settingsChanges[key];
- });
-
- // Retrieve features for the updated hidden CKEditor configuration.
- getCKEditorFeatures(hiddenEditorConfig, (features) => {
- // Trigger a standardized text editor configuration event for each
- // feature that was modified by the configuration changes.
- const featuresMetadata = view.model.get('featuresMetadata');
- Object.keys(features || {}).forEach((name) => {
- const feature = features[name];
- if (
- featuresMetadata.hasOwnProperty(name) &&
- !_.isEqual(featuresMetadata[name], feature)
- ) {
- Drupal.editorConfiguration.modifiedFeature(feature);
- }
- });
- // Update the CKEditor features metadata.
- view.model.set('featuresMetadata', features);
- });
- },
- );
- },
-
- /**
- * Returns the list of buttons from an editor configuration.
- *
- * @param {object} config
- * A CKEditor configuration object.
- *
- * @return {Array}
- * A list of buttons in the CKEditor configuration.
- */
- getButtonList(config) {
- const buttons = [];
- // Remove the rows.
- config = _.flatten(config);
-
- // Loop through the button groups and pull out the buttons.
- config.forEach((group) => {
- group.items.forEach((button) => {
- buttons.push(button);
- });
- });
-
- // Remove the dividing elements if any.
- return _.without(buttons, '-');
- },
- },
- );
-})(jQuery, Drupal, Backbone, CKEDITOR, _);
diff --git a/core/modules/ckeditor/js/views/ControllerView.js b/core/modules/ckeditor/js/views/ControllerView.js
deleted file mode 100644
index d2f5cfb8b026..000000000000
--- a/core/modules/ckeditor/js/views/ControllerView.js
+++ /dev/null
@@ -1,236 +0,0 @@
-/**
-* DO NOT EDIT THIS FILE.
-* See the following change record for more information,
-* https://www.drupal.org/node/2815083
-* @preserve
-**/
-
-(function ($, Drupal, Backbone, CKEDITOR, _) {
- Drupal.ckeditor.ControllerView = Backbone.View.extend({
- events: {},
-
- initialize() {
- this.getCKEditorFeatures(this.model.get('hiddenEditorConfig'), this.disableFeaturesDisallowedByFilters.bind(this));
- this.model.listenTo(this.model, 'change:activeEditorConfig', this.model.sync);
- this.listenTo(this.model, 'change:isDirty', this.parseEditorDOM);
- },
-
- parseEditorDOM(model, isDirty, options) {
- if (isDirty) {
- const currentConfig = this.model.get('activeEditorConfig');
- const rows = [];
- this.$el.find('.ckeditor-active-toolbar-configuration').children('.ckeditor-row').each(function () {
- const groups = [];
- $(this).find('.ckeditor-toolbar-group').each(function () {
- const $group = $(this);
- const $buttons = $group.find('.ckeditor-button');
-
- if ($buttons.length) {
- const group = {
- name: $group.attr('data-drupal-ckeditor-toolbar-group-name'),
- items: []
- };
- $group.find('.ckeditor-button, .ckeditor-multiple-button').each(function () {
- group.items.push($(this).attr('data-drupal-ckeditor-button-name'));
- });
- groups.push(group);
- }
- });
-
- if (groups.length) {
- rows.push(groups);
- }
- });
- this.model.set('activeEditorConfig', rows);
- this.model.set('isDirty', false);
-
- if (options.broadcast !== false) {
- const prev = this.getButtonList(currentConfig);
- const next = this.getButtonList(rows);
-
- if (prev.length !== next.length) {
- this.$el.find('.ckeditor-toolbar-active').trigger('CKEditorToolbarChanged', [prev.length < next.length ? 'added' : 'removed', _.difference(_.union(prev, next), _.intersection(prev, next))[0]]);
- }
- }
- }
- },
-
- getCKEditorFeatures(CKEditorConfig, callback) {
- const getProperties = function (CKEPropertiesList) {
- return _.isObject(CKEPropertiesList) ? _.keys(CKEPropertiesList) : [];
- };
-
- const convertCKERulesToEditorFeature = function (feature, CKEFeatureRules) {
- for (let i = 0; i < CKEFeatureRules.length; i++) {
- const CKERule = CKEFeatureRules[i];
- const rule = new Drupal.EditorFeatureHTMLRule();
- const tags = getProperties(CKERule.elements);
- rule.required.tags = CKERule.propertiesOnly ? [] : tags;
- rule.allowed.tags = tags;
- rule.required.attributes = getProperties(CKERule.requiredAttributes);
- rule.allowed.attributes = getProperties(CKERule.attributes);
- rule.required.styles = getProperties(CKERule.requiredStyles);
- rule.allowed.styles = getProperties(CKERule.styles);
- rule.required.classes = getProperties(CKERule.requiredClasses);
- rule.allowed.classes = getProperties(CKERule.classes);
- rule.raw = CKERule;
- feature.addHTMLRule(rule);
- }
- };
-
- const hiddenCKEditorID = 'ckeditor-hidden';
-
- if (CKEDITOR.instances[hiddenCKEditorID]) {
- CKEDITOR.instances[hiddenCKEditorID].destroy(true);
- }
-
- const hiddenEditorConfig = this.model.get('hiddenEditorConfig');
-
- if (hiddenEditorConfig.drupalExternalPlugins) {
- const externalPlugins = hiddenEditorConfig.drupalExternalPlugins;
- Object.keys(externalPlugins || {}).forEach(pluginName => {
- CKEDITOR.plugins.addExternal(pluginName, externalPlugins[pluginName], '');
- });
- }
-
- CKEDITOR.inline($(`#${hiddenCKEditorID}`).get(0), CKEditorConfig);
- CKEDITOR.once('instanceReady', e => {
- if (e.editor.name === hiddenCKEditorID) {
- const CKEFeatureRulesMap = {};
- const rules = e.editor.filter.allowedContent;
- let rule;
- let name;
-
- for (let i = 0; i < rules.length; i++) {
- rule = rules[i];
- name = rule.featureName || ':(';
-
- if (!CKEFeatureRulesMap[name]) {
- CKEFeatureRulesMap[name] = [];
- }
-
- CKEFeatureRulesMap[name].push(rule);
- }
-
- const features = {};
- const buttonsToFeatures = {};
- Object.keys(CKEFeatureRulesMap).forEach(featureName => {
- const feature = new Drupal.EditorFeature(featureName);
- convertCKERulesToEditorFeature(feature, CKEFeatureRulesMap[featureName]);
- features[featureName] = feature;
- const command = e.editor.getCommand(featureName);
-
- if (command) {
- buttonsToFeatures[command.uiItems[0].name] = featureName;
- }
- });
- callback(features, buttonsToFeatures);
- }
- });
- },
-
- getFeatureForButton(button) {
- if (button === '-') {
- return false;
- }
-
- let featureName = this.model.get('buttonsToFeatures')[button.toLowerCase()];
-
- if (!featureName) {
- featureName = button.toLowerCase();
- }
-
- const featuresMetadata = this.model.get('featuresMetadata');
-
- if (!featuresMetadata[featureName]) {
- featuresMetadata[featureName] = new Drupal.EditorFeature(featureName);
- this.model.set('featuresMetadata', featuresMetadata);
- }
-
- return featuresMetadata[featureName];
- },
-
- disableFeaturesDisallowedByFilters(features, buttonsToFeatures) {
- this.model.set('featuresMetadata', features);
- this.model.set('buttonsToFeatures', buttonsToFeatures);
- this.broadcastConfigurationChanges(this.$el);
- let existingButtons = [];
-
- const buttonGroups = _.flatten(this.model.get('activeEditorConfig'));
-
- for (let i = 0; i < buttonGroups.length; i++) {
- const buttons = buttonGroups[i].items;
-
- for (let k = 0; k < buttons.length; k++) {
- existingButtons.push(buttons[k]);
- }
- }
-
- existingButtons = _.unique(existingButtons);
-
- for (let n = 0; n < existingButtons.length; n++) {
- const button = existingButtons[n];
- const feature = this.getFeatureForButton(button);
-
- if (feature === false) {
- continue;
- }
-
- if (Drupal.editorConfiguration.featureIsAllowedByFilters(feature)) {
- this.$el.find('.ckeditor-toolbar-active').trigger('CKEditorToolbarChanged', ['added', existingButtons[n]]);
- } else {
- $(`.ckeditor-toolbar-active li[data-drupal-ckeditor-button-name="${button}"]`).detach().appendTo('.ckeditor-toolbar-disabled > .ckeditor-toolbar-available > ul');
- this.model.set({
- isDirty: true
- }, {
- broadcast: false
- });
- }
- }
- },
-
- broadcastConfigurationChanges($ckeditorToolbar) {
- const view = this;
- const hiddenEditorConfig = this.model.get('hiddenEditorConfig');
- const getFeatureForButton = this.getFeatureForButton.bind(this);
- const getCKEditorFeatures = this.getCKEditorFeatures.bind(this);
- $ckeditorToolbar.find('.ckeditor-toolbar-active').on('CKEditorToolbarChanged.ckeditorAdmin', (event, action, button) => {
- const feature = getFeatureForButton(button);
-
- if (feature === false) {
- return;
- }
-
- const configEvent = action === 'added' ? 'addedFeature' : 'removedFeature';
- Drupal.editorConfiguration[configEvent](feature);
- }).on('CKEditorPluginSettingsChanged.ckeditorAdmin', (event, settingsChanges) => {
- Object.keys(settingsChanges || {}).forEach(key => {
- hiddenEditorConfig[key] = settingsChanges[key];
- });
- getCKEditorFeatures(hiddenEditorConfig, features => {
- const featuresMetadata = view.model.get('featuresMetadata');
- Object.keys(features || {}).forEach(name => {
- const feature = features[name];
-
- if (featuresMetadata.hasOwnProperty(name) && !_.isEqual(featuresMetadata[name], feature)) {
- Drupal.editorConfiguration.modifiedFeature(feature);
- }
- });
- view.model.set('featuresMetadata', features);
- });
- });
- },
-
- getButtonList(config) {
- const buttons = [];
- config = _.flatten(config);
- config.forEach(group => {
- group.items.forEach(button => {
- buttons.push(button);
- });
- });
- return _.without(buttons, '-');
- }
-
- });
-})(jQuery, Drupal, Backbone, CKEDITOR, _); \ No newline at end of file
diff --git a/core/modules/ckeditor/js/views/KeyboardView.es6.js b/core/modules/ckeditor/js/views/KeyboardView.es6.js
deleted file mode 100644
index 764889b66ce1..000000000000
--- a/core/modules/ckeditor/js/views/KeyboardView.es6.js
+++ /dev/null
@@ -1,307 +0,0 @@
-/**
- * @file
- * Backbone View providing the aural view of CKEditor keyboard UX configuration.
- */
-
-(function ($, Drupal, Backbone, _) {
- Drupal.ckeditor.KeyboardView = Backbone.View.extend(
- /** @lends Drupal.ckeditor.KeyboardView# */ {
- /**
- * Backbone View for CKEditor toolbar configuration; keyboard UX.
- *
- * @constructs
- *
- * @augments Backbone.View
- */
- initialize() {
- // Add keyboard arrow support.
- this.$el.on(
- 'keydown.ckeditor',
- '.ckeditor-buttons a, .ckeditor-multiple-buttons a',
- this.onPressButton.bind(this),
- );
- this.$el.on(
- 'keydown.ckeditor',
- '[data-drupal-ckeditor-type="group"]',
- this.onPressGroup.bind(this),
- );
- },
-
- /**
- * {@inheritdoc}
- */
- render() {},
-
- /**
- * Handles keypresses on a CKEditor configuration button.
- *
- * @param {jQuery.Event} event
- * The keypress event triggered.
- */
- onPressButton(event) {
- const upDownKeys = [
- 38, // Up arrow.
- 63232, // Safari up arrow.
- 40, // Down arrow.
- 63233, // Safari down arrow.
- ];
- const leftRightKeys = [
- 37, // Left arrow.
- 63234, // Safari left arrow.
- 39, // Right arrow.
- 63235, // Safari right arrow.
- ];
-
- // Respond to an enter key press. Prevent the bubbling of the enter key
- // press to the button group parent element.
- if (event.keyCode === 13) {
- event.stopPropagation();
- }
-
- // Only take action when a direction key is pressed.
- if (_.indexOf(_.union(upDownKeys, leftRightKeys), event.keyCode) > -1) {
- let view = this;
- let $target = $(event.currentTarget);
- let $button = $target.parent();
- const $container = $button.parent();
- let $group = $button.closest('.ckeditor-toolbar-group');
- let $row;
- const containerType = $container.data(
- 'drupal-ckeditor-button-sorting',
- );
- const $availableButtons = this.$el.find(
- '[data-drupal-ckeditor-button-sorting="source"]',
- );
- const $activeButtons = this.$el.find('.ckeditor-toolbar-active');
- // The current location of the button, just in case it needs to be put
- // back.
- const $originalGroup = $group;
- let dir;
-
- // Move available buttons between their container and the active
- // toolbar.
- if (containerType === 'source') {
- // Move the button to the active toolbar configuration when the down
- // or up keys are pressed.
- if (_.indexOf([40, 63233], event.keyCode) > -1) {
- // Move the button to the first row, first button group index
- // position.
- $activeButtons
- .find('.ckeditor-toolbar-group-buttons')
- .eq(0)
- .prepend($button);
- }
- } else if (containerType === 'target') {
- // Move buttons between sibling buttons in a group and between groups.
- if (_.indexOf(leftRightKeys, event.keyCode) > -1) {
- // Move left.
- const $siblings = $container.children();
- const index = $siblings.index($button);
- if (_.indexOf([37, 63234], event.keyCode) > -1) {
- // Move between sibling buttons.
- if (index > 0) {
- $button.insertBefore($container.children().eq(index - 1));
- }
- // Move between button groups and rows.
- else {
- // Move between button groups.
- $group = $container.parent().prev();
- if ($group.length > 0) {
- $group
- .find('.ckeditor-toolbar-group-buttons')
- .append($button);
- }
- // Wrap between rows.
- else {
- $container
- .closest('.ckeditor-row')
- .prev()
- .find('.ckeditor-toolbar-group')
- .not('.placeholder')
- .find('.ckeditor-toolbar-group-buttons')
- .eq(-1)
- .append($button);
- }
- }
- }
- // Move right.
- else if (_.indexOf([39, 63235], event.keyCode) > -1) {
- // Move between sibling buttons.
- if (index < $siblings.length - 1) {
- $button.insertAfter($container.children().eq(index + 1));
- }
- // Move between button groups. Moving right at the end of a row
- // will create a new group.
- else {
- $container
- .parent()
- .next()
- .find('.ckeditor-toolbar-group-buttons')
- .prepend($button);
- }
- }
- }
- // Move buttons between rows and the available button set.
- else if (_.indexOf(upDownKeys, event.keyCode) > -1) {
- dir =
- _.indexOf([38, 63232], event.keyCode) > -1 ? 'prev' : 'next';
- $row = $container.closest('.ckeditor-row')[dir]();
- // Move the button back into the available button set.
- if (dir === 'prev' && $row.length === 0) {
- // If this is a divider, just destroy it.
- if ($button.data('drupal-ckeditor-type') === 'separator') {
- $button.off().remove();
- // Focus on the first button in the active toolbar.
- $activeButtons
- .find('.ckeditor-toolbar-group-buttons')
- .eq(0)
- .children()
- .eq(0)
- .children()
- .trigger('focus');
- }
- // Otherwise, move it.
- else {
- $availableButtons.prepend($button);
- }
- } else {
- $row
- .find('.ckeditor-toolbar-group-buttons')
- .eq(0)
- .prepend($button);
- }
- }
- }
- // Move dividers between their container and the active toolbar.
- else if (containerType === 'dividers') {
- // Move the button to the active toolbar configuration when the down
- // or up keys are pressed.
- if (_.indexOf([40, 63233], event.keyCode) > -1) {
- // Move the button to the first row, first button group index
- // position.
- $button = $button.clone(true);
- $activeButtons
- .find('.ckeditor-toolbar-group-buttons')
- .eq(0)
- .prepend($button);
- $target = $button.children();
- }
- }
-
- view = this;
- // Attempt to move the button to the new toolbar position.
- Drupal.ckeditor.registerButtonMove(this, $button, (result) => {
- // Put the button back if the registration failed.
- // If the button was in a row, then it was in the active toolbar
- // configuration. The button was probably placed in a new group, but
- // that action was canceled.
- if (!result && $originalGroup) {
- $originalGroup.find('.ckeditor-buttons').append($button);
- }
- // Refocus the target button so that the user can continue from a
- // known place.
- $target.trigger('focus');
- });
-
- event.preventDefault();
- event.stopPropagation();
- }
- },
-
- /**
- * Handles keypresses on a CKEditor configuration group.
- *
- * @param {jQuery.Event} event
- * The keypress event triggered.
- */
- onPressGroup(event) {
- const upDownKeys = [
- 38, // Up arrow.
- 63232, // Safari up arrow.
- 40, // Down arrow.
- 63233, // Safari down arrow.
- ];
- const leftRightKeys = [
- 37, // Left arrow.
- 63234, // Safari left arrow.
- 39, // Right arrow.
- 63235, // Safari right arrow.
- ];
-
- // Respond to an enter key press.
- if (event.keyCode === 13) {
- const view = this;
- // Open the group renaming dialog in the next evaluation cycle so that
- // this event can be cancelled and the bubbling wiped out. Otherwise,
- // Firefox has issues because the page focus is shifted to the dialog
- // along with the keydown event.
- window.setTimeout(() => {
- Drupal.ckeditor.openGroupNameDialog(view, $(event.currentTarget));
- }, 0);
- event.preventDefault();
- event.stopPropagation();
- }
-
- // Respond to direction key presses.
- if (_.indexOf(_.union(upDownKeys, leftRightKeys), event.keyCode) > -1) {
- const $group = $(event.currentTarget);
- const $container = $group.parent();
- const $siblings = $container.children();
- let index;
- let dir;
- // Move groups between sibling groups.
- if (_.indexOf(leftRightKeys, event.keyCode) > -1) {
- index = $siblings.index($group);
- // Move left between sibling groups.
- if (_.indexOf([37, 63234], event.keyCode) > -1) {
- if (index > 0) {
- $group.insertBefore($siblings.eq(index - 1));
- }
- // Wrap between rows. Insert the group before the placeholder group
- // at the end of the previous row.
- else {
- const $rowChildElement = $container
- .closest('.ckeditor-row')
- .prev()
- .find('.ckeditor-toolbar-groups')
- .children()
- .eq(-1);
- $group.insertBefore($rowChildElement);
- }
- }
- // Move right between sibling groups.
- else if (_.indexOf([39, 63235], event.keyCode) > -1) {
- // Move to the right if the next group is not a placeholder.
- if (!$siblings.eq(index + 1).hasClass('placeholder')) {
- $group.insertAfter($container.children().eq(index + 1));
- }
- // Wrap group between rows.
- else {
- $container
- .closest('.ckeditor-row')
- .next()
- .find('.ckeditor-toolbar-groups')
- .prepend($group);
- }
- }
- }
- // Move groups between rows.
- else if (_.indexOf(upDownKeys, event.keyCode) > -1) {
- dir = _.indexOf([38, 63232], event.keyCode) > -1 ? 'prev' : 'next';
- $group
- .closest('.ckeditor-row')
- [dir]()
- .find('.ckeditor-toolbar-groups')
- .eq(0)
- .prepend($group);
- }
-
- Drupal.ckeditor.registerGroupMove(this, $group);
- $group.trigger('focus');
- event.preventDefault();
- event.stopPropagation();
- }
- },
- },
- );
-})(jQuery, Drupal, Backbone, _);
diff --git a/core/modules/ckeditor/js/views/KeyboardView.js b/core/modules/ckeditor/js/views/KeyboardView.js
deleted file mode 100644
index 76f0a98d4f10..000000000000
--- a/core/modules/ckeditor/js/views/KeyboardView.js
+++ /dev/null
@@ -1,152 +0,0 @@
-/**
-* DO NOT EDIT THIS FILE.
-* See the following change record for more information,
-* https://www.drupal.org/node/2815083
-* @preserve
-**/
-
-(function ($, Drupal, Backbone, _) {
- Drupal.ckeditor.KeyboardView = Backbone.View.extend({
- initialize() {
- this.$el.on('keydown.ckeditor', '.ckeditor-buttons a, .ckeditor-multiple-buttons a', this.onPressButton.bind(this));
- this.$el.on('keydown.ckeditor', '[data-drupal-ckeditor-type="group"]', this.onPressGroup.bind(this));
- },
-
- render() {},
-
- onPressButton(event) {
- const upDownKeys = [38, 63232, 40, 63233];
- const leftRightKeys = [37, 63234, 39, 63235];
-
- if (event.keyCode === 13) {
- event.stopPropagation();
- }
-
- if (_.indexOf(_.union(upDownKeys, leftRightKeys), event.keyCode) > -1) {
- let view = this;
- let $target = $(event.currentTarget);
- let $button = $target.parent();
- const $container = $button.parent();
- let $group = $button.closest('.ckeditor-toolbar-group');
- let $row;
- const containerType = $container.data('drupal-ckeditor-button-sorting');
- const $availableButtons = this.$el.find('[data-drupal-ckeditor-button-sorting="source"]');
- const $activeButtons = this.$el.find('.ckeditor-toolbar-active');
- const $originalGroup = $group;
- let dir;
-
- if (containerType === 'source') {
- if (_.indexOf([40, 63233], event.keyCode) > -1) {
- $activeButtons.find('.ckeditor-toolbar-group-buttons').eq(0).prepend($button);
- }
- } else if (containerType === 'target') {
- if (_.indexOf(leftRightKeys, event.keyCode) > -1) {
- const $siblings = $container.children();
- const index = $siblings.index($button);
-
- if (_.indexOf([37, 63234], event.keyCode) > -1) {
- if (index > 0) {
- $button.insertBefore($container.children().eq(index - 1));
- } else {
- $group = $container.parent().prev();
-
- if ($group.length > 0) {
- $group.find('.ckeditor-toolbar-group-buttons').append($button);
- } else {
- $container.closest('.ckeditor-row').prev().find('.ckeditor-toolbar-group').not('.placeholder').find('.ckeditor-toolbar-group-buttons').eq(-1).append($button);
- }
- }
- } else if (_.indexOf([39, 63235], event.keyCode) > -1) {
- if (index < $siblings.length - 1) {
- $button.insertAfter($container.children().eq(index + 1));
- } else {
- $container.parent().next().find('.ckeditor-toolbar-group-buttons').prepend($button);
- }
- }
- } else if (_.indexOf(upDownKeys, event.keyCode) > -1) {
- dir = _.indexOf([38, 63232], event.keyCode) > -1 ? 'prev' : 'next';
- $row = $container.closest('.ckeditor-row')[dir]();
-
- if (dir === 'prev' && $row.length === 0) {
- if ($button.data('drupal-ckeditor-type') === 'separator') {
- $button.off().remove();
- $activeButtons.find('.ckeditor-toolbar-group-buttons').eq(0).children().eq(0).children().trigger('focus');
- } else {
- $availableButtons.prepend($button);
- }
- } else {
- $row.find('.ckeditor-toolbar-group-buttons').eq(0).prepend($button);
- }
- }
- } else if (containerType === 'dividers') {
- if (_.indexOf([40, 63233], event.keyCode) > -1) {
- $button = $button.clone(true);
- $activeButtons.find('.ckeditor-toolbar-group-buttons').eq(0).prepend($button);
- $target = $button.children();
- }
- }
-
- view = this;
- Drupal.ckeditor.registerButtonMove(this, $button, result => {
- if (!result && $originalGroup) {
- $originalGroup.find('.ckeditor-buttons').append($button);
- }
-
- $target.trigger('focus');
- });
- event.preventDefault();
- event.stopPropagation();
- }
- },
-
- onPressGroup(event) {
- const upDownKeys = [38, 63232, 40, 63233];
- const leftRightKeys = [37, 63234, 39, 63235];
-
- if (event.keyCode === 13) {
- const view = this;
- window.setTimeout(() => {
- Drupal.ckeditor.openGroupNameDialog(view, $(event.currentTarget));
- }, 0);
- event.preventDefault();
- event.stopPropagation();
- }
-
- if (_.indexOf(_.union(upDownKeys, leftRightKeys), event.keyCode) > -1) {
- const $group = $(event.currentTarget);
- const $container = $group.parent();
- const $siblings = $container.children();
- let index;
- let dir;
-
- if (_.indexOf(leftRightKeys, event.keyCode) > -1) {
- index = $siblings.index($group);
-
- if (_.indexOf([37, 63234], event.keyCode) > -1) {
- if (index > 0) {
- $group.insertBefore($siblings.eq(index - 1));
- } else {
- const $rowChildElement = $container.closest('.ckeditor-row').prev().find('.ckeditor-toolbar-groups').children().eq(-1);
- $group.insertBefore($rowChildElement);
- }
- } else if (_.indexOf([39, 63235], event.keyCode) > -1) {
- if (!$siblings.eq(index + 1).hasClass('placeholder')) {
- $group.insertAfter($container.children().eq(index + 1));
- } else {
- $container.closest('.ckeditor-row').next().find('.ckeditor-toolbar-groups').prepend($group);
- }
- }
- } else if (_.indexOf(upDownKeys, event.keyCode) > -1) {
- dir = _.indexOf([38, 63232], event.keyCode) > -1 ? 'prev' : 'next';
- $group.closest('.ckeditor-row')[dir]().find('.ckeditor-toolbar-groups').eq(0).prepend($group);
- }
-
- Drupal.ckeditor.registerGroupMove(this, $group);
- $group.trigger('focus');
- event.preventDefault();
- event.stopPropagation();
- }
- }
-
- });
-})(jQuery, Drupal, Backbone, _); \ No newline at end of file
diff --git a/core/modules/ckeditor/js/views/VisualView.es6.js b/core/modules/ckeditor/js/views/VisualView.es6.js
deleted file mode 100644
index b0095d9c844d..000000000000
--- a/core/modules/ckeditor/js/views/VisualView.es6.js
+++ /dev/null
@@ -1,302 +0,0 @@
-/**
- * @file
- * A Backbone View that provides the visual UX view of CKEditor toolbar
- * configuration.
- */
-
-(function (Drupal, Backbone, $, Sortable) {
- Drupal.ckeditor.VisualView = Backbone.View.extend(
- /** @lends Drupal.ckeditor.VisualView# */ {
- events: {
- 'click .ckeditor-toolbar-group-name': 'onGroupNameClick',
- 'click .ckeditor-groupnames-toggle': 'onGroupNamesToggleClick',
- 'click .ckeditor-add-new-group button': 'onAddGroupButtonClick',
- },
-
- /**
- * Backbone View for CKEditor toolbar configuration; visual UX.
- *
- * @constructs
- *
- * @augments Backbone.View
- */
- initialize() {
- this.listenTo(
- this.model,
- 'change:isDirty change:groupNamesVisible',
- this.render,
- );
-
- // Add a toggle for the button group names.
- $(Drupal.theme('ckeditorButtonGroupNamesToggle')).prependTo(
- this.$el.find('#ckeditor-active-toolbar').parent(),
- );
-
- this.render();
- },
-
- /**
- * Render function for rendering the toolbar configuration.
- *
- * @param {*} model
- * Model used for the view.
- * @param {string} [value]
- * The value that was changed.
- * @param {object} changedAttributes
- * The attributes that was changed.
- *
- * @return {Drupal.ckeditor.VisualView}
- * The {@link Drupal.ckeditor.VisualView} object.
- */
- render(model, value, changedAttributes) {
- this.insertPlaceholders();
- this.applySorting();
-
- // Toggle button group names.
- let groupNamesVisible = this.model.get('groupNamesVisible');
- // If a button was just placed in the active toolbar, ensure that the
- // button group names are visible.
- if (
- changedAttributes &&
- changedAttributes.changes &&
- changedAttributes.changes.isDirty
- ) {
- this.model.set({ groupNamesVisible: true }, { silent: true });
- groupNamesVisible = true;
- }
- this.$el
- .find('[data-toolbar="active"]')
- .toggleClass('ckeditor-group-names-are-visible', groupNamesVisible);
- const $toggle = this.$el.find('.ckeditor-groupnames-toggle');
- $toggle
- .each((index, element) => {
- element.textContent = groupNamesVisible
- ? Drupal.t('Hide group names')
- : Drupal.t('Show group names');
- })
- .attr('aria-pressed', groupNamesVisible);
- return this;
- },
-
- /**
- * Handles clicks to a button group name.
- *
- * @param {jQuery.Event} event
- * The click event on the button group.
- */
- onGroupNameClick(event) {
- const $group = $(event.currentTarget).closest(
- '.ckeditor-toolbar-group',
- );
- Drupal.ckeditor.openGroupNameDialog(this, $group);
-
- event.stopPropagation();
- event.preventDefault();
- },
-
- /**
- * Handles clicks on the button group names toggle button.
- *
- * @param {jQuery.Event} event
- * The click event on the toggle button.
- */
- onGroupNamesToggleClick(event) {
- this.model.set(
- 'groupNamesVisible',
- !this.model.get('groupNamesVisible'),
- );
- event.preventDefault();
- },
-
- /**
- * Prompts the user to provide a name for a new button group; inserts it.
- *
- * @param {jQuery.Event} event
- * The event of the button click.
- */
- onAddGroupButtonClick(event) {
- /**
- * Inserts a new button if the openGroupNameDialog function returns true.
- *
- * @param {bool} success
- * A flag that indicates if the user created a new group (true) or
- * canceled out of the dialog (false).
- * @param {jQuery} $group
- * A jQuery DOM fragment that represents the new button group. It has
- * not been added to the DOM yet.
- */
- function insertNewGroup(success, $group) {
- if (success) {
- $group.appendTo(
- $(event.currentTarget)
- .closest('.ckeditor-row')
- .children('.ckeditor-toolbar-groups'),
- );
- // Focus on the new group.
- $group.trigger('focus');
- }
- }
-
- // Pass in a DOM fragment of a placeholder group so that the new group
- // name can be applied to it.
- Drupal.ckeditor.openGroupNameDialog(
- this,
- $(Drupal.theme('ckeditorToolbarGroup')),
- insertNewGroup,
- );
-
- event.preventDefault();
- },
-
- /**
- * Handles Sortable stop sort of a button group.
- *
- * @param {CustomEvent} event
- * The event triggered on the group drag.
- */
- endGroupDrag(event) {
- const $item = $(event.item);
- Drupal.ckeditor.registerGroupMove(this, $item);
- },
-
- /**
- * Handles Sortable start sort of a button.
- *
- * @param {CustomEvent} event
- * The event triggered on the button drag.
- */
- startButtonDrag(event) {
- this.$el.find('a:focus').trigger('blur');
-
- // Show the button group names as soon as the user starts dragging.
- this.model.set('groupNamesVisible', true);
- },
-
- /**
- * Handles Sortable stop sort of a button.
- *
- * @param {CustomEvent} event
- * The event triggered on the button drag.
- */
- endButtonDrag(event) {
- const $item = $(event.item);
-
- Drupal.ckeditor.registerButtonMove(this, $item, (success) => {
- // Refocus the target button so that the user can continue
- // from a known place.
- $item.find('a').trigger('focus');
- });
- },
-
- /**
- * Invokes Sortable() on new buttons and groups in a CKEditor config.
- * Array.prototype.forEach is used here because of the lack of support for
- * NodeList.forEach in older browsers.
- */
- applySorting() {
- // Make the buttons sortable.
- Array.prototype.forEach.call(
- this.el.querySelectorAll('.ckeditor-buttons:not(.js-sortable)'),
- (buttons) => {
- buttons.classList.add('js-sortable');
- Sortable.create(buttons, {
- ghostClass: 'ckeditor-button-placeholder',
- group: 'ckeditor-buttons',
- onStart: this.startButtonDrag.bind(this),
- onEnd: this.endButtonDrag.bind(this),
- });
- },
- );
-
- Array.prototype.forEach.call(
- this.el.querySelectorAll(
- '.ckeditor-toolbar-groups:not(.js-sortable)',
- ),
- (buttons) => {
- buttons.classList.add('js-sortable');
- Sortable.create(buttons, {
- ghostClass: 'ckeditor-toolbar-group-placeholder',
- onEnd: this.endGroupDrag.bind(this),
- });
- },
- );
-
- Array.prototype.forEach.call(
- this.el.querySelectorAll(
- '.ckeditor-multiple-buttons:not(.js-sortable)',
- ),
- (buttons) => {
- buttons.classList.add('js-sortable');
- Sortable.create(buttons, {
- group: {
- name: 'ckeditor-buttons',
- pull: 'clone',
- },
- onEnd: this.endButtonDrag.bind(this),
- });
- },
- );
- },
-
- /**
- * Wraps the invocation of methods to insert blank groups and rows.
- */
- insertPlaceholders() {
- this.insertPlaceholderRow();
- this.insertNewGroupButtons();
- },
-
- /**
- * Inserts a blank row at the bottom of the CKEditor configuration.
- */
- insertPlaceholderRow() {
- let $rows = this.$el.find('.ckeditor-row');
- // Add a placeholder row. to the end of the list if one does not exist.
- if (!$rows.eq(-1).hasClass('placeholder')) {
- this.$el
- .find('.ckeditor-toolbar-active')
- .children('.ckeditor-active-toolbar-configuration')
- .append(Drupal.theme('ckeditorRow'));
- }
- // Update the $rows variable to include the new row.
- $rows = this.$el.find('.ckeditor-row');
- // Remove blank rows except the last one.
- const len = $rows.length;
- $rows
- .filter((index, row) => {
- // Do not remove the last row.
- if (index + 1 === len) {
- return false;
- }
- return (
- $(row).find('.ckeditor-toolbar-group').not('.placeholder')
- .length === 0
- );
- })
- // Then get all rows that are placeholders and remove them.
- .remove();
- },
-
- /**
- * Inserts a button in each row that will add a new CKEditor button group.
- */
- insertNewGroupButtons() {
- // Insert an add group button to each row.
- this.$el.find('.ckeditor-row').each(function () {
- const $row = $(this);
- const $groups = $row.find('.ckeditor-toolbar-group');
- const $button = $row.find('.ckeditor-add-new-group');
- if ($button.length === 0) {
- $row
- .children('.ckeditor-toolbar-groups')
- .append(Drupal.theme('ckeditorNewButtonGroup'));
- }
- // If a placeholder group exists, make sure it's at the end of the row.
- else if (!$groups.eq(-1).hasClass('ckeditor-add-new-group')) {
- $button.appendTo($row.children('.ckeditor-toolbar-groups'));
- }
- });
- },
- },
- );
-})(Drupal, Backbone, jQuery, Sortable);
diff --git a/core/modules/ckeditor/js/views/VisualView.js b/core/modules/ckeditor/js/views/VisualView.js
deleted file mode 100644
index 9040038ccf84..000000000000
--- a/core/modules/ckeditor/js/views/VisualView.js
+++ /dev/null
@@ -1,152 +0,0 @@
-/**
-* DO NOT EDIT THIS FILE.
-* See the following change record for more information,
-* https://www.drupal.org/node/2815083
-* @preserve
-**/
-
-(function (Drupal, Backbone, $, Sortable) {
- Drupal.ckeditor.VisualView = Backbone.View.extend({
- events: {
- 'click .ckeditor-toolbar-group-name': 'onGroupNameClick',
- 'click .ckeditor-groupnames-toggle': 'onGroupNamesToggleClick',
- 'click .ckeditor-add-new-group button': 'onAddGroupButtonClick'
- },
-
- initialize() {
- this.listenTo(this.model, 'change:isDirty change:groupNamesVisible', this.render);
- $(Drupal.theme('ckeditorButtonGroupNamesToggle')).prependTo(this.$el.find('#ckeditor-active-toolbar').parent());
- this.render();
- },
-
- render(model, value, changedAttributes) {
- this.insertPlaceholders();
- this.applySorting();
- let groupNamesVisible = this.model.get('groupNamesVisible');
-
- if (changedAttributes && changedAttributes.changes && changedAttributes.changes.isDirty) {
- this.model.set({
- groupNamesVisible: true
- }, {
- silent: true
- });
- groupNamesVisible = true;
- }
-
- this.$el.find('[data-toolbar="active"]').toggleClass('ckeditor-group-names-are-visible', groupNamesVisible);
- const $toggle = this.$el.find('.ckeditor-groupnames-toggle');
- $toggle.each((index, element) => {
- element.textContent = groupNamesVisible ? Drupal.t('Hide group names') : Drupal.t('Show group names');
- }).attr('aria-pressed', groupNamesVisible);
- return this;
- },
-
- onGroupNameClick(event) {
- const $group = $(event.currentTarget).closest('.ckeditor-toolbar-group');
- Drupal.ckeditor.openGroupNameDialog(this, $group);
- event.stopPropagation();
- event.preventDefault();
- },
-
- onGroupNamesToggleClick(event) {
- this.model.set('groupNamesVisible', !this.model.get('groupNamesVisible'));
- event.preventDefault();
- },
-
- onAddGroupButtonClick(event) {
- function insertNewGroup(success, $group) {
- if (success) {
- $group.appendTo($(event.currentTarget).closest('.ckeditor-row').children('.ckeditor-toolbar-groups'));
- $group.trigger('focus');
- }
- }
-
- Drupal.ckeditor.openGroupNameDialog(this, $(Drupal.theme('ckeditorToolbarGroup')), insertNewGroup);
- event.preventDefault();
- },
-
- endGroupDrag(event) {
- const $item = $(event.item);
- Drupal.ckeditor.registerGroupMove(this, $item);
- },
-
- startButtonDrag(event) {
- this.$el.find('a:focus').trigger('blur');
- this.model.set('groupNamesVisible', true);
- },
-
- endButtonDrag(event) {
- const $item = $(event.item);
- Drupal.ckeditor.registerButtonMove(this, $item, success => {
- $item.find('a').trigger('focus');
- });
- },
-
- applySorting() {
- Array.prototype.forEach.call(this.el.querySelectorAll('.ckeditor-buttons:not(.js-sortable)'), buttons => {
- buttons.classList.add('js-sortable');
- Sortable.create(buttons, {
- ghostClass: 'ckeditor-button-placeholder',
- group: 'ckeditor-buttons',
- onStart: this.startButtonDrag.bind(this),
- onEnd: this.endButtonDrag.bind(this)
- });
- });
- Array.prototype.forEach.call(this.el.querySelectorAll('.ckeditor-toolbar-groups:not(.js-sortable)'), buttons => {
- buttons.classList.add('js-sortable');
- Sortable.create(buttons, {
- ghostClass: 'ckeditor-toolbar-group-placeholder',
- onEnd: this.endGroupDrag.bind(this)
- });
- });
- Array.prototype.forEach.call(this.el.querySelectorAll('.ckeditor-multiple-buttons:not(.js-sortable)'), buttons => {
- buttons.classList.add('js-sortable');
- Sortable.create(buttons, {
- group: {
- name: 'ckeditor-buttons',
- pull: 'clone'
- },
- onEnd: this.endButtonDrag.bind(this)
- });
- });
- },
-
- insertPlaceholders() {
- this.insertPlaceholderRow();
- this.insertNewGroupButtons();
- },
-
- insertPlaceholderRow() {
- let $rows = this.$el.find('.ckeditor-row');
-
- if (!$rows.eq(-1).hasClass('placeholder')) {
- this.$el.find('.ckeditor-toolbar-active').children('.ckeditor-active-toolbar-configuration').append(Drupal.theme('ckeditorRow'));
- }
-
- $rows = this.$el.find('.ckeditor-row');
- const len = $rows.length;
- $rows.filter((index, row) => {
- if (index + 1 === len) {
- return false;
- }
-
- return $(row).find('.ckeditor-toolbar-group').not('.placeholder').length === 0;
- }).remove();
- },
-
- insertNewGroupButtons() {
- this.$el.find('.ckeditor-row').each(function () {
- const $row = $(this);
- const $groups = $row.find('.ckeditor-toolbar-group');
- const $button = $row.find('.ckeditor-add-new-group');
-
- if ($button.length === 0) {
- $row.children('.ckeditor-toolbar-groups').append(Drupal.theme('ckeditorNewButtonGroup'));
- } else if (!$groups.eq(-1).hasClass('ckeditor-add-new-group')) {
- $button.appendTo($row.children('.ckeditor-toolbar-groups'));
- }
- });
- }
-
- });
-})(Drupal, Backbone, jQuery, Sortable); \ No newline at end of file
diff --git a/core/modules/ckeditor/src/Ajax/AddStyleSheetCommand.php b/core/modules/ckeditor/src/Ajax/AddStyleSheetCommand.php
deleted file mode 100644
index 2ae2ed9d9c7a..000000000000
--- a/core/modules/ckeditor/src/Ajax/AddStyleSheetCommand.php
+++ /dev/null
@@ -1,64 +0,0 @@
-<?php
-
-namespace Drupal\ckeditor\Ajax;
-
-use Drupal\Core\Ajax\CommandInterface;
-
-/**
- * AJAX command to add style sheets to a CKEditor instance.
- */
-class AddStyleSheetCommand implements CommandInterface {
-
- /**
- * The CKEditor instance ID.
- *
- * @var string
- */
- protected $editorId;
-
- /**
- * The style sheet URLs to add to the CKEditor instance.
- *
- * @var string[]
- */
- protected $styleSheets = [];
-
- /**
- * AddStyleSheetCommand constructor.
- *
- * @param string $editor_id
- * The CKEditor instance ID.
- * @param string[] $stylesheets
- * The style sheet URLs to add to the CKEditor instance.
- */
- public function __construct($editor_id, array $stylesheets = []) {
- $this->editorId = $editor_id;
- $this->styleSheets = $stylesheets;
- }
-
- /**
- * Adds a style sheet to the CKEditor instance.
- *
- * @param string $stylesheet
- * The style sheet URL.
- *
- * @return $this
- * The called object, for chaining.
- */
- public function addStyleSheet($stylesheet) {
- $this->styleSheets[] = $stylesheet;
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function render() {
- return [
- 'command' => 'ckeditor_add_stylesheet',
- 'editor_id' => $this->editorId,
- 'stylesheets' => $this->styleSheets,
- ];
- }
-
-}
diff --git a/core/modules/ckeditor/src/Annotation/CKEditorPlugin.php b/core/modules/ckeditor/src/Annotation/CKEditorPlugin.php
deleted file mode 100644
index 155d5dd49610..000000000000
--- a/core/modules/ckeditor/src/Annotation/CKEditorPlugin.php
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-namespace Drupal\ckeditor\Annotation;
-
-use Drupal\Component\Annotation\Plugin;
-
-/**
- * Defines a CKEditorPlugin annotation object.
- *
- * Plugin Namespace: Plugin\CKEditorPlugin
- *
- * For a working example, see \Drupal\ckeditor\Plugin\CKEditorPlugin\DrupalImage
- *
- * @see \Drupal\ckeditor\CKEditorPluginInterface
- * @see \Drupal\ckeditor\CKEditorPluginBase
- * @see \Drupal\ckeditor\CKEditorPluginManager
- * @see hook_ckeditor_plugin_info_alter()
- * @see plugin_api
- *
- * @Annotation
- */
-class CKEditorPlugin extends Plugin {
-
- /**
- * The plugin ID.
- *
- * This MUST match the name of the CKEditor plugin itself (written in
- * JavaScript). Otherwise CKEditor will throw JavaScript errors when it runs,
- * because it fails to load this CKEditor plugin.
- *
- * @var string
- */
- public $id;
-
- /**
- * The human-readable name of the CKEditor plugin.
- *
- * @ingroup plugin_translatable
- *
- * @var \Drupal\Core\Annotation\Translation
- */
- public $label;
-
-}
diff --git a/core/modules/ckeditor/src/CKEditorPluginBase.php b/core/modules/ckeditor/src/CKEditorPluginBase.php
deleted file mode 100644
index aa76200519cd..000000000000
--- a/core/modules/ckeditor/src/CKEditorPluginBase.php
+++ /dev/null
@@ -1,93 +0,0 @@
-<?php
-
-namespace Drupal\ckeditor;
-
-use Drupal\Core\Extension\ModuleExtensionList;
-use Drupal\Core\Plugin\PluginBase;
-use Drupal\editor\Entity\Editor;
-
-/**
- * Defines a base CKEditor plugin implementation.
- *
- * No other CKEditor plugins can be internal, unless a different CKEditor build
- * than the one provided by Drupal core is used. Most CKEditor plugins don't
- * need to provide additional settings forms.
- *
- * This base class assumes that your plugin has buttons that you want to be
- * enabled through the toolbar builder UI. It is still possible to also
- * implement the CKEditorPluginContextualInterface (for contextual enabling) and
- * CKEditorPluginConfigurableInterface interfaces (for configuring plugin
- * settings).
- *
- * NOTE: the Drupal plugin ID should correspond to the CKEditor plugin name.
- *
- * @see \Drupal\ckeditor\CKEditorPluginInterface
- * @see \Drupal\ckeditor\CKEditorPluginButtonsInterface
- * @see \Drupal\ckeditor\CKEditorPluginContextualInterface
- * @see \Drupal\ckeditor\CKEditorPluginConfigurableInterface
- * @see \Drupal\ckeditor\CKEditorPluginManager
- * @see \Drupal\ckeditor\Annotation\CKEditorPlugin
- * @see plugin_api
- */
-abstract class CKEditorPluginBase extends PluginBase implements CKEditorPluginInterface, CKEditorPluginButtonsInterface {
-
- /**
- * The module list service.
- *
- * @var \Drupal\Core\Extension\ModuleExtensionList
- */
- protected $moduleList;
-
- /**
- * Gets the module list service.
- *
- * @return \Drupal\Core\Extension\ModuleExtensionList
- * The module extension list service.
- */
- protected function getModuleList(): ModuleExtensionList {
- if (!$this->moduleList) {
- $this->moduleList = \Drupal::service('extension.list.module');
- }
- return $this->moduleList;
- }
-
- /**
- * Gets the Drupal-root relative installation directory of a module.
- *
- * @param string $module_name
- * The machine name of the module.
- *
- * @return string
- * The module installation directory.
- *
- * @throws \InvalidArgumentException
- * If there is no extension with the supplied machine name.
- *
- * @see \Drupal\Core\Extension\ExtensionList::getPath()
- */
- protected function getModulePath(string $module_name): string {
- return $this->getModuleList()->getPath($module_name);
- }
-
- /**
- * {@inheritdoc}
- */
- public function isInternal() {
- return FALSE;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getDependencies(Editor $editor) {
- return [];
- }
-
- /**
- * {@inheritdoc}
- */
- public function getLibraries(Editor $editor) {
- return [];
- }
-
-}
diff --git a/core/modules/ckeditor/src/CKEditorPluginButtonsInterface.php b/core/modules/ckeditor/src/CKEditorPluginButtonsInterface.php
deleted file mode 100644
index 4b2d1281e776..000000000000
--- a/core/modules/ckeditor/src/CKEditorPluginButtonsInterface.php
+++ /dev/null
@@ -1,58 +0,0 @@
-<?php
-
-namespace Drupal\ckeditor;
-
-/**
- * Defines an interface for CKEditor plugins with buttons.
- *
- * This allows a CKEditor plugin to define which buttons it provides, so that
- * users can configure a CKEditor toolbar instance via the toolbar builder UI.
- * If at least one button that this plugin provides is added to the toolbar via
- * the toolbar builder UI, then this plugin will be enabled automatically.
- *
- * If a CKEditor plugin implements this interface, it can still also implement
- * CKEditorPluginContextualInterface if it wants a button to conditionally be
- * added as well. The downside of conditionally adding buttons is that the user
- * cannot see these buttons in the toolbar builder UI.
- *
- * @see \Drupal\ckeditor\CKEditorPluginInterface
- * @see \Drupal\ckeditor\CKEditorPluginContextualInterface
- * @see \Drupal\ckeditor\CKEditorPluginConfigurableInterface
- * @see \Drupal\ckeditor\CKEditorPluginCssInterface
- * @see \Drupal\ckeditor\CKEditorPluginBase
- * @see \Drupal\ckeditor\CKEditorPluginManager
- * @see \Drupal\ckeditor\Annotation\CKEditorPlugin
- * @see plugin_api
- */
-interface CKEditorPluginButtonsInterface extends CKEditorPluginInterface {
-
- /**
- * Returns the buttons that this plugin provides, along with metadata.
- *
- * The metadata is used by the CKEditor module to generate a visual CKEditor
- * toolbar builder UI.
- *
- * @return array
- * An array of buttons that are provided by this plugin. This will
- * only be used in the administrative section for assembling the toolbar.
- * Each button should be keyed by its CKEditor button name (you can look up
- * the button name up in the plugin.js file), and should contain an array of
- * button properties, including:
- * - label: A human-readable, translated button name.
- * - image: An image for the button to be used in the toolbar.
- * - image_rtl: If the image needs to have a right-to-left version, specify
- * an alternative file that will be used in RTL editors.
- * - image_alternative: If this button does not render as an image, specify
- * an HTML string representing the contents of this button.
- * - image_alternative_rtl: Similar to image_alternative, but a
- * right-to-left version.
- * - attributes: An array of HTML attributes which should be added to this
- * button when rendering the button in the administrative section for
- * assembling the toolbar.
- * - multiple: Boolean value indicating if this button may be added multiple
- * times to the toolbar. This typically is only applicable for dividers
- * and group indicators.
- */
- public function getButtons();
-
-}
diff --git a/core/modules/ckeditor/src/CKEditorPluginConfigurableInterface.php b/core/modules/ckeditor/src/CKEditorPluginConfigurableInterface.php
deleted file mode 100644
index 6c453cae3a52..000000000000
--- a/core/modules/ckeditor/src/CKEditorPluginConfigurableInterface.php
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-
-namespace Drupal\ckeditor;
-
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\editor\Entity\Editor;
-
-/**
- * Defines an interface for configurable CKEditor plugins.
- *
- * This allows a CKEditor plugin to define a settings form. These settings can
- * then be automatically passed on to the corresponding CKEditor instance via
- * CKEditorPluginInterface::getConfig().
- *
- * @see \Drupal\ckeditor\CKEditorPluginInterface
- * @see \Drupal\ckeditor\CKEditorPluginButtonsInterface
- * @see \Drupal\ckeditor\CKEditorPluginContextualInterface
- * @see \Drupal\ckeditor\CKEditorPluginCssInterface
- * @see \Drupal\ckeditor\CKEditorPluginBase
- * @see \Drupal\ckeditor\CKEditorPluginManager
- * @see \Drupal\ckeditor\Annotation\CKEditorPlugin
- * @see plugin_api
- */
-interface CKEditorPluginConfigurableInterface extends CKEditorPluginInterface {
-
- /**
- * Returns a settings form to configure this CKEditor plugin.
- *
- * If the plugin's behavior depends on extensive options and/or external data,
- * then the implementing module can choose to provide a separate, global
- * configuration page rather than per-text-editor settings. In that case, this
- * form should provide a link to the separate settings page.
- *
- * @param array $form
- * An empty form array to be populated with a configuration form, if any.
- * @param \Drupal\Core\Form\FormStateInterface $form_state
- * The state of the entire filter administration form.
- * @param \Drupal\editor\Entity\Editor $editor
- * A configured text editor object.
- *
- * @return array
- * A render array for the settings form.
- */
- public function settingsForm(array $form, FormStateInterface $form_state, Editor $editor);
-
-}
diff --git a/core/modules/ckeditor/src/CKEditorPluginContextualInterface.php b/core/modules/ckeditor/src/CKEditorPluginContextualInterface.php
deleted file mode 100644
index 55d9d7a74c20..000000000000
--- a/core/modules/ckeditor/src/CKEditorPluginContextualInterface.php
+++ /dev/null
@@ -1,42 +0,0 @@
-<?php
-
-namespace Drupal\ckeditor;
-
-use Drupal\editor\Entity\Editor;
-
-/**
- * Defines an interface for contextually enabled CKEditor plugins.
- *
- * Contextually enabled CKEditor plugins can be enabled via an explicit setting,
- * or enable themselves based on the configuration of another setting, such as
- * enabling based on a particular button being present in the toolbar.
- *
- * If a contextually enabled CKEditor plugin must also be configurable (for
- * instance, in the case where it must be enabled based on an explicit setting),
- * then one must also implement the CKEditorPluginConfigurableInterface
- * interface.
- *
- * @see \Drupal\ckeditor\CKEditorPluginInterface
- * @see \Drupal\ckeditor\CKEditorPluginButtonsInterface
- * @see \Drupal\ckeditor\CKEditorPluginConfigurableInterface
- * @see \Drupal\ckeditor\CKEditorPluginCssInterface
- * @see \Drupal\ckeditor\CKEditorPluginBase
- * @see \Drupal\ckeditor\CKEditorPluginManager
- * @see \Drupal\ckeditor\Annotation\CKEditorPlugin
- * @see plugin_api
- */
-interface CKEditorPluginContextualInterface extends CKEditorPluginInterface {
-
- /**
- * Checks if this plugin should be enabled based on the editor configuration.
- *
- * The editor's settings can be retrieved via $editor->getSettings().
- *
- * @param \Drupal\editor\Entity\Editor $editor
- * A configured text editor object.
- *
- * @return bool
- */
- public function isEnabled(Editor $editor);
-
-}
diff --git a/core/modules/ckeditor/src/CKEditorPluginCssInterface.php b/core/modules/ckeditor/src/CKEditorPluginCssInterface.php
deleted file mode 100644
index 171a26afabd9..000000000000
--- a/core/modules/ckeditor/src/CKEditorPluginCssInterface.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-namespace Drupal\ckeditor;
-
-use Drupal\editor\Entity\Editor;
-
-/**
- * Defines an interface for CKEditor plugins with associated CSS.
- *
- * This allows a CKEditor plugin to add additional CSS in iframe CKEditor
- * instances without needing to implement hook_ckeditor_css_alter().
- *
- * @see \Drupal\ckeditor\CKEditorPluginInterface
- * @see \Drupal\ckeditor\CKEditorPluginButtonsInterface
- * @see \Drupal\ckeditor\CKEditorPluginContextualInterface
- * @see \Drupal\ckeditor\CKEditorPluginConfigurableInterface
- * @see \Drupal\ckeditor\CKEditorPluginBase
- * @see \Drupal\ckeditor\CKEditorPluginManager
- * @see \Drupal\ckeditor\Annotation\CKEditorPlugin
- * @see plugin_api
- */
-interface CKEditorPluginCssInterface extends CKEditorPluginInterface {
-
- /**
- * Retrieves enabled plugins' iframe instance CSS files.
- *
- * Note: this does not use a Drupal asset library because this CSS will be
- * loaded by CKEditor, not by Drupal.
- *
- * @param \Drupal\editor\Entity\Editor $editor
- * A configured text editor object.
- *
- * @return string[]
- * An array of CSS files. This is a flat list of file paths relative to
- * the Drupal root.
- */
- public function getCssFiles(Editor $editor);
-
-}
diff --git a/core/modules/ckeditor/src/CKEditorPluginInterface.php b/core/modules/ckeditor/src/CKEditorPluginInterface.php
deleted file mode 100644
index 1f3158b58b71..000000000000
--- a/core/modules/ckeditor/src/CKEditorPluginInterface.php
+++ /dev/null
@@ -1,102 +0,0 @@
-<?php
-
-namespace Drupal\ckeditor;
-
-use Drupal\Component\Plugin\PluginInspectionInterface;
-use Drupal\editor\Entity\Editor;
-
-/**
- * Defines an interface for (loading of) CKEditor plugins.
- *
- * This is the most basic CKEditor plugin interface; it provides the bare
- * minimum information. Solely implementing this interface is not sufficient to
- * be able to enable the plugin though — a CKEditor plugin can either be enabled
- * automatically when a button it provides is present in the toolbar, or when
- * some programmatically defined condition is true. In the former case,
- * implement the CKEditorPluginButtonsInterface interface, in the latter case,
- * implement the CKEditorPluginContextualInterface interface. It is also
- * possible to implement both, for advanced use cases.
- *
- * Finally, if your plugin must be configurable, you can also implement the
- * CKEditorPluginConfigurableInterface interface.
- *
- * @see \Drupal\ckeditor\CKEditorPluginButtonsInterface
- * @see \Drupal\ckeditor\CKEditorPluginContextualInterface
- * @see \Drupal\ckeditor\CKEditorPluginConfigurableInterface
- * @see \Drupal\ckeditor\CKEditorPluginCssInterface
- * @see \Drupal\ckeditor\CKEditorPluginBase
- * @see \Drupal\ckeditor\CKEditorPluginManager
- * @see \Drupal\ckeditor\Annotation\CKEditorPlugin
- * @see plugin_api
- */
-interface CKEditorPluginInterface extends PluginInspectionInterface {
-
- /**
- * Indicates if this plugin is part of the optimized CKEditor build.
- *
- * Plugins marked as internal are implicitly loaded as part of CKEditor.
- *
- * @return bool
- */
- public function isInternal();
-
- /**
- * Returns a list of plugins this plugin requires.
- *
- * @param \Drupal\editor\Entity\Editor $editor
- * A configured text editor object.
- *
- * @return array
- * An unindexed array of plugin names this plugin requires. Each plugin is
- * is identified by its annotated ID.
- */
- public function getDependencies(Editor $editor);
-
- /**
- * Returns a list of libraries this plugin requires.
- *
- * These libraries will be attached to the text_format element on which the
- * editor is being loaded.
- *
- * @param \Drupal\editor\Entity\Editor $editor
- * A configured text editor object.
- *
- * @return array
- * An array of libraries suitable for usage in a render API #attached
- * property.
- */
- public function getLibraries(Editor $editor);
-
- /**
- * Returns the Drupal root-relative file path to the plugin JavaScript file.
- *
- * Note: this does not use a Drupal library because this uses CKEditor's API.
- * @see https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_resourceManager.html#method-addExternal
- *
- * @return string|false
- * The Drupal root-relative path to the file, FALSE if an internal plugin.
- */
- public function getFile();
-
- /**
- * Returns the additions to CKEDITOR.config for a specific CKEditor instance.
- *
- * The editor's settings can be retrieved via $editor->getSettings(), but be
- * aware that it may not yet contain plugin-specific settings, because the
- * user may not yet have configured the form.
- * If there are plugin-specific settings (verify with isset()), they can be
- * found at
- * @code
- * $settings = $editor->getSettings();
- * $plugin_specific_settings = $settings['plugins'][$plugin_id];
- * @endcode
- *
- * @param \Drupal\editor\Entity\Editor $editor
- * A configured text editor object.
- *
- * @return array
- * A keyed array, whose keys will end up as keys under CKEDITOR.config.
- */
- public function getConfig(Editor $editor);
-
-}
diff --git a/core/modules/ckeditor/src/CKEditorPluginManager.php b/core/modules/ckeditor/src/CKEditorPluginManager.php
deleted file mode 100644
index e3eda0d2b1b6..000000000000
--- a/core/modules/ckeditor/src/CKEditorPluginManager.php
+++ /dev/null
@@ -1,222 +0,0 @@
-<?php
-
-namespace Drupal\ckeditor;
-
-use Drupal\Component\Utility\NestedArray;
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Plugin\DefaultPluginManager;
-use Drupal\Core\Cache\CacheBackendInterface;
-use Drupal\Core\Extension\ModuleHandlerInterface;
-use Drupal\editor\Entity\Editor;
-
-/**
- * Provides a CKEditor Plugin plugin manager.
- *
- * @see \Drupal\ckeditor\CKEditorPluginInterface
- * @see \Drupal\ckeditor\CKEditorPluginButtonsInterface
- * @see \Drupal\ckeditor\CKEditorPluginContextualInterface
- * @see \Drupal\ckeditor\CKEditorPluginConfigurableInterface
- * @see \Drupal\ckeditor\CKEditorPluginCssInterface
- * @see \Drupal\ckeditor\CKEditorPluginBase
- * @see \Drupal\ckeditor\Annotation\CKEditorPlugin
- * @see plugin_api
- */
-class CKEditorPluginManager extends DefaultPluginManager {
-
- /**
- * Constructs a CKEditorPluginManager object.
- *
- * @param \Traversable $namespaces
- * An object that implements \Traversable which contains the root paths
- * keyed by the corresponding namespace to look for plugin implementations.
- * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
- * Cache backend instance to use.
- * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
- * The module handler to invoke the alter hook with.
- */
- public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
- parent::__construct('Plugin/CKEditorPlugin', $namespaces, $module_handler, 'Drupal\ckeditor\CKEditorPluginInterface', 'Drupal\ckeditor\Annotation\CKEditorPlugin');
- $this->alterInfo('ckeditor_plugin_info');
- $this->setCacheBackend($cache_backend, 'ckeditor_plugins');
- }
-
- /**
- * Retrieves enabled plugins' files, keyed by plugin ID.
- *
- * For CKEditor plugins that implement:
- * - CKEditorPluginButtonsInterface, not CKEditorPluginContextualInterface,
- * a plugin is enabled if at least one of its buttons is in the toolbar;
- * - CKEditorPluginContextualInterface, not CKEditorPluginButtonsInterface,
- * a plugin is enabled if its isEnabled() method returns TRUE
- * - both of these interfaces, a plugin is enabled if either is the case.
- *
- * Internal plugins (those that are part of the bundled build of CKEditor) are
- * excluded by default, since they are loaded implicitly. If you need to know
- * even implicitly loaded (i.e. internal) plugins, then set the optional
- * second parameter.
- *
- * @param \Drupal\editor\Entity\Editor $editor
- * A configured text editor object.
- * @param bool $include_internal_plugins
- * Defaults to FALSE. When set to TRUE, plugins whose isInternal() method
- * returns TRUE will also be included.
- *
- * @return array
- * A list of the enabled CKEditor plugins, with the plugin IDs as keys and
- * the Drupal root-relative plugin files as values.
- * For internal plugins, the value is NULL.
- */
- public function getEnabledPluginFiles(Editor $editor, $include_internal_plugins = FALSE) {
- $plugins = array_keys($this->getDefinitions());
- $toolbar_buttons = $this->getEnabledButtons($editor);
- $enabled_plugins = [];
- $additional_plugins = [];
-
- foreach ($plugins as $plugin_id) {
- $plugin = $this->createInstance($plugin_id);
-
- if (!$include_internal_plugins && $plugin->isInternal()) {
- continue;
- }
-
- $enabled = FALSE;
- // Enable this plugin if it provides a button that has been enabled.
- if ($plugin instanceof CKEditorPluginButtonsInterface) {
- $plugin_buttons = array_keys($plugin->getButtons());
- $enabled = (count(array_intersect($toolbar_buttons, $plugin_buttons)) > 0);
- }
- // Otherwise enable this plugin if it declares itself as enabled.
- if (!$enabled && $plugin instanceof CKEditorPluginContextualInterface) {
- $enabled = $plugin->isEnabled($editor);
- }
-
- if ($enabled) {
- $enabled_plugins[$plugin_id] = ($plugin->isInternal()) ? NULL : $plugin->getFile();
- // Check if this plugin has dependencies that also need to be enabled.
- $additional_plugins = array_merge($additional_plugins, array_diff($plugin->getDependencies($editor), $additional_plugins));
- }
- }
-
- // Add the list of dependent plugins.
- foreach ($additional_plugins as $plugin_id) {
- $plugin = $this->createInstance($plugin_id);
- $enabled_plugins[$plugin_id] = ($plugin->isInternal()) ? NULL : $plugin->getFile();
- }
-
- // Always return plugins in the same order.
- asort($enabled_plugins);
-
- return $enabled_plugins;
- }
-
- /**
- * Gets the enabled toolbar buttons in the given text editor instance.
- *
- * @param \Drupal\editor\Entity\Editor $editor
- * A configured text editor object.
- *
- * @return string[]
- * A list of the toolbar buttons enabled in the given text editor instance.
- */
- public static function getEnabledButtons(Editor $editor) {
- $toolbar_rows = [];
- $settings = $editor->getSettings();
- foreach ($settings['toolbar']['rows'] as $row_number => $row) {
- $toolbar_rows[] = array_reduce($settings['toolbar']['rows'][$row_number], function ($result, $button_group) {
- return array_merge($result, $button_group['items']);
- }, []);
- }
- return array_unique(NestedArray::mergeDeepArray($toolbar_rows));
- }
-
- /**
- * Retrieves all available CKEditor buttons, keyed by plugin ID.
- *
- * @return array
- * All available CKEditor buttons, with plugin IDs as keys and button
- * metadata (as implemented by getButtons()) as values.
- *
- * @see \Drupal\ckeditor\CKEditorPluginButtonsInterface::getButtons()
- */
- public function getButtons() {
- $plugins = array_keys($this->getDefinitions());
- $buttons_plugins = [];
-
- foreach ($plugins as $plugin_id) {
- $plugin = $this->createInstance($plugin_id);
- if ($plugin instanceof CKEditorPluginButtonsInterface) {
- $buttons_plugins[$plugin_id] = $plugin->getButtons();
- }
- }
-
- return $buttons_plugins;
- }
-
- /**
- * Injects the CKEditor plugins settings forms as a vertical tabs subform.
- *
- * @param array &$form
- * A reference to an associative array containing the structure of the form.
- * @param \Drupal\Core\Form\FormStateInterface $form_state
- * The current state of the form.
- * @param \Drupal\editor\Entity\Editor $editor
- * A configured text editor object.
- */
- public function injectPluginSettingsForm(array &$form, FormStateInterface $form_state, Editor $editor) {
- $definitions = $this->getDefinitions();
-
- foreach (array_keys($definitions) as $plugin_id) {
- $plugin = $this->createInstance($plugin_id);
- if ($plugin instanceof CKEditorPluginConfigurableInterface) {
- $plugin_settings_form = [];
- $form['plugins'][$plugin_id] = [
- '#type' => 'details',
- '#title' => $definitions[$plugin_id]['label'],
- '#open' => TRUE,
- '#group' => 'editor][settings][plugin_settings',
- '#attributes' => [
- 'data-ckeditor-plugin-id' => $plugin_id,
- ],
- ];
- // Provide enough metadata for the drupal.ckeditor.admin library to
- // allow it to automatically show/hide the vertical tab containing the
- // settings for this plugin. Only do this if it's a CKEditor plugin that
- // just provides buttons, don't do this if it's a contextually enabled
- // CKEditor plugin. After all, in the latter case, we can't know when
- // its settings should be shown!
- if ($plugin instanceof CKEditorPluginButtonsInterface && !$plugin instanceof CKEditorPluginContextualInterface) {
- $form['plugins'][$plugin_id]['#attributes']['data-ckeditor-buttons'] = implode(' ', array_keys($plugin->getButtons()));
- }
- $form['plugins'][$plugin_id] += $plugin->settingsForm($plugin_settings_form, $form_state, $editor);
- }
- }
- }
-
- /**
- * Retrieves enabled plugins' iframe instance CSS files, keyed by plugin ID.
- *
- * @param \Drupal\editor\Entity\Editor $editor
- * A configured text editor object.
- *
- * @return string[]
- * Enabled plugins CKEditor CSS files, with plugin IDs as keys and CSS file
- * paths relative to the Drupal root (as implemented by getCssFiles()) as
- * values.
- *
- * @see \Drupal\ckeditor\CKEditorPluginCssInterface::getCssFiles()
- */
- public function getCssFiles(Editor $editor) {
- $enabled_plugins = array_keys($this->getEnabledPluginFiles($editor, TRUE));
- $css_files = [];
-
- foreach ($enabled_plugins as $plugin_id) {
- $plugin = $this->createInstance($plugin_id);
- if ($plugin instanceof CKEditorPluginCssInterface) {
- $css_files[$plugin_id] = $plugin->getCssFiles($editor);
- }
- }
-
- return $css_files;
- }
-
-}
diff --git a/core/modules/ckeditor/src/Plugin/CKEditorPlugin/DrupalImage.php b/core/modules/ckeditor/src/Plugin/CKEditorPlugin/DrupalImage.php
deleted file mode 100644
index 26e6300d13e2..000000000000
--- a/core/modules/ckeditor/src/Plugin/CKEditorPlugin/DrupalImage.php
+++ /dev/null
@@ -1,88 +0,0 @@
-<?php
-
-namespace Drupal\ckeditor\Plugin\CKEditorPlugin;
-
-use Drupal\ckeditor\CKEditorPluginBase;
-use Drupal\ckeditor\CKEditorPluginConfigurableInterface;
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\editor\Entity\Editor;
-
-/**
- * Defines the "drupalimage" plugin.
- *
- * @CKEditorPlugin(
- * id = "drupalimage",
- * label = @Translation("Image"),
- * module = "ckeditor"
- * )
- */
-class DrupalImage extends CKEditorPluginBase implements CKEditorPluginConfigurableInterface {
-
- /**
- * {@inheritdoc}
- */
- public function getFile() {
- return $this->getModulePath('ckeditor') . '/js/plugins/drupalimage/plugin.js';
- }
-
- /**
- * {@inheritdoc}
- */
- public function getLibraries(Editor $editor) {
- return [
- 'core/drupal.ajax',
- ];
- }
-
- /**
- * {@inheritdoc}
- */
- public function getConfig(Editor $editor) {
- return [
- 'drupalImage_dialogTitleAdd' => $this->t('Insert Image'),
- 'drupalImage_dialogTitleEdit' => $this->t('Edit Image'),
- ];
- }
-
- /**
- * {@inheritdoc}
- */
- public function getButtons() {
- return [
- 'DrupalImage' => [
- 'label' => $this->t('Image'),
- 'image' => $this->getModulePath('ckeditor') . '/js/plugins/drupalimage/icons/drupalimage.png',
- ],
- ];
- }
-
- /**
- * {@inheritdoc}
- *
- * @see \Drupal\editor\Form\EditorImageDialog
- * @see editor_image_upload_settings_form()
- */
- public function settingsForm(array $form, FormStateInterface $form_state, Editor $editor) {
- $form_state->loadInclude('editor', 'admin.inc');
- $form['image_upload'] = editor_image_upload_settings_form($editor);
- $form['image_upload']['#attached']['library'][] = 'ckeditor/drupal.ckeditor.drupalimage.admin';
- $form['image_upload']['#element_validate'][] = [$this, 'validateImageUploadSettings'];
- return $form;
- }
-
- /**
- * #element_validate handler for the "image_upload" element in settingsForm().
- *
- * Moves the text editor's image upload settings from the DrupalImage plugin's
- * own settings into $editor->image_upload.
- *
- * @see \Drupal\editor\Form\EditorImageDialog
- * @see editor_image_upload_settings_form()
- */
- public function validateImageUploadSettings(array $element, FormStateInterface $form_state) {
- $settings = &$form_state->getValue(['editor', 'settings', 'plugins', 'drupalimage', 'image_upload']);
- $form_state->get('editor')->setImageUploadSettings($settings);
- $form_state->unsetValue(['editor', 'settings', 'plugins', 'drupalimage']);
- }
-
-}
diff --git a/core/modules/ckeditor/src/Plugin/CKEditorPlugin/DrupalImageCaption.php b/core/modules/ckeditor/src/Plugin/CKEditorPlugin/DrupalImageCaption.php
deleted file mode 100644
index 18afc56f3de4..000000000000
--- a/core/modules/ckeditor/src/Plugin/CKEditorPlugin/DrupalImageCaption.php
+++ /dev/null
@@ -1,129 +0,0 @@
-<?php
-
-namespace Drupal\ckeditor\Plugin\CKEditorPlugin;
-
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Drupal\Core\Plugin\PluginBase;
-use Drupal\editor\Entity\Editor;
-use Drupal\ckeditor\CKEditorPluginInterface;
-use Drupal\ckeditor\CKEditorPluginContextualInterface;
-use Drupal\ckeditor\CKEditorPluginCssInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * Defines the "drupalimagecaption" plugin.
- *
- * @CKEditorPlugin(
- * id = "drupalimagecaption",
- * label = @Translation("Drupal image caption widget"),
- * module = "ckeditor"
- * )
- */
-class DrupalImageCaption extends PluginBase implements CKEditorPluginInterface, CKEditorPluginContextualInterface, CKEditorPluginCssInterface, ContainerFactoryPluginInterface {
-
- /**
- * The module extension list.
- *
- * @var \Drupal\Core\Extension\ModuleExtensionList
- */
- protected $moduleList;
-
- /**
- * {@inheritdoc}
- */
- public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
- $instance = new static(
- $configuration,
- $plugin_id,
- $plugin_definition,
- );
- $instance->moduleList = $container->get('extension.list.module');
- return $instance;
- }
-
- /**
- * {@inheritdoc}
- */
- public function isInternal() {
- return FALSE;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getDependencies(Editor $editor) {
- return [];
- }
-
- /**
- * {@inheritdoc}
- */
- public function getLibraries(Editor $editor) {
- return [
- 'ckeditor/drupal.ckeditor.plugins.drupalimagecaption',
- ];
- }
-
- /**
- * {@inheritdoc}
- */
- public function getFile() {
- return $this->moduleList->getPath('ckeditor') . '/js/plugins/drupalimagecaption/plugin.js';
- }
-
- /**
- * {@inheritdoc}
- */
- public function getConfig(Editor $editor) {
- $format = $editor->getFilterFormat();
- return [
- 'image2_captionedClass' => 'caption caption-img',
- 'image2_alignClasses' => ['align-left', 'align-center', 'align-right'],
- 'drupalImageCaption_captionPlaceholderText' => $this->t('Enter caption here'),
- // Only enable those parts of DrupalImageCaption for which the
- // corresponding Drupal text filters are enabled.
- 'drupalImageCaption_captionFilterEnabled' => $format->filters('filter_caption')->status,
- 'drupalImageCaption_alignFilterEnabled' => $format->filters('filter_align')->status,
- ];
- }
-
- /**
- * {@inheritdoc}
- */
- public function getCssFiles(Editor $editor) {
- return [
- $this->moduleList->getPath('ckeditor') . '/css/plugins/drupalimagecaption/ckeditor.drupalimagecaption.css',
- ];
- }
-
- /**
- * {@inheritdoc}
- */
- public function isEnabled(Editor $editor) {
- if (!$editor->hasAssociatedFilterFormat()) {
- return FALSE;
- }
-
- // Automatically enable this plugin if the text format associated with this
- // text editor uses the filter_align or filter_caption filter and the
- // DrupalImage button is enabled.
- $format = $editor->getFilterFormat();
- if ($format->filters('filter_align')->status || $format->filters('filter_caption')->status) {
- $enabled = FALSE;
- $settings = $editor->getSettings();
- foreach ($settings['toolbar']['rows'] as $row) {
- foreach ($row as $group) {
- foreach ($group['items'] as $button) {
- if ($button === 'DrupalImage') {
- $enabled = TRUE;
- }
- }
- }
- }
- return $enabled;
- }
-
- return FALSE;
- }
-
-}
diff --git a/core/modules/ckeditor/src/Plugin/CKEditorPlugin/DrupalLink.php b/core/modules/ckeditor/src/Plugin/CKEditorPlugin/DrupalLink.php
deleted file mode 100644
index 55db9294b73c..000000000000
--- a/core/modules/ckeditor/src/Plugin/CKEditorPlugin/DrupalLink.php
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php
-
-namespace Drupal\ckeditor\Plugin\CKEditorPlugin;
-
-use Drupal\ckeditor\CKEditorPluginBase;
-use Drupal\editor\Entity\Editor;
-
-/**
- * Defines the "drupallink" plugin.
- *
- * @CKEditorPlugin(
- * id = "drupallink",
- * label = @Translation("Drupal link"),
- * module = "ckeditor"
- * )
- */
-class DrupalLink extends CKEditorPluginBase {
-
- /**
- * {@inheritdoc}
- */
- public function getFile() {
- return $this->getModulePath('ckeditor') . '/js/plugins/drupallink/plugin.js';
- }
-
- /**
- * {@inheritdoc}
- */
- public function getLibraries(Editor $editor) {
- return [
- 'core/drupal.ajax',
- ];
- }
-
- /**
- * {@inheritdoc}
- */
- public function getConfig(Editor $editor) {
- return [
- 'drupalLink_dialogTitleAdd' => $this->t('Add Link'),
- 'drupalLink_dialogTitleEdit' => $this->t('Edit Link'),
- ];
- }
-
- /**
- * {@inheritdoc}
- */
- public function getButtons() {
- $path = $this->getModulePath('ckeditor') . '/js/plugins/drupallink';
- return [
- 'DrupalLink' => [
- 'label' => $this->t('Link'),
- 'image' => $path . '/icons/drupallink.png',
- ],
- 'DrupalUnlink' => [
- 'label' => $this->t('Unlink'),
- 'image' => $path . '/icons/drupalunlink.png',
- ],
- ];
- }
-
-}
diff --git a/core/modules/ckeditor/src/Plugin/CKEditorPlugin/DrupalMedia.php b/core/modules/ckeditor/src/Plugin/CKEditorPlugin/DrupalMedia.php
deleted file mode 100644
index 9365b096dcdb..000000000000
--- a/core/modules/ckeditor/src/Plugin/CKEditorPlugin/DrupalMedia.php
+++ /dev/null
@@ -1,136 +0,0 @@
-<?php
-
-namespace Drupal\ckeditor\Plugin\CKEditorPlugin;
-
-use Drupal\ckeditor\CKEditorPluginContextualInterface;
-use Drupal\ckeditor\CKEditorPluginCssInterface;
-use Drupal\Core\Extension\ModuleExtensionList;
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-use Drupal\Core\Plugin\PluginBase;
-use Drupal\editor\Entity\Editor;
-
-/**
- * Defines the "drupalmedia" plugin.
- *
- * @CKEditorPlugin(
- * id = "drupalmedia",
- * label = @Translation("Media Embed"),
- * provider = "media",
- * )
- *
- * @internal
- * This is an internal part of the media system in Drupal core and may be
- * subject to change in minor releases. This class should not be
- * instantiated or extended by external code.
- */
-class DrupalMedia extends PluginBase implements ContainerFactoryPluginInterface, CKEditorPluginContextualInterface, CKEditorPluginCssInterface {
-
- /**
- * The module extension list.
- *
- * @var \Drupal\Core\Extension\ModuleExtensionList
- */
- protected $moduleExtensionList;
-
- /**
- * Constructs a new DrupalMedia plugin object.
- *
- * @param array $configuration
- * A configuration array containing information about the plugin instance.
- * @param string $plugin_id
- * The plugin_id for the plugin instance.
- * @param array $plugin_definition
- * The plugin implementation definition.
- * @param \Drupal\Core\Extension\ModuleExtensionList $extension_list_module
- * The module extension list.
- */
- public function __construct(array $configuration, $plugin_id, $plugin_definition, ModuleExtensionList $extension_list_module) {
- parent::__construct($configuration, $plugin_id, $plugin_definition);
- $this->moduleExtensionList = $extension_list_module;
- }
-
- /**
- * {@inheritdoc}
- */
- public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
- return new static(
- $configuration,
- $plugin_id,
- $plugin_definition,
- $container->get('extension.list.module')
- );
- }
-
- /**
- * {@inheritdoc}
- */
- public function isInternal() {
- return FALSE;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getDependencies(Editor $editor) {
- return [];
- }
-
- /**
- * {@inheritdoc}
- */
- public function getLibraries(Editor $editor) {
- return [
- 'core/jquery',
- 'core/drupal',
- 'core/drupal.ajax',
- // @see Drupal.theme.mediaEmbedPreviewError()
- 'media/media_embed_ckeditor_theme',
- // @see Drupal.theme.mediaEmbedEditButton()
- 'ckeditor/drupal.ckeditor.plugins.drupalmedia',
- ];
- }
-
- /**
- * {@inheritdoc}
- */
- public function getFile() {
- return $this->moduleExtensionList->getPath('ckeditor') . '/js/plugins/drupalmedia/plugin.js';
- }
-
- /**
- * {@inheritdoc}
- */
- public function getConfig(Editor $editor) {
- return [
- 'drupalMedia_previewCsrfToken' => \Drupal::csrfToken()->get('X-Drupal-MediaPreview-CSRF-Token'),
- ];
- }
-
- /**
- * {@inheritdoc}
- */
- public function isEnabled(Editor $editor) {
- if (!$editor->hasAssociatedFilterFormat()) {
- return FALSE;
- }
-
- // Automatically enable this plugin if the text format associated with this
- // text editor uses the media_embed filter.
- $filters = $editor->getFilterFormat()->filters();
- return $filters->has('media_embed') && $filters->get('media_embed')->status;
- }
-
- /**
- * {@inheritdoc}
- *
- * @todo Improve this in https://www.drupal.org/project/drupal/issues/3072063
- */
- public function getCssFiles(Editor $editor) {
- return [
- $this->moduleExtensionList->getPath('ckeditor') . '/css/plugins/drupalmedia/ckeditor.drupalmedia.css',
- $this->moduleExtensionList->getPath('system') . '/css/components/hidden.module.css',
- ];
- }
-
-}
diff --git a/core/modules/ckeditor/src/Plugin/CKEditorPlugin/DrupalMediaLibrary.php b/core/modules/ckeditor/src/Plugin/CKEditorPlugin/DrupalMediaLibrary.php
deleted file mode 100644
index bfff786dbe3d..000000000000
--- a/core/modules/ckeditor/src/Plugin/CKEditorPlugin/DrupalMediaLibrary.php
+++ /dev/null
@@ -1,171 +0,0 @@
-<?php
-
-namespace Drupal\ckeditor\Plugin\CKEditorPlugin;
-
-use Drupal\ckeditor\CKEditorPluginBase;
-use Drupal\Core\Entity\EntityTypeManagerInterface;
-use Drupal\Core\Extension\ModuleExtensionList;
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Drupal\Core\Url;
-use Drupal\editor\Entity\Editor;
-use Drupal\media_library\MediaLibraryState;
-use Drupal\media_library\MediaLibraryUiBuilder;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * Defines the "drupalmedialibrary" plugin.
- *
- * @CKEditorPlugin(
- * id = "drupalmedialibrary",
- * label = @Translation("Embed media from the Media Library"),
- * provider = "media_library",
- * )
- *
- * @internal
- * Plugin classes are internal.
- */
-class DrupalMediaLibrary extends CKEditorPluginBase implements ContainerFactoryPluginInterface {
-
- /**
- * The module extension list.
- *
- * @var \Drupal\Core\Extension\ModuleExtensionList
- */
- protected $moduleExtensionList;
-
- /**
- * The media type entity storage.
- *
- * @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface
- */
- protected $mediaTypeStorage;
-
- /**
- * Constructs a new DrupalMediaLibrary plugin object.
- *
- * @param array $configuration
- * A configuration array containing information about the plugin instance.
- * @param string $plugin_id
- * The plugin_id for the plugin instance.
- * @param array $plugin_definition
- * The plugin implementation definition.
- * @param \Drupal\Core\Extension\ModuleExtensionList $extension_list_module
- * The module extension list.
- * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
- * The entity type manager.
- */
- public function __construct(array $configuration, $plugin_id, array $plugin_definition, ModuleExtensionList $extension_list_module, EntityTypeManagerInterface $entity_type_manager) {
- parent::__construct($configuration, $plugin_id, $plugin_definition);
- $this->moduleExtensionList = $extension_list_module;
- $this->mediaTypeStorage = $entity_type_manager->getStorage('media_type');
- }
-
- /**
- * {@inheritdoc}
- */
- public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
- return new static(
- $configuration,
- $plugin_id,
- $plugin_definition,
- $container->get('extension.list.module'),
- $container->get('entity_type.manager')
- );
- }
-
- /**
- * {@inheritdoc}
- */
- public function isInternal() {
- return FALSE;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getDependencies(Editor $editor) {
- return [
- 'drupalmedia',
- ];
- }
-
- /**
- * {@inheritdoc}
- */
- public function getLibraries(Editor $editor) {
- return [
- 'editor/drupal.editor.dialog',
- ];
- }
-
- /**
- * {@inheritdoc}
- */
- public function getFile() {
- return $this->moduleExtensionList->getPath('ckeditor') . '/js/plugins/drupalmedialibrary/plugin.js';
- }
-
- /**
- * {@inheritdoc}
- */
- public function getConfig(Editor $editor) {
- // If the editor has not been saved yet, we may not be able to create a
- // coherent MediaLibraryState object, which is needed in order to generate
- // the required configuration. But, if we're creating a new editor, we don't
- // need to do that anyway, so just return an empty array.
- if ($editor->isNew()) {
- return [];
- }
-
- $media_type_ids = $this->mediaTypeStorage->getQuery()->execute();
- if ($editor->hasAssociatedFilterFormat()) {
- if ($media_embed_filter = $editor->getFilterFormat()->filters()->get('media_embed')) {
- // Optionally limit the allowed media types based on the MediaEmbed
- // setting. If the setting is empty, do not limit the options.
- if (!empty($media_embed_filter->settings['allowed_media_types'])) {
- $media_type_ids = array_intersect_key($media_type_ids, $media_embed_filter->settings['allowed_media_types']);
- }
- }
- }
-
- if (in_array('image', $media_type_ids, TRUE)) {
- // Due to a bug where the active item styling and the focus styling
- // create the visual appearance of two active items, we'll move
- // the 'image' media type to first position, so that the focused item and
- // the active item are the same.
- // This workaround can be removed once this issue is fixed:
- // @see https://www.drupal.org/project/drupal/issues/3073799
- array_unshift($media_type_ids, 'image');
- $media_type_ids = array_unique($media_type_ids);
- }
-
- $state = MediaLibraryState::create(
- 'media_library.opener.editor',
- $media_type_ids,
- reset($media_type_ids),
- 1,
- ['filter_format_id' => $editor->getFilterFormat()->id()]
- );
-
- return [
- 'DrupalMediaLibrary_url' => Url::fromRoute('media_library.ui')
- ->setOption('query', $state->all())
- ->toString(TRUE)
- ->getGeneratedUrl(),
- 'DrupalMediaLibrary_dialogOptions' => MediaLibraryUiBuilder::dialogOptions(),
- ];
- }
-
- /**
- * {@inheritdoc}
- */
- public function getButtons() {
- return [
- 'DrupalMediaLibrary' => [
- 'label' => $this->t('Insert from Media Library'),
- 'image' => $this->moduleExtensionList->getPath('ckeditor') . '/js/plugins/drupalmedialibrary/icons/drupalmedialibrary.png',
- ],
- ];
- }
-
-}
diff --git a/core/modules/ckeditor/src/Plugin/CKEditorPlugin/Internal.php b/core/modules/ckeditor/src/Plugin/CKEditorPlugin/Internal.php
deleted file mode 100644
index 5c2dce25b6b4..000000000000
--- a/core/modules/ckeditor/src/Plugin/CKEditorPlugin/Internal.php
+++ /dev/null
@@ -1,609 +0,0 @@
-<?php
-
-namespace Drupal\ckeditor\Plugin\CKEditorPlugin;
-
-use Drupal\ckeditor\CKEditorPluginBase;
-use Drupal\ckeditor\CKEditorPluginContextualInterface;
-use Drupal\ckeditor\CKEditorPluginManager;
-use Drupal\Component\Utility\Html;
-use Drupal\Core\Cache\Cache;
-use Drupal\Core\Cache\CacheBackendInterface;
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Drupal\editor\Entity\Editor;
-use Drupal\filter\Plugin\FilterInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * Defines the "internal" plugin (i.e. core plugins part of our CKEditor build).
- *
- * @CKEditorPlugin(
- * id = "internal",
- * label = @Translation("CKEditor core")
- * )
- */
-class Internal extends CKEditorPluginBase implements ContainerFactoryPluginInterface, CKEditorPluginContextualInterface {
-
- /**
- * The cache backend.
- *
- * @var \Drupal\Core\Cache\CacheBackendInterface
- */
- protected $cache;
-
- /**
- * Constructs a \Drupal\ckeditor\Plugin\CKEditorPlugin\Internal object.
- *
- * @param array $configuration
- * A configuration array containing information about the plugin instance.
- * @param string $plugin_id
- * The plugin_id for the plugin instance.
- * @param mixed $plugin_definition
- * The plugin implementation definition.
- * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
- * The cache backend.
- */
- public function __construct(array $configuration, $plugin_id, $plugin_definition, CacheBackendInterface $cache_backend) {
- $this->cache = $cache_backend;
- parent::__construct($configuration, $plugin_id, $plugin_definition);
- }
-
- /**
- * Creates an instance of the plugin.
- *
- * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
- * The container to pull out services used in the plugin.
- * @param array $configuration
- * A configuration array containing information about the plugin instance.
- * @param string $plugin_id
- * The plugin ID for the plugin instance.
- * @param mixed $plugin_definition
- * The plugin implementation definition.
- *
- * @return static
- * Returns an instance of this plugin.
- */
- public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
- return new static(
- $configuration,
- $plugin_id,
- $plugin_definition,
- $container->get('cache.default')
- );
- }
-
- /**
- * {@inheritdoc}
- */
- public function isInternal() {
- return TRUE;
- }
-
- /**
- * {@inheritdoc}
- */
- public function isEnabled(Editor $editor) {
- // This plugin represents the core CKEditor plugins. They're always enabled:
- // its configuration is always necessary.
- return TRUE;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getFile() {
- // This plugin is already part of Drupal core's CKEditor build.
- return FALSE;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getConfig(Editor $editor) {
- // Reasonable defaults that provide expected basic behavior.
- $config = [
- // Don't load CKEditor's config.js file.
- 'customConfig' => '',
- 'pasteFromWordPromptCleanup' => TRUE,
- 'resize_dir' => 'vertical',
- 'justifyClasses' => ['text-align-left', 'text-align-center', 'text-align-right', 'text-align-justify'],
- 'entities' => FALSE,
- 'disableNativeSpellChecker' => FALSE,
- ];
-
- // Add the allowedContent setting, which ensures CKEditor only allows tags
- // and attributes that are allowed by the text format for this text editor.
- [$config['allowedContent'], $config['disallowedContent']] = $this->generateACFSettings($editor);
-
- // Add the format_tags setting, if its button is enabled.
- $toolbar_buttons = CKEditorPluginManager::getEnabledButtons($editor);
- if (in_array('Format', $toolbar_buttons) && $format_string = $this->generateFormatTagsSetting($editor)) {
- $config['format_tags'] = $format_string;
- }
-
- return $config;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getButtons() {
- $button = function ($name, $direction = 'ltr') {
- // In the markup below, we mostly use the name (which may include spaces),
- // but in one spot we use it as a CSS class, so strip spaces.
- // Note: this uses str_replace() instead of Html::cleanCssIdentifier()
- // because we must provide these class names exactly how CKEditor expects
- // them in its library, which cleanCssIdentifier() does not do.
- $class_name = str_replace(' ', '', $name);
- return [
- '#type' => 'inline_template',
- '#template' => '<a href="#" class="cke-icon-only cke_{{ direction }}" role="button" title="{{ name }}" aria-label="{{ name }}"><span class="cke_button_icon cke_button__{{ classname }}_icon">{{ name }}</span></a>',
- '#context' => [
- 'direction' => $direction,
- 'name' => $name,
- 'classname' => $class_name,
- ],
- ];
- };
-
- return [
- // "basicstyles" plugin.
- 'Bold' => [
- 'label' => $this->t('Bold'),
- 'image_alternative' => $button('bold'),
- 'image_alternative_rtl' => $button('bold', 'rtl'),
- ],
- 'Italic' => [
- 'label' => $this->t('Italic'),
- 'image_alternative' => $button('italic'),
- 'image_alternative_rtl' => $button('italic', 'rtl'),
- ],
- 'Underline' => [
- 'label' => $this->t('Underline'),
- 'image_alternative' => $button('underline'),
- 'image_alternative_rtl' => $button('underline', 'rtl'),
- ],
- 'Strike' => [
- 'label' => $this->t('Strike-through'),
- 'image_alternative' => $button('strike'),
- 'image_alternative_rtl' => $button('strike', 'rtl'),
- ],
- 'Superscript' => [
- 'label' => $this->t('Superscript'),
- 'image_alternative' => $button('super script'),
- 'image_alternative_rtl' => $button('super script', 'rtl'),
- ],
- 'Subscript' => [
- 'label' => $this->t('Subscript'),
- 'image_alternative' => $button('sub script'),
- 'image_alternative_rtl' => $button('sub script', 'rtl'),
- ],
- // "removeformat" plugin.
- 'RemoveFormat' => [
- 'label' => $this->t('Remove format'),
- 'image_alternative' => $button('remove format'),
- 'image_alternative_rtl' => $button('remove format', 'rtl'),
- ],
- // "justify" plugin.
- 'JustifyLeft' => [
- 'label' => $this->t('Align left'),
- 'image_alternative' => $button('justify left'),
- 'image_alternative_rtl' => $button('justify left', 'rtl'),
- ],
- 'JustifyCenter' => [
- 'label' => $this->t('Align center'),
- 'image_alternative' => $button('justify center'),
- 'image_alternative_rtl' => $button('justify center', 'rtl'),
- ],
- 'JustifyRight' => [
- 'label' => $this->t('Align right'),
- 'image_alternative' => $button('justify right'),
- 'image_alternative_rtl' => $button('justify right', 'rtl'),
- ],
- 'JustifyBlock' => [
- 'label' => $this->t('Justify'),
- 'image_alternative' => $button('justify block'),
- 'image_alternative_rtl' => $button('justify block', 'rtl'),
- ],
- // "list" plugin.
- 'BulletedList' => [
- 'label' => $this->t('Bullet list'),
- 'image_alternative' => $button('bulleted list'),
- 'image_alternative_rtl' => $button('bulleted list', 'rtl'),
- ],
- 'NumberedList' => [
- 'label' => $this->t('Numbered list'),
- 'image_alternative' => $button('numbered list'),
- 'image_alternative_rtl' => $button('numbered list', 'rtl'),
- ],
- // "indent" plugin.
- 'Outdent' => [
- 'label' => $this->t('Outdent'),
- 'image_alternative' => $button('outdent'),
- 'image_alternative_rtl' => $button('outdent', 'rtl'),
- ],
- 'Indent' => [
- 'label' => $this->t('Indent'),
- 'image_alternative' => $button('indent'),
- 'image_alternative_rtl' => $button('indent', 'rtl'),
- ],
- // "undo" plugin.
- 'Undo' => [
- 'label' => $this->t('Undo'),
- 'image_alternative' => $button('undo'),
- 'image_alternative_rtl' => $button('undo', 'rtl'),
- ],
- 'Redo' => [
- 'label' => $this->t('Redo'),
- 'image_alternative' => $button('redo'),
- 'image_alternative_rtl' => $button('redo', 'rtl'),
- ],
- // "blockquote" plugin.
- 'Blockquote' => [
- 'label' => $this->t('Blockquote'),
- 'image_alternative' => $button('blockquote'),
- 'image_alternative_rtl' => $button('blockquote', 'rtl'),
- ],
- // "horizontalrule" plugin
- 'HorizontalRule' => [
- 'label' => $this->t('Horizontal rule'),
- 'image_alternative' => $button('horizontal rule'),
- 'image_alternative_rtl' => $button('horizontal rule', 'rtl'),
- ],
- // "clipboard" plugin.
- 'Cut' => [
- 'label' => $this->t('Cut'),
- 'image_alternative' => $button('cut'),
- 'image_alternative_rtl' => $button('cut', 'rtl'),
- ],
- 'Copy' => [
- 'label' => $this->t('Copy'),
- 'image_alternative' => $button('copy'),
- 'image_alternative_rtl' => $button('copy', 'rtl'),
- ],
- 'Paste' => [
- 'label' => $this->t('Paste'),
- 'image_alternative' => $button('paste'),
- 'image_alternative_rtl' => $button('paste', 'rtl'),
- ],
- // "pastetext" plugin.
- 'PasteText' => [
- 'label' => $this->t('Paste Text'),
- 'image_alternative' => $button('paste text'),
- 'image_alternative_rtl' => $button('paste text', 'rtl'),
- ],
- // "pastefromword" plugin.
- 'PasteFromWord' => [
- 'label' => $this->t('Paste from Word'),
- 'image_alternative' => $button('paste from word'),
- 'image_alternative_rtl' => $button('paste from word', 'rtl'),
- ],
- // "specialchar" plugin.
- 'SpecialChar' => [
- 'label' => $this->t('Character map'),
- 'image_alternative' => $button('special char'),
- 'image_alternative_rtl' => $button('special char', 'rtl'),
- ],
- 'Format' => [
- 'label' => $this->t('HTML block format'),
- 'image_alternative' => [
- '#type' => 'inline_template',
- '#template' => '<a href="#" role="button" aria-label="{{ format_text }}"><span class="ckeditor-button-dropdown">{{ format_text }}<span class="ckeditor-button-arrow"></span></span></a>',
- '#context' => [
- 'format_text' => $this->t('Format'),
- ],
- ],
- ],
- // "table" plugin.
- 'Table' => [
- 'label' => $this->t('Table'),
- 'image_alternative' => $button('table'),
- 'image_alternative_rtl' => $button('table', 'rtl'),
- ],
- // "showblocks" plugin.
- 'ShowBlocks' => [
- 'label' => $this->t('Show blocks'),
- 'image_alternative' => $button('show blocks'),
- 'image_alternative_rtl' => $button('show blocks', 'rtl'),
- ],
- // "sourcearea" plugin.
- 'Source' => [
- 'label' => $this->t('Source code'),
- 'image_alternative' => $button('source'),
- 'image_alternative_rtl' => $button('source', 'rtl'),
- ],
- // "maximize" plugin.
- 'Maximize' => [
- 'label' => $this->t('Maximize'),
- 'image_alternative' => $button('maximize'),
- 'image_alternative_rtl' => $button('maximize', 'rtl'),
- ],
- // No plugin, separator "button" for toolbar builder UI use only.
- '-' => [
- 'label' => $this->t('Separator'),
- 'image_alternative' => [
- '#type' => 'inline_template',
- '#template' => '<a href="#" role="button" aria-label="{{ button_separator_text }}" class="ckeditor-separator"></a>',
- '#context' => [
- 'button_separator_text' => $this->t('Button separator'),
- ],
- ],
- 'attributes' => [
- 'class' => ['ckeditor-button-separator'],
- 'data-drupal-ckeditor-type' => 'separator',
- ],
- 'multiple' => TRUE,
- ],
- ];
- }
-
- /**
- * Builds the "format_tags" configuration part of the CKEditor JS settings.
- *
- * @see getConfig()
- *
- * @param \Drupal\editor\Entity\Editor $editor
- * A configured text editor object.
- *
- * @return string|false
- * A string containing the "format_tags" configuration or FALSE if the
- * editor has not an associated filter format.
- */
- protected function generateFormatTagsSetting(Editor $editor) {
- // When no text format is associated yet, assume no tag is allowed.
- // @see \Drupal\editor\EditorInterface::hasAssociatedFilterFormat()
- if (!$editor->hasAssociatedFilterFormat()) {
- return FALSE;
- }
-
- $format = $editor->getFilterFormat();
- $cid = 'ckeditor_internal_format_tags:' . $format->id();
-
- if ($cached = $this->cache->get($cid)) {
- $format_tags = $cached->data;
- }
- else {
- // The <p> tag is always allowed — HTML without <p> tags is nonsensical.
- $format_tags = ['p'];
-
- // Given the list of possible format tags, automatically determine whether
- // the current text format allows this tag, and thus whether it should show
- // up in the "Format" dropdown.
- $possible_format_tags = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'pre'];
- foreach ($possible_format_tags as $tag) {
- $input = '<' . $tag . '>TEST</' . $tag . '>';
- $output = trim(check_markup($input, $editor->id()));
- if (Html::load($output)->getElementsByTagName($tag)->length !== 0) {
- $format_tags[] = $tag;
- }
- }
- $format_tags = implode(';', $format_tags);
-
- // Cache the "format_tags" configuration. This cache item is infinitely
- // valid; it only changes whenever the text format is changed, hence it's
- // tagged with the text format's cache tag.
- $this->cache->set($cid, $format_tags, Cache::PERMANENT, $format->getCacheTags());
- }
-
- return $format_tags;
- }
-
- /**
- * Builds the ACF part of the CKEditor JS settings.
- *
- * This ensures that CKEditor obeys the HTML restrictions defined by Drupal's
- * filter system, by enabling CKEditor's Advanced Content Filter (ACF)
- * functionality: http://ckeditor.com/blog/CKEditor-4.1-RC-Released.
- *
- * @see getConfig()
- *
- * @param \Drupal\editor\Entity\Editor $editor
- * A configured text editor object.
- *
- * @return array
- * An array with two values:
- * - the first value is the "allowedContent" setting: a well-formatted array
- * or TRUE. The latter indicates that anything is allowed.
- * - the second value is the "disallowedContent" setting: a well-formatted
- * array or FALSE. The latter indicates that nothing is disallowed.
- */
- protected function generateACFSettings(Editor $editor) {
- // When no text format is associated yet, assume nothing is disallowed, so
- // set allowedContent to true.
- if (!$editor->hasAssociatedFilterFormat()) {
- return TRUE;
- }
-
- $format = $editor->getFilterFormat();
- $filter_types = $format->getFilterTypes();
-
- // When nothing is disallowed, set allowedContent to true.
- if (!in_array(FilterInterface::TYPE_HTML_RESTRICTOR, $filter_types)) {
- return [TRUE, FALSE];
- }
- // Generate setting that accurately reflects allowed tags and attributes.
- else {
- $get_attribute_values = function ($attribute_values, $allowed_values) {
- $values = array_keys(array_filter($attribute_values, function ($value) use ($allowed_values) {
- if ($allowed_values) {
- return $value !== FALSE;
- }
- else {
- return $value === FALSE;
- }
- }));
- if (count($values)) {
- return implode(',', $values);
- }
- else {
- return NULL;
- }
- };
-
- $html_restrictions = $format->getHtmlRestrictions();
- // When all HTML is allowed, also set allowedContent to true and
- // disallowedContent to false.
- if ($html_restrictions === FALSE) {
- return [TRUE, FALSE];
- }
- $allowed = [];
- $disallowed = [];
- if (isset($html_restrictions['forbidden_tags'])) {
- foreach ($html_restrictions['forbidden_tags'] as $tag) {
- $disallowed[$tag] = TRUE;
- }
- }
- foreach ($html_restrictions['allowed'] as $tag => $attributes) {
- // Tell CKEditor the tag is allowed, but no attributes.
- if ($attributes === FALSE) {
- $allowed[$tag] = [
- 'attributes' => FALSE,
- 'styles' => FALSE,
- 'classes' => FALSE,
- ];
- }
- // Tell CKEditor the tag is allowed, as well as any attribute on it. The
- // "style" and "class" attributes are handled separately by CKEditor:
- // they are disallowed even if you specify it in the list of allowed
- // attributes, unless you state specific values for them that are
- // allowed. Or, in this case: any value for them is allowed.
- elseif ($attributes === TRUE) {
- $allowed[$tag] = [
- 'attributes' => TRUE,
- 'styles' => TRUE,
- 'classes' => TRUE,
- ];
- // We've just marked that any value for the "style" and "class"
- // attributes is allowed. However, that may not be the case: the "*"
- // tag may still apply restrictions.
- // Since CKEditor's ACF follows the following principle:
- // - Once validated, an element or its property cannot be
- // invalidated by another rule.
- // That means that the most permissive setting wins. Which means that
- // it will still be allowed by CKEditor, for instance, to define any
- // style, no matter what the "*" tag's restrictions may be. If there
- // is a setting for either the "style" or "class" attribute, it cannot
- // possibly be more permissive than what was set above. Hence, inherit
- // from the "*" tag where possible.
- if (isset($html_restrictions['allowed']['*'])) {
- $wildcard = $html_restrictions['allowed']['*'];
- if (isset($wildcard['style'])) {
- if (!is_array($wildcard['style'])) {
- $allowed[$tag]['styles'] = $wildcard['style'];
- }
- else {
- $allowed_styles = $get_attribute_values($wildcard['style'], TRUE);
- if (isset($allowed_styles)) {
- $allowed[$tag]['styles'] = $allowed_styles;
- }
- else {
- unset($allowed[$tag]['styles']);
- }
- }
- }
- if (isset($wildcard['class'])) {
- if (!is_array($wildcard['class'])) {
- $allowed[$tag]['classes'] = $wildcard['class'];
- }
- else {
- $allowed_classes = $get_attribute_values($wildcard['class'], TRUE);
- if (isset($allowed_classes)) {
- $allowed[$tag]['classes'] = $allowed_classes;
- }
- else {
- unset($allowed[$tag]['classes']);
- }
- }
- }
- }
- }
- // Tell CKEditor the tag is allowed, along with some tags.
- elseif (is_array($attributes)) {
- // Set defaults (these will be overridden below if more specific
- // values are present).
- $allowed[$tag] = [
- 'attributes' => FALSE,
- 'styles' => FALSE,
- 'classes' => FALSE,
- ];
- // Configure allowed attributes, allowed "style" attribute values and
- // allowed "class" attribute values.
- // CKEditor only allows specific values for the "class" and "style"
- // attributes; so ignore restrictions on other attributes, which
- // Drupal filters may provide.
- // NOTE: A Drupal contrib module can subclass this class, override the
- // getConfig() method, and override the JavaScript at
- // Drupal.editors.ckeditor to somehow make validation of values for
- // attributes other than "class" and "style" work.
- $allowed_attributes = array_filter($attributes, function ($value) {
- return $value !== FALSE;
- });
- if (count($allowed_attributes)) {
- $allowed[$tag]['attributes'] = implode(',', array_keys($allowed_attributes));
- }
- if (isset($allowed_attributes['style'])) {
- if (is_bool($allowed_attributes['style'])) {
- $allowed[$tag]['styles'] = $allowed_attributes['style'];
- }
- elseif (is_array($allowed_attributes['style'])) {
- $allowed_classes = $get_attribute_values($allowed_attributes['style'], TRUE);
- if (isset($allowed_classes)) {
- $allowed[$tag]['styles'] = $allowed_classes;
- }
- }
- }
- if (isset($allowed_attributes['class'])) {
- if (is_bool($allowed_attributes['class'])) {
- $allowed[$tag]['classes'] = $allowed_attributes['class'];
- }
- elseif (is_array($allowed_attributes['class'])) {
- $allowed_classes = $get_attribute_values($allowed_attributes['class'], TRUE);
- if (isset($allowed_classes)) {
- $allowed[$tag]['classes'] = $allowed_classes;
- }
- }
- }
-
- // Handle disallowed attributes analogously. However, to handle *dis-
- // allowed* attribute values, we must look at *allowed* attributes'
- // disallowed attribute values! After all, a disallowed attribute
- // implies that all of its possible attribute values are disallowed,
- // thus we must look at the disallowed attribute values on allowed
- // attributes.
- $disallowed_attributes = array_filter($attributes, function ($value) {
- return $value === FALSE;
- });
- if (count($disallowed_attributes)) {
- // No need to blacklist the 'class' or 'style' attributes; CKEditor
- // handles them separately (if no specific class or style attribute
- // values are allowed, then those attributes are disallowed).
- if (isset($disallowed_attributes['class'])) {
- unset($disallowed_attributes['class']);
- }
- if (isset($disallowed_attributes['style'])) {
- unset($disallowed_attributes['style']);
- }
- $disallowed[$tag]['attributes'] = implode(',', array_keys($disallowed_attributes));
- }
- if (isset($allowed_attributes['style']) && is_array($allowed_attributes['style'])) {
- $disallowed_styles = $get_attribute_values($allowed_attributes['style'], FALSE);
- if (isset($disallowed_styles)) {
- $disallowed[$tag]['styles'] = $disallowed_styles;
- }
- }
- if (isset($allowed_attributes['class']) && is_array($allowed_attributes['class'])) {
- $disallowed_classes = $get_attribute_values($allowed_attributes['class'], FALSE);
- if (isset($disallowed_classes)) {
- $disallowed[$tag]['classes'] = $disallowed_classes;
- }
- }
- }
- }
-
- ksort($allowed);
- ksort($disallowed);
-
- return [$allowed, $disallowed];
- }
- }
-
-}
diff --git a/core/modules/ckeditor/src/Plugin/CKEditorPlugin/Language.php b/core/modules/ckeditor/src/Plugin/CKEditorPlugin/Language.php
deleted file mode 100644
index 5334bae991da..000000000000
--- a/core/modules/ckeditor/src/Plugin/CKEditorPlugin/Language.php
+++ /dev/null
@@ -1,136 +0,0 @@
-<?php
-
-namespace Drupal\ckeditor\Plugin\CKEditorPlugin;
-
-use Drupal\ckeditor\CKEditorPluginBase;
-use Drupal\ckeditor\CKEditorPluginConfigurableInterface;
-use Drupal\ckeditor\CKEditorPluginCssInterface;
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Language\LanguageManager;
-use Drupal\Core\Language\LanguageInterface;
-use Drupal\editor\Entity\Editor;
-
-/**
- * Defines the "language" plugin.
- *
- * @CKEditorPlugin(
- * id = "language",
- * label = @Translation("Language")
- * )
- */
-class Language extends CKEditorPluginBase implements CKEditorPluginConfigurableInterface, CKEditorPluginCssInterface {
-
- /**
- * {@inheritdoc}
- */
- public function isInternal() {
- return TRUE;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getFile() {
- // This plugin is already part of Drupal core's CKEditor build.
- return FALSE;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getLibraries(Editor $editor) {
- return ['ckeditor/drupal.ckeditor.plugins.language'];
- }
-
- /**
- * {@inheritdoc}
- */
- public function getConfig(Editor $editor) {
- $language_list = [];
- $config = ['language_list' => 'un'];
- $settings = $editor->getSettings();
- if (isset($settings['plugins']['language'])) {
- $config = $settings['plugins']['language'];
- }
-
- $predefined_languages = ($config['language_list'] === 'all') ?
- LanguageManager::getStandardLanguageList() :
- LanguageManager::getUnitedNationsLanguageList();
-
- // Generate the language_list setting as expected by the CKEditor Language
- // plugin, but key the values by the full language name so that we can sort
- // them later on.
- foreach ($predefined_languages as $langcode => $language) {
- $english_name = $language[0];
- $direction = empty($language[2]) ? NULL : $language[2];
- if ($direction === LanguageInterface::DIRECTION_RTL) {
- $language_list[$english_name] = $langcode . ':' . $english_name . ':rtl';
- }
- else {
- $language_list[$english_name] = $langcode . ':' . $english_name;
- }
- }
-
- // Sort on full language name.
- ksort($language_list);
- $config = ['language_list' => array_values($language_list)];
- return $config;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getButtons() {
- $label = $this->t('Language');
- return [
- 'Language' => [
- 'label' => $label,
- 'image_alternative' => [
- '#type' => 'inline_template',
- '#template' => '<a href="#" class="cke-icon-only" role="button" title="' . $label . '" aria-label="' . $label . '"><span class="cke_button_icon cke_button__language_icon">' . $label . '</span></a>',
- ],
- ],
- ];
- }
-
- /**
- * {@inheritdoc}
- */
- public function settingsForm(array $form, FormStateInterface $form_state, Editor $editor) {
- // Defaults.
- $config = ['language_list' => 'un'];
- $settings = $editor->getSettings();
- if (isset($settings['plugins']['language'])) {
- $config = $settings['plugins']['language'];
- }
-
- $predefined_languages = LanguageManager::getStandardLanguageList();
- $form['language_list'] = [
- '#title' => $this->t('Language list'),
- '#title_display' => 'invisible',
- '#type' => 'select',
- '#options' => [
- 'un' => $this->t("United Nations' official languages"),
- 'all' => $this->t('All @count languages', ['@count' => count($predefined_languages)]),
- ],
- '#default_value' => $config['language_list'],
- '#description' => $this->t('The list of languages to show in the language dropdown. The basic list will only show the <a href=":url">six official languages of the UN</a>. The extended list will show all @count languages that are available in Drupal.', [
- ':url' => 'https://www.un.org/en/sections/about-un/official-languages',
- '@count' => count($predefined_languages),
- ]),
- '#attached' => ['library' => ['ckeditor/drupal.ckeditor.language.admin']],
- ];
-
- return $form;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getCssFiles(Editor $editor) {
- return [
- $this->getModulePath('ckeditor') . '/css/plugins/language/ckeditor.language.css',
- ];
- }
-
-}
diff --git a/core/modules/ckeditor/src/Plugin/CKEditorPlugin/StylesCombo.php b/core/modules/ckeditor/src/Plugin/CKEditorPlugin/StylesCombo.php
deleted file mode 100644
index e35c5ac1f51d..000000000000
--- a/core/modules/ckeditor/src/Plugin/CKEditorPlugin/StylesCombo.php
+++ /dev/null
@@ -1,169 +0,0 @@
-<?php
-
-namespace Drupal\ckeditor\Plugin\CKEditorPlugin;
-
-use Drupal\ckeditor\CKEditorPluginBase;
-use Drupal\ckeditor\CKEditorPluginConfigurableInterface;
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\editor\Entity\Editor;
-
-/**
- * Defines the "stylescombo" plugin.
- *
- * @CKEditorPlugin(
- * id = "stylescombo",
- * label = @Translation("Styles dropdown")
- * )
- */
-class StylesCombo extends CKEditorPluginBase implements CKEditorPluginConfigurableInterface {
-
- /**
- * {@inheritdoc}
- */
- public function isInternal() {
- return TRUE;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getFile() {
- // This plugin is already part of Drupal core's CKEditor build.
- return FALSE;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getConfig(Editor $editor) {
- $config = [];
- $settings = $editor->getSettings();
- if (!isset($settings['plugins']['stylescombo']['styles'])) {
- return $config;
- }
- $styles = $settings['plugins']['stylescombo']['styles'];
- $config['stylesSet'] = $this->generateStylesSetSetting($styles);
- return $config;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getButtons() {
- return [
- 'Styles' => [
- 'label' => $this->t('Font style'),
- 'image_alternative' => [
- '#type' => 'inline_template',
- '#template' => '<a href="#" role="button" aria-label="{{ styles_text }}"><span class="ckeditor-button-dropdown">{{ styles_text }}<span class="ckeditor-button-arrow"></span></span></a>',
- '#context' => [
- 'styles_text' => $this->t('Styles'),
- ],
- ],
- ],
- ];
- }
-
- /**
- * {@inheritdoc}
- */
- public function settingsForm(array $form, FormStateInterface $form_state, Editor $editor) {
- // Defaults.
- $config = ['styles' => ''];
- $settings = $editor->getSettings();
- if (isset($settings['plugins']['stylescombo'])) {
- $config = $settings['plugins']['stylescombo'];
- }
-
- $form['styles'] = [
- '#title' => $this->t('Styles'),
- '#title_display' => 'invisible',
- '#type' => 'textarea',
- '#default_value' => $config['styles'],
- '#description' => $this->t('A list of classes that will be provided in the "Styles" dropdown. Enter one or more classes on each line in the format: element.classA.classB|Label. Example: h1.title|Title. Advanced example: h1.fancy.title|Fancy title.<br />These styles should be available in your theme\'s CSS file.'),
- '#attached' => [
- 'library' => ['ckeditor/drupal.ckeditor.stylescombo.admin'],
- ],
- '#element_validate' => [
- [$this, 'validateStylesValue'],
- ],
- ];
-
- return $form;
- }
-
- /**
- * #element_validate handler for the "styles" element in settingsForm().
- */
- public function validateStylesValue(array $element, FormStateInterface $form_state) {
- $styles_setting = $this->generateStylesSetSetting($element['#value']);
- if ($styles_setting === FALSE) {
- $form_state->setError($element, $this->t('The provided list of styles is syntactically incorrect.'));
- }
- else {
- $style_names = array_map(function ($style) {
- return $style['name'];
- }, $styles_setting);
- if (count($style_names) !== count(array_unique($style_names))) {
- $form_state->setError($element, $this->t('Each style must have a unique label.'));
- }
- }
- }
-
- /**
- * Builds the "stylesSet" configuration part of the CKEditor JS settings.
- *
- * @see getConfig()
- *
- * @param string $styles
- * The "styles" setting.
- *
- * @return array|false
- * An array containing the "stylesSet" configuration, or FALSE when the
- * syntax is invalid.
- */
- protected function generateStylesSetSetting($styles) {
- $styles_set = [];
-
- // Early-return when empty.
- $styles = trim($styles);
- if (empty($styles)) {
- return $styles_set;
- }
-
- $styles = str_replace(["\r\n", "\r"], "\n", $styles);
- foreach (explode("\n", $styles) as $style) {
- $style = trim($style);
-
- // Ignore empty lines in between non-empty lines.
- if (empty($style)) {
- continue;
- }
-
- // Validate syntax: element[.class...]|label pattern expected.
- if (!preg_match('@^ *[a-zA-Z0-9-]+ *(\\.[a-zA-Z0-9_-]+ *)*\\| *.+ *$@', $style)) {
- return FALSE;
- }
-
- // Parse.
- [$selector, $label] = explode('|', $style);
- $classes = explode('.', $selector);
- $element = array_shift($classes);
-
- // Build the data structure CKEditor's stylescombo plugin expects.
- // @see https://ckeditor.com/docs/ckeditor4/latest/guide/dev_howtos_styles.html
- $configured_style = [
- 'name' => trim($label),
- 'element' => trim($element),
- ];
- if (!empty($classes)) {
- $configured_style['attributes'] = [
- 'class' => implode(' ', array_map('trim', $classes)),
- ];
- }
- $styles_set[] = $configured_style;
- }
- return $styles_set;
- }
-
-}
diff --git a/core/modules/ckeditor/src/Plugin/Editor/CKEditor.php b/core/modules/ckeditor/src/Plugin/Editor/CKEditor.php
deleted file mode 100644
index eca98b75fed6..000000000000
--- a/core/modules/ckeditor/src/Plugin/Editor/CKEditor.php
+++ /dev/null
@@ -1,491 +0,0 @@
-<?php
-
-namespace Drupal\ckeditor\Plugin\Editor;
-
-use Drupal\Core\Extension\ModuleExtensionList;
-use Drupal\Core\Extension\ModuleHandlerInterface;
-use Drupal\ckeditor\CKEditorPluginManager;
-use Drupal\Core\File\FileUrlGeneratorInterface;
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Language\LanguageManagerInterface;
-use Drupal\Core\Render\Element;
-use Drupal\Core\Render\RendererInterface;
-use Drupal\Core\State\StateInterface;
-use Drupal\editor\Plugin\EditorBase;
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Drupal\editor\Entity\Editor;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * Defines a CKEditor-based text editor for Drupal.
- *
- * @Editor(
- * id = "ckeditor",
- * label = @Translation("CKEditor"),
- * supports_content_filtering = TRUE,
- * supports_inline_editing = TRUE,
- * is_xss_safe = FALSE,
- * supported_element_types = {
- * "textarea"
- * }
- * )
- */
-class CKEditor extends EditorBase implements ContainerFactoryPluginInterface {
-
- /**
- * The module handler to invoke hooks on.
- *
- * @var \Drupal\Core\Extension\ModuleHandlerInterface
- */
- protected $moduleHandler;
-
- /**
- * The language manager.
- *
- * @var \Drupal\Core\Language\LanguageManagerInterface
- */
- protected $languageManager;
-
- /**
- * The CKEditor plugin manager.
- *
- * @var \Drupal\ckeditor\CKEditorPluginManager
- */
- protected $ckeditorPluginManager;
-
- /**
- * The renderer.
- *
- * @var \Drupal\Core\Render\RendererInterface
- */
- protected $renderer;
-
- /**
- * The file URL generator.
- *
- * @var \Drupal\Core\File\FileUrlGeneratorInterface
- */
- protected $fileUrlGenerator;
-
- /**
- * The state key/value store.
- *
- * @var \Drupal\Core\State\StateInterface
- */
- protected $state;
-
- /**
- * The module list service.
- *
- * @var \Drupal\Core\Extension\ModuleExtensionList
- */
- protected $moduleList;
-
- /**
- * Constructs a \Drupal\ckeditor\Plugin\Editor\CKEditor object.
- *
- * @param array $configuration
- * A configuration array containing information about the plugin instance.
- * @param string $plugin_id
- * The plugin_id for the plugin instance.
- * @param mixed $plugin_definition
- * The plugin implementation definition.
- * @param \Drupal\ckeditor\CKEditorPluginManager $ckeditor_plugin_manager
- * The CKEditor plugin manager.
- * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
- * The module handler to invoke hooks on.
- * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
- * The language manager.
- * @param \Drupal\Core\Render\RendererInterface $renderer
- * The renderer.
- * @param \Drupal\Core\State\StateInterface $state
- * The state key/value store.
- * @param \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator
- * The file URL generator.
- * @param \Drupal\Core\Extension\ModuleExtensionList $module_list
- * The module list service.
- */
- public function __construct(array $configuration, $plugin_id, $plugin_definition, CKEditorPluginManager $ckeditor_plugin_manager, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager, RendererInterface $renderer, StateInterface $state, FileUrlGeneratorInterface $file_url_generator, ModuleExtensionList $module_list) {
- parent::__construct($configuration, $plugin_id, $plugin_definition);
- $this->ckeditorPluginManager = $ckeditor_plugin_manager;
- $this->moduleHandler = $module_handler;
- $this->languageManager = $language_manager;
- $this->renderer = $renderer;
- $this->state = $state;
- $this->fileUrlGenerator = $file_url_generator;
- $this->moduleList = $module_list;
- }
-
- /**
- * {@inheritdoc}
- */
- public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
- return new static(
- $configuration,
- $plugin_id,
- $plugin_definition,
- $container->get('plugin.manager.ckeditor.plugin'),
- $container->get('module_handler'),
- $container->get('language_manager'),
- $container->get('renderer'),
- $container->get('state'),
- $container->get('file_url_generator'),
- $container->get('extension.list.module')
- );
- }
-
- /**
- * {@inheritdoc}
- */
- public function getDefaultSettings() {
- return [
- 'toolbar' => [
- 'rows' => [
- // Button groups.
- [
- [
- 'name' => $this->t('Formatting'),
- 'items' => ['Bold', 'Italic'],
- ],
- [
- 'name' => $this->t('Links'),
- 'items' => ['DrupalLink', 'DrupalUnlink'],
- ],
- [
- 'name' => $this->t('Lists'),
- 'items' => ['BulletedList', 'NumberedList'],
- ],
- [
- 'name' => $this->t('Media'),
- 'items' => ['Blockquote', 'DrupalImage'],
- ],
- [
- 'name' => $this->t('Tools'),
- 'items' => ['Source'],
- ],
- ],
- ],
- ],
- 'plugins' => [],
- ];
- }
-
- /**
- * {@inheritdoc}
- */
- public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
- $editor = $form_state->get('editor');
- $settings = $editor->getSettings();
-
- $ckeditor_settings_toolbar = [
- '#theme' => 'ckeditor_settings_toolbar',
- '#editor' => $editor,
- '#plugins' => $this->ckeditorPluginManager->getButtons(),
- ];
- $form['toolbar'] = [
- '#type' => 'container',
- '#attached' => [
- 'library' => ['ckeditor/drupal.ckeditor.admin'],
- 'drupalSettings' => [
- 'ckeditor' => [
- 'toolbarAdmin' => (string) $this->renderer->renderPlain($ckeditor_settings_toolbar),
- ],
- ],
- ],
- '#attributes' => ['class' => ['ckeditor-toolbar-configuration']],
- ];
-
- $form['toolbar']['button_groups'] = [
- '#type' => 'textarea',
- '#title' => $this->t('Toolbar buttons'),
- '#default_value' => json_encode($settings['toolbar']['rows']),
- '#attributes' => ['class' => ['ckeditor-toolbar-textarea']],
- ];
-
- // CKEditor plugin settings, if any.
- $form['plugin_settings'] = [
- '#type' => 'vertical_tabs',
- '#title' => $this->t('CKEditor plugin settings'),
- '#attributes' => [
- 'id' => 'ckeditor-plugin-settings',
- ],
- ];
- $this->ckeditorPluginManager->injectPluginSettingsForm($form, $form_state, $editor);
- if (count(Element::children($form['plugins'])) === 0) {
- unset($form['plugins']);
- unset($form['plugin_settings']);
- }
-
- // Hidden CKEditor instance. We need a hidden CKEditor instance with all
- // plugins enabled, so we can retrieve CKEditor's per-feature metadata (on
- // which tags, attributes, styles and classes are enabled). This metadata is
- // necessary for certain filters' (for instance, the html_filter filter)
- // settings to be updated accordingly.
- // Get a list of all external plugins and their corresponding files.
- $plugins = array_keys($this->ckeditorPluginManager->getDefinitions());
- $all_external_plugins = [];
- foreach ($plugins as $plugin_id) {
- $plugin = $this->ckeditorPluginManager->createInstance($plugin_id);
- if (!$plugin->isInternal()) {
- $all_external_plugins[$plugin_id] = $plugin->getFile();
- }
- }
- // Get a list of all buttons that are provided by all plugins.
- $all_buttons = array_reduce($this->ckeditorPluginManager->getButtons(), function ($result, $item) {
- return array_merge($result, array_keys($item));
- }, []);
- // Build a fake Editor object, which we'll use to generate JavaScript
- // settings for this fake Editor instance.
- $fake_editor = Editor::create([
- 'format' => $editor->id(),
- 'editor' => 'ckeditor',
- 'settings' => [
- // Single toolbar row, single button group, all existing buttons.
- 'toolbar' => [
- 'rows' => [
- 0 => [
- 0 => [
- 'name' => 'All existing buttons',
- 'items' => $all_buttons,
- ],
- ],
- ],
- ],
- 'plugins' => $settings['plugins'],
- ],
- ]);
- $config = $this->getJSSettings($fake_editor);
- // Remove the ACF configuration that is generated based on filter settings,
- // because otherwise we cannot retrieve per-feature metadata.
- unset($config['allowedContent']);
- $form['hidden_ckeditor'] = [
- '#markup' => '<div id="ckeditor-hidden" class="hidden"></div>',
- '#attached' => [
- 'drupalSettings' => ['ckeditor' => ['hiddenCKEditorConfig' => $config]],
- ],
- ];
-
- return $form;
- }
-
- /**
- * {@inheritdoc}
- */
- public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
- }
-
- /**
- * {@inheritdoc}
- */
- public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
- // The rows key is not built into the form structure, so decode the button
- // groups data into this new key and remove the button_groups key.
- $form_state->setValue(['toolbar', 'rows'], json_decode($form_state->getValue(['toolbar', 'button_groups']), TRUE));
- $form_state->unsetValue(['toolbar', 'button_groups']);
-
- // Remove the plugin settings' vertical tabs state; no need to save that.
- if ($form_state->hasValue('plugins')) {
- $form_state->unsetValue('plugin_settings');
- }
-
- // Ensure plugin settings are only saved for plugins that are actually
- // enabled.
- $about_to_be_saved_editor = Editor::create([
- 'editor' => 'ckeditor',
- 'settings' => [
- 'toolbar' => $form_state->getValue('toolbar'),
- 'plugins' => $form_state->getValue('plugins'),
- ],
- ]);
- $enabled_plugins = _ckeditor_get_enabled_plugins($about_to_be_saved_editor);
- $plugin_settings = $form_state->getValue('plugins', []);
- foreach (array_keys($plugin_settings) as $plugin_id) {
- if (!in_array($plugin_id, $enabled_plugins, TRUE)) {
- unset($plugin_settings[$plugin_id]);
- }
- }
- $form_state->setValue('plugins', $plugin_settings);
- }
-
- /**
- * {@inheritdoc}
- */
- public function getJSSettings(Editor $editor) {
- $settings = [];
-
- // Get the settings for all enabled plugins, even the internal ones.
- $enabled_plugins = array_keys($this->ckeditorPluginManager->getEnabledPluginFiles($editor, TRUE));
- foreach ($enabled_plugins as $plugin_id) {
- $plugin = $this->ckeditorPluginManager->createInstance($plugin_id);
- $settings += $plugin->getConfig($editor);
- }
-
- // Fall back on English if no matching language code was found.
- $display_langcode = 'en';
-
- // Map the interface language code to a CKEditor translation if interface
- // translation is enabled.
- if ($this->moduleHandler->moduleExists('locale')) {
- $ckeditor_langcodes = $this->getLangcodes();
- $language_interface = $this->languageManager->getCurrentLanguage();
- if (isset($ckeditor_langcodes[$language_interface->getId()])) {
- $display_langcode = $ckeditor_langcodes[$language_interface->getId()];
- }
- }
-
- // Next, set the most fundamental CKEditor settings.
- $external_plugin_files = $this->ckeditorPluginManager->getEnabledPluginFiles($editor);
- $settings += [
- 'toolbar' => $this->buildToolbarJSSetting($editor),
- 'contentsCss' => $this->buildContentsCssJSSetting($editor),
- 'extraPlugins' => implode(',', array_keys($external_plugin_files)),
- 'language' => $display_langcode,
- // Configure CKEditor to not load styles.js. The StylesCombo plugin will
- // set stylesSet according to the user's settings, if the "Styles" button
- // is enabled. We cannot get rid of this until CKEditor will stop loading
- // styles.js by default.
- // See http://dev.ckeditor.com/ticket/9992#comment:9.
- 'stylesSet' => FALSE,
- ];
-
- // Finally, set Drupal-specific CKEditor settings.
- $settings += [
- 'drupalExternalPlugins' => array_map([$this->fileUrlGenerator, 'generateString'], $external_plugin_files),
- ];
-
- // Parse all CKEditor plugin JavaScript files for translations.
- if ($this->moduleHandler->moduleExists('locale')) {
- locale_js_translate(array_values($external_plugin_files), $language_interface);
- }
-
- ksort($settings);
-
- return $settings;
- }
-
- /**
- * Returns a list of language codes supported by CKEditor.
- *
- * @return array
- * An associative array keyed by language codes.
- */
- public function getLangcodes() {
- // Cache the file system based language list calculation because this would
- // be expensive to calculate all the time. The cache is cleared on core
- // upgrades which is the only situation the CKEditor file listing should
- // change.
- $langcode_cache = \Drupal::cache()->get('ckeditor.langcodes');
- if (!empty($langcode_cache)) {
- $langcodes = $langcode_cache->data;
- }
- if (empty($langcodes)) {
- $langcodes = [];
- // Collect languages included with CKEditor based on file listing.
- $files = scandir('core/assets/vendor/ckeditor/lang');
- foreach ($files as $file) {
- if ($file[0] !== '.' && preg_match('/\.js$/', $file)) {
- $langcode = basename($file, '.js');
- $langcodes[$langcode] = $langcode;
- }
- }
- \Drupal::cache()->set('ckeditor.langcodes', $langcodes);
- }
-
- // Get language mapping if available to map to Drupal language codes.
- // This is configurable in the user interface and not expensive to get, so
- // we don't include it in the cached language list.
- $language_mappings = $this->moduleHandler->moduleExists('language') ? language_get_browser_drupal_langcode_mappings() : [];
- foreach ($langcodes as $langcode) {
- // If this language code is available in a Drupal mapping, use that to
- // compute a possibility for matching from the Drupal langcode to the
- // CKEditor langcode.
- // For instance, CKEditor uses the langcode 'no' for Norwegian, Drupal
- // uses 'nb'. This would then remove the 'no' => 'no' mapping and replace
- // it with 'nb' => 'no'. Now Drupal knows which CKEditor translation to
- // load.
- if (isset($language_mappings[$langcode]) && !isset($langcodes[$language_mappings[$langcode]])) {
- $langcodes[$language_mappings[$langcode]] = $langcode;
- unset($langcodes[$langcode]);
- }
- }
-
- return $langcodes;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getLibraries(Editor $editor) {
- $libraries = [
- 'ckeditor/drupal.ckeditor',
- ];
-
- // Get the required libraries for any enabled plugins.
- $enabled_plugins = array_keys($this->ckeditorPluginManager->getEnabledPluginFiles($editor));
- foreach ($enabled_plugins as $plugin_id) {
- $plugin = $this->ckeditorPluginManager->createInstance($plugin_id);
- $additional_libraries = array_diff($plugin->getLibraries($editor), $libraries);
- $libraries = array_merge($libraries, $additional_libraries);
- }
-
- return $libraries;
- }
-
- /**
- * Builds the "toolbar" configuration part of the CKEditor JS settings.
- *
- * @see getJSSettings()
- *
- * @param \Drupal\editor\Entity\Editor $editor
- * A configured text editor object.
- *
- * @return array
- * An array containing the "toolbar" configuration.
- */
- public function buildToolbarJSSetting(Editor $editor) {
- $toolbar = [];
-
- $settings = $editor->getSettings();
- foreach ($settings['toolbar']['rows'] as $row) {
- foreach ($row as $group) {
- $toolbar[] = $group;
- }
- $toolbar[] = '/';
- }
- return $toolbar;
- }
-
- /**
- * Builds the "contentsCss" configuration part of the CKEditor JS settings.
- *
- * @see getJSSettings()
- *
- * @param \Drupal\editor\Entity\Editor $editor
- * A configured text editor object.
- *
- * @return array
- * An array containing the "contentsCss" configuration.
- */
- public function buildContentsCssJSSetting(Editor $editor) {
- $css = [
- $this->moduleList->getPath('ckeditor') . '/css/ckeditor-iframe.css',
- $this->moduleList->getPath('system') . '/css/components/align.module.css',
- ];
- $this->moduleHandler->alter('ckeditor_css', $css, $editor);
- // Get a list of all enabled plugins' iframe instance CSS files.
- $plugins_css = array_reduce($this->ckeditorPluginManager->getCssFiles($editor), function ($result, $item) {
- return array_merge($result, array_values($item));
- }, []);
- $css = array_merge($css, $plugins_css);
- $css = array_merge($css, _ckeditor_theme_css());
- $query_string = $this->state->get('system.css_js_query_string', '0');
- $css = array_map(function ($item) use ($query_string) {
- $query_string_separator = (strpos($item, '?') !== FALSE) ? '&' : '?';
- return $item . $query_string_separator . $query_string;
- }, $css);
- $css = array_map([$this->fileUrlGenerator, 'generateString'], $css);
-
- return array_values($css);
- }
-
-}
diff --git a/core/modules/ckeditor/templates/ckeditor-settings-toolbar.html.twig b/core/modules/ckeditor/templates/ckeditor-settings-toolbar.html.twig
deleted file mode 100644
index 3636bf06c543..000000000000
--- a/core/modules/ckeditor/templates/ckeditor-settings-toolbar.html.twig
+++ /dev/null
@@ -1,75 +0,0 @@
-{#
-/**
- * @file
- * Default theme implementation for the CKEditor settings toolbar.
- *
- * Available variables:
- * - multiple_buttons: A list of buttons that may be added multiple times.
- * - disabled_buttons: A list of disabled buttons.
- * - active_buttons: A list of active button rows.
- *
- * @see template_preprocess_ckeditor_settings_toolbar()
- *
- * @ingroup themeable
- */
-#}
-{% apply spaceless %}
- <fieldset role="form" aria-labelledby="ckeditor-button-configuration ckeditor-button-description">
- <legend id="ckeditor-button-configuration">{{ 'Toolbar configuration'|t }}</legend>
- <div class="fieldset-wrapper">
- <div id="ckeditor-button-description" class="fieldset-description">
- {%- trans -%}
- Move a button into the <em>Active toolbar</em> to enable it, or into the list of <em>Available buttons</em> to disable it. Buttons may be moved with the mouse or keyboard arrow keys. Toolbar group names are provided to support screen reader users. Empty toolbar groups will be removed upon save.
- {%- endtrans -%}
- </div>
- <div class="ckeditor-toolbar-disabled clearfix">
- {# Available buttons. #}
- <div class="ckeditor-toolbar-available">
- <label for="ckeditor-available-buttons">{{ 'Available buttons'|t }}</label>
- <ul id="ckeditor-available-buttons" class="ckeditor-buttons clearfix" role="form" data-drupal-ckeditor-button-sorting="source">
- {% for disabled_button in disabled_buttons %}
- <li{{ disabled_button.attributes.addClass('ckeditor-button') }}>{{ disabled_button.value }}</li>
- {% endfor %}
- </ul>
- </div>
- {# Dividers. #}
- <div class="ckeditor-toolbar-dividers">
- <label for="ckeditor-multiple-buttons">{{ 'Button divider'|t }}</label>
- <ul id="ckeditor-multiple-buttons" class="ckeditor-multiple-buttons" role="form" data-drupal-ckeditor-button-sorting="dividers">
- {% for multiple_button in multiple_buttons %}
- <li{{ multiple_button.attributes.addClass('ckeditor-multiple-button') }}>{{ multiple_button.value }}</li>
- {% endfor %}
- </ul>
- </div>
- </div>
- {# Active toolbar. #}
- <div class="clearfix">
- <label id="ckeditor-active-toolbar">{{ 'Active toolbar'|t }}</label>
- </div>
- <div data-toolbar="active" role="form" class="ckeditor-toolbar ckeditor-toolbar-active clearfix">
- <ul class="ckeditor-active-toolbar-configuration" role="presentation" aria-label="{{ 'CKEditor toolbar and button configuration.'|t }}">
- {% for button_row in active_buttons %}
- <li class="ckeditor-row" role="group" aria-labelledby="ckeditor-active-toolbar">
- <ul class="ckeditor-toolbar-groups clearfix">
- {% for group_name, button_group in button_row %}
- <li class="ckeditor-toolbar-group" role="presentation" data-drupal-ckeditor-type="group" data-drupal-ckeditor-toolbar-group-name="{{ group_name }}" tabindex="0">
- <h3 class="ckeditor-toolbar-group-name" id="ckeditor-toolbar-group-aria-label-for-{{ button_group.group_name_class }}">{{ group_name }}</h3>
- <ul class="ckeditor-buttons ckeditor-toolbar-group-buttons" role="toolbar" data-drupal-ckeditor-button-sorting="target" aria-labelledby="ckeditor-toolbar-group-aria-label-for-{{ button_group.group_name_class }}">
- {% for active_button in button_group.buttons %}
- <li{{ active_button.attributes.addClass(active_button.multiple ? 'ckeditor-multiple-button' : 'ckeditor-button') }}>{{ active_button.value }}</li>
- {% endfor %}
- </ul>
- </li>
- {% endfor %}
- </ul>
- </li>
- {% else %}
- <li>
- <ul class="ckeditor-buttons"></ul>
- </li>
- {% endfor %}
- </ul>
- </div>
- </div>
- </fieldset>
-{% endapply %}
diff --git a/core/modules/ckeditor/tests/modules/ckeditor_test.info.yml b/core/modules/ckeditor/tests/modules/ckeditor_test.info.yml
deleted file mode 100644
index 6ab5b9aea699..000000000000
--- a/core/modules/ckeditor/tests/modules/ckeditor_test.info.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-name: CKEditor test
-type: module
-description: Support module for the CKEditor module tests.
-package: Testing
-version: VERSION
diff --git a/core/modules/ckeditor/tests/modules/ckeditor_test.libraries.yml b/core/modules/ckeditor/tests/modules/ckeditor_test.libraries.yml
deleted file mode 100644
index 82bd7eee4e29..000000000000
--- a/core/modules/ckeditor/tests/modules/ckeditor_test.libraries.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-ajax_css:
- js:
- js/ajax-css.js: {}
- dependencies:
- - ckeditor/drupal.ckeditor
diff --git a/core/modules/ckeditor/tests/modules/ckeditor_test.module b/core/modules/ckeditor/tests/modules/ckeditor_test.module
deleted file mode 100644
index 44e68834aa30..000000000000
--- a/core/modules/ckeditor/tests/modules/ckeditor_test.module
+++ /dev/null
@@ -1,15 +0,0 @@
-<?php
-
-/**
- * @file
- * Helper module for the CKEditor tests.
- */
-
-use Drupal\editor\Entity\Editor;
-
-/**
- * Implements hook_ckeditor_css_alter().
- */
-function ckeditor_test_ckeditor_css_alter(array &$css, Editor $editor) {
- $css[] = \Drupal::service('extension.list.module')->getPath('ckeditor_test') . '/ckeditor_test.css';
-}
diff --git a/core/modules/ckeditor/tests/modules/ckeditor_test.routing.yml b/core/modules/ckeditor/tests/modules/ckeditor_test.routing.yml
deleted file mode 100644
index 01aeca0e2dfb..000000000000
--- a/core/modules/ckeditor/tests/modules/ckeditor_test.routing.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-ckeditor_test.ajax_css:
- path: '/ckeditor_test/ajax_css'
- defaults:
- _title: 'AJAX CSS Test'
- _form: '\Drupal\ckeditor_test\Form\AjaxCssForm'
- requirements:
- _access: 'TRUE'
-
-ckeditor_test.off_canvas:
- path: '/ckeditor_test/off_canvas'
- defaults:
- _controller: '\Drupal\ckeditor_test\CkeditorOffCanvasTestController::testOffCanvas'
- requirements:
- _access: 'TRUE'
diff --git a/core/modules/ckeditor/tests/modules/config/schema/ckeditor_test.schema.yml b/core/modules/ckeditor/tests/modules/config/schema/ckeditor_test.schema.yml
deleted file mode 100644
index e7305a29181a..000000000000
--- a/core/modules/ckeditor/tests/modules/config/schema/ckeditor_test.schema.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-ckeditor.plugin.llama_contextual_and_button:
- type: mapping
- label: 'Contextual Llama With Button'
- mapping:
- ultra_llama_mode:
- type: boolean
- label: 'Ultra llama mode'
-
-filter_settings.test_attribute_filter:
- type: filter
- label: 'Test Attribute Filter'
- mapping:
- tags:
- type: sequence
- sequence:
- type: string
diff --git a/core/modules/ckeditor/tests/modules/css/test.css b/core/modules/ckeditor/tests/modules/css/test.css
deleted file mode 100644
index 60f1eab97137..000000000000
--- a/core/modules/ckeditor/tests/modules/css/test.css
+++ /dev/null
@@ -1,3 +0,0 @@
-body {
- color: red;
-}
diff --git a/core/modules/ckeditor/tests/modules/js/ajax-css.es6.js b/core/modules/ckeditor/tests/modules/js/ajax-css.es6.js
deleted file mode 100644
index 3f36758539da..000000000000
--- a/core/modules/ckeditor/tests/modules/js/ajax-css.es6.js
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * @file
- * Contains client-side code for testing CSS delivered to CKEditor via AJAX.
- */
-
-(function (Drupal, ckeditor, editorSettings, $) {
- Drupal.behaviors.ajaxCssForm = {
- attach(context) {
- // Initialize an inline CKEditor on the #edit-inline element if it
- // isn't editable already.
- $(context)
- .find('#edit-inline')
- .not('[contenteditable]')
- .each(function () {
- ckeditor.attachInlineEditor(this, editorSettings.formats.test_format);
- });
- },
- };
-})(Drupal, Drupal.editors.ckeditor, drupalSettings.editor, jQuery);
diff --git a/core/modules/ckeditor/tests/modules/js/ajax-css.js b/core/modules/ckeditor/tests/modules/js/ajax-css.js
deleted file mode 100644
index 9fc31459985b..000000000000
--- a/core/modules/ckeditor/tests/modules/js/ajax-css.js
+++ /dev/null
@@ -1,17 +0,0 @@
-/**
-* DO NOT EDIT THIS FILE.
-* See the following change record for more information,
-* https://www.drupal.org/node/2815083
-* @preserve
-**/
-
-(function (Drupal, ckeditor, editorSettings, $) {
- Drupal.behaviors.ajaxCssForm = {
- attach(context) {
- $(context).find('#edit-inline').not('[contenteditable]').each(function () {
- ckeditor.attachInlineEditor(this, editorSettings.formats.test_format);
- });
- }
-
- };
-})(Drupal, Drupal.editors.ckeditor, drupalSettings.editor, jQuery); \ No newline at end of file
diff --git a/core/modules/ckeditor/tests/modules/src/CkeditorOffCanvasTestController.php b/core/modules/ckeditor/tests/modules/src/CkeditorOffCanvasTestController.php
deleted file mode 100644
index 7f02ced45a2c..000000000000
--- a/core/modules/ckeditor/tests/modules/src/CkeditorOffCanvasTestController.php
+++ /dev/null
@@ -1,33 +0,0 @@
-<?php
-
-namespace Drupal\ckeditor_test;
-
-use Drupal\Core\Url;
-
-/**
- * Provides controller for testing CKEditor in off-canvas dialogs.
- */
-class CkeditorOffCanvasTestController {
-
- /**
- * Returns a link that can open a node add form in an off-canvas dialog.
- *
- * @return array
- * A render array.
- */
- public function testOffCanvas() {
- $build['link'] = [
- '#type' => 'link',
- '#title' => 'Add Node',
- '#url' => Url::fromRoute('node.add', ['node_type' => 'page']),
- '#attributes' => [
- 'class' => ['use-ajax'],
- 'data-dialog-type' => 'dialog',
- 'data-dialog-renderer' => 'off_canvas',
- ],
- ];
- $build['#attached']['library'][] = 'core/drupal.dialog.off_canvas';
- return $build;
- }
-
-}
diff --git a/core/modules/ckeditor/tests/modules/src/Form/AjaxCssForm.php b/core/modules/ckeditor/tests/modules/src/Form/AjaxCssForm.php
deleted file mode 100644
index fd8ef0e58d77..000000000000
--- a/core/modules/ckeditor/tests/modules/src/Form/AjaxCssForm.php
+++ /dev/null
@@ -1,138 +0,0 @@
-<?php
-
-namespace Drupal\ckeditor_test\Form;
-
-use Drupal\ckeditor\Ajax\AddStyleSheetCommand;
-use Drupal\Core\Ajax\AjaxResponse;
-use Drupal\Core\File\FileUrlGeneratorInterface;
-use Drupal\Core\Form\FormBase;
-use Drupal\Core\Form\FormStateInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * A form for testing delivery of CSS to CKEditor via AJAX.
- *
- * @internal
- */
-class AjaxCssForm extends FormBase {
-
- /**
- * The file URL generator.
- *
- * @var \Drupal\Core\File\FileUrlGeneratorInterface
- */
- protected $fileUrlGenerator;
-
- /**
- * Constructs an AjaxCssForm.
- *
- * @param \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator
- * The file URL generator.
- */
- public function __construct(FileUrlGeneratorInterface $file_url_generator) {
- $this->fileUrlGenerator = $file_url_generator;
- }
-
- /**
- * @inheritDoc
- */
- public static function create(ContainerInterface $container) {
- return new static($container->get('file_url_generator'));
- }
-
- /**
- * {@inheritdoc}
- */
- public function getFormId() {
- return 'ckeditor_test_ajax_css_form';
- }
-
- /**
- * {@inheritdoc}
- */
- public function buildForm(array $form, FormStateInterface $form_state) {
- // Create an inline and iframe CKEditor instance so we can test against
- // both.
- $form['inline'] = [
- '#type' => 'container',
- '#attached' => [
- 'library' => [
- 'ckeditor_test/ajax_css',
- ],
- ],
- '#children' => $this->t('Here be dragons.'),
- ];
- $form['iframe'] = [
- '#type' => 'text_format',
- '#default_value' => $this->t('Here be llamas.'),
- ];
-
- // A pair of buttons to trigger the AJAX events.
- $form['actions'] = [
- 'css_inline' => [
- '#type' => 'submit',
- '#value' => $this->t('Add CSS to inline CKEditor instance'),
- '#ajax' => [
- 'callback' => [$this, 'addCssInline'],
- ],
- ],
- 'css_frame' => [
- '#type' => 'submit',
- '#value' => $this->t('Add CSS to iframe CKEditor instance'),
- '#ajax' => [
- 'callback' => [$this, 'addCssIframe'],
- ],
- ],
- '#type' => 'actions',
- ];
-
- return $form;
- }
-
- /**
- * {@inheritdoc}
- */
- public function submitForm(array &$form, FormStateInterface $form_state) {
- // Nothing to do here.
- }
-
- /**
- * Generates an AJAX response to add CSS to a CKEditor Text Editor instance.
- *
- * @param string $editor_id
- * The Text Editor instance ID.
- *
- * @return \Drupal\Core\Ajax\AjaxResponse
- * An AJAX response.
- */
- protected function generateResponse($editor_id) {
- // Build a URL to the style sheet that will be added.
- $url = \Drupal::service('extension.list.module')->getPath('ckeditor_test') . '/css/test.css';
- $url = $this->fileUrlGenerator->generateString($url);
-
- $response = new AjaxResponse();
- return $response
- ->addCommand(new AddStyleSheetCommand($editor_id, [$url]));
- }
-
- /**
- * Handles the AJAX request to add CSS to the inline editor.
- *
- * @return \Drupal\Core\Ajax\AjaxResponse
- * An AJAX response.
- */
- public function addCssInline() {
- return $this->generateResponse('edit-inline');
- }
-
- /**
- * Handles the AJAX request to add CSS to the iframe editor.
- *
- * @return \Drupal\Core\Ajax\AjaxResponse
- * An AJAX response.
- */
- public function addCssIframe() {
- return $this->generateResponse('edit-iframe-value');
- }
-
-}
diff --git a/core/modules/ckeditor/tests/modules/src/Plugin/CKEditorPlugin/Llama.php b/core/modules/ckeditor/tests/modules/src/Plugin/CKEditorPlugin/Llama.php
deleted file mode 100644
index b724ff159252..000000000000
--- a/core/modules/ckeditor/tests/modules/src/Plugin/CKEditorPlugin/Llama.php
+++ /dev/null
@@ -1,86 +0,0 @@
-<?php
-
-namespace Drupal\ckeditor_test\Plugin\CKEditorPlugin;
-
-use Drupal\ckeditor\CKEditorPluginInterface;
-use Drupal\Component\Plugin\PluginBase;
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Drupal\editor\Entity\Editor;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * Defines the "Llama" plugin, with a CKEditor "llama" feature.
- *
- * This feature does not correspond to a toolbar button. Because this plugin
- * does not implement the CKEditorPluginContextualInterface nor the
- * CKEditorPluginButtonsInterface interface, there is no way of actually loading
- * this plugin.
- *
- * @see \Drupal\ckeditor_test\Plugin\CKEditorPlugin\LlamaContextual
- * @see \Drupal\ckeditor_test\Plugin\CKEditorPlugin\LlamaButton
- * @see \Drupal\ckeditor_test\Plugin\CKEditorPlugin\LlamaContextualAndButton
- * @see \Drupal\ckeditor_test\Plugin\CKEditorPlugin\LlamaCss
- *
- * @CKEditorPlugin(
- * id = "llama",
- * label = @Translation("Llama")
- * )
- */
-class Llama extends PluginBase implements CKEditorPluginInterface, ContainerFactoryPluginInterface {
-
- /**
- * The module list service.
- *
- * @var \Drupal\Core\Extension\ModuleExtensionList
- */
- protected $moduleList;
-
- /**
- * {@inheritdoc}
- */
- public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
- $instance = new static(
- $configuration,
- $plugin_id,
- $plugin_definition,
- );
- $instance->moduleList = $container->get('extension.list.module');
- return $instance;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getDependencies(Editor $editor) {
- return [];
- }
-
- /**
- * {@inheritdoc}
- */
- public function getLibraries(Editor $editor) {
- return [];
- }
-
- /**
- * {@inheritdoc}
- */
- public function isInternal() {
- return FALSE;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getFile() {
- return $this->moduleList->getPath('ckeditor_test') . '/js/llama.js';
- }
-
- /**
- * {@inheritdoc}
- */
- public function getConfig(Editor $editor) {
- return [];
- }
-
-}
diff --git a/core/modules/ckeditor/tests/modules/src/Plugin/CKEditorPlugin/LlamaButton.php b/core/modules/ckeditor/tests/modules/src/Plugin/CKEditorPlugin/LlamaButton.php
deleted file mode 100644
index 646eaa6d25da..000000000000
--- a/core/modules/ckeditor/tests/modules/src/Plugin/CKEditorPlugin/LlamaButton.php
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-
-namespace Drupal\ckeditor_test\Plugin\CKEditorPlugin;
-
-use Drupal\ckeditor\CKEditorPluginButtonsInterface;
-
-/**
- * Defines a "LlamaButton" plugin, with a toolbar builder-enabled "llama" feature.
- *
- * @CKEditorPlugin(
- * id = "llama_button",
- * label = @Translation("Llama Button")
- * )
- */
-class LlamaButton extends Llama implements CKEditorPluginButtonsInterface {
-
- /**
- * {@inheritdoc}
- */
- public function getButtons() {
- return [
- 'Llama' => [
- 'label' => t('Insert Llama'),
- ],
- ];
- }
-
- /**
- * {@inheritdoc}
- */
- public function getFile() {
- return $this->moduleList->getPath('ckeditor_test') . '/js/llama_button.js';
- }
-
-}
diff --git a/core/modules/ckeditor/tests/modules/src/Plugin/CKEditorPlugin/LlamaContextual.php b/core/modules/ckeditor/tests/modules/src/Plugin/CKEditorPlugin/LlamaContextual.php
deleted file mode 100644
index 4fcbdb142b36..000000000000
--- a/core/modules/ckeditor/tests/modules/src/Plugin/CKEditorPlugin/LlamaContextual.php
+++ /dev/null
@@ -1,41 +0,0 @@
-<?php
-
-namespace Drupal\ckeditor_test\Plugin\CKEditorPlugin;
-
-use Drupal\ckeditor\CKEditorPluginContextualInterface;
-use Drupal\editor\Entity\Editor;
-
-/**
- * Defines a "Llama" plugin, with a contextually enabled "llama" feature.
- *
- * @CKEditorPlugin(
- * id = "llama_contextual",
- * label = @Translation("Contextual Llama")
- * )
- */
-class LlamaContextual extends Llama implements CKEditorPluginContextualInterface {
-
- /**
- * {@inheritdoc}
- */
- public function isEnabled(Editor $editor) {
- // Automatically enable this plugin if the Underline button is enabled.
- $settings = $editor->getSettings();
- foreach ($settings['toolbar']['rows'] as $row) {
- foreach ($row as $group) {
- if (in_array('Strike', $group['items'])) {
- return TRUE;
- }
- }
- }
- return FALSE;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getFile() {
- return $this->moduleList->getPath('ckeditor_test') . '/js/llama_contextual.js';
- }
-
-}
diff --git a/core/modules/ckeditor/tests/modules/src/Plugin/CKEditorPlugin/LlamaContextualAndButton.php b/core/modules/ckeditor/tests/modules/src/Plugin/CKEditorPlugin/LlamaContextualAndButton.php
deleted file mode 100644
index 3033904f2952..000000000000
--- a/core/modules/ckeditor/tests/modules/src/Plugin/CKEditorPlugin/LlamaContextualAndButton.php
+++ /dev/null
@@ -1,76 +0,0 @@
-<?php
-
-namespace Drupal\ckeditor_test\Plugin\CKEditorPlugin;
-
-use Drupal\ckeditor\CKEditorPluginButtonsInterface;
-use Drupal\ckeditor\CKEditorPluginContextualInterface;
-use Drupal\ckeditor\CKEditorPluginConfigurableInterface;
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\editor\Entity\Editor;
-
-/**
- * Defines a "LlamaContextualAndButton" plugin, with a contextually OR toolbar
- * builder-enabled "llama" feature.
- *
- * @CKEditorPlugin(
- * id = "llama_contextual_and_button",
- * label = @Translation("Contextual Llama With Button")
- * )
- */
-class LlamaContextualAndButton extends Llama implements CKEditorPluginContextualInterface, CKEditorPluginButtonsInterface, CKEditorPluginConfigurableInterface {
-
- /**
- * {@inheritdoc}
- */
- public function isEnabled(Editor $editor) {
- // Automatically enable this plugin if the Strike button is enabled.
- $settings = $editor->getSettings();
- foreach ($settings['toolbar']['rows'] as $row) {
- foreach ($row as $group) {
- if (in_array('Strike', $group['items'])) {
- return TRUE;
- }
- }
- }
- return FALSE;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getButtons() {
- return [
- 'Llama' => [
- 'label' => t('Insert Llama'),
- ],
- ];
- }
-
- /**
- * {@inheritdoc}
- */
- public function getFile() {
- return $this->moduleList->getPath('ckeditor_test') . '/js/llama_contextual_and_button.js';
- }
-
- /**
- * {@inheritdoc}
- */
- public function settingsForm(array $form, FormStateInterface $form_state, Editor $editor) {
- // Defaults.
- $config = ['ultra_llama_mode' => FALSE];
- $settings = $editor->getSettings();
- if (isset($settings['plugins']['llama_contextual_and_button'])) {
- $config = $settings['plugins']['llama_contextual_and_button'];
- }
-
- $form['ultra_llama_mode'] = [
- '#title' => t('Ultra llama mode'),
- '#type' => 'checkbox',
- '#default_value' => $config['ultra_llama_mode'],
- ];
-
- return $form;
- }
-
-}
diff --git a/core/modules/ckeditor/tests/modules/src/Plugin/CKEditorPlugin/LlamaCss.php b/core/modules/ckeditor/tests/modules/src/Plugin/CKEditorPlugin/LlamaCss.php
deleted file mode 100644
index 23ffc8b5c96d..000000000000
--- a/core/modules/ckeditor/tests/modules/src/Plugin/CKEditorPlugin/LlamaCss.php
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-
-namespace Drupal\ckeditor_test\Plugin\CKEditorPlugin;
-
-use Drupal\ckeditor\CKEditorPluginButtonsInterface;
-use Drupal\ckeditor\CKEditorPluginCssInterface;
-use Drupal\editor\Entity\Editor;
-
-/**
- * Defines a "LlamaCss" plugin, with an associated "llama" CSS.
- *
- * @CKEditorPlugin(
- * id = "llama_css",
- * label = @Translation("Llama CSS")
- * )
- */
-class LlamaCss extends Llama implements CKEditorPluginButtonsInterface, CKEditorPluginCssInterface {
-
- /**
- * {@inheritdoc}
- */
- public function getButtons() {
- return [
- 'LlamaCSS' => [
- 'label' => t('Insert Llama CSS'),
- ],
- ];
- }
-
- /**
- * {@inheritdoc}
- */
- public function getCssFiles(Editor $editor) {
- return [
- $this->moduleList->getPath('ckeditor_test') . '/css/llama.css',
- ];
- }
-
- /**
- * {@inheritdoc}
- */
- public function getFile() {
- return $this->moduleList->getPath('ckeditor_test') . '/js/llama_css.js';
- }
-
-}
diff --git a/core/modules/ckeditor/tests/modules/src/Plugin/Filter/TestAttributeFilter.php b/core/modules/ckeditor/tests/modules/src/Plugin/Filter/TestAttributeFilter.php
deleted file mode 100644
index 885877b4b3a1..000000000000
--- a/core/modules/ckeditor/tests/modules/src/Plugin/Filter/TestAttributeFilter.php
+++ /dev/null
@@ -1,38 +0,0 @@
-<?php
-
-namespace Drupal\ckeditor_test\Plugin\Filter;
-
-use Drupal\Component\Utility\Html;
-use Drupal\filter\FilterProcessResult;
-use Drupal\filter\Plugin\FilterBase;
-
-/**
- * A filter that adds a test attribute to any configured HTML tags.
- *
- * @Filter(
- * id = "test_attribute_filter",
- * title = @Translation("Test Attribute Filter"),
- * type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_REVERSIBLE,
- * settings = {
- * "tags" = {},
- * },
- * weight = -10
- * )
- */
-class TestAttributeFilter extends FilterBase {
-
- /**
- * {@inheritdoc}
- */
- public function process($text, $langcode) {
- $document = Html::load($text);
- foreach ($this->settings['tags'] as $tag) {
- $tag_elements = $document->getElementsByTagName($tag);
- foreach ($tag_elements as $tag_element) {
- $tag_element->setAttribute('test_attribute', 'test attribute value');
- }
- }
- return new FilterProcessResult(Html::serialize($document));
- }
-
-}
diff --git a/core/modules/ckeditor/tests/src/Functional/CKEditorAdminTest.php b/core/modules/ckeditor/tests/src/Functional/CKEditorAdminTest.php
deleted file mode 100644
index ded1d54c79f1..000000000000
--- a/core/modules/ckeditor/tests/src/Functional/CKEditorAdminTest.php
+++ /dev/null
@@ -1,306 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ckeditor\Functional;
-
-use Drupal\Component\Serialization\Json;
-use Drupal\editor\Entity\Editor;
-use Drupal\filter\FilterFormatInterface;
-use Drupal\filter\Entity\FilterFormat;
-use Drupal\Tests\BrowserTestBase;
-
-/**
- * Tests administration of CKEditor.
- *
- * @group ckeditor
- */
-class CKEditorAdminTest extends BrowserTestBase {
-
- /**
- * Modules to enable.
- *
- * @var array
- */
- protected static $modules = ['filter', 'editor', 'ckeditor'];
-
- /**
- * {@inheritdoc}
- */
- protected $defaultTheme = 'stark';
-
- /**
- * A user with the 'administer filters' permission.
- *
- * @var \Drupal\user\UserInterface
- */
- protected $adminUser;
-
- protected function setUp(): void {
- parent::setUp();
-
- // Create text format.
- $filtered_html_format = FilterFormat::create([
- 'format' => 'filtered_html',
- 'name' => 'Filtered HTML',
- 'weight' => 0,
- 'filters' => [],
- ]);
- $filtered_html_format->save();
-
- // Create admin user.
- $this->adminUser = $this->drupalCreateUser(['administer filters']);
- }
-
- /**
- * Tests configuring a text editor for an existing text format.
- */
- public function testExistingFormat() {
- $ckeditor = $this->container->get('plugin.manager.editor')->createInstance('ckeditor');
-
- $this->drupalLogin($this->adminUser);
- $this->drupalGet('admin/config/content/formats/manage/filtered_html');
-
- // Ensure no Editor config entity exists yet.
- $editor = Editor::load('filtered_html');
- $this->assertNull($editor, 'No Editor config entity exists yet.');
-
- // Verify the "Text Editor" <select> when a text editor is available.
- $select = $this->assertSession()->selectExists('editor[editor]');
- $this->assertFalse($select->hasAttribute('disabled'));
- $options = $select->findAll('css', 'option');
- $this->assertCount(2, $options);
- $this->assertSame('None', $options[0]->getText());
- $this->assertSame('CKEditor', $options[1]->getText());
- $this->assertTrue($options[0]->isSelected());
-
- // Select the "CKEditor" editor and click the "Save configuration" button.
- $edit = [
- 'editor[editor]' => 'ckeditor',
- ];
- $this->submitForm($edit, 'Save configuration');
- $this->assertSession()->pageTextContains('You must configure the selected text editor.');
-
- // Ensure the CKEditor editor returns the expected default settings.
- $expected_default_settings = [
- 'toolbar' => [
- 'rows' => [
- // Button groups
- [
- [
- 'name' => 'Formatting',
- 'items' => ['Bold', 'Italic'],
- ],
- [
- 'name' => 'Links',
- 'items' => ['DrupalLink', 'DrupalUnlink'],
- ],
- [
- 'name' => 'Lists',
- 'items' => ['BulletedList', 'NumberedList'],
- ],
- [
- 'name' => 'Media',
- 'items' => ['Blockquote', 'DrupalImage'],
- ],
- [
- 'name' => 'Tools',
- 'items' => ['Source'],
- ],
- ],
- ],
- ],
- 'plugins' => [],
- ];
- $this->assertEquals($expected_default_settings, $ckeditor->getDefaultSettings());
-
- // Keep the "CKEditor" editor selected and click the "Configure" button.
- $this->submitForm($edit, 'editor_configure');
- $editor = Editor::load('filtered_html');
- $this->assertNull($editor, 'No Editor config entity exists yet.');
-
- // Ensure that drupalSettings is correct.
- $ckeditor_settings_toolbar = [
- '#theme' => 'ckeditor_settings_toolbar',
- '#editor' => Editor::create(['editor' => 'ckeditor']),
- '#plugins' => $this->container->get('plugin.manager.ckeditor.plugin')->getButtons(),
- ];
- $settings = $this->getDrupalSettings();
- $expected = $settings['ckeditor']['toolbarAdmin'];
- $this->assertEquals(
- $expected,
- $this->container->get('renderer')->renderPlain($ckeditor_settings_toolbar),
- 'CKEditor toolbar settings are rendered as part of drupalSettings.'
- );
-
- // Ensure the toolbar buttons configuration value is initialized to the
- // expected default value.
- $expected_buttons_value = json_encode($expected_default_settings['toolbar']['rows']);
- $this->assertSession()->fieldValueEquals('editor[settings][toolbar][button_groups]', $expected_buttons_value);
-
- // Submit the form to save the selection of CKEditor as the chosen editor.
- $this->submitForm($edit, 'Save configuration');
-
- // Ensure an Editor object exists now, with the proper settings.
- $expected_settings = $expected_default_settings;
- $editor = Editor::load('filtered_html');
- $this->assertInstanceOf(Editor::class, $editor);
- $this->assertEquals($expected_settings, $editor->getSettings(), 'The Editor config entity has the correct settings.');
-
- // Configure the Styles plugin, and ensure the updated settings are saved.
- $this->drupalGet('admin/config/content/formats/manage/filtered_html');
-
- // Ensure the styles textarea exists and is initialized empty.
- $this->assertSession()->fieldValueEquals('editor[settings][plugins][stylescombo][styles]', '');
-
- $expected_settings['toolbar']['rows'][0][] = [
- 'name' => 'Styles dropdown',
- 'items' => ['Styles'],
- ];
- $edit = [
- 'editor[settings][toolbar][button_groups]' => json_encode($expected_settings['toolbar']['rows']),
- 'editor[settings][plugins][stylescombo][styles]' => "h1.title|Title\np.callout|Callout\n\n",
- ];
- $this->submitForm($edit, 'Save configuration');
- $expected_settings['plugins']['stylescombo']['styles'] = "h1.title|Title\np.callout|Callout\n\n";
- $editor = Editor::load('filtered_html');
- $this->assertInstanceOf(Editor::class, $editor);
- $this->assertEquals($expected_settings, $editor->getSettings(), 'The Editor config entity has the correct settings.');
-
- // Change the buttons that appear on the toolbar (in JavaScript, this is
- // done via drag and drop, but here we can only emulate the end result of
- // that interaction). Test multiple toolbar rows and a divider within a row.
- $this->drupalGet('admin/config/content/formats/manage/filtered_html');
- $expected_settings = $expected_default_settings;
- $expected_settings['toolbar']['rows'][0][] = [
- 'name' => 'Action history',
- 'items' => ['Undo', '|', 'Redo', 'JustifyCenter'],
- ];
- $edit = [
- 'editor[settings][toolbar][button_groups]' => json_encode($expected_settings['toolbar']['rows']),
- ];
- $this->submitForm($edit, 'Save configuration');
- $editor = Editor::load('filtered_html');
- $this->assertInstanceOf(Editor::class, $editor);
- $this->assertEquals($expected_settings, $editor->getSettings(), 'The Editor config entity has the correct settings.');
-
- // Check that the markup we're setting for the toolbar buttons (actually in
- // JavaScript's drupalSettings, and Unicode-escaped) is correctly rendered.
- $this->drupalGet('admin/config/content/formats/manage/filtered_html');
- // Create function to encode HTML as we expect it in drupalSettings.
- $json_encode = function ($html) {
- return trim(Json::encode($html), '"');
- };
- // Check the Button separator.
- $this->assertSession()->responseContains($json_encode('<li data-drupal-ckeditor-button-name="-" class="ckeditor-button-separator ckeditor-multiple-button" data-drupal-ckeditor-type="separator"><a href="#" role="button" aria-label="Button separator" class="ckeditor-separator"></a></li>'));
- // Check the Format dropdown.
- $this->assertSession()->responseContains($json_encode('<li data-drupal-ckeditor-button-name="Format" class="ckeditor-button"><a href="#" role="button" aria-label="Format"><span class="ckeditor-button-dropdown">Format<span class="ckeditor-button-arrow"></span></span></a></li>'));
- // Check the Styles dropdown.
- $this->assertSession()->responseContains($json_encode('<li data-drupal-ckeditor-button-name="Styles" class="ckeditor-button"><a href="#" role="button" aria-label="Styles"><span class="ckeditor-button-dropdown">Styles<span class="ckeditor-button-arrow"></span></span></a></li>'));
- // Check strikethrough.
- $this->assertSession()->responseContains($json_encode('<li data-drupal-ckeditor-button-name="Strike" class="ckeditor-button"><a href="#" class="cke-icon-only cke_ltr" role="button" title="strike" aria-label="strike"><span class="cke_button_icon cke_button__strike_icon">strike</span></a></li>'));
-
- // Now enable the ckeditor_test module, which provides one configurable
- // CKEditor plugin — this should not affect the Editor config entity.
- \Drupal::service('module_installer')->install(['ckeditor_test']);
- $this->resetAll();
- $this->container->get('plugin.manager.ckeditor.plugin')->clearCachedDefinitions();
- $this->drupalGet('admin/config/content/formats/manage/filtered_html');
- $this->assertSession()->checkboxNotChecked('editor[settings][plugins][llama_contextual_and_button][ultra_llama_mode]');
- $editor = Editor::load('filtered_html');
- $this->assertInstanceOf(Editor::class, $editor);
- $this->assertEquals($expected_settings, $editor->getSettings(), 'The Editor config entity has the correct settings.');
-
- // Finally, check the "Ultra llama mode" checkbox.
- $this->drupalGet('admin/config/content/formats/manage/filtered_html');
- $expected_settings['toolbar']['rows'][0][] = [
- 'name' => 'Ultra llama mode',
- 'items' => ['Llama'],
- ];
- $edit = [
- 'editor[settings][toolbar][button_groups]' => json_encode($expected_settings['toolbar']['rows']),
- 'editor[settings][plugins][llama_contextual_and_button][ultra_llama_mode]' => '1',
- ];
- $this->submitForm($edit, 'Save configuration');
- $this->drupalGet('admin/config/content/formats/manage/filtered_html');
- $this->assertSession()->checkboxChecked('editor[settings][plugins][llama_contextual_and_button][ultra_llama_mode]');
- $expected_settings['plugins']['llama_contextual_and_button']['ultra_llama_mode'] = TRUE;
- $editor = Editor::load('filtered_html');
- $this->assertInstanceOf(Editor::class, $editor);
- $this->assertEquals($expected_settings, $editor->getSettings());
-
- $this->drupalGet('admin/config/content/formats/add');
- // Now attempt to add another filter format with the same editor and same
- // machine name.
- $edit = [
- 'format' => 'filtered_html',
- 'name' => 'Filtered HTML',
- 'editor[editor]' => 'ckeditor',
- ];
- $this->submitForm($edit, 'editor_configure');
- $this->submitForm($edit, 'Save configuration');
- $this->assertSession()->statusCodeEquals(200);
- $this->assertSession()->pageTextContains('The machine-readable name is already in use. It must be unique.');
- }
-
- /**
- * Tests configuring a text editor for a new text format.
- *
- * This test only needs to ensure that the basics of the CKEditor
- * configuration form work; details are tested in testExistingFormat().
- */
- public function testNewFormat() {
- $this->drupalLogin($this->adminUser);
- $this->drupalGet('admin/config/content/formats/add');
-
- // Verify the "Text Editor" <select> when a text editor is available.
- $select = $this->assertSession()->selectExists('editor[editor]');
- $this->assertFalse($select->hasAttribute('disabled'));
- $options = $select->findAll('css', 'option');
- $this->assertCount(2, $options);
- $this->assertSame('None', $options[0]->getText());
- $this->assertSame('CKEditor', $options[1]->getText());
- $this->assertTrue($options[0]->isSelected());
-
- // Name our fancy new text format, select the "CKEditor" editor and click
- // the "Configure" button.
- $edit = [
- 'name' => 'My amazing text format',
- 'format' => 'amazing_format',
- 'editor[editor]' => 'ckeditor',
- ];
- $this->submitForm($edit, 'editor_configure');
- $filter_format = FilterFormat::load('amazing_format');
- $this->assertNull($filter_format, 'No FilterFormat config entity exists yet.');
- $editor = Editor::load('amazing_format');
- $this->assertNull($editor, 'No Editor config entity exists yet.');
-
- // Ensure the toolbar buttons configuration value is initialized to the
- // default value.
- $ckeditor = $this->container->get('plugin.manager.editor')->createInstance('ckeditor');
- $default_settings = $ckeditor->getDefaultSettings();
- $expected_buttons_value = json_encode($default_settings['toolbar']['rows']);
- $this->assertSession()->fieldValueEquals('editor[settings][toolbar][button_groups]', $expected_buttons_value);
-
- // Regression test for https://www.drupal.org/node/2606460.
- $settings = $this->getDrupalSettings();
- $expected = $settings['ckeditor']['toolbarAdmin'];
- $this->assertStringContainsString('<li data-drupal-ckeditor-button-name="Bold" class="ckeditor-button"><a href="#" class="cke-icon-only cke_ltr" role="button" title="bold" aria-label="bold"><span class="cke_button_icon cke_button__bold_icon">bold</span></a></li>', $expected);
-
- // Ensure the styles textarea exists and is initialized empty.
- $this->assertSession()->fieldValueEquals('editor[settings][plugins][stylescombo][styles]', '');
-
- // Submit the form to create both a new text format and an associated text
- // editor.
- $this->submitForm($edit, 'Save configuration');
-
- // Ensure a FilterFormat object exists now.
- $filter_format = FilterFormat::load('amazing_format');
- $this->assertInstanceOf(FilterFormatInterface::class, $filter_format);
-
- // Ensure an Editor object exists now, with the proper settings.
- $expected_settings = $default_settings;
- $editor = Editor::load('amazing_format');
- $this->assertInstanceOf(Editor::class, $editor);
- $this->assertEquals($expected_settings, $editor->getSettings(), 'The Editor config entity has the correct settings.');
- }
-
-}
diff --git a/core/modules/ckeditor/tests/src/Functional/CKEditorLoadingTest.php b/core/modules/ckeditor/tests/src/Functional/CKEditorLoadingTest.php
deleted file mode 100644
index eb56fc722017..000000000000
--- a/core/modules/ckeditor/tests/src/Functional/CKEditorLoadingTest.php
+++ /dev/null
@@ -1,233 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ckeditor\Functional;
-
-use Drupal\editor\Entity\Editor;
-use Drupal\filter\Entity\FilterFormat;
-use Drupal\Tests\BrowserTestBase;
-
-/**
- * Tests loading of CKEditor.
- *
- * @group ckeditor
- */
-class CKEditorLoadingTest extends BrowserTestBase {
-
- /**
- * Modules to enable.
- *
- * @var array
- */
- protected static $modules = ['filter', 'editor', 'ckeditor', 'node'];
-
- /**
- * {@inheritdoc}
- */
- protected $defaultTheme = 'stark';
-
- /**
- * An untrusted user with access to only the 'plain_text' format.
- *
- * @var \Drupal\user\UserInterface
- */
- protected $untrustedUser;
-
- /**
- * A normal user with access to the 'plain_text' and 'filtered_html' formats.
- *
- * @var \Drupal\user\UserInterface
- */
- protected $normalUser;
-
- protected function setUp(): void {
- parent::setUp();
-
- // Create text format, associate CKEditor.
- $filtered_html_format = FilterFormat::create([
- 'format' => 'filtered_html',
- 'name' => 'Filtered HTML',
- 'weight' => 0,
- 'filters' => [],
- ]);
- $filtered_html_format->save();
- $editor = Editor::create([
- 'format' => 'filtered_html',
- 'editor' => 'ckeditor',
- ]);
- $editor->save();
-
- // Create a second format without an associated editor so a drop down select
- // list is created when selecting formats.
- $full_html_format = FilterFormat::create([
- 'format' => 'full_html',
- 'name' => 'Full HTML',
- 'weight' => 1,
- 'filters' => [],
- ]);
- $full_html_format->save();
-
- // Create node type.
- $this->drupalCreateContentType([
- 'type' => 'article',
- 'name' => 'Article',
- ]);
-
- $this->untrustedUser = $this->drupalCreateUser([
- 'create article content',
- 'edit any article content',
- ]);
- $this->normalUser = $this->drupalCreateUser([
- 'create article content',
- 'edit any article content',
- 'use text format filtered_html',
- 'use text format full_html',
- ]);
- }
-
- /**
- * Tests loading of CKEditor CSS, JS and JS settings.
- */
- public function testLoading() {
- // The untrusted user:
- // - has access to 1 text format (plain_text);
- // - doesn't have access to the filtered_html text format, so: no text editor.
- $this->drupalLogin($this->untrustedUser);
- $this->drupalGet('node/add/article');
- [$settings, $editor_settings_present, $editor_js_present] = $this->getThingsToCheck();
- $this->assertFalse($editor_settings_present, 'No Text Editor module settings.');
- $this->assertFalse($editor_js_present, 'No Text Editor JavaScript.');
- $this->assertSession()->fieldExists('edit-body-0-value');
- $this->assertSession()->elementNotExists('css', 'select.js-filter-list');
- // Verify that a single text format hidden input does not exist on the page.
- $this->assertSession()->elementNotExists('xpath', '//input[@type="hidden" and contains(@class, "editor")]');
- // Verify that CKEditor glue JS is absent.
- $this->assertSession()->responseNotContains($this->getModulePath('ckeditor') . '/js/ckeditor.js');
-
- // On pages where there would never be a text editor, CKEditor JS is absent.
- $this->drupalGet('user');
- $this->assertSession()->responseNotContains($this->getModulePath('ckeditor') . '/js/ckeditor.js');
-
- // The normal user:
- // - has access to 2 text formats;
- // - does have access to the filtered_html text format, so: CKEditor.
- $this->drupalLogin($this->normalUser);
- $this->drupalGet('node/add/article');
- [$settings, $editor_settings_present, $editor_js_present] = $this->getThingsToCheck();
- $ckeditor_plugin = $this->container->get('plugin.manager.editor')->createInstance('ckeditor');
- $editor = Editor::load('filtered_html');
- $expected = [
- 'formats' => [
- 'filtered_html' => [
- 'format' => 'filtered_html',
- 'editor' => 'ckeditor',
- 'editorSettings' => $ckeditor_plugin->getJSSettings($editor),
- 'editorSupportsContentFiltering' => TRUE,
- 'isXssSafe' => FALSE,
- ],
- ],
- ];
- $this->assertTrue($editor_settings_present, "Text Editor module's JavaScript settings are on the page.");
- $this->assertEquals($expected, $settings['editor'], "Text Editor module's JavaScript settings on the page are correct.");
- $this->assertTrue($editor_js_present, 'Text Editor JavaScript is present.');
- $this->assertSession()->fieldExists('edit-body-0-value');
- // Verify that a single text format selector exists on the page and has a
- // "data-editor-for" attribute with the correct value.
- $this->assertSession()->elementsCount('css', 'select.js-filter-list', 1);
- $select = $this->assertSession()->elementExists('css', 'select.js-filter-list');
- $this->assertSame('edit-body-0-value', $select->getAttribute('data-editor-for'));
- $this->assertContains('ckeditor/drupal.ckeditor', explode(',', $settings['ajaxPageState']['libraries']), 'CKEditor glue library is present.');
-
- // Enable the ckeditor_test module, customize configuration. In this case,
- // there is additional CSS and JS to be loaded.
- // NOTE: the tests in CKEditorTest already ensure that changing the
- // configuration also results in modified CKEditor configuration, so we
- // don't test that here.
- \Drupal::service('module_installer')->install(['ckeditor_test']);
- $this->container->get('plugin.manager.ckeditor.plugin')->clearCachedDefinitions();
- $editor_settings = $editor->getSettings();
- $editor_settings['toolbar']['rows'][0][0]['items'][] = 'Llama';
- $editor->setSettings($editor_settings);
- $editor->save();
- $this->drupalGet('node/add/article');
- [$settings, $editor_settings_present, $editor_js_present] = $this->getThingsToCheck();
- $expected = [
- 'formats' => [
- 'filtered_html' => [
- 'format' => 'filtered_html',
- 'editor' => 'ckeditor',
- 'editorSettings' => $ckeditor_plugin->getJSSettings($editor),
- 'editorSupportsContentFiltering' => TRUE,
- 'isXssSafe' => FALSE,
- ],
- ],
- ];
- $this->assertTrue($editor_settings_present, "Text Editor module's JavaScript settings are on the page.");
- $this->assertEquals($expected, $settings['editor'], "Text Editor module's JavaScript settings on the page are correct.");
- $this->assertTrue($editor_js_present, 'Text Editor JavaScript is present.');
- $this->assertContains('ckeditor/drupal.ckeditor', explode(',', $settings['ajaxPageState']['libraries']), 'CKEditor glue library is present.');
-
- // Assert that CKEditor uses Drupal's cache-busting query string by
- // comparing the setting sent with the page with the current query string.
- $settings = $this->getDrupalSettings();
- $expected = $settings['ckeditor']['timestamp'];
- $this->assertSame($expected, \Drupal::state()->get('system.css_js_query_string'), "CKEditor scripts cache-busting string is correct before flushing all caches.");
- // Flush all caches then make sure that $settings['ckeditor']['timestamp']
- // still matches.
- $this->resetAll();
- $this->assertSame($expected, \Drupal::state()->get('system.css_js_query_string'), "CKEditor scripts cache-busting string is correct after flushing all caches.");
- }
-
- /**
- * Tests presence of essential configuration even without Internal's buttons.
- */
- public function testLoadingWithoutInternalButtons() {
- // Change the CKEditor text editor configuration to only have link buttons.
- // This means:
- // - 0 buttons are from \Drupal\ckeditor\Plugin\CKEditorPlugin\Internal
- // - 2 buttons are from \Drupal\ckeditor\Plugin\CKEditorPlugin\DrupalLink
- $filtered_html_editor = Editor::load('filtered_html');
- $settings = $filtered_html_editor->getSettings();
- $settings['toolbar']['rows'] = [
- 0 => [
- 0 => [
- 'name' => 'Links',
- 'items' => [
- 'DrupalLink',
- 'DrupalUnlink',
- ],
- ],
- ],
- ];
- $filtered_html_editor->setSettings($settings)->save();
-
- // Even when no buttons of \Drupal\ckeditor\Plugin\CKEditorPlugin\Internal
- // are in use, its configuration (Internal::getConfig()) is still essential:
- // this is configuration that is associated with the (custom, optimized)
- // build of CKEditor that Drupal core ships with. For example, it configures
- // CKEditor to not perform its default action of loading a config.js file,
- // to not convert special characters into HTML entities, and the allowedContent
- // setting to configure CKEditor's Advanced Content Filter.
- $this->drupalLogin($this->normalUser);
- $this->drupalGet('node/add/article');
- $editor_settings = $this->getDrupalSettings()['editor']['formats']['filtered_html']['editorSettings'];
- $this->assertTrue(isset($editor_settings['customConfig']));
- $this->assertTrue(isset($editor_settings['entities']));
- $this->assertTrue(isset($editor_settings['allowedContent']));
- $this->assertTrue(isset($editor_settings['disallowedContent']));
- }
-
- protected function getThingsToCheck() {
- $settings = $this->getDrupalSettings();
- return [
- // JavaScript settings.
- $settings,
- // Editor.module's JS settings present.
- isset($settings['editor']),
- // Editor.module's JS present. Note: ckeditor/drupal.ckeditor depends on
- // editor/drupal.editor, hence presence of the former implies presence of
- // the latter.
- isset($settings['ajaxPageState']['libraries']) && in_array('ckeditor/drupal.ckeditor', explode(',', $settings['ajaxPageState']['libraries'])),
- ];
- }
-
-}
diff --git a/core/modules/ckeditor/tests/src/Functional/CKEditorStylesComboAdminTest.php b/core/modules/ckeditor/tests/src/Functional/CKEditorStylesComboAdminTest.php
deleted file mode 100644
index 10ef5c316392..000000000000
--- a/core/modules/ckeditor/tests/src/Functional/CKEditorStylesComboAdminTest.php
+++ /dev/null
@@ -1,114 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ckeditor\Functional;
-
-use Drupal\editor\Entity\Editor;
-use Drupal\filter\Entity\FilterFormat;
-use Drupal\Tests\BrowserTestBase;
-
-/**
- * Tests administration of the CKEditor StylesCombo plugin.
- *
- * @group ckeditor
- */
-class CKEditorStylesComboAdminTest extends BrowserTestBase {
-
- /**
- * Modules to enable.
- *
- * @var array
- */
- protected static $modules = ['filter', 'editor', 'ckeditor'];
-
- /**
- * {@inheritdoc}
- */
- protected $defaultTheme = 'stark';
-
- /**
- * A user with the 'administer filters' permission.
- *
- * @var \Drupal\user\UserInterface
- */
- protected $adminUser;
-
- /**
- * A random generated format machine name.
- *
- * @var string
- */
- protected $format;
-
- /**
- * The default editor settings.
- *
- * @var array
- */
- protected $defaultSettings;
-
- /**
- * {@inheritdoc}
- */
- protected function setUp(): void {
- parent::setUp();
-
- $this->format = strtolower($this->randomMachineName());
- $filter_format = FilterFormat::create([
- 'format' => $this->format,
- 'name' => $this->randomString(),
- 'filters' => [],
- ]);
- $filter_format->save();
- $ckeditor = $this->container->get('plugin.manager.editor')->createInstance('ckeditor');
- $this->defaultSettings = $ckeditor->getDefaultSettings();
- $this->defaultSettings['toolbar']['rows'][0][] = [
- 'name' => 'Styles dropdown',
- 'items' => ['Styles'],
- ];
- $editor = Editor::create([
- 'format' => $this->format,
- 'editor' => 'ckeditor',
- 'settings' => $this->defaultSettings,
- ]);
- $editor->save();
-
- $this->adminUser = $this->drupalCreateUser(['administer filters']);
- }
-
- /**
- * Tests StylesCombo settings for an existing text format.
- */
- public function testExistingFormat() {
- $this->drupalLogin($this->adminUser);
- $this->drupalGet('admin/config/content/formats/manage/' . $this->format);
-
- // Ensure an Editor config entity exists, with the proper settings.
- $expected_settings = $this->defaultSettings;
- $editor = Editor::load($this->format);
- $this->assertEquals($expected_settings, $editor->getSettings(), 'The Editor config entity has the correct settings.');
-
- // Case 1: Configure the Styles plugin with different labels for each style,
- // and ensure the updated settings are saved.
- $this->drupalGet('admin/config/content/formats/manage/' . $this->format);
- $edit = [
- 'editor[settings][plugins][stylescombo][styles]' => "h1.title|Title\np.callout|Callout\ndrupal-entity.has-dashes|Allowing Dashes\n\n",
- ];
- $this->submitForm($edit, 'Save configuration');
- $expected_settings['plugins']['stylescombo']['styles'] = "h1.title|Title\np.callout|Callout\ndrupal-entity.has-dashes|Allowing Dashes\n\n";
- $editor = Editor::load($this->format);
- $this->assertEquals($expected_settings, $editor->getSettings(), 'The Editor config entity has the correct settings.');
-
- // Case 2: Configure the Styles plugin with same labels for each style, and
- // ensure that an error is displayed and that the updated settings are not
- // saved.
- $this->drupalGet('admin/config/content/formats/manage/' . $this->format);
- $edit = [
- 'editor[settings][plugins][stylescombo][styles]' => "h1.title|Title\np.callout|Title\n\n",
- ];
- $this->submitForm($edit, 'Save configuration');
- $this->assertSession()->pageTextContains('Each style must have a unique label.');
- $editor = Editor::load($this->format);
- $this->assertEquals($expected_settings, $editor->getSettings(), 'The Editor config entity has the correct settings.');
- }
-
-}
diff --git a/core/modules/ckeditor/tests/src/Functional/CKEditorStylesComboTranslationTest.php b/core/modules/ckeditor/tests/src/Functional/CKEditorStylesComboTranslationTest.php
deleted file mode 100644
index 0e4cba82e1b2..000000000000
--- a/core/modules/ckeditor/tests/src/Functional/CKEditorStylesComboTranslationTest.php
+++ /dev/null
@@ -1,96 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ckeditor\Functional;
-
-use Drupal\editor\Entity\Editor;
-use Drupal\filter\Entity\FilterFormat;
-use Drupal\language\Entity\ConfigurableLanguage;
-use Drupal\Tests\BrowserTestBase;
-
-/**
- * Tests administration of the CKEditor StylesCombo plugin.
- *
- * @group ckeditor
- */
-class CKEditorStylesComboTranslationTest extends BrowserTestBase {
-
- /**
- * {@inheritdoc}
- */
- protected static $modules = ['ckeditor', 'config_translation'];
-
- /**
- * {@inheritdoc}
- */
- protected $defaultTheme = 'stark';
-
- /**
- * A user with the 'administer filters' permission.
- *
- * @var \Drupal\user\UserInterface
- */
- protected $adminUser;
-
- /**
- * A randomly generated format machine name.
- *
- * @var string
- */
- protected $format;
-
- /**
- * {@inheritdoc}
- */
- protected function setUp(): void {
- parent::setUp();
-
- $this->format = strtolower($this->randomMachineName());
- $filter_format = FilterFormat::create([
- 'format' => $this->format,
- 'name' => $this->randomString(),
- 'filters' => [],
- ]);
- $filter_format->save();
- $ckeditor = $this->container->get('plugin.manager.editor')->createInstance('ckeditor');
- $settings = $ckeditor->getDefaultSettings();
- $settings['toolbar']['rows'][0][] = [
- 'name' => 'Styles dropdown',
- 'items' => ['Styles'],
- ];
- $editor = Editor::create([
- 'format' => $this->format,
- 'editor' => 'ckeditor',
- 'settings' => $settings,
- ]);
- $editor->save();
-
- $this->adminUser = $this->drupalCreateUser([
- 'administer filters',
- 'translate configuration',
- ]);
-
- ConfigurableLanguage::createFromLangcode('de')->save();
- }
-
- /**
- * Tests translations of CKEditor styles configuration.
- */
- public function testExistingFormat() {
- $this->drupalLogin($this->adminUser);
- $edit = [
- 'editor[settings][plugins][stylescombo][styles]' => 'h1.title|Title',
- ];
- $this->drupalGet('admin/config/content/formats/manage/' . $this->format);
- $this->submitForm($edit, 'Save configuration');
-
- $this->drupalGet('admin/config/content/formats/manage/' . $this->format . '/translate/de/add');
- $this->assertEquals('textarea', $this->assertSession()->fieldExists('List of styles')->getTagName());
- $this->assertSession()->fieldValueEquals('List of styles', 'h1.title|Title');
-
- $page = $this->getSession()->getPage();
- $page->fillField('List of styles', 'h1.title|Titel');
- $page->pressButton('Save translation');
- $this->assertSession()->pageTextContains('Successfully saved German translation.');
- }
-
-}
diff --git a/core/modules/ckeditor/tests/src/Functional/CKEditorToolbarButtonTest.php b/core/modules/ckeditor/tests/src/Functional/CKEditorToolbarButtonTest.php
deleted file mode 100644
index a427817104dc..000000000000
--- a/core/modules/ckeditor/tests/src/Functional/CKEditorToolbarButtonTest.php
+++ /dev/null
@@ -1,93 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ckeditor\Functional;
-
-use Drupal\filter\Entity\FilterFormat;
-use Drupal\editor\Entity\Editor;
-use Drupal\Tests\BrowserTestBase;
-use Drupal\Component\Serialization\Json;
-use Drupal\user\Entity\User;
-
-/**
- * Tests CKEditor toolbar buttons when the language direction is RTL.
- *
- * @group ckeditor
- */
-class CKEditorToolbarButtonTest extends BrowserTestBase {
-
- /**
- * Modules to enable for this test.
- *
- * @var array
- */
- protected static $modules = ['filter', 'editor', 'ckeditor', 'locale'];
-
- /**
- * {@inheritdoc}
- */
- protected $defaultTheme = 'stark';
-
- /**
- * The admin user.
- *
- * @var \Drupal\user\Entity\User
- */
- protected User $adminUser;
-
- /**
- * {@inheritdoc}
- */
- protected function setUp(): void {
- parent::setUp();
-
- // Create a text format and associate this with CKEditor.
- FilterFormat::create([
- 'format' => 'full_html',
- 'name' => 'Full HTML',
- 'weight' => 1,
- 'filters' => [],
- ])->save();
- Editor::create([
- 'format' => 'full_html',
- 'editor' => 'ckeditor',
- ])->save();
-
- // Create a new user with admin rights.
- $this->adminUser = $this->drupalCreateUser([
- 'administer languages',
- 'access administration pages',
- 'administer site configuration',
- 'administer filters',
- ]);
- }
-
- /**
- * Method tests CKEditor image buttons.
- */
- public function testImageButtonDisplay() {
- $this->drupalLogin($this->adminUser);
-
- // Install the Arabic language (which is RTL) and configure as the default.
- $edit = [];
- $edit['predefined_langcode'] = 'ar';
- $this->drupalGet('admin/config/regional/language/add');
- $this->submitForm($edit, 'Add language');
-
- $edit = ['site_default_language' => 'ar'];
- $this->drupalGet('admin/config/regional/language');
- $this->submitForm($edit, 'Save configuration');
- // Once the default language is changed, go to the tested text format
- // configuration page.
- $this->drupalGet('admin/config/content/formats/manage/full_html');
-
- // Check if any image button is loaded in CKEditor json.
- $json_encode = function ($html) {
- return trim(Json::encode($html), '"');
- };
- /** @var \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator */
- $file_url_generator = \Drupal::service('file_url_generator');
- $markup = $json_encode($file_url_generator->generateString('core/modules/ckeditor/js/plugins/drupalimage/icons/drupalimage.png'));
- $this->assertSession()->responseContains($markup);
- }
-
-}
diff --git a/core/modules/ckeditor/tests/src/FunctionalJavascript/AjaxCssTest.php b/core/modules/ckeditor/tests/src/FunctionalJavascript/AjaxCssTest.php
deleted file mode 100644
index dcd10351aade..000000000000
--- a/core/modules/ckeditor/tests/src/FunctionalJavascript/AjaxCssTest.php
+++ /dev/null
@@ -1,128 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ckeditor\FunctionalJavascript;
-
-use Drupal\editor\Entity\Editor;
-use Drupal\filter\Entity\FilterFormat;
-use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
-
-/**
- * Tests delivery of CSS to CKEditor via AJAX.
- *
- * @group ckeditor
- */
-class AjaxCssTest extends WebDriverTestBase {
-
- /**
- * {@inheritdoc}
- */
- protected static $modules = ['ckeditor', 'ckeditor_test'];
-
- /**
- * {@inheritdoc}
- */
- protected $defaultTheme = 'stark';
-
- /**
- * {@inheritdoc}
- */
- protected function setUp(): void {
- parent::setUp();
-
- FilterFormat::create([
- 'format' => 'test_format',
- 'name' => $this->randomMachineName(),
- ])->save();
-
- Editor::create([
- 'editor' => 'ckeditor',
- 'format' => 'test_format',
- ])->save();
-
- user_role_grant_permissions('anonymous', ['use text format test_format']);
- }
-
- /**
- * Tests adding style sheets dynamically to CKEditor.
- */
- public function testCkeditorAjaxAddCss() {
- $this->drupalGet('/ckeditor_test/ajax_css');
-
- $session = $this->getSession();
- $page = $session->getPage();
-
- $this->waitOnCkeditorInstance('edit-iframe-value');
- $this->waitOnCkeditorInstance('edit-inline');
-
- $style_color = 'rgb(255, 0, 0)';
-
- // Add the inline CSS and assert that the style is applied to the main body,
- // but not the iframe.
- $page->pressButton('Add CSS to inline CKEditor instance');
-
- $result = $page->waitFor(10, function () use ($style_color) {
- return ($this->getEditorStyle('edit-inline', 'color') == $style_color)
- && ($this->getEditorStyle('edit-iframe-value', 'color') != $style_color);
- });
- $this->assertTrue($result);
-
- $session->reload();
-
- $this->waitOnCkeditorInstance('edit-iframe-value');
- $this->waitOnCkeditorInstance('edit-inline');
-
- // Add the iframe CSS and assert that the style is applied to the iframe,
- // but not the main body.
- $page->pressButton('Add CSS to iframe CKEditor instance');
-
- $result = $page->waitFor(10, function () use ($style_color) {
- return ($this->getEditorStyle('edit-inline', 'color') != $style_color)
- && ($this->getEditorStyle('edit-iframe-value', 'color') == $style_color);
- });
-
- $this->assertTrue($result);
- }
-
- /**
- * Gets a computed style value for a CKEditor instance.
- *
- * @param string $instance_id
- * The CKEditor instance ID.
- * @param string $attribute
- * The style attribute.
- *
- * @return string
- * The computed style value.
- */
- protected function getEditorStyle($instance_id, $attribute) {
- $js = sprintf(
- 'CKEDITOR.instances["%s"].document.getBody().getComputedStyle("%s")',
- $instance_id,
- $attribute
- );
- return $this->getSession()->evaluateScript($js);
- }
-
- /**
- * Wait for a CKEditor instance to finish loading and initializing.
- *
- * @param string $instance_id
- * The CKEditor instance ID.
- * @param int $timeout
- * (optional) Timeout in milliseconds, defaults to 10000.
- */
- protected function waitOnCkeditorInstance($instance_id, $timeout = 10000) {
- $condition = <<<JS
- (function() {
- return (
- typeof CKEDITOR !== 'undefined'
- && typeof CKEDITOR.instances["$instance_id"] !== 'undefined'
- && CKEDITOR.instances["$instance_id"].instanceReady
- );
- }())
-JS;
-
- $this->getSession()->wait($timeout, $condition);
- }
-
-}
diff --git a/core/modules/ckeditor/tests/src/FunctionalJavascript/BigPipeRegressionTest.php b/core/modules/ckeditor/tests/src/FunctionalJavascript/BigPipeRegressionTest.php
deleted file mode 100644
index 188aef4ff485..000000000000
--- a/core/modules/ckeditor/tests/src/FunctionalJavascript/BigPipeRegressionTest.php
+++ /dev/null
@@ -1,119 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ckeditor\FunctionalJavascript;
-
-use Drupal\comment\CommentInterface;
-use Drupal\comment\Entity\Comment;
-use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
-use Drupal\comment\Tests\CommentTestTrait;
-use Drupal\editor\Entity\Editor;
-use Drupal\filter\Entity\FilterFormat;
-use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
-use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
-use Drupal\Tests\node\Traits\NodeCreationTrait;
-
-/**
- * BigPipe regression test for CKEditor 4.
- *
- * @group legacy
- */
-class BigPipeRegressionTest extends WebDriverTestBase {
-
- use CommentTestTrait;
- use ContentTypeCreationTrait;
- use NodeCreationTrait;
-
- /**
- * {@inheritdoc}
- */
- protected static $modules = [
- 'big_pipe',
- 'big_pipe_regression_test',
- ];
-
- /**
- * {@inheritdoc}
- */
- protected $defaultTheme = 'stark';
-
- /**
- * {@inheritdoc}
- */
- public function setUp(): void {
- parent::setUp();
-
- // Use the big_pipe_test_theme theme.
- $this->container->get('theme_installer')->install(['big_pipe_test_theme']);
- $this->container->get('config.factory')->getEditable('system.theme')->set('default', 'big_pipe_test_theme')->save();
- }
-
- /**
- * Ensure comment form works with history and big_pipe modules.
- *
- * @see https://www.drupal.org/node/2698811
- */
- public function testCommentForm_2698811() {
- $this->assertTrue($this->container->get('module_installer')->install(['comment', 'history', 'ckeditor'], TRUE), 'Installed modules.');
-
- // Ensure an `article` node type exists.
- $this->createContentType(['type' => 'article']);
- $this->addDefaultCommentField('node', 'article');
-
- // Enable CKEditor.
- $format = $this->randomMachineName();
- FilterFormat::create([
- 'format' => $format,
- 'name' => $this->randomString(),
- 'weight' => 1,
- 'filters' => [],
- ])->save();
- $settings['toolbar']['rows'] = [
- [
- [
- 'name' => 'Links',
- 'items' => [
- 'DrupalLink',
- 'DrupalUnlink',
- ],
- ],
- ],
- ];
- $editor = Editor::create([
- 'format' => $format,
- 'editor' => 'ckeditor',
- ]);
- $editor->setSettings($settings);
- $editor->save();
-
- $admin_user = $this->drupalCreateUser([
- 'access comments',
- 'post comments',
- 'use text format ' . $format,
- ]);
- $this->drupalLogin($admin_user);
-
- $node = $this->createNode([
- 'type' => 'article',
- 'comment' => CommentItemInterface::OPEN,
- ]);
- // Create some comments.
- foreach (range(1, 5) as $i) {
- $comment = Comment::create([
- 'status' => CommentInterface::PUBLISHED,
- 'field_name' => 'comment',
- 'entity_type' => 'node',
- 'entity_id' => $node->id(),
- ]);
- $comment->save();
- }
- $this->drupalGet($node->toUrl()->toString());
- // Confirm that CKEditor loaded.
- $javascript = <<<JS
- (function(){
- return window.CKEDITOR && Object.keys(CKEDITOR.instances).length > 0;
- }())
-JS;
- $this->assertJsCondition($javascript);
- }
-
-}
diff --git a/core/modules/ckeditor/tests/src/FunctionalJavascript/CKEditor5CKEditor4Compatibility.php b/core/modules/ckeditor/tests/src/FunctionalJavascript/CKEditor5CKEditor4Compatibility.php
deleted file mode 100644
index aa3c14968f09..000000000000
--- a/core/modules/ckeditor/tests/src/FunctionalJavascript/CKEditor5CKEditor4Compatibility.php
+++ /dev/null
@@ -1,139 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ckeditor\FunctionalJavascript;
-
-use Drupal\ckeditor5\Plugin\Editor\CKEditor5;
-use Drupal\Core\Entity\Entity\EntityFormDisplay;
-use Drupal\editor\Entity\Editor;
-use Drupal\field\Entity\FieldConfig;
-use Drupal\field\Entity\FieldStorageConfig;
-use Drupal\filter\Entity\FilterFormat;
-use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
-use Symfony\Component\Validator\ConstraintViolation;
-
-/**
- * Ensures that CKEditor 5 can be used on the same page with CKEditor 4.
- *
- * @group ckeditor
- * @internal
- */
-class CKEditor5CKEditor4Compatibility extends WebDriverTestBase {
-
- /**
- * {@inheritdoc}
- */
- protected static $modules = [
- 'ckeditor',
- 'node',
- 'ckeditor5',
- 'ckeditor5_test',
- ];
-
- /**
- * {@inheritdoc}
- */
- protected $defaultTheme = 'stark';
-
- /**
- * {@inheritdoc}
- */
- protected function setUp(): void {
- parent::setUp();
-
- $this->drupalCreateContentType(['type' => 'page']);
-
- $this->drupalLogin($this->drupalCreateUser([
- 'administer filters',
- 'create page content',
- 'edit own page content',
- ]));
-
- $current_user_roles = $this->loggedInUser->getRoles(TRUE);
-
- // Create text format, text editor and text fields for CKEditor 5 and 4.
- foreach ([5 => 'ckeditor5', 4 => 'ckeditor'] as $version => $text_editor_plugin_id) {
- $format_id = sprintf('test_format_for_ckeditor%d', $version);
- $field_name = sprintf('field_text_ckeditor%d', $version);
-
- FilterFormat::create([
- 'format' => $format_id,
- 'name' => sprintf('CKEditor %d editor', $version),
- 'roles' => $current_user_roles,
- 'filters' => [
- 'filter_html' => [
- 'status' => TRUE,
- 'settings' => [
- 'allowed_html' => '<p> <br> <h2> <h3> <h4> <h5> <h6> <strong> <em>',
- ],
- ],
- ],
- ])->save();
- Editor::create([
- 'editor' => $text_editor_plugin_id,
- 'format' => $format_id,
- 'settings' => $version === 4 ? [] : [
- 'toolbar' => [
- 'items' => ['heading', 'bold', 'italic'],
- ],
- 'plugins' => [
- 'ckeditor5_heading' => [
- 'enabled_headings' => [
- 'heading2',
- 'heading3',
- 'heading4',
- 'heading5',
- 'heading6',
- ],
- ],
- ],
- ],
- 'image_upload' => [
- 'status' => FALSE,
- ],
- ])->save();
- if ($version === 5) {
- $this->assertSame([], array_map(
- function (ConstraintViolation $v) {
- return (string) $v->getMessage();
- },
- iterator_to_array(CKEditor5::validatePair(
- Editor::load($format_id),
- FilterFormat::load($format_id)
- ))
- ));
- }
- $field_storage = FieldStorageConfig::create([
- 'field_name' => $field_name,
- 'entity_type' => 'node',
- 'type' => 'text_long',
- ]);
- $field_storage->save();
- FieldConfig::create([
- 'field_storage' => $field_storage,
- 'entity_type' => 'node',
- 'bundle' => 'page',
- ])->save();
-
- // Add the new field to the default form display.
- EntityFormDisplay::load('node.page.default')
- ->setComponent($field_name, ['type' => 'text_textarea'])
- ->save();
- }
- }
-
- /**
- * Ensures that CKEditor 5 and CKEditor 4 can be used on the same page.
- */
- public function testCkeCompatibility() {
- $page = $this->getSession()->getPage();
- $assert_session = $this->assertSession();
-
- $this->drupalGet('node/add/page');
- $page->selectFieldOption('field_text_ckeditor4[0][format]', 'test_format_for_ckeditor4');
- $page->selectFieldOption('field_text_ckeditor5[0][format]', 'test_format_for_ckeditor5');
-
- $this->assertNotEmpty($assert_session->waitForElement('css', '.cke_wysiwyg_frame'));
- $this->assertNotEmpty($assert_session->waitForElement('css', '.ck-editor'));
- }
-
-}
diff --git a/core/modules/ckeditor/tests/src/FunctionalJavascript/CKEditorIntegrationTest.php b/core/modules/ckeditor/tests/src/FunctionalJavascript/CKEditorIntegrationTest.php
deleted file mode 100644
index cd07517e5acc..000000000000
--- a/core/modules/ckeditor/tests/src/FunctionalJavascript/CKEditorIntegrationTest.php
+++ /dev/null
@@ -1,244 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ckeditor\FunctionalJavascript;
-
-use Drupal\Core\Entity\Entity\EntityFormDisplay;
-use Drupal\editor\Entity\Editor;
-use Drupal\field\Entity\FieldConfig;
-use Drupal\field\Entity\FieldStorageConfig;
-use Drupal\filter\Entity\FilterFormat;
-use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
-use Drupal\node\Entity\NodeType;
-use Drupal\Tests\ckeditor\Traits\CKEditorTestTrait;
-
-/**
- * Tests the integration of CKEditor.
- *
- * @group ckeditor
- */
-class CKEditorIntegrationTest extends WebDriverTestBase {
-
- use CKEditorTestTrait;
-
- /**
- * {@inheritdoc}
- */
- protected $defaultTheme = 'stark';
-
- /**
- * The account.
- *
- * @var \Drupal\user\UserInterface
- */
- protected $account;
-
- /**
- * The FilterFormat config entity used for testing.
- *
- * @var \Drupal\filter\FilterFormatInterface
- */
- protected $filterFormat;
-
- /**
- * {@inheritdoc}
- */
- protected static $modules = ['node', 'ckeditor', 'filter', 'ckeditor_test'];
-
- /**
- * The theme to install as the default for testing.
- *
- * @var string
- *
- * @todo This test's reliance on classes makes Stark a bad fit as a base theme.
- * Change the default theme to Starterkit once it is stable.
- *
- * @see https://www.drupal.org/project/drupal/issues/3275827
- */
- protected function setUp(): void {
- parent::setUp();
-
- // Create a text format and associate CKEditor.
- $this->filterFormat = FilterFormat::create([
- 'format' => 'filtered_html',
- 'name' => 'Filtered HTML',
- 'weight' => 0,
- ]);
- $this->filterFormat->save();
-
- Editor::create([
- 'format' => 'filtered_html',
- 'editor' => 'ckeditor',
- ])->save();
-
- // Create a node type for testing.
- NodeType::create(['type' => 'page', 'name' => 'page'])->save();
-
- $field_storage = FieldStorageConfig::loadByName('node', 'body');
-
- // Create a body field instance for the 'page' node type.
- FieldConfig::create([
- 'field_storage' => $field_storage,
- 'bundle' => 'page',
- 'label' => 'Body',
- 'settings' => ['display_summary' => TRUE],
- 'required' => TRUE,
- ])->save();
-
- // Assign widget settings for the 'default' form mode.
- EntityFormDisplay::create([
- 'targetEntityType' => 'node',
- 'bundle' => 'page',
- 'mode' => 'default',
- 'status' => TRUE,
- ])->setComponent('body', ['type' => 'text_textarea_with_summary'])
- ->save();
-
- $this->account = $this->drupalCreateUser([
- 'administer nodes',
- 'create page content',
- 'use text format filtered_html',
- ]);
- $this->drupalLogin($this->account);
- }
-
- /**
- * Tests if the fragment link to a textarea works with CKEditor enabled.
- */
- public function testFragmentLink() {
- $session = $this->getSession();
- $web_assert = $this->assertSession();
- $ckeditor_id = '#cke_edit-body-0-value';
-
- $this->drupalGet('node/add/page');
-
- $session->getPage();
-
- // Add a bottom margin to the title field to be sure the body field is not
- // visible.
- $session->executeScript("document.getElementById('edit-title-0-value').style.marginBottom = window.innerHeight*2 +'px';");
-
- $this->assertSession()->waitForElementVisible('css', $ckeditor_id);
- // Check that the CKEditor-enabled body field is currently not visible in
- // the viewport.
- $web_assert->assertNotVisibleInViewport('css', $ckeditor_id, 'topLeft', 'CKEditor-enabled body field is visible.');
-
- $before_url = $session->getCurrentUrl();
-
- // Trigger a hash change with as target the hidden textarea.
- $session->executeScript("location.hash = '#edit-body-0-value';");
-
- // Check that the CKEditor-enabled body field is visible in the viewport.
- $web_assert->assertVisibleInViewport('css', $ckeditor_id, 'topLeft', 'CKEditor-enabled body field is not visible.');
-
- // Use JavaScript to go back in the history instead of
- // \Behat\Mink\Session::back() because that function doesn't work after a
- // hash change.
- $session->executeScript("history.back();");
-
- $after_url = $session->getCurrentUrl();
-
- // Check that going back in the history worked.
- self::assertEquals($before_url, $after_url, 'History back works.');
- }
-
- /**
- * Tests if the Image button appears and works as expected.
- */
- public function testDrupalImageDialog() {
- $session = $this->getSession();
- $web_assert = $this->assertSession();
-
- $this->drupalGet('node/add/page');
- $session->getPage();
-
- // Asserts the Image button is present in the toolbar.
- $web_assert->elementExists('css', '#cke_edit-body-0-value .cke_button__drupalimage');
-
- // Asserts the image dialog opens when clicking the Image button.
- $this->click('.cke_button__drupalimage');
- $this->assertNotEmpty($web_assert->waitForElement('css', '.ui-dialog'));
-
- $web_assert->elementContains('css', '.ui-dialog .ui-dialog-titlebar', 'Insert Image');
- }
-
- /**
- * Tests if the Drupal Image Caption plugin appears and works as expected.
- */
- public function testDrupalImageCaptionDialog() {
- $web_assert = $this->assertSession();
-
- // Disable the caption filter.
- $this->filterFormat->setFilterConfig('filter_caption', [
- 'status' => FALSE,
- ]);
- $this->filterFormat->save();
-
- // If the caption filter is disabled, its checkbox should be absent.
- $this->drupalGet('node/add/page');
- $this->waitForEditor();
- $this->pressEditorButton('drupalimage');
- $this->assertNotEmpty($web_assert->waitForElement('css', '.ui-dialog'));
- $web_assert->elementNotExists('css', '.ui-dialog input[name="attributes[hasCaption]"]');
-
- // Enable the caption filter again.
- $this->filterFormat->setFilterConfig('filter_caption', [
- 'status' => TRUE,
- ]);
- $this->filterFormat->save();
-
- // If the caption filter is enabled, its checkbox should be present.
- $this->drupalGet('node/add/page');
- $this->waitForEditor();
- $this->pressEditorButton('drupalimage');
- $this->assertNotEmpty($web_assert->waitForElement('css', '.ui-dialog'));
- $web_assert->elementExists('css', '.ui-dialog input[name="attributes[hasCaption]"]');
- }
-
- /**
- * Tests if CKEditor is properly styled inside an off-canvas dialog.
- */
- public function testOffCanvasStyles() {
- $assert_session = $this->assertSession();
- $page = $this->getSession()->getPage();
-
- $this->drupalGet('/ckeditor_test/off_canvas');
-
- // The "Add Node" link triggers an off-canvas dialog with an add node form
- // that includes CKEditor.
- $page->clickLink('Add Node');
- $assert_session->waitForElementVisible('css', '#drupal-off-canvas');
- $assert_session->assertWaitOnAjaxRequest();
-
- // Check the background color of two CKEditor elements to confirm they are
- // not overridden by the off-canvas css reset.
- $assert_session->elementExists('css', '.cke_top');
- $ckeditor_top_bg_color = $this->getSession()->evaluateScript('window.getComputedStyle(document.getElementsByClassName(\'cke_top\')[0]).backgroundColor');
- $this->assertEquals('rgb(248, 248, 248)', $ckeditor_top_bg_color);
-
- $assert_session->elementExists('css', '.cke_button__source');
- $ckeditor_source_button_bg_color = $this->getSession()->evaluateScript('window.getComputedStyle(document.getElementsByClassName(\'cke_button__source\')[0]).backgroundColor');
- $this->assertEquals('rgba(0, 0, 0, 0)', $ckeditor_source_button_bg_color);
-
- // Check that only one off-canvas style is cached in local storage and that
- // it gets updated with the cache-busting query string.
- $get_cache_keys = 'Object.keys(window.localStorage).filter(function (i) {return i.indexOf(\'Drupal.off-canvas.css.\') === 0})';
- $old_keys = $this->getSession()->evaluateScript($get_cache_keys);
- // Flush the caches to ensure the new timestamp is altered into the
- // drupal.ckeditor library's javascript settings.
- $this->resetAll();
- // Normally flushing caches regenerates the cache busting query string, but
- // as it's based on the request time, it won't change within this test so
- // explicitly set it.
- \Drupal::state()->set('system.css_js_query_string', '0');
- $this->drupalGet('/ckeditor_test/off_canvas');
- $page->clickLink('Add Node');
- $assert_session->waitForElementVisible('css', '#drupal-off-canvas');
- $assert_session->assertWaitOnAjaxRequest();
- $new_keys = $this->getSession()->evaluateScript($get_cache_keys);
-
- $this->assertCount(1, $old_keys, 'Only one off-canvas style was cached before clearing caches.');
- $this->assertCount(1, $new_keys, 'Only one off-canvas style was cached after clearing caches.');
- $this->assertNotEquals($old_keys, $new_keys, 'Clearing caches changed the off-canvas style cache key.');
- }
-
-}
diff --git a/core/modules/ckeditor/tests/src/FunctionalJavascript/FormErrorHandlerCKEditorTest.php b/core/modules/ckeditor/tests/src/FunctionalJavascript/FormErrorHandlerCKEditorTest.php
deleted file mode 100644
index 354f97bf0c8f..000000000000
--- a/core/modules/ckeditor/tests/src/FunctionalJavascript/FormErrorHandlerCKEditorTest.php
+++ /dev/null
@@ -1,120 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ckeditor\FunctionalJavascript;
-
-use Drupal\Core\Entity\Entity\EntityFormDisplay;
-use Drupal\editor\Entity\Editor;
-use Drupal\field\Entity\FieldConfig;
-use Drupal\field\Entity\FieldStorageConfig;
-use Drupal\filter\Entity\FilterFormat;
-use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
-use Drupal\node\Entity\NodeType;
-
-/**
- * Tests the inline errors fragment link to a CKEditor-enabled textarea.
- *
- * @group ckeditor
- */
-class FormErrorHandlerCKEditorTest extends WebDriverTestBase {
-
- /**
- * {@inheritdoc}
- */
- protected static $modules = [
- 'node',
- 'ckeditor',
- 'inline_form_errors',
- 'filter',
- ];
-
- /**
- * {@inheritdoc}
- */
- protected $defaultTheme = 'starterkit_theme';
-
- /**
- * {@inheritdoc}
- */
- protected function setUp(): void {
- parent::setUp();
-
- // Create a text format and associate CKEditor.
- $filtered_html_format = FilterFormat::create([
- 'format' => 'filtered_html',
- 'name' => 'Filtered HTML',
- 'weight' => 0,
- ]);
- $filtered_html_format->save();
-
- Editor::create([
- 'format' => 'filtered_html',
- 'editor' => 'ckeditor',
- ])->save();
-
- // Create a node type for testing.
- NodeType::create(['type' => 'page', 'name' => 'page'])->save();
-
- $field_storage = FieldStorageConfig::loadByName('node', 'body');
-
- // Create a body field instance for the 'page' node type.
- FieldConfig::create([
- 'field_storage' => $field_storage,
- 'bundle' => 'page',
- 'label' => 'Body',
- 'settings' => ['display_summary' => TRUE],
- 'required' => TRUE,
- ])->save();
-
- // Assign widget settings for the 'default' form mode.
- EntityFormDisplay::create([
- 'targetEntityType' => 'node',
- 'bundle' => 'page',
- 'mode' => 'default',
- 'status' => TRUE,
- ])->setComponent('body', ['type' => 'text_textarea_with_summary'])
- ->save();
-
- $account = $this->drupalCreateUser([
- 'administer nodes',
- 'create page content',
- 'use text format filtered_html',
- ]);
- $this->drupalLogin($account);
- }
-
- /**
- * Tests if the fragment link to a textarea works with CKEditor enabled.
- */
- public function testFragmentLink() {
- $session = $this->getSession();
- $web_assert = $this->assertSession();
- $ckeditor_id = '#cke_edit-body-0-value';
-
- $this->drupalGet('node/add/page');
-
- // Only enter a title in the node add form and leave the body field empty.
- $edit = ['edit-title-0-value' => 'Test inline form error with CKEditor'];
-
- $this->submitForm($edit, 'Save');
-
- $this->assertSession()->waitForElement('css', '#cke_edit-body-0-value');
- // Add a bottom margin to the title field to be sure the body field is not
- // visible.
- $session->executeScript("document.getElementById('edit-title-0-value').style.marginBottom = window.innerHeight*2 + 'px';");
-
- // Check that the CKEditor-enabled body field is currently not visible in
- // the viewport.
- $web_assert->assertNotVisibleInViewport('css', $ckeditor_id, 'topLeft', 'CKEditor-enabled body field is not visible.');
-
- // Check if we can find the error fragment link within the errors summary
- // message.
- $errors_link = $this->assertSession()->waitForElementVisible('css', '.messages--error a[href="#edit-body-0-value"]');
- $this->assertNotEmpty($errors_link, 'Error fragment link is visible.');
-
- $errors_link->click();
-
- // Check that the CKEditor-enabled body field is visible in the viewport.
- $web_assert->assertVisibleInViewport('css', $ckeditor_id, 'topLeft', 'CKEditor-enabled body field is visible.');
- }
-
-}
diff --git a/core/modules/ckeditor/tests/src/FunctionalJavascript/MediaLibraryTest.php b/core/modules/ckeditor/tests/src/FunctionalJavascript/MediaLibraryTest.php
deleted file mode 100644
index 9c51a809edd2..000000000000
--- a/core/modules/ckeditor/tests/src/FunctionalJavascript/MediaLibraryTest.php
+++ /dev/null
@@ -1,327 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ckeditor\FunctionalJavascript;
-
-use Drupal\Component\Utility\Html;
-use Drupal\editor\Entity\Editor;
-use Drupal\file\Entity\File;
-use Drupal\filter\Entity\FilterFormat;
-use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
-use Drupal\media\Entity\Media;
-use Drupal\Tests\ckeditor\Traits\CKEditorTestTrait;
-use Drupal\Tests\ckeditor\Traits\CKEditorAdminSortTrait;
-use Drupal\Tests\media\Traits\MediaTypeCreationTrait;
-use Drupal\Tests\TestFileCreationTrait;
-
-/**
- * @coversDefaultClass \Drupal\ckeditor\Plugin\CKEditorPlugin\DrupalMediaLibrary
- * @group ckeditor
- */
-class MediaLibraryTest extends WebDriverTestBase {
-
- use CKEditorTestTrait;
- use CKEditorAdminSortTrait;
- use MediaTypeCreationTrait;
- use TestFileCreationTrait;
-
- /**
- * {@inheritdoc}
- */
- protected $defaultTheme = 'starterkit_theme';
-
- /**
- * The user to use during testing.
- *
- * @var \Drupal\user\UserInterface
- */
- protected $user;
-
- /**
- * The media item to embed.
- *
- * @var \Drupal\media\MediaInterface
- */
- protected $media;
-
- /**
- * {@inheritdoc}
- */
- protected static $modules = [
- 'ckeditor',
- 'media_library',
- 'node',
- 'text',
- ];
-
- /**
- * {@inheritdoc}
- */
- protected function setUp(): void {
- parent::setUp();
-
- FilterFormat::create([
- 'format' => 'test_format',
- 'name' => 'Test format',
- 'filters' => [
- 'media_embed' => ['status' => TRUE],
- ],
- ])->save();
- Editor::create([
- 'editor' => 'ckeditor',
- 'format' => 'test_format',
- 'settings' => [
- 'toolbar' => [
- 'rows' => [
- [
- [
- 'name' => 'Main',
- 'items' => [
- 'Source',
- 'Undo',
- 'Redo',
- ],
- ],
- ],
- [
- [
- 'name' => 'Embeds',
- 'items' => [
- 'DrupalMediaLibrary',
- ],
- ],
- ],
- ],
- ],
- ],
- ])->save();
-
- $this->drupalCreateContentType(['type' => 'blog']);
-
- // Note that media_install() grants 'view media' to all users by default.
- $this->user = $this->drupalCreateUser([
- 'use text format test_format',
- 'access media overview',
- 'create blog content',
- ]);
-
- // Create a media type that starts with the letter a, to test tab order.
- $this->createMediaType('image', ['id' => 'arrakis', 'label' => 'Arrakis']);
-
- // Create a sample media entity to be embedded.
- $this->createMediaType('image', ['id' => 'image', 'label' => 'Image']);
- File::create([
- 'uri' => $this->getTestFiles('image')[0]->uri,
- ])->save();
- $this->media = Media::create([
- 'bundle' => 'image',
- 'name' => 'Fear is the mind-killer',
- 'field_media_image' => [
- [
- 'target_id' => 1,
- 'alt' => 'default alt',
- 'title' => 'default title',
- ],
- ],
- ]);
- $this->media->save();
-
- $arrakis_media = Media::create([
- 'bundle' => 'arrakis',
- 'name' => 'Le baron Vladimir Harkonnen',
- 'field_media_image' => [
- [
- 'target_id' => 1,
- 'alt' => 'Il complote pour détruire le duc Leto',
- 'title' => 'Il complote pour détruire le duc Leto',
- ],
- ],
- ]);
- $arrakis_media->save();
-
- $this->drupalLogin($this->user);
- }
-
- /**
- * Tests validation that DrupalMediaLibrary requires media_embed filter.
- */
- public function testConfigurationValidation() {
- $page = $this->getSession()->getPage();
- $assert_session = $this->assertSession();
- $admin_user = $this->drupalCreateUser([
- 'access administration pages',
- 'administer site configuration',
- 'administer filters',
- ]);
- $this->drupalLogin($admin_user);
- $this->drupalGet('/admin/config/content/formats/manage/test_format');
- $page->uncheckField('filters[media_embed][status]');
- $page->pressButton('Save configuration');
- $assert_session->pageTextContains('The Embed media filter must be enabled to use the Insert from Media Library button.');
- $page->checkField('filters[media_embed][status]');
- $page->pressButton('Save configuration');
- $assert_session->pageTextContains('The text format Test format has been updated.');
-
- // Now test adding a new format.
- $this->drupalGet('/admin/config/content/formats/add');
- $page->fillField('name', 'Sulaco');
- // Wait for machine name to be filled in.
- $this->assertNotEmpty($assert_session->waitForText('sulaco'));
- $page->checkField('roles[authenticated]');
- $page->selectFieldOption('editor[editor]', 'ckeditor');
-
- $targetSelector = 'ul.ckeditor-toolbar-group-buttons';
- $buttonSelector = 'li[data-drupal-ckeditor-button-name="DrupalMediaLibrary"]';
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', $targetSelector));
- $this->assertNotEmpty($assert_session->elementExists('css', $buttonSelector));
- $this->sortableTo($buttonSelector, 'ul.ckeditor-available-buttons', $targetSelector);
- $page->pressButton('Save configuration');
- $assert_session->pageTextContains('The Embed media filter must be enabled to use the Insert from Media Library button.');
- $page->checkField('filters[media_embed][status]');
- $page->pressButton('Save configuration');
- $assert_session->pageTextContains('Added text format Sulaco.');
-
- // Test that when adding the DrupalMediaLibrary button to the editor the
- // correct attributes are added to the <drupal-media> tag in the Allowed
- // HTML tags.
- $this->drupalGet('/admin/config/content/formats/manage/sulaco');
- $page->checkField('filters[filter_html][status]');
- $expected = 'drupal-media data-entity-type data-entity-uuid data-view-mode data-align data-caption alt title';
- $allowed_html = $assert_session->fieldExists('filters[filter_html][settings][allowed_html]')->getValue();
- $this->assertStringContainsString($expected, $allowed_html);
- $page->pressButton('Save configuration');
- $assert_session->pageTextContains('The text format Sulaco has been updated.');
-
- // Test that the config form allows removing non-required attributes from
- // the <drupal-media> tag.
- $this->drupalGet('/admin/config/content/formats/manage/sulaco');
- $allowed_html_field = $assert_session->fieldExists('filters[filter_html][settings][allowed_html]');
- $allowed_html = $allowed_html_field->getValue();
- $search = 'drupal-media data-entity-type data-entity-uuid data-view-mode data-align data-caption alt title';
- $replace = 'drupal-media data-entity-type data-entity-uuid';
- $allowed_html = str_replace($search, $replace, $allowed_html);
- $page->clickLink('Limit allowed HTML tags and correct faulty HTML');
- $this->assertTrue($allowed_html_field->waitFor(10, function ($allowed_html_field) {
- return $allowed_html_field->isVisible();
- }));
- $allowed_html_field->setValue($allowed_html);
- $page->pressButton('Save configuration');
- $assert_session->pageTextContains('The text format Sulaco has been updated.');
- }
-
- /**
- * Tests using DrupalMediaLibrary button to embed media into CKEditor.
- */
- public function testButton() {
- $this->drupalGet('/node/add/blog');
- $this->waitForEditor();
- $this->pressEditorButton('drupalmedialibrary');
- $assert_session = $this->assertSession();
- $page = $this->getSession()->getPage();
- $this->assertNotEmpty($assert_session->waitForId('drupal-modal'));
-
- // Test that the order is the order set in DrupalMediaLibrary::getConfig().
- $tabs = $page->findAll('css', '.media-library-menu__link');
- $expected_tab_order = [
- 'Show Image media (selected)',
- 'Show Arrakis media',
- ];
- foreach ($tabs as $key => $tab) {
- $this->assertSame($expected_tab_order[$key], $tab->getText());
- }
-
- $assert_session->pageTextContains('0 of 1 item selected');
- $assert_session->elementExists('css', '.js-media-library-item')->click();
- $assert_session->pageTextContains('1 of 1 item selected');
- $assert_session->elementExists('css', '.ui-dialog-buttonpane')->pressButton('Insert selected');
- $this->assignNameToCkeditorIframe();
- $this->getSession()->switchToIFrame('ckeditor');
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', '.cke_widget_drupalmedia drupal-media .media'));
- $this->pressEditorButton('source');
- $value = $assert_session->elementExists('css', 'textarea.cke_source')->getValue();
- $dom = Html::load($value);
- $xpath = new \DOMXPath($dom);
- $drupal_media = $xpath->query('//drupal-media')[0];
- $expected_attributes = [
- 'data-entity-type' => 'media',
- 'data-entity-uuid' => $this->media->uuid(),
- 'data-align' => 'center',
- ];
- foreach ($expected_attributes as $name => $expected) {
- $this->assertSame($expected, $drupal_media->getAttribute($name));
- }
- $this->pressEditorButton('source');
- // Why do we keep switching to the 'ckeditor' iframe? Because the buttons
- // are in a separate iframe from the markup, so after calling
- // ::pressEditorButton() (which switches to the button iframe), we'll need
- // to switch back to the CKEditor iframe.
- $this->assignNameToCkeditorIframe();
- $this->getSession()->switchToIFrame('ckeditor');
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', '.cke_widget_drupalmedia drupal-media .media'));
- $this->assertEditorButtonEnabled('undo');
- $this->pressEditorButton('undo');
- $this->getSession()->switchToIFrame('ckeditor');
- $this->assertEmpty($assert_session->waitForElementVisible('css', '.cke_widget_drupalmedia drupal-media .media'));
- $this->assertEditorButtonDisabled('undo');
- $this->pressEditorButton('redo');
- $this->getSession()->switchToIFrame('ckeditor');
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', '.cke_widget_drupalmedia drupal-media .media'));
- $this->assertEditorButtonEnabled('undo');
- }
-
- /**
- * Tests the allowed media types setting on the MediaEmbed filter.
- */
- public function testAllowedMediaTypes() {
- $test_cases = [
- 'all_media_types' => [],
- 'only_image' => ['image' => 'image'],
- 'only_arrakis' => ['arrakis' => 'arrakis'],
- 'both_items_checked' => [
- 'image' => 'image',
- 'arrakis' => 'arrakis',
- ],
- ];
-
- foreach ($test_cases as $allowed_media_types) {
- // Update the filter format to set the allowed media types.
- FilterFormat::load('test_format')
- ->setFilterConfig('media_embed', [
- 'status' => TRUE,
- 'settings' => [
- 'default_view_mode' => 'view_mode_1',
- 'allowed_media_types' => $allowed_media_types,
- 'allowed_view_modes' => [
- 'view_mode_1' => 'view_mode_1',
- 'view_mode_2' => 'view_mode_2',
- ],
- ],
- ])->save();
-
- // Now test opening the media library from the CKEditor plugin, and
- // verify the expected behavior.
- $this->drupalGet('/node/add/blog');
- $this->waitForEditor();
- $this->pressEditorButton('drupalmedialibrary');
- $assert_session = $this->assertSession();
- $this->assertNotEmpty($assert_session->waitForId('media-library-wrapper'));
-
- if (empty($allowed_media_types) || count($allowed_media_types) === 2) {
- $menu = $assert_session->elementExists('css', '.js-media-library-menu');
- $assert_session->elementExists('named', ['link', 'Image'], $menu);
- $assert_session->elementExists('named', ['link', 'Arrakis'], $menu);
- $assert_session->elementTextContains('css', '.js-media-library-item', 'Fear is the mind-killer');
- }
- elseif (count($allowed_media_types) === 1 && !empty($allowed_media_types['image'])) {
- // No tabs should appear if there's only one media type available.
- $assert_session->elementNotExists('css', '.js-media-library-menu');
- $assert_session->elementTextContains('css', '.js-media-library-item', 'Fear is the mind-killer');
- }
- elseif (count($allowed_media_types) === 1 && !empty($allowed_media_types['arrakis'])) {
- // No tabs should appear if there's only one media type available.
- $assert_session->elementNotExists('css', '.js-media-library-menu');
- $assert_session->elementTextContains('css', '.js-media-library-item', 'Le baron Vladimir Harkonnen');
- }
- }
- }
-
-}
diff --git a/core/modules/ckeditor/tests/src/FunctionalJavascript/MediaTest.php b/core/modules/ckeditor/tests/src/FunctionalJavascript/MediaTest.php
deleted file mode 100644
index a970b3907763..000000000000
--- a/core/modules/ckeditor/tests/src/FunctionalJavascript/MediaTest.php
+++ /dev/null
@@ -1,1603 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ckeditor\FunctionalJavascript;
-
-use Drupal\Component\Utility\Html;
-use Drupal\Core\Database\Database;
-use Drupal\Core\Entity\Entity\EntityViewDisplay;
-use Drupal\editor\Entity\Editor;
-use Drupal\field\Entity\FieldConfig;
-use Drupal\file\Entity\File;
-use Drupal\filter\Entity\FilterFormat;
-use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
-use Drupal\language\Entity\ConfigurableLanguage;
-use Drupal\language\Entity\ContentLanguageSettings;
-use Drupal\media\Entity\Media;
-use Drupal\Tests\ckeditor\Traits\CKEditorTestTrait;
-use Drupal\Tests\media\Traits\MediaTypeCreationTrait;
-use Drupal\Tests\TestFileCreationTrait;
-use Drupal\user\Entity\Role;
-use Drupal\user\RoleInterface;
-use Drupal\Core\Entity\Entity\EntityViewMode;
-
-/**
- * @coversDefaultClass \Drupal\ckeditor\Plugin\CKEditorPlugin\DrupalMedia
- * @group ckeditor
- */
-class MediaTest extends WebDriverTestBase {
-
- use CKEditorTestTrait;
- use MediaTypeCreationTrait;
- use TestFileCreationTrait;
-
- /**
- * The user to use during testing.
- *
- * @var \Drupal\user\UserInterface
- */
- protected $adminUser;
-
- /**
- * The sample Media entity to embed.
- *
- * @var \Drupal\media\MediaInterface
- */
- protected $media;
-
- /**
- * A host entity with a body field to embed media in.
- *
- * @var \Drupal\node\NodeInterface
- */
- protected $host;
-
- /**
- * The character code for the return key.
- *
- * @var int
- */
- const RETURN_KEY = 13;
-
- /**
- * The character code for the space bar.
- *
- * @var int
- */
- const SPACE_BAR = 32;
-
- /**
- * {@inheritdoc}
- */
- protected static $modules = [
- 'ckeditor',
- 'media',
- 'node',
- 'text',
- 'media_test_embed',
- ];
-
- /**
- * {@inheritdoc}
- */
- protected $defaultTheme = 'starterkit_theme';
-
- /**
- * {@inheritdoc}
- */
- protected function setUp(): void {
- parent::setUp();
-
- FilterFormat::create([
- 'format' => 'test_format',
- 'name' => 'Test format',
- 'filters' => [
- 'filter_align' => ['status' => TRUE],
- 'filter_caption' => ['status' => TRUE],
- 'media_embed' => ['status' => TRUE],
- ],
- ])->save();
- Editor::create([
- 'editor' => 'ckeditor',
- 'format' => 'test_format',
- 'settings' => [
- 'toolbar' => [
- 'rows' => [
- [
- [
- 'name' => 'All the things',
- 'items' => [
- 'Source',
- 'Bold',
- 'Italic',
- 'DrupalLink',
- 'DrupalUnlink',
- 'DrupalImage',
- ],
- ],
- ],
- ],
- ],
- ],
- ])->save();
-
- // Note that media_install() grants 'view media' to all users by default.
- $this->adminUser = $this->drupalCreateUser([
- 'use text format test_format',
- 'bypass node access',
- ]);
-
- // Create a sample media entity to be embedded.
- $this->createMediaType('image', ['id' => 'image']);
- File::create([
- 'uri' => $this->getTestFiles('image')[0]->uri,
- ])->save();
- $this->media = Media::create([
- 'bundle' => 'image',
- 'name' => 'Screaming hairy armadillo',
- 'field_media_image' => [
- [
- 'target_id' => 1,
- 'alt' => 'default alt',
- 'title' => 'default title',
- ],
- ],
- ]);
- $this->media->save();
-
- // Create a sample host entity to embed media in.
- $this->drupalCreateContentType(['type' => 'blog']);
- $this->host = $this->createNode([
- 'type' => 'blog',
- 'title' => 'Animals with strange names',
- 'body' => [
- 'value' => '<drupal-media data-caption="baz" data-entity-type="media" data-entity-uuid="' . $this->media->uuid() . '"></drupal-media>',
- 'format' => 'test_format',
- ],
- ]);
- $this->host->save();
-
- $this->drupalLogin($this->adminUser);
- }
-
- /**
- * Tests that only <drupal-media> tags are processed.
- *
- * @see \Drupal\Tests\media\Kernel\MediaEmbedFilterTest::testOnlyDrupalMediaTagProcessed()
- */
- public function testOnlyDrupalMediaTagProcessed() {
- $original_value = $this->host->body->value;
- $this->host->body->value = str_replace('drupal-media', 'p', $original_value);
- $this->host->save();
-
- // Assert that `<p data-* …>` is not upcast into a CKEditor Widget.
- $this->drupalGet($this->host->toUrl('edit-form'));
- $this->waitForEditor();
- $this->assignNameToCkeditorIframe();
- $this->getSession()->switchToIFrame('ckeditor');
- $assert_session = $this->assertSession();
- $this->assertEmpty($assert_session->waitForElementVisible('css', 'img[src*="image-test.png"]', 1000));
- $assert_session->elementNotExists('css', 'figure');
-
- $this->host->body->value = $original_value;
- $this->host->save();
-
- // Assert that `<drupal-media data-* …>` is upcast into a CKEditor Widget.
- $this->getSession()->reload();
- $this->waitForEditor();
- $this->assignNameToCkeditorIframe();
- $this->getSession()->switchToIFrame('ckeditor');
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'img[src*="image-test.png"]'));
- $assert_session->elementExists('css', 'figure');
- }
-
- /**
- * Tests that failed media embed preview requests inform the end user.
- */
- public function testErrorMessages() {
- // This test currently frequently causes the SQLite database to lock, so
- // skip the test on SQLite until the issue can be resolved.
- // @todo https://www.drupal.org/project/drupal/issues/3273626
- if (Database::getConnection()->driver() === 'sqlite') {
- $this->markTestSkipped('Test frequently causes a locked database on SQLite');
- }
-
- // Assert that a request to the `media.filter.preview` route that does not
- // result in a 200 response (due to server error or network error) is
- // handled in the JavaScript by displaying the expected error message.
- // @see core/modules/media/js/media_embed_ckeditor.theme.js
- // @see core/modules/media/js/plugins/drupalmedia/plugin.js
- $this->container->get('state')->set('test_media_filter_controller_throw_error', TRUE);
- $this->drupalGet($this->host->toUrl('edit-form'));
- $this->waitForEditor();
- $this->assignNameToCkeditorIframe();
- $this->getSession()->switchToIFrame('ckeditor');
- $assert_session = $this->assertSession();
- $this->assertEmpty($assert_session->waitForElementVisible('css', 'img[src*="image-test.png"]', 1000));
- $assert_session->elementNotExists('css', 'figure');
- $this->assertNotEmpty($assert_session->waitForText('An error occurred while trying to preview the media. Please save your work and reload this page.'));
- // Now assert that the error doesn't appear when the override to force an
- // error is removed.
- $this->container->get('state')->set('test_media_filter_controller_throw_error', FALSE);
- $this->getSession()->reload();
- $this->waitForEditor();
- $this->assignNameToCkeditorIframe();
- $this->getSession()->switchToIFrame('ckeditor');
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'img[src*="image-test.png"]'));
-
- // There's a second kind of error message that comes from the back end
- // that happens when the media uuid can't be converted to a media preview.
- // In this case, the error will appear in a the themeable
- // media-embed-error.html template. We have a hook altering the css
- // classes to test the twi template is working properly and picking up our
- // extra class.
- // @see \Drupal\media\Plugin\Filter\MediaEmbed::renderMissingMediaIndicator()
- // @see core/modules/media/templates/media-embed-error.html.twig
- // @see media_test_embed_preprocess_media_embed_error()
- $original_value = $this->host->body->value;
- $this->host->body->value = str_replace($this->media->uuid(), 'invalid_uuid', $original_value);
- $this->host->save();
- $this->drupalGet($this->host->toUrl('edit-form'));
- $this->waitForEditor();
- $this->assignNameToCkeditorIframe();
- $this->getSession()->switchToIFrame('ckeditor');
- $this->assertNotEmpty($assert_session->waitForElement('css', 'drupal-media figure.caption-drupal-media .this-error-message-is-themeable'));
-
- // Test when using the starterkit_theme theme, an additional class is added
- // to the error, which is supported by
- // stable9/templates/content/media-embed-error.html.twig.
- $this->assertTrue($this->container->get('theme_installer')->install(['starterkit_theme']));
- $this->config('system.theme')
- ->set('default', 'starterkit_theme')
- ->save();
- $this->drupalGet($this->host->toUrl('edit-form'));
- $this->waitForEditor();
- $this->assignNameToCkeditorIframe();
- $this->getSession()->switchToIFrame('ckeditor');
- $this->assertNotEmpty($assert_session->waitForElement('css', 'drupal-media figure.caption-drupal-media .this-error-message-is-themeable'));
-
- // Test that restoring a valid UUID results in the media embed preview
- // displaying.
- $this->host->body->value = $original_value;
- $this->host->save();
- $this->drupalGet($this->host->toUrl('edit-form'));
- $this->waitForEditor();
- $this->assignNameToCkeditorIframe();
- $this->getSession()->switchToIFrame('ckeditor');
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'img[src*="image-test.png"]'));
- $assert_session->elementNotExists('css', 'drupal-media figure.caption-drupal-media .this-error-message-is-themeable');
- }
-
- /**
- * The CKEditor Widget must load a preview generated using the default theme.
- */
- public function testPreviewUsesDefaultThemeAndIsClientCacheable() {
- // Make the node edit form use the admin theme, like on most Drupal sites.
- $this->config('node.settings')
- ->set('use_admin_theme', TRUE)
- ->save();
-
- // Allow the test user to view the admin theme.
- $this->adminUser->addRole($this->drupalCreateRole(['view the administration theme']));
- $this->adminUser->save();
-
- // Configure a different default and admin theme, like on most Drupal sites.
- $this->config('system.theme')
- ->set('default', 'stable9')
- ->set('admin', 'starterkit_theme')
- ->save();
-
- // Assert that when looking at an embedded entity in the CKEditor Widget,
- // the preview is generated using the default theme, not the admin theme.
- // @see media_test_embed_entity_view_alter()
- $this->drupalGet($this->host->toUrl('edit-form'));
- $this->waitForEditor();
- $this->assignNameToCkeditorIframe();
- $this->getSession()->switchToIFrame('ckeditor');
- $assert_session = $this->assertSession();
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'img[src*="image-test.png"]'));
- $element = $assert_session->elementExists('css', '[data-media-embed-test-active-theme]');
- $this->assertSame('stable9', $element->getAttribute('data-media-embed-test-active-theme'));
- // Assert that the first preview request transferred >500 B over the wire.
- // Then toggle source mode on and off. This causes the CKEditor widget to be
- // destroyed and then reconstructed. Assert that during this reconstruction,
- // a second request is sent. This second request should have transferred 0
- // bytes: the browser should have cached the response, thus resulting in a
- // much better user experience.
- $this->assertGreaterThan(500, $this->getLastPreviewRequestTransferSize());
- $this->pressEditorButton('source');
- $this->assertNotEmpty($assert_session->waitForElement('css', 'textarea.cke_source'));
- $this->pressEditorButton('source');
- $this->assignNameToCkeditorIframe();
- $this->getSession()->switchToIFrame('ckeditor');
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'img[src*="image-test.png"]'));
- $this->assertSame(0, $this->getLastPreviewRequestTransferSize());
- }
-
- /**
- * Tests caption editing in the CKEditor widget.
- */
- public function testEditableCaption() {
- $page = $this->getSession()->getPage();
- $assert_session = $this->assertSession();
- // Test that setting caption to blank string doesn't break 'Edit media'
- // button.
- $original_value = $this->host->body->value;
- $this->host->body->value = str_replace('data-caption="baz"', 'data-caption=""', $original_value);
- $this->host->save();
- $this->drupalGet($this->host->toUrl('edit-form'));
- $this->waitForEditor();
- $this->assignNameToCkeditorIframe();
- $this->getSession()->switchToIFrame('ckeditor');
- $this->assertNotEmpty($assert_session->waitForButton('Edit media'));
- // Test `aria-label` attribute appears on the widget wrapper.
- $assert_session->elementExists('css', '.cke_widget_drupalmedia[aria-label="Screaming hairy armadillo"]');
- $assert_session->elementContains('css', 'figcaption', '');
- $assert_session->elementAttributeContains('css', 'figcaption', 'data-placeholder', 'Enter caption here');
- // Test if you leave the caption blank, but change another attribute,
- // such as the alt text, the editable caption is still there and the edit
- // button still exists.
- $this->fillFieldInMetadataDialogAndSubmit('attributes[alt]', 'Mama, life had just begun');
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media img[alt*="Mama, life had just begun"]'));
- $assert_session->buttonExists('Edit media');
- $assert_session->elementContains('css', 'figcaption', '');
- $assert_session->elementAttributeContains('css', 'figcaption', 'data-placeholder', 'Enter caption here');
-
- // Restore caption in saved body value.
- $original_value = $this->host->body->value;
- $this->host->body->value = str_replace('data-caption=""', 'data-caption="baz"', $original_value);
- $this->host->save();
-
- $this->drupalGet($this->host->toUrl('edit-form'));
- $this->waitForEditor();
- $this->assignNameToCkeditorIframe();
- $this->getSession()->switchToIFrame('ckeditor');
- // Assert that figcaption element exists within the drupal-media element.
- $this->assertNotEmpty($figcaption = $assert_session->waitForElement('css', 'drupal-media figcaption'));
- $this->assertSame('baz', $figcaption->getHtml());
-
- // Test that disabling the caption in the metadata dialog removes it
- // from the drupal-media element.
- $this->openMetadataDialogWithKeyPress(static::SPACE_BAR);
- $page->uncheckField('hasCaption');
- $this->submitDialog();
- $this->getSession()->switchToIFrame('ckeditor');
- $this->assertNotEmpty($drupal_media = $assert_session->waitForElementVisible('css', 'drupal-media'));
-
- // Wait for element to update without figcaption.
- $result = $page->waitFor(10, function () use ($drupal_media) {
- return empty($drupal_media->find('css', 'figcaption'));
- });
- // Will be true if no figcaption exists within the drupal-media element.
- $this->assertTrue($result);
-
- // Test that enabling the caption in the metadata dialog adds an editable
- // caption to the embedded media.
- $this->openMetadataDialogWithKeyPress(static::SPACE_BAR);
- $page->checkField('hasCaption');
- $this->submitDialog();
- $this->getSession()->switchToIFrame('ckeditor');
- $this->assertNotEmpty($drupal_media = $assert_session->waitForElementVisible('css', 'drupal-media figcaption'));
-
- // Type into the widget's caption element.
- $this->assertNotEmpty($assert_session->waitForElement('css', 'figcaption'));
- $this->setCaption('Caught in a <strong>landslide</strong>! No escape from <em>reality</em>!');
- $this->getSession()->switchToIFrame('ckeditor');
- $assert_session->elementExists('css', 'figcaption > em');
- $assert_session->elementExists('css', 'figcaption > strong')->click();
-
- // Select the <strong> element and unbold it.
- $this->clickPathLinkByTitleAttribute("strong element");
- $this->pressEditorButton('bold');
- $this->getSession()->switchToIFrame('ckeditor');
- $assert_session->elementExists('css', 'figcaption > em');
- $assert_session->elementNotExists('css', 'figcaption > strong');
-
- // Select the <em> element and unitalicize it.
- $assert_session->elementExists('css', 'figcaption > em')->click();
- $this->clickPathLinkByTitleAttribute("em element");
- $this->pressEditorButton('italic');
-
- // The "source" button should reveal the HTML source in a state matching
- // what is shown in the CKEditor widget.
- $this->pressEditorButton('source');
- $source = $assert_session->elementExists('css', 'textarea.cke_source');
- $value = $source->getValue();
- $dom = Html::load($value);
- $xpath = new \DOMXPath($dom);
- $drupal_media = $xpath->query('//drupal-media')[0];
- $this->assertSame('Caught in a landslide! No escape from reality!', $drupal_media->getAttribute('data-caption'));
-
- // Change the caption by modifying the HTML source directly. When exiting
- // "source" mode, this should be respected.
- $poor_boy_text = "I'm just a <strong>poor boy</strong>, I need no sympathy!";
- $drupal_media->setAttribute("data-caption", $poor_boy_text);
- $source->setValue(Html::serialize($dom));
- $this->pressEditorButton('source');
- $this->assignNameToCkeditorIframe();
- $this->getSession()->switchToIFrame('ckeditor');
- $figcaption = $assert_session->waitForElement('css', 'figcaption');
- $this->assertNotEmpty($figcaption);
- $this->assertSame($poor_boy_text, $figcaption->getHtml());
-
- // Select the <strong> element that we just set in "source" mode. This
- // proves that it was indeed rendered by the CKEditor widget.
- $strong = $figcaption->find('css', 'strong');
- $this->assertNotEmpty($strong);
- $strong->click();
- $this->pressEditorButton('bold');
-
- // Insert a link into the caption.
- $this->clickPathLinkByTitleAttribute("Caption element");
- $this->pressEditorButton('drupallink');
- $field = $assert_session->waitForElementVisible('xpath', '//input[@name="attributes[href]"]');
- $this->assertNotEmpty($field);
- $field->setValue('https://www.drupal.org');
- $assert_session->elementExists('css', 'button.form-submit')->press();
-
- // Wait for the live preview in the CKEditor widget to finish loading, then
- // edit the link; no `data-cke-saved-href` attribute should exist on it.
- $this->getSession()->switchToIFrame('ckeditor');
- $figcaption = $assert_session->waitForElement('css', 'figcaption');
- $page = $this->getSession()->getPage();
- // Wait for AJAX refresh.
- $page->waitFor(10, function () use ($figcaption) {
- return $figcaption->find('xpath', '//a[@href="https://www.drupal.org"]');
- });
- $assert_session->elementExists('css', 'a', $figcaption)->click();
- $this->clickPathLinkByTitleAttribute("a element");
- $this->pressEditorButton('drupallink');
- $field = $assert_session->waitForElementVisible('xpath', '//input[@name="attributes[href]"]');
- $this->assertNotEmpty($field);
- $field->setValue('https://www.drupal.org/project/drupal');
- $assert_session->elementExists('css', 'button.form-submit')->press();
- $this->getSession()->switchToIFrame('ckeditor');
- $figcaption = $assert_session->waitForElement('css', 'figcaption');
- $page = $this->getSession()->getPage();
- // Wait for AJAX refresh.
- $page->waitFor(10, function () use ($figcaption) {
- return $figcaption->find('xpath', '//a[@href="https://www.drupal.org/project/drupal"]');
- });
- $this->pressEditorButton('source');
- $source = $assert_session->elementExists('css', "textarea.cke_source");
- $value = $source->getValue();
- $this->assertStringContainsString('https://www.drupal.org/project/drupal', $value);
- $this->assertStringNotContainsString('data-cke-saved-href', $value);
-
- // Save the entity.
- $assert_session->buttonExists('Save')->press();
-
- // Verify the saved entity when viewed also contains the captioned media.
- $link = $assert_session->elementExists('css', 'figcaption > a');
- $this->assertSame('https://www.drupal.org/project/drupal', $link->getAttribute('href'));
- $this->assertSame("I'm just a poor boy, I need no sympathy!", $link->getText());
-
- // Edit it again, type a different caption in the widget.
- $this->drupalGet($this->host->toUrl('edit-form'));
- $this->waitForEditor();
- $this->assignNameToCkeditorIframe();
- $this->getSession()->switchToIFrame('ckeditor');
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'figcaption'));
- $this->setCaption('Scaramouch, <em>Scaramouch</em>, will you do the <strong>Fandango</strong>?');
-
- // Erase the caption in the CKEditor Widget, verify the <figcaption> still
- // exists and contains placeholder text, then type something else.
- $this->setCaption('');
- $this->getSession()->switchToIFrame('ckeditor');
- $assert_session->elementContains('css', 'figcaption', '');
- $assert_session->elementAttributeContains('css', 'figcaption', 'data-placeholder', 'Enter caption here');
- $this->setCaption('Fin.');
- $this->getSession()->switchToIFrame('ckeditor');
- $assert_session->elementContains('css', 'figcaption', 'Fin.');
- }
-
- /**
- * Tests the EditorMediaDialog's form elements' #access logic.
- */
- public function testDialogAccess() {
- $page = $this->getSession()->getPage();
- $assert_session = $this->assertSession();
- $this->drupalGet($this->host->toUrl('edit-form'));
- $this->waitForEditor();
- $this->assignNameToCkeditorIframe();
- $this->getSession()->switchToIFrame('ckeditor');
-
- // Enable `filter_html` without "alt", "data-align" or "data-caption"
- // attributes added to the drupal-media tag.
- $allowed_html = "<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type='1 A I'> <li> <dl> <dt> <dd> <h2 id='jump-*'> <h3 id> <h4 id> <h5 id> <h6 id> <drupal-media data-entity-type data-entity-uuid data-view-mode>";
- $filter_format = FilterFormat::load('test_format');
- $filter_format->setFilterConfig('filter_html', [
- 'status' => TRUE,
- 'settings' => [
- 'allowed_html' => $allowed_html,
- ],
- ])->save();
-
- // Test the validation of attributes in the dialog. If the alt,
- // data-caption, and data-align attributes are not set on the drupal-media
- // tag, the respective fields shouldn't display in the dialog.
- $this->drupalGet($this->host->toUrl('edit-form'));
- $this->waitForEditor();
- $this->assignNameToCkeditorIframe();
- $this->getSession()->switchToIFrame('ckeditor');
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media', 2000));
- // Test `aria-label` attribute appears on the widget wrapper.
- $assert_session->elementExists('css', '.cke_widget_drupalmedia[aria-label="Screaming hairy armadillo"]');
- $page->pressButton('Edit media');
- $this->waitForMetadataDialog();
- $assert_session->fieldNotExists('attributes[alt]');
- $assert_session->fieldNotExists('attributes[align]');
- $assert_session->fieldNotExists('hasCaption');
- $assert_session->pageTextContains('There is nothing to configure for this media.');
- // The edit link for the format shouldn't appear unless the user has
- // permission to edit the text format.
- $assert_session->pageTextNotContains('Edit the text format Test format to modify the attributes that can be overridden.');
- $page->pressButton('Close');
- $this->getSession()->switchToIFrame('ckeditor');
-
- // Now test the same thing with a user who has access to edit text formats.
- // An extra message containing a link to edit the text format should
- // appear.
- Role::load(RoleInterface::AUTHENTICATED_ID)
- ->grantPermission('administer filters')
- ->save();
- $this->drupalGet($this->host->toUrl('edit-form'));
- $this->waitForEditor();
- $this->assignNameToCkeditorIframe();
- $this->getSession()->switchToIFrame('ckeditor');
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media', 2000));
- $page->pressButton('Edit media');
- $this->waitForMetadataDialog();
- $assert_session->fieldNotExists('attributes[alt]');
- $assert_session->fieldNotExists('attributes[align]');
- $assert_session->fieldNotExists('hasCaption');
- $assert_session->pageTextContains('There is nothing to configure for this media. Edit the text format Test format to modify the attributes that can be overridden.');
- $assert_session->linkExists('Edit the text format Test format');
- $page->pressButton('Close');
- $this->getSession()->switchToIFrame('ckeditor');
-
- // Now test that adding the attributes to the allowed HTML will allow
- // the fields to display in the dialog.
- $allowed_html = str_replace('<drupal-media data-entity-type data-entity-uuid data-view-mode>', '<drupal-media alt data-align data-caption data-entity-type data-entity-uuid data-view-mode>', $allowed_html);
- $filter_format->setFilterConfig('filter_html', [
- 'status' => TRUE,
- 'settings' => [
- 'allowed_html' => $allowed_html,
- ],
- ])->save();
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media', 2000));
- $page->pressButton('Edit media');
- $this->waitForMetadataDialog();
- $assert_session->fieldExists('attributes[alt]');
- $assert_session->fieldExists('attributes[data-align]');
- $assert_session->fieldExists('hasCaption');
- $page->pressButton('Close');
- $this->getSession()->switchToIFrame('ckeditor');
-
- // Test that setting the media image field to not display alt field also
- // disables it in the dialog.
- FieldConfig::loadByName('media', 'image', 'field_media_image')
- ->setSetting('alt_field', FALSE)
- ->save();
- // @todo This manual cache clearing should not be necessary, fix in
- // https://www.drupal.org/project/drupal/issues/3076544
- $this->container
- ->get('cache.discovery')
- ->delete('entity_bundle_field_definitions:media:image:en');
- // Wait for preview.
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media', 2000));
- $page->pressButton('Edit media');
- $this->waitForMetadataDialog();
- $assert_session->fieldNotExists('attributes[alt]');
- $assert_session->fieldExists('attributes[data-align]');
- $assert_session->fieldExists('hasCaption');
- $page->pressButton('Close');
- $this->getSession()->switchToIFrame('ckeditor');
-
- // Test that enabling the alt field on the media image field restores
- // the field in the dialog.
- FieldConfig::loadByName('media', 'image', 'field_media_image')
- ->setSetting('alt_field', TRUE)
- ->save();
- // @todo This manual cache clearing should not be necessary, fix in
- // https://www.drupal.org/project/drupal/issues/3076544
- $this->container
- ->get('cache.discovery')
- ->delete('entity_bundle_field_definitions:media:image:en');
- // Wait for preview.
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media', 2000));
- $page->pressButton('Edit media');
- $this->waitForMetadataDialog();
- $assert_session->fieldExists('attributes[alt]');
- $assert_session->fieldExists('attributes[data-align]');
- $assert_session->fieldExists('hasCaption');
- $page->pressButton('Close');
- $this->getSession()->switchToIFrame('ckeditor');
-
- // Test that disabling `filter_caption` and `filter_align` disables the
- // respective fields in the dialog.
- $filter_format
- ->setFilterConfig('filter_caption', [
- 'status' => FALSE,
- ])->setFilterConfig('filter_align', [
- 'status' => FALSE,
- ])->save();
- // Wait for preview.
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media', 2000));
- $page->pressButton('Edit media');
- $this->waitForMetadataDialog();
- $assert_session->fieldNotExists('attributes[data-align]');
- $assert_session->fieldNotExists('hasCaption');
- // The alt field should be unaffected.
- $assert_session->fieldExists('attributes[alt]');
- $page->pressButton('Close');
- $this->getSession()->switchToIFrame('ckeditor');
-
- // Test that enabling the two filters restores the fields in the dialog.
- $filter_format
- ->setFilterConfig('filter_caption', [
- 'status' => TRUE,
- ])->setFilterConfig('filter_align', [
- 'status' => TRUE,
- ])->save();
- // Wait for preview.
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media', 2000));
- $page->pressButton('Edit media');
- $this->waitForMetadataDialog();
- $assert_session->fieldExists('attributes[data-align]');
- $assert_session->fieldExists('hasCaption');
- $assert_session->pageTextNotContains('There is nothing to configure for this media. Edit the text format Test format to modify the attributes that can be overridden.');
- // The alt field should be unaffected.
- $assert_session->fieldExists('attributes[alt]');
- }
-
- /**
- * Tests the EditorMediaDialog can set the alt attribute.
- */
- public function testAlt() {
- $page = $this->getSession()->getPage();
- $assert_session = $this->assertSession();
- $this->drupalGet($this->host->toUrl('edit-form'));
- $this->waitForEditor();
- $this->assignNameToCkeditorIframe();
- $this->getSession()->switchToIFrame('ckeditor');
- // Wait for the media preview to load.
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media img'));
- // Test that by default no alt attribute is present on the drupal-media
- // element.
- $this->pressEditorButton('source');
- $this->assertSourceAttributeSame('alt', NULL);
- $this->leaveSourceMode();
- // Test that the preview shows the alt value from the media field's
- // alt text.
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media img[alt*="default alt"]'));
- $this->openMetadataDialogWithKeyPress(static::RETURN_KEY);
- // Assert that the placeholder is set to the value of the media field's
- // alt text.
- $assert_session->elementAttributeContains('named', ['field', 'attributes[alt]'], 'placeholder', 'default alt');
-
- // Fill in the alt field, submit and return to CKEditor.
- // cSpell:disable-next-line
- $who_is_zartan = 'Zartan is the leader of the Dreadnoks.';
- $page->fillField('attributes[alt]', $who_is_zartan);
- $this->submitDialog();
- $this->getSession()->switchToIFrame('ckeditor');
-
- // Assert that the img within the media embed within the CKEditor contains
- // the overridden alt text set in the dialog.
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media img[alt*="' . $who_is_zartan . '"]'));
- // Test `aria-label` attribute appears on the widget wrapper.
- $assert_session->elementExists('css', '.cke_widget_drupalmedia[aria-label="Screaming hairy armadillo"]');
-
- // Test that the downcast drupal-media element now has the alt attribute
- // entered in the dialog.
- $this->pressEditorButton('source');
- $this->assertSourceAttributeSame('alt', $who_is_zartan);
-
- // The alt field should now display the override instead of the default.
- $this->leaveSourceMode();
- $this->openMetadataDialog();
- $assert_session->fieldValueEquals('attributes[alt]', $who_is_zartan);
-
- // Test the process again with a different alt text to make sure it works
- // the second time around.
- $cobra_commander_bio = 'The supreme leader of the terrorist organization Cobra';
- // Set the alt field to the new alt text.
- $page->fillField('attributes[alt]', $cobra_commander_bio);
- $this->submitDialog();
- $this->getSession()->switchToIFrame('ckeditor');
- // Assert that the img within the media embed preview
- // within the CKEditor contains the overridden alt text set in the dialog.
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media img[alt*="' . $cobra_commander_bio . '"]'));
-
- // Test that the downcast drupal-media element now has the alt attribute
- // entered in the dialog.
- $this->pressEditorButton('source');
- $this->assertSourceAttributeSame('alt', $cobra_commander_bio);
-
- // The default value of the alt field should now display the override
- // instead of the value on the media image field.
- $this->leaveSourceMode();
- $this->openMetadataDialogWithKeyPress(static::RETURN_KEY);
- $assert_session->fieldValueEquals('attributes[alt]', $cobra_commander_bio);
-
- // Test that setting alt value to two double quotes will signal to the
- // MediaEmbed filter to unset the attribute on the media image field. We
- // intentionally add a space after the two double quotes to test the string
- // is trimmed to two quotes.
- $page->fillField('attributes[alt]', '"" ');
- $this->submitDialog();
- $this->getSession()->switchToIFrame('ckeditor');
- // Verify that the two double quote empty alt indicator ('""') set in
- // the dialog has successfully resulted in a media image field with the
- // alt attribute present but without a value.
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media img[alt=""]'));
-
- // Test that the downcast drupal-media element's alt attribute now has the
- // empty string indicator.
- $this->pressEditorButton('source');
- $this->assertSourceAttributeSame('alt', '""');
-
- // Test that setting alt to back to an empty string within the dialog will
- // restore the default alt value saved in to the media image field of the
- // media item.
- $this->leaveSourceMode();
- $this->openMetadataDialog();
- $page->fillField('attributes[alt]', '');
- $this->submitDialog();
- $this->getSession()->switchToIFrame('ckeditor');
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media img[alt*="default alt"]'));
-
- // Test that the downcast drupal-media element no longer has an alt
- // attribute.
- $this->pressEditorButton('source');
- $this->assertSourceAttributeSame('alt', NULL);
- }
-
- /**
- * Tests that dialog loads appropriate translation's alt text.
- */
- public function testTranslationAlt() {
- \Drupal::service('module_installer')->install(['language', 'content_translation']);
- $this->resetAll();
- ConfigurableLanguage::create(['id' => 'fr'])->save();
- ContentLanguageSettings::loadByEntityTypeBundle('media', 'image')
- ->setDefaultLangcode('en')
- ->setLanguageAlterable(TRUE)
- ->save();
- $media = Media::create([
- 'bundle' => 'image',
- 'name' => 'Screaming hairy armadillo',
- 'field_media_image' => [
- [
- 'target_id' => 1,
- 'alt' => 'default alt',
- 'title' => 'default title',
- ],
- ],
- ]);
- $media->save();
- $media_fr = $media->addTranslation('fr');
- $media_fr->name = "Tatou poilu hurlant";
- $media_fr->field_media_image->setValue([
- [
- 'target_id' => '1',
- 'alt' => "texte alternatif par défaut",
- 'title' => "titre alternatif par défaut",
- ],
- ]);
- $media_fr->save();
-
- ContentLanguageSettings::loadByEntityTypeBundle('node', 'blog')
- ->setDefaultLangcode('en')
- ->setLanguageAlterable(TRUE)
- ->save();
-
- $host = $this->createNode([
- 'type' => 'blog',
- 'title' => 'Animals with strange names',
- 'body' => [
- 'value' => '<drupal-media data-caption="baz" data-entity-type="media" data-entity-uuid="' . $media->uuid() . '"></drupal-media>',
- 'format' => 'test_format',
- ],
- ]);
- $host->save();
-
- $translation = $host->addTranslation('fr');
- // cSpell:disable-next-line
- $translation->title = 'Animaux avec des noms étranges';
- $translation->body->value = $host->body->value;
- $translation->body->format = $host->body->format;
- $translation->save();
-
- Role::load(RoleInterface::AUTHENTICATED_ID)
- ->grantPermission('translate any entity')
- ->save();
-
- $page = $this->getSession()->getPage();
- $assert_session = $this->assertSession();
- $this->drupalGet('/fr/node/' . $host->id() . '/edit');
- $this->waitForEditor();
- $this->assignNameToCkeditorIframe();
- $this->getSession()->switchToIFrame('ckeditor');
-
- // Test that the default alt attribute displays without an override.
- // cSpell:disable-next-line
- $this->assertNotEmpty($assert_session->waitForElementVisible('xpath', '//img[contains(@alt, "texte alternatif par défaut")]'));
- // Test `aria-label` attribute appears on the widget wrapper.
- // cSpell:disable-next-line
- $assert_session->elementExists('css', '.cke_widget_drupalmedia[aria-label="Tatou poilu hurlant"]');
- $page->pressButton('Edit media');
- $this->waitForMetadataDialog();
- // Assert that the placeholder is set to the value of the media field's
- // alt text.
- // cSpell:disable-next-line
- $assert_session->elementAttributeContains('named', ['field', 'attributes[alt]'], 'placeholder', 'texte alternatif par défaut');
-
- // Fill in the alt field in the dialog.
- // cSpell:disable-next-line
- $qui_est_zartan = 'Zartan est le chef des Dreadnoks.';
- $page->fillField('attributes[alt]', $qui_est_zartan);
- $this->submitDialog();
- $this->getSession()->switchToIFrame('ckeditor');
-
- // Assert that the img within the media embed within CKEditor contains
- // the overridden alt text set in the dialog.
- $this->assertNotEmpty($assert_session->waitForElementVisible('xpath', '//img[contains(@alt, "' . $qui_est_zartan . '")]'));
- $this->getSession()->switchToIFrame();
- $page->pressButton('Save');
- $assert_session->elementExists('xpath', '//img[contains(@alt, "' . $qui_est_zartan . '")]');
- }
-
- /**
- * Tests linkability of the CKEditor widget.
- *
- * @dataProvider linkabilityProvider
- */
- public function testLinkability($drupalimage_is_enabled) {
- if (!$drupalimage_is_enabled) {
- // Remove the `drupalimage` plugin's `DrupalImage` button.
- $editor = Editor::load('test_format');
- $settings = $editor->getSettings();
- $rows = $settings['toolbar']['rows'];
- foreach ($rows as $row_key => $row) {
- foreach ($row as $group_key => $group) {
- foreach ($group['items'] as $item_key => $item) {
- if ($item === 'DrupalImage') {
- unset($settings['toolbar']['rows'][$row_key][$group_key]['items'][$item_key]);
- }
- }
- }
- }
- $editor->setSettings($settings);
- $editor->save();
- }
-
- $this->host->body->value .= '<p>The pirate is irate.</p><p>';
- if ($drupalimage_is_enabled) {
- // Add an image with a link wrapped around it.
- $uri = $this->media->field_media_image->entity->getFileUri();
- /** @var \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator */
- $file_url_generator = \Drupal::service('file_url_generator');
- $src = $file_url_generator->generateString($uri);
- $this->host->body->value .= '<a href="http://www.drupal.org/association"><img alt="drupalimage test image" data-entity-type="" data-entity-uuid="" src="' . $src . '" /></a></p>';
- }
- $this->host->save();
-
- $this->drupalGet($this->host->toUrl('edit-form'));
- $this->waitForEditor();
- $this->assignNameToCkeditorIframe();
- $this->getSession()->switchToIFrame('ckeditor');
- $assert_session = $this->assertSession();
-
- // Select the CKEditor Widget.
- $drupalmedia = $assert_session->waitForElementVisible('css', 'drupal-media');
- $this->assertNotEmpty($drupalmedia);
- $drupalmedia->click();
-
- // While the CKEditor Widget is selected, assert the context menu does not
- // contain link-related context menu items.
- $this->openContextMenu();
- $this->assignNameToCkeditorPanelIframe();
- $this->getSession()->switchToIFrame('panel');
- $this->assertContextMenuItemNotExists('Edit Link');
- $this->assertContextMenuItemNotExists('Unlink');
- $this->closeContextMenu();
-
- // While the CKEditor Widget is selected, click the "link" button.
- $this->pressEditorButton('drupallink');
- $assert_session->waitForId('drupal-modal');
-
- // Enter a link in the link dialog and save.
- $field = $assert_session->waitForElementVisible('xpath', '//input[@name="attributes[href]"]');
- $this->assertNotEmpty($field);
- $field->setValue('https://www.drupal.org');
- $assert_session->elementExists('css', 'button.form-submit')->press();
- $this->getSession()->switchToIFrame('ckeditor');
- $link = $assert_session->waitForElementVisible('css', 'a[href="https://www.drupal.org"]');
- $this->assertNotEmpty($link);
-
- // Select the CKEditor Widget again and assert the context menu now does
- // contain link-related context menu items.
- $drupalmedia = $assert_session->waitForElementVisible('css', 'drupal-media');
- $this->assertNotEmpty($drupalmedia);
- $drupalmedia->click();
- $this->openContextMenu();
- $this->getSession()->switchToIFrame('panel');
- $this->assertContextMenuItemExists('Edit Link');
- $this->assertContextMenuItemExists('Unlink');
- $this->closeContextMenu();
-
- // Save the entity.
- $this->getSession()->switchToIFrame();
- $assert_session->buttonExists('Save')->press();
-
- // Verify the saved entity when viewed also contains the linked media.
- $assert_session->elementExists('css', 'figure > a[href="https://www.drupal.org"] > .media--type-image > .field--type-image > img[src*="image-test.png"]');
-
- // Test that `drupallink` also still works independently: inserting a link
- // is possible.
- $this->drupalGet($this->host->toUrl('edit-form'));
- $this->waitForEditor();
- $this->pressEditorButton('drupallink');
- $assert_session->waitForId('drupal-modal');
- $field = $assert_session->waitForElementVisible('xpath', '//input[@name="attributes[href]"]');
- $this->assertNotEmpty($field);
- $field->setValue('https://wikipedia.org');
- $assert_session->elementExists('css', 'button.form-submit')->press();
- $this->assignNameToCkeditorIframe();
- $this->getSession()->switchToIFrame('ckeditor');
- $link = $assert_session->waitForElementVisible('css', 'body > a[href="https://wikipedia.org"]');
- $this->assertNotEmpty($link);
- $assert_session->elementExists('css', 'body > .cke_widget_drupalmedia > drupal-media > figure > a[href="https://www.drupal.org"]');
-
- // Select the CKEditor Widget again and assert the `drupalunlink` button is
- // enabled. Also assert the context menu again contains link-related context
- // menu items.
- $drupalmedia = $assert_session->waitForElementVisible('css', 'drupal-media');
- $this->assertNotEmpty($drupalmedia);
- $drupalmedia->click();
- $this->openContextMenu();
- $this->getSession()->switchToIFrame();
- $this->assertEditorButtonEnabled('drupalunlink');
- $this->assignNameToCkeditorPanelIframe();
- $this->getSession()->switchToIFrame('panel');
- $this->assertContextMenuItemExists('Edit Link');
- $this->assertContextMenuItemExists('Unlink');
-
- // Test that moving focus to another element causes the `drupalunlink`
- // button to become disabled and causes link-related context menu items to
- // disappear.
- $this->getSession()->switchToIFrame();
- $this->getSession()->switchToIFrame('ckeditor');
- $p = $assert_session->waitForElementVisible('xpath', "//p[contains(text(), 'The pirate is irate')]");
- $this->assertNotEmpty($p);
- $p->click();
- $this->assertEditorButtonDisabled('drupalunlink');
- $this->getSession()->switchToIFrame('panel');
- $this->assertContextMenuItemExists('Edit Link');
- $this->assertContextMenuItemExists('Unlink');
-
- // To switch from the context menu iframe ("panel") back to the CKEditor
- // iframe, we first have to reset to top frame.
- $this->getSession()->switchToIFrame();
- $this->getSession()->switchToIFrame('ckeditor');
-
- // Test that moving focus to the `drupalimage` CKEditor Widget enables the
- // `drupalunlink` button again, because it is a linked image.
- if ($drupalimage_is_enabled) {
- $drupalimage = $assert_session->waitForElementVisible('xpath', '//img[@alt="drupalimage test image"]');
- $this->assertNotEmpty($drupalimage);
- $drupalimage->click();
- $this->assertEditorButtonEnabled('drupalunlink');
- $this->getSession()->switchToIFrame('panel');
- $this->assertContextMenuItemExists('Edit Link');
- $this->assertContextMenuItemExists('Unlink');
- $this->getSession()->switchToIFrame();
- $this->getSession()->switchToIFrame('ckeditor');
- }
-
- // Tests the `drupalunlink` button for the `drupalmedia` CKEditor Widget.
- $drupalmedia->click();
- $this->assertEditorButtonEnabled('drupalunlink');
- $this->getSession()->switchToIFrame('panel');
- $this->assertContextMenuItemExists('Edit Link');
- $this->assertContextMenuItemExists('Unlink');
- $this->pressEditorButton('drupalunlink');
- $this->assertEditorButtonDisabled('drupalunlink');
- $this->getSession()->switchToIFrame('ckeditor');
- $assert_session->elementNotExists('css', 'figure > a[href="https://www.drupal.org"] > .media--type-image > .field--type-image > img[src*="image-test.png"]');
- $assert_session->elementExists('css', 'figure .media--type-image > .field--type-image > img[src*="image-test.png"]');
- if ($drupalimage_is_enabled) {
- // Tests the `drupalunlink` button for the `drupalimage` CKEditor Widget.
- $drupalimage->click();
- $this->assertEditorButtonEnabled('drupalunlink');
- $this->pressEditorButton('drupalunlink');
- $this->assertEditorButtonDisabled('drupalunlink');
- $this->getSession()->switchToIFrame('ckeditor');
- $assert_session->elementNotExists('css', 'p > a[href="https://www.drupal.org/association"] > img[src*="image-test.png"]');
- $assert_session->elementExists('css', 'p > img[src*="image-test.png"]');
- }
- }
-
- /**
- * Data Provider for ::testLinkability.
- */
- public function linkabilityProvider() {
- return [
- 'linkability when `drupalimage` is enabled' => [
- TRUE,
- ],
- 'linkability when `drupalimage` is disabled' => [
- FALSE,
- ],
- ];
- }
-
- /**
- * Tests preview route access.
- *
- * @param bool $media_embed_enabled
- * Whether to test with media_embed filter enabled on the text format.
- * @param bool $can_use_format
- * Whether the logged in user is allowed to use the text format.
- *
- * @dataProvider previewAccessProvider
- */
- public function testEmbedPreviewAccess($media_embed_enabled, $can_use_format) {
- // Reconfigure the host entity's text format to suit our needs.
- /** @var \Drupal\filter\FilterFormatInterface $format */
- $format = FilterFormat::load($this->host->body->format);
- $format->set('filters', [
- 'filter_align' => ['status' => TRUE],
- 'filter_caption' => ['status' => TRUE],
- 'media_embed' => ['status' => $media_embed_enabled],
- ]);
- $format->save();
-
- $permissions = [
- 'bypass node access',
- ];
- if ($can_use_format) {
- $permissions[] = $format->getPermissionName();
- }
- $this->drupalLogin($this->drupalCreateUser($permissions));
- $this->drupalGet($this->host->toUrl('edit-form'));
-
- $assert_session = $this->assertSession();
- if ($can_use_format) {
- $this->waitForEditor();
- $this->assignNameToCkeditorIframe();
- $this->getSession()->switchToIFrame('ckeditor');
- if ($media_embed_enabled) {
- // The preview rendering, which in this test will use Starterkit's
- // media.html.twig template, will fail without the CSRF token/header.
- // @see ::testEmbeddedMediaPreviewWithCsrfToken()
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'article.media'));
- }
- else {
- // If the filter isn't enabled, there won't be an error, but the
- // preview shouldn't be rendered.
- $assert_session->assertWaitOnAjaxRequest();
- $assert_session->elementNotExists('css', 'article.media');
- }
- }
- else {
- $assert_session->pageTextContains('This field has been disabled because you do not have sufficient permissions to edit it.');
- }
- }
-
- /**
- * Data provider for ::testEmbedPreviewAccess.
- */
- public function previewAccessProvider() {
- return [
- 'media_embed filter enabled' => [
- TRUE,
- TRUE,
- ],
- 'media_embed filter disabled' => [
- FALSE,
- TRUE,
- ],
- 'media_embed filter enabled, user not allowed to use text format' => [
- TRUE,
- FALSE,
- ],
- ];
- }
-
- /**
- * Tests alignment integration.
- *
- * Tests that alignment is reflected onto the CKEditor Widget wrapper, that
- * the EditorMediaDialog allows altering the alignment and that the changes
- * are reflected on the widget and downcast drupal-media tag.
- */
- public function testAlignment() {
- $assert_session = $this->assertSession();
-
- $this->drupalGet($this->host->toUrl('edit-form'));
- $this->waitForEditor();
- $this->assignNameToCkeditorIframe();
- $this->getSession()->switchToIFrame('ckeditor');
- // Wait for preview to load.
- $this->assertNotEmpty($assert_session->waitForElement('css', 'drupal-media img'));
- // Assert the drupal-media element starts without a data-align attribute.
- $drupal_media = $assert_session->elementExists('css', 'drupal-media');
- $this->assertFalse($drupal_media->hasAttribute('data-align'));
-
- // Assert that setting the data-align property in the dialog adds the
- // `align-right', `align-left` or `align-center' class on the widget,
- // caption figure and drupal-media element.
- $alignments = [
- 'right',
- 'left',
- 'center',
- ];
- foreach ($alignments as $alignment) {
- $this->fillFieldInMetadataDialogAndSubmit('attributes[data-align]', $alignment);
- // Wait for preview to load.
- $this->assertNotEmpty($assert_session->waitForElement('css', 'drupal-media img'));
- // Now verify the result. Assert the first element within the
- // <drupal-media> element has the alignment class.
- $selector = sprintf('drupal-media[data-align="%s"] .caption-drupal-media.align-%s', $alignment, $alignment);
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', $selector, 2000));
-
- // Assert that the resultant downcast drupal-media element has the proper
- // `data-align` attribute.
- $this->pressEditorButton('source');
- $this->assertSourceAttributeSame('data-align', $alignment);
- $this->leaveSourceMode();
- }
- // Test that setting the "Align" field to "none" in the dialog will
- // remove the attribute from the drupal-media element in the CKEditor.
- $this->fillFieldInMetadataDialogAndSubmit('attributes[data-align]', 'none');
-
- // Assert the drupal-media element no longer has data-align attribute.
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media .caption-drupal-media:not(.align-center)', 2000));
- $drupal_media = $assert_session->elementExists('css', 'drupal-media');
- $this->assertFalse($drupal_media->hasAttribute('data-align'));
- // Assert that the resultant downcast <drupal-media> tag has no data-align
- // attribute.
- $this->pressEditorButton('source');
- $this->assertNotEmpty($drupal_media = $this->getDrupalMediaFromSource());
- $this->assertFalse($drupal_media->hasAttribute('data-align'));
- }
-
- /**
- * Tests the EditorMediaDialog can set the data-view-mode attribute.
- */
- public function testViewMode() {
- EntityViewMode::create([
- 'id' => 'media.view_mode_1',
- 'targetEntityType' => 'media',
- 'status' => TRUE,
- 'enabled' => TRUE,
- 'label' => 'View Mode 1',
- ])->save();
- EntityViewMode::create([
- 'id' => 'media.22222',
- 'targetEntityType' => 'media',
- 'status' => TRUE,
- 'enabled' => TRUE,
- 'label' => 'View Mode 2 has Numeric ID',
- ])->save();
- EntityViewMode::create([
- 'id' => 'media.view_mode_3',
- 'targetEntityType' => 'media',
- 'status' => TRUE,
- 'enabled' => TRUE,
- 'label' => 'View Mode 3',
- ])->save();
-
- // Only enable view mode 1 & 2 for Image.
- EntityViewDisplay::create([
- 'id' => 'media.image.view_mode_1',
- 'targetEntityType' => 'media',
- 'status' => TRUE,
- 'bundle' => 'image',
- 'mode' => 'view_mode_1',
- ])->save();
- EntityViewDisplay::create([
- 'id' => 'media.image.22222',
- 'targetEntityType' => 'media',
- 'status' => TRUE,
- 'bundle' => 'image',
- 'mode' => '22222',
- ])->save();
-
- $filter_format = FilterFormat::load('test_format');
- $filter_format->setFilterConfig('media_embed', [
- 'status' => TRUE,
- 'settings' => [
- 'default_view_mode' => 'view_mode_1',
- 'allowed_media_types' => [],
- 'allowed_view_modes' => [
- 'view_mode_1' => 'view_mode_1',
- '22222' => '22222',
- 'view_mode_3' => 'view_mode_3',
- ],
- ],
- ])->save();
-
- // Test that view mode dependencies are returned from the MediaEmbed
- // filter's ::getDependencies() method.
- $expected_config_dependencies = [
- 'core.entity_view_mode.media.view_mode_1',
- 'core.entity_view_mode.media.22222',
- 'core.entity_view_mode.media.view_mode_3',
- ];
- $dependencies = $filter_format->getDependencies();
- $this->assertArrayHasKey('config', $dependencies);
- $this->assertEqualsCanonicalizing($expected_config_dependencies, $dependencies['config']);
-
- // Test MediaEmbed's allowed_view_modes option setting enables a view mode
- // selection field.
- $page = $this->getSession()->getPage();
- $assert_session = $this->assertSession();
- $this->drupalGet($this->host->toUrl('edit-form'));
- $this->waitForEditor();
- $this->assignNameToCkeditorIframe();
- $this->getSession()->switchToIFrame('ckeditor');
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media'));
- $assert_session->elementExists('css', '.cke_widget_drupalmedia[aria-label="Screaming hairy armadillo"]');
- $page->pressButton('Edit media');
- $this->waitForMetadataDialog();
- $assert_session->optionExists('attributes[data-view-mode]', 'view_mode_1');
- $assert_session->optionExists('attributes[data-view-mode]', '22222');
- $assert_session->optionNotExists('attributes[data-view-mode]', 'view_mode_3');
- $assert_session->selectExists('attributes[data-view-mode]')->selectOption('22222');
- $this->submitDialog();
- $this->getSession()->switchToIFrame('ckeditor');
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'article.media--view-mode-_2222'));
- // Test that the downcast drupal-media element contains the
- // `data-view-mode` attribute set in the dialog.
- $this->pressEditorButton('source');
- $this->assertNotEmpty($drupal_media = $this->getDrupalMediaFromSource());
- $this->assertSame('22222', $drupal_media->getAttribute('data-view-mode'));
-
- // Press the source button again to leave source mode.
- $this->pressEditorButton('source');
- // Having entered source mode means we need to reassign an ID to the
- // CKEditor iframe.
- $this->assignNameToCkeditorIframe();
- $this->getSession()->switchToIFrame('ckeditor');
-
- // Test that setting the allowed_view_modes option to only one option hides
- // the field (it requires more than one option).
- $filter_format->setFilterConfig('media_embed', [
- 'status' => TRUE,
- 'settings' => [
- 'default_view_mode' => 'view_mode_1',
- 'allowed_media_types' => [],
- 'allowed_view_modes' => [
- 'view_mode_1' => 'view_mode_1',
- ],
- ],
- ])->save();
-
- // Test that the dependencies change when the allowed_view_modes change.
- $dependencies = $filter_format->getDependencies();
- $this->assertArrayHasKey('config', $dependencies);
- $this->assertSame(['core.entity_view_mode.media.view_mode_1'], $dependencies['config']);
-
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media'));
- $page->pressButton('Edit media');
- $this->waitForMetadataDialog();
- $assert_session->fieldNotExists('attributes[data-view-mode]');
- $page->pressButton('Close');
- $this->getSession()->switchToIFrame('ckeditor');
-
- // Test that setting allowed_view_modes back to two items restores the
- // field.
- $filter_format->setFilterConfig('media_embed', [
- 'status' => TRUE,
- 'settings' => [
- 'default_view_mode' => 'view_mode_1',
- 'allowed_media_types' => [],
- 'allowed_view_modes' => [
- 'view_mode_1' => 'view_mode_1',
- '22222' => '22222',
- ],
- ],
- ])->save();
-
- // Test that the dependencies change when the allowed_view_modes change.
- $expected_config_dependencies = [
- 'core.entity_view_mode.media.view_mode_1',
- 'core.entity_view_mode.media.22222',
- ];
- $dependencies = $filter_format->getDependencies();
- $this->assertArrayHasKey('config', $dependencies);
- $this->assertEqualsCanonicalizing($expected_config_dependencies, $dependencies['config']);
-
- // Test that setting the view mode back to the default removes the
- // `data-view-mode` attribute.
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media'));
- $page->pressButton('Edit media');
- $this->waitForMetadataDialog();
- $assert_session->optionExists('attributes[data-view-mode]', 'view_mode_1');
- $assert_session->optionExists('attributes[data-view-mode]', '22222');
- $assert_session->selectExists('attributes[data-view-mode]')->selectOption('view_mode_1');
- $this->submitDialog();
- $this->getSession()->switchToIFrame('ckeditor');
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'article.media--view-mode-view-mode-1'));
- $this->pressEditorButton('source');
- $this->assertNotEmpty($drupal_media = $this->getDrupalMediaFromSource());
- $this->assertFalse($drupal_media->hasAttribute('data-view-mode'));
-
- // Test that changing the view mode with an empty editable caption
- // preserves the empty editable caption when the preview reloads.
- $original_value = $this->host->body->value;
- $this->host->body->value = str_replace('data-caption="baz"', '', $original_value);
- $this->host->save();
- $this->drupalGet($this->host->toUrl('edit-form'));
- $this->waitForEditor();
- $this->assignNameToCkeditorIframe();
- $this->getSession()->switchToIFrame('ckeditor');
- // Wait for preview to load with default view mode.
- $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'article.media--view-mode-view-mode-1'));
- }
-
- /**
- * Waits for the form that allows editing metadata.
- *
- * @see \Drupal\media\Form\EditorMediaDialog
- */
- protected function waitForMetadataDialog() {
- $page = $this->getSession()->getPage();
- $this->getSession()->switchToIFrame();
- // Wait for the dialog to open.
- $result = $page->waitFor(10, function ($page) {
- $metadata_editor = $page->find('css', 'form.editor-media-dialog');
- return !empty($metadata_editor);
- });
- $this->assertTrue($result);
- }
-
- /**
- * Fills in a field in the metadata dialog for an embedded media item.
- *
- * This method assumes that the calling code has already switched into the
- * CKEditor iframe.
- *
- * @param string $locator
- * The field ID, name, or label.
- * @param string $value
- * The value to set on the field.
- */
- protected function fillFieldInMetadataDialogAndSubmit($locator, $value) {
- // Wait for the drupal-media which holds the "Edit media" button which
- // opens the dialog.
- $this->openMetadataDialog();
- $this->getSession()->getPage()->fillField($locator, $value);
- $this->submitDialog();
- // Since ::waitforMetadataDialog() switches back to the main iframe, we'll
- // need to switch back.
- $this->getSession()->switchToIFrame('ckeditor');
- }
-
- /**
- * Clicks the `Edit media` button and waits for the metadata dialog.
- *
- * This method assumes that the calling code has already switched into the
- * CKEditor iframe.
- */
- protected function openMetadataDialog() {
- $this->assertNotEmpty($embedded_media = $this->assertSession()->waitForElementVisible('css', 'drupal-media'));
- $embedded_media->pressButton('Edit media');
- $this->waitForMetadataDialog();
- }
-
- /**
- * Focuses on `Edit media` button and presses the given key.
- *
- * @param int $char
- * The character code to press.
- *
- * This method assumes that the calling code has already switched into the
- * CKEditor iframe.
- */
- protected function openMetadataDialogWithKeyPress($char) {
- $this->assertNotEmpty($button = $this->assertSession()->waitForButton('Edit media'));
- $button->keyDown($char);
- $this->waitForMetadataDialog();
- }
-
- /**
- * Leaves source mode and returns to the CKEditor iframe.
- */
- protected function leaveSourceMode() {
- // Press the source button again to leave source mode.
- $this->pressEditorButton('source');
- // Having entered source mode means we need to reassign an ID to the
- // CKEditor iframe.
- $this->assignNameToCkeditorIframe();
- $this->getSession()->switchToIFrame('ckeditor');
- }
-
- /**
- * Verifies value of an attribute on the downcast <drupal-media> element.
- *
- * Assumes CKEditor is in source mode.
- *
- * @param string $attribute
- * The attribute to check.
- * @param string|null $value
- * Either a string value or if NULL, asserts that <drupal-media> element
- * doesn't have the attribute.
- *
- * @internal
- */
- protected function assertSourceAttributeSame(string $attribute, ?string $value): void {
- $this->assertNotEmpty($drupal_media = $this->getDrupalMediaFromSource());
- if ($value === NULL) {
- $this->assertFalse($drupal_media->hasAttribute($attribute));
- }
- else {
- $this->assertSame($value, $drupal_media->getAttribute($attribute));
- }
- }
-
- /**
- * Closes and submits the metadata dialog.
- */
- protected function submitDialog() {
- $this->assertNotEmpty($dialog_buttons = $this->assertSession()->elementExists('css', 'div.ui-dialog-buttonpane'));
- $dialog_buttons->pressButton('Save');
- }
-
- /**
- * Closes the metadata dialog.
- */
- protected function closeDialog() {
- $page = $this->getSession()->getPage();
- $page->pressButton('Close');
- $result = $page->waitFor(10, function ($page) {
- $metadata_editor = $page->find('css', 'form.editor-media-dialog');
- return empty($metadata_editor);
- });
- $this->assertTrue($result);
- }
-
- /**
- * Gets the transfer size of the last preview request.
- *
- * @return int
- * The size of the bytes transferred.
- */
- protected function getLastPreviewRequestTransferSize() {
- $this->getSession()->switchToIFrame();
- $javascript = <<<JS
-(function(){
- return window.performance
- .getEntries()
- .filter(function (entry) {
- return entry.initiatorType == 'xmlhttprequest' && entry.name.indexOf('/media/test_format/preview') !== -1;
- })
- .pop()
- .transferSize;
-})()
-JS;
- return $this->getSession()->evaluateScript($javascript);
- }
-
- /**
- * Sets the text of the editable caption to the given text.
- *
- * @param string $text
- * The text to set in the caption.
- */
- protected function setCaption($text) {
- $this->getSession()->switchToIFrame();
- $select_and_edit_caption = "var editor = CKEDITOR.instances['edit-body-0-value'];
- var figcaption = editor.widgets.getByElement(editor.editable().findOne('figcaption'));
- figcaption.editables.caption.setData('" . $text . "')";
- $this->getSession()->executeScript($select_and_edit_caption);
- }
-
- /**
- * Assigns a name to the CKEditor context menu iframe.
- *
- * Note that this iframe doesn't appear until context menu appears.
- *
- * @see \Behat\Mink\Session::switchToIFrame()
- */
- protected function assignNameToCkeditorPanelIframe() {
- $javascript = <<<JS
-(function(){
- document.getElementsByClassName('cke_panel_frame')[0].id = 'panel';
-})()
-JS;
- $this->getSession()->evaluateScript($javascript);
- }
-
- /**
- * Opens the context menu for the currently selected widget.
- *
- * @param string $instance_id
- * The CKEditor instance ID.
- */
- protected function openContextMenu($instance_id = 'edit-body-0-value') {
- $this->getSession()->switchToIFrame();
- $script = <<<JS
- (function() {
- var editor = CKEDITOR.instances["$instance_id"];
- editor.contextMenu.open(editor.widgets.selected[0].element);
- }());
-JS;
- $this->getSession()->executeScript($script);
- }
-
- /**
- * Asserts that a context menu item exists by aria-label attribute.
- *
- * @param string $label
- * The `aria-label` attribute value of the context menu item.
- *
- * @internal
- */
- protected function assertContextMenuItemExists(string $label): void {
- $this->assertSession()->elementExists('xpath', '//a[@aria-label="' . $label . '"]');
- }
-
- /**
- * Asserts that a context menu item does not exist by aria-label attribute.
- *
- * @param string $label
- * The `aria-label` attribute value of the context menu item.
- *
- * @internal
- */
- protected function assertContextMenuItemNotExists(string $label): void {
- $this->assertSession()->elementNotExists('xpath', '//a[@aria-label="' . $label . '"]');
- }
-
- /**
- * Closes the open context menu.
- *
- * @param string $instance_id
- * The CKEditor instance ID.
- */
- protected function closeContextMenu($instance_id = 'edit-body-0-value') {
- $this->getSession()->switchToIFrame();
- $script = <<<JS
- (function() {
- var editor = CKEDITOR.instances["$instance_id"];
- editor.contextMenu.hide();
- }());
-JS;
- $this->getSession()->executeScript($script);
- }
-
- /**
- * Clicks a link in the editor's path links with the given title text.
- *
- * @param string $text
- * The title attribute of the link to click.
- */
- protected function clickPathLinkByTitleAttribute($text) {
- $this->getSession()->switchToIFrame();
- $selector = '//span[@id="cke_1_path"]//a[@title="' . $text . '"]';
- $this->assertSession()->elementExists('xpath', $selector)->click();
- }
-
- /**
- * Parses the <drupal-media> element from CKEditor's "source" view.
- *
- * Assumes CKEditor is in source mode.
- *
- * @return \DOMNode|null
- * The drupal-media element or NULL if it can't be found.
- */
- protected function getDrupalMediaFromSource() {
- $value = $this->assertSession()
- ->elementExists('css', 'textarea.cke_source')
- ->getValue();
- $dom = Html::load($value);
- $xpath = new \DOMXPath($dom);
- $list = $xpath->query('//drupal-media');
- return count($list) > 0 ? $list[0] : NULL;
- }
-
-}
diff --git a/core/modules/ckeditor/tests/src/Kernel/CKEditorPluginManagerTest.php b/core/modules/ckeditor/tests/src/Kernel/CKEditorPluginManagerTest.php
deleted file mode 100644
index 8de0c7038676..000000000000
--- a/core/modules/ckeditor/tests/src/Kernel/CKEditorPluginManagerTest.php
+++ /dev/null
@@ -1,165 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ckeditor\Kernel;
-
-use Drupal\editor\Entity\Editor;
-use Drupal\KernelTests\KernelTestBase;
-use Drupal\filter\Entity\FilterFormat;
-
-/**
- * Tests different ways of enabling CKEditor plugins.
- *
- * @group ckeditor
- */
-class CKEditorPluginManagerTest extends KernelTestBase {
-
- /**
- * Modules to enable.
- *
- * @var array
- */
- protected static $modules = [
- 'system',
- 'user',
- 'filter',
- 'editor',
- 'ckeditor',
- ];
-
- /**
- * The manager for "CKEditor plugin" plugins.
- *
- * @var \Drupal\Component\Plugin\PluginManagerInterface
- */
- protected $manager;
-
- protected function setUp(): void {
- parent::setUp();
-
- // Install the Filter module.
-
- // Create text format, associate CKEditor.
- $filtered_html_format = FilterFormat::create([
- 'format' => 'filtered_html',
- 'name' => 'Filtered HTML',
- 'weight' => 0,
- 'filters' => [],
- ]);
- $filtered_html_format->save();
- $editor = Editor::create([
- 'format' => 'filtered_html',
- 'editor' => 'ckeditor',
- ]);
- $editor->save();
- }
-
- /**
- * Tests the enabling of plugins.
- */
- public function testEnabledPlugins() {
- $this->manager = $this->container->get('plugin.manager.ckeditor.plugin');
- $editor = Editor::load('filtered_html');
-
- // Case 1: no CKEditor plugins.
- $definitions = array_keys($this->manager->getDefinitions());
- sort($definitions);
- $this->assertSame(['drupalimage', 'drupalimagecaption', 'drupallink', 'internal', 'language', 'stylescombo'], $definitions, 'No CKEditor plugins found besides the built-in ones.');
- $enabled_plugins = [
- 'drupalimage' => $this->getModulePath('ckeditor') . '/js/plugins/drupalimage/plugin.js',
- 'drupallink' => $this->getModulePath('ckeditor') . '/js/plugins/drupallink/plugin.js',
- ];
- $this->assertSame($enabled_plugins, $this->manager->getEnabledPluginFiles($editor), 'Only built-in plugins are enabled.');
- $this->assertSame(['internal' => NULL] + $enabled_plugins, $this->manager->getEnabledPluginFiles($editor, TRUE), 'Only the "internal" plugin is enabled.');
-
- // Enable the CKEditor Test module, which has the Llama plugin (plus four
- // variations of it, to cover all possible ways a plugin can be enabled) and
- // clear the editor manager's cache so it is picked up.
- $this->enableModules(['ckeditor_test']);
- $this->manager = $this->container->get('plugin.manager.ckeditor.plugin');
- $this->manager->clearCachedDefinitions();
-
- // Case 2: CKEditor plugins are available.
- $plugin_ids = array_keys($this->manager->getDefinitions());
- sort($plugin_ids);
- $this->assertSame(['drupalimage', 'drupalimagecaption', 'drupallink', 'internal', 'language', 'llama', 'llama_button', 'llama_contextual', 'llama_contextual_and_button', 'llama_css', 'stylescombo'], $plugin_ids, 'Additional CKEditor plugins found.');
- $this->assertSame($enabled_plugins, $this->manager->getEnabledPluginFiles($editor), 'Only the internal plugins are enabled.');
- $this->assertSame(['internal' => NULL] + $enabled_plugins, $this->manager->getEnabledPluginFiles($editor, TRUE), 'Only the "internal" plugin is enabled.');
-
- // Case 3: enable each of the newly available plugins, if possible:
- // 1. Llama: cannot be enabled, since it does not implement
- // CKEditorPluginContextualInterface nor CKEditorPluginButtonsInterface.
- // 2. LlamaContextual: enabled by adding the 'Strike' button, which is
- // part of another plugin!
- // 3. LlamaButton: automatically enabled by adding its 'Llama' button.
- // 4. LlamaContextualAndButton: enabled by either 2 or 3.
- // 5. LlamaCSS: automatically enabled by add its 'LlamaCSS' button.
- // Below, we will first enable the "Llama" button, which will cause the
- // LlamaButton and LlamaContextualAndButton plugins to be enabled. Then we
- // will remove the "Llama" button and add the "Strike" button, which will
- // cause the LlamaContextual and LlamaContextualAndButton plugins to be
- // enabled. Then we will add the "Strike" button back again, which would
- // cause LlamaButton, LlamaContextual and LlamaContextualAndButton to be
- // enabled. Finally, we will add the "LlamaCSS" button which would cause
- // all four plugins to be enabled.
- $settings = $editor->getSettings();
- $original_toolbar = $settings['toolbar'];
- $settings['toolbar']['rows'][0][0]['items'][] = 'Llama';
- $editor->setSettings($settings);
- $editor->save();
- $file = [];
- $file['b'] = $this->getModulePath('ckeditor_test') . '/js/llama_button.js';
- $file['c'] = $this->getModulePath('ckeditor_test') . '/js/llama_contextual.js';
- $file['cb'] = $this->getModulePath('ckeditor_test') . '/js/llama_contextual_and_button.js';
- $file['css'] = $this->getModulePath('ckeditor_test') . '/js/llama_css.js';
- $expected = $enabled_plugins + ['llama_button' => $file['b'], 'llama_contextual_and_button' => $file['cb']];
- $this->assertSame($expected, $this->manager->getEnabledPluginFiles($editor), 'The LlamaButton and LlamaContextualAndButton plugins are enabled.');
- $this->assertSame(['internal' => NULL] + $expected, $this->manager->getEnabledPluginFiles($editor, TRUE), 'The LlamaButton and LlamaContextualAndButton plugins are enabled.');
- $settings['toolbar'] = $original_toolbar;
- $settings['toolbar']['rows'][0][0]['items'][] = 'Strike';
- $editor->setSettings($settings);
- $editor->save();
- $expected = $enabled_plugins + ['llama_contextual' => $file['c'], 'llama_contextual_and_button' => $file['cb']];
- $this->assertSame($expected, $this->manager->getEnabledPluginFiles($editor), 'The LLamaContextual and LlamaContextualAndButton plugins are enabled.');
- $this->assertSame(['internal' => NULL] + $expected, $this->manager->getEnabledPluginFiles($editor, TRUE), 'The LlamaContextual and LlamaContextualAndButton plugins are enabled.');
- $settings['toolbar']['rows'][0][0]['items'][] = 'Llama';
- $editor->setSettings($settings);
- $editor->save();
- $expected = $enabled_plugins + ['llama_button' => $file['b'], 'llama_contextual' => $file['c'], 'llama_contextual_and_button' => $file['cb']];
- $this->assertSame($expected, $this->manager->getEnabledPluginFiles($editor), 'The LlamaButton, LlamaContextual and LlamaContextualAndButton plugins are enabled.');
- $this->assertSame(['internal' => NULL] + $expected, $this->manager->getEnabledPluginFiles($editor, TRUE), 'The LLamaButton, LlamaContextual and LlamaContextualAndButton plugins are enabled.');
- $settings['toolbar']['rows'][0][0]['items'][] = 'LlamaCSS';
- $editor->setSettings($settings);
- $editor->save();
- $expected = $enabled_plugins + ['llama_button' => $file['b'], 'llama_contextual' => $file['c'], 'llama_contextual_and_button' => $file['cb'], 'llama_css' => $file['css']];
- $this->assertSame($expected, $this->manager->getEnabledPluginFiles($editor), 'The LlamaButton, LlamaContextual, LlamaContextualAndButton and LlamaCSS plugins are enabled.');
- $this->assertSame(['internal' => NULL] + $expected, $this->manager->getEnabledPluginFiles($editor, TRUE), 'The LLamaButton, LlamaContextual, LlamaContextualAndButton and LlamaCSS plugins are enabled.');
- }
-
- /**
- * Tests the iframe instance CSS files of plugins.
- */
- public function testCssFiles() {
- $this->manager = $this->container->get('plugin.manager.ckeditor.plugin');
- $editor = Editor::load('filtered_html');
-
- // Case 1: no CKEditor iframe instance CSS file.
- $this->assertSame([], $this->manager->getCssFiles($editor), 'No iframe instance CSS file found.');
-
- // Enable the CKEditor Test module, which has the LlamaCss plugin and
- // clear the editor manager's cache so it is picked up.
- $this->enableModules(['ckeditor_test']);
- $this->manager = $this->container->get('plugin.manager.ckeditor.plugin');
- $settings = $editor->getSettings();
- // LlamaCss: automatically enabled by adding its 'LlamaCSS' button.
- $settings['toolbar']['rows'][0][0]['items'][] = 'LlamaCSS';
- $editor->setSettings($settings);
- $editor->save();
-
- // Case 2: CKEditor iframe instance CSS file.
- $expected = [
- 'llama_css' => [$this->getModulePath('ckeditor_test') . '/css/llama.css'],
- ];
- $this->assertSame($expected, $this->manager->getCssFiles($editor), 'Iframe instance CSS file found.');
- }
-
-}
diff --git a/core/modules/ckeditor/tests/src/Kernel/CKEditorTest.php b/core/modules/ckeditor/tests/src/Kernel/CKEditorTest.php
deleted file mode 100644
index 5e0ccae39209..000000000000
--- a/core/modules/ckeditor/tests/src/Kernel/CKEditorTest.php
+++ /dev/null
@@ -1,562 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ckeditor\Kernel;
-
-use Drupal\KernelTests\KernelTestBase;
-use Drupal\language\Entity\ConfigurableLanguage;
-use Drupal\editor\Entity\Editor;
-use Drupal\filter\Entity\FilterFormat;
-
-/**
- * Tests for the 'CKEditor' text editor plugin.
- *
- * @group ckeditor
- */
-class CKEditorTest extends KernelTestBase {
-
- /**
- * Modules to enable.
- *
- * @var array
- */
- protected static $modules = [
- 'system',
- 'user',
- 'filter',
- 'editor',
- 'ckeditor',
- 'filter_test',
- ];
-
- /**
- * An instance of the "CKEditor" text editor plugin.
- *
- * @var \Drupal\ckeditor\Plugin\Editor\CKEditor
- */
- protected $ckeditor;
-
- /**
- * The file URL generator.
- *
- * @var \Drupal\Core\File\FileUrlGeneratorInterface
- */
- protected $fileUrlGenerator;
-
- /**
- * The Editor Plugin Manager.
- *
- * @var \Drupal\editor\Plugin\EditorManager
- */
- protected $manager;
-
- protected function setUp(): void {
- parent::setUp();
- $this->fileUrlGenerator = $this->container->get('file_url_generator');
-
- // Install the Filter module.
-
- // Create text format, associate CKEditor.
- $filtered_html_format = FilterFormat::create([
- 'format' => 'filtered_html',
- 'name' => 'Filtered HTML',
- 'weight' => 0,
- 'filters' => [
- 'filter_html' => [
- 'status' => 1,
- 'settings' => [
- 'allowed_html' => '<h2 id> <h3> <h4> <h5> <h6> <p> <br> <strong> <a href hreflang>',
- ],
- ],
- ],
- ]);
- $filtered_html_format->save();
- $editor = Editor::create([
- 'format' => 'filtered_html',
- 'editor' => 'ckeditor',
- ]);
- $editor->save();
-
- // Create "CKEditor" text editor plugin instance.
- $this->ckeditor = $this->container->get('plugin.manager.editor')->createInstance('ckeditor');
- }
-
- /**
- * Tests CKEditor::getJSSettings().
- */
- public function testGetJSSettings() {
- $editor = Editor::load('filtered_html');
- $query_string = '?0=';
-
- // Default toolbar.
- $expected_config = $this->getDefaultInternalConfig() + [
- 'drupalImage_dialogTitleAdd' => 'Insert Image',
- 'drupalImage_dialogTitleEdit' => 'Edit Image',
- 'drupalLink_dialogTitleAdd' => 'Add Link',
- 'drupalLink_dialogTitleEdit' => 'Edit Link',
- 'allowedContent' => $this->getDefaultAllowedContentConfig(),
- 'disallowedContent' => $this->getDefaultDisallowedContentConfig(),
- 'toolbar' => $this->getDefaultToolbarConfig(),
- 'contentsCss' => $this->getDefaultContentsCssConfig(),
- 'extraPlugins' => 'drupalimage,drupallink',
- 'language' => 'en',
- 'stylesSet' => FALSE,
- 'drupalExternalPlugins' => [
- 'drupalimage' => $this->fileUrlGenerator->generateString('core/modules/ckeditor/js/plugins/drupalimage/plugin.js'),
- 'drupallink' => $this->fileUrlGenerator->generateString('core/modules/ckeditor/js/plugins/drupallink/plugin.js'),
- ],
- ];
- $this->assertEquals($expected_config, $this->ckeditor->getJSSettings($editor), 'Generated JS settings are correct for default configuration.');
-
- // Customize the configuration: add button, have two contextually enabled
- // buttons, and configure a CKEditor plugin setting.
- $this->enableModules(['ckeditor_test']);
- $this->container->get('plugin.manager.editor')->clearCachedDefinitions();
- $this->ckeditor = $this->container->get('plugin.manager.editor')->createInstance('ckeditor');
- $this->container->get('plugin.manager.ckeditor.plugin')->clearCachedDefinitions();
- $settings = $editor->getSettings();
- $settings['toolbar']['rows'][0][0]['items'][] = 'Strike';
- $settings['toolbar']['rows'][0][0]['items'][] = 'Format';
- $editor->setSettings($settings);
- $editor->save();
- $expected_config['toolbar'][0]['items'][] = 'Strike';
- $expected_config['toolbar'][0]['items'][] = 'Format';
- $expected_config['format_tags'] = 'p;h2;h3;h4;h5;h6';
- $expected_config['extraPlugins'] .= ',llama_contextual,llama_contextual_and_button';
- $expected_config['drupalExternalPlugins']['llama_contextual'] = $this->fileUrlGenerator->generateString('core/modules/ckeditor/tests/modules/js/llama_contextual.js');
- $expected_config['drupalExternalPlugins']['llama_contextual_and_button'] = $this->fileUrlGenerator->generateString('core/modules/ckeditor/tests/modules/js/llama_contextual_and_button.js');
- $expected_config['contentsCss'][] = $this->fileUrlGenerator->generateString('core/modules/ckeditor/tests/modules/ckeditor_test.css') . $query_string;
- $this->assertEquals($expected_config, $this->ckeditor->getJSSettings($editor), 'Generated JS settings are correct for customized configuration.');
-
- // Change the allowed HTML tags; the "allowedContent" and "format_tags"
- // settings for CKEditor should automatically be updated as well.
- $format = $editor->getFilterFormat();
- $format->filters('filter_html')->settings['allowed_html'] .= '<pre class> <h1> <blockquote class="*"> <address class="foo bar-* *">';
- $format->save();
-
- $expected_config['allowedContent']['pre'] = ['attributes' => 'class', 'styles' => FALSE, 'classes' => TRUE];
- $expected_config['allowedContent']['h1'] = ['attributes' => FALSE, 'styles' => FALSE, 'classes' => FALSE];
- $expected_config['allowedContent']['blockquote'] = ['attributes' => 'class', 'styles' => FALSE, 'classes' => TRUE];
- $expected_config['allowedContent']['address'] = ['attributes' => 'class', 'styles' => FALSE, 'classes' => 'foo,bar-*'];
- $expected_config['format_tags'] = 'p;h1;h2;h3;h4;h5;h6;pre';
- $this->assertEquals($expected_config, $this->ckeditor->getJSSettings($editor), 'Generated JS settings are correct for customized configuration.');
-
- // Disable the filter_html filter: allow *all *tags.
- $format->setFilterConfig('filter_html', ['status' => 0]);
- $format->save();
-
- $expected_config['allowedContent'] = TRUE;
- $expected_config['disallowedContent'] = FALSE;
- $expected_config['format_tags'] = 'p;h1;h2;h3;h4;h5;h6;pre';
- $this->assertEquals($expected_config, $this->ckeditor->getJSSettings($editor), 'Generated JS settings are correct for customized configuration.');
-
- // Enable the filter_test_restrict_tags_and_attributes filter.
- $format->setFilterConfig('filter_test_restrict_tags_and_attributes', [
- 'status' => 1,
- 'settings' => [
- 'restrictions' => [
- 'allowed' => [
- 'p' => TRUE,
- 'a' => [
- 'href' => TRUE,
- 'rel' => ['nofollow' => TRUE],
- 'class' => ['external' => TRUE],
- 'target' => ['_blank' => FALSE],
- ],
- 'span' => [
- 'class' => ['dodo' => FALSE],
- 'property' => ['dc:*' => TRUE],
- 'rel' => ['foaf:*' => FALSE],
- 'style' => ['underline' => FALSE, 'color' => FALSE, 'font-size' => TRUE],
- ],
- '*' => [
- 'style' => FALSE,
- 'on*' => FALSE,
- 'class' => ['is-a-hipster-llama' => TRUE, 'and-more' => TRUE],
- 'data-*' => TRUE,
- ],
- 'del' => FALSE,
- ],
- ],
- ],
- ]);
- $format->save();
-
- $expected_config['allowedContent'] = [
- 'p' => [
- 'attributes' => TRUE,
- 'styles' => FALSE,
- 'classes' => 'is-a-hipster-llama,and-more',
- ],
- 'a' => [
- 'attributes' => 'href,rel,class,target',
- 'styles' => FALSE,
- 'classes' => 'external',
- ],
- 'span' => [
- 'attributes' => 'class,property,rel,style',
- 'styles' => 'font-size',
- 'classes' => FALSE,
- ],
- '*' => [
- 'attributes' => 'class,data-*',
- 'styles' => FALSE,
- 'classes' => 'is-a-hipster-llama,and-more',
- ],
- 'del' => [
- 'attributes' => FALSE,
- 'styles' => FALSE,
- 'classes' => FALSE,
- ],
- ];
- $expected_config['disallowedContent'] = [
- 'span' => [
- 'styles' => 'underline,color',
- 'classes' => 'dodo',
- ],
- '*' => [
- 'attributes' => 'on*',
- ],
- ];
- $expected_config['format_tags'] = 'p';
- $this->assertEquals($expected_config, $this->ckeditor->getJSSettings($editor), 'Generated JS settings are correct for customized configuration.');
- }
-
- /**
- * Tests CKEditor::buildToolbarJSSetting().
- */
- public function testBuildToolbarJSSetting() {
- $editor = Editor::load('filtered_html');
-
- // Default toolbar.
- $expected = $this->getDefaultToolbarConfig();
- $this->assertSame($expected, $this->ckeditor->buildToolbarJSSetting($editor), '"toolbar" configuration part of JS settings built correctly for default toolbar.');
-
- // Customize the configuration.
- $settings = $editor->getSettings();
- $settings['toolbar']['rows'][0][0]['items'][] = 'Strike';
- $editor->setSettings($settings);
- $editor->save();
- $expected[0]['items'][] = 'Strike';
- $this->assertEquals($expected, $this->ckeditor->buildToolbarJSSetting($editor), '"toolbar" configuration part of JS settings built correctly for customized toolbar.');
-
- // Enable the editor_test module, customize further.
- $this->enableModules(['ckeditor_test']);
- $this->container->get('plugin.manager.ckeditor.plugin')->clearCachedDefinitions();
- // Override the label of a toolbar component.
- $settings['toolbar']['rows'][0][0]['name'] = 'JunkScience';
- $settings['toolbar']['rows'][0][0]['items'][] = 'Llama';
- $editor->setSettings($settings);
- $editor->save();
- $expected[0]['name'] = 'JunkScience';
- $expected[0]['items'][] = 'Llama';
- $this->assertEquals($expected, $this->ckeditor->buildToolbarJSSetting($editor), '"toolbar" configuration part of JS settings built correctly for customized toolbar with contrib module-provided CKEditor plugin.');
- }
-
- /**
- * Tests CKEditor::buildContentsCssJSSetting().
- */
- public function testBuildContentsCssJSSetting() {
- $editor = Editor::load('filtered_html');
- $query_string = '?0=';
-
- // Default toolbar.
- $expected = $this->getDefaultContentsCssConfig();
- $this->assertEquals($expected, $this->ckeditor->buildContentsCssJSSetting($editor), '"contentsCss" configuration part of JS settings built correctly for default toolbar.');
-
- // Enable the editor_test module, which implements hook_ckeditor_css_alter().
- $this->enableModules(['ckeditor_test']);
- $expected[] = $this->fileUrlGenerator->generateString($this->getModulePath('ckeditor_test') . '/ckeditor_test.css') . $query_string;
- $this->assertSame($expected, $this->ckeditor->buildContentsCssJSSetting($editor), '"contentsCss" configuration part of JS settings built correctly while a hook_ckeditor_css_alter() implementation exists.');
-
- // Enable LlamaCss plugin, which adds an additional CKEditor stylesheet.
- $this->container->get('plugin.manager.editor')->clearCachedDefinitions();
- $this->ckeditor = $this->container->get('plugin.manager.editor')->createInstance('ckeditor');
- $this->container->get('plugin.manager.ckeditor.plugin')->clearCachedDefinitions();
- $settings = $editor->getSettings();
- // LlamaCss: automatically enabled by adding its 'LlamaCSS' button.
- $settings['toolbar']['rows'][0][0]['items'][] = 'LlamaCSS';
- $editor->setSettings($settings);
- $editor->save();
- $expected[] = $this->fileUrlGenerator->generateString($this->getModulePath('ckeditor_test') . '/css/llama.css') . $query_string;
- $this->assertSame($expected, $this->ckeditor->buildContentsCssJSSetting($editor), '"contentsCss" configuration part of JS settings built correctly while a CKEditorPluginInterface implementation exists.');
-
- // Enable the Olivero theme, which specifies a CKEditor stylesheet.
- \Drupal::service('theme_installer')->install(['olivero']);
- $this->config('system.theme')->set('default', 'olivero')->save();
- $expected[] = $this->fileUrlGenerator->generateString('core/themes/olivero/css/base/fonts.css') . $query_string;
- $expected[] = $this->fileUrlGenerator->generateString('core/themes/olivero/css/base/base.css') . $query_string;
- $expected[] = $this->fileUrlGenerator->generateString('core/themes/olivero/css/components/embedded-media.css') . $query_string;
- $expected[] = $this->fileUrlGenerator->generateString('core/themes/olivero/css/components/table.css') . $query_string;
- $expected[] = $this->fileUrlGenerator->generateString('core/themes/olivero/css/components/text-content.css') . $query_string;
- $expected[] = $this->fileUrlGenerator->generateString('core/themes/olivero/css/theme/ckeditor-frame.css') . $query_string;
- $this->assertSame($expected, $this->ckeditor->buildContentsCssJSSetting($editor), '"contentsCss" configuration part of JS settings built correctly while a theme providing a CKEditor stylesheet exists.');
- }
-
- /**
- * Tests Internal::getConfig().
- */
- public function testInternalGetConfig() {
- $editor = Editor::load('filtered_html');
- $internal_plugin = $this->container->get('plugin.manager.ckeditor.plugin')->createInstance('internal');
-
- // Default toolbar.
- $expected = $this->getDefaultInternalConfig();
- $expected['disallowedContent'] = $this->getDefaultDisallowedContentConfig();
- $expected['allowedContent'] = $this->getDefaultAllowedContentConfig();
- $this->assertEquals($expected, $internal_plugin->getConfig($editor), '"Internal" plugin configuration built correctly for default toolbar.');
-
- // Format dropdown/button enabled: new setting should be present.
- $settings = $editor->getSettings();
- $settings['toolbar']['rows'][0][0]['items'][] = 'Format';
- $editor->setSettings($settings);
- $expected['format_tags'] = 'p;h2;h3;h4;h5;h6';
- $this->assertEquals($expected, $internal_plugin->getConfig($editor), '"Internal" plugin configuration built correctly for customized toolbar.');
- }
-
- /**
- * Tests StylesCombo::getConfig().
- */
- public function testStylesComboGetConfig() {
- $editor = Editor::load('filtered_html');
- $stylescombo_plugin = $this->container->get('plugin.manager.ckeditor.plugin')->createInstance('stylescombo');
-
- // Styles dropdown/button enabled: new setting should be present.
- $settings = $editor->getSettings();
- $settings['toolbar']['rows'][0][0]['items'][] = 'Styles';
- $settings['plugins']['stylescombo']['styles'] = '';
- $editor->setSettings($settings);
- $editor->save();
- $expected['stylesSet'] = [];
- $this->assertSame($expected, $stylescombo_plugin->getConfig($editor), '"StylesCombo" plugin configuration built correctly for customized toolbar.');
-
- // Configure the optional "styles" setting in odd ways that shouldn't affect
- // the end result.
- $settings['plugins']['stylescombo']['styles'] = " \n";
- $editor->setSettings($settings);
- $editor->save();
- $this->assertSame($expected, $stylescombo_plugin->getConfig($editor));
- $settings['plugins']['stylescombo']['styles'] = "\r\n \n \r \n ";
- $editor->setSettings($settings);
- $editor->save();
- $this->assertSame($expected, $stylescombo_plugin->getConfig($editor), '"StylesCombo" plugin configuration built correctly for customized toolbar.');
-
- // Now configure it properly, the end result should change.
- $settings['plugins']['stylescombo']['styles'] = "h1.title|Title\np.mAgical.Callout|Callout";
- $editor->setSettings($settings);
- $editor->save();
- $expected['stylesSet'] = [
- ['name' => 'Title', 'element' => 'h1', 'attributes' => ['class' => 'title']],
- ['name' => 'Callout', 'element' => 'p', 'attributes' => ['class' => 'mAgical Callout']],
- ];
- $this->assertSame($expected, $stylescombo_plugin->getConfig($editor), '"StylesCombo" plugin configuration built correctly for customized toolbar.');
-
- // Same configuration, but now interspersed with nonsense. Should yield the
- // same result.
- $settings['plugins']['stylescombo']['styles'] = " h1 .title | Title \r \n\r \np.mAgical .Callout|Callout\r";
- $editor->setSettings($settings);
- $editor->save();
- $this->assertSame($expected, $stylescombo_plugin->getConfig($editor), '"StylesCombo" plugin configuration built correctly for customized toolbar.');
-
- // Slightly different configuration: class names are optional.
- $settings['plugins']['stylescombo']['styles'] = " h1 | Title ";
- $editor->setSettings($settings);
- $editor->save();
- $expected['stylesSet'] = [['name' => 'Title', 'element' => 'h1']];
- $this->assertSame($expected, $stylescombo_plugin->getConfig($editor), '"StylesCombo" plugin configuration built correctly for customized toolbar.');
-
- // Invalid syntax should cause stylesSet to be set to FALSE.
- $settings['plugins']['stylescombo']['styles'] = "h1";
- $editor->setSettings($settings);
- $editor->save();
- $expected['stylesSet'] = FALSE;
- $this->assertSame($expected, $stylescombo_plugin->getConfig($editor), '"StylesCombo" plugin configuration built correctly for customized toolbar.');
-
- // Configuration that includes a dash in either the element or class name.
- $settings['plugins']['stylescombo']['styles'] = "drupal-entity.has-dashes|Allowing Dashes";
- $editor->setSettings($settings);
- $editor->save();
- $expected['stylesSet'] = [
- [
- 'name' => 'Allowing Dashes',
- 'element' => 'drupal-entity',
- 'attributes' => ['class' => 'has-dashes'],
- ],
- ];
- $this->assertSame($expected, $stylescombo_plugin->getConfig($editor), '"StylesCombo" plugin configuration built correctly for customized toolbar.');
-
- }
-
- /**
- * Tests language list availability in CKEditor.
- */
- public function testLanguages() {
- // Get CKEditor supported language codes and spot-check.
- $this->enableModules(['language']);
- $this->installConfig(['language']);
- $langcodes = $this->ckeditor->getLangcodes();
-
- // Language codes transformed with browser mappings.
- $this->assertSame('pt', $langcodes['pt-pt'], '"pt" properly resolved');
- $this->assertSame('zh-cn', $langcodes['zh-hans'], '"zh-hans" properly resolved');
-
- // Language code both in Drupal and CKEditor.
- $this->assertSame('gl', $langcodes['gl'], '"gl" properly resolved');
-
- // Language codes only in CKEditor.
- $this->assertSame('en-au', $langcodes['en-au'], '"en-au" properly resolved');
- $this->assertSame('sr-latn', $langcodes['sr-latn'], '"sr-latn" properly resolved');
-
- // No locale module, so even though languages are enabled, CKEditor should
- // still be in English.
- $this->assertCKEditorLanguage('en');
- }
-
- /**
- * Tests that CKEditor plugins participate in JS translation.
- */
- public function testJSTranslation() {
- $this->enableModules(['language', 'locale']);
- $this->installSchema('locale', 'locales_source');
- $this->installSchema('locale', 'locales_location');
- $this->installSchema('locale', 'locales_target');
- $editor = Editor::load('filtered_html');
- $this->ckeditor->getJSSettings($editor);
- $localeStorage = $this->container->get('locale.storage');
- $string = $localeStorage->findString(['source' => 'Edit Link', 'context' => '']);
- $this->assertNotEmpty($string, 'String from JavaScript file saved.');
-
- // With locale module, CKEditor should not adhere to the language selected.
- $this->assertCKEditorLanguage();
- }
-
- /**
- * Tests loading of theme's CKEditor stylesheets defined in the .info file.
- */
- public function testExternalStylesheets() {
- /** @var \Drupal\Core\Extension\ThemeInstallerInterface $theme_installer */
- $theme_installer = \Drupal::service('theme_installer');
- // Case 1: Install theme which has an absolute external CSS URL.
- $theme_installer->install(['test_ckeditor_stylesheets_external']);
- $this->config('system.theme')->set('default', 'test_ckeditor_stylesheets_external')->save();
- $expected = [
- 'https://fonts.googleapis.com/css?family=Open+Sans',
- ];
- $this->assertSame($expected, _ckeditor_theme_css('test_ckeditor_stylesheets_external'));
-
- // Case 2: Install theme which has an external protocol-relative CSS URL.
- $theme_installer->install(['test_ckeditor_stylesheets_protocol_relative']);
- $this->config('system.theme')->set('default', 'test_ckeditor_stylesheets_protocol_relative')->save();
- $expected = [
- '//fonts.googleapis.com/css?family=Open+Sans',
- ];
- $this->assertSame($expected, _ckeditor_theme_css('test_ckeditor_stylesheets_protocol_relative'));
-
- // Case 3: Install theme which has a relative CSS URL.
- $theme_installer->install(['test_ckeditor_stylesheets_relative']);
- $this->config('system.theme')->set('default', 'test_ckeditor_stylesheets_relative')->save();
- $expected = [
- 'core/modules/system/tests/themes/test_ckeditor_stylesheets_relative/css/yokotsoko.css',
- ];
- $this->assertSame($expected, _ckeditor_theme_css('test_ckeditor_stylesheets_relative'));
-
- // Case 4: Install theme which has a Drupal root CSS URL.
- $theme_installer->install(['test_ckeditor_stylesheets_drupal_root']);
- $this->config('system.theme')->set('default', 'test_ckeditor_stylesheets_drupal_root')->save();
- $expected = [
- 'core/modules/system/tests/themes/test_ckeditor_stylesheets_drupal_root/css/yokotsoko.css',
- ];
- $this->assertSame($expected, _ckeditor_theme_css('test_ckeditor_stylesheets_drupal_root'));
- }
-
- /**
- * Assert that CKEditor picks the expected language when French is default.
- *
- * @param string $langcode
- * Language code to assert for. Defaults to French. That is the default
- * language set in this assertion.
- *
- * @internal
- */
- protected function assertCKEditorLanguage(string $langcode = 'fr'): void {
- // Set French as the site default language.
- ConfigurableLanguage::createFromLangcode('fr')->save();
- $this->config('system.site')->set('default_langcode', 'fr')->save();
-
- // Reset the language manager so new negotiations attempts will fall back on
- // French. Reinject the language manager CKEditor to use the current one.
- $this->container->get('language_manager')->reset();
- $this->ckeditor = $this->container->get('plugin.manager.editor')->createInstance('ckeditor');
-
- // Test that we now get the expected language.
- $editor = Editor::load('filtered_html');
- $settings = $this->ckeditor->getJSSettings($editor);
- $this->assertEquals($langcode, $settings['language']);
- }
-
- protected function getDefaultInternalConfig() {
- return [
- 'customConfig' => '',
- 'pasteFromWordPromptCleanup' => TRUE,
- 'resize_dir' => 'vertical',
- 'justifyClasses' => ['text-align-left', 'text-align-center', 'text-align-right', 'text-align-justify'],
- 'entities' => FALSE,
- 'disableNativeSpellChecker' => FALSE,
- ];
- }
-
- protected function getDefaultAllowedContentConfig() {
- return [
- 'h2' => ['attributes' => 'id', 'styles' => FALSE, 'classes' => FALSE],
- 'h3' => ['attributes' => FALSE, 'styles' => FALSE, 'classes' => FALSE],
- 'h4' => ['attributes' => FALSE, 'styles' => FALSE, 'classes' => FALSE],
- 'h5' => ['attributes' => FALSE, 'styles' => FALSE, 'classes' => FALSE],
- 'h6' => ['attributes' => FALSE, 'styles' => FALSE, 'classes' => FALSE],
- 'p' => ['attributes' => FALSE, 'styles' => FALSE, 'classes' => FALSE],
- 'br' => ['attributes' => FALSE, 'styles' => FALSE, 'classes' => FALSE],
- 'strong' => ['attributes' => FALSE, 'styles' => FALSE, 'classes' => FALSE],
- 'a' => ['attributes' => 'href,hreflang', 'styles' => FALSE, 'classes' => FALSE],
- '*' => ['attributes' => 'lang,dir', 'styles' => FALSE, 'classes' => FALSE],
- ];
- }
-
- protected function getDefaultDisallowedContentConfig() {
- return [
- '*' => ['attributes' => 'on*'],
- ];
- }
-
- protected function getDefaultToolbarConfig() {
- return [
- [
- 'name' => 'Formatting',
- 'items' => ['Bold', 'Italic'],
- ],
- [
- 'name' => 'Links',
- 'items' => ['DrupalLink', 'DrupalUnlink'],
- ],
- [
- 'name' => 'Lists',
- 'items' => ['BulletedList', 'NumberedList'],
- ],
- [
- 'name' => 'Media',
- 'items' => ['Blockquote', 'DrupalImage'],
- ],
- [
- 'name' => 'Tools',
- 'items' => ['Source'],
- ],
- '/',
- ];
- }
-
- protected function getDefaultContentsCssConfig() {
- $query_string = '?0=';
- return [
- $this->fileUrlGenerator->generateString('core/modules/ckeditor/css/ckeditor-iframe.css') . $query_string,
- $this->fileUrlGenerator->generateString('core/modules/system/css/components/align.module.css') . $query_string,
- ];
- }
-
-}
diff --git a/core/modules/ckeditor/tests/src/Kernel/Plugin/CKEditorPlugin/InternalTest.php b/core/modules/ckeditor/tests/src/Kernel/Plugin/CKEditorPlugin/InternalTest.php
deleted file mode 100644
index 1de3df5f7e5f..000000000000
--- a/core/modules/ckeditor/tests/src/Kernel/Plugin/CKEditorPlugin/InternalTest.php
+++ /dev/null
@@ -1,140 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ckeditor\Kernel\Plugin\CKEditorPlugin;
-
-use Drupal\editor\Entity\Editor;
-use Drupal\filter\Entity\FilterFormat;
-use Drupal\KernelTests\KernelTestBase;
-
-/**
- * @coversDefaultClass \Drupal\ckeditor\Plugin\CKEditorPlugin\Internal
- *
- * @group ckeditor
- */
-class InternalTest extends KernelTestBase {
-
- /**
- * Modules to enable.
- *
- * @var array
- */
- protected static $modules = [
- 'ckeditor',
- 'ckeditor_test',
- 'filter',
- 'editor',
- ];
-
- /**
- * A testing text format.
- *
- * @var \Drupal\filter\Entity\FilterFormat
- */
- protected $format;
-
- /**
- * A testing text editor.
- *
- * @var \Drupal\editor\Entity\Editor
- */
- protected $editor;
-
- /**
- * The CKEditor plugin manager.
- *
- * @var \Drupal\Component\Plugin\PluginManagerInterface
- */
- protected $ckeditorPluginManager;
-
- /**
- * {@inheritdoc}
- */
- protected function setUp(): void {
- parent::setUp();
- $this->installEntitySchema('editor');
- $this->installEntitySchema('filter_format');
-
- $this->format = FilterFormat::create([
- 'format' => 'test_format',
- 'name' => $this->randomMachineName(),
- ]);
- $this->format->save();
-
- $this->editor = Editor::create([
- 'editor' => 'ckeditor',
- 'format' => 'test_format',
- 'settings' => [
- 'toolbar' => [
- 'rows' => [
- [
- [
- 'name' => 'Enabled Buttons',
- 'items' => [
- 'Format',
- ],
- ],
- ],
- ],
- ],
- ],
- ]);
- $this->editor->save();
-
- $this->ckeditorPluginManager = $this->container->get('plugin.manager.ckeditor.plugin');
- }
-
- /**
- * Tests the format tags settings.
- *
- * @dataProvider formatTagsSettingsTestCases
- */
- public function testFormatTagsSettings($filter_plugins, $expected_format_tags) {
- foreach ($filter_plugins as $filter_plugin_id => $filter_plugin_settings) {
- $this->format->setFilterConfig($filter_plugin_id, $filter_plugin_settings);
- }
- $this->format->save();
-
- $internal_plugin = $this->ckeditorPluginManager->createInstance('internal', []);
- $plugin_config = $internal_plugin->getConfig($this->editor);
- $this->assertEquals($expected_format_tags, explode(';', $plugin_config['format_tags']));
- }
-
- /**
- * A data provider for testFormatTagsSettings.
- */
- public function formatTagsSettingsTestCases() {
- $all_tags = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'pre'];
-
- return [
- 'No filter plugins enabled (all tags allowed)' => [
- [],
- $all_tags,
- ],
- 'HTML filter plugin enabled (some tags filtered out)' => [
- [
- 'filter_html' => [
- 'status' => 1,
- 'settings' => [
- 'allowed_html' => '<h1> <h2>',
- 'filter_html_help' => 1,
- 'filter_html_nofollow' => 0,
- ],
- ],
- ],
- ['p', 'h1', 'h2'],
- ],
- 'Test attribute filter enabled (all tags allowed)' => [
- [
- 'test_attribute_filter' => [
- 'status' => 1,
- 'settings' => [
- 'tags' => ['h1', 'h2'],
- ],
- ],
- ],
- $all_tags,
- ],
- ];
- }
-
-}
diff --git a/core/modules/ckeditor/tests/src/Traits/CKEditorAdminSortTrait.php b/core/modules/ckeditor/tests/src/Traits/CKEditorAdminSortTrait.php
deleted file mode 100644
index adbafc2df6c2..000000000000
--- a/core/modules/ckeditor/tests/src/Traits/CKEditorAdminSortTrait.php
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ckeditor\Traits;
-
-use Drupal\FunctionalJavascriptTests\SortableTestTrait;
-
-/**
- * Provides callback for simulated CKEditor toolbar configuration change.
- */
-trait CKEditorAdminSortTrait {
-
- use SortableTestTrait;
-
- /**
- * {@inheritdoc}
- */
- protected function sortableUpdate($item, $from, $to = NULL) {
- $script = <<<JS
-(function () {
- // Set backbone model after a DOM change.
- Drupal.ckeditor.models.Model.set('isDirty', true);
-})()
-
-JS;
-
- $options = [
- 'script' => $script,
- 'args' => [],
- ];
-
- $this->getSession()->getDriver()->getWebDriverSession()->execute($options);
- }
-
-}
diff --git a/core/modules/ckeditor/tests/src/Traits/CKEditorTestTrait.php b/core/modules/ckeditor/tests/src/Traits/CKEditorTestTrait.php
deleted file mode 100644
index 0048d7df3aa9..000000000000
--- a/core/modules/ckeditor/tests/src/Traits/CKEditorTestTrait.php
+++ /dev/null
@@ -1,111 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ckeditor\Traits;
-
-/**
- * Provides methods to test CKEditor.
- *
- * This trait is meant to be used only by functional JavaScript test classes.
- */
-trait CKEditorTestTrait {
-
- /**
- * Waits for CKEditor to initialize.
- *
- * @param string $instance_id
- * (optional) The CKEditor instance ID. Defaults to 'edit-body-0-value'.
- * @param int $timeout
- * (optional) Timeout in milliseconds, defaults to 10000.
- */
- protected function waitForEditor($instance_id = 'edit-body-0-value', $timeout = 10000) {
- $condition = <<<JS
- (function() {
- return (
- typeof CKEDITOR !== 'undefined'
- && typeof CKEDITOR.instances["{$instance_id}"] !== 'undefined'
- && CKEDITOR.instances["{$instance_id}"].instanceReady
- );
- }())
-JS;
- $this->assertJsCondition($condition, $timeout);
- }
-
- /**
- * Assigns a name to the CKEditor iframe.
- *
- * @param string $id
- * (optional) The id to assign the iframe element. Defaults to 'ckeditor'.
- * @param string $instance_id
- * (optional) The CKEditor instance ID. Defaults to 'edit-body-0-value'.
- *
- * @see \Behat\Mink\Session::switchToIFrame()
- */
- protected function assignNameToCkeditorIframe($id = 'ckeditor', $instance_id = 'edit-body-0-value') {
- $javascript = <<<JS
-(function(){
- CKEDITOR.instances['{$instance_id}'].element.getParent().find('.cke_wysiwyg_frame').$[0].id = '{$id}';
-})()
-JS;
- $this->getSession()->evaluateScript($javascript);
- }
-
- /**
- * Clicks a CKEditor button.
- *
- * @param string $name
- * The name of the button, such as `drupallink`, `source`, etc.
- * @param string $instance_id
- * (optional) The CKEditor instance ID. Defaults to 'edit-body-0-value'.
- */
- protected function pressEditorButton($name, $instance_id = 'edit-body-0-value') {
- $this->getEditorButton($name, $instance_id)->click();
- }
-
- /**
- * Waits for a CKEditor button and returns it when available and visible.
- *
- * @param string $name
- * The name of the button, such as `drupallink`, `source`, etc.
- * @param string $instance_id
- * (optional) The CKEditor instance ID. Defaults to 'edit-body-0-value'.
- *
- * @return \Behat\Mink\Element\NodeElement|null
- * The page element node if found, NULL if not.
- */
- protected function getEditorButton($name, $instance_id = 'edit-body-0-value') {
- $this->getSession()->switchToIFrame();
- $button = $this->assertSession()->waitForElementVisible('css', "#cke_$instance_id a.cke_button__" . $name);
- $this->assertNotEmpty($button);
-
- return $button;
- }
-
- /**
- * Asserts a CKEditor button is disabled.
- *
- * @param string $name
- * The name of the button, such as `drupallink`, `source`, etc.
- * @param string $instance_id
- * (optional) The CKEditor instance ID. Defaults to 'edit-body-0-value'.
- */
- protected function assertEditorButtonDisabled($name, $instance_id = 'edit-body-0-value') {
- $button = $this->getEditorButton($name, $instance_id);
- $this->assertTrue($button->hasClass('cke_button_disabled'));
- $this->assertSame('true', $button->getAttribute('aria-disabled'));
- }
-
- /**
- * Asserts a CKEditor button is enabled.
- *
- * @param string $name
- * The name of the button, such as `drupallink`, `source`, etc.
- * @param string $instance_id
- * (optional) The CKEditor instance ID. Defaults to 'edit-body-0-value'.
- */
- protected function assertEditorButtonEnabled($name, $instance_id = 'edit-body-0-value') {
- $button = $this->getEditorButton($name, $instance_id);
- $this->assertFalse($button->hasClass('cke_button_disabled'));
- $this->assertSame('false', $button->getAttribute('aria-disabled'));
- }
-
-}
diff --git a/core/modules/ckeditor/tests/src/Unit/CKEditorPluginManagerTest.php b/core/modules/ckeditor/tests/src/Unit/CKEditorPluginManagerTest.php
deleted file mode 100644
index dfa1f163f7ea..000000000000
--- a/core/modules/ckeditor/tests/src/Unit/CKEditorPluginManagerTest.php
+++ /dev/null
@@ -1,97 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ckeditor\Unit;
-
-use Drupal\ckeditor\CKEditorPluginManager;
-use Drupal\editor\Entity\Editor;
-use Drupal\Tests\UnitTestCase;
-
-/**
- * @coversDefaultClass \Drupal\ckeditor\CKEditorPluginManager
- *
- * @group ckeditor
- */
-class CKEditorPluginManagerTest extends UnitTestCase {
-
- /**
- * Provides a list of configs to test.
- */
- public function providerGetEnabledButtons() {
- return [
- 'empty' => [
- [],
- [],
- ],
- '1 row, 1 group' => [
- [
- // Row 1.
- [
- // Group 1.
- ['name' => 'Formatting', 'items' => ['Bold', 'Italic']],
- ],
- ],
- ['Bold', 'Italic'],
- ],
- '1 row, >1 groups' => [
- [
- // Row 1.
- [
- // Group 1.
- ['name' => 'Formatting', 'items' => ['Bold', 'Italic']],
- // Group 2.
- ['name' => 'Linking', 'items' => ['Link']],
- ],
- ],
- ['Bold', 'Italic', 'Link'],
- ],
- '2 rows, 1 group each' => [
- [
- // Row 1.
- [
- // Group 1.
- ['name' => 'Formatting', 'items' => ['Bold', 'Italic']],
- ],
- // Row 2.
- [
- // Group 1.
- ['name' => 'Tools', 'items' => ['Source']],
- ],
- ],
- ['Bold', 'Italic', 'Source'],
- ],
- '2 rows, >1 groups each' => [
- [
- // Row 1.
- [
- // Group 1.
- ['name' => 'Formatting', 'items' => ['Bold', 'Italic']],
- // Group 2.
- ['name' => 'Linking', 'items' => ['Link']],
- ],
- // Row 2.
- [
- // Group 1.
- ['name' => 'Tools', 'items' => ['Source']],
- // Group 2.
- ['name' => 'Advanced', 'items' => ['Llama']],
- ],
- ],
- ['Bold', 'Italic', 'Link', 'Source', 'Llama'],
- ],
- ];
- }
-
- /**
- * @covers ::getEnabledButtons
- * @dataProvider providerGetEnabledButtons
- */
- public function testGetEnabledButtons(array $toolbar_rows, array $expected_buttons) {
- $editor = $this->prophesize(Editor::class);
- $editor->getSettings()
- ->willReturn(['toolbar' => ['rows' => $toolbar_rows]]);
-
- $enabled_buttons = CKEditorPluginManager::getEnabledButtons($editor->reveal());
- $this->assertEquals($expected_buttons, $enabled_buttons);
- }
-
-}
diff --git a/core/modules/ckeditor/tests/src/Unit/Plugin/CKEditorPlugin/LanguageTest.php b/core/modules/ckeditor/tests/src/Unit/Plugin/CKEditorPlugin/LanguageTest.php
deleted file mode 100644
index 76e296197e84..000000000000
--- a/core/modules/ckeditor/tests/src/Unit/Plugin/CKEditorPlugin/LanguageTest.php
+++ /dev/null
@@ -1,65 +0,0 @@
-<?php
-
-namespace Drupal\Tests\ckeditor\Unit\Plugin\CKEditorPlugin;
-
-use Drupal\ckeditor\Plugin\CKEditorPlugin\Language;
-use Drupal\Core\Language\LanguageManager;
-use Drupal\Tests\UnitTestCase;
-
-/**
- * @coversDefaultClass \Drupal\ckeditor\Plugin\CKEditorPlugin\Language
- *
- * @group ckeditor
- */
-class LanguageTest extends UnitTestCase {
-
- /**
- * The plugin under test.
- *
- * @var \Drupal\ckeditor\Plugin\CKEditorPlugin\Language
- */
- protected $plugin;
-
- /**
- * {@inheritdoc}
- */
- protected function setUp(): void {
- $this->plugin = new Language([], $this->randomMachineName(), []);
- }
-
- /**
- * Provides a list of configs to test.
- */
- public function providerGetConfig() {
- return [
- ['un', LanguageManager::getUnitedNationsLanguageList()],
- ['all', LanguageManager::getStandardLanguageList()],
- ];
- }
-
- /**
- * @covers ::getConfig
- *
- * @dataProvider providerGetConfig
- */
- public function testGetConfig($language_list, $expected_languages) {
- $editor = $this->getMockBuilder('Drupal\editor\Entity\Editor')
- ->disableOriginalConstructor()
- ->getMock();
- $editor->expects($this->once())
- ->method('getSettings')
- ->willReturn(['plugins' => ['language' => ['language_list' => $language_list]]]);
-
- $config = $this->plugin->getConfig($editor);
-
- $this->assertIsArray($config);
- $this->assertContains('ar:Arabic:rtl', $config['language_list']);
- $this->assertContains('zh-hans:Chinese, Simplified', $config['language_list']);
- $this->assertContains('en:English', $config['language_list']);
- $this->assertContains('fr:French', $config['language_list']);
- $this->assertContains('ru:Russian', $config['language_list']);
- $this->assertContains('ar:Arabic:rtl', $config['language_list']);
- $this->assertSameSize($expected_languages, $config['language_list']);
- }
-
-}
diff --git a/core/modules/ckeditor5/tests/src/Kernel/CKEditor4to5UpgradeCompletenessTest.php b/core/modules/ckeditor5/tests/src/Kernel/CKEditor4to5UpgradeCompletenessTest.php
deleted file mode 100644
index 753fcb664cdd..000000000000
--- a/core/modules/ckeditor5/tests/src/Kernel/CKEditor4to5UpgradeCompletenessTest.php
+++ /dev/null
@@ -1,275 +0,0 @@
-<?php
-
-declare(strict_types = 1);
-
-namespace Drupal\Tests\ckeditor5\Kernel;
-
-use Drupal\ckeditor\CKEditorPluginConfigurableInterface;
-use Drupal\ckeditor5\HTMLRestrictions;
-use Drupal\ckeditor5\Plugin\CKEditor5PluginElementsSubsetInterface;
-use Drupal\Component\Assertion\Inspector;
-use Drupal\Component\Utility\NestedArray;
-use Drupal\filter\Entity\FilterFormat;
-use Drupal\KernelTests\KernelTestBase;
-
-/**
- * @covers \Drupal\ckeditor5\Plugin\CKEditor4To5Upgrade\Core
- * @group ckeditor5
- * @internal
- */
-class CKEditor4to5UpgradeCompletenessTest extends KernelTestBase {
-
- /**
- * The CKEditor 4 toolbar buttons that no longer require a contrib module.
- *
- * @var string[]
- *
- * @see \Drupal\ckeditor5\Plugin\CKEditor4To5Upgrade\Contrib
- */
- const CONTRIB_BUTTONS_NOW_IN_CORE = [
- // @see https://www.drupal.org/project/codetag
- // @see ckeditor5_code's `basicStyles.Code` plugin
- 'Code',
- ];
-
- /**
- * The "CKEditor 4 plugin" plugin manager.
- *
- * @var \Drupal\ckeditor\CKEditorPluginManager
- */
- protected $cke4PluginManager;
-
- /**
- * The "CKEditor 5 plugin" plugin manager.
- *
- * @var \Drupal\ckeditor5\Plugin\CKEditor5PluginManagerInterface
- */
- protected $cke5PluginManager;
-
- /**
- * The CKEditor 4 to 5 upgrade plugin manager.
- *
- * @var \Drupal\ckeditor5\Plugin\CKEditor4To5UpgradePluginManager
- */
- protected $upgradePluginManager;
-
- /**
- * Smart default settings utility.
- *
- * @var \Drupal\ckeditor5\SmartDefaultSettings
- */
- protected $smartDefaultSettings;
-
- /**
- * {@inheritdoc}
- */
- protected static $modules = [
- 'ckeditor',
- 'ckeditor5',
- // Enabled because of ::testCKEditor5ConfigurableSubsetPlugins().
- 'filter',
- // Enabled because of \Drupal\media\Plugin\CKEditorPlugin\DrupalMedia.
- 'media',
- // Enabled because of \Drupal\media_library\Plugin\CKEditorPlugin\DrupalMediaLibrary.
- 'media_library',
- // Enabled for media_library.
- 'views',
- // These modules must be installed for ckeditor5_config_schema_info_alter()
- // to work, which in turn is necessary for the plugin definition validation
- // logic.
- // @see \Drupal\ckeditor5\Plugin\CKEditor5PluginDefinition::validateDrupalAspects()
- 'filter',
- 'editor',
- ];
-
- /**
- * {@inheritdoc}
- */
- protected function setUp(): void {
- parent::setUp();
-
- // The tested service is private; expose it under a public test-only alias.
- $this->container->setAlias('sut', 'plugin.manager.ckeditor4to5upgrade.plugin');
-
- $this->cke4PluginManager = $this->container->get('plugin.manager.ckeditor.plugin');
- $this->cke5PluginManager = $this->container->get('plugin.manager.ckeditor5.plugin');
- $this->upgradePluginManager = $this->container->get('sut');
- $this->smartDefaultSettings = $this->container->get('ckeditor5.smart_default_settings');
- }
-
- /**
- * Tests that all CKEditor 4 buttons in core have an upgrade path.
- */
- public function testButtons(): void {
- $cke4_buttons = array_keys(NestedArray::mergeDeepArray($this->cke4PluginManager->getButtons()));
- $cke4_buttons = array_merge($cke4_buttons, self::CONTRIB_BUTTONS_NOW_IN_CORE);
-
- foreach ($cke4_buttons as $button) {
- $equivalent = $this->upgradePluginManager->mapCKEditor4ToolbarButtonToCKEditor5ToolbarItem($button, HTMLRestrictions::emptySet());
- $this->assertTrue($equivalent === NULL || (is_array($equivalent) && Inspector::assertAllStrings($equivalent)));
- // The returned equivalent CKEditor 5 toolbar item(s) must exist.
- if (is_string($equivalent)) {
- foreach (explode(',', $equivalent) as $equivalent_cke5_toolbar_item) {
- $this->assertArrayHasKey($equivalent_cke5_toolbar_item, $this->cke5PluginManager->getToolbarItems());
- }
- }
- }
- }
-
- /**
- * Tests that the test-only CKEditor 4 module does not have an upgrade path.
- */
- public function testButtonsWithTestOnlyModule(): void {
- $this->enableModules(['ckeditor_test']);
- $this->cke4PluginManager = $this->container->get('plugin.manager.ckeditor.plugin');
-
- $this->expectException(\OutOfBoundsException::class);
- $this->expectExceptionMessage('No upgrade path found for the "LlamaCSS" button.');
-
- $this->testButtons();
- }
-
- /**
- * Tests that all configurable CKEditor 4 plugins in core have an upgrade path.
- */
- public function testSettings(): void {
- $cke4_configurable_plugins = [];
- foreach ($this->cke4PluginManager->getDefinitions() as $plugin_id => $definition) {
- // Special case: DrupalImage.
- // @see \Drupal\ckeditor\Plugin\CKEditorPlugin\DrupalImage
- // @see \Drupal\editor\Entity\Editor::getImageUploadSettings()
- if ($plugin_id === 'drupalimage') {
- continue;
- }
-
- if (is_subclass_of($definition['class'], CKEditorPluginConfigurableInterface::class)) {
- $cke4_configurable_plugins[] = $plugin_id;
- }
- }
-
- foreach ($cke4_configurable_plugins as $plugin_id) {
- $cke5_plugin_settings = $this->upgradePluginManager->mapCKEditor4SettingsToCKEditor5Configuration($plugin_id, []);
- $this->assertTrue($cke5_plugin_settings === NULL || is_array($cke5_plugin_settings));
- // The returned equivalent CKEditor 5 plugin must exist.
- if (is_array($cke5_plugin_settings)) {
- $cke5_plugin_id = array_keys($cke5_plugin_settings)[0];
- $this->assertArrayHasKey($cke5_plugin_id, $this->cke5PluginManager->getDefinitions());
- }
- }
- }
-
- /**
- * Tests that the test-only CKEditor 4 module does not have an upgrade path.
- */
- public function testSettingsWithTestOnlyModule(): void {
- $this->enableModules(['ckeditor_test']);
- $this->cke4PluginManager = $this->container->get('plugin.manager.ckeditor.plugin');
-
- $this->expectException(\OutOfBoundsException::class);
- $this->expectExceptionMessage('No upgrade path found for the "llama_contextual_and_button" plugin settings.');
-
- $this->testSettings();
- }
-
- /**
- * Tests that all elements subset plugins in core have an upgrade path.
- */
- public function testCKEditor5ConfigurableSubsetPlugins(): void {
- $cke5_elements_subset_plugins = [];
- foreach ($this->cke5PluginManager->getDefinitions() as $plugin_id => $definition) {
- // Special case: SourceEditing.
- // @see \Drupal\ckeditor5\SmartDefaultSettings::computeSubsetSettingForEnabledPluginsWithSubsets()
- if ($plugin_id === 'ckeditor5_sourceEditing') {
- continue;
- }
-
- if (is_a($definition->getClass(), CKEditor5PluginElementsSubsetInterface::class, TRUE)) {
- $cke5_elements_subset_plugins[] = $plugin_id;
- }
- }
-
- foreach ($cke5_elements_subset_plugins as $plugin_id) {
- $cke5_plugin_configuration = $this->upgradePluginManager->computeCKEditor5PluginSubsetConfiguration($plugin_id, FilterFormat::create());
- $this->assertTrue($cke5_plugin_configuration === NULL || is_array($cke5_plugin_configuration));
- }
- }
-
- /**
- * Tests that only one plugin can provide an upgrade path for a button.
- */
- public function testOnlyOneUpgradePluginAllowedPerCKEditor4Button(): void {
- $this->enableModules(['ckeditor4to5upgrade_plugin_test']);
- \Drupal::state()->set('ckeditor4to5upgrade_plugin_test', 'duplicate_button');
-
- $this->expectException(\OutOfBoundsException::class);
- $this->expectExceptionMessage('The "DrupalImage" CKEditor 4 button is already being upgraded by the "core" CKEditor4To5Upgrade plugin, the "foo" plugin is as well. This conflict needs to be resolved.');
-
- $this->upgradePluginManager->mapCKEditor4ToolbarButtonToCKEditor5ToolbarItem('foo', HTMLRestrictions::emptySet());
- }
-
- /**
- * Tests detecting a lying upgrade plugin cke4_button annotation.
- */
- public function testLyingUpgradePluginForCKEditor4Button(): void {
- $this->enableModules(['ckeditor4to5upgrade_plugin_test']);
- \Drupal::state()->set('ckeditor4to5upgrade_plugin_test', 'lying_button');
-
- $this->expectException(\LogicException::class);
- $this->expectExceptionMessage('The "foo" CKEditor4To5Upgrade plugin claims to provide an upgrade path for the "foo" CKEditor 4 button but does not.');
-
- $this->upgradePluginManager->mapCKEditor4ToolbarButtonToCKEditor5ToolbarItem('foo', HTMLRestrictions::emptySet());
- }
-
- /**
- * Tests that only one plugin can provide an upgrade path for plugin settings.
- */
- public function testOnlyOneUpgradePluginAllowedPerCKEditor4PluginSettings(): void {
- $this->enableModules(['ckeditor4to5upgrade_plugin_test']);
- \Drupal::state()->set('ckeditor4to5upgrade_plugin_test', 'duplicate_plugin_settings');
-
- $this->expectException(\OutOfBoundsException::class);
- $this->expectExceptionMessage('The "stylescombo" CKEditor 4 plugin\'s settings are already being upgraded by the "core" CKEditor4To5Upgrade plugin, the "foo" plugin is as well. This conflict needs to be resolved.');
-
- $this->upgradePluginManager->mapCKEditor4ToolbarButtonToCKEditor5ToolbarItem('foo', HTMLRestrictions::emptySet());
- }
-
- /**
- * Tests detecting a lying upgrade plugin cke4_plugin_settings annotation.
- */
- public function testLyingUpgradePluginForCKEditor4PluginSettings(): void {
- $this->enableModules(['ckeditor4to5upgrade_plugin_test']);
- \Drupal::state()->set('ckeditor4to5upgrade_plugin_test', 'lying_plugin_settings');
-
- $this->expectException(\LogicException::class);
- $this->expectExceptionMessage('The "foo" CKEditor4To5Upgrade plugin claims to provide an upgrade path for the "foo" CKEditor 4 plugin settings but does not.');
-
- $this->upgradePluginManager->mapCKEditor4SettingsToCKEditor5Configuration('foo', []);
- }
-
- /**
- * Tests that only one plugin can provide an upgrade path for a subset plugin.
- */
- public function testOnlyOneUpgradePluginAllowedPerCKEditor5ConfigurableSubsetPlugin(): void {
- $this->enableModules(['ckeditor4to5upgrade_plugin_test']);
- \Drupal::state()->set('ckeditor4to5upgrade_plugin_test', 'duplicate_subset');
-
- $this->expectException(\OutOfBoundsException::class);
- $this->expectExceptionMessage('The "ckeditor5_heading" CKEditor 5 plugin\'s elements subset configuration is already being computed by the "core" CKEditor4To5Upgrade plugin, the "foo" plugin is as well. This conflict needs to be resolved.');
-
- $this->upgradePluginManager->computeCKEditor5PluginSubsetConfiguration('foo', FilterFormat::create());
- }
-
- /**
- * Tests detecting lying cke5_plugin_elements_subset_configuration annotation.
- */
- public function testLyingUpgradePluginForCKEditor5ConfigurableSubsetPlugin(): void {
- $this->enableModules(['ckeditor4to5upgrade_plugin_test']);
- \Drupal::state()->set('ckeditor4to5upgrade_plugin_test', 'lying_subset');
-
- $this->expectException(\LogicException::class);
- $this->expectExceptionMessage('The "foo" CKEditor4To5Upgrade plugin claims to provide an upgrade path for the "foo" CKEditor 4 plugin settings but does not.');
-
- $this->upgradePluginManager->computeCKEditor5PluginSubsetConfiguration('foo', FilterFormat::create());
- }
-
-}
diff --git a/core/modules/editor/editor.admin.inc b/core/modules/editor/editor.admin.inc
index 6c03ad7c4ac0..9e2cdc6114ac 100644
--- a/core/modules/editor/editor.admin.inc
+++ b/core/modules/editor/editor.admin.inc
@@ -64,8 +64,7 @@ function editor_image_upload_settings_form(Editor $editor) {
];
}
// Set data- attributes with human-readable names for all possible stream
- // wrappers, so that it can be used by the summary rendering of
- // drupal.ckeditor.drupalimage.admin.
+ // wrappers, so that it can be used by the summary rendering of other code.
foreach (\Drupal::service('stream_wrapper_manager')->getNames(StreamWrapperInterface::WRITE_VISIBLE) as $scheme => $name) {
$form['scheme'][$scheme]['#attributes']['data-label'] = t('Storage: @name', ['@name' => $name]);
}
diff --git a/core/modules/editor/editor.module b/core/modules/editor/editor.module
index fcada7ffb8a4..a4d86712fb6c 100644
--- a/core/modules/editor/editor.module
+++ b/core/modules/editor/editor.module
@@ -28,11 +28,11 @@ function editor_help($route_name, RouteMatchInterface $route_match) {
case 'help.page.editor':
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
- $output .= '<p>' . t('The Text Editor module provides a framework that other modules (such as <a href=":ckeditor">CKEditor module</a>) can use to provide toolbars and other functionality that allow users to format text more easily than typing HTML tags directly. For more information, see the <a href=":documentation">online documentation for the Text Editor module</a>.', [':documentation' => 'https://www.drupal.org/documentation/modules/editor', ':ckeditor' => (\Drupal::moduleHandler()->moduleExists('ckeditor')) ? Url::fromRoute('help.page', ['name' => 'ckeditor'])->toString() : '#']) . '</p>';
+ $output .= '<p>' . t('The Text Editor module provides a framework that other modules (such as <a href=":ckeditor5">CKEditor5 module</a>) can use to provide toolbars and other functionality that allow users to format text more easily than typing HTML tags directly. For more information, see the <a href=":documentation">online documentation for the Text Editor module</a>.', [':documentation' => 'https://www.drupal.org/documentation/modules/editor', ':ckeditor5' => (\Drupal::moduleHandler()->moduleExists('ckeditor5')) ? Url::fromRoute('help.page', ['name' => 'ckeditor5'])->toString() : '#']) . '</p>';
$output .= '<h3>' . t('Uses') . '</h3>';
$output .= '<dl>';
$output .= '<dt>' . t('Installing text editors') . '</dt>';
- $output .= '<dd>' . t('The Text Editor module provides a framework for managing editors. To use it, you also need to enable a text editor. This can either be the core <a href=":ckeditor">CKEditor module</a>, which can be enabled on the <a href=":extend">Extend page</a>, or a contributed module for any other text editor. When installing a contributed text editor module, be sure to check the installation instructions, because you will most likely need to download and install an external library as well as the Drupal module.', [':ckeditor' => (\Drupal::moduleHandler()->moduleExists('ckeditor')) ? Url::fromRoute('help.page', ['name' => 'ckeditor'])->toString() : '#', ':extend' => Url::fromRoute('system.modules_list')->toString()]) . '</dd>';
+ $output .= '<dd>' . t('The Text Editor module provides a framework for managing editors. To use it, you also need to enable a text editor. This can either be the core <a href=":ckeditor5">CKEditor5 module</a>, which can be enabled on the <a href=":extend">Extend page</a>, or a contributed module for any other text editor. When installing a contributed text editor module, be sure to check the installation instructions, because you will most likely need to download and install an external library as well as the Drupal module.', [':ckeditor5' => (\Drupal::moduleHandler()->moduleExists('ckeditor5')) ? Url::fromRoute('help.page', ['name' => 'ckeditor5'])->toString() : '#', ':extend' => Url::fromRoute('system.modules_list')->toString()]) . '</dd>';
$output .= '<dt>' . t('Enabling a text editor for a text format') . '</dt>';
$output .= '<dd>' . t('On the <a href=":formats">Text formats and editors page</a> you can see which text editor is associated with each text format. You can change this by clicking on the <em>Configure</em> link, and then choosing a text editor or <em>none</em> from the <em>Text editor</em> drop-down list. The text editor will then be displayed with any text field for which this text format is chosen.', [':formats' => Url::fromRoute('filter.admin_overview')->toString()]) . '</dd>';
$output .= '<dt>' . t('Configuring a text editor') . '</dt>';
diff --git a/core/modules/editor/src/Annotation/Editor.php b/core/modules/editor/src/Annotation/Editor.php
index e90d2a15e2da..9ae86d8fdc9f 100644
--- a/core/modules/editor/src/Annotation/Editor.php
+++ b/core/modules/editor/src/Annotation/Editor.php
@@ -40,7 +40,7 @@ use Drupal\Component\Annotation\Plugin;
* )
* @endcode
*
- * For a working example, see \Drupal\ckeditor\Plugin\Editor\CKEditor
+ * For a working example, see \Drupal\ckeditor5\Plugin\Editor\CKEditor5
*
* @see \Drupal\editor\Plugin\EditorPluginInterface
* @see \Drupal\editor\Plugin\EditorBase
diff --git a/core/modules/system/tests/themes/test_ckeditor_stylesheets_drupal_root/test_ckeditor_stylesheets_drupal_root.info.yml b/core/modules/system/tests/themes/test_ckeditor_stylesheets_drupal_root/test_ckeditor_stylesheets_drupal_root.info.yml
index 26c711c2f115..ffe8e82baf38 100644
--- a/core/modules/system/tests/themes/test_ckeditor_stylesheets_drupal_root/test_ckeditor_stylesheets_drupal_root.info.yml
+++ b/core/modules/system/tests/themes/test_ckeditor_stylesheets_drupal_root/test_ckeditor_stylesheets_drupal_root.info.yml
@@ -5,7 +5,5 @@ package: Testing
version: VERSION
base theme: false
-ckeditor_stylesheets:
- - /core/modules/system/tests/themes/test_ckeditor_stylesheets_drupal_root/css/yokotsoko.css
ckeditor5-stylesheets:
- /core/modules/system/tests/themes/test_ckeditor_stylesheets_drupal_root/css/yokotsoko.css