Rich text editor in the frontend form 

New in version 11.2.0

Textarea elements of the event form based on EXT:form can be edited with a CKEditor 5.

Textarea elements of the event form based on EXT:form (site setting events2.form.useExtForm) can be edited with a small CKEditor 5. Frontend users can format the text, and formatting added by editors in the backend is kept when the event is edited in the frontend. By default, the element detail-description (column detail_information) gets the editor. Further elements can be added by configuration.

The feature is disabled by default. Without it, all elements are plain textareas as before.

Requirements for the frontend rich text editor 

  • The event form based on EXT:form (site setting events2.form.useExtForm).
  • The system extension EXT:rte_ckeditor ( typo3/cms-rte-ckeditor ). The editor is not activated if it is not installed.

No build step (npm, webpack) is needed. The CKEditor 5 ES modules, translations and styles shipped with EXT:rte_ckeditor are used.

Site settings of the frontend rich text editor 

The following settings are provided by the site set jweiland/events2 and are available in TypoScript as plugin.tx_events2.settings.new.richText, see richText.enable.

Name Type Default
bool false
string detail-description
events2.form.richText.enable
Type
bool
Default
false

Use CKEditor 5 of EXT:rte_ckeditor for the rich text elements of the event form based on EXT:form.

events2.form.richText.elements
Type
string
Default
detail-description

Comma-separated identifiers of the form elements which get the rich text editor. Elements with dataType: richText in their dbMapping get the editor, too.

Enable the frontend rich text editor 

Enable the site setting events2.form.richText.enable of the site set jweiland/events2.

That is all for the description: the element detail-description is the default of the site setting events2.form.richText.elements. This works for overridden copies of NewEvent.form.yaml as well, as long as they use this identifier.

Example: Enable the rich text editor for the description 

config/sites/<my-site>/settings.yaml
events2:
  form:
    richText:
      enable: true
Copied!

Rich text elements of the event form 

An element of the form is a rich text element if

If the editor is enabled, events2 configures each rich text element automatically:

  • The dataType of the element is richText, so the edit form gets the stored HTML instead of plain text.
  • The mapping of the element in the SaveEventFinisher gets sanitizeRichText: true (instead of convertPlainTextToHtml or normalizePlainText).
  • While rendering, the textarea gets the attribute data-events2-rich-text with the options of the editor, including the allowed tags of its column (see Toolbar of the frontend rich text editor). The assets are only loaded on pages of the form which contain a rich text element. Each textarea gets its own editor.

If the editor is disabled, rich text elements fall back to plain text: dataType: plainText and convertPlainTextToHtml instead of sanitizeRichText.

The editor of an element is configured for the column properties.dbMapping.column of the table properties.dbMapping.table. The table is optional and defaults to tx_events2_domain_model_event. Set it only for elements which are stored in another table, so the toolbar matches the allowed tags of that column. Use the same table as the mapping of the element in the SaveEventFinisher:

EXT:my_sitepackage/Configuration/Form/NewEvent.form.yaml (excerpt)
- label: 'Notes of the organizer'
  type: Textarea
  identifier: organizer-notes
  properties:
    dbMapping:
      table: tx_events2_domain_model_organizer
      column: tx_myext_notes
      dataType: richText
Copied!

Example: Add a rich text editor to an additional form element 

Add an own textarea to your copy of NewEvent.form.yaml and map it to an own column of the event table, which you have configured with 'enableRichtext' => true in TCA:

EXT:my_sitepackage/Configuration/Form/NewEvent.form.yaml (excerpt)
- label: 'Program'
  type: Textarea
  identifier: program
  properties:
    dbMapping:
      column: tx_myext_program
      # Optional: Instead of listing "program" in the site setting
      dataType: richText
Copied!

Map the element in the SaveEventFinisher options of the table tx_events2_domain_model_event:

EXT:my_sitepackage/Configuration/Form/NewEvent.form.yaml (excerpt)
finishers:
  1:
    identifier: SaveEventFinisher
    options:
      4:
        table: tx_events2_domain_model_event
        elements:
          program:
            mapOnDatabaseColumn: tx_myext_program
Copied!

Then either keep dataType: richText in the form definition or add the identifier to the site setting:

config/sites/<my-site>/settings.yaml
events2:
  form:
    richText:
      enable: true
      elements: 'detail-description, program'
Copied!

Loaded files of the frontend rich text editor 

The import map of the CKEditor modules is defined in EXT:events2/Configuration/JavaScriptModules.php and rendered by the PageRenderer of TYPO3 (<script type="importmap">, respecting the Content Security Policy of the site). The ES module EXT:events2/Resources/Public/JavaScript/rich-text-editor.js creates an editor for each textarea with the attribute data-events2-rich-text. It loads the UI translation of the current site language. The style sheets EXT:rte_ckeditor/Resources/Public/Css/editor.css and EXT:events2/Resources/Public/Css/RichTextEditor.css are added with the AssetCollector.

The files are about 1.6 MB uncompressed and about 400 KB compressed with gzip.

Toolbar of the frontend rich text editor 

The toolbar is built from the tags which are allowed for the column of the element (see Allowed markup of the frontend rich text editor). With the default configuration, it contains:

  • Heading: Paragraph, Heading 2, Heading 3 (Heading 1 is used for the title of the event)
  • Bold, Italic
  • Bulleted list, numbered list
  • Link with the protocols http, https, mailto and tel and the option Open in a new tab
  • Undo, Redo

New in version 11.2.0

The editor gets one heading option for each allowed element of <h1> to <h6>. The titles of the headings are translated by CKEditor. The heading dropdown is only shown if at least one heading is allowed. The toolbar items are only shown if their element is allowed:

  • Bold: <strong>
  • Italic: <em>
  • Bulleted list: <ul>
  • Numbered list: <ol>
  • Link: <a>

