Skip to content

fix(content-import): validate Content Type before streaming sample CSV - #37535

Queued
Neehakethi wants to merge 2 commits into
mainfrom
issue-37461-import-content-csv-sample-download
Queued

Neehakethi wants to merge 2 commits into
mainfrom
issue-37461-import-content-csv-sample-download

Conversation

@Neehakethi

@Neehakethi Neehakethi commented Sep 14, 2026

Copy link
Copy Markdown
Member

Fixes #37461

Proposed Changes

  • Guard ImportContentletsAction._downloadCSVTemplate so a missing Content Type re-renders the Import Content screen with the existing structure-type-is-required message instead of throwing after the download headers are committed. The field lookup now happens before any header is set, so a failed lookup can no longer leave the response flagged as an attachment with an empty error body.
  • Validate the Content Type client-side in import_contentlets.jsp#downloadCSVExample() and show "A Content Type is required" via showDotCMSSystemMessage, so the form never submits an empty structure.

Root cause

The link is not dead — it breaks only when no Content Type is selected, which is the screen's default state. In that state the form POSTs structure=""; the action set Content-Type: application/octet-stream + Content-Disposition: attachment and opened the output stream first, then FieldsCache.getFieldsByStructureInode("")ContentTypeAPI.find("") threw. The result was an HTTP 500 with attachment headers and an empty body, which Chrome renders inside the portlet iframe as "webpage might be temporarily down or it may have moved permanently" — the error page in the issue screenshots. With a Content Type selected the download worked all along (verified on demo.dotcms.com 26.09.03-01: valid structure → 200 + correct CSV; empty structure → 500 + attachment headers + empty body).

The fix mirrors the validation the preview branch of the same processAction already applies, reusing its existing i18n key (translations already present).

Checklist

  • Tests — no automated test added: ImportContentletsAction has no existing test coverage and no portlet-action test harness; flagged as a known gap. Verified by reproduction (below).
  • Translations — reuses the existing structure-type-is-required key (Language.properties), no new keys.
  • Security Implications Contemplated — no new surface; the change adds input validation to an existing authenticated portlet action.

Additional Info

Verified on a local build from this branch (empty starter):

  • The rendered Import Content screen contains the new client-side guard.
  • The exact request that previously produced the broken response (cmd=downloadCSVTemplate with empty structure) now returns HTTP 200 re-rendering the portlet, with no Content-Disposition header.
  • With a valid structure, CSV_Template.csv still downloads with the correct header row for the selected type.
  • Regression: the preview validation path (whose pattern this reuses) behaves as before.

The issue floated reimplementing the download as a REST endpoint (e.g. GET /v1/content/_import/_template). Deliberately out of scope here — the legacy endpoint works once validated; a REST template endpoint can be a follow-up if wanted.

Screenshots

Original Updated
Chrome error page inside the portlet iframe (see issue #37461 screenshots) "A Content Type is required" system message; screen stays put. With a type selected, CSV_Template.csv downloads.

Before : image

After :
image

@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Neehakethi's task in 52s —— View job


Code Review

Reviewed the diff against origin/main. Both the empty-selection guard and the deleted-type DotStateException catch are correct.

Verified:

  • FieldsCache.getFieldsByStructureInode (FieldsCache.java:28) wraps DotStateException | DotDataException | DotSecurityException into DotStateException, so catching DotStateException covers the "inode set but Content Type no longer exists" case (NotFoundInDbExceptionDotDataException → rewrapped). The catch is placed before the download headers are committed, so no more attachment-headers-with-empty-body 500.
  • The reordering keeps fields resolution ahead of setContentType/Content-Disposition, which is the actual root-cause fix. final List<Field> fields; assigned in both the try and each early-return path is sound.
  • Both i18n keys (structure-type-is-required, Workflow-does-not-exists-content-type) exist in Language.properties — no new keys, as claimed.
  • The JSP guard at import_contentlets.jsp:130 uses dijit.byId("structuresSelect").attr('value'), consistent with the existing calls at lines 67/375, and returns early with a system message before building the action URL.

New Issues

No issues found.

Two non-blocking observations (not defects):

  • 🟡 Minor: ImportContentletsAction.java:394 — reusing the Workflow-does-not-exists-content-type key here is semantically a slight stretch (it's a workflow-domain key), though its rendered text ("Content Type does not exist") reads correctly in this context. Fine to keep; a dedicated import-scoped key would be marginally cleaner.
  • The PR notes no automated test was added because ImportContentletsAction has no test harness. Reasonable given the legacy Struts portlet-action surface; the client-side + server-side double guard and manual reproduction cover the reported failure.

Matches the approved review — the reviewer's request to also validate that the selected Content Type exists (not just that structure is non-empty) is handled by the DotStateException catch. LGTM.
· issue-37461-import-content-csv-sample-download

@Neehakethi
Neehakethi force-pushed the issue-37461-import-content-csv-sample-download branch from e153318 to a593a0f Compare September 17, 2026 17:20
#37461)

Clicking "download a CSV sample file" with no Content Type selected threw
after the attachment headers were committed, returning an empty 500 the
browser rendered as an error page. Guard the action (reusing the preview
branch's structure-type-is-required message) and validate client-side
before submitting.

Refs: #37461
… no longer exists (#37461)

Addresses PR review feedback: a set-but-nonexistent structure inode (type
deleted after the screen loaded, or a crafted request) made the fields
lookup throw to an error page. Catch the lookup failure and re-render the
import screen with the existing Content-Type-does-not-exist message.

Refs: #37461
@Neehakethi
Neehakethi force-pushed the issue-37461-import-content-csv-sample-download branch from a593a0f to bdea283 Compare September 17, 2026 17:27
@Neehakethi
Neehakethi added this pull request to the merge queue Sep 17, 2026
Any commits made after this event will not be merged.
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 17, 2026
@Neehakethi
Neehakethi added this pull request to the merge queue Sep 17, 2026
Any commits made after this event will not be merged.
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: In Progress

Development

Successfully merging this pull request may close these issues.

Import Content: "Click here to download a CSV sample file" link is broken - no sample CSV can be downloaded

2 participants