Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
80.60% |
241 / 299 |
|
37.50% |
12 / 32 |
CRAP | |
0.00% |
0 / 1 |
manager | |
80.60% |
241 / 299 |
|
37.50% |
12 / 32 |
193.14 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
1 | |||
load_notifications | |
40.00% |
4 / 10 |
|
0.00% |
0 / 1 |
4.94 | |||
mark_notifications_read | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
mark_notifications | |
57.14% |
4 / 7 |
|
0.00% |
0 / 1 |
5.26 | |||
mark_notifications_read_by_parent | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
mark_notifications_by_parent | |
80.00% |
4 / 5 |
|
0.00% |
0 / 1 |
3.07 | |||
mark_notifications_by_id | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
12 | |||
add_notifications | |
96.77% |
30 / 31 |
|
0.00% |
0 / 1 |
4 | |||
add_notifications_for_users | |
92.11% |
35 / 38 |
|
0.00% |
0 / 1 |
11.06 | |||
update_notifications | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
update_notification | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
delete_notifications | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
delete_notifications_by_types | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
get_subscription_types | |
100.00% |
17 / 17 |
|
100.00% |
1 / 1 |
7 | |||
get_subscription_methods | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
6 | |||
get_subscription_methods_instances | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
get_available_subscription_methods | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
get_user_notifications | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
2 | |||
get_global_subscriptions | |
94.74% |
18 / 19 |
|
0.00% |
0 / 1 |
10.01 | |||
add_subscription | |
61.29% |
19 / 31 |
|
0.00% |
0 / 1 |
8.09 | |||
delete_subscription | |
100.00% |
21 / 21 |
|
100.00% |
1 / 1 |
5 | |||
disable_notifications | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
purge_notifications | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
enable_notifications | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
prune_notifications | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
get_default_methods | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
4 | |||
get_item_type_class | |
80.00% |
4 / 5 |
|
0.00% |
0 / 1 |
2.03 | |||
get_method_class | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
load_object | |
83.33% |
5 / 6 |
|
0.00% |
0 / 1 |
4.07 | |||
get_notification_type_id | |
100.00% |
18 / 18 |
|
100.00% |
1 / 1 |
5 | |||
get_notification_type_ids | |
83.33% |
5 / 6 |
|
0.00% |
0 / 1 |
3.04 | |||
get_notified_users | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 |
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; |
15 | |
16 | use phpbb\exception\runtime_exception; |
17 | use Symfony\Component\DependencyInjection\ContainerInterface; |
18 | |
19 | /** |
20 | * Notifications service class |
21 | */ |
22 | class manager |
23 | { |
24 | /** @var array */ |
25 | protected $notification_types; |
26 | |
27 | /** @var array */ |
28 | protected $subscription_types; |
29 | |
30 | /** @var method\method_interface[] */ |
31 | protected $notification_methods; |
32 | |
33 | /** @var ContainerInterface */ |
34 | protected $phpbb_container; |
35 | |
36 | /** @var \phpbb\user_loader */ |
37 | protected $user_loader; |
38 | |
39 | /** @var \phpbb\event\dispatcher_interface */ |
40 | protected $phpbb_dispatcher; |
41 | |
42 | /** @var \phpbb\db\driver\driver_interface */ |
43 | protected $db; |
44 | |
45 | /** @var \phpbb\cache\service */ |
46 | protected $cache; |
47 | |
48 | /** @var \phpbb\language\language */ |
49 | protected $language; |
50 | |
51 | /** @var \phpbb\user */ |
52 | protected $user; |
53 | |
54 | /** @var string */ |
55 | protected $notification_types_table; |
56 | |
57 | /** @var string */ |
58 | protected $user_notifications_table; |
59 | |
60 | /** |
61 | * Notification Constructor |
62 | * |
63 | * @param array $notification_types |
64 | * @param array $notification_methods |
65 | * @param ContainerInterface $phpbb_container |
66 | * @param \phpbb\user_loader $user_loader |
67 | * @param \phpbb\event\dispatcher_interface $phpbb_dispatcher |
68 | * @param \phpbb\db\driver\driver_interface $db |
69 | * @param \phpbb\cache\service $cache |
70 | * @param \phpbb\language\language $language |
71 | * @param \phpbb\user $user |
72 | * @param string $notification_types_table |
73 | * @param string $user_notifications_table |
74 | */ |
75 | public function __construct($notification_types, $notification_methods, ContainerInterface $phpbb_container, \phpbb\user_loader $user_loader, \phpbb\event\dispatcher_interface $phpbb_dispatcher, \phpbb\db\driver\driver_interface $db, \phpbb\cache\service $cache, \phpbb\language\language $language, \phpbb\user $user, $notification_types_table, $user_notifications_table) |
76 | { |
77 | $this->notification_types = $notification_types; |
78 | $this->notification_methods = $notification_methods; |
79 | $this->phpbb_container = $phpbb_container; |
80 | |
81 | $this->user_loader = $user_loader; |
82 | $this->phpbb_dispatcher = $phpbb_dispatcher; |
83 | $this->db = $db; |
84 | $this->cache = $cache; |
85 | $this->language = $language; |
86 | $this->user = $user; |
87 | |
88 | $this->notification_types_table = $notification_types_table; |
89 | $this->user_notifications_table = $user_notifications_table; |
90 | } |
91 | |
92 | /** |
93 | * Load the user's notifications for a given method |
94 | * |
95 | * @param string $method_name |
96 | * @param array $options Optional options to control what notifications are loaded |
97 | * notification_id Notification id to load (or array of notification ids) |
98 | * user_id User id to load notifications for (Default: $user->data['user_id']) |
99 | * order_by Order by (Default: notification_time) |
100 | * order_dir Order direction (Default: DESC) |
101 | * limit Number of notifications to load (Default: 5) |
102 | * start Notifications offset (Default: 0) |
103 | * all_unread Load all unread notifications? If set to true, count_unread is set to true (Default: false) |
104 | * count_unread Count all unread notifications? (Default: false) |
105 | * count_total Count all notifications? (Default: false) |
106 | * @return array Array of information based on the request with keys: |
107 | * 'notifications' array of notification type objects |
108 | * 'unread_count' number of unread notifications the user has if count_unread is true in the options |
109 | * 'total_count' number of notifications the user has if count_total is true in the options |
110 | * @throws \phpbb\notification\exception when the method doesn't refer to a class extending \phpbb\notification\method\method_interface |
111 | */ |
112 | public function load_notifications($method_name, array $options = array()) |
113 | { |
114 | $method = $this->get_method_class($method_name); |
115 | |
116 | if (! $method instanceof \phpbb\notification\method\method_interface) |
117 | { |
118 | throw new \phpbb\notification\exception($this->language->lang('NOTIFICATION_METHOD_INVALID', $method_name)); |
119 | } |
120 | else if ($method->is_available()) |
121 | { |
122 | return $method->load_notifications($options); |
123 | } |
124 | else |
125 | { |
126 | return array( |
127 | 'notifications' => array(), |
128 | 'unread_count' => 0, |
129 | 'total_count' => 0, |
130 | ); |
131 | } |
132 | } |
133 | |
134 | /** |
135 | * Mark notifications read or unread for all available methods |
136 | * |
137 | * @param bool|string|array $notification_type_name Type identifier or array of item types (only acceptable if the $data is identical for the specified types). False to mark read for all item types |
138 | * @param bool|int|array $item_id Item id or array of item ids. False to mark read for all item ids |
139 | * @param bool|int|array $user_id User id or array of user ids. False to mark read for all user ids |
140 | * @param bool|int $time Time at which to mark all notifications prior to as read. False to mark all as read. (Default: False) |
141 | * |
142 | * @deprecated since 3.2 |
143 | */ |
144 | public function mark_notifications_read($notification_type_name, $item_id, $user_id, $time = false) |
145 | { |
146 | $this->mark_notifications($notification_type_name, $item_id, $user_id, $time); |
147 | } |
148 | |
149 | /** |
150 | * Mark notifications read or unread for all available methods |
151 | * |
152 | * @param bool|string|array $notification_type_name Type identifier or array of item types (only acceptable if the $data is identical for the specified types). False to mark read for all item types |
153 | * @param bool|int|array $item_id Item id or array of item ids. False to mark read for all item ids |
154 | * @param bool|int|array $user_id User id or array of user ids. False to mark read for all user ids |
155 | * @param bool|int $time Time at which to mark all notifications prior to as read. False to mark all as read. (Default: False) |
156 | * @param bool $mark_read Define if the notification as to be set to True or False. (Default: True) |
157 | */ |
158 | public function mark_notifications($notification_type_name, $item_id, $user_id, $time = false, $mark_read = true) |
159 | { |
160 | if (is_array($notification_type_name)) |
161 | { |
162 | $notification_type_id = $this->get_notification_type_ids($notification_type_name); |
163 | } |
164 | else if ($notification_type_name !== false) |
165 | { |
166 | $notification_type_id = $this->get_notification_type_id($notification_type_name); |
167 | } |
168 | else |
169 | { |
170 | $notification_type_id = false; |
171 | } |
172 | |
173 | /** @var method\method_interface $method */ |
174 | foreach ($this->get_available_subscription_methods() as $method) |
175 | { |
176 | $method->mark_notifications($notification_type_id, $item_id, $user_id, $time, $mark_read); |
177 | } |
178 | } |
179 | |
180 | /** |
181 | * Mark notifications read or unread from a parent identifier for all available methods |
182 | * |
183 | * @param string|array $notification_type_name Type identifier or array of item types (only acceptable if the $data is identical for the specified types) |
184 | * @param bool|int|array $item_parent_id Item parent id or array of item parent ids. False to mark read for all item parent ids |
185 | * @param bool|int|array $user_id User id or array of user ids. False to mark read for all user ids |
186 | * @param bool|int $time Time at which to mark all notifications prior to as read. False to mark all as read. (Default: False) |
187 | * |
188 | * @deprecated since 3.2 |
189 | */ |
190 | public function mark_notifications_read_by_parent($notification_type_name, $item_parent_id, $user_id, $time = false) |
191 | { |
192 | $this->mark_notifications_by_parent($notification_type_name, $item_parent_id, $user_id, $time); |
193 | } |
194 | |
195 | /** |
196 | * Mark notifications read or unread from a parent identifier for all available methods |
197 | * |
198 | * @param string|array $notification_type_name Type identifier or array of item types (only acceptable if the $data is identical for the specified types) |
199 | * @param bool|int|array $item_parent_id Item parent id or array of item parent ids. False to mark read for all item parent ids |
200 | * @param bool|int|array $user_id User id or array of user ids. False to mark read for all user ids |
201 | * @param bool|int $time Time at which to mark all notifications prior to as read. False to mark all as read. (Default: False) |
202 | * @param bool $mark_read Define if the notification as to be set to True or False. (Default: True) |
203 | */ |
204 | public function mark_notifications_by_parent($notification_type_name, $item_parent_id, $user_id, $time = false, $mark_read = true) |
205 | { |
206 | if (is_array($notification_type_name)) |
207 | { |
208 | $notification_type_id = $this->get_notification_type_ids($notification_type_name); |
209 | } |
210 | else |
211 | { |
212 | $notification_type_id = $this->get_notification_type_id($notification_type_name); |
213 | } |
214 | |
215 | /** @var method\method_interface $method */ |
216 | foreach ($this->get_available_subscription_methods() as $method) |
217 | { |
218 | $method->mark_notifications_by_parent($notification_type_id, $item_parent_id, $user_id, $time, $mark_read); |
219 | } |
220 | } |
221 | |
222 | /** |
223 | * Mark notifications read or unread for a given method |
224 | * |
225 | * @param string $method_name |
226 | * @param array $notification_id Notification id or array of notification ids. |
227 | * @param bool|int $time Time at which to mark all notifications prior to as read. False to mark all as read. (Default: False) |
228 | * @param bool $mark_read Define if the notification as to be set to True or False. (Default: True) |
229 | */ |
230 | public function mark_notifications_by_id($method_name, $notification_id, $time = false, $mark_read = true) |
231 | { |
232 | $method = $this->get_method_class($method_name); |
233 | |
234 | if ($method instanceof \phpbb\notification\method\method_interface && $method->is_available()) |
235 | { |
236 | $method->mark_notifications_by_id($notification_id, $time, $mark_read); |
237 | } |
238 | } |
239 | |
240 | /** |
241 | * Add a notification |
242 | * |
243 | * @param string|array $notification_type_name Type identifier or array of item types (only acceptable if the $data is identical for the specified types) |
244 | * Note: If you send an array of types, any user who could receive multiple notifications from this single item will only receive |
245 | * a single notification. If they MUST receive multiple notifications, call this function multiple times instead of sending an array |
246 | * @param array $data Data specific for this type that will be inserted |
247 | * @param array $options Optional options to control what notifications are loaded |
248 | * ignore_users array of data to specify which users should not receive certain types of notifications |
249 | * @return array Information about what users were notified and how they were notified |
250 | */ |
251 | public function add_notifications($notification_type_name, $data, array $options = array()) |
252 | { |
253 | $options = array_merge(array( |
254 | 'ignore_users' => array(), |
255 | ), $options); |
256 | |
257 | $notified_users = []; |
258 | $add_notifications_override = false; |
259 | |
260 | /** |
261 | * Get notification data before find_users_for_notification() execute |
262 | * |
263 | * @event core.notification_manager_add_notifications_before |
264 | * @var bool add_notifications_override Flag indicating whether function should return after event |
265 | * @var array|string notification_type_name Type identifier or array of item types |
266 | * @var string data Data specific for this notification type that will be inserted |
267 | * @var array notified_users Array of notified users |
268 | * @var string options Optional options to control what notifications are loaded |
269 | * @since 3.3.6-RC1 |
270 | */ |
271 | $vars = [ |
272 | 'add_notifications_override', |
273 | 'notification_type_name', |
274 | 'data', |
275 | 'notified_users', |
276 | 'options', |
277 | ]; |
278 | extract($this->phpbb_dispatcher->trigger_event('core.notification_manager_add_notifications_before', compact($vars))); |
279 | |
280 | if ($add_notifications_override) |
281 | { |
282 | return $notified_users; |
283 | } |
284 | |
285 | if (is_array($notification_type_name)) |
286 | { |
287 | $temp_options = $options; |
288 | |
289 | foreach ($notification_type_name as $type) |
290 | { |
291 | $temp_options['ignore_users'] = $options['ignore_users'] + $notified_users; |
292 | $notified_users += $this->add_notifications($type, $data, $temp_options); |
293 | } |
294 | |
295 | return $notified_users; |
296 | } |
297 | |
298 | // find out which users want to receive this type of notification |
299 | $notify_users = $this->get_item_type_class($notification_type_name)->find_users_for_notification($data, $options); |
300 | |
301 | /** |
302 | * Allow filtering the notify_users array for a notification that is about to be sent. |
303 | * Here, $notify_users is already filtered by f_read and the ignored list included in the options variable |
304 | * |
305 | * @event core.notification_manager_add_notifications |
306 | * @var string notification_type_name The notification type identifier |
307 | * @var array data Data specific for the notification_type_name used will be inserted |
308 | * @var array notify_users The array of userid that are going to be notified for this notification. Set to array() to cancel. |
309 | * @var array options The options that were used when this method was called (read only) |
310 | * |
311 | * @since 3.1.3-RC1 |
312 | */ |
313 | $vars = array( |
314 | 'notification_type_name', |
315 | 'data', |
316 | 'notify_users', |
317 | 'options', |
318 | ); |
319 | extract($this->phpbb_dispatcher->trigger_event('core.notification_manager_add_notifications', compact($vars))); |
320 | |
321 | $this->add_notifications_for_users($notification_type_name, $data, $notify_users); |
322 | |
323 | return $notify_users; |
324 | } |
325 | |
326 | /** |
327 | * Add a notification for specific users |
328 | * |
329 | * @param string|array $notification_type_name Type identifier or array of item types (only acceptable if the $data is identical for the specified types) |
330 | * @param array $data Data specific for this type that will be inserted |
331 | * @param array $notify_users User list to notify |
332 | */ |
333 | public function add_notifications_for_users($notification_type_name, $data, $notify_users) |
334 | { |
335 | if (is_array($notification_type_name)) |
336 | { |
337 | foreach ($notification_type_name as $type) |
338 | { |
339 | $this->add_notifications_for_users($type, $data, $notify_users); |
340 | } |
341 | |
342 | return; |
343 | } |
344 | |
345 | $notification_type_id = $this->get_notification_type_id($notification_type_name); |
346 | |
347 | $item_id = $this->get_item_type_class($notification_type_name)->get_item_id($data); |
348 | |
349 | $user_ids = array(); |
350 | $notification_methods = array(); |
351 | |
352 | // Never send notifications to the anonymous user! |
353 | unset($notify_users[ANONYMOUS]); |
354 | |
355 | // Make sure not to send new notifications to users who've already been notified about this item |
356 | // This may happen when an item was added, but now new users are able to see the item |
357 | // We remove each user which was already notified by at least one method. |
358 | /** @var method\method_interface $method */ |
359 | foreach ($this->get_subscription_methods_instances() as $method) |
360 | { |
361 | $notified_users = $method->get_notified_users($notification_type_id, array('item_id' => $item_id)); |
362 | |
363 | foreach ($notified_users as $user => $notifications) |
364 | { |
365 | unset($notify_users[$user]); |
366 | } |
367 | } |
368 | |
369 | /** |
370 | * Allow filtering the $notify_users array by $notification_type_name for a notification that is about to be sent. |
371 | * Here, $notify_users is already filtered from users who've already been notified. |
372 | * |
373 | * @event core.notification_manager_add_notifications_for_users_modify_data |
374 | * @var string notification_type_name The notification type identifier |
375 | * @var array data Data specific for this type that will be inserted |
376 | * @var array notify_users User list to notify |
377 | * |
378 | * @since 3.2.10-RC1 |
379 | * @since 3.3.1-RC1 |
380 | */ |
381 | $vars = [ |
382 | 'notification_type_name', |
383 | 'data', |
384 | 'notify_users', |
385 | ]; |
386 | extract($this->phpbb_dispatcher->trigger_event('core.notification_manager_add_notifications_for_users_modify_data', compact($vars))); |
387 | |
388 | if (!count($notify_users)) |
389 | { |
390 | return; |
391 | } |
392 | |
393 | // Allow notifications to perform actions before creating the insert array (such as run a query to cache some data needed for all notifications) |
394 | $notification = $this->get_item_type_class($notification_type_name); |
395 | $pre_create_data = $notification->pre_create_insert_array($data, $notify_users); |
396 | unset($notification); |
397 | |
398 | // Go through each user so we can insert a row in the DB and then notify them by their desired means |
399 | foreach ($notify_users as $user => $methods) |
400 | { |
401 | $notification = $this->get_item_type_class($notification_type_name); |
402 | |
403 | $notification->user_id = (int) $user; |
404 | |
405 | // Generate the insert_array |
406 | $notification->create_insert_array($data, $pre_create_data); |
407 | |
408 | // Users are needed to send notifications |
409 | $user_ids = array_merge($user_ids, $notification->users_to_query()); |
410 | |
411 | foreach ($methods as $method) |
412 | { |
413 | // Do not load non-existent notification methods |
414 | if (!isset($this->notification_methods[$method])) |
415 | { |
416 | continue; |
417 | } |
418 | |
419 | // Setup the notification methods and add the notification to the queue |
420 | if (!isset($notification_methods[$method])) |
421 | { |
422 | $notification_methods[$method] = $this->get_method_class($method); |
423 | } |
424 | $notification_methods[$method]->add_to_queue($notification); |
425 | } |
426 | } |
427 | |
428 | // We need to load all of the users to send notifications |
429 | $this->user_loader->load_users($user_ids); |
430 | |
431 | // run the queue for each method to send notifications |
432 | foreach ($notification_methods as $method) |
433 | { |
434 | $method->notify(); |
435 | } |
436 | } |
437 | |
438 | /** |
439 | * Update notification |
440 | * |
441 | * @param string|array $notification_type_name Type identifier or array of item types (only acceptable if the $data is identical for the specified types) |
442 | * @param array $data Data specific for this type that will be updated |
443 | * @param array $options |
444 | */ |
445 | public function update_notifications($notification_type_name, array $data, array $options = array()) |
446 | { |
447 | if (is_array($notification_type_name)) |
448 | { |
449 | foreach ($notification_type_name as $type) |
450 | { |
451 | $this->update_notifications($type, $data); |
452 | } |
453 | |
454 | return; |
455 | } |
456 | |
457 | $this->update_notification($this->get_item_type_class($notification_type_name), $data, $options); |
458 | } |
459 | |
460 | /** |
461 | * Update a notification |
462 | * |
463 | * @param \phpbb\notification\type\type_interface $notification The notification |
464 | * @param array $data Data specific for this type that will be updated |
465 | * @param array $options |
466 | */ |
467 | public function update_notification(\phpbb\notification\type\type_interface $notification, array $data, array $options = array()) |
468 | { |
469 | if (empty($options)) |
470 | { |
471 | $options['item_id'] = $notification->get_item_id($data); |
472 | } |
473 | |
474 | /** @var method\method_interface $method */ |
475 | foreach ($this->get_available_subscription_methods() as $method) |
476 | { |
477 | $method->update_notification($notification, $data, $options); |
478 | } |
479 | } |
480 | |
481 | /** |
482 | * Delete notifications of specified type |
483 | * |
484 | * @param string $notification_type_name Type identifier |
485 | * @param int|array $item_id Identifier within the type (or array of ids) |
486 | * @param mixed $parent_id Parent identifier within the type (or array of ids), used in combination with item_id if specified (Default: false; not checked) |
487 | * @param mixed $user_id User id (Default: false; not checked) |
488 | * |
489 | * @return void |
490 | */ |
491 | public function delete_notifications(string $notification_type_name, $item_id, $parent_id = false, $user_id = false): void |
492 | { |
493 | $notification_type_id = $this->get_notification_type_id($notification_type_name); |
494 | |
495 | /** @var method\method_interface $method */ |
496 | foreach ($this->get_available_subscription_methods() as $method) |
497 | { |
498 | $method->delete_notifications($notification_type_id, $item_id, $parent_id, $user_id); |
499 | } |
500 | } |
501 | |
502 | /** |
503 | * Delete notifications specified by multiple types |
504 | * |
505 | * @param array $notification_type_names Array of item types (only acceptable if the $item_id is identical for the specified types) |
506 | * @param int|array $item_id Identifier within the type (or array of ids) |
507 | * @param mixed $parent_id Parent identifier within the type (or array of ids), used in combination with item_id if specified (Default: false; not checked) |
508 | * @param mixed $user_id User id (Default: false; not checked) |
509 | * |
510 | * @return void |
511 | */ |
512 | public function delete_notifications_by_types(array $notification_type_names, $item_id, $parent_id = false, $user_id = false): void |
513 | { |
514 | foreach ($notification_type_names as $type) |
515 | { |
516 | $this->delete_notifications($type, $item_id, $parent_id, $user_id); |
517 | } |
518 | } |
519 | |
520 | /** |
521 | * Get all of the subscription types |
522 | * |
523 | * @return array Array of item types |
524 | */ |
525 | public function get_subscription_types() |
526 | { |
527 | if ($this->subscription_types === null) |
528 | { |
529 | $this->subscription_types = array(); |
530 | |
531 | foreach ($this->notification_types as $type_name => $data) |
532 | { |
533 | /** @var type\base $type */ |
534 | $type = $this->get_item_type_class($type_name); |
535 | |
536 | if ($type instanceof \phpbb\notification\type\type_interface && $type->is_available()) |
537 | { |
538 | $options = array_merge(array( |
539 | 'type' => $type, |
540 | 'id' => $type->get_type(), |
541 | 'lang' => 'NOTIFICATION_TYPE_' . strtoupper($type->get_type()), |
542 | 'group' => 'NOTIFICATION_GROUP_MISCELLANEOUS', |
543 | ), (($type::$notification_option !== false) ? $type::$notification_option : array())); |
544 | |
545 | $this->subscription_types[$options['group']][$options['id']] = $options; |
546 | } |
547 | } |
548 | |
549 | // Move Miscellaneous to the very last section |
550 | if (isset($this->subscription_types['NOTIFICATION_GROUP_MISCELLANEOUS'])) |
551 | { |
552 | $miscellaneous = $this->subscription_types['NOTIFICATION_GROUP_MISCELLANEOUS']; |
553 | unset($this->subscription_types['NOTIFICATION_GROUP_MISCELLANEOUS']); |
554 | $this->subscription_types['NOTIFICATION_GROUP_MISCELLANEOUS'] = $miscellaneous; |
555 | } |
556 | } |
557 | |
558 | return $this->subscription_types; |
559 | } |
560 | |
561 | /** |
562 | * Get all of the subscription methods |
563 | * |
564 | * @return array Array of methods |
565 | */ |
566 | public function get_subscription_methods() |
567 | { |
568 | $subscription_methods = array(); |
569 | |
570 | /** @var method\method_interface $method */ |
571 | foreach ($this->get_available_subscription_methods() as $method_name => $method) |
572 | { |
573 | $subscription_methods[$method_name] = array( |
574 | 'method' => $method, |
575 | 'id' => $method->get_type(), |
576 | 'lang' => str_replace('.', '_', strtoupper($method->get_type())), |
577 | ); |
578 | } |
579 | |
580 | return $subscription_methods; |
581 | } |
582 | |
583 | /** |
584 | * Get all of the subscription methods |
585 | * |
586 | * @return array Array of method's instances |
587 | */ |
588 | private function get_subscription_methods_instances() |
589 | { |
590 | $subscription_methods = array(); |
591 | |
592 | foreach ($this->notification_methods as $method_name => $data) |
593 | { |
594 | $method = $this->get_method_class($method_name); |
595 | |
596 | if ($method instanceof \phpbb\notification\method\method_interface) |
597 | { |
598 | $subscription_methods[$method_name] = $method; |
599 | } |
600 | } |
601 | |
602 | return $subscription_methods; |
603 | } |
604 | |
605 | /** |
606 | * Get all of the available subscription methods |
607 | * |
608 | * @return array Array of method's instances |
609 | */ |
610 | private function get_available_subscription_methods() |
611 | { |
612 | $subscription_methods = array(); |
613 | |
614 | /** @var method\method_interface $method */ |
615 | foreach ($this->get_subscription_methods_instances() as $method_name => $method) |
616 | { |
617 | if ($method->is_available()) |
618 | { |
619 | $subscription_methods[$method_name] = $method; |
620 | } |
621 | } |
622 | |
623 | return $subscription_methods; |
624 | } |
625 | |
626 | |
627 | /** |
628 | * Get user's notification data |
629 | * |
630 | * @param int $user_id The user_id of the user to get the notifications for |
631 | * |
632 | * @return array User's notification |
633 | */ |
634 | protected function get_user_notifications($user_id) |
635 | { |
636 | $sql = 'SELECT method, notify, item_type |
637 | FROM ' . $this->user_notifications_table . ' |
638 | WHERE user_id = ' . (int) $user_id . ' |
639 | AND item_id = 0'; |
640 | |
641 | $result = $this->db->sql_query($sql); |
642 | $user_notifications = array(); |
643 | |
644 | while ($row = $this->db->sql_fetchrow($result)) |
645 | { |
646 | $user_notifications[$row['item_type']][] = $row; |
647 | } |
648 | |
649 | $this->db->sql_freeresult($result); |
650 | |
651 | return $user_notifications; |
652 | } |
653 | |
654 | /** |
655 | * Get global subscriptions (item_id = 0) |
656 | * |
657 | * @param bool|int $user_id The user_id to add the subscription for (bool false for current user) |
658 | * |
659 | * @return array Subscriptions |
660 | */ |
661 | public function get_global_subscriptions($user_id = false) |
662 | { |
663 | $user_id = $user_id ?: $this->user->data['user_id']; |
664 | |
665 | $subscriptions = array(); |
666 | $default_methods = $this->get_default_methods(); |
667 | |
668 | $user_notifications = $this->get_user_notifications($user_id); |
669 | |
670 | foreach ($this->get_subscription_types() as $types) |
671 | { |
672 | foreach ($types as $id => $type) |
673 | { |
674 | $type_subscriptions = $default_methods; |
675 | if (!empty($user_notifications[$id])) |
676 | { |
677 | foreach ($user_notifications[$id] as $user_notification) |
678 | { |
679 | $key = array_search($user_notification['method'], $type_subscriptions, true); |
680 | if (!$user_notification['notify']) |
681 | { |
682 | if ($key !== false) |
683 | { |
684 | unset($type_subscriptions[$key]); |
685 | } |
686 | |
687 | continue; |
688 | } |
689 | else if ($key === false) |
690 | { |
691 | $type_subscriptions[] = $user_notification['method']; |
692 | } |
693 | } |
694 | } |
695 | |
696 | if (!empty($type_subscriptions)) |
697 | { |
698 | $subscriptions[$id] = $type_subscriptions; |
699 | } |
700 | } |
701 | } |
702 | |
703 | return $subscriptions; |
704 | } |
705 | |
706 | /** |
707 | * Add a subscription |
708 | * |
709 | * @param string $item_type Type identifier of the subscription |
710 | * @param int $item_id The id of the item |
711 | * @param string $method The method of the notification e.g. 'board', 'email', or 'jabber' |
712 | * (if null a subscription will be added for all the defaults methods) |
713 | * @param bool|int $user_id The user_id to add the subscription for (bool false for current user) |
714 | */ |
715 | public function add_subscription($item_type, $item_id = 0, $method = null, $user_id = false) |
716 | { |
717 | if ($method === null) |
718 | { |
719 | foreach ($this->get_default_methods() as $method_name) |
720 | { |
721 | $this->add_subscription($item_type, $item_id, $method_name, $user_id); |
722 | } |
723 | |
724 | return; |
725 | } |
726 | |
727 | $user_id = ($user_id === false) ? $this->user->data['user_id'] : $user_id; |
728 | |
729 | $sql = 'SELECT notify |
730 | FROM ' . $this->user_notifications_table . " |
731 | WHERE item_type = '" . $this->db->sql_escape($item_type) . "' |
732 | AND item_id = " . (int) $item_id . ' |
733 | AND user_id = ' .(int) $user_id . " |
734 | AND method = '" . $this->db->sql_escape($method) . "'"; |
735 | $this->db->sql_query($sql); |
736 | $current = $this->db->sql_fetchfield('notify'); |
737 | $this->db->sql_freeresult(); |
738 | |
739 | if ($current === false) |
740 | { |
741 | $sql = 'INSERT INTO ' . $this->user_notifications_table . ' ' . |
742 | $this->db->sql_build_array('INSERT', array( |
743 | 'item_type' => $item_type, |
744 | 'item_id' => (int) $item_id, |
745 | 'user_id' => (int) $user_id, |
746 | 'method' => $method, |
747 | 'notify' => 1, |
748 | )); |
749 | $this->db->sql_query($sql); |
750 | } |
751 | else if (!$current) |
752 | { |
753 | $sql = 'UPDATE ' . $this->user_notifications_table . " |
754 | SET notify = 1 |
755 | WHERE item_type = '" . $this->db->sql_escape($item_type) . "' |
756 | AND item_id = " . (int) $item_id . ' |
757 | AND user_id = ' .(int) $user_id . " |
758 | AND method = '" . $this->db->sql_escape($method) . "'"; |
759 | $this->db->sql_query($sql); |
760 | } |
761 | } |
762 | |
763 | /** |
764 | * Delete a subscription |
765 | * |
766 | * @param string $item_type Type identifier of the subscription |
767 | * @param int $item_id The id of the item |
768 | * @param string $method The method of the notification e.g. 'board', 'email', or 'jabber' |
769 | * @param bool|int $user_id The user_id to add the subscription for (bool false for current user) |
770 | */ |
771 | public function delete_subscription($item_type, $item_id = 0, $method = null, $user_id = false) |
772 | { |
773 | if ($method === null) |
774 | { |
775 | foreach ($this->get_default_methods() as $method_name) |
776 | { |
777 | $this->delete_subscription($item_type, $item_id, $method_name, $user_id); |
778 | } |
779 | |
780 | return; |
781 | } |
782 | |
783 | $user_id = $user_id ?: $this->user->data['user_id']; |
784 | |
785 | $sql = 'UPDATE ' . $this->user_notifications_table . " |
786 | SET notify = 0 |
787 | WHERE item_type = '" . $this->db->sql_escape($item_type) . "' |
788 | AND item_id = " . (int) $item_id . ' |
789 | AND user_id = ' .(int) $user_id . " |
790 | AND method = '" . $this->db->sql_escape($method) . "'"; |
791 | $this->db->sql_query($sql); |
792 | |
793 | if (!$this->db->sql_affectedrows()) |
794 | { |
795 | $sql = 'INSERT INTO ' . $this->user_notifications_table . ' ' . |
796 | $this->db->sql_build_array('INSERT', array( |
797 | 'item_type' => $item_type, |
798 | 'item_id' => (int) $item_id, |
799 | 'user_id' => (int) $user_id, |
800 | 'method' => $method, |
801 | 'notify' => 0, |
802 | )); |
803 | $this->db->sql_query($sql); |
804 | } |
805 | } |
806 | |
807 | /** |
808 | * Disable all notifications of a certain type |
809 | * |
810 | * This should be called when an extension which has notification types |
811 | * is disabled so that all those notifications are hidden and do not |
812 | * cause errors |
813 | * |
814 | * @param string $notification_type_name Type identifier of the subscription |
815 | */ |
816 | public function disable_notifications($notification_type_name) |
817 | { |
818 | $sql = 'UPDATE ' . $this->notification_types_table . " |
819 | SET notification_type_enabled = 0 |
820 | WHERE notification_type_name = '" . $this->db->sql_escape($notification_type_name) . "'"; |
821 | $this->db->sql_query($sql); |
822 | } |
823 | |
824 | /** |
825 | * Purge all notifications of a certain type |
826 | * |
827 | * This should be called when an extension which has notification types |
828 | * is purged so that all those notifications are removed |
829 | * |
830 | * @param string $notification_type_name Type identifier of the subscription |
831 | */ |
832 | public function purge_notifications($notification_type_name) |
833 | { |
834 | // If a notification is never used, its type will not be added to the database |
835 | // nor its id cached. If this method is called by an extension during the |
836 | // purge step, and that extension never used its notifications, |
837 | // get_notification_type_id() will throw an exception. However, |
838 | // because no notification type was added to the database, |
839 | // there is nothing to delete, so we can silently drop the exception. |
840 | try |
841 | { |
842 | $notification_type_id = $this->get_notification_type_id($notification_type_name); |
843 | |
844 | /** @var method\method_interface $method */ |
845 | foreach ($this->get_available_subscription_methods() as $method) |
846 | { |
847 | $method->purge_notifications($notification_type_id); |
848 | } |
849 | |
850 | } |
851 | catch (\phpbb\notification\exception $e) |
852 | { |
853 | // Continue |
854 | } |
855 | } |
856 | |
857 | /** |
858 | * Enable all notifications of a certain type |
859 | * |
860 | * This should be called when an extension which has notification types |
861 | * that was disabled is re-enabled so that all those notifications that |
862 | * were hidden are shown again |
863 | * |
864 | * @param string $notification_type_name Type identifier of the subscription |
865 | */ |
866 | public function enable_notifications($notification_type_name) |
867 | { |
868 | $sql = 'UPDATE ' . $this->notification_types_table . " |
869 | SET notification_type_enabled = 1 |
870 | WHERE notification_type_name = '" . $this->db->sql_escape($notification_type_name) . "'"; |
871 | $this->db->sql_query($sql); |
872 | } |
873 | |
874 | /** |
875 | * Delete all notifications older than a certain time |
876 | * |
877 | * @param int $timestamp Unix timestamp to delete all notifications that were created before |
878 | * @param bool $only_read True (default) to only prune read notifications |
879 | */ |
880 | public function prune_notifications($timestamp, $only_read = true) |
881 | { |
882 | /** @var method\method_interface $method */ |
883 | foreach ($this->get_available_subscription_methods() as $method) |
884 | { |
885 | $method->prune_notifications($timestamp, $only_read); |
886 | } |
887 | } |
888 | |
889 | /** |
890 | * Helper to get the list of methods enabled by default |
891 | * |
892 | * @return string[] Default method types |
893 | */ |
894 | public function get_default_methods(): array |
895 | { |
896 | $default_methods = array(); |
897 | |
898 | foreach ($this->notification_methods as $method) |
899 | { |
900 | if ($method->is_enabled_by_default() && $method->is_available()) |
901 | { |
902 | $default_methods[] = $method->get_type(); |
903 | } |
904 | } |
905 | |
906 | return $default_methods; |
907 | } |
908 | |
909 | /** |
910 | * Helper to get the notifications item type class and set it up |
911 | * |
912 | * @param string $notification_type_name |
913 | * @param array $data |
914 | * |
915 | * @return type\type_interface |
916 | * @throws runtime_exception When type name is not o notification type |
917 | */ |
918 | public function get_item_type_class($notification_type_name, $data = array()) |
919 | { |
920 | $item = $this->load_object($notification_type_name); |
921 | |
922 | if (!$item instanceof type\type_interface) |
923 | { |
924 | throw new runtime_exception('Supplied type name returned invalid service: ' . $notification_type_name); |
925 | } |
926 | |
927 | $item->set_initial_data($data); |
928 | |
929 | return $item; |
930 | } |
931 | |
932 | /** |
933 | * Helper to get the notifications method class and set it up |
934 | * |
935 | * @param string $method_name |
936 | * |
937 | * @return method\method_interface |
938 | * @throws runtime_exception When object name is not o notification method |
939 | */ |
940 | public function get_method_class($method_name) |
941 | { |
942 | $object = $this->load_object($method_name); |
943 | |
944 | if (!$object instanceof method\method_interface) |
945 | { |
946 | throw new runtime_exception('Supplied method name returned invalid service: ' . $method_name); |
947 | } |
948 | |
949 | return $object; |
950 | } |
951 | |
952 | /** |
953 | * Helper to load objects (notification types/methods) |
954 | * |
955 | * @param string $object_name |
956 | * |
957 | * @return method\method_interface|type\type_interface |
958 | * @psalm-suppress NullableReturnStatement Invalid service will result in exception |
959 | * @throws runtime_exception When object name is not o notification method or type |
960 | */ |
961 | protected function load_object($object_name) |
962 | { |
963 | $object = $this->phpbb_container->get($object_name); |
964 | |
965 | if (method_exists($object, 'set_notification_manager')) |
966 | { |
967 | $object->set_notification_manager($this); |
968 | } |
969 | |
970 | if (!$object instanceof method\method_interface && !$object instanceof type\type_interface) |
971 | { |
972 | throw new runtime_exception('Supplied object name returned invalid service: ' . $object_name); |
973 | } |
974 | |
975 | return $object; |
976 | } |
977 | |
978 | /** |
979 | * Get the notification type id from the name |
980 | * |
981 | * @param string $notification_type_name The name |
982 | * @return int the notification_type_id |
983 | * @throws \phpbb\notification\exception |
984 | */ |
985 | public function get_notification_type_id($notification_type_name) |
986 | { |
987 | $notification_type_ids = []; |
988 | |
989 | $sql = 'SELECT notification_type_id, notification_type_name |
990 | FROM ' . $this->notification_types_table; |
991 | $result = $this->db->sql_query($sql, 604800); // cache for one week |
992 | while ($row = $this->db->sql_fetchrow($result)) |
993 | { |
994 | $notification_type_ids[$row['notification_type_name']] = (int) $row['notification_type_id']; |
995 | } |
996 | $this->db->sql_freeresult($result); |
997 | |
998 | if (!isset($notification_type_ids[$notification_type_name])) |
999 | { |
1000 | if (!isset($this->notification_types[$notification_type_name]) && !isset($this->notification_types['notification.type.' . $notification_type_name])) |
1001 | { |
1002 | throw new \phpbb\notification\exception('NOTIFICATION_TYPE_NOT_EXIST', array($notification_type_name)); |
1003 | } |
1004 | |
1005 | $sql = 'INSERT INTO ' . $this->notification_types_table . ' ' . $this->db->sql_build_array('INSERT', array( |
1006 | 'notification_type_name' => $notification_type_name, |
1007 | 'notification_type_enabled' => 1, |
1008 | )); |
1009 | $this->db->sql_query($sql); |
1010 | |
1011 | // expose new notification type ID for this request |
1012 | $notification_type_ids[$notification_type_name] = (int) $this->db->sql_nextid(); |
1013 | |
1014 | // destroy cache, we have a new addition which we have to to load next time |
1015 | $this->cache->destroy('sql', $this->notification_types_table); |
1016 | } |
1017 | |
1018 | return $notification_type_ids[$notification_type_name]; |
1019 | } |
1020 | |
1021 | /** |
1022 | * Get notification type ids (as an array) |
1023 | * |
1024 | * @param string|array $notification_type_names Notification type names |
1025 | * @return array Array of integers |
1026 | */ |
1027 | public function get_notification_type_ids($notification_type_names) |
1028 | { |
1029 | if (!is_array($notification_type_names)) |
1030 | { |
1031 | $notification_type_names = array($notification_type_names); |
1032 | } |
1033 | |
1034 | $notification_type_ids = array(); |
1035 | |
1036 | foreach ($notification_type_names as $name) |
1037 | { |
1038 | $notification_type_ids[$name] = $this->get_notification_type_id($name); |
1039 | } |
1040 | |
1041 | return $notification_type_ids; |
1042 | } |
1043 | |
1044 | /** |
1045 | * Find the users which are already notified |
1046 | * |
1047 | * @param bool|string|array $notification_type_name Type identifier or array of item types (only acceptable if the $data is identical for the specified types). False to retrieve all item types |
1048 | * @param array $options |
1049 | * @return array The list of the notified users |
1050 | */ |
1051 | public function get_notified_users($notification_type_name, array $options) |
1052 | { |
1053 | $notification_type_id = $this->get_notification_type_id($notification_type_name); |
1054 | |
1055 | $notified_users = array(); |
1056 | |
1057 | /** @var method\method_interface $method */ |
1058 | foreach ($this->get_available_subscription_methods() as $method) |
1059 | { |
1060 | $notified_users = $notified_users + $method->get_notified_users($notification_type_id, $options); |
1061 | } |
1062 | |
1063 | return $notified_users; |
1064 | } |
1065 | } |