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 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 Portlet #36909

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions