Parish Events & Programs releasesself-updates
WordPress plugin importing parish calendar events from a CCB feed into a custom post type with scheduled sync, structured data, and display shortcodes.
by St. Paul the Apostle Catholic Church · github.com/wakcyscanner/parish-events · website
Install
The author publishes release zips, so WP-CLI can install straight from GitHub:
wp plugin install https://github.com/wakcyscanner/parish-events/releases/download/v1.3.0/parish-events-1.3.0.zipShips its own WordPress updater (built-in updater), so new versions show up under Dashboard → Updates.
Readme
Parish Events & Programs
A WordPress plugin that turns a parish calendar feed into real WordPress content. It imports events from a Church Community Builder (CCB) XML feed into a custom post type on a schedule, giving every event a permanent URL, search-engine structured data, and full editorial control — replacing client-side calendar embeds.
It also provides Programs: hand-authored sets of events packaged together for promotion, displayed as card grids or carousels. See Programs.
Requires: WordPress 6.0+, PHP 7.4+ · License: GPLv2 or later
Installable zips are published under Releases.
Features
Import & sync
- Scheduled import (hourly to daily, or on demand) of the current month plus the next two, directly from the Pushpay ChMS v1 API (
public_calendar_listing, HTTP Basic Auth) — or from any custom proxy endpoint returning the same XML. Credentials live in Settings or, preferably on production, inPE_CHMS_SUBDOMAIN/PE_CHMS_USERNAME/PE_CHMS_PASSWORDwp-config constants that keep the secret out of the database. - One
parish_eventpost per occurrence, keyed by CCB event ID + date — upstream edits update the same post and its URL never changes. - Safe by design: a failed, empty, or malformed fetch never removes anything; unchanged feeds produce zero writes; past events are never touched.
- Import failure alerts by email (after three consecutive failed runs, with a recovery notice), plus a run log in the settings screen.
- Only text wrapped in
[public]...[/public]in the feed description is published. Internal staff notes are never stored in WordPress, and leader contact details are visible to admins only.
Event lifecycle
- Events deleted upstream move to a Removed upstream status — never deleted. Their pages show a neutral "no longer listed" notice for a grace period, then return
410 Gone. - Removed is not cancelled: marking an event cancelled is an explicit checkbox that switches the notice and emits schema.org
EventCancelledmarkup. Absence from the feed is never assumed to mean cancelled. - If a suppression rule with a link later covers an already-imported series, those posts' URLs permanently redirect (301) to the rule's destination, so saved bookmarks and indexed links keep working.
- Events that return to the feed are restored automatically.
Editorial control
- Manual override flag per event: imports stop touching every field, and the date, times, location, group, and type become editable alongside the content (block editor supported).
- Always editable, override or not: featured image, featured flag, cancelled flag, a featured video slot (YouTube/Vimeo URL shown above the details), an event flyer image slot (shown below the details, linked to full size), a registration/RSVP link (shown as a button; the feed has no sign-up field), and an optional cost field (shown in the details and reflected in structured data).
- Admin list table with event date sorting, sync-status column, flag icons, and filters by event month, group, and upcoming/past.
Suppression & locations
- Suppression rules (by CCB event ID, exact title, or title keyword) stop recurring events like daily Mass from generating hundreds of posts. Suppressed occurrences still appear on the calendar, linking to a single URL you choose per rule (for example, a Mass times page).
- Location directory: give each location a link to its own page, or an expandable "where is this?" description shown wherever the location appears. Global and per-event location substitutions clean up raw feed values.
Display
[parish_events_calendar]— server-rendered list and month views with a group filter and month navigation. No JavaScript framework, crawlable, cached.[parish_events_featured]— card grid of featured upcoming events.[parish_events_upcoming]— compact list of the next events (also available as a widget).[parish_events_subscribe]— calendar subscribe button.
Calendar integration & SEO
- "Add to calendar (.ics)" and "Google Calendar" buttons on every upcoming event page.
- Subscribable webcal/ICS feed of all published events at
/?pe_ics=feed. The feed body is cached server-side, served withCache-Control: publicand anETag(so polling calendar apps get cheap304s), and refreshes whenever an import changes content. - Schema.org Event JSON-LD, Open Graph, and Twitter meta tags on single event pages.
wp parish-events importWP-CLI command (non-zero exit on failure) andwp parish-events statusfor recent runs.
Programs
A program is a set of events packaged together for promotion — "Financial Peace University", "That Man is You!", an adoration chapel signup. Unlike calendar events they are hand-authored, have no upstream feed, and have no pages of their own: every card links out to a ministry or registration page.
- Separate
parish_programpost type, edited under Programs. Title, description, image, an optional free-text schedule line ("Tuesdays · Aug 4 – Oct 6 · 6:30 PM"), and a link with custom button text. - Program Groups taxonomy for sectioning ("Upcoming Series", "Grow in Community"), and drag-free ordering via the Order field.
- Display as a card grid or carousel with
[parish_programs], the Parish Programs block, or homepage injection — a CSS-selector-based insert for themes whose homepage template can't be edited. - Settings under Programs → Settings, kept separate from calendar settings.
wp parish-programs import --from=<url-or-path>migrates programs from the legacy Come to Meprograms.json, sideloading images into the media library.
Programs are deliberately not stored as parish events. Events are feed-owned — the importer reconciles them against ChMS on every run and marks rows Removed upstream when they disappear — so hand-authored rows in that post type would be reconciled away. The importer is scoped to the event post type throughout, so the two features never touch.
Installation
- Download the latest
parish-events-x.y.z.zipfrom Releases. - In wp-admin, go to Plugins → Add New Plugin → Upload Plugin, and upload the zip (uploading a newer zip over an existing install upgrades it in place).
- Activate. The plugin flushes rewrite rules on activation; if event URLs 404, re-save Settings → Permalinks once.
- Configure Parish Events → Settings: ChMS subdomain + API credentials (or a legacy custom feed URL), schedule, suppression rules, location directory, and alert emails.
After the initial install, updates arrive like any other plugin: the plugin checks this repo's Releases and offers new versions on the Plugins screen, where they can be installed in one click (or auto-updated, if enabled).
Release channels
There are two update channels:
- Stable (the default) — sees full releases only. Production sites stay here.
- Beta — additionally sees pre-releases. Enable it per site with the "Receive beta updates" checkbox in Parish Events → Settings. Meant for staging sites; a stable release newer than the newest beta always wins.
The development flow behind that:
- Feature work lands on the
betabranch and ships as a pre-release: version like1.1.0-beta.1, tagv1.1.0-beta.1. Any tag containing a hyphen publishes as a GitHub prerelease, which the stable channel never sees. - Staging (on the beta channel) receives it as a normal plugin update and soaks it.
- When it's proven,
betamerges tomainand ships as a stable release (1.1.0, tagv1.1.0) — production picks it up.
Both channels use the same release workflow: bump the plugin Version header, add a CHANGELOG.md section for the version, and push the matching tag.
Launch checklist
- The plugin claims the
/events/URL base for event posts. If another plugin or page already serves/events/, resolve that first: deactivate the conflicting plugin, or keep a page at/events/and put the[parish_events_calendar]shortcode in it — the page URL and the post type coexist. - Review the settings, then click Run import now and spot-check a few event pages and the admin list.
- Place
[parish_events_calendar]on your calendar page and[parish_events_featured]wherever featured cards should appear. - Set up a real cron job: WordPress cron only fires on site visits, which is unreliable on low-traffic sites. Either have the host request
wp-cron.phpevery 15 minutes and setdefine( 'DISABLE_WP_CRON', true );inwp-config.php, or point a system cron job atwp parish-events importdirectly. - If the site sits behind Cloudflare (or another CDN), add a cache rule for the ICS subscribe feed so calendar-app polling is absorbed at the edge instead of hitting PHP: cache URLs whose query string contains
pe_ics=feed, respecting the origin cache headers (the plugin sendsCache-Control: public, max-age=900, s-maxage=3600plus anETag). CDNs don't cache this URL by default because of the query string. - Once satisfied, remove any old calendar embed the plugin replaces.
- Keep calendar customizations in this plugin (or a site plugin), not in theme files, so theme updates can't overwrite them.
Shortcodes
[parish_events_calendar view="list" months="2" group="" show_filter="1" show_toggle="1"]
[parish_events_featured count="3" order="date" columns="3" show_excerpt="1"]
[parish_events_upcoming count="5" show_location="0"]
[parish_events_subscribe label="Subscribe to calendar"]
[parish_programs layout="grid" count="0" heading="" align="" group="" groups=""]
[parish_programs]: layout is grid or carousel; count="0" shows all; group filters to one Program Group slug; groups renders one titled section per group (comma-separated slugs, or all) and takes precedence over group.
FAQ
Imports aren't running on schedule. See step 4 of the launch checklist — use a real cron job instead of visit-driven WordPress cron.
An event I edited got flagged "missing upstream". The event has the manual-override flag and disappeared from the feed. Decide whether to unpublish it yourself or leave it; imports won't touch it either way.
Why did a suppressed event's old page start redirecting? A suppression rule with a link URL now covers it. The redirect preserves saved bookmarks and search results by sending them to the rule's destination.
Changelog
See Releases for per-version notes.
Read the full README on GitHub →
Releases
| Tag | Published | Asset | Downloads |
|---|---|---|---|
| v1.3.0 | Aug 5, 2026 | parish-events-1.3.0.zip | 2 |
| v1.3.0-beta.8 | Jul 31, 2026 | parish-events-1.3.0-beta.8.zip | 1 |
| v1.3.0-beta.7 | Jul 31, 2026 | parish-events-1.3.0-beta.7.zip | 1 |
| v1.3.0-beta.6 | Jul 31, 2026 | parish-events-1.3.0-beta.6.zip | 1 |
| v1.3.0-beta.5 | Jul 31, 2026 | parish-events-1.3.0-beta.5.zip | 1 |
| v1.3.0-beta.4 | Jul 31, 2026 | parish-events-1.3.0-beta.4.zip | 1 |
| v1.3.0-beta.3 | Jul 31, 2026 | parish-events-1.3.0-beta.3.zip | 1 |
| v1.3.0-beta.2 | Jul 31, 2026 | parish-events-1.3.0-beta.2.zip | 1 |
| v1.3.0-beta.1 | Jul 31, 2026 | parish-events-1.3.0-beta.1.zip | 2 |
| v1.2.1 | Jul 31, 2026 | parish-events-1.2.1.zip | 1 |
| v1.2.0 | Jul 22, 2026 | parish-events-1.2.0.zip | 3 |
| v1.2.0-beta.2 | Jul 22, 2026 | parish-events-1.2.0-beta.2.zip | 2 |
| v1.2.0-beta.1 | Jul 22, 2026 | parish-events-1.2.0-beta.1.zip | 2 |
| v1.1.0 | Jul 22, 2026 | parish-events-1.1.0.zip | 3 |
| v1.1.0-beta.2 | Jul 22, 2026 | parish-events-1.1.0-beta.2.zip | 2 |
| v1.0.13 | Jul 17, 2026 | parish-events-1.0.13.zip | 3 |
| v1.0.12 | Jul 17, 2026 | parish-events-1.0.12.zip | 2 |
| v1.0.11 | Jul 17, 2026 | parish-events-1.0.11.zip | 2 |
| v1.0.10 | Jul 16, 2026 | parish-events-1.0.10.zip | 3 |
| v1.0.9 | Jul 16, 2026 | parish-events-1.0.9.zip | 1 |
| v1.0.8 | Jul 16, 2026 | parish-events-1.0.8.zip | 3 |