UVE: navigation active state lost after in-editor navigation — isActive() fails on folder-style Page API URLs with no page segment
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 970
- Forks
- 486
- Avg merge
- 3d 33m
- Merged PRs (30d)
- 170
Description
Problem Statement
NavResultHydrated.isActive() determines the "current parent path" by chopping the request URI at the last slash:
// dotCMS/src/main/java/com/dotcms/rendering/velocity/viewtools/navigation/NavResultHydrated.java:44-69
String reqURI = req.getRequestURI().replace("/api/v1/page/render", "");
String parentPath = reqURI.substring(0, reqURI.lastIndexOf("/"));
if (!parentPath.endsWith("/")) {
parentPath = parentPath + "/";
}
if (isFolder() && !navResult.getHref().endsWith("/")) {
String tempHref = navResult.getHref() + "/";
return parentPath.startsWith(tempHref);
} else {
return !isCodeLink() && navResult.getHref().equalsIgnoreCase(reqURI);
}
This assumes the request URI always ends in a page name. When the Page API renders a folder-style URL with no page segment, lastIndexOf("/") is 0, parentPath becomes the empty string and is then normalized to /. Nothing can match, so every nav item returns active == false. The page branch fails identically: a nav item whose href is /TravelHub/index never equals the URI /TravelHub.
| Request URI | parentPath |
tempHref for folder /TravelHub |
isActive() |
|---|---|---|---|
/api/v1/page/render/TravelHub/index |
/TravelHub/ |
/TravelHub/ |
true ✅ |
/api/v1/page/render/TravelHub |
/ |
/TravelHub/ |
false ❌ |
/api/v1/page/render/Partners |
/ |
/Partners/ |
false ❌ |
Why this only breaks in the editor. On the front end, CMSFilter 301-redirects a folder URL without a trailing slash and then appends CMS_INDEX_PAGE, so Velocity always sees /folder/index (CMSFilter.java:127-138). The Page API (/api/v1/page/render/<url>) is a JAX-RS resource and applies no such normalization. UVE navigates in-editor using the hrefs it gets from $navtool, which are folder identifier URIs with no /index — so the first load (opened from the Site Browser, which targets the page asset) is correct, and every navigation after that silently loses all active state.
Impact. Any custom navigation that keys off $nav.active — a very common Velocity pattern for marking the current section and expanding its children — renders collapsed and, depending on the theme CSS, completely empty in UVE. There is no error in the logs, in the console, or in the editor. The nav data itself is correct ($navtool.getNav(0) returns its children normally), so the failure looks nothing like a navigation problem and is extremely hard to diagnose. Once triggered it persists for the rest of the editing session, because every subsequent in-editor navigation also uses folder-style hrefs.
Prior art. Issue #17896 (closed 2020, commit a6ee51fe71) fixed the adjacent half of this — it added the .replace("/api/v1/page/render", "") prefix strip so that menu selection stayed active under the Page API. It never handled the case where the resulting URI has no page segment. This is the same defect class resurfacing under UVE.
Browser & OS: not browser-specific — the defect is server-side and reproduces wherever the Page API renders a folder-style URL.
Steps to Reproduce
- On any site, create a folder (e.g.
/TravelHub) containing anindexpage, with child pages set to show on menu. - Add a Velocity navigation snippet to the template that keys off the active flag, e.g.:
#set($list = $navtool.getNav(0)) #foreach($n in $list) <li class="#if($n.active)active visible#end">$n.title</li> #end - Open
/TravelHub/indexin the Universal Visual Editor from the Site Browser. The correct item is markedactive— the request URI is/api/v1/page/render/TravelHub/index. - Click any internal navigation link in the rendered page so UVE navigates in-editor.
- Observe that no item is marked
active— the request URI is now/api/v1/page/render/TravelHub(no/index). - Navigate back to the original page. The active state does not return.
- View the same page on the published site — the active state is correct, because
CMSFilternormalizes the URL to/TravelHub/index.
Expected: a folder-style URL resolves the same active nav item as the explicit index-page URL, in the editor exactly as it does on the front end.
Actual: every nav item reports active == false for the remainder of the editing session.
Confirmed by the reporting customer with in-template debug output — site, host identifier and $navtool.getNav(0) children were byte-identical between the working and broken renders, with requestURI the only variable that changed.
Acceptance Criteria
-
NavResultHydrated.isActive()returns the same result for/api/v1/page/render/<folder>as it does for/api/v1/page/render/<folder>/index -
parentPathis never computed as an empty string for a valid page URL - The folder branch and the page branch both resolve correctly when the URI has no page segment (a nav item whose href is
/folder/indexis active when the URI is/folder) - Active state resolved through the Page API matches the active state the front end resolves for the equivalent URL
- Navigating between pages in UVE preserves the active/expanded state of navigation menus built on
$nav.active, and returning to the original page restores its original state - Site-root URIs (
/api/v1/page/render/and/api/v1/page/render/index) neither throw nor mark unrelated items active - The
/api/v1/page/renderprefix strip added in #17896 continues to work — no regression to that fix - Unit tests cover:
/folder/index,/folder,/folder/, nested/a/b/c, site root, and a URL-mapped page
dotCMS Version
Reported on Current Release (dotEvergreen), dotCMS Cloud. The same symptom was reported by the same customer a year earlier on 24.12.27 LTS. The isActive() logic is unchanged on main as of 2026-08-18 — verified against the upstream file, so this is still open on latest.
Severity
High - Major functionality broken
Links
- Freshdesk ticket 38763 — current report (Evergreen, staging)
- Freshdesk ticket 32533 — same customer, same symptom, 24.12.27 LTS. Closed by rewriting the customer's template and recommending Professional Services; the underlying core defect was never identified.
- #17896 — earlier fix to the same method that handled the API prefix but not the missing page segment
- #36141 — separate UVE defect from the same investigation area (duplicate
document.writere-executing inline scripts)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in dotCMS/src/main/java/com/dotcms/rendering/velocity/viewtools/navigation/NavResultHydrated.java, focusing on NavResultHydrated.isActive() and its request-URI handling. Review or run the existing navigation tests, then add coverage for folder, nested, root, and URL-mapped Page API paths. Done means folder-style and explicit index URLs produce equivalent active states without regressions to the existing prefix handling.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- backend-api-design
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100