Upgrade framework

This commit is contained in:
2023-11-14 16:54:35 +01:00
parent 1648a5cd42
commit 4fcf6fffcc
10548 changed files with 693138 additions and 466698 deletions

View File

@@ -0,0 +1,79 @@
<?php
namespace Illuminate\Notifications;
use Illuminate\Contracts\Notifications\Dispatcher;
use InvalidArgumentException;
class AnonymousNotifiable
{
/**
* All of the notification routing information.
*
* @var array
*/
public $routes = [];
/**
* Add routing information to the target.
*
* @param string $channel
* @param mixed $route
* @return $this
*
* @throws \InvalidArgumentException
*/
public function route($channel, $route)
{
if ($channel === 'database') {
throw new InvalidArgumentException('The database channel does not support on-demand notifications.');
}
$this->routes[$channel] = $route;
return $this;
}
/**
* Send the given notification.
*
* @param mixed $notification
* @return void
*/
public function notify($notification)
{
app(Dispatcher::class)->send($this, $notification);
}
/**
* Send the given notification immediately.
*
* @param mixed $notification
* @return void
*/
public function notifyNow($notification)
{
app(Dispatcher::class)->sendNow($this, $notification);
}
/**
* Get the notification routing information for the given driver.
*
* @param string $driver
* @return mixed
*/
public function routeNotificationFor($driver)
{
return $this->routes[$driver] ?? null;
}
/**
* Get the value of the notifiable's primary key.
*
* @return mixed
*/
public function getKey()
{
//
}
}

View File

