mirror of
https://github.com/flarum/framework.git
synced 2024-11-29 21:11:55 +08:00
Add Notification Channel Extender (#2432)
This commit is contained in:
parent
5dddc2e663
commit
185a737273
|
@ -16,23 +16,63 @@ class Notification implements ExtenderInterface
|
|||
{
|
||||
private $blueprints = [];
|
||||
private $serializers = [];
|
||||
private $drivers = [];
|
||||
private $typesEnabledByDefault = [];
|
||||
|
||||
public function type(string $blueprint, string $serializer, array $channelsEnabledByDefault = [])
|
||||
/**
|
||||
* @param string $blueprint The ::class attribute of the blueprint class.
|
||||
* This blueprint should implement \Flarum\Notification\Blueprint\BlueprintInterface.
|
||||
* @param string $serializer The ::class attribute of the serializer class.
|
||||
* This serializer should extend from \Flarum\Api\Serializer\AbstractSerializer.
|
||||
* @param string[] $driversEnabledByDefault The names of the drivers enabled by default for this notification type.
|
||||
* (example: alert, email).
|
||||
* @return self
|
||||
*/
|
||||
public function type(string $blueprint, string $serializer, array $driversEnabledByDefault = [])
|
||||
{
|
||||
$this->blueprints[$blueprint] = $channelsEnabledByDefault;
|
||||
$this->blueprints[$blueprint] = $driversEnabledByDefault;
|
||||
$this->serializers[$blueprint::getType()] = $serializer;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $driverName The name of the notification driver.
|
||||
* @param string $driver The ::class attribute of the driver class.
|
||||
* This driver should implement \Flarum\Notification\Driver\NotificationDriverInterface.
|
||||
* @param string[] $typesEnabledByDefault The names of blueprint classes of types enabled by default for this driver.
|
||||
* @return self
|
||||
*/
|
||||
public function driver(string $driverName, string $driver, array $typesEnabledByDefault = [])
|
||||
{
|
||||
$this->drivers[$driverName] = $driver;
|
||||
$this->typesEnabledByDefault[$driverName] = $typesEnabledByDefault;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function extend(Container $container, Extension $extension = null)
|
||||
{
|
||||
$container->extend('flarum.notification.blueprints', function ($existingBlueprints) {
|
||||
return array_merge($existingBlueprints, $this->blueprints);
|
||||
$existingBlueprints = array_merge($existingBlueprints, $this->blueprints);
|
||||
|
||||
foreach ($this->typesEnabledByDefault as $driverName => $typesEnabledByDefault) {
|
||||
foreach ($typesEnabledByDefault as $blueprintClass) {
|
||||
if (isset($existingBlueprints[$blueprintClass]) && (! in_array($driverName, $existingBlueprints[$blueprintClass]))) {
|
||||
$existingBlueprints[$blueprintClass][] = $driverName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $existingBlueprints;
|
||||
});
|
||||
|
||||
$container->extend('flarum.api.notification_serializers', function ($existingSerializers) {
|
||||
return array_merge($existingSerializers, $this->serializers);
|
||||
});
|
||||
|
||||
$container->extend('flarum.notification.drivers', function ($existingDrivers) {
|
||||
return array_merge($existingDrivers, $this->drivers);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* For detailed copyright and license information, please view the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Notification\Driver;
|
||||
|
||||
use Flarum\Notification\Blueprint\BlueprintInterface;
|
||||
use Flarum\Notification\Job\SendNotificationsJob;
|
||||
use Flarum\User\User;
|
||||
use Illuminate\Contracts\Queue\Queue;
|
||||
|
||||
class AlertNotificationDriver implements NotificationDriverInterface
|
||||
{
|
||||
/**
|
||||
* @var Queue
|
||||
*/
|
||||
private $queue;
|
||||
|
||||
public function __construct(Queue $queue)
|
||||
{
|
||||
$this->queue = $queue;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function send(BlueprintInterface $blueprint, array $users): void
|
||||
{
|
||||
if (count($users)) {
|
||||
$this->queue->push(new SendNotificationsJob($blueprint, $users));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function registerType(string $blueprintClass, array $driversEnabledByDefault): void
|
||||
{
|
||||
User::addPreference(
|
||||
User::getNotificationPreferenceKey($blueprintClass::getType(), 'alert'),
|
||||
'boolval',
|
||||
in_array('alert', $driversEnabledByDefault)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* For detailed copyright and license information, please view the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Notification\Driver;
|
||||
|
||||
use Flarum\Notification\Blueprint\BlueprintInterface;
|
||||
use Flarum\Notification\Job\SendEmailNotificationJob;
|
||||
use Flarum\Notification\MailableInterface;
|
||||
use Flarum\User\User;
|
||||
use Illuminate\Contracts\Queue\Queue;
|
||||
use ReflectionClass;
|
||||
|
||||
class EmailNotificationDriver implements NotificationDriverInterface
|
||||
{
|
||||
/**
|
||||
* @var Queue
|
||||
*/
|
||||
private $queue;
|
||||
|
||||
public function __construct(Queue $queue)
|
||||
{
|
||||
$this->queue = $queue;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function send(BlueprintInterface $blueprint, array $users): void
|
||||
{
|
||||
if ($blueprint instanceof MailableInterface) {
|
||||
$this->mailNotifications($blueprint, $users);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mail a notification to a list of users.
|
||||
*
|
||||
* @param MailableInterface $blueprint
|
||||
* @param User[] $recipients
|
||||
*/
|
||||
protected function mailNotifications(MailableInterface $blueprint, array $recipients)
|
||||
{
|
||||
foreach ($recipients as $user) {
|
||||
if ($user->shouldEmail($blueprint::getType())) {
|
||||
$this->queue->push(new SendEmailNotificationJob($blueprint, $user));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function registerType(string $blueprintClass, array $driversEnabledByDefault): void
|
||||
{
|
||||
if ((new ReflectionClass($blueprintClass))->implementsInterface(MailableInterface::class)) {
|
||||
User::addPreference(
|
||||
User::getNotificationPreferenceKey($blueprintClass::getType(), 'email'),
|
||||
'boolval',
|
||||
in_array('email', $driversEnabledByDefault)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* For detailed copyright and license information, please view the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Notification\Driver;
|
||||
|
||||
use Flarum\Notification\Blueprint\BlueprintInterface;
|
||||
use Flarum\User\User;
|
||||
|
||||
interface NotificationDriverInterface
|
||||
{
|
||||
/**
|
||||
* Conditionally sends a notification to users, generally using a queue.
|
||||
*
|
||||
* @param BlueprintInterface $blueprint
|
||||
* @param User[] $users
|
||||
* @return void
|
||||
*/
|
||||
public function send(BlueprintInterface $blueprint, array $users): void;
|
||||
|
||||
/**
|
||||
* Logic for registering a notification type, generally used for adding a user preference.
|
||||
*
|
||||
* @param string $blueprintClass
|
||||
* @param array $driversEnabledByDefault
|
||||
* @return void
|
||||
*/
|
||||
public function registerType(string $blueprintClass, array $driversEnabledByDefault): void;
|
||||
}
|
|
@ -11,6 +11,9 @@ namespace Flarum\Notification\Event;
|
|||
|
||||
use Flarum\Notification\Blueprint\BlueprintInterface;
|
||||
|
||||
/**
|
||||
* @deprecated in beta 15, removed in beta 16
|
||||
*/
|
||||
class Sending
|
||||
{
|
||||
/**
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
namespace Flarum\Notification\Job;
|
||||
|
||||
use Flarum\Notification\Blueprint\BlueprintInterface;
|
||||
use Flarum\Notification\Event\Sending;
|
||||
use Flarum\Notification\Notification;
|
||||
use Flarum\Queue\AbstractJob;
|
||||
use Flarum\User\User;
|
||||
|
@ -35,8 +34,6 @@ class SendNotificationsJob extends AbstractJob
|
|||
|
||||
public function handle()
|
||||
{
|
||||
event(new Sending($this->blueprint, $this->recipients));
|
||||
|
||||
Notification::notify($this->recipients, $this->blueprint);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,8 +12,6 @@ namespace Flarum\Notification;
|
|||
use Flarum\Event\ConfigureNotificationTypes;
|
||||
use Flarum\Foundation\AbstractServiceProvider;
|
||||
use Flarum\Notification\Blueprint\DiscussionRenamedBlueprint;
|
||||
use Flarum\User\User;
|
||||
use ReflectionClass;
|
||||
|
||||
class NotificationServiceProvider extends AbstractServiceProvider
|
||||
{
|
||||
|
@ -22,6 +20,13 @@ class NotificationServiceProvider extends AbstractServiceProvider
|
|||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->app->singleton('flarum.notification.drivers', function () {
|
||||
return [
|
||||
'alert' => Driver\AlertNotificationDriver::class,
|
||||
'email' => Driver\EmailNotificationDriver::class,
|
||||
];
|
||||
});
|
||||
|
||||
$this->app->singleton('flarum.notification.blueprints', function () {
|
||||
return [
|
||||
DiscussionRenamedBlueprint::class => ['alert']
|
||||
|
@ -34,9 +39,20 @@ class NotificationServiceProvider extends AbstractServiceProvider
|
|||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->setNotificationDrivers();
|
||||
$this->setNotificationTypes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register notification drivers.
|
||||
*/
|
||||
protected function setNotificationDrivers()
|
||||
{
|
||||
foreach ($this->app->make('flarum.notification.drivers') as $driverName => $driver) {
|
||||
NotificationSyncer::addNotificationDriver($driverName, $this->app->make($driver));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register notification types.
|
||||
*/
|
||||
|
@ -49,29 +65,22 @@ class NotificationServiceProvider extends AbstractServiceProvider
|
|||
new ConfigureNotificationTypes($blueprints)
|
||||
);
|
||||
|
||||
foreach ($blueprints as $blueprint => $channelsEnabledByDefault) {
|
||||
$this->addType($blueprint, $channelsEnabledByDefault);
|
||||
foreach ($blueprints as $blueprint => $driversEnabledByDefault) {
|
||||
$this->addType($blueprint, $driversEnabledByDefault);
|
||||
}
|
||||
}
|
||||
|
||||
protected function addType(string $blueprint, array $channelsEnabledByDefault)
|
||||
protected function addType(string $blueprint, array $driversEnabledByDefault)
|
||||
{
|
||||
Notification::setSubjectModel(
|
||||
$type = $blueprint::getType(),
|
||||
$blueprint::getSubjectModel()
|
||||
);
|
||||
|
||||
User::addPreference(
|
||||
User::getNotificationPreferenceKey($type, 'alert'),
|
||||
'boolval',
|
||||
in_array('alert', $channelsEnabledByDefault)
|
||||
);
|
||||
|
||||
if ((new ReflectionClass($blueprint))->implementsInterface(MailableInterface::class)) {
|
||||
User::addPreference(
|
||||
User::getNotificationPreferenceKey($type, 'email'),
|
||||
'boolval',
|
||||
in_array('email', $channelsEnabledByDefault)
|
||||
foreach (NotificationSyncer::getNotificationDrivers() as $driverName => $driver) {
|
||||
$driver->registerType(
|
||||
$blueprint,
|
||||
$driversEnabledByDefault
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,10 +10,9 @@
|
|||
namespace Flarum\Notification;
|
||||
|
||||
use Flarum\Notification\Blueprint\BlueprintInterface;
|
||||
use Flarum\Notification\Job\SendEmailNotificationJob;
|
||||
use Flarum\Notification\Job\SendNotificationsJob;
|
||||
use Flarum\Notification\Driver\NotificationDriverInterface;
|
||||
use Flarum\Notification\Event\Sending;
|
||||
use Flarum\User\User;
|
||||
use Illuminate\Contracts\Queue\Queue;
|
||||
|
||||
/**
|
||||
* The Notification Syncer commits notification blueprints to the database, and
|
||||
|
@ -38,14 +37,11 @@ class NotificationSyncer
|
|||
protected static $sentTo = [];
|
||||
|
||||
/**
|
||||
* @var Queue
|
||||
* A map of notification drivers.
|
||||
*
|
||||
* @var NotificationDriverInterface[]
|
||||
*/
|
||||
protected $queue;
|
||||
|
||||
public function __construct(Queue $queue)
|
||||
{
|
||||
$this->queue = $queue;
|
||||
}
|
||||
protected static $notificationDrivers = [];
|
||||
|
||||
/**
|
||||
* Sync a notification so that it is visible to the specified users, and not
|
||||
|
@ -102,12 +98,13 @@ class NotificationSyncer
|
|||
// receiving this notification for the first time (we know because they
|
||||
// didn't have a record in the database). As both operations can be
|
||||
// intensive on resources (database and mail server), we queue them.
|
||||
if (count($newRecipients)) {
|
||||
$this->queue->push(new SendNotificationsJob($blueprint, $newRecipients));
|
||||
foreach (static::getNotificationDrivers() as $driverName => $driver) {
|
||||
$driver->send($blueprint, $newRecipients);
|
||||
}
|
||||
|
||||
if ($blueprint instanceof MailableInterface) {
|
||||
$this->mailNotifications($blueprint, $newRecipients);
|
||||
if (count($newRecipients)) {
|
||||
// Deprecated in beta 15, removed in beta 16
|
||||
event(new Sending($blueprint, $newRecipients));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,21 +147,6 @@ class NotificationSyncer
|
|||
static::$onePerUser = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mail a notification to a list of users.
|
||||
*
|
||||
* @param MailableInterface $blueprint
|
||||
* @param User[] $recipients
|
||||
*/
|
||||
protected function mailNotifications(MailableInterface $blueprint, array $recipients)
|
||||
{
|
||||
foreach ($recipients as $user) {
|
||||
if ($user->shouldEmail($blueprint::getType())) {
|
||||
$this->queue->push(new SendEmailNotificationJob($blueprint, $user));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the deleted status of a list of notification records.
|
||||
*
|
||||
|
@ -175,4 +157,23 @@ class NotificationSyncer
|
|||
{
|
||||
Notification::whereIn('id', $ids)->update(['is_deleted' => $isDeleted]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a notification driver to the list.
|
||||
*
|
||||
* @param string $driverName
|
||||
* @param NotificationDriverInterface $driver
|
||||
*/
|
||||
public static function addNotificationDriver(string $driverName, NotificationDriverInterface $driver)
|
||||
{
|
||||
static::$notificationDrivers[$driverName] = $driver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return NotificationDriverInterface[]
|
||||
*/
|
||||
public static function getNotificationDrivers(): array
|
||||
{
|
||||
return static::$notificationDrivers;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,9 +11,12 @@ namespace Flarum\Tests\integration\extenders;
|
|||
|
||||
use Flarum\Extend;
|
||||
use Flarum\Notification\Blueprint\BlueprintInterface;
|
||||
use Flarum\Notification\Driver\NotificationDriverInterface;
|
||||
use Flarum\Notification\Notification;
|
||||
use Flarum\Notification\NotificationSyncer;
|
||||
use Flarum\Tests\integration\TestCase;
|
||||
|
||||
class NotificationTest extends \Flarum\Tests\integration\TestCase
|
||||
class NotificationTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @test
|
||||
|
@ -23,6 +26,27 @@ class NotificationTest extends \Flarum\Tests\integration\TestCase
|
|||
$this->assertArrayNotHasKey('customNotificationType', Notification::getSubjectModels());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function notification_serializer_doesnt_exist_by_default()
|
||||
{
|
||||
$this->app();
|
||||
|
||||
$this->assertNotContains(
|
||||
'customNotificationTypeSerializer',
|
||||
$this->app->getContainer()->make('flarum.api.notification_serializers')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function notification_driver_doesnt_exist_by_default()
|
||||
{
|
||||
$this->assertArrayNotHasKey('customNotificationDriver', NotificationSyncer::getNotificationDrivers());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
|
@ -37,6 +61,64 @@ class NotificationTest extends \Flarum\Tests\integration\TestCase
|
|||
|
||||
$this->assertArrayHasKey('customNotificationType', Notification::getSubjectModels());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function notification_serializer_exists_if_added()
|
||||
{
|
||||
$this->extend((new Extend\Notification)->type(
|
||||
CustomNotificationType::class,
|
||||
'customNotificationTypeSerializer'
|
||||
));
|
||||
|
||||
$this->app();
|
||||
|
||||
$this->assertContains(
|
||||
'customNotificationTypeSerializer',
|
||||
$this->app->getContainer()->make('flarum.api.notification_serializers')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function notification_driver_exists_if_added()
|
||||
{
|
||||
$this->extend((new Extend\Notification())->driver(
|
||||
'customNotificationDriver',
|
||||
CustomNotificationDriver::class
|
||||
));
|
||||
|
||||
$this->app();
|
||||
|
||||
$this->assertArrayHasKey('customNotificationDriver', NotificationSyncer::getNotificationDrivers());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function notification_driver_enabled_types_exist_if_added()
|
||||
{
|
||||
$this->extend(
|
||||
(new Extend\Notification())
|
||||
->type(CustomNotificationType::class, 'customSerializer')
|
||||
->type(SecondCustomNotificationType::class, 'secondCustomSerializer', ['customDriver'])
|
||||
->type(ThirdCustomNotificationType::class, 'thirdCustomSerializer')
|
||||
->driver('customDriver', CustomNotificationDriver::class, [CustomNotificationType::class])
|
||||
->driver('secondCustomDriver', SecondCustomNotificationDriver::class, [SecondCustomNotificationType::class])
|
||||
);
|
||||
|
||||
$this->app();
|
||||
|
||||
$blueprints = $this->app->getContainer()->make('flarum.notification.blueprints');
|
||||
|
||||
$this->assertContains('customDriver', $blueprints[CustomNotificationType::class]);
|
||||
$this->assertCount(1, $blueprints[CustomNotificationType::class]);
|
||||
$this->assertContains('customDriver', $blueprints[SecondCustomNotificationType::class]);
|
||||
$this->assertContains('secondCustomDriver', $blueprints[SecondCustomNotificationType::class]);
|
||||
$this->assertEmpty($blueprints[ThirdCustomNotificationType::class]);
|
||||
}
|
||||
}
|
||||
|
||||
class CustomNotificationType implements BlueprintInterface
|
||||
|
@ -66,3 +148,37 @@ class CustomNotificationType implements BlueprintInterface
|
|||
return 'customNotificationTypeSubjectModel';
|
||||
}
|
||||
}
|
||||
|
||||
class SecondCustomNotificationType extends CustomNotificationType
|
||||
{
|
||||
public static function getType()
|
||||
{
|
||||
return 'secondCustomNotificationType';
|
||||
}
|
||||
}
|
||||
|
||||
class ThirdCustomNotificationType extends CustomNotificationType
|
||||
{
|
||||
public static function getType()
|
||||
{
|
||||
return 'thirdCustomNotificationType';
|
||||
}
|
||||
}
|
||||
|
||||
class CustomNotificationDriver implements NotificationDriverInterface
|
||||
{
|
||||
public function send(BlueprintInterface $blueprint, array $users): void
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
public function registerType(string $blueprintClass, array $driversEnabledByDefault): void
|
||||
{
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
class SecondCustomNotificationDriver extends CustomNotificationDriver
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user