Skip to content

fix(dotcms-ui): redirect Content Search and Site Browser routes to Content Drive (#37759) - #37829

Closed
zJaaal wants to merge 4 commits into
mainfrom
issue-37827-content-drive-route-redirect
Closed

zJaaal wants to merge 4 commits into
mainfrom
issue-37827-content-drive-route-redirect

Conversation

@zJaaal

@zJaaal zJaaal commented Sep 30, 2026 •

Copy link
Copy Markdown
Member

Part of #37759. This PR does the routing half of that issue; the Content Drive side lands separately, so merging this should not close it.

Proposed Changes

When a user's menu has Content Drive but not Content Search or Site Browser, MenuGuardService now redirects their URLs to the Content Drive equivalent. Before this change, it sent the user to their first portlet with no explanation. Every /c/:id route goes through this guard, so internal navigations, routerLinks, and full-page links from JSPs, emails and bookmarks are all covered.

Requested URL Redirects to
/c/content /content-drive
/c/content?filter=<variable> /content-drive?filters=contentType:<variable>
/c/content/<inode> /content-drive?editContent=<identifier>&editContentLang=<languageId> (looked up with GET /api/v1/content/<inode>)
/c/content/new/<type> /content-drive?createContent=<type>
/c/content/new /content-drive
/c/site-browser /content-drive
/c/site-browser?path=//<host>/<folder>/ /content-drive?path=/<folder>/, switching to <host> first when it is not the current site
  • Edit and create links keep your place: they restore the CD_-prefixed params Content Drive adds when it hands off to the legacy editor, so the user lands back on their folder and filters. Other params, such as variantName and the legacy folder inode, have no Content Drive equivalent and are dropped.
  • Containers and templates stored as files open on their folder: those callers only saved the folder in the server session, which Content Drive can't read. They now also pass it to DotRouterService.goToSiteBrowser(path), and the guard maps it with the host stripped (Content Drive's path is a folder inside the current site). The legacy Site Browser still reads the session, so users who have it see no change. If the host can't be found, the user lands on Content Drive at the root.
  • When the inode lookup fails: the standard HTTP error handler reports the error and the user lands on Content Drive. If the handler has already navigated away itself (a 401 goes to login, for example), the guard stops without a second navigation.
  • Loop protection, edit links only: an edit link is not redirected when the user is navigating away from Content Drive itself. It keeps today's behavior of going to the first portlet, because Content Drive still sends legacy-editor content to /c/content/<inode>, and opening editContent would send it straight back. Create links are always redirected: nothing reads createContent yet, so a legacy create from Content Drive now stays in Content Drive instead of jumping to the first portlet.
  • Unchanged: users who still have Content Search or Site Browser in their menu, and users without Content Drive.

Content Types chip (shared libs/ui component)

A link that names only a content type (filters=contentType:Blog) filtered the grid but left the Content Types chip empty, because the chip lists content types under their selected base type. DotContentTypeFilterComponent now:

  • Fills in the base type: it adds the base types of the host's content types, taken from the content types it already loads with ensure, so there is no extra request. The chip reads "Content Types: Blog" and the next selection emitted carries both.
  • Drops unknown content types: content types the server does not return (deleted, or a typo in an old link) are removed from the host's selection after a successful load, so they don't keep filtering with nothing to clear. A failed load leaves the selection alone.

The asset picker uses the same component. The change only adds base types that a selected content type already implies, so a host that passes both sees no difference.

For that cleanup to stick in Content Drive, its init effect no longer tracks the default language. It used to re-run when the language arrived, re-reading the page's original URL over any filter changed in between. loadDefaultLanguage already adds the language to the current filters, so only a site change re-runs the init now.

What is left on #37759

This PR builds the URLs. Opening them fully is the Content Drive side of the same issue:

  • editContent already opens content that uses the new editor. For content that uses the legacy editor, it needs the side-panel handling that the Content Drive side adds.
  • createContent is a new param that Content Drive doesn't read yet, so for now a create link opens Content Drive with nothing selected. The Content Drive side should confirm the name and consume it, and must open legacy creates inside Content Drive rather than navigate to /c/content/new/<type>, or the redirect loops.
  • The edit-link loop check can go once Content Drive stops navigating to /c/content/<inode> for legacy content, or stay for the case where the side-panel flag is off.

