Scheduled

totalcms/scheduled — bundled with Total CMS. Adds a scheduled page feature that only renders a page during a configured time window. Outside the window, visitors get redirected to a fallback URL or see a 404.

Use cases

  • Holiday landing pages. Black Friday, Cyber Monday, seasonal promos — the page appears on the start date and disappears after the end date, automatically.
  • Embargoed announcements. Set scheduledFrom to the embargo lift time. The page exists in the builder but isn't live until the moment arrives.
  • Time-limited sales. Set both bounds. When the sale ends, visitors land on a "sale ended" page instead.
  • Coming soon → live. scheduledFrom only, no end date. The page goes live at a specific moment and stays live forever.

Enabling

  1. Go to Admin → Extensions, find Scheduled, click Enable (or run tcms extension:enable totalcms/scheduled).
  2. Open a page in Site Builder, tick scheduled under Features.
  3. Add the time window to the page's Page Data JSON field:
    {
      "scheduledFrom": "2026-11-25T00:00:00Z",
      "scheduledUntil": "2026-12-31T23:59:59Z",
      "beforeWindow": "/coming-soon",
      "afterWindow": "/sale-ended"
    }
  4. The page only renders between the two timestamps. Before the window opens, visitors are sent to beforeWindow (or get a 404); after it closes, they're sent to afterWindow (or a 404). Logged-in operators bypass the schedule and always see the page, so you can preview it before it goes live.

Per-page configuration

Set inside the page's Page Data JSON field. All keys are read from page.data.*.

Key Type Default Description
scheduledFrom string (ISO 8601) (optional) Start of the visibility window. Before this time → outside. Missing → no lower bound.
scheduledUntil string (ISO 8601) (optional) End of the visibility window. After this time → outside. Missing → no upper bound.
beforeWindow string (optional) URL to redirect to before the window opens ("coming soon"). Missing → 404.
afterWindow string (optional) URL to redirect to after the window closes ("sale ended"). Missing → 404.

Both bounds are optional — you can use one or both:

Config Behavior
scheduledFrom only Page is invisible before the date, live forever after.
scheduledUntil only Page is live immediately, goes dark after the date.
Both Page is live only during the window.
Neither Middleware no-ops — page always renders.

Timezone handling

Timestamps are parsed by PHP's DateTimeImmutable constructor, which supports any format PHP recognizes. The comparison is always done in UTC.

Recommended formats:

  • 2026-11-25T00:00:00Z — UTC, unambiguous
  • 2026-11-25T00:00:00-05:00 — explicit offset (e.g. US Eastern)
  • 2026-11-25T00:00:00 — interpreted as UTC (no offset = UTC)

Failure modes

All silent — scheduling breaking should never break the live page:

  • Both bounds empty or missing → middleware does nothing; page renders normally.
  • Malformed date strings (not parseable by PHP) → that bound is ignored. If both are malformed, page renders normally.
  • Non-string values → ignored.
  • beforeWindow / afterWindow empty or missing for the relevant side → 404 instead of redirect.

Testing locally

Use ISO 8601 timestamps in the near future, then wait — or adjust system clock for testing. The middleware reads wall-clock time on each request, so there's no caching to worry about.

Composing with other features

Order matters. A typical setup might be:

  1. auth — check login first
  2. scheduled — then check the time window
  3. protect — then check the passcode

If scheduled runs before auth, an outside-window visitor gets redirected before they're even checked for login — which might be what you want (don't reveal the page exists outside its window).

Disabling

Go to Admin → Extensions → Scheduled and click Disable, or run:

tcms extension:disable totalcms/scheduled

When disabled, scheduled disappears from the page-features picker. Pages that already have it ticked will silently skip it.

Implementation notes

The middleware lives at resources/extensions/totalcms/scheduled/ScheduledMiddleware.php. Its one runtime dependency is an injected isAdmin closure (wired in Extension.php to AccessManager::userLoggedIn) so logged-in operators bypass the schedule; otherwise it's a pure reader of page data and the system clock.

Source: resources/extensions/totalcms/scheduled/

See also