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.
Solved! Go to Solution.
Views
Replies
Total Likes
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!
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!
Views
Likes
Replies
Views
Likes
Replies