Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
88.65% covered (warning)
88.65%
203 / 229
22.22% covered (danger)
22.22%
4 / 18
CRAP
0.00% covered (danger)
0.00%
0 / 1
teampage
88.65% covered (warning)
88.65%
203 / 229
22.22% covered (danger)
22.22%
4 / 18
74.77
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 get_group_value
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
2
 get_group_values
90.00% covered (success)
90.00%
9 / 10
0.00% covered (danger)
0.00%
0 / 1
2.00
 get_teampage_value
88.89% covered (warning)
88.89%
8 / 9
0.00% covered (danger)
0.00%
0 / 1
2.01
 get_teampage_values
88.89% covered (warning)
88.89%
8 / 9
0.00% covered (danger)
0.00%
0 / 1
2.01
 get_group_count
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 add_group
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 add_group_teampage
100.00% covered (success)
100.00%
33 / 33
100.00% covered (success)
100.00%
1 / 1
4
 add_category_teampage
92.31% covered (success)
92.31%
12 / 13
0.00% covered (danger)
0.00%
0 / 1
2.00
 delete_group
83.33% covered (warning)
83.33%
10 / 12
0.00% covered (danger)
0.00%
0 / 1
2.02
 delete_teampage
86.67% covered (warning)
86.67%
13 / 15
0.00% covered (danger)
0.00%
0 / 1
2.01
 move_up
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 move_up_teampage
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 move_down
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 move_down_teampage
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 move
97.56% covered (success)
97.56%
40 / 41
0.00% covered (danger)
0.00%
0 / 1
17
 move_teampage
