Skip to content
Snippets Groups Projects
Verified Commit b00d317e authored by Alex Pott's avatar Alex Pott
Browse files

Issue #3399036 by godotislate, quietone, Wim Leers, kim.pepper, Keshav Patel,...

Issue #3399036 by godotislate, quietone, Wim Leers, kim.pepper, Keshav Patel, smustgrave, alexpott, larowlan: CKEditor5PluginManager: use PHP attributes instead of doctrine annotations

(cherry picked from commit db518452)
parent bd2add4c
Branches
Tags
31 merge requests!13092Issue #3498963 by benjifisher, heddn: Add lookup_migrations configuration to...,!12802Issue #3537193 by opauwlo: Add enable absolute path option for CKEditor5 image uploads,!12745Fixed: Path alias language doesn't changes on changing of node language,!12684Issue #3220784,!12537Add ViewsConfigUpdater deprecation support for default_argument_skip_url,!12523Issue #3493858 by vidorado, xavier.masson, smustgrave: Extend ViewsBlockBase...,!122353526426-warning-for-missing,!12212Issue #3445525 by alexpott, japerry, catch, mglaman, longwave: Add BC layer...,!11958Issue #3490507 by alexpott, smustgrave: Fix bogus mocking in...,!11769Issue #3517987: Add option to contextual filters to encode slashes in query parameter.,!11185Issue #3477324 by andypost, alexpott: Fix usage of str_getcsv() and fgetcsv() for PHP 8.4,!10602Issue #3438769 by vinmayiswamy, antonnavi, michelle, amateescu: Sub workspace does not clear,!10301Issue #3469309 by mstrelan, smustgrave, moshe weitzman: Use one-time login...,!10187Issue #3487488 by dakwamine: ExtensionMimeTypeGuesser::guessMimeType must support file names with "0" (zero) like foo.0.zip,!9944Issue #3483353: Consider making the createCopy config action optionally fail...,!9929Issue #3445469 by pooja_sharma, smustgrave: Add additional test coverage for...,!9787Resolve issue 3479427 - bootstrap barrio issue under Windows,!9742Issue #3463908 by catch, quietone: Split OptionsFieldUiTest into two,!9526Issue #3458177 by mondrake, catch, quietone, godotislate, longwave, larowlan,...,!8738Issue #3424162 by camilledavis, dineshkumarbollu, smustgrave: Claro...,!8704Make greek characters available in ckeditor5,!8597Draft: Issue #3442259 by catch, quietone, dww: Reduce time of Migrate Upgrade tests...,!8533Issue #3446962 by kim.pepper: Remove incorrectly added...,!8517Issue #3443748 by NexusNovaz, smustgrave: Testcase creates false positive,!8325Update file Sort.php,!8095Expose document root on install,!7930Resolve #3427374 "Taxonomytid viewsargumentdefault plugin",!7445Issue #3440169: When using drupalGet(), provide an associative array for $headers,!6502Draft: Resolve #2938524 "Plach testing issue",!38582585169-10.1.x,!3226Issue #2987537: Custom menu link entity type should not declare "bundle" entity key
Pipeline #159558 passed with warnings
Pipeline: drupal

