WP Manifestindependent plugin directory
manifest / events / ordo_kalendar

Saint Paul's 1928 BCP Calendar

1928 BCP Calendar Standalone WordPress plugin scaffold for a lightweight 1928 Book of Common Prayer liturgical calendar.

★ 0stars

Install

Download source zip No release zip yet

This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/stevemacias/ordo_kalendar/archive/refs/heads/main.zip
No built-in updater. New versions won't appear under Dashboard → Updates. Install new releases yourself, or let Git Updater track the repository.

Standalone WordPress plugin scaffold for a lightweight 1928 Book of Common Prayer liturgical calendar.

Version 0.5.3 — What's New

May–July 2026 content batch — 32 canonical feast pages are now live (up from 17). All content uses 1928 BCP propers only.

New and completed movable feast pages:

  • Fourth Sunday after Easter (Easter +28, May 3, 2026) — new entry; Collect "O Almighty God, who alone canst order the unruly wills"; James 1:17; John 16:5.
  • Fifth Sunday after Easter (Easter +35, May 10) — completed stub; Collect "O Lord, from whom all good things do come"; James 1:22; John 16:23.
  • Sunday after Ascension Day (Easter +42, May 17) — completed stub; Collect "O God, the King of glory"; 1 Peter 4:7; John 15:26.
  • Monday in Whitsun Week (Easter +50, May 25) — completed stub; Acts 10:34; John 3:16.
  • Tuesday in Whitsun Week (Easter +51, May 26) — completed stub; Acts 8:14; John 10:1.
  • First through Eighth Sundays after Trinity (Easter +63 through +112, June 7–July 26) — nine new entries with full 1928 BCP Collects, Epistle and Gospel references, 3-paragraph theological notes, and three FAQs each.

New and completed fixed feast pages:

  • Saint Philip and Saint James (May 1) — James 1:1; John 14:1; Collect "O Almighty God, whom truly to know is everlasting life."
  • Saint Barnabas (June 11) — Acts 11:22; John 15:12; Collect "O Lord God Almighty, who didst endue thy holy Apostle Barnabas."
  • Nativity of Saint John Baptist (June 24) — Isaiah 40:1; Luke 1:57; Collect "Almighty God, by whose providence thy servant John Baptist was wonderfully born."
  • Saint Peter (June 29) — Acts 12:1; Matthew 16:13; Collect "O Almighty God, who by thy Son Jesus Christ didst give to thy Apostle Saint Peter."
  • Saint James (July 25) — Acts 11:27; Matthew 20:20; Collect "Grant, O merciful God, that as thine holy Apostle Saint James."

Sitemap expanded — get_content_day_dates() in SPBCP_Calendar_Sitemaps updated to include all new movable feast offsets (+28, +63 through +112) and all five new fixed-feast dates (May 1, June 11, June 24, June 29, July 25).

Version 0.5.2 — What's New

  • Fatal-error fix: render_season() — DateTimeImmutable::createFromFormat() was chained directly with ->format() without a null check. If the date object was unexpectedly false this would have caused a fatal error. The return value is now checked with instanceof DateTimeImmutable before formatting.
  • SEO: invalid feast and season slugs now 404 — Previously /feast/anything/ and /season/anything/ returned HTTP 200 with placeholder content, which Google would index as thin pages. maybe_404_invalid_calendar_request() now validates feast and season slugs against the data layer and issues a proper 404 for any unrecognised slug.
  • Require order corrected — class-spbcp-calendar-propers.php is now loaded before class-spbcp-calendar-data.php in the main plugin bootstrap, matching the dependency relationship (the data class calls the propers class).
  • Sitemap performance — SPBCP_Calendar_Sitemaps::get_url_entries() now caches its result in a static variable. WordPress core calls both get_max_num_pages() and get_url_list() per sitemap request, which previously triggered two full URL-list computations; now it computes once.
  • Developer tool hardening — tools/extract-propers-1928.php now sends HTTP 403 when accessed from a browser (matching the existing behaviour of tools/validate-calendar-data.php).
  • Production deployment documentation — README now includes an Installation section, pre-release validator workflow, AIOSEO integration notes, developer tool access notes, supported year range explanation, and invalid-route SEO safety summary.
  • QA checklist expanded — TESTING.md rewritten as a 13-section checklist covering activation, valid/invalid routes, SEO titles, meta descriptions, canonical URLs, JSON-LD, sitemaps, content rendering, security, plugin compatibility, and CLI validation.

