sayamaapps / kirby-koyomi
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
Requires
- php: ^8.2
- ext-json: *
- ext-mbstring: *
- getkirby/composer-installer: ^1.2
Requires (Dev)
- ergebnis/composer-normalize: ^2.54
- getkirby/cms: ^5.0
- icanhazstring/composer-unused: ^0.9.7
- maglnet/composer-require-checker: ^4.0
- phpmd/phpmd: ^2.15
- phpunit/phpunit: 11.5.55
- vimeo/psalm: ^6.20
Suggests
None
Provides
None
Conflicts
- sayamaapps/kirby-fude: <1.7
Replaces
None
This package is auto-updated.
Last update: 2026-10-31 17:58:11 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
EventJSON-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 showSayamaapps\Koyomi\….instanceof, type hints and exception classes are unchanged. - PHP messages that contain a file name or line number (for example a
TypeErrorraised 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 helpersFormat::reset()andRepository::reset(). - Translation keys
koyomi.<key>: onlysayamaapps.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.