Skip to content

feat(maintenance): optional public 503 and an editable banner - #517

Merged
navjottomer merged 2 commits into
mindstellar:developfrom
t0ma5:feature/maintenance-soft-banner
Sep 14, 2026
Merged

navjottomer merged 2 commits into
mindstellar:developfrom
t0ma5:feature/maintenance-soft-banner

Conversation

@t0ma5

@t0ma5 t0ma5 commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Why

.maintenance is still the only on/off switch. Today that file always 503s the public site. That is the right default for an upgrade (core already touches the file) and for anyone who already relies on lockout. This PR adds a second, optional layer: keep the site up and show a banner whose text the admin edits.

Nothing site-specific, no extra routes, no HTML in the banner.

Logic

  1. File on/off. Tools → Maintenance still creates or removes .maintenance. That is unchanged.
  2. Lockout defaults on. Missing/empty maintenance_lockout reads as on. Existing installs and the upgrade path that writes the file still 503 visitors. Only an explicit 0 (unchecked checkbox, saved) is the soft mode.
  3. Soft mode. File present + lockout off → HTTP 200 for everyone. __OSC_MAINTENANCE__ is defined so the header bar renders. The bar shows the saved message (escaped); if the message is blank, the previous default copy (with the site title) is used.
  4. Hard mode. File present + lockout on → public HTTP 503, same as today. Theme maintenance.php still wins if the theme ships one; otherwise osc_die() uses the same (escaped) message. Admins skip the 503 and see the bar, with copy that says only signed-in admins can see the site.
  5. Admins never 503. osc_is_admin_user_logged_in() still bypasses lockout.
  6. CLI never 503. php index.php -p cron is not an admin session, so today it would hit the 503 path during lockout and cron would not run. CLI is exempted. oc-cli.php never went through index.php and is unchanged.
  7. Plain text only. The saved message is trimmed, strip_tags, max 500 characters. HTML is not stored. Output goes through osc_esc_html (and nl2br) on the banner and on the 503 page.
  8. fclose. fopen of .maintenance only fcloses a real handle. PHP 8 type-errors if fopen failed and we still closed false.

Prefs: section osclass, maintenance_lockout BOOLEAN, maintenance_message STRING. The 503/banner decision is a pair of pure helpers so the default-on lockout and the sanitizer can be pinned without a database (tests/maintenance-mode.php).

How to test

  • Tools → Maintenance → Enable. Incognito: HTTP 503. Admin session: site works, yellow bar (admins-only copy).
  • Uncheck Block the public site, save a short message. Incognito: HTTP 200 + banner with that text. <script> in the message must not run.
  • Clear the message and save: default copy comes back.
  • php index.php -p cron -t hourly while .maintenance exists and lockout is on: cron still runs (no 503).
  • Disable maintenance: banner gone, site as usual. Re-enable: the last lockout/message choice is still there.

Made with Cursor

Keep .maintenance as the on/off switch and default lockout on so existing
sites and the upgrade path that touches the file still 503 visitors.
Unchecking lockout leaves the site up with a plain-text banner. CLI cron
is not 503'd. Only fclose the flag file when fopen succeeded.

Co-authored-by: Cursor <cursoragent@cursor.com>
@t0ma5
t0ma5 requested a review from navjottomer as a code owner August 24, 2026 15:43
@navjottomer

Copy link
Copy Markdown
Member

Holding this one, I'll come back to it.

One thing to look at meanwhile: osc_show_maintenance is registered on the header hook, which themes run inside <head>. Only admins see it today, so the misplaced div and inline <style> never showed. Showing it to every visitor puts it in <head> on every page — third-party themes will paint it unstyled above the fold. It needs a hook themes run in <body>.

Themes run the header hook inside <head>, so showing the bar to every visitor would dump a div and inline CSS above the fold. footer is the required body hook; a short script then prepends the bar as body's first child.

Co-authored-by: Cursor <cursoragent@cursor.com>
@t0ma5

t0ma5 commented Sep 10, 2026

Copy link
Copy Markdown
Contributor Author

Moved osc_show_maintenance off header onto footer.

Themes run header inside <head> (PACKAGE-SPEC; Storefront and Bender both do this in common/head.php). The bar is a <div> plus inline CSS, so putting it there on every public page would paint unstyled above the fold. footer is the required hook at the end of <body>.

footer is the bottom of the document, so the bar is position: fixed until a few lines of JS prepend it as document.body's first child. After that it sits in flow at the top. No-JS keeps the fixed fallback.

A new required body hook (top of <body>, skip-link aside) would be cleaner than relocating from footer. That is a theme-contract change (bundled themes + PACKAGE-SPEC), not this PR — happy to follow if you would rather go that way.

navjottomer added a commit that referenced this pull request Sep 14, 2026
# Conflicts:
#	.github/workflows/test.yml
@navjottomer
navjottomer merged commit 1a427d8 into mindstellar:develop Sep 14, 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.

2 participants