Skip to main content
Version: 2.0.0

CP Notifications

The yStore CP notification system delivers real-time in-app alerts to administrators directly in the Craft Control Panel. Notifications are distinct from customer-facing emails: they are internal admin messages that appear in the bell-icon header dropdown and on the dedicated notifications index page.


What the notifications system does

  • Automatically creates CP alerts for key commerce events (new orders, customer registrations, order status changes)
  • Stores all notifications in a persistent database table scoped by site and optionally by user
  • Shows unread count in the CP header badge
  • Provides a dropdown with the five most recent notifications and a "mark all read" action
  • Automatically purges notifications older than 90 days
  • Provides a full paginated notifications index page at yui/notifications with store-level filtering
  • Exposes JSON controller endpoints for mark-as-read and header refresh
  • Exposes a PHP service API and a Flow Manager action/trigger pair for custom automation

Auto-generated notifications

yStore registers event listeners at boot and automatically creates CP notifications for the following events.

EventTypeSeverityTitle pattern
Successful checkout (EVENT_AFTER_PURCHASE_SUCCESS)orderinfoNew order #<incrementId>
Customer sign-up (EVENT_AFTER_CUSTOMER_SIGN_UP)customerinfoNew customer registered
Order status change (EVENT_AFTER_ORDER_STATUS_CHANGE)orderinfoOrder #<orderId> status changed

Auto-generated notifications link directly to the relevant CP page (order detail or customer record). They are broadcast to all admins (no userId target).


Notifications index page

Navigate to yStore → Notifications (yui/notifications) to see a paginated list of all CP notifications.

  • Pagination: 20 notifications per page; older entries accessible via page query parameter.
  • Store filter: A store-code query parameter (?store=<code>) filters notifications to those belonging to the matching site. Store badges are shown per notification row to identify origin.
  • Required permission: yui:orders:view.

The header bell icon dropdown shows the five most recent notifications and loads unread count via yui/notifications/get-latest (JSON). Mark-as-read actions post to yui/notifications/mark-read or yui/notifications/mark-all-read.


Notification record fields

Each notification row in {{%yui_notifications}} has the following fields.

FieldTypeConstraintsDescription
idintPKAuto-increment primary key
siteIdintNOT NULL, FK → {{%sites}}Site the notification belongs to; defaults to current site
userIdint|nullFK → {{%users}} (CASCADE)Target user ID; null = broadcast to all admins
typestringmax 50 chars, default systemCategory label: order, customer, system, flow, or any custom string
severitystringmax 20 chars, default infoVisual urgency: info, warning, or critical
titlestringmax 255 charsShort title shown in the header dropdown
messagetext|nullOptional body with additional context
urlstring|nullmax 500 charsCP URL for quick navigation when clicking the notification
isReadboolNOT NULL, default falseRead/unread state
dateCreateddatetimeNOT NULLCreation timestamp (UTC)
dateUpdateddatetimeNOT NULLLast updated timestamp
uidstringCraft UID

Indexes: (userId, isRead, dateCreated), (siteId, dateCreated), (dateCreated) — optimized for unread-count queries and paginated listing filtered by site.


Flow Manager integration

Create admin notification action

Use the Create admin notification action (create-notification) in a flow to write a notification programmatically. All string fields support Twig tokens from the flow context.

FieldRequiredDefaultDescription
typeYesflowNotification category string
severityYesinfoinfo, warning, or critical
titleYesShort title; Twig-rendered
messageNoOptional body text; Twig-rendered
urlNoCP navigation URL; Twig-rendered
userIdNoTarget user ID; leave empty for broadcast
siteIdNoTarget site ID; leave empty for current site

Output variables: created, type, severity, title, url, userId, siteId

After a successful createNotification() call the service automatically dispatches the notification-created Flow trigger — any matching flows run asynchronously via the Craft queue. You do not need to call the trigger manually.

Example — critical stock alert:

Place this action after a product-out-of-stock trigger to escalate to the admin panel:

type: system
severity: critical
title: "{{ product.title }} is out of stock"
url: /admin/yui/products/{{ product.id }}

Notification created trigger

