Skip to main content

Documentation

No results found.
Features Members

Events

WebProCMS ships with a built-in events system: events with a scheduling-grade date (start/end, all-day, time zone), recurring series, venue address, cost, website link, featured image, categories, and comments — surfaced on a public events...

WebProCMS ships with a built-in events system: events with a scheduling-grade date (start/end, all-day, time zone), recurring series, venue address, cost, website link, featured image, categories, and comments — surfaced on a public events listing (a searchable Upcoming/Past list with a List / Calendar view toggle) at /events plus per-event detail pages. Events are an ordinary content type, managed at Dashboard → Events, so they share the media library, the design library, taxonomies, scheduling, search, and SEO with everything else in the CMS.

Pricing. The Date & Time field type (and its facet + JSON-LD) is free for any content type — every install can give a type a deadline, webinar time, or booking window. The Events product — the pre-built Events content type plus the calendar and event-detail layouts — is a membership feature (events, default OFF), toggled at Dashboard → Settings → Features. So the platform date capability is core; the curated Events experience is the paid unlock.


The problem

An events calendar is the textbook case of a feature most sites don't need and the ones that do don't want to re-learn. Bolting it on as its own subsystem — its own table, its own media uploader, its own category model, its own design conventions — means editors learn one media flow for blog posts and a different one for events, and the two diverge over time. But modelling events as a plain custom content type loses everything that makes an events calendar an events calendar: a real date (not a naive string), upcoming/past filtering, a month-grid view, and repeating series.

The trick is to make events feel native — opinionated about the date shape, pre-wired to a calendar and to schema.org Event — while still being just a content type that composes with the media library, taxonomies, comments, relations, and the design library like every other type.

The fix

Events is a seeded content type whose fields reproduce the event shape using reusable capabilities that any content type can use — none of them are events-specific:

Field Type Capability it uses
Title text auto-derived item title
Subtitle text —
Event Date datetime the scheduling-grade date field — range, per-entry recurrence, time zone, and an Upcoming/Past index facet
Venue text —
Venue Address address the international address field → directions link + schema.org location
Cost text —
Website URL text —
Details richtext_tiptap the body; auto-appends content blocks (CTA / gallery / FAQ)
Category taxonomy real categories with pretty archive URLs

Type-level config declares schema_type = Event, excerpt + comments on, and the calendar (Calendar / List toggle) as the default list layout with the event-detail show layout.

The datetime field — the one genuine moat

The thing an events calendar needs that plain content types don't have is a real date. The datetime field type stores { start, end?, all_day?, tz?, repeat? } and is reusable by any type (a job-opening's closing date, a webinar time, a promotion window) — and it's free on every install. It drives four things:

  • Display — {item.data.event_date} renders a human, range-aware string ("May 1, 2030, 7:00 PM – 10:30 PM", all-day, or a multi-day span).
  • The Upcoming / Past facet — a flag on the field turns it into a relative date facet on the listing page (>= now / < now), instead of filtering by a stored value.
  • The calendar/list layouts — two @requiresFeature events page-design layouts a datetime-having type can choose between from the layout picker: content-calendar-month (List view — opens to the searchable Upcoming/Past list) and content-calendar-grid (Calendar view — opens to the month grid). Both carry the same Calendar / List toggle, title search, day view, and Upcoming/Past tabs; they differ only in which view loads first and which toggle button sits on the left. These layouts are the membership-gated piece.
  • schema.org Event — startDate/endDate auto-map to the field's start/end and location to the venue address, so the generated show page emits Event JSON-LD with zero events-specific code.

Recurrence — owned occurrences