Version 0.5.1 — What's New

  • SEO titles for all route types — Feast, season, day, and month pages now each produce a unique, correctly-formatted <title> tag. Season pages use "Anglican Liturgical Calendar (1928 BCP)"; day pages include the feast name and date; month pages include the month and year.
  • Meta descriptions — Every plugin-generated route now outputs a distinct 130–155 character <meta name="description"> tag derived from the page's feast, season, date, or month.
  • Canonical links for day and month pages — In addition to existing feast and season canonicals, /1928-bcp-calendar/{year}/{month}/{day}/ and /1928-bcp-calendar/{year}/{month}/ routes now each emit a <link rel="canonical"> pointing to their exact URL.
  • All in One SEO integration hardened — When AIOSEO is active the plugin no longer outputs canonical or description tags directly. Instead it injects values through aioseo_title, aioseo_description, and aioseo_canonical_url filters so AIOSEO's own output carries the correct data without duplication. A pre_get_document_title hook at priority 20 overrides AIOSEO's title on plugin pages.
  • Single SEO context method — All five SEO hooks (filter_document_title_parts, filter_pre_get_document_title, render_canonical, render_meta_description, AIOSEO filters) share one get_seo_context() call cached statically per request; no duplicate data-layer lookups.

Version 0.5.0 — What's New

  • Completed Holy Week feasts — Palm Sunday, Maundy Thursday, Good Friday, and Easter Even are now marked complete => true with full theological notes and three FAQs each; all four are live canonical feast pages included in the sitemap.
  • Circumcision of Christ corrected — The theological note, which had been accidentally overwritten with Epiphany content, has been replaced with the correct note covering the eighth-day circumcision, the Epistle from Philippians 2, and the Gospel from Luke 2.
  • Easter Week octave pages — Monday in Easter Week and Tuesday in Easter Week are now marked public => true and complete => true with full 3-paragraph theological notes and three FAQs each (Acts 10:34 + Luke 24:13 Emmaus; Acts 13:26 + Luke 24:36 upper-room appearance).

Version 0.4.0 — What's New

  • complete flag — Feast definitions now require both 'public' => true AND 'complete' => true before a canonical feast page is published or included in the sitemap. Entries with only public => true are treated as staging intent and are excluded from public routing until marked complete.
  • Renderer safeguards — Day-detail entries with no collect, epistle, or gospel no longer render empty liturgical content sections. Incomplete entries appear by title and date only.
  • Validator hardened — New checks warn when public=true but complete is absent or false; warn for any calendar entry that appears on the grid with no propers data; and FAIL only when a declared-complete public feast is missing required fields.

Version 0.3.0 — What's New

  • Standardised internal linking — Feast pages show a clickable next-occurrence date link; day-detail pages show prev/next navigation with actual dates as anchor text; season pages show a current-month link; feast-link anchor text uses the feast's own title throughout.

Version 0.2.0 — What's New

  • Season hub pages at clean URLs (/1928-bcp-calendar/season/{slug}/) for all six liturgical seasons (Advent, Christmas, Epiphany, Lent, Eastertide, Trinity).
  • Theological notes — 150–300 word original notes for seven major feasts: Easter Day, Ascension Day, Whitsunday, Trinity Sunday, Christmas Day, Epiphany, All Saints' Day.
  • FAQ blocks — Three short FAQs per major feast, stored in the data layer, rendered as semantic HTML, with FAQPage JSON-LD on supporting feast pages.
  • Internal linking — Feast pages link to their season and related season-mates; season pages list their feasts; day-detail pages show a season link and prev/next day navigation bounded to the supported year range.
  • Public flag — Feast definitions now carry 'public' => true to mark entries that have complete enough content to be canonical pages. Sitemaps and feast-page routing respect this flag. Incomplete entries appear in month views but do not link to feast pages.
  • Validator warnings — The validator emits non-failing WARN lines for non-public feasts with partial data, reserving hard FAIL for public feasts with missing required fields.

Current Scope

  • Plugin slug: spbcp-calendar
  • Shortcode: [spbcp_calendar]
  • Optional test attributes: [spbcp_calendar year="2026" month="5"]
  • Clean month URLs: /1928-bcp-calendar/2026/05/
  • Clean day URLs: /1928-bcp-calendar/2026/05/14/
  • Canonical feast URLs: /1928-bcp-calendar/feast/easter-day/
  • Season hub URLs: /1928-bcp-calendar/season/easter/
  • Supported calendar route range: 2025 through 2028
  • Invalid dates and out-of-range URLs return the active theme's 404 response
  • XML sitemap entries for public canonical feast pages, season pages, supported month pages, and populated day pages
  • JSON-LD structured data including AboutPage, Event, BreadcrumbList, and FAQPage
  • No React, no Blocksy theme edits, no The Events Calendar integration

TODO — Remaining 1928 BCP Propers

The following feasts and Sundays are defined in the data files but not yet marked 'public' => true. They need full collects, epistle references, and gospel references before they are ready to publish as canonical pages.

Fixed Feasts (not yet public)

  • Purification of the BVM (February 2)
  • Annunciation of the BVM (March 25)
  • St. Philip and St. James (May 1)
  • St. Barnabas (June 11)
  • St. John Baptist (June 24)
  • St. Peter (June 29)
  • St. James (July 25)
  • St. Bartholomew (August 24)
  • St. Matthew (September 21)
  • St. Michael and All Angels (September 29)
  • St. Luke (October 18)
  • St. Simon and St. Jude (October 28)
  • St. Andrew (November 30)
  • St. Thomas (December 21)
  • St. Stephen (December 26)
  • St. John Evangelist (December 27)
  • Holy Innocents (December 28)

Movable Feasts (not yet public)

All Holy Week feasts (Palm Sunday, Maundy Thursday, Good Friday, Easter Even) and Easter Week octave days (Monday and Tuesday in Easter Week) are now complete and public.

Sundays of the Year (not yet in data)

All 27 Sundays after Trinity, all 4 Sundays in Advent, all Sundays after Epiphany, Septuagesima, Sexagesima, Quinquagesima, the five Sundays in Lent (including Passion Sunday and Palm Sunday), and Easter Sunday through the Sunday after Ascension are not yet added as recurring entries.

These require the full Easter computus (already implemented in SPBCP_Calendar_Data::get_easter_for_year()) and offset-based date computation per the existing movable feast pattern.

Adding Future Feasts and Sundays Safely

The plugin uses a two-flag gate to prevent incomplete entries from accidentally becoming public canonical pages. Follow these rules when adding new feast or Sunday entries.

Flag meanings

public complete Result
absent / false absent / false Entry appears in the calendar grid only. No feast page, no sitemap URL.
true absent / false Staging intent declared. Still no feast page or sitemap URL. Validator emits a WARN.
true true Full canonical feast page published. Included in sitemap. Validator fails if required fields are missing.

Step-by-step process for a new feast or Sunday

  1. Add the stub. In fixed-feasts.php or movable-feasts.php, add a minimal entry with slug, title, type, rank, liturgical_color, and either month/day (fixed) or offset (movable). No flags yet.

    'second-sunday-after-epiphany' => array(
        'slug'             => 'second-sunday-after-epiphany',
        'title'            => 'Second Sunday after Epiphany',
        'type'             => 'sunday',
        'rank'             => 'Sunday',
        'liturgical_color' => 'green',
        'offset'           => 14,  // Easter − 35 = approximate; recalculate per BCP table
    ),

    The entry will appear by title in month and day views. No feast page will be generated. No empty content sections will render.

  2. Add propers. Once the Collect, Epistle reference, and Gospel reference are ready, add them directly to the definition or via a proper_key entry in includes/data/propers-1928.php. Add a theological_note (150–300 words, multi-paragraph using \n\n) and at least three faqs entries.

  3. Mark as staging. Set 'public' => true on the definition. The feast page still will not publish (the complete gate blocks it), but the validator will now warn you that the feast is queued for publication.

  4. Run the validator.

    php tools/validate-calendar-data.php

    Fix any FAIL lines. Review WARN lines. The validator will confirm whether all required fields (collect, epistle_reference, gospel_reference) are present.

  5. Mark as complete. Once the validator passes cleanly, set 'complete' => true. The canonical feast page will now be live, the URL will appear in the sitemap, and the feast will appear in related-feasts lists on season pages.

  6. Rebuild and deploy.

    zip -r spbcp-calendar.zip spbcp-calendar/ --exclude "*.DS_Store"

