Elgg  Version master
NotificationEventHandler.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg\Notifications;
4 
8 
15 
16  use Loggable;
17 
19  protected $event;
20 
22  protected $service;
23 
25  protected $params = [];
26 
34  public function __construct(
37  array $params = []
38  ) {
39  $this->event = $event;
40  $this->service = $service;
41  $this->params = $params;
42  }
43 
49  final public function send(): array {
50  $deliveries = [];
51 
53  $params['handler'] = $this;
54  $params['event'] = $this->event;
55  $params['subscriptions'] = $this->prepareSubscriptions();
56 
57  // return false to stop the default notification sender
58  if (_elgg_services()->events->triggerResults('send:before', 'notifications', $params, true)) {
59  $deliveries = $this->sendNotifications($params['subscriptions'], $params);
60  }
61 
62  $params['deliveries'] = $deliveries;
63  _elgg_services()->events->triggerResults('send:after', 'notifications', $params);
64 
65  return $deliveries;
66  }
67 
73  final protected function prepareSubscriptions(): array {
75 
76  $params = [
77  'event' => $this->event,
78  'methods' => $this->getMethods(),
79  'methods_override' => (array) elgg_extract('methods_override', $this->params, []),
80  ];
81  $subscriptions = _elgg_services()->events->triggerResults('get', 'subscriptions', $params, $subscriptions);
82 
83  return _elgg_services()->subscriptions->filterSubscriptions($subscriptions, $this->event, $this->filterMutedSubscriptions());
84  }
85 
92  protected function filterMutedSubscriptions(): bool {
93  return (bool) elgg_extract('apply_muting', $this->params, true);
94  }
95 
101  public function getSubscriptions(): array {
102  return _elgg_services()->subscriptions->getNotificationEventSubscriptions($this->event, $this->getMethods(), $this->getNotificationSubsciptionExclusionGUIDs());
103  }
104 
110  final protected function getNotificationSubsciptionExclusionGUIDs(): array {
111  $object = $this->event->getObject();
112  if (!$object instanceof \ElggEntity) {
113  return [];
114  }
115 
116  $exclude = [];
117  if ($this->excludeOwnerSubscribers()) {
118  $exclude[] = $object->owner_guid;
119  }
120 
121  if ($this->excludeContainerSubscribers()) {
122  $exclude[] = $object->container_guid;
123  }
124 
125  if ($this->excludeEntitySubscribers()) {
126  $exclude[] = $object->guid;
127  }
128 
129  return $exclude;
130  }
131 
138  protected function excludeOwnerSubscribers(): bool {
139  return false;
140  }
141 
148  protected function excludeContainerSubscribers(): bool {
149  return false;
150  }
151 
158  protected function excludeEntitySubscribers(): bool {
159  return false;
160  }
161 
167  final public function getMethods(): array {
168  return $this->service->getMethods();
169  }
170 
190  final protected function sendNotifications(array $subscriptions, array $params = []): array {
191  if (empty($this->getMethods())) {
192  return [];
193  }
194 
195  $translator = _elgg_services()->translator;
196  $current_language = $translator->getCurrentLanguage();
197 
198  $result = [];
199  foreach ($subscriptions as $guid => $methods) {
200  $recipient = _elgg_services()->entityTable->get($guid);
201  if ($recipient instanceof \ElggUser) {
202  $translator->setCurrentLanguage($recipient->getLanguage());
203  }
204 
205  try {
206  foreach ($methods as $method) {
207  $result[$guid][$method] = false;
208 
209  if ($this->service->isRegisteredMethod($method)) {
210  $result[$guid][$method] = $this->sendNotification($guid, $method, $params);
211  }
212  }
213  } catch (\Throwable $t) {
214  $translator->setCurrentLanguage($current_language);
215  throw $t;
216  }
217 
218  $translator->setCurrentLanguage($current_language);
219  }
220 
221  $this->getLogger()->info("Results for the notification event {$this->event->getDescription()}: " . print_r($result, true));
222  return $result;
223  }
224 
234  final protected function sendNotification(int $guid, string $method, array $params = []): bool {
235  if (!_elgg_services()->events->hasHandler('send', "notification:{$method}")) {
236  // no way to deliver given the current method, so quitting early
237  return false;
238  }
239 
240  $actor = $this->event->getActor();
241  $object = $this->event->getObject();
242 
243  if ($this->event instanceof InstantNotificationEvent) {
244  /* @var \ElggEntity $recipient */
245  $recipient = _elgg_services()->entityTable->get($guid);
246 
247  $subject = elgg_extract('subject', $params, '');
248  $body = elgg_extract('body', $params, '');
249  $summary = elgg_extract('summary', $params, '');
250  } else {
251  $recipient = _elgg_services()->entityTable->get($guid, 'user');
252  if (!$recipient instanceof \ElggUser || $recipient->isBanned()) {
253  return false;
254  }
255 
256  if (!$actor || !$object) {
257  return false;
258  }
259 
260  if ($object instanceof \ElggEntity && !$object->hasAccess($recipient->guid)) {
261  // Recipient does not have access to the notification object
262  // The access level may have changed since the event was enqueued
263  return false;
264  }
265 
266  $subject = $this->getNotificationSubject($recipient, $method);
267  $body = $this->getNotificationBody($recipient, $method);
268  $summary = $this->getNotificationSummary($recipient, $method);
269 
270  if (!isset($params['url'])) {
271  $params['url'] = $this->getNotificationURL($recipient, $method) ?: null;
272  }
273  }
274 
275  $params['subject'] = $subject;
276  $params['body'] = $body;
277  $params['summary'] = $summary;
278  $params['event'] = $this->event;
279  $params['method'] = $method;
280  $params['sender'] = $actor;
281  $params['recipient'] = $recipient;
282  $params['language'] = $recipient instanceof \ElggUser ? $recipient->getLanguage() : _elgg_services()->translator->getCurrentLanguage();
283  $params['object'] = $object;
284  $params['action'] = $this->event->getAction();
285  $params['add_salutation'] = elgg_extract('add_salutation', $params, true);
286  $params['add_mute_link'] = elgg_extract('add_mute_link', $params, $this->addMuteLink());
287 
289  return $this->deliverNotification($notification, $method);
290  }
291 
300  final protected function deliverNotification(Notification $notification, string $method): bool {
301  // return true to indicate the notification has been sent
302  $params = [
303  'notification' => $notification,
304  'event' => $this->event,
305  ];
306 
307  $result = _elgg_services()->events->triggerResults('send', "notification:{$method}", $params, false);
308 
309  if ($this->getLogger()->isLoggable(LogLevel::INFO)) {
310  $logger_data = print_r((array) $notification->toObject(), true);
311  if ($result) {
312  $this->getLogger()->info('Notification sent: ' . $logger_data);
313  } else {
314  $this->getLogger()->info('Notification was not sent: ' . $logger_data);
315  }
316  }
317 
318  return $result;
319  }
320 
329  final protected function prepareNotification(array $params): Notification {
330  $notification = new Notification($params['sender'], $params['recipient'], $params['language'], $params['subject'], $params['body'], $params['summary'], $params);
331 
332  $notification = _elgg_services()->events->triggerResults('prepare', 'notification', $params, $notification);
333  if (!$notification instanceof Notification) {
334  throw new RuntimeException("'prepare','notification' event must return an instance of " . Notification::class);
335  }
336 
337  $type = 'notification:' . $this->event->getDescription();
338  $notification = _elgg_services()->events->triggerResults('prepare', $type, $params, $notification);
339  if (!$notification instanceof Notification) {
340  throw new RuntimeException("'prepare','{$type}' event must return an instance of " . Notification::class);
341  }
342 
343  if (elgg_extract('add_salutation', $notification->params) === true) {
344  $viewtype = elgg_view_exists('notifications/body') ? '' : 'default';
345  $notification->body = _elgg_view_under_viewtype('notifications/body', ['notification' => $notification], $viewtype);
346  }
347 
348  $notification = _elgg_services()->events->triggerResults('format', "notification:{$params['method']}", [], $notification);
349  if (!$notification instanceof Notification) {
350  throw new RuntimeException("'format','notification:{$params['method']}' event must return an instance of " . Notification::class);
351  }
352 
353  return $notification;
354  }
355 
367  protected function getNotificationSubject(\ElggUser $recipient, string $method): string {
368  $actor = $this->event->getActor() ?: null;
369  $object = $this->event->getObject();
370 
371  // Check custom notification subject for the action/type/subtype combination
372  $subject_key = "notification:{$this->event->getDescription()}:subject";
373  if (_elgg_services()->translator->languageKeyExists($subject_key)) {
374  return _elgg_services()->translator->translate($subject_key, [
375  $actor?->getDisplayName(),
376  $object instanceof \ElggEntity ? $object->getDisplayName() : '',
377  ]);
378  }
379 
380  // Fall back to default subject
381  return _elgg_services()->translator->translate('notification:subject', [$actor?->getDisplayName()]);
382  }
383 
406  protected function getNotificationBody(\ElggUser $recipient, string $method): string {
407  $actor = $this->event->getActor() ?: null;
408  $object = $this->event->getObject() ?: null;
409 
410  // Check custom notification body for the action/type/subtype combination
411  $body_key = "notification:{$this->event->getDescription()}:body";
412  if (_elgg_services()->translator->languageKeyExists($body_key)) {
413  if ($object instanceof \ElggEntity) {
414  $display_name = $object->getDisplayName();
415  $container_name = '';
416  $container = $object->getContainerEntity();
417  if ($container instanceof \ElggEntity) {
418  $container_name = $container->getDisplayName();
419  }
420  } else {
421  $display_name = '';
422  $container_name = '';
423  }
424 
425  return _elgg_services()->translator->translate($body_key, [
426  $actor?->getDisplayName(),
428  $container_name,
430  $object?->getURL(),
431  ]);
432  }
433 
434  // Fall back to default body
435  return _elgg_services()->translator->translate('notification:body', [$object->getURL()]);
436  }
437 
446  protected function getNotificationSummary(\ElggUser $recipient, string $method): string {
447  return '';
448  }
449 
458  protected function getNotificationURL(\ElggUser $recipient, string $method): string {
459  $object = $this->event->getObject() ?: null;
460 
461  return (string) $object?->getURL();
462  }
463 
470  protected function getEventActor(): ?\ElggUser {
471  $actor = $this->event->getActor();
472 
473  return $actor instanceof \ElggUser ? $actor : null;
474  }
475 
482  protected function getEventEntity(): ?\ElggEntity {
483  $object = $this->event->getObject();
484 
485  return $object instanceof \ElggEntity ? $object : null;
486  }
487 
493  public static function isConfigurableByUser(): bool {
494  return true;
495  }
496 
507  final public static function isConfigurableForEntity(\ElggEntity $entity): bool {
508  if ($entity instanceof \ElggUser) {
509  return static::isConfigurableForUser($entity);
510  } elseif ($entity instanceof \ElggGroup) {
511  return static::isConfigurableForGroup($entity);
512  }
513 
514  return true;
515  }
516 
525  protected static function isConfigurableForUser(\ElggUser $user): bool {
526  return true;
527  }
528 
539  protected static function isConfigurableForGroup(\ElggGroup $group): bool {
540  return true;
541  }
542 
548  protected function addMuteLink(): bool {
549  return true;
550  }
551 }
static isConfigurableByUser()
Is this event configurable by the user on the notification settings page.
static isConfigurableForEntity(\ElggEntity $entity)
Can this event be configured for a specific entity.
if(!elgg_get_config('trash_enabled')) $group
Definition: group.php:13
toObject()
Export notification.
Exception thrown if an error which can only be found on runtime occurs.
static isConfigurableForUser(\ElggUser $user)
Can this event be configured for a specific user.
prepareNotification(array $params)
Prepares a notification for delivery.
if(($owner instanceof\ElggGroup||$owner instanceof\ElggUser)&&!in_array($owner->guid, $mute_guids)) $actor
Definition: mute.php:78
excludeOwnerSubscribers()
Exclude the NotificationEvent object owner_guid when fetching the subscription records for this notif...
getMethods()
Returns methods to be used for this notification.
getNotificationURL(\ElggUser $recipient, string $method)
Returns the url related to this notification.
$type
Definition: delete.php:21
getNotificationSubject(\ElggUser $recipient, string $method)
Get subject for the notification.
getEventEntity()
Get the entity from the notification event.
Notification container.
sendNotification(int $guid, string $method, array $params=[])
Send a notification to a subscriber.
getSubscriptions()
Returns subscriptions for the event.
if($item instanceof\ElggEntity) elseif($item instanceof\ElggRiverItem) elseif($item instanceof\ElggRelationship) elseif(is_callable([$item, 'getType']))
Definition: item.php:48
sendNotifications(array $subscriptions, array $params=[])
Sends the notifications based on subscriptions.
elgg_extract($key, $array, $default=null, bool $strict=true)
Checks for $array[$key] and returns its value if it exists, else returns $default.
Definition: elgglib.php:254
trait Loggable
Enables adding a logger.
Definition: Loggable.php:14
Notification Event Handler handles preparation of a notification.
deliverNotification(Notification $notification, string $method)
Deliver a notification.
filterMutedSubscriptions()
Should muted subscribers be filtered.
$entity
Definition: reset.php:8
excludeContainerSubscribers()
Exclude the NotificationEvent object container_guid when fetching the subscription records for this n...
$notification
Definition: body.php:13
$site description
Definition: settings.php:14
getEventActor()
Get the acting user from the notification event.
$user
Definition: ban.php:7
$container
Definition: delete.php:23
addMuteLink()
Add a mute link in the email notification.
$viewtype
Definition: default.php:11
getNotificationBody(\ElggUser $recipient, string $method)
Get body for the notification.
$body
Definition: useradd.php:55
getNotificationSubsciptionExclusionGUIDs()
Get an array of GUIDs to not get the subscription records for.
getLogger()
Returns logger.
Definition: Loggable.php:37
if($email instanceof\Elgg\Email) $object
Definition: body.php:24
getNotificationSummary(\ElggUser $recipient, string $method)
Return the summary for a notification.
Notification event interface.
excludeEntitySubscribers()
Exclude the NotificationEvent object guid when fetching the subscription records for this notificatio...
if(!$owner instanceof ElggEntity) $summary
Definition: summary.php:33
_elgg_services()
Get the global service provider.
Definition: elgglib.php:351
static isConfigurableForGroup(\ElggGroup $group)
Can this event be configured for a specific group.
if(!$new_container instanceof\ElggEntity) if(!$new_container->canWriteToContainer(0, $entity->type, $entity->subtype)) $display_name
if(empty($methods)) $subscriptions
_elgg_view_under_viewtype(string $view, array $vars, string $viewtype)
Render a view while the global viewtype is temporarily changed.
Definition: views.php:1401
elgg_view_exists(string $view, string $viewtype= '', bool $recurse=true)
Returns whether the specified view exists.
Definition: views.php:131
$methods
Definition: subscribe.php:8
$recipient
Definition: mute.php:8
$subject
Definition: useradd.php:54
__construct(NotificationEvent $event, NotificationsService $service, array $params=[])
Constructor.
$guid
Reset an ElggUpgrade.
Definition: reset.php:6