Expand my Community achievements bar.

Introducing Adobe LLM Optimizer: Own your brand’s presence in AI-Powered search and discovery

Servlet-Based RTE Config Approach

Avatar

Level 2

Anyone implemented Servlet-Based RTE Config Approach? 
This is requirement i have
1. Proxy RTE Component
Location: /apps/your-project/components/content/rich-text
This is a reusable component used in dialogs wherever RTE is needed.
2. Editor Config Definitions
We create config nodes under:
/apps/your-project/components/content/rich-text/editor
For example:
- default – for common toolbar
- hero – for rich toolbar in hero component
- footer – for restricted toolbar
3. In Dialogs of Other Components, we use:
<text
sling:resourceType="merkle-core/components/content/rich-text"
name="./text"
editor="/apps/your-project/components/content/rich-text/editor/hero"
useFixedInlineToolbar="{Boolean}true" />
4. Servlet Logic
When dialog opens:
- The servlet reads the editor path.
- It loads child nodes under that path (plugin config).
- Returns a merged JSON config (like { "bold": true, "link": true })
- If editor is missing, it falls back to /editor/default.

1 Reply

Avatar

Community Advisor

Hi @VanitaNa,

Here's an explanation of the approach with some example code and structure to help you understand how it works. The code is just for reference and might need some tweaks based on your project setup.

1. Proxy RTE Component (rich-text)

Path: /apps/your-project/components/content/rich-text

This component wraps the Coral RTE and reads its configuration from a servlet via the editor attribute.

Example Dialog Field (used in other components):

<text
    jcr:primaryType="nt:unstructured"
    sling:resourceType="your-project/components/content/rich-text"
    name="./text"
    editor="/apps/your-project/components/content/rich-text/editor/hero"
    useFixedInlineToolbar="{Boolean}true"/>

2. Editor Config Definitions

You create config folders like:

/apps/your-project/components/content/rich-text/editor/
    default/
        bold     {Boolean=true}
        italic   {Boolean=true}
        link     {Boolean=true}
/    hero/
        bold     {Boolean=true}
        italic   {Boolean=true}
        underline{Boolean=true}
        link     {Boolean=true}
/    footer/
        bold     {Boolean=false}
        italic   {Boolean=true}

Each property under the node becomes part of your RTE configuration.

3. Servlet (Core Logic)

You write a servlet that:

  • Reads the editor path (passed via request param/resource property)

  • Fetches all key-value pairs under that editor config node

  • Merges them and returns a JSON object

Servlet Path Suggestion:

/bin/rte-config

Example Servlet Code (Java):

@Component(service = Servlet.class,
           property = {
               "sling.servlet.methods=GET",
               "sling.servlet.paths=/bin/rte-config"
           })
public class RTEConfigServlet extends SlingAllMethodsServlet {

    @Reference
    private ResourceResolverFactory resolverFactory;

    @Override
    protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException {
        String editorPath = request.getParameter("editor");
        if (StringUtils.isBlank(editorPath)) {
            editorPath = "/apps/your-project/components/content/rich-text/editor/default";
        }

        try (ResourceResolver serviceResolver = resolverFactory.getServiceResourceResolver(
                Collections.singletonMap(ResourceResolverFactory.SUBSERVICE, "data-reader"))) {

            Resource configResource = serviceResolver.getResource(editorPath);
            if (configResource == null) {
                configResource = serviceResolver.getResource("/apps/your-project/components/content/rich-text/editor/default");
            }

            JSONObject json = new JSONObject();
            if (configResource != null) {
                for (Resource child : configResource.getChildren()) {
                    ValueMap vm = child.getValueMap();
                    String key = child.getName();
                    boolean value = vm.get("booleanValue", true);  // assuming nt:unstructured nodes with boolean props
                    json.put(key, value);
                }
            }

            response.setContentType("application/json");
            response.getWriter().write(json.toString());
        } catch (Exception e) {
            log.error("Failed to load RTE config", e);
            response.setStatus(500);
        }
    }
}

4. JavaScript to Inject RTE Config into Dialog

You will write a custom clientlib under:

/apps/your-project/clientlibs/rte-config-loader

JS Snippet:

(function($, $document) {
    $document.on("dialog-ready", function() {
        $("coral-multifield, coral-textfield, coral-rte").each(function() {
            const editorPath = $(this).attr("editor");

            if (editorPath) {
                $.get("/bin/rte-config", { editor: editorPath }).done(function(config) {
                    // Apply the RTE config
                    // This part depends on how your RTE is initialized in the proxy component
                    // E.g., set plugin config dynamically or initialize a custom RTE instance
                    console.log("Loaded RTE config:", config);
                });
            }
        });
    });
})(Granite.$, jQuery(document));

Include this clientlib in cq.authoring.dialog category.

Hope that helps!


Santosh Sai

AEM BlogsLinkedIn