Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
75.00% covered (warning)
75.00%
120 / 160
66.67% covered (warning)
66.67%
10 / 15
CRAP
0.00% covered (danger)
0.00%
0 / 1
board
75.00% covered (warning)
75.00%
120 / 160
66.67% covered (warning)
66.67%
10 / 15
146.56
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 add_to_queue
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_type
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 is_available
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 is_enabled_by_default
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_notified_users
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
6
 load_notifications
71.62% covered (warning)
71.62%
53 / 74
0.00% covered (danger)
0.00%
0 / 1
27.25
 notify
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 update_notification
92.31% covered (success)
92.31%
12 / 13
0.00% covered (danger)
0.00%
0 / 1
7.02
 mark_notifications
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
9
 mark_notifications_by_parent
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
9
 mark_notifications_by_id
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
20
 delete_notifications
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
6
 prune_notifications
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 purge_notifications
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 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
14namespace phpbb\notification\method;
15
16/**
17* In Board notification method class
18* This class handles in board notifications. This method is enabled by default.
19*
20* @package notifications
21*/
22class board extends \phpbb\notification\method\base
23{
24    /** @var \phpbb\user_loader */
25    protected $user_loader;
26
27    /** @var \phpbb\db\driver\driver_interface */
28    protected $db;
29
30    /** @var \phpbb\cache\driver\driver_interface */
31    protected $cache;
32
33    /** @var \phpbb\user */
34    protected $user;
35
36    /** @var \phpbb\config\config */
37    protected $config;
38
39    /** @var string */
40    protected $notification_types_table;
41
42    /** @var string */
43    protected $notifications_table;
44
45    /**
46    * Notification Method Board Constructor
47    *
48    * @param \phpbb\user_loader $user_loader
49    * @param \phpbb\db\driver\driver_interface $db
50    * @param \phpbb\cache\driver\driver_interface $cache
51    * @param \phpbb\user $user
52    * @param \phpbb\config\config $config
53    * @param string $notification_types_table
54    * @param string $notifications_table
55    */
56    public function __construct(\phpbb\user_loader $user_loader, \phpbb\db\driver\driver_interface $db, \phpbb\cache\driver\driver_interface $cache, \phpbb\user $user, \phpbb\config\config $config, $notification_types_table, $notifications_table)
57    {
58        $this->user_loader = $user_loader;
59        $this->db = $db;
60        $this->cache = $cache;
61        $this->user = $user;
62        $this->config = $config;
63        $this->notification_types_table = $notification_types_table;
64        $this->notifications_table = $notifications_table;
65
66    }
67
68    /**
69    * {@inheritdoc}
70    */
71    public function add_to_queue(\phpbb\notification\type\type_interface $notification)
72    {
73        $this->queue[] = $notification;
74    }
75
76    /**
77    * {@inheritdoc}
78    */
79    public function get_type()
80    {
81        return 'notification.method.board';
82    }
83
84    /**
85    * {@inheritdoc}
86    */
87    public function is_available()
88    {
89        return $this->config['allow_board_notifications'];
90    }
91
92    /**
93    * {@inheritdoc}
94    */
95    public function is_enabled_by_default()
96    {
97        return true;
98    }
99
100    /**
101    * {@inheritdoc}
102    */
103    public function get_notified_users($notification_type_id, array $options)
104    {
105        $notified_users = array();
106        $sql = 'SELECT n.*
107            FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt
108            WHERE n.notification_type_id = ' . (int) $notification_type_id .
109            (isset($options['item_id']) ? ' AND n.item_id = ' . (int) $options['item_id'] : '') .
110            (isset($options['item_parent_id']) ? ' AND n.item_parent_id = ' . (int) $options['item_parent_id'] : '') .
111            (isset($options['user_id']) ? ' AND n.user_id = ' . (int) $options['user_id'] : '') .
112            (isset($options['read']) ? ' AND n.notification_read = ' . (int) $options['read'] : '') .'
113                AND nt.notification_type_id = n.notification_type_id
114                AND nt.notification_type_enabled = 1';
115        $result = $this->db->sql_query($sql);
116        while ($row = $this->db->sql_fetchrow($result))
117        {
118            $notified_users[$row['user_id']] = $row;
119        }
120        $this->db->sql_freeresult($result);
121
122        return $notified_users;
123    }
124
125    /**
126    * {@inheritdoc}
127    */
128    public function load_notifications(array $options = array())
129    {
130        // Merge default options
131        $options = array_merge(array(
132            'notification_id'    => false,
133            'user_id'            => $this->user->data['user_id'],
134            'order_by'            => 'notification_time',
135            'order_dir'            => 'DESC',
136            'limit'                => 0,
137            'start'                => 0,
138            'all_unread'        => false,
139            'count_unread'        => false,
140            'count_total'        => false,
141        ), $options);
142
143        // If all_unread, count_unread must be true
144        $options['count_unread'] = ($options['all_unread']) ? true : $options['count_unread'];
145
146        // Anonymous users and bots never receive notifications
147        if ($options['user_id'] == $this->user->data['user_id'] && ($this->user->data['user_id'] == ANONYMOUS || $this->user->data['user_type'] == USER_IGNORE))
148        {
149            return array(
150                'notifications'        => array(),
151                'unread_count'        => 0,
152                'total_count'        => 0,
153            );
154        }
155
156        $notifications = $user_ids = array();
157        $load_special = array();
158        $total_count = $unread_count = 0;
159
160        if ($options['count_unread'])
161        {
162            // Get the total number of unread notifications
163            $sql = 'SELECT COUNT(n.notification_id) AS unread_count
164                FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt
165                WHERE n.user_id = ' . (int) $options['user_id'] . '
166                    AND n.notification_read = 0
167                    AND nt.notification_type_id = n.notification_type_id
168                    AND nt.notification_type_enabled = 1';
169            $result = $this->db->sql_query($sql);
170            $unread_count = (int) $this->db->sql_fetchfield('unread_count');
171            $this->db->sql_freeresult($result);
172        }
173
174        if ($options['count_total'])
175        {
176            // Get the total number of notifications
177            $sql = 'SELECT COUNT(n.notification_id) AS total_count
178                FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt
179                WHERE n.user_id = ' . (int) $options['user_id'] . '
180                    AND nt.notification_type_id = n.notification_type_id
181                    AND nt.notification_type_enabled = 1';
182            $result = $this->db->sql_query($sql);
183            $total_count = (int) $this->db->sql_fetchfield('total_count');
184            $this->db->sql_freeresult($result);
185        }
186
187        if (!$options['count_total'] || $total_count)
188        {
189            $rowset = array();
190
191            // Get the main notifications
192            $sql = 'SELECT n.*, nt.notification_type_name
193                FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt
194                WHERE n.user_id = ' . (int) $options['user_id'] .
195                (($options['notification_id']) ? ((is_array($options['notification_id'])) ? ' AND ' . $this->db->sql_in_set('n.notification_id', $options['notification_id']) : ' AND n.notification_id = ' . (int) $options['notification_id']) : '') . '
196                    AND nt.notification_type_id = n.notification_type_id
197                    AND nt.notification_type_enabled = 1
198                ORDER BY n.' . $this->db->sql_escape($options['order_by']) . ' ' . $this->db->sql_escape($options['order_dir']);
199            $result = $this->db->sql_query_limit($sql, $options['limit'], $options['start']);
200
201            while ($row = $this->db->sql_fetchrow($result))
202            {
203                $rowset[$row['notification_id']] = $row;
204            }
205            $this->db->sql_freeresult($result);
206
207            // Get all unread notifications
208            if ($unread_count && $options['all_unread'] && !empty($rowset))
209            {
210                $sql = 'SELECT n.*, nt.notification_type_name
211                FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt
212                    WHERE n.user_id = ' . (int) $options['user_id'] . '
213                        AND n.notification_read = 0
214                        AND ' . $this->db->sql_in_set('n.notification_id', array_keys($rowset), true) . '
215                        AND nt.notification_type_id = n.notification_type_id
216                        AND nt.notification_type_enabled = 1
217                    ORDER BY n.' . $this->db->sql_escape($options['order_by']) . ' ' . $this->db->sql_escape($options['order_dir']);
218                $result = $this->db->sql_query_limit($sql, $options['limit'], $options['start']);
219
220                while ($row = $this->db->sql_fetchrow($result))
221                {
222                    $rowset[$row['notification_id']] = $row;
223                }
224                $this->db->sql_freeresult($result);
225            }
226
227            foreach ($rowset as $row)
228            {
229                $notification = $this->notification_manager->get_item_type_class($row['notification_type_name'], $row);
230
231                // Array of user_ids to query all at once
232                $user_ids = array_merge($user_ids, $notification->users_to_query());
233
234                // Some notification types also require querying additional tables themselves
235                if (!isset($load_special[$row['notification_type_name']]))
236                {
237                    $load_special[$row['notification_type_name']] = array();
238                }
239                $load_special[$row['notification_type_name']] = array_merge($load_special[$row['notification_type_name']], $notification->get_load_special());
240
241                $notifications[$row['notification_id']] = $notification;
242            }
243
244            $this->user_loader->load_users($user_ids);
245
246            // Allow each type to load its own special items
247            foreach ($load_special as $item_type => $data)
248            {
249                $item_class = $this->notification_manager->get_item_type_class($item_type);
250
251                $item_class->load_special($data, $notifications);
252            }
253        }
254
255        return array(
256            'notifications'        => $notifications,
257            'unread_count'        => $unread_count,
258            'total_count'        => $total_count,
259        );
260    }
261
262    /**
263    * {@inheritdoc}
264    */
265    public function notify()
266    {
267        $insert_buffer = new \phpbb\db\sql_insert_buffer($this->db, $this->notifications_table);
268
269        /** @var \phpbb\notification\type\type_interface $notification */
270        foreach ($this->queue as $notification)
271        {
272            $data = $notification->get_insert_array();
273            $insert_buffer->insert($data);
274        }
275
276        $insert_buffer->flush();
277
278        // We're done, empty the queue
279        $this->empty_queue();
280    }
281
282    /**
283    * {@inheritdoc}
284    */
285    public function update_notification($notification, array $data, array $options)
286    {
287        // Allow the notifications class to over-ride the update_notifications functionality
288        if (method_exists($notification, 'update_notifications'))
289        {
290            // Return False to over-ride the rest of the update
291            if ($notification->update_notifications($data) === false)
292            {
293                return;
294            }
295        }
296
297        $notification_type_id = $this->notification_manager->get_notification_type_id($notification->get_type());
298        $update_array = $notification->create_update_array($data);
299
300        $sql = 'UPDATE ' . $this->notifications_table . '
301            SET ' . $this->db->sql_build_array('UPDATE', $update_array) . '
302            WHERE notification_type_id = ' . (int) $notification_type_id .
303            (isset($options['item_id']) ? ' AND item_id = ' . (int) $options['item_id'] : '') .
304            (isset($options['item_parent_id']) ? ' AND item_parent_id = ' . (int) $options['item_parent_id'] : '') .
305            (isset($options['user_id']) ? ' AND user_id = ' . (int) $options['user_id'] : '') .
306            (isset($options['read']) ? ' AND notification_read = ' . (int) $options['read'] : '');
307        $this->db->sql_query($sql);
308    }
309
310    /**
311    * {@inheritdoc}
312    */
313    public function mark_notifications($notification_type_id, $item_id, $user_id, $time = false, $mark_read = true)
314    {
315        $time = ($time !== false) ? $time : time();
316
317        $sql = 'UPDATE ' . $this->notifications_table . '
318            SET notification_read = ' . ($mark_read ? 1 : 0) . '
319            WHERE notification_time <= ' . (int) $time .
320            (($notification_type_id !== false) ? ' AND ' .
321                (is_array($notification_type_id) ? $this->db->sql_in_set('notification_type_id', $notification_type_id) : 'notification_type_id = ' . $notification_type_id) : '') .
322            (($user_id !== false) ? ' AND ' . (is_array($user_id) ? $this->db->sql_in_set('user_id', $user_id) : 'user_id = ' . (int) $user_id) : '') .
323            (($item_id !== false) ? ' AND ' . (is_array($item_id) ? $this->db->sql_in_set('item_id', $item_id) : 'item_id = ' . (int) $item_id) : '');
324        $this->db->sql_query($sql);
325    }
326
327    /**
328    * {@inheritdoc}
329    */
330    public function mark_notifications_by_parent($notification_type_id, $item_parent_id, $user_id, $time = false, $mark_read = true)
331    {
332        $time = ($time !== false) ? $time : time();
333
334        $sql = 'UPDATE ' . $this->notifications_table . '
335            SET notification_read = ' . ($mark_read ? 1 : 0) . '
336            WHERE notification_time <= ' . (int) $time .
337            (($notification_type_id !== false) ? ' AND ' .
338                (is_array($notification_type_id) ? $this->db->sql_in_set('notification_type_id', $notification_type_id) : 'notification_type_id = ' . $notification_type_id) : '') .
339            (($item_parent_id !== false) ? ' AND ' . (is_array($item_parent_id) ? $this->db->sql_in_set('item_parent_id', $item_parent_id, false, true) : 'item_parent_id = ' . (int) $item_parent_id) : '') .
340            (($user_id !== false) ? ' AND ' . (is_array($user_id) ? $this->db->sql_in_set('user_id', $user_id) : 'user_id = ' . (int) $user_id) : '');
341        $this->db->sql_query($sql);
342    }
343
344    /**
345    * {@inheritdoc}
346    */
347    public function mark_notifications_by_id($notification_id, $time = false, $mark_read = true)
348    {
349        $time = ($time !== false) ? $time : time();
350
351        $sql = 'UPDATE ' . $this->notifications_table . '
352            SET notification_read = ' . ($mark_read ? 1 : 0) . '
353            WHERE notification_time <= ' . (int) $time . '
354                AND ' . ((is_array($notification_id)) ? $this->db->sql_in_set('notification_id', $notification_id) : 'notification_id = ' . (int) $notification_id);
355        $this->db->sql_query($sql);
356    }
357
358    /**
359    * {@inheritdoc}
360    */
361    public function delete_notifications($notification_type_id, $item_id, $parent_id = false, $user_id = false)
362    {
363        $sql = 'DELETE FROM ' . $this->notifications_table . '
364            WHERE notification_type_id = ' . (int) $notification_type_id . '
365                AND ' . (is_array($item_id) ? $this->db->sql_in_set('item_id', $item_id) : 'item_id = ' . (int) $item_id) .
366            (($parent_id !== false) ? ' AND ' . ((is_array($parent_id) ? $this->db->sql_in_set('item_parent_id', $parent_id) : 'item_parent_id = ' . (int) $parent_id)) : '') .
367            (($user_id !== false) ? ' AND ' . ((is_array($user_id) ? $this->db->sql_in_set('user_id', $user_id) : 'user_id = ' . (int) $user_id)) : '');
368        $this->db->sql_query($sql);
369    }
370
371    /**
372    * {@inheritdoc}
373    */
374    public function prune_notifications($timestamp, $only_read = true)
375    {
376        $sql = 'DELETE FROM ' . $this->notifications_table . '
377            WHERE notification_time < ' . (int) $timestamp .
378            (($only_read) ? ' AND notification_read = 1' : '');
379        $this->db->sql_query($sql);
380
381        $this->config->set('read_notification_last_gc', time(), false);
382    }
383
384    /**
385    * {@inheritdoc}
386    */
387    public function purge_notifications($notification_type_id)
388    {
389        $sql = 'DELETE FROM ' . $this->notifications_table . '
390            WHERE notification_type_id = ' . (int) $notification_type_id;
391        $this->db->sql_query($sql);
392
393        $sql = 'DELETE FROM ' . $this->notification_types_table . '
394            WHERE notification_type_id = ' . (int) $notification_type_id;
395        $this->db->sql_query($sql);
396
397        $this->cache->destroy('sql', $this->notification_types_table);
398    }
399}