@@ -2,16 +2,12 @@
namespace Illuminate\Notifications;
use Illuminate\Mail\Markdown;
use InvalidArgumentException;
use Illuminate\Support\Manager;
use Nexmo\Client as NexmoClient;
use GuzzleHttp\Client as HttpClient;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Bus\Dispatcher as Bus;
use Nexmo\Client\Credentials\Basic as NexmoCredentials;
use Illuminate\Contracts\Notifications\Factory as FactoryContract;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Notifications\Dispatcher as DispatcherContract;
use Illuminate\Contracts\Notifications\Factory as FactoryContract;
use Illuminate\Support\Manager;
use InvalidArgumentException;
class ChannelManager extends Manager implements DispatcherContract, FactoryContract
{
@@ -22,6 +18,13 @@ class ChannelManager extends Manager implements DispatcherContract, FactoryContr
*/
protected $defaultChannel = 'mail';
/**
* The locale used when sending notifications.
*
* @var string|null
*/
protected $locale;
/**
* Send the given notification to the given notifiable entities.
*
@@ -31,8 +34,8 @@ class ChannelManager extends Manager implements DispatcherContract, FactoryContr
*/
public function send($notifiables, $notification)
{
return (new NotificationSender(
$this, $this->app->make(Bus::class), $this->app->make(Dispatcher::class))
(new NotificationSender(
$this, $this->container->make(Bus::class), $this->container->make(Dispatcher::class), $this->locale)
)->send($notifiables, $notification);
}
@@ -46,8 +49,8 @@ class ChannelManager extends Manager implements DispatcherContract, FactoryContr
*/
public function sendNow($notifiables, $notification, array $channels = null)
{
return (new NotificationSender(
$this, $this->app->make(Bus::class), $this->app->make(Dispatcher::class))
(new NotificationSender(
$this, $this->container->make(Bus::class), $this->container->make(Dispatcher::class), $this->locale)
)->sendNow($notifiables, $notification, $channels);
}
@@ -69,7 +72,7 @@ class ChannelManager extends Manager implements DispatcherContract, FactoryContr
*/
protected function createDatabaseDriver()
{
return $this->app->make(Channels\DatabaseChannel::class);
return $this->container->make(Channels\DatabaseChannel::class);
}
/**
@@ -79,7 +82,7 @@ class ChannelManager extends Manager implements DispatcherContract, FactoryContr
*/
protected function createBroadcastDriver()
{
return $this->app->make(Channels\BroadcastChannel::class);
return $this->container->make(Channels\BroadcastChannel::class);
}
/**
@@ -89,35 +92,7 @@ class ChannelManager extends Manager implements DispatcherContract, FactoryContr
*/
protected function createMailDriver()
{
return $this->app->make(Channels\MailChannel::class)->setMarkdownResolver(function () {
return $this->app->make(Markdown::class);
});
}
/**
* Create an instance of the Nexmo driver.
*
* @return \Illuminate\Notifications\Channels\NexmoSmsChannel
*/
protected function createNexmoDriver()
{
return new Channels\NexmoSmsChannel(
new NexmoClient(new NexmoCredentials(
$this->app['config']['services.nexmo.key'],
$this->app['config']['services.nexmo.secret']
)),
$this->app['config']['services.nexmo.sms_from']
);
}
/**
* Create an instance of the Slack driver.
*
* @return \Illuminate\Notifications\Channels\SlackWebhookChannel
*/
protected function createSlackDriver()
{
return new Channels\SlackWebhookChannel(new HttpClient);
return $this->container->make(Channels\MailChannel::class);
}
/**
@@ -134,7 +109,7 @@ class ChannelManager extends Manager implements DispatcherContract, FactoryContr
return parent::createDriver($driver);
} catch (InvalidArgumentException $e) {
if (class_exists($driver)) {
return $this->app->make($driver);
return $this->container->make($driver);
}
throw $e;
@@ -171,4 +146,17 @@ class ChannelManager extends Manager implements DispatcherContract, FactoryContr
{
$this->defaultChannel = $channel;
}
/**
* Set the locale of notifications.
*
* @param string $locale
* @return $this
*/
public function locale($locale)
{
$this->locale = $locale;
return $this;
}
}

View File

@@ -2,11 +2,11 @@
namespace Illuminate\Notifications\Channels;
use RuntimeException;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Notifications\Messages\BroadcastMessage;
use Illuminate\Notifications\Events\BroadcastNotificationCreated;
use Illuminate\Notifications\Messages\BroadcastMessage;
use Illuminate\Notifications\Notification;
use RuntimeException;
class BroadcastChannel
{
@@ -18,7 +18,7 @@ class BroadcastChannel
protected $events;
/**
* Create a new database channel.
* Create a new broadcast channel.
*
* @param \Illuminate\Contracts\Events\Dispatcher $events
* @return void
@@ -70,8 +70,6 @@ class BroadcastChannel
return $notification->toArray($notifiable);
}
throw new RuntimeException(
'Notification is missing toArray method.'
);
throw new RuntimeException('Notification is missing toArray method.');
}
}

View File

@@ -2,8 +2,8 @@
namespace Illuminate\Notifications\Channels;
use RuntimeException;
use Illuminate\Notifications\Notification;
use RuntimeException;
class DatabaseChannel
{
@@ -16,12 +16,28 @@ class DatabaseChannel
*/
public function send($notifiable, Notification $notification)
{
return $notifiable->routeNotificationFor('database')->create([
return $notifiable->routeNotificationFor('database', $notification)->create(
$this->buildPayload($notifiable, $notification)
);
}
/**
* Build an array payload for the DatabaseNotification Model.
*
* @param mixed $notifiable
* @param \Illuminate\Notifications\Notification $notification
* @return array
*/
protected function buildPayload($notifiable, Notification $notification)
{
return [
'id' => $notification->id,
'type' => get_class($notification),
'type' => method_exists($notification, 'databaseType')
? $notification->databaseType($notifiable)
: get_class($notification),
'data' => $this->getData($notifiable, $notification),
'read_at' => null,
]);
];
}
/**
@@ -44,8 +60,6 @@ class DatabaseChannel
return $notification->toArray($notifiable);
}
throw new RuntimeException(
'Notification is missing toDatabase / toArray method.'
);
throw new RuntimeException('Notification is missing toDatabase / toArray method.');
}
}

View File

@@ -2,38 +2,43 @@
namespace Illuminate\Notifications\Channels;
use Closure;
use Illuminate\Contracts\Mail\Factory as MailFactory;
use Illuminate\Contracts\Mail\Mailable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Markdown;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Contracts\Mail\Mailer;
use Illuminate\Contracts\Mail\Mailable;
use Illuminate\Notifications\Notification;
use Symfony\Component\Mailer\Header\MetadataHeader;
use Symfony\Component\Mailer\Header\TagHeader;
class MailChannel
{
/**
* The mailer implementation.
*
* @var \Illuminate\Contracts\Mail\Mailer
* @var \Illuminate\Contracts\Mail\Factory
*/
protected $mailer;
/**
* The Markdown resolver callback.
* The markdown implementation.
*
* @var \Closure
* @var \Illuminate\Mail\Markdown
*/
protected $markdownResolver;
protected $markdown;
/**
* Create a new mail channel instance.
*
* @param \Illuminate\Contracts\Mail\Mailer $mailer
* @param \Illuminate\Contracts\Mail\Factory $mailer
* @param \Illuminate\Mail\Markdown $markdown
* @return void
*/
public function __construct(Mailer $mailer)
public function __construct(MailFactory $mailer, Markdown $markdown)
{
$this->mailer = $mailer;
$this->markdown = $markdown;
}
/**
@@ -45,26 +50,44 @@ class MailChannel
*/
public function send($notifiable, Notification $notification)
{
if (! $notifiable->routeNotificationFor('mail')) {
$message = $notification->toMail($notifiable);
if (! $notifiable->routeNotificationFor('mail', $notification) &&
! $message instanceof Mailable) {
return;
}
$message = $notification->toMail($notifiable);
if ($message instanceof Mailable) {
return $message->send($this->mailer);
}
$this->mailer->send($this->buildView($message), $message->data(), function ($mailMessage) use ($notifiable, $notification, $message) {
$this->mailer->mailer($message->mailer ?? null)->send(
$this->buildView($message),
array_merge($message->data(), $this->additionalMessageData($notification)),
$this->messageBuilder($notifiable, $notification, $message)
);
}
/**
* Get the mailer Closure for the message.
*
* @param mixed $notifiable
* @param \Illuminate\Notifications\Notification $notification
* @param \Illuminate\Notifications\Messages\MailMessage $message
* @return \Closure
*/
protected function messageBuilder($notifiable, $notification, $message)
{
return function ($mailMessage) use ($notifiable, $notification, $message) {
$this->buildMessage($mailMessage, $notifiable, $notification, $message);
});
};
}
/**
* Build the notification's view.
*
* @param \Illuminate\Notifications\Messages\MailMessage $message
* @return void
* @return string|array
*/
protected function buildView($message)
{
@@ -72,11 +95,31 @@ class MailChannel
return $message->view;
}
$markdown = call_user_func($this->markdownResolver);
if (property_exists($message, 'theme') && ! is_null($message->theme)) {
$this->markdown->theme($message->theme);
}
return [
'html' => $markdown->render($message->markdown, $message->data()),
'text' => $markdown->renderText($message->markdown, $message->data()),
'html' => $this->markdown->render($message->markdown, $message->data()),
'text' => $this->markdown->renderText($message->markdown, $message->data()),
];
}
/**
* Get additional meta-data to pass along with the view data.
*
* @param \Illuminate\Notifications\Notification $notification
* @return array
*/
protected function additionalMessageData($notification)
{
return [
'__laravel_notification_id' => $notification->id,
'__laravel_notification' => get_class($notification),
'__laravel_notification_queued' => in_array(
ShouldQueue::class,
class_implements($notification)
),
];
}
@@ -91,7 +134,7 @@ class MailChannel
*/
protected function buildMessage($mailMessage, $notifiable, $notification, $message)
{
$this->addressMessage($mailMessage, $notifiable, $message);
$this->addressMessage($mailMessage, $notifiable, $notification, $message);
$mailMessage->subject($message->subject ?: Str::title(
Str::snake(class_basename($notification), ' ')
@@ -100,8 +143,22 @@ class MailChannel
$this->addAttachments($mailMessage, $message);
if (! is_null($message->priority)) {
$mailMessage->setPriority($message->priority);
$mailMessage->priority($message->priority);
}
if ($message->tags) {
foreach ($message->tags as $tag) {
$mailMessage->getHeaders()->add(new TagHeader($tag));
}
}
if ($message->metadata) {
foreach ($message->metadata as $key => $value) {
$mailMessage->getHeaders()->add(new MetadataHeader($key, $value));
}
}
$this->runCallbacks($mailMessage, $message);
}
/**
@@ -109,21 +166,26 @@ class MailChannel
*
* @param \Illuminate\Mail\Message $mailMessage
* @param mixed $notifiable
* @param \Illuminate\Notifications\Notification $notification
* @param \Illuminate\Notifications\Messages\MailMessage $message
* @return void
*/
protected function addressMessage($mailMessage, $notifiable, $message)
protected function addressMessage($mailMessage, $notifiable, $notification, $message)
{
$this->addSender($mailMessage, $message);
$mailMessage->to($this->getRecipients($notifiable, $message));
$mailMessage->to($this->getRecipients($notifiable, $notification, $message));
if ($message->cc) {
$mailMessage->cc($message->cc[0], Arr::get($message->cc, 1));
if (! empty($message->cc)) {
foreach ($message->cc as $cc) {
$mailMessage->cc($cc[0], Arr::get($cc, 1));
}
}
if ($message->bcc) {
$mailMessage->bcc($message->bcc[0], Arr::get($message->bcc, 1));
if (! empty($message->bcc)) {
foreach ($message->bcc as $bcc) {
$mailMessage->bcc($bcc[0], Arr::get($bcc, 1));
}
}
}
@@ -141,7 +203,9 @@ class MailChannel
}
if (! empty($message->replyTo)) {
$mailMessage->replyTo($message->replyTo[0], Arr::get($message->replyTo, 1));
foreach ($message->replyTo as $replyTo) {
$mailMessage->replyTo($replyTo[0], Arr::get($replyTo, 1));
}
}
}
@@ -149,17 +213,20 @@ class MailChannel
* Get the recipients of the given message.
*
* @param mixed $notifiable
* @param \Illuminate\Notifications\Notification $notification
* @param \Illuminate\Notifications\Messages\MailMessage $message
* @return mixed
*/
protected function getRecipients($notifiable, $message)
protected function getRecipients($notifiable, $notification, $message)
{
if (is_string($recipients = $notifiable->routeNotificationFor('mail'))) {
if (is_string($recipients = $notifiable->routeNotificationFor('mail', $notification))) {
$recipients = [$recipients];
}
return collect($recipients)->map(function ($recipient) {
return is_string($recipient) ? $recipient : $recipient->email;
return collect($recipients)->mapWithKeys(function ($recipient, $email) {
return is_numeric($email)
? [$email => (is_string($recipient) ? $recipient : $recipient->email)]
: [$email => $recipient];
})->all();
}
@@ -182,14 +249,17 @@ class MailChannel
}
/**
* Set the Markdown resolver callback.
* Run the callbacks for the message.
*
* @param \Closure $callback
* @param \Illuminate\Mail\Message $mailMessage
* @param \Illuminate\Notifications\Messages\MailMessage $message
* @return $this
*/
public function setMarkdownResolver(Closure $callback)
protected function runCallbacks($mailMessage, $message)
{
$this->markdownResolver = $callback;
foreach ($message->callbacks as $callback) {
$callback($mailMessage->getSymfonyMessage());
}
return $this;
}

View File

@@ -1,64 +0,0 @@
<?php
namespace Illuminate\Notifications\Channels;
use Nexmo\Client as NexmoClient;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\NexmoMessage;
class NexmoSmsChannel
{
/**
* The Nexmo client instance.
*
* @var \Nexmo\Client
*/
protected $nexmo;
/**
* The phone number notifications should be sent from.
*
* @var string
*/
protected $from;
/**
* Create a new Nexmo channel instance.
*
* @param \Nexmo\Client $nexmo
* @param string $from
* @return void
*/
public function __construct(NexmoClient $nexmo, $from)
{
$this->from = $from;
$this->nexmo = $nexmo;
}
/**
* Send the given notification.
*
* @param mixed $notifiable
* @param \Illuminate\Notifications\Notification $notification
* @return \Nexmo\Message\Message
*/
public function send($notifiable, Notification $notification)
{
if (! $to = $notifiable->routeNotificationFor('nexmo')) {
return;
}
$message = $notification->toNexmo($notifiable);
if (is_string($message)) {
$message = new NexmoMessage($message);
}
return $this->nexmo->message()->send([
'type' => $message->type,
'from' => $message->from ?: $this->from,
'to' => $to,
'text' => trim($message->content),
]);
}
}

View File

@@ -1,114 +0,0 @@
<?php
namespace Illuminate\Notifications\Channels;
use GuzzleHttp\Client as HttpClient;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Messages\SlackAttachment;
use Illuminate\Notifications\Messages\SlackAttachmentField;
class SlackWebhookChannel
{
/**
* The HTTP client instance.
*
* @var \GuzzleHttp\Client
*/
protected $http;
/**
* Create a new Slack channel instance.
*
* @param \GuzzleHttp\Client $http
* @return void
*/
public function __construct(HttpClient $http)
{
$this->http = $http;
}
/**
* Send the given notification.
*
* @param mixed $notifiable
* @param \Illuminate\Notifications\Notification $notification
* @return \Psr\Http\Message\ResponseInterface
*/
public function send($notifiable, Notification $notification)
{
if (! $url = $notifiable->routeNotificationFor('slack')) {
return;
}
$this->http->post($url, $this->buildJsonPayload(
$notification->toSlack($notifiable)
));
}
/**
* Build up a JSON payload for the Slack webhook.
*
* @param \Illuminate\Notifications\Messages\SlackMessage $message
* @return array
*/
protected function buildJsonPayload(SlackMessage $message)
{
$optionalFields = array_filter([
'channel' => data_get($message, 'channel'),
'icon_emoji' => data_get($message, 'icon'),
'icon_url' => data_get($message, 'image'),
'link_names' => data_get($message, 'linkNames'),
'username' => data_get($message, 'username'),
]);
return array_merge([
'json' => array_merge([
'text' => $message->content,
'attachments' => $this->attachments($message),
], $optionalFields),
], $message->http);
}
/**
* Format the message's attachments.
*
* @param \Illuminate\Notifications\Messages\SlackMessage $message
* @return array
*/
protected function attachments(SlackMessage $message)
{
return collect($message->attachments)->map(function ($attachment) use ($message) {
return array_filter([
'color' => $attachment->color ?: $message->color(),
'fallback' => $attachment->fallback,
'fields' => $this->fields($attachment),
'footer' => $attachment->footer,
'footer_icon' => $attachment->footerIcon,
'image_url' => $attachment->imageUrl,
'mrkdwn_in' => $attachment->markdown,
'text' => $attachment->content,
'title' => $attachment->title,
'title_link' => $attachment->url,
'ts' => $attachment->timestamp,
]);
})->all();
}
/**
* Format the attachment's fields.
*
* @param \Illuminate\Notifications\Messages\SlackAttachment $attachment
* @return array
*/
protected function fields(SlackAttachment $attachment)
{
return collect($attachment->fields)->map(function ($value, $key) {
if ($value instanceof SlackAttachmentField) {
return $value->toArray();
}
return ['title' => $key, 'value' => $value, 'short' => true];
})->values()->all();
}
}

View File

@@ -3,9 +3,11 @@
namespace Illuminate\Notifications\Console;
use Illuminate\Console\Command;
use Illuminate\Support\Composer;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Composer;
use Symfony\Component\Console\Attribute\AsCommand;
#[AsCommand(name: 'notifications:table')]
class NotificationTableCommand extends Command
{
/**
@@ -15,6 +17,17 @@ class NotificationTableCommand extends Command
*/
protected $name = 'notifications:table';
/**
* The name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = 'notifications:table';
/**
* The console command description.
*
@@ -38,7 +51,7 @@ class NotificationTableCommand extends Command
* Create a new notifications table command instance.
*
* @param \Illuminate\Filesystem\Filesystem $files
* @param \Illuminate\Support\Composer $composer
* @param \Illuminate\Support\Composer $composer
* @return void
*/
public function __construct(Filesystem $files, Composer $composer)
@@ -54,13 +67,13 @@ class NotificationTableCommand extends Command
*
* @return void
*/
public function fire()
public function handle()
{
$fullPath = $this->createBaseMigration();
$this->files->put($fullPath, $this->files->get(__DIR__.'/stubs/notifications.stub'));
$this->info('Migration created successfully!');
$this->components->info('Migration created successfully.');
$this->composer->dumpAutoloads();
}

View File

@@ -1,10 +1,10 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateNotificationsTable extends Migration
return new class extends Migration
{
/**
* Run the migrations.
@@ -32,4 +32,4 @@ class CreateNotificationsTable extends Migration
{
Schema::dropIfExists('notifications');
}
}
};

View File

@@ -2,10 +2,18 @@
namespace Illuminate\Notifications;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class DatabaseNotification extends Model
{
/**
* The "type" of the primary key ID.
*
* @var string
*/
protected $keyType = 'string';
/**
* Indicates if the IDs are auto-incrementing.
*
@@ -39,6 +47,8 @@ class DatabaseNotification extends Model
/**
* Get the notifiable entity that the notification belongs to.
*
* @return \Illuminate\Database\Eloquent\Relations\MorphTo
*/
public function notifiable()
{
@@ -57,6 +67,18 @@ class DatabaseNotification extends Model
}
}
/**
* Mark the notification as unread.
*
* @return void
*/
public function markAsUnread()
{
if (! is_null($this->read_at)) {
$this->forceFill(['read_at' => null])->save();
}
}
/**
* Determine if a notification has been read.
*
@@ -77,6 +99,28 @@ class DatabaseNotification extends Model
return $this->read_at === null;
}
/**
* Scope a query to only include read notifications.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeRead(Builder $query)
{
return $query->whereNotNull('read_at');
}
/**
* Scope a query to only include unread notifications.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeUnread(Builder $query)
{
return $query->whereNull('read_at');
}
/**
* Create a new database notification collection instance.
*

View File

@@ -7,14 +7,22 @@ use Illuminate\Database\Eloquent\Collection;
class DatabaseNotificationCollection extends Collection
{
/**
* Mark all notification as read.
* Mark all notifications as read.
*
* @return void
*/
public function markAsRead()
{
$this->each(function ($notification) {
$notification->markAsRead();
});
$this->each->markAsRead();
}
/**
* Mark all notifications as unread.
*
* @return void
*/
public function markAsUnread()
{
$this->each->markAsUnread();
}
}

View File

@@ -2,10 +2,12 @@
namespace Illuminate\Notifications\Events;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Notifications\AnonymousNotifiable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Arr;
class BroadcastNotificationCreated implements ShouldBroadcast
{
@@ -54,32 +56,30 @@ class BroadcastNotificationCreated implements ShouldBroadcast
*/
public function broadcastOn()
{
$channels = $this->notification->broadcastOn();
if ($this->notifiable instanceof AnonymousNotifiable &&
$this->notifiable->routeNotificationFor('broadcast')) {
$channels = Arr::wrap($this->notifiable->routeNotificationFor('broadcast'));
} else {
$channels = $this->notification->broadcastOn();
}
if (! empty($channels)) {
return $channels;
}
return [new PrivateChannel($this->channelName())];
}
if (is_string($channels = $this->channelName())) {
return [new PrivateChannel($channels)];
}
/**
* Get the data that should be sent with the broadcasted event.
*
* @return array
*/
public function broadcastWith()
{
return array_merge($this->data, [
'id' => $this->notification->id,
'type' => get_class($this->notification),
]);
return collect($channels)->map(function ($channel) {
return new PrivateChannel($channel);
})->all();
}
/**
* Get the broadcast channel name for the event.
*
* @return string
* @return array|string
*/
protected function channelName()
{
@@ -91,4 +91,33 @@ class BroadcastNotificationCreated implements ShouldBroadcast
return $class.'.'.$this->notifiable->getKey();
}
/**
* Get the data that should be sent with the broadcasted event.
*
* @return array
*/
public function broadcastWith()
{
if (method_exists($this->notification, 'broadcastWith')) {
return $this->notification->broadcastWith();
}
return array_merge($this->data, [
'id' => $this->notification->id,
'type' => $this->broadcastType(),
]);
}
/**
* Get the type of the notification being broadcast.
*
* @return string
*/
public function broadcastType()
{
return method_exists($this->notification, 'broadcastType')
? $this->notification->broadcastType()
: get_class($this->notification);
}
}

View File

@@ -2,8 +2,13 @@
namespace Illuminate\Notifications\Events;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
class NotificationFailed
{
use Queueable, SerializesModels;
/**
* The notifiable entity who received the notification.
*

View File

@@ -2,8 +2,13 @@
namespace Illuminate\Notifications\Events;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
class NotificationSending
{
use Queueable, SerializesModels;
/**
* The notifiable entity who received the notification.
*

View File

@@ -2,8 +2,13 @@
namespace Illuminate\Notifications\Events;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
class NotificationSent
{
use Queueable, SerializesModels;
/**
* The notifiable entity who received the notification.
*

View File

@@ -6,28 +6,31 @@ trait HasDatabaseNotifications
{
/**
* Get the entity's notifications.
*
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function notifications()
{
return $this->morphMany(DatabaseNotification::class, 'notifiable')
->orderBy('created_at', 'desc');
return $this->morphMany(DatabaseNotification::class, 'notifiable')->latest();
}
/**
* Get the entity's read notifications.
*
* @return \Illuminate\Database\Query\Builder
*/
public function readNotifications()
{
return $this->notifications()
->whereNotNull('read_at');
return $this->notifications()->read();
}
/**
* Get the entity's unread notifications.
*
* @return \Illuminate\Database\Query\Builder
*/
public function unreadNotifications()
{
return $this->notifications()
->whereNull('read_at');
return $this->notifications()->unread();
}
}

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Taylor Otwell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -2,8 +2,18 @@
namespace Illuminate\Notifications\Messages;
class MailMessage extends SimpleMessage
use Illuminate\Container\Container;
use Illuminate\Contracts\Mail\Attachable;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Support\Renderable;
use Illuminate\Mail\Attachment;
use Illuminate\Mail\Markdown;
use Illuminate\Support\Traits\Conditionable;
class MailMessage extends SimpleMessage implements Renderable
{
use Conditionable;
/**
* The view to be rendered.
*
@@ -25,6 +35,13 @@ class MailMessage extends SimpleMessage
*/
public $markdown = 'notifications::email';
/**
* The current theme being used when generating emails.
*
* @var string|null
*/
public $theme;
/**
* The "from" information for the message.
*
@@ -67,6 +84,20 @@ class MailMessage extends SimpleMessage
*/
public $rawAttachments = [];
/**
* The tags for the message.
*
* @var array
*/
public $tags = [];
/**
* The metadata for the message.
*
* @var array
*/
public $metadata = [];
/**
* Priority level of the message.
*
@@ -74,6 +105,13 @@ class MailMessage extends SimpleMessage
*/
public $priority;
/**
* The callbacks for the message.
*
* @var array
*/
public $callbacks = [];
/**
* Set the view for the mail message.
*
@@ -108,6 +146,32 @@ class MailMessage extends SimpleMessage
return $this;
}
/**
* Set the default markdown template.
*
* @param string $template
* @return $this
*/
public function template($template)
{
$this->markdown = $template;
return $this;
}
/**
* Set the theme to use with the Markdown template.
*
* @param string $theme
* @return $this
*/
public function theme($theme)
{
$this->theme = $theme;
return $this;
}
/**
* Set the from address for the mail message.
*
@@ -131,7 +195,11 @@ class MailMessage extends SimpleMessage
*/
public function replyTo($address, $name = null)
{
$this->replyTo = [$address, $name];
if ($this->arrayOfAddresses($address)) {
$this->replyTo += $this->parseAddresses($address);
} else {
$this->replyTo[] = [$address, $name];
}
return $this;
}
@@ -139,13 +207,17 @@ class MailMessage extends SimpleMessage
/**
* Set the cc address for the mail message.
*
* @param string $address
* @param array|string $address
* @param string|null $name
* @return $this
*/
public function cc($address, $name = null)
{
$this->cc = [$address, $name];
if ($this->arrayOfAddresses($address)) {
$this->cc += $this->parseAddresses($address);
} else {
$this->cc[] = [$address, $name];
}
return $this;
}
@@ -153,13 +225,17 @@ class MailMessage extends SimpleMessage
/**
* Set the bcc address for the mail message.
*
* @param string $address
* @param array|string $address
* @param string|null $name
* @return $this
*/
public function bcc($address, $name = null)
{
$this->bcc = [$address, $name];
if ($this->arrayOfAddresses($address)) {
$this->bcc += $this->parseAddresses($address);
} else {
$this->bcc[] = [$address, $name];
}
return $this;
}
@@ -167,12 +243,20 @@ class MailMessage extends SimpleMessage
/**
* Attach a file to the message.
*
* @param string $file
* @param string|\Illuminate\Contracts\Mail\Attachable|\Illuminate\Mail\Attachment $file
* @param array $options
* @return $this
*/
public function attach($file, array $options = [])
{
if ($file instanceof Attachable) {
$file = $file->toMailAttachment();
}
if ($file instanceof Attachment) {
return $file->attachTo($this);
}
$this->attachments[] = compact('file', 'options');
return $this;
@@ -193,6 +277,33 @@ class MailMessage extends SimpleMessage
return $this;
}
/**
* Add a tag header to the message when supported by the underlying transport.
*
* @param string $value
* @return $this
*/
public function tag($value)
{
array_push($this->tags, $value);
return $this;
}
/**
* Add a metadata header to the message when supported by the underlying transport.
*
* @param string $key
* @param string $value
* @return $this
*/
public function metadata($key, $value)
{
$this->metadata[$key] = $value;
return $this;
}
/**
* Set the priority of this message.
*
@@ -217,4 +328,60 @@ class MailMessage extends SimpleMessage
{
return array_merge($this->toArray(), $this->viewData);
}
/**
* Parse the multi-address array into the necessary format.
*
* @param array $value
* @return array
*/
protected function parseAddresses($value)
{
return collect($value)->map(function ($address, $name) {
return [$address, is_numeric($name) ? null : $name];
})->values()->all();
}
/**
* Determine if the given "address" is actually an array of addresses.
*
* @param mixed $address
* @return bool
*/
protected function arrayOfAddresses($address)
{
return is_iterable($address) || $address instanceof Arrayable;
}
/**
* Render the mail notification message into an HTML string.
*
* @return \Illuminate\Support\HtmlString
*/
public function render()
{
if (isset($this->view)) {
return Container::getInstance()->make('mailer')->render(
$this->view, $this->data()
);
}
$markdown = Container::getInstance()->make(Markdown::class);
return $markdown->theme($this->theme ?: $markdown->getTheme())
->render($this->markdown, $this->data());
}
/**
* Register a callback to be called with the Symfony message instance.
*
* @param callable $callback
* @return $this
*/
public function withSymfonyMessage($callback)
{
$this->callbacks[] = $callback;
return $this;
}
}

View File

@@ -1,76 +0,0 @@
<?php
namespace Illuminate\Notifications\Messages;
class NexmoMessage
{
/**
* The message content.
*
* @var string
*/
public $content;
/**
* The phone number the message should be sent from.
*
* @var string
*/
public $from;
/**
* The message type.
*
* @var string
*/
public $type = 'text';
/**
* Create a new message instance.
*
* @param string $content
* @return void
*/
public function __construct($content = '')
{
$this->content = $content;
}
/**
* Set the message content.
*
* @param string $content
* @return $this
*/
public function content($content)
{
$this->content = $content;
return $this;
}
/**
* Set the phone number the message should be sent from.
*
* @param string $from
* @return $this
*/
public function from($from)
{
$this->from = $from;
return $this;
}
/**
* Set the message type.
*
* @return $this
*/
public function unicode()
{
$this->type = 'unicode';
return $this;
}
}

View File

@@ -2,6 +2,7 @@
namespace Illuminate\Notifications\Messages;
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Notifications\Action;
class SimpleMessage
@@ -62,6 +63,13 @@ class SimpleMessage
*/
public $actionUrl;
/**
* The name of the mailer that should send the notification.
*
* @var string
*/
public $mailer;
/**
* Indicate that the notification gives information about a successful operation.
*
@@ -141,7 +149,7 @@ class SimpleMessage
/**
* Add a line of text to the notification.
*
* @param \Illuminate\Notifications\Action|string $line
* @param mixed $line
* @return $this
*/
public function line($line)
@@ -149,10 +157,57 @@ class SimpleMessage
return $this->with($line);
}
/**
* Add a line of text to the notification if the given condition is true.
*
* @param bool $boolean
* @param mixed $line
* @return $this
*/
public function lineIf($boolean, $line)
{
if ($boolean) {
return $this->line($line);
}
return $this;
}
/**
* Add lines of text to the notification.
*
* @param iterable $lines
* @return $this
*/
public function lines($lines)
{
foreach ($lines as $line) {
$this->line($line);
}
return $this;
}
/**
* Add lines of text to the notification if the given condition is true.
*
* @param bool $boolean
* @param iterable $lines
* @return $this
*/
public function linesIf($boolean, $lines)
{
if ($boolean) {
return $this->lines($lines);
}
return $this;
}
/**
* Add a line of text to the notification.
*
* @param \Illuminate\Notifications\Action|string|array $line
* @param mixed $line
* @return $this
*/
public function with($line)
@@ -171,16 +226,20 @@ class SimpleMessage
/**
* Format the given line of text.
*
* @param string|array $line
* @return string
* @param \Illuminate\Contracts\Support\Htmlable|string|array $line
* @return \Illuminate\Contracts\Support\Htmlable|string
*/
protected function formatLine($line)
{
if ($line instanceof Htmlable) {
return $line;
}
if (is_array($line)) {
return implode(' ', array_map('trim', $line));
}
return trim(implode(' ', array_map('trim', preg_split('/\\r\\n|\\r|\\n/', $line))));
return trim(implode(' ', array_map('trim', preg_split('/\\r\\n|\\r|\\n/', $line ?? ''))));
}
/**
@@ -198,6 +257,19 @@ class SimpleMessage
return $this;
}
/**
* Set the name of the mailer that should send the notification.
*
* @param string $mailer
* @return $this
*/
public function mailer($mailer)
{
$this->mailer = $mailer;
return $this;
}
/**
* Get an array representation of the message.
*
@@ -214,6 +286,7 @@ class SimpleMessage
'outroLines' => $this->outroLines,
'actionText' => $this->actionText,
'actionUrl' => $this->actionUrl,
'displayableActionUrl' => str_replace(['mailto:', 'tel:'], '', $this->actionUrl ?? ''),
];
}
}

View File

@@ -1,241 +0,0 @@
<?php
namespace Illuminate\Notifications\Messages;
use Carbon\Carbon;
class SlackAttachment
{
/**
* The attachment's title.
*
* @var string
*/
public $title;
/**
* The attachment's URL.
*
* @var string
*/
public $url;
/**
* The attachment's text content.
*
* @var string
*/
public $content;
/**
* A plain-text summary of the attachment.
*
* @var string
*/
public $fallback;
/**
* The attachment's color.
*
* @var string
*/
public $color;
/**
* The attachment's fields.
*
* @var array
*/
public $fields;
/**
* The fields containing markdown.
*
* @var array
*/
public $markdown;
/**
* The attachment's image url.
*
* @var string
*/
public $imageUrl;
/**
* The attachment's footer.
*
* @var string
*/
public $footer;
/**
* The attachment's footer icon.
*
* @var string
*/
public $footerIcon;
/**
* The attachment's timestamp.
*
* @var int
*/
public $timestamp;
/**
* Set the title of the attachment.
*
* @param string $title
* @param string $url
* @return $this
*/
public function title($title, $url = null)
{
$this->title = $title;
$this->url = $url;
return $this;
}
/**
* Set the content (text) of the attachment.
*
* @param string $content
* @return $this
*/
public function content($content)
{
$this->content = $content;
return $this;
}
/**
* A plain-text summary of the attachment.
*
* @param string $fallback
* @return $this
*/
public function fallback($fallback)
{
$this->fallback = $fallback;
return $this;
}
/**
* Set the color of the attachment.
*
* @param string $color
* @return $this
*/
public function color($color)
{
$this->color = $color;
return $this;
}
/**
* Add a field to the attachment.
*
* @param \Closure|string $title
* @param string $content
* @return $this
*/
public function field($title, $content = '')
{
if (is_callable($title)) {
$callback = $title;
$callback($attachmentField = new SlackAttachmentField);
$this->fields[] = $attachmentField;
return $this;
}
$this->fields[$title] = $content;
return $this;
}
/**
* Set the fields of the attachment.
*
* @param array $fields
* @return $this
*/
public function fields(array $fields)
{
$this->fields = $fields;
return $this;
}
/**
* Set the fields containing markdown.
*
* @param array $fields
* @return $this
*/
public function markdown(array $fields)
{
$this->markdown = $fields;
return $this;
}
/**
* Set the image URL.
*
* @param string $url
* @return $this
*/
public function image($url)
{
$this->imageUrl = $url;
return $this;
}
/**
* Set the footer content.
*
* @param string $footer
* @return $this
*/
public function footer($footer)
{
$this->footer = $footer;
return $this;
}
/**
* Set the footer icon.
*
* @param string $icon
* @return $this
*/
public function footerIcon($icon)
{
$this->footerIcon = $icon;
return $this;
}
/**
* Set the timestamp.
*
* @param Carbon $timestamp
* @return $this
*/
public function timestamp(Carbon $timestamp)
{
$this->timestamp = $timestamp->getTimestamp();
return $this;
}
}

View File

@@ -1,79 +0,0 @@
<?php
namespace Illuminate\Notifications\Messages;
class SlackAttachmentField
{
/**
* The title field of the attachment field.
*
* @var string
*/
protected $title;
/**
* The content of the attachment field.
*
* @var string
*/
protected $content;
/**
* Whether the content is short.
*
* @var bool
*/
protected $short = true;
/**
* Set the title of the field.
*
* @param string $title
* @return $this
*/
public function title($title)
{
$this->title = $title;
return $this;
}
/**
* Set the content of the field.
*
* @param string $content
* @return $this
*/
public function content($content)
{
$this->content = $content;
return $this;
}
/**
* Indicates that the content should not be displayed side-by-side with other fields.
*
* @return $this
*/
public function long()
{
$this->short = false;
return $this;
}
/**
* Get the array representation of the attachment field.
*
* @return array
*/
public function toArray()
{
return [
'title' => $this->title,
'value' => $this->content,
'short' => $this->short,
];
}
}

View File

@@ -1,221 +0,0 @@
<?php
namespace Illuminate\Notifications\Messages;
use Closure;
class SlackMessage
{
/**
* The "level" of the notification (info, success, warning, error).
*
* @var string
*/
public $level = 'info';
/**
* The username to send the message from.
*
* @var string|null
*/
public $username;
/**
* The user emoji icon for the message.
*
* @var string|null
*/
public $icon;
/**
* The user image icon for the message.
*
* @var string|null
*/
public $image;
/**
* The channel to send the message on.
*
* @var string|null
*/
public $channel;
/**
* The text content of the message.
*
* @var string
*/
public $content;
/**
* Indicates if channel names and usernames should be linked.
*
* @var bool
*/
public $linkNames = 0;
/**
* The message's attachments.
*
* @var array
*/
public $attachments = [];
/**
* Additional request options for the Guzzle HTTP client.
*
* @var array
*/
public $http = [];
/**
* Indicate that the notification gives information about a successful operation.
*
* @return $this
*/
public function success()
{
$this->level = 'success';
return $this;
}
/**
* Indicate that the notification gives information about a warning.
*
* @return $this
*/
public function warning()
{
$this->level = 'warning';
return $this;
}
/**
* Indicate that the notification gives information about an error.
*
* @return $this
*/
public function error()
{
$this->level = 'error';
return $this;
}
/**
* Set a custom username and optional emoji icon for the Slack message.
*
* @param string $username
* @param string|null $icon
* @return $this
*/
public function from($username, $icon = null)
{
$this->username = $username;
if (! is_null($icon)) {
$this->icon = $icon;
}
return $this;
}
/**
* Set a custom image icon the message should use.
*
* @param string $image
* @return $this
*/
public function image($image)
{
$this->image = $image;
return $this;
}
/**
* Set the Slack channel the message should be sent to.
*
* @param string $channel
* @return $this
*/
public function to($channel)
{
$this->channel = $channel;
return $this;
}
/**
* Set the content of the Slack message.
*
* @param string $content
* @return $this
*/
public function content($content)
{
$this->content = $content;
return $this;
}
/**
* Define an attachment for the message.
*
* @param \Closure $callback
* @return $this
*/
public function attachment(Closure $callback)
{
$this->attachments[] = $attachment = new SlackAttachment;
$callback($attachment);
return $this;
}
/**
* Get the color for the message.
*
* @return string
*/
public function color()
{
switch ($this->level) {
case 'success':
return 'good';
case 'error':
return 'danger';
case 'warning':
return 'warning';
}
}
/**
* Find and link channel names and usernames.
*
* @return $this
*/
public function linkNames()
{
$this->linkNames = 1;
return $this;
}
/**
* Set additional request options for the Guzzle HTTP client.
*
* @param array $options
* @return $this
*/
public function http(array $options)
{
$this->http = $options;
return $this;
}
}

View File

@@ -15,6 +15,13 @@ class Notification
*/
public $id;
/**
* The locale to be used when sending the notification.
*
* @var string|null
*/
public $locale;
/**
* Get the channels the event should broadcast on.
*
@@ -24,4 +31,17 @@ class Notification
{
return [];
}
/**
* Set the locale to send this notification in.
*
* @param string $locale
* @return $this
*/
public function locale($locale)
{
$this->locale = $locale;
return $this;
}
}

View File

@@ -2,14 +2,20 @@
namespace Illuminate\Notifications;
use Ramsey\Uuid\Uuid;
use Illuminate\Support\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Translation\HasLocalePreference;
use Illuminate\Database\Eloquent\Collection as ModelCollection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Events\NotificationSending;
use Illuminate\Notifications\Events\NotificationSent;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use Illuminate\Support\Traits\Localizable;
class NotificationSender
{
use Localizable;
/**
* The notification manager instance.
*
@@ -31,18 +37,27 @@ class NotificationSender
*/
protected $events;
/**
* The locale to be used when sending notifications.
*
* @var string|null
*/
protected $locale;
/**
* Create a new notification sender instance.
*
* @param \Illuminate\Notifications\ChannelManager $manager
* @param \Illuminate\Contracts\Bus\Dispatcher $bus
* @param \Illuminate\Contracts\Events\Dispatcher $events
* @param string|null $locale
* @return void
*/
public function __construct($manager, $bus, $events)
public function __construct($manager, $bus, $events, $locale = null)
{
$this->bus = $bus;
$this->events = $events;
$this->locale = $locale;
$this->manager = $manager;
}
@@ -61,7 +76,7 @@ class NotificationSender
return $this->queueNotification($notifiables, $notification);
}
return $this->sendNow($notifiables, $notification);
$this->sendNow($notifiables, $notification);
}
/**
@@ -69,7 +84,7 @@ class NotificationSender
*
* @param \Illuminate\Support\Collection|array|mixed $notifiables
* @param mixed $notification
* @param array $channels
* @param array|null $channels
* @return void
*/
public function sendNow($notifiables, $notification, array $channels = null)
@@ -79,18 +94,38 @@ class NotificationSender
$original = clone $notification;
foreach ($notifiables as $notifiable) {
$notificationId = Uuid::uuid4()->toString();
if (empty($viaChannels = $channels ?: $notification->via($notifiable))) {
continue;
}
foreach ((array) $viaChannels as $channel) {
$this->sendToNotifiable($notifiable, $notificationId, clone $original, $channel);
}
$this->withLocale($this->preferredLocale($notifiable, $notification), function () use ($viaChannels, $notifiable, $original) {
$notificationId = Str::uuid()->toString();
foreach ((array) $viaChannels as $channel) {
if (! ($notifiable instanceof AnonymousNotifiable && $channel === 'database')) {
$this->sendToNotifiable($notifiable, $notificationId, clone $original, $channel);
}
}
});
}
}
/**
* Get the notifiable's preferred locale for the notification.
*
* @param mixed $notifiable
* @param mixed $notification
* @return string|null
*/
protected function preferredLocale($notifiable, $notification)
{
return $notification->locale ?? $this->locale ?? value(function () use ($notifiable) {
if ($notifiable instanceof HasLocalePreference) {
return $notifiable->preferredLocale();
}
});
}
/**
* Send the given notification to the given notifiable via a channel.
*
@@ -113,7 +148,7 @@ class NotificationSender
$response = $this->manager->driver($channel)->send($notifiable, $notification);
$this->events->dispatch(
new Events\NotificationSent($notifiable, $notification, $channel, $response)
new NotificationSent($notifiable, $notification, $channel, $response)
);
}
@@ -127,8 +162,13 @@ class NotificationSender
*/
protected function shouldSendNotification($notifiable, $notification, $channel)
{
if (method_exists($notification, 'shouldSend') &&
$notification->shouldSend($notifiable, $channel) === false) {
return false;
}
return $this->events->until(
new Events\NotificationSending($notifiable, $notification, $channel)
new NotificationSending($notifiable, $notification, $channel)
) !== false;
}
@@ -136,7 +176,7 @@ class NotificationSender
* Queue the given notification instances.
*
* @param mixed $notifiables
* @param array[\Illuminate\Notifications\Channels\Notification] $notification
* @param \Illuminate\Notifications\Notification $notification
* @return void
*/
protected function queueNotification($notifiables, $notification)
@@ -146,18 +186,52 @@ class NotificationSender
$original = clone $notification;
foreach ($notifiables as $notifiable) {
$notificationId = Uuid::uuid4()->toString();
$notificationId = Str::uuid()->toString();
foreach ($original->via($notifiable) as $channel) {
foreach ((array) $original->via($notifiable) as $channel) {
$notification = clone $original;
$notification->id = $notificationId;
if (! $notification->id) {
$notification->id = $notificationId;
}
if (! is_null($this->locale)) {
$notification->locale = $this->locale;
}
$connection = $notification->connection;
if (method_exists($notification, 'viaConnections')) {
$connection = $notification->viaConnections()[$channel] ?? null;
}
$queue = $notification->queue;
if (method_exists($notification, 'viaQueues')) {
$queue = $notification->viaQueues()[$channel] ?? null;
}
$delay = $notification->delay;
if (method_exists($notification, 'withDelay')) {
$delay = $notification->withDelay($notifiable, $channel) ?? null;
}
$middleware = $notification->middleware ?? [];
if (method_exists($notification, 'middleware')) {
$middleware = array_merge(
$notification->middleware($notifiable, $channel),
$middleware
);
}
$this->bus->dispatch(
(new SendQueuedNotifications($this->formatNotifiables($notifiable), $notification, [$channel]))
->onConnection($notification->connection)
->onQueue($notification->queue)
->delay($notification->delay)
(new SendQueuedNotifications($notifiable, $notification, [$channel]))
->onConnection($connection)
->onQueue($queue)
->delay(is_array($delay) ? ($delay[$channel] ?? null) : $delay)
->through($middleware)
);
}
}
@@ -167,7 +241,7 @@ class NotificationSender
* Format the notifiables into a Collection / array if necessary.
*
* @param mixed $notifiables
* @return ModelCollection|array
* @return \Illuminate\Database\Eloquent\Collection|array
*/
protected function formatNotifiables($notifiables)
{

View File

@@ -2,9 +2,9 @@
namespace Illuminate\Notifications;
use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Notifications\Factory as FactoryContract;
use Illuminate\Contracts\Notifications\Dispatcher as DispatcherContract;
use Illuminate\Contracts\Notifications\Factory as FactoryContract;
use Illuminate\Support\ServiceProvider;
class NotificationServiceProvider extends ServiceProvider
{

View File

@@ -2,8 +2,8 @@
namespace Illuminate\Notifications;
use Illuminate\Support\Str;
use Illuminate\Contracts\Notifications\Dispatcher;
use Illuminate\Support\Str;
trait RoutesNotifications
{
@@ -18,25 +18,35 @@ trait RoutesNotifications
app(Dispatcher::class)->send($this, $instance);
}
/**
* Send the given notification immediately.
*
* @param mixed $instance
* @param array|null $channels
* @return void
*/
public function notifyNow($instance, array $channels = null)
{
app(Dispatcher::class)->sendNow($this, $instance, $channels);
}
/**
* Get the notification routing information for the given driver.
*
* @param string $driver
* @param \Illuminate\Notifications\Notification|null $notification
* @return mixed
*/
public function routeNotificationFor($driver)
public function routeNotificationFor($driver, $notification = null)
{
if (method_exists($this, $method = 'routeNotificationFor'.Str::studly($driver))) {
return $this->{$method}();
return $this->{$method}($notification);
}
switch ($driver) {
case 'database':
return $this->notifications();
case 'mail':
return $this->email;
case 'nexmo':
return $this->phone_number;
}
return match ($driver) {
'database' => $this->notifications(),
'mail' => $this->email,
default => null,
};
}
}

View File

@@ -3,12 +3,17 @@
namespace Illuminate\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Collection;
class SendQueuedNotifications implements ShouldQueue
{
use Queueable, SerializesModels;
use InteractsWithQueue, Queueable, SerializesModels;
/**
* The notifiable entities that should receive the notification.
@@ -25,25 +30,75 @@ class SendQueuedNotifications implements ShouldQueue
public $notification;
/**
* All of the channels to send the notification too.
* All of the channels to send the notification to.
*
* @var array
*/
public $channels;
/**
* The number of times the job may be attempted.
*
* @var int
*/
public $tries;
/**
* The number of seconds the job can run before timing out.
*
* @var int
*/
public $timeout;
/**
* The maximum number of unhandled exceptions to allow before failing.
*
* @var int
*/
public $maxExceptions;
/**
* Indicates if the job should be encrypted.
*
* @var bool
*/
public $shouldBeEncrypted = false;
/**
* Create a new job instance.
*
* @param \Illuminate\Support\Collection $notifiables
* @param \Illuminate\Notifications\Notifiable|\Illuminate\Support\Collection $notifiables
* @param \Illuminate\Notifications\Notification $notification
* @param array $channels
* @param array|null $channels
* @return void
*/
public function __construct($notifiables, $notification, array $channels = null)
{
$this->channels = $channels;
$this->notifiables = $notifiables;
$this->notification = $notification;
$this->notifiables = $this->wrapNotifiables($notifiables);
$this->tries = property_exists($notification, 'tries') ? $notification->tries : null;
$this->timeout = property_exists($notification, 'timeout') ? $notification->timeout : null;
$this->maxExceptions = property_exists($notification, 'maxExceptions') ? $notification->maxExceptions : null;
$this->afterCommit = property_exists($notification, 'afterCommit') ? $notification->afterCommit : null;
$this->shouldBeEncrypted = $notification instanceof ShouldBeEncrypted;
}
/**
* Wrap the notifiable(s) in a collection.
*
* @param \Illuminate\Notifications\Notifiable|\Illuminate\Support\Collection $notifiables
* @return \Illuminate\Support\Collection
*/
protected function wrapNotifiables($notifiables)
{
if ($notifiables instanceof Collection) {
return $notifiables;
} elseif ($notifiables instanceof Model) {
return EloquentCollection::wrap($notifiables);
}
return Collection::wrap($notifiables);
}
/**
@@ -66,4 +121,56 @@ class SendQueuedNotifications implements ShouldQueue
{
return get_class($this->notification);
}
/**
* Call the failed method on the notification instance.
*
* @param \Throwable $e
* @return void
*/
public function failed($e)
{
if (method_exists($this->notification, 'failed')) {
$this->notification->failed($e);
}
}
/**
* Get the number of seconds before a released notification will be available.
*
* @return mixed
*/
public function backoff()
{
if (! method_exists($this->notification, 'backoff') && ! isset($this->notification->backoff)) {
return;
}
return $this->notification->backoff ?? $this->notification->backoff();
}
/**
* Determine the time at which the job should timeout.
*
* @return \DateTime|null
*/
public function retryUntil()
{
if (! method_exists($this->notification, 'retryUntil') && ! isset($this->notification->retryUntil)) {
return;
}
return $this->notification->retryUntil ?? $this->notification->retryUntil();
}
/**
* Prepare the instance for cloning.
*
* @return void
*/
public function __clone()
{
$this->notifiables = clone $this->notifiables;
$this->notification = clone $this->notification;
}
}

View File

@@ -14,16 +14,16 @@
}
],
"require": {
"php": ">=5.6.4",
"illuminate/broadcasting": "5.4.*",
"illuminate/bus": "5.4.*",
"illuminate/container": "5.4.*",
"illuminate/contracts": "5.4.*",
"illuminate/filesystem": "5.4.*",
"illuminate/mail": "5.4.*",
"illuminate/queue": "5.4.*",
"illuminate/support": "5.4.*",
"ramsey/uuid": "~3.0"
"php": "^8.0.2",
"illuminate/broadcasting": "^9.0",
"illuminate/bus": "^9.0",
"illuminate/collections": "^9.0",
"illuminate/container": "^9.0",
"illuminate/contracts": "^9.0",
"illuminate/filesystem": "^9.0",
"illuminate/mail": "^9.0",
"illuminate/queue": "^9.0",
"illuminate/support": "^9.0"
},
"autoload": {
"psr-4": {
@@ -32,13 +32,11 @@
},
"extra": {
"branch-alias": {
"dev-master": "5.4-dev"
"dev-master": "9.x-dev"
}
},
"suggest": {
"guzzlehttp/guzzle": "Required to use the Slack transport (~6.0).",
"illuminate/database": "Required to use the database transport (5.4.*).",
"nexmo/client": "Required to use the Nexmo transport (~1.0)."
"illuminate/database": "Required to use the database transport (^9.0)."
},
"config": {
"sort-packages": true

View File

@@ -1,12 +1,12 @@
@component('mail::message')
<x-mail::message>
{{-- Greeting --}}
@if (! empty($greeting))
# {{ $greeting }}
@else
@if ($level == 'error')
# Whoops!
@if ($level === 'error')
# @lang('Whoops!')
@else
# Hello!
# @lang('Hello!')
@endif
@endif
@@ -19,20 +19,14 @@
{{-- Action Button --}}
@isset($actionText)
<?php
switch ($level) {
case 'success':
$color = 'green';
break;
case 'error':
$color = 'red';
break;
default:
$color = 'blue';
}
$color = match ($level) {
'success', 'error' => $level,
default => 'primary',
};
?>
@component('mail::button', ['url' => $actionUrl, 'color' => $color])
<x-mail::button :url="$actionUrl" :color="$color">
{{ $actionText }}
@endcomponent
</x-mail::button>
@endisset
{{-- Outro Lines --}}
@@ -45,14 +39,20 @@
@if (! empty($salutation))
{{ $salutation }}
@else
Regards,<br>{{ config('app.name') }}
@lang('Regards'),<br>
{{ config('app.name') }}
@endif
{{-- Subcopy --}}
@isset($actionText)
@component('mail::subcopy')
If youre having trouble clicking the "{{ $actionText }}" button, copy and paste the URL below
into your web browser: [{{ $actionUrl }}]({{ $actionUrl }})
@endcomponent
<x-slot:subcopy>
@lang(
"If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\n".
'into your web browser:',
[
'actionText' => $actionText,
]
) <span class="break-all">[{{ $displayableActionUrl }}]({{ $actionUrl }})</span>
</x-slot:subcopy>
@endisset
@endcomponent
</x-mail::message>