1 | <?php |
||||||
2 | |||||||
3 | namespace Elgg\Notifications; |
||||||
4 | |||||||
5 | use Elgg\Database\EntityTable; |
||||||
6 | use Elgg\I18n\Translator; |
||||||
7 | use Elgg\Logger; |
||||||
8 | use Elgg\PluginHooksService; |
||||||
9 | use Elgg\Queue\Queue; |
||||||
10 | use ElggData; |
||||||
11 | use ElggEntity; |
||||||
12 | use ElggSession; |
||||||
13 | use ElggUser; |
||||||
14 | use InvalidArgumentException; |
||||||
15 | use RuntimeException; |
||||||
16 | |||||||
17 | /** |
||||||
18 | * WARNING: API IN FLUX. DO NOT USE DIRECTLY. |
||||||
19 | * |
||||||
20 | * @access private |
||||||
21 | * |
||||||
22 | * @package Elgg.Core |
||||||
23 | * @subpackage Notifications |
||||||
24 | * @since 1.9.0 |
||||||
25 | */ |
||||||
26 | class NotificationsService { |
||||||
27 | |||||||
28 | const QUEUE_NAME = 'notifications'; |
||||||
29 | |||||||
30 | /** @var SubscriptionsService */ |
||||||
31 | protected $subscriptions; |
||||||
32 | |||||||
33 | /** @var Queue */ |
||||||
34 | protected $queue; |
||||||
35 | |||||||
36 | /** @var PluginHooksService */ |
||||||
37 | protected $hooks; |
||||||
38 | |||||||
39 | /** @var ElggSession */ |
||||||
40 | protected $session; |
||||||
41 | |||||||
42 | /** @var Translator */ |
||||||
43 | protected $translator; |
||||||
44 | |||||||
45 | /** @var EntityTable */ |
||||||
46 | protected $entities; |
||||||
47 | |||||||
48 | /** @var Logger */ |
||||||
49 | protected $logger; |
||||||
50 | |||||||
51 | /** @var array Registered notification events */ |
||||||
52 | protected $events = []; |
||||||
53 | |||||||
54 | /** @var array Registered notification methods */ |
||||||
55 | protected $methods = []; |
||||||
56 | |||||||
57 | /** @var array Deprecated notification handlers */ |
||||||
58 | protected $deprHandlers = []; |
||||||
59 | |||||||
60 | /** @var array Deprecated message subjects */ |
||||||
61 | protected $deprSubjects = []; |
||||||
62 | |||||||
63 | /** |
||||||
64 | * Constructor |
||||||
65 | * |
||||||
66 | * @param SubscriptionsService $subscriptions Subscription service |
||||||
67 | * @param Queue $queue Queue |
||||||
68 | * @param PluginHooksService $hooks Plugin hook service |
||||||
69 | * @param ElggSession $session Session service |
||||||
70 | * @param Translator $translator Translator |
||||||
71 | * @param EntityTable $entities Entity table |
||||||
72 | * @param Logger $logger Logger |
||||||
73 | */ |
||||||
74 | 127 | public function __construct( |
|||||
75 | SubscriptionsService $subscriptions, |
||||||
76 | Queue $queue, PluginHooksService $hooks, |
||||||
77 | ElggSession $session, |
||||||
78 | Translator $translator, |
||||||
79 | EntityTable $entities, |
||||||
80 | Logger $logger) { |
||||||
81 | |||||||
82 | 127 | $this->subscriptions = $subscriptions; |
|||||
83 | 127 | $this->queue = $queue; |
|||||
84 | 127 | $this->hooks = $hooks; |
|||||
85 | 127 | $this->session = $session; |
|||||
86 | 127 | $this->translator = $translator; |
|||||
87 | 127 | $this->entities = $entities; |
|||||
88 | 127 | $this->logger = $logger; |
|||||
89 | 127 | } |
|||||
90 | |||||||
91 | /** |
||||||
92 | * Register a notification event |
||||||
93 | * |
||||||
94 | * @param string $type 'object', 'user', 'group', 'site' |
||||||
95 | * @param string $subtype The subtype or name of the entity |
||||||
96 | * @param array $actions Array of actions or empty array for the action event. |
||||||
97 | * An event is usually described by the first string passed |
||||||
98 | * to elgg_trigger_event(). Examples include |
||||||
99 | * 'create', 'update', and 'publish'. The default is 'create'. |
||||||
100 | * @return void |
||||||
101 | * |
||||||
102 | * @see elgg_register_notification_event() |
||||||
103 | * @access private |
||||||
104 | */ |
||||||
105 | 97 | public function registerEvent($type, $subtype, array $actions = []) { |
|||||
106 | |||||||
107 | 97 | if (!isset($this->events[$type])) { |
|||||
108 | 84 | $this->events[$type] = []; |
|||||
109 | } |
||||||
110 | 97 | if (!isset($this->events[$type][$subtype])) { |
|||||
111 | 84 | $this->events[$type][$subtype] = []; |
|||||
112 | } |
||||||
113 | |||||||
114 | 97 | $action_list =& $this->events[$type][$subtype]; |
|||||
115 | 97 | if ($actions) { |
|||||
0 ignored issues
–
show
|
|||||||
116 | 79 | $action_list = array_unique(array_merge($action_list, $actions)); |
|||||
117 | 55 | } elseif (!in_array('create', $action_list)) { |
|||||
118 | 42 | $action_list[] = 'create'; |
|||||
119 | } |
||||||
120 | 97 | } |
|||||
121 | |||||||
122 | /** |
||||||
123 | * Unregister a notification event |
||||||
124 | * |
||||||
125 | * @param string $type 'object', 'user', 'group', 'site' |
||||||
126 | * @param string $subtype The type of the entity |
||||||
127 | * |
||||||
128 | * @return bool |
||||||
129 | * |
||||||
130 | * @see elgg_unregister_notification_event() |
||||||
131 | * @access private |
||||||
132 | */ |
||||||
133 | 6 | public function unregisterEvent($type, $subtype) { |
|||||
134 | |||||||
135 | 6 | if (!isset($this->events[$type]) || !isset($this->events[$type][$subtype])) { |
|||||
136 | 6 | return false; |
|||||
137 | } |
||||||
138 | |||||||
139 | 6 | unset($this->events[$type][$subtype]); |
|||||
140 | |||||||
141 | 6 | return true; |
|||||
142 | } |
||||||
143 | |||||||
144 | /** |
||||||
145 | * Return the notification events |
||||||
146 | * |
||||||
147 | * @return array |
||||||
148 | * |
||||||
149 | * @access private |
||||||
150 | */ |
||||||
151 | 12 | public function getEvents() { |
|||||
152 | 12 | return $this->events; |
|||||
153 | } |
||||||
154 | |||||||
155 | /** |
||||||
156 | * Register a delivery method for notifications |
||||||
157 | * |
||||||
158 | * @param string $name The notification method name |
||||||
159 | * @return void |
||||||
160 | * |
||||||
161 | * @see elgg_register_notification_method() |
||||||
162 | * @access private |
||||||
163 | */ |
||||||
164 | 98 | public function registerMethod($name) { |
|||||
165 | 98 | $this->methods[$name] = $name; |
|||||
166 | 98 | } |
|||||
167 | |||||||
168 | /** |
||||||
169 | * Unregister a delivery method for notifications |
||||||
170 | * |
||||||
171 | * @param string $name The notification method name |
||||||
172 | * @return bool |
||||||
173 | * |
||||||
174 | * @see elgg_unregister_notification_method() |
||||||
175 | * @access private |
||||||
176 | */ |
||||||
177 | 6 | public function unregisterMethod($name) { |
|||||
178 | 6 | if (isset($this->methods[$name])) { |
|||||
179 | 6 | unset($this->methods[$name]); |
|||||
180 | 6 | return true; |
|||||
181 | } |
||||||
182 | 6 | return false; |
|||||
183 | } |
||||||
184 | |||||||
185 | /** |
||||||
186 | * Returns registered delivery methods for notifications |
||||||
187 | * |
||||||
188 | * @return string[] |
||||||
189 | * |
||||||
190 | * @see elgg_get_notification_methods() |
||||||
191 | * @access private |
||||||
192 | */ |
||||||
193 | 35 | public function getMethods() { |
|||||
194 | 35 | return $this->methods; |
|||||
195 | } |
||||||
196 | |||||||
197 | /** |
||||||
198 | * Add a notification event to the queue |
||||||
199 | * |
||||||
200 | * @param string $action Action name |
||||||
201 | * @param string $type Type of the object of the action |
||||||
202 | * @param ElggData $object The object of the action |
||||||
203 | * @return void |
||||||
204 | * @access private |
||||||
205 | */ |
||||||
206 | 484 | public function enqueueEvent($action, $type, $object) { |
|||||
207 | |||||||
208 | 484 | if ($object instanceof ElggData) { |
|||||
209 | 483 | $object_type = $object->getType(); |
|||||
210 | 483 | $object_subtype = $object->getSubtype(); |
|||||
211 | |||||||
212 | 483 | $registered = false; |
|||||
213 | 483 | if (!empty($this->events[$object_type][$object_subtype]) && in_array($action, $this->events[$object_type][$object_subtype])) { |
|||||
214 | 61 | $registered = true; |
|||||
215 | } |
||||||
216 | |||||||
217 | 483 | if ($registered) { |
|||||
218 | $params = [ |
||||||
219 | 61 | 'action' => $action, |
|||||
220 | 61 | 'object' => $object, |
|||||
221 | ]; |
||||||
222 | 61 | $registered = $this->hooks->trigger('enqueue', 'notification', $params, $registered); |
|||||
223 | } |
||||||
224 | |||||||
225 | 483 | if ($registered) { |
|||||
226 | 49 | $this->queue->enqueue(new SubscriptionNotificationEvent($object, $action)); |
|||||
227 | } |
||||||
228 | } |
||||||
229 | 484 | } |
|||||
230 | |||||||
231 | /** |
||||||
232 | * Pull notification events from queue until stop time is reached |
||||||
233 | * |
||||||
234 | * @param int $stopTime The Unix time to stop sending notifications |
||||||
235 | * @param bool $matrix If true, will return delivery matrix instead of a notifications event count |
||||||
236 | * @return int|array The number of notification events handled, or a delivery matrix |
||||||
237 | * @access private |
||||||
238 | */ |
||||||
239 | 48 | public function processQueue($stopTime, $matrix = false) { |
|||||
240 | |||||||
241 | 48 | $this->subscriptions->methods = $this->methods; |
|||||
242 | |||||||
243 | 48 | $delivery_matrix = []; |
|||||
244 | |||||||
245 | 48 | $count = 0; |
|||||
246 | |||||||
247 | // @todo grab mutex |
||||||
248 | |||||||
249 | 48 | $ia = $this->session->setIgnoreAccess(true); |
|||||
250 | |||||||
251 | 48 | while (time() < $stopTime) { |
|||||
252 | // dequeue notification event |
||||||
253 | 42 | $event = $this->queue->dequeue(); |
|||||
254 | /* @var $event NotificationEvent */ |
||||||
255 | |||||||
256 | 42 | if (!$event) { |
|||||
257 | // queue is empty |
||||||
258 | 42 | break; |
|||||
259 | } |
||||||
260 | |||||||
261 | 30 | if (!$event instanceof NotificationEvent || !$event->getObject() || !$event->getActor()) { |
|||||
262 | // event object or actor have been deleted since the event was enqueued |
||||||
263 | continue; |
||||||
264 | } |
||||||
265 | |||||||
266 | 30 | $subscriptions = $this->subscriptions->getSubscriptions($event); |
|||||
267 | |||||||
268 | // return false to stop the default notification sender |
||||||
269 | $params = [ |
||||||
270 | 30 | 'event' => $event, |
|||||
271 | 30 | 'subscriptions' => $subscriptions |
|||||
272 | ]; |
||||||
273 | |||||||
274 | 30 | $deliveries = []; |
|||||
275 | 30 | if ($this->hooks->trigger('send:before', 'notifications', $params, true)) { |
|||||
276 | 24 | $deliveries = $this->sendNotifications($event, $subscriptions); |
|||||
277 | } |
||||||
278 | 30 | $params['deliveries'] = $deliveries; |
|||||
279 | 30 | $this->hooks->trigger('send:after', 'notifications', $params); |
|||||
280 | 30 | $count++; |
|||||
281 | |||||||
282 | 30 | $delivery_matrix[$event->getDescription()] = $deliveries; |
|||||
283 | } |
||||||
284 | |||||||
285 | // release mutex |
||||||
286 | |||||||
287 | 48 | $this->session->setIgnoreAccess($ia); |
|||||
288 | |||||||
289 | 48 | return $matrix ? $delivery_matrix : $count; |
|||||
290 | } |
||||||
291 | |||||||
292 | /** |
||||||
293 | * Sends the notifications based on subscriptions |
||||||
294 | * |
||||||
295 | * Returns an array in the form: |
||||||
296 | * <code> |
||||||
297 | * [ |
||||||
298 | * 25 => [ |
||||||
299 | * 'email' => true, |
||||||
300 | * 'sms' => false, |
||||||
301 | * ], |
||||||
302 | * 55 => [], |
||||||
303 | * ] |
||||||
304 | * </code> |
||||||
305 | * |
||||||
306 | * @param NotificationEvent $event Notification event |
||||||
307 | * @param array $subscriptions Subscriptions for this event |
||||||
308 | * @param array $params Default notification parameters |
||||||
309 | * @return array |
||||||
310 | * @access private |
||||||
311 | */ |
||||||
312 | 44 | protected function sendNotifications($event, $subscriptions, array $params = []) { |
|||||
313 | |||||||
314 | 44 | if (!$this->methods) { |
|||||
0 ignored issues
–
show
The expression
$this->methods of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using
Loading history...
|
|||||||
315 | 6 | return 0; |
|||||
316 | } |
||||||
317 | |||||||
318 | 38 | $result = []; |
|||||
319 | 38 | foreach ($subscriptions as $guid => $methods) { |
|||||
320 | 38 | foreach ($methods as $method) { |
|||||
321 | 38 | $result[$guid][$method] = false; |
|||||
322 | 38 | if (in_array($method, $this->methods)) { |
|||||
323 | 38 | $result[$guid][$method] = $this->sendNotification($event, $guid, $method, $params); |
|||||
324 | } |
||||||
325 | } |
||||||
326 | } |
||||||
327 | |||||||
328 | 38 | $this->logger->info("Results for the notification event {$event->getDescription()}: " . print_r($result, true)); |
|||||
329 | 38 | return $result; |
|||||
330 | } |
||||||
331 | |||||||
332 | /** |
||||||
333 | * Notify a user via their preferences. |
||||||
334 | * |
||||||
335 | * Returns an array in the form: |
||||||
336 | * <code> |
||||||
337 | * [ |
||||||
338 | * 25 => [ |
||||||
339 | * 'email' => true, |
||||||
340 | * 'sms' => false, |
||||||
341 | * ], |
||||||
342 | * 55 => [], |
||||||
343 | * ] |
||||||
344 | * </code> |
||||||
345 | * |
||||||
346 | * @param ElggEntity $sender Sender of the notification |
||||||
347 | * @param ElggUser[] $recipients An array of entities to notify |
||||||
348 | * @param array $params Notification parameters |
||||||
349 | * |
||||||
350 | * @uses $params['subject'] string |
||||||
351 | * Default message subject |
||||||
352 | * @uses $params['body'] string |
||||||
353 | * Default message body |
||||||
354 | * @uses $params['object'] null|\ElggEntity|\ElggAnnotation |
||||||
355 | * The object that is triggering the notification. |
||||||
356 | * @uses $params['action'] null|string |
||||||
357 | * Word that describes the action that is triggering the notification |
||||||
358 | * (e.g. "create" or "update"). Defaults to "notify_user" |
||||||
359 | * @uses $params['summary'] null|string |
||||||
360 | * Summary that notification plugins can use alongside the notification title and body. |
||||||
361 | * @uses $params['methods_override'] string|array |
||||||
362 | * A string, or an array of strings specifying the delivery |
||||||
363 | * methods to use - or leave blank for delivery using the |
||||||
364 | * user's chosen delivery methods. |
||||||
365 | * |
||||||
366 | * @return array |
||||||
367 | * @access private |
||||||
368 | */ |
||||||
369 | 26 | public function sendInstantNotifications(\ElggEntity $sender, array $recipients = [], array $params = []) { |
|||||
370 | |||||||
371 | 26 | if (!$sender instanceof \ElggEntity) { |
|||||
372 | throw new InvalidArgumentException("Notification sender must be a valid entity"); |
||||||
373 | } |
||||||
374 | |||||||
375 | 26 | $deliveries = []; |
|||||
376 | |||||||
377 | 26 | if (!$this->methods) { |
|||||
0 ignored issues
–
show
The expression
$this->methods of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using
Loading history...
|
|||||||
378 | return $deliveries; |
||||||
379 | } |
||||||
380 | |||||||
381 | 26 | $recipients = array_filter($recipients, function($e) { |
|||||
382 | 26 | return ($e instanceof \ElggUser); |
|||||
383 | 26 | }); |
|||||
384 | |||||||
385 | 26 | $object = elgg_extract('object', $params); |
|||||
386 | 26 | $action = elgg_extract('action', $params); |
|||||
387 | |||||||
388 | 26 | $methods_override = elgg_extract('methods_override', $params); |
|||||
389 | 26 | unset($params['methods_override']); |
|||||
390 | 26 | if ($methods_override && !is_array($methods_override)) { |
|||||
391 | 6 | $methods_override = [$methods_override]; |
|||||
392 | } |
||||||
393 | |||||||
394 | 26 | $event = new InstantNotificationEvent($object, $action, $sender); |
|||||
395 | |||||||
396 | 26 | $params['event'] = $event; |
|||||
397 | 26 | $params['origin'] = Notification::ORIGIN_INSTANT; |
|||||
398 | |||||||
399 | 26 | $subscriptions = []; |
|||||
400 | |||||||
401 | 26 | foreach ($recipients as $recipient) { |
|||||
402 | // Are we overriding delivery? |
||||||
403 | 26 | $methods = $methods_override; |
|||||
404 | 26 | if (empty($methods)) { |
|||||
405 | 20 | $methods = []; |
|||||
406 | 20 | $user_settings = $recipient->getNotificationSettings(); |
|||||
407 | 20 | foreach ($user_settings as $method => $enabled) { |
|||||
408 | 20 | if ($enabled) { |
|||||
409 | 20 | $methods[] = $method; |
|||||
410 | } |
||||||
411 | } |
||||||
412 | } |
||||||
413 | |||||||
414 | 26 | $subscriptions[$recipient->guid] = $methods; |
|||||
415 | } |
||||||
416 | |||||||
417 | $hook_params = [ |
||||||
418 | 26 | 'event' => $params['event'], |
|||||
419 | 26 | 'origin' => $params['origin'], |
|||||
420 | 26 | 'methods_override' => $methods_override, |
|||||
421 | ]; |
||||||
422 | 26 | $subscriptions = $this->hooks->trigger('get', 'subscriptions', $hook_params, $subscriptions); |
|||||
423 | |||||||
424 | 26 | $params['subscriptions'] = $subscriptions; |
|||||
425 | |||||||
426 | // return false to stop the default notification sender |
||||||
427 | 26 | if ($this->hooks->trigger('send:before', 'notifications', $params, true)) { |
|||||
428 | 20 | $deliveries = $this->sendNotifications($event, $subscriptions, $params); |
|||||
429 | } |
||||||
430 | 26 | $params['deliveries'] = $deliveries; |
|||||
431 | 26 | $this->hooks->trigger('send:after', 'notifications', $params); |
|||||
432 | |||||||
433 | 26 | return $deliveries; |
|||||
434 | } |
||||||
435 | |||||||
436 | /** |
||||||
437 | * Send a notification to a subscriber |
||||||
438 | * |
||||||
439 | * @param NotificationEvent $event The notification event |
||||||
440 | * @param int $guid The guid of the subscriber |
||||||
441 | * @param string $method The notification method |
||||||
442 | * @param array $params Default notification params |
||||||
443 | * @return bool |
||||||
444 | * @access private |
||||||
445 | */ |
||||||
446 | 38 | protected function sendNotification(NotificationEvent $event, $guid, $method, array $params = []) { |
|||||
447 | |||||||
448 | 38 | $actor = $event->getActor(); |
|||||
449 | 38 | $object = $event->getObject(); |
|||||
450 | |||||||
451 | 38 | if ($event instanceof InstantNotificationEvent) { |
|||||
452 | 20 | $recipient = $this->entities->get($guid); |
|||||
453 | /* @var \ElggEntity $recipient */ |
||||||
454 | 20 | $subject = elgg_extract('subject', $params, ''); |
|||||
455 | 20 | $body = elgg_extract('body', $params, ''); |
|||||
456 | 20 | $summary = elgg_extract('summary', $params, ''); |
|||||
457 | } else { |
||||||
458 | 18 | $recipient = $this->entities->get($guid, 'user'); |
|||||
459 | /* @var \ElggUser $recipient */ |
||||||
460 | 18 | if (!$recipient || $recipient->isBanned()) { |
|||||
461 | return false; |
||||||
462 | } |
||||||
463 | |||||||
464 | 18 | if ($recipient->getGUID() == $event->getActorGUID()) { |
|||||
465 | // Content creators should not be receiving subscription |
||||||
466 | // notifications about their own content |
||||||
467 | return false; |
||||||
468 | } |
||||||
469 | |||||||
470 | 18 | if (!$actor || !$object) { |
|||||
471 | return false; |
||||||
472 | } |
||||||
473 | |||||||
474 | 18 | if ($object instanceof ElggEntity && !has_access_to_entity($object, $recipient)) { |
|||||
475 | // Recipient does not have access to the notification object |
||||||
476 | // The access level may have changed since the event was enqueued |
||||||
477 | return false; |
||||||
478 | } |
||||||
479 | |||||||
480 | 18 | $subject = $this->getNotificationSubject($event, $recipient); |
|||||
481 | 18 | $body = $this->getNotificationBody($event, $recipient); |
|||||
482 | 18 | $summary = ''; |
|||||
483 | |||||||
484 | 18 | $params['origin'] = Notification::ORIGIN_SUBSCRIPTIONS; |
|||||
485 | } |
||||||
486 | |||||||
487 | 38 | $language = $recipient->language; |
|||||
488 | 38 | $params['event'] = $event; |
|||||
489 | 38 | $params['method'] = $method; |
|||||
490 | 38 | $params['sender'] = $actor; |
|||||
491 | 38 | $params['recipient'] = $recipient; |
|||||
492 | 38 | $params['language'] = $language; |
|||||
493 | 38 | $params['object'] = $object; |
|||||
494 | 38 | $params['action'] = $event->getAction(); |
|||||
495 | |||||||
496 | 38 | $notification = new Notification($actor, $recipient, $language, $subject, $body, $summary, $params); |
|||||
0 ignored issues
–
show
It seems like
$language can also be of type array ; however, parameter $language of Elgg\Notifications\Notification::__construct() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
It seems like
$actor can also be of type false ; however, parameter $from of Elgg\Notifications\Notification::__construct() does only seem to accept ElggEntity , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||||
497 | |||||||
498 | 38 | $notification = $this->hooks->trigger('prepare', 'notification', $params, $notification); |
|||||
499 | 38 | if (!$notification instanceof Notification) { |
|||||
500 | throw new RuntimeException("'prepare','notification' hook must return an instance of " . Notification::class); |
||||||
501 | } |
||||||
502 | |||||||
503 | 38 | $type = 'notification:' . $event->getDescription(); |
|||||
504 | 38 | if ($this->hooks->hasHandler('prepare', $type)) { |
|||||
505 | 18 | $notification = $this->hooks->trigger('prepare', $type, $params, $notification); |
|||||
506 | 18 | if (!$notification instanceof Notification) { |
|||||
507 | 18 | throw new RuntimeException("'prepare','$type' hook must return an instance of " . Notification::class); |
|||||
508 | } |
||||||
509 | } else { |
||||||
510 | // pre Elgg 1.9 notification message generation |
||||||
511 | 20 | $notification = $this->getDeprecatedNotificationBody($notification, $event, $method); |
|||||
512 | } |
||||||
513 | |||||||
514 | 38 | $notification = $this->hooks->trigger('format', "notification:$method", [], $notification); |
|||||
515 | 38 | if (!$notification instanceof Notification) { |
|||||
516 | throw new RuntimeException("'format','notification:$method' hook must return an instance of " . Notification::class); |
||||||
517 | } |
||||||
518 | |||||||
519 | 38 | if ($this->hooks->hasHandler('send', "notification:$method")) { |
|||||
520 | // return true to indicate the notification has been sent |
||||||
521 | $params = [ |
||||||
522 | 38 | 'notification' => $notification, |
|||||
523 | 38 | 'event' => $event, |
|||||
524 | ]; |
||||||
525 | |||||||
526 | 38 | $result = $this->hooks->trigger('send', "notification:$method", $params, false); |
|||||
527 | 38 | if ($this->logger->getLevel() == Logger::INFO) { |
|||||
528 | $logger_data = print_r((array) $notification->toObject(), true); |
||||||
529 | if ($result) { |
||||||
530 | $this->logger->info("Notification sent: " . $logger_data); |
||||||
531 | } else { |
||||||
532 | $this->logger->info("Notification was not sent: " . $logger_data); |
||||||
533 | } |
||||||
534 | } |
||||||
535 | 38 | return $result; |
|||||
536 | } else { |
||||||
537 | // pre Elgg 1.9 notification handler |
||||||
538 | $userGuid = $notification->getRecipientGUID(); |
||||||
539 | $senderGuid = $notification->getSenderGUID(); |
||||||
540 | $subject = $notification->subject; |
||||||
541 | $body = $notification->body; |
||||||
542 | $params = $notification->params; |
||||||
543 | return (bool) _elgg_notify_user($userGuid, $senderGuid, $subject, $body, $params, [$method]); |
||||||
544 | } |
||||||
545 | } |
||||||
546 | |||||||
547 | /** |
||||||
548 | * Get subject for the notification |
||||||
549 | * |
||||||
550 | * Plugins can define a subtype specific subject simply by providing a |
||||||
551 | * translation for the string "notification:subject:<action>:<type>:<subtype". |
||||||
552 | * |
||||||
553 | * For example in mod/blog/languages/en.php: |
||||||
554 | * |
||||||
555 | * 'notification:subject:publish:object:blog' => '%s published a blog called %s' |
||||||
556 | * |
||||||
557 | * @param NotificationEvent $event Notification event |
||||||
558 | * @param ElggUser $recipient Notification recipient |
||||||
559 | * @return string Notification subject in the recipient's language |
||||||
560 | */ |
||||||
561 | 18 | private function getNotificationSubject(NotificationEvent $event, ElggUser $recipient) { |
|||||
562 | 18 | $actor = $event->getActor(); |
|||||
563 | 18 | $object = $event->getObject(); |
|||||
564 | /* @var \ElggObject $object */ |
||||||
565 | 18 | $language = $recipient->language; |
|||||
566 | |||||||
567 | // Check custom notification subject for the action/type/subtype combination |
||||||
568 | 18 | $subject_key = "notification:{$event->getDescription()}:subject"; |
|||||
569 | 18 | if ($this->translator->languageKeyExists($subject_key, $language)) { |
|||||
570 | 12 | if ($object instanceof \ElggEntity) { |
|||||
571 | 6 | $display_name = $object->getDisplayName(); |
|||||
572 | } else { |
||||||
573 | 6 | $display_name = ''; |
|||||
574 | } |
||||||
575 | 12 | return $this->translator->translate($subject_key, [ |
|||||
576 | 12 | $actor->name, |
|||||
577 | 12 | $display_name, |
|||||
578 | 12 | ], $language); |
|||||
579 | } |
||||||
580 | |||||||
581 | // Fall back to default subject |
||||||
582 | 6 | return $this->translator->translate('notification:subject', [$actor->name], $language); |
|||||
583 | } |
||||||
584 | |||||||
585 | /** |
||||||
586 | * Get body for the notification |
||||||
587 | * |
||||||
588 | * Plugin can define a subtype specific body simply by providing a |
||||||
589 | * translation for the string "notification:body:<action>:<type>:<subtype". |
||||||
590 | * |
||||||
591 | * For example in mod/blog/languages/en.php: |
||||||
592 | * |
||||||
593 | * 'notification:body:publish:object:blog' => ' |
||||||
594 | * Hi %s! |
||||||
595 | * |
||||||
596 | * %s has created a new post called "%s" in the group %s. |
||||||
597 | * |
||||||
598 | * It says: |
||||||
599 | * |
||||||
600 | * "%s" |
||||||
601 | * |
||||||
602 | * You can comment the post here: |
||||||
603 | * %s |
||||||
604 | * ', |
||||||
605 | * |
||||||
606 | * The arguments passed into the translation are: |
||||||
607 | * 1. Recipient's name |
||||||
608 | * 2. Name of the user who triggered the notification |
||||||
609 | * 3. Title of the content |
||||||
610 | * 4. Name of the content's container |
||||||
611 | * 5. The actual content (entity's 'description' field) |
||||||
612 | * 6. URL to the content |
||||||
613 | * |
||||||
614 | * Argument swapping can be used to change the order of the parameters. |
||||||
615 | * See http://php.net/manual/en/function.sprintf.php#example-5427 |
||||||
616 | * |
||||||
617 | * @param NotificationEvent $event Notification event |
||||||
618 | * @param ElggUser $recipient Notification recipient |
||||||
619 | * @return string Notification body in the recipient's language |
||||||
620 | */ |
||||||
621 | 18 | private function getNotificationBody(NotificationEvent $event, ElggUser $recipient) { |
|||||
622 | 18 | $actor = $event->getActor(); |
|||||
623 | 18 | $object = $event->getObject(); |
|||||
624 | /* @var \ElggObject $object */ |
||||||
625 | 18 | $language = $recipient->language; |
|||||
626 | |||||||
627 | // Check custom notification body for the action/type/subtype combination |
||||||
628 | 18 | $body_key = "notification:{$event->getDescription()}:body"; |
|||||
629 | 18 | if ($this->translator->languageKeyExists($body_key, $language)) { |
|||||
630 | 12 | if ($object instanceof \ElggEntity) { |
|||||
631 | 6 | $display_name = $object->getDisplayName(); |
|||||
632 | 6 | $container_name = ''; |
|||||
633 | 6 | $container = $object->getContainerEntity(); |
|||||
634 | 6 | if ($container) { |
|||||
635 | 6 | $container_name = $container->getDisplayName(); |
|||||
636 | } |
||||||
637 | } else { |
||||||
638 | 6 | $display_name = ''; |
|||||
639 | 6 | $container_name = ''; |
|||||
640 | } |
||||||
641 | |||||||
642 | 12 | return $this->translator->translate($body_key, [ |
|||||
643 | 12 | $recipient->name, |
|||||
644 | 12 | $actor->name, |
|||||
645 | 12 | $display_name, |
|||||
646 | 12 | $container_name, |
|||||
647 | 12 | $object->description, |
|||||
648 | 12 | $object->getURL(), |
|||||
649 | 12 | ], $language); |
|||||
650 | } |
||||||
651 | |||||||
652 | // Fall back to default body |
||||||
653 | 6 | return $this->translator->translate('notification:body', [$object->getURL()], $language); |
|||||
654 | } |
||||||
655 | |||||||
656 | /** |
||||||
657 | * Register a deprecated notification handler |
||||||
658 | * |
||||||
659 | * @param string $method Method name |
||||||
660 | * @param string $handler Handler callback |
||||||
661 | * @return void |
||||||
662 | */ |
||||||
663 | public function registerDeprecatedHandler($method, $handler) { |
||||||
664 | $this->deprHandlers[$method] = $handler; |
||||||
665 | } |
||||||
666 | |||||||
667 | /** |
||||||
668 | * Get a deprecated notification handler callback |
||||||
669 | * |
||||||
670 | * @param string $method Method name |
||||||
671 | * @return callback|null |
||||||
672 | */ |
||||||
673 | public function getDeprecatedHandler($method) { |
||||||
674 | if (isset($this->deprHandlers[$method])) { |
||||||
675 | return $this->deprHandlers[$method]; |
||||||
676 | } else { |
||||||
677 | return null; |
||||||
678 | } |
||||||
679 | } |
||||||
680 | |||||||
681 | /** |
||||||
682 | * Provides a way to incrementally wean Elgg's notifications code from the |
||||||
683 | * global $NOTIFICATION_HANDLERS |
||||||
684 | * |
||||||
685 | * @return array |
||||||
686 | */ |
||||||
687 | 14 | public function getMethodsAsDeprecatedGlobal() { |
|||||
688 | 14 | $data = []; |
|||||
689 | 14 | foreach ($this->methods as $method) { |
|||||
690 | 14 | $data[$method] = 'empty'; |
|||||
691 | } |
||||||
692 | 14 | return $data; |
|||||
693 | } |
||||||
694 | |||||||
695 | /** |
||||||
696 | * Get the notification body using a pre-Elgg 1.9 plugin hook |
||||||
697 | * |
||||||
698 | * @param Notification $notification Notification |
||||||
699 | * @param NotificationEvent $event Event |
||||||
700 | * @param string $method Method |
||||||
701 | * @return Notification |
||||||
702 | */ |
||||||
703 | 20 | protected function getDeprecatedNotificationBody(Notification $notification, NotificationEvent $event, $method) { |
|||||
704 | 20 | $entity = $event->getObject(); |
|||||
705 | 20 | if (!$entity) { |
|||||
706 | 6 | return $notification; |
|||||
707 | } |
||||||
708 | $params = [ |
||||||
709 | 14 | 'entity' => $entity, |
|||||
710 | 14 | 'to_entity' => $notification->getRecipient(), |
|||||
711 | 14 | 'method' => $method, |
|||||
712 | ]; |
||||||
713 | 14 | $subject = $this->getDeprecatedNotificationSubject($entity->getType(), $entity->getSubtype()); |
|||||
714 | 14 | $string = $subject . ": " . $entity->getURL(); |
|||||
715 | 14 | $body = $this->hooks->trigger('notify:entity:message', $entity->getType(), $params, $string); |
|||||
716 | |||||||
717 | 14 | if ($subject) { |
|||||
718 | $notification->subject = $subject; |
||||||
719 | $notification->body = $body; |
||||||
720 | } |
||||||
721 | |||||||
722 | 14 | return $notification; |
|||||
723 | } |
||||||
724 | |||||||
725 | /** |
||||||
726 | * Set message subject for deprecated notification code |
||||||
727 | * |
||||||
728 | * @param string $type Entity type |
||||||
729 | * @param string $subtype Entity subtype |
||||||
730 | * @param string $subject Subject line |
||||||
731 | * @return void |
||||||
732 | */ |
||||||
733 | public function setDeprecatedNotificationSubject($type, $subtype, $subject) { |
||||||
734 | if ($type == '') { |
||||||
735 | $type = '__BLANK__'; |
||||||
736 | } |
||||||
737 | if ($subtype == '') { |
||||||
738 | $subtype = '__BLANK__'; |
||||||
739 | } |
||||||
740 | |||||||
741 | if (!isset($this->deprSubjects[$type])) { |
||||||
742 | $this->deprSubjects[$type] = []; |
||||||
743 | } |
||||||
744 | |||||||
745 | $this->deprSubjects[$type][$subtype] = $subject; |
||||||
746 | } |
||||||
747 | |||||||
748 | /** |
||||||
749 | * Get the deprecated subject |
||||||
750 | * |
||||||
751 | * @param string $type Entity type |
||||||
752 | * @param string $subtype Entity subtype |
||||||
753 | * @return string |
||||||
754 | */ |
||||||
755 | 14 | protected function getDeprecatedNotificationSubject($type, $subtype) { |
|||||
756 | 14 | if ($type == '') { |
|||||
757 | $type = '__BLANK__'; |
||||||
758 | } |
||||||
759 | 14 | if ($subtype == '') { |
|||||
760 | $subtype = '__BLANK__'; |
||||||
761 | } |
||||||
762 | |||||||
763 | 14 | if (!isset($this->deprSubjects[$type])) { |
|||||
764 | 14 | return ''; |
|||||
765 | } |
||||||
766 | |||||||
767 | if (!isset($this->deprSubjects[$type][$subtype])) { |
||||||
768 | return ''; |
||||||
769 | } |
||||||
770 | |||||||
771 | return $this->deprSubjects[$type][$subtype]; |
||||||
772 | } |
||||||
773 | } |
||||||
774 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.