A datetime field can opt into a repeat rule with RRULE-grade options: daily / weekly / monthly / yearly frequency, an interval ("every 2 weeks"), an until date, end after N occurrences (COUNT), multiple weekdays per week ("every Mon + Wed"), and monthly same-day-of-month or same-nth-weekday anchoring ("the 2nd Tuesday"; an anchor on its month's last such weekday tracks "the last Friday" through 4- and 5-week months). On save, RepeatingItemChildrenJob expands the rule into occurrence items owned by the parent (owner_id), reusing the existing owned-items infrastructure wholesale — occurrences are already excluded from lists and search, already cascade-deleted with the parent, and an occurrence's detail URL redirects to its parent. The only new code is generic date arithmetic, so recurring office-hours / classes / sessions get the same machinery. The month calendar surfaces every occurrence on its date.

Public listing and detail

The generated content-type pages register the usual routes — GET /events (events.index), GET /events/{record} (events.show), and GET /events/{taxonomy}/{term} (events.taxonomy) for the category archives. The index opens on the chronological Upcoming / Past list (with a title search), which a visitor flips to a month-grid calendar via a ?view= / ?tab= toggle (server-rendered, no Livewire state). The detail page is the events-scoped event-detail design-library row (hero image, a date / venue+directions / cost info card, the body with auto-appended content blocks, and a back link). Editors swap either layout from the dashboard like any content type.

Subscribable calendar feed + add-to-calendar

Every datetime-carrying type gets an iCalendar feed at /calendar/{type}.ics (content.calendar-feed) — the calendar layouts show a Subscribe button that hands the webcal:// form to Google/Apple/Outlook, so new events appear in subscribers' calendars automatically. Recurring events are expanded into individual VEVENTs (occurrence children), so no client-side RRULE support is needed; all-day events use proper VALUE=DATE semantics, and venue name + address flatten into LOCATION. Each event's detail page also gets a per-event Add to calendar link (/calendar/{type}/{slug}.ics). Types without a datetime field 404.

Alongside the .ics download, every datetime-carrying type also exposes two computed add-to-calendar template link tokens — {item.calendar_google_url} (Google Calendar's action=TEMPLATE render URL) and {item.calendar_outlook_url} (Outlook web's compose URL) — prefilled with the event's title, UTC start/end (all-day events use bare dates with an exclusive end, matching the ICS semantics), venue name + address as the location, and the subtitle + detail-page URL as the description. The event-detail layout renders all three side by side under the Date & Time card (Add to calendar / Google Calendar / Outlook); the tokens are also pickable in the design-library editor for any custom row. Items without a usable start resolve the tokens to nothing.

Free RSVP (no ticketing required)

The Event RSVP block (built into event-detail, also available as a standalone row) collects name, email, and party size — no payment, no capacity math, the tier below Event Ticketing. Recurring events get an occurrence picker so each date keeps its own headcount. Attendees get a confirmation email with the event's calendar invite attached and a tokenized cancel link; repeat submissions with the same email are idempotent (they update the party size instead of duplicating). Responses live under Dashboard → Event RSVPs (dashboard.events.rsvps, manager+) with an event filter, cancelled toggle, headcount total, and CSV export. When an event has paid tickets on sale, the detail layout shows the ticket picker instead of the RSVP form.

Member-submitted events with moderation

The Submit an Event row (design library → Events category) lets signed-in members (Memberships feature) propose events: title, start/end, all-day flag, venue, cost, and a description. Each submission is created as a draft events item — publishing it from the content dashboard is the moderation step, so nothing appears publicly unapproved. The submitter's member id/name/email ride along in the item's data (_submission), the business email gets a review notification, and submissions are rate-limited per member. Guests see a member sign-in prompt instead of the form.

Native, shared infrastructure

Because events are content items, they get — for free, with the same UX as the blog — the media library (featured image + galleries, one alt-text/Replace flow), Scout search, scheduled publishing (scheduled status + the publish-due cron), previous_slugs redirects, translations, the SEO meta editor, the per-record detail-layout picker, the dashboard column picker, custom per-item pages, relations to other content types, comments, and the design-library data-source preset (content_type:events) that binds any row to events.

What lives where

Path Purpose
app/Jobs/SeedDemoDataJob.php demoContentTypes() defines the events type (fields + schema_type + the calendar/event-detail default-layout keys); seedContentItems() seeds demo events (incl. a weekly-recurring one) + photos.
app/Support/DateValue.php The datetime value object — normalize, format, recurrence expansion (occurrences()).
app/Jobs/RepeatingItemChildrenJob.php Materializes a repeating datetime field's occurrences as owned child items.
app/Support/CalendarGrid.php Builds the 6×7 month grid (forType) and the Upcoming/Past list (listForType), including owned occurrence-children.
resources/design-library/rows/page-layouts/list/content-calendar-month.blade.php List view — the calendar+list layout that opens to the list (the events default). @requiresFeature events.
resources/design-library/rows/page-layouts/list/content-calendar-grid.blade.php Calendar view — the same layout that opens to the month grid instead. @requiresFeature events.
resources/design-library/rows/page-layouts/detail/event-detail.blade.php The events-scoped detail layout (@requiresContentType events, @requiresFeature events).
app/Support/StructuredData.php schema.org emitter — maps Event startDate/endDate to a datetime field and location to the address field.
app/Support/ContentIcsBuilder.php + app/Http/Controllers/ContentCalendarFeedController.php The subscribable /calendar/{type}.ics feed + per-event .ics download (routes in routes/cms.php — committed core, since routes/web.php is per-install).
app/Support/EventRsvps.php + app/Models/EventRsvp.php + app/Livewire/EventRsvp.php The free-RSVP flow: idempotent create, confirmation email w/ invite, tokenized cancel (events.rsvp.cancel), event_rsvps table, dashboard list at dashboard.events.rsvps.
app/Livewire/EventSubmit.php + resources/design-library/rows/events/event-submit.blade.php Member event submissions → draft items + owner notification.
resources/design-library/rows/page-layouts/detail/event-rsvp.blade.php Standalone RSVP row for detail layouts that predate the built-in block.
app/Support/DataSources/Presets/ContentTypePreset.php The per-type data-source preset; the datetime facet, address/directions tokens, the {item.calendar_google_url}/{item.calendar_outlook_url} add-to-calendar template links, and {item.auto.date}/{item.data.X} date formatting live here.
app/Support/Features.php + DesignRow::scopeAvailableFeature() The events feature flag + the per-row @requiresFeature gate that hides the calendar/event-detail layouts and the seeded type unless the feature is on.

For how content types work in general, see custom-content-types.md, content-type-relations.md, and blog.md (the other built-in record system folded into the content-type model).

Settings reference

The Events product is gated by the events membership feature; once it's on, events are configured like any content type.

Key / Where Notes
features.events.enabled Master toggle for the Events product (admin-only, Dashboard → Settings → Features, default OFF). Gates the seeded Events type and the calendar + event-detail layouts. The datetime field type is unaffected — it's always available.
Dashboard → Events Create/edit events; pick a per-record detail layout; manage categories.
Dashboard → Content Types → Events Edit the field schema, the default list/detail layouts, the schema.org mapping, and the comments/excerpt toggles.
index_layout.default.events The default list layout (content-calendar-month, the List view variant, out of the box). Swap to the Calendar view (content-calendar-grid) — or any other list layout — from the layout picker.
detail_layout.default.events The default detail layout (event-detail out of the box).