Repository source: docs/plans/archive/multi_site_config_step3_development_plan.md
Multi-Site Config — Step 3: Development Plan
Stage 1
Goal: Introduce a single source of truth for per-site branding.
Dependencies: none
Expected changes:
New
src/ForumRewrite/SiteProfileRegistry.php, mirroringThemeRegistry's shapeall(): array— returnszenmemesandchouseentries, each withname,defaultTheme,composerPromptactive(): array— resolvesgetenv('FORUM_SITE_ID'), defaults tozenmemes, falls back tozenmemesfor an unrecognized value
Verification approach: new unit test asserting default resolution,
FORUM_SITE_IDoverride resolution, and unknown-value fallbackRisks or open questions:
none
Canonical components/API contracts touched: new
SiteProfileRegistry::all(),SiteProfileRegistry::active()
Stage 2
Goal: Replace the hardcoded site name with the profile-driven value everywhere it renders.
Dependencies: Stage 1
Expected changes:
SiteConfig::SITE_NAMEconstant replaced bySiteConfig::siteName(): string, delegating toSiteProfileRegistry::active()['name']Update all call sites:
Application.php:966,997,2346,2422,Host/FrontController.php:265,278,281,View/TemplateRenderer.php:66,templates/pages/about.php
Verification approach: existing tests pass unmodified with
FORUM_SITE_IDunset (zenmemes output unchanged); add assertion thatFORUM_SITE_ID=chousechanges rendered site name at each call siteRisks or open questions:
confirm no other literal
'zenmemes'string exists outside the known call sites
Canonical components/API contracts touched:
SiteConfig::siteName()
Stage 3
Goal: Let each site profile set the client-visible default theme.
Dependencies: Stage 1
Expected changes:
TemplateRendererpasses the active profile'sdefaultThemeinto template context alongside the existingthemeslistBase layout renders it as a
data-default-themeattribute on an existing root elementpublic/assets/theme_toggle.jsreads that attribute as its fallback instead of the literal"auto"at its current fallback points, keeping"auto"as the ultimate fallback when the attribute is absent
Verification approach: manual check per
docs/runbooks/theme_development_guide.md— load with no theme cookie under eachFORUM_SITE_ID, confirm the rendered default theme; existing theme-toggle behavior still passesRisks or open questions:
theme_toggle.jsis shared by every page; fallback chain must keep zenmemes' current behavior (auto) provably unchanged
Canonical components/API contracts touched:
ThemeRegistry(read-only reference, unchanged),theme_toggle.jsfallback resolution
Stage 4
Goal: Let each site profile override the inline composer's prompt copy.
Dependencies: Stage 1
Expected changes:
templates/pages/board.phpreadscomposerPrompt(placeholder + aria-label text) from the active profile instead of the literal"Start a thread..."stringDefault value preserves the current zenmemes text
Verification approach: template smoke test rendering
board.phpunder bothFORUM_SITE_IDvalues, asserting the placeholder/aria-label textRisks or open questions:
confirm no other page duplicates this literal string outside
board.php
Canonical components/API contracts touched:
templates/pages/board.php
Stage 5
Goal: Let a developer preview either profile in one checkout with a single
FORUM_SITE_ID=<id> ./v3 start, without hand-setting content-path env vars.Dependencies: Stage 1
Expected changes:
LocalRepositoryBootstrap::defaultRepositoryRoot()becomes site-scoped whenFORUM_REPOSITORY_ROOTis unset; conceptual signature:defaultRepositoryRoot(string $projectRoot, string $siteId): stringzenmemesresolves to today's exact path (state/local_repository) so existing local dev state is untouchedchouseresolves to a separate sibling directory, auto-initialized from the same fixture the same waypublic/index.php's equivalent local defaults for database/artifact paths become site-scoped the same way
Verification approach: run
./v3 startandFORUM_SITE_ID=chouse ./v3 startlocally; confirm each auto-initializes and serves a distinct local repository/database with correct branding, and that omittingFORUM_SITE_IDreproduces today's exact pathRisks or open questions:
must not change the default path for the zenmemes case, or existing local dev state would appear to "disappear"
Canonical components/API contracts touched:
LocalRepositoryBootstrap::defaultRepositoryRoot(),public/index.phplocal-default resolution
Stage 6
Goal: Document the shipped behavior and extend automated coverage.
Dependencies: Stages 1-5
Expected changes:
Update
docs/examples/apache_vhost.confanddocs/examples/env.production.examplewithFORUM_SITE_IDUpdate
docs/runbooks/production_deploy.mdprecedence/rollback sectionExtend
tests/LocalAppSmokeTest.php(or add a sibling test) to run once perFORUM_SITE_IDvalue
Verification approach:
./v3 testpasses; doc read-through against the Step 1 "How to configure" section for consistencyRisks or open questions:
none
Canonical components/API contracts touched:
tests/LocalAppSmokeTest.php,docs/examples/*,docs/runbooks/production_deploy.md