From e8aaf3772e12c17a34be9abeda21984dca97939a Mon Sep 17 00:00:00 2001 From: Lauri Eskola <lauri.eskola@acquia.com> Date: Thu, 17 Feb 2022 16:04:07 +0200 Subject: [PATCH] Issue #3224652 by Wim Leers, hooroomoo, vlyalko, lauriii, bnjmnm, huzooka: [drupalImage] Add ckeditor5-image's imageresize plugin to allow image resizing --- core/modules/ckeditor5/ckeditor5.api.php | 2 + .../modules/ckeditor5/ckeditor5.ckeditor5.yml | 21 +++ .../config/schema/ckeditor5.schema.yml | 11 ++ core/modules/ckeditor5/js/ckeditor5.es6.js | 4 + core/modules/ckeditor5/js/ckeditor5.js | 4 + .../Plugin/CKEditor5Plugin/ImageResize.php | 58 +++++++ .../src/Plugin/CKEditor5PluginDefinition.php | 90 ++++++++--- .../src/Plugin/CKEditor5PluginManager.php | 4 + .../ckeditor5/src/Plugin/Editor/CKEditor5.php | 21 ++- .../FunctionalJavascript/CKEditor5Test.php | 4 +- .../src/FunctionalJavascript/ImageTest.php | 96 +++++++++++- .../src/Kernel/CKEditor5PluginManagerTest.php | 141 +++++++++++++++++- .../src/Kernel/ConfigurablePluginTest.php | 3 + .../src/Kernel/SmartDefaultSettingsTest.php | 7 + .../tests/src/Kernel/ValidatorsTest.php | 6 +- 15 files changed, 436 insertions(+), 36 deletions(-) create mode 100644 core/modules/ckeditor5/src/Plugin/CKEditor5Plugin/ImageResize.php diff --git a/core/modules/ckeditor5/ckeditor5.api.php b/core/modules/ckeditor5/ckeditor5.api.php index dce79025ebf5..763236299318 100644 --- a/core/modules/ckeditor5/ckeditor5.api.php +++ b/core/modules/ckeditor5/ckeditor5.api.php @@ -134,6 +134,8 @@ * - 'filter': a filter that must be enabled * - 'imageUploadStatus': TRUE if image upload must be enabled, FALSE if it * must not be enabled + * - 'requiresConfiguration': a subset of the configuration for this plugin + * that must match (exactly) * - 'plugins': a list of CKEditor 5 Drupal plugin IDs that must be enabled * * All of these can be defined in YAML or annotations. A given plugin should diff --git a/core/modules/ckeditor5/ckeditor5.ckeditor5.yml b/core/modules/ckeditor5/ckeditor5.ckeditor5.yml index 8a588ee09ea0..894d6c4b49f6 100644 --- a/core/modules/ckeditor5/ckeditor5.ckeditor5.yml +++ b/core/modules/ckeditor5/ckeditor5.ckeditor5.yml @@ -430,6 +430,27 @@ ckeditor5_image: toolbarItem: uploadImage imageUploadStatus: true +ckeditor5_imageResize: + ckeditor5: + plugins: + - image.ImageResize + config: + image: + resizeUnit: '%' + resizeOptions: + - + name: 'resizeImage:original' + value: null + toolbar: [resizeImage] + drupal: + label: Image resize + class: \Drupal\ckeditor5\Plugin\CKEditor5Plugin\ImageResize + elements: false + conditions: + requiresConfiguration: + allow_resize: true + plugins: [ckeditor5_image] + ckeditor5_imageCaption: ckeditor5: plugins: diff --git a/core/modules/ckeditor5/config/schema/ckeditor5.schema.yml b/core/modules/ckeditor5/config/schema/ckeditor5.schema.yml index 29171f9e36d9..09b81657591d 100644 --- a/core/modules/ckeditor5/config/schema/ckeditor5.schema.yml +++ b/core/modules/ckeditor5/config/schema/ckeditor5.schema.yml @@ -62,6 +62,17 @@ ckeditor5.plugin.ckeditor5_heading: Choice: callback: \Drupal\ckeditor5\Plugin\CKEditor5Plugin\Heading::validChoices +# Plugin \Drupal\ckeditor5\Plugin\CKEditor5Plugin\ImageResize +ckeditor5.plugin.ckeditor5_imageResize: + type: mapping + label: Image Resize + mapping: + allow_resize: + type: boolean + label: 'Allow resize' + constraints: + NotNull: [] + ckeditor5.plugin.ckeditor5_sourceEditing: type: mapping label: Source Editing diff --git a/core/modules/ckeditor5/js/ckeditor5.es6.js b/core/modules/ckeditor5/js/ckeditor5.es6.js index e6916bc443e4..3ced334c3ac1 100644 --- a/core/modules/ckeditor5/js/ckeditor5.es6.js +++ b/core/modules/ckeditor5/js/ckeditor5.es6.js @@ -97,6 +97,10 @@ return Object.entries(config).reduce((processed, [key, value]) => { if (typeof value === 'object') { + // Check for null values. + if (!value) { + return processed; + } if (value.hasOwnProperty('func')) { processed[key] = buildFunc(value); } else if (value.hasOwnProperty('regexp')) { diff --git a/core/modules/ckeditor5/js/ckeditor5.js b/core/modules/ckeditor5/js/ckeditor5.js index 2cc8deea9686..6784c686a87c 100644 --- a/core/modules/ckeditor5/js/ckeditor5.js +++ b/core/modules/ckeditor5/js/ckeditor5.js @@ -60,6 +60,10 @@ return Object.entries(config).reduce((processed, [key, value]) => { if (typeof value === 'object') { + if (!value) { + return processed; + } + if (value.hasOwnProperty('func')) { processed[key] = buildFunc(value); } else if (value.hasOwnProperty('regexp')) { diff --git a/core/modules/ckeditor5/src/Plugin/CKEditor5Plugin/ImageResize.php b/core/modules/ckeditor5/src/Plugin/CKEditor5Plugin/ImageResize.php new file mode 100644 index 000000000000..353cb2cbacc3 --- /dev/null +++ b/core/modules/ckeditor5/src/Plugin/CKEditor5Plugin/ImageResize.php @@ -0,0 +1,58 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\ckeditor5\Plugin\CKEditor5Plugin; + +use Drupal\ckeditor5\Plugin\CKEditor5PluginConfigurableTrait; +use Drupal\ckeditor5\Plugin\CKEditor5PluginDefault; +use Drupal\ckeditor5\Plugin\CKEditor5PluginConfigurableInterface; +use Drupal\Core\Form\FormStateInterface; + +/** + * CKEditor 5 ImageResize plugin. + * + * @internal + * Plugin classes are internal. + */ +class ImageResize extends CKEditor5PluginDefault implements CKEditor5PluginConfigurableInterface { + + use CKEditor5PluginConfigurableTrait; + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return ['allow_resize' => TRUE]; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $form['allow_resize'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Allow the user to resize images'), + '#default_value' => $this->configuration['allow_resize'], + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { + // Match the config schema structure at ckeditor5.plugin.ckeditor5_imageResize. + $form_value = $form_state->getValue('allow_resize'); + $form_state->setValue('allow_resize', (bool) $form_value); + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { + $this->configuration['allow_resize'] = $form_state->getValue('allow_resize'); + } + +} diff --git a/core/modules/ckeditor5/src/Plugin/CKEditor5PluginDefinition.php b/core/modules/ckeditor5/src/Plugin/CKEditor5PluginDefinition.php index 871e3bef7444..0222d76f2f09 100644 --- a/core/modules/ckeditor5/src/Plugin/CKEditor5PluginDefinition.php +++ b/core/modules/ckeditor5/src/Plugin/CKEditor5PluginDefinition.php @@ -10,6 +10,7 @@ use Drupal\Component\Plugin\Definition\PluginDefinitionInterface; use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException; use Drupal\Core\Config\Schema\SchemaCheckTrait; +use Drupal\Core\Config\TypedConfigManagerInterface; use Drupal\Core\StringTranslation\TranslatableMarkup; /** @@ -160,33 +161,14 @@ private function validateDrupalAspects(string $id, array $definition): void { $default_configuration = (new \ReflectionClass($definition['drupal']['class'])) ->newInstanceWithoutConstructor() ->defaultConfiguration(); - $typed_config = \Drupal::service('config.typed'); if (!empty($default_configuration)) { $configuration_name = sprintf("ckeditor5.plugin.%s", $definition['id']); - if (!$typed_config->hasConfigSchema($configuration_name)) { + if (!$this->getTypedConfig()->hasConfigSchema($configuration_name)) { throw new InvalidPluginDefinitionException($id, sprintf('The "%s" CKEditor 5 plugin definition is configurable, has non-empty default configuration but has no config schema. Config schema is required for validation.', $id)); } - // TRICKY: SchemaCheckTrait::checkConfigSchema() dynamically adds a - // 'langcode' key-value pair that is irrelevant here. Also, - // ::checkValue() may (counter to its docs) trigger an exception. - $this->configName = 'STRIP'; - $this->schema = $typed_config->createFromNameAndData($configuration_name, $default_configuration); - $schema_errors = []; - foreach ($default_configuration as $key => $value) { - try { - $schema_error = $this->checkValue($key, $value); - } - catch (\InvalidArgumentException $e) { - $schema_error = [$key => $e->getMessage()]; - } - $schema_errors = array_merge($schema_errors, $schema_error); - } - $formatted_schema_errors = []; - foreach ($schema_errors as $key => $value) { - $formatted_schema_errors[] = sprintf("[%s] %s", str_replace('STRIP:', '', $key), trim($value, '.')); - } - if (!empty($schema_errors)) { - throw new InvalidPluginDefinitionException($id, sprintf('The "%s" CKEditor 5 plugin definition is configurable, but its default configuration does not match its config schema. The following errors were found: %s.', $id, implode(', ', $formatted_schema_errors))); + $error_message = $this->validateConfiguration($default_configuration); + if ($error_message) { + throw new InvalidPluginDefinitionException($id, sprintf('The "%s" CKEditor 5 plugin definition is configurable, but its default configuration does not match its config schema. %s', $id, $error_message)); } } } @@ -204,6 +186,16 @@ private function validateDrupalAspects(string $id, array $definition): void { 'filter' => function ($value): ?string { return is_string($value) ? NULL : 'A string corresponding to a filter plugin ID must be specified.'; }, + 'requiresConfiguration' => function ($required_configuration, array $definition): ?string { + if (!is_array($required_configuration)) { + return 'An array structure matching the required configuration for this plugin must be specified.'; + } + if (!in_array(CKEditor5PluginConfigurableInterface::class, class_implements($definition['drupal']['class'], TRUE))) { + return 'This condition type is only available for CKEditor 5 plugins implementing CKEditor5PluginConfigurableInterface.'; + } + $error_message = $this->validateConfiguration($required_configuration); + return is_string($error_message) ? sprintf('The required configuration does not match its config schema. %s', $error_message) : NULL; + }, 'plugins' => function ($value): ?string { return is_array($value) && Inspector::assertAllStrings($value) ? NULL : 'A list of strings, each corresponding to a CKEditor 5 plugin ID must be specified.'; }, @@ -213,7 +205,7 @@ private function validateDrupalAspects(string $id, array $definition): void { throw new InvalidPluginDefinitionException($id, sprintf('The "%s" CKEditor 5 plugin definition has a "drupal.conditions" value that contains some unsupported condition types: "%s". Only the following conditions types are supported: "%s".', $id, implode(', ', $unsupported_condition_types), implode('", "', array_keys($supported_condition_types)))); } foreach ($definition['drupal']['conditions'] as $condition_type => $value) { - $assessment = $supported_condition_types[$condition_type]($value); + $assessment = $supported_condition_types[$condition_type]($value, $definition); if (is_string($assessment)) { throw new InvalidPluginDefinitionException($id, sprintf('The "%s" CKEditor 5 plugin definition has an invalid "drupal.conditions" item. "%s" is set to an invalid value. %s', $id, $condition_type, $assessment)); } @@ -228,6 +220,56 @@ private function validateDrupalAspects(string $id, array $definition): void { } } + /** + * Returns the typed configuration service. + * + * @return \Drupal\Core\Config\TypedConfigManagerInterface + * The typed configuration service. + */ + private function getTypedConfig(): TypedConfigManagerInterface { + return \Drupal::service('config.typed'); + } + + /** + * Validates the given configuration array. + * + * @param array $configuration + * The configuration to validate. + * + * @return string|null + * NULL if there are no validation errors, a string containing the schema + * violation error messages otherwise. + */ + private function validateConfiguration(array $configuration): ?string { + if (!isset($this->schema)) { + $configuration_name = sprintf("ckeditor5.plugin.%s", $this->id); + // TRICKY: SchemaCheckTrait::checkConfigSchema() dynamically adds a + // 'langcode' key-value pair that is irrelevant here. Also, + // ::checkValue() may (counter to its docs) trigger an exception. + $this->configName = 'STRIP'; + $this->schema = $this->getTypedConfig()->createFromNameAndData($configuration_name, $configuration); + } + + $schema_errors = []; + foreach ($configuration as $key => $value) { + try { + $schema_error = $this->checkValue($key, $value); + } + catch (\InvalidArgumentException $e) { + $schema_error = [$key => $e->getMessage()]; + } + $schema_errors = array_merge($schema_errors, $schema_error); + } + $formatted_schema_errors = []; + foreach ($schema_errors as $key => $value) { + $formatted_schema_errors[] = sprintf("[%s] %s", str_replace('STRIP:', '', $key), trim($value, '.')); + } + if (!empty($formatted_schema_errors)) { + return sprintf('The following errors were found: %s.', implode(', ', $formatted_schema_errors)); + } + return NULL; + } + /** * {@inheritdoc} * diff --git a/core/modules/ckeditor5/src/Plugin/CKEditor5PluginManager.php b/core/modules/ckeditor5/src/Plugin/CKEditor5PluginManager.php index 05290ebd6ff3..295699ee420b 100644 --- a/core/modules/ckeditor5/src/Plugin/CKEditor5PluginManager.php +++ b/core/modules/ckeditor5/src/Plugin/CKEditor5PluginManager.php @@ -380,6 +380,10 @@ protected function isPluginDisabled(CKEditor5PluginInterface $plugin, EditorInte } break; + case 'requiresConfiguration': + $intersection = array_intersect($plugin->getConfiguration(), $required_value); + return $intersection !== $required_value; + case 'plugins': // Tricky: this cannot yet be evaluated here. It will evaluated later. // @see \Drupal\ckeditor5\Plugin\CKEditor5PluginManager::getEnabledDefinitions() diff --git a/core/modules/ckeditor5/src/Plugin/Editor/CKEditor5.php b/core/modules/ckeditor5/src/Plugin/Editor/CKEditor5.php index d824ddf429cc..8286c734f477 100644 --- a/core/modules/ckeditor5/src/Plugin/Editor/CKEditor5.php +++ b/core/modules/ckeditor5/src/Plugin/Editor/CKEditor5.php @@ -384,12 +384,19 @@ private function shouldHaveVisiblePluginSettingsForm(CKEditor5PluginDefinition $ // due to isEnabled() returning false, that should still have its config // form provided: // 1 - A conditionally enabled plugin that does not depend on a toolbar item - // to be active. + // to be active AND the plugins it depends on are enabled. // 2 - A conditionally enabled plugin that does depend on a toolbar item, // and that toolbar item is active. if ($definition->hasConditions()) { $conditions = $definition->getConditions(); if (!array_key_exists('toolbarItem', $conditions)) { + // The CKEditor 5 plugins this plugin depends on must be enabled. + if (array_key_exists('plugins', $conditions)) { + $all_plugins = $this->ckeditor5PluginManager->getDefinitions(); + $dependencies = array_intersect_key($all_plugins, array_flip($conditions['plugins'])); + $unmet_dependencies = array_diff_key($dependencies, $enabled_plugins); + return empty($unmet_dependencies); + } return TRUE; } elseif (in_array($conditions['toolbarItem'], $editor->getSettings()['toolbar']['items'], TRUE)) { @@ -681,11 +688,19 @@ protected function getEventualEditorWithPrimedFilterFormat(SubformStateInterface $pair = static::createEphemeralPairedEditor($submitted_editor, $submitted_filter_format); // When CKEditor 5 plugins are disabled in the form-based admin UI, the - // associated settings (if any) should be omitted too. + // associated settings (if any) should be omitted too, except for plugins + // that are enabled using `requiresConfiguration` (because whether they are + // enabled or not depends on the associated settings). $original_settings = $pair->getSettings(); $enabled_plugins = $this->ckeditor5PluginManager->getEnabledDefinitions($pair); + $config_enabled_plugins = []; + foreach ($this->ckeditor5PluginManager->getDefinitions() as $id => $definition) { + if ($definition->hasConditions() && isset($definition->getConditions()['requiresConfiguration'])) { + $config_enabled_plugins[$id] = TRUE; + } + } $updated_settings = [ - 'plugins' => array_intersect_key($original_settings['plugins'], $enabled_plugins), + 'plugins' => array_intersect_key($original_settings['plugins'], $enabled_plugins + $config_enabled_plugins), ] + $original_settings; $pair->setSettings($updated_settings); diff --git a/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5Test.php b/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5Test.php index f68274ddc67b..ea7183764ecc 100644 --- a/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5Test.php +++ b/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5Test.php @@ -85,7 +85,7 @@ public function testAttributeEncoding() { 'toolbar' => [ 'items' => ['uploadImage'], ], - 'plugins' => [], + 'plugins' => ['ckeditor5_imageResize' => ['allow_resize' => FALSE]], ], 'image_upload' => [ 'status' => TRUE, @@ -109,7 +109,7 @@ function (ConstraintViolation $v) { $this->assertNotEmpty($image_upload_field = $page->find('css', '.ck-file-dialog-button input[type="file"]')); $image = $this->getTestFiles('image')[0]; $image_upload_field->attachFile($this->container->get('file_system')->realpath($image->uri)); - $assert_session->assertWaitOnAjaxRequest(); + $assert_session->waitForElementVisible('css', '.ck-widget.image'); $this->click('.ck-widget.image'); $balloon_panel = $page->find('css', '.ck-balloon-panel'); diff --git a/core/modules/ckeditor5/tests/src/FunctionalJavascript/ImageTest.php b/core/modules/ckeditor5/tests/src/FunctionalJavascript/ImageTest.php index 3865884b873a..deb8733b23ae 100644 --- a/core/modules/ckeditor5/tests/src/FunctionalJavascript/ImageTest.php +++ b/core/modules/ckeditor5/tests/src/FunctionalJavascript/ImageTest.php @@ -5,18 +5,19 @@ use Drupal\editor\Entity\Editor; use Drupal\file\Entity\File; use Drupal\filter\Entity\FilterFormat; -use Drupal\FunctionalJavascriptTests\WebDriverTestBase; use Drupal\Tests\TestFileCreationTrait; use Drupal\Tests\ckeditor5\Traits\CKEditor5TestTrait; use Drupal\ckeditor5\Plugin\Editor\CKEditor5; use Symfony\Component\Validator\ConstraintViolation; +// cspell:ignore imageresize imageupload + /** * @coversDefaultClass \Drupal\ckeditor5\Plugin\CKEditor5Plugin\ImageUpload * @group ckeditor5 * @internal */ -class ImageTest extends WebDriverTestBase { +class ImageTest extends CKEditor5TestBase { use CKEditor5TestTrait; use TestFileCreationTrait; @@ -91,6 +92,9 @@ protected function setUp(): void { 'ckeditor5_sourceEditing' => [ 'allowed_tags' => [], ], + 'ckeditor5_imageResize' => [ + 'allow_resize' => TRUE, + ], ], ], 'image_upload' => [ @@ -113,6 +117,7 @@ function (ConstraintViolation $v) { $this->adminUser = $this->drupalCreateUser([ 'use text format test_format', 'bypass node access', + 'administer filters', ]); // Create a sample host entity to embed images in. @@ -120,9 +125,8 @@ function (ConstraintViolation $v) { 'uri' => $this->getTestFiles('image')[0]->uri, ]); $this->file->save(); - $this->drupalCreateContentType(['type' => 'blog']); $this->host = $this->createNode([ - 'type' => 'blog', + 'type' => 'page', 'title' => 'Animals with strange names', 'body' => [ 'value' => '<p>The pirate is irate.</p>', @@ -327,7 +331,6 @@ public function testWidth(string $width): void { $this->assertNotEmpty($image_upload_field = $page->find('css', '.ck-file-dialog-button input[type="file"]')); $image = $this->getTestFiles('image')[0]; $image_upload_field->attachFile($this->container->get('file_system')->realpath($image->uri)); - $assert_session->assertWaitOnAjaxRequest(); $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'figure.image')); // Edit the source of the image through the UI. @@ -370,4 +373,87 @@ public function providerWidth(): array { ]; } + /** + * Tests the image resize plugin. + * + * Confirms that enabling the resize plugin introduces the resize class to + * images within CKEditor 5. + * + * @param bool $is_resize_enabled + * Boolean flag to test enabled or disabled. + * + * @dataProvider providerResize + */ + public function testResize(bool $is_resize_enabled): void { + // Disable resize plugin because it is enabled by default. + if (!$is_resize_enabled) { + Editor::load('test_format')->setSettings([ + 'toolbar' => [ + 'items' => [ + 'uploadImage', + ], + ], + 'plugins' => [ + 'ckeditor5_imageResize' => [ + 'allow_resize' => FALSE, + ], + ], + ])->save(); + } + + $page = $this->getSession()->getPage(); + $assert_session = $this->assertSession(); + $this->drupalGet('node/add'); + $page->fillField('title[0][value]', 'My test content'); + $this->assertNotEmpty($image_upload_field = $page->find('css', '.ck-file-dialog-button input[type="file"]')); + $image = $this->getTestFiles('image')[0]; + $image_upload_field->attachFile($this->container->get('file_system')->realpath($image->uri)); + $image_figure = $assert_session->waitForElementVisible('css', 'figure'); + $this->assertSame($is_resize_enabled, $image_figure->hasClass('ck-widget_with-resizer')); + } + + /** + * Data provider for ::testResize(). + * + * @return array + * The test cases. + */ + public function providerResize(): array { + return [ + 'Image resize is enabled' => [ + 'is_resize_enabled' => TRUE, + ], + 'Image resize is disabled' => [ + 'is_resize_enabled' => FALSE, + ], + ]; + } + + /** + * Tests the ckeditor5_imageResize and ckeditor5_imageUpload settings forms. + */ + public function testImageSettingsForm() { + $assert_session = $this->assertSession(); + + $this->drupalGet('admin/config/content/formats/manage/test_format'); + + // The image resize and upload plugin settings forms should be present. + $assert_session->elementExists('css', '[data-drupal-selector="edit-editor-settings-plugins-ckeditor5-imageresize"]'); + $assert_session->elementExists('css', '[data-drupal-selector="edit-editor-settings-plugins-ckeditor5-imageupload"]'); + + // Removing the imageUpload button from the toolbar must remove the plugin + // settings forms too. + $this->triggerKeyUp('.ckeditor5-toolbar-item-uploadImage', 'ArrowUp'); + $assert_session->assertWaitOnAjaxRequest(); + $assert_session->elementNotExists('css', '[data-drupal-selector="edit-editor-settings-plugins-ckeditor5-imageresize"]'); + $assert_session->elementNotExists('css', '[data-drupal-selector="edit-editor-settings-plugins-ckeditor5-imageupload"]'); + + // Re-adding the imageUpload button to the toolbar must re-add the plugin + // settings forms too. + $this->triggerKeyUp('.ckeditor5-toolbar-item-uploadImage', 'ArrowDown'); + $assert_session->assertWaitOnAjaxRequest(); + $assert_session->elementExists('css', '[data-drupal-selector="edit-editor-settings-plugins-ckeditor5-imageresize"]'); + $assert_session->elementExists('css', '[data-drupal-selector="edit-editor-settings-plugins-ckeditor5-imageupload"]'); + } + } diff --git a/core/modules/ckeditor5/tests/src/Kernel/CKEditor5PluginManagerTest.php b/core/modules/ckeditor5/tests/src/Kernel/CKEditor5PluginManagerTest.php index 9c3b0d45dfff..e912ab75e480 100644 --- a/core/modules/ckeditor5/tests/src/Kernel/CKEditor5PluginManagerTest.php +++ b/core/modules/ckeditor5/tests/src/Kernel/CKEditor5PluginManagerTest.php @@ -352,7 +352,7 @@ public function providerTestInvalidPluginDefinitions(): \Generator { conditions: foo: bar YAML, - 'The "ckeditor5_invalid_plugin_foo_bar" CKEditor 5 plugin definition has a "drupal.conditions" value that contains some unsupported condition types: "foo". Only the following conditions types are supported: "toolbarItem", "imageUploadStatus", "filter", "plugins".', + 'The "ckeditor5_invalid_plugin_foo_bar" CKEditor 5 plugin definition has a "drupal.conditions" value that contains some unsupported condition types: "foo". Only the following conditions types are supported: "toolbarItem", "imageUploadStatus", "filter", "requiresConfiguration", "plugins".', ]; yield 'invalid condition: toolbarItem' => [ <<<YAML @@ -833,6 +833,139 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {} public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {} } +PHP, + ], + ], + ], + ], + ]; + + yield 'invalid condition: requiresConfiguration not specifying a configuration array' => [ + <<<YAML +ckeditor5_invalid_plugin_foo_bar: + ckeditor5: + plugins: {} + drupal: + label: "Foo bar" + elements: false + conditions: + requiresConfiguration: true +YAML, + 'The "ckeditor5_invalid_plugin_foo_bar" CKEditor 5 plugin definition has an invalid "drupal.conditions" item. "requiresConfiguration" is set to an invalid value. An array structure matching the required configuration for this plugin must be specified.', + ]; + + yield 'invalid condition: requiresConfiguration without configurable plugin' => [ + <<<YAML +ckeditor5_invalid_plugin_foo_bar: + ckeditor5: + plugins: {} + drupal: + label: "Foo bar" + elements: false + conditions: + requiresConfiguration: + allow_resize: true +YAML, + 'The "ckeditor5_invalid_plugin_foo_bar" CKEditor 5 plugin definition has an invalid "drupal.conditions" item. "requiresConfiguration" is set to an invalid value. This condition type is only available for CKEditor 5 plugins implementing CKEditor5PluginConfigurableInterface.', + ]; + + yield 'invalid condition: requiresConfiguration with configurable plugin but required configuration does not match config schema' => [ + <<<YAML +ckeditor5_invalid_plugin_foo_bar: + ckeditor5: + plugins: {} + drupal: + class: Drupal\ckeditor5_invalid_plugin\Plugin\CKEditor5Plugin\FooBar + label: "Foo bar" + elements: false + conditions: + requiresConfiguration: + allow_resize: true +YAML, + 'The "ckeditor5_invalid_plugin_foo_bar" CKEditor 5 plugin definition has an invalid "drupal.conditions" item. "requiresConfiguration" is set to an invalid value. The required configuration does not match its config schema. The following errors were found: [allow_resize] The configuration property allow_resize doesn\'t exist.', + [ + 'config' => [ + 'schema' => [ + 'ckeditor5_invalid_plugin.schema.yml' => <<<YAML +ckeditor5.plugin.ckeditor5_invalid_plugin_foo_bar: + type: mapping + label: 'Foo Bar' + mapping: + foo: + type: boolean + label: 'Foo' +YAML, + ], + ], + 'src' => [ + 'Plugin' => [ + 'CKEditor5Plugin' => [ + 'FooBar.php' => <<<'PHP' +<?php +namespace Drupal\ckeditor5_invalid_plugin\Plugin\CKEditor5Plugin; +use Drupal\ckeditor5\Plugin\CKEditor5PluginDefault; +use Drupal\ckeditor5\Plugin\CKEditor5PluginConfigurableInterface; +use Drupal\ckeditor5\Plugin\CKEditor5PluginConfigurableTrait; +use Drupal\Core\Form\FormStateInterface; +class FooBar extends CKEditor5PluginDefault implements CKEditor5PluginConfigurableInterface { + use CKEditor5PluginConfigurableTrait; + public function defaultConfiguration() { return ['foo' => FALSE]; } + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { return []; } + public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {} + public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {} +} +PHP, + ], + ], + ], + ], + ]; + + yield 'valid condition: requiresConfiguration' => [ + <<<YAML +ckeditor5_invalid_plugin_foo_bar: + ckeditor5: + plugins: {} + drupal: + class: Drupal\ckeditor5_invalid_plugin\Plugin\CKEditor5Plugin\FooBar + label: "Foo bar" + elements: false + conditions: + requiresConfiguration: + foo: true +YAML, + NULL, + [ + 'config' => [ + 'schema' => [ + 'ckeditor5_invalid_plugin.schema.yml' => <<<YAML +ckeditor5.plugin.ckeditor5_invalid_plugin_foo_bar: + type: mapping + label: 'Foo Bar' + mapping: + foo: + type: boolean + label: 'Foo' +YAML, + ], + ], + 'src' => [ + 'Plugin' => [ + 'CKEditor5Plugin' => [ + 'FooBar.php' => <<<'PHP' +<?php +namespace Drupal\ckeditor5_invalid_plugin\Plugin\CKEditor5Plugin; +use Drupal\ckeditor5\Plugin\CKEditor5PluginDefault; +use Drupal\ckeditor5\Plugin\CKEditor5PluginConfigurableInterface; +use Drupal\ckeditor5\Plugin\CKEditor5PluginConfigurableTrait; +use Drupal\Core\Form\FormStateInterface; +class FooBar extends CKEditor5PluginDefault implements CKEditor5PluginConfigurableInterface { + use CKEditor5PluginConfigurableTrait; + public function defaultConfiguration() { return ['foo' => FALSE]; } + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { return []; } + public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {} + public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {} +} PHP, ], ], @@ -1060,6 +1193,12 @@ public function providerTestProvidedElements(): array { 'expected_elements' => [], 'expected_readable_string' => '', ], + 'imageResize' => [ + 'plugins' => ['ckeditor5_imageResize'], + 'text_editor_settings' => [], + 'expected_elements' => [], + 'expected_readable_string' => '', + ], 'language' => [ 'plugins' => ['ckeditor5_language'], 'text_editor_settings' => [], diff --git a/core/modules/ckeditor5/tests/src/Kernel/ConfigurablePluginTest.php b/core/modules/ckeditor5/tests/src/Kernel/ConfigurablePluginTest.php index 9a598b165804..d444f726b500 100644 --- a/core/modules/ckeditor5/tests/src/Kernel/ConfigurablePluginTest.php +++ b/core/modules/ckeditor5/tests/src/Kernel/ConfigurablePluginTest.php @@ -67,6 +67,9 @@ public function testDefaults() { 'ckeditor5_sourceEditing' => [ 'allowed_tags' => [], ], + 'ckeditor5_imageResize' => [ + 'allow_resize' => TRUE, + ], 'ckeditor5_language' => [ 'language_list' => 'un', ], diff --git a/core/modules/ckeditor5/tests/src/Kernel/SmartDefaultSettingsTest.php b/core/modules/ckeditor5/tests/src/Kernel/SmartDefaultSettingsTest.php index a2ff35e0efc9..0f6eb4a625c9 100644 --- a/core/modules/ckeditor5/tests/src/Kernel/SmartDefaultSettingsTest.php +++ b/core/modules/ckeditor5/tests/src/Kernel/SmartDefaultSettingsTest.php @@ -399,6 +399,9 @@ public function provider() { 'heading6', ], ], + 'ckeditor5_imageResize' => [ + 'allow_resize' => TRUE, + ], 'ckeditor5_language' => [ 'language_list' => 'un', ], @@ -480,6 +483,7 @@ public function provider() { 'heading5', ], ], + 'ckeditor5_imageResize' => ['allow_resize' => TRUE], 'ckeditor5_language' => $basic_html_test_case['expected_ckeditor5_settings']['plugins']['ckeditor5_language'], ], ], @@ -677,6 +681,9 @@ public function provider() { 'heading6', ], ], + 'ckeditor5_imageResize' => [ + 'allow_resize' => TRUE, + ], 'ckeditor5_sourceEditing' => [ 'allowed_tags' => [], ], diff --git a/core/modules/ckeditor5/tests/src/Kernel/ValidatorsTest.php b/core/modules/ckeditor5/tests/src/Kernel/ValidatorsTest.php index 48093d6d7701..b879e408e682 100644 --- a/core/modules/ckeditor5/tests/src/Kernel/ValidatorsTest.php +++ b/core/modules/ckeditor5/tests/src/Kernel/ValidatorsTest.php @@ -755,7 +755,11 @@ public function providerPair(): array { 'uploadImage', ], ], - 'plugins' => [], + 'plugins' => [ + 'ckeditor5_imageResize' => [ + 'allow_resize' => FALSE, + ], + ], ], 'image_upload' => [ 'status' => TRUE, -- GitLab