Required fields for complete => true

A definition must have all of these before setting complete => true:

  • slug — WordPress-style slug (lowercase, hyphens only)
  • title — Human-readable name
  • collect — Full 1928 BCP collect text (or via proper_key)
  • epistle_reference — Scripture reference (e.g. Colossians 3:1)
  • gospel_reference — Scripture reference (e.g. John 20:1)
  • theological_note — 150–300 word original note, two or three paragraphs separated by \n\n
  • faqs — Array of at least three question/answer pairs

What will NOT work

  • Setting complete => true without public => true — the validator will flag this as an inconsistent state.
  • Linking to a feast page from a month view — the renderer only generates feast links for entries where SPBCP_Calendar_Data::get_feast() returns a result, which requires both flags.
  • Adding a feast to the sitemap manually — the sitemap delegates entirely to SPBCP_Calendar_Data::get_public_feast_slugs(), which reads the flags dynamically. There is no hardcoded slug list to update.

Production Deployment

Installation

  1. Upload spbcp-calendar.zip through Plugins → Add New → Upload Plugin or unzip into wp-content/plugins/.
  2. Activate the plugin. Activation automatically creates the calendar page (/1928-bcp-calendar/) if it does not exist and flushes rewrite rules.
  3. If any plugin-generated URL returns a 404 after activation, go to Settings → Permalinks and click Save Changes to flush rewrite rules manually.

Before releasing content changes

Run the CLI validator from the plugin directory before packaging and uploading a new version:

php tools/validate-calendar-data.php

Or via WP-CLI on the server:

wp eval-file wp-content/plugins/spbcp-calendar/tools/validate-calendar-data.php

Fix all FAIL lines before proceeding. WARN lines are informational and do not block release. The validator will exit 0 on PASS or WARN and 1 on any FAIL.

All in One SEO

When AIOSEO is active the plugin supplies title, meta description, and canonical URL through AIOSEO's own filters (aioseo_title, aioseo_description, aioseo_canonical_url). Direct wp_head output is suppressed to prevent duplicate tags. No manual AIOSEO configuration is required.

If AIOSEO is not active the plugin outputs <meta name="description"> and <link rel="canonical"> directly in wp_head at priority 1 and 2.

Developer tools

Both files in the tools/ directory respond with HTTP 403 when accessed from a browser. They must be run from the command line or via WP-CLI. Do not expose the tools/ directory to the public internet (the default WordPress directory structure keeps it inaccessible from the web unless the webserver is misconfigured).

Supported year range

The plugin serves calendar routes for years 2025 through 2028, controlled by SPBCP_CALENDAR_MIN_YEAR and SPBCP_CALENDAR_MAX_YEAR constants in spbcp-calendar.php. Date URLs outside this range return the active theme's 404 response. Month navigation links are bounded to this range and will not generate links to unsupported years.

Invalid routes and SEO safety

  • URLs with unrecognised feast or season slugs (e.g. /feast/anything/, /season/anything/) return HTTP 404 — not placeholder content — so crawlers never index thin pages.
  • Invalid date combinations (e.g. February 30, November 31) return HTTP 404.
  • Prev/next day navigation links are suppressed at the boundary of the supported year range.
  • The sitemap includes only URLs for feast definitions where both public => true and complete => true are set. Incomplete or staging-only entries are excluded automatically.

Files

  • spbcp-calendar.php boots the plugin.
  • includes/class-spbcp-calendar-data.php loads and normalizes calendar data files.
  • includes/class-spbcp-calendar-propers.php loads the separate propers database.
  • includes/data/fixed-feasts.php stores recurring fixed-date feast definitions.
  • includes/data/movable-feasts.php stores movable feast definitions and explicit sample year dates.
  • includes/data/propers-1928.php stores structured Collect, Epistle, and Gospel data.
  • includes/class-spbcp-calendar-renderer.php renders the semantic month table, day detail view, feast pages, and season hub pages.
  • includes/class-spbcp-calendar-schema.php outputs route-aware JSON-LD structured data in wp_head.
  • includes/class-spbcp-calendar-sitemaps.php builds plugin-generated XML sitemap URL entries.
  • includes/class-spbcp-calendar-sitemap-provider.php registers those entries with WordPress core sitemaps.
  • includes/class-spbcp-calendar-plugin.php registers assets, rewrite rules, query vars, and the shortcode.
  • assets/css/spbcp-calendar.css styles the desktop table, mobile stacked cards, feast pages, and season hub pages.
  • tools/extract-propers-1928.php documents and scaffolds extraction from a converted Propers_1928.txt source.
  • tools/validate-calendar-data.php validates feast data from the command line before release.

