跳至主要内容
版本: 5.1

编辑器插件

本部分描述了如何开发编辑器插件。

编辑器插件的工作原理如下:插件在onEditorSetup 事件中注册编辑器提供者,然后系统将根据全局配置中选择的选项以及表单中配置的编辑器字段来使用它。

此组中的事件:

onEditorSetup

onEditorSetup

当系统需要渲染编辑器字段时,该事件在运行时触发一次。

事件签名

function onEditorSetup(Joomla\CMS\Event\Editor\EditorSetupEvent $event){}

事件属性

/**
* @var Joomla\CMS\Editor\EditorsRegistry $subject
*/
$subject = $event->getEditorsRegistry();

创建编辑器插件

插件包含三个主要部分

  • 编辑器提供者类,它提供用于渲染输入和加载编辑器按钮 (XTD) 插件的逻辑。
  • 用于客户端集成的编辑器 JavaScript 代码。
  • 编辑器组中的插件,它注册提供者,以便系统知道它的存在。

以下示例假设您已经了解如何创建 Joomla 插件。

让我们创建一个简单的编辑器

在示例中,我们显示一个<textarea> 作为我们的编辑器。

后端

首先,在plugins/editors/example/ 文件夹下创建一个插件,命名为example,假设命名空间为JoomlaExample\Plugin\Editors\Example

然后创建一个提供者,它将位于plugins/editors/example/src/Provider/ExampleEditorProvider.php 下。

namespace JoomlaExample\Plugin\Editors\Example\Provider;

use Joomla\CMS\Application\CMSApplicationInterface;
use Joomla\CMS\Editor\AbstractEditorProvider;
use Joomla\Event\DispatcherInterface;
use Joomla\Registry\Registry;

/**
* Provider for Example editor
*/
final class ExampleEditorProvider extends AbstractEditorProvider
{
/**
* A Registry object holding the parameters for the plugin
*
* @var Registry
*/
protected $params;

/**
* The application object
*
* @var CMSApplicationInterface
*/
protected $application;

/**
* Class constructor
*
* @param Registry $params
* @param CMSApplicationInterface $application
* @param DispatcherInterface $dispatcher
*/
public function __construct(Registry $params, CMSApplicationInterface $application, DispatcherInterface $dispatcher)
{
$this->params = $params;
$this->application = $application;

$this->setDispatcher($dispatcher);
}

/**
* Return Editor name, CMD string.
*
* @return string
*/
public function getName(): string
{
return 'example';
}

/**
* Gets the editor HTML markup
*
* @param string $name Input name.
* @param string $content The content of the field.
* @param array $attributes Associative array of editor attributes.
* @param array $params Associative array of editor parameters.
*
* @return string The HTML markup of the editor
*/
public function display(string $name, string $content = '', array $attributes = [], array $params = [])
{
// Check editor attributes and parameters
$col = $attributes['col'] ?? '';
$row = $attributes['row'] ?? '';
$id = $attributes['id'] ?? '';
$buttons = $params['buttons'] ?? true;
$asset = $params['asset'] ?? 0;
$author = $params['author'] ?? 0;

// Render the editor markup
return '<joomla-editor-example>'
. '<textarea name="' . $name . '" id="' . $id . '" cols="' . $col . '" rows="' . $row . '">' . $content . '</textarea>';
. $this->displayButtons($buttons, ['asset' => $asset, 'author' => $author, 'editorId' => $id]);
. '</joomla-editor-example>'
}
}

注意: 我们使用AbstractEditorProvider 作为基类,它已经包含了加载和渲染编辑器按钮 (XTD) 插件的逻辑。

这里display() 方法渲染了编辑器标记,重要的是将编辑器按钮 (XTD) 保留在与编辑器相同的容器中。我们使用<joomla-editor-example> 自定义元素,这将简化客户端集成,但它也可以只是一个<div> 包装器。

现在使用插件和事件在系统中注册提供者

namespace JoomlaExample\Plugin\Editors\Example\Extension;

use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\SubscriberInterface;
use JoomlaExample\Plugin\Editors\Example\Provider\ExampleEditorProvider;

