fix(content-drive): truncate long-text field values in listing rows (#37185) - #37396
fix(content-drive): truncate long-text field values in listing rows (#37185)#37396ihoffmann-dot wants to merge 6 commits into
Conversation
Red confirmed: LongTextPreviewStrategy/TransformOptions.LONG_TEXT_PREVIEW don't exist yet, so the test class doesn't compile.
…37185) Adds TransformOptions.LONG_TEXT_PREVIEW (declared after STORY_BLOCK_VIEW/ JSON_VIEW so EnumSet order runs it last) backed by LongTextPreviewStrategy, which replaces WYSIWYG/TextArea/Story Block values with a <=150-char extracted plain-text preview. Wired opt-in only at BrowserAPIImpl#dotContentMap via a new DotTransformerBuilder#longTextPreview() chain method -- never added to defaultOptions, so no other transformer consumer is affected.
…preview (#37185, AC-008) If a content type's title-source field is itself WYSIWYG/TextArea (its variable is literally 'title'), LongTextPreviewStrategy would match it by field type and clobber the value COMMON_PROPS already populated from Contentlet#getTitle() with a truncated/HTML-stripped preview. Skip the 'title' key explicitly.
…w trim (#37185) T030-T033: generic-Content row shape from getPaginatedContents (Drive) and getFolderContent (Site Browser) both carry AC-002's required keys and reduced long-text values; Show In List (AC-003) renders a readable preview; a WYSIWYG title-source field (AC-008) keeps an untruncated title. Also: AC-005 Schema description update on ContentDriveResource#search (endpoint is @hidden, no openapi.yaml regen needed), and AC-006 Postman fix removing the dead item.body read (a listing row never carried that key) in favor of an assertion that actually runs.
|
Claude finished @ihoffmann-dot's task in 2m 27s —— View job Code Review — long-text preview for Content Drive listings
I verified the core correctness claim end-to-end: New Issues
Everything else checks out:
No blocking issues. The one Medium is non-blocking and may already be a no-op depending on |
…gyTest/BrowserAPITest (#37185) - defaultOptions_neverIncludesLongTextPreview referenced DotContentletTransformerImpl.defaultOptions directly across packages (...transform vs. this test's ...transform.strategy) -- the field is package-private, so it doesn't compile. Read it via reflection instead. - The wysiwygTitleField test's long title HTML (400+ chars) exceeded the contentlet.title column's varchar(255) limit. Reduced while keeping the stripped plain text well over the 150-char preview bound. - REQUIRED_LISTING_KEYS listed the actual icon key as '__icon__' (it's 'icon') and included mimeType/extension, which are File Asset-specific and legitimately absent on a generic-Content row.
…ate-pair split (#37185) Found in code review (claude[bot] on PR #37396): - collectText walked the entire story-block tree before truncate() threw away everything past 150 chars -- full-payload work on the exact path this feature exists to keep cheap. Short-circuit once enough text is collected. - truncate() could split a UTF-16 surrogate pair (emoji, some CJK) at the 150-char boundary, leaving a lone high surrogate. Back off one char when the boundary char is a high surrogate.
|
dotbot code review:
Opt-in LONG_TEXT_PREVIEW correctly runs last via enum ordinal order, is isolated to dotContentMap, and handles HTML/Story Block truncation with title-key protection and surrogate safety. No P0/P1 defect with concrete repo evidence was found. Tip: comment with "/dotbot address comments" to attempt automated fixes for unresolved review threads. reviewed by dotbot · meta/muse-spark-1.3 · medium |
| // the content type's title-source field is itself WYSIWYG/TextArea/Story Block. | ||
| .filter(field -> !TITTLE_KEY.equals(field.variable())) | ||
| .forEach(field -> Try.run(() -> | ||
| map.put(field.variable(), extractor.apply(map.get(field.variable())))) |
There was a problem hiding this comment.
LongTextPreviewStrategy.java:74 absent field keys get inserted as empty strings
Current code:
fields.stream()
.filter(field -> !TITTLE_KEY.equals(field.variable()))
.forEach(field -> Try.run(() ->
map.put(field.variable(), extractor.apply(map.get(field.variable()))))Problem: Fields absent from the map (unset value) get a new "" key inserted, changing row shape.
Fix:
.forEach(field -> Try.run(() -> {
if (map.containsKey(field.variable())) {
map.put(field.variable(), extractor.apply(map.get(field.variable())));
}
}))What to verify: a Content Type with an empty WYSIWYG/Story Block field whose key is absent from Contentlet#getMap(); the listing row currently gains key: "".
|
dotbot code review:
The opt-in LONG_TEXT_PREVIEW strategy is correctly ordered last via enum ordinal, isolated to the dotContentMap call site, and handles HTML/Story Block truncation, the title-key collision, and surrogate pairs safely. The only residual issue is a low-severity possible empty-string key insertion for unset fields. Tip: comment with "/dotbot address comments" to attempt automated fixes for unresolved review threads. reviewed by dotbot · ~z-ai/glm-latest · medium |
Summary
TransformOptions.LONG_TEXT_PREVIEW(declared afterSTORY_BLOCK_VIEW/JSON_VIEWsoEnumSetiteration order runs it last) backed by a newLongTextPreviewStrategy.BrowserAPIImpl#dotContentMapvia a newDotTransformerBuilder#longTextPreview()chain method — never added todefaultOptions, so no other transformer consumer (Content Editor,ContentResource, GraphQL, asset picker) is affected (AC-007).title), the strategy would have overwritten the already-correct, untruncated titleCOMMON_PROPScomputes. Fixed by explicitly skipping thetitlekey.@Schemadescription onContentDriveResource#search(AC-005; endpoint is@Hidden, noopenapi.yamlregen needed) and removes a deaditem.bodyassertion in the Postman collection (AC-006, a listing row never carried that key).Test plan
./mvnw test -pl :dotcms-core -Dtest=LongTextPreviewStrategyTestjust test-integration-ide./mvnw verify -pl :dotcms-integration -Dcoreit.test.skip=false -Dit.test=BrowserAPITest./mvnw verify -pl :dotcms-postman -Dpostman.test.skip=false -Dpostman.collections=ContentDriveResourceSystem.out/System.getProperty/System.getenvintroduced (checked via diff)Branched off the approved spec branch per this repo's Spec-Kit flow (spec.md-only in PR1, not merged to
mainyet).🤖 Generated with Claude Code
This PR fixes: #37185
Verification (2026-09-04, local)
LongTextPreviewStrategyTest) pass.BrowserAPITest) pass.ContentDriveResourcecollection) pass.Bugs found and fixed in the test code along the way (not the production fix): a package-private field access across packages (fixed via reflection), a test title exceeding the
contentlet.titlecolumn'svarchar(255)limit, an incorrect expected key name (__icon__vs. the realicon) plus two File-Asset-only keys (mimeType/extension) wrongly expected on a generic-Content row.