Skip to content

feat(content-search): add date range filters for date/datetime fields#35745

Open
dsilvam wants to merge 3 commits into
mainfrom
issue-33724-date-range-filter-content-search
Open

feat(content-search): add date range filters for date/datetime fields#35745
dsilvam wants to merge 3 commits into
mainfrom
issue-33724-date-range-filter-content-search

Conversation

@dsilvam
Copy link
Copy Markdown
Member

@dsilvam dsilvam commented May 19, 2026

Summary

  • Replaces free-text Lucene date inputs in the Content Search portlet with proper FROM / TO date picker pairs (Dojo dijit.form.DateTextBox) for each date and date_time field in the selected content type
  • Pickers automatically build a Lucene range value ([YYYYMMDD000000 TO YYYYMMDD235959]) stored in the existing hidden field consumed by ContentletAjax — no backend changes required
  • clearSearch() now resets the date pickers alongside other fields
  • Removed the ? Lucene-syntax tooltip from date/date_time dynamic fields since the pickers make it redundant
  • Added date-from / date-to i18n keys to Language.properties

Test plan

  • Navigate to Content → Search and select a content type that has date or date_time fields marked as Searchable + Indexed
  • Verify FROM/TO date pickers appear for each date field in the left filter panel
  • Set a FROM date only → results show content with modDate ≥ FROM
  • Set a TO date only → results show content with modDate ≤ TO
  • Set both FROM + TO → results are within the date window
  • Click Clear → both pickers reset and search re-runs with no date filter
  • Switch to "All" content types → no date pickers shown (correct: no content type selected)

Closes #33724

🤖 Generated with Claude Code

When a content type is selected in the Content Search portlet, date
and date_time fields now render as FROM–TO date picker pairs instead
of free-text Lucene syntax inputs. Each picker pair builds a Lucene
range value ([YYYYMMDD000000 TO YYYYMMDD235959]) and stores it in the
existing hidden field consumed by ContentletAjax. clearSearch() resets
the pickers. No backend changes required.

Closes #33724

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 19, 2026

Claude finished @dsilvam's task in 3m 38s —— View job


Claude PR Review

  • Load context (PR body, diff)
  • Read modified files
  • Verify backend handling of the [from TO to] value
  • Post review feedback

Findings

1. (Bug) Initial-state inconsistency on saved search restorationview_contentlets_js_inc.jsp:805-816

When renderSearchField is invoked with a previously-stored Lucene range value, the code parses value and pre-populates displayedValue on the FROM/TO pickers, but the hidden input is hard-coded to value="":

var result = "<input type=\"hidden\" id=\"" + fieldId + "\" name=\"" + ... + "\" value=\"\" />"

If the user clicks Search without touching either picker (e.g. on session restore or after switching tabs/content types), the UI shows a date range but the hidden field is empty, so the filter is silently dropped. The pickers do not fire onChange from their displayedValue attribute. Either (a) seed value= on the hidden input with the original range, or (b) call updateDateRangeField(fieldId) after widget instantiation.

2. (Robustness) mmddyyyyToLuceneDate only validates structure, not digits — lines 446-453

var parts = mmddyyyy.split('/');
if (parts.length !== 3 || parts[2].length !== 4) return null;

Length checks only. Input like "ab/cd/efgh" slips through and produces "efghabcd000000" which is then injected directly into a Lucene query (ContentletAjax.java:939 appends fieldValue verbatim with no escaping). Dojo's DateTextBox normally prevents this, but the function is reachable via updateDateRangeField if any other caller passes raw input. A /^\d+$/ check per part — or a single /^\d{1,2}\/\d{1,2}\/\d{4}$/ — closes the gap cheaply.

3. (Minor regression) Tooltip fallback fell through to time.hintview_contentlets_js_inc.jsp:1302-1314

Original:

if (type=='date') hintLabel = date.hint;
if (type=='date_time') hintLabel = datetime.hint;
else if (type=='time') hintLabel = time.hint;

New: date/date_time return early, all other types (any field whose fieldContentlet starts with "date" but isn't one of the three) now unconditionally get time.hint. Previously they got undefined. In practice only time fields land here, so this is mostly cosmetic — flagging for awareness.

4. (UX) Double doSearch() when both pickers are set

updateDateRangeField is wired to both _from and _to onChange, and each call triggers doSearch(). Picking FROM then TO fires two consecutive AJAX searches; if results race, the panel may flash. A small debounce or comparing the assembled value against the previous hidden value before calling doSearch() would avoid the redundant request.

5. (Compat) Legacy single-value date searches won't restore

Any saved search whose date field stored a plain MM/DD/YYYY (old format) won't match the [... TO ...] parse predicate, so both pickers come up empty after the upgrade. Acceptable, but worth a line in release notes if anyone depends on saved searches.


Looks fine

  • Identifier construction matches between renderSearchField and clearSearch (selectedStructureVarName === selectedStruct, fieldContentlet === field.fieldVelocityVarName at line 501).
  • Far-future / far-past bounds (30000101000000 / 18000101000000) match the existing convention in ContentletAjax.java:1026-1028.
  • No injection via fieldId interpolation in onChange="updateDateRangeField('...')" — structure inode is a UUID and velocity var name follows Java identifier rules.
  • Dijit widgets are destroyed before recreation.
  • luceneDateToMmddyyyy strictly validates ^\d{14}$ — safe for display.
    · Branch: issue-33724-date-range-filter-content-search

- Replace invalid sentinel dates 00000000000000/99999999999999 with
  18000101000000/30000101000000 to match the existing convention in
  ContentletAjax.java
- Wrap promptMessage i18n values with UtilMethods.escapeSingleQuotes()
  to prevent attribute injection from localized translations
- Move date-from/date-to keys to alphabetical position near other
  date-* entries in Language.properties

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Validate 14-digit format in luceneDateToMmddyyyy with /^\d{14}$/ regex,
  blocking XSS injection via malformed persisted range values
- Add value.endsWith(']') guard to prevent silent mis-parse when the
  closing bracket is missing from a stored range value
- Add comments on sentinel bounds (18000101/30000101) to document why
  they match ContentletAjax convention

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.

[FEATURE] Date range filter enhancement for Content Search

2 participants