AEM Dialog Components #6: RichText Editor - Contenuti Formattati

AEM Dialog Components #6: RichText Editor (RTE)

Cos'è il RichText Editor?

Il RichText Editor (RTE) è un componente Granite UI che permette agli autori di creare contenuti formattati con bold, italic, liste, link, tabelle e molto altro.

Casi d'uso comuni:

  • Contenuti editoriali
  • Descrizioni lunghe con formattazione
  • Articoli e blog posts
  • Testi con link interni
  • Contenuti con liste e tabelle

Configurazione Base

XML Dialog Minimo

<text
    jcr:primaryType="nt:unstructured"
    sling:resourceType="cq/gui/components/authoring/dialog/richtext"
    fieldLabel="Text"
    name="./text"
    useFixedInlineToolbar="{Boolean}true"/>

Proprietà essenziali:

  • sling:resourceType - Sempre cq/gui/components/authoring/dialog/richtext
  • fieldLabel - Etichetta
  • name - Percorso JCR (usa sempre ./)
  • useFixedInlineToolbar - Toolbar inline fissa (raccomandato)

Proprietà Principali

1. useFixedInlineToolbar

<text
    sling:resourceType="cq/gui/components/authoring/dialog/richtext"
    fieldLabel="Text"
    name="./text"
    useFixedInlineToolbar="{Boolean}true"/>

Comportamento:

  • true - Toolbar sempre visibile in alto (UX migliore)
  • false - Toolbar appare al sele

zione testo (default)

Best practice: Usa sempre true per authoring migliore!


2. Required

<text
    sling:resourceType="cq/gui/components/authoring/dialog/richtext"
    fieldLabel="Article Content"
    name="./text"
    required="{Boolean}true"
    useFixedInlineToolbar="{Boolean}true"/>

Configurazione Plugin RTE

Il RTE è configurato tramite plugin che abilitano feature specifiche. Ogni plugin va dichiarato sotto il nodo <rtePlugins>.

Struttura Base con Plugin

<text
    jcr:primaryType="nt:unstructured"
    sling:resourceType="cq/gui/components/authoring/dialog/richtext"
    fieldLabel="Text"
    name="./text"
    useFixedInlineToolbar="{Boolean}true">

    <!-- UI Configuration -->
    <uiSettings jcr:primaryType="nt:unstructured">
        <cui jcr:primaryType="nt:unstructured">
            <!-- Inline toolbar -->
            <inline
                jcr:primaryType="nt:unstructured"
                toolbar="[format#bold,format#italic,format#underline,links#modifylink,lists#unordered,lists#ordered]">
                <popovers jcr:primaryType="nt:unstructured">
                    <format
                        jcr:primaryType="nt:unstructured"
                        ref="format"/>
                    <lists
                        jcr:primaryType="nt:unstructured"
                        ref="lists"/>
                    <links
                        jcr:primaryType="nt:unstructured"
                        ref="links"/>
                </popovers>
            </inline>
        </cui>
    </uiSettings>

    <!-- RTE Plugins -->
    <rtePlugins jcr:primaryType="nt:unstructured">

        <!-- Format (Bold, Italic, Underline) -->
        <format
            jcr:primaryType="nt:unstructured"
            features="[bold,italic,underline]"/>

        <!-- Lists -->
        <lists
            jcr:primaryType="nt:unstructured"
            features="[ordered,unordered]"/>

        <!-- Links -->
        <links
            jcr:primaryType="nt:unstructured"
            features="[modifylink,unlink]"/>

    </rtePlugins>

</text>

Plugin Disponibili

1. Format (Bold, Italic, Underline)

<format
    jcr:primaryType="nt:unstructured"
    features="[bold,italic,underline]"/>

Genera: <strong>, <em>, <u>


2. Lists (Ordered, Unordered)

<lists
    jcr:primaryType="nt:unstructured"
    features="[ordered,unordered,indent,outdent]"/>

Genera: <ul>, <ol>, <li>


3. Links (Modifica, Rimuovi)

<links
    jcr:primaryType="nt:unstructured"
    features="[modifylink,unlink]">
    <linkDialogConfig jcr:primaryType="nt:unstructured">
        <linkDialogFields jcr:primaryType="nt:unstructured">
            <targetToggle
                jcr:primaryType="nt:unstructured"
                checked="{Boolean}false"/>
        </linkDialogFields>
    </linkDialogConfig>
</links>

Features:

  • modifylink - Crea/modifica link
  • unlink - Rimuovi link

Genera: <a href="...">


4. Justify (Allineamento)

<justify
    jcr:primaryType="nt:unstructured"
    features="[justifyleft,justifycenter,justifyright]"/>

5. Paraformat (Heading, Paragrafi)

<paraformat
    jcr:primaryType="nt:unstructured"
    features="*">
    <formats jcr:primaryType="nt:unstructured">
        <p
            jcr:primaryType="nt:unstructured"
            description="Paragraph"
            tag="p"/>
        <h2
            jcr:primaryType="nt:unstructured"
            description="Heading 2"
            tag="h2"/>
        <h3
            jcr:primaryType="nt:unstructured"
            description="Heading 3"
            tag="h3"/>
        <h4
            jcr:primaryType="nt:unstructured"
            description="Heading 4"
            tag="h4"/>
    </formats>
</paraformat>

Genera: <h2>, <h3>, <h4>, <p>


6. Subsuperscript (Pedice, Apice)

<subsuperscript
    jcr:primaryType="nt:unstructured"
    features="[subscript,superscript]"/>

Genera: <sub>, <sup>


7. Table (Tabelle)

<table
    jcr:primaryType="nt:unstructured"
    features="[createtable,removetable,insertrow,removerow,insertcolumn,removecolumn,cellprops,mergecells,splitcell,selectrow,selectcolumn]"/>

Genera: <table>, <tr>, <td>, <th>


8. Misctools (Codice, Cerca/Sostituisci)

<misctools
    jcr:primaryType="nt:unstructured"
    features="[sourceedit,findreplace]"/>
  • sourceedit - Modifica HTML raw
  • findreplace - Cerca e sostituisci

Esempi Pratici

Esempio 1: RTE Semplice (Bold, Italic, Lists, Link)

<description
    jcr:primaryType="nt:unstructured"
    sling:resourceType="cq/gui/components/authoring/dialog/richtext"
    fieldLabel="Description"
    name="./description"
    useFixedInlineToolbar="{Boolean}true">

    <uiSettings jcr:primaryType="nt:unstructured">
        <cui jcr:primaryType="nt:unstructured">
            <inline
                jcr:primaryType="nt:unstructured"
                toolbar="[format#bold,format#italic,links#modifylink,lists#unordered,lists#ordered]">
                <popovers jcr:primaryType="nt:unstructured">
                    <format
                        jcr:primaryType="nt:unstructured"
                        ref="format"/>
                    <lists
                        jcr:primaryType="nt:unstructured"
                        ref="lists"/>
                    <links
                        jcr:primaryType="nt:unstructured"
                        ref="links"/>
                </popovers>
            </inline>
        </cui>
    </uiSettings>

    <rtePlugins jcr:primaryType="nt:unstructured">
        <format
            jcr:primaryType="nt:unstructured"
            features="[bold,italic]"/>
        <lists
            jcr:primaryType="nt:unstructured"
            features="[ordered,unordered]"/>
        <links
            jcr:primaryType="nt:unstructured"
            features="[modifylink,unlink]"/>
    </rtePlugins>

</description>

Esempio 2: RTE Completo (Articolo Editoriale)

<articleText
    jcr:primaryType="nt:unstructured"
    sling:resourceType="cq/gui/components/authoring/dialog/richtext"
    fieldLabel="Article Content"
    name="./articleText"
    required="{Boolean}true"
    useFixedInlineToolbar="{Boolean}true">

    <uiSettings jcr:primaryType="nt:unstructured">
        <cui jcr:primaryType="nt:unstructured">
            <inline
                jcr:primaryType="nt:unstructured"
                toolbar="[format#bold,format#italic,format#underline,#paraformat,links#modifylink,lists#unordered,lists#ordered,justify#justifyleft,justify#justifycenter,justify#justifyright]">
                <popovers jcr:primaryType="nt:unstructured">
                    <format
                        jcr:primaryType="nt:unstructured"
                        ref="format"/>
                    <paraformat
                        jcr:primaryType="nt:unstructured"
                        ref="paraformat"/>
                    <lists
                        jcr:primaryType="nt:unstructured"
                        ref="lists"/>
                    <links
                        jcr:primaryType="nt:unstructured"
                        ref="links"/>
                    <justify
                        jcr:primaryType="nt:unstructured"
                        ref="justify"/>
                </popovers>
            </inline>
        </cui>
    </uiSettings>

    <rtePlugins jcr:primaryType="nt:unstructured">
        <format
            jcr:primaryType="nt:unstructured"
            features="[bold,italic,underline]"/>
        <paraformat
            jcr:primaryType="nt:unstructured"
            features="*">
            <formats jcr:primaryType="nt:unstructured">
                <p
                    jcr:primaryType="nt:unstructured"
                    description="Paragraph"
                    tag="p"/>
                <h2
                    jcr:primaryType="nt:unstructured"
                    description="Heading 2"
                    tag="h2"/>
                <h3
                    jcr:primaryType="nt:unstructured"
                    description="Heading 3"
                    tag="h3"/>
                <h4
                    jcr:primaryType="nt:unstructured"
                    description="Heading 4"
                    tag="h4"/>
            </formats>
        </paraformat>
        <lists
            jcr:primaryType="nt:unstructured"
            features="[ordered,unordered,indent,outdent]"/>
        <links
            jcr:primaryType="nt:unstructured"
            features="[modifylink,unlink]"/>
        <justify
            jcr:primaryType="nt:unstructured"
            features="[justifyleft,justifycenter,justifyright]"/>
    </rtePlugins>

</articleText>

Uso in HTL

Rendering HTML

<!-- RTE genera già HTML, usa context='html' -->
<div class="article-content">
  ${properties.text @ context='html'}
