Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
23.71% covered (danger)
23.71%
147 / 620
16.67% covered (danger)
16.67%
2 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
auth_admin
23.62% covered (danger)
23.62%
146 / 618
16.67% covered (danger)
16.67%
2 / 12
25049.30
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
5
 get_mask
0.00% covered (danger)
0.00%
0 / 64
0.00% covered (danger)
0.00%
0 / 1
1190
 get_role_mask
94.74% covered (success)
94.74%
18 / 19
0.00% covered (danger)
0.00%
0 / 1
3.00
 display_mask
0.00% covered (danger)
0.00%
0 / 194
0.00% covered (danger)
0.00%
0 / 1
6480
 display_role_mask
0.00% covered (danger)
0.00%
0 / 46
0.00% covered (danger)
0.00%
0 / 1
156
 acl_add_option
95.83% covered (success)
95.83%
46 / 48
0.00% covered (danger)
0.00%
0 / 1
19
 acl_set
100.00% covered (success)
100.00%
65 / 65
100.00% covered (success)
100.00%
1 / 1
19
 acl_set_role
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
90
 acl_delete
0.00% covered (danger)
0.00%
0 / 51
0.00% covered (danger)
0.00%
0 / 1
306
 assign_cat_array
0.00% covered (danger)
0.00%
0 / 41
0.00% covered (danger)
0.00%
0 / 1
552
 build_permission_array