98.08% covered (success)
98.08%
51 / 52
0.00% covered (danger)
0.00%
0 / 1
19
 group_type_language
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
56
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\groupposition;
15
16/**
17* Teampage group position class
18*
19* Teampage position is an ascending list 1, 2, ..., n for items which are displayed. 1 is the first item, n the last.
20*/
21class teampage implements \phpbb\groupposition\groupposition_interface
22{
23    /**
24    * Group is not displayed
25    */
26    const GROUP_DISABLED = 0;
27
28    /**
29    * No parent item
30    */
31    const NO_PARENT = 0;
32
33    /**
34    * Database object
35    * @var \phpbb\db\driver\driver_interface
36    */
37    protected $db;
38
39    /**
40    * Cache object
41    * @var \phpbb\cache\driver\driver_interface
42    */
43    protected $cache;
44
45    /**
46    * Constructor
47    *
48    * @param \phpbb\db\driver\driver_interface                $db        Database object
49    * @param \phpbb\cache\driver\driver_interface    $cache    Cache object
50    */
51    public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\cache\driver\driver_interface $cache)
52    {
53        $this->db = $db;
54        $this->cache = $cache;
55    }
56
57    /**
58    * Returns the teampage position for a given group, if the group exists.
59    *
60    * @param    int        $group_id    group_id of the group to be selected
61    *
62    * @return    int            position of the group
63    * @throws exception
64    */
65    public function get_group_value($group_id)
66    {
67        // The join is required to ensure that the group itself exists
68        $sql = 'SELECT g.group_id, t.teampage_position
69            FROM ' . GROUPS_TABLE . ' g
70            LEFT JOIN ' . TEAMPAGE_TABLE . ' t
71                ON (t.group_id = g.group_id)
72            WHERE g.group_id = ' . (int) $group_id;
73        $result = $this->db->sql_query($sql);
74        $row = $this->db->sql_fetchrow($result);
75        $this->db->sql_freeresult($result);
76
77        if ($row === false)
78        {
79            // Group not found.
80            throw new exception('NO_GROUP');
81        }
82
83        return (int) $row['teampage_position'];
84    }
85
86    /**
87    * Returns the row for a given group, if the group exists.
88    *
89    * @param    int        $group_id    group_id of the group to be selected
90    *
91    * @return    array            Data row of the group
92    * @throws exception
93    */
94    public function get_group_values($group_id)
95    {
96        // The join is required to ensure that the group itself exists
97        $sql = 'SELECT *
98            FROM ' . GROUPS_TABLE . ' g
99            LEFT JOIN ' . TEAMPAGE_TABLE . ' t
100                ON (t.group_id = g.group_id)
101            WHERE g.group_id = ' . (int) $group_id;
102        $result = $this->db->sql_query($sql);
103        $row = $this->db->sql_fetchrow($result);
104        $this->db->sql_freeresult($result);
105
106        if ($row === false)
107        {
108            // Group not found.
109            throw new exception('NO_GROUP');
110        }
111
112        return $row;
113    }
114
115    /**
116    * Returns the teampage position for a given teampage item, if the item exists.
117    *
118    * @param    int        $teampage_id    Teampage_id of the selected item
119    *
120    * @return    int            Teampage position of the item
121    * @throws exception
122    */
123    public function get_teampage_value($teampage_id)
124    {
125        $sql = 'SELECT teampage_position
126            FROM ' . TEAMPAGE_TABLE . '
127            WHERE teampage_id = ' . (int) $teampage_id;
128        $result = $this->db->sql_query($sql);
129        $current_value = $this->db->sql_fetchfield('teampage_position');
130        $this->db->sql_freeresult($result);
131
132        if ($current_value === false)
133        {
134            // Group not found.
135            throw new exception('NO_GROUP');
136        }
137
138        return (int) $current_value;
139    }
140
141    /**
142    * Returns the teampage row for a given teampage item, if the item exists.
143    *
144    * @param    int        $teampage_id    Teampage_id of the selected item
145    *
146    * @return    array            Teampage row of the item
147     * @throws exception
148    */
149    public function get_teampage_values($teampage_id)
150    {
151        $sql = 'SELECT teampage_position, teampage_parent
152            FROM ' . TEAMPAGE_TABLE . '
153            WHERE teampage_id = ' . (int) $teampage_id;
154        $result = $this->db->sql_query($sql);
155        $row = $this->db->sql_fetchrow($result);
156        $this->db->sql_freeresult($result);
157
158        if ($row === false)
159        {
160            // Group not found.
161            throw new exception('NO_GROUP');
162        }
163
164        return $row;
165    }
166
167
168    /**
169    * {@inheritDoc}
170    */
171    public function get_group_count()
172    {
173        $sql = 'SELECT teampage_position
174            FROM ' . TEAMPAGE_TABLE . '
175            ORDER BY teampage_position DESC';
176        $result = $this->db->sql_query_limit($sql, 1);
177        $group_count = (int) $this->db->sql_fetchfield('teampage_position');
178        $this->db->sql_freeresult($result);
179
180        return $group_count;
181    }
182
183    /**
184    * {@inheritDoc}
185    */
186    public function add_group($group_id)
187    {
188        return $this->add_group_teampage($group_id, self::NO_PARENT);
189    }
190
191    /**
192    * Adds a group by group_id
193    *
194    * @param    int        $group_id    group_id of the group to be added
195    * @param    int        $parent_id    Teampage ID of the parent item
196    * @return    bool        True if the group was added successfully
197    */
198    public function add_group_teampage($group_id, $parent_id)
199    {
200        $current_value = $this->get_group_value($group_id);
201
202        if ($current_value == self::GROUP_DISABLED)
203        {
204            if ($parent_id != self::NO_PARENT)
205            {
206                // Check, whether the given parent is a category
207                $sql = 'SELECT teampage_id
208                    FROM ' . TEAMPAGE_TABLE . '
209                    WHERE group_id = 0
210                        AND teampage_id = ' . (int) $parent_id;
211                $result = $this->db->sql_query_limit($sql, 1);
212                $parent_is_category = (bool) $this->db->sql_fetchfield('teampage_id');
213                $this->db->sql_freeresult($result);
214
215                if ($parent_is_category)
216                {
217                    // Get value of last child from this parent and add group there
218                    $sql = 'SELECT teampage_position
219                        FROM ' . TEAMPAGE_TABLE . '
220                        WHERE teampage_parent = ' . (int) $parent_id . '
221                            OR teampage_id = ' . (int) $parent_id . '
222                        ORDER BY teampage_position DESC';
223                    $result = $this->db->sql_query_limit($sql, 1);
224                    $new_position = (int) $this->db->sql_fetchfield('teampage_position');
225                    $this->db->sql_freeresult($result);
226
227                    $sql = 'UPDATE ' . TEAMPAGE_TABLE . '
228                        SET teampage_position = teampage_position + 1
229                        WHERE teampage_position > ' . $new_position;
230                    $this->db->sql_query($sql);
231                }
232            }
233            else
234            {
235                // Add group at the end
236                $new_position = $this->get_group_count();
237            }
238
239            $sql_ary = array(
240                'group_id'            => $group_id,
241                'teampage_position'    => $new_position + 1,
242                'teampage_parent'    => $parent_id,
243            );
244
245            $sql = 'INSERT INTO ' . TEAMPAGE_TABLE . ' ' . $this->db->sql_build_array('INSERT', $sql_ary);
246            $this->db->sql_query($sql);
247
248            $this->cache->destroy('sql', TEAMPAGE_TABLE);
249            return true;
250        }
251
252        $this->cache->destroy('sql', TEAMPAGE_TABLE);
253        return false;
254    }
255
256    /**
257    * Adds a new category
258    *
259    * @param    string    $category_name    Name of the category to be added
260    * @return    bool        True if the category was added successfully
261    */
262    public function add_category_teampage($category_name)
263    {
264        if ($category_name === '')
265        {
266            return false;
267        }
268
269        $num_entries = $this->get_group_count();
270
271        $sql_ary = array(
272            'group_id'            => 0,
273            'teampage_position'    => $num_entries + 1,
274            'teampage_parent'    => 0,
275            'teampage_name'        => truncate_string($category_name, 255, 255),
276        );
277
278        $sql = 'INSERT INTO ' . TEAMPAGE_TABLE . ' ' . $this->db->sql_build_array('INSERT', $sql_ary);
279        $this->db->sql_query($sql);
280
281        $this->cache->destroy('sql', TEAMPAGE_TABLE);
282        return true;
283    }
284
285    /**
286    * Deletes a group from the list and closes the gap in the position list.
287    *
288    * @param    int        $group_id        group_id of the group to be deleted
289    * @param    bool    $skip_group        Skip setting the value for this group, to save the query, when you need to update it anyway.
290    * @return    bool        True if the group was deleted successfully
291    */
292    public function delete_group($group_id, $skip_group = false)
293    {
294        $current_value = $this->get_group_value($group_id);
295
296        if ($current_value != self::GROUP_DISABLED)
297        {
298            $sql = 'UPDATE ' . TEAMPAGE_TABLE . '
299                SET teampage_position = teampage_position - 1
300                WHERE teampage_position > ' . $current_value;
301            $this->db->sql_query($sql);
302
303            $sql = 'DELETE FROM ' . TEAMPAGE_TABLE . '
304                WHERE group_id = ' . $group_id;
305            $this->db->sql_query($sql);
306
307            $this->cache->destroy('sql', TEAMPAGE_TABLE);
308            return true;
309        }
310
311        $this->cache->destroy('sql', TEAMPAGE_TABLE);
312        return false;
313    }
314
315    /**
316    * Deletes an item from the list and closes the gap in the position list.
317    *
318    * @param    int        $teampage_id    teampage_id of the item to be deleted
319    * @param    bool    $skip_group        Skip setting the group to GROUP_DISABLED, to save the query, when you need to update it anyway.
320    * @return    bool        True if the item was deleted successfully
321    */
322    public function delete_teampage($teampage_id, $skip_group = false)
323    {
324        $current_value = $this->get_teampage_value($teampage_id);
325
326        if ($current_value != self::GROUP_DISABLED)
327        {
328            $sql = 'DELETE FROM ' . TEAMPAGE_TABLE . '
329                WHERE teampage_id = ' . $teampage_id . '
330                    OR teampage_parent = ' . $teampage_id;
331            $this->db->sql_query($sql);
332
333            $delta = (int) $this->db->sql_affectedrows();
334
335            $sql = 'UPDATE ' . TEAMPAGE_TABLE . '
336                SET teampage_position = teampage_position - ' . $delta . '
337                WHERE teampage_position > ' . $current_value;
338            $this->db->sql_query($sql);
339
340            $this->cache->destroy('sql', TEAMPAGE_TABLE);
341            return true;
342        }
343
344        $this->cache->destroy('sql', TEAMPAGE_TABLE);
345        return false;
346    }
347
348    /**
349    * {@inheritDoc}
350    */
351    public function move_up($group_id)
352    {
353        return $this->move($group_id, 1);
354    }
355
356    /**
357    * Moves an item up by teampage_id
358    *
359    * @param    int        $teampage_id    teampage_id of the item to be move
360    * @return    bool        True if the group was moved successfully
361    */
362    public function move_up_teampage($teampage_id)
363    {
364        return $this->move_teampage($teampage_id, 1);
365    }
366
367    /**
368    * {@inheritDoc}
369    */
370    public function move_down($group_id)
371    {
372        return $this->move($group_id, -1);
373    }
374
375    /**
376    * Moves an item down by teampage_id
377    *
378    * @param    int        $teampage_id    teampage_id of the item to be moved
379    * @return    bool        True if the group was moved successfully
380    */
381    public function move_down_teampage($teampage_id)
382    {
383        return $this->move_teampage($teampage_id, -1);
384    }
385
386    /**
387    * {@inheritDoc}
388    */
389    public function move($group_id, $delta)
390    {
391        $delta = (int) $delta;
392        if (!$delta)
393        {
394            return false;
395        }
396
397        $move_up = ($delta > 0) ? true : false;
398        $data = $this->get_group_values($group_id);
399
400        $current_value = (int) $data['teampage_position'];
401        if ($current_value != self::GROUP_DISABLED)
402        {
403            $this->db->sql_transaction('begin');
404
405            if (!$move_up && $data['teampage_parent'] == self::NO_PARENT)
406            {
407                // If we move items down, we need to grab the one sibling more,
408                // so we do not ignore the children of the previous sibling.
409                // We will remove the additional sibling later on.
410                $delta = abs($delta) + 1;
411            }
412
413            $sql = 'SELECT teampage_position
414                FROM ' . TEAMPAGE_TABLE . '
415                WHERE teampage_parent = ' . (int) $data['teampage_parent'] . '
416                    AND teampage_position' . (($move_up) ? ' < ' : ' > ') . $current_value . '
417                ORDER BY teampage_position' . (($move_up) ? ' DESC' : ' ASC');
418            $result = $this->db->sql_query_limit($sql, $delta);
419
420            $sibling_count = 0;
421            $sibling_limit = $delta;
422
423            // Reset the delta, as we recalculate the new real delta
424            $delta = 0;
425            while ($row = $this->db->sql_fetchrow($result))
426            {
427                $sibling_count++;
428                $delta = $current_value - $row['teampage_position'];
429
430                if (!$move_up && $data['teampage_parent'] == self::NO_PARENT && $sibling_count == $sibling_limit)
431                {
432                    // Remove the additional sibling we added previously
433                    $delta++;
434                }
435            }
436            $this->db->sql_freeresult($result);
437
438            if ($delta)
439            {
440                // First we move all items between our current value and the target value up/down 1,
441                // so we have a gap for our item to move.
442                $sql = 'UPDATE ' . TEAMPAGE_TABLE . '
443                    SET teampage_position = teampage_position' . (($move_up) ? ' + 1' : ' - 1') . '
444                    WHERE teampage_position' . (($move_up) ? ' >= ' : ' <= ') . ($current_value - $delta) . '
445                        AND teampage_position' . (($move_up) ? ' < ' : ' > ') . $current_value;
446                $this->db->sql_query($sql);
447
448                // And now finally, when we moved some other items and built a gap,
449                // we can move the desired item to it.
450                $sql = 'UPDATE ' . TEAMPAGE_TABLE . '
451                    SET teampage_position = teampage_position ' . (($move_up) ? ' - ' : ' + ') . abs($delta) . '
452                    WHERE group_id = ' . (int) $group_id;
453                $this->db->sql_query($sql);
454
455                $this->db->sql_transaction('commit');
456                $this->cache->destroy('sql', TEAMPAGE_TABLE);
457
458                return true;
459            }
460
461            $this->db->sql_transaction('commit');
462        }
463
464        $this->cache->destroy('sql', TEAMPAGE_TABLE);
465        return false;
466    }
467
468    /**
469    * Moves an item up/down
470    *
471    * @param    int        $teampage_id    teampage_id of the item to be moved
472    * @param    int        $delta        number of steps:
473    *                                - positive = move up
474    *                                - negative = move down
475    * @return    bool        True if the group was moved successfully
476    */
477    public function move_teampage($teampage_id, $delta)
478    {
479        $delta = (int) $delta;
480        if (!$delta)
481        {
482            return false;
483        }
484
485        $move_up = ($delta > 0) ? true : false;
486        $data = $this->get_teampage_values($teampage_id);
487
488        $current_value = (int) $data['teampage_position'];
489        if ($current_value != self::GROUP_DISABLED)
490        {
491            $this->db->sql_transaction('begin');
492
493            if (!$move_up && $data['teampage_parent'] == self::NO_PARENT)
494            {
495                // If we move items down, we need to grab the one sibling more,
496                // so we do not ignore the children of the previous sibling.
497                // We will remove the additional sibling later on.
498                $delta = abs($delta) + 1;
499            }
500
501            $sql = 'SELECT teampage_id, teampage_position
502                FROM ' . TEAMPAGE_TABLE . '
503                WHERE teampage_parent = ' . (int) $data['teampage_parent'] . '
504                    AND teampage_position' . (($move_up) ? ' < ' : ' > ') . $current_value . '
505                ORDER BY teampage_position' . (($move_up) ? ' DESC' : ' ASC');
506            $result = $this->db->sql_query_limit($sql, $delta);
507
508            $sibling_count = 0;
509            $sibling_limit = $delta;
510
511            // Reset the delta, as we recalculate the new real delta
512            $delta = 0;
513            while ($row = $this->db->sql_fetchrow($result))
514            {
515                $sibling_count++;
516                $delta = $current_value - $row['teampage_position'];
517
518                // Remove the additional sibling we added previously
519                // But only, if we included it, this is not be the case
520                // when we reached the end of our list
521                if (!$move_up && $data['teampage_parent'] == self::NO_PARENT && $sibling_count == $sibling_limit)
522                {
523                    $delta++;
524                }
525            }
526            $this->db->sql_freeresult($result);
527
528            if ($delta)
529            {
530                $sql = 'SELECT COUNT(teampage_id) as num_items
531                    FROM ' . TEAMPAGE_TABLE . '
532                    WHERE teampage_id = ' . (int) $teampage_id . '
533                        OR teampage_parent = ' . (int) $teampage_id;
534                $result = $this->db->sql_query($sql);
535                $num_items = (int) $this->db->sql_fetchfield('num_items');
536                $this->db->sql_freeresult($result);
537
538                // First we move all items between our current value and the target value up/down 1,
539                // so we have a gap for our item to move.
540                $sql = 'UPDATE ' . TEAMPAGE_TABLE . '
541                    SET teampage_position = teampage_position' . (($move_up) ? ' + ' : ' - ') . $num_items . '
542                    WHERE teampage_position' . (($move_up) ? ' >= ' : ' <= ') . ($current_value - $delta) . '
543                        AND teampage_position' . (($move_up) ? ' < ' : ' > ') . $current_value . '
544                        AND NOT (teampage_id = ' . (int) $teampage_id . '
545                            OR teampage_parent = ' . (int) $teampage_id . ')';
546                $this->db->sql_query($sql);
547
548                $delta = (!$move_up && $data['teampage_parent'] == self::NO_PARENT) ? (abs($delta) - ($num_items - 1)) : abs($delta);
549
550                // And now finally, when we moved some other items and built a gap,
551                // we can move the desired item to it.
552                $sql = 'UPDATE ' . TEAMPAGE_TABLE . '
553                    SET teampage_position = teampage_position ' . (($move_up) ? ' - ' : ' + ') . $delta . '
554                    WHERE teampage_id = ' . (int) $teampage_id . '
555                        OR teampage_parent = ' . (int) $teampage_id;
556                $this->db->sql_query($sql);
557
558                $this->db->sql_transaction('commit');
559                $this->cache->destroy('sql', TEAMPAGE_TABLE);
560
561                return true;
562            }
563
564            $this->db->sql_transaction('commit');
565        }
566
567        $this->cache->destroy('sql', TEAMPAGE_TABLE);
568        return false;
569    }
570
571    /**
572     * Get group type language var
573     *
574     * @param int $group_type group_type from the groups-table
575     *
576     * @return    string        name of the language variable for the given group-type.
577     * @throws exception        If invalid group type is supplied
578     */
579    public static function group_type_language($group_type)
580    {
581        switch ($group_type)
582        {
583            case GROUP_OPEN:
584                return 'GROUP_REQUEST';
585            case GROUP_CLOSED:
586                return 'GROUP_CLOSED';
587            case GROUP_HIDDEN:
588                return 'GROUP_HIDDEN';
589            case GROUP_SPECIAL:
590                return 'GROUP_SPECIAL';
591            case GROUP_FREE:
592                return 'GROUP_OPEN';
593            default:
594                throw new exception('NO_GROUP');
595        }
596    }
597}