</div>

⚠️ IMPORTANTE: Usa sempre @ context='html' per il RTE!


Con Validazione

<div data-sly-test="${properties.text}" class="content">
  ${properties.text @ context='html'}
</div>

<!-- Oppure con fallback -->
<div class="content">
  ${properties.text @ context='html' || '<p>No content available</p>'}
</div>

Con Sling Model

@Model(adaptables = Resource.class)
public class ArticleModel {

    @ValueMapValue
    private String articleText;

    public String getArticleText() {
        return articleText;
    }

    public boolean hasContent() {
        return articleText != null && !articleText.isEmpty();
    }

    /**
     * Strip HTML per excerpt
     */
    public String getPlainTextExcerpt(int maxLength) {
        if (articleText == null) {
            return "";
        }

        // Strip HTML tags
        String plainText = articleText.replaceAll("<[^>]*>", "");

        // Truncate
        if (plainText.length() > maxLength) {
            return plainText.substring(0, maxLength) + "...";
        }

        return plainText;
    }

    /**
     * Word count
     */
    public int getWordCount() {
        if (articleText == null) {
            return 0;
        }

        String plainText = articleText.replaceAll("<[^>]*>", "");
        return plainText.split("\\s+").length;
    }
}

HTL con Model:

<article data-sly-use.model="com.mysite.models.ArticleModel">

  <div data-sly-test="${model.hasContent}" class="article-body">
    ${model.articleText @ context='html'}
  </div>

  <div class="article-meta">
    <span>Word count: ${model.wordCount}</span>
  </div>

  <!-- Plain text excerpt per SEO -->
  <meta name="description" content="${model.getPlainTextExcerpt(160)}"/>

</article>

RTE vs Textarea

RTE Textarea
Formattazione Sì (bold, link, liste) No (testo puro)
HTML Output No
Complessità Alta Bassa
Performance Più pesante Leggero
Use Case Contenuti editoriali Testi semplici, metadata

Regola: Usa RTE solo se serve formattazione!


Problemi Comuni

❌ Problema 1: HTML non renderizzato

<!-- SBAGLIATO - mostra tag HTML come testo -->
<div>${properties.text}</div>

<!-- CORRETTO - renderizza HTML -->
<div>${properties.text @ context='html'}</div>

❌ Problema 2: Plugin non funzionante

<!-- SBAGLIATO - plugin dichiarato ma non nella toolbar -->
<rtePlugins>
    <format features="[bold,italic]"/>
</rtePlugins>

<!-- Toolbar non include format#bold -->
<inline toolbar="[lists#unordered]"/>

<!-- CORRETTO - toolbar include i plugin -->
<inline toolbar="[format#bold,format#italic,lists#unordered]"/>

❌ Problema 3: Link rotti per pagine interne

Problema: RTE salva link come /content/mysite/page senza .html

Soluzione in HTL: Post-process il contenuto

public String getProcessedText() {
    if (articleText == null) {
        return "";
    }

    // Add .html to internal links
    return articleText.replaceAll(
        "href=\"(/content/[^\"]+)\"",
        "href=\"$1.html\""
    );
}

❌ Problema 4: HTML non valido salvato

Problema: Autori copiano HTML da Word/Email

Soluzione: Usa plugin paste con opzioni di cleanup

<paste
    jcr:primaryType="nt:unstructured"
    features="[plaintext,wordhtml,markdown]"
    defaultPasteMode="plaintext"/>

Best Practices

  1. useFixedInlineToolbar: Sempre true per UX migliore
  2. Solo plugin necessari: Non abilitare tutto
  3. Heading gerarchici: h2, h3, h4 (non h1)
  4. context='html' in HTL: Sempre per RTE
  5. Limita formattazione: Bold, italic, liste, link sono sufficienti
  6. Test copia-incolla: Verifica comportamento con Word/Google Docs
  7. Validazione server-side: Sanifica HTML nel backend
  8. Accessibility: Configura plugin per HTML semantico

Configurazione Globale RTE

Puoi configurare l'RTE globalmente in:

/apps/cq/gui/components/authoring/editors/clientlibs/core/rte/config.json

Questo permette configurazioni riutilizzabili across tutti i componenti.


Plugin Avanzati

Styles (Classi CSS Custom)

<styles
    jcr:primaryType="nt:unstructured"
    features="*">
    <styles jcr:primaryType="nt:unstructured">
        <highlight
            jcr:primaryType="nt:unstructured"
            cssName="text-highlight"
            text="Highlight"/>
        <quote
            jcr:primaryType="nt:unstructured"
            cssName="blockquote"
            text="Quote"
            tag="blockquote"/>
    </styles>
</styles>

Prossima Lezione

Questa conclude la serie base sui componenti dialog di AEM! Nelle prossime guide vedremo componenti avanzati come FileUpload, ColorField, NumberField e altri.

Risorse:


Guida #6 della serie AEM Dialog Components - ← Lezione precedente

Serie completa: Textfield | Textarea | Checkbox | Select | PathBrowser | RichText