#159589

    Pipeline: drupal

    #159582

      Pipeline: drupal

      #159569

        +1
        ......@@ -44,8 +44,8 @@
        * @see https://ckeditor.com/docs/ckeditor5/latest/framework/guides/architecture/editing-engine.html#element-types-and-custom-data
        *
        * @section plugins CKEditor 5 Plugins
        * CKEditor 5 plugins may use either YAML or a PHP annotation for their
        * definitions. A PHP class does not need an annotation if it is defined in yml.
        * CKEditor 5 plugins may use either YAML or a PHP attribute for their
        * definitions. A PHP class does not need an attribute if it is defined in yml.
        *
        * To be discovered, YAML definition files must be named
        * {module_name}.ckeditor5.yml.
        ......@@ -71,22 +71,24 @@
        * - <marquee behavior>
        * @endcode
        *
        * Declared as an Annotation:
        * Declared as an Attribute:
        * @code
        * # In a scr/Plugin/CKEditor5Plugin/Marquee.php file.
        * /**
        * * @CKEditor5Plugin(
        * * id = "MODULE_NAME_marquee",
        * * ckeditor5 = @CKEditor5AspectsOfCKEditor5Plugin(
        * * plugins = { "PACKAGE.CLASS" },
        * * ),
        * * drupal = @DrupalAspectsOfCKEditor5Plugin(
        * * label = @Translation("Marquee"),
        * * library = "MODULE_NAME/ckeditor5.marquee"
        * * elements = { "<marquee>", "<marquee behavior>" },
        * * )
        * * )
        * * /
        * use Drupal\ckeditor5\Attribute\CKEditor5AspectsOfCKEditor5Plugin;
        * use Drupal\ckeditor5\Attribute\CKEditor5Plugin;
        * use Drupal\ckeditor5\Attribute\DrupalAspectsOfCKEditor5Plugin;
        * use Drupal\Core\StringTranslation\TranslatableMarkup;
        *
        * #[CKEditor5Plugin(
        * id: 'MODULE_NAME_marquee',
        * ckeditor5: new CKEditor5AspectsOfCKEditor5Plugin(
        * plugins: ['PACKAGE.CLASS'],
        * ),
        * drupal: new DrupalAspectsOfCKEditor5Plugin(
        * label: new TranslatableMarkup('Marquee'),
        * library: 'MODULE_NAME/ckeditor5.marquee',
        * elements: ['<marquee>', '<marquee behavior>'],
        * ),
        * )]
        * @endcode
        *
        * The metadata relating strictly to the CKEditor 5 plugin's JS code is stored
        ......@@ -95,7 +97,7 @@
        * If the plugin has a dependency on another module, adding the 'provider' key
        * will prevent the plugin from being loaded if that module is not installed.
        *
        * All of these can be defined in YAML or annotations. A given plugin should
        * All of these can be defined in YAML or attributes. A given plugin should
        * choose one or the other, as a definition can't parse both at once.
        *
        * Overview of all available plugin definition properties:
        ......@@ -158,7 +160,7 @@
        * need arises, see
        * https://www.drupal.org/docs/drupal-apis/ckeditor-5-api/overview#conditions.
        *
        * All of these can be defined in YAML or annotations. A given plugin should
        * All of these can be defined in YAML or attributes. A given plugin should
        * choose one or the other, as a definition can't parse both at once.
        *
        * If the CKEditor 5 plugin contains translation they can be automatically
        ......@@ -181,9 +183,9 @@
        * assets/ckeditor5/marquee/translations/* in this example.
        *
        *
        * @see \Drupal\ckeditor5\Annotation\CKEditor5Plugin
        * @see \Drupal\ckeditor5\Annotation\CKEditor5AspectsOfCKEditor5Plugin
        * @see \Drupal\ckeditor5\Annotation\DrupalAspectsOfCKEditor5Plugin
        * @see \Drupal\ckeditor5\Attribute\CKEditor5Plugin
        * @see \Drupal\ckeditor5\Attribute\CKEditor5AspectsOfCKEditor5Plugin
        * @see \Drupal\ckeditor5\Attribute\DrupalAspectsOfCKEditor5Plugin
        *
        * @section upgrade_path Upgrade path
        *
        ......@@ -204,7 +206,7 @@
        * @section public_api Public API
        *
        * The CKEditor 5 module provides no public API, other than:
        * - the annotations and interfaces mentioned above;
        * - the attributes and interfaces mentioned above;
        * - to help implement CKEditor 5 plugins:
        * \Drupal\ckeditor5\Plugin\CKEditor5PluginConfigurableTrait and
        * \Drupal\ckeditor5\Plugin\CKEditor5PluginDefault;
        ......
        <?php
        declare(strict_types = 1);
        namespace Drupal\ckeditor5\Attribute;
        use Drupal\Component\Plugin\Attribute\Plugin;
        #[\Attribute(\Attribute::TARGET_CLASS)]
        class CKEditor5AspectsOfCKEditor5Plugin extends Plugin {
        /**
        * Constructs a CKEditor5AspectsOfCKEditor5Plugin attribute.
        *
        * @param class-string[] $plugins
        * The CKEditor 5 plugin classes provided. Found in the CKEditor5 global js
        * object as {package.Class}.
        * @param array $config
        * (optional) A keyed array of additional values for the CKEditor 5
        * configuration.
        */
        public function __construct(
        public readonly array $plugins,
        public readonly array $config = [],
        ) {}
        /**
        * {@inheritdoc}
        */
        public function get(): array|object {
        return [
        'plugins' => $this->plugins,
        'config' => $this->config,
        ];
        }
        }
        <?php
        declare(strict_types = 1);
        namespace Drupal\ckeditor5\Attribute;
        use Drupal\ckeditor5\Plugin\CKEditor5PluginDefinition;
        use Drupal\Component\Plugin\Attribute\Plugin;
        /**
        * The CKEditor5Plugin attribute.
        */
        #[\Attribute(\Attribute::TARGET_CLASS)]
        class CKEditor5Plugin extends Plugin {
        /**
        * The CKEditor 5 aspects of the plugin definition.
        *
        * @var \Drupal\ckeditor5\Attribute\CKEditor5AspectsOfCKEditor5Plugin|null
        */
        public readonly ?CKEditor5AspectsOfCKEditor5Plugin $ckeditor5;
        /**
        * The Drupal aspects of the plugin definition.
        *
        * @var \Drupal\ckeditor5\Attribute\DrupalAspectsOfCKEditor5Plugin|null
        */
        public readonly ?DrupalAspectsOfCKEditor5Plugin $drupal;
        /**
        * Constructs a CKEditor5Plugin attribute.
        *
        * Overridden for compatibility with the AttributeBridgeDecorator, which
        * ensures YAML-defined CKEditor 5 plugin definitions are also processed by
        * attributes. Unfortunately it does not (yet) support nested attributes.
        * Force YAML-defined plugin definitions to be parsed by the attributes, to
        * ensure consistent handling of defaults.
        *
        * @see \Drupal\Component\Plugin\Discovery\AttributeBridgeDecorator::getDefinitions()
        *
        * @param string $id
        * The plugin ID.
        * @param array|\Drupal\ckeditor5\Attribute\CKEditor5AspectsOfCKEditor5Plugin|null $ckeditor5
        * (optional) The CKEditor 5 aspects of the plugin definition. Required
        * unless set by deriver.
        * @param array|\Drupal\ckeditor5\Attribute\DrupalAspectsOfCKEditor5Plugin|null $drupal
        * (optional) The Drupal aspects of the plugin definition. Required unless
        * set by deriver.
        * @param class-string|null $deriver
        * (optional) The deriver class.
        */
        public function __construct(
        public readonly string $id,
        array|CKEditor5AspectsOfCKEditor5Plugin|null $ckeditor5 = NULL,
        array|DrupalAspectsOfCKEditor5Plugin|null $drupal = NULL,
        public readonly ?string $deriver = NULL,
        ) {
        $this->ckeditor5 = is_array($ckeditor5) ? new CKEditor5AspectsOfCKEditor5Plugin(...$ckeditor5) : $ckeditor5;
        $this->drupal = is_array($drupal) ? new DrupalAspectsOfCKEditor5Plugin(...$drupal) : $drupal;
        }
        /**
        * {@inheritdoc}
        */
        public function getClass(): string {
        return $this->drupal?->getClass() ?? '';
        }
        /**
        * {@inheritdoc}
        */
        public function setClass($class): void {
        $this->drupal?->setClass($class);
        }
        /**
        * {@inheritdoc}
        */
        public function get(): CKEditor5PluginDefinition {
        return new CKEditor5PluginDefinition([
        'id' => $this->id,
        'ckeditor5' => $this->ckeditor5?->get(),
        'drupal' => $this->drupal?->get(),
        'provider' => $this->getProvider(),
        ]);
        }
        }
        <?php
        declare(strict_types = 1);
        namespace Drupal\ckeditor5\Attribute;
        use Drupal\ckeditor5\Plugin\CKEditor5PluginDefault;
        use Drupal\Component\Plugin\Attribute\Plugin;
        use Drupal\Core\StringTranslation\TranslatableMarkup;
        #[\Attribute(\Attribute::TARGET_CLASS)]
        class DrupalAspectsOfCKEditor5Plugin extends Plugin {
        /**
        * Constructs a DrupalAspectsOfCKEditor5Plugin attribute.
        *
        * @param string|\Drupal\Core\StringTranslation\TranslatableMarkup|null $label
        * (optional) The human-readable name of the CKEditor plugin. Required
        * unless set by deriver.
        * @param class-string $class
        * (optional) The CKEditor 5 plugin class.If not specified, the
        * CKEditor5PluginDefault class is used.
        * @param string|false $library
        * (optional) The library this plugin requires.
        * @param string|false $admin_library
        * (optional) The admin library this plugin provides.
        * @param string[]|false|null $elements
        * (optional) List of elements and attributes provided. An array of strings,
        * or false if no elements are provided. Required unless set by deriver.
        * Syntax for each array value:
        * - <element> only allows that HTML element with no attributes
        * - <element attrA attrB> only allows that HTML element with attributes
        * attrA and attrB, and any value for those attributes.
        * - <element attrA="foo bar baz" attrB="qux-*"> only allows that HTML
        * element with attributes attrA (if attrA contains one of the three
        * listed values) and attrB (if its value has the provided prefix).
        * - <element data-*> only allows that HTML element with any attribute that
        * has the given prefix.
        * Note that <element> means such an element (tag) can be created, whereas
        * <element attrA attrB> means that `attrA` and `attrB` can be created on
        * the tag. If a plugin supports both creating the element as well as
        * setting some attributes or attribute values on it, it should have
        * distinct entries in the list.
        * For example, for a link plugin: `<a>` and `<a href>`. The first indicates
        * the plugin can create such tags, the second indicates it can set the
        * `href` attribute on it. If the first were omitted, the Drupal CKEditor 5
        * module would interpret that as "this plugin cannot create `<a>`, it can
        * only set the `href` attribute on it".
        * @param array $toolbar_items
        * (optional) List of toolbar items the plugin provides.
        * @param array|false $conditions
        * (optional) List of conditions to enable this plugin.
        * @param class-string|null $deriver
        * (optional) The deriver class.
        *
        * @see \Drupal\ckeditor5\Plugin\CKEditor5PluginDefinition::getCreatableElements()
        */
        public function __construct(
        public readonly string|TranslatableMarkup|null $label = NULL,
        public string $class = CKEditor5PluginDefault::class,
        public readonly string|false $library = FALSE,
        public readonly string|false $admin_library = FALSE,
        public readonly array|false|null $elements = NULL,
        public readonly array $toolbar_items = [],
        public readonly array|false $conditions = FALSE,
        public readonly ?string $deriver = NULL,
        ) {}
        }
        ......@@ -4,16 +4,16 @@
        namespace Drupal\ckeditor5\Plugin;
        use Drupal\ckeditor5\Annotation\CKEditor5Plugin;
        use Drupal\ckeditor5\Attribute\CKEditor5Plugin;
        use Drupal\ckeditor5\HTMLRestrictions;
        use Drupal\Component\Annotation\Plugin\Discovery\AnnotationBridgeDecorator;
        use Drupal\Component\Assertion\Inspector;
        use Drupal\Component\Plugin\Discovery\AttributeBridgeDecorator;
        use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
        use Drupal\Component\Utility\NestedArray;
        use Drupal\Core\Cache\CacheBackendInterface;
        use Drupal\Core\Extension\ModuleHandlerInterface;
        use Drupal\Core\Plugin\DefaultPluginManager;
        use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
        use Drupal\Core\Plugin\Discovery\AttributeDiscoveryWithAnnotations;
        use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator;
        use Drupal\Core\Plugin\Discovery\YamlDiscoveryDecorator;
        use Drupal\editor\EditorInterface;
        ......@@ -46,7 +46,14 @@ class CKEditor5PluginManager extends DefaultPluginManager implements CKEditor5Pl
        * The module handler to invoke the alter hook with.
        */
        public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
        parent::__construct('Plugin/CKEditor5Plugin', $namespaces, $module_handler, CKEditor5PluginInterface::class, CKEditor5Plugin::class);
        parent::__construct(
        'Plugin/CKEditor5Plugin',
        $namespaces,
        $module_handler,
        CKEditor5PluginInterface::class,
        CKEditor5Plugin::class,
        '\Drupal\ckeditor5\Annotation\CKEditor5Plugin',
        );
        $this->alterInfo('ckeditor5_plugin_info');
        $this->setCacheBackend($cache_backend, 'ckeditor5_plugins');
        ......@@ -57,12 +64,12 @@ public function __construct(\Traversable $namespaces, CacheBackendInterface $cac
        */
        protected function getDiscovery() {
        if (!$this->discovery) {
        $discovery = new AnnotatedClassDiscovery($this->subdir, $this->namespaces, $this->pluginDefinitionAnnotationName, $this->additionalAnnotationNamespaces);
        $discovery = new AttributeDiscoveryWithAnnotations($this->subdir, $this->namespaces, $this->pluginDefinitionAttributeName, $this->pluginDefinitionAnnotationName, $this->additionalAnnotationNamespaces);
        $discovery = new YamlDiscoveryDecorator($discovery, 'ckeditor5', $this->moduleHandler->getModuleDirectories());
        // Note: adding translatable properties here is impossible because it only
        // supports top-level properties.
        // @see \Drupal\ckeditor5\Plugin\CKEditor5PluginDefinition::label()
        $discovery = new AnnotationBridgeDecorator($discovery, $this->pluginDefinitionAnnotationName);
        $discovery = new AttributeBridgeDecorator($discovery, $this->pluginDefinitionAttributeName);
        $discovery = new ContainerDerivativeDiscoveryDecorator($discovery);
        $this->discovery = $discovery;
        }
        ......
        ......@@ -189,9 +189,11 @@ private function mockModuleInVfs(string $module_name, string $yaml, array $addit
        * @covers \Drupal\ckeditor5\Plugin\CKEditor5PluginManager::processDefinition
        * @dataProvider providerTestInvalidPluginDefinitions
        */
        public function testInvalidPluginDefinitions(string $yaml, ?string $expected_message, array $additional_files = []): void {
        public function testInvalidPluginDefinitions(string $yaml, ?string $expected_exception = NULL, ?string $expected_message = NULL, ?array $additional_files = []): void {
        if ($expected_exception) {
        $this->expectException($expected_exception);
        }
        if ($expected_message) {
        $this->expectException(InvalidPluginDefinitionException::class);
        $this->expectExceptionMessage($expected_message);
        }
        $container = $this->mockModuleInVfs('ckeditor5_invalid_plugin', $yaml, $additional_files);
        ......@@ -214,6 +216,7 @@ public static function providerTestInvalidPluginDefinitions(): \Generator {
        label: TEST
        elements: false
        YAML,
        InvalidPluginDefinitionException::class,
        'The "foo_bar" CKEditor 5 plugin definition must have a plugin ID that starts with "ckeditor5_invalid_plugin_".',
        ];
        ......@@ -224,6 +227,7 @@ public static function providerTestInvalidPluginDefinitions(): \Generator {
        <<<YAML
        foo_bar: {}
        YAML,
        InvalidPluginDefinitionException::class,
        'The "foo_bar" CKEditor 5 plugin definition must have a plugin ID that starts with "ckeditor5_invalid_plugin_".',
        ];
        ......@@ -231,6 +235,7 @@ public static function providerTestInvalidPluginDefinitions(): \Generator {
        <<<YAML
        ckeditor5_invalid_plugin_foo_bar: {}
        YAML,
        InvalidPluginDefinitionException::class,
        'The "ckeditor5_invalid_plugin_foo_bar" CKEditor 5 plugin definition must contain a "ckeditor5" key.',
        ];
        ......@@ -239,7 +244,8 @@ public static function providerTestInvalidPluginDefinitions(): \Generator {
        ckeditor5_invalid_plugin_foo_bar:
        ckeditor5: {}
        YAML,
        'The "ckeditor5_invalid_plugin_foo_bar" CKEditor 5 plugin definition must contain a "ckeditor5.plugins" key.',
        \ArgumentCountError::class,
        NULL,
        ];
        yield 'added ckeditor5.plugins' => [
        ......@@ -248,6 +254,7 @@ public static function providerTestInvalidPluginDefinitions(): \Generator {
        ckeditor5:
        plugins: {}
        YAML,
        InvalidPluginDefinitionException::class,
        'The "ckeditor5_invalid_plugin_foo_bar" CKEditor 5 plugin definition must contain a "drupal" key.',
        ];
        ......@@ -258,6 +265,7 @@ public static function providerTestInvalidPluginDefinitions(): \Generator {
        plugins: {}
        drupal: {}
        YAML,
        InvalidPluginDefinitionException::class,
        'The "ckeditor5_invalid_plugin_foo_bar" CKEditor 5 plugin definition must contain a "drupal.label" key.',
        ];
        ......@@ -269,7 +277,7 @@ public static function providerTestInvalidPluginDefinitions(): \Generator {
        drupal:
        label: {}
        YAML,
        'The "ckeditor5_invalid_plugin_foo_bar" CKEditor 5 plugin definition has a "drupal.label" value that is not a string nor a TranslatableMarkup instance.',
        \TypeError::class,
        ];
        yield 'fixed drupal.label' => [
        ......@@ -280,6 +288,7 @@ public static function providerTestInvalidPluginDefinitions(): \Generator {
        drupal:
        label: "Foo bar"
        YAML,
        InvalidPluginDefinitionException::class,
        'The "ckeditor5_invalid_plugin_foo_bar" CKEditor 5 plugin definition must contain a "drupal.elements" key.',
        ];
        ......@@ -292,6 +301,7 @@ public static function providerTestInvalidPluginDefinitions(): \Generator {
        label: "Foo bar"
        elements: {}
        YAML,
        InvalidPluginDefinitionException::class,
        'The "ckeditor5_invalid_plugin_foo_bar" CKEditor 5 plugin definition has a "drupal.elements" value that is neither a list of HTML tags/attributes nor false.',
        ];
        ......@@ -306,6 +316,7 @@ public static function providerTestInvalidPluginDefinitions(): \Generator {
        - foo
        - bar
        YAML,
        InvalidPluginDefinitionException::class,
        'The "ckeditor5_invalid_plugin_foo_bar" CKEditor 5 plugin definition has a value at "drupal.elements.0" that is not an HTML tag with optional attributes: "foo". Expected structure: "<tag allowedAttribute="allowedValue1 allowedValue2">".',
        ];
        ......@@ -319,6 +330,7 @@ public static function providerTestInvalidPluginDefinitions(): \Generator {
        elements:
        - <foo> <bar>
        YAML,
        InvalidPluginDefinitionException::class,
        'The "ckeditor5_invalid_plugin_foo_bar" CKEditor 5 plugin definition has a value at "drupal.elements.0": multiple tags listed, should be one: "<foo> <bar>".',
        ];
        ......@@ -333,7 +345,6 @@ public static function providerTestInvalidPluginDefinitions(): \Generator {
        - <foo>
        - <bar>
        YAML,
        NULL,
        ];
        yield 'alternative fix for drupal.elements' => [
        ......@@ -345,7 +356,6 @@ public static function providerTestInvalidPluginDefinitions(): \Generator {
        label: "Foo bar"
        elements: false
        YAML,
        NULL,
        ];
        yield 'added invalid optional metadata: drupal.admin_library' => [
        ......@@ -360,6 +370,7 @@ public static function providerTestInvalidPluginDefinitions(): \Generator {
        - <bar>
        admin_library: ckeditor5/foo_bar
        YAML,
        InvalidPluginDefinitionException::class,
        'The "ckeditor5_invalid_plugin_foo_bar" CKEditor 5 plugin definition has a "drupal.admin_library" key whose asset library "ckeditor5/foo_bar" does not exist.',
        ];
        ......@@ -375,7 +386,6 @@ public static function providerTestInvalidPluginDefinitions(): \Generator {
        - <bar>
        admin_library: ckeditor5/internal.admin.basic
        YAML,
        NULL,
        ];
        // Add conditions.
        ......@@ -393,6 +403,7 @@ public static function providerTestInvalidPluginDefinitions(): \Generator {
        conditions:
        foo: bar
        YAML,
        InvalidPluginDefinitionException::class,
        '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' => [
        ......@@ -409,6 +420,7 @@ public static function providerTestInvalidPluginDefinitions(): \Generator {
        conditions:
        toolbarItem: [bold, italic]
        YAML,
        InvalidPluginDefinitionException::class,
        'The "ckeditor5_invalid_plugin_foo_bar" CKEditor 5 plugin definition has an invalid "drupal.conditions" item. "toolbarItem" is set to an invalid value. A string corresponding to a CKEditor 5 toolbar item must be specified.',
        ];
        yield 'valid condition: toolbarItem' => [
        ......@@ -425,7 +437,6 @@ public static function providerTestInvalidPluginDefinitions(): \Generator {
        conditions:
        toolbarItem: bold
        YAML,
        NULL,
        ];
        yield 'invalid condition: filter' => [
        <<<YAML
        ......@@ -442,6 +453,7 @@ public static function providerTestInvalidPluginDefinitions(): \Generator {
        toolbarItem: bold
        filter: true
        YAML,
        InvalidPluginDefinitionException::class,
        'The "ckeditor5_invalid_plugin_foo_bar" CKEditor 5 plugin definition has an invalid "drupal.conditions" item. "filter" is set to an invalid value. A string corresponding to a filter plugin ID must be specified.',
        ];
        yield 'valid condition: filter' => [
        ......@@ -459,7 +471,6 @@ public static function providerTestInvalidPluginDefinitions(): \Generator {
        toolbarItem: bold
        filter: filter_caption
        YAML,
        NULL,
        ];
        yield 'invalid condition: imageUploadStatus' => [
        <<<YAML
        ......@@ -477,6 +488,7 @@ public static function providerTestInvalidPluginDefinitions(): \Generator {
        filter: filter_caption
        imageUploadStatus: 'true'
        YAML,
        InvalidPluginDefinitionException::class,
        'The "ckeditor5_invalid_plugin_foo_bar" CKEditor 5 plugin definition has an invalid "drupal.conditions" item. "imageUploadStatus" is set to an invalid value. A boolean indicating whether image uploads must be enabled (true) or not (false) must be specified.',
        ];
        yield 'valid condition: imageUploadStatus' => [
        ......@@ -495,7 +507,6 @@ public static function providerTestInvalidPluginDefinitions(): \Generator {
        filter: filter_caption
        imageUploadStatus: true
        YAML,
        NULL,
        ];
        yield 'invalid condition: plugins' => [
        <<<YAML
        ......@@ -514,6 +525,7 @@ public static function providerTestInvalidPluginDefinitions(): \Generator {
        imageUploadStatus: true
        plugins: ckeditor5_imageCaption
        YAML,
        InvalidPluginDefinitionException::class,
        'The "ckeditor5_invalid_plugin_foo_bar" CKEditor 5 plugin definition has an invalid "drupal.conditions" item. "plugins" is set to an invalid value. A list of strings, each corresponding to a CKEditor 5 plugin ID must be specified.',
        ];
        yield 'valid condition: plugins' => [
        ......@@ -533,7 +545,6 @@ public static function providerTestInvalidPluginDefinitions(): \Generator {
        imageUploadStatus: true
        plugins: [ckeditor5_imageCaption]
        YAML,
        NULL,
        ];
        yield 'unconditional: for plugins that should always loaded' => [
        <<<YAML
        ......@@ -548,7 +559,6 @@ public static function providerTestInvalidPluginDefinitions(): \Generator {
        admin_library: ckeditor5/internal.admin.basic
        conditions: []
        YAML,
        NULL,
        ];
        yield 'explicitly unconditional' => [
        <<<YAML
        ......@@ -563,7 +573,6 @@ public static function providerTestInvalidPluginDefinitions(): \Generator {
        admin_library: ckeditor5/internal.admin.basic
        conditions: false
        YAML,
        NULL,
        ];
        // Add a plugin class; observe what additional requirements need to be met.
        ......@@ -580,6 +589,7 @@ class: Drupal\ckeditor5_invalid_plugin\Plugin\CKEditor5Plugin\FooBar
        - <bar>
        admin_library: ckeditor5/internal.admin.basic
        YAML,
        InvalidPluginDefinitionException::class,
        'The CKEditor 5 "ckeditor5_invalid_plugin_foo_bar" provides a plugin class: "Drupal\ckeditor5_invalid_plugin\Plugin\CKEditor5Plugin\FooBar", but it does not exist.',
        ];
        ......@@ -596,6 +606,7 @@ class: Drupal\ckeditor5_invalid_plugin\Plugin\CKEditor5Plugin\FooBar
        - <bar>
        admin_library: ckeditor5/internal.admin.basic
        YAML,
        InvalidPluginDefinitionException::class,
        'CKEditor 5 plugins must implement \Drupal\ckeditor5\Plugin\CKEditor5PluginInterface. "ckeditor5_invalid_plugin_foo_bar" does not.',
        [
        'src' => [
        ......@@ -625,6 +636,7 @@ class: Drupal\ckeditor5_invalid_plugin\Plugin\CKEditor5Plugin\FooBar
        - <bar>
        admin_library: ckeditor5/internal.admin.basic
        YAML,
        NULL,
        NULL,
        [
        'src' => [
        ......@@ -657,6 +669,7 @@ class: Drupal\ckeditor5_invalid_plugin\Plugin\CKEditor5Plugin\FooBar
        - <bar>
        admin_library: ckeditor5/internal.admin.basic
        YAML,
        NULL,
        NULL,
        [
        'src' => [
        ......@@ -696,6 +709,7 @@ class: Drupal\ckeditor5_invalid_plugin\Plugin\CKEditor5Plugin\FooBar
        - <bar>
        admin_library: ckeditor5/internal.admin.basic
        YAML,
        InvalidPluginDefinitionException::class,
        'The "ckeditor5_invalid_plugin_foo_bar" CKEditor 5 plugin definition is configurable, has non-empty default configuration but has no config schema. Config schema is required for validation.',
        [
        'src' => [
        ......@@ -735,6 +749,7 @@ class: Drupal\ckeditor5_invalid_plugin\Plugin\CKEditor5Plugin\FooBar
        - <bar>
        admin_library: ckeditor5/internal.admin.basic
        YAML,
        NULL,
        NULL,
        [
        'config' => [
        ......@@ -787,6 +802,7 @@ class: Drupal\ckeditor5_invalid_plugin\Plugin\CKEditor5Plugin\FooBar
        - <bar>
        admin_library: ckeditor5/internal.admin.basic
        YAML,
        InvalidPluginDefinitionException::class,
        'The "ckeditor5_invalid_plugin_foo_bar" CKEditor 5 plugin definition is configurable, but its default configuration does not match its config schema. The following errors were found: [foo] The configuration property foo.bar doesn\'t exist, [baz] missing schema.',
        [
        'config' => [
        ......@@ -839,6 +855,7 @@ class: Drupal\ckeditor5_invalid_plugin\Plugin\CKEditor5Plugin\FooBar
        - <bar>
        admin_library: ckeditor5/internal.admin.basic
        YAML,
        NULL,
        NULL,
        [
        'config' => [
        ......@@ -892,6 +909,7 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s
        conditions:
        requiresConfiguration: true
        YAML,
        InvalidPluginDefinitionException::class,
        '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.',
        ];
        ......@@ -907,6 +925,7 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s
        requiresConfiguration:
        allow_resize: true
        YAML,
        InvalidPluginDefinitionException::class,
        '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.',
        ];
        ......@@ -923,6 +942,7 @@ class: Drupal\ckeditor5_invalid_plugin\Plugin\CKEditor5Plugin\FooBar
        requiresConfiguration:
        allow_resize: true
        YAML,
        InvalidPluginDefinitionException::class,
        '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' => [
        ......@@ -975,6 +995,7 @@ class: Drupal\ckeditor5_invalid_plugin\Plugin\CKEditor5Plugin\FooBar
        requiresConfiguration:
        foo: true
        YAML,
        NULL,
        NULL,
        [
        'config' => [
        ......@@ -1580,9 +1601,11 @@ public function testExternalLinkAutomaticLinkDecoratorDisallowed(): void {
        * @covers \Drupal\ckeditor5\Plugin\CKEditor5PluginManager::getDiscovery
        * @dataProvider providerTestDerivedPluginDefinitions
        */
        public function testDerivedPluginDefinitions(string $yaml, ?string $expected_message, array $additional_files = [], ?array $expected_derived_plugin_definitions = NULL): void {
        public function testDerivedPluginDefinitions(string $yaml, ?string $expected_exception = NULL, ?string $expected_message = NULL, array $additional_files = [], ?array $expected_derived_plugin_definitions = NULL): void {
        if ($expected_exception) {
        $this->expectException($expected_exception);
        }
        if ($expected_message) {
        $this->expectException(InvalidPluginDefinitionException::class);
        $this->expectExceptionMessage($expected_message);
        }
        $container = $this->mockModuleInVfs('ckeditor5_derived_plugin', $yaml, $additional_files);
        ......@@ -1638,6 +1661,7 @@ public function getDerivativeDefinitions($base_plugin_definition) {
        drupal:
        deriver: Drupal\ckeditor5_derived_plugin\Plugin\CKEditor5Plugin\SimpleDeriver
        YAML,
        InvalidPluginDefinitionException::class,
        'The "ckeditor5_derived_plugin_foo:bar" CKEditor 5 derived plugin definition must contain a "drupal.elements" key.',
        $simple_deriver_additional_files,
        ];
        ......@@ -1650,7 +1674,8 @@ public function getDerivativeDefinitions($base_plugin_definition) {
        elements: false
        deriver: Drupal\ckeditor5_derived_plugin\Plugin\CKEditor5Plugin\SimpleDeriver
        YAML,
        'The "ckeditor5_derived_plugin_foo:bar" CKEditor 5 derived plugin definition must contain a "ckeditor5.plugins" key.',
        \ArgumentCountError::class,
        NULL,
        $simple_deriver_additional_files,
        ];
        ......@@ -1661,6 +1686,7 @@ public function getDerivativeDefinitions($base_plugin_definition) {
        elements: false
        deriver: Drupal\ckeditor5_derived_plugin\Plugin\CKEditor5Plugin\SimpleDeriver
        YAML,
        InvalidPluginDefinitionException::class,
        'The "ckeditor5_derived_plugin_foo:bar" CKEditor 5 derived plugin definition must contain a "ckeditor5" key.',
        $simple_deriver_additional_files,
        ];
        ......@@ -1674,6 +1700,7 @@ public function getDerivativeDefinitions($base_plugin_definition) {
        elements: false
        deriver: Drupal\ckeditor5_derived_plugin\Plugin\CKEditor5Plugin\SimpleDeriver
        YAML,
        InvalidPluginDefinitionException::class,
        'The "ckeditor5_derived_plugin_foo:bar" CKEditor 5 plugin definition must extend Drupal\ckeditor5\Plugin\CKEditor5PluginDefinition',
        [
        'src' => [
        ......@@ -1712,6 +1739,7 @@ public function getDerivativeDefinitions($base_plugin_definition) {
        elements: false
        deriver: Drupal\ckeditor5_derived_plugin\Plugin\CKEditor5Plugin\SimpleDeriver
        YAML,
        NULL,
        NULL,
        $simple_deriver_additional_files,
        [
        ......@@ -1738,9 +1766,72 @@ public function getDerivativeDefinitions($base_plugin_definition) {
        ],
        ];
        yield 'VALID: simple deriver, base definition in PHP' => [
        yield 'VALID: simple deriver, base definition in PHP with Attribute' => [
        '',
        NULL,
        NULL,
        [
        'src' => [
        'Plugin' => [
        'CKEditor5Plugin' => [
        'Foo.php' => <<<'PHP'
        <?php
        declare(strict_types = 1);
        namespace Drupal\ckeditor5_derived_plugin\Plugin\CKEditor5Plugin;
        use Drupal\ckeditor5\Attribute\CKEditor5AspectsOfCKEditor5Plugin;
        use Drupal\ckeditor5\Attribute\CKEditor5Plugin;
        use Drupal\ckeditor5\Attribute\DrupalAspectsOfCKEditor5Plugin;
        use Drupal\ckeditor5\Plugin\CKEditor5PluginDefault;
        use Drupal\Core\StringTranslation\TranslatableMarkup;
        #[CKEditor5Plugin(
        id: 'ckeditor5_derived_plugin_foo',
        ckeditor5: new CKEditor5AspectsOfCKEditor5Plugin(
        plugins: [],
        ),
        drupal: new DrupalAspectsOfCKEditor5Plugin(
        elements: false,
        deriver: 'Drupal\ckeditor5_derived_plugin\Plugin\CKEditor5Plugin\SimpleDeriver',
        ),
        )]
        class Foo extends CKEditor5PluginDefault {
        }
        PHP,
        'SimpleDeriver.php' => $simple_deriver_additional_files['src']['Plugin']['CKEditor5Plugin']['SimpleDeriver.php'],
        ],
        ],
        ],
        ],
        [
        'ckeditor5_derived_plugin_foo:bar' => new CKEditor5PluginDefinition([
        'provider' => 'ckeditor5_derived_plugin',
        'id' => 'ckeditor5_derived_plugin_foo:bar',
        'ckeditor5' => ['plugins' => []] + $ckeditor5_aspects_defaults,
        'drupal' => [
        'class' => 'Drupal\ckeditor5_derived_plugin\Plugin\CKEditor5Plugin\Foo',
        'label' => 'Foo bar',
        'elements' => FALSE,
        'deriver' => 'Drupal\ckeditor5_derived_plugin\Plugin\CKEditor5Plugin\SimpleDeriver',
        ] + $drupal_aspects_defaults,
        ]),
        'ckeditor5_derived_plugin_foo:baz' => new CKEditor5PluginDefinition([
        'provider' => 'ckeditor5_derived_plugin',
        'id' => 'ckeditor5_derived_plugin_foo:baz',
        'ckeditor5' => ['plugins' => []] + $ckeditor5_aspects_defaults,
        'drupal' => [
        'class' => 'Drupal\ckeditor5_derived_plugin\Plugin\CKEditor5Plugin\Foo',
        'label' => 'Foo baz',
        'elements' => FALSE,
        'deriver' => 'Drupal\ckeditor5_derived_plugin\Plugin\CKEditor5Plugin\SimpleDeriver',
        ] + $drupal_aspects_defaults,
        ]),
        ],
        ];
        yield 'VALID: simple deriver, base definition in PHP with Annotation' => [
        '',
        NULL,
        NULL,
        [
        'src' => [
        'Plugin' => [
        ......@@ -1803,6 +1894,7 @@ class Foo extends CKEditor5PluginDefault {
        drupal:
        deriver: Drupal\ckeditor5_derived_plugin\Plugin\CKEditor5Plugin\MaximalDeriver
        YAML,
        NULL,
        NULL,
        [
        'src' => [
        ......@@ -1865,6 +1957,7 @@ public function getDerivativeDefinitions($base_plugin_definition) {
        elements: false
        deriver: Drupal\ckeditor5_derived_plugin\Plugin\CKEditor5Plugin\ContainerDependentDeriver
        YAML,
        NULL,
        NULL,
        [
        'config' => [
        ......
        0% Loading or .
        You are about to add 0 people to the discussion. Proceed with caution.
        Please register or to comment