Elgg  Version 6.3
NotificationEventHandler.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg\Notifications;
4 
6 use Elgg\Traits\Loggable;
7 use Psr\Log\LogLevel;
8 
15 
16  use Loggable;
17 
21  protected array $params = [];
22 
26  protected array $methods_override = [];
27 
35  public function __construct(
36  protected NotificationEvent $event,
37  protected NotificationsService $service,
38  array $params = []
39  ) {
40  $methods_override = elgg_extract('methods_override', $params);
41  unset($params['methods_override']);
42  if (!empty($methods_override) && is_array($methods_override)) {
43  $this->setMethodsOverride($methods_override);
44  }
45 
46  $this->params = $params;
47  }
48 
57  final protected function setMethodsOverride(array $methods): void {
58  $this->methods_override = $methods;
59  }
60 
67  final protected function getMethodsOverride(): array {
68  return $this->methods_override;
69  }
70 
77  protected function getNotificationMethods(): array {
78  return [];
79  }
80 
86  final public function send(): array {
87  $deliveries = [];
88 
90  $params['handler'] = $this;
91  $params['event'] = $this->event;
92  $params['subscriptions'] = $this->prepareSubscriptions();
93 
94  // return false to stop the default notification sender
95  if (_elgg_services()->events->triggerResults('send:before', 'notifications', $params, true)) {
96  $deliveries = $this->sendNotifications($params['subscriptions'], $params);
97  }
98 
99  $params['deliveries'] = $deliveries;
100  _elgg_services()->events->triggerResults('send:after', 'notifications', $params);
101 
102  return $deliveries;
103  }
104 
110  final protected function prepareSubscriptions(): array {
111  $subscriptions = $this->getSubscriptions();
112 
113  $params = [
114  'event' => $this->event,
115  'methods' => $this->getMethods(),
116  'methods_override' => $this->getMethodsOverride(),
117  ];
118  $subscriptions = _elgg_services()->events->triggerResults('get', 'subscriptions', $params, $subscriptions);
119 
120  return _elgg_services()->subscriptions->filterSubscriptions($subscriptions, $this->event, $this->filterMutedSubscriptions());
121  }
122 
129  protected function filterMutedSubscriptions(): bool {
130  return (bool) $this->getParam('apply_muting', true);
131  }
132 
138  public function getSubscriptions(): array {
139  return _elgg_services()->subscriptions->getNotificationEventSubscriptions($this->event, $this->getMethods(), $this->getNotificationSubsciptionExclusionGUIDs());
140  }
141 
147  final protected function getNotificationSubsciptionExclusionGUIDs(): array {
148  $object = $this->event->getObject();
149  if (!$object instanceof \ElggEntity) {
150  return [];
151  }
152 
153  $exclude = [];
154  if ($this->excludeOwnerSubscribers()) {
155  $exclude[] = $object->owner_guid;
156  }
157 
158  if ($this->excludeContainerSubscribers()) {
159  $exclude[] = $object->container_guid;
160  }
161 
162  if ($this->excludeEntitySubscribers()) {
163  $exclude[] = $object->guid;
164  }
165 
166  return $exclude;
167  }
168 
175  protected function excludeOwnerSubscribers(): bool {
176  return false;
177  }
178 
185  protected function excludeContainerSubscribers(): bool {
186  return false;
187  }
188 
195  protected function excludeEntitySubscribers(): bool {
196  return false;
197  }
198 
204  final public function getMethods(): array {
205  return $this->service->getMethods();
206  }
207 
227  final protected function sendNotifications(array $subscriptions, array $params = []): array {
228  if (empty($this->getMethods())) {
229  return [];
230  }
231 
232  $translator = _elgg_services()->translator;
233  $current_language = $translator->getCurrentLanguage();
234 
235  $result = [];
236  foreach ($subscriptions as $guid => $methods) {
237  $recipient = _elgg_services()->entityTable->get($guid);
238  if (!$recipient instanceof \ElggEntity) {
239  continue;
240  }
241 
242  if ($recipient instanceof \ElggUser) {
243  $translator->setCurrentLanguage($recipient->getLanguage());
244  }
245 
246  try {
247  foreach ($methods as $method) {
248  $result[$guid][$method] = false;
249 
250  if ($this->service->isRegisteredMethod($method)) {
251  $result[$guid][$method] = $this->sendNotification($recipient, $method, $params);
252  }
253  }
254  } catch (\Throwable $t) {
255  $translator->setCurrentLanguage($current_language);
256  throw $t;
257  }
258 
259  $translator->setCurrentLanguage($current_language);
260  }
261 
262  $this->getLogger()->info("Results for the notification event {$this->event->getDescription()}: " . print_r($result, true));
263  return $result;
264  }
265 
275  final protected function sendNotification(\ElggEntity $recipient, string $method, array $params = []): bool {
276  if (!_elgg_services()->events->hasHandler('send', "notification:{$method}")) {
277  // no way to deliver given the current method, so quitting early
278  return false;
279  }
280 
281  $actor = $this->event->getActor();
282  $object = $this->event->getObject();
283 
284  if (!$this->event instanceof InstantNotificationEvent) {
285  if (!$recipient instanceof \ElggUser || $recipient->isBanned()) {
286  return false;
287  }
288 
289  if (!$actor || !$object) {
290  return false;
291  }
292 
293  if ($object instanceof \ElggEntity && !$object->hasAccess($recipient->guid)) {
294  // Recipient does not have access to the notification object
295  // The access level may have changed since the event was enqueued
296  return false;
297  }
298  }
299 
300  $subject = ($recipient instanceof \ElggUser) ? $this->getNotificationSubject($recipient, $method) : '';
301  $body = ($recipient instanceof \ElggUser) ? $this->getNotificationBody($recipient, $method) : '';
302  $summary = ($recipient instanceof \ElggUser) ? $this->getNotificationSummary($recipient, $method) : '';
303  $url = ($recipient instanceof \ElggUser) ? $this->getNotificationURL($recipient, $method) : null;
304  $attachments = ($recipient instanceof \ElggUser) ? $this->getNotificationAttachments($recipient, $method) : null;
305 
306  $params['subject'] = $params['subject'] ?? $subject;
307  $params['body'] = $params['body'] ?? $body;
308  $params['summary'] = $params['summary'] ?? $summary;
309  $params['url'] = $params['url'] ?? $url;
310  $params['attachments'] = $params['attachments'] ?? $attachments;
311  $params['event'] = $this->event;
312  $params['method'] = $method;
313  $params['sender'] = $actor;
314  $params['recipient'] = $recipient;
315  $params['language'] = $recipient instanceof \ElggUser ? $recipient->getLanguage() : _elgg_services()->translator->getCurrentLanguage();
316  $params['object'] = $object;
317  $params['action'] = $this->event->getAction();
318  $params['add_salutation'] = elgg_extract('add_salutation', $params, true);
319  $params['add_mute_link'] = elgg_extract('add_mute_link', $params, $this->addMuteLink());
320 
321  $notification = $this->prepareNotification($params);
322  return $this->deliverNotification($notification, $method);
323  }
324 
333  final protected function deliverNotification(Notification $notification, string $method): bool {
334  // return true to indicate the notification has been sent
335  $params = [
336  'notification' => $notification,
337  'event' => $this->event,
338  ];
339 
340  $result = _elgg_services()->events->triggerResults('send', "notification:{$method}", $params, false);
341 
342  if ($this->getLogger()->isLoggable(LogLevel::INFO)) {
343  $logger_data = print_r((array) $notification->toObject(), true);
344  if ($result) {
345  $this->getLogger()->info('Notification sent: ' . $logger_data);
346  } else {
347  $this->getLogger()->info('Notification was not sent: ' . $logger_data);
348  }
349  }
350 
351  return $result;
352  }
353 
362  final protected function prepareNotification(array $params): Notification {
363  $notification = new Notification($params['sender'], $params['recipient'], $params['language'], $params['subject'], $params['body'], $params['summary'], $params);
364 
365  $notification = _elgg_services()->events->triggerResults('prepare', 'notification', $params, $notification);
366  if (!$notification instanceof Notification) {
367  throw new RuntimeException("'prepare','notification' event must return an instance of " . Notification::class);
368  }
369 
370  $type = 'notification:' . $this->event->getDescription();
371  $notification = _elgg_services()->events->triggerResults('prepare', $type, $params, $notification);
372  if (!$notification instanceof Notification) {
373  throw new RuntimeException("'prepare','{$type}' event must return an instance of " . Notification::class);
374  }
375 
376  if (elgg_extract('add_salutation', $notification->params) === true) {
377  $viewtype = elgg_view_exists('notifications/body') ? '' : 'default';
378  $notification->body = _elgg_view_under_viewtype('notifications/body', ['notification' => $notification], $viewtype);
379  }
380 
381  $notification = _elgg_services()->events->triggerResults('format', "notification:{$params['method']}", [], $notification);
382  if (!$notification instanceof Notification) {
383  throw new RuntimeException("'format','notification:{$params['method']}' event must return an instance of " . Notification::class);
384  }
385 
386  return $notification;
387  }
388 
400  protected function getNotificationSubject(\ElggUser $recipient, string $method): string {
401  $actor = $this->event->getActor() ?: null;
402  $object = $this->event->getObject();
403 
404  // Check custom notification subject for the action/type/subtype combination
405  $subject_key = "notification:{$this->event->getDescription()}:subject";
406  if (_elgg_services()->translator->languageKeyExists($subject_key)) {
407  return _elgg_services()->translator->translate($subject_key, [
408  $actor?->getDisplayName(),
409  $object instanceof \ElggEntity ? $object->getDisplayName() : '',
410  ]);
411  }
412 
413  // Fall back to default subject
414  return _elgg_services()->translator->translate('notification:subject', [$actor?->getDisplayName()]);
415  }
416 
439  protected function getNotificationBody(\ElggUser $recipient, string $method): string {
440  $actor = $this->event->getActor() ?: null;
441  $object = $this->event->getObject() ?: null;
442 
443  // Check custom notification body for the action/type/subtype combination
444  $body_key = "notification:{$this->event->getDescription()}:body";
445  if (_elgg_services()->translator->languageKeyExists($body_key)) {
446  $display_name = '';
447  $container_name = '';
448  if ($object instanceof \ElggEntity) {
449  $display_name = $object->getDisplayName();
450  $container_name = $object->getContainerEntity()?->getDisplayName();
451  }
452 
453  return _elgg_services()->translator->translate($body_key, [
454  $actor?->getDisplayName(),
456  $container_name,
458  $object?->getURL(),
459  ]);
460  }
461 
462  // Fall back to default body
463  return _elgg_services()->translator->translate('notification:body', [$object?->getURL()]);
464  }
465 
474  protected function getNotificationSummary(\ElggUser $recipient, string $method): string {
475  return '';
476  }
477 
486  protected function getNotificationURL(\ElggUser $recipient, string $method): string {
487  $object = $this->event->getObject() ?: null;
488 
489  return (string) $object?->getURL();
490  }
491 
500  protected function getNotificationAttachments(\ElggUser $recipient, string $method): array {
501  return [];
502  }
503 
510  protected function getEventActor(): ?\ElggUser {
511  $actor = $this->event->getActor();
512 
513  return $actor instanceof \ElggUser ? $actor : null;
514  }
515 
522  protected function getEventEntity(): ?\ElggEntity {
523  $object = $this->event->getObject();
524 
525  return $object instanceof \ElggEntity ? $object : null;
526  }
527 
533  public static function isConfigurableByUser(): bool {
534  return true;
535  }
536 
547  final public static function isConfigurableForEntity(\ElggEntity $entity): bool {
548  if ($entity instanceof \ElggUser) {
549  return static::isConfigurableForUser($entity);
550  } elseif ($entity instanceof \ElggGroup) {
551  return static::isConfigurableForGroup($entity);
552  }
553 
554  return false;
555  }
556 
565  protected static function isConfigurableForUser(\ElggUser $user): bool {
566  return true;
567  }
568 
579  protected static function isConfigurableForGroup(\ElggGroup $group): bool {
580  return true;
581  }
582 
588  protected function addMuteLink(): bool {
589  return true;
590  }
591 
600  final protected function getParam(string $param, mixed $default = null): mixed {
601  return elgg_extract($param, $this->params, $default);
602  }
603 }
getLogger()
Returns logger.
Definition: Loggable.php:37
$site description
Definition: settings.php:12
$entity
Definition: reset.php:8
$guid
Reset an ElggUpgrade.
Definition: reset.php:6
if(! $new_container instanceof \ElggEntity) if(! $new_container->canWriteToContainer(0, $entity->type, $entity->subtype)) $display_name
$type
Definition: delete.php:21
$recipient
Definition: mute.php:8
$params
Saves global plugin settings.
Definition: save.php:13
return[ 'admin/delete_admin_notices'=>['access'=> 'admin'], 'admin/menu/save'=>['access'=> 'admin'], 'admin/plugins/activate'=>['access'=> 'admin'], 'admin/plugins/activate_all'=>['access'=> 'admin'], 'admin/plugins/deactivate'=>['access'=> 'admin'], 'admin/plugins/deactivate_all'=>['access'=> 'admin'], 'admin/plugins/set_priority'=>['access'=> 'admin'], 'admin/security/security_txt'=>['access'=> 'admin'], 'admin/security/settings'=>['access'=> 'admin'], 'admin/security/regenerate_site_secret'=>['access'=> 'admin'], 'admin/site/cache/invalidate'=>['access'=> 'admin'], 'admin/site/flush_cache'=>['access'=> 'admin'], 'admin/site/icons'=>['access'=> 'admin'], 'admin/site/set_maintenance_mode'=>['access'=> 'admin'], 'admin/site/set_robots'=>['access'=> 'admin'], 'admin/site/theme'=>['access'=> 'admin'], 'admin/site/unlock_upgrade'=>['access'=> 'admin'], 'admin/site/settings'=>['access'=> 'admin'], 'admin/upgrade'=>['access'=> 'admin'], 'admin/upgrade/reset'=>['access'=> 'admin'], 'admin/user/ban'=>['access'=> 'admin'], 'admin/user/bulk/ban'=>['access'=> 'admin'], 'admin/user/bulk/delete'=>['access'=> 'admin'], 'admin/user/bulk/unban'=>['access'=> 'admin'], 'admin/user/bulk/validate'=>['access'=> 'admin'], 'admin/user/change_email'=>['access'=> 'admin'], 'admin/user/delete'=>['access'=> 'admin'], 'admin/user/login_as'=>['access'=> 'admin'], 'admin/user/logout_as'=>[], 'admin/user/makeadmin'=>['access'=> 'admin'], 'admin/user/resetpassword'=>['access'=> 'admin'], 'admin/user/removeadmin'=>['access'=> 'admin'], 'admin/user/unban'=>['access'=> 'admin'], 'admin/user/validate'=>['access'=> 'admin'], 'annotation/delete'=>[], 'avatar/upload'=>[], 'comment/save'=>[], 'diagnostics/download'=>['access'=> 'admin'], 'entity/chooserestoredestination'=>[], 'entity/delete'=>[], 'entity/mute'=>[], 'entity/restore'=>[], 'entity/subscribe'=>[], 'entity/trash'=>[], 'entity/unmute'=>[], 'entity/unsubscribe'=>[], 'login'=>['access'=> 'logged_out'], 'logout'=>[], 'notifications/mute'=>['access'=> 'public'], 'plugins/settings/remove'=>['access'=> 'admin'], 'plugins/settings/save'=>['access'=> 'admin'], 'plugins/usersettings/save'=>[], 'register'=>['access'=> 'logged_out', 'middleware'=>[\Elgg\Router\Middleware\RegistrationAllowedGatekeeper::class,],], 'river/delete'=>[], 'settings/notifications'=>[], 'settings/notifications/subscriptions'=>[], 'user/changepassword'=>['access'=> 'public'], 'user/requestnewpassword'=>['access'=> 'public'], 'useradd'=>['access'=> 'admin'], 'usersettings/save'=>[], 'widgets/add'=>[], 'widgets/delete'=>[], 'widgets/move'=>[], 'widgets/save'=>[],]
Definition: actions.php:73
if(! $owner instanceof ElggEntity) $summary
Definition: summary.php:33
$attachments
Outputs attachments.
Definition: attachments.php:9
$user
Definition: ban.php:7
foreach($categories as $key=> $category) $body
Definition: categories.php:35
Exception thrown if an error which can only be found on runtime occurs.
Notification Event Handler handles preparation of a notification.
__construct(protected NotificationEvent $event, protected NotificationsService $service, array $params=[])
Constructor.
sendNotification(\ElggEntity $recipient, string $method, array $params=[])
Send a notification to a subscriber.
deliverNotification(Notification $notification, string $method)
Deliver a notification.
addMuteLink()
Add a mute link in the email notification.
getMethods()
Returns methods to be used for this notification.
getNotificationSummary(\ElggUser $recipient, string $method)
Return the summary for a notification.
setMethodsOverride(array $methods)
Override the default user preferred delivery methods with the given methods.
filterMutedSubscriptions()
Should muted subscribers be filtered.
getEventActor()
Get the acting user from the notification event.
static isConfigurableByUser()
Is this event configurable by the user on the notification settings page.
excludeOwnerSubscribers()
Exclude the NotificationEvent object owner_guid when fetching the subscription records for this notif...
getEventEntity()
Get the entity from the notification event.
static isConfigurableForUser(\ElggUser $user)
Can this event be configured for a specific user.
static isConfigurableForGroup(\ElggGroup $group)
Can this event be configured for a specific group.
prepareNotification(array $params)
Prepares a notification for delivery.
getNotificationAttachments(\ElggUser $recipient, string $method)
Get the attachments for this notification.
getNotificationMethods()
Get the notification methods to use.
sendNotifications(array $subscriptions, array $params=[])
Sends the notifications based on subscriptions.
getNotificationSubject(\ElggUser $recipient, string $method)
Get subject for the notification.
getNotificationSubsciptionExclusionGUIDs()
Get an array of GUIDs to not get the subscription records for.
static isConfigurableForEntity(\ElggEntity $entity)
Can this event be configured for a specific entity.
getNotificationBody(\ElggUser $recipient, string $method)
Get body for the notification.
excludeEntitySubscribers()
Exclude the NotificationEvent object guid when fetching the subscription records for this notificatio...
getMethodsOverride()
Get the delivery methods override.
getParam(string $param, mixed $default=null)
Get a parameter from the notification parameters.
excludeContainerSubscribers()
Exclude the NotificationEvent object container_guid when fetching the subscription records for this n...
getSubscriptions()
Returns subscriptions for the event.
getNotificationURL(\ElggUser $recipient, string $method)
Returns the url related to this notification.
Notification container.
foreach($plugin_guids as $guid) if(empty($deactivated_plugins)) $url
Definition: deactivate.php:39
if($email instanceof \Elgg\Email) $object
Definition: body.php:24
$subject
HTML body of an email.
Definition: body.php:11
$notification
Definition: body.php:13
if($item instanceof \ElggEntity) elseif($item instanceof \ElggRiverItem) elseif($item instanceof \ElggRelationship) elseif(is_callable([ $item, 'getType']))
Definition: item.php:48
_elgg_services()
Get the global service provider.
Definition: elgglib.php:337
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:240
$default
Definition: checkbox.php:30
Notification event interface.
$viewtype
Definition: default.php:11
elgg_view_exists(string $view, string $viewtype='', bool $recurse=true)
Returns whether the specified view exists.
Definition: views.php:131
_elgg_view_under_viewtype(string $view, array $vars, string $viewtype)
Render a view while the global viewtype is temporarily changed.
Definition: views.php:1404
if(!elgg_get_config('trash_enabled')) $group
Definition: group.php:13
if(parse_url(elgg_get_site_url(), PHP_URL_PATH) !=='/') if(file_exists(elgg_get_root_path() . 'robots.txt'))
Set robots.txt.
Definition: robots.php:10
$methods
Definition: subscribe.php:8
if(empty($methods)) $subscriptions
if(($owner instanceof \ElggGroup|| $owner instanceof \ElggUser) &&!in_array($owner->guid, $mute_guids)) $actor
Definition: mute.php:78