Commit 85bcf12d authored by Sergey Shadrin's avatar Sergey Shadrin

[#124455] Removed ckeditor5_premium_features module, ckeditor5_plugin_pack…

[#124455] Removed ckeditor5_premium_features module, ckeditor5_plugin_pack patched to apply translation
parent f6787db1

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

......@@ -134,6 +134,9 @@
"oomphinc/composer-installers-extender": "^2.0",
"wikimedia/composer-merge-plugin": "^2.1"
},
"replace": {
"drupal/ckeditor5_premium_features": "*"
},
"conflict": {
"drupal/drupal": "*"
},
......@@ -233,7 +236,7 @@
"Replace icons": "./patches/admin_toolbar/replace-icons.patch"
},
"drupal/ckeditor5_plugin_pack": {
"Path to library": "./patches/ckeditor5_plugin_pack/path_to_library.patch"
"3453170: UI is not translated": "https://git.drupalcode.org/project/ckeditor5_plugin_pack/-/merge_requests/7.diff"
},
"drupal/select2": {
"3271205: Removing selected option sometimes needs multiple clicks": "https://www.drupal.org/files/issues/2022-04-20/select2-check-indexes-3271205-8.patch",
......
This diff is collapsed.
This file was automatically generated by Composer Patches (https://github.com/cweagans/composer-patches)
Patches applied to this directory:
Path to library
Source: ./patches/ckeditor5_plugin_pack/path_to_library.patch
3453170: UI is not translated
Source: https://git.drupalcode.org/project/ckeditor5_plugin_pack/-/merge_requests/7.diff
......@@ -16,22 +16,9 @@ function ckeditor5_plugin_pack_library_loader(array $libraries): array {
$config_handler = \Drupal::service('ckeditor5_plugin_pack.config_handler.settings');
$definitions = [];
foreach ($libraries as $library) {
$dll_location = $config_handler->getDllLocation();
// Local library.
$dll_location = str_replace('LIBRARY_NAME', $library, $dll_location);
$js_data = [
$dll_location . "$library.js" => [
'group' => JS_LIBRARY,
'type' => 'file',
'version' => $config_handler->getDllVersion(),
'minified' => FALSE,
],
];
$definition = new LibraryDefinitionItem(
$library,
"ckeditor5.$library",
$js_data,
);
$definition = new LibraryDefinitionItem($library, $config_handler->getDllLocation());
$definition->addRemoteJs($definition->id());
$definitions[$definition->id()] = $definition->getDefinition();
}
......
......@@ -49,7 +49,7 @@ class SettingsConfigHandler implements SettingsConfigHandlerInterface {
* {@inheritdoc}
*/
public function getDefaultDllLocation(): string {
return '/libraries/ckeditor_plugins/LIBRARY_NAME/' . SettingsConfigHandlerInterface::DLL_PATH_VERSION_TOKEN . '/dll/';
return 'https://cdn.ckeditor.com/ckeditor5/' . SettingsConfigHandlerInterface::DLL_PATH_VERSION_TOKEN . '/dll/';
}
/**
......
......@@ -16,6 +16,17 @@ use Drupal\Component\Utility\NestedArray;
*/
class LibraryDefinitionItem {
// Translations available through CKSource CDN.
const AVAILABLE_TRANSLATIONS = [
'ar', 'bg', 'bn', 'ca', 'cs', 'da', 'de', 'el', 'en-au', 'es', 'et', 'fi', 'fr', 'gl', 'he', 'hi', 'hr', 'hu',
'id', 'it', 'ja', 'ko', 'lt', 'lv', 'ms', 'nl', 'no', 'pl', 'pt', 'pt-br', 'ro', 'ru', 'sk', 'sr', 'sr-latn', 'sv',
'th', 'tr', 'uk', 'vi', 'zh', 'zh-cn',
];
// Plugins that does not have any translations.
// Currently all Plugin Pack plugins have translations.
const UNTRANSLATABLE_PLUGINS = [];
/**
* Constructs the library instance.
*
......@@ -56,16 +67,26 @@ class LibraryDefinitionItem {
* The name of the library file without extension.
*/
public function addRemoteJs(string $name): void {
$file_name = "{$this->baseDirectory}{$name}/{$name}.js";
$this->jsData[$file_name] = [
'type' => 'external',
'minified' => 'true',
'preprocess' => FALSE,
'attributes' => [
'crossorigin' => 'anonymous'
]
];
$file_names = ["{$this->baseDirectory}{$name}/{$name}.js"];
if (!in_array($name, $this::UNTRANSLATABLE_PLUGINS)) {
$languages = $this->getAvailableTranslations();
foreach ($languages as $language) {
$file_names[] = "{$this->baseDirectory}{$name}/translations/{$language}.js";
}
}
foreach ($file_names as $file_name) {
$this->jsData[$file_name] = [
'type' => 'external',
'minified' => 'true',
'preprocess' => FALSE,
'attributes' => [
'crossorigin' => 'anonymous'
]
];
}
}
/**
......@@ -119,4 +140,17 @@ class LibraryDefinitionItem {
];
}
/**
* Gets langcodes of all enabled UI languages
*
* @return array
* Array of ISO 639 language codes for all enabled UI languages.
*/
private function getAvailableTranslations(): array {
$languages = \Drupal::entityTypeManager()->getStorage('configurable_language')->loadMultiple();
$langcodes = array_keys($languages);
return array_intersect($this::AVAILABLE_TRANSLATIONS, $langcodes);
}
}
This diff is collapsed.
# CKEditor 5 Premium Features
CKEditor 5 Premium features are a module for Drupal 9/10 that will provide a set of collaborative solutions and the ability to export your content to popular portable and cross-platform formats.
At the moment the CKEditor 5 Premium features offer:
* Real-time collaboration - **optional**, you may use Track changes and Comments without Real-time collaboration too (!)
* Track changes
* Comments
* Revision history - a more sophisticated history of changes inside the document
* Notifications - configurable notifications to stay up to date whenever someone mentions you in a document, comment, accepts/rejects suggestions, replies to your comment and so on. You may use your own plugin for notifications to get e.g. notifications on slack.
* Productivity pack - a set of exclusive premium features, that make editing faster, easier, and more efficient.
* Full screen mode - a free-to-use plugin that allows maximising the editing area.
It also provides the following file formats converters:
* Export to Word - marked as experimental (works but requires manual corrections to CSS for the best experience).
* Export to PDF - marked as experimental (works but requires manual corrections to CSS for the best experience).
For a full description of the module, visit the
[project page](https://www.drupal.org/project/ckeditor5_premium_features).
Submit bug reports and feature suggestions, or track changes in the
[issue queue](https://www.drupal.org/project/issues/ckeditor5_premium_features).
## CKEditor 5 Premium Features Module
The CKEditor 5 Premium Features module: [project page](https://www.drupal.org/project/ckeditor5_premium_features)
Online documentation: [CKEditor 5 Premium Features](https://www.drupal.org/docs/contributed-modules/ckeditor-5-premium-features)
## Status
The module is actively maintained. Having questions about the status, roadmap or would you like to use it in your project? Contact us: [ckeditor](https://ckeditor.com/contact/)
Reporting issues: [issue queue](https://www.drupal.org/project/issues/ckeditor5_premium_features)
## Installation:
Install as you would normally install a contributed Drupal module. For further
information, see
[Installing Drupal Modules](https://www.drupal.org/docs/extending-drupal/installing-drupal-modules).
### Using composer:
```composer require drupal/ckeditor5_premium_features```
## Requirements
**PHP** >= 8.1
**Drupal** >= 9.4 || 10
## Configuration
Online documentation: [CKEditor 5 Premium Features](https://www.drupal.org/docs/contributed-modules/ckeditor-5-premium-features)
## Free trial
In order to use the module, you will need to sign up to the free trial of CKEditor 5 premium features: [CKEditor features](https://orders.ckeditor.com/trial/premium-features) and configure the module later.
## Uninstallation
Uninstall all submodules and the main CKEditor 5 Premium Features module in the administrative GUI: /admin/modules/uninstall
### Using composer:
To remove the module files after uninstallation, run
```composer remove drupal/ckeditor5_premium_features```
## License
Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
Licensed under the terms of GNU General Public License Version 2 or later.
ckeditor5_premium_features__cloud_services:
ckeditor5:
plugins: []
drupal:
label: CloudServices
class: Drupal\ckeditor5_premium_features\Plugin\CKEditor5Plugin\CloudServices
elements: false
ckeditor5_premium_features__collaboration_integration_base:
ckeditor5:
plugins:
- disableGhsTableIntegration.DisableGhsTableIntegration
- disableCollaborationMarkersInCaption.DisableCollaborationMarkersInCaption
- sidebarAdapter.SidebarAdapter
- toolbarAdapter.ToolbarAdapter
- removeIncorrectCollaborationMarkers.RemoveIncorrectCollaborationMarkers
- drupalMediaTrackChangesIntegration.DrupalMediaTrackChangesIntegration
drupal:
label: The load & save integration of collaboration tools.
library: ckeditor5_premium_features/collaboration-integration-base
class: Drupal\ckeditor5_premium_features\Plugin\CKEditor5Plugin\CollaborationBase
elements: false
conditions: [ ]
ckeditor5_premium_features__export_adapter:
ckeditor5:
plugins: []
drupal:
label: Export Adapter
library: ckeditor5_premium_features/export-integration
elements: false
ckeditor5_premium_features__error_notifications:
ckeditor5:
plugins:
- errorNotifications.ErrorNotifications
drupal:
label: Error notifications
library: ckeditor5_premium_features/error-notifications
elements: false
name: CKEditor 5 Premium Features
type: module
description: "Provides general configuration and authentication used by all CKEditor 5 Premium Features."
package: CKEditor 5 Premium
core_version_requirement: ^9.4 || ^10.0
configure: ckeditor5_premium_features.form.settings
dependencies:
- drupal:editor
- drupal:ckeditor5
# Information added by Drupal.org packaging script on 2024-06-13
version: '1.2.9'
project: 'ckeditor5_premium_features'
datestamp: 1718272069
<?php
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @file
* Install and update hooks for the CKEditor 5 Premium Features module.
*/
use Drupal\Core\Database\Database;
/**
* Change uid field to nullable.
*/
function ckeditor5_premium_features_update_9001() {
$schema = Database::getConnection()->schema();
$schema->changeField('ckeditor5_revision', 'uid', 'uid', [
'not null' => FALSE,
'mysql_type' => 'INT(10)',
'unsigned' => TRUE,
]);
}
/**
* Set alter_node_form_css config field to TRUE as default.
*/
function ckeditor5_premium_features_update_10000() {
$cke5PremiumFeaturesSettings = \Drupal::configFactory()->getEditable('ckeditor5_premium_features.settings');
$cke5PremiumFeaturesSettings->set('alter_node_form_css', 1);
$cke5PremiumFeaturesSettings->save();
}
collaboration-integration-base:
version: 20240220
license:
name: GNU-GPL-2.0-or-later
url: https://raw.githubusercontent.com/ckeditor/ckeditor5/master/LICENSE.md
gpl-compatible: true
css:
theme:
css/sidebar.css: { }
css/general.css: { }
js:
js/ckeditor5_premium_features.js: { minified: false }
js/build/sidebarAdapter.js: { minified: true }
js/build/disableGhsTableIntegration.js: { minified: true }
js/build/disableCollaborationMarkersInCaption.js: { minified: true }
js/build/removeIncorrectCollaborationMarkers.js: { minified: true }
js/build/commentsAdapter.js: { minified: true }
js/build/drupalMediaTrackChangesIntegration.js: { minified: true }
dependencies:
- ckeditor5_premium_features/ckeditor5-collaboration-dll
- ckeditor5_premium_features/toolbar-adapter
media-export-features:
version: 20231024
license:
name: GNU-GPL-2.0-or-later
url: https://raw.githubusercontent.com/ckeditor/ckeditor5/master/LICENSE.md
gpl-compatible: true
js:
js/libraries/document-export-base.js: { minified: false }
js/libraries/media-tags-converter.js: { minified: false }
js/libraries/relative-paths-processor.js: { minified: false }
js/libraries/base64-image-converter.js: { minified: false }
export-integration:
version: 20230626
license:
name: GNU-GPL-2.0-or-later
url: https://raw.githubusercontent.com/ckeditor/ckeditor5/master/LICENSE.md
gpl-compatible: true
js:
js/build/exportAdapters.js: { minified: true }
dependencies:
- ckeditor5_premium_features/cloud-services
- ckeditor5_premium_features/collaboration-integration-base
- ckeditor5_premium_features/media-export-features
claro--override--node-form:
version: 20240326
css:
layout:
css/claro/node-form.css: { }
error-notifications:
version: 20230626
license:
name: GNU-GPL-2.0-or-later
url: https://raw.githubusercontent.com/ckeditor/ckeditor5/master/LICENSE.md
gpl-compatible: true
css:
theme:
css/notifications.css: { }
js:
js/build/errorNotifications.js: { minified: true }
toolbar-adapter:
version: 20231012
license:
name: GNU-GPL-2.0-or-later
url: https://raw.githubusercontent.com/ckeditor/ckeditor5/master/LICENSE.md
gpl-compatible: true
js:
js/build/toolbarAdapter.js: { minified: true }
ckeditor5_premium_features.form.settings_general:
title: General Settings
route_name: ckeditor5_premium_features.form.settings_general
parent: ckeditor5_premium_features.form.settings
description: 'General configuration for all premium features (such as license keys etc.).'
weight: 20
ckeditor5_premium_features.form.settings:
title: CKEditor 5 Premium Features
route_name: ckeditor5_premium_features.form.settings
parent: system.admin_config_content
description: 'Setup additional features such as Real-time collaboration, Track changes or Export to PDF/Word.'
weight: 20
ckeditor5_premium_features.form.settings:
title: General Settings
route_name: ckeditor5_premium_features.form.settings_general
base_route: ckeditor5_premium_features.form.settings
description: 'General configuration for all premium features (such as license keys etc.).'
weight: 20
<?php
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @file
* Implements hooks for the CKEditor 5 Premium Features module.
*/
declare(strict_types=1);
use Drupal\Core\Asset\AttachedAssetsInterface;
use Drupal\ckeditor5\Plugin\CKEditor5PluginDefinition;
use Drupal\ckeditor5_premium_features\Utility\Html;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\ckeditor5_premium_features\Utility\LibraryDefinitionItem;
use Drupal\ckeditor5_premium_features\EditorXssFilter\CollaborationXSSFilter;
use Drupal\filter\FilterFormatInterface;
/**
* Implements hook_help().
*/
function ckeditor5_premium_features_help($route_name, RouteMatchInterface $route_match) {
switch ($route_name) {
case 'help.page.ckeditor5_premium_features':
$text = file_get_contents(__DIR__ . '/README.md');
if (!\Drupal::moduleHandler()->moduleExists('markdown')) {
return '<pre>' . Html::escape($text) . '</pre>';
}
else {
// Use the Markdown filter to render the README.
$filter_manager = \Drupal::service('plugin.manager.filter');
$settings = \Drupal::configFactory()->get('markdown.settings')->getRawData();
$config = ['settings' => $settings];
$filter = $filter_manager->createInstance('markdown', $config);
return $filter->process($text, 'en');
}
// @todo Load text from Readme.md.
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('The CKEditor Premium Features, see <a href=":doc_url">online documentation for the CKEditor Premium Features</a>.', [':doc_url' => 'https://ckeditor.com/docs/trial/latest/guides/overview.html']) . '</p>';
return $output;
}
}
/**
* Implements hook_library_info_build().
*/
function ckeditor5_premium_features_library_info_build(): array {
/** @var \Drupal\ckeditor5_premium_features\Config\SettingsConfigHandlerInterface $config_handler */
$config_handler = \Drupal::service('ckeditor5_premium_features.config_handler.settings');
$libraries = [
'ckeditor5-collaboration-dll',
'cloud-services',
];
$definitions = [];
foreach ($libraries as $library) {
$definition = new LibraryDefinitionItem($library, $config_handler->getDllLocation());
$definition->addRemoteJs($definition->id());
$definitions[$definition->id()] = $definition->getDefinition();
}
return $definitions;
}
/**
* Implements hook_editor_xss_filter_alter().
*/
function ckeditor5_premium_features_editor_xss_filter_alter(
&$editor_xss_filter_class,
FilterFormatInterface $format,
FilterFormatInterface $original_format = NULL) {
$editor_xss_filter_class = CollaborationXSSFilter::class;
}
/**
* Implements hook_library_info_alter().
*/
function ckeditor5_premium_features_library_info_alter(&$libraries, $extension) {
// We don't need disableGhsTableIntegration.js for ckeditor5 >35.4.0.
if ($extension == 'ckeditor5_premium_features') {
if (\Drupal::service('ckeditor5_premium_features.core_library_version_checker')->isLibraryVersionHigherOrEqual('35.4.0')) {
unset($libraries["collaboration-integration-base"]["js"]["js/build/disableGhsTableIntegration.js"]);
}
}
// @todo Change this to use `after` instead of `dependencies` after https://www.drupal.org/project/drupal/issues/1945262 is released.
if ($extension === 'claro' && isset($libraries['node-form'])) {
$cke5PremiumFeaturesSettings = \Drupal::configFactory()->get('ckeditor5_premium_features.settings');
if ($cke5PremiumFeaturesSettings->get('alter_node_form_css')) {
$libraries['node-form']['dependencies'][] = 'ckeditor5_premium_features/claro--override--node-form';
}
}
}
/**
* Implements hook_ckeditor5_plugin_info_alter().
*/
function ckeditor5_premium_features_ckeditor5_plugin_info_alter(array &$plugin_definitions) {
// In order to safely remove disableGhsTableIntegration.js we have to alter
// CKEditor5 plugin that wa using this file.
if (\Drupal::service('ckeditor5_premium_features.core_library_version_checker')->isLibraryVersionHigherOrEqual('35.4.0')) {
$definition = $plugin_definitions["ckeditor5_premium_features__collaboration_integration_base"];
$definition_array = $definition->toArray();
$key = array_search(
'disableGhsTableIntegration.DisableGhsTableIntegration',
$definition_array["ckeditor5"]["plugins"]
);
if (is_numeric($key)) {
unset($definition_array["ckeditor5"]["plugins"][$key]);
$new_definition = new CKEditor5PluginDefinition($definition_array);
$plugin_definitions["ckeditor5_premium_features__collaboration_integration_base"] = $new_definition;
}
}
}
/**
* Implements hook_module_implements_alter().
*/
function ckeditor5_premium_features_module_implements_alter(&$implementations, $hook) {
if (in_array($hook, ['library_info_alter', 'ckeditor5_plugin_info_alter'])) {
// Make sure this module's implementations are executed in the end.
if (isset($implementations["ckeditor5_premium_features"])) {
unset($implementations["ckeditor5_premium_features"]);
$implementations["ckeditor5_premium_features"] = FALSE;
}
}
}
/**
* Implements hook_js_settings_alter().
*/
function ckeditor5_premium_features_js_settings_alter(array &$settings, AttachedAssetsInterface $assets) {
$moduleHandler = \Drupal::service('module_handler');
$settings["ckeditor5Premium"]["isMediaInstalled"] = $moduleHandler->moduleExists('media');
}
/**
* Implements hook_filter_format_disable().
*/
function ckeditor5_premium_features_filter_format_disable(FilterFormatInterface $format) {
// Revoke all collaboration permissions after text format is disabled.
/** @var \Drupal\ckeditor5_premium_features\Utility\PermissionHelper $permissionHelper */
$permissionHelper = \Drupal::service('ckeditor5_premium_features.permission_helper');
$permissionHelper->deleteCollaborationPermissions($format);
}
/**
* Implements hook_theme().
*/
function ckeditor5_premium_features_theme($existing, $type, $theme, $path): array {
return [
'ckeditor5_textarea' => [
'render element' => 'element',
'template' => 'ckeditor5-textarea'
],
];
}
/**
* Implements hook_preprocess_HOOK().
*/
function ckeditor5_premium_features_preprocess_ckeditor5_textarea(&$variables): void {
template_preprocess_textarea($variables);
$element = $variables['element'];
$variables['sidebar'] = !empty($element['#sidebar']) ? $element['#sidebar'] : NULL;
$variables['document_outline'] = !empty($element['#document_outline']) ? $element['#document_outline'] : NULL;
}
use ckeditor5 access token:
title: 'Use premium features access token'
description: 'Give this permission to roles that will use cloud services or on-premises server functionalities such as real-time collaboration or export to Word/PDF'
ckeditor5_premium_features.form.settings:
path: '/admin/config/ckeditor5-premium-features'
defaults:
_title: 'CKEditor 5 Premium Features'
_controller: '\Drupal\system\Controller\SystemController::systemAdminMenuBlockPage'
requirements:
_user_is_logged_in: 'TRUE'
ckeditor5_premium_features.form.settings_general:
path: '/admin/config/ckeditor5-premium-features/settings'
defaults:
_title: 'CKEditor 5 Premium Features - Settings'
_form: 'Drupal\ckeditor5_premium_features\Form\SettingsForm'
requirements:
# TODO: It may be more granullar - TBD if there is a need for dedicated permission.
_permission: 'administer site configuration'
ckeditor5_premium_features.endpoint.jwt_token:
path: '/ckeditor5-premium-features/token'
defaults:
_controller: '\Drupal\ckeditor5_premium_features\Controller\EndpointController::jwtToken'
requirements:
_permission: 'use ckeditor5 access token'
options:
no_cache: 'TRUE'
ckeditor5_premium_features.media_tags:
path: '/ck5/api/media-tags/{format}'
defaults:
_controller: 'Drupal\ckeditor5_premium_features\Controller\MediaTagConverterController::decodeMediaTags'
methods: [POST]
requirements:
_permission: 'view media'
_format: json
ckeditor5_premium_features.base64_image_converter:
path: '/ck5/api/base64-image-converter'
defaults:
_controller: 'Drupal\ckeditor5_premium_features\Controller\Base64ImageConverterController::convertImages'
methods: [POST]
requirements:
_permission: 'access content'
_format: json
services:
ckeditor5_premium_features.config_handler.settings:
class: Drupal\ckeditor5_premium_features\Config\SettingsConfigHandler
arguments:
- '@config.factory'
- '@library.discovery'
- '@module_handler'
ckeditor5_premium_features.config_handler.export_settings:
class: Drupal\ckeditor5_premium_features\Config\ExportFeaturesConfigHandler
arguments:
- '@config.factory'
ckeditor5_premium_features.token_generator:
class: Drupal\ckeditor5_premium_features\Generator\TokenGenerator
arguments:
- '@current_user'
- '@ckeditor5_premium_features.config_handler.settings'
- '@ckeditor5_premium_features.user_helper'
ckeditor5_premium_features.user_helper:
class: Drupal\ckeditor5_premium_features\Utility\UserHelper
arguments:
- '@file_url_generator'
- '@entity_type.manager'
- '@config.factory'
ckeditor5_premium_features.file_name_generator:
class: \Drupal\ckeditor5_premium_features\Generator\FileNameGenerator
arguments:
- '@current_route_match'
ckeditor5_premium_features.css_style_provider:
class: \Drupal\ckeditor5_premium_features\Utility\CssStyleProvider
arguments:
- '@theme.manager'
- '@file_system'
- '@file_url_generator'
ckeditor5_premium_features.plugin_helper:
class: \Drupal\ckeditor5_premium_features\Utility\PluginHelper
ckeditor5_premium_features.html_helper:
class: \Drupal\ckeditor5_premium_features\Utility\HtmlHelper
ckeditor5_premium_features.permission_helper:
class: \Drupal\ckeditor5_premium_features\Utility\PermissionHelper
arguments:
- '@entity_type.manager'
ckeditor5_premium_features.api_adapter:
class: Drupal\ckeditor5_premium_features\Utility\ApiAdapter
arguments:
- '@ckeditor5_premium_features.config_handler.settings'
- '@http_client'
- '@current_user'
ckeditor5_premium_features.mention_integrator:
class: \Drupal\ckeditor5_premium_features\Utility\MentionsIntegrator
arguments:
- '@module_handler'
- '@service_container'
ckeditor5_premium_features.storage_handler.editor:
class: Drupal\ckeditor5_premium_features\Storage\EditorStorageHandler
arguments:
- '@entity_type.manager'
- '@ckeditor5_premium_features.config_handler.settings'
- '@current_user'
- '@messenger'
ckeditor5_premium_features.context_helper:
class: Drupal\ckeditor5_premium_features\Utility\ContextHelper
arguments:
- '@ckeditor5_premium_features.html_helper'
- '@ckeditor5_premium_features.core_library_version_checker'
ckeditor5_premium_features.diff:
class: Drupal\ckeditor5_premium_features\Diff\Ckeditor5Diff
arguments:
- '@ckeditor5_premium_features.context_helper'
ckeditor5_premium_features.document_diff_helper:
class: Drupal\ckeditor5_premium_features\Diff\DocumentDiffHelper
arguments:
- '@ckeditor5_premium_features.diff'
- '@plugin.manager.filter'
ckeditor5_premium_features.access_handler:
class: Drupal\ckeditor5_premium_features\CollaborationAccessHandler
arguments:
- '@entity_type.manager'
ckeditor5_premium_features.config_event_subscriber:
class: Drupal\ckeditor5_premium_features\EventSubscriber\ConfigSubscriber
arguments: [ '@config.factory', '@entity_type.manager', '@ckeditor5_premium_features.permission_helper' ]
tags:
- { name: event_subscriber }
ckeditor5_premium_features.core_library_version_checker:
class: Drupal\ckeditor5_premium_features\Utility\LibraryVersionChecker
arguments:
- '@library.discovery'
ckeditor5_premium_features.collaborators:
class: Drupal\ckeditor5_premium_features\Utility\Collaborators
arguments:
- '@database'
- '@entity_type.manager'
- '@ckeditor5_premium_features.mention_integrator'
- '@ckeditor5_premium_features.collaboration_module_integrator'
ckeditor5_premium_features.collaboration_module_integrator:
class: Drupal\ckeditor5_premium_features\Utility\CollaborationModuleIntegrator
arguments:
- '@module_handler'
{
"name": "drupal/ckeditor5_premium_features",
"type": "drupal-module",
"description": "The premium features for the CKEditor 5 WYSIWYG editor.",
"license": "GPL-2.0+",
"minimum-stability": "dev",
"homepage": "https://www.drupal.org/project/ckeditor5_premium_features",
"authors": [
{
"name": "Wojciech Kukowski (salmonek)",
"homepage": "https://www.drupal.org/u/salmonek",
"role": "Maintainer"
},
{
"name": "Dawid Olszewski (dolszewski)",
"homepage": "https://www.drupal.org/u/dolszewski",
"role": "Maintainer"
}
],
"support": {
"issues": "https://www.drupal.org/project/issues/ckeditor5_premium_features"
},
"require": {
"php": "^8.0",
"ext-dom": "*",
"ext-libxml": "*",
"drupal/core": "^9.3 || ^10.0",
"firebase/php-jwt": "^6.0",
"caxy/php-htmldiff": "~0.1.14",
"openai-php/client": "^0.4.0 || ^0.8.0",
"aws/aws-sdk-php": "^3.296"
}
}
ckeditor5_premium_features.settings:
type: config_object
label: 'CKEditor 5 Premium Features'
mapping:
license_key:
type: string
label: 'License key'
auth_type:
type: string
label: 'Authorization type'
env:
type: string
label: 'Environment ID'
access_key:
type: string
label: 'Access key'
web_socket_url:
type: string
label: 'Web Socket Url'
dev_token_url:
type: string
label: 'Development token URL'
dev_token_accept:
type: integer
label: 'Accept using development token URL'
dll_location:
type: string
label: 'DLL packages location'
organization_id:
type: string
label: 'Organization ID'
alter_node_form_css:
type: boolean
label: 'Allow the module to alter the default Drupal theme CSS'
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
@media (min-width: 61rem) {
.layout-region--node-main,
.layout-region--node-footer {
/* @todo Remove the `!important` once https://www.drupal.org/project/drupal/issues/1945262 is released. */
width: 100% !important;
overflow: hidden;
}
}
@media screen and (min-width: 61rem) {
.layout-region--node-main .layout-region__content,
.layout-region--node-footer .layout-region__content {
max-width: none !important;
}
}
.layout-node-form {
display: grid;
grid-template-columns: 3fr minmax(22.5rem, 1fr);
gap: var(--space-l);
}
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
.ck-content .table table {
margin-top: 0;
margin-bottom: 0;
}
/*
This is a fix of a bug with multiple instances of the editor added when users is changing fields order.
Works for RTC.
*/
.ck-editor-sidebar-wrapper .form-item .ck.ck-editor:nth-child(n+3) {
display: none;
}
.ck-presence-list-container .ck.ck-presence-list:nth-child(n+3) {
display: none;
}
/*
End of:
This is a fix of a bug with multiple instances of the editor added when users is changing fields order.
Works for RTC.
*/
.ck-content .ck-suggestion-marker-formatBlock {
box-shadow: -0.2em 0 0 0 var(--ck-color-base-background), -0.4em 0 0 0 var(--ck-color-suggestion-marker-format-border)!important;
}
.ck-content .ck-suggestion-marker-formatBlock.ck-suggestion-marker--active {
box-shadow: -0.2em 0 0 0 var(--ck-color-base-background),-0.4em 0 0 0 var(--ck-color-suggestion-marker-format-border-active)!important;
}
.ck-comments-archive-dropdown .ck-editor__main > :is(.ck-editor__editable, .ck-source-editing-area) {
min-height: unset;
}
.ck.ck-user__img {
box-sizing: border-box;
}
.ck-toolbar-container .ck.ck-toolbar {
border: 1px solid var(--ck-color-toolbar-border)!important;
border-bottom: 0!important;
}
.ck-editor-container {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
position: relative;
}
.ck-editor-container .ck-editor-instance {
width: 100%;
height: 100%;
overflow-x: auto;
}
.ck-editor-container .ck-editor-instance .ck-content{
word-break: break-word;
overflow-y: auto;
}
.ck-editor-container .ck-editor-instance .ck-editor,
.ck-editor-container .ck-editor-instance .ck-editor .ck-editor__main,
.ck-editor-container .ck-editor-instance .ck-editor .ck-editor__main .ck-content {
box-sizing: border-box;
height: 100%;
}
.ck-comment__input .ck-editor__main > :is(.ck-editor__editable, .ck-source-editing-area) {
max-height: none;
}
.ck-notification {
min-width: 250px;
max-width: 325px;
display: flex;
flex-direction: column;
border-style: solid;
border-color: #ebebeb;
border-top-width: 1px;
border-bottom-width: 1px;
border-left-width: 0px;
border-right-width: 1px;
border-radius: 5px;
background: #fffbfb;
box-shadow: 5px 2px 16px -10px rgba(66,68,90,1);
}
.ck-notification__error::after {
content: '';
position: absolute;
left: 0;
height:100%;
border-left: 3px solid #f58282;
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
}
.ck-notification__header {
font-weight: 600;
padding: 5px 10px 0 10px;
font-size: 1em;
margin: 0;
line-height: unset;
}
.ck-notification__header-error {
color: #f58282;
}
.ck-notification__description {
font-weight: 400;
color: #3f3f3f;
padding: 0 10px 10px 10px;
font-size: .8em;
margin: 0;
line-height: unset;
}
.ck-notification__close {
position: absolute;
top: 3px;
right: 10px;
font-size: 1.2em;
color: #d8d3d3;
transition: color .5s;
}
.ck-notification__close:hover {
cursor: pointer;
color: #8b8989;
transition: color .5s;
}
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
.revision-viewer-container-data,
.revision-history-container-data {
display: none;
}
.editor-container {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
position: relative;
width: 100%;
}
.ck-editor__main .ck-content:not(.ck-comment__input *) {
min-height: 200px;
word-break: break-word;
}
.revision-history-container-data > .editor-container > .ck-editor {
min-width: max(50%, calc(100% - 340px));
max-width: calc(100% - 260px);
width: 75%;
}
.revision-viewer-sidebar {
border-left: 1px solid var(--ck-color-toolbar-border);
width: 25%;
min-width: min(50%, 260px);
max-width: 340px;
}
.revision-history-container-data {
border: 1px solid var(--ck-color-toolbar-border);
}
.revision-history-container-data .ck.ck-editor__main>.ck-editor__editable:not(.ck-focused),
.revision-history-container-data .ck.ck-toolbar {
border: none;
}
.revision-history-container-data .ck.ck-editor__main {
border-top: 1px solid var(--ck-color-toolbar-border);
}
/* revision sidebar header */
.revision-history-container-data * {
--header-height: calc(var(--ck-ui-component-min-height) + var(--ck-spacing-small) * 2 + 2px)!important;
}
/* revision sidebar scrollable */
.revision-history-container-data .ck.ck-revision-history-sidebar {
position: relative;
height: 100%;
}
.revision-history-container-data .ck.ck-revision-history-sidebar .ck-revision-history-sidebar__timeline {
display: flex;
flex-direction: column;
height: calc(100% - var(--header-height));
max-height: inherit;
position: absolute;
bottom: 0;
width: 100%;
box-sizing: border-box;
}
.revision-history-active .filter-wrapper {
display: none;
}
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
.ck-editor-container .ck-sidebar-enabled > .ck-editor__main > .ck-content {
padding-right: 30px;
}
.ck-editor-container .ck-editor-sidebar-wrapper {
display: flex;
}
.ck-editor-container .ck-editor-sidebar-wrapper .form-item__label {
margin: 0;
line-height: 27px;
}
.ck-editor-container .ck-editor-sidebar-wrapper {
width: 0;
min-height: var(--ck-min-height);
max-height: calc(100vh - var(--drupal-displace-offset-top, 0px) - var(--drupal-displace-offset-bottom, 0px) - 20px);
}
.ck-editor-container .ck-editor-sidebar-wrapper.wideSidebar {
min-width: 250px;
max-width: 350px;
width: 25%;
}
.ck-editor-container .ck-editor-sidebar-wrapper.narrowSidebar {
width: 56px;
}
.ck-editor-container .ck-editor-sidebar-wrapper.slider-off {
display: none;
}
.ck-editor-container .ck-editor-sidebar-wrapper .form-item {
margin: 0;
flex: 0 1 auto;
min-width: 0;
width: 100%;
}
.ck-editor-container .ck-editor-sidebar-wrapper.wideSidebar .form-item {
max-width: calc(100% - 250px);
}
.ck-editor-container .ck-editor-sidebar-wrapper.narrowSidebar .form-item {
max-width: calc(100% - 40px);
}
.ck-editor-container .ck-editor-sidebar-wrapper.slider-off .form-item {
max-width: unset;
}
.ck-editor-container .ck-editor-sidebar-wrapper .ck-content:not(.ck-comment__input *) {
min-height: 120px;
padding-bottom: 120px;
}
.ck-editor-container .ck-editor-sidebar-wrapper .ck-sidebar-wrapper:not(.prevent-scroll-out-of-view) .ck-sidebar {
height: 40px;
margin-top: -40px;
}
.ck-editor-container .ck-editor-sidebar-wrapper .ck-sidebar-wrapper .ck-sidebar {
min-height: 10px!important;
transition: none;
}
.ck-editor-container .ck-editor-sidebar-wrapper.wideSidebar .ck-sidebar-wrapper .ck-sidebar:focus-within *:focus {
outline: none;
box-shadow: none;
}
.ck-editor-container .ck-editor-sidebar-wrapper.narrowSidebar .ck-sidebar-wrapper.prevent-scroll-out-of-view .ck-sidebar {
margin-top: 8px;
}
/*sidebar */
.ck-editor-container .ck-editor-sidebar-wrapper.slider-off .ck-sidebar-wrapper {
display: none;
}
.ck-editor-container .ck-editor-sidebar-wrapper.wideSidebar .ck-sidebar-wrapper,
.ck-editor-container .ck-editor-sidebar-wrapper.narrowSidebar .ck-sidebar-wrapper {
background: var(--ck-color-toolbar-background);
border: 1px solid var(--ck-color-toolbar-border);
border-left: 0;
overflow: hidden;
overflow-y: auto;
width: 100%;
}
tr.draggable .ck-editor-sidebar-wrapper .ck-sidebar-wrapper {
margin-top: unset;
}
#block-bartik-content .ck-sidebar-wrapper.wideSidebar {
margin: 1.35em 0 0 0;
}
.ck-editor-container .ck-editor-sidebar-wrapper.wideSidebar .ck-sidebar-wrapper {
padding: 0 10px;
}
.ck-editor-container .ck-editor-sidebar-wrapper.wideSidebar .ck-sidebar-wrapper .ck-sidebar .ck-sidebar-item {
padding-bottom: 10px;
}
/*Toggle*/
.ck-editor-sidebar-wrapper .ck-sidebar-auto-toggle-wrapper {
width: 30px;
height: 30px;
position: absolute;
top: 1px;
background: transparent;
z-index: 10;
}
.ck-editor-container .ck-editor-sidebar-wrapper.inline .ck-sidebar-auto-toggle-wrapper {
display: none;
}
.ck-editor-sidebar-wrapper .ck-sidebar-auto-toggle-wrapper a.ck-sidebar-auto-toggle {
display: block;
width: 30px;
height: 30px;
transition: box-shadow .2s ease-in-out,border .2s ease-in-out
}
.ck-editor-sidebar-wrapper .ck-sidebar-auto-toggle-wrapper a.ck-sidebar-auto-toggle:active {
border: var(--ck-focus-ring);
box-shadow: var(--ck-focus-outer-shadow),0 0;
outline: none;
}
.ck-editor-sidebar-wrapper .ck-sidebar-auto-toggle-wrapper a.ck-sidebar-auto-toggle:hover {
background-color: rgba(0, 0, 0, 0.06);
}
.ck-editor-container .ck-editor-sidebar-wrapper.narrowSidebar .ck-sidebar-wrapper .ck-sidebar-auto-toggle-wrapper {
width: 100%;
}
.ck-editor-container .ck-editor-sidebar-wrapper.wideSidebar .ck-sidebar-wrapper .ck-sidebar-auto-toggle-wrapper {
margin-left: -10px;
}
.ck-editor-container .ck-editor-sidebar-wrapper.narrowSidebar .ck-sidebar-auto-toggle-wrapper a.ck-sidebar-auto-toggle {
width: 30px;
margin: 0;
}
.ck-sidebar-wrapper a.ck-sidebar-auto-toggle:hover {
text-decoration: none;
cursor: pointer;
}
/*Toggle Icon*/
.ck-editor-container .ck-editor-sidebar-wrapper.narrowSidebar a.ck-sidebar-auto-toggle::after,
.ck-editor-container .ck-editor-sidebar-wrapper.wideSidebar a.ck-sidebar-auto-toggle::after {
margin-left: 5px;
padding-top: 5px;
color: var(--ck-color-toolbar-border);
display: block;
width: 20px;
height: 20px;
-webkit-transform: scaleX(-1);
transform: scaleX(-1);
}
.ck-editor-container .ck-editor-sidebar-wrapper.narrowSidebar a.ck-sidebar-auto-toggle::after {
content: url(../icons/sidebar.svg);
}
.ck-editor-container .ck-editor-sidebar-wrapper.wideSidebar a.ck-sidebar-auto-toggle::after {
content: url(../icons/arrow.svg);
}
/* Fullscreen mode */
.ck-fullscreen .ck-editor-container .ck-editor-sidebar-wrapper {
max-height: 100%;
}
.ck-fullscreen .ck-editor-container .ck-editor-sidebar-wrapper.wideSidebar {
min-width: 350px;
}
<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M11.463 5.187a.888.888 0 1 1 1.254 1.255L9.16 10l3.557 3.557a.888.888 0 1 1-1.254 1.255L7.26 10.61a.888.888 0 0 1 .16-1.382l4.043-4.042z"/></svg>
<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M5 9.5a.5.5 0 0 0 .5-.5v-.5A.5.5 0 0 0 5 8H3.5a.5.5 0 0 0-.5.5V9a.5.5 0 0 0 .5.5H5Z"/><path d="M5.5 12a.5.5 0 0 1-.5.5H3.5A.5.5 0 0 1 3 12v-.5a.5.5 0 0 1 .5-.5H5a.5.5 0 0 1 .5.5v.5Z"/><path d="M5 6.5a.5.5 0 0 0 .5-.5v-.5A.5.5 0 0 0 5 5H3.5a.5.5 0 0 0-.5.5V6a.5.5 0 0 0 .5.5H5Z"/><path clip-rule="evenodd" d="M2 19a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H2Zm6-1.5h10a.5.5 0 0 0 .5-.5V3a.5.5 0 0 0-.5-.5H8v15Zm-1.5-15H2a.5.5 0 0 0-.5.5v14a.5.5 0 0 0 .5.5h4.5v-15Z"/></svg>
/*!
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.CKEditor5=t():(e.CKEditor5=e.CKEditor5||{},e.CKEditor5.collaborationStorage=t())}(self,(()=>(()=>{"use strict";var e={d:(t,o)=>{for(var i in o)e.o(o,i)&&!e.o(t,i)&&Object.defineProperty(t,i,{enumerable:!0,get:o[i]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};e.d(t,{default:()=>o});const o={CollaborationStorage:class{constructor(e){this.editor=e,this.elementId=this.editor.sourceElement.dataset.ckeditor5PremiumElementId}processCollaborationCommandDisable(e){if(!this.isCollaborationDisabled())return!1;const t=this.editor.commands._commands.get(e);return void 0===t||t.forceDisabled("premium-features-module"),!0}processRevisionDisable(){return!!this.isCollaborationDisabled()&&(this.editor.plugins.has("RevisionTracker")&&(this.editor.plugins.get("RevisionTracker").isEnabled=!1),!0)}isCollaborationDisabled(){return void 0!==drupalSettings.ckeditor5Premium&&void 0!==drupalSettings.ckeditor5Premium.disableCollaboration&&!0===drupalSettings.ckeditor5Premium.disableCollaboration}getEditorParentContainer(e){let t=document.getElementById(e);for(;t&&void 0!==t&&void 0!==t.classList&&!t.classList.contains("ck-editor-container");)t=t.parentElement;return t&&void 0!==t?t.parentElement:null}getSourceDataSelector(e){return{trackChanges:".track-changes",comments:".comments",revisionHistory:".revision-history",revisionHistoryContainer:".revision-history-container",resolvedSuggestionsComments:".resolved-suggestions-comments"}[e]+"-data"+`[data-ckeditor5-premium-element-id="${this.elementId}"]`}}};return t=t.default})()));
\ No newline at end of file
/*!
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.CKEditor5=t():(e.CKEditor5=e.CKEditor5||{},e.CKEditor5.commentsAdapter=t())}(self,(()=>(()=>{"use strict";var e={d:(t,o)=>{for(var i in o)e.o(o,i)&&!e.o(t,i)&&Object.defineProperty(t,i,{enumerable:!0,get:o[i]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};e.d(t,{default:()=>i});const o=class{constructor(e){this.editor=e,this.elementId=this.editor.sourceElement.dataset.ckeditor5PremiumElementId}processCollaborationCommandDisable(e){if(!this.isCollaborationDisabled())return!1;const t=this.editor.commands._commands.get(e);return void 0===t||t.forceDisabled("premium-features-module"),!0}processRevisionDisable(){return!!this.isCollaborationDisabled()&&(this.editor.plugins.has("RevisionTracker")&&(this.editor.plugins.get("RevisionTracker").isEnabled=!1),!0)}isCollaborationDisabled(){return void 0!==drupalSettings.ckeditor5Premium&&void 0!==drupalSettings.ckeditor5Premium.disableCollaboration&&!0===drupalSettings.ckeditor5Premium.disableCollaboration}getEditorParentContainer(e){let t=document.getElementById(e);for(;t&&void 0!==t&&void 0!==t.classList&&!t.classList.contains("ck-editor-container");)t=t.parentElement;return t&&void 0!==t?t.parentElement:null}getSourceDataSelector(e){return{trackChanges:".track-changes",comments:".comments",revisionHistory:".revision-history",revisionHistoryContainer:".revision-history-container",resolvedSuggestionsComments:".resolved-suggestions-comments"}[e]+"-data"+`[data-ckeditor5-premium-element-id="${this.elementId}"]`}};const i={CommentsAdapter:class{constructor(e){this.editor=e,this.storage=new o(e);const t=Array.from(this.editor.plugins._availablePlugins.values()).filter((e=>["Bold","Italic","DocumentList","Autoformat"].includes(e.pluginName)));this.editor.config._config.comments.editorConfig.extraPlugins.push(...t)}static get pluginName(){return"CommentsAdapter"}static get requires(){return["CommentsRepository","Bold","Italic","DocumentList","Autoformat"]}init(){if(this.storage.processCollaborationCommandDisable("addCommentThread"))return;if(!this.editor.plugins.has("CommentsRepository"))return;const e=this.editor.plugins.get("CommentsRepository"),t=this.editor.plugins.has("RealTimeCollaborativeComments"),o=document.querySelector(this.storage.getSourceDataSelector("comments"));if(!o||""==o.value||t)return;const i=JSON.parse(o.value);for(const t of i)e.addCommentThread(t);this.editor.model.document.on("comments:change:data",(()=>{this.updateStorage(e,o)})),this.editor.model.document.on("trackchanges:change:data",(()=>{this.updateStorage(e,o)}));const r=["addComment","change","removeComment","removeCommentThread","updateComment","resolveCommentThread","reopenCommentThread"];for(const t of r)e.on(t,(()=>{this.editor.model.document.fire("comments:change:data")}));this.editor.sourceElement.closest("form").addEventListener("submit",(()=>{this.updateStorage(e,o)}))}updateStorage(e,t){t.value=JSON.stringify(e.getCommentThreads({skipNotAttached:!1,skipEmpty:!0,toJSON:!0}))}}};return t=t.default})()));
\ No newline at end of file
/*!
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.CKEditor5=t():(e.CKEditor5=e.CKEditor5||{},e.CKEditor5.disableCollaborationMarkersInCaption=t())}(self,(()=>(()=>{"use strict";var e={d:(t,o)=>{for(var a in o)e.o(o,a)&&!e.o(t,a)&&Object.defineProperty(t,a,{enumerable:!0,get:o[a]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};e.d(t,{default:()=>o});const o={DisableCollaborationMarkersInCaption:class{constructor(e){this.editor=e}afterInit(){const e=this.editor,t=e.commands.get("addCommentThread"),o=e.commands.get("trackChanges");e.set("disabledCommands",!1);const a=e.plugins.has("CommentsRepository"),r=e.plugins.has("TrackChangesEditing"),i=e.plugins.has("DrupalImage");if(!a&&!r||!i)return;if(r){const t=e.plugins.get("TrackChangesEditing");if(!this.editor.commands.get("toggleImageCaption"))try{t.enableCommand("toggleImageCaption",((e,t)=>{e(t)}),{priority:"high"})}catch(e){return}}if(o){const e=this.editor.commands.get("toggleImageCaption");o.on("change:value",((t,o,a)=>{a?e.forceDisabled("drupal-premium-features"):e.clearForceDisabled("drupal-premium-features")}))}let s;e.model.document.on("change",(()=>{!e.disabledCommands&&o&&(s=o.value)}),{priority:"highest"}),e.model.document.on("change",(()=>{"caption"===e.model.document.selection.getFirstRange().getCommonAncestor().name?(t&&t.forceDisabled("drupal-premium-features"),o&&(o.value=!1,o.forceDisabled("drupal-premium-features")),e.set("disabledCommands",!0)):e.disabledCommands&&(t&&t.clearForceDisabled("drupal-premium-features"),o&&o.clearForceDisabled("drupal-premium-features"),e.set("disabledCommands",!1),s&&o&&(o.value=!0))}),{priority:"low"})}}};return t=t.default})()));
\ No newline at end of file
/*!
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.CKEditor5=e():(t.CKEditor5=t.CKEditor5||{},t.CKEditor5.disableGhsTableIntegration=e())}(self,(()=>(()=>{"use strict";var t={d:(e,o)=>{for(var r in o)t.o(o,r)&&!t.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:o[r]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e)},e={};t.d(e,{default:()=>o});const o={DisableGhsTableIntegration:class{constructor(t){this.editor=t}static get requires(){return["DataFilter"]}init(){this.editor.plugins.get("DataFilter").on("register:table",(t=>{t.stop()}),{priority:"high"})}}};return e=e.default})()));
\ No newline at end of file
/*!
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
!function(e,r){"object"==typeof exports&&"object"==typeof module?module.exports=r():"function"==typeof define&&define.amd?define([],r):"object"==typeof exports?exports.CKEditor5=r():(e.CKEditor5=e.CKEditor5||{},e.CKEditor5.drupalMediaTrackChangesIntegration=r())}(self,(()=>(()=>{var e={"ckeditor5/src/core.js":(e,r,t)=>{e.exports=t("dll-reference CKEditor5.dll")("./src/core.js")},"dll-reference CKEditor5.dll":e=>{"use strict";e.exports=CKEditor5.dll}},r={};function t(o){var a=r[o];if(void 0!==a)return a.exports;var i=r[o]={exports:{}};return e[o](i,i.exports,t),i.exports}t.d=(e,r)=>{for(var o in r)t.o(r,o)&&!t.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,get:r[o]})},t.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r);var o={};return(()=>{"use strict";t.d(o,{default:()=>a});var e=t("ckeditor5/src/core.js");class r extends e.Plugin{static get pluginName(){return"DrupalMediaTrackChangesIntegration"}afterInit(){const e=this.editor,r=e.plugins.get("TrackChangesEditing");r.enableCommand("insertDrupalMedia");const t=e.t;r._descriptionFactory.registerElementLabel("drupalMedia",(e=>t({string:"drupal media",plural:"%0 drupal medias",id:"ELEMENT_DRUPAL_MEDIA"},e)))}}const a={DrupalMediaTrackChangesIntegration:r}})(),o=o.default})()));
\ No newline at end of file
/*!
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.CKEditor5=e():(t.CKEditor5=t.CKEditor5||{},t.CKEditor5.errorNotifications=e())}(self,(()=>(()=>{"use strict";var t={d:(e,i)=>{for(var o in i)t.o(i,o)&&!t.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,get:i[o]})}};t.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}(),t.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e);var e={};t.d(e,{default:()=>d});const{View:i}=window.CKEditor5.ui,{Rect:o,Collection:r}=window.CKEditor5.utils,{Plugin:s}=window.CKEditor5.core,n=[{header:"Oops...",description:"It seems that the editor encountered an error. Save your content and refresh the page. If the error persists, contact your site administrator.",type:"error",reactsTo:{name:"CKEditorError"}},{header:"Trial limit exceeded",description:"Your premium features trial limit for this node has been exceeded. Create a new node or contact sales@cksource.com if you want to upgrade to the full version. ",type:"error",reactsTo:{message:"trial-license-key-reached-limit"}},{header:"WProofreader Authorization Error",description:"Some problems occurred during WProofreader initialization. Check the WProofreader plugin configuration.",type:"error",reactsTo:{message:"wproofreader-service-id-error"}},{header:"WProofreader usage limit exceeded",description:"The daily limit for the number of words checked using the WProofreader grammar and spell checker has been reached. Please contact your site administrator for help. Access to the service will resume at 00:00 UTC.",type:"error",reactsTo:{message:"wproofreader-usage-limit-error"}},{header:"WProofreader Error",description:"You have no permission to access the WProofreader proxy.",type:"error",reactsTo:{message:"wproofreader-permission-error"}}];class a extends i{constructor(e,i){super(e),this.reactsTo=i.reactsTo,this.closeNotificationButton=null,this.set("_editable",null),this.set("isVisible",!1),this.set("positionBottom","20px"),this.set("positionRight","15px"),this.createTemplate(i),this.render(),this.on("change:isVisible",(()=>this._updateNotificationPosition())),this.listenTo(t.g.document,"scroll",((t,e)=>{this.isVisible&&this._updateNotificationPosition()}))}createTemplate(t){const e=this.bindTemplate,i=this._createNotificationHeader(t.header,t.type),o=this._createNotificationDescription(t.description),r=this._createCloseNotificationButton();this.setTemplate({tag:"div",attributes:{class:["ck-notification",`ck-notification__${t.type}`,e.if("isVisible","ck-hidden",(t=>!t))],style:{position:"absolute",bottom:e.to("positionBottom"),right:e.to("positionRight"),"z-index":999}},children:[i,o,r]})}show(){this.isVisible=!0}hide(){this.isVisible=!1}_createNotificationHeader(t,e){const o=new i;return o.setTemplate({tag:"h4",attributes:{class:["ck-notification__header",`ck-notification__header-${e}`]},children:[t]}),o}_createNotificationDescription(t){const e=new i;return e.setTemplate({tag:"p",attributes:{class:["ck-notification__description"]},children:[t]}),e}_createCloseNotificationButton(){const t=new i,e=t.bindTemplate;return t.setTemplate({tag:"span",attributes:{class:["ck-notification__close"]},children:["x"],on:{click:e.to((t=>this.fire("closeNotification")))}}),t}_updateNotificationPosition(){const t=this._editable;if(!t)return;const e=new o(t).getVisible(),i=window.innerHeight,r=e.bottom-i;e.top>=i-100||(e.bottom>i?this.set("positionBottom",`${r+20}px`):this.set("positionBottom","20px"))}}const c=class extends s{constructor(...t){super(...t),this.availableNotifications=new r,this.activeNotification=null}static get pluginName(){return"ErrorNotifications"}init(){const t=this.editor;this._setupNotifications(n),this.set("_editable",null),t.ui.once("ready",(()=>this.set("_editable",t.ui.view.editable.element))),this._attachListeners()}destroy(){this.activeNotification&&(this.activeNotification.hide(),this.editor.ui.view.main.remove(this.activeNotification)),this.activeNotification=null,this._detachListeners(),super.destroy()}_setupNotifications(t){for(const e of t){const t=new a(this.editor.locale,e);t.bind("_editable").to(this,"_editable"),t.on("closeNotification",(()=>{t.hide(),this.activeNotification=null,this.editor.ui.view.main.remove(t),this.editor.editing.view.focus()})),this.availableNotifications.add(t)}}_attachListeners(){window.addEventListener("error",this._handleError.bind(this)),window.addEventListener("unhandledrejection",this._handleError.bind(this))}_detachListeners(){window.removeEventListener("error",this._handleError.bind(this)),window.removeEventListener("unhandledrejection",this._handleError.bind(this))}_handleError(t){let e=null;const i=new r;if(!this.activeNotification&&t.error){for(const e of this.availableNotifications){const o=e.reactsTo;for(const r in o)t.error[r]&&t.error[r].includes(o[r])&&i.add(e)}e=i.length>1?i.find((t=>t.reactsTo.message)):i.first,e&&(this.activeNotification=e,this.activeNotification.show(),this.editor.ui.view.main.add(this.activeNotification))}}},d={ErrorNotifications:c};return e=e.default})()));
\ No newline at end of file
/*!
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
!function(o,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.CKEditor5=e():(o.CKEditor5=o.CKEditor5||{},o.CKEditor5.exportAdapters=e())}(self,(()=>(()=>{"use strict";var o={d:(e,t)=>{for(var r in t)o.o(t,r)&&!o.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},o:(o,e)=>Object.prototype.hasOwnProperty.call(o,e)},e={};o.d(e,{default:()=>t});const t={ExportAdapters:class{constructor(o){o.config._config.exportPdf&&void 0!==o.config._config.exportPdf&&(o.config._config.exportPdf.dataCallback=o=>Drupal.CKEditor5PremiumFeatures.editorContentExportProcessor(o,{enableHighlighting:!0,convertImagesToBase64:o.config._config.exportPdf.convertImagesToBase64})),o.config._config.exportWord&&void 0!==o.config._config.exportWord&&(o.config._config.exportWord.dataCallback=o=>Drupal.CKEditor5PremiumFeatures.editorContentExportProcessor(o,{enableHighlighting:!0,convertImagesToBase64:o.config._config.exportWord.convertImagesToBase64}))}static get pluginName(){return"ExportAdapters"}}};return e=e.default})()));
\ No newline at end of file
/*!
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
!function(e,o){"object"==typeof exports&&"object"==typeof module?module.exports=o():"function"==typeof define&&define.amd?define([],o):"object"==typeof exports?exports.CKEditor5=o():(e.CKEditor5=e.CKEditor5||{},e.CKEditor5.removeIncorrectCollaborationMarkers=o())}(self,(()=>(()=>{"use strict";var e={d:(o,t)=>{for(var r in t)e.o(t,r)&&!e.o(o,r)&&Object.defineProperty(o,r,{enumerable:!0,get:t[r]})},o:(e,o)=>Object.prototype.hasOwnProperty.call(e,o)},o={};e.d(o,{default:()=>t});const t={RemoveIncorrectCollaborationMarkers:class{constructor(e){this.editor=e}afterInit(){this.editor.model.document.once("change:data",(()=>{const e=this.editor.plugins.has("TrackChanges"),o=this.editor.plugins.has("CommentsRepository"),t=Array.from(this.editor.model.document.differ.getChangedMarkers()),r=t.filter((e=>e.name.includes("suggestion"))),i=t.filter((e=>e.name.includes("comment")));if(e){const e=this.editor.plugins.get("TrackChanges");for(const o of r){const{name:t}=o,r=t.split(":"),i=r.length<5?r[2]:r[3];e.getSuggestions().some((e=>e.id==i))||this._removeSuggestionMarker(i)}}if(o){const e=this.editor.plugins.get("CommentsRepository");for(const o of i){const{name:t}=o,r=t.split(":")[1];e.getCommentThreads().some((e=>e.id==r))||this._removeCommentMarker(r)}}}),{priority:"high"})}_removeSuggestionMarker(e){const o=this.editor.model.markers.getMarkersGroup("suggestion"),t=Array.from(o).filter((o=>o.name.includes(e)));this.editor.model.change((e=>{e.removeMarker(...t)}),{priority:"high"})}_removeCommentMarker(e){const o=this.editor.model.markers.getMarkersGroup("comment"),t=Array.from(o).filter((o=>o.name.includes(e)));this.editor.model.change((e=>{e.removeMarker(...t)}))}}};return o=o.default})()));
\ No newline at end of file
/*!
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
!function(e,i){"object"==typeof exports&&"object"==typeof module?module.exports=i():"function"==typeof define&&define.amd?define([],i):"object"==typeof exports?exports.CKEditor5=i():(e.CKEditor5=e.CKEditor5||{},e.CKEditor5.sidebarAdapter=i())}(self,(()=>(()=>{"use strict";var e={d:(i,t)=>{for(var r in t)e.o(t,r)&&!e.o(i,r)&&Object.defineProperty(i,r,{enumerable:!0,get:t[r]})},o:(e,i)=>Object.prototype.hasOwnProperty.call(e,i)},i={};e.d(i,{default:()=>r});const t=class{constructor(e){this.editor=e,this.elementId=this.editor.sourceElement.dataset.ckeditor5PremiumElementId}processCollaborationCommandDisable(e){if(!this.isCollaborationDisabled())return!1;const i=this.editor.commands._commands.get(e);return void 0===i||i.forceDisabled("premium-features-module"),!0}processRevisionDisable(){return!!this.isCollaborationDisabled()&&(this.editor.plugins.has("RevisionTracker")&&(this.editor.plugins.get("RevisionTracker").isEnabled=!1),!0)}isCollaborationDisabled(){return void 0!==drupalSettings.ckeditor5Premium&&void 0!==drupalSettings.ckeditor5Premium.disableCollaboration&&!0===drupalSettings.ckeditor5Premium.disableCollaboration}getEditorParentContainer(e){let i=document.getElementById(e);for(;i&&void 0!==i&&void 0!==i.classList&&!i.classList.contains("ck-editor-container");)i=i.parentElement;return i&&void 0!==i?i.parentElement:null}getSourceDataSelector(e){return{trackChanges:".track-changes",comments:".comments",revisionHistory:".revision-history",revisionHistoryContainer:".revision-history-container",resolvedSuggestionsComments:".resolved-suggestions-comments"}[e]+"-data"+`[data-ckeditor5-premium-element-id="${this.elementId}"]`}};const r={SidebarAdapter:class{constructor(e){this.editor=e,this.storage=new t(e),this.toolbar=this.editor.ui._toolbarConfig.items,this.sidebarMode=drupalSettings.ckeditor5SidebarMode??"auto",this.resizeThreshold=0;let i=this.getSidebarWrapper(this.editor.sourceElement.id);void 0!==i&&i&&(this.sidebarColumn=i,this.sidebar=i.parentElement,this.editorContainer=this.sidebar.parentElement,this.editor.config._config.sidebar={container:i,preventScrollOutOfView:drupalSettings.ckeditor5Premium.preventScrollOutOfView})}static get pluginName(){return"SidebarAdapter"}sidebarVisibilityModify(e=!1){this.sidebar&&void 0!==this.sidebar&&this.sidebar.classList.toggle("slider-off",e)}init(){this.sidebar&&this.editor.plugins.has("AnnotationsUIs")&&this.addToggleButton()}afterInit(){if(!this.annotationsUIs||void 0===this.annotationsUIs||!this.sidebar||void 0===this.sidebar)return;let e=!this.toolbar.includes("trackChanges")&&!this.toolbar.includes("comment")||this.storage.isCollaborationDisabled();this.sidebarVisibilityModify(e),this.handleSidebarMode(),this.editor.config._config.sidebar.preventScrollOutOfView&&this.sidebarColumn.classList.add("prevent-scroll-out-of-view"),this.editor.on("ready",(()=>{this.setScrollBarObservers(),this.editor.ui.view.element&&(this.editor.ui.view.element.classList+=" ck-sidebar-enabled")}))}destroy(){if(!this.annotationsUIs||void 0===this.annotationsUIs||!this.sidebar||void 0===this.sidebar)return;this.resizeObserver&&this.resizeObserver.disconnect(),this.viewElementScrollbarObserver.disconnect(),this.sidebarVisibilityModify(!0);let e=this.getSidebarToggleWrapper();e&&e.remove()}addToggleButton(){this.annotationsUIs=this.editor.plugins.get("AnnotationsUIs"),this.toggleWrapper=document.createElement("div"),this.toggleWrapper.classList.add("ck-sidebar-auto-toggle-wrapper");let e=document.createElement("a");e.classList+="ck-sidebar-auto-toggle "+this.sidebarMode,e.id="ck-sidebar-auto-toggle",e.title="Switch to narrow sidebar mode",this.toggleWrapper.prepend(e),this.sidebar.prepend(this.toggleWrapper)}setScrollBarObservers(){this.viewElementScrollbarObserver=new ResizeObserver((e=>{const i=-29-(e[0].target.offsetWidth-e[0].target.clientWidth);this.toggleWrapper.style.marginLeft=i+"px"})),this.viewElementScrollbarObserver.observe(this.editor.ui.view.editable.element)}getSidebarWrapper(e){let i=this.storage.getEditorParentContainer(e);return i?i.querySelector(".ck-sidebar-wrapper"):null}handleSidebarMode(){let e=this.getSidebarToggleWrapper(),i=0;if(this.resizeObserver=new ResizeObserver((e=>{for(const t of e){const e=t.borderBoxSize?.[0].inlineSize;clearTimeout(this.resizeThreshold),this.resizeThreshold=setTimeout((()=>{"number"==typeof e&&e!==i&&(i=e,"auto"!==this.sidebarMode?this.setCkEditorSidebarMode(this.sidebarMode):this.updateCkeditorMode())}),100)}})),this.resizeObserver.observe(this.editorContainer),"auto"!==this.sidebarMode)return this.setCkEditorSidebarMode(this.sidebarMode),void(e&&(e.style.display="none"));this.updateCkeditorMode(),e&&e.addEventListener("click",(()=>{this.sidebar.classList.contains("narrowSidebar")?(this.sidebar.classList.remove("manual-toggled"),this.setCkEditorSidebarMode("wideSidebar")):(this.setCkEditorSidebarMode("narrowSidebar"),this.sidebar.classList.add("manual-toggled"))}))}getSidebarToggle(){return this.sidebar&&void 0!==this.sidebar?this.sidebar.querySelector(".ck-sidebar-auto-toggle"):null}getSidebarToggleWrapper(){return this.sidebar&&void 0!==this.sidebar?this.sidebar.querySelector(".ck-sidebar-auto-toggle-wrapper"):null}setCkEditorSidebarMode(e){if(!this.sidebar||void 0===this.sidebar)return;let i=this.getSidebarToggle();if(i.title="wideSidebar"===e?"Switch to narrow sidebar mode":"narrowSidebar"===e?"Switch to wide sidebar mode":"",this.sidebar.classList.contains("manual-toggled")&&"wideSidebar"===e){if(!this.annotationsUIs.isActive("inline")&&!this.annotationsUIs.isActive("wideSidebar"))return;e="narrowSidebar"}this.sidebar.classList.remove("inline","narrowSidebar","wideSidebar"),this.annotationsUIs.switchTo(e),this.sidebar.classList.add(e)}updateCkeditorMode(){let e=this.editorContainer.clientWidth,i=e>=720?"wideSidebar":e>=500?"narrowSidebar":"inline";this.setCkEditorSidebarMode(i)}}};return i=i.default})()));
\ No newline at end of file
/*!
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.CKEditor5=t():(e.CKEditor5=e.CKEditor5||{},e.CKEditor5.toolbarAdapter=t())}(self,(()=>(()=>{"use strict";var e={d:(t,o)=>{for(var r in o)e.o(o,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:o[r]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};e.d(t,{default:()=>r});const o=class{constructor(e){this.editor=e,this.elementId=this.editor.sourceElement.dataset.ckeditor5PremiumElementId}processCollaborationCommandDisable(e){if(!this.isCollaborationDisabled())return!1;const t=this.editor.commands._commands.get(e);return void 0===t||t.forceDisabled("premium-features-module"),!0}processRevisionDisable(){return!!this.isCollaborationDisabled()&&(this.editor.plugins.has("RevisionTracker")&&(this.editor.plugins.get("RevisionTracker").isEnabled=!1),!0)}isCollaborationDisabled(){return void 0!==drupalSettings.ckeditor5Premium&&void 0!==drupalSettings.ckeditor5Premium.disableCollaboration&&!0===drupalSettings.ckeditor5Premium.disableCollaboration}getEditorParentContainer(e){let t=document.getElementById(e);for(;t&&void 0!==t&&void 0!==t.classList&&!t.classList.contains("ck-editor-container");)t=t.parentElement;return t&&void 0!==t?t.parentElement:null}getSourceDataSelector(e){return{trackChanges:".track-changes",comments:".comments",revisionHistory:".revision-history",revisionHistoryContainer:".revision-history-container",resolvedSuggestionsComments:".resolved-suggestions-comments"}[e]+"-data"+`[data-ckeditor5-premium-element-id="${this.elementId}"]`}};const r={ToolbarAdapter:class{constructor(e){this.editor=e,this.storage=new o(e)}afterInit(){this.editor.on("ready",(()=>{this.toolbarContainer=this.getToolbarElement(this.editor.sourceElement.id),this.toolbarContainer&&this.toolbarContainer.appendChild(this.editor.ui.view.toolbar.element)}))}destroy(){this.toolbarContainer&&(this.toolbarContainer.innerHTML="")}getToolbarElement(e){let t=this.storage.getEditorParentContainer(e);return t?t.querySelector(".ck-toolbar-container"):null}}};return t=t.default})()));
\ No newline at end of file
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
class CollaborationStorage {
constructor( editor ) {
this.editor = editor;
this.elementId = this.editor.sourceElement.dataset.ckeditor5PremiumElementId;
}
/**
* Checks if collaboration is set to be disabled and blocks the specified command (button).
*
* @param commandName
* Command name (related to a button)
*
* @returns {boolean}
* TRUE if command was blocked, FALSE otherwise.
*/
processCollaborationCommandDisable(commandName) {
if (!this.isCollaborationDisabled()) {
return false;
}
const command = this.editor.commands._commands.get( commandName );
if (typeof command == 'undefined') {
return true;
}
command.forceDisabled( 'premium-features-module' );
return true;
}
/**
* Checks if collaboration is set to be disabled and blocks the revision history feature (button).
*
* @returns {boolean}
* TRUE if feature was blocked, FALSE otherwise.
*/
processRevisionDisable() {
if (!this.isCollaborationDisabled()) {
return false;
}
if (this.editor.plugins.has( 'RevisionTracker' )) {
this.editor.plugins.get( 'RevisionTracker' ).isEnabled = false;
}
return true;
}
/**
* Checks if collaboration is set to be disabled.
*
* @returns {boolean}
* TRUE if conditions for blocking collaboration are met, FALSE otherwise.
*/
isCollaborationDisabled() {
return typeof drupalSettings.ckeditor5Premium != 'undefined' &&
typeof drupalSettings.ckeditor5Premium.disableCollaboration != "undefined" &&
drupalSettings.ckeditor5Premium.disableCollaboration === true;
}
/**
* Returns parent element of an editors' element matching passed ID.
*
* @param elementId
* HTML ID of an editor.
*
* @returns {HTMLElement|null}
*/
getEditorParentContainer(elementId) {
let editorElement = document.getElementById(elementId);
while (editorElement && typeof editorElement !== "undefined"
&& typeof editorElement.classList !== "undefined" &&
!editorElement.classList.contains('ck-editor-container')) {
editorElement = editorElement.parentElement;
}
if (!editorElement || typeof editorElement === "undefined") {
return null;
}
// We get parentElement one more time to be able to search for all related
// editor elements (like sidebar, presence list etc)
return editorElement.parentElement;
}
getSourceDataSelector(type) {
const types = {
'trackChanges': '.track-changes',
'comments': '.comments',
'revisionHistory': '.revision-history',
'revisionHistoryContainer': '.revision-history-container',
'resolvedSuggestionsComments': '.resolved-suggestions-comments',
};
const cssClass = types[type] + '-data';
const dataAttribute = `[data-ckeditor5-premium-element-id="${this.elementId}"]`;
return cssClass + dataAttribute;
}
}
export default CollaborationStorage;
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @file The build process always expects an index.js file. Anything exported
* here will be recognized by CKEditor 5 as an available plugin. Multiple
* plugins can be exported in this one file.
*
* I.e. this file's purpose is to make plugin(s) discoverable.
*/
// cSpell:ignore simplebox
import CollaborationStorage from "./collaborationStorage";
export default {
CollaborationStorage,
};
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
import CollaborationStorage
from "../../collaborationStorage/src/collaborationStorage";
class CommentsAdapter {
constructor( editor ) {
this.editor = editor;
this.storage = new CollaborationStorage(editor);
const extraCommentsPlugins = Array.from(this.editor.plugins._availablePlugins.values()).filter(
plugin => [ 'Bold', 'Italic', 'DocumentList', 'Autoformat' ].includes( plugin.pluginName ),
);
this.editor.config._config.comments.editorConfig.extraPlugins.push(...extraCommentsPlugins);
}
static get pluginName() {
return 'CommentsAdapter'
}
static get requires() {
return [ 'CommentsRepository', 'Bold', 'Italic', 'DocumentList', 'Autoformat' ];
}
init() {
if (this.storage.processCollaborationCommandDisable("addCommentThread")) {
return;
}
if (!this.editor.plugins.has('CommentsRepository')) {
return
}
const commentsRepositoryPlugin = this.editor.plugins.get( 'CommentsRepository' );
const isRealtimeCommentsEnabled = this.editor.plugins.has('RealTimeCollaborativeComments');
const commentsRepositoryElement = document.querySelector(this.storage.getSourceDataSelector('comments'));
if (!commentsRepositoryElement || commentsRepositoryElement.value == '' || isRealtimeCommentsEnabled) {
return;
}
// Load comments.
const threads = JSON.parse(commentsRepositoryElement.value);
for (const thread of threads) {
commentsRepositoryPlugin.addCommentThread(thread);
}
// Observe data change and update the data fields.
this.editor.model.document.on( 'comments:change:data', () => {
this.updateStorage(commentsRepositoryPlugin, commentsRepositoryElement);
});
this.editor.model.document.on( 'trackchanges:change:data', () => {
this.updateStorage(commentsRepositoryPlugin, commentsRepositoryElement);
});
const events = [
'addComment',
'change',
'removeComment',
'removeCommentThread',
'updateComment',
'resolveCommentThread',
'reopenCommentThread'
];
for (const event of events) {
commentsRepositoryPlugin.on(event, () => {
this.editor.model.document.fire('comments:change:data');
});
}
// Hook to form submit.
const form = this.editor.sourceElement.closest('form');
form.addEventListener("submit", () => {
this.updateStorage(commentsRepositoryPlugin, commentsRepositoryElement);
});
}
updateStorage(plugin, storageElement) {
storageElement.value = JSON.stringify(plugin.getCommentThreads({
skipNotAttached: false,
skipEmpty: true,
toJSON: true
}));
}
}
export default CommentsAdapter;
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @file The build process always expects an index.js file. Anything exported
* here will be recognized by CKEditor 5 as an available plugin. Multiple
* plugins can be exported in this one file.
*
* I.e. this file's purpose is to make plugin(s) discoverable.
*/
// cSpell:ignore simplebox
import CommentsAdapter from "./commentsAdapter";
export default {
CommentsAdapter,
};
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
class DisableCollaborationMarkersInCaption {
constructor( editor ) {
this.editor = editor;
}
afterInit() {
const editor = this.editor;
const commentCommand = editor.commands.get( 'addCommentThread' );
const trackChangesCommand = editor.commands.get( 'trackChanges' );
editor.set( 'disabledCommands', false );
const hasCommentsRepository = editor.plugins.has('CommentsRepository');
const hasTrackChangesEditing = editor.plugins.has('TrackChangesEditing');
const hasDrupalImage = editor.plugins.has('DrupalImage');
if ((!hasCommentsRepository && !hasTrackChangesEditing) || !hasDrupalImage) {
return;
}
if (hasTrackChangesEditing) {
const tcEditing = editor.plugins.get( 'TrackChangesEditing' );
if (!this.editor.commands.get('toggleImageCaption')) {
try {
tcEditing.enableCommand( 'toggleImageCaption', ( executeCommand, options ) => {
executeCommand( options );
}, { priority: 'high' } );
} catch (error) {
return;
}
}
}
if (trackChangesCommand) {
const toggleImageCaptionCommand = this.editor.commands.get('toggleImageCaption');
trackChangesCommand.on('change:value', (evt, data, value) => {
if (value) {
toggleImageCaptionCommand.forceDisabled('drupal-premium-features')
} else {
toggleImageCaptionCommand.clearForceDisabled('drupal-premium-features')
}
})
}
let tcOriginalValue;
editor.model.document.on( 'change', () => {
if ( !editor.disabledCommands && trackChangesCommand) {
tcOriginalValue = trackChangesCommand.value;
}
}, { priority: 'highest' } );
editor.model.document.on( 'change', () => {
const range = editor.model.document.selection.getFirstRange();
const ancestor = range.getCommonAncestor();
if ( ancestor.name === 'caption' ) {
if (commentCommand) {
commentCommand.forceDisabled( 'drupal-premium-features' );
}
if (trackChangesCommand) {
trackChangesCommand.value = false;
trackChangesCommand.forceDisabled( 'drupal-premium-features' );
}
editor.set( 'disabledCommands', true );
} else {
if ( editor.disabledCommands ) {
if (commentCommand) {
commentCommand.clearForceDisabled( 'drupal-premium-features' );
}
if (trackChangesCommand) {
trackChangesCommand.clearForceDisabled('drupal-premium-features');
}
editor.set( 'disabledCommands', false );
if ( tcOriginalValue && trackChangesCommand) {
trackChangesCommand.value = true;
}
}
}
}, { priority: 'low' } );
}
}
export default DisableCollaborationMarkersInCaption;
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @file The build process always expects an index.js file. Anything exported
* here will be recognized by CKEditor 5 as an available plugin. Multiple
* plugins can be exported in this one file.
*
* I.e. this file's purpose is to make plugin(s) discoverable.
*/
import DisableCollaborationMarkersInCaption from "./disableCollaborationMarkersInCaption";
export default {
DisableCollaborationMarkersInCaption
};
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
class DisableGhsTableIntegration {
constructor( editor ) {
this.editor = editor;
}
static get requires() {
return [ 'DataFilter' ]
}
init() {
const dataFilter = this.editor.plugins.get( 'DataFilter' );
dataFilter.on('register:table', (e) => {
e.stop();
}, { priority: 'high' });
}
}
export default DisableGhsTableIntegration;
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @file The build process always expects an index.js file. Anything exported
* here will be recognized by CKEditor 5 as an available plugin. Multiple
* plugins can be exported in this one file.
*
* I.e. this file's purpose is to make plugin(s) discoverable.
*/
// cSpell:ignore simplebox
import DisableGhsTableIntegration from "./disableGhsTableIntegration";
export default {
DisableGhsTableIntegration
};
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
import { Plugin } from 'ckeditor5/src/core';
class DrupalMediaTrackChangesIntegration extends Plugin {
static get pluginName() {
return 'DrupalMediaTrackChangesIntegration';
}
afterInit() {
const editor = this.editor;
const trackChangesEditing = editor.plugins.get( 'TrackChangesEditing' );
trackChangesEditing.enableCommand( 'insertDrupalMedia' );
const t = editor.t;
trackChangesEditing._descriptionFactory.registerElementLabel(
'drupalMedia',
quantity => t( {
string: 'drupal media',
plural: '%0 drupal medias',
id: 'ELEMENT_DRUPAL_MEDIA'
}, quantity )
);
}
}
export default DrupalMediaTrackChangesIntegration;
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @file The build process always expects an index.js file. Anything exported
* here will be recognized by CKEditor 5 as an available plugin. Multiple
* plugins can be exported in this one file.
*
* I.e. this file's purpose is to make plugin(s) discoverable.
*/
import DrupalMediaTrackChangesIntegration from "./drupalMediaTrackChangesIntegration";
export default {
DrupalMediaTrackChangesIntegration
};
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
const { View } = window.CKEditor5.ui;
const { Rect, Collection } = window.CKEditor5.utils;
const { Plugin } = window.CKEditor5.core;
const definitions = [
{
header: 'Oops...',
description: 'It seems that the editor encountered an error. Save your content and refresh the page. If the error persists, contact your site administrator.',
type: 'error',
reactsTo: { name: 'CKEditorError' }
},
{
header: 'Trial limit exceeded',
description: 'Your premium features trial limit for this node has been exceeded. Create a new node or contact sales@cksource.com if you want to upgrade to the full version. ',
type: 'error',
reactsTo: { message: 'trial-license-key-reached-limit' }
},
{
header: 'WProofreader Authorization Error',
description: 'Some problems occurred during WProofreader initialization. Check the WProofreader plugin configuration.',
type: 'error',
reactsTo: { message: 'wproofreader-service-id-error' }
},
{
header: 'WProofreader usage limit exceeded',
description: 'The daily limit for the number of words checked using the WProofreader grammar and spell checker has been reached. Please contact your site administrator for help. Access to the service will resume at 00:00 UTC.',
type: 'error',
reactsTo: { message: 'wproofreader-usage-limit-error' }
},
{
header: 'WProofreader Error',
description: 'You have no permission to access the WProofreader proxy.',
type: 'error',
reactsTo: { message: 'wproofreader-permission-error' }
}
]
class ErrorNotifications extends Plugin {
constructor( ...args ) {
super( ...args );
this.availableNotifications = new Collection();
this.activeNotification = null;
}
static get pluginName() {
return 'ErrorNotifications'
}
init() {
const editor = this.editor;
this._setupNotifications( definitions );
this.set( '_editable', null );
editor.ui.once( 'ready', () => this.set( '_editable', editor.ui.view.editable.element ) );
this._attachListeners();
}
destroy() {
if (this.activeNotification) {
this.activeNotification.hide();
this.editor.ui.view.main.remove( this.activeNotification );
}
this.activeNotification = null;
this._detachListeners();
super.destroy();
}
_setupNotifications( definitions ) {
for ( const definition of definitions ) {
const notification = new NotificationView( this.editor.locale, definition );
notification.bind( '_editable' ).to( this, '_editable' );
notification.on( 'closeNotification', () => {
notification.hide();
this.activeNotification = null;
this.editor.ui.view.main.remove( notification );
this.editor.editing.view.focus();
} );
this.availableNotifications.add( notification )
}
}
_attachListeners() {
window.addEventListener( 'error', this._handleError.bind( this ) );
window.addEventListener( 'unhandledrejection', this._handleError.bind( this ) );
}
_detachListeners() {
window.removeEventListener( 'error', this._handleError.bind( this ) );
window.removeEventListener( 'unhandledrejection', this._handleError.bind( this ) );
}
_handleError( evt ) {
let notificationToShow = null;
const matches = new Collection();
if ( this.activeNotification || !evt.error ) {
return;
}
for ( const notification of this.availableNotifications ) {
const reactsTo = notification.reactsTo;
for ( const key in reactsTo ) {
if ( evt.error[ key ] && evt.error[ key ].includes( reactsTo[ key ] ) ) {
matches.add( notification );
}
}
}
// Notifications that react to the specific error message have higher priority
if ( matches.length > 1 ) {
notificationToShow = matches.find( notification => notification.reactsTo.message );
} else {
notificationToShow = matches.first;
}
if ( !notificationToShow ) {
return;
}
this.activeNotification = notificationToShow;
this.activeNotification.show();
this.editor.ui.view.main.add( this.activeNotification );
}
}
class NotificationView extends View {
constructor( locale, definition ) {
super( locale );
this.reactsTo = definition.reactsTo;
this.closeNotificationButton = null;
this.set( '_editable', null );
this.set( 'isVisible', false );
this.set( 'positionBottom', '20px' );
this.set( 'positionRight', '15px' );
this.createTemplate( definition );
this.render();
this.on( 'change:isVisible', () => this._updateNotificationPosition() )
this.listenTo( global.document, 'scroll', ( evt, data ) => {
if ( this.isVisible ) {
this._updateNotificationPosition();
}
} );
}
createTemplate( definition ) {
const bind = this.bindTemplate;
const notificationHeader = this._createNotificationHeader( definition.header, definition.type );
const notificationDescription = this._createNotificationDescription( definition.description );
const closeNotificationButton = this._createCloseNotificationButton();
this.setTemplate( {
tag: 'div',
attributes: {
class: [
'ck-notification',
`ck-notification__${ definition.type }`,
bind.if( 'isVisible', 'ck-hidden', value => !value )
],
style: {
position: 'absolute',
bottom: bind.to( 'positionBottom' ),
right: bind.to( 'positionRight' ),
'z-index': 999
}
},
children: [
notificationHeader,
notificationDescription,
closeNotificationButton
]
} )
}
show() {
this.isVisible = true;
}
hide() {
this.isVisible = false;
}
_createNotificationHeader( text, type ) {
const view = new View();
view.setTemplate( {
tag: 'h4',
attributes: {
class: [
'ck-notification__header',
`ck-notification__header-${ type }`
]
},
children: [ text ]
} )
return view;
}
_createNotificationDescription( text ) {
const view = new View();
view.setTemplate( {
tag: 'p',
attributes: {
class: [
'ck-notification__description'
]
},
children: [ text ]
} )
return view;
}
_createCloseNotificationButton() {
const view = new View();
const bind = view.bindTemplate;
view.setTemplate( {
tag: 'span',
attributes: {
class: [
'ck-notification__close'
]
},
children: [ 'x' ],
on: {
click: bind.to( evt => this.fire( 'closeNotification' ) )
}
} )
return view;
}
_updateNotificationPosition() {
const editable = this._editable;
if ( !editable ) {
return;
}
const editableRect = new Rect( editable );
const visibleRect = editableRect.getVisible();
const viewportHeight = window.innerHeight;
const bottomBoundary = visibleRect.bottom - viewportHeight;
const topBoundary = visibleRect.top;
// Prevent sticking out of the editable.
// viewportHeight - (notification height + margin)
if ( topBoundary >= viewportHeight - 100 ) {
return;
}
// If the editable's bottom boundary is invisible, stick the notification
// to the viewport's position in the editable.
if ( visibleRect.bottom > viewportHeight ) {
this.set( 'positionBottom', `${ bottomBoundary + 20 }px` );
} else {
this.set( 'positionBottom', '20px' );
}
}
}
export default ErrorNotifications;
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @file The build process always expects an index.js file. Anything exported
* here will be recognized by CKEditor 5 as an available plugin. Multiple
* plugins can be exported in this one file.
*
* I.e. this file's purpose is to make plugin(s) discoverable.
*/
// cSpell:ignore simplebox
import ErrorNotifications from "./errorNotifications";
export default {
ErrorNotifications,
};
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
class ExportAdapters {
constructor( editor ) {
// Attach custom dataCallback when PDF export is enabled.
if (editor.config._config.exportPdf && typeof editor.config._config.exportPdf !== 'undefined') {
editor.config._config.exportPdf.dataCallback = (editor) => {
return Drupal.CKEditor5PremiumFeatures.editorContentExportProcessor(
editor, { enableHighlighting:true, convertImagesToBase64: editor.config._config.exportPdf.convertImagesToBase64 });
}
}
// Attach custom dataCallback when Word export is enabled.
if (editor.config._config.exportWord && typeof editor.config._config.exportWord !== 'undefined') {
editor.config._config.exportWord.dataCallback = (editor) => {
return Drupal.CKEditor5PremiumFeatures.editorContentExportProcessor(
editor, { enableHighlighting: true, convertImagesToBase64: editor.config._config.exportWord.convertImagesToBase64 });
}
}
}
static get pluginName() {
return 'ExportAdapters'
}
}
export default ExportAdapters;
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @file The build process always expects an index.js file. Anything exported
* here will be recognized by CKEditor 5 as an available plugin. Multiple
* plugins can be exported in this one file.
*
* I.e. this file's purpose is to make plugin(s) discoverable.
*/
// cSpell:ignore simplebox
import ExportAdapters from "./exportAdapters";
export default {
ExportAdapters,
};
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @file The build process always expects an index.js file. Anything exported
* here will be recognized by CKEditor 5 as an available plugin. Multiple
* plugins can be exported in this one file.
*
* I.e. this file's purpose is to make plugin(s) discoverable.
*/
// cSpell:ignore simplebox
import RemoveIncorrectCollaborationMarkers
from "./removeIncorrectCollaborationMarkers";
export default {
RemoveIncorrectCollaborationMarkers
};
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
class RemoveIncorrectCollaborationMarkers {
constructor( editor ) {
this.editor = editor;
}
afterInit() {
// Subscribes to the `change:data` event to recognize markers in the document,
// then checks if the suggestion/comment data for the marker is available. If not - removes the marker.
this.editor.model.document.once( 'change:data', () => {
const isTrackChangesEnabled = this.editor.plugins.has( 'TrackChanges' );
const isCommentsEnabled = this.editor.plugins.has( 'CommentsRepository' );
const markers = Array.from( this.editor.model.document.differ.getChangedMarkers() );
const suggestionMarkers = markers.filter( marker => marker.name.includes( 'suggestion' ) );
const commentMarkers = markers.filter( marker => marker.name.includes( 'comment' ) );
if ( isTrackChangesEnabled ) {
const trackChanges = this.editor.plugins.get( 'TrackChanges' );
// Check if the marker has a corresponding suggestion data. If not - remove the marker.
for ( const marker of suggestionMarkers ) {
const { name } = marker;
const parts = name.split( ':' );
const suggestionId = parts.length < 5 ? parts[ 2 ] : parts[ 3 ];
const hasSuggestion = trackChanges.getSuggestions().some( suggestion => suggestion.id == suggestionId );
if ( !hasSuggestion ) {
this._removeSuggestionMarker( suggestionId );
}
}
}
if ( isCommentsEnabled ) {
const commentsRepository = this.editor.plugins.get( 'CommentsRepository' );
// Check if the marker has a corresponding comment data. If not - remove the marker.
for ( const marker of commentMarkers ) {
const { name } = marker;
const commentId = name.split( ':' )[ 1 ];
const hasComment = commentsRepository.getCommentThreads().some( comment => comment.id == commentId );
if ( !hasComment ) {
this._removeCommentMarker( commentId );
}
}
}
}, { priority: 'high' } )
}
/*
* Removes the suggestion marker from the document.
*/
_removeSuggestionMarker( id ) {
const markers = this.editor.model.markers.getMarkersGroup( 'suggestion' );
const suggestionMarker = Array.from( markers ).filter( marker => marker.name.includes( id ) );
this.editor.model.change( writer => {
writer.removeMarker( ...suggestionMarker );
}, { priority: 'high' } );
}
/*
* Removes the comment marker from the document.
*/
_removeCommentMarker( id ) {
const markers = this.editor.model.markers.getMarkersGroup( 'comment' );
const commentMarker = Array.from( markers ).filter( marker => marker.name.includes( id ) );
this.editor.model.change( writer => {
writer.removeMarker( ...commentMarker );
} );
}
}
export default RemoveIncorrectCollaborationMarkers;
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @file The build process always expects an index.js file. Anything exported
* here will be recognized by CKEditor 5 as an available plugin. Multiple
* plugins can be exported in this one file.
*
* I.e. this file's purpose is to make plugin(s) discoverable.
*/
// cSpell:ignore simplebox
import SidebarAdapter from "./sidebarAdapter";
export default {
SidebarAdapter,
};
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
import CollaborationStorage from "../../collaborationStorage/src/collaborationStorage";
class SidebarAdapter {
constructor( editor ) {
this.editor = editor;
this.storage = new CollaborationStorage(editor);
this.toolbar = this.editor.ui._toolbarConfig.items
this.sidebarMode = drupalSettings.ckeditor5SidebarMode ?? 'auto';
this.resizeThreshold = 0;
let sidebar_column = this.getSidebarWrapper(this.editor.sourceElement.id);
if (typeof sidebar_column === 'undefined' || !sidebar_column) {
return;
}
this.sidebarColumn = sidebar_column;
this.sidebar = sidebar_column.parentElement;
this.editorContainer = this.sidebar.parentElement;
this.editor.config._config.sidebar = {
container: sidebar_column,
preventScrollOutOfView: drupalSettings.ckeditor5Premium.preventScrollOutOfView,
}
}
static get pluginName() {
return 'SidebarAdapter'
}
sidebarVisibilityModify(hide = false) {
if (!this.sidebar || typeof this.sidebar === 'undefined') {
return;
}
this.sidebar.classList.toggle('slider-off', hide);
}
init() {
if (!this.sidebar || !this.editor.plugins.has('AnnotationsUIs')) {
return;
}
this.addToggleButton();
}
afterInit() {
if (!this.annotationsUIs || typeof this.annotationsUIs === "undefined" ||
!this.sidebar || typeof this.sidebar === 'undefined') {
return;
}
let sidebarHide = !this.toolbar.includes('trackChanges') && !this.toolbar.includes('comment') || this.storage.isCollaborationDisabled();
this.sidebarVisibilityModify(sidebarHide);
this.handleSidebarMode();
if (this.editor.config._config.sidebar.preventScrollOutOfView) {
this.sidebarColumn.classList.add('prevent-scroll-out-of-view');
}
this.editor.on('ready', () => {
this.setScrollBarObservers();
if (this.editor.ui.view.element) {
this.editor.ui.view.element.classList += ' ck-sidebar-enabled';
}
});
}
destroy() {
if (!this.annotationsUIs || typeof this.annotationsUIs === "undefined" ||
!this.sidebar || typeof this.sidebar === 'undefined') {
return;
}
if (this.resizeObserver) {
this.resizeObserver.disconnect();
}
this.viewElementScrollbarObserver.disconnect();
this.sidebarVisibilityModify(true);
let toggle = this.getSidebarToggleWrapper()
if (toggle) {
toggle.remove();
}
}
/**
* Set the sidebar toggle button.
*/
addToggleButton() {
this.annotationsUIs = this.editor.plugins.get('AnnotationsUIs');
this.toggleWrapper = document.createElement('div');
this.toggleWrapper.classList.add('ck-sidebar-auto-toggle-wrapper');
let toggle = document.createElement('a');
toggle.classList += 'ck-sidebar-auto-toggle ' + this.sidebarMode;
toggle.id = 'ck-sidebar-auto-toggle';
toggle.title = 'Switch to narrow sidebar mode';
this.toggleWrapper.prepend(toggle);
this.sidebar.prepend(this.toggleWrapper);
}
/**
* Set margin on toggle wrapper, so the toggle doesn't cover sidebar if it is visible.
*/
setScrollBarObservers() {
this.viewElementScrollbarObserver = new ResizeObserver(entries => {
const baseMargin = -29;
const scrollbarWidth = entries[0].target.offsetWidth - entries[0].target.clientWidth;
const totalMargin = baseMargin - scrollbarWidth;
this.toggleWrapper.style.marginLeft = totalMargin + "px";
});
this.viewElementScrollbarObserver.observe(this.editor.ui.view.editable.element);
}
/**
* Search sidebar element near the element with provided ID.
*
* @param elementId
* Editor related tag ID.
*
* @returns {null|Element}
* Sidebar tag or NULL if tag not found.
*/
getSidebarWrapper(elementId) {
let editorParent = this.storage.getEditorParentContainer(elementId);
if (!editorParent) {
return null;
}
return editorParent.querySelector('.ck-sidebar-wrapper');
}
/**
* Checks sidebar mode setting and attaches event listeners if required.
*/
handleSidebarMode() {
let toggle = this.getSidebarToggleWrapper();
let prevWidth = 0;
// Set the resize observer
this.resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
const width = entry.borderBoxSize?.[0].inlineSize;
clearTimeout(this.resizeThreshold);
this.resizeThreshold = setTimeout(() => {
if (typeof width === 'number' && width !== prevWidth) {
prevWidth = width;
if (this.sidebarMode !== 'auto') {
this.setCkEditorSidebarMode(this.sidebarMode);
} else {
this.updateCkeditorMode();
}
}
}, 100);
}
});
this.resizeObserver.observe(this.editorContainer);
if (this.sidebarMode !== 'auto') {
this.setCkEditorSidebarMode(this.sidebarMode);
if (toggle) {
toggle.style.display = 'none';
}
return;
}
this.updateCkeditorMode();
if (!toggle) {
return;
}
toggle.addEventListener('click', () => {
if (this.sidebar.classList.contains('narrowSidebar')) {
this.sidebar.classList.remove('manual-toggled');
this.setCkEditorSidebarMode('wideSidebar');
}
else {
this.setCkEditorSidebarMode('narrowSidebar');
this.sidebar.classList.add('manual-toggled');
}
});
}
/**
* Returns a toggle button for handled sidebar or null if not found.
*
* @returns {null|Element}
*/
getSidebarToggle() {
if (!this.sidebar || typeof this.sidebar === 'undefined') {
return null;
}
return this.sidebar.querySelector(".ck-sidebar-auto-toggle");
}
/**
* Returns a toggle button wrapper for handled sidebar or null if not found.
*
* @returns {null|Element}
*/
getSidebarToggleWrapper() {
if (!this.sidebar || typeof this.sidebar === 'undefined') {
return null;
}
return this.sidebar.querySelector(".ck-sidebar-auto-toggle-wrapper");
}
/**
* Setup new sidebar mode.
*
* @param newMode
* Sidebar mode to setup.
*/
setCkEditorSidebarMode(newMode) {
if (!this.sidebar || typeof this.sidebar === 'undefined') {
return;
}
let toggle = this.getSidebarToggle();
if (newMode === "wideSidebar") {
toggle.title = 'Switch to narrow sidebar mode';
} else if (newMode === "narrowSidebar") {
toggle.title = 'Switch to wide sidebar mode';
} else {
toggle.title = '';
}
if (this.sidebar.classList.contains('manual-toggled') && newMode === 'wideSidebar') {
if (this.annotationsUIs.isActive('inline') || this.annotationsUIs.isActive('wideSidebar')) {
newMode = 'narrowSidebar';
} else {
return;
}
}
this.sidebar.classList.remove('inline', 'narrowSidebar', 'wideSidebar');
this.annotationsUIs.switchTo(newMode);
this.sidebar.classList.add(newMode);
}
/**
* Setup sidebar mode depends on resolution.
*/
updateCkeditorMode() {
// TODO: move to config?
let w = this.editorContainer.clientWidth;
let newMode = w >= 720 ? 'wideSidebar' : (w >= 500 ? 'narrowSidebar' : 'inline');
this.setCkEditorSidebarMode(newMode);
}
}
export default SidebarAdapter;
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @file The build process always expects an index.js file. Anything exported
* here will be recognized by CKEditor 5 as an available plugin. Multiple
* plugins can be exported in this one file.
*
* I.e. this file's purpose is to make plugin(s) discoverable.
*/
// cSpell:ignore simplebox
import ToolbarAdapter from "./toolbarAdapter";
export default {
ToolbarAdapter,
};
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
import SidebarAdapter from "../../sidebarAdapter/src/sidebarAdapter";
import CollaborationStorage
from "../../collaborationStorage/src/collaborationStorage";
class ToolbarAdapter {
constructor(editor) {
this.editor = editor;
this.storage = new CollaborationStorage(editor);
}
afterInit() {
this.editor.on('ready', () => {
this.toolbarContainer = this.getToolbarElement(this.editor.sourceElement.id);
if (this.toolbarContainer) {
this.toolbarContainer.appendChild(this.editor.ui.view.toolbar.element);
}
});
}
destroy() {
if (this.toolbarContainer) {
this.toolbarContainer.innerHTML = "";
}
}
getToolbarElement(elementId) {
let editorParent = this.storage.getEditorParentContainer(elementId);
if (!editorParent) {
return null;
}
return editorParent.querySelector('.ck-toolbar-container');
}
}
export default ToolbarAdapter;
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @file
* CKEditor 5 premium features override of CKEditor 5 implementation
* of {@link Drupal.editors} API.
*
* Prevents executing original detach on tabledrag and ensures there is no
* editor already initialized before attaching new one.
*/
((Drupal) => {
Drupal.editors.ckeditor5.coreAttach = Drupal.editors.ckeditor5.attach;
Drupal.editors.ckeditor5.coreDetach = Drupal.editors.ckeditor5.detach;
Drupal.editors.ckeditor5.attach = function(element, format) {
const id = element.getAttribute('data-ckeditor5-id');
const editor = Drupal.CKEditor5Instances.get(id);
// Init new editor only if we don't yet have it on element.
if (!editor) {
Drupal.editors.ckeditor5.coreAttach(element, format);
}
}
Drupal.editors.ckeditor5.detach = function(element, format, trigger) {
if (trigger !== 'move') {
Drupal.editors.ckeditor5.coreDetach(element, format, trigger);
}
}
})(Drupal);
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
(function ($, Drupal) {
Drupal.CKEditor5PremiumFeatures.base64ImageConverter = {
/**
* Convert images url into base64 in HTML
* @param content
* Document content.
* @param filesType
* Type of conversion. Private or All files.
*
* @returns {Promise<string>}
*/
async convert(content, filesType) {
return new Promise( async resolve => {
let result = await new Promise( resolve => {
$.post('/ck5/api/base64-image-converter', {
document: JSON.stringify(content),
filesType: filesType,
}).done(function(result) {
if(!result.document) {
resolve(content);
}
resolve(result.document);
});
});
resolve(result);
});
},
}
})(jQuery, Drupal);
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
(function ($, Drupal) {
Drupal.CKEditor5PremiumFeatures = {
editorContentExportProcessor: async function(editor, config = { enableHighlighting : true }) {
this.editor = editor;
let editorContent = this.getEditorContent(config.enableHighlighting);
editorContent = await Drupal.CKEditor5PremiumFeatures.mediaTagsConverter.convertMediaTags(
editorContent,
editor.sourceElement.dataset.editorActiveTextFormat
);
if (config.convertImagesToBase64.enabled) {
editorContent = await Drupal.CKEditor5PremiumFeatures.base64ImageConverter.convert(
editorContent,
config.convertImagesToBase64.filesType
);
}
editorContent = Drupal.CKEditor5PremiumFeatures.relativePathsProcessor(editorContent);
return editorContent;
},
getEditorContent(enableHighlighting = true) {
return this.editor.getData( {
showSuggestionHighlights: enableHighlighting,
});
},
}
})(jQuery, Drupal);
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
(function ($, Drupal) {
Drupal.CKEditor5PremiumFeatures.mediaTagsConverter = {
/**
* Convert drupal-media tags to proper HTML rendered content.
* @param content
* Document content.
* @param format
* Text format.
*
* @returns {Promise<string>}
*/
async convertMediaTags(content, format) {
return new Promise( async resolve => {
const parser = new DOMParser();
const documentDom = parser.parseFromString( content, 'text/html' ).body;
documentDom.innerHTML = content;
if (drupalSettings.ckeditor5Premium.isMediaInstalled) {
let elementsAttributes = this.getTagProperties(documentDom);
let mediaPaths = await this.queryMediaPaths(elementsAttributes, format);
this.replaceMediaTags(documentDom, mediaPaths);
}
resolve(documentDom.innerHTML);
});
},
/**
* Returns a list of properties collected for drupal-media tags.
*
* @param documentDom
* A DOM element to search in.
*
* @returns {*[]}
*/
getTagProperties(documentDom) {
let elements = documentDom.getElementsByTagName("drupal-media");
let elementsAttributes = [];
for (let e = 0; e < elements.length; ++e) {
let type = elements[e].dataset.entityType;
let id = elements[e].dataset.entityUuid;
elementsAttributes.push({type: type, id: id});
}
return elementsAttributes;
},
/**
* Queries a backed API to get media elements rendered.
*
* @param elementAttributes
* List of media tag attributes.
* @param format
* Text editor format.
*
* @returns {Promise<unknown>}
*/
queryMediaPaths(elementAttributes, format) {
return new Promise( resolve => {
$.post('/ck5/api/media-tags/' + format, {
media: JSON.stringify(elementAttributes),
}).done(function(result) {
resolve(result);
});
});
},
/**
* Replaces media tags with rendered entities HTML.
*
* @param documentElements
* A document DOM element to search in and replace media tags.
* @param mediaTagContent
* A list of rendered media tags.
*/
replaceMediaTags(documentElements, mediaTagContent) {
for (const mediaInfo of mediaTagContent) {
let mediaTags = documentElements.querySelectorAll('[data-entity-uuid="' + mediaInfo.uuid + '"]');
for (const tag of mediaTags) {
tag.innerHTML = mediaInfo.rendered;
}
}
},
}
})(jQuery, Drupal);
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
(function ($, Drupal) {
Drupal.CKEditor5PremiumFeatures.relativePathsProcessor = function(content) {
let basePath = window.location.origin + drupalSettings.path.baseUrl;
let attributes = [
'src',
'href',
'poster',
'icon',
'data',
'background'
];
let pattern = new RegExp("(" + attributes.join("|") + ")\s*=\s*(\"|')(((?!\/\/)[^\"'><])+)(\"|')", "igd");
content = content.replace(pattern, function(matched){
// Let's make sure there is no additional spaces around "=" and '"' characters.
matched = matched.replace(/\s*\=\s*\"\s*\/?/g,'="');
let splitted = matched.split('="');
// At this point we're sure that basePath is a string with "/" at the end,
// and that there is no "/" at the beginning of URL path stored in splitted[1]
splitted[1] = basePath + splitted[1];
return splitted.join('="');
})
return '<base href="' + basePath + '" />'
+ content;
}
}) (jQuery, Drupal);
ckeditor5_premium_features_ai_assistant__ai_assistant:
ckeditor5:
plugins:
- ai.AIAssistant
- aiServiceAdapter.AIServiceAdapter
drupal:
label: AI Assistant
elements: false
library: ckeditor5_premium_features_ai_assistant/ai-service-adapter
class: Drupal\ckeditor5_premium_features_ai_assistant\Plugin\CKEditor5Plugin\AiAssistant
admin_library: ckeditor5_premium_features_ai_assistant/admin.ai_assistant
toolbar_items:
aiAssistant:
label: AI Assistant
aiCommands:
label: AI Commands
name: CKEditor 5 Premium Features AI Assistant
type: module
description: "Provides AI Assistant features."
package: CKEditor 5 Premium
core_version_requirement: ^10.2 || ^11
configure: ckeditor5_premium_features_ai_assistant.form.settings
dependencies:
- ckeditor5_premium_features:ckeditor5_premium_features
# Information added by Drupal.org packaging script on 2024-06-13
version: '1.2.9'
project: 'ckeditor5_premium_features'
datestamp: 1718272069
admin.ai_assistant:
version: 20231121
css:
theme:
css/ai-assistant.admin.css: { }
ai-service-adapter:
version: 20231213
license:
name: GNU-GPL-2.0-or-later
url: https://raw.githubusercontent.com/ckeditor/ckeditor5/master/LICENSE.md
gpl-compatible: true
js:
js/build/aiServiceAdapter.js: { minified: true }
dependencies:
- ckeditor5_premium_features_ai_assistant/ai
entity.ckeditor5_ai_command_group.add_form:
route_name: 'entity.ckeditor5_ai_command_group.add_form'
title: 'Add CKEditor 5 AI Commands Group'
appears_on:
- entity.ckeditor5_ai_command_group.collection
ckeditor5_premium_features_ai_assistant.form.settings:
title: AI Assistant
route_name: ckeditor5_premium_features_ai_assistant.form.settings
parent: ckeditor5_premium_features.form.settings
description: 'CKEditor 5 AI Assistant Configuration Form'
weight: 30
entity.ckeditor5_ai_command_group.overview:
title: AI Commands Group
parent: ckeditor5_premium_features.form.settings
description: 'List of CKEditor 5 AI Commands Group.'
route_name: entity.ckeditor5_ai_command_group.collection
weight: 31
ckeditor5_premium_features_ai_assistant.form.settings:
title: AI Assistant
route_name: ckeditor5_premium_features_ai_assistant.form.settings
base_route: ckeditor5_premium_features.form.settings
description: 'AI Assistant Configuration Form'
weight: 30
<?php
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
declare(strict_types = 1);
use Drupal\ckeditor5_premium_features\Utility\LibraryDefinitionItem;
/**
* Implements hook_library_info_build().
*/
function ckeditor5_premium_features_ai_assistant_library_info_build(): array {
/** @var \Drupal\ckeditor5_premium_features\Config\SettingsConfigHandlerInterface $config_handler */
$config_handler = \Drupal::service('ckeditor5_premium_features.config_handler.settings');
$definition = new LibraryDefinitionItem('ai', $config_handler->getDllLocation());
$definition->addRemoteJs($definition->id());
$definitions[$definition->id()] = $definition->getDefinition();
return $definitions;
}
administer ckeditor5_ai_command_group:
title: 'Administer CKEditor 5 AI Commands Group'
access ai assistant provider:
title: 'Access to the CKEditor 5 AI Completion API'
ckeditor5_premium_features_ai_assistant.form.settings:
path: '/admin/config/ckeditor5-premium-features/ai-assistant'
defaults:
_title: 'AI Assistant Configuration'
_form: 'Drupal\ckeditor5_premium_features_ai_assistant\Form\SettingsForm'
requirements:
_permission: 'administer site configuration'
entity.ckeditor5_ai_command_group.collection:
path: '/admin/config/ckeditor5-premium-features/ai-assistant/ckeditor5-ai-command-group'
defaults:
_entity_list: 'ckeditor5_ai_command_group'
_title: 'CKEditor 5 AI Commands Group Configuration'
requirements:
_permission: 'administer ckeditor5_ai_command_group'
entity.ckeditor5_ai_command_group.add_form:
path: '/admin/config/ckeditor5-premium-features/ai-assistant/ckeditor5-ai-command-group/add'
defaults:
_entity_form: 'ckeditor5_ai_command_group.add'
_title: 'Add a CKEditor 5 AI Commands Group'
requirements:
_permission: 'administer ckeditor5_ai_command_group'
entity.ckeditor5_ai_command_group.edit_form:
path: '/admin/config/ckeditor5-premium-features/ai-assistant/ckeditor5-ai-command-group/{ckeditor5_ai_command_group}'
defaults:
_entity_form: 'ckeditor5_ai_command_group.edit'
_title: 'Edit a CKEditor 5 AI Commands Group'
requirements:
_permission: 'administer ckeditor5_ai_command_group'
entity.ckeditor5_ai_command_group.delete_form:
path: '/admin/config/ckeditor5-premium-features/ai-assistant/ckeditor5-ai-command-group/{ckeditor5_ai_command_group}/delete'
defaults:
_entity_form: 'ckeditor5_ai_command_group.delete'
_title: 'Delete a CKEditor 5 AI Commands Group'
requirements:
_permission: 'administer ckeditor5_ai_command_group'
ckeditor5_ai_command.add_form:
path: '/admin/config/ckeditor5-premium-features/ai-assistant/ckeditor5-ai-command-group/{ckeditor5_ai_command_group}/ckeditor5-ai-command/add'
defaults:
_form: '\Drupal\ckeditor5_premium_features_ai_assistant\Form\CKEditor5AiCommandAddForm'
_title: 'Add a CKEditor 5 AI Command'
requirements:
_permission: 'administer ckeditor5_ai_command_group'
ckeditor5_ai_command.delete_form:
path: '/admin/config/ckeditor5-premium-features/ai-assistant/ckeditor5-ai-command-group/{ckeditor5_ai_command_group}/ckeditor5-ai-command/{uuid}/delete'
defaults:
_form: '\Drupal\ckeditor5_premium_features_ai_assistant\Form\CKEditor5AiCommandDeleteForm'
_title: 'Delete a CKEditor 5 AI Command'
requirements:
_permission: 'administer ckeditor5_ai_command_group'
ckeditor5_ai_command.edit_form:
path: '/admin/config/ckeditor5-premium-features/ai-assistant/ckeditor5-ai-command-group/{ckeditor5_ai_command_group}/ckeditor5-ai-command/{uuid}'
defaults:
_form: '\Drupal\ckeditor5_premium_features_ai_assistant\Form\CKEditor5AiCommandEditForm'
_title: 'Edit a CKEditor 5 AI Command'
requirements:
_permission: 'administer ckeditor5_ai_command_group'
ckeditor5_premium_features_ai_assistant.ai_assistant_proxy_provider:
path: '/ckeditor5-premium-features-ai-assistant/completion'
defaults:
_title: 'Ai Assistant proxy provider'
_controller: '\Drupal\ckeditor5_premium_features_ai_assistant\Controller\AiAssistantProviderProxyController'
methods: [POST]
options:
no_cache: TRUE
requirements:
_permission: 'access ai assistant provider'
services:
plugin.manager.ckeditor5_ai_provider:
class: Drupal\ckeditor5_premium_features_ai_assistant\CKEditor5AiProviderPluginManager
parent: default_plugin_manager
ckeditor5_premium_features_ai_assistant.ai_assistant_helper:
class: Drupal\ckeditor5_premium_features_ai_assistant\Utility\AiAssistantHelper
arguments:
- '@config.factory'
- '@plugin.manager.ckeditor5_ai_provider'
ai_command:
type: mapping
mapping:
uuid:
type: string
label: 'Uuid'
command_id:
type: string
label: 'Command id'
label:
type: string
label: 'Label'
prompt:
type: prompt
label: 'Prompt'
weight:
type: integer
label: 'Wight'
ckeditor5.plugin.ckeditor5_premium_features_ai_assistant__ai_assistant:
type: mapping
label: 'AI Assistant'
mapping:
remove_commands:
type: sequence
sequence:
type: string
label: 'Command to be removed from editor'
ckeditor5_premium_features_ai_assistant.settings:
type: config_object
label: 'CKEditor 5 Premium Features - AI Assistant'
mapping:
api_url:
type: string
label: 'Api Url'
auth_key:
type: string
label: 'Auth key'
proxy_auth_key:
type: boolean
label: 'Use the Auth key as auth endpoint'
disable_default_styles:
type: boolean
label: 'Disable default styles'
ckeditor5_premium_features_ai_assistant.ckeditor5_ai_command_group.*:
type: config_entity
label: CKEditor 5 AI Commands group
mapping:
id:
type: string
label: ID
label:
type: label
label: Label
textFormats:
type: sequence
label: Available in text format
commands:
type: sequence
sequence:
type: ai_command
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
.ckeditor5-toolbar-button-aiAssistant {
background-image: url(../icons/ai-assistant.svg);
}
.ckeditor5-toolbar-button-aiCommands {
background-image: url(../icons/ai-commands.svg);
}
<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M9.61 2.66a1.406 1.406 0 1 0-1.407 0v.891H3.28a2.11 2.11 0 0 0-2.11 2.11v10.312a2.11 2.11 0 0 0 2.11 2.109h5.684l-.054-1.157.18-.25H3.28a.703.703 0 0 1-.703-.702V5.66c0-.389.315-.704.703-.704h11.25c.388 0 .703.315.703.704v2.484l.358-.497a2.492 2.492 0 0 1 1.048-.84V5.66a2.11 2.11 0 0 0-2.11-2.11H9.61v-.89Z"/><path d="M5.625 10.817c.518 0 .937-.63.937-1.407 0-.776-.42-1.406-.937-1.406-.518 0-.938.63-.938 1.406 0 .777.42 1.407.938 1.407Z"/><path d="M13.125 9.41c0 .777-.42 1.407-.938 1.407s-.937-.63-.937-1.407c0-.776.42-1.406.937-1.406.518 0 .938.63.938 1.406Z"/><path d="M.937 8.004A.937.937 0 0 0 0 8.942v1.875c0 .517.42.937.937.937v-3.75Z"/><path d="M6.128 12.51a.782.782 0 0 1 1.085.216c.272.408.907.707 1.693.707s1.421-.3 1.693-.707a.782.782 0 0 1 1.302.868c-.666 1-1.906 1.403-2.995 1.403-1.089 0-2.329-.404-2.995-1.403a.782.782 0 0 1 .217-1.085Z"/><path d="m16.987 8.91-.622.864 2.879 2.074.622-.864a.71.71 0 0 0-.161-.99l-1.728-1.245a.71.71 0 0 0-.99.161Z"/><path d="M11.635 19.951a.355.355 0 0 1-.449-.31l-.214-2.38 4.978-6.911 2.88 2.074-4.978 6.91-2.217.617Z"/></svg>
\ No newline at end of file
<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M15.346 9.422a.151.151 0 0 1 .284 0l.548 1.484a.152.152 0 0 0 .09.089l1.483.549a.151.151 0 0 1 0 .284l-1.483.548a.151.151 0 0 0-.09.09l-.548 1.483a.152.152 0 0 1-.142.1.151.151 0 0 1-.142-.1l-.549-1.483a.15.15 0 0 0-.09-.09l-1.483-.548a.15.15 0 0 1 0-.284l1.484-.549a.152.152 0 0 0 .089-.09l.549-1.483Z"/><path d="M16.306 1.742a.151.151 0 0 1 .284 0l.549 1.483a.15.15 0 0 0 .089.09l1.483.548a.151.151 0 0 1 .072.229.151.151 0 0 1-.072.055l-1.483.549a.15.15 0 0 0-.09.09l-.548 1.482a.151.151 0 0 1-.284 0l-.549-1.483a.15.15 0 0 0-.09-.09l-1.483-.548a.151.151 0 0 1 0-.284l1.484-.549a.152.152 0 0 0 .09-.089l.548-1.483Z"/><path d="M7.665 1.742a.151.151 0 0 1 .284 0l.549 1.483a.151.151 0 0 0 .09.09l1.482.548a.151.151 0 0 1 .072.229.151.151 0 0 1-.072.055l-1.483.549a.151.151 0 0 0-.09.09L7.95 6.267a.151.151 0 0 1-.284 0l-.549-1.483a.151.151 0 0 0-.089-.09l-1.483-.548a.151.151 0 0 1 0-.284l1.483-.549a.151.151 0 0 0 .09-.089l.548-1.483-.001.001Z"/><path d="M14.72 7.946a.848.848 0 0 0 .25-.591.824.824 0 0 0-.241-.588l-1.943-1.938a.812.812 0 0 0-.588-.241.838.838 0 0 0-.591.25l-1.545 1.539 3.115 3.115 1.542-1.546h.001Z"/><path clip-rule="evenodd" d="M1.19 15.636a.96.96 0 0 1 .281-.679l7.835-7.834 3.121 3.12-7.834 7.835a.959.959 0 0 1-1.358 0l-1.764-1.764a.96.96 0 0 1-.28-.678Zm9.22-5.391-1.121-1.12-6.479 6.478 1.121 1.121 6.479-6.479Z"/></svg>
\ No newline at end of file
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
import AWSTextAdapter from "@ckeditor/ckeditor5-ai/src/adapters/awstextadapter";
import OpenAITextAdapter from "@ckeditor/ckeditor5-ai/src/adapters/openaitextadapter";
class AIServiceAdapter {
static get pluginName() {
return 'AIServiceAdapter'
}
constructor( editor ) {
this.editor = editor;
const AIAdapter = this.editor.plugins.get('AIAdapter');
let textAdapter;
if (this.editor.config._config.ai.textAdapter === 'aws') {
textAdapter = new AWSTextAdapter(editor);
} else {
textAdapter = new OpenAITextAdapter(editor);
}
AIAdapter.set('textAdapter', textAdapter)
}
}
export default AIServiceAdapter
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
import AIServiceAdapter from "./aiServiceAdapter";
export default {
AIServiceAdapter,
};
<?php
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
declare(strict_types = 1);
namespace Drupal\ckeditor5_premium_features_ai_assistant;
/**
* Enumeration of the types of AITextAdapter.
*/
enum AITextAdapter: string {
case OpenAI = 'openAI';
case AWS = 'aws';
}
<?php
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
declare(strict_types = 1);
namespace Drupal\ckeditor5_premium_features_ai_assistant\Annotation;
use Drupal\Component\Annotation\Plugin;
/**
* Defines ckeditor5_ai_provider annotation object.
*
* @Annotation
*/
final class CKEditor5AiProvider extends Plugin {
/**
* The plugin ID.
*/
public readonly string $id;
/**
* The human-readable name of the plugin.
*
* @ingroup plugin_translatable
*/
public readonly string $title;
/**
* The description of the plugin.
*
* @ingroup plugin_translatable
*/
public readonly string $description;
}
<?php
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
declare(strict_types = 1);
namespace Drupal\ckeditor5_premium_features_ai_assistant;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
/**
* Provides an interface defining a CKEditor 5 AI commands group entity type.
*/
interface CKEditor5AiCommandGroupInterface extends ConfigEntityInterface {
/**
* Add command to the commands list.
*
* @param array $command
* Array with command values. ai_command
* [ command_id, label, weight, prompt ].
*
* @return \Drupal\ckeditor5_premium_features_ai_assistant\Entity\CKEditor5AiCommandGroup
*
* @throws \Drupal\Core\Entity\EntityStorageException
*/
public function addCommand(array $command): static;
/**
* Get command from commands list.
*
* @param string $uuid
* Command uuid.
*
* @return array
*/
public function getCommandByUuid(string $uuid): array;
/**
* Remove command from commands list.
*
* @param string $uuid
* Command uuid.
*
* @return \Drupal\ckeditor5_premium_features_ai_assistant\Entity\CKEditor5AiCommandGroup
*
* @throws \Drupal\Core\Entity\EntityStorageException
*/
public function removeCommand(string $uuid):static;
/**
* Update command values.
*
* @param array $command
* Array with command values.
* [ uuid, command_id, label, weight, prompt ].
*
* @return \Drupal\ckeditor5_premium_features_ai_assistant\Entity\CKEditor5AiCommandGroup
*
* @throws \Drupal\Core\Entity\EntityStorageException
*/
public function updateCommand(array $command): static;
/**
* Update weights of commands.
*
* @param array $weights
* Array with weights associated with commands.
*
* @return \Drupal\ckeditor5_premium_features_ai_assistant\Entity\CKEditor5AiCommandGroup
*/
public function updateWeights(array $weights): static;
/**
* Returns an array of definitions.
*/
public function getDefinition(): array;
/**
* Checks if command with provided id exists.
*
* @param string $id
* Command id.
*
* @return bool
*/
public function commandExists(string $id): bool;
}
<?php
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
declare(strict_types = 1);
namespace Drupal\ckeditor5_premium_features_ai_assistant;
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
use Drupal\Core\Entity\EntityInterface;
/**
* Provides a listing of ckeditor 5 ai commands groups.
*/
class CKEditor5AiCommandGroupListBuilder extends ConfigEntityListBuilder {
/**
* {@inheritdoc}
*/
public function buildHeader() {
$header['label'] = $this->t('Label');
$header['id'] = $this->t('ID');
return $header + parent::buildHeader();
}
/**
* {@inheritdoc}
*/
public function buildRow(EntityInterface $entity) {
/** @var \Drupal\ckeditor5_premium_features_ai_assistant\CKEditor5AiCommandGroupInterface $entity */
$row['label'] = $entity->label();
$row['id'] = $entity->id();
return $row + parent::buildRow($entity);
}
}
<?php
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
declare(strict_types = 1);
namespace Drupal\ckeditor5_premium_features_ai_assistant;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* Interface for ckeditor5_ai_provider plugins.
*/
interface CKEditor5AiProviderInterface {
/**
* Returns the translated plugin label.
*/
public function label(): string;
/**
* Handle completions request.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* Request object.
*
* @return \Symfony\Component\HttpFoundation\Response
* The completions response.
*/
public function processRequest(Request $request): Response;
/**
* Returns array of field configuration to build in AI Settings form.
*
* Fields should have structure of Form API fields.
* [
* "example_field" => ['#type' => 'textfield', '#title' => 'Sample title']
* ]
*
* @return array
* Array of fields.
*/
public function getConfigFields(): array;
/**
* Returns CKEditor5 AITextAdapter.
*
* @return AITextAdapter
* AITextAdapter.
*/
public function getTextAdapter(): AITextAdapter;
/**
* Returns service description.
*
* @return string|TranslatableMarkup
* The Description.
*/
public function getDescription(): string|TranslatableMarkup;
/**
* Validate form with plugin fields.
*
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*
* @return void
*/
public function validateFields(FormStateInterface &$form_state): void;
}
<?php
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
declare(strict_types = 1);
namespace Drupal\ckeditor5_premium_features_ai_assistant;
use Drupal\Component\Plugin\PluginBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
/**
* Base class for ckeditor5_ai_provider plugins.
*/
abstract class CKEditor5AiProviderPluginBase extends PluginBase implements CKEditor5AiProviderInterface, ContainerFactoryPluginInterface {
/**
* {@inheritdoc}
*/
public function label(): string {
return (string) $this->pluginDefinition['label'];
}
/**
* {@inheritdoc}
*/
public function validateFields(FormStateInterface &$form_state): void {
}
}
<?php
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
declare(strict_types = 1);
namespace Drupal\ckeditor5_premium_features_ai_assistant;
use Drupal\ckeditor5_premium_features_ai_assistant\Annotation\CKEditor5AiProvider;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
/**
* CKEditor5AiProvider plugin manager.
*/
final class CKEditor5AiProviderPluginManager extends DefaultPluginManager {
/**
* Constructs the object.
*/
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
parent::__construct('Plugin/CKEditor5AiProvider', $namespaces, $module_handler, CKEditor5AiProviderInterface::class, CKEditor5AiProvider::class);
$this->alterInfo('ckeditor5_ai_provider_info');
$this->setCacheBackend($cache_backend, 'ckeditor5_ai_provider_plugins');
}
/**
* Return all available AI providers.
*
* @return array
*/
public function getAllProviders(): array {
return $this->getDefinitions() ?? [];
}
}
<?php
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
declare(strict_types = 1);
namespace Drupal\ckeditor5_premium_features_ai_assistant\Controller;
use Drupal\ckeditor5_premium_features_ai_assistant\Utility\AiAssistantHelper;
use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* Returns responses for CKEditor 5 Premium Features AI Assistant services.
*/
final class AiAssistantProviderProxyController extends ControllerBase {
/**
* Constructs the object.
*
* @param \Drupal\ckeditor5_premium_features_ai_assistant\Utility\AiAssistantHelper $aiAssistantHelper
* Helper for AI Assistant client.
*/
public function __construct(private readonly AiAssistantHelper $aiAssistantHelper) {
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('ckeditor5_premium_features_ai_assistant.ai_assistant_helper')
);
}
/**
* Builds the response.
*/
public function __invoke(Request $request): Response {
$aiProvider = $this->aiAssistantHelper->getProvider();
if (!$aiProvider) {
return new Response('No AI service available.', 501);
}
return $aiProvider->processRequest($request);
}
}
<?php
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
declare(strict_types = 1);
namespace Drupal\ckeditor5_premium_features_ai_assistant\Entity;
use Drupal\ckeditor5_premium_features_ai_assistant\CKEditor5AiCommandGroupInterface;
use Drupal\Core\Config\Entity\ConfigEntityBase;
/**
* Defines the ckeditor 5 AI commands group entity type.
*
* @ConfigEntityType(
* id = "ckeditor5_ai_command_group",
* label = @Translation("CKEditor 5 AI Command Group"),
* label_collection = @Translation("CKEditor 5 AI Commands Group"),
* label_singular = @Translation("CKEditor 5 AI Command Group"),
* label_plural = @Translation("CKEditor 5 AI Commands Group"),
* label_count = @PluralTranslation(
* singular = "@count CKEditor 5 AI Command Group",
* plural = "@count CKEditor 5 AI Commands Group",
* ),
* handlers = {
* "list_builder" = "Drupal\ckeditor5_premium_features_ai_assistant\CKEditor5AiCommandGroupListBuilder",
* "form" = {
* "add" = "Drupal\ckeditor5_premium_features_ai_assistant\Form\CKEditor5AiCommandGroupForm",
* "edit" = "Drupal\ckeditor5_premium_features_ai_assistant\Form\CKEditor5AiCommandGroupForm",
* "delete" = "Drupal\Core\Entity\EntityDeleteForm"
* }
* },
* config_prefix = "ckeditor5_ai_command_group",
* admin_permission = "administer ckeditor5_ai_command_group",
* links = {
* "collection" = "/admin/structure/ckeditor5-ai-command-group",
* "add-form" = "/admin/structure/ckeditor5-ai-command-group/add",
* "edit-form" = "/admin/structure/ckeditor5-ai-command-group/{ckeditor5_ai_command_group}",
* "delete-form" = "/admin/structure/ckeditor5-ai-command-group/{ckeditor5_ai_command_group}/delete"
* },
* entity_keys = {
* "id" = "id",
* "uuid" = "uuid",
* "label" = "label",
* },
* config_export = {
* "id",
* "label",
* "commands",
* "textFormats",
* }
* )
*/
class CKEditor5AiCommandGroup extends ConfigEntityBase implements CKEditor5AiCommandGroupInterface {
/**
* The CKEditor 5 AI Commands group ID.
*
* @var string
*/
protected string $id;
/**
* The CKEditor 5 AI Commands group label.
*
* @var string
*/
protected string $label;
/**
* The commands associated with the CommandGroup.
*
* @var array
*/
protected ?array $commands;
/**
* Allowed text formats.
*
* @var array
*/
protected ?array $textFormats;
/**
* {@inheritdoc}
*/
public function addCommand(array $command): static {
$command['uuid'] = $this->uuidGenerator()->generate();
$this->commands[] = $command;
$this->set('commands', $this->commands)->save();
return $this;
}
/**
* {@inheritdoc}
*/
public function getCommandByUuid(string $uuid): array {
$command = array_filter($this->commands, fn($command) => $command['uuid'] === $uuid);
return reset($command);
}
/**
* {@inheritdoc}
*/
public function removeCommand(string $uuid):static {
$commands = array_filter($this->commands, fn($command) => $command['uuid'] !== $uuid);
$this->set('commands', $commands)->save();
return $this;
}
/**
* {@inheritdoc}
*/
public function updateCommand(array $command): static {
foreach ($this->commands as $key => $value) {
if ($value['uuid'] === $command['uuid']) {
$this->commands[$key] = $command;
$this->save();
return $this;
}
}
return $this;
}
/**
* {@inheritdoc}
*/
public function updateWeights(array $weights): static {
foreach ($this->commands as $key => $command) {
if (array_key_exists($command['uuid'], $weights)) {
$this->commands[$key]['weight'] = $weights[$command['uuid']]['weight'];
}
}
usort($this->commands, function ($a, $b) {
return $a['weight'] <=> $b['weight'];
});
return $this;
}
/**
* {@inheritdoc}
*/
public function getDefinition(): array {
return [
'id' => $this->id(),
'label' => $this->label(),
'commands' => $this->commands,
];
}
/**
* {@inheritdoc}
*/
public function commandExists(string $id): bool {
if (!empty($this->commands)) {
foreach ($this->commands as $command) {
if (isset($command['command_id']) && $command['command_id'] === $id) {
return TRUE;
}
}
}
return FALSE;
}
}
<?php
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
declare(strict_types = 1);
namespace Drupal\ckeditor5_premium_features_ai_assistant\Form;
use Drupal\ckeditor5_premium_features_ai_assistant\Entity\CKEditor5AiCommandGroup;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
/**
* CKEditor 5 AI Command add form.
*/
class CKEditor5AiCommandAddForm extends FormBase {
/**
* Command group entity.
*
* @var \Drupal\ckeditor5_premium_features_ai_assistant\Entity\CKEditor5AiCommandGroup|null
*/
protected ?CKEditor5AiCommandGroup $commandGroup;
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'ckeditor5_ai_command_add_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, CKEditor5AiCommandGroup $ckeditor5_ai_command_group = NULL, string $uuid = NULL): array {
$this->commandGroup = $ckeditor5_ai_command_group;
$command = [];
if ($uuid) {
$command = $this->commandGroup->getCommandByUuid($uuid);
}
$form['label'] = [
'#type' => 'textfield',
'#title' => $this->t('Label'),
'#maxlength' => 255,
'#default_value' => $command['label'] ?? '',
'#description' => $this->t('Label for the CKEditor 5 AI command.'),
'#required' => TRUE,
];
$form['command_id'] = [
'#type' => 'machine_name',
'#label' => 'Command id',
'#default_value' => $command['command_id'] ?? '',
'#machine_name' => [
'source' => ['label'],
'exists' => [$this, 'exists'],
],
'#maxlength' => 32,
];
$form['prompt'] = [
'#type' => 'textarea',
'#title' => $this->t('Prompt'),
'#required' => TRUE,
'#default_value' => $command['prompt'] ?? '',
'#description' => $this->t('Description of the CKEditor 5 AI command.'),
];
$form['weight'] = [
'#type' => 'weight',
'#title_display' => 'invisible',
"#disabled" => TRUE,
"#access" => FALSE,
'#default_value' => $command['weight'] ?? 0,
];
$form['uuid'] = [
'#type' => 'string',
'#title_display' => 'invisible',
"#disabled" => TRUE,
"#access" => FALSE,
'#default_value' => $command['uuid'] ?? '',
];
$form['actions'] = ['#type' => 'actions'];
$form['actions']['submit'] = [
'#type' => 'submit',
'#button_type' => 'primary',
];
$form['actions']['submit']['#value'] = $this->t('Add Command');
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$values = $form_state->cleanValues()->getValues();
$values['weight'] = 0;
$values['command_id'] = strip_tags(str_replace(' ', '_', $values['command_id']));
$this->commandGroup->addCommand($values);
$form_state->setRedirectUrl($this->commandGroup->toUrl('edit-form'));
}
/**
* Determines if the command already exists.
*
* @param string $id
* The commad ID.
*
* @return bool
* TRUE if the command exists, FALSE otherwise.
*/
public function exists(string $id): bool {
return $this->commandGroup->commandExists($id);
}
}
<?php
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
declare(strict_types = 1);
namespace Drupal\ckeditor5_premium_features_ai_assistant\Form;
use Drupal\ckeditor5_premium_features_ai_assistant\Entity\CKEditor5AiCommandGroup;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
/**
* CKEditor 5 AI Command delete form.
*/
class CKEditor5AiCommandDeleteForm extends ConfirmFormBase {
/**
* Command group entity.
*
* @var \Drupal\ckeditor5_premium_features_ai_assistant\Entity\CKEditor5AiCommandGroup|null
*/
protected ?CKEditor5AiCommandGroup $commandGroup;
/**
* @var mixed|null
*/
protected mixed $commandUuid;
/**
* {@inheritdoc}
*/
public function getQuestion() {
return $this->t('Are you sure you want to delete the command from the group?');
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return $this->t('Delete');
}
/**
* {@inheritdoc}
*/
public function getCancelUrl() {
return $this->commandGroup->toUrl('edit-form');
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, ConfigEntityInterface $ckeditor5_ai_command_group = NULL, $uuid = NULL) {
$this->commandGroup = $ckeditor5_ai_command_group;
$this->commandUuid = $uuid;
return parent::buildForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->commandGroup->removeCommand($this->commandUuid);
$this->messenger()->addStatus($this->t('The command has been deleted.'));
$form_state->setRedirectUrl($this->commandGroup->toUrl('edit-form'));
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'ckeditor5_ai_command_delete_form';
}
}
<?php
/*
* Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
declare(strict_types = 1);
namespace Drupal\ckeditor5_premium_features_ai_assistant\Form;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
use Drupal\Core\Form\FormStateInterface;
/**
* CKEditor 5 AI Command edit form.
*/
class CKEditor5AiCommandEditForm extends CKEditor5AiCommandAddForm {
/**
* Command uuid.
*
* @var string|null
*/
protected ?string $commandUuid;
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, ConfigEntityInterface $ckeditor5_ai_command_group = NULL, string $uuid = NULL): array {
$form = parent::buildForm($form, $form_state, $ckeditor5_ai_command_group, $uuid);
$form['actions']['submit']['#value'] = $this->t('Update Command');
$this->commandUuid = $uuid;
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$values = $form_state->cleanValues()->getValues();
$values['command_id'] = strip_tags(str_replace(' ', '_', $values['command_id']));
$values['uuid'] = $this->commandUuid;
$this->commandGroup->updateCommand($values);
$form_state->setRedirectUrl($this->commandGroup->toUrl('edit-form'));
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'ckeditor5_ai_command_edit_form';
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment