From 89296c0aad8e9a56b5e2da3c73eb8d3a20a86a69 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Tue, 17 Mar 2026 09:35:58 +0100 Subject: [PATCH 01/10] Draft ibexa/notifications doc --- .../notifications/config/packages/ibexa.yaml | 18 +++++ .../src/Command/NotificationSenderCommand.php | 67 +++++++++++++++++++ .../src/Notifications/CommandExecuted.php | 55 +++++++++++++++ docs/users/notifications.md | 60 +++++++++++++++++ mkdocs.yml | 1 + 5 files changed, 201 insertions(+) create mode 100644 code_samples/user_management/notifications/config/packages/ibexa.yaml create mode 100644 code_samples/user_management/notifications/src/Command/NotificationSenderCommand.php create mode 100644 code_samples/user_management/notifications/src/Notifications/CommandExecuted.php create mode 100644 docs/users/notifications.md diff --git a/code_samples/user_management/notifications/config/packages/ibexa.yaml b/code_samples/user_management/notifications/config/packages/ibexa.yaml new file mode 100644 index 0000000000..5a385e7e5f --- /dev/null +++ b/code_samples/user_management/notifications/config/packages/ibexa.yaml @@ -0,0 +1,18 @@ +ibexa: + system: + default: + notifier: + subscriptions: + Ibexa\Contracts\User\Notification\UserPasswordReset: + channels: + - email + Ibexa\Contracts\User\Notification\UserInvitation: + channels: + - email + Ibexa\Contracts\FormBuilder\Notifications\FormSubmitted: + channels: + - email + App\Notifications\CommandExecuted: + channels: + - ibexa + - email diff --git a/code_samples/user_management/notifications/src/Command/NotificationSenderCommand.php b/code_samples/user_management/notifications/src/Command/NotificationSenderCommand.php new file mode 100644 index 0000000000..4d30f95423 --- /dev/null +++ b/code_samples/user_management/notifications/src/Command/NotificationSenderCommand.php @@ -0,0 +1,67 @@ + $recipientLogins */ + public function __construct( + private readonly NotificationServiceInterface $notificationService, + private readonly UserService $userService, + private readonly array $recipientLogins = ['admin'], + ) { + parent::__construct(); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + /** @var array $exceptions */ + $exceptions = []; + + try { + // Do something + if (rand(0, 1) == 1) { + throw new \RuntimeException('Something went wrong'); + } + $exitCode = Command::SUCCESS; + } catch (\Exception $exception) { + $exceptions[] = $exception; + $exitCode = Command::FAILURE; + } + + $recipients = []; + foreach ($this->recipientLogins as $login) { + try { + $user = $this->userService->loadUserByLogin($login); + $recipients[] = new UserRecipient($user); + } catch (\Exception $exception) { + $exceptions[] = $exception; + } + } + + $this->notificationService->send( + new SymfonyNotificationAdapter(new CommandExecuted($this, $exitCode, $exceptions)), + array_map( + static fn (RecipientInterface $recipient): SymfonyRecipientAdapter => new SymfonyRecipientAdapter($recipient), + $recipients + ) + ); + + return $exitCode; + } +} diff --git a/code_samples/user_management/notifications/src/Notifications/CommandExecuted.php b/code_samples/user_management/notifications/src/Notifications/CommandExecuted.php new file mode 100644 index 0000000000..47f2a5b3f8 --- /dev/null +++ b/code_samples/user_management/notifications/src/Notifications/CommandExecuted.php @@ -0,0 +1,55 @@ + $exceptions */ + public function __construct( + private readonly Command $command, + private readonly int $exitCode, + private readonly array $exceptions + ) { + parent::__construct($this->command->getName()); + } + + public function asEmailMessage(EmailRecipientInterface $recipient, ?string $transport = null): ?EmailMessage + { + $subject = (0 === $this->exitCode ? '✔' : '✖') . $this->command->getName(); + + $body = ''; + foreach ($this->exceptions as $exception) { + $body .= $exception->getMessage() . '
'; + } + + $email = NotificationEmail::asPublicEmail() + ->to($recipient->getEmail()) + ->subject($subject) + ->html($body); + + return new EmailMessage($email); + } + + public function asSystemNotification(UserRecipientInterface $recipient, ?string $transport = null): ?SystemMessage + { + $message = new SystemMessage($recipient->getUser()); + $message->setContext([ + 'icon' => 0 === $this->exitCode ? 'check-circle' : 'discard-circle', + 'subject' => $this->command->getName(), + 'content' => 0 === $this->exitCode ? 'No error' : count($this->exceptions) . ' error' . (count($this->exceptions) > 1 ? 's' : ''), + ]); + + return $message; + } +} diff --git a/docs/users/notifications.md b/docs/users/notifications.md new file mode 100644 index 0000000000..df648661aa --- /dev/null +++ b/docs/users/notifications.md @@ -0,0 +1,60 @@ +--- +description: Notify users TODO. +month_change: true +--- + +# Notifications + +TODO: [notification bar](/administration/back_office/notifications.md) VS [🔔 user notification](/administration/back_office/notifications.md#create-custom-notifications) VS this + +TODO: +[Ibexa\Contracts\Core\Repository\NotificationService](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-NotificationService.html) +versus +[Ibexa\Contracts\Notifications\Service\NotificationServiceInterface](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Notifications-Service-NotificationServiceInterface.html) + +Based on [Symfony notifier]([[= symfony_doc =]]/notifier.html) + +Use an existing notification class + +TODO: List available classes + +Create a notification class + +TODO: List what type of channel notification interfaces can be implemented +TODO: Namespaces, Ibexa custom vs Symfony native + +| Channel | Notification interface | Description | +|:--------|:----------------------------------------------------------------------------------------------------------------------------------------------------------|:------------| +| `ibexa` | [`SystemNotificationInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Notifications-SystemNotification-SystemNotificationInterface.html) | TODO | +| `email` | `EmailNotificationInterface` | TODO | +| `chat` | `ChatNotificationInterface` | TODO | +| `sms` | `SmsNotificationInterface` | TODO | + +TODO: About `ibexa` channel being the [🔔 user notification](/administration/back_office/notifications.md#create-custom-notifications) +https://github.com/ibexa/notifications/blob/v5.0.6/src/lib/SystemNotification/SystemNotificationChannel.php#L51 + +TODO: About `SymfonyNotificationAdapter` and `SymfonyRecipientAdapter` + +### Example + +The following example is a command that sends a notification to users on several channels simultaneously. +it could be a scheduled task, a cronjob, warning users about its final result. + +First, a `CommandExecuted` notification type is created. +It is supported by two channels for the example but could be extended to more. + +``` php +[[= include_file('code_samples/user_management/notifications/src/Notifications/CommandExecuted.php') =]] +``` + +The channels subscribing to this notification are set in `config/packages/ibexa.yaml` below the default ones: + +``` yaml +[[= include_file('code_samples/user_management/notifications/config/packages/ibexa.yaml') =]] +``` + +TODO: Explain the command + +``` php +[[= include_file('code_samples/user_management/notifications/src/Command/NotificationSenderCommand.php') =]] +``` diff --git a/mkdocs.yml b/mkdocs.yml index c7cb98d738..6262ffb473 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -479,6 +479,7 @@ nav: - User grouping: - Customer groups: users/customer_groups.md - Segment API: users/segment_api.md + - User notifications: users/notifications.md - Personalization: - Personalization: personalization/personalization.md - Personalization guide : personalization/personalization_guide.md From 0e7ca027af40d479677ba6c7d1a6c89f191a5b48 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Tue, 17 Mar 2026 09:37:14 +0100 Subject: [PATCH 02/10] install_with_ddev.md: Mailer --- docs/getting_started/install_with_ddev.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/getting_started/install_with_ddev.md b/docs/getting_started/install_with_ddev.md index bc28a942c8..22b5122471 100644 --- a/docs/getting_started/install_with_ddev.md +++ b/docs/getting_started/install_with_ddev.md @@ -88,6 +88,14 @@ Depending on your database of choice (MySQL or PostgreSQL), use the appropriate ddev config --web-environment-add DATABASE_URL=postgresql://db:db@db:5432/db ``` +#### Configure mailer (optional) + +You can configure [Symfony Mailer]([[= symfony_doc =]]/mailer.html) to use the [integrated mail catcher Mailpit](https://docs.ddev.com/en/stable/users/usage/developer-tools/#email-capture-and-review-mailpit): + +```bash +ddev config --web-environment-add MAILER_DSN=smtp://localhost:1025 +``` + #### Enable Mutagen (optional) If you're using macOS or Windows, you might want to enable [Mutagen](https://ddev.readthedocs.io/en/latest/users/install/performance/#mutagen) to improve performance. From a137ac02d904e50162e291b856471d91545622cd Mon Sep 17 00:00:00 2001 From: adriendupuis Date: Tue, 17 Mar 2026 08:55:08 +0000 Subject: [PATCH 03/10] PHP & JS CS Fixes --- .../notifications/src/Notifications/CommandExecuted.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code_samples/user_management/notifications/src/Notifications/CommandExecuted.php b/code_samples/user_management/notifications/src/Notifications/CommandExecuted.php index 47f2a5b3f8..819879c97e 100644 --- a/code_samples/user_management/notifications/src/Notifications/CommandExecuted.php +++ b/code_samples/user_management/notifications/src/Notifications/CommandExecuted.php @@ -1,4 +1,4 @@ - Date: Tue, 17 Mar 2026 12:37:08 +0100 Subject: [PATCH 04/10] BO notifications.md: Minor fixes --- docs/administration/back_office/notifications.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/administration/back_office/notifications.md b/docs/administration/back_office/notifications.md index 29c2d69f54..60e57f09bd 100644 --- a/docs/administration/back_office/notifications.md +++ b/docs/administration/back_office/notifications.md @@ -5,17 +5,17 @@ month_change: false # Notifications -You can send two types on notifications to the users. +You can send two types of notifications to the users. [Notification bar](#notification-bars) is displayed in specific situations as a message bar appearing at the bottom of the page. It appears to whoever is doing a specific operation in the back office. -![Example of an info notification](notification2.png "Example of the notification bar") +![Example of an info notification](notification2.png "Example of notification bar") [Custom notifications](#create-custom-notifications) are sent to a specific user. They appear in their profile in the back office. -![Notification in profile](notification3.png) +![Notification in profile](notification3.png "Profile notification bell menu") ## Notification bars @@ -56,7 +56,7 @@ Dispatch the event with `document.body.dispatchEvent(eventInfo);`. You can send your own custom notifications to the user which are displayed in the user menu. -To create a new notification you must use the `createNotification(Ibexa\Contracts\Core\Repository\Values\Notification\CreateStruct $createStruct)` method from `Ibexa\Contracts\Core\Repository\NotificationService`. +To create a new notification you can use the [core `NotificationService::createNotification(CreateStruct $createStruct)` method](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-NotificationService.html#method_createNotification) like in the example below. Example: @@ -117,3 +117,9 @@ ibexa: The values shown above are the defaults. `0` means the notification doesn't hide automatically. + +### `ibexa` notification channel + +You can also subscribe to a notification with the channel `ibexa` + +See [Notifications to users](users/notifications.md) for more details about notifications and channel subscription. From 43b3df8536aca850b083f1cc17be3473f51c2257 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Tue, 17 Mar 2026 12:39:13 +0100 Subject: [PATCH 05/10] Users notifications.md: Continue --- .../config/packages/custom_notifications.yaml | 22 ++++++++++ .../notifications/config/packages/ibexa.yaml | 18 -------- docs/users/notifications.md | 44 ++++++++++++++----- 3 files changed, 56 insertions(+), 28 deletions(-) create mode 100644 code_samples/user_management/notifications/config/packages/custom_notifications.yaml delete mode 100644 code_samples/user_management/notifications/config/packages/ibexa.yaml diff --git a/code_samples/user_management/notifications/config/packages/custom_notifications.yaml b/code_samples/user_management/notifications/config/packages/custom_notifications.yaml new file mode 100644 index 0000000000..5674488f6d --- /dev/null +++ b/code_samples/user_management/notifications/config/packages/custom_notifications.yaml @@ -0,0 +1,22 @@ +framework: + notifier: + chatter_transports: + slack: '%env(SLACK_DSN)%' +ibexa: + system: + default: + notifier: + subscriptions: + Ibexa\OrderManagement\Notification\OrderStatusChange: + channels: + - chat + Ibexa\Payment\Notification\PaymentStatusChange: + channels: + - chat + Ibexa\Shipping\Notification\ShipmentStatusChange: + channels: + - chat + App\Notifications\CommandExecuted: + channels: + - ibexa + - email diff --git a/code_samples/user_management/notifications/config/packages/ibexa.yaml b/code_samples/user_management/notifications/config/packages/ibexa.yaml deleted file mode 100644 index 5a385e7e5f..0000000000 --- a/code_samples/user_management/notifications/config/packages/ibexa.yaml +++ /dev/null @@ -1,18 +0,0 @@ -ibexa: - system: - default: - notifier: - subscriptions: - Ibexa\Contracts\User\Notification\UserPasswordReset: - channels: - - email - Ibexa\Contracts\User\Notification\UserInvitation: - channels: - - email - Ibexa\Contracts\FormBuilder\Notifications\FormSubmitted: - channels: - - email - App\Notifications\CommandExecuted: - channels: - - ibexa - - email diff --git a/docs/users/notifications.md b/docs/users/notifications.md index df648661aa..41d7bb3e0f 100644 --- a/docs/users/notifications.md +++ b/docs/users/notifications.md @@ -5,18 +5,39 @@ month_change: true # Notifications -TODO: [notification bar](/administration/back_office/notifications.md) VS [🔔 user notification](/administration/back_office/notifications.md#create-custom-notifications) VS this +the `ibexa/notifications` package offers an extension to the [Symfony notifier]([[= symfony_doc =]]/notifier.html) allowing to subscribe to notifications and sent them to information channels like email addresses, SMS, communication platforms, etc., including the [🔔 Back Office user profile notification](/administration/back_office/notifications.md#create-custom-notifications). -TODO: -[Ibexa\Contracts\Core\Repository\NotificationService](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-NotificationService.html) -versus -[Ibexa\Contracts\Notifications\Service\NotificationServiceInterface](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Notifications-Service-NotificationServiceInterface.html) +Those notifications must not be confused with the [notification bars](/administration/back_office/notifications.md) (sent with [`TranslatableNotificationHandlerInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-AdminUi-Notification-TranslatableNotificationHandlerInterface.html)) +or the [🔔 user notifications](/administration/back_office/notifications.md#create-custom-notifications) (sent with [`Ibexa\Contracts\Core\Repository\NotificationService`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-NotificationService.html)). -Based on [Symfony notifier]([[= symfony_doc =]]/notifier.html) +TODO: Introduce the [`Ibexa\Contracts\Notifications\Service\NotificationServiceInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Notifications-Service-NotificationServiceInterface.html) -Use an existing notification class +## Subscribe to notifications -TODO: List available classes +Some events send notifications you can subscribe to with one or more channels. + +Available notifications: + +* [`Ibexa\Contracts\FormBuilder\Notifications\FormSubmitted`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-FormBuilder-Notifications-FormSubmitted.html) +* [`Ibexa\Contracts\Notifications\SystemNotification\SystemNotification`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Notifications-SystemNotification-SystemNotification.html) +* [`Ibexa\Contracts\OrderManagement\Notification\OrderStatusChange`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-OrderManagement-Notification-OrderStatusChange.html) +* [`Ibexa\Contracts\Payment\Notification\PaymentStatusChange`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Payment-Notification-PaymentStatusChange.html) +* [`Ibexa\Contracts\Shipping\Notification\ShipmentStatusChange`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Shipping-Notification-ShipmentStatusChange.html) +* [`Ibexa\Contracts\User\Notification\UserInvitation`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-User-Notification-UserInvitation.html) +* [`Ibexa\Contracts\User\Notification\UserPasswordReset`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-User-Notification-UserPasswordReset.html) +* [`Ibexa\Contracts\User\Notification\UserRegister`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-User-Notification-UserRegister.html) + +TODO: What about notifications outside the `Ibexa\Contracts` namespace?? + +* `Ibexa\Share\Notification\ContentEditInvitationNotification` +* `Ibexa\Share\Notification\ContentViewInvitationNotification` +* `Ibexa\Share\Notification\ExternalParticipantContentViewInvitationNotification` + +For example, let's subscribe to Commerce activity with a Slack channel: + +``` yaml +[[= include_file('code_samples/user_management/notifications/config/packages/custom_notifications.yaml', 0, 18) =]] +``` Create a notification class @@ -33,6 +54,8 @@ TODO: Namespaces, Ibexa custom vs Symfony native TODO: About `ibexa` channel being the [🔔 user notification](/administration/back_office/notifications.md#create-custom-notifications) https://github.com/ibexa/notifications/blob/v5.0.6/src/lib/SystemNotification/SystemNotificationChannel.php#L51 +TODO: How to deal with channels not needing a user like `chat` + Slack channel? + TODO: About `SymfonyNotificationAdapter` and `SymfonyRecipientAdapter` ### Example @@ -47,10 +70,11 @@ It is supported by two channels for the example but could be extended to more. [[= include_file('code_samples/user_management/notifications/src/Notifications/CommandExecuted.php') =]] ``` -The channels subscribing to this notification are set in `config/packages/ibexa.yaml` below the default ones: +The channels subscribing to this notification are set in `config/packages/ibexa.yaml`: ``` yaml -[[= include_file('code_samples/user_management/notifications/config/packages/ibexa.yaml') =]] +[[= include_file('code_samples/user_management/notifications/config/packages/custom_notifications.yaml', 5, 9) =]] # … +[[= include_file('code_samples/user_management/notifications/config/packages/custom_notifications.yaml', 18) =]] ``` TODO: Explain the command From 96f91460842aaddd8291035126154cd51dcb06dd Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Tue, 17 Mar 2026 15:35:42 +0100 Subject: [PATCH 06/10] CommandExecuted enhancement --- .../src/Notifications/CommandExecuted.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/code_samples/user_management/notifications/src/Notifications/CommandExecuted.php b/code_samples/user_management/notifications/src/Notifications/CommandExecuted.php index 819879c97e..489cce61bb 100644 --- a/code_samples/user_management/notifications/src/Notifications/CommandExecuted.php +++ b/code_samples/user_management/notifications/src/Notifications/CommandExecuted.php @@ -21,13 +21,12 @@ public function __construct( private readonly int $exitCode, private readonly array $exceptions ) { - parent::__construct($this->command->getName()); + parent::__construct((Command::SUCCESS === $this->exitCode ? '✔' : '✖') . $this->command->getName()); + $this->importance(Command::SUCCESS === $this->exitCode ? Notification::IMPORTANCE_LOW : Notification::IMPORTANCE_HIGH); } public function asEmailMessage(EmailRecipientInterface $recipient, ?string $transport = null): ?EmailMessage { - $subject = (0 === $this->exitCode ? '✔' : '✖') . $this->command->getName(); - $body = ''; foreach ($this->exceptions as $exception) { $body .= $exception->getMessage() . '
'; @@ -35,7 +34,7 @@ public function asEmailMessage(EmailRecipientInterface $recipient, ?string $tran $email = NotificationEmail::asPublicEmail() ->to($recipient->getEmail()) - ->subject($subject) + ->subject($this->getSubject()) ->html($body); return new EmailMessage($email); @@ -45,9 +44,9 @@ public function asSystemNotification(UserRecipientInterface $recipient, ?string { $message = new SystemMessage($recipient->getUser()); $message->setContext([ - 'icon' => 0 === $this->exitCode ? 'check-circle' : 'discard-circle', + 'icon' => Command::SUCCESS === $this->exitCode ? 'check-circle' : 'discard-circle', 'subject' => $this->command->getName(), - 'content' => 0 === $this->exitCode ? 'No error' : count($this->exceptions) . ' error' . (count($this->exceptions) > 1 ? 's' : ''), + 'content' => Command::SUCCESS === $this->exitCode ? 'No error' : count($this->exceptions) . ' error' . (count($this->exceptions) > 1 ? 's' : ''), ]); return $message; From ffe904df8902bf2c69657316d13fab5caf93f8ff Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Tue, 17 Mar 2026 15:42:55 +0100 Subject: [PATCH 07/10] Continue users/notifications.md; Add channel examples --- .../notifications/config/services.yaml | 4 ++ .../src/Notifier/Channel/LogChannel.php | 28 +++++++++ docs/users/notifications.md | 61 ++++++++++++++++--- 3 files changed, 86 insertions(+), 7 deletions(-) create mode 100644 code_samples/user_management/notifications/config/services.yaml create mode 100644 code_samples/user_management/notifications/src/Notifier/Channel/LogChannel.php diff --git a/code_samples/user_management/notifications/config/services.yaml b/code_samples/user_management/notifications/config/services.yaml new file mode 100644 index 0000000000..51d97cdfbe --- /dev/null +++ b/code_samples/user_management/notifications/config/services.yaml @@ -0,0 +1,4 @@ +services: + App\Notifier\Channel\LogChannel: + tags: + - { name: 'notifier.channel', channel: 'log' } diff --git a/code_samples/user_management/notifications/src/Notifier/Channel/LogChannel.php b/code_samples/user_management/notifications/src/Notifier/Channel/LogChannel.php new file mode 100644 index 0000000000..8611a69a3d --- /dev/null +++ b/code_samples/user_management/notifications/src/Notifier/Channel/LogChannel.php @@ -0,0 +1,28 @@ +logger->info($notification->getSubject(), [ + 'class' => get_class($notification), + 'importance' => $notification->getImportance(), + 'content' => $notification->getContent(), + ]); + } + + public function supports(Notification $notification, RecipientInterface $recipient): bool + { + return true; + } +} diff --git a/docs/users/notifications.md b/docs/users/notifications.md index 41d7bb3e0f..fb2e10864a 100644 --- a/docs/users/notifications.md +++ b/docs/users/notifications.md @@ -33,23 +33,54 @@ TODO: What about notifications outside the `Ibexa\Contracts` namespace?? * `Ibexa\Share\Notification\ContentViewInvitationNotification` * `Ibexa\Share\Notification\ExternalParticipantContentViewInvitationNotification` +Available notification channels: + +```bash +php bin/console debug:container --tag=notifier.channel +``` + For example, let's subscribe to Commerce activity with a Slack channel: +```bash +composer require symfony/slack-notifier +``` + +* `browser` - Notification as flash message TODO: Test from a controller to see if it works +* `chat` - Notification sent to a communication platform like Slack, Microsoft Teams, Google Chat, etc. +* `desktop` - Notification sent to JoliNotif TODO: Do we support this? +* `email` - Notification sent to email addresses +* `ibexa` - Notification sent to back office user profiles +* `push` - TODO +* `sms` - Notification sent to phone numbers + +In a .env file, [set the DSN for the targetted Slack channel or user](https://github.com/symfony/slack-notifier?tab=readme-ov-file#dsn-example): + +```dotenv +SLACK_DSN=slack://xoxb-token@default?channel=ibexa-notifications +``` + ``` yaml [[= include_file('code_samples/user_management/notifications/config/packages/custom_notifications.yaml', 0, 18) =]] ``` -Create a notification class +## Create a notification class + +A new notification class can be created to send a new type of message to a new set of channels. +It must extend `Symfony\Component\Notifier\Notification\Notification` +and optionally implements some interfaces depending on the channels it could be sent to. + +- Some channels don't accept the notification if it doesn't implement its related notification interface. +- Some channels accept every notification and have a default behavior if the notification doesn't implement their related notification interface. TODO: List what type of channel notification interfaces can be implemented TODO: Namespaces, Ibexa custom vs Symfony native -| Channel | Notification interface | Description | -|:--------|:----------------------------------------------------------------------------------------------------------------------------------------------------------|:------------| -| `ibexa` | [`SystemNotificationInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Notifications-SystemNotification-SystemNotificationInterface.html) | TODO | -| `email` | `EmailNotificationInterface` | TODO | -| `chat` | `ChatNotificationInterface` | TODO | -| `sms` | `SmsNotificationInterface` | TODO | +| Channel | Notification interface | ! | Description | +|:--------|:----------------------------------------------------------------------------------------------------------------------------------------------------------|---|:------------| +| `chat` | `ChatNotificationInterface` | | TODO | +| `email` | `EmailNotificationInterface` | ✔ | TODO | +| `ibexa` | [`SystemNotificationInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Notifications-SystemNotification-SystemNotificationInterface.html) | ✔ | TODO | +| `sms` | `SmsNotificationInterface` | ✔ | TODO | TODO: About `ibexa` channel being the [🔔 user notification](/administration/back_office/notifications.md#create-custom-notifications) https://github.com/ibexa/notifications/blob/v5.0.6/src/lib/SystemNotification/SystemNotificationChannel.php#L51 @@ -82,3 +113,19 @@ TODO: Explain the command ``` php [[= include_file('code_samples/user_management/notifications/src/Command/NotificationSenderCommand.php') =]] ``` + +TODO: Screenshots + +## Create a channel + +A channel is a service implementing `Symfony\Component\Notifier\Channel\ChannelInterface`, and tagged `notifier.channel` alongside a `channel` shortname. + +The following example is a custom channel that sends notifications to the logger. + +``` php +[[= include_file('code_samples/user_management/notifications/src/Notifier/Channel/LogChannel.php') =]] +``` + +``` yaml +[[= include_file('code_samples/user_management/notifications/config/services.yaml') =]] +``` From 0d9abdcc33723ab68c5dd5a701d9f87c945105a4 Mon Sep 17 00:00:00 2001 From: adriendupuis Date: Tue, 17 Mar 2026 14:53:04 +0000 Subject: [PATCH 08/10] PHP & JS CS Fixes --- .../notifications/src/Notifier/Channel/LogChannel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code_samples/user_management/notifications/src/Notifier/Channel/LogChannel.php b/code_samples/user_management/notifications/src/Notifier/Channel/LogChannel.php index 8611a69a3d..3bf3bda913 100644 --- a/code_samples/user_management/notifications/src/Notifier/Channel/LogChannel.php +++ b/code_samples/user_management/notifications/src/Notifier/Channel/LogChannel.php @@ -1,4 +1,4 @@ - Date: Wed, 18 Mar 2026 10:05:31 +0100 Subject: [PATCH 09/10] =?UTF-8?q?users/notifications.md=20=E2=86=92=20user?= =?UTF-8?q?s/notification=5Fchannels.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mainly to avoid confusion with administration/back_office/notifications.md and fix duplicate: WARNING - Doc file 'administration/back_office/notifications.md' contains a link 'users/notifications.md', but the target 'administration/back_office/users/notifications.md' is not found among documentation files. WARNING - AutoLinksPlugin: Duplicate filename referred to in 'docs/administration/project_organization/bundles.md': 'notifications.md' exists at ['docs/administration/back_office/notifications.md', 'docs/users/notifications.md'] WARNING - AutoLinksPlugin: Duplicate filename referred to in 'docs/api/event_reference/other_events.md': 'notifications.md' exists at ['docs/administration/back_office/notifications.md', 'docs/users/notifications.md'] WARNING - AutoLinksPlugin: Duplicate filename referred to in 'docs/resources/new_in_doc.md': 'notifications.md' exists at ['docs/administration/back_office/notifications.md', 'docs/users/notifications.md'] --- docs/administration/back_office/notifications.md | 2 +- docs/administration/project_organization/bundles.md | 2 +- .../{notifications.md => notification_channels.md} | 10 +++++----- mkdocs.yml | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) rename docs/users/{notifications.md => notification_channels.md} (89%) diff --git a/docs/administration/back_office/notifications.md b/docs/administration/back_office/notifications.md index 60e57f09bd..5e41f3dd3e 100644 --- a/docs/administration/back_office/notifications.md +++ b/docs/administration/back_office/notifications.md @@ -122,4 +122,4 @@ The values shown above are the defaults. You can also subscribe to a notification with the channel `ibexa` -See [Notifications to users](users/notifications.md) for more details about notifications and channel subscription. +See [Notifications to channels](notification_channels.md) for more details about notifications and channel subscription. diff --git a/docs/administration/project_organization/bundles.md b/docs/administration/project_organization/bundles.md index be3c393e9a..36bcf221d5 100644 --- a/docs/administration/project_organization/bundles.md +++ b/docs/administration/project_organization/bundles.md @@ -54,7 +54,7 @@ To remove a bundle (either one you created yourself, or an out-of-the-box one th |[ibexa/http-cache](https://github.com/ibexa/http-cache)|[HTTP cache handling](http_cache.md), using multi tagging| |[ibexa/i18n](https://github.com/ibexa/i18n)|Centralized translations to ease synchronization with Crowdin| |[ibexa/messenger](https://github.com/ibexa/messenger)|[Background and asynchronous task processing](background_tasks.md) using Symfony Messenger| -|[ibexa/notifications](https://github.com/ibexa/notifications)| Sending [notifications](notifications.md)| +|[ibexa/notifications](https://github.com/ibexa/notifications)| Sending [notifications to channels](notification_channels.md)| |[ibexa/post-install](https://github.com/ibexa/post-install)|Apache and nginx templates| |[ibexa/rest](https://github.com/ibexa/rest)|REST API| |[ibexa/search](https://github.com/ibexa/search)|Common search functionalities| diff --git a/docs/users/notifications.md b/docs/users/notification_channels.md similarity index 89% rename from docs/users/notifications.md rename to docs/users/notification_channels.md index fb2e10864a..839766daf8 100644 --- a/docs/users/notifications.md +++ b/docs/users/notification_channels.md @@ -3,12 +3,12 @@ description: Notify users TODO. month_change: true --- -# Notifications +# Notification channels -the `ibexa/notifications` package offers an extension to the [Symfony notifier]([[= symfony_doc =]]/notifier.html) allowing to subscribe to notifications and sent them to information channels like email addresses, SMS, communication platforms, etc., including the [🔔 Back Office user profile notification](/administration/back_office/notifications.md#create-custom-notifications). +the `ibexa/notifications` package offers an extension to the [Symfony notifier]([[= symfony_doc =]]/notifier.html) allowing to subscribe to notifications and sent them to information channels like email addresses, SMS, communication platforms, etc., including the [🔔 Back Office user profile notification](notifications.md#create-custom-notifications). -Those notifications must not be confused with the [notification bars](/administration/back_office/notifications.md) (sent with [`TranslatableNotificationHandlerInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-AdminUi-Notification-TranslatableNotificationHandlerInterface.html)) -or the [🔔 user notifications](/administration/back_office/notifications.md#create-custom-notifications) (sent with [`Ibexa\Contracts\Core\Repository\NotificationService`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-NotificationService.html)). +Those notifications must not be confused with the [notification bars](notifications.md#notification-bars) (sent with [`TranslatableNotificationHandlerInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-AdminUi-Notification-TranslatableNotificationHandlerInterface.html)) +or the [🔔 user notifications](notifications.md#create-custom-notifications) (sent with [`Ibexa\Contracts\Core\Repository\NotificationService`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-NotificationService.html)). TODO: Introduce the [`Ibexa\Contracts\Notifications\Service\NotificationServiceInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Notifications-Service-NotificationServiceInterface.html) @@ -82,7 +82,7 @@ TODO: Namespaces, Ibexa custom vs Symfony native | `ibexa` | [`SystemNotificationInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Notifications-SystemNotification-SystemNotificationInterface.html) | ✔ | TODO | | `sms` | `SmsNotificationInterface` | ✔ | TODO | -TODO: About `ibexa` channel being the [🔔 user notification](/administration/back_office/notifications.md#create-custom-notifications) +TODO: About `ibexa` channel being the [🔔 user notification](notifications.md#create-custom-notifications) https://github.com/ibexa/notifications/blob/v5.0.6/src/lib/SystemNotification/SystemNotificationChannel.php#L51 TODO: How to deal with channels not needing a user like `chat` + Slack channel? diff --git a/mkdocs.yml b/mkdocs.yml index 6262ffb473..912fc50326 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -479,7 +479,7 @@ nav: - User grouping: - Customer groups: users/customer_groups.md - Segment API: users/segment_api.md - - User notifications: users/notifications.md + - User notifications: users/notification_channels.md - Personalization: - Personalization: personalization/personalization.md - Personalization guide : personalization/personalization_guide.md From 7aa8f3a96bcd765c13edd29e6c54c7828525be36 Mon Sep 17 00:00:00 2001 From: Adrien Dupuis <61695653+adriendupuis@users.noreply.github.com> Date: Wed, 18 Mar 2026 10:14:48 +0100 Subject: [PATCH 10/10] LogChannel: $this->logger isn't guaranteed Trying to fix "Cannot call method info() on Psr\Log\LoggerInterface|null." --- .../src/Notifier/Channel/LogChannel.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/code_samples/user_management/notifications/src/Notifier/Channel/LogChannel.php b/code_samples/user_management/notifications/src/Notifier/Channel/LogChannel.php index 3bf3bda913..80bc374566 100644 --- a/code_samples/user_management/notifications/src/Notifier/Channel/LogChannel.php +++ b/code_samples/user_management/notifications/src/Notifier/Channel/LogChannel.php @@ -14,11 +14,13 @@ class LogChannel implements ChannelInterface, LoggerAwareInterface public function notify(Notification $notification, RecipientInterface $recipient, ?string $transportName = null): void { - $this->logger->info($notification->getSubject(), [ - 'class' => get_class($notification), - 'importance' => $notification->getImportance(), - 'content' => $notification->getContent(), - ]); + if (isset($this->logger)) { + $this->logger->info($notification->getSubject(), [ + 'class' => get_class($notification), + 'importance' => $notification->getImportance(), + 'content' => $notification->getContent(), + ]); + } } public function supports(Notification $notification, RecipientInterface $recipient): bool