Skip to content
Snippets Groups Projects
Verified Commit e1fc76a1 authored by Lauri Timmanee's avatar Lauri Timmanee
Browse files

Issue #3273986 by ifrik, xurizaemon, rpayanm, Wim Leers, Charles Belov,...

Issue #3273986 by ifrik, xurizaemon, rpayanm, Wim Leers, Charles Belov, jonathan_hunt: Third option for the CKEditor 5 "Language" button: site_configured (in addition to un and all)
parent 288f761c
No related branches found
No related tags found
42 merge requests!12227Issue #3181946 by jonmcl, mglaman,!54479.5.x SF update,!5014Issue #3071143: Table Render Array Example Is Incorrect,!3878Removed unused condition head title for views,!38582585169-10.1.x,!3818Issue #2140179: $entity->original gets stale between updates,!3742Issue #3328429: Create item list field formatter for displaying ordered and unordered lists,!3731Claro: role=button on status report items,!3668Resolve #3347842 "Deprecate the trusted",!3651Issue #3347736: Create new SDC component for Olivero (header-search),!3546refactored dialog.pcss file,!3531Issue #3336994: StringFormatter always displays links to entity even if the user in context does not have access,!3502Issue #3335308: Confusing behavior with FormState::setFormState and FormState::setMethod,!3478Issue #3337882: Deleted menus are not removed from content type config,!3452Issue #3332701: Refactor Claro's tablesort-indicator stylesheet,!3451Issue #2410579: Allows setting the current language programmatically.,!3355Issue #3209129: Scrolling problems when adding a block via layout builder,!3226Issue #2987537: Custom menu link entity type should not declare "bundle" entity key,!3154Fixes #2987987 - CSRF token validation broken on routes with optional parameters.,!3147Issue #3328457: Replace most substr($a, $i) where $i is negative with str_ends_with(),!3146Issue #3328456: Replace substr($a, 0, $i) with str_starts_with(),!3133core/modules/system/css/components/hidden.module.css,!31312878513-10.1.x,!2964Issue #2865710 : Dependencies from only one instance of a widget are used in display modes,!2812Issue #3312049: [Followup] Fix Drupal.Commenting.FunctionComment.MissingReturnType returns for NULL,!2614Issue #2981326: Replace non-test usages of \Drupal::logger() with IoC injection,!2378Issue #2875033: Optimize joins and table selection in SQL entity query implementation,!2334Issue #3228209: Add hasRole() method to AccountInterface,!2062Issue #3246454: Add weekly granularity to views date sort,!1591Issue #3199697: Add JSON:API Translation experimental module,!1255Issue #3238922: Refactor (if feasible) uses of the jQuery serialize function to use vanillaJS,!1105Issue #3025039: New non translatable field on translatable content throws error,!1073issue #3191727: Focus states on mobile second level navigation items fixed,!10223132456: Fix issue where views instances are emptied before an ajax request is complete,!877Issue #2708101: Default value for link text is not saved,!844Resolve #3036010 "Updaters",!673Issue #3214208: FinishResponseSubscriber could create duplicate headers,!579Issue #2230909: Simple decimals fail to pass validation,!560Move callback classRemove outside of the loop,!555Issue #3202493,!485Sets the autocomplete attribute for username/password input field on login form.,!213Issue #2906496: Give Media a menu item under Content
Pipeline #24610 failed
Pipeline: drupal

