Expand my Community achievements bar.

Nomination window for the Adobe Community Advisor Program, Class of 2025, is now open!

How to create a custom RTE plugin just like paragraph formats

Avatar

Level 2

I would like to create a custom RTE plugin just like paragraph formats (it will have the dropdown) we see in the RTE where upon clicking of a particular text in that custom plugin I would like to print it to the console. And for that custom plugin also I would like to set an icon.

 

Just like this I would like to implement. 

 

Thank you.

 

khaSHA_0-1698689713902.png

 

5 Replies

Avatar

Community Advisor

Hi,

 

Please refer to these articles where there is a good detail explanation about how to add a custom plugin for an RTE:

https://www.bounteous.com/insights/2022/01/06/custom-rich-text-editor-plugins-adobe-experience-manag...

https://medium.com/globant/build-a-custom-rte-plugin-with-chatgpt-for-aem-4e373487a6fe 

 

Hope this helps

 



Esteban Bustamante

Avatar

Level 2

@EstebanBustamante 

 

I am getting this error.

 

khaSHA_1-1698734990643.png

 

So I am registering the plugin like this /libs/clientlibs/granite/richtext/core/js/plugins/ParagraphFormatPlugin.js and even in the paraformat in uisettings i kept the items just like it is there but instead of paraformat i changed to my own 'translate' which I have written.

khaSHA_0-1698734907240.png

 

Avatar

Community Advisor

Are you following the examples I shared? There's a downloadable package available for you to customize. It seems the error you're encountering stems from improper registration of certain JavaScript components. Based on the examples I provided, make sure to register your plugin under the category 'rte.coralui3'. Kindly verify this and make use of the resources I shared.



Esteban Bustamante

Avatar

Level 1

 

  • Left Section:
    • Title (Careers Opportunities)
    • Description (Some text about careers)
    • CTA (View All Jobs)
  • Right Section:
    • Table with 3 columns:
      • Job Title (Clickable anchor)
      • Location (e.g., "United States")
      • Posted Date (e.g., "2 days ago")

Implementation Plan

  1. Back-End (Sling Model or Servlet)

    • Fetch dynamic data from the Careers API.
    • Process response and pass data to the front-end component.
  2. Front-End (HTL & JavaScript)

    • Use HTL (Sightly) for HTML structure.
    • Use CSS for layout (left-right structure).
    • Use JavaScript (AJAX) to call servlet and populate the table dynamically.
  3. Caching Strategy

    • Use Sling Dynamic Include (SDI) or Dispatcher caching if needed.

POC: AEM Component Code

Let's start with the AEM component implementation:

Backend - Sling Servlet to Fetch Careers API Data

We'll create a servlet to fetch jobs dynamically.

CareersServlet.java

 

java
CopyEdit
package com.myproject.core.servlets; import com.google.gson.JsonElement; import com.google.gson.JsonParser; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.SlingHttpServletResponse; import org.apache.sling.api.servlets.SlingSafeMethodsServlet; import org.osgi.service.component.annotations.Component; import javax.servlet.Servlet; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; @component( service = Servlet.class, property = { "sling.servlet.paths=/bin/careers", "sling.servlet.methods=GET", "sling.servlet.extensions=json" } ) public class CareersServlet extends SlingSafeMethodsServlet { private static final String API_URL = "https://example.com/api/careers"; // Replace with actual API URL @Override protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) { try { URL url = new URL(API_URL); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setRequestProperty("Accept", "application/json"); if (conn.getResponseCode() != 200) { response.setStatus(HttpURLConnection.HTTP_BAD_REQUEST); response.getWriter().write("Failed to fetch careers data"); return; } BufferedReader br = new BufferedReader(new InputStreamReader((conn.getInputStream()))); StringBuilder result = new StringBuilder(); String output; while ((output = br.readLine()) != null) { result.append(output); } conn.disconnect(); // Parse and return JSON response JsonElement jsonResponse = JsonParser.parseString(result.toString()); response.setContentType("application/json"); response.getWriter().write(jsonResponse.toString()); } catch (Exception e) { response.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR); try { response.getWriter().write("Error: " + e.getMessage()); } catch (Exception ignored) { } } } }

 

 

Front-End - HTL & JavaScript

Now, let's create the HTL (Sightly) and JavaScript to display the careers component.

/apps/myproject/components/careers/careers.html

 

html
CopyEdit
<div class="careers-container"> <!-- Left Side --> <div class="careers-info"> <h2>Career Opportunities</h2> <p>Explore exciting job openings and build your career with us.</p> <a href="/careers.html" class="btn-cta">View All Jobs</a> </div> <!-- Right Side (Table) --> <div class="careers-table"> <table> <thead> <tr> <th>Job Title</th> <th>Location</th> <th>Posted Date</th> </tr> </thead> <tbody id="careers-list"> <!-- Jobs will be populated here via JavaScript --> </tbody> </table> </div> </div> <sly data-sly-use.clientLib="/libs/granite/sightly/templates/clientlib.html"> <sly data-sly-call="${clientLib.js @ categories='myproject.careers'}" /> </sly>

 

 

JavaScript to Fetch API Data

We'll use JavaScript to fetch job data dynamically.

/apps/myproject/components/careers/clientlib-site/js/careers.js

 

javascript
CopyEdit
document.addEventListener("DOMContentLoaded", function () { fetch('/bin/careers.json') // Call the servlet .then(response => response.json()) .then(data => { let careersList = document.getElementById("careers-list"); careersList.innerHTML = ""; // Clear previous data data.jobs.forEach(job => { let row = document.createElement("tr"); // Job Title (Anchor Tag) let jobTitle = document.createElement("td"); let jobLink = document.createElement("a"); jobLink.href = `/careers/${job.id}`; jobLink.textContent = job.title; jobTitle.appendChild(jobLink); // Location let location = document.createElement("td"); location.textContent = job.location; // Posted Date let postedDate = document.createElement("td"); postedDate.textContent = formatPostedDate(job.posted_date); row.appendChild(jobTitle); row.appendChild(location); row.appendChild(postedDate); careersList.appendChild(row); }); }) .catch(error => console.error("Error fetching careers:", error)); }); // Function to format date (convert "2024-02-01" to "2 days ago") function formatPostedDate(dateString) { let jobDate = new Date(dateString); let today = new Date(); let diffTime = Math.abs(today - jobDate); let diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); return diffDays === 1 ? "1 day ago" : `${diffDays} days ago`; }
 

Avatar

Administrator

@khaSHA Did you find the suggestions from users helpful? Please let us know if more information is required. Otherwise, please mark the answer as correct for posterity. If you have found out solution yourself, please share it with the community.

 



Kautuk Sahni