Context
Migrate the ES Search developer-tool portlet from legacy Dojo/JSP to Angular (PrimeNG + Tailwind + NgRx Signals), following the modernization pattern established in Q1 by Categories (#34731 / PR #35146), Tags (#34552), and Plugins (#34732).
Scope: Feature parity with Dojo + UI/UX improvements that emerge during design.
Why now: #34733 is one of ~10 remaining Dojo portlets under the [PILLAR] #33458. ES Search is lower-risk than admin-critical portlets (only admin + STANDARD-licensed users access it), making it a good next step before larger pieces like Users Management (#34736) or Permissions (#34739).
Current State (Dojo)
Entry point: /dotAdmin/#/c/es-search → iframe loads JSP.
Files:
- UI:
dotCMS/src/main/webapp/WEB-INF/jsp/es-search/render.jsp (ACE editor, dijit widgets, XHR)
- Help modal:
dotCMS/src/main/webapp/WEB-INF/jsp/es-search/es-search-help.jsp
- Not-licensed:
dotCMS/src/main/webapp/WEB-INF/jsp/es-search/not_licensed.jsp
- Resource:
dotCMS/src/main/java/com/dotcms/rest/elasticsearch/ESContentResourcePortlet.java
- Portlet registration:
portlet.xml:345-348 (portlet class ESContentResourcePortlet)
Backend endpoints:
| Method |
Path |
Purpose |
| GET/POST |
/api/es/search |
Run ES query → contentlets + raw ES metadata |
| POST |
/api/es/search |
POST variant with Swagger docs |
| GET/POST |
/api/es/raw |
Unprocessed SearchResponse |
| POST |
/api/portlet/es-search/render |
Legacy JSP form submit (to be removed) |
⚠️ OpenAPI gap discovered: a live fetch of https://demo.dotcms.com/api/openapi.json confirms these endpoints are NOT in the OpenAPI spec — only the tag "Elasticsearch Content Search" ("Backend Elasticsearch search endpoints for portlet context") exists with zero paths attached. No schemas for ESSearchResults, ESSearchResponse, or related error shapes are defined. Fixing this is a mandatory deliverable in the BE audit sub-task.
Dojo features (catalog):
- ACE JSON editor with syntax highlighting
- Live vs working toggle
- Admin user impersonation (
userid param, admin-only)
- Query timing (query time + population time)
- Hit count + results table (title, inode, identifier, full printable map)
- Aggregations table (buckets)
- Term suggestions
- Raw ES response JSON
- Wrap code toggle
- Help dialog with example queries, REST API docs, Velocity viewtool usage, geolocation examples
Gates:
- License: requires
LicenseLevel.STANDARD minimum (render.jsp:35-49)
- Role:
userid impersonation admin-only (render.jsp:73)
Target State (Angular)
- New portlet at
core-web/libs/portlets/dot-es-search/
- Not a CRUD portlet — single page with query editor → results (tabs for hits / aggregations / raw). The standard
dot-tags CRUD shape does not apply; the portlet needs a minimal shell + page + single SignalStore + 1 service. The FE spec sub-task formalizes this deviation.
- Angular service in
core-web/libs/data-access/src/lib/dot-es-search/ calling /api/es/search and /api/es/raw.
- PrimeNG components for JSON editor, results tabs (TabView), help/examples dialog, Toast for errors.
- All text via
DotMessagePipe (i18n keys esSearch.*).
- License-gated UI: render
dot-not-licensed component (or equivalent) when license < STANDARD.
- Admin-only controls (impersonation) gated via user role check.
Backend wiring (mirrors Categories PR #35146)
portlet.xml: switch es-search entry from ESContentResourcePortlet (JSP render) to PortletController route /es-search.
- Add
es-search-legacy portlet entry preserving the Dojo JSP view (displayed as "ES Search Legacy" in admin menu) — rollback escape hatch.
- Add
ES_SEARCH_LEGACY enum to PortletID.
Language.properties: add label com.dotcms.repackage.javax.portlet.title.es-search-legacy = ES Search Legacy.
- New Angular route with
MenuGuard + reuseRoute: false.
Definition of Done (Gherkin)
Feature: ES Search portlet migrated to Angular
Scenario: Admin with STANDARD license opens ES Search
Given I am logged in as a user with the ES-Search portlet role
And my license is STANDARD or higher
When I navigate to /dotAdmin/#/c/es-search
Then I see the Angular ES-Search portlet (no iframe, no Dojo)
And I see the query editor, Run button, and examples link
Scenario: Run a valid ES query
Given the Angular portlet is loaded
When I paste a valid ES DSL JSON and click Run
Then I see the hit count, query time, and results tab populated
And the Aggregations tab is populated if aggregations were returned
And the Raw Response tab shows the unprocessed ES response
Scenario: Run an invalid ES query
Given the Angular portlet is loaded
When I paste invalid JSON and click Run
Then I see a toast error via DotHttpErrorManagerService
And the previous results (if any) remain visible
Scenario: User without license
Given my license is COMMUNITY
When I navigate to /dotAdmin/#/c/es-search
Then I see the not-licensed state (no query editor)
Scenario: Rollback available
Given the new portlet is live
When an admin navigates to the "ES Search Legacy" portlet in the menu
Then the original Dojo JSP portlet loads and functions
Scenario: Live vs Working toggle
Given I have the same contentlet in live and working states
When I toggle "Live" and Run the same query
Then the results reflect the live version
And toggling off returns the working version
Scenario: Admin impersonation
Given I am a CMS Admin
When I enter a userid and Run a query
Then the query executes as that user
And a non-admin user does not see the impersonation input
Sub-tasks
Tracked as native GitHub sub-issues (see panel on the right):
[Spike] Current-state audit + feature parity matrix
[Spike] Backend API audit (includes OpenAPI annotation gap)
[Task] UX/UI design (parity + improvements)
[Task] Frontend implementation spec
[Task] Backend wiring + rollback entry
References
Context
Migrate the ES Search developer-tool portlet from legacy Dojo/JSP to Angular (PrimeNG + Tailwind + NgRx Signals), following the modernization pattern established in Q1 by Categories (#34731 / PR #35146), Tags (#34552), and Plugins (#34732).
Scope: Feature parity with Dojo + UI/UX improvements that emerge during design.
Why now: #34733 is one of ~10 remaining Dojo portlets under the [PILLAR] #33458. ES Search is lower-risk than admin-critical portlets (only admin + STANDARD-licensed users access it), making it a good next step before larger pieces like Users Management (#34736) or Permissions (#34739).
Current State (Dojo)
Entry point:
/dotAdmin/#/c/es-search→ iframe loads JSP.Files:
dotCMS/src/main/webapp/WEB-INF/jsp/es-search/render.jsp(ACE editor, dijit widgets, XHR)dotCMS/src/main/webapp/WEB-INF/jsp/es-search/es-search-help.jspdotCMS/src/main/webapp/WEB-INF/jsp/es-search/not_licensed.jspdotCMS/src/main/java/com/dotcms/rest/elasticsearch/ESContentResourcePortlet.javaportlet.xml:345-348(portlet classESContentResourcePortlet)Backend endpoints:
/api/es/search/api/es/search/api/es/rawSearchResponse/api/portlet/es-search/renderDojo features (catalog):
useridparam, admin-only)Gates:
LicenseLevel.STANDARDminimum (render.jsp:35-49)useridimpersonation admin-only (render.jsp:73)Target State (Angular)
core-web/libs/portlets/dot-es-search/dot-tagsCRUD shape does not apply; the portlet needs a minimal shell + page + single SignalStore + 1 service. The FE spec sub-task formalizes this deviation.core-web/libs/data-access/src/lib/dot-es-search/calling/api/es/searchand/api/es/raw.DotMessagePipe(i18n keysesSearch.*).dot-not-licensedcomponent (or equivalent) when license < STANDARD.Backend wiring (mirrors Categories PR #35146)
portlet.xml: switches-searchentry fromESContentResourcePortlet(JSP render) toPortletControllerroute/es-search.es-search-legacyportlet entry preserving the Dojo JSP view (displayed as "ES Search Legacy" in admin menu) — rollback escape hatch.ES_SEARCH_LEGACYenum toPortletID.Language.properties: add labelcom.dotcms.repackage.javax.portlet.title.es-search-legacy = ES Search Legacy.MenuGuard+reuseRoute: false.Definition of Done (Gherkin)
Sub-tasks
Tracked as native GitHub sub-issues (see panel on the right):
[Spike]Current-state audit + feature parity matrix[Spike]Backend API audit (includes OpenAPI annotation gap)[Task]UX/UI design (parity + improvements)[Task]Frontend implementation spec[Task]Backend wiring + rollback entryReferences
core-web/libs/portlets/dot-categories/(Categories — most recent, richest reference)core-web/libs/portlets/CLAUDE.mdcore-web/CLAUDE.md