Sitemap Behavior

The plugin registers a WordPress core sitemap provider named spbcpcalendar when core sitemap classes are available. The generated sitemap includes:

  • Season hub pages for all six liturgical seasons.
  • Canonical feast pages for all feast definitions with 'public' => true (currently: Palm Sunday, Maundy Thursday, Good Friday, Easter Even, Easter Day, Ascension Day, Whitsunday, Trinity Sunday, Christmas Day, Circumcision of Christ, Epiphany, All Saints' Day).
  • Month pages for every supported month from 2025 through 2028.
  • Day pages for populated movable and fixed feast dates across all supported years.

If All in One SEO is active, the plugin also hooks aioseo_sitemap_additional_pages and appends the same URL set. AIOSEO requires its Sitemaps > Additional Pages toggle to be enabled before that filter is reflected in its sitemap output.

Structured Data

The plugin outputs JSON-LD only on /1928-bcp-calendar/ plugin routes:

  • Canonical feast pages receive AboutPage and BreadcrumbList schema. Pages with FAQs also receive FAQPage schema.
  • Date/day pages receive Event and BreadcrumbList schema.
  • Season hub pages receive BreadcrumbList schema.

Calendar Data Structure

The calendar data is split by liturgical date type:

  • includes/data/fixed-feasts.php contains fixed civil-date definitions such as Christmas Day, Epiphany, All Saints' Day, and the fixed Saints' days. Entries with both 'public' => true and 'complete' => true are published as canonical feast pages.
  • includes/data/movable-feasts.php contains movable definitions computed from Easter offsets. The seven fully-complete feasts (Easter Day, Ascension Day, Whitsunday, Trinity Sunday, and Christmas, Epiphany, All Saints' Day) are marked both public and complete. Holy Week feasts are marked public only and will not generate feast pages until marked complete.
  • includes/data/propers-1928.php remains the separate database for Collect, Epistle, and Gospel content. Calendar entries link to it through proper_key.

Both 'public' => true AND 'complete' => true must be set for an entry to produce a canonical feast page, appear in the sitemap, or be linked from month/day views. Either flag absent means the entry shows in the calendar grid by title only, with no feast-page link and no empty content sections rendered.

Data Validation Workflow

Run the developer-only validator from the plugin directory before packaging larger data changes:

php tools/validate-calendar-data.php

In a WordPress environment with WP-CLI available:

wp eval-file wp-content/plugins/spbcp-calendar/tools/validate-calendar-data.php

The script is command-line only and exits with HTTP 403 if requested from a browser. Checks include: required feast fields, fixed and movable date data, populated Collect/Epistle/Gospel fields, public/complete flag consistency, calendar entries without propers, duplicate slugs, Lectionary Central URL formatting, and canonical feast page URL generation.

Exit codes:

  • 0 — all checks passed (PASS) or passed with warnings only (WARN)
  • 1 — one or more hard failures (FAIL)

Failure and warning rules:

  • FAIL — entry has public=true AND complete=true but is missing collect, epistle_reference, or gospel_reference.
  • WARN — entry has public=true but complete is absent or false (staging intent; feast page won't publish yet).
  • WARN — entry has some propers data but is missing required fields and is not yet public+complete.
  • WARN — entry appears on the calendar grid (has a valid date) but has no propers data at all.

Run the validator before every release and before setting complete => true on any new feast.

Propers Extraction Workflow

  1. Convert Propers_1928.wpd to plain text, for example with LibreOffice: soffice --headless --convert-to txt --outdir /tmp /path/to/Propers_1928.wpd
  2. Run the extractor against the verified text export: php tools/extract-propers-1928.php /tmp/Propers_1928.txt > includes/data/propers-1928.generated.php
  3. Review the generated PHP carefully against the source wording.
  4. Replace or merge into includes/data/propers-1928.php.
  5. Add matching proper_key values to calendar entries.