Commit 5be422b7 authored by Benjamin Kott's avatar Benjamin Kott Committed by Benni Mack
Browse files

[FEATURE] Introduce events to modify CKEditor configuration

This patch introduces a set of PSR-14 Events to modify
the CKEditor configuration.

- AfterGetExternalPluginsEvent
- BeforeGetExternalPluginsEvent
- AfterPrepareConfigurationForEditorEvent
- BeforePrepareConfigurationForEditorEvent

Resolves: #88818
Releases: master
Change-Id: I1f810e31274a05d52082de5e03fc530b3cee1a44
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/59304


Tested-by: default avatarTYPO3com <noreply@typo3.com>
Tested-by: Georg Ringer's avatarGeorg Ringer <georg.ringer@gmail.com>
Tested-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
Reviewed-by: Georg Ringer's avatarGeorg Ringer <georg.ringer@gmail.com>
Reviewed-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
parent bdce4388
.. include:: ../../Includes.txt
===================================================================
Feature: #88818 - Introduce events to modify CKEditor configuration
===================================================================
See :issue:`88818`
Description
===========
The following new PSR-14-based Events are introduced which allow
to modify CKEditor configuration.
- :php:`TYPO3\CMS\RteCKEditor\Form\Element\Event\AfterGetExternalPluginsEvent`
- :php:`TYPO3\CMS\RteCKEditor\Form\Element\Event\BeforeGetExternalPluginsEvent`
- :php:`TYPO3\CMS\RteCKEditor\Form\Element\Event\AfterPrepareConfigurationForEditorEvent`
- :php:`TYPO3\CMS\RteCKEditor\Form\Element\Event\BeforePrepareConfigurationForEditorEvent`
Example
=======
An example implementation how you could extend the existing
configuration to register a new plugin:
:file:`EXT:my_extension/Configuration/Services.yaml`
.. code-block:: yaml
services:
Vendor\MyExtension\EventListener\RteConfigEnhancer:
tags:
- name: event.listener
identifier: 'ext-myextension/rteConfigEnhancer'
method: 'beforeGetExternalPlugins'
event: TYPO3\CMS\RteCKEditor\Form\Element\Event\BeforeGetExternalPluginsEvent
- name: event.listener
identifier: 'ext-myextension/rteConfigEnhancer'
method: 'beforePrepareConfiguration'
event: TYPO3\CMS\RteCKEditor\Form\Element\Event\BeforePrepareConfigurationForEditorEvent
:file:`EXT:my_extension/Classes/EventListener/RteConfigEnhancer.php`
.. code-block:: php
namespace Vendor\MyExtension\EventListener;
use TYPO3\CMS\RteCKEditor\Form\Element\Event\BeforeGetExternalPluginsEvent;
use TYPO3\CMS\RteCKEditor\Form\Element\Event\BeforePrepareConfigurationForEditorEvent;
class RteConfigEnhancer
{
public function beforeGetExternalPlugins(BeforeGetExternalPluginsEvent $event): void
{
$data = $event->getData();
// @todo make useful decisions on fetched data
$configuration = $event->getConfiguration();
$configuration['example_plugin'] = [
'resource' => 'EXT:my_extension/Resources/Public/CKEditor/Plugins/ExamplePlugin/plugin.js'
];
$event->setConfiguration($configuration);
}
public function beforePrepareConfiguration(BeforePrepareConfigurationForEditorEvent $event): void
{
$data = $event->getData();
// @todo make useful decisions on fetched data
$configuration = $event->getConfiguration();
$configuration['extraPlugins'][] = 'example_plugin';
$event->setConfiguration($configuration);
}
}
.. index:: Backend, PHP-API, RTE, ext:rte_ckeditor
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\RteCKEditor\Form\Element\Event;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* This event is fired after processing external plugin configuration.
*/
final class AfterGetExternalPluginsEvent
{
/**
* @var array
*/
private $configuration;
/**
* @var array
*/
private $data;
public function __construct(array $configuration, array $data)
{
$this->configuration = $configuration;
$this->data = $data;
}
public function getData(): array
{
return $this->data;
}
public function getConfiguration(): array
{
return $this->configuration;
}
public function setConfiguration(array $configuration): void
{
$this->configuration = $configuration;
}
}
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\RteCKEditor\Form\Element\Event;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* This event is fired after preparing the editor configuration.
*/
final class AfterPrepareConfigurationForEditorEvent
{
/**
* @var array
*/
private $configuration;
/**
* @var array
*/
private $data;
public function __construct(array $configuration, array $data)
{
$this->configuration = $configuration;
$this->data = $data;
}
public function getData(): array
{
return $this->data;
}
public function getConfiguration(): array
{
return $this->configuration;
}
public function setConfiguration(array $configuration): void
{
$this->configuration = $configuration;
}
}
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\RteCKEditor\Form\Element\Event;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* This event is fired before processing external plugin configuration.
*/
final class BeforeGetExternalPluginsEvent
{
/**
* @var array
*/
private $configuration;
/**
* @var array
*/
private $data;
public function __construct(array $configuration, array $data)
{
$this->configuration = $configuration;
$this->data = $data;
}
public function getData(): array
{
return $this->data;
}
public function getConfiguration(): array
{
return $this->configuration;
}
public function setConfiguration(array $configuration): void
{
$this->configuration = $configuration;
}
}
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\RteCKEditor\Form\Element\Event;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* This event is fired before starting the prepare of the editor configuration.
*/
final class BeforePrepareConfigurationForEditorEvent
{
/**
* @var array
*/
private $configuration;
/**
* @var array
*/
private $data;
public function __construct(array $configuration, array $data)
{
$this->configuration = $configuration;
$this->data = $data;
}
public function getData(): array
{
return $this->data;
}
public function getConfiguration(): array
{
return $this->configuration;
}
public function setConfiguration(array $configuration): void
{
$this->configuration = $configuration;
}
}
......@@ -15,12 +15,18 @@ namespace TYPO3\CMS\RteCKEditor\Form\Element;
* The TYPO3 project - inspiring people to share!
*/
use Psr\EventDispatcher\EventDispatcherInterface;
use TYPO3\CMS\Backend\Form\Element\AbstractFormElement;
use TYPO3\CMS\Backend\Form\NodeFactory;
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Localization\Locales;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
use TYPO3\CMS\RteCKEditor\Form\Element\Event\AfterGetExternalPluginsEvent;
use TYPO3\CMS\RteCKEditor\Form\Element\Event\AfterPrepareConfigurationForEditorEvent;
use TYPO3\CMS\RteCKEditor\Form\Element\Event\BeforeGetExternalPluginsEvent;
use TYPO3\CMS\RteCKEditor\Form\Element\Event\BeforePrepareConfigurationForEditorEvent;
/**
* Render rich text editor in FormEngine
......@@ -70,6 +76,24 @@ class RichTextElement extends AbstractFormElement
*/
protected $rteConfiguration = [];
/**
* @var EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* Container objects give $nodeFactory down to other containers.
*
* @param NodeFactory $nodeFactory
* @param array $data
* @param EventDispatcherInterface|null $eventDispatcher
*/
public function __construct(NodeFactory $nodeFactory, array $data, EventDispatcherInterface $eventDispatcher = null)
{
parent::__construct($nodeFactory, $data);
$this->eventDispatcher = $eventDispatcher ?? GeneralUtility::getContainer()->get(EventDispatcherInterface::class);
}
/**
* Renders the ckeditor element
*
......@@ -232,6 +256,11 @@ class RichTextElement extends AbstractFormElement
*/
protected function getExtraPlugins(): array
{
$externalPlugins = $this->rteConfiguration['externalPlugins'] ?? [];
$externalPlugins = $this->eventDispatcher
->dispatch(new BeforeGetExternalPluginsEvent($externalPlugins, $this->data))
->getConfiguration();
$urlParameters = [
'P' => [
'table' => $this->data['tableName'],
......@@ -244,21 +273,23 @@ class RichTextElement extends AbstractFormElement
];
$pluginConfiguration = [];
if (isset($this->rteConfiguration['externalPlugins']) && is_array($this->rteConfiguration['externalPlugins'])) {
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
foreach ($this->rteConfiguration['externalPlugins'] as $pluginName => $configuration) {
$pluginConfiguration[$pluginName] = [
'resource' => $this->resolveUrlPath($configuration['resource'])
];
unset($configuration['resource']);
if ($configuration['route']) {
$configuration['routeUrl'] = (string)$uriBuilder->buildUriFromRoute($configuration['route'], $urlParameters);
}
$pluginConfiguration[$pluginName]['config'] = $configuration;
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
foreach ($externalPlugins as $pluginName => $configuration) {
$pluginConfiguration[$pluginName] = [
'resource' => $this->resolveUrlPath($configuration['resource'])
];
unset($configuration['resource']);
if ($configuration['route']) {
$configuration['routeUrl'] = (string)$uriBuilder->buildUriFromRoute($configuration['route'], $urlParameters);
}
$pluginConfiguration[$pluginName]['config'] = $configuration;
}
$pluginConfiguration = $this->eventDispatcher
->dispatch(new AfterGetExternalPluginsEvent($pluginConfiguration, $this->data))
->getConfiguration();
return $pluginConfiguration;
}
......@@ -327,6 +358,11 @@ class RichTextElement extends AbstractFormElement
if (is_array($this->rteConfiguration['config'])) {
$configuration = array_replace_recursive($configuration, $this->rteConfiguration['config']);
}
$configuration = $this->eventDispatcher
->dispatch(new BeforePrepareConfigurationForEditorEvent($configuration, $this->data))
->getConfiguration();
// Set the UI language of the editor if not hard-coded by the existing configuration
if (empty($configuration['language'])) {
$configuration['language'] = $this->getBackendUser()->uc['lang'] ?: ($this->getBackendUser()->user['lang'] ?: 'en');
......@@ -349,6 +385,10 @@ class RichTextElement extends AbstractFormElement
$configuration['removeButtons'] = implode(',', $configuration['removeButtons']);
}
$configuration = $this->eventDispatcher
->dispatch(new AfterPrepareConfigurationForEditorEvent($configuration, $this->data))
->getConfiguration();
return $configuration;
}
......
Supports Markdown
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