Search by

sayamaapps / kirby-koyomi

simsimmal

Event calendar for Kirby 5: venues, organizers, status, online events, ICS, RSS and JSON-LD.

Package info

github.com/sayamaapps/kirby-koyomi

Type:kirby-plugin

pkg:composer/sayamaapps/kirby-koyomi

Statistics

Installs: 167

Dependents: 0

Suggesters: 1

Stars: 0

Open Issues: 0

v1.6.0 2026-10-31 17:57 UTC

README

Event calendar for Kirby CMS 5, with ICS files, feeds and structured data.

Koyomi keeps venues and organizers as pages of their own, so an address changes in one place. Status and attendance are event fields, and the event page, calendar files, feeds and JSON-LD all read the same data. The plugin loads nothing from third parties itself; media an editor embeds from another server is the exception, see Privacy.

Features

  • Venues and organizers as reusable pages, edited once and linked from events.
  • Status (cancelled, postponed, rescheduled, moved online) and in-person, online or hybrid attendance.
  • Local wall time with an IANA zone; daylight saving time is handled on output.
  • ICS download per event, webcal:// subscription, RSS and an optional JSON feed.
  • schema.org Event JSON-LD on every event page.
  • English and German texts; no cookies, no scripts, no third-party requests from the plugin itself.

Requirements

Supported
Kirby 5 (getkirby/cms ^5.0)
PHP 8.2 to 8.5, with mbstring

Kirby 5.0 to 5.5 have security advisories, and Composer refuses to install them, so the lowest version the tests can install is 5.6.0. The constraint stays ^5.0, so sites on those versions are not blocked from updating.

Installation

Pick one of three ways. Koyomi works the same in each.

Composer (recommended), from the site's root:

composer require sayamaapps/kirby-koyomi

Composer installs to site/plugins/koyomi/. Add that path to .gitignore and deploy it like vendor/.

Git submodule, in the site's site/plugins/ folder:

git submodule add https://github.com/sayamaapps/kirby-koyomi.git site/plugins/koyomi

ZIP: download the release archive from the releases page and extract it to site/plugins/koyomi/.

Use the release tags (for example 1.4.0), not the main branch, on a live site.

Setup

Koyomi is off by default. In site/config/config.php, switch it on and set the default zone:

return [
    'sayamaapps.koyomi.active'   => true,
    'sayamaapps.koyomi.timezone' => 'Europe/Berlin', // default zone of new events
];

Allow the calendar and the containers in the Panel, in the pages section of site/blueprints/site.yml. Events, venues and organizers are created inside their containers, so they need no entry of their own:

columns:
  - width: 1/1
    sections:
      pages:
        type: pages
        templates:
          - koyomi-calendar
          - koyomi-venues
          - koyomi-organizers
          # your own templates stay in this list

Create the pages in this structure. The calendar is listed; the containers are unlisted.

content/
  events/                koyomi-calendar    (listed)
    20270312_retreat/    koyomi-event
  venues/                koyomi-venues      (unlisted)
    gasthaus-muster/     koyomi-venue
  organizers/            koyomi-organizers  (unlisted)
    max-muster/          koyomi-organizer

Event folders are named YYYYMMDD_slug; the URL drops the date (/events/retreat). The calendar's slug sets the feed URLs. A venue or organizer appears on events only when it is published and not below a draft, so publish it and its container first.

The plugin's templates render a complete calendar with filters, event pages, and venue and organizer pages. You need no template of your own to start.

Usage

Snippets print unstyled markup with koyomi-* classes and no scripts:

<?php snippet('koyomi/list', ['list' => $page->koyomiList()]) ?>
<?php snippet('koyomi/event', ['event' => $page]) ?>

Override a snippet with a file at the same path under site/snippets/, for example site/snippets/koyomi/card.php. A custom koyomi/layout must print snippet('koyomi/jsonld', ['page' => $page]) in its <head>, or event pages lose their JSON-LD.

Fields, snippets, page methods and helpers are listed in docs/reference.md.

Feeds

URL Option Content
/events.ics feed Subscription feed: upcoming events and recently ended ones. Cancelled events stay, marked CANCELLED.
/events/<slug>.ics ics One event as a download.
/events.rss rss RSS 2.0 with upcoming events.
/events.json json Upcoming events as JSON. Off by default.

A switched-off output returns Kirby's 404 page, and sayamaapps.koyomi.active switches all of them off. The koyomi/subscribe snippet shows the webcal:// address.

Options

Every option is sayamaapps.koyomi.<name>, written flat: 'sayamaapps.koyomi.active' => true. null means "not set". The 1.x name koyomi.<name> still works as a fallback until 2.0, flat or nested, and is read without a warning. If both names are set, the new one wins. See Deprecations.

