Custom Extensions 

Advanced examples for extending the image plugin with custom fields, external image handling, multi-language support, and backend processing.

Custom Image Dialog 

Extended Image Properties 

Objective: Add custom fields to image dialog

CKEditor Plugin Extension 

EXT:my_site/Resources/Public/JavaScript/Plugins/extended-typo3image.js
import { Plugin } from '@ckeditor/ckeditor5-core';

export default class ExtendedTypo3Image extends Plugin {
    static get pluginName() {
        return 'ExtendedTypo3Image';
    }

    init() {
        const editor = this.editor;

        // Extend schema with custom attributes
        editor.model.schema.extend('typo3image', {
            allowAttributes: ['customCaption', 'customCopyright']
        });

        // Add upcast conversion
        editor.conversion.for('upcast').attributeToAttribute({
            view: 'data-custom-caption',
            model: 'customCaption'
        });

        // Add downcast conversion
        editor.conversion.for('downcast').attributeToAttribute({
            model: 'customCaption',
            view: 'data-custom-caption'
        });

        // Modify image dialog
        editor.on('typo3image:dialog', (evt, { dialog, modelElement }) => {
            // Add custom fields to dialog
            const customFields = $(`
                <div class="form-group">
                    <label>Custom Caption</label>
                    <input type="text" class="form-control" name="customCaption"
                           value="${modelElement.getAttribute('customCaption') || ''}" />
                </div>
                <div class="form-group">
                    <label>Copyright</label>
                    <input type="text" class="form-control" name="customCopyright"
                           value="${modelElement.getAttribute('customCopyright') || ''}" />
                </div>
            `);

            dialog.$el.append(customFields);

            // Override dialog.get() to include custom fields
            const originalGet = dialog.get;
            dialog.get = function() {
                const attrs = originalGet.call(this);
                attrs.customCaption = customFields.find('[name="customCaption"]').val();
                attrs.customCopyright = customFields.find('[name="customCopyright"]').val();
                return attrs;
            };
        });
    }
}
Copied!

Register Plugin 

Configuration/RTE/Extended.yaml
editor:
  config:
    importModules:
      - '@my-vendor/my-site/extended-typo3image.js'
Copied!

Result: Custom image metadata fields ✅

External Image Handling 

Fetch and Upload External Images 

Extension Configuration 

settings.php
$GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['rte_ckeditor_image'] = [
    'fetchExternalImages' => true,
];
Copied!

Custom Upload Folder 

User TSConfig
options.defaultUploadFolder = 1:user_upload/rte_images/
Copied!

Custom Fetch Handler 

EXT:my_site/Classes/Hooks/CustomImageFetchHook.php
namespace MyVendor\MySite\Hooks;

use TYPO3\CMS\Core\Resource\File;
use TYPO3\CMS\Core\Resource\Folder;

class CustomImageFetchHook
{
    public function postProcessExternalImage(
        string $externalUrl,
        File $uploadedFile,
        Folder $targetFolder
    ): void {
        // Add custom metadata
        $uploadedFile->updateProperties([
            'title' => 'Imported from ' . parse_url($externalUrl, PHP_URL_HOST),
            'description' => 'Automatically fetched external image',
        ]);

        // Trigger image optimization
        $this->optimizeImage($uploadedFile);
    }

    protected function optimizeImage(File $file): void
    {
        // Custom optimization logic
        // e.g., compress, resize, convert format
    }
}
Copied!

Register Hook 

ext_localconf.php
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['rte_ckeditor_image']['postProcessExternalImage'][]
    = \MyVendor\MySite\Hooks\CustomImageFetchHook::class . '->postProcessExternalImage';
Copied!

Result: Automatic external image import ✅

Multi-Language Setup 

Language-Specific Image Variants 

Page TSConfig 

[siteLanguage("languageId") == 0]
    # Default language (English)
    RTE.default.preset = default
[END]

[siteLanguage("languageId") == 1]
    # German
    RTE.default.preset = german
    RTE.default.buttons.image.options.defaultUploadFolder = 1:user_upload/de/
[END]

[siteLanguage("languageId") == 2]
    # French
    RTE.default.preset = french
    RTE.default.buttons.image.options.defaultUploadFolder = 1:user_upload/fr/
[END]
Copied!

RTE Configuration 

Configuration/RTE/German.yaml
imports:
  - { resource: "EXT:rte_ckeditor_image/Configuration/RTE/Plugin.yaml" }

editor:
  config:
    language: de
    style:
      definitions:
        - name: 'Bild Links'
          element: 'img'
          classes: ['float-left']
        - name: 'Bild Rechts'
          element: 'img'
          classes: ['float-right']
Copied!

Result: Language-specific configurations ✅

Custom Backend Processing 

Automatic Image Optimization 

Custom Hook 

EXT:my_site/Classes/Hooks/ImageOptimizationHook.php
namespace MyVendor\MySite\Hooks;

use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Resource\FileRepository;
use TYPO3\CMS\Core\Utility\GeneralUtility;

class ImageOptimizationHook
{
    public function processDatamap_afterDatabaseOperations(
        string $status,
        string $table,
        string $id,
        array $fieldArray,
        DataHandler $dataHandler
    ): void {
        if ($table !== 'tt_content') {
            return;
        }

        foreach ($fieldArray as $field => $value) {
            if (!$this->isRteField($table, $field)) {
                continue;
            }

            // Find images in RTE content
            preg_match_all('/data-htmlarea-file-uid="(\d+)"/', $value, $matches);

            foreach ($matches[1] as $fileUid) {
                $this->optimizeImage((int)$fileUid);
            }
        }
    }

    protected function isRteField(string $table, string $field): bool
    {
        $tcaConfig = $GLOBALS['TCA'][$table]['columns'][$field]['config'] ?? [];
        return ($tcaConfig['enableRichtext'] ?? false) === true;
    }

    protected function optimizeImage(int $fileUid): void
    {
        $fileRepository = GeneralUtility::makeInstance(FileRepository::class);

        try {
            $file = $fileRepository->findByUid($fileUid);

            // Generate optimized variants
            $file->process(
                \TYPO3\CMS\Core\Resource\ProcessedFile::CONTEXT_IMAGECROPSCALEMASK,
                ['width' => 1920, 'height' => 1080]
            );

            // Generate WebP variant
            $file->process(
                \TYPO3\CMS\Core\Resource\ProcessedFile::CONTEXT_IMAGECROPSCALEMASK,
                ['width' => 1920, 'height' => 1080, 'fileExtension' => 'webp']
            );
        } catch (\Exception $e) {
            // Log error
        }
    }
}
Copied!

Register Hook 

ext_localconf.php
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'][]
    = \MyVendor\MySite\Hooks\ImageOptimizationHook::class;
Copied!

Result: Automatic optimization on save ✅