From 1a3458a29bf3d8937d9067d2b52c66c75c11e0f0 Mon Sep 17 00:00:00 2001 From: Kevin Wenger Date: Mon, 8 Apr 2024 10:14:22 +0200 Subject: [PATCH] support only CKEditor 5 and Drupal 10.x (dropping legacy CK4 & Drupal 9.x) --- .github/workflows/ci.yml | 8 +- .gitlab-ci.yml | 7 +- CHANGELOG.md | 6 + CONTRIBUTING.md | 2 +- Dockerfile | 6 +- README.md | 2 +- composer.json | 3 +- editor_advanced_image.info.yml | 4 +- editor_advanced_image.libraries.yml | 10 - editor_advanced_image.module | 143 -------- js/editor_advanced_image.js | 165 --------- .../EditorAdvancedImage.php | 97 ----- .../CKEditorPlugin/EditorAdvancedImage.php | 159 -------- ...CKEditor4EditorAdvancedImageDialogTest.php | 327 ----------------- ...or4EditorAdvancedImageEditorFormatTest.php | 128 ------- .../UpgradePathCompletenessTest.php | 66 ---- .../CKEditor4To5Upgrade/UpgradePathTest.php | 338 ------------------ 17 files changed, 22 insertions(+), 1449 deletions(-) delete mode 100644 editor_advanced_image.module delete mode 100644 js/editor_advanced_image.js delete mode 100644 src/Plugin/CKEditor4To5Upgrade/EditorAdvancedImage.php delete mode 100644 src/Plugin/CKEditorPlugin/EditorAdvancedImage.php delete mode 100644 tests/src/FunctionalJavascript/CKEditor4EditorAdvancedImageDialogTest.php delete mode 100644 tests/src/FunctionalJavascript/CKEditor4EditorAdvancedImageEditorFormatTest.php delete mode 100644 tests/src/Kernel/CKEditor4To5Upgrade/UpgradePathCompletenessTest.php delete mode 100644 tests/src/Kernel/CKEditor4To5Upgrade/UpgradePathTest.php diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8d7545e..48e777e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: strategy: matrix: - drupal_version: ['9.5', '10.0', '10.1', '10.2'] + drupal_version: ['10.0', '10.1', '10.2', '11.0'] module: ['editor_advanced_image'] steps: @@ -34,7 +34,7 @@ jobs: strategy: matrix: - drupal_version: ['9.5', '10.0', '10.1', '10.2'] + drupal_version: ['10.0', '10.1', '10.2'] module: ['editor_advanced_image'] steps: @@ -61,7 +61,7 @@ jobs: strategy: matrix: - drupal_version: ['9.5'] + drupal_version: ['10.2'] module: ['editor_advanced_image'] steps: @@ -74,7 +74,7 @@ jobs: - name: Up a persistent Docker Container run: docker compose -f docker-compose.yml up -d drupal - name: Add upgrade status dependency - run: docker compose exec -T drupal wait-for-it db:3306 -- composer require --dev drupal/upgrade_status --no-interaction + run: docker compose exec -T drupal wait-for-it db:3306 -- composer require --dev drupal/upgrade_status --no-interaction --with-all-dependencies - name: Bootstrap Drupal run: docker compose -f docker-compose.yml exec -T -u www-data drupal drush site-install standard --db-url="mysql://drupal:drupal@db/drupal" -y - name: Enable upgrade status diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index aaba832..cbbc9dc 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -31,10 +31,13 @@ include: variables: # Opt in to testing current minor against max supported PHP version. OPT_IN_TEST_MAX_PHP: '1' - # Opt in to testing previous (Drupal 10.1.x). + # Opt in to testing previous & next minor (Drupal 10.0.x and 10.2.x). OPT_IN_TEST_PREVIOUS_MINOR: '1' - # The 4.x branch of the CDN module requires PHP >=8.1, rather than core's >=7.4. + OPT_IN_TEST_NEXT_MINOR: '1' + # The 3.x branch of the CDN module requires Drupal 10.x minimum. CORE_PREVIOUS_PHP_MIN: '8.1' + # Opt in to testing $CORE_MAJOR_DEVELOPMENT (currently Drupal 11). + OPT_IN_TEST_NEXT_MAJOR: '1' # This module wants to strictly comply with Drupal's coding standards. phpcs: diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d59f87..1ac6dbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Removed +- drop support of drupal 9.x +- drop support of CKEditor 4.x + +### Added +- add official support of drupal 11 ## [2.3.0] - 2024-04-08 ### Fixed diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f4ed8c3..774926f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,7 +24,7 @@ First of all, you need to have the following tools installed globally on your environment: * drush - * Latest dev release of Drupal 8.x/9.x/10.x. + * Latest dev release of Drupal 8.x/9.x/10.x/11.x. * docker * docker compose diff --git a/Dockerfile b/Dockerfile index c9a155b..a71766d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,11 +2,7 @@ ARG BASE_IMAGE_TAG=10.1 FROM wengerk/drupal-for-contrib:${BASE_IMAGE_TAG} # Disable deprecation notice as CKEditor4 module will throw an exception until we remove support of Drupal 9. -ENV SYMFONY_DEPRECATIONS_HELPER=disabled - -# Install drupal/ckeditor (CKEditor 4) as we keep supporting CK4 & CK5. -ENV COMPOSER_ALLOW_SUPERUSER=1 -RUN COMPOSER_MEMORY_LIMIT=-1 composer require "drupal/ckeditor" +# ENV SYMFONY_DEPRECATIONS_HELPER=disabled # Register the Drupal and DrupalPractice Standard with PHPCS. RUN ./vendor/bin/phpcs --config-set installed_paths \ diff --git a/README.md b/README.md index b2889a8..9e6b3ff 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Allows to define the following attributes on an Image: | 9.x | 4.x | 8.x-2.1 | | 9.x | 5.x | 8.x-2.1 | | 10.x | 4.x | 8.x-2.1 | -| 10.x | 5.x | 3.0.x | +| 10.x | 5.x | 8.x-2.1 or 3.x | ## Dependencies diff --git a/composer.json b/composer.json index 7266968..aa54205 100644 --- a/composer.json +++ b/composer.json @@ -29,8 +29,7 @@ }, "license": "GPL-2.0-or-later", "require-dev": { - "drupal/coder": "^8.3.1", - "drupal/ckeditor": "^1.0" + "drupal/coder": "^8.3.1" }, "config": { "allow-plugins": { diff --git a/editor_advanced_image.info.yml b/editor_advanced_image.info.yml index a9a424b..1145615 100644 --- a/editor_advanced_image.info.yml +++ b/editor_advanced_image.info.yml @@ -1,8 +1,10 @@ name: 'Editor Advanced Image' type: module description: 'Enhances the inline-image Dialog in D8 CKEditor.' -core_version_requirement: ^9.5 || ^10 +core_version_requirement: ^10 || ^11 + package: CKEditor dependencies: - drupal:editor + - drupal:ckeditor5 diff --git a/editor_advanced_image.libraries.yml b/editor_advanced_image.libraries.yml index 38979a8..ca7394a 100644 --- a/editor_advanced_image.libraries.yml +++ b/editor_advanced_image.libraries.yml @@ -1,13 +1,3 @@ -editor_advanced_image: - version: VERSION - js: - js/editor_advanced_image.js: {} - dependencies: - - core/drupal - - core/drupal.announce - - core/jquery - - core/once - ckeditor5: version: VERSION js: diff --git a/editor_advanced_image.module b/editor_advanced_image.module deleted file mode 100644 index 57060e5..0000000 --- a/editor_advanced_image.module +++ /dev/null @@ -1,143 +0,0 @@ -getBuildInfo()['args'][0]; - $settings = $argument->getSettings(); - - // In case the only argument we get is the Editor instead of the FilterFormat. - if ($argument instanceof Editor) { - $argument = $argument->getFilterFormat(); - } - $restrictions = $argument->getHtmlRestrictions(); - - if (isset($form_state->getUserInput()['editor_object'])) { - $input = $form_state->getUserInput()['editor_object']; - $form_state->set('image_element', $input); - $form_state->setCached(TRUE); - } - else { - // Retrieve the link element's attributes from form state. - $input = $form_state->get('image_element') ?: []; - } - - /* - * Helper to retrieve form fields' default values. - * - * @param string $attribute_name - * @param string $fallback - * - * @return mixed - * The existing value or the fallback. - */ - $get_default_value = function ($attribute_name, $fallback = '') use ($input) { - return !empty($input[$attribute_name]) ? $input[$attribute_name] : $fallback; - }; - - /* - * Helper to set the status of a form field according to the status of the - * filter about the attribute it is defining. - * - * @param string $attribute_name - * - * @return bool - * TRUE if the filter is disabled or if the entire "img" tag is allowed - * or if the given attribute is allowed for the "img" tag. - */ - $is_accessible = function ($attribute_name) use ($restrictions) { - return $restrictions === FALSE - || $restrictions['allowed']['img'] === TRUE - || !empty($restrictions['allowed']['img'][$attribute_name]); - }; - - $form['attributes']['title'] = [ - '#type' => 'textfield', - '#title' => t('Title'), - '#description' => t('Populates the title attribute of the image, usually shown as a small tooltip on hover.'), - '#default_value' => $get_default_value('title'), - '#maxlength' => 1024, - '#weight' => 1, - '#access' => $is_accessible('title'), - ]; - - $form['advanced'] = [ - '#type' => 'details', - '#title' => t('Advanced'), - '#access' => FALSE, - '#weight' => 2, - ]; - - $default_class = NULL; - if (!empty($settings['plugins']['editoradvancedimage']['default_class'])) { - $default_class = '
' . t('Default: :classes.', [':classes' => $settings['plugins']['editoradvancedimage']['default_class']]); - } - $form['attributes']['class'] = [ - '#type' => 'textfield', - '#title' => t('CSS classes'), - '#description' => t('List of CSS classes to add to the image, separated by spaces.') . $default_class, - '#default_value' => $get_default_value('class'), - '#maxlength' => 1024, - '#access' => $is_accessible('class'), - '#group' => 'advanced', - ]; - - $form['attributes']['id'] = [ - '#type' => 'textfield', - '#title' => t('ID'), - '#description' => t('Allows linking to this content using a URL fragment. Must be unique.'), - '#default_value' => $get_default_value('id'), - '#maxlength' => 1024, - '#access' => $is_accessible('id'), - '#group' => 'advanced', - ]; - - // Show the advanced group if at least one of its fields is accessible. - foreach ($form['attributes'] as $element) { - if (!empty($element['#group']) && $element['#group'] === 'advanced' && (!isset($element['#access']) || $element['#access'] === TRUE)) { - $form['advanced']['#access'] = TRUE; - break; - } - } - - // Add #validate callback that handles empty attributes. - array_unshift($form['#validate'], '_editor_advanced_image_attributes_validate'); -} - -/** - * Filter empty attributes to avoid empty HTML output. - */ -function _editor_advanced_image_attributes_validate(array &$form, FormStateInterface $form_state) { - $values = $form_state->getValue('attributes'); - $image_element = $form_state->get('image_element'); - foreach ($values as $key => $value) { - if (empty($value)) { - $form_state->setValue(['attributes', $key], ''); - // Special case on content creation. - if (empty($image_element)) { - $form_state->unsetValue(['attributes', $key]); - } - } - } -} diff --git a/js/editor_advanced_image.js b/js/editor_advanced_image.js deleted file mode 100644 index 6897c62..0000000 --- a/js/editor_advanced_image.js +++ /dev/null @@ -1,165 +0,0 @@ -/** - * @file - * Drupal Advanced Image plugin. - * - * This alters the existing CKEditor image2 widget plugin, which is already - * altered by the Drupal Image plugin, to: - * - allow for the title, class & id attributes to be set - * - mimic the upcasting behavior of the caption_filter filter. - * - * This is the CKEditor 4 version of the plugin. - */ - -/* global CKEDITOR */ - -(function (CKEDITOR) { - "use strict"; - - CKEDITOR.plugins.add("editoradvancedimage", { - requires: "drupalimage", - - beforeInit: function (editor) { - // Add CSS file. - editor.addContentsCss(this.path + "css/ckeditor.editoradvancedimage.css"); - - // Retrieve config from Drupal\editor_advanced_image\Plugin\CKEditorPlugin::getConfig. - var defaultClasses = editor.config.defaultClasses - ? editor.config.defaultClasses.trim() - : ""; - - // Override the image2 widget definition to handle the additional - // title, class and id attributes. - editor.on( - "widgetDefinition", - function (event) { - var widgetDefinition = event.data; - if (widgetDefinition.name !== "image") { - return; - } - - // Protected; keys of the widget data to be sent to the Drupal dialog. - // Append to the values defined by the drupalimage plugin. - // @see core/modules/ckeditor/js/plugins/drupalimage/plugin.js - CKEDITOR.tools.extend(widgetDefinition._mapDataToDialog, { - title: "title", - class: "class", - id: "id", - }); - - // Override downcast(): since we only accept in our upcast method, - // the element is already correct. We only need to update the element's - // title attribute. - var originalDowncast = widgetDefinition.downcast; - widgetDefinition.downcast = function (element) { - var img = findElementByName(element, "img"); - originalDowncast.call(this, img); - - img.attributes["title"] = this.data["title"]; - img.attributes["class"] = this.data["class"] - ? this.data["class"].trim() - : defaultClasses; - img.attributes["id"] = this.data["id"]; - - var captionFilterEnabled = - editor.config.drupalImageCaption_captionFilterEnabled; - var alignFilterEnabled = - editor.config.drupalImageCaption_alignFilterEnabled; - - var caption = this.editables.caption; - var captionHtml = caption && caption.getData(); - var attrs = img.attributes; - - if (captionFilterEnabled) { - if (captionHtml) { - attrs["data-caption"] = captionHtml; - } - } - if (alignFilterEnabled) { - if (this.data.align !== "none") { - attrs["data-align"] = this.data.align; - } - } - - if (img.parent.name === "a") { - return img.parent; - } - - return img; - }; - - // We want to upcast elements to a DOM structure required by the - // image2 widget; we only accept an tag, and that tag MAY - // have a data-entity-type and a data-entity-uuid attribute. - var originalUpcast = widgetDefinition.upcast; - widgetDefinition.upcast = function (element, data) { - if (element.name !== "img") { - return; - // Don't initialize on pasted fake objects. - } else if (element.attributes["data-cke-realelement"]) { - return; - } - - element = originalUpcast.call(this, element, data); - - // Check the originalUpcast detect an element. - if (typeof element === "undefined") { - return; - } - - // Apply attributes on
when dealing with captioned images. - var el = element; - if (el.name === "figure") { - el = el.children[0]; - } - - // Parse the title attribute. - data["title"] = el.attributes["title"]; - // Parse the class attribute & remove default class from it. - data["class"] = el.attributes["class"] - ? el.attributes["class"].trim() - : defaultClasses; - // Parse the id attribute. - data["id"] = el.attributes["id"]; - - return element; - }; - - // Low priority to ensure drupalimage's event handler runs first. - }, - null, - null, - 20, - ); - }, - }); - - /** - * Finds an element by its name. - * - * Function will check first the passed element itself and then all its - * children in DFS order. - * - * @param {CKEDITOR.htmlParser.element} element - * The element to search. - * @param {string} name - * The element name to search for. - * - * @return {?CKEDITOR.htmlParser.element} - * The found element, or null. - */ - function findElementByName(element, name) { - if (element.name === name) { - return element; - } - - var found = null; - element.forEach(function (el) { - if (el.name === name) { - found = el; - // Stop here. - return false; - } - }, CKEDITOR.NODE_ELEMENT); - return found; - } -})(CKEDITOR); diff --git a/src/Plugin/CKEditor4To5Upgrade/EditorAdvancedImage.php b/src/Plugin/CKEditor4To5Upgrade/EditorAdvancedImage.php deleted file mode 100644 index 5799eaa..0000000 --- a/src/Plugin/CKEditor4To5Upgrade/EditorAdvancedImage.php +++ /dev/null @@ -1,97 +0,0 @@ - [ - 'disable_balloon' => FALSE, - 'default_class' => $default_class, - 'enabled_attributes' => [], - ], - ]; - - default: - throw new \OutOfBoundsException(); - } - } - - /** - * {@inheritdoc} - * - * phpcs:disable Drupal.NamingConventions.ValidFunctionName - */ - public function computeCKEditor5PluginSubsetConfiguration(string $cke5_plugin_id, FilterFormatInterface $text_format): ?array { - switch ($cke5_plugin_id) { - case 'editor_advanced_image_image': - default: - $configuration = []; - - $restrictions = $text_format->getHtmlRestrictions(); - if ($restrictions === FALSE) { - // When no restrictions are given, then enable all the options. - return ['enabled_attributes' => array_keys(CKEditor5Plugin::SUPPORTED_ATTRIBUTES)]; - } - - // Otherwise, only enable attributes that allowed by the restrictions. - foreach (array_keys(CKEditor5Plugin::SUPPORTED_ATTRIBUTES) as $attribute) { - $img_allowed_attributes = $restrictions['allowed']['img'] ?: []; - // Check whether the attribute is allowed. - // @see \Drupal\filter\Plugin\FilterInterface::getHTMLRestrictions() - if (array_key_exists($attribute, $img_allowed_attributes) && $img_allowed_attributes[$attribute] === TRUE) { - $configuration['enabled_attributes'][] = $attribute; - } - } - return $configuration; - } - } - -} diff --git a/src/Plugin/CKEditorPlugin/EditorAdvancedImage.php b/src/Plugin/CKEditorPlugin/EditorAdvancedImage.php deleted file mode 100644 index f5c85f0..0000000 --- a/src/Plugin/CKEditorPlugin/EditorAdvancedImage.php +++ /dev/null @@ -1,159 +0,0 @@ -moduleExtensionList = $extensionListModule; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - return new static( - $configuration, - $plugin_id, - $plugin_definition, - $container->get('extension.list.module') - ); - } - - /** - * {@inheritdoc} - */ - public function isInternal() { - return FALSE; - } - - /** - * {@inheritdoc} - */ - public function getDependencies(Editor $editor) { - return []; - } - - /** - * {@inheritdoc} - */ - public function getLibraries(Editor $editor) { - return [ - 'ckeditor/drupal.ckeditor.plugins.editoradvancedimage', - ]; - } - - /** - * {@inheritdoc} - */ - public function getFile() { - return $this->moduleExtensionList->getPath('editor_advanced_image') . '/js/editor_advanced_image.js'; - } - - /** - * {@inheritdoc} - */ - public function getConfig(Editor $editor) { - $config = []; - $settings = $editor->getSettings(); - - if (!isset($settings['plugins']['editoradvancedimage']['default_class'])) { - return $config; - } - - $config['defaultClasses'] = $settings['plugins']['editoradvancedimage']['default_class']; - return $config; - } - - /** - * {@inheritdoc} - * - * @see \Drupal\editor\Form\EditorImageDialog - * @see editor_image_upload_settings_form() - */ - public function settingsForm(array $form, FormStateInterface $form_state, Editor $editor) { - // Defaults. - $settings = $editor->getSettings(); - - $form['default_class'] = [ - '#title' => $this->t('Default image class(es)'), - '#type' => 'textfield', - '#default_value' => !empty($settings['plugins']['editoradvancedimage']['default_class']) ? $settings['plugins']['editoradvancedimage']['default_class'] : '', - '#description' => $this->t('A list of classes that will be added when the user adds an inline-image with CKEditor.
Enter one or more classes separated by spaces. Example: img-responsive or img-fluid.'), - ]; - - return $form; - } - - /** - * {@inheritdoc} - */ - public function isEnabled(Editor $editor) { - // Check if a DrupalImage has been placed in the CKeditor. - $settings = $editor->getSettings(); - if ($this->checkImageEnable($settings['toolbar']['rows'][0])) { - return TRUE; - } - return FALSE; - } - - /** - * Check if a DrupalImage exists in the given toolbar row. - * - * @param array $toolbar - * A CKeditor toolbar row containing Ckeditor plugin items. - * - * @return bool - * Does the DrupalImage has been placed in the CKeditor. - */ - public function checkImageEnable(array $toolbar) { - foreach ($toolbar as $items) { - foreach ($items['items'] as $item) { - if ('DrupalImage' === $item) { - return TRUE; - } - } - } - return FALSE; - } - -} diff --git a/tests/src/FunctionalJavascript/CKEditor4EditorAdvancedImageDialogTest.php b/tests/src/FunctionalJavascript/CKEditor4EditorAdvancedImageDialogTest.php deleted file mode 100644 index 2becddf..0000000 --- a/tests/src/FunctionalJavascript/CKEditor4EditorAdvancedImageDialogTest.php +++ /dev/null @@ -1,327 +0,0 @@ -editorFilterFormat = FilterFormat::create([ - 'format' => 'full_html', - 'name' => 'Full HTML', - 'weight' => 0, - 'filters' => [], - ]); - $this->editorFilterFormat->save(); - - $this->editor = Editor::create([ - 'format' => 'full_html', - 'editor' => 'ckeditor', - ]); - $settings['toolbar']['rows'] = [ - [ - [ - 'name' => 'Image', - 'items' => [ - 'DrupalImage', - ], - ], - ], - ]; - $this->editor->setSettings($settings); - $this->editor->save(); - - // Create a node type for testing. - NodeType::create(['type' => 'page', 'name' => 'page'])->save(); - - $field_storage = FieldStorageConfig::loadByName('node', 'body'); - - // Create a body field instance for the 'page' node type. - FieldConfig::create([ - 'field_storage' => $field_storage, - 'bundle' => 'page', - 'label' => 'Body', - 'settings' => ['display_summary' => TRUE], - 'required' => TRUE, - ])->save(); - - // Assign widget settings for the 'default' form mode. - EntityFormDisplay::create([ - 'targetEntityType' => 'node', - 'bundle' => 'page', - 'mode' => 'default', - 'status' => TRUE, - ])->setComponent('body', ['type' => 'text_textarea_with_summary']) - ->save(); - - // Create a user for tests. - $this->adminUser = $this->drupalCreateUser([ - 'administer nodes', - 'create page content', - 'use text format full_html', - ]); - } - - /** - * Tests the node add page is reachable. - */ - public function testNodeAddPageReachable() { - $this->drupalLogin($this->adminUser); - $this->drupalGet('node/add/page'); - $this->assertSession()->elementExists('css', 'form.node-page-form'); - } - - /** - * Tests CKEditor button image still apprear, works & dialog open. - */ - public function testImageBaseDialogWorks() { - $this->drupalLogin($this->adminUser); - $this->drupalGet('node/add/page'); - - // Asserts the Image button is present. - $this->assertSession()->elementExists('css', '#cke_edit-body-0-value .cke_button__drupalimage'); - - // Asserts the Image button is present. - $this->clickOnElement('css', '.cke_button__drupalimage'); - $this->assertSession()->assertWaitOnAjaxRequest(); - - $this->assertSession()->elementExists('css', '.ui-dialog'); - $this->assertSession()->elementContains('css', '.ui-dialog .ui-dialog-titlebar', 'Insert Image'); - } - - /** - * Test the appearance of every EIA attributes when no filters enabled. - */ - public function testFilterHtmlDisable() { - // Disable the filter_html filter: allow *all *tags. - $this->editorFilterFormat->setFilterConfig('filter_html', [ - 'status' => 0, - ]); - $this->editorFilterFormat->save(); - - $this->testImageBaseDialogWorks(); - - $this->assertSession()->elementExists('css', '.ui-dialog .form-item-attributes-title'); - $this->assertSession()->elementExists('css', '.ui-dialog .form-item-attributes-class'); - $this->assertSession()->elementExists('css', '.ui-dialog .form-item-attributes-id'); - } - - /** - * Test the appearance of EIA attributes when no filters enabled/partial. - */ - public function testFilterHtmlEnable() { - // Enable the filter_html filter: only a few img attributes. - $this->editorFilterFormat->setFilterConfig('filter_html', [ - 'status' => 1, - 'settings' => [ - 'allowed_html' => '', - ], - ]); - $this->editorFilterFormat->save(); - - $this->testImageBaseDialogWorks(); - - $this->assertSession()->elementNotExists('css', '.ui-dialog .form-item-attributes-title'); - $this->assertSession()->elementNotExists('css', '.ui-dialog .form-item-attributes-class'); - $this->assertSession()->elementNotExists('css', '.ui-dialog .form-item-attributes-id'); - - // Enable the filter_html filter: only a title img attributes. - $this->editorFilterFormat->setFilterConfig('filter_html', [ - 'status' => 1, - 'settings' => [ - 'allowed_html' => '', - ], - ]); - $this->editorFilterFormat->save(); - - $this->testImageBaseDialogWorks(); - - $this->assertSession()->elementExists('css', '.ui-dialog .form-item-attributes-title'); - $this->assertSession()->elementNotExists('css', '.ui-dialog .form-item-attributes-class'); - $this->assertSession()->elementNotExists('css', '.ui-dialog .form-item-attributes-id'); - - // Enable the filter_html filter: only a class img attributes. - $this->editorFilterFormat->setFilterConfig('filter_html', [ - 'status' => 1, - 'settings' => [ - 'allowed_html' => '', - ], - ]); - $this->editorFilterFormat->save(); - - $this->testImageBaseDialogWorks(); - - $this->assertSession()->elementNotExists('css', '.ui-dialog .form-item-attributes-title'); - $this->assertSession()->elementExists('css', '.ui-dialog .form-item-attributes-class'); - $this->assertSession()->elementNotExists('css', '.ui-dialog .form-item-attributes-id'); - - // Enable the filter_html filter: only a id img attributes. - $this->editorFilterFormat->setFilterConfig('filter_html', [ - 'status' => 1, - 'settings' => [ - 'allowed_html' => '', - ], - ]); - $this->editorFilterFormat->save(); - - $this->testImageBaseDialogWorks(); - - $this->assertSession()->elementNotExists('css', '.ui-dialog .form-item-attributes-title'); - $this->assertSession()->elementNotExists('css', '.ui-dialog .form-item-attributes-class'); - $this->assertSession()->elementExists('css', '.ui-dialog .form-item-attributes-id'); - - // Enable the filter_html filter: only a id, class, title img attributes. - $this->editorFilterFormat->setFilterConfig('filter_html', [ - 'status' => 1, - 'settings' => [ - 'allowed_html' => '', - ], - ]); - $this->editorFilterFormat->save(); - - $this->testImageBaseDialogWorks(); - - $this->assertSession()->elementExists('css', '.ui-dialog .form-item-attributes-title'); - $this->assertSession()->elementExists('css', '.ui-dialog .form-item-attributes-class'); - $this->assertSession()->elementExists('css', '.ui-dialog .form-item-attributes-id'); - } - - /** - * Test Default Class is shown when configured. - */ - public function testDefaultClass() { - // Disable the filter_html filter: allow *all *tags. - $this->editorFilterFormat->setFilterConfig('filter_html', [ - 'status' => 0, - ]); - $this->editorFilterFormat->save(); - - // Add a default class in the settings. - $settings = [ - 'toolbar' => [ - 'rows' => [ - [ - [ - 'name' => 'Image', - 'items' => [ - 'DrupalImage', - ], - ], - ], - ], - ], - 'plugins' => [ - 'editoradvancedimage' => [ - 'default_class' => 'test-default-class', - ], - ], - ]; - $this->editor->setSettings($settings); - $this->editor->save(); - - $this->testImageBaseDialogWorks(); - - $this->assertSession()->elementExists('css', '.ui-dialog .form-item-attributes-class'); - $this->assertSession()->elementContains('css', '.ui-dialog .form-item-attributes-class', 'Default: test-default-class'); - } - - /** - * Ensure the CKeditor still works when DrupalImage is not in Toolbar. - */ - public function testWhenImageNotEnabled() { - // Add a default class in the settings. - $settings = [ - 'toolbar' => [ - 'rows' => [ - [ - [ - 'name' => 'All the things', - 'items' => [ - 'Source', - 'Bold', - 'Italic', - 'DrupalLink', - 'DrupalUnlink', - ], - ], - ], - ], - ], - 'plugins' => [], - ]; - $this->editor->setSettings($settings); - $this->editor->save(); - - $this->drupalLogin($this->adminUser); - $this->drupalGet('node/add/page'); - - $this->waitForEditor(); - $this->assignNameToCkeditorIframe(); - $this->getSession()->switchToIFrame('ckeditor'); - - $assert_session = $this->assertSession(); - $this->assertNotEmpty($assert_session->waitForElementVisible('css', '.cke_editable', 1000)); - } - -} diff --git a/tests/src/FunctionalJavascript/CKEditor4EditorAdvancedImageEditorFormatTest.php b/tests/src/FunctionalJavascript/CKEditor4EditorAdvancedImageEditorFormatTest.php deleted file mode 100644 index e8efd0b..0000000 --- a/tests/src/FunctionalJavascript/CKEditor4EditorAdvancedImageEditorFormatTest.php +++ /dev/null @@ -1,128 +0,0 @@ - 'full_html', - 'name' => 'Full HTML', - 'weight' => 0, - 'filters' => [], - ]); - $full_html_format->save(); - - // Create a user for tests. - $admin = $this->drupalCreateUser(['administer filters']); - $this->drupalLogin($admin); - } - - /** - * Tests the node add page is reachable. - */ - public function testAdminFormatsManageReachable() { - $this->drupalGet('admin/config/content/formats/manage/full_html'); - - $this->assertSession()->elementExists('css', 'form.filter-format-edit-form'); - } - - /** - * Tests a CKEditor editor and visibilit of Editor Advanced Image config. - */ - public function testAdminForm() { - $this->drupalGet('admin/config/content/formats/manage/full_html'); - - // Select the "CKEditor" editor. - $this->fillField('Text editor', 'ckeditor'); - - // Wait on CKEditor Ajax call to load plugins forms. - $this->assertSession()->assertWaitOnAjaxRequest(); - - // Check the Editor Advanced Image tab is visible. - $this->assertSession()->elementExists('css', '.vertical-tabs'); - - // Ensure the default class is initialized to the expected default value. - $this->assertSession()->fieldValueEquals('editor[settings][plugins][editoradvancedimage][default_class]', ''); - } - - /** - * Tests a CKEditor editor & storage of default class field. - */ - public function testDefaultClass() { - $page = $this->getSession()->getPage(); - $web_assert = $this->assertSession(); - - $this->drupalGet('admin/config/content/formats/manage/full_html'); - - // Select the "CKEditor" editor. - $this->fillField('Text editor', 'ckeditor'); - - // Wait on CKEditor Ajax call to load plugins forms. - $this->assertSession()->assertWaitOnAjaxRequest(); - - $this->assertSession()->elementExists('css', '.vertical-tabs__menu a[href^="#edit-editor-settings-plugins-editoradvancedimage--"]'); - - $web_assert->waitForElementVisible('css', '.vertical-tabs__menu a[href^="#edit-editor-settings-plugins-editoradvancedimage--"]', 50); - - // Find & click on the Editor Advanced Plugin Form tab. - $page->find('css', '.vertical-tabs__menu a[href^="#edit-editor-settings-plugins-editoradvancedimage--"]')->click(); - - // Assert that the Editor Advanced Image Form becomes visible. - $web_assert->waitForElementVisible('css', '#edit-editor-settings-plugins-editoradvancedimage-default-class', 50); - - // Change the default class for 'my-class'. - $this->fillField('editor[settings][plugins][editoradvancedimage][default_class]', 'my-class'); - - // Submit the new value. - $this->pressButton('edit-actions-submit'); - - // Return on the editor configuration. - $this->drupalGet('admin/config/content/formats/manage/full_html'); - - // Ensure the previously filled data has been stored. - $this->assertSession()->fieldValueEquals('editor[settings][plugins][editoradvancedimage][default_class]', 'my-class'); - } - -} diff --git a/tests/src/Kernel/CKEditor4To5Upgrade/UpgradePathCompletenessTest.php b/tests/src/Kernel/CKEditor4To5Upgrade/UpgradePathCompletenessTest.php deleted file mode 100644 index e7c84f1..0000000 --- a/tests/src/Kernel/CKEditor4To5Upgrade/UpgradePathCompletenessTest.php +++ /dev/null @@ -1,66 +0,0 @@ -enableModules(['ckeditor_test']); - // @phpstan-ignore-next-line - $this->cke4PluginManager = $this->container->get('plugin.manager.ckeditor.plugin'); - - // @phpstan-ignore-next-line - $this->expectException(\OutOfBoundsException::class); - - // Since Drupal 10.0.x the Plugin name has changed LlamaCSS vs Llama. - // @phpstan-ignore-next-line - $this->expectExceptionMessageMatches('/^No upgrade path found for the "Llama(CSS)?" button\.$/'); - // @phpstan-ignore-next-line - $this->testButtons(); - } - - } -} -else { - class CKEditor4to5UpgradeCompletenessTest extends KernelTestBase { - - public function testImpossible() { - $this->markTestSkipped(); - } - - } -} - -/** - * @covers \Drupal\editor_advanced_image\Plugin\CKEditor4To5Upgrade\EditorAdvancedImage - * - * @group editor_advanced_image - * @group editor_advanced_image_kernel - * @group editor_advanced_image_ckeditor5 - * @group ckeditor5 - * - * @internal - * - * @requires module ckeditor5 - * @requires function Drupal\Tests\ckeditor5\Kernel\CKEditor4to5UpgradeCompletenessTest::setUp - */ -class UpgradePathCompletenessTest extends CKEditor4to5UpgradeCompletenessTest { - - /** - * {@inheritdoc} - */ - protected static $modules = ['editor_advanced_image']; - -} diff --git a/tests/src/Kernel/CKEditor4To5Upgrade/UpgradePathTest.php b/tests/src/Kernel/CKEditor4To5Upgrade/UpgradePathTest.php deleted file mode 100644 index 38b1ce4..0000000 --- a/tests/src/Kernel/CKEditor4To5Upgrade/UpgradePathTest.php +++ /dev/null @@ -1,338 +0,0 @@ -markTestSkipped('Upgrade path will only run properly on Drupal 10+ because of config sorting.'); - } - - // Create test FilterFormat config entities: one per option to test in - // isolation, plus one to test with the default configuration (class attr - // enabled), plus one with ALL attributes enabled but with additional - // attributes not supported by EditorAdvancedImage. - $get_filter_config = function (string $img_allowed_html_addition): array { - return [ - 'filter_html' => [ - 'status' => 1, - 'settings' => [ - 'allowed_html' => '