Checklist

  • Tests: menu-guard.service.spec.ts covers each redirect row, CD_ param restoration, lookup failure and the already-redirected case, folder paths on the current site, another site and an unknown host, the "portlet still in menu" and "no Content Drive" cases, and the loop protection (31 tests). dot-content-type-filter.component.spec.ts covers the base type fill-in and the unknown-type drop, including a failed load. dot-content-drive.store.spec.ts covers a filter changed before the default language lands. The router service, container list and template list specs assert the folder is passed.
  • Translations: not applicable, no new strings
  • Security Implications Contemplated: the inode lookup uses the existing contentlet endpoint with the user's own permissions, and the redirect only targets the internal /content-drive route.

Additional Info

  • No e2e tests on purpose: covering this end to end needs a user whose menu has Content Drive but not Content Search, and the e2e setup has no helper to build that role and menu layout. The flows were checked by hand against a local instance with that menu.
  • The e2e helpers stay as they are, though the issue lists them: the helpers that build /c/content?filter= URLs (contentListingNavigation.ts, listingContent.page.ts) drive the legacy listing as an admin who still has Content Search, so this guard never redirects them.

🤖 Generated with Claude Code

…ntent Drive (#37827)

When a user's menu has Content Drive but not Content Search or Site
Browser, MenuGuardService now sends their URLs to the Content Drive
equivalent instead of the first portlet:

- /c/content and /c/site-browser open Content Drive
- /c/content?filter=<variable> opens it filtered by that content type
- /c/content/<inode> looks the content up and opens it by identifier
  and language (?editContent=&editContentLang=)
- /c/content/new/<type> opens ?createContent=<type>

Edit and create links restore the CD_ params Content Drive adds when it
hands off to the legacy editor. Edit and create links that come from
Content Drive itself are not redirected, since Content Drive still sends
legacy-editor content to those URLs and redirecting would loop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@claude

claude Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @zJaaal's task in 2m 5s —— View job


PR Review

Re-reviewed after the fixes in 0936c76c. Verified against origin/main...HEAD.

Resolved

  • ✅ menu-guard.service.ts:78-95 — Double navigation after an error redirect is fixed. getContentDriveRedirect now distinguishes null ("no equivalent → first portlet") from false ("already handled → stop"). In canAccessPortlet, only result === null calls goToFirstPortlet(); a false from an error handler that already navigated (e.g. a 401 → login) returns without a second navigation. Covered by "should stop without a second navigation when the error handler already navigated".
  • ✅ menu-guard.service.ts:88 — Variant id not cleared on redirect is fixed. removeVariantId() now runs for any non-true result, so the redirect path clears it too. Covered by "should clear the variant when redirecting to Content Drive".
  • ✅ menu-guard.service.ts:245-253 — 200 with a null entity — accepted. getContentletByInode returns response.entity, so a 200 with entity: null would make the map destructure throw a TypeError, which catchError would then hand to handle() cast as an HttpErrorResponse. The author verified on a running instance that unknown and malformed inodes both return 404 (real HttpErrorResponse, catchError path), so this stays a latent edge case that depends on the endpoint's contract rather than a live bug.

New Issues

No new issues. Spot-checked the rest of the diff:

  • parseReplacedUrl — segment decomposition is correct for the listing/edit/create rows, and the rest.length / contentType-present guards fall through to the first portlet as intended.
  • getFolderPath — the //<host>/<path>/ regex, host-strip, current-site short-circuit, and site-switch-then-navigate all line up with the documented behavior; an unknown host / failed getSites correctly yields null (Content Drive at root).
  • dot-content-type-filter.component.ts — the base-type fill-in from #hostContentTypes and the "drop unknown content types only on a successful load" guard (loaded && #hostContentTypes().length < variables.length) are sound; the failed-load branch leaves the selection untouched.
  • dot-content-drive.store.ts — the untracked(...) wrap stops the init effect from re-running when the default language lands; currentSite ?? SYSTEM_HOST is redundant with initContentDrive's own fallback but harmless.
  • dot-router.service.ts / container + template lists — goToSiteBrowser(path) threading is correct and backward-compatible (no path → same navigate as before).

Looks good to merge from a review standpoint.
· issue-37827-content-drive-route-redirect

@zJaaal zJaaal changed the title fix(dotcms-ui): redirect Content Search and Site Browser routes to Content Drive (#37827) fix(dotcms-ui): redirect Content Search and Site Browser routes to Content Drive (#37759) Sep 30, 2026
@github-actions github-actions Bot added the Area : Frontend PR changes Angular/TypeScript frontend code label Sep 30, 2026
zJaaal and others added 2 commits September 30, 2026 14:20
… double navigation (#37759)

- Containers and templates stored as files now pass their folder to
  goToSiteBrowser(path). The menu guard turns //<host>/<folder>/ into
  Content Drive's ?path=, switching to that site first when it is not
  the current one. The legacy Site Browser still reads the folder from
  the session, so users who have it see no change.
- When the inode lookup fails and the error handler has already
  navigated (a 401 goes to login), the guard now stops instead of also
  going to the first portlet. Rejections with no Content Drive
  equivalent are told apart with null.
- The variant id is cleared on the Content Drive redirect too, not only
  when going to the first portlet.
- The loop protection now covers edit links only. Nothing reads
  createContent yet, so a legacy create from Content Drive stays in
  Content Drive instead of jumping to the first portlet.
- The Content Search filter goes over as contentType only, with no
  lookup: the Content Types chip now fills in the base type.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#37759)

The chip lists content types under their selected base type, so a link
that names only a content type (filters=contentType:Blog) filtered the
grid but left the chip empty. The chip now adds the base types of the
host's content types, resolved from the content types it already loads,
so no extra request. Content types the server does not return are
dropped from the host's selection after a successful load, so a deleted
type or a typo in an old link does not keep filtering with nothing to
clear.

Content Drive's init effect no longer tracks the default language: it
re-ran when the language landed and re-read the page's original URL
over any filter changed in between, including that cleanup.
loadDefaultLanguage already seeds the language into the current
filters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@zJaaal

zJaaal commented Sep 30, 2026

Copy link
Copy Markdown
Member Author

Replying to the review above (#37829 (comment)):

  • High, double navigation after an error redirect: fixed in 0936c76. The redirect helper now returns null for "no Content Drive equivalent, go to the first portlet" and false only for "already handled, stop", so a 401 that sent the user to login no longer also fires goToFirstPortlet. New test: should stop without a second navigation when the error handler already navigated.
  • Medium, variant id not cleared on the redirect: fixed in 0936c76. The variant id is now cleared whenever the requested portlet is rejected, redirect included. New test: should clear the variant when redirecting to Content Drive.
  • Medium, 200 with a null entity: no change. Checked against a running instance: both an unknown inode (00000000-0000-0000-0000-000000000000) and a malformed one return 404 from GET /api/v1/content/<inode>, so they take the catchError path with a real HttpErrorResponse.

Written by Claude Code on behalf of @zJaaal.

- menu-guard spec: the guard result starts undefined until the guard emits.
- container list spec: narrow the file container found in the mock list.
- Content Drive store: pass SYSTEM_HOST while the site has not loaded,
  as initContentDrive already falls back to.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dario-daza
dario-daza added this pull request to stack #37833 September 30, 2026 18:48
dario-daza added a commit that referenced this pull request Sep 30, 2026
…ith the stack (#37759)

- Follow the stack's numbering: route redirects (#37829) are Part 1, the
  legacy-editor side panel is Part 2.
- Renumber the functional requirements so they increase in document order.
- State the create location once: the folder Content Drive is showing, the
  `path` of a `createContent` link, or the site root.
- Record that a workflow wizard action reports a save like a plain save
  (checked in edit_contentlet_js_inc.jsp), and add the rejected-save case.
- Align with what #37829 implemented: the redirect keeps folder and filters,
  CD_ params are added on every legacy hand-off until this work, and page
  creates follow the same create flow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dario-daza
dario-daza removed this pull request from stack #37833 September 30, 2026 19:59
@zJaaal

zJaaal commented Sep 30, 2026

Copy link
Copy Markdown
Member Author

@dario-daza will take over the rest of the work on #37832

@zJaaal zJaaal closed this Sep 30, 2026
dario-daza added a commit that referenced this pull request Sep 30, 2026
…l spec

The stack with #37829 was closed and its commits brought into this branch,
so the spec now says Part 1 is already on the branch and Part 2's
implementation follows in the same PR.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area : Frontend PR changes Angular/TypeScript frontend code

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Content Drive: replace Content Search and Site Browser (legacy editor in the side panel, route redirects)

1 participant