0.00% covered (danger)
0.00%
0 / 27
0.00% covered (danger)
0.00%
0 / 1
110
 ghost_permissions
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
30
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/**
15* @ignore
16*/
17if (!defined('IN_PHPBB'))
18{
19    exit;
20}
21
22/**
23* ACP Permission/Auth class
24*/
25class auth_admin extends \phpbb\auth\auth
26{
27    /**
28    * Init auth settings
29    */
30    function __construct()
31    {
32        global $db, $cache;
33
34        if (($this->acl_options = $cache->get('_acl_options')) === false)
35        {
36            $sql = 'SELECT auth_option_id, auth_option, is_global, is_local
37                FROM ' . ACL_OPTIONS_TABLE . '
38                ORDER BY auth_option_id';
39            $result = $db->sql_query($sql);
40
41            $global = $local = 0;
42            $this->acl_options = array();
43            while ($row = $db->sql_fetchrow($result))
44            {
45                if ($row['is_global'])
46                {
47                    $this->acl_options['global'][$row['auth_option']] = $global++;
48                }
49
50                if ($row['is_local'])
51                {
52                    $this->acl_options['local'][$row['auth_option']] = $local++;
53                }
54
55                $this->acl_options['id'][$row['auth_option']] = (int) $row['auth_option_id'];
56                $this->acl_options['option'][(int) $row['auth_option_id']] = $row['auth_option'];
57            }
58            $db->sql_freeresult($result);
59
60            $cache->put('_acl_options', $this->acl_options);
61        }
62    }
63
64    /**
65    * Get permission mask
66    * This function only supports getting permissions of one type (for example a_)
67    *
68    * @param set|view $mode defines the permissions we get, view gets effective permissions (checking user AND group permissions), set only gets the user or group permission set alone
69    * @param mixed $user_id user ids to search for (a user_id or a group_id has to be specified at least)
70    * @param mixed $group_id group ids to search for, return group related settings (a user_id or a group_id has to be specified at least)
71    * @param mixed $forum_id forum_ids to search for. Defining a forum id also means getting local settings
72    * @param string $auth_option the auth_option defines the permission setting to look for (a_ for example)
73    * @param local|global $scope the scope defines the permission scope. If local, a forum_id is additionally required
74    * @param ACL_NEVER|ACL_NO|ACL_YES $acl_fill defines the mode those permissions not set are getting filled with
75    */
76    function get_mask($mode, $user_id = false, $group_id = false, $forum_id = false, $auth_option = false, $scope = false, $acl_fill = ACL_NEVER)
77    {
78        global $db, $user;
79
80        $hold_ary = array();
81        $view_user_mask = ($mode == 'view' && $group_id === false) ? true : false;
82
83        if ($auth_option === false || $scope === false)
84        {
85            return array();
86        }
87
88        $acl_user_function = ($mode == 'set') ? 'acl_user_raw_data' : 'acl_raw_data';
89
90        if (!$view_user_mask)
91        {
92            if ($forum_id !== false)
93            {
94                $hold_ary = ($group_id !== false) ? $this->acl_group_raw_data($group_id, $auth_option . '%', $forum_id) : $this->$acl_user_function($user_id, $auth_option . '%', $forum_id);
95            }
96            else
97            {
98                $hold_ary = ($group_id !== false) ? $this->acl_group_raw_data($group_id, $auth_option . '%') : $this->$acl_user_function($user_id, $auth_option . '%', ($scope == 'global') ? 0 : false);
99            }
100        }
101
102        // Make sure hold_ary is filled with every setting (prevents missing forums/users/groups)
103        $ug_id = ($group_id !== false) ? ((!is_array($group_id)) ? array($group_id) : $group_id) : ((!is_array($user_id)) ? array($user_id) : $user_id);
104        $forum_ids = ($forum_id !== false) ? ((!is_array($forum_id)) ? array($forum_id) : $forum_id) : (($scope == 'global') ? array(0) : array());
105
106        // Only those options we need
107        $compare_options = array_diff(preg_replace('/^((?!' . $auth_option . ').+)|(' . $auth_option . ')$/', '', array_keys($this->acl_options[$scope])), array(''));
108
109        // If forum_ids is false and the scope is local we actually want to have all forums within the array
110        if ($scope == 'local' && !count($forum_ids))
111        {
112            $sql = 'SELECT forum_id
113                FROM ' . FORUMS_TABLE;
114            $result = $db->sql_query($sql, 120);
115
116            while ($row = $db->sql_fetchrow($result))
117            {
118                $forum_ids[] = (int) $row['forum_id'];
119            }
120            $db->sql_freeresult($result);
121        }
122
123        if ($view_user_mask)
124        {
125            $auth2 = null;
126
127            $sql = 'SELECT user_id, user_permissions, user_type
128                FROM ' . USERS_TABLE . '
129                WHERE ' . $db->sql_in_set('user_id', $ug_id);
130            $result = $db->sql_query($sql);
131
132            while ($userdata = $db->sql_fetchrow($result))
133            {
134                if ($user->data['user_id'] != $userdata['user_id'])
135                {
136                    $auth2 = new \phpbb\auth\auth();
137                    $auth2->acl($userdata);
138                }
139                else
140                {
141                    global $auth;
142                    $auth2 = &$auth;
143                }
144
145                $hold_ary[$userdata['user_id']] = array();
146                foreach ($forum_ids as $f_id)
147                {
148                    $hold_ary[$userdata['user_id']][$f_id] = array();
149                    foreach ($compare_options as $option)
150                    {
151                        $hold_ary[$userdata['user_id']][$f_id][$option] = $auth2->acl_get($option, $f_id);
152                    }
153                }
154            }
155            $db->sql_freeresult($result);
156
157            unset($userdata);
158            unset($auth2);
159        }
160
161        foreach ($ug_id as $_id)
162        {
163            if (!isset($hold_ary[$_id]))
164            {
165                $hold_ary[$_id] = array();
166            }
167
168            foreach ($forum_ids as $f_id)
169            {
170                if (!isset($hold_ary[$_id][$f_id]))
171                {
172                    $hold_ary[$_id][$f_id] = array();
173                }
174            }
175        }
176
177        // Now, we need to fill the gaps with $acl_fill. ;)
178
179        // Now switch back to keys
180        if (count($compare_options))
181        {
182            $compare_options = array_combine($compare_options, array_fill(1, count($compare_options), $acl_fill));
183        }
184
185        // Defining the user-function here to save some memory
186        $return_acl_fill = function () use ($acl_fill)
187        {
188            return $acl_fill;
189        };
190
191        // Actually fill the gaps
192        if (count($hold_ary))
193        {
194            foreach ($hold_ary as $ug_id => $row)
195            {
196                foreach ($row as $id => $options)
197                {
198                    // Do not include the global auth_option
199                    unset($options[$auth_option]);
200
201                    // Not a "fine" solution, but at all it's a 1-dimensional
202                    // array_diff_key function filling the resulting array values with zeros
203                    // The differences get merged into $hold_ary (all permissions having $acl_fill set)
204                    $hold_ary[$ug_id][$id] = array_merge($options,
205
206                        array_map($return_acl_fill,
207                            array_flip(
208                                array_diff(
209                                    array_keys($compare_options), array_keys($options)
210                                )
211                            )
212                        )
213                    );
214                }
215            }
216        }
217        else
218        {
219            $hold_ary[($group_id !== false) ? $group_id : $user_id][(int) $forum_id] = $compare_options;
220        }
221
222        return $hold_ary;
223    }
224
225    /**
226    * Get permission mask for roles
227    * This function only supports getting masks for one role
228    */
229    function get_role_mask($role_id)
230    {
231        global $db;
232
233        $hold_ary = array();
234
235        // Get users having this role set...
236        $sql = 'SELECT user_id, forum_id
237            FROM ' . ACL_USERS_TABLE . '
238            WHERE auth_role_id = ' . $role_id . '
239            ORDER BY forum_id';
240        $result = $db->sql_query($sql);
241
242        while ($row = $db->sql_fetchrow($result))
243        {
244            $hold_ary[$row['forum_id']]['users'][] = $row['user_id'];
245        }
246        $db->sql_freeresult($result);
247
248        // Now grab groups...
249        $sql = 'SELECT group_id, forum_id
250            FROM ' . ACL_GROUPS_TABLE . '
251            WHERE auth_role_id = ' . $role_id . '
252            ORDER BY forum_id';
253        $result = $db->sql_query($sql);
254
255        while ($row = $db->sql_fetchrow($result))
256        {
257            $hold_ary[$row['forum_id']]['groups'][] = $row['group_id'];
258        }
259        $db->sql_freeresult($result);
260
261        return $hold_ary;
262    }
263
264    /**
265    * Display permission mask (assign to template)
266    */
267    function display_mask($mode, $permission_type, &$hold_ary, $user_mode = 'user', $local = false, $group_display = true)
268    {
269        global $template, $user, $db, $phpbb_container;
270
271        /* @var $phpbb_permissions \phpbb\permissions */
272        $phpbb_permissions = $phpbb_container->get('acl.permissions');
273
274        /** @var \phpbb\group\helper $group_helper */
275        $group_helper = $phpbb_container->get('group_helper');
276
277        // Define names for template loops, might be able to be set
278        $tpl_pmask = 'p_mask';
279        $tpl_fmask = 'f_mask';
280        $tpl_category = 'category';
281        $tpl_mask = 'mask';
282
283        $l_acl_type = $phpbb_permissions->get_type_lang($permission_type, (($local) ? 'local' : 'global'));
284
285        // Allow trace for viewing permissions and in user mode
286        $show_trace = ($mode == 'view' && $user_mode == 'user') ? true : false;
287
288        // Get names
289        if ($user_mode == 'user')
290        {
291            $sql = 'SELECT user_id as ug_id, username as ug_name
292                FROM ' . USERS_TABLE . '
293                WHERE ' . $db->sql_in_set('user_id', array_keys($hold_ary)) . '
294                ORDER BY username_clean ASC';
295        }
296        else
297        {
298            $sql = 'SELECT group_id as ug_id, group_name as ug_name, group_type
299                FROM ' . GROUPS_TABLE . '
300                WHERE ' . $db->sql_in_set('group_id', array_keys($hold_ary)) . '
301                ORDER BY group_type DESC, group_name ASC';
302        }
303        $result = $db->sql_query($sql);
304
305        $ug_names_ary = array();
306        while ($row = $db->sql_fetchrow($result))
307        {
308            $ug_names_ary[$row['ug_id']] = ($user_mode == 'user') ? $row['ug_name'] : $group_helper->get_name($row['ug_name']);
309        }
310        $db->sql_freeresult($result);
311
312        // Get used forums
313        $forum_ids = array();
314        foreach ($hold_ary as $ug_id => $row)
315        {
316            $forum_ids = array_merge($forum_ids, array_keys($row));
317        }
318        $forum_ids = array_unique($forum_ids);
319
320        $forum_names_ary = array();
321        if ($local)
322        {
323            $forum_names_ary = make_forum_select(false, false, true, false, false, false, true);
324
325            // Remove the disabled ones, since we do not create an option field here...
326            foreach ($forum_names_ary as $key => $value)
327            {
328                if (!$value['disabled'])
329                {
330                    continue;
331                }
332                unset($forum_names_ary[$key]);
333            }
334        }
335        else
336        {
337            $forum_names_ary[0] = $l_acl_type;
338        }
339
340        // Get available roles
341        $sql = 'SELECT *
342            FROM ' . ACL_ROLES_TABLE . "
343            WHERE role_type = '" . $db->sql_escape($permission_type) . "'
344            ORDER BY role_order ASC";
345        $result = $db->sql_query($sql);
346
347        $roles = array();
348        while ($row = $db->sql_fetchrow($result))
349        {
350            $roles[$row['role_id']] = $row;
351        }
352        $db->sql_freeresult($result);
353
354        $cur_roles = $this->acl_role_data($user_mode, $permission_type, array_keys($hold_ary));
355
356        // Build js roles array (role data assignments)
357        $s_role_js_array = '';
358
359        if (count($roles))
360        {
361            $s_role_js_array = array();
362
363            // Make sure every role (even if empty) has its array defined
364            foreach ($roles as $_role_id => $null)
365            {
366                $s_role_js_array[$_role_id] = "\n" . 'role_options[' . $_role_id . '] = new Array();' . "\n";
367            }
368
369            $sql = 'SELECT r.role_id, o.auth_option, r.auth_setting
370                FROM ' . ACL_ROLES_DATA_TABLE . ' r, ' . ACL_OPTIONS_TABLE . ' o
371                WHERE o.auth_option_id = r.auth_option_id
372                    AND ' . $db->sql_in_set('r.role_id', array_keys($roles));
373            $result = $db->sql_query($sql);
374
375            while ($row = $db->sql_fetchrow($result))
376            {
377                $flag = substr($row['auth_option'], 0, strpos($row['auth_option'], '_') + 1);
378                if ($flag == $row['auth_option'])
379                {
380                    continue;
381                }
382
383                $s_role_js_array[$row['role_id']] .= 'role_options[' . $row['role_id'] . '][\'' . addslashes($row['auth_option']) . '\'] = ' . $row['auth_setting'] . '; ';
384            }
385            $db->sql_freeresult($result);
386
387            $s_role_js_array = implode('', $s_role_js_array);
388        }
389
390        $template->assign_var('S_ROLE_JS_ARRAY', $s_role_js_array);
391        unset($s_role_js_array);
392
393        // Now obtain memberships
394        $user_groups_default = $user_groups_custom = array();
395        if ($user_mode == 'user' && $group_display)
396        {
397            $sql = 'SELECT group_id, group_name, group_type
398                FROM ' . GROUPS_TABLE . '
399                ORDER BY group_type DESC, group_name ASC';
400            $result = $db->sql_query($sql);
401
402            $groups = array();
403            while ($row = $db->sql_fetchrow($result))
404            {
405                $groups[$row['group_id']] = $row;
406            }
407            $db->sql_freeresult($result);
408
409            $memberships = group_memberships(false, array_keys($hold_ary), false);
410
411            // User is not a member of any group? Bad admin, bad bad admin...
412            if ($memberships)
413            {
414                foreach ($memberships as $row)
415                {
416                    $user_groups_default[$row['user_id']][] = $group_helper->get_name($groups[$row['group_id']]['group_name']);
417                }
418            }
419            unset($memberships, $groups);
420        }
421
422        // If we only have one forum id to display or being in local mode and more than one user/group to display,
423        // we switch the complete interface to group by user/usergroup instead of grouping by forum
424        // To achieve this, we need to switch the array a bit
425        if (count($forum_ids) == 1 || ($local && count($ug_names_ary) > 1))
426        {
427            $hold_ary_temp = $hold_ary;
428            $hold_ary = array();
429            foreach ($hold_ary_temp as $ug_id => $row)
430            {
431                foreach ($forum_names_ary as $forum_id => $forum_row)
432                {
433                    if (isset($row[$forum_id]))
434                    {
435                        $hold_ary[$forum_id][$ug_id] = $row[$forum_id];
436                    }
437                }
438            }
439            unset($hold_ary_temp);
440
441            foreach ($hold_ary as $forum_id => $forum_array)
442            {
443                $content_array = $categories = array();
444                $this->build_permission_array($hold_ary[$forum_id], $content_array, $categories, array_keys($ug_names_ary));
445
446                $template->assign_block_vars($tpl_pmask, array(
447                    'NAME'            => ($forum_id == 0) ? $forum_names_ary[0] : $forum_names_ary[$forum_id]['forum_name'],
448                    'PADDING'        => ($forum_id == 0) ? '' : $forum_names_ary[$forum_id]['padding'],
449
450                    'CATEGORIES'    => implode('</th><th>', $categories),
451
452                    'L_ACL_TYPE'    => $l_acl_type,
453
454                    'S_LOCAL'        => ($local) ? true : false,
455                    'S_GLOBAL'        => (!$local) ? true : false,
456                    'S_NUM_CATS'    => count($categories),
457                    'S_VIEW'        => ($mode == 'view') ? true : false,
458                    'S_NUM_OBJECTS'    => count($content_array),
459                    'S_USER_MODE'    => ($user_mode == 'user') ? true : false,
460                    'S_GROUP_MODE'    => ($user_mode == 'group') ? true : false)
461                );
462
463                foreach ($content_array as $ug_id => $ug_array)
464                {
465                    // Build role dropdown options
466                    $current_role_id = (isset($cur_roles[$ug_id][$forum_id])) ? $cur_roles[$ug_id][$forum_id] : 0;
467
468                    $role_options = array();
469
470                    $s_role_options = '';
471                    $current_role_id = (isset($cur_roles[$ug_id][$forum_id])) ? $cur_roles[$ug_id][$forum_id] : 0;
472
473                    foreach ($roles as $role_id => $role_row)
474                    {
475                        $role_description = (!empty($user->lang[$role_row['role_description']])) ? $user->lang[$role_row['role_description']] : nl2br($role_row['role_description']);
476                        $role_name = (!empty($user->lang[$role_row['role_name']])) ? $user->lang[$role_row['role_name']] : $role_row['role_name'];
477
478                        $title = ($role_description) ? ' title="' . $role_description . '"' : '';
479                        $s_role_options .= '<option value="' . $role_id . '"' . (($role_id == $current_role_id) ? ' selected="selected"' : '') . $title . '>' . $role_name . '</option>';
480
481                        $role_options[] = array(
482                            'ID'    => $role_id,
483                            'ROLE_NAME'    => $role_name,
484                            'TITLE'        => $role_description,
485                            'SELECTED'    => $role_id == $current_role_id,
486                        );
487                    }
488
489                    if ($s_role_options)
490                    {
491                        $s_role_options = '<option value="0"' . ((!$current_role_id) ? ' selected="selected"' : '') . ' title="' . htmlspecialchars($user->lang['NO_ROLE_ASSIGNED_EXPLAIN'], ENT_COMPAT) . '">' . $user->lang['NO_ROLE_ASSIGNED'] . '</option>' . $s_role_options;
492                    }
493
494                    if (!$current_role_id && $mode != 'view')
495                    {
496                        $s_custom_permissions = false;
497
498                        foreach ($ug_array as $key => $value)
499                        {
500                            if ($value['S_NEVER'] || $value['S_YES'])
501                            {
502                                $s_custom_permissions = true;
503                                break;
504                            }
505                        }
506                    }
507                    else
508                    {
509                        $s_custom_permissions = false;
510                    }
511
512                    $template->assign_block_vars($tpl_pmask . '.' . $tpl_fmask, array(
513                        'NAME'                => $ug_names_ary[$ug_id],
514                        'UG_ID'                => $ug_id,
515                        'S_ROLE_OPTIONS'    => $s_role_options,
516                        'S_CUSTOM'            => $s_custom_permissions,
517                        'FORUM_ID'            => $forum_id,
518                        'S_ROLE_ID'            => $current_role_id,
519                    ));
520
521                    $template->assign_block_vars_array($tpl_pmask . '.' . $tpl_fmask . '.role_options', $role_options);
522
523                    $this->assign_cat_array($ug_array, $tpl_pmask . '.' . $tpl_fmask . '.' . $tpl_category, $tpl_mask, $ug_id, $forum_id, ($mode == 'view'), $show_trace);
524
525                    unset($content_array[$ug_id]);
526                }
527
528                unset($hold_ary[$forum_id]);
529            }
530        }
531        else
532        {
533            foreach ($ug_names_ary as $ug_id => $ug_name)
534            {
535                if (!isset($hold_ary[$ug_id]))
536                {
537                    continue;
538                }
539
540                $content_array = $categories = array();
541                $this->build_permission_array($hold_ary[$ug_id], $content_array, $categories, array_keys($forum_names_ary));
542
543                $template->assign_block_vars($tpl_pmask, array(
544                    'NAME'            => $ug_name,
545                    'CATEGORIES'    => implode('</th><th>', $categories),
546
547                    'USER_GROUPS_DEFAULT'    => ($user_mode == 'user' && isset($user_groups_default[$ug_id]) && count($user_groups_default[$ug_id])) ? implode($user->lang['COMMA_SEPARATOR'], $user_groups_default[$ug_id]) : '',
548                    'USER_GROUPS_CUSTOM'    => ($user_mode == 'user' && isset($user_groups_custom[$ug_id]) && count($user_groups_custom[$ug_id])) ? implode($user->lang['COMMA_SEPARATOR'], $user_groups_custom[$ug_id]) : '',
549                    'L_ACL_TYPE'            => $l_acl_type,
550
551                    'S_LOCAL'        => ($local) ? true : false,
552                    'S_GLOBAL'        => (!$local) ? true : false,
553                    'S_NUM_CATS'    => count($categories),
554                    'S_VIEW'        => ($mode == 'view') ? true : false,
555                    'S_NUM_OBJECTS'    => count($content_array),
556                    'S_USER_MODE'    => ($user_mode == 'user') ? true : false,
557                    'S_GROUP_MODE'    => ($user_mode == 'group') ? true : false)
558                );
559
560                foreach ($content_array as $forum_id => $forum_array)
561                {
562                    // Build role dropdown options
563                    $current_role_id = (isset($cur_roles[$ug_id][$forum_id])) ? $cur_roles[$ug_id][$forum_id] : 0;
564
565                    $role_options = array();
566
567                    $current_role_id = (isset($cur_roles[$ug_id][$forum_id])) ? $cur_roles[$ug_id][$forum_id] : 0;
568                    $s_role_options = '';
569
570                    foreach ($roles as $role_id => $role_row)
571                    {
572                        $role_description = (!empty($user->lang[$role_row['role_description']])) ? $user->lang[$role_row['role_description']] : nl2br($role_row['role_description']);
573                        $role_name = (!empty($user->lang[$role_row['role_name']])) ? $user->lang[$role_row['role_name']] : $role_row['role_name'];
574
575                        $title = ($role_description) ? ' title="' . $role_description . '"' : '';
576                        $s_role_options .= '<option value="' . $role_id . '"' . (($role_id == $current_role_id) ? ' selected="selected"' : '') . $title . '>' . $role_name . '</option>';
577
578                        $role_options[] = array(
579                            'ID'    => $role_id,
580                            'ROLE_NAME'    => $role_name,
581                            'TITLE'        => $role_description,
582                            'SELECTED'    => $role_id == $current_role_id,
583                        );
584                    }
585
586                    if ($s_role_options)
587                    {
588                        $s_role_options = '<option value="0"' . ((!$current_role_id) ? ' selected="selected"' : '') . ' title="' . htmlspecialchars($user->lang['NO_ROLE_ASSIGNED_EXPLAIN'], ENT_COMPAT) . '">' . $user->lang['NO_ROLE_ASSIGNED'] . '</option>' . $s_role_options;
589                    }
590
591                    if (!$current_role_id && $mode != 'view')
592                    {
593                        $s_custom_permissions = false;
594
595                        foreach ($forum_array as $key => $value)
596                        {
597                            if ($value['S_NEVER'] || $value['S_YES'])
598                            {
599                                $s_custom_permissions = true;
600                                break;
601                            }
602                        }
603                    }
604                    else
605                    {
606                        $s_custom_permissions = false;
607                    }
608
609                    $template->assign_block_vars($tpl_pmask . '.' . $tpl_fmask, array(
610                        'NAME'                => ($forum_id == 0) ? $forum_names_ary[0] : $forum_names_ary[$forum_id]['forum_name'],
611                        'PADDING'            => ($forum_id == 0) ? '' : $forum_names_ary[$forum_id]['padding'],
612                        'S_CUSTOM'            => $s_custom_permissions,
613                        'UG_ID'                => $ug_id,
614                        'S_ROLE_OPTIONS'    => $s_role_options,
615                        'FORUM_ID'            => $forum_id)
616                    );
617
618                    $template->assign_block_vars_array($tpl_pmask . '.' . $tpl_fmask . '.role_options', $role_options);
619
620                    $this->assign_cat_array($forum_array, $tpl_pmask . '.' . $tpl_fmask . '.' . $tpl_category, $tpl_mask, $ug_id, $forum_id, ($mode == 'view'), $show_trace);
621                }
622
623                unset($hold_ary[$ug_id], $ug_names_ary[$ug_id]);
624            }
625        }
626    }
627
628    /**
629    * Display permission mask for roles
630    */
631    function display_role_mask(&$hold_ary)
632    {
633        global $db, $template, $user, $phpbb_root_path, $phpEx;
634        global $phpbb_container;
635
636        if (!count($hold_ary))
637        {
638            return;
639        }
640
641        /** @var \phpbb\group\helper $group_helper */
642        $group_helper = $phpbb_container->get('group_helper');
643
644        // Get forum names
645        $sql = 'SELECT forum_id, forum_name
646            FROM ' . FORUMS_TABLE . '
647            WHERE ' . $db->sql_in_set('forum_id', array_keys($hold_ary)) . '
648            ORDER BY left_id';
649        $result = $db->sql_query($sql);
650
651        // If the role is used globally, then reflect that
652        $forum_names = (isset($hold_ary[0])) ? array(0 => '') : array();
653        while ($row = $db->sql_fetchrow($result))
654        {
655            $forum_names[$row['forum_id']] = $row['forum_name'];
656        }
657        $db->sql_freeresult($result);
658
659        foreach ($forum_names as $forum_id => $forum_name)
660        {
661            $auth_ary = $hold_ary[$forum_id];
662
663            $template->assign_block_vars('role_mask', array(
664                'NAME'                => ($forum_id == 0) ? $user->lang['GLOBAL_MASK'] : $forum_name,
665                'FORUM_ID'            => $forum_id)
666            );
667
668            if (isset($auth_ary['users']) && count($auth_ary['users']))
669            {
670                $sql = 'SELECT user_id, username
671                    FROM ' . USERS_TABLE . '
672                    WHERE ' . $db->sql_in_set('user_id', $auth_ary['users']) . '
673                    ORDER BY username_clean ASC';
674                $result = $db->sql_query($sql);
675
676                while ($row = $db->sql_fetchrow($result))
677                {
678                    $template->assign_block_vars('role_mask.users', array(
679                        'USER_ID'        => $row['user_id'],
680                        'USERNAME'        => get_username_string('username', $row['user_id'], $row['username']),
681                        'U_PROFILE'        => get_username_string('profile', $row['user_id'], $row['username']),
682                    ));
683                }
684                $db->sql_freeresult($result);
685            }
686
687            if (isset($auth_ary['groups']) && count($auth_ary['groups']))
688            {
689                $sql = 'SELECT group_id, group_name, group_type
690                    FROM ' . GROUPS_TABLE . '
691                    WHERE ' . $db->sql_in_set('group_id', $auth_ary['groups']) . '
692                    ORDER BY group_type ASC, group_name';
693                $result = $db->sql_query($sql);
694
695                while ($row = $db->sql_fetchrow($result))
696                {
697                    $template->assign_block_vars('role_mask.groups', array(
698                        'GROUP_ID'        => $row['group_id'],
699                        'GROUP_NAME'    => $group_helper->get_name($row['group_name']),
700                        'U_PROFILE'        => append_sid("{$phpbb_root_path}memberlist.$phpEx", "mode=group&amp;g={$row['group_id']}"))
701                    );
702                }
703                $db->sql_freeresult($result);
704            }
705        }
706    }
707
708    /**
709    * NOTE: this function is not in use atm
710    * Add a new option to the list ... $options is a hash of form ->
711    * $options = array(
712    *    'local'        => array('option1', 'option2', ...),
713    *    'global'    => array('optionA', 'optionB', ...)
714    * );
715    */
716    function acl_add_option($options)
717    {
718        global $db, $cache;
719
720        if (!is_array($options))
721        {
722            return false;
723        }
724
725        $cur_options = array();
726
727        // Determine current options
728        $sql = 'SELECT auth_option, is_global, is_local
729            FROM ' . ACL_OPTIONS_TABLE . '
730            ORDER BY auth_option_id';
731        $result = $db->sql_query($sql);
732
733        while ($row = $db->sql_fetchrow($result))
734        {
735            $cur_options[$row['auth_option']] = ($row['is_global'] && $row['is_local']) ? 'both' : (($row['is_global']) ? 'global' : 'local');
736        }
737        $db->sql_freeresult($result);
738
739        // Here we need to insert new options ... this requires discovering whether
740        // an options is global, local or both and whether we need to add an permission
741        // set flag (x_)
742        $new_options = array('local' => array(), 'global' => array());
743
744        foreach ($options as $type => $option_ary)
745        {
746            $option_ary = array_unique($option_ary);
747
748            foreach ($option_ary as $option_value)
749            {
750                $new_options[$type][] = $option_value;
751
752                $flag = substr($option_value, 0, strpos($option_value, '_') + 1);
753
754                if (!in_array($flag, $new_options[$type]))
755                {
756                    $new_options[$type][] = $flag;
757                }
758            }
759        }
760        unset($options);
761
762        $options = array();
763        $options['local'] = array_diff($new_options['local'], $new_options['global']);
764        $options['global'] = array_diff($new_options['global'], $new_options['local']);
765        $options['both'] = array_intersect($new_options['local'], $new_options['global']);
766
767        // Now check which options to add/update
768        $add_options = $update_options = array();
769
770        // First local ones...
771        foreach ($options as $type => $option_ary)
772        {
773            foreach ($option_ary as $option)
774            {
775                if (!isset($cur_options[$option]))
776                {
777                    $add_options[] = array(
778                        'auth_option'    => (string) $option,
779                        'is_global'        => ($type == 'global' || $type == 'both') ? 1 : 0,
780                        'is_local'        => ($type == 'local' || $type == 'both') ? 1 : 0
781                    );
782
783                    continue;
784                }
785
786                // Else, update existing entry if it is changed...
787                if ($type === $cur_options[$option])
788                {
789                    continue;
790                }
791
792                // New type is always both:
793                // If is now both, we set both.
794                // If it was global the new one is local and we need to set it to both
795                // If it was local the new one is global and we need to set it to both
796                $update_options[] = $option;
797            }
798        }
799
800        if (!empty($add_options))
801        {
802            $db->sql_multi_insert(ACL_OPTIONS_TABLE, $add_options);
803        }
804
805        if (!empty($update_options))
806        {
807            $sql = 'UPDATE ' . ACL_OPTIONS_TABLE . '
808                SET is_global = 1, is_local = 1
809                WHERE ' . $db->sql_in_set('auth_option', $update_options);
810            $db->sql_query($sql);
811        }
812
813        $cache->destroy('_acl_options');
814        $this->acl_clear_prefetch();
815
816        // Because we just changed the options and also purged the options cache, we instantly update/regenerate it for later calls to succeed.
817        $this->acl_options = array();
818        $this->__construct();
819
820        return true;
821    }
822
823    /**
824    * Set a user or group ACL record
825    */
826    function acl_set($ug_type, $forum_id, $ug_id, $auth, $role_id = 0, $clear_prefetch = true)
827    {
828        global $db;
829
830        // One or more forums
831        if (!is_array($forum_id))
832        {
833            $forum_id = array($forum_id);
834        }
835
836        // One or more users
837        if (!is_array($ug_id))
838        {
839            $ug_id = array($ug_id);
840        }
841
842        $ug_id_sql = $db->sql_in_set($ug_type . '_id', array_map('intval', $ug_id));
843        $forum_sql = $db->sql_in_set('forum_id', array_map('intval', $forum_id));
844
845        // Instead of updating, inserting, removing we just remove all current settings and re-set everything...
846        $table = ($ug_type == 'user') ? ACL_USERS_TABLE : ACL_GROUPS_TABLE;
847        $id_field = $ug_type . '_id';
848
849        // Get any flags as required
850        reset($auth);
851        $flag = key($auth);
852        $flag = substr($flag, 0, strpos($flag, '_') + 1);
853
854        // This ID (the any-flag) is set if one or more permissions are true...
855        $any_option_id = (int) $this->acl_options['id'][$flag];
856
857        // Remove any-flag from auth ary
858        if (isset($auth[$flag]))
859        {
860            unset($auth[$flag]);
861        }
862
863        // Remove current auth options...
864        $auth_option_ids = array((int) $any_option_id);
865        foreach ($auth as $auth_option => $auth_setting)
866        {
867            $auth_option_ids[] = (int) $this->acl_options['id'][$auth_option];
868        }
869
870        $sql = "DELETE FROM $table
871            WHERE $forum_sql
872                AND $ug_id_sql
873                AND " . $db->sql_in_set('auth_option_id', $auth_option_ids);
874        $db->sql_query($sql);
875
876        // Remove those having a role assigned... the correct type of course...
877        $sql = 'SELECT role_id
878            FROM ' . ACL_ROLES_TABLE . "
879            WHERE role_type = '" . $db->sql_escape($flag) . "'";
880        $result = $db->sql_query($sql);
881
882        $role_ids = array();
883        while ($row = $db->sql_fetchrow($result))
884        {
885            $role_ids[] = $row['role_id'];
886        }
887        $db->sql_freeresult($result);
888
889        if (count($role_ids))
890        {
891            $sql = "DELETE FROM $table
892                WHERE $forum_sql
893                    AND $ug_id_sql
894                    AND auth_option_id = 0
895                    AND " . $db->sql_in_set('auth_role_id', $role_ids);
896            $db->sql_query($sql);
897        }
898
899        // Ok, include the any-flag if one or more auth options are set to yes...
900        foreach ($auth as $auth_option => $setting)
901        {
902            if ($setting == ACL_YES && (!isset($auth[$flag]) || $auth[$flag] == ACL_NEVER))
903            {
904                $auth[$flag] = ACL_YES;
905            }
906        }
907
908        $sql_ary = array();
909        foreach ($forum_id as $forum)
910        {
911            $forum = (int) $forum;
912
913            if ($role_id)
914            {
915                foreach ($ug_id as $id)
916                {
917                    $sql_ary[] = array(
918                        $id_field            => (int) $id,
919                        'forum_id'            => (int) $forum,
920                        'auth_option_id'    => 0,
921                        'auth_setting'        => 0,
922                        'auth_role_id'        => (int) $role_id,
923                    );
924                }
925            }
926            else
927            {
928                foreach ($auth as $auth_option => $setting)
929                {
930                    $auth_option_id = (int) $this->acl_options['id'][$auth_option];
931
932                    if ($setting != ACL_NO)
933                    {
934                        foreach ($ug_id as $id)
935                        {
936                            $sql_ary[] = array(
937                                $id_field            => (int) $id,
938                                'forum_id'            => (int) $forum,
939                                'auth_option_id'    => (int) $auth_option_id,
940                                'auth_setting'        => (int) $setting
941                            );
942                        }
943                    }
944                }
945            }
946        }
947
948        $db->sql_multi_insert($table, $sql_ary);
949
950        if ($clear_prefetch)
951        {
952            $this->acl_clear_prefetch();
953        }
954    }
955
956    /**
957    * Set a role-specific ACL record
958    */
959    function acl_set_role($role_id, $auth)
960    {
961        global $db;
962
963        // Get any-flag as required
964        reset($auth);
965        $flag = key($auth);
966        $flag = substr($flag, 0, strpos($flag, '_') + 1);
967
968        // Remove any-flag from auth ary
969        if (isset($auth[$flag]))
970        {
971            unset($auth[$flag]);
972        }
973
974        // Re-set any flag...
975        foreach ($auth as $auth_option => $setting)
976        {
977            if ($setting == ACL_YES && (!isset($auth[$flag]) || $auth[$flag] == ACL_NEVER))
978            {
979                $auth[$flag] = ACL_YES;
980            }
981        }
982
983        $sql_ary = array();
984        foreach ($auth as $auth_option => $setting)
985        {
986            $auth_option_id = (int) $this->acl_options['id'][$auth_option];
987
988            if ($setting != ACL_NO)
989            {
990                $sql_ary[] = array(
991                    'role_id'            => (int) $role_id,
992                    'auth_option_id'    => (int) $auth_option_id,
993                    'auth_setting'        => (int) $setting
994                );
995            }
996        }
997
998        // If no data is there, we set the any-flag to ACL_NEVER...
999        if (!count($sql_ary))
1000        {
1001            $sql_ary[] = array(
1002                'role_id'            => (int) $role_id,
1003                'auth_option_id'    => (int) $this->acl_options['id'][$flag],
1004                'auth_setting'        => ACL_NEVER
1005            );
1006        }
1007
1008        // Remove current auth options...
1009        $sql = 'DELETE FROM ' . ACL_ROLES_DATA_TABLE . '
1010            WHERE role_id = ' . $role_id;
1011        $db->sql_query($sql);
1012
1013        // Now insert the new values
1014        $db->sql_multi_insert(ACL_ROLES_DATA_TABLE, $sql_ary);
1015
1016        $this->acl_clear_prefetch();
1017    }
1018
1019    /**
1020    * Remove local permission
1021    */
1022    function acl_delete($mode, $ug_id = false, $forum_id = false, $permission_type = false)
1023    {
1024        global $db;
1025
1026        if ($ug_id === false && $forum_id === false)
1027        {
1028            return;
1029        }
1030
1031        $option_id_ary = array();
1032        $table = ($mode == 'user') ? ACL_USERS_TABLE : ACL_GROUPS_TABLE;
1033        $id_field = $mode . '_id';
1034
1035        $where_sql = array();
1036
1037        if ($forum_id !== false)
1038        {
1039            $where_sql[] = (!is_array($forum_id)) ? 'forum_id = ' . (int) $forum_id : $db->sql_in_set('forum_id', array_map('intval', $forum_id));
1040        }
1041
1042        if ($ug_id !== false)
1043        {
1044            $where_sql[] = (!is_array($ug_id)) ? $id_field . ' = ' . (int) $ug_id : $db->sql_in_set($id_field, array_map('intval', $ug_id));
1045        }
1046
1047        // There seem to be auth options involved, therefore we need to go through the list and make sure we capture roles correctly
1048        if ($permission_type !== false)
1049        {
1050            // Get permission type
1051            $sql = 'SELECT auth_option, auth_option_id
1052                FROM ' . ACL_OPTIONS_TABLE . "
1053                WHERE auth_option " . $db->sql_like_expression($permission_type . $db->get_any_char());
1054            $result = $db->sql_query($sql);
1055
1056            $auth_id_ary = array();
1057            while ($row = $db->sql_fetchrow($result))
1058            {
1059                $option_id_ary[] = $row['auth_option_id'];
1060                $auth_id_ary[$row['auth_option']] = ACL_NO;
1061            }
1062            $db->sql_freeresult($result);
1063
1064            // First of all, lets grab the items having roles with the specified auth options assigned
1065            $sql = "SELECT auth_role_id, $id_field, forum_id
1066                FROM $table" . ACL_ROLES_TABLE . " r
1067                WHERE auth_role_id <> 0
1068                    AND auth_role_id = r.role_id
1069                    AND r.role_type = '{$permission_type}'
1070                    AND " . implode(' AND ', $where_sql) . '
1071                ORDER BY auth_role_id';
1072            $result = $db->sql_query($sql);
1073
1074            $cur_role_auth = array();
1075            while ($row = $db->sql_fetchrow($result))
1076            {
1077                $cur_role_auth[$row['auth_role_id']][$row['forum_id']][] = $row[$id_field];
1078            }
1079            $db->sql_freeresult($result);
1080
1081            // Get role data for resetting data
1082            if (count($cur_role_auth))
1083            {
1084                $sql = 'SELECT ao.auth_option, rd.role_id, rd.auth_setting
1085                    FROM ' . ACL_OPTIONS_TABLE . ' ao, ' . ACL_ROLES_DATA_TABLE . ' rd
1086                    WHERE ao.auth_option_id = rd.auth_option_id
1087                        AND ' . $db->sql_in_set('rd.role_id', array_keys($cur_role_auth));
1088                $result = $db->sql_query($sql);
1089
1090                $auth_settings = array();
1091                while ($row = $db->sql_fetchrow($result))
1092                {
1093                    // We need to fill all auth_options, else setting it will fail...
1094                    if (!isset($auth_settings[$row['role_id']]))
1095                    {
1096                        $auth_settings[$row['role_id']] = $auth_id_ary;
1097                    }
1098                    $auth_settings[$row['role_id']][$row['auth_option']] = $row['auth_setting'];
1099                }
1100                $db->sql_freeresult($result);
1101
1102                // Set the options
1103                foreach ($cur_role_auth as $role_id => $auth_row)
1104                {
1105                    foreach ($auth_row as $f_id => $ug_row)
1106                    {
1107                        $this->acl_set($mode, $f_id, $ug_row, $auth_settings[$role_id], 0, false);
1108                    }
1109                }
1110            }
1111        }
1112
1113        // Now, normally remove permissions...
1114        if ($permission_type !== false)
1115        {
1116            $where_sql[] = $db->sql_in_set('auth_option_id', array_map('intval', $option_id_ary));
1117        }
1118
1119        $sql = "DELETE FROM $table
1120            WHERE " . implode(' AND ', $where_sql);
1121        $db->sql_query($sql);
1122
1123        $this->acl_clear_prefetch();
1124    }
1125
1126    /**
1127    * Assign category to template
1128    * used by display_mask()
1129    */
1130    function assign_cat_array(&$category_array, $tpl_cat, $tpl_mask, $ug_id, $forum_id, $s_view, $show_trace = false)
1131    {
1132        global $template, $phpbb_admin_path, $phpEx, $phpbb_container;
1133
1134        /** @var \phpbb\permissions $phpbb_permissions */
1135        $phpbb_permissions = $phpbb_container->get('acl.permissions');
1136
1137        $order = array_flip(array_keys($phpbb_permissions->get_permissions()));
1138
1139        foreach ($category_array as $cat => $cat_array)
1140        {
1141            if (!$phpbb_permissions->category_defined($cat))
1142            {
1143                continue;
1144            }
1145
1146            $template->assign_block_vars($tpl_cat, array(
1147                'S_YES'        => ($cat_array['S_YES'] && !$cat_array['S_NEVER'] && !$cat_array['S_NO']) ? true : false,
1148                'S_NEVER'    => ($cat_array['S_NEVER'] && !$cat_array['S_YES'] && !$cat_array['S_NO']) ? true : false,
1149                'S_NO'        => ($cat_array['S_NO'] && !$cat_array['S_NEVER'] && !$cat_array['S_YES']) ? true : false,
1150
1151                'CAT_NAME'    => $phpbb_permissions->get_category_lang($cat),
1152            ));
1153
1154            $permissions = array_filter($cat_array['permissions'], [$phpbb_permissions, 'permission_defined'], ARRAY_FILTER_USE_KEY);
1155
1156            uksort($permissions, function($a, $b) use ($order) {
1157                return $order[$a] <=> $order[$b];
1158            });
1159
1160            foreach ($permissions as $permission => $allowed)
1161            {
1162                if ($s_view)
1163                {
1164                    $template->assign_block_vars($tpl_cat . '.' . $tpl_mask, array(
1165                        'S_YES'        => ($allowed == ACL_YES) ? true : false,
1166                        'S_NEVER'    => ($allowed == ACL_NEVER) ? true : false,
1167
1168                        'UG_ID'            => $ug_id,
1169                        'FORUM_ID'        => $forum_id,
1170                        'FIELD_NAME'    => $permission,
1171                        'S_FIELD_NAME'    => 'setting[' . $ug_id . '][' . $forum_id . '][' . $permission . ']',
1172
1173                        'U_TRACE'        => ($show_trace) ? append_sid("{$phpbb_admin_path}index.$phpEx", "i=permissions&amp;mode=trace&amp;u=$ug_id&amp;f=$forum_id&amp;auth=$permission") : '',
1174                        'UA_TRACE'        => ($show_trace) ? append_sid("{$phpbb_admin_path}index.$phpEx", "i=permissions&mode=trace&u=$ug_id&f=$forum_id&auth=$permission", false) : '',
1175
1176                        'PERMISSION'    => $phpbb_permissions->get_permission_lang($permission),
1177                    ));
1178                }
1179                else
1180                {
1181                    $template->assign_block_vars($tpl_cat . '.' . $tpl_mask, array(
1182                        'S_YES'        => ($allowed == ACL_YES) ? true : false,
1183                        'S_NEVER'    => ($allowed == ACL_NEVER) ? true : false,
1184                        'S_NO'        => ($allowed == ACL_NO) ? true : false,
1185
1186                        'UG_ID'            => $ug_id,
1187                        'FORUM_ID'        => $forum_id,
1188                        'FIELD_NAME'    => $permission,
1189                        'S_FIELD_NAME'    => 'setting[' . $ug_id . '][' . $forum_id . '][' . $permission . ']',
1190
1191                        'U_TRACE'        => ($show_trace) ? append_sid("{$phpbb_admin_path}index.$phpEx", "i=permissions&amp;mode=trace&amp;u=$ug_id&amp;f=$forum_id&amp;auth=$permission") : '',
1192                        'UA_TRACE'        => ($show_trace) ? append_sid("{$phpbb_admin_path}index.$phpEx", "i=permissions&mode=trace&u=$ug_id&f=$forum_id&auth=$permission", false) : '',
1193
1194                        'PERMISSION'    => $phpbb_permissions->get_permission_lang($permission),
1195                    ));
1196                }
1197            }
1198        }
1199    }
1200
1201    /**
1202    * Building content array from permission rows with explicit key ordering
1203    * used by display_mask()
1204    */
1205    function build_permission_array(&$permission_row, &$content_array, &$categories, $key_sort_array)
1206    {
1207        global $phpbb_container;
1208
1209        /** @var \phpbb\permissions $phpbb_permissions */
1210        $phpbb_permissions = $phpbb_container->get('acl.permissions');
1211
1212        $order = array_flip(array_keys($phpbb_permissions->get_permissions()));
1213
1214        foreach ($key_sort_array as $forum_id)
1215        {
1216            if (!isset($permission_row[$forum_id]))
1217            {
1218                continue;
1219            }
1220
1221            $permissions = array_filter($permission_row[$forum_id], [$phpbb_permissions, 'permission_defined'], ARRAY_FILTER_USE_KEY);
1222
1223            uksort($permissions, function($a, $b) use ($order) {
1224                return $order[$a] <=> $order[$b];
1225            });
1226
1227            foreach ($permissions as $permission => $auth_setting)
1228            {
1229                $cat = $phpbb_permissions->get_permission_category($permission);
1230
1231                // Build our categories array
1232                if (!isset($categories[$cat]))
1233                {
1234                    $categories[$cat] = $phpbb_permissions->get_category_lang($cat);
1235                }
1236
1237                // Build our content array
1238                if (!isset($content_array[$forum_id]))
1239                {
1240                    $content_array[$forum_id] = array();
1241                }
1242
1243                if (!isset($content_array[$forum_id][$cat]))
1244                {
1245                    $content_array[$forum_id][$cat] = array(
1246                        'S_YES'            => false,
1247                        'S_NEVER'        => false,
1248                        'S_NO'            => false,
1249                        'permissions'    => array(),
1250                    );
1251                }
1252
1253                $content_array[$forum_id][$cat]['S_YES'] |= ($auth_setting == ACL_YES) ? true : false;
1254                $content_array[$forum_id][$cat]['S_NEVER'] |= ($auth_setting == ACL_NEVER) ? true : false;
1255                $content_array[$forum_id][$cat]['S_NO'] |= ($auth_setting == ACL_NO) ? true : false;
1256
1257                $content_array[$forum_id][$cat]['permissions'][$permission] = $auth_setting;
1258            }
1259        }
1260    }
1261
1262    /**
1263    * Use permissions from another user. This transferes a permission set from one user to another.
1264    * The other user is always able to revert back to his permission set.
1265    * This function does not check for lower/higher permissions, it is possible for the user to gain
1266    * "more" permissions by this.
1267    * Admin permissions will not be copied.
1268    */
1269    function ghost_permissions($from_user_id, $to_user_id)
1270    {
1271        global $db;
1272
1273        if ($to_user_id == ANONYMOUS)
1274        {
1275            return false;
1276        }
1277
1278        $hold_ary = $this->acl_raw_data_single_user($from_user_id);
1279
1280        // Key 0 in $hold_ary are global options, all others are forum_ids
1281
1282        // We disallow copying admin permissions
1283        foreach ($this->acl_options['global'] as $opt => $id)
1284        {
1285            if (strpos($opt, 'a_') === 0)
1286            {
1287                $hold_ary[0][$this->acl_options['id'][$opt]] = ACL_NEVER;
1288            }
1289        }
1290
1291        // Force a_switchperm to be allowed
1292        $hold_ary[0][$this->acl_options['id']['a_switchperm']] = ACL_YES;
1293
1294        $user_permissions = $this->build_bitstring($hold_ary);
1295
1296        if (!$user_permissions)
1297        {
1298            return false;
1299        }
1300
1301        $sql = 'UPDATE ' . USERS_TABLE . "
1302            SET user_permissions = '" . $db->sql_escape($user_permissions) . "',
1303                user_perm_from = $from_user_id
1304            WHERE user_id = " . $to_user_id;
1305        $db->sql_query($sql);
1306
1307        return true;
1308    }
1309}