#24615

    Pipeline: drupal

    #24614

      Pipeline: drupal

      #24613

        ......@@ -41,10 +41,14 @@ ckeditor5.plugin.ckeditor5_language:
        # Configuring this does not make sense without the corresponding button.
        CKEditor5ToolbarItemDependencyConstraint:
        toolbarItem: textPartLanguage
        # Only two possible values are accepted.
        # Only the following values are accepted.
        Choice:
        # United Nations "official languages".
        - un
        # Drupal's predefined language list.
        - all
        # Languages configured at /admin/config/regional/language for the site.
        - site_configured
        # Plugin \Drupal\ckeditor5\Plugin\CKEditor5Plugin\Heading
        ckeditor5.plugin.ckeditor5_heading:
        ......
        ......@@ -4,13 +4,19 @@
        namespace Drupal\ckeditor5\Plugin\CKEditor5Plugin;
        use Drupal\ckeditor5\Plugin\CKEditor5PluginConfigurableInterface;
        use Drupal\ckeditor5\Plugin\CKEditor5PluginConfigurableTrait;
        use Drupal\ckeditor5\Plugin\CKEditor5PluginDefault;
        use Drupal\ckeditor5\Plugin\CKEditor5PluginConfigurableInterface;
        use Drupal\ckeditor5\Plugin\CKEditor5PluginDefinition;
        use Drupal\Core\Form\FormStateInterface;
        use Drupal\Core\Language\LanguageManager;
        use Drupal\Core\Language\LanguageInterface;
        use Drupal\Core\Language\LanguageManager;
        use Drupal\Core\Language\LanguageManagerInterface;
        use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
        use Drupal\Core\Routing\RouteProviderInterface;
        use Drupal\Core\Url;
        use Drupal\editor\EditorInterface;
        use Symfony\Component\DependencyInjection\ContainerInterface;
        /**
        * CKEditor 5 Language plugin.
        ......@@ -18,23 +24,72 @@
        * @internal
        * Plugin classes are internal.
        */
        class Language extends CKEditor5PluginDefault implements CKEditor5PluginConfigurableInterface {
        class Language extends CKEditor5PluginDefault implements CKEditor5PluginConfigurableInterface, ContainerFactoryPluginInterface {
        use CKEditor5PluginConfigurableTrait;
        /**
        * Language constructor.
        *
        * @param array $configuration
        * A configuration array containing information about the plugin instance.
        * @param string $plugin_id
        * The plugin_id for the plugin instance.
        * @param \Drupal\ckeditor5\Plugin\CKEditor5PluginDefinition $plugin_definition
        * The plugin implementation definition.
        * @param \Drupal\Core\Language\LanguageManagerInterface $languageManager
        * The language manager.
        * @param \Drupal\Core\Routing\RouteProviderInterface $routeProvider
        * The route provider.
        */
        public function __construct(array $configuration, string $plugin_id, CKEditor5PluginDefinition $plugin_definition, protected LanguageManagerInterface $languageManager, protected RouteProviderInterface $routeProvider) {
        parent::__construct($configuration, $plugin_id, $plugin_definition);
        }
        /**
        * {@inheritdoc}
        */
        public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
        return new static(
        $configuration,
        $plugin_id,
        $plugin_definition,
        $container->get('language_manager'),
        $container->get('router.route_provider'),
        );
        }
        /**
        * {@inheritdoc}
        */
        public function getDynamicPluginConfig(array $static_plugin_config, EditorInterface $editor): array {
        $predefined_languages = $this->configuration['language_list'] === 'all' ?
        LanguageManager::getStandardLanguageList() :
        LanguageManager::getUnitedNationsLanguageList();
        $languages = NULL;
        switch ($this->configuration['language_list']) {
        case 'site_configured':
        $configured_languages = $this->languageManager->getLanguages();
        $languages = [];
        foreach ($configured_languages as $language) {
        $languages[$language->getId()] = [
        $language->getName(),
        '',
        $language->getDirection(),
        ];
        }
        break;
        case 'all':
        $languages = LanguageManager::getStandardLanguageList();
        break;
        case 'un':
        $languages = LanguageManager::getUnitedNationsLanguageList();
        }
        // Generate the language_list setting as expected by the CKEditor Language
        // plugin, but key the values by the full language name so that we can sort
        // them later on.
        $language_list = [];
        foreach ($predefined_languages as $langcode => $language) {
        foreach ($languages as $langcode => $language) {
        $english_name = $language[0];
        $direction = empty($language[2]) ? NULL : $language[2];
        $language_list[$english_name] = [
        ......@@ -60,20 +115,35 @@ public function getDynamicPluginConfig(array $static_plugin_config, EditorInterf
        * @see editor_image_upload_settings_form()
        */
        public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
        $predefined_languages = LanguageManager::getStandardLanguageList();
        $configured = count($this->languageManager->getLanguages());
        $predefined = count(LanguageManager::getStandardLanguageList());
        $united_nations = count(LanguageManager::getUnitedNationsLanguageList());
        $language_list_description_args = [
        ':united-nations-official' => 'https://www.un.org/en/sections/about-un/official-languages',
        '@count_predefined' => $predefined,
        '@count_united_nations' => $united_nations,
        '@count_configured' => $configured,
        ];
        // If Language is enabled, link to the configuration route.
        if ($this->routeProvider->getRoutesByNames(['entity.configurable_language.collection'])) {
        $language_list_description = $this->t('The list of languages in the CKEditor "Language" dropdown can present the <a href=":united-nations-official">@count_united_nations official languages of the UN</a>, all @count_predefined languages predefined in Drupal, or the <a href=":admin-configure-languages">@count_configured languages configured for this site</a>.', $language_list_description_args + [':admin-configure-languages' => Url::fromRoute('entity.configurable_language.collection')->toString()]);
        }
        else {
        $language_list_description = $this->t('The list of languages in the CKEditor "Language" dropdown can present the <a href=":united-nations-official">@count_united_nations official languages of the UN</a>, all @count_predefined languages predefined in Drupal, or the languages configured for this site.', $language_list_description_args);
        }
        $form['language_list'] = [
        '#title' => $this->t('Language list'),
        '#title_display' => 'invisible',
        '#type' => 'select',
        '#options' => [
        'un' => $this->t("United Nations' official languages"),
        'all' => $this->t('All @count languages', ['@count' => count($predefined_languages)]),
        'un' => $this->t("United Nations' official languages (@count)", ['@count' => $united_nations]),
        'all' => $this->t('Drupal predefined languages (@count)', ['@count' => $predefined]),
        'site_configured' => $this->t("Site-configured languages (@count)", ['@count' => $configured]),
        ],
        '#default_value' => $this->configuration['language_list'],
        '#description' => $this->t('The list of languages to show in the language dropdown. The basic list will only show the <a href=":url">six official languages of the UN</a>. The extended list will show all @count languages that are available in Drupal.', [
        ':url' => 'https://www.un.org/en/sections/about-un/official-languages',
        '@count' => count($predefined_languages),
        ]),
        '#description' => $language_list_description,
        ];
        return $form;
        ......
        ......@@ -7,13 +7,14 @@
        use Drupal\editor\Entity\Editor;
        use Drupal\file\Entity\File;
        use Drupal\filter\Entity\FilterFormat;
        use Drupal\language\Entity\ConfigurableLanguage;
        use Drupal\node\Entity\Node;
        use Drupal\Tests\ckeditor5\Traits\CKEditor5TestTrait;
        use Drupal\Tests\TestFileCreationTrait;
        use Drupal\user\RoleInterface;
        use Symfony\Component\Validator\ConstraintViolation;
        // cspell:ignore esque splitbutton upcasted sourceediting
        // cspell:ignore esque māori sourceediting splitbutton upcasted
        /**
        * Tests for CKEditor 5.
        ......@@ -31,6 +32,7 @@ class CKEditor5Test extends CKEditor5TestBase {
        */
        protected static $modules = [
        'media_library',
        'language',
        ];
        /**
        ......@@ -41,7 +43,7 @@ public function testExistingContent() {
        $assert_session = $this->assertSession();
        // Add a node with text rendered via the Plain Text format.
        $this->drupalGet('node/add');
        $this->drupalGet('node/add/page');
        $page->fillField('title[0][value]', 'My test content');
        $page->fillField('body[0][value]', '<p>This is test content</p>');
        $page->pressButton('Save');
        ......@@ -103,7 +105,7 @@ function (ConstraintViolation $v) {
        ))
        ));
        $this->drupalGet('node/add');
        $this->drupalGet('node/add/page');
        $this->waitForEditor();
        $page->fillField('title[0][value]', 'My test content');
        ......@@ -147,7 +149,7 @@ public function testHeadingsPlugin() {
        $this->drupalGet('admin/config/content/formats/manage/ckeditor5');
        $this->assertHtmlEsqueFieldValueEquals('filters[filter_html][settings][allowed_html]', '<br> <p> <h2> <h3> <h4> <h5> <h6> <strong> <em>');
        $this->drupalGet('node/add');
        $this->drupalGet('node/add/page');
        $this->assertNotEmpty($assert_session->waitForElement('css', '.ck-heading-dropdown button'));
        $page->find('css', '.ck-heading-dropdown button')->click();
        ......@@ -188,7 +190,7 @@ public function testHeadingsPlugin() {
        $page->pressButton('Save configuration');
        $this->drupalGet('node/add');
        $this->drupalGet('node/add/page');
        $this->assertNotEmpty($assert_session->waitForElement('css', '.ck-heading-dropdown button'));
        $page->find('css', '.ck-heading-dropdown button')->click();
        ......@@ -212,12 +214,43 @@ public function testHeadingsPlugin() {
        }
        /**
        * Test for plugin Language of parts.
        * Test for Language of Parts plugin.
        */
        public function testLanguageOfPartsPlugin() {
        $page = $this->getSession()->getPage();
        $assert_session = $this->assertSession();
        $this->languageOfPartsPluginInitialConfigurationHelper($page, $assert_session);
        // Test for "United Nations' official languages" option.
        $languages = LanguageManager::getUnitedNationsLanguageList();
        $this->languageOfPartsPluginConfigureLanguageListHelper($page, $assert_session, 'un');
        $this->languageOfPartsPluginTestHelper($page, $assert_session, $languages);
        // Test for "Drupal predefined languages" option.
        $languages = LanguageManager::getStandardLanguageList();
        $this->languageOfPartsPluginConfigureLanguageListHelper($page, $assert_session, 'all');
        $this->languageOfPartsPluginTestHelper($page, $assert_session, $languages);
        // Test for "Site-configured languages" option.
        ConfigurableLanguage::createFromLangcode('ar')->save();
        ConfigurableLanguage::createFromLangcode('fr')->save();
        ConfigurableLanguage::createFromLangcode('mi')->setName('Māori')->save();
        $configured_languages = \Drupal::languageManager()->getLanguages();
        $languages = [];
        foreach ($configured_languages as $language) {
        $language_name = $language->getName();
        $language_code = $language->getId();
        $languages[$language_code] = [$language_name];
        }
        $this->languageOfPartsPluginConfigureLanguageListHelper($page, $assert_session, 'site_configured');
        $this->languageOfPartsPluginTestHelper($page, $assert_session, $languages);
        }
        /**
        * Helper to configure CKEditor5 with Language plugin.
        */
        public function languageOfPartsPluginInitialConfigurationHelper($page, $assert_session) {
        $this->createNewTextFormat($page, $assert_session);
        // Press arrow down key to add the button to the active toolbar.
        $this->assertNotEmpty($assert_session->waitForElement('css', '.ckeditor5-toolbar-item-textPartLanguage'));
        ......@@ -248,21 +281,15 @@ public function testLanguageOfPartsPlugin() {
        // Confirm there are no longer any warnings.
        $assert_session->waitForElementRemoved('css', '[data-drupal-messages] [role="alert"]');
        // Test for "United Nations' official languages" option.
        $languages = LanguageManager::getUnitedNationsLanguageList();
        $this->languageOfPartsPluginTestHelper($page, $assert_session, $languages, "un");
        // Test for "All 95 languages" option.
        $this->drupalGet('admin/config/content/formats/manage/ckeditor5');
        $languages = LanguageManager::getStandardLanguageList();
        $this->languageOfPartsPluginTestHelper($page, $assert_session, $languages, "all");
        $page->pressButton('Save configuration');
        $assert_session->responseContains('Added text format <em class="placeholder">ckeditor5</em>.');
        }
        /**
        * Validate the available languages on the basis of selected language option.
        * Helper to set language list option for CKEditor.
        */
        public function languageOfPartsPluginTestHelper($page, $assert_session, $predefined_languages, $option) {
        public function languageOfPartsPluginConfigureLanguageListHelper($page, $assert_session, $option) {
        $this->drupalGet('admin/config/content/formats/manage/ckeditor5');
        $this->assertNotEmpty($assert_session->waitForElement('css', 'a[href^="#edit-editor-settings-plugins-ckeditor5-language"]'));
        // Set correct value.
        ......@@ -271,9 +298,14 @@ public function languageOfPartsPluginTestHelper($page, $assert_session, $predefi
        $page->selectFieldOption('editor[settings][plugins][ckeditor5_language][language_list]', $option);
        $assert_session->assertWaitOnAjaxRequest();
        $page->pressButton('Save configuration');
        $assert_session->responseContains('The text format <em class="placeholder">ckeditor5</em> has been updated.');
        }
        // Validate plugin on node add page.
        $this->drupalGet('node/add');
        /**
        * Validate expected languages available in editor.
        */
        public function languageOfPartsPluginTestHelper($page, $assert_session, $configured_languages) {
        $this->drupalGet('node/add/page');
        $this->assertNotEmpty($assert_session->waitForText('Choose language'));
        // Click on the dropdown button.
        ......@@ -290,13 +322,13 @@ public function languageOfPartsPluginTestHelper($page, $assert_session, $predefi
        foreach ($current_languages as $item) {
        $languages[] = $item->getText();
        }
        // Return the values from a single column.
        $predefined_languages = array_column($predefined_languages, 0);
        $configured_languages = array_column($configured_languages, 0);
        // Sort on full language name.
        asort($predefined_languages);
        $this->assertSame(array_values($predefined_languages), $languages);
        asort($configured_languages);
        $this->assertSame(array_values($configured_languages), $languages);
        }
        /**
        ......@@ -518,7 +550,7 @@ public function testEditorFileReferenceIntegration() {
        $assert_session->assertWaitOnAjaxRequest();
        $this->saveNewTextFormat($page, $assert_session);
        $this->drupalGet('node/add');
        $this->drupalGet('node/add/page');
        $page->fillField('title[0][value]', 'My test content');
        // Ensure that CKEditor 5 is focused.
        ......@@ -566,7 +598,7 @@ public function testEmphasis() {
        $assert_session = $this->assertSession();
        // Add a node with text rendered via the Plain Text format.
        $this->drupalGet('node/add');
        $this->drupalGet('node/add/page');
        $page->fillField('title[0][value]', 'My test content');
        $page->fillField('body[0][value]', '<p>This is a <em>test!</em></p>');
        $page->pressButton('Save');
        ......@@ -624,7 +656,7 @@ function (ConstraintViolation $v) {
        $ordered_list_html = '<ol><li>apple</li><li>banana</li><li>cantaloupe</li></ol>';
        $page = $this->getSession()->getPage();
        $assert_session = $this->assertSession();
        $this->drupalGet('node/add');
        $this->drupalGet('node/add/page');
        $page->fillField('title[0][value]', 'My test content');
        $this->pressEditorButton('Source');
        $source_text_area = $assert_session->waitForElement('css', '.ck-source-editing-area textarea');
        ......@@ -690,7 +722,7 @@ public function testFilterHtmlAllowedGlobalAttributes(): void {
        $assert_session = $this->assertSession();
        // Add a node with text rendered via the Plain Text format.
        $this->drupalGet('node/add');
        $this->drupalGet('node/add/page');
        $page->fillField('title[0][value]', 'Multilingual Hello World');
        // cSpell:disable-next-line
        $page->fillField('body[0][value]', '<p dir="ltr" lang="en">Hello World</p><p dir="rtl" lang="ar">مرحبا بالعالم</p>');
        ......
        ......@@ -5,7 +5,11 @@
        namespace Drupal\Tests\ckeditor5\Unit;
        use Drupal\ckeditor5\Plugin\CKEditor5Plugin\Language;
        use Drupal\ckeditor5\Plugin\CKEditor5PluginDefinition;
        use Drupal\Core\Language\Language as LanguageLanguage;
        use Drupal\Core\Language\LanguageManager;
        use Drupal\Core\Language\LanguageManagerInterface;
        use Drupal\Core\Routing\RouteProviderInterface;
        use Drupal\editor\EditorInterface;
        use Drupal\Tests\UnitTestCase;
        ......@@ -20,7 +24,7 @@ class LanguagePluginTest extends UnitTestCase {
        * Provides a list of configs to test.
        */
        public static function providerGetDynamicPluginConfig(): array {
        $un_expected_output = [
        $united_nations_expected_output = [
        'language' => [
        'textPartLanguage' => [
        [
        ......@@ -54,7 +58,25 @@ public static function providerGetDynamicPluginConfig(): array {
        return [
        'un' => [
        ['language_list' => 'un'],
        $un_expected_output,
        $united_nations_expected_output,
        ],
        'site_configured' => [
        ['language_list' => 'site_configured'],
        [
        'language' => [
        'textPartLanguage' => [
        [
        'title' => 'Arabic',
        'languageCode' => 'ar',
        'textDirection' => 'rtl',
        ],
        [
        'title' => 'German',
        'languageCode' => 'de',
        ],
        ],
        ],
        ],
        ],
        'all' => [
        ['language_list' => 'all'],
        ......@@ -66,7 +88,7 @@ public static function providerGetDynamicPluginConfig(): array {
        ],
        'default configuration' => [
        [],
        $un_expected_output,
        $united_nations_expected_output,
        ],
        ];
        }
        ......@@ -101,7 +123,20 @@ protected static function buildExpectedDynamicConfig(array $language_list) {
        * @dataProvider providerGetDynamicPluginConfig
        */
        public function testGetDynamicPluginConfig(array $configuration, array $expected_dynamic_config): void {
        $plugin = new Language($configuration, 'ckeditor5_language', NULL);
        $route_provider = $this->prophesize(RouteProviderInterface::class);
        $language_manager = $this->prophesize(LanguageManagerInterface::class);
        $language_manager->getLanguages()->willReturn([
        new LanguageLanguage([
        'id' => 'de',
        'name' => 'German',
        ]),
        new LanguageLanguage([
        'id' => 'ar',
        'name' => 'Arabic',
        'direction' => 'rtl',
        ]),
        ]);
        $plugin = new Language($configuration, 'ckeditor5_language', new CKEditor5PluginDefinition(['id' => 'IRRELEVANT-FOR-A-UNIT-TEST']), $language_manager->reveal(), $route_provider->reveal());
        $dynamic_config = $plugin->getDynamicPluginConfig([], $this->prophesize(EditorInterface::class)
        ->reveal());
        $this->assertSame($expected_dynamic_config, $dynamic_config);
        ......
        0% Loading or .
        You are about to add 0 people to the discussion. Proceed with caution.
        Please register or to comment