Verified Commit bf98c003 authored by Alex Bronstein's avatar Alex Bronstein Committed by Alex Pott
Browse files

Issue #3313473 by Wim Leers, tim.plunkett, effulgentsia, smustgrave, mglaman,...

Issue #3313473 by Wim Leers, tim.plunkett, effulgentsia, smustgrave, mglaman, balintpekker: CKEditor 5 plugin definitions should be derivable

(cherry picked from commit 4ff26945)
(cherry picked from commit b472d17d)
parent 888dea01
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -42,6 +42,15 @@ class DrupalAspectsOfCKEditor5Plugin extends Plugin {
   */
  public $class = CKEditor5PluginDefault::class;

  /**
   * The CKEditor 5 plugin deriver class.
   *
   * This property is optional and it does not need to be declared.
   *
   * @var string|null
   */
  public $deriver = NULL;

  /**
   * The library this plugin requires.
   *
+43 −16
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@

use Drupal\ckeditor5\HTMLRestrictions;
use Drupal\Component\Assertion\Inspector;
use Drupal\Component\Plugin\Definition\DerivablePluginDefinitionInterface;
use Drupal\Component\Plugin\Definition\PluginDefinition;
use Drupal\Component\Plugin\Definition\PluginDefinitionInterface;
use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
@@ -16,7 +17,7 @@
/**
 * Provides an implementation of a CKEditor 5 plugin definition.
 */
final class CKEditor5PluginDefinition extends PluginDefinition implements PluginDefinitionInterface {
final class CKEditor5PluginDefinition extends PluginDefinition implements PluginDefinitionInterface, DerivablePluginDefinitionInterface {

  use SchemaCheckTrait;

@@ -40,22 +41,17 @@ final class CKEditor5PluginDefinition extends PluginDefinition implements Plugin
   * @param array $definition
   *   An array of values from the annotation/YAML.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \InvalidArgumentException
   */
  public function __construct(array $definition) {
    $this->id = $id = $definition['id'];

    $expected_prefix = sprintf("%s_", $definition['provider']);
    if (strpos($id, $expected_prefix) !== 0) {
      throw new InvalidPluginDefinitionException($id, sprintf('The "%s" CKEditor 5 plugin definition must have a plugin ID that starts with "%s".', $id, $expected_prefix));
    foreach ($definition as $property => $value) {
      if (property_exists($this, $property)) {
        $this->{$property} = $value;
      }
      else {
        throw new \InvalidArgumentException(sprintf('Property %s with value %s does not exist on %s.', $property, $value, __CLASS__));
      }
    }
    $this->provider = $definition['provider'];

    static::validateCKEditor5Aspects($id, $definition);
    $this->ckeditor5 = $definition['ckeditor5'];

    $this->validateDrupalAspects($id, $definition);
    $this->drupal = $definition['drupal'];
  }

  /**
@@ -81,8 +77,11 @@ public function toArray(): array {
   *   The plugin definition to validate.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   *
   * @internal
   * @see \Drupal\ckeditor5\Plugin\CKEditor5PluginManager::processDefinition()
   */
  private static function validateCKEditor5Aspects(string $id, array $definition): void {
  public static function validateCKEditor5Aspects(string $id, array $definition): void {
    if (!isset($definition['ckeditor5'])) {
      throw new InvalidPluginDefinitionException($id, sprintf('The "%s" CKEditor 5 plugin definition must contain a "ckeditor5" key.', $id));
    }
@@ -122,8 +121,11 @@ private static function validateCKEditor5Aspects(string $id, array $definition):
   *   The plugin definition to validate.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   *
   * @internal
   * @see \Drupal\ckeditor5\Plugin\CKEditor5PluginManager::processDefinition()
   */
  private function validateDrupalAspects(string $id, array $definition): void {
  public function validateDrupalAspects(string $id, array $definition): void {
    if (!isset($definition['drupal'])) {
      throw new InvalidPluginDefinitionException($id, sprintf('The "%s" CKEditor 5 plugin definition must contain a "drupal" key.', $id));
    }
@@ -304,6 +306,31 @@ public function setClass($class) {
    return $this;
  }

  /**
   * {@inheritdoc}
   *
   * @see \Drupal\ckeditor5\Annotation\DrupalAspectsOfCKEditor5Plugin::$deriver
   */
  public function getDeriver() {
    // TRICKY: this is the only key that is allowed to not be set, because it is
    // possible that this plugin definition is a partial/incomplete one, and the
    // default from the annotation is only applied automatically for class
    // annotation CKEditor 5 plugin definitions (because they create an instance
    // of the DrupalAspectsOfCKEditor5Plugin annotation level), not for CKEditor
    // 5 plugin definitions in YAML.
    // @see \Drupal\ckeditor5\Plugin\CKEditor5PluginManager::processDefinition()
    // @see \Drupal\ckeditor5\Annotation\CKEditor5Plugin::__construct()
    return $this->drupal['deriver'] ?? NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function setDeriver($deriver) {
    $this->drupal['deriver'] = $deriver;
    return $this;
  }

  /**
   * Whether this plugin is configurable by the user.
   *
+46 −0
Original line number Diff line number Diff line
@@ -8,11 +8,13 @@
use Drupal\ckeditor5\HTMLRestrictions;
use Drupal\Component\Annotation\Plugin\Discovery\AnnotationBridgeDecorator;
use Drupal\Component\Assertion\Inspector;
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\ContainerDerivativeDiscoveryDecorator;
use Drupal\Core\Plugin\Discovery\YamlDiscoveryDecorator;
use Drupal\editor\EditorInterface;
use Drupal\filter\FilterPluginCollection;
@@ -61,11 +63,55 @@ protected function getDiscovery() {
      // supports top-level properties.
      // @see \Drupal\ckeditor5\Plugin\CKEditor5PluginDefinition::label()
      $discovery = new AnnotationBridgeDecorator($discovery, $this->pluginDefinitionAnnotationName);
      $discovery = new ContainerDerivativeDiscoveryDecorator($discovery);
      $this->discovery = $discovery;
    }
    return $this->discovery;
  }

  /**
   * {@inheritdoc}
   */
  public function processDefinition(&$definition, $plugin_id) {
    if (!$definition instanceof CKEditor5PluginDefinition) {
      throw new InvalidPluginDefinitionException($plugin_id, sprintf('The "%s" CKEditor 5 plugin definition must extend %s', $plugin_id, CKEditor5PluginDefinition::class));
    }

    // A derived plugin will still have the ID of the derivative, rather than
    // that of the derived plugin ID (`<base plugin ID>:<derivative ID>`).
    // Generate an updated CKEditor5PluginDefinition.
    // @see \Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator::encodePluginId()
    // @todo Remove this in https://www.drupal.org/project/drupal/issues/2458769.
    $is_derived = $definition->id() !== $plugin_id;
    if ($is_derived) {
      $definition = new CKEditor5PluginDefinition(['id' => $plugin_id] + $definition->toArray());
    }

    $expected_prefix = sprintf("%s_", $definition->getProvider());
    $id = $definition->id();
    if (strpos($id, $expected_prefix) !== 0) {
      throw new InvalidPluginDefinitionException($id, sprintf('The "%s" CKEditor 5 plugin definition must have a plugin ID that starts with "%s".', $id, $expected_prefix));
    }

    try {
      $definition->validateCKEditor5Aspects($id, $definition->toArray());
      $definition->validateDrupalAspects($id, $definition->toArray());
    }
    catch (InvalidPluginDefinitionException $e) {
      // If this exception is thrown for a derived CKEditor 5 plugin definition,
      // it means the deriver did not generate a valid plugin definition.
      // Re-throw the exception, but tweak the language for DX: clarify it is
      // for a derived plugin definition.
      if ($is_derived) {
        throw new InvalidPluginDefinitionException($e->getPluginId(), str_replace('plugin definition', 'derived plugin definition', $e->getMessage()));
      }
      // Otherwise, the exception was appropriate: re-throw it.
      throw $e;
    }

    parent::processDefinition($definition, $plugin_id);
  }

  /**
   * {@inheritdoc}
   */
+403 −19

File changed.

Preview size limit exceeded, changes collapsed.