The Notification created trigger (notification-created) fires whenever any notification is written to the CP notification center, regardless of source. Use it to mirror important alerts into external channels (Slack, Teams, webhook).

FieldDefaultDescription
type(empty = any)Filter by notification type string
severityanyany, info, warning, or critical
scopeanyany, broadcast (all admins), or targeted (specific user)

Payload variables: notificationId, type, severity, title, message, url, siteId, userId, broadcast

Example — escalate critical notifications to Slack:

  1. Add notification-created trigger, set severity = critical.
  2. Add a slack-notification action with the message body using {{ title }} and {{ message }}.

Email style theming

The email style system controls the global visual layout of all customer-facing emails (not CP notifications). It is separate from email template content.

Built-in styles

Style keyDescription
defaultGradient header, rounded cards — the standard yStore look
minimalFlat, clean layout with no gradients and minimal decoration
boldLarger typography, stronger visuals, vivid colours

The active style is selected per-template via the Style override field in yStore → Marketing → Email Templates.

Registering a custom email style

Modules and plugins can register additional styles or override the CSS of existing built-in styles by listening to EmailStyleService::EVENT_REGISTER_EMAIL_STYLES.

use yui\craft\events\RegisterEmailStyleEvent;
use yui\craft\services\EmailStyleService;
use yui\craft\support\EventManager;

EventManager::listen(
EmailStyleService::class,
EmailStyleService::EVENT_REGISTER_EMAIL_STYLES,
function (RegisterEmailStyleEvent $event): void {
// Register a new style
$event->styles['corporate'] = [
'label' => 'Corporate',
'description' => 'A clean corporate email layout',
'css' => '@mymodule/assets/email/corporate.min.css',
'template' => 'my-module/email/themes/corporate',
];

// Override only the CSS of the built-in default style
$event->styleOverrides['default'] = [
'css' => '@mymodule/assets/email/default-override.min.css',
];
}
);

$event->styles keys:

KeyTypeRequiredDescription
labelstringYesHuman-readable name shown in settings dropdowns
descriptionstringNoShort description shown in the style picker
cssstringYesYii alias path to the compiled CSS file
templatestringNoTwig template path for a custom layout; omit to use the built-in theme wrapper
builtInboolNoReserved for core styles; do not set in third-party code

$event->styleOverrides accepts the same css and template keys for any existing style key, including core ones. Overrides take precedence over the base style definition.

Access the service at runtime:

$emailStyles = \yui\craft\Plugin::getInstance()->getEmailStyles();

// List all registered styles (built-in + custom)
$styles = $emailStyles->getAllStyles();

// Get the CSS path for a given style key
$cssPath = $emailStyles->getStyleCssPath('corporate');

// Get the Twig layout path
$templatePath = $emailStyles->getStyleTemplatePath('corporate');

// Check whether a style key exists
if ($emailStyles->styleExists('corporate')) { ... }

Registering additional notification types

The notification preferences page (yui-core/notifications/preferences) lets admins opt in or out of individual notification types, grouped by the plugin that registers them. A type only appears there if a plugin registers it. craft-core defines and fires the EVENT_REGISTER_NOTIFICATION_TYPES event as an extension point, but does not register any notification types itself through it — registration is left entirely to other plugins/modules that listen on NotificationService::EVENT_REGISTER_NOTIFICATION_TYPES.

Listen to NotificationService::EVENT_REGISTER_NOTIFICATION_TYPES and push a NotificationType instance onto $event->types:

use Craft;
use yii\base\Event;
use yui\craftcore\events\RegisterNotificationTypesEvent;
use yui\craftcore\models\NotificationType;
use yui\craftcore\services\NotificationService;

Event::on(
NotificationService::class,
NotificationService::EVENT_REGISTER_NOTIFICATION_TYPES,
function (RegisterNotificationTypesEvent $event): void {
$event->types[] = new NotificationType([
'handle' => 'invoice.created',
'severity' => 'info',
'label' => Craft::t('superfaktura', 'Invoice created'),
'source' => 'superfaktura',
]);
}
);

NotificationType fields:

FieldTypeDefaultDescription
handlestring''Unique identifier for this type within source; combined with source (source::handle) as the preference storage key
sourcestring''Plugin/module identifier; also groups types under the same provider tab on the preferences page
labelstring''Human-readable name shown in the preferences list; translate with Craft::t()
severitystring'info'info, warning, or critical
groupstring''Sub-section label within the provider's tab; types with no group fall under General
descriptionstring''Optional short helper text shown under the label
providerLabelstring''Human-readable provider tab name; falls back to ucfirst(source) when empty

NotificationService::getRegisteredTypes() normalizes legacy listeners that push a plain array with a type key instead of a handle key, so older third-party registrations keep working, but new code should always use NotificationType with handle.

Registered types are grouped first by source/providerLabel (provider tabs), then by group (collapsible sections within a tab), and rendered as individually toggleable checkboxes on the preferences page. Registering a type does not create notifications by itself — it only makes an existing type/source pair (used with createNotification()) visible and toggleable here.

createNotification() skips creating a targeted ($userId-scoped) notification if the user has opted out of that source/type pair (or the whole source). Already-stored notifications — including broadcast ones (userId = null) — are only filtered out of getUnreadCount() and getLatestNotifications() when the entire source is opted out; a per-type opt-out does not retroactively hide broadcast notifications that were already created for that type.


PHP developer API

NotificationService implements NotificationServiceInterface and is registered as the notification component. Access it through the plugin:

$notifications = \yui\craft\Plugin::getInstance()->getNotification();

createNotification()

public function createNotification(
string $type,
string $severity,
string $title,
?string $message = null,
?string $url = null,
?int $siteId = null,
?int $userId = null,
): bool

Creates a new notification record and automatically dispatches the notification-created Flow trigger. Returns true on success.

ParameterConstraintsDescription
$typemax 50 charsCategory string: order, customer, system, flow, or any custom value
$severitymax 20 charsinfo, warning, or critical
$titlemax 255 charsShort title displayed in the header dropdown
$messageOptional additional context
$urlmax 500 charsOptional CP URL for the notification link
$siteIdSite scope; defaults to the current site when null
$userIdTarget user; null broadcasts to all admins

getUnreadCount(int $userId): int

Returns the unread notification count for a given user. Includes both broadcast notifications (userId = null) and those targeted directly to the user.

getLatestNotifications(int $userId, int $limit = 5): array

Returns the most recent notifications for a user as a plain array of records, ordered newest-first. Used by the CP header dropdown.

getAllNotifications(int $userId, int $page = 1, int $perPage = 20, ?int $siteId = null): array

Returns a paginated result set for the notifications index page. The return value is an array with the following keys:

KeyTypeDescription
notificationsarrayNotification rows for the current page
totalintTotal matching notification count
pageintCurrent page number
perPageintItems per page
totalPagesintTotal page count

Pass $siteId to filter notifications to a specific site. null returns notifications for all sites.

markAsRead(int $id, int $userId): bool

Marks a single notification as read. Only succeeds if the notification belongs to the user or is a broadcast notification.

markAllAsRead(int $userId): int

Marks all unread notifications for a user as read. Returns the number of rows updated.

deleteOlderThan(int $days = 90): int

Deletes notifications older than the given number of days. The plugin runs this automatically during a daily maintenance task. Returns the number of deleted rows.

Replacing the service

The service is bound to the notification component via NotificationServiceInterface. You can swap it in a module:

use yui\craft\Plugin;
use yui\craft\contracts\NotificationServiceInterface;

// In your module's init()
Plugin::getInstance()->set('notification', MyCustomNotificationService::class);

Your class must implement NotificationServiceInterface.



Audience check

AudienceCovered
Customer / storefrontNot applicable — CP notifications are internal admin-only; the customer account activity stub is not yet a functional feature in v2.2.1
Admin / Craft CPYes — bell icon header dropdown, paginated notifications index (yui/notifications), unread badge, store filter, auto-generated events, Flow action
Developer / integratorYes — NotificationService PHP API, NotificationServiceInterface for replacement, NotificationEventListener for auto-wiring custom events, email style RegisterEmailStyleEvent, notification type registration via EVENT_REGISTER_NOTIFICATION_TYPES, Flow create-notification action + notification-created trigger