Skip to content

fix(push-publishing): don't fail bundle on archived multi-language HTML pages (#36051)#36380

Merged
dsilvam merged 3 commits into
mainfrom
issue-36051-archived-multilang-page-pp
Jul 3, 2026
Merged

fix(push-publishing): don't fail bundle on archived multi-language HTML pages (#36051)#36380
dsilvam merged 3 commits into
mainfrom
issue-36051-archived-multilang-page-pp

Conversation

@dsilvam

@dsilvam dsilvam commented Jul 1, 2026

Copy link
Copy Markdown
Member

Proposed Changes

  • Fix ESContentletAPIImpl.updateTemplateInAllLanguageVersions so it no longer throws DotDataException: Contentlet is currently marked as 'Archived'. when the only existing versions of an HTML page identifier are archived. On the push-publishing receiver, language versions of the same page are processed sequentially — once the first version is saved and archived, checking in the second version found only the archived first version and threw, aborting the entire bundle. Template propagation is meaningless for archived pages, so the method now logs a warning and returns (mirroring the sibling NotFoundInDbException path already swallowed at the call site) instead of throwing.
  • Add an early return when the incoming contentlet is itself archived (defensive guard for the direct-checkin path).
  • Add integration test PublisherTest#testPushArchivedMultiLanguageHTMLPage that push-publishes an archived English + Spanish HTML page and asserts the bundle succeeds and both language versions are archived on the receiver.

Fixes #36051

Checklist

  • Tests
  • Translations
  • Security Implications Contemplated (add notes if applicable)

Additional Info

Root cause: updateTemplateInAllLanguageVersions runs on every HTML-page checkin (only does work when the content type has a TEMPLATE_FIELD). It first searches for a non-archived version of the identifier; when none exists it retries with includeArchived=true. If that finds an archived version it used to throw — but unlike NotFoundInDbException, the DotDataException was not caught in internalCheckin, so it propagated up through ContentHandler.saveContent and failed the whole bundle. This commonly surfaces when push-publishing an entire Site containing archived multi-language pages.

The method is private with a single caller (internalCheckin), so removing the throw affects no external callers.

🤖 Generated with Claude Code

This PR fixes: #36051