final class ExampleEditor extends CMSPlugin implements SubscriberInterface
{
/**
* Returns an array of events this plugin will listen to.
*
* @return array
*/
public static function getSubscribedEvents(): array
{
return [
'onEditorSetup' => 'onEditorSetup',
];
}

/**
* Register Editor instance
*
* @param EditorSetupEvent $event
*
* @return void
*
* @since __DEPLOY_VERSION__
*/
public function onEditorSetup(EditorSetupEvent $event)
{
$event->getEditorsRegistry()->add(new ExampleEditorProvider($this->params, $this->getApplication(), $this->getDispatcher()));
}
}

此时,在安装并启用插件后,编辑器应该已经在全局配置中可用,并渲染我们的textarea。但是,我们仍然缺少客户端集成,这将是下一步。

前端

这允许其他 Joomla 扩展与编辑器及其内容交互。在media/plg_editors_example/js/editor-example.js 下创建一个 JavaScript 文件,并修改编辑器提供者的display() 方法,以加载此文件。

public function display(string $name, string $content = '', array $attributes = [], array $params = [])
{
// Load the editor asset files
$this->application->getDocument()->getWebAssetManager()
->registerAndUseScript(
'plg_editors_example.editor-example',
'plg_editors_example/editor-example.js',
[],
['type' => 'module'],
['editors']
);

// Check editor attributes and parameters
$col = $attributes['col'] ?? '';
$row = $attributes['row'] ?? '';
$id = $attributes['id'] ?? '';
$buttons = $params['buttons'] ?? true;
$asset = $params['asset'] ?? 0;
$author = $params['author'] ?? 0;

// Render the editor markup
return '<joomla-editor-example>'
. '<textarea name="' . $name . '" id="' . $id . '" cols="' . $col . '" rows="' . $row . '">' . $content . '</textarea>';
. $this->displayButtons($buttons, ['asset' => $asset, 'author' => $author, 'editorId' => $id]);
. '</joomla-editor-example>'
}

该脚本应注册编辑器实例,并提供基本方法,用于设置/获取值和替换选择等。

// Import required components
import { JoomlaEditor, JoomlaEditorDecorator } from 'editor-api';

/**
* EditorExample Decorator which implements required methods per Editor instance.
* Joomla will use this to share it between Extension, to interact with a main Editor instance.
*/
class EditorExampleDecorator extends JoomlaEditorDecorator {

getValue() {
return this.instance.input.value;
}

setValue(value) {
this.instance.input.value = value;
return this;
}

getSelection() {
const input = this.instance.input;

if (input.selectionStart || input.selectionStart === 0) {
return input.value.substring(input.selectionStart, input.selectionEnd);
}
return input.value;
}

replaceSelection(value) {
const input = this.instance.input;
if (input.selectionStart || input.selectionStart === 0) {
input.value = input.value.substring(0, input.selectionStart)
+ text
+ input.value.substring(input.selectionEnd, input.value.length);
} else {
input.value += text;
}
return this;
}

disable(enable) {
this.instance.input.disabled = !enable;
this.instance.input.readOnly = !enable;
return this;
}
}

/**
* The editor initialisation
*/
class JoomlaEditorExample extends HTMLElement {
// Element attached to DOM
connectedCallback() {
// Pick <textarea> input which is a first children in markup
this.input = this.firstElementChild;

// Register the Decorator in Joomla.Editor
const jEditor = new EditorExampleDecorator(this, 'example', this.input.id);
JoomlaEditor.register(jEditor);

// Find out when editor is interacted
// The script should tell to joomla when editor or one of Editor buttons (XTD) is interacted
if (!this.interactionCallback) {
this.interactionCallback = () => {
JoomlaEditor.setActive(this.input.id);
};
}
this.addEventListener('click', this.interactionCallback);
}

// Element removed from DOM
disconnectedCallback() {
// Unregister editor and unbind all events
JoomlaEditor.unregister(this.input.id);
this.removeEventListener('click', this.interactionCallback);
}
}

customElements.define('joomla-editor-example', JoomlaEditorExample);

一切都完成了 🎉 现在我们拥有一个全新的、完全集成的编辑器。