Elgg  Version 6.2
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 
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 {
74  $subscriptions = $this->getSubscriptions();
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 
288  $notification = $this->prepareNotification($params);
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  $display_name = '';
414  $container_name = '';
415  if ($object instanceof \ElggEntity) {
416  $display_name = $object->getDisplayName();
417  $container_name = $object->getContainerEntity()?->getDisplayName();
418  }
419 
420  return _elgg_services()->translator->translate($body_key, [
421  $actor?->getDisplayName(),
423  $container_name,
425  $object?->getURL(),
426  ]);
427  }
428 
429  // Fall back to default body
430  return _elgg_services()->translator->translate('notification:body', [$object?->getURL()]);
431  }
432 
441  protected function getNotificationSummary(\ElggUser $recipient, string $method): string {
442  return '';
443  }
444 
453  protected function getNotificationURL(\ElggUser $recipient, string $method): string {
454  $object = $this->event->getObject() ?: null;
455 
456  return (string) $object?->getURL();
457  }
458 
465  protected function getEventActor(): ?\ElggUser {
466  $actor = $this->event->getActor();
467 
468  return $actor instanceof \ElggUser ? $actor : null;
469  }
470 
477  protected function getEventEntity(): ?\ElggEntity {
478  $object = $this->event->getObject();
479 
480  return $object instanceof \ElggEntity ? $object : null;
481  }
482 
488  public static function isConfigurableByUser(): bool {
489  return true;
490  }
491 
502  final public static function isConfigurableForEntity(\ElggEntity $entity): bool {
503  if ($entity instanceof \ElggUser) {
504  return static::isConfigurableForUser($entity);
505  } elseif ($entity instanceof \ElggGroup) {
506  return static::isConfigurableForGroup($entity);
507  }
508 
509  return true;
510  }
511 
520  protected static function isConfigurableForUser(\ElggUser $user): bool {
521  return true;
522  }
523 
534  protected static function isConfigurableForGroup(\ElggGroup $group): bool {
535  return true;
536  }
537 
543  protected function addMuteLink(): bool {
544  return true;
545  }
546 }
getLogger()
Returns logger.
Definition: Loggable.php:37
$site description
Definition: settings.php:14
$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
$body
Definition: useradd.php:55
$subject
Definition: useradd.php:54
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
$user
Definition: ban.php:7
Exception thrown if an error which can only be found on runtime occurs.
Notification Event Handler handles preparation of a notification.
deliverNotification(Notification $notification, string $method)
Deliver a notification.
sendNotification(int $guid, string $method, array $params=[])
Send a notification to a subscriber.
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.
__construct(NotificationEvent $event, NotificationsService $service, array $params=[])
Constructor.
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.
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...
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.
if($email instanceof \Elgg\Email) $object
Definition: body.php:24
$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:353
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:256
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:1401
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