Loading core/modules/ckeditor/ckeditor.module +87 −0 Original line number Diff line number Diff line Loading @@ -10,8 +10,11 @@ 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(). Loading Loading @@ -122,6 +125,59 @@ function _ckeditor_theme_css($theme = NULL) { 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(). */ Loading Loading @@ -203,3 +259,34 @@ function ckeditor_filter_format_edit_form_validate($form, FormStateInterface $fo } } } /** * 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); } } core/modules/ckeditor/ckeditor.post_update.php 0 → 100644 +41 −0 Original line number Diff line number Diff line <?php /** * @file * Post update functions for CKEditor. */ use Drupal\Core\Config\Entity\ConfigEntityUpdater; use Drupal\editor\Entity\Editor; /** * Updates Text Editors using CKEditor 4 to omit settings for disabled plugins. */ function ckeditor_post_update_omit_settings_for_disabled_plugins(&$sandbox = []) { $config_entity_updater = \Drupal::classResolver(ConfigEntityUpdater::class); $config_entity_updater->update($sandbox, 'editor', function (Editor $editor): bool { // 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 FALSE; } foreach (array_keys($settings['plugins']) as $plugin_id) { if (!in_array($plugin_id, $enabled_plugins, TRUE)) { $needs_update = TRUE; } } return $needs_update; }); } core/modules/ckeditor/src/Plugin/Editor/CKEditor.php +19 −1 Original line number Diff line number Diff line Loading @@ -166,7 +166,7 @@ public function getDefaultSettings() { ], ], ], 'plugins' => ['language' => ['language_list' => 'un']], 'plugins' => [], ]; } Loading Loading @@ -287,6 +287,24 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s 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); } /** Loading core/modules/ckeditor/tests/src/Functional/CKEditorAdminTest.php +16 −6 Original line number Diff line number Diff line Loading @@ -108,7 +108,7 @@ public function testExistingFormat() { ], ], ], 'plugins' => ['language' => ['language_list' => 'un']], 'plugins' => [], ]; $this->assertEquals($expected_default_settings, $ckeditor->getDefaultSettings()); Loading Loading @@ -136,22 +136,27 @@ public function testExistingFormat() { $expected_buttons_value = json_encode($expected_default_settings['toolbar']['rows']); $this->assertSession()->fieldValueEquals('editor[settings][toolbar][button_groups]', $expected_buttons_value); // Ensure the styles textarea exists and is initialized empty. $this->assertSession()->fieldValueEquals('editor[settings][plugins][stylescombo][styles]', ''); // 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; $expected_settings['plugins']['stylescombo']['styles'] = ''; $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'); Loading @@ -164,6 +169,7 @@ public function testExistingFormat() { // 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'], Loading Loading @@ -205,7 +211,12 @@ public function testExistingFormat() { // 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'); Loading Loading @@ -287,7 +298,6 @@ public function testNewFormat() { // Ensure an Editor object exists now, with the proper settings. $expected_settings = $default_settings; $expected_settings['plugins']['stylescombo']['styles'] = ''; $editor = Editor::load('amazing_format'); $this->assertInstanceOf(Editor::class, $editor); $this->assertEquals($expected_settings, $editor->getSettings(), 'The Editor config entity has the correct settings.'); Loading core/modules/ckeditor/tests/src/Functional/CKEditorStylesComboAdminTest.php +15 −4 Original line number Diff line number Diff line Loading @@ -39,6 +39,13 @@ class CKEditorStylesComboAdminTest extends BrowserTestBase { */ protected $format; /** * The default editor settings. * * @var array */ protected $defaultSettings; /** * {@inheritdoc} */ Loading @@ -52,9 +59,16 @@ protected function setUp(): void { '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(); Loading @@ -65,14 +79,11 @@ protected function setUp(): void { * Tests StylesCombo settings for an existing text format. */ public function testExistingFormat() { $ckeditor = $this->container->get('plugin.manager.editor')->createInstance('ckeditor'); $default_settings = $ckeditor->getDefaultSettings(); $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 = $default_settings; $expected_settings = $this->defaultSettings; $editor = Editor::load($this->format); $this->assertEquals($expected_settings, $editor->getSettings(), 'The Editor config entity has the correct settings.'); Loading Loading
core/modules/ckeditor/ckeditor.module +87 −0 Original line number Diff line number Diff line Loading @@ -10,8 +10,11 @@ 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(). Loading Loading @@ -122,6 +125,59 @@ function _ckeditor_theme_css($theme = NULL) { 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(). */ Loading Loading @@ -203,3 +259,34 @@ function ckeditor_filter_format_edit_form_validate($form, FormStateInterface $fo } } } /** * 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); } }
core/modules/ckeditor/ckeditor.post_update.php 0 → 100644 +41 −0 Original line number Diff line number Diff line <?php /** * @file * Post update functions for CKEditor. */ use Drupal\Core\Config\Entity\ConfigEntityUpdater; use Drupal\editor\Entity\Editor; /** * Updates Text Editors using CKEditor 4 to omit settings for disabled plugins. */ function ckeditor_post_update_omit_settings_for_disabled_plugins(&$sandbox = []) { $config_entity_updater = \Drupal::classResolver(ConfigEntityUpdater::class); $config_entity_updater->update($sandbox, 'editor', function (Editor $editor): bool { // 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 FALSE; } foreach (array_keys($settings['plugins']) as $plugin_id) { if (!in_array($plugin_id, $enabled_plugins, TRUE)) { $needs_update = TRUE; } } return $needs_update; }); }
core/modules/ckeditor/src/Plugin/Editor/CKEditor.php +19 −1 Original line number Diff line number Diff line Loading @@ -166,7 +166,7 @@ public function getDefaultSettings() { ], ], ], 'plugins' => ['language' => ['language_list' => 'un']], 'plugins' => [], ]; } Loading Loading @@ -287,6 +287,24 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s 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); } /** Loading
core/modules/ckeditor/tests/src/Functional/CKEditorAdminTest.php +16 −6 Original line number Diff line number Diff line Loading @@ -108,7 +108,7 @@ public function testExistingFormat() { ], ], ], 'plugins' => ['language' => ['language_list' => 'un']], 'plugins' => [], ]; $this->assertEquals($expected_default_settings, $ckeditor->getDefaultSettings()); Loading Loading @@ -136,22 +136,27 @@ public function testExistingFormat() { $expected_buttons_value = json_encode($expected_default_settings['toolbar']['rows']); $this->assertSession()->fieldValueEquals('editor[settings][toolbar][button_groups]', $expected_buttons_value); // Ensure the styles textarea exists and is initialized empty. $this->assertSession()->fieldValueEquals('editor[settings][plugins][stylescombo][styles]', ''); // 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; $expected_settings['plugins']['stylescombo']['styles'] = ''; $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'); Loading @@ -164,6 +169,7 @@ public function testExistingFormat() { // 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'], Loading Loading @@ -205,7 +211,12 @@ public function testExistingFormat() { // 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'); Loading Loading @@ -287,7 +298,6 @@ public function testNewFormat() { // Ensure an Editor object exists now, with the proper settings. $expected_settings = $default_settings; $expected_settings['plugins']['stylescombo']['styles'] = ''; $editor = Editor::load('amazing_format'); $this->assertInstanceOf(Editor::class, $editor); $this->assertEquals($expected_settings, $editor->getSettings(), 'The Editor config entity has the correct settings.'); Loading
core/modules/ckeditor/tests/src/Functional/CKEditorStylesComboAdminTest.php +15 −4 Original line number Diff line number Diff line Loading @@ -39,6 +39,13 @@ class CKEditorStylesComboAdminTest extends BrowserTestBase { */ protected $format; /** * The default editor settings. * * @var array */ protected $defaultSettings; /** * {@inheritdoc} */ Loading @@ -52,9 +59,16 @@ protected function setUp(): void { '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(); Loading @@ -65,14 +79,11 @@ protected function setUp(): void { * Tests StylesCombo settings for an existing text format. */ public function testExistingFormat() { $ckeditor = $this->container->get('plugin.manager.editor')->createInstance('ckeditor'); $default_settings = $ckeditor->getDefaultSettings(); $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 = $default_settings; $expected_settings = $this->defaultSettings; $editor = Editor::load($this->format); $this->assertEquals($expected_settings, $editor->getSettings(), 'The Editor config entity has the correct settings.'); Loading