Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
services:
App\Notifier\Channel\LogChannel:
tags:
- { name: 'notifier.channel', channel: 'log' }
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

declare(strict_types=1);

namespace App\src\Command;

use App\Notifications\CommandExecuted;
use Ibexa\Contracts\Core\Repository\UserService;
use Ibexa\Contracts\Notifications\Service\NotificationServiceInterface;
use Ibexa\Contracts\Notifications\Value\Notification\SymfonyNotificationAdapter;
use Ibexa\Contracts\Notifications\Value\Recipent\SymfonyRecipientAdapter;
use Ibexa\Contracts\Notifications\Value\Recipent\UserRecipient;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Notifier\Recipient\RecipientInterface;

#[AsCommand(name: 'app:send_notification', description: 'Example of command sending a notification')]
class NotificationSenderCommand extends Command
{
/** @param array<int, string> $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<int, \Throwable> $exceptions */
$exceptions = [];

try {
// Do something
if (rand(0, 1) == 1) {
throw new \RuntimeException('Something went wrong');

Check warning on line 39 in code_samples/user_management/notifications/src/Command/NotificationSenderCommand.php

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define and throw a dedicated exception instead of using a generic one.

See more on https://sonarcloud.io/project/issues?id=ezsystems_developer-documentation&issues=AZz7BK7JSbHSMhyKnsyM&open=AZz7BK7JSbHSMhyKnsyM&pullRequest=3090
}
$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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php declare(strict_types=1);

namespace App\Notifications;

use Ibexa\Contracts\Notifications\SystemNotification\SystemMessage;
use Ibexa\Contracts\Notifications\SystemNotification\SystemNotificationInterface;
use Ibexa\Contracts\Notifications\Value\Recipent\UserRecipientInterface;
use Symfony\Bridge\Twig\Mime\NotificationEmail;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Notifier\Message\EmailMessage;
use Symfony\Component\Notifier\Notification\EmailNotificationInterface;
use Symfony\Component\Notifier\Notification\Notification;
use Symfony\Component\Notifier\Recipient\EmailRecipientInterface;
use Throwable;

class CommandExecuted extends Notification implements SystemNotificationInterface, EmailNotificationInterface
{
/** @param array<int, Throwable> $exceptions */
public function __construct(
private readonly Command $command,
private readonly int $exitCode,
private readonly array $exceptions
) {
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
{
$body = '';
foreach ($this->exceptions as $exception) {
$body .= $exception->getMessage() . '<br>';
}

$email = NotificationEmail::asPublicEmail()
->to($recipient->getEmail())
->subject($this->getSubject())
->html($body);

return new EmailMessage($email);
}

public function asSystemNotification(UserRecipientInterface $recipient, ?string $transport = null): ?SystemMessage
{
$message = new SystemMessage($recipient->getUser());
$message->setContext([
'icon' => Command::SUCCESS === $this->exitCode ? 'check-circle' : 'discard-circle',
'subject' => $this->command->getName(),
'content' => Command::SUCCESS === $this->exitCode ? 'No error' : count($this->exceptions) . ' error' . (count($this->exceptions) > 1 ? 's' : ''),

Check warning on line 49 in code_samples/user_management/notifications/src/Notifications/CommandExecuted.php

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=ezsystems_developer-documentation&issues=AZz8SjXllynT5ak2mLm6&open=AZz8SjXllynT5ak2mLm6&pullRequest=3090
]);

return $message;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php declare(strict_types=1);

namespace App\Notifier\Channel;

use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Symfony\Component\Notifier\Channel\ChannelInterface;
use Symfony\Component\Notifier\Notification\Notification;
use Symfony\Component\Notifier\Recipient\RecipientInterface;

class LogChannel implements ChannelInterface, LoggerAwareInterface
{
use LoggerAwareTrait;

public function notify(Notification $notification, RecipientInterface $recipient, ?string $transportName = null): void
{
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
{
return true;
}
}
14 changes: 10 additions & 4 deletions docs/administration/back_office/notifications.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@

# 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

Expand Down Expand Up @@ -56,7 +56,7 @@

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.

Check failure on line 59 in docs/administration/back_office/notifications.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/administration/back_office/notifications.md#L59

[Ibexa.Spellcheck] Did you really mean 'createStruct'?
Raw output
{"message": "[Ibexa.Spellcheck] Did you really mean 'createStruct'?", "location": {"path": "docs/administration/back_office/notifications.md", "range": {"start": {"line": 59, "column": 107}}}, "severity": "ERROR"}

Example:

Expand Down Expand Up @@ -117,3 +117,9 @@

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 channels](notification_channels.md) for more details about notifications and channel subscription.
2 changes: 1 addition & 1 deletion docs/administration/project_organization/bundles.md
Original file line number Diff line number Diff line change
Expand Up @@ -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|
Expand Down
8 changes: 8 additions & 0 deletions docs/getting_started/install_with_ddev.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
131 changes: 131 additions & 0 deletions docs/users/notification_channels.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
---
description: Notify users TODO.
month_change: true
---

# 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](notifications.md#create-custom-notifications).

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))

Check notice on line 10 in docs/users/notification_channels.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/users/notification_channels.md#L10

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/users/notification_channels.md", "range": {"start": {"line": 10, "column": 30}}}, "severity": "INFO"}

Check failure on line 10 in docs/users/notification_channels.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/users/notification_channels.md#L10

[Ibexa.DomainTerms] Use 'back office' instead of 'AdminUi'
Raw output
{"message": "[Ibexa.DomainTerms] Use 'back office' instead of 'AdminUi'", "location": {"path": "docs/users/notification_channels.md", "range": {"start": {"line": 10, "column": 218}}}, "severity": "ERROR"}
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)

## Subscribe to notifications

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`

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

Check notice on line 48 in docs/users/notification_channels.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/users/notification_channels.md#L48

[Ibexa.Whether] Consider replacing 'if' with 'whether' for clarity
Raw output
{"message": "[Ibexa.Whether] Consider replacing 'if' with 'whether' for clarity", "location": {"path": "docs/users/notification_channels.md", "range": {"start": {"line": 48, "column": 77}}}, "severity": "INFO"}
* `chat` - Notification sent to a communication platform like Slack, Microsoft Teams, Google Chat, etc.
* `desktop` - Notification sent to JoliNotif TODO: Do we support this?

Check warning on line 50 in docs/users/notification_channels.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/users/notification_channels.md#L50

[Ibexa.FirstPersonPronouns] Do not use first person pronouns unless specifically referring to our company.
Raw output
{"message": "[Ibexa.FirstPersonPronouns] Do not use first person pronouns unless specifically referring to our company.", "location": {"path": "docs/users/notification_channels.md", "range": {"start": {"line": 50, "column": 55}}}, "severity": "WARNING"}
* `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

Check notice on line 66 in docs/users/notification_channels.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/users/notification_channels.md#L66

[Ibexa.ArticlesInHeadings] Avoid articles in headings.
Raw output
{"message": "[Ibexa.ArticlesInHeadings] Avoid articles in headings.", "location": {"path": "docs/users/notification_channels.md", "range": {"start": {"line": 66, "column": 11}}}, "severity": "INFO"}

A new notification class can be created to send a new type of message to a new set of channels.

Check notice on line 68 in docs/users/notification_channels.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/users/notification_channels.md#L68

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/users/notification_channels.md", "range": {"start": {"line": 68, "column": 30}}}, "severity": "INFO"}
It must extend `Symfony\Component\Notifier\Notification\Notification`
and optionally implements some interfaces depending on the channels it could be sent to.

Check notice on line 70 in docs/users/notification_channels.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/users/notification_channels.md#L70

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/users/notification_channels.md", "range": {"start": {"line": 70, "column": 78}}}, "severity": "INFO"}

- 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

Check notice on line 75 in docs/users/notification_channels.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/users/notification_channels.md#L75

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/users/notification_channels.md", "range": {"start": {"line": 75, "column": 61}}}, "severity": "INFO"}
TODO: Namespaces, Ibexa custom vs Symfony native

Check failure on line 76 in docs/users/notification_channels.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/users/notification_channels.md#L76

[Ibexa.VariablesGlobal] Use global variable '[[= product_name_base =]]' instead of 'Ibexa'
Raw output
{"message": "[Ibexa.VariablesGlobal] Use global variable '[[= product_name_base =]]' instead of 'Ibexa'", "location": {"path": "docs/users/notification_channels.md", "range": {"start": {"line": 76, "column": 19}}}, "severity": "ERROR"}

| Channel | Notification interface | ! | Description |
|:--------|:----------------------------------------------------------------------------------------------------------------------------------------------------------|---|:------------|
| `chat` | `ChatNotificationInterface` | | TODO |
| `email` | `EmailNotificationInterface` | &#10004; | TODO |
| `ibexa` | [`SystemNotificationInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Notifications-SystemNotification-SystemNotificationInterface.html) | &#10004; | TODO |
| `sms` | `SmsNotificationInterface` | &#10004; | TODO |

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?

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.

Check warning on line 95 in docs/users/notification_channels.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/users/notification_channels.md#L95

[Ibexa.EOLWhitespace] Remove whitespace characters from the end of the line.
Raw output
{"message": "[Ibexa.EOLWhitespace] Remove whitespace characters from the end of the line.", "location": {"path": "docs/users/notification_channels.md", "range": {"start": {"line": 95, "column": 79}}}, "severity": "WARNING"}

First, a `CommandExecuted` notification type is created.

Check notice on line 97 in docs/users/notification_channels.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/users/notification_channels.md#L97

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/users/notification_channels.md", "range": {"start": {"line": 97, "column": 46}}}, "severity": "INFO"}
It is supported by two channels for the example but could be extended to more.

Check notice on line 98 in docs/users/notification_channels.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/users/notification_channels.md#L98

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/users/notification_channels.md", "range": {"start": {"line": 98, "column": 4}}}, "severity": "INFO"}

Check notice on line 98 in docs/users/notification_channels.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/users/notification_channels.md#L98

[Ibexa.Passive] Try to avoid passive tense, when possible.
Raw output
{"message": "[Ibexa.Passive] Try to avoid passive tense, when possible.", "location": {"path": "docs/users/notification_channels.md", "range": {"start": {"line": 98, "column": 59}}}, "severity": "INFO"}

``` 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`:

``` 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

``` php
[[= include_file('code_samples/user_management/notifications/src/Command/NotificationSenderCommand.php') =]]
```

TODO: Screenshots

## Create a channel

Check notice on line 119 in docs/users/notification_channels.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/users/notification_channels.md#L119

[Ibexa.ArticlesInHeadings] Avoid articles in headings.
Raw output
{"message": "[Ibexa.ArticlesInHeadings] Avoid articles in headings.", "location": {"path": "docs/users/notification_channels.md", "range": {"start": {"line": 119, "column": 11}}}, "severity": "INFO"}

A channel is a service implementing `Symfony\Component\Notifier\Channel\ChannelInterface`, and tagged `notifier.channel` alongside a `channel` shortname.

Check failure on line 121 in docs/users/notification_channels.md

View workflow job for this annotation

GitHub Actions / vale

[vale] docs/users/notification_channels.md#L121

[Ibexa.Spellcheck] Did you really mean 'shortname'?
Raw output
{"message": "[Ibexa.Spellcheck] Did you really mean 'shortname'?", "location": {"path": "docs/users/notification_channels.md", "range": {"start": {"line": 121, "column": 144}}}, "severity": "ERROR"}

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') =]]
```
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,7 @@ nav:
- User grouping:
- Customer groups: users/customer_groups.md
- Segment API: users/segment_api.md
- User notifications: users/notification_channels.md
- Personalization:
- Personalization: personalization/personalization.md
- Personalization guide : personalization/personalization_guide.md
Expand Down
Loading