', - ], - ], - ]; - }; - - // Build filter format, one per enabled attribute (option) to test in - // isolation. - $options = EditorAdvancedImage::SUPPORTED_ATTRIBUTES; - foreach (array_keys($options) as $option) { - $string_representation = EditorAdvancedImage::getAllowedHtmlForSupportedAttribute($option); - FilterFormat::create([ - 'format' => "editor_advanced_image__$option", - 'name' => $string_representation, - 'filters' => $get_filter_config(EditorAdvancedImage::getAllowedStringForSupportedAttribute($option)), - ])->setSyncing(TRUE)->save(); - } - FilterFormat::create([ - 'format' => 'editor_advanced_image__none', - 'name' => 'None, just plain img', - 'filters' => [ - 'filter_html' => [ - 'status' => 1, - 'settings' => [ - 'allowed_html' => '


', - ], - ], - ], - ])->setSyncing(TRUE)->save(); - FilterFormat::create([ - 'format' => 'editor_advanced_image__all_and_more', - 'name' => 'All and more', - 'filters' => [ - 'filter_html' => [ - 'status' => 1, - 'settings' => [ - 'allowed_html' => '


', - ], - ], - ], - ])->setSyncing(TRUE)->save(); - - // Create matching Text Editors. - $cke4_settings = [ - 'toolbar' => [ - 'rows' => [ - 0 => [ - [ - 'name' => 'Basic Formatting', - 'items' => [ - 'Bold', - 'Format', - 'DrupalImage', - ], - ], - ], - ], - ], - 'plugins' => [], - ]; - foreach (array_keys($options) as $option) { - Editor::create([ - 'format' => "editor_advanced_image__$option", - 'editor' => 'ckeditor', - 'settings' => $cke4_settings, - ])->setSyncing(TRUE)->save(); - } - Editor::create([ - 'format' => 'editor_advanced_image__none', - 'editor' => 'ckeditor', - 'settings' => $cke4_settings, - ])->setSyncing(TRUE)->save(); - - $cke4_settings['plugins']['editoradvancedimage'] = [ - 'default_class' => 'foobar', - ]; - Editor::create([ - 'format' => 'editor_advanced_image__all_and_more', - 'editor' => 'ckeditor', - 'settings' => $cke4_settings, - ])->setSyncing(TRUE)->save(); - } - - /** - * {@inheritdoc} - */ - public function provider() { - $expected_ckeditor5_toolbar = [ - 'items' => [ - 'bold', - 'drupalInsertImage', - ], - ]; - - yield ' + CKEditor 4 DrupalImage' => [ - 'format_id' => 'editor_advanced_image__title', - 'filters_to_drop' => [], - 'expected_ckeditor5_settings' => [ - 'toolbar' => $expected_ckeditor5_toolbar, - 'plugins' => [ - 'ckeditor5_imageResize' => ['allow_resize' => TRUE], - 'editor_advanced_image_image' => [ - 'disable_balloon' => FALSE, - 'default_class' => '', - 'enabled_attributes' => [ - 'title', - ], - ], - ], - ], - 'expected_superset' => '', - 'expected_fundamental_compatibility_violations' => [], - 'expected_db_logs' => [], - 'expected_messages' => [], - ]; - - yield ' + CKEditor 4 DrupalImage' => [ - 'format_id' => 'editor_advanced_image__class', - 'filters_to_drop' => [], - 'expected_ckeditor5_settings' => [ - 'toolbar' => $expected_ckeditor5_toolbar, - 'plugins' => [ - 'ckeditor5_imageResize' => ['allow_resize' => TRUE], - 'editor_advanced_image_image' => [ - 'disable_balloon' => FALSE, - 'default_class' => '', - 'enabled_attributes' => [ - 'class', - ], - ], - ], - ], - 'expected_superset' => '', - 'expected_fundamental_compatibility_violations' => [], - 'expected_db_logs' => [], - 'expected_messages' => [], - ]; - - yield ' + CKEditor 4 DrupalImage' => [ - 'format_id' => 'editor_advanced_image__id', - 'filters_to_drop' => [], - 'expected_ckeditor5_settings' => [ - 'toolbar' => $expected_ckeditor5_toolbar, - 'plugins' => [ - 'ckeditor5_imageResize' => ['allow_resize' => TRUE], - 'editor_advanced_image_image' => [ - 'disable_balloon' => FALSE, - 'default_class' => '', - 'enabled_attributes' => [ - 'id', - ], - ], - ], - ], - 'expected_superset' => '', - 'expected_fundamental_compatibility_violations' => [], - 'expected_db_logs' => [], - 'expected_messages' => [], - ]; - - yield 'None, just plain img + CKEditor 4 DrupalImage' => [ - 'format_id' => 'editor_advanced_image__none', - 'filters_to_drop' => [], - 'expected_ckeditor5_settings' => [ - 'toolbar' => [ - 'items' => [ - 'bold', - 'drupalInsertImage', - ], - ], - 'plugins' => [ - 'ckeditor5_imageResize' => ['allow_resize' => TRUE], - 'editor_advanced_image_image' => EditorAdvancedImage::DEFAULT_CONFIGURATION, - ], - ], - 'expected_superset' => '', - 'expected_fundamental_compatibility_violations' => [], - 'expected_db_logs' => [], - 'expected_messages' => [ - 'warning' => [ - 'Updating to CKEditor 5 added support for some previously unsupported tags/attributes. A plugin introduced support for the following: This attribute: class (for <img>); Additional details are available in your logs.', - ], - ], - ]; - - yield ' + CKEditor 4 DrupalImage' => [ - 'format_id' => 'editor_advanced_image__all_and_more', - 'filters_to_drop' => [], - 'expected_ckeditor5_settings' => [ - 'toolbar' => [ - 'items' => [ - 'bold', - 'drupalInsertImage', - 'sourceEditing', - ], - ], - 'plugins' => [ - 'ckeditor5_imageResize' => ['allow_resize' => TRUE], - 'ckeditor5_sourceEditing' => [ - 'allowed_tags' => [ - '', - ], - ], - 'editor_advanced_image_image' => [ - 'disable_balloon' => FALSE, - 'default_class' => 'foobar', - 'enabled_attributes' => [ - 'class', - 'id', - 'title', - ], - ], - ], - ], - 'expected_superset' => '', - 'expected_fundamental_compatibility_violations' => [], - 'expected_db_logs' => [ - 'status' => [ - 'As part of migrating to CKEditor 5, it was found that the All and more text format\'s HTML filters includes plugins that support the following tags, but not some of their attributes. To ensure these attributes remain supported, the following were added to the Source Editing plugin\'s Manually editable HTML tags: <img foo bar="baz">. The text format must be saved to make these changes active.', - ], - ], - 'expected_messages' => [ - 'status' => [ - 'To maintain the capabilities of this text format, the CKEditor 5 migration did the following: Added these tags/attributes to the Source Editing Plugin\'s Manually editable HTML tags setting: <img foo bar="baz">. Additional details are available in your logs.', - ], - ], - ]; - - // Verify that none of the core test cases are broken; especially important - // for EditorAdvancedImage since it extends the behavior of Drupal core. - // @see Drupal\Tests\ckeditor5\Kernel\SmartDefaultSettingsTest - $formats_not_supporting_img = [ - 'cke4_stylescombo_span', - 'filter_only__filter_html', - 'restricted_html', - 'cke4_plugins_with_settings', - 'cke4_contrib_plugins_now_in_core', - 'minimal_ckeditor_wrong_allowed_html', - ]; - $full_html_configuration = [ - 'disable_balloon' => FALSE, - 'default_class' => '', - 'enabled_attributes' => array_keys(EditorAdvancedImage::SUPPORTED_ATTRIBUTES), - ]; - sort($full_html_configuration['enabled_attributes']); - - foreach (parent::provider() as $label => $case) { - // The `editor_advanced_image_image` plugin settings will appear for every - // upgraded text editor while editor_advanced_image is installed, as long - // as it has the `DrupalImage` button enabled in CKEditor 4. - if (!in_array($case['format_id'], $formats_not_supporting_img, TRUE)) { - $case['expected_superset'] .= ' '; - $case['expected_superset'] = trim($case['expected_superset'], ' '); - - // A Warning message will be triggered as was not present on the - // HTML limited tag. - $case['expected_messages']['warning'][] = 'Updating to CKEditor 5 added support for some previously unsupported tags/attributes. A plugin introduced support for the following: This attribute: class (for <img>); Additional details are available in your logs.'; - - // The previous warning is a bit different on basic_html_with_pre. - if ($case['format_id'] === 'basic_html_with_pre') { - $case['expected_messages']['warning'] = ['Updating to CKEditor 5 added support for some previously unsupported tags/attributes. A plugin introduced support for the following: This attribute: class (for <code>, <img>); Additional details are available in your logs.']; - } - - // The previous warning is a bit different on - // basic_html_with_alignable_p. - if ($case['format_id'] === 'basic_html_with_alignable_p') { - $case['expected_messages']['warning'] = ['Updating to CKEditor 5 added support for some previously unsupported tags/attributes. A plugin introduced support for the following: This attribute: class (for <h2>, <h3>, <h4>, <h5>, <h6>, <img>); Additional details are available in your logs.']; - } - - // Add the default Editor Advanced Image configuration excepted for - // full_html that must enable every Editor Advanced Image options. - $case['expected_ckeditor5_settings']['plugins']['editor_advanced_image_image'] = EditorAdvancedImage::DEFAULT_CONFIGURATION; - if ($case['format_id'] === 'full_html') { - $case['expected_ckeditor5_settings']['plugins']['editor_advanced_image_image'] = $full_html_configuration; - unset($case['expected_messages']['warning']); - } - - // Reorder the plugins settings as we manually added Editor Advanced - // Image one. - ksort($case['expected_ckeditor5_settings']['plugins']); - } - - yield $label => $case; - } - } - -}