Undo and Redo are always available. Allowed elements without a toolbar item, like <u> or <blockquote>, are kept when the value is saved, but can not be selected in the editor.

Toolbar items which do not fit into the width of the form are grouped into a dropdown. The toolbar can be focused with Alt + F10.

Allowed markup of the frontend rich text editor 

The submitted HTML is sanitized on the server with typo3/html-sanitizer , as a request can bypass the editor. By default, only these tags are allowed:

  • <p>, <br>, <strong>, <em>, <h2>, <h3>, <ul>, <ol>, <li>
  • <a> with href (http, https, mailto, tel) and target="_blank", which gets rel="noopener noreferrer"

<b> is converted to <strong>, <i> to <em> and <h1>, <h4>, <h5>, <h6> to <p>. All attributes like class, style or on* are removed. Links with other protocols like javascript: or data: are removed, their text is kept. <script>, <style>, <iframe>, images, forms and similar elements are removed including their content. The text of all other elements is kept. Other block elements like <div>, <blockquote> or table cells become paragraphs. Text and inline formatting outside of blocks is wrapped into a paragraph like the editor does, so <strong>, <em> and links are kept.

The tags can be modified with the PSR-14 event ModifyRichTextSanitizerConfigurationEvent, see Modify the allowed markup of the frontend rich text editor.

Modify the allowed markup of the frontend rich text editor 

New in version 11.2.0

The PSR-14 event ModifyRichTextSanitizerConfigurationEvent allows to modify the tags of the rich text sanitizer and of the toolbar of the frontend editor.

The PSR-14 event JWeilandEvents2EventModifyRichTextSanitizerConfigurationEvent is dispatched for each rich text element:

  • While rendering the form element, to build the headings and toolbar items of the editor (see Toolbar of the frontend rich text editor). Table and column are taken from properties.dbMapping.table (default: tx_events2_domain_model_event) and properties.dbMapping.column of the form element.
  • Each time a submitted value which contains HTML is sanitized. Values without any tag are converted like plain text and do not dispatch the event.

The event receives the defaults, which are defined as constants in JWeilandEvents2SanitizerVisitorRichTextVisitor. So one listener configures both the editor and the sanitizing on the server.

Methods of the ModifyRichTextSanitizerConfigurationEvent 

getAllowedTags() / setAllowedTags(array $allowedTags)
Elements which are kept. Default: <p>, <br>, <strong>, <em>, <h2>, <h3>, <ul>, <ol>, <li> and <a>.
getMappedTags() / setMappedTags(array $mappedTags)
Elements which are renamed, as array of source tag name to target tag name, for example 'b' => 'strong'. Default: <b> to <strong>, <i> to <em> and <h1>, <h4>, <h5>, <h6> to <p>.
getRemovedTags() / setRemovedTags(array $removedTags)
Elements which are removed including their content, for example <script>, <img>, <form> or <video>.
getBlockTags() / setBlockTags(array $blockTags)
Elements which start a new block, for example <div>, <blockquote> or <td>. A block element which is not allowed becomes a paragraph if it does not contain other block elements. Otherwise, it is unwrapped and only its content is kept.
getTable()
Table of the column the HTML is stored in, for example tx_events2_domain_model_event. Empty if unknown.
getColumn()
Column the HTML is stored in, for example detail_information. Empty if unknown.

Rules for the configuration of the rich text sanitizer 

After all listeners are called, the configuration is normalized:

  • Tag names are converted to lower case. Invalid tag names are ignored.
  • <p> is always allowed, as inline content outside of blocks is wrapped into paragraphs.
  • Elements with active or embedded content can never be allowed: <applet>, <base>, <embed>, <frame>, <frameset>, <iframe>, <link>, <math>, <meta>, <noscript>, <object>, <script>, <style>, <svg> and <template>. They are always removed including their content.
  • Removed tags take precedence over allowed tags.
  • A mapping is only used if its target is allowed and its source is neither allowed nor removed. So allowing <h4> is enough to keep it, the default mapping of <h4> to <p> is skipped.
  • Allowed elements never get any attribute. The only exception is <a>, which keeps a checked href and target="_blank".

After sanitizing, the value is transformed with the RTE configuration of the backend (RteHtmlParser). An element allowed by the event must be allowed by the RTE configuration of the column as well. The default RTE configuration keeps <h4>, for example.

Example: Allow h4 in the description of events 

The following event listener allows <h4> for the column detail_information of the event table only. The editor of the description gets the additional heading Heading 4, and the heading is kept when the event is saved. All other rich text elements keep the default configuration.

EXT:my_sitepackage/Classes/EventListener/AllowHeading4EventListener.php
<?php

declare(strict_types=1);

namespace MyVendor\MySitepackage\EventListener;

use JWeiland\Events2\Event\ModifyRichTextSanitizerConfigurationEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;

#[AsEventListener(identifier: 'my-sitepackage/allow-heading-4')]
final class AllowHeading4EventListener
{
    public function __invoke(ModifyRichTextSanitizerConfigurationEvent $event): void
    {
        if ($event->getTable() !== 'tx_events2_domain_model_event'
            || $event->getColumn() !== 'detail_information'
        ) {
            return;
        }

        // Keep <h4> instead of converting it to <p>
        $event->setAllowedTags([...$event->getAllowedTags(), 'h4']);
    }
}
Copied!

The attribute #[AsEventListener] registers the listener, see Event dispatcher (PSR-14 events).

Frontend form without JavaScript 

Without JavaScript, the plain textarea is shown. For new events, plain text can be entered as before. When editing an event, the textarea contains the stored HTML. Values without any tag are converted like the plain text description.