Verified Commit 2d19c2fe authored by Alex Pott's avatar Alex Pott
Browse files

Issue #3265626 by bnjmnm, Wim Leers, nod_, lauriii, alexpott, andregp: Changes...

Issue #3265626 by bnjmnm, Wim Leers, nod_, lauriii, alexpott, andregp: Changes to "Manually editable HTML tags" lost if form is submitted without triggering AJAX

(cherry picked from commit 9117ebf1)
parent ec891b85
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@

declare(strict_types = 1);

use Drupal\ckeditor5\HTMLRestrictions;
use Drupal\ckeditor5\Plugin\Editor\CKEditor5;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Ajax\AjaxResponse;
@@ -245,6 +246,28 @@ function _add_ajax_listeners_to_plugin_inputs(array &$plugins_config_form): void

  $form['#after_build'][] = [CKEditor5::class, 'assessActiveTextEditorAfterBuild'];
  $form['#validate'][] = [CKEditor5::class, 'validateSwitchingToCKEditor5'];
  array_unshift($form['actions']['submit']['#submit'], 'ckeditor5_filter_format_edit_form_submit');
}

/**
 * Form submission handler for filter format forms.
 */
function ckeditor5_filter_format_edit_form_submit(array $form, FormStateInterface $form_state) {
  $limit_allowed_html_tags = isset($form['filters']['settings']['filter_html']['allowed_html']);
  $manually_editable_tags = $form_state->getValue(['editor', 'settings', 'plugins', 'ckeditor5_sourceEditing', 'allowed_tags']);
  if ($limit_allowed_html_tags && is_array($manually_editable_tags)) {
    // When "Manually editable tags" and "limit allowed HTML tags" are both
    // configured, the former informs the value of the latter. This dependent
    // value is typically updated via AJAX, but it's possible for "Manually
    // editable tags" to update without triggering the AJAX rebuild. That value
    // is recalculated here on save to ensure it happens even if the AJAX
    // rebuild doesn't happen.
    $manually_editable_tags_restrictions = HTMLRestrictions::fromString(implode($manually_editable_tags));
    $format = $form_state->get('ckeditor5_validated_pair')->getFilterFormat();
    $allowed_html = HTMLRestrictions::fromTextFormat($format);
    $combined_tags_string = $manually_editable_tags_restrictions->merge($allowed_html)->toFilterHtmlAllowedTagsString();
    $form_state->setValue(['filters', 'filter_html', 'settings', 'allowed_html'], $combined_tags_string);
  }
}

/**
+20 −0
Original line number Diff line number Diff line
@@ -6,6 +6,26 @@
((Drupal, once) => {
  Drupal.behaviors.allowedTagsListener = {
    attach: function attach(context) {
      once(
        'ajax-conflict-prevention',
        '[data-drupal-selector="filter-format-edit-form"], [data-drupal-selector="filter-format-add-form"]',
        context,
      ).forEach((form) => {
        // When the form is submitted, remove the disabled attribute from all
        // AJAX enabled form elements. The disabled state is added as part of
        // AJAX processing, but will prevent the value from being added to
        // $form_state.
        form.addEventListener('submit', () => {
          once
            .filter(
              'drupal-ajax',
              '[data-drupal-selector="filter-format-edit-form"] [disabled], [data-drupal-selector="filter-format-add-form"] [disabled]',
            )
            .forEach((disabledElement) => {
              disabledElement.removeAttribute('disabled');
            });
        });
      });
      once(
        'allowed-tags-listener',
        context.querySelector(
+7 −0
Original line number Diff line number Diff line
@@ -8,6 +8,13 @@
(function (Drupal, once) {
  Drupal.behaviors.allowedTagsListener = {
    attach: function attach(context) {
      once('ajax-conflict-prevention', '[data-drupal-selector="filter-format-edit-form"], [data-drupal-selector="filter-format-add-form"]', context).forEach(function (form) {
        form.addEventListener('submit', function () {
          once.filter('drupal-ajax', '[data-drupal-selector="filter-format-edit-form"] [disabled], [data-drupal-selector="filter-format-add-form"] [disabled]').forEach(function (disabledElement) {
            disabledElement.removeAttribute('disabled');
          });
        });
      });
      once('allowed-tags-listener', context.querySelector('[data-drupal-selector="edit-filters-filter-html-settings-allowed-html"]')).forEach(function (textarea) {
        var editorSelect = document.querySelector('[data-drupal-selector="edit-editor-editor"]');
        var filterCheckbox = document.querySelector('[data-drupal-selector="edit-filters-filter-html-status"]');
+47 −1
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@
use Drupal\ckeditor5\Plugin\Editor\CKEditor5;
use Symfony\Component\Validator\ConstraintViolation;

// cspell:ignore gramma
// cspell:ignore gramma sourceediting

/**
 * @coversDefaultClass \Drupal\ckeditor5\Plugin\CKEditor5Plugin\SourceEditing
@@ -117,6 +117,52 @@ function (ConstraintViolation $v) {
    $this->drupalLogin($this->adminUser);
  }

  /**
   * @covers \Drupal\ckeditor5\Plugin\CKEditor5Plugin\SourceEditing::buildConfigurationForm
   */
  public function testSourceEditingSettingsForm() {
    $this->drupalLogin($this->drupalCreateUser(['administer filters']));

    $page = $this->getSession()->getPage();
    $assert_session = $this->assertSession();

    $this->createNewTextFormat($page, $assert_session);
    $assert_session->assertWaitOnAjaxRequest();

    // The Source Editing plugin settings form should not be present.
    $assert_session->elementNotExists('css', '[data-drupal-selector="edit-editor-settings-plugins-ckeditor5-sourceediting"]');

    $this->assertNotEmpty($assert_session->waitForElement('css', '.ckeditor5-toolbar-item-sourceEditing'));
    $this->triggerKeyUp('.ckeditor5-toolbar-item-sourceEditing', 'ArrowDown');
    $assert_session->assertWaitOnAjaxRequest();

    // The Source Editing plugin settings form should now be present and should
    // have no allowed tags configured.
    $page->clickLink('Source editing');
    $this->assertNotNull($assert_session->waitForElementVisible('css', '[data-drupal-selector="edit-editor-settings-plugins-ckeditor5-sourceediting-allowed-tags"]'));

    $javascript = <<<JS
      const allowedTags = document.querySelector('[data-drupal-selector="edit-editor-settings-plugins-ckeditor5-sourceediting-allowed-tags"]');
      allowedTags.value = '<div data-foo>';
      allowedTags.dispatchEvent(new Event('input'));
JS;
    $this->getSession()->executeScript($javascript);

    // Immediately save the configuration. Intentionally do nothing that would
    // trigger an AJAX rebuild.
    $page->pressButton('Save configuration');

    // Verify that the configuration was saved.
    $this->drupalGet('admin/config/content/formats/manage/ckeditor5');
    $page->clickLink('Source editing');
    $this->assertNotNull($ghs_textarea = $assert_session->waitForElementVisible('css', '[data-drupal-selector="edit-editor-settings-plugins-ckeditor5-sourceediting-allowed-tags"]'));

    $ghs_string = '<div data-foo>';
    $this->assertSame($ghs_string, $ghs_textarea->getValue());
    $allowed_html_field = $assert_session->fieldExists('filters[filter_html][settings][allowed_html]');
    $this->assertStringContainsString($ghs_string, $allowed_html_field->getValue(), "$ghs_string not found in the allowed tags value of: {$allowed_html_field->getValue()}");
  }

  /**
   * Tests allowing extra attributes on already supported tags using GHS.
   *