Passed
Push — solr-auth-refactor ( 3bee90 )
by Ilia
15:04
created

NotificationsService::getMethods()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
namespace Elgg\Notifications;
3
4
use ElggEntity;
5
6
/**
7
 * WARNING: API IN FLUX. DO NOT USE DIRECTLY.
8
 *
9
 * @access private
10
 * 
11
 * @package    Elgg.Core
12
 * @subpackage Notifications
13
 * @since      1.9.0
14
 */
15
class NotificationsService {
16
17
	const QUEUE_NAME = 'notifications';
18
19
	/** @var \Elgg\Notifications\SubscriptionsService */
20
	protected $subscriptions;
21
22
	/** @var \Elgg\Queue\Queue */
23
	protected $queue;
24
25
	/** @var \Elgg\PluginHooksService */
26
	protected $hooks;
27
28
	/** @var \ElggSession */
29
	protected $session;
30
31
	/** @var array Registered notification events */
32
	protected $events = array();
33
34
	/** @var array Registered notification methods */
35
	protected $methods = array();
36
37
	/** @var array Deprecated notification handlers */
38
	protected $deprHandlers = array();
39
40
	/** @var array Deprecated message subjects */
41
	protected $deprSubjects = array();
42
43
	/**
44
	 * Constructor
45
	 *
46
	 * @param \Elgg\Notifications\SubscriptionsService $subscriptions Subscription service
47
	 * @param \Elgg\Queue\Queue                        $queue         Queue
48
	 * @param \Elgg\PluginHooksService                 $hooks         Plugin hook service
49
	 * @param \ElggSession                             $session       Session service
50
	 */
51 10
	public function __construct(\Elgg\Notifications\SubscriptionsService $subscriptions,
52
			\Elgg\Queue\Queue $queue, \Elgg\PluginHooksService $hooks, \ElggSession $session) {
53 10
		$this->subscriptions = $subscriptions;
54 10
		$this->queue = $queue;
55 10
		$this->hooks = $hooks;
56 10
		$this->session = $session;
57 10
	}
58
59
	/**
60
	 * @see elgg_register_notification_event()
61
	 * @access private
62
	 */
63 7
	public function registerEvent($type, $subtype, array $actions = array()) {
64
65 7
		if (!isset($this->events[$type])) {
66 7
			$this->events[$type] = array();
67 7
		}
68 7
		if (!isset($this->events[$type][$subtype])) {
69 7
			$this->events[$type][$subtype] = array();
70 7
		}
71
72 7
		$action_list =& $this->events[$type][$subtype];
73 7
		if ($actions) {
0 ignored issues
show
Bug Best Practice introduced by
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...
74 1
			$action_list = array_unique(array_merge($action_list, $actions));
75 7
		} elseif (!in_array('create', $action_list)) {
76 7
			$action_list[] = 'create';
77 7
		}
78 7
	}
79
80
	/**
81
	 * @see elgg_unregister_notification_event()
82
	 * @access private
83
	 */
84 1
	public function unregisterEvent($type, $subtype) {
85
86 1
		if (!isset($this->events[$type]) || !isset($this->events[$type][$subtype])) {
87 1
			return false;
88
		}
89
90 1
		unset($this->events[$type][$subtype]);
91
92 1
		return true;
93
	}
94
95
	/**
96
	 * @access private
97
	 */
98 2
	public function getEvents() {
99 2
		return $this->events;
100
	}
101
102
	/**
103
	 * @see elgg_register_notification_method()
104
	 * @access private
105
	 */
106 2
	public function registerMethod($name) {
107 2
		$this->methods[$name] = $name;
108 2
	}
109
110
	/**
111
	 * @see elgg_unregister_notification_method()
112
	 * @access private
113
	 */
114 1
	public function unregisterMethod($name) {
115 1
		if (isset($this->methods[$name])) {
116 1
			unset($this->methods[$name]);
117 1
			return true;
118
		}
119 1
		return false;
120
	}
121
122
	/**
123
	 * @access private
124
	 */
125 2
	public function getMethods() {
126 2
		return $this->methods;
127
	}
128
129
	/**
130
	 * Add a notification event to the queue
131
	 * 
132
	 * @param string   $action Action name
133
	 * @param string   $type   Type of the object of the action
134
	 * @param \ElggData $object The object of the action 
135
	 * @return void
136
	 * @access private
137
	 */
138 5
	public function enqueueEvent($action, $type, $object) {
139 5
		if ($object instanceof \ElggData) {
140 5
			$object_type = $object->getType();
141 5
			$object_subtype = $object->getSubtype();
142
143 5
			$registered = false;
144 5
			if (isset($this->events[$object_type])
145 5
				&& isset($this->events[$object_type][$object_subtype])
146 5
				&& in_array($action, $this->events[$object_type][$object_subtype])) {
147 5
				$registered = true;
148 5
			}
149
150 5
			if ($registered) {
151
				$params = array(
152 5
					'action' => $action,
153 5
					'object' => $object,
154 5
				);
155 5
				$registered = $this->hooks->trigger('enqueue', 'notification', $params, $registered);
156 5
			}
157
158 5
			if ($registered) {
159 3
				$this->queue->enqueue(new \Elgg\Notifications\Event($object, $action));
160 3
			}
161 5
		}
162 5
	}
163
164
	/**
165
	 * Pull notification events from queue until stop time is reached
166
	 *
167
	 * @param int $stopTime The Unix time to stop sending notifications
168
	 * @return int The number of notification events handled
169
	 * @access private
170
	 */
171 3
	public function processQueue($stopTime) {
172
173 3
		$this->subscriptions->methods = $this->methods;
174
175 3
		$count = 0;
176
177
		// @todo grab mutex
178
		
179 3
		$ia = $this->session->setIgnoreAccess(true);
180
181 3
		while (time() < $stopTime) {
182
			// dequeue notification event
183 2
			$event = $this->queue->dequeue();
184 2
			if (!$event) {
185 2
				break;
186
			}
187
188
			// test for usage of the deprecated override hook
189 1
			if ($this->existsDeprecatedNotificationOverride($event)) {
190
				continue;
191
			}
192
193 1
			$subscriptions = $this->subscriptions->getSubscriptions($event);
194
195
			// return false to stop the default notification sender
196 1
			$params = array('event' => $event, 'subscriptions' => $subscriptions);
197 1
			if ($this->hooks->trigger('send:before', 'notifications', $params, true)) {
198 1
				$this->sendNotifications($event, $subscriptions);
199 1
			}
200 1
			$this->hooks->trigger('send:after', 'notifications', $params);
201 1
			$count++;
202 1
		}
203
204
		// release mutex
205
206 3
		$this->session->setIgnoreAccess($ia);
207
208 3
		return $count;
209
	}
210
211
	/**
212
	 * Sends the notifications based on subscriptions
213
	 *
214
	 * @param \Elgg\Notifications\Event $event         Notification event
215
	 * @param array                    $subscriptions Subscriptions for this event
216
	 * @return int The number of notifications handled
217
	 * @access private
218
	 */
219 1
	protected function sendNotifications($event, $subscriptions) {
220
221 1
		if (!$this->methods) {
0 ignored issues
show
Bug Best Practice introduced by
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...
222 1
			return 0;
223
		}
224
225
		$count = 0;
226
		foreach ($subscriptions as $guid => $methods) {
227
			foreach ($methods as $method) {
228
				if (in_array($method, $this->methods)) {
229
					if ($this->sendNotification($event, $guid, $method)) {
230
						$count++;
231
					}
232
				}
233
			}
234
		}
235
		return $count;
236
	}
237
238
	/**
239
	 * Send a notification to a subscriber
240
	 *
241
	 * @param \Elgg\Notifications\Event $event  The notification event
242
	 * @param int                      $guid   The guid of the subscriber
243
	 * @param string                   $method The notification method
244
	 * @return bool
245
	 * @access private
246
	 */
247 3
	protected function sendNotification(\Elgg\Notifications\Event $event, $guid, $method) {
248
249
		$recipient = get_user($guid);
250
		if (!$recipient || $recipient->isBanned()) {
251
			return false;
252
		}
253
254
		// don't notify the creator of the content
255
		if ($recipient->getGUID() == $event->getActorGUID()) {
256
			return false;
257
		}
258
259
		$actor = $event->getActor();
260
		$object = $event->getObject();
261
		if (!$actor || !$object) {
262
			return false;
263
		}
264
265
		if (($object instanceof ElggEntity) && !has_access_to_entity($object, $recipient)) {
0 ignored issues
show
Documentation introduced by
$recipient is of type object<ElggEntity>, but the function expects a object<ElggUser>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
266
			return false;
267
		}
268
269
		$language = $recipient->language;
270
		$params = array(
271
			'event' => $event,
272
			'method' => $method,
273
			'recipient' => $recipient,
274
			'language' => $language,
275
			'object' => $object,
276
		);
277
278
		$subject = _elgg_services()->translator->translate('notification:subject', array($actor->name), $language);
279 3
		$body = _elgg_services()->translator->translate('notification:body', array($object->getURL()), $language);
280
		$notification = new \Elgg\Notifications\Notification($event->getActor(), $recipient, $language, $subject, $body, '', $params);
281
282
		$type = 'notification:' . $event->getDescription();
283
		if ($this->hooks->hasHandler('prepare', $type)) {
284
			$notification = $this->hooks->trigger('prepare', $type, $params, $notification);
285
		} else {
286
			// pre Elgg 1.9 notification message generation
287
			$notification = $this->getDeprecatedNotificationBody($notification, $event, $method);
288
		}
289
290
		if ($this->hooks->hasHandler('send', "notification:$method")) {
291
			// return true to indicate the notification has been sent
292
			$params = array(
293
				'notification' => $notification,
294
				'event' => $event,
295
			);
296
			return $this->hooks->trigger('send', "notification:$method", $params, false);
297
		} else {
298
			// pre Elgg 1.9 notification handler
299
			$userGuid = $notification->getRecipientGUID();
300
			$senderGuid = $notification->getSenderGUID();
301
			$subject = $notification->subject;
302
			$body = $notification->body;
303
			$params = $notification->params;
304
			return (bool)_elgg_notify_user($userGuid, $senderGuid, $subject, $body, $params, array($method));
305
		}
306
	}
307
308
	/**
309
	 * Register a deprecated notification handler
310
	 * 
311
	 * @param string $method  Method name
312
	 * @param string $handler Handler callback
313
	 * @return void
314
	 */
315
	public function registerDeprecatedHandler($method, $handler) {
316
		$this->deprHandlers[$method] = $handler;
317
	}
318
319
	/**
320
	 * Get a deprecated notification handler callback
321
	 * 
322
	 * @param string $method Method name
323
	 * @return callback|null
324
	 */
325
	public function getDeprecatedHandler($method) {
326
		if (isset($this->deprHandlers[$method])) {
327
			return $this->deprHandlers[$method];
328
		} else {
329
			return null;
330
		}
331
	}
332
333
	/**
334
	 * Provides a way to incrementally wean Elgg's notifications code from the
335
	 * global $NOTIFICATION_HANDLERS
336
	 * 
337
	 * @return array
338
	 */
339
	public function getMethodsAsDeprecatedGlobal() {
340
		$data = array();
341
		foreach ($this->methods as $method) {
342
			$data[$method] = 'empty';
343
		}
344
		return $data;
345
	}
346
347
	/**
348
	 * Get the notification body using a pre-Elgg 1.9 plugin hook
349
	 * 
350
	 * @param \Elgg\Notifications\Notification $notification Notification
351
	 * @param \Elgg\Notifications\Event        $event        Event
352
	 * @param string                           $method       Method
353
	 * @return \Elgg\Notifications\Notification
354
	 */
355
	protected function getDeprecatedNotificationBody(\Elgg\Notifications\Notification $notification, \Elgg\Notifications\Event $event, $method) {
356
		$entity = $event->getObject();
357
		$params = array(
358
			'entity' => $entity,
359
			'to_entity' => $notification->getRecipient(),
360
			'method' => $method,
361
		);
362
		$subject = $this->getDeprecatedNotificationSubject($entity->getType(), $entity->getSubtype());
363
		$string = $subject . ": " . $entity->getURL();
364
		$body = $this->hooks->trigger('notify:entity:message', $entity->getType(), $params, $string);
365
366
		if ($subject) {
367
			$notification->subject = $subject;
368
			$notification->body = $body;
369
		}
370
371
		return $notification;
372
	}
373
374
	/**
375
	 * Set message subject for deprecated notification code
376
	 * 
377
	 * @param string $type    Entity type
378
	 * @param string $subtype Entity subtype
379
	 * @param string $subject Subject line
380
	 * @return void
381
	 */
382
	public function setDeprecatedNotificationSubject($type, $subtype, $subject) {
383
		if ($type == '') {
384
			$type = '__BLANK__';
385
		}
386
		if ($subtype == '') {
387
			$subtype = '__BLANK__';
388
		}
389
390
		if (!isset($this->deprSubjects[$type])) {
391
			$this->deprSubjects[$type] = array();
392
		}
393
394
		$this->deprSubjects[$type][$subtype] = $subject;
395
	}
396
397
	/**
398
	 * Get the deprecated subject
399
	 * 
400
	 * @param string $type    Entity type
401
	 * @param string $subtype Entity subtype
402
	 * @return string
403
	 */
404
	protected function getDeprecatedNotificationSubject($type, $subtype) {
405
		if ($type == '') {
406
			$type = '__BLANK__';
407
		}
408
		if ($subtype == '') {
409
			$subtype = '__BLANK__';
410
		}
411
412
		if (!isset($this->deprSubjects[$type])) {
413
			return '';
414
		}
415
416
		if (!isset($this->deprSubjects[$type][$subtype])) {
417
			return '';
418
		}
419
420
		return $this->deprSubjects[$type][$subtype];
421
	}
422
423
	/**
424
	 * Is someone using the deprecated override
425
	 * 
426
	 * @param \Elgg\Notifications\Event $event Event
427
	 * @return boolean
428
	 */
429 1
	protected function existsDeprecatedNotificationOverride(\Elgg\Notifications\Event $event) {
430 1
		$entity = $event->getObject();
431 1
		if (!elgg_instanceof($entity)) {
432 1
			return false;
433
		}
434
		$params = array(
435
			'event' => $event->getAction(),
436
			'object_type' => $entity->getType(),
437
			'object' => $entity,
438
		);
439
		$hookresult = $this->hooks->trigger('object:notifications', $entity->getType(), $params, false);
440
		if ($hookresult === true) {
441
			elgg_deprecated_notice("Using the plugin hook 'object:notifications' has been deprecated by the hook 'send:before', 'notifications'", 1.9);
442
			return true;
443
		} else {
444
			return false;
445
		}
446
	}
447
}
448