Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
57.45% |
81 / 141 |
|
50.00% |
15 / 30 |
CRAP | |
0.00% |
0 / 1 |
base | |
57.45% |
81 / 141 |
|
50.00% |
15 / 30 |
358.20 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
1 | |||
set_notification_manager | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
set_initial_data | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
__get | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
__set | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
__isset | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
__toString | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
6 | |||
get_data | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
set_data | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
create_insert_array | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
1 | |||
get_insert_array | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
create_update_array | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
1 | |||
mark_read | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
mark_unread | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
get_redirect_url | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
prepare_for_display | |
0.00% |
0 / 25 |
|
0.00% |
0 / 1 |
156 | |||
get_unsubscribe_url | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
get_style_class | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
get_avatar | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
get_reference | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
get_forum | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
get_reason | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
get_load_special | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
load_special | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
is_available | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
get_email_template | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
pre_create_insert_array | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
check_user_notification_options | |
73.33% |
33 / 45 |
|
0.00% |
0 / 1 |
19.27 | |||
mark | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
6 | |||
get_authorised_recipients | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
4 |
1 | <?php |
2 | /** |
3 | * |
4 | * This file is part of the phpBB Forum Software package. |
5 | * |
6 | * @copyright (c) phpBB Limited <https://www.phpbb.com> |
7 | * @license GNU General Public License, version 2 (GPL-2.0) |
8 | * |
9 | * For full copyright and license information, please see |
10 | * the docs/CREDITS.txt file. |
11 | * |
12 | */ |
13 | |
14 | namespace phpbb\notification\type; |
15 | |
16 | /** |
17 | * Base notifications class |
18 | */ |
19 | abstract class base implements \phpbb\notification\type\type_interface |
20 | { |
21 | /** @var \phpbb\notification\manager */ |
22 | protected $notification_manager; |
23 | |
24 | /** @var \phpbb\db\driver\driver_interface */ |
25 | protected $db; |
26 | |
27 | /** @var \phpbb\language\language */ |
28 | protected $language; |
29 | |
30 | /** @var \phpbb\user */ |
31 | protected $user; |
32 | |
33 | /** @var \phpbb\auth\auth */ |
34 | protected $auth; |
35 | |
36 | /** @var string */ |
37 | protected $phpbb_root_path; |
38 | |
39 | /** @var string */ |
40 | protected $php_ext; |
41 | |
42 | /** @var string */ |
43 | protected $user_notifications_table; |
44 | |
45 | /** |
46 | * Notification option data (for outputting to the user) |
47 | * |
48 | * @var bool|array False if the service should use its default data |
49 | * Array of data (including keys 'id', 'lang', and 'group') |
50 | */ |
51 | public static $notification_option = false; |
52 | |
53 | /** |
54 | * The notification_type_id, set upon creation of the class |
55 | * This is the notification_type_id from the notification_types table |
56 | * |
57 | * @var int |
58 | */ |
59 | protected $notification_type_id; |
60 | |
61 | /** |
62 | * Identification data |
63 | * notification_type_id - ID of the item type (auto generated, from notification types table) |
64 | * item_id - ID of the item (e.g. post_id, msg_id) |
65 | * item_parent_id - Parent item id (ex: for topic => forum_id, for post => topic_id, etc) |
66 | * user_id |
67 | * notification_read |
68 | * notification_time |
69 | * notification_data (special serialized field that each notification type can use to store stuff) |
70 | * |
71 | * @var array $data Notification row from the database |
72 | * This must be private, all interaction should use __get(), __set(), get_data(), set_data() |
73 | */ |
74 | private $data = array(); |
75 | |
76 | /** |
77 | * Notification Type Base Constructor |
78 | * |
79 | * @param \phpbb\db\driver\driver_interface $db |
80 | * @param \phpbb\language\language $language |
81 | * @param \phpbb\user $user |
82 | * @param \phpbb\auth\auth $auth |
83 | * @param string $phpbb_root_path |
84 | * @param string $php_ext |
85 | * @param string $user_notifications_table |
86 | */ |
87 | public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\language\language $language, \phpbb\user $user, \phpbb\auth\auth $auth, $phpbb_root_path, $php_ext, $user_notifications_table) |
88 | { |
89 | $this->db = $db; |
90 | $this->language = $language; |
91 | $this->user = $user; |
92 | $this->auth = $auth; |
93 | |
94 | $this->phpbb_root_path = $phpbb_root_path; |
95 | $this->php_ext = $php_ext; |
96 | |
97 | $this->user_notifications_table = $user_notifications_table; |
98 | } |
99 | |
100 | /** |
101 | * Set notification manager (required) |
102 | * |
103 | * @param \phpbb\notification\manager $notification_manager |
104 | */ |
105 | public function set_notification_manager(\phpbb\notification\manager $notification_manager) |
106 | { |
107 | $this->notification_manager = $notification_manager; |
108 | |
109 | $this->notification_type_id = $this->notification_manager->get_notification_type_id($this->get_type()); |
110 | } |
111 | |
112 | /** |
113 | * Set initial data from the database |
114 | * |
115 | * @param array $data Row directly from the database |
116 | */ |
117 | public function set_initial_data($data = array()) |
118 | { |
119 | // The row from the database (unless this is a new notification we're going to add) |
120 | $this->data = $data; |
121 | $this->data['notification_data'] = !empty($this->data['notification_data']) ? unserialize($this->data['notification_data']) : []; |
122 | } |
123 | |
124 | /** |
125 | * Magic method to get data from this notification |
126 | * |
127 | * @param mixed $name |
128 | * @return mixed |
129 | */ |
130 | public function __get($name) |
131 | { |
132 | return $this->data[$name] ?? null; |
133 | } |
134 | |
135 | |
136 | /** |
137 | * Magic method to set data on this notification |
138 | * |
139 | * @param mixed $name |
140 | * @param mixed $value |
141 | * |
142 | * @return void |
143 | */ |
144 | public function __set($name, $value) |
145 | { |
146 | $this->data[$name] = $value; |
147 | } |
148 | |
149 | /** |
150 | * Magic method check if a variable is defined and is not null |
151 | * |
152 | * @param mixed $name |
153 | * |
154 | * @return bool |
155 | */ |
156 | public function __isset($name) |
157 | { |
158 | return isset($this->data[$name]); |
159 | } |
160 | |
161 | /** |
162 | * Magic method to get a string of this notification |
163 | * |
164 | * Primarily for testing |
165 | * |
166 | * @return mixed |
167 | */ |
168 | public function __toString() |
169 | { |
170 | return (!empty($this->data)) ? var_export($this->data, true) : $this->get_type(); |
171 | } |
172 | |
173 | /** |
174 | * Get special data (only important for the classes that extend this) |
175 | * |
176 | * @param string|false $name Name of the variable to get, false if all data should be returned |
177 | * @return mixed |
178 | */ |
179 | protected function get_data($name) |
180 | { |
181 | return ($name === false) ? $this->data['notification_data'] : ($this->data['notification_data'][$name] ?? null); |
182 | } |
183 | |
184 | /** |
185 | * Set special data (only important for the classes that extend this) |
186 | * |
187 | * @param string $name Name of the variable to set |
188 | * @param mixed $value Value to set to the variable |
189 | */ |
190 | protected function set_data($name, $value) |
191 | { |
192 | $this->data['notification_data'][$name] = $value; |
193 | } |
194 | |
195 | /** |
196 | * {@inheritdoc} |
197 | */ |
198 | public function create_insert_array($type_data, $pre_create_data = array()) |
199 | { |
200 | // Defaults |
201 | $this->data = array_merge(array( |
202 | 'item_id' => static::get_item_id($type_data), |
203 | 'notification_type_id' => $this->notification_type_id, |
204 | 'item_parent_id' => static::get_item_parent_id($type_data), |
205 | |
206 | 'notification_time' => time(), |
207 | 'notification_read' => false, |
208 | |
209 | 'notification_data' => array(), |
210 | ), $this->data); |
211 | } |
212 | |
213 | /** |
214 | * {@inheritdoc} |
215 | */ |
216 | public function get_insert_array() |
217 | { |
218 | $data = $this->data; |
219 | |
220 | $data['notification_data'] = serialize($data['notification_data']); |
221 | |
222 | return $data; |
223 | } |
224 | |
225 | /** |
226 | * Function for preparing the data for update in an SQL query |
227 | * (The service handles insertion) |
228 | * |
229 | * @param array $type_data Data unique to this notification type |
230 | * @return array Array of data ready to be updated in the database |
231 | */ |
232 | public function create_update_array($type_data) |
233 | { |
234 | $this->create_insert_array($type_data); |
235 | $data = $this->get_insert_array(); |
236 | |
237 | // Unset data unique to each row |
238 | unset( |
239 | $data['notification_time'], // Also unsetting time, since it always tries to change the time to current (if you actually need to change the time, over-ride this function) |
240 | $data['notification_id'], |
241 | $data['notification_read'], |
242 | $data['user_id'] |
243 | ); |
244 | |
245 | return $data; |
246 | } |
247 | |
248 | /** |
249 | * Mark this item read |
250 | * |
251 | * @param bool $return True to return a string containing the SQL code to update this item, False to execute it (Default: False) |
252 | * @return string|null If $return is False, nothing will be returned, else the sql code to update this item |
253 | */ |
254 | public function mark_read($return = false) |
255 | { |
256 | return $this->mark(false, $return); |
257 | } |
258 | |
259 | /** |
260 | * Mark this item unread |
261 | * |
262 | * @param bool $return True to return a string containing the SQL code to update this item, False to execute it (Default: False) |
263 | * @return string|null If $return is False, nothing will be returned, else the sql code to update this item |
264 | */ |
265 | public function mark_unread($return = false) |
266 | { |
267 | return $this->mark(true, $return); |
268 | } |
269 | |
270 | /** |
271 | * {inheritDoc} |
272 | */ |
273 | public function get_redirect_url() |
274 | { |
275 | return $this->get_url(); |
276 | } |
277 | |
278 | /** |
279 | * Prepare to output the notification to the template |
280 | * |
281 | * @return array Template variables |
282 | */ |
283 | public function prepare_for_display() |
284 | { |
285 | $mark_hash = generate_link_hash('mark_notification_read'); |
286 | |
287 | if ($this->get_url()) |
288 | { |
289 | $u_mark_read = append_sid($this->phpbb_root_path . 'index.' . $this->php_ext, 'mark_notification=' . $this->notification_id . '&hash=' . $mark_hash); |
290 | } |
291 | else |
292 | { |
293 | $redirect = (($this->user->page['page_dir']) ? $this->user->page['page_dir'] . '/' : '') . $this->user->page['page_name'] . (($this->user->page['query_string']) ? '?' . $this->user->page['query_string'] : ''); |
294 | |
295 | $u_mark_read = append_sid($this->phpbb_root_path . 'index.' . $this->php_ext, 'mark_notification=' . $this->notification_id . '&hash=' . $mark_hash . '&redirect=' . urlencode($redirect)); |
296 | } |
297 | |
298 | $avatar = $this->get_avatar(); |
299 | |
300 | return [ |
301 | 'NOTIFICATION_ID' => $this->notification_id, |
302 | 'STYLING' => $this->get_style_class(), |
303 | 'FORMATTED_TITLE' => $this->get_title(), |
304 | 'REFERENCE' => $this->get_reference(), |
305 | 'FORUM' => $this->get_forum(), |
306 | 'REASON' => $this->get_reason(), |
307 | 'URL' => $this->get_url(), |
308 | 'TIME' => $this->user->format_date($this->notification_time), |
309 | 'UNREAD' => !$this->notification_read, |
310 | |
311 | 'AVATAR_SOURCE' => $avatar ? $avatar['src'] : '', |
312 | 'AVATAR_TITLE' => $avatar ? $avatar['title'] : '', |
313 | 'AVATAR_TYPE' => $avatar ? $avatar['type'] : '', |
314 | |
315 | 'AVATAR_WIDTH' => $avatar ? $avatar['width'] : 0, |
316 | 'AVATAR_HEIGHT' => $avatar ? $avatar['height'] : 0, |
317 | |
318 | 'AVATAR_HTML' => $avatar ? $avatar['html'] : '', |
319 | 'AVATAR_LAZY' => $avatar ? $avatar['lazy'] : true, |
320 | |
321 | 'U_MARK_READ' => (!$this->notification_read) ? $u_mark_read : '', |
322 | ]; |
323 | } |
324 | |
325 | /** |
326 | * -------------- Fall back functions ------------------- |
327 | */ |
328 | |
329 | /** |
330 | * URL to unsubscribe to this notification (fall back) |
331 | * |
332 | * @param string|bool $method Method name to unsubscribe from (email|jabber|etc), False to unsubscribe from all notifications for this item |
333 | * @return false |
334 | */ |
335 | public function get_unsubscribe_url($method = false) |
336 | { |
337 | return false; |
338 | } |
339 | |
340 | /** |
341 | * Get the CSS style class of the notification (fall back) |
342 | * |
343 | * @return string |
344 | */ |
345 | public function get_style_class() |
346 | { |
347 | return ''; |
348 | } |
349 | |
350 | /** |
351 | * Get the user's avatar (fall back) |
352 | * |
353 | * @return array |
354 | */ |
355 | public function get_avatar() |
356 | { |
357 | return []; |
358 | } |
359 | |
360 | /** |
361 | * Get the reference of the notification (fall back) |
362 | * |
363 | * @return string |
364 | */ |
365 | public function get_reference() |
366 | { |
367 | return ''; |
368 | } |
369 | |
370 | /** |
371 | * Get the forum of the notification reference (fall back) |
372 | * |
373 | * @return string |
374 | */ |
375 | public function get_forum() |
376 | { |
377 | return ''; |
378 | } |
379 | |
380 | /** |
381 | * Get the reason for the notification (fall back) |
382 | * |
383 | * @return string |
384 | */ |
385 | public function get_reason() |
386 | { |
387 | return ''; |
388 | } |
389 | |
390 | /** |
391 | * Get the special items to load (fall back) |
392 | * |
393 | * @return array |
394 | */ |
395 | public function get_load_special() |
396 | { |
397 | return array(); |
398 | } |
399 | |
400 | /** |
401 | * Load the special items (fall back) |
402 | * |
403 | * @param array $data |
404 | * @param array $notifications |
405 | */ |
406 | public function load_special($data, $notifications) |
407 | { |
408 | } |
409 | |
410 | /** |
411 | * Is available (fall back) |
412 | * |
413 | * @return bool |
414 | */ |
415 | public function is_available() |
416 | { |
417 | return true; |
418 | } |
419 | |
420 | /** |
421 | * {@inheritdoc} |
422 | */ |
423 | public function get_email_template() |
424 | { |
425 | return false; |
426 | } |
427 | |
428 | /** |
429 | * Pre create insert array function (fall back) |
430 | * |
431 | * @param array $type_data |
432 | * @param array $notify_users |
433 | * @return array |
434 | */ |
435 | public function pre_create_insert_array($type_data, $notify_users) |
436 | { |
437 | return array(); |
438 | } |
439 | |
440 | /** |
441 | * -------------- Helper functions ------------------- |
442 | */ |
443 | |
444 | /** |
445 | * Find the users who want to receive notifications (helper) |
446 | * |
447 | * @param array|bool $user_ids User IDs to check if they want to receive notifications |
448 | * (Bool False to check all users besides anonymous and bots (USER_IGNORE)) |
449 | * @param array $options |
450 | * @return array |
451 | */ |
452 | protected function check_user_notification_options($user_ids = false, $options = array()) |
453 | { |
454 | $options = array_merge(array( |
455 | 'ignore_users' => array(), |
456 | 'item_type' => $this->get_type(), |
457 | 'item_id' => 0, // Global by default |
458 | ), $options); |
459 | |
460 | if ($user_ids === false) |
461 | { |
462 | $user_ids = array(); |
463 | |
464 | $sql = 'SELECT user_id |
465 | FROM ' . USERS_TABLE . ' |
466 | WHERE user_id <> ' . ANONYMOUS . ' |
467 | AND user_type <> ' . USER_IGNORE; |
468 | $result = $this->db->sql_query($sql); |
469 | while ($row = $this->db->sql_fetchrow($result)) |
470 | { |
471 | $user_ids[] = $row['user_id']; |
472 | } |
473 | $this->db->sql_freeresult($result); |
474 | } |
475 | |
476 | if (empty($user_ids)) |
477 | { |
478 | return array(); |
479 | } |
480 | |
481 | $rowset = $output = array(); |
482 | |
483 | $sql = 'SELECT user_id, method, notify |
484 | FROM ' . $this->user_notifications_table . ' |
485 | WHERE ' . $this->db->sql_in_set('user_id', $user_ids) . " |
486 | AND item_type = '" . $this->db->sql_escape($options['item_type']) . "' |
487 | AND item_id = " . (int) $options['item_id']; |
488 | $result = $this->db->sql_query($sql); |
489 | |
490 | while ($row = $this->db->sql_fetchrow($result)) |
491 | { |
492 | if (isset($options['ignore_users'][$row['user_id']]) && in_array($row['method'], $options['ignore_users'][$row['user_id']])) |
493 | { |
494 | continue; |
495 | } |
496 | |
497 | if (!isset($rowset[$row['user_id']])) |
498 | { |
499 | $rowset[$row['user_id']] = array(); |
500 | } |
501 | $rowset[$row['user_id']][$row['method']] = $row['notify']; |
502 | |
503 | if (!isset($output[$row['user_id']])) |
504 | { |
505 | $output[$row['user_id']] = array(); |
506 | } |
507 | if ($row['notify']) |
508 | { |
509 | $output[$row['user_id']][] = $row['method']; |
510 | } |
511 | } |
512 | |
513 | $this->db->sql_freeresult($result); |
514 | |
515 | $default_methods = $this->notification_manager->get_default_methods(); |
516 | |
517 | foreach ($user_ids as $user_id) |
518 | { |
519 | if (isset($options['ignore_users'][$user_id])) |
520 | { |
521 | continue; |
522 | } |
523 | if (!array_key_exists($user_id, $rowset)) |
524 | { |
525 | // No rows at all for this user, use the default methods |
526 | $output[$user_id] = $default_methods; |
527 | } |
528 | else |
529 | { |
530 | foreach ($default_methods as $default_method) |
531 | { |
532 | if (!array_key_exists($default_method, $rowset[$user_id])) |
533 | { |
534 | // No user preference for this type recorded, but it should be enabled by default. |
535 | $output[$user_id][] = $default_method; |
536 | } |
537 | } |
538 | } |
539 | } |
540 | |
541 | return $output; |
542 | } |
543 | |
544 | /** |
545 | * Mark this item read/unread helper |
546 | * |
547 | * @param bool $unread Unread (True/False) (Default: False) |
548 | * @param bool $return True to return a string containing the SQL code to update this item, False to execute it (Default: False) |
549 | * @return string|null If $return is False, nothing will be returned, else the sql code to update this item |
550 | */ |
551 | protected function mark($unread = true, $return = false) |
552 | { |
553 | $this->notification_read = (bool) !$unread; |
554 | |
555 | if ($return) |
556 | { |
557 | $where = array( |
558 | 'notification_type_id = ' . (int) $this->notification_type_id, |
559 | 'item_id = ' . (int) $this->item_id, |
560 | 'user_id = ' . (int) $this->user_id, |
561 | ); |
562 | |
563 | $where = implode(' AND ', $where); |
564 | return $where; |
565 | } |
566 | else |
567 | { |
568 | $this->notification_manager->mark_notifications($this->get_type(), (int) $this->item_id, (int) $this->user_id, false, $this->notification_read); |
569 | } |
570 | |
571 | return null; |
572 | } |
573 | |
574 | /** |
575 | * Get a list of users that are authorised to receive notifications |
576 | * |
577 | * @param array $users Array of users that have subscribed to a notification |
578 | * @param int $forum_id Forum ID of the forum |
579 | * @param array $options Array of notification options |
580 | * @param bool $sort Whether the users array should be sorted. Default: false |
581 | * @return array Array of users that are authorised recipients |
582 | */ |
583 | protected function get_authorised_recipients($users, $forum_id, $options, $sort = false) |
584 | { |
585 | if (empty($users)) |
586 | { |
587 | return array(); |
588 | } |
589 | |
590 | $users = array_unique($users); |
591 | |
592 | if ($sort) |
593 | { |
594 | sort($users); |
595 | } |
596 | |
597 | $auth_read = $this->auth->acl_get_list($users, 'f_read', $forum_id); |
598 | |
599 | if (empty($auth_read)) |
600 | { |
601 | return array(); |
602 | } |
603 | |
604 | return $this->check_user_notification_options($auth_read[$forum_id]['f_read'], $options); |
605 | } |
606 | } |