.. _ckeditor-plugin-development-guide:
==============================
CKEditor Plugin Development
==============================
Complete guide to the Typo3Image CKEditor 5 plugin architecture and development patterns.
Plugin Overview
===============
**File**: ``Resources/Public/JavaScript/Plugins/typo3image.js``
**Plugin Class**: ``Typo3Image extends Core.Plugin``
**Required Dependencies**:
.. code-block:: javascript
static get requires() {
return ['StyleUtils', 'GeneralHtmlSupport'];
}
.. warning::
Both ``StyleUtils`` and ``GeneralHtmlSupport`` are **mandatory**. Missing them causes style functionality to fail.
Plugin Structure
================
.. code-block:: javascript
export default class Typo3Image extends Core.Plugin {
static pluginName = 'Typo3Image';
static get requires() {
return ['StyleUtils', 'GeneralHtmlSupport'];
}
init() {
// Plugin initialization
// - Define schema
// - Register conversions
// - Add UI components
// - Register event listeners
}
}
Custom Model Element: typo3image
=================================
Schema Definition
-----------------
.. code-block:: javascript
editor.model.schema.register('typo3image', {
inheritAllFrom: '$blockObject',
allowIn: ['$text', '$block'],
allowAttributes: [
'src', 'fileUid', 'fileTable',
'alt', 'altOverride', 'title', 'titleOverride',
'class', 'enableZoom', 'width', 'height',
'htmlA', 'linkHref', 'linkTarget', 'linkTitle'
],
});
Attribute Descriptions
----------------------
================ ======== ===========================================
Attribute Type Description
================ ======== ===========================================
src string Image source URL
fileUid number TYPO3 FAL file UID
fileTable string Database table (default: 'sys_file')
alt string Alternative text
altOverride boolean Alt text override flag
title string Advisory title
titleOverride boolean Title override flag
class string CSS classes (space-separated)
enableZoom boolean Zoom/clickenlarge functionality
width string Image width
height string Image height
htmlA string Link wrapper HTML
linkHref string Link URL
linkTarget string Link target
linkTitle string Link title
================ ======== ===========================================
Conversion System
=================
Upcast: HTML → Model
--------------------
Converts ```` elements with FAL attributes to ``typo3image`` model elements:
.. code-block:: javascript
editor.conversion.for('upcast').elementToElement({
view: {
name: 'img',
attributes: ['data-htmlarea-file-uid', 'src']
},
model: (viewElement, { writer }) => {
return writer.createElement('typo3image', {
fileUid: viewElement.getAttribute('data-htmlarea-file-uid'),
fileTable: viewElement.getAttribute('data-htmlarea-file-table') || 'sys_file',
src: viewElement.getAttribute('src'),
width: viewElement.getAttribute('width') || '',
height: viewElement.getAttribute('height') || '',
class: viewElement.getAttribute('class') || '',
alt: viewElement.getAttribute('alt') || '',
altOverride: viewElement.getAttribute('data-alt-override') || false,
title: viewElement.getAttribute('title') || '',
titleOverride: viewElement.getAttribute('data-title-override') || false,
enableZoom: viewElement.getAttribute('data-htmlarea-zoom') || false,
});
}
});
Downcast: Model → HTML
-----------------------
Converts ``typo3image`` model elements to ``
`` HTML:
.. code-block:: javascript
editor.conversion.for('downcast').elementToElement({
model: {
name: 'typo3image',
attributes: ['fileUid', 'fileTable', 'src']
},
view: (modelElement, { writer }) => {
const attributes = {
'src': modelElement.getAttribute('src'),
'data-htmlarea-file-uid': modelElement.getAttribute('fileUid'),
'data-htmlarea-file-table': modelElement.getAttribute('fileTable'),
'width': modelElement.getAttribute('width'),
'height': modelElement.getAttribute('height'),
'class': modelElement.getAttribute('class') || '',
'title': modelElement.getAttribute('title') || '',
'alt': modelElement.getAttribute('alt') || '',
};
if (modelElement.getAttribute('titleOverride')) {
attributes['data-title-override'] = true;
}
if (modelElement.getAttribute('altOverride')) {
attributes['data-alt-override'] = true;
}
if (modelElement.getAttribute('enableZoom')) {
attributes['data-htmlarea-zoom'] = true;
}
return writer.createEmptyElement('img', attributes);
},
});
Class Attribute Converter
--------------------------
Makes class changes immediately visible in the editor:
.. code-block:: javascript
editor.conversion.for('downcast').attributeToAttribute({
model: { name: 'typo3image', key: 'class' },
view: 'class'
});
UI Components
=============
Insert Image Button
-------------------
Registered in ``editor.ui.componentFactory``:
.. code-block:: javascript
editor.ui.componentFactory.add('insertimage', () => {
const button = new UI.ButtonView();
button.set({
label: 'Insert image',
icon: '',
tooltip: true,
withText: false,
});
button.on('execute', () => {
const selectedElement = editor.model.document.selection.getSelectedElement();
if (selectedElement && selectedElement.name === 'typo3image') {
// Edit existing image
edit(selectedElement, editor, attributes);
} else {
// Insert new image
selectImage(editor).then(selectedImage => {
edit(selectedImage, editor, {});
});
}
});
return button;
});
Image Selection Flow
====================
selectImage() Function
----------------------
Opens TYPO3 Modal with file browser:
.. code-block:: javascript
function selectImage(editor) {
const deferred = $.Deferred();
const bparams = ['', '', '', ''];
const contentUrl = editor.config.get('style').typo3image.routeUrl
+ '&contentsLanguage=en&editorId=123&bparams=' + bparams.join('|');
const modal = Modal.advanced({
type: Modal.types.iframe,
title: 'Select Image',
content: contentUrl,
size: Modal.sizes.large,
callback: function (currentModal) {
$(currentModal).find('iframe').on('load', function (e) {
$(this).contents().on('click', '[data-filelist-element]', function (e) {
if ($(this).data('filelist-type') !== 'file') {
return;
}
const selectedItem = {
uid: $(this).data('filelist-uid'),
table: 'sys_file',
};
currentModal.hideModal();
deferred.resolve(selectedItem);
});
});
}
});
return deferred;
}
Image Properties Dialog
========================
getImageDialog() Function
--------------------------
Creates image properties form:
.. code-block:: javascript
function getImageDialog(editor, img, attributes) {
const d = {};
const fields = [
{
width: { label: 'Width', type: 'number' },
height: { label: 'Height', type: 'number' }
},
{
title: { label: 'Advisory Title', type: 'text' },
alt: { label: 'Alternative Text', type: 'text' }
}
];
// Create form elements
d.$el = $('