Skip to content

CMS home page: searchable path selector (any CMS/theme/module page) + serve any theme's page at / - #313

Merged
WebTigers merged 3 commits into
mainfrom
feat/home-path-selector
Sep 26, 2026
Merged

WebTigers merged 3 commits into
mainfrom
feat/home-path-selector

Conversation

@WebTigers

Copy link
Copy Markdown
Owner

What

The CMS home-page selector was a <select> of published CMS pages plus a reveal-on-"custom" text box — it couldn't offer a theme page, and it framed the home page as a short menu rather than what it really is: any valid path. This reworks it into a searchable combobox over a path-discovery service, and teaches / to serve any installed theme's page (default theme or not).

A — the control + discovery

  • Cms_Service_Paths (/api, admin+): grouped, searchable path discovery.
    • Basic — the built-in landing, published CMS pages, each installed theme's home, and module home prefixes.
    • Advanced (a checkbox — the "litterbox") — adds every installed theme's individual content page.
    • labelFor() renders a stored value's friendly label (shared with the controller's initial render).
  • tiger.combobox.js — a reusable, zero-dep "pick OR type" primitive over an /api search service: visible search input + hidden value field, grouped results, keyboard nav, and free-text passthrough (a typed path commits verbatim — validated on save). House infrastructure, reusable anywhere a field is "one of a set, but may be typed".
  • Cms_Form_Settings: home_page is now a single validated free value (drops the CUSTOM sentinel + home_page_custom); the combobox writes it. The Settings view's inline <script> moved to cms.settings.js (house rule).

B — serve any theme's page at / (just a forward())

  • IndexController resolves a @theme:<key>[:<slug>] home value and forwards to the theme-content action.
  • PageController::themeContentAction honors a theme_content_theme param: it sets that theme active for the request (Tiger_ThemeDir + themeAssets), so the whole existing render pipeline (dir, assets, layout, skins) resolves to it — no pipeline refactor. Tiger_Theme gains dirForKey/assetBaseForKey/pagesForKey.

Proven on dev

With Puma the default theme, home_page = @theme:grey-mist renders Grey Mist's home at / with its own /_greymist assets (title "Grey Mist", 11 asset refs), while /login and everything else stay on Puma. Discovery is admin-gated; a guest/user is denied. Advanced is a strict superset of basic. The stored value's shape is validated (rejects unrooted paths, absolute URLs, query strings).

Tests

HomePageSelectorTest rewritten to the new contract: the home_page value shape (accepts ''/path/@theme:…/UUID, rejects junk), discovery ACL (guest/user denied, admin clears), module pages from the routing registry with non-page endpoints filtered, the search-term filter, advanced ⊇ basic, and labelFor for each value shape. i18n added to en/es/pt/hi/de/fr (tlh is the joke marketing-only locale, intentionally skipped).

🤖 Generated with Claude Code

https://claude.ai/code/session_01ASauLLscjqdsNqBNsx2Typ

WebTigers and others added 3 commits September 26, 2026 09:53
…eme/module), + serve any theme's page at /

The home page is ANY valid path — a CMS page, a theme page, a module page, a login screen, a dynamic
product page the control never sees. So the selector becomes a combobox that ALWAYS accepts a free-typed
value, with a discovery service supplying the convenience list it searches.