Option Default Description
active false Master switch. Only a real true enables Koyomi.
timezone Europe/Berlin Default zone of new events and the site zone.
timezones all PHP zones Zones offered in the Panel, as a list of IANA names.
currency first of currencies Default currency of new events.
currencies ['EUR', 'CZK', 'CHF'] Currencies offered in the Panel.
country null Home country as ISO code, for example DE. Addresses omit it.
languages ['de', 'en', 'cs'] Event languages offered in the Panel and the filter. A list, or a map of code to label.
language site language Language of snippets, feeds and calendar files: en or de.
perPage 12 Events per list page, 1 to 100.
defaultDuration 120 Minutes an event without an end time lasts.
feedPastDays 30 Days after its end an event stays in the .ics feed.
ics true Per-event .ics download.
feed true Subscription feed /<calendar>.ics.
rss true RSS feed /<calendar>.rss.
json false JSON feed /<calendar>.json.
google true "Google Calendar" link on upcoming events.
groupBy 'month' 'month' groups lists under month headings; false prints one flat list.
attendanceSwitch true "All · In person · Online" switch above the calendar list.
hideCancelled false Leave cancelled events out of the upcoming list and the RSS feed.
duplicateReset true A duplicate loses its status, previous date, status note and availability.
formats [] Date and time patterns per language, for example ['de' => ['time' => 'G.i']]. See docs/reference.md.
mapUrl null Closure fn (Sayamaapps\Koyomi\Venue $venue): string that replaces the OpenStreetMap link.
calendar first koyomi-calendar Page ID of the calendar used by snippets and feeds.
venues first koyomi-venues Page ID of the venues container.
organizers first koyomi-organizers Page ID of the organizers container.
now real time Pins the clock, for example '2027-03-10 12:00', for tests and previews.

Koyomi's texts are also registered as Kirby translations (config/translations.php), so a site can call t('sayamaapps.koyomi.status.cancelled'). The 1.x keys t('koyomi.status.cancelled') stay registered too. Koyomi reads its own copy in translations/ through its I18n class, not through Kirby's $t: the language of the plugin follows the language option, which can differ from the Kirby site language, and a missing Kirby translation must not turn a German page English.

Deprecations

The 1.x names keep working in every 1.x release. Koyomi logs nothing for them, so debug mode and the error log stay as they are. Each name is marked @deprecated in the code; the table shows what replaces it.

1.x Use instead
koyomi_option($name, $default) Sayamaapps\Koyomi\Api::option($kirby, $name, $default)
koyomi_active() Sayamaapps\Koyomi\Api::active($kirby)
koyomi_now() Sayamaapps\Koyomi\Api::now($kirby)
koyomi_i18n() Sayamaapps\Koyomi\Api::i18n($kirby)
koyomi_t($key, $values) Sayamaapps\Koyomi\Api::t($key, $values)
koyomi_event($value) Sayamaapps\Koyomi\Api::event($value)
koyomi_expires($events) Sayamaapps\Koyomi\Api::expires($kirby, $events)
koyomi_expires_at($timestamp) Sayamaapps\Koyomi\Api::expiresAt($kirby, $timestamp)
'koyomi.<option>' in config.php 'sayamaapps.koyomi.<option>', flat. Still read as fallback; rename the keys in your site config.
Classes Koyomi\…, for example Koyomi\Event Sayamaapps\Koyomi\…, same names (class aliases in legacy.php)

The functions are guarded by Kirby's Helpers::hasOverride(), which looks only for the constant KIRBY_HELPER_<NAME>, for example KIRBY_HELPER_KOYOMI_ACTIVE. A site that declares its own koyomi_active() must define that constant first; without it, the declaration fails with "Cannot redeclare", as it did in 1.x.

Blueprint, snippet, template, page method and route names do not change, and neither do the feed URLs or the stored content.

The 1.x page methods $page->icsUrl(), jsonUrl(), rssUrl(), webcalUrl(), googleCalendarUrl(), isOngoing(), isPast() and upcomingEvents() keep their names and results and are not deprecated. The prefixed twins koyomiIcsUrl(), koyomiJsonUrl(), koyomiRssUrl(), koyomiWebcalUrl(), koyomiGoogleCalendarUrl(), koyomiIsOngoing(), koyomiIsPast() and koyomiUpcomingEvents() return the same values; use them in new templates.

Changes you may notice

No site output changes. Two things differ in the code itself:

  • Class names read from objects (get_class(), ::class, var_dump()) and in some error messages (for example "Cannot modify readonly property") now show Sayamaapps\Koyomi\…. instanceof, type hints and exception classes are unchanged.
  • PHP messages that contain a file name or line number (for example a TypeError raised by a feed helper) show the new location.

Planned for 2.0

These changes would break existing sites, so they wait for the next major version:

  • Deprecation notices at runtime for the 1.x names, shown in debug mode.
  • Removal of the 1.x functions, the Koyomi\ class aliases, and the test helpers Format::reset() and Repository::reset().
  • Translation keys koyomi.<key>: only sayamaapps.koyomi.<key> remains.

Privacy

Koyomi processes no visitor data: no cookies, no browser storage, no third-party requests from the plugin. The "Google Calendar" link is an ordinary link; Google receives event data only when a visitor follows it. The online meeting link (onlineUrl) is never output, so no page or feed contains it.

The exception is media in an event's text. An image or video block with an address on another server loads from there when the page opens, and a video embed runs that provider's script. Upload images to the site instead, or leave such blocks out. Without them there is nothing to add to a site's privacy policy.

Changes by chat (Fude)

With the separate package Fude 1.7 or later, a chat bot can create and change events. Koyomi's blueprints mark the fields and the calendar's events section, so each calendar is one Fude collection without a line in site/config/fude.php; an event is found by its start date. Date, times, venue, online link, price, booking link, status and short text are reachable; the full text (blocks), categories (tags), languages (multiselect), organizers and series (several pages) are not, because Fude cannot set those types, and neither are the cover and the other fields. Details: docs/reference.md. Without Fude, the fude and fudeAsk keys do nothing.

Maintenance

Tests: composer test. Issues and pull requests are welcome, without a support promise. Report security problems privately through GitHub's vulnerability reporting. Changes: CHANGELOG.md.

License

MIT, see LICENSE.