/
NotificationSyncer.php
232 lines (203 loc) · 7.08 KB
/
NotificationSyncer.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
<?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;
use Carbon\Carbon;
use Flarum\Notification\Blueprint\BlueprintInterface;
use Flarum\Notification\Event\Sending;
use Flarum\User\User;
/**
* The Notification Syncer commits notification blueprints to the database, and
* sends them via email depending on user preference. Where a blueprint
* represents a single notification, the syncer associates it with a particular
* user(s) and makes it available in their inbox.
*/
class NotificationSyncer
{
/**
* Whether or not notifications are being limited to one per user.
*
* @var bool
*/
protected static $onePerUser = false;
/**
* An internal list of user IDs that notifications have been sent to.
*
* @var int[]
*/
protected static $sentTo = [];
/**
* @var NotificationRepository
*/
protected $notifications;
/**
* @var NotificationMailer
*/
protected $mailer;
/**
* @param NotificationRepository $notifications
* @param NotificationMailer $mailer
*/
public function __construct(
NotificationRepository $notifications,
NotificationMailer $mailer
) {
$this->notifications = $notifications;
$this->mailer = $mailer;
}
/**
* Sync a notification so that it is visible to the specified users, and not
* visible to anyone else. If it is being made visible for the first time,
* attempt to send the user an email.
*
* @param \Flarum\Notification\Blueprint\BlueprintInterface $blueprint
* @param User[] $users
* @return void
*/
public function sync(Blueprint\BlueprintInterface $blueprint, array $users)
{
$attributes = $this->getAttributes($blueprint);
// Find all existing notification records in the database matching this
// blueprint. We will begin by assuming that they all need to be
// deleted in order to match the provided list of users.
$toDelete = Notification::where($attributes)->get();
$toUndelete = [];
$newRecipients = [];
// For each of the provided users, check to see if they already have
// a notification record in the database. If they do, we will make sure
// it isn't marked as deleted. If they don't, we will want to create a
// new record for them.
foreach ($users as $user) {
if (! ($user instanceof User)) {
continue;
}
$existing = $toDelete->first(function ($notification, $i) use ($user) {
return $notification->user_id === $user->id;
});
if ($existing) {
$toUndelete[] = $existing->id;
$toDelete->forget($toDelete->search($existing));
} elseif (! static::$onePerUser || ! in_array($user->id, static::$sentTo)) {
$newRecipients[] = $user;
static::$sentTo[] = $user->id;
}
}
// Delete all of the remaining notification records which weren't
// removed from this collection by the above loop. Un-delete the
// existing records that we want to keep.
if (count($toDelete)) {
$this->setDeleted($toDelete->pluck('id')->all(), true);
}
if (count($toUndelete)) {
$this->setDeleted($toUndelete, false);
}
// Create a notification record, and send an email, for all users
// receiving this notification for the first time (we know because they
// didn't have a record in the database).
if (count($newRecipients)) {
$this->sendNotifications($blueprint, $newRecipients);
}
}
/**
* Delete a notification for all users.
*
* @param \Flarum\Notification\Blueprint\BlueprintInterface $blueprint
* @return void
*/
public function delete(BlueprintInterface $blueprint)
{
Notification::where($this->getAttributes($blueprint))->update(['is_deleted' => true]);
}
/**
* Restore a notification for all users.
*
* @param BlueprintInterface $blueprint
* @return void
*/
public function restore(BlueprintInterface $blueprint)
{
Notification::where($this->getAttributes($blueprint))->update(['is_deleted' => false]);
}
/**
* Limit notifications to one per user for the entire duration of the given
* callback.
*
* @param callable $callback
* @return void
*/
public function onePerUser(callable $callback)
{
static::$sentTo = [];
static::$onePerUser = true;
$callback();
static::$onePerUser = false;
}
/**
* Create a notification record and send an email (depending on user
* preference) from a blueprint to a list of recipients.
*
* @param \Flarum\Notification\Blueprint\BlueprintInterface $blueprint
* @param User[] $recipients
*/
protected function sendNotifications(Blueprint\BlueprintInterface $blueprint, array $recipients)
{
$now = Carbon::now('utc')->toDateTimeString();
event(new Sending($blueprint, $recipients));
$attributes = $this->getAttributes($blueprint);
Notification::insert(
array_map(function (User $user) use ($attributes, $now) {
return $attributes + [
'user_id' => $user->id,
'created_at' => $now
];
}, $recipients)
);
if ($blueprint instanceof MailableInterface) {
$this->mailNotifications($blueprint, $recipients);
}
}
/**
* 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->mailer->send($blueprint, $user);
}
}
}
/**
* Set the deleted status of a list of notification records.
*
* @param int[] $ids
* @param bool $isDeleted
*/
protected function setDeleted(array $ids, $isDeleted)
{
Notification::whereIn('id', $ids)->update(['is_deleted' => $isDeleted]);
}
/**
* Construct an array of attributes to be stored in a notification record in
* the database, given a notification blueprint.
*
* @param \Flarum\Notification\Blueprint\BlueprintInterface $blueprint
* @return array
*/
protected function getAttributes(Blueprint\BlueprintInterface $blueprint)
{
return [
'type' => $blueprint::getType(),
'from_user_id' => ($fromUser = $blueprint->getFromUser()) ? $fromUser->id : null,
'subject_id' => ($subject = $blueprint->getSubject()) ? $subject->id : null,
'data' => ($data = $blueprint->getData()) ? json_encode($data) : null
];
}
}