Skip to content

[Task] Roles and Tools: Angular implementation + backend wiring #36930

Description

@hmoreras

Goal

Ship the Angular Roles and Tools portlet end-to-end — UI, portlet.xml registration as an opt-in Beta portlet, and rollback story — in a single PR.

Frontend scope

New Nx library: core-web/libs/portlets/dot-roles/

  • dot-roles-shell.component.ts
  • dot-roles-page.component.ts
  • lib.routes.ts exported as dotRolesRoutes, imported at roles in core-web/apps/dotcms-ui/src/app/app.routes.ts
  • SignalStore: DotRolesStore (role tree, selected role, active tab, per-tab pagination + search, drag state, in-flight flags)
  • Data-access service: extend core-web/libs/data-access/src/lib/dot-roles/dot-roles.service.ts to cover /v1/roles/*, /v1/users/filter — replacing every RoleAjax DWR call needed by the Users tab and the roles tree that the Angular portlet renders directly. Permission endpoints and Tool Group endpoints (/v1/permissions/*, /v1/roles/layouts, /v1/portlet/custom, /v1/toolgroups/*, DWR RoleAjax.getRolePermissions / saveRolePermission / loadRoleLayouts / saveRoleLayouts / addNewLayout / etc.) are not consumed from the Angular portlet in this Epic — they stay behind the iframe wrappers described below
  • Reuse models from libs/dotcms-models: DotRole (extend as needed)
  • PrimeNG only for every UI component: p-tree (roles hierarchy with drag-and-drop), p-tabView (Users / Permissions / Tools), p-table (users grid), p-dialog (Add Role / Edit Role / confirm-destroy), p-select (Parent dropdown), p-checkbox (Can Grant flags), p-textarea (Description), p-overlay-panel or p-autoComplete (Grant to User popover)
  • Users table follows the project's shared table styling conventions (see dot-users and dot-tags for reference); the design is a guide, not pixel-locked
  • Tests: Jest + Spectator on shell / page / store / each tab, with data-testid selectors on every interactive element

Screen structure (per new design)

Layout: two-column shell — left Roles panel, right Role detail panel. No page-level top bar beyond the shared dotCMS header (breadcrumbs Home > Settings > Roles, site selector, notifications, user avatar).

Left panel — Roles

  • Header: ROLES label + New button (opens Add Role dialog)
  • Filter roles search input (debounced client-side filter of the loaded tree)
  • Hierarchical p-tree list:
    • Top-level roles use a folder icon with expand/collapse chevron
    • Child roles use a shield icon
    • Per-row badge: user count with person icon (e.g., 👤 2)
    • Hover reveals inline + action to add a child role under that parent (opens Add Role dialog with Parent prefilled)
  • Drag-and-drop reparenting: dragging a role node onto a different parent updates Role.parent via a v1 endpoint (endpoint identified during the spike; if the current REST surface does not expose a reparent operation, file it as a backend hardening [Task] linked to this issue)
  • Selected node highlighted; selection drives the right-hand detail area

Right panel — Role detail

  • Header row: role icon (folder for parents, shield for children) + role name + subtitle N users · M tools granted + Edit Role button top-right

  • Three tabs (p-tabView):

    1. Users — full Angular implementation

    • Grant to User button top-right opens an overlay panel with:
      • Search users input (debounced, hits /v1/users/filter or the equivalent identified in the spike)
      • Scrollable list of users (avatar + name + email); click a row to grant this role to that user
    • Below: p-table of current members
      • Columns: NAME (avatar + name), EMAIL, GRANTED FROM (chip showing the role that granted access — direct assignment shows the current role's name; inherited shows the parent role's name)
      • Row hover reveals Remove action; multi-select supports bulk remove
      • Empty state: dashed card with helper text This role has no users yet and copy Grant this role to a user using the selector above.

    2. Permissionsiframe interim

    • Angular tab renders an <iframe> whose src points at a new lightweight JSP wrapper (see Backend wiring below) that hosts the existing Dojo/DWR permissions UI unchanged
    • No Angular re-implementation of the accordion permission matrix in this Epic; a follow-up [Task] is filed for the eventual Angular replacement so the iframe can be swapped by dropping in the new Angular component without touching the surrounding shell
    • Existing permission endpoints (RoleAjax.getRolePermissions, saveRolePermission, /v1/permissions/* where already used) stay behind the iframe
    • Iframe height / resize behavior: fills the tab body with a postMessage or auto-resize helper so users don't see nested scrollbars

    3. Toolsiframe interim

    • Angular tab renders an <iframe> whose src points at a new lightweight JSP wrapper hosting the existing Dojo/DWR Tool Groups management UI unchanged (available / assigned layouts, New Layout dialog with dot-material-icon-picker, save role-layouts binding)
    • No Angular re-implementation of Tool Groups in this Epic because the design is still being defined; a follow-up [Task] is filed for the eventual Angular replacement so the iframe can be swapped by dropping in the new component
    • Existing Tool Group endpoints (RoleAjax.loadRoleLayouts / saveRoleLayouts / addNewLayout / etc., /v1/roles/layouts, /v1/portlet/custom) stay behind the iframe

Add Role dialog (triggered by New in the roles panel or the inline + on a row)

  • Title: Add Role
  • Fields:
    • Role * (required, red border on validation fail)
    • Key
    • Parent (p-select, defaults to None (top level); when opened from an inline +, prefilled with that parent role)
    • Can Grant (three p-checkbox: Users, Permissions, Tools — all checked by default per the design)
    • Description (p-textarea)
  • Actions: Cancel / Save (Save disabled until required fields valid)
  • closable: true, closeOnEscape: true

Edit Role dialog (triggered by Edit Role in the detail header)

  • Same fields as Add Role, prefilled with the selected role's values
  • Additional Delete Role destructive action bottom-left (red outline). Click triggers a confirm-destroy dialog
  • closable: true, closeOnEscape: true

Confirm-destroy dialog

  • Confirm-remove role, per dotCMS dialog standards (closable: true, closeOnEscape: true)

Backend wiring

dotCMS/src/main/webapp/WEB-INF/portlet.xml:

  • Add a new roles-beta portlet entry: <portlet-class>com.dotcms.spring.portlet.PortletController</portlet-class> + <portlet-url>/roles</portlet-url> + display name Roles and Tools (Beta)
  • The existing Dojo roles portlet entry stays untouched so it remains reachable via the "Add Portlet" picker
  • Not added to any default layout or UpgradeTask — admins opt in via "Add Portlet" in Tools
  • Angular route wired at roles in core-web/apps/dotcms-ui/src/app/app.routes.ts loading @dotcms/portlets/dot-roles/portlet
  • Reuse the existing PortletID.ROLES enum entry and the existing roles i18n title key; add a roles-beta i18n title key for the Beta display name
  • License + role gating preserved (requiredPortlet("roles") on v1 endpoints, admin-only surfaces enforced)

Permissions iframe wrapper JSP

  • Add a new minimal JSP under dotCMS/src/main/webapp/html/portlet/ext/roleadmin/ (naming to be decided in the spike, e.g., view_role_permissions_wrapper.jsp) that includes the existing view_role_permissions_inc.jsp + view_role_permissions_js_inc.jsp output and nothing else
  • The wrapper accepts the roleId via query string, initializes the existing Dojo/DWR components, and communicates readiness / size to the parent Angular tab via postMessage
  • Kept intentionally thin so the eventual Angular replacement (tracked as a separate [Task]) only requires swapping the iframe src for a new Angular component in dot-roles-page.component.ts

Tools iframe wrapper JSP

  • Add a second minimal JSP under dotCMS/src/main/webapp/html/portlet/ext/roleadmin/ (e.g., view_role_tools_wrapper.jsp) that surfaces just the Tool Groups management section of the existing view_roles_js_inc.jsp (available / assigned grids, New Layout dialog, save role-layouts)
  • Same postMessage contract as the permissions wrapper, same roleId query-string input, same thin-wrapper philosophy so the future Angular replacement is a drop-in

Untouched files

  • view_roles.jsp remains untouched — the wrappers are new files dedicated to the iframe use case, so the current Dojo portlet at /#/c/roles keeps working for admins who don't opt into the Beta

Reparent endpoint

  • If the drag-to-reparent flow requires a new v1 endpoint (spike output will confirm), open a backend [Task] linked to this issue and unblock it before merge

Inputs

  • Spike output from the consolidated audit issue (parity matrix + API audit + UI mapping + hardening backlog + reparent endpoint decision + iframe wrapper design for Permissions and Tools)
  • Portlet conventions: core-web/libs/portlets/CLAUDE.md
  • Structural reference: core-web/libs/portlets/dot-users/ (same Beta pattern, same iframe-for-undesigned-surfaces approach) and core-web/libs/portlets/dot-velocity-playground/
  • New design: https://claude.ai/design/p/20548219-a454-4b14-becf-5dd4dc0b84a1?file=Roles.dc.html&via=share (internal — auth required)

Definition of Done

Every acceptance criterion in epic #36909 passes manually against the local build:

  • Beta portlet appears in the "Add Portlet" picker as Roles and Tools (Beta)
  • Adding the Beta portlet to a layout serves the Angular shell at /#/c/roles
  • Roles panel renders the full hierarchy with folder icons for parents and shield icons for children, expand/collapse, and user-count badges
  • Filter roles narrows the visible tree client-side
  • New opens the Add Role dialog; save creates the role and refreshes the tree
  • Inline + on a parent row opens Add Role dialog with Parent prefilled
  • Drag-and-drop of a role onto a different parent persists the reparent via v1 endpoint
  • Edit Role in the detail header opens the Edit dialog; save updates the role; Delete Role opens a confirm-destroy and removes the role
  • Users tab: Grant to User popover searches users and grants the role on selection; the members table renders NAME / EMAIL / GRANTED FROM (with inheritance chip); remove and bulk-remove work; empty state matches the design copy
  • Permissions tab: iframe loads the new JSP wrapper and renders the existing permissions UI functional (matrix, cascade toggle, apply changes, cascade-progress) — behavior parity with the current Dojo portlet
  • Tools tab: iframe loads the new JSP wrapper and renders the existing Tool Groups management UI functional (available / assigned layouts, New Layout dialog, save role-layouts) — behavior parity with the current Dojo portlet
  • License + role gating preserved on every write path
  • Dojo roles portlet entry remains registered so admins can revert by removing the Beta portlet from their layout
  • Follow-up [Task] filed for the Angular replacement of the Permissions tab, linked to this issue and to epic Dojo to Angular: Roles and Tools Portlet #36909
  • Follow-up [Task] filed for the Angular replacement of the Tools tab (once design is defined), linked to this issue and to epic Dojo to Angular: Roles and Tools Portlet #36909

Metadata

Metadata

Assignees

Type

No type

Projects

Status
In Progress

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions