Skip to content

fix(browser): resolve application/dotpage MIME filter to the HTMLPAGE base type (#36916) - #37032

Open
ihoffmann-dot wants to merge 1 commit into
mainfrom
issue-36916-redirect-page-selector-dotpage
Open

fix(browser): resolve application/dotpage MIME filter to the HTMLPAGE base type (#36916)#37032
ihoffmann-dot wants to merge 1 commit into
mainfrom
issue-36916-redirect-page-selector-dotpage

Conversation

@ihoffmann-dot

Copy link
Copy Markdown
Member

Fixes #36916

Problem

The legacy "Select a file" / "Select link" dialog used to pick a redirect target page from a Page's Properties returned zero results for every folder — "No files found" even for folders that visibly contain pages. BrowserAjax.getFolderContentWithDotAssets(...) came back as {total: 0, list: []}.

application/dotpage is a synthetic, display-time-only MIME type. It is stamped onto a page's view map in memory (PageViewStrategy, ContentletAjax, BrowserAjax) and is never persisted to contentlet_as_json. The SQL MIME predicate added in #34217 tests for an asset-metadata contentType, so it matched zero HTMLPAGE rows and every page was filtered out in SQL before it could be transformed.

Regression origin: f4ff8039"feat(Content Drive) #34205: Support mimeTypes filtering in /api/v1/drive/search endpoint" (#34217). That change targeted the new Content Drive endpoint but landed in the shared selectQuery(...) path the legacy DWR browser also uses.

Fix

BrowserAPIImpl.appendMIMETypeQuery(...) now routes each requested MIME type to the only clause that can actually match it, OR'd together inside the existing AND (...) wrapper:

  • exact application/dotpagestruc.structuretype = BaseContentType.HTMLPAGE.getType()
  • anything else → the existing jsonb_path_exists(...) asset-metadata check

struc is already joined by buildSelectBaseQuery(...), so this needs no new join, no new bound parameter and no caller change.

Why not simply exempt HTMLPAGE rows from the predicate

That was the spec's original idea and it was rejected during Phase 0 research. The in-memory filterReturnList(...) pass runs on only one of the three consumers of getContentUnderParentFromDB(...) — Content Drive (/api/v1/drive/search) and getFolderContentList(...) have no in-memory MIME filter at all. Exempting pages in SQL would have leaked pages into image/jpeg browses on Content Drive, a new regression. The per-MIME translation is correct on all three paths and is a strictly smaller behavioural change.

Generated SQL per request shape

mimeTypes Emitted fragment
["application/dotpage"] AND (struc.structuretype = 5)
["image/jpeg"] AND (META(image/jpeg)) — unchanged
["application/dotpage","image/jpeg"] AND (struc.structuretype = 5 OR META(image/jpeg))
["image/jpeg","application/pdf"] AND (META(image/jpeg) OR META(application/pdf)) — unchanged

For any request that carries no application/dotpage, the emitted fragment is byte-identical to before this PR. The 5 is emitted from the BaseContentType.HTMLPAGE enum, never written as a literal. The match on the synthetic value is exact, so application/dotpage-foo still takes the metadata branch.

Testing

TDD, Red confirmed before any implementation. The Red run failed with exactly the expected signature — 5 failures, all and only the application/dotpage cases, every one an AssertionError on an empty result set:

BrowserAPITest.test_getFolderContentList_dotPageMimeType_returnsPages
  AssertionError: Pages must be returned for an 'application/dotpage' browse, but got: []
BrowserAjaxTest.test_getFolderContentWithDotAssets_dotPageMimeType_returnsPages
  AssertionError: The dialog must report at least one item ... but reported: 0

The real-MIME regression guards were green before and after.

After the fix: BrowserAPITest 40/40, BrowserAjaxTest 7/7.

./mvnw verify -pl :dotcms-core,:dotcms-integration -Dcoreit.test.skip=false -Dit.test=BrowserAPITest,BrowserAjaxTest -Dtest=NumberUtilTest -DfailIfNoTests=false

New coverage:

  • BrowserAPITestapplication/dotpage returns pages; image/jpeg returns matching file assets and no pages; ["image/jpeg","application/pdf"] unchanged; application/dotpage-foo not routed to base type; mixed request returns both; MIME order independence; nested sub-folder; language + default-language fallback.
  • BrowserAjaxTest — the legacy DWR entry point the dialog actually calls.
  • Browser_Resource Postman collection — two new requests against POST /api/v1/browser (it had no MIME coverage at all). ContentDriveResource already covers MIME filtering and is untouched.

Postman not run locally. Both collections aborted on their first setup request with Timeout connecting to [es/172.17.0.3:9200] — the OpenSearch container died mid-run from Docker memory pressure on the dev machine. No browse request executed, so nothing about this change was exercised either way. They need to go green in CI before merge.

Manual confirmation still pending: selecting a page in the dialog and saving it as a redirect target, and comparing the dialog listing against the Site Browser for the same folder.

Risk

Rollback-safe: no DB schema change, no ES mapping change, no REST/API contract change, no @Schema change, no openapi.yaml regeneration, no data migration or reindex. One private method in one class.

Out of scope

Flagged during planning, deliberately not fixed here:

  • appendMIMETypeQuery emits PostgreSQL-only jsonb_path_exists with no dialect branch — on MS SQL any MIME-filtered browse fails outright. Pre-existing, separate issue.
  • Caller-supplied MIME strings are interpolated into the SQL string rather than bound. Pre-existing from feat(Content Drive) #34205 : Support mimeTypes filtering in /api/v1/drive/search endpoint #34217, unchanged here.
  • The PURE_ES path builds its own +mimeType:(...) clause and has the same synthetic-MIME blind spot if it ever becomes the default heuristic. Not the default today.
  • The three legacy application/dotpage string literals are left in place; only the modern class gets a constant.

ADR alignment

ADR-0018 (Database-First Search for Content Drive, proposed) assigns MIME-type resolution to the DB via jsonb_path_exists, scoped to file assets. This PR implements that scoping rather than deviating from it: MIME stays DB-resolved, and the asset-metadata check simply stops being applied to rows that structurally cannot carry asset metadata. Nothing moves to the index.

🤖 Generated with Claude Code

@claude

claude Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ihoffmann-dot's task in 2m 0s —— View job


SDK Compatibility Analysis

  • Read SDK breaking-change categories reference (docs/core/SDK_BREAKING_CHANGE_CATEGORIES.md)
  • Get full PR diff
  • Analyze diff against each category (G-1/G-2/G-3, R-1, U-1/U-2, H-1)
  • Conclusion

No SDK breaking change detected.

The only production-code change is in BrowserAPIImpl.appendMIMETypeQuery(...) (dotCMS/src/main/java/com/dotcms/browser/BrowserAPIImpl.java), which alters the SQL WHERE clause used by the legacy DWR "Select a file/link" dialog (BrowserAjax) and the /api/v1/browser admin-UI browse endpoint to correctly route the synthetic application/dotpage MIME type to a struc.structuretype = 5 condition instead of a jsonb_path_exists(...) metadata check that could never match it.

Checked against every category in the reference doc:

  • G-1/G-2/G-3 (GraphQL page/content surface) — not touched; no changes to graphql.page/graphql.content schema, resolvers, or error extensions.code.
  • R-1 (REST response shape) — not applicable; the endpoints in scope for this category are /api/v1/nav, /api/v1/content, /api/v1/page/*. This change touches /api/v1/browser (legacy Site Browser / redirect-page-picker dialog) and, incidentally, the shared query path also used by Content Drive's /api/v1/drive/search — neither is consumed by @dotcms/client, @dotcms/react, @dotcms/angular, or @dotcms/uve. The change also only affects which rows match the filter, not the JSON envelope/field shape returned.
  • U-1/U-2 (postMessage protocol) — not touched.
  • H-1 (version headers/compareVersions()) — not touched.

The remaining changed files (BrowserAPITest.java, BrowserAjaxTest.java, Browser_Resource.postman_collection.json) are test/Postman-only additions, explicitly non-breaking per the reference doc's calibration list.

No comment or label added, per instructions for non-breaking changes.

@ihoffmann-dot
ihoffmann-dot force-pushed the issue-36916-redirect-page-selector-dotpage branch from 06749ef to ed97477 Compare August 12, 2026 03:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI: Safe To Rollback Area : Backend PR changes Java/Maven backend code

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Redirect target page selector ("Select a file" dialog) shows all folders empty — application/dotpage mimeType filter excludes all pages

1 participant