A — the control + discovery
- Cms_Service_Paths (/api, admin+): grouped, searchable path discovery. BASIC = the built-in landing,
  published CMS pages, each installed theme's HOME, and module home prefixes; ADVANCED (a checkbox — the
  "litterbox") adds every installed theme's individual content page. labelFor() renders a stored value's
  friendly label (shared with the controller's initial render).
- tiger.combobox.js — a reusable, zero-dep "pick OR type" primitive over an /api search service:
  visible search input + hidden value field, grouped results, keyboard nav, and free-text passthrough
  (a typed path commits verbatim — "right or wrong", validated on save). House infrastructure, reusable
  anywhere a field is "one of a set, but may be typed".
- Cms_Form_Settings: home_page is now a single validated free value (drops the CUSTOM sentinel +
  home_page_custom dance); the combobox writes it. Settings service/controller/view updated; the view's
  inline <script> moved to cms.settings.js (house rule).

B — serve any theme's page at / (just a forward)
- IndexController resolves a "@theme:<key>[:<slug>]" home value and forwards to the theme-content action.
- PageController::themeContentAction honors a theme_content_theme param: it sets that theme active FOR
  THE REQUEST (Tiger_ThemeDir + themeAssets), so the whole existing render pipeline (dir, assets, layout,
  skins) resolves to it — no pipeline refactor. Tiger_Theme gains dirForKey/assetBaseForKey/pagesForKey.

Proven live on dev: with Puma the default theme, home_page=@theme:grey-mist renders Grey Mist's home at /
with its own /_greymist assets, while /login and the rest stay on Puma. Discovery is admin-gated;
advanced is a strict superset of basic; the value's shape is validated. Integration test rewritten to the
new contract (form shape, discovery ACL/basic/advanced/filter, labelFor). i18n added to en/es/pt/hi/de/fr
(tlh is the joke marketing-only locale, intentionally skipped).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASauLLscjqdsNqBNsx2Typ
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASauLLscjqdsNqBNsx2Typ
The home page is any valid path (the combobox fills home_page; the discoverable page list moved to
Cms_Service_Paths, covered by HomePageSelectorTest). Assert the value's SHAPE + the required site name
instead of the removed getMultiOptions() select.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASauLLscjqdsNqBNsx2Typ
@WebTigers
WebTigers merged commit c8ca9a6 into main Sep 26, 2026
14 checks passed
@WebTigers
WebTigers deleted the feat/home-path-selector branch September 26, 2026 14:11
WebTigers added a commit that referenced this pull request Sep 26, 2026
…pathbox/discovery (#314)

* Address PR #313 review (TIGER-232..236): restore TigerCombo, harden the pathbox + discovery

Astra (Codex) static review of PR #313. Five P2 findings, all remediated.

TIGER-233 (regression I introduced): PR #313 overwrote the shared tiger.combobox.js (TigerCombo — the
closed <select> enhancer the admin layout loads globally for the timezone fields on profile + access),
reverting them to plain native selects. Restored TigerCombo verbatim; moved the new remote "pick or type"
control to its own asset tiger.pathbox.js (TigerPathbox / data-tg-pathbox). Closed-set fields keep their
filter-as-you-type; the CMS home field keeps pick-or-type.

TIGER-232: the pathbox committed free text only in a delayed blur, so clicking Save fast captured the old
value. It now exposes el._tgPathbox.flush() + TigerPathbox.flushAll(scope); cms.settings.js calls flushAll
synchronously before building FormData. home_page validation errors now decorate the VISIBLE search input,
not the hidden backing field.

TIGER-234: every fetch response rendered unconditionally and reopened the panel. Added a per-request
generation stamp + open-intent guard: a superseded or post-dismissal/post-selection response is dropped
(can't overwrite newer results or reopen a closed control).

TIGER-235: Cms_Service_Paths built a Zend_Db_Select and fetched full published-page rows (bodies + meta)
per keystroke, then filtered/capped after — against the "query building belongs in a model" rule. Added
Tiger_Model_Page::publishedSummaries($q, $limit) (small columns, published+scheduled gate, search + cap in
the model); the service calls it, no query builder. Theme HOMES are now emitted for ALL themes before any
advanced sub-pages so the per-group cap can't crowd a later theme's home out. One memoised
Tiger_Theme::inventory() replaces re-globbing/re-reading manifests per dirForKey/pagesForKey call.

TIGER-236: theme discovery (dirForKey et al.) scanned only 2 of the 4 locations Bootstrap::_initTheme
resolves from, so a theme in app/themes/<name> or core/modules/theme-<name> was activatable but invisible
to the selector and unresolvable by @theme:<key>. inventory() now scans all four in the SAME precedence
(app over core), keyed by manifest key.

Tests: HomePageSelectorTest unchanged (service contract stable); new PageTest coverage for
publishedSummaries (published-only + small columns; query filter + limit).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASauLLscjqdsNqBNsx2Typ

* PageTest: softDelete takes a WHERE clause, not a bare id

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASauLLscjqdsNqBNsx2Typ

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
@WebTigers WebTigers mentioned this pull request Sep 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant