-
Notifications
You must be signed in to change notification settings - Fork 81
Ibexa Notifications #3090
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 5.0
Are you sure you want to change the base?
Ibexa Notifications #3090
Changes from 31 commits
89296c0
0e7ca02
a137ac0
c3936c5
43b3df8
96f9146
ffe904d
0d9abdc
a76baed
233029e
f5dd2d9
7aa8f3a
c79653c
0840840
54254ab
5f6bc24
a4818cd
6965996
29b09c6
9c186a7
fad8c6a
91d8af7
251d463
2a748a5
fb3774a
1161d9f
64edb65
f463636
b7ef37e
818ae24
0ebcb95
3714c67
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| @use '../../vendor/ibexa/admin-ui/src/bundle/Resources/public/scss/_alerts.scss' as *; | ||
|
|
||
| .ibexa-alert { | ||
| &--notification { | ||
| @extend .ibexa-alert--info; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| framework: | ||
| notifier: | ||
| chatter_transports: | ||
| slack: '%env(SLACK_DSN)%' | ||
| ibexa: | ||
| system: | ||
| default: | ||
| notifier: | ||
| subscriptions: | ||
| # Default subscriptions from ibexa.yaml are properly merged. | ||
| # Custom subscriptions: | ||
| Ibexa\OrderManagement\Notification\OrderStatusChange: | ||
| channels: | ||
| - chat | ||
| Ibexa\Payment\Notification\PaymentStatusChange: | ||
| channels: | ||
| - chat | ||
| Ibexa\Shipping\Notification\ShipmentStatusChange: | ||
| channels: | ||
| - chat | ||
| App\Notifications\CommandExecuted: | ||
| channels: | ||
| - ibexa | ||
| - log | ||
| admin_group: | ||
| notifier: | ||
| subscriptions: | ||
| # Default subscriptions from ibexa_admin_ui.yaml are properly merged | ||
| # Custom subscriptions: | ||
| App\Notifications\CommandExecuted: | ||
| channels: | ||
| - ibexa | ||
| - log | ||
| App\Notifications\ControllerFeedback: | ||
| channels: | ||
| - browser | ||
| storefront_group: | ||
| notifier: | ||
| subscriptions: | ||
| # Default subscriptions from ibexa.yaml: | ||
| Ibexa\Contracts\User\Notification\UserPasswordReset: | ||
| channels: | ||
| Ibexa\Contracts\User\Notification\UserInvitation: | ||
| channels: | ||
| Ibexa\Contracts\FormBuilder\Notifications\FormSubmitted: | ||
| channels: | ||
| # Custom subscriptions: | ||
| Ibexa\OrderManagement\Notification\OrderStatusChange: | ||
| channels: | ||
| - chat | ||
| Ibexa\Payment\Notification\PaymentStatusChange: | ||
| channels: | ||
| - chat | ||
| Ibexa\Shipping\Notification\ShipmentStatusChange: | ||
| channels: | ||
| - chat | ||
| App\Notifications\CommandExecuted: | ||
| channels: | ||
| - ibexa | ||
| - log | ||
| App\Notifications\ControllerFeedback: | ||
| channels: | ||
| - browser |
| 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\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 (random_int(0, 1) == 1) { | ||
| throw new \RuntimeException('Something went wrong'); | ||
|
Check warning on line 39 in code_samples/user_management/notifications/src/Command/NotificationSenderCommand.php
|
||
| } | ||
| $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,28 @@ | ||
| <?php declare(strict_types=1); | ||
|
|
||
| namespace App\Controller; | ||
|
|
||
| use App\Notifications\ControllerFeedback; | ||
| use Ibexa\Contracts\Notifications\Service\NotificationServiceInterface; | ||
| use Ibexa\Contracts\Notifications\Value\Notification\SymfonyNotificationAdapter; | ||
| use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; | ||
| use Symfony\Component\HttpFoundation\Response; | ||
| use Symfony\Component\Routing\Attribute\Route; | ||
|
|
||
| class NotificationSenderController extends AbstractController | ||
| { | ||
| public function __construct( | ||
| private readonly NotificationServiceInterface $notificationService, | ||
| ) { | ||
| } | ||
|
|
||
| #[Route('/notification-sender')] | ||
| public function index(): Response | ||
| { | ||
| $this->notificationService->send( | ||
| new SymfonyNotificationAdapter((new ControllerFeedback('Message sent from controller'))->emoji('👍')), | ||
| ); | ||
|
|
||
| return $this->render('@ibexadesign/notification-sender-controller.html.twig'); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| <?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 | ||
| { | ||
| $errorCount = 'No error'; | ||
| if (count($this->exceptions) > 0) { | ||
| $plural = count($this->exceptions) > 1 ? 's' : ''; | ||
| $errorCount = count($this->exceptions) . ' error' . $plural; | ||
| } | ||
| $message = new SystemMessage($recipient->getUser()); | ||
| $message->setContext([ | ||
| 'icon' => Command::SUCCESS === $this->exitCode ? 'check-circle' : 'discard-circle', | ||
| 'subject' => $this->command->getName(), | ||
| 'content' => $errorCount, | ||
| ]); | ||
|
|
||
| return $message; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| <?php declare(strict_types=1); | ||
|
|
||
| namespace App\Notifications; | ||
|
|
||
| use Symfony\Component\Notifier\Notification\Notification; | ||
|
|
||
| class ControllerFeedback extends Notification | ||
| { | ||
| } |
| 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' => $notification::class, | ||
| 'importance' => $notification->getImportance(), | ||
| 'content' => $notification->getContent(), | ||
| ]); | ||
| } | ||
| } | ||
|
|
||
| public function supports(Notification $notification, RecipientInterface $recipient): bool | ||
| { | ||
| return true; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| {% extends '@ibexadesign/ui/layout.html.twig' %} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| {% extends '@ibexadesign/storefront/layout.html.twig' %} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| const fs = require('fs'); | ||
|
Check warning on line 1 in code_samples/user_management/notifications/webpack.config.js
|
||
| const path = require('path'); | ||
|
Check warning on line 2 in code_samples/user_management/notifications/webpack.config.js
|
||
| const Encore = require('@symfony/webpack-encore'); | ||
| const getWebpackConfigs = require('@ibexa/frontend-config/webpack-config/get-configs'); | ||
| const customConfigsPaths = require('./var/encore/ibexa.webpack.custom.config.js'); | ||
|
|
||
| const customConfigs = getWebpackConfigs(Encore, customConfigsPaths); | ||
| const isReactBlockPathCreated = fs.existsSync('./assets/page-builder/react/blocks'); | ||
|
|
||
| Encore.reset(); | ||
| Encore | ||
| .setOutputPath('public/build/') | ||
| .setPublicPath('/build') | ||
| .enableSassLoader() | ||
| .enableReactPreset((options) => { | ||
| options.runtime = 'classic'; | ||
| }) | ||
| .enableSingleRuntimeChunk() | ||
| .copyFiles({ | ||
| from: './assets/images', | ||
| to: 'images/[path][name].[ext]', | ||
| pattern: /\.(png|svg)$/, | ||
| }) | ||
| .configureBabelPresetEnv((config) => { | ||
| config.useBuiltIns = 'usage'; | ||
| config.corejs = 3; | ||
| }); | ||
|
|
||
| // Welcome page stylesheets | ||
| Encore.addEntry('welcome-page-css', [ | ||
| path.resolve(__dirname, './assets/scss/welcome-page.scss'), | ||
| ]); | ||
|
|
||
| // Welcome page javascripts | ||
| Encore.addEntry('welcome-page-js', [ | ||
| path.resolve(__dirname, './assets/js/welcome.page.js'), | ||
| ]); | ||
|
|
||
| if (isReactBlockPathCreated) { | ||
| // React Blocks javascript | ||
| Encore.addEntry('react-blocks-js', './assets/js/react.blocks.js'); | ||
| } | ||
|
|
||
| Encore.addEntry('app', './assets/app.js'); | ||
|
|
||
| const projectConfig = Encore.getWebpackConfig(); | ||
|
|
||
| projectConfig.name = 'app'; | ||
|
|
||
| const ibexaConfigManager = require('@ibexa/frontend-config/webpack-config/manager'); | ||
| const getIbexaConfig = require('@ibexa/frontend-config/webpack-config/ibexa'); | ||
| const ibexaConfig = getIbexaConfig(); | ||
|
|
||
| ibexaConfigManager.add({ | ||
| ibexaConfig, | ||
| entryName: 'ibexa-admin-ui-layout-css', | ||
| newItems: [ | ||
| path.resolve(__dirname, './assets/scss/notifications.scss'), | ||
| ], | ||
| }); | ||
|
|
||
| module.exports = [ibexaConfig, ...customConfigs, projectConfig]; | ||
Uh oh!
There was an error while loading. Please reload this page.