Passed
Push — master ( c0a3a7...3b84a4 )
by Jeroen
58:51
created

NotificationsService::enqueueEvent()   B

Complexity

Conditions 6
Paths 9

Size

Total Lines 21
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 6

Importance

Changes 0
Metric Value
cc 6
eloc 13
nc 9
nop 3
dl 0
loc 21
rs 8.7624
c 0
b 0
f 0
ccs 13
cts 13
cp 1
crap 6
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
Bug Best Practice introduced by Cash Costello
The expression $actions 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 empty(..) or ! empty(...) instead.

Loading history...
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) {
1 ignored issue
show
Unused Code introduced by Cash Costello
The parameter $type is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

206
	public function enqueueEvent($action, /** @scrutinizer ignore-unused */ $type, $object) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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
Bug Best Practice introduced by Cash Costello
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 empty(..) or ! empty(...) instead.

Loading history...
315 6
			return 0;
0 ignored issues
show
Bug Best Practice introduced by Cash Costello
The expression return 0 returns the type integer which is incompatible with the documented return type array.
Loading history...
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
Bug Best Practice introduced by Ismayil Khayredinov
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 empty(..) or ! empty(...) instead.

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
Bug introduced by Ismayil Khayredinov
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 ignore-type  annotation

496
		$notification = new Notification($actor, $recipient, /** @scrutinizer ignore-type */ $language, $subject, $body, $summary, $params);
Loading history...
Bug introduced by Ismayil Khayredinov
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 ignore-type  annotation

496
		$notification = new Notification(/** @scrutinizer ignore-type */ $actor, $recipient, $language, $subject, $body, $summary, $params);
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