…ML pages (#36051)

updateTemplateInAllLanguageVersions threw a DotDataException when the only
existing versions of an HTML page identifier were archived. On the push
publishing receiver, language versions are processed sequentially: once the
first version is saved and archived, checking in the second version found only
the archived first version and threw, aborting the entire bundle. Template
propagation is meaningless for archived pages, so skip it (log + return)
instead of throwing, matching the sibling NotFoundInDbException handling.

Adds PublisherTest#testPushArchivedMultiLanguageHTMLPage covering an archived
English + Spanish HTML page pushed to the receiver.

Refs: #36051

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Claude finished @dsilvam's task in 13m 5s —— View job


🔍 dotCMS Backend Review

  • Load dotCMS conventions (CLAUDE.md, JAVA_STANDARDS, DATABASE_PATTERNS, REST_API_PATTERNS, SECURITY_PRINCIPLES)
  • Get Java diff (5d458ee9...d1d6b462, i.e. origin/main...HEAD for *.java)
  • Spawn 4 specialized sub-agents (Security, Database, Java Standards, REST API)
  • Consolidate findings
  • Post consolidated review comment

No issues found.

All four sub-agents reviewed the diff (ESContentletAPIImpl.java +9/-3, plus the two integration test files) against their respective checklists and returned no findings:

  • Security: The two new early returns in updateTemplateInAllLanguageVersions don't touch permission checks, SQL, file/system calls, or logging of sensitive data — they only short-circuit template propagation for archived content, before any existing authorization logic in the call chain runs.
  • Database: Both early returns occur before any DB write in this method (the write/checkout loop is further down), so nothing is left partially applied and no new cache-invalidation gap is introduced. The archived-check reuses the same cache-backed ContentletVersionInfo lookup pattern as the pre-existing branch right below it.
  • Java Standards: New code uses existing valid APIs (Logger.warn(Class, String), Contentlet.isArchived()), no raw types, no System.out/System.getProperty, no direct API/Factory instantiation. Confirmed the pre-existing contentletByIdentifierAnyLanguageArchived.isArchived() branch is not made dead code by the new early guard — it still covers the distinct case where the incoming contentlet itself is live but every other language version is archived.
  • REST API: No @Path resource classes are touched by this diff; the modified method is private with a single internal caller (internalCheckin), so there's no REST-facing surface to review.

Note on process: I'm unable to submit a formal GitHub PR review (approve/request-changes) — that's outside what I'm permitted to do. This comment is the full review output; no Critical/High findings means there's nothing blocking merge from this pass.

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🤖 dotBot Review (Bedrock)

Reviewed 3 file(s); 1 candidate(s) → 0 confirmed, 0 uncertain (unverified, kept for review).

✅ No issues found after verification.


us.deepseek.r1-v1:0 · Run: #28658856176 · tokens: in: 10839 · out: 3514 · total: 14353 · calls: 4 · est. ~$0.034

@claude

claude Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🔍 dotCMS Backend Review

[🟡 Medium] dotCMS/src/main/java/com/dotcms/content/elasticsearch/business/ESContentletAPIImpl.java:6833

The else { return; } branch after the isArchived() check has no comment and is structurally misleading. When findContentletByIdentifierAnyLanguage(id, true) returns a non-null contentlet that is not archived (possible when the variant-scoped earlier call missed a live version in another language), this branch silently skips template propagation without logging — making it indistinguishable from the archived path on the outside. Both branches return but only one logs, so the control flow looks like a bug where a case was forgotten.

} else if (contentletByIdentifierAnyLanguageArchived.isArchived()) {
    Logger.warn(ESContentletAPIImpl.class, String.format(
            "Skipping template propagation: all existing versions of Contentlet"
                    + " '%s' are archived.", contentlet.getIdentifier()));
    return;
} else {
    return;   // silent return — no explanation, no log
}

💡 Collapse the two returns and guard only the log on isArchived():

if (contentletByIdentifierAnyLanguageArchived.isArchived()) {
    Logger.warn(ESContentletAPIImpl.class, String.format(
            "Skipping template propagation: all existing versions of Contentlet"
                    + " '%s' are archived.", contentlet.getIdentifier()));
}
// Both archived and unrelated-live fallback: nothing to propagate.
return;

dotBot finding at line 6829 — dismissed (false positive)

dotBot flagged "2 placeholders but only 1 argument" — the actual format string "Skipping template propagation: all existing versions of Contentlet '%s' are archived." has exactly one %s and exactly one argument (contentlet.getIdentifier()). No format mismatch exists.


Next steps

  • 🟡 You can ask me to handle the mechanical simplification: @claude fix the else-return dead branch in ESContentletAPIImpl.java
  • Every new push triggers a fresh review automatically

@dsilvam dsilvam enabled auto-merge July 3, 2026 11:49
…hrow behavior (#36051)

testCheckInArchivedPageShouldThrowDotDataException asserted the old contract
where checking in a new version of an archived HTML page threw a
DotDataException from updateTemplateInAllLanguageVersions. The #36051 fix makes
that path skip template propagation and return instead of throwing, so the test
is renamed to testCheckInArchivedPageSkipsTemplatePropagation and now asserts
the check-in succeeds (and still never NPEs). This test exercises the archived
incoming-contentlet early-return.

Refs: #36051

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dsilvam dsilvam added this pull request to the merge queue Jul 3, 2026
@mergify

mergify Bot commented Jul 3, 2026

Copy link
Copy Markdown

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

Merged via the queue into main with commit 127f274 Jul 3, 2026
66 checks passed
@dsilvam dsilvam deleted the issue-36051-archived-multilang-page-pp branch July 3, 2026 13:51
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.

Push publishing archived HTML pages with multiple language versions fails on receiver

3 participants