philippoehrlein / kirby-push-notifications
Web Push for Kirby: channel-based subscriptions, Panel UI, frontend dialog, hooks and API
Package info
github.com/philippoehrlein/Kirby-Push-Notifications
Type:kirby-plugin
pkg:composer/philippoehrlein/kirby-push-notifications
Requires
- getkirby/composer-installer: ^1.2
- minishlink/web-push: ^10.0
README
Kirby Push Notifications is a plugin for Kirby CMS that adds Web Push support. Visitors can subscribe to channels (e.g. “Notes”, “News”), and you can send notifications from the Panel or via hooks. Subscriptions are stored in SQLite; sending is powered by the minishlink/web-push library with VAPID authentication.
Features
- 🔔 Visitors subscribe to channels
- 👨💻 Editors send notifications from the Panel
- 🚀 Developers automate via hooks
Requirements
- Kirby 5.x
- PHP 8.2+
- HTTPS (required for Web Push)
Installation
Composer (recommended)
In your site root:
composer require philippoehrlein/kirby-push-notifications
Manual installation
- Download or clone the repo.
- Copy the
kirby-push-notificationsfolder intosite/plugins/.
The plugin ships with its own vendor/, so no composer install in the plugin folder is needed.
Configuration
In site/config/config.php (or a separate config file):
return [ 'philippoehrlein.push-notifications' => [ 'vapid' => [ 'publicKey' => 'your-vapid-public-key', 'privateKey' => 'your-vapid-private-key', 'subject' => 'https://yourdomain.com', ], 'channels' => [ 'panel' => [ [ 'value' => 'note-approval', 'text' => 'Note Approval', 'info' => 'Get informed when a note is waiting for approval', ] ], 'website' => [ [ 'value' => 'notes', 'text' => 'Notes', 'info' => 'Receive new notes', ], [ 'value' => 'photos', 'text' => 'Photography', 'info' => 'Receive the latest Photo series', ], ] ], // optional: change DB location // 'db' => [ // 'name' => 'push_notifications', // 'dir' => 'site/push-notifications', // ], // optional: Web Push default options (TTL, contentType, batchSize, etc.) // 'webPush' => [ // 'contentType' => 'application/json', // 'TTL' => 3600, // 'urgency' => null, // 'topic' => null, // 'batchSize' => 1000, // 'requestConcurrency' => 100, // ], ], ];
Note Defining channels in the plugin options is optional. It is only required if you want to use the built‑in subscription UIs that read channels from the config (like kpn-dialog). If you use the helper script (helper.js), panel buttons or your own custom UI, you can pass channel names directly in your code without configuring them in channels.
Alternative: flat channels (no panel/website groups)
By default, you can configure channels in two groups (panel and website) so the Panel groups them visually. Channels in the website group are exposed to the frontend snippet, while panel channels are only available inside the Panel.
If you don’t need this distinction, you can also use a flat list of channels.
// site/config/config.php return [ 'philippoehrlein.push-notifications' => [ 'channels' => [ [ 'value' => 'news', 'text' => 'News', 'info' => 'General website updates', ], [ 'value' => 'notes', 'text' => 'Notes', 'info' => 'New notes and articles', ], ], ], ];
VAPID keys
Web Push requires a VAPID key pair. Set vapid.publicKey, vapid.privateKey and vapid.subject (your site URL, e.g. https://yourdomain.com). Keep the private key secret.
Generating keys: Use the Create VAPID keys section in the web-push-php README (OpenSSL in bash or VAPID::createVapidKeys() in PHP).
Option webPush
Optional: Set global defaults for all push sends with the webPush option. Keys: contentType (e.g. application/json), TTL (seconds, e.g. 3600), urgency, topic, batchSize, requestConcurrency. Usually urgency and topic are left null here and set per send via the hook payload (see Payload for send hooks). You can still set global defaults if needed.
Usage
There are two main use cases: website (visitors subscribe) and panel (editors manage or send notifications). Each can be used in a simple way (ready-made UI) or a custom way (your own UI with the same APIs).
Website
Simple: Dialog snippet
Use the kpn-dialog snippet for a button that opens a subscribe/unsubscribe dialog with channel checkboxes. No custom JS needed.
In a template or snippet:
<?php snippet('kpn-dialog', slots: true) ?> 🔔 Notifications <?php endsnippet() ?>
Optional: pass data to override defaults or translations:
<?php snippet('kpn-dialog', [ 'headline' => 'Your Headline', 'description' => 'Choose the channels you want to receive.', // 'channels' => [['value' => 'news', 'text' => 'News', 'info' => '…']], ], slots: true) ?> 🔔 Notifications <?php endsnippet() ?>
Custom: helper.js
If you don’t want the dialog (e.g. a single button with fixed channels, or your own UI), use the helper script. Set window.KPN_CONFIG (vapidPublicKey, subscribeUrl, unsubscribeUrl, swPath), load /assets/kpn/helper.js, then call window.KPN.subscribe(channels) and window.KPN.unsubscribe(). You stay in control of markup and flow; the helper only handles the Web Push API and the plugin routes.
Panel
Simple: View buttons, button components, dialog
The plugin ships with two view buttons (kpn-subscribe, kpn-notification), two button components (kpn-subscribe-button, kpn-notification-button) and one dialog (kpn-subscribe-dialog). Register and place them in your Panel blueprints or views where needed. That’s enough to let logged-in users subscribe/unsubscribe and to send a notification (channel, title, body) without building your own UI. See src/index.js and src/components/ for how they’re wired.
Custom: Your own UI with hooks and API
Build your own Panel UI and call the hooks (subscribe, unsubscribe, send-to-one, send-to-many) and the Panel API (e.g. get-channels, get-keys, status, subscribe, unsubscribe). Same backend, full control over the interface.
Example: send a push when a note is published:
// site/config/config.php 'hooks' => [ 'page.changeStatus:after' => function ($newPage, $oldPage) { if ($newPage->intendedTemplate() === 'note' && $newPage->status() === 'listed') { kirby()->trigger('philippoehrlein.push-notifications.send-to-many', [ 'payload' => [ 'message' => [ 'title' => kirby()->site()->title()->value(), 'body' => 'New note: ' . $newPage->title()->value(), 'data' => ['url' => $newPage->url()], ], 'channel' => 'notes', 'options' => ['urgency' => 'normal'], ], ]); } }, ],
Hooks
| Hook | Use case |
|---|---|
philippoehrlein.push-notifications.subscribe |
After a subscription is created (payload: endpoint, keys, channel, user_id). |
philippoehrlein.push-notifications.unsubscribe |
After unsubscription (payload: endpoint and/or user_id, channel). |
philippoehrlein.push-notifications.send-to-one |
Send to one user (payload: user_id, message, channel, language?, options?). |
philippoehrlein.push-notifications.send-to-many |
Send to many users or to a full channel (payload: message, user_ids?, channel?, language?, options?). |
Payload for send hooks
For send-to-one and send-to-many, the payload can include an optional options array with Web Push options for that send:
urgency: Delivery priority –'very-low','low','normal','high'. Affects delivery timing and presentation (e.g. sound, vibration).topic: Optional. When set, push services may replace older notifications with the same topic by newer ones (collapse). Whether and when to use e.g. the channel as topic is up to you (e.g.'topic' => $channelfor one notification per channel).TTL: Optional, time-to-live in seconds for this message.
Example: 'options' => ['urgency' => 'high', 'TTL' => 600].
Routes and API
- Frontend (public):
POST /push-notifications/subscribe— Body:endpoint,keys,channel. Param:lang(optional).POST /push-notifications/unsubscribeGET /push-notifications-sw.js(service worker)GET /assets/kpn/helper.js
- Panel API:
philippoehrlein/push-notifications/*(subscribe, unsubscribe, get-channels, get-keys, status, etc.).
License
MIT. See LICENSE for details.
Support
- Issues: GitHub Issues
- Contact: github@philippoehrlein.de



