xref: /dokuwiki/lib/plugins/acl/admin.php (revision a7e1021671c06230d0a9890646b3531d28d5d29a)
1<?php
2
3use dokuwiki\Extension\AdminPlugin;
4use dokuwiki\Utf8\Sort;
5
6/**
7 * ACL administration functions
8 *
9 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
10 * @author     Andreas Gohr <andi@splitbrain.org>
11 * @author     Anika Henke <anika@selfthinker.org> (concepts)
12 * @author     Frank Schubert <frank@schokilade.de> (old version)
13 */
14
15/**
16 * All DokuWiki plugins to extend the admin function
17 * need to inherit from this class
18 */
19class admin_plugin_acl extends AdminPlugin
20{
21    public $acl;
22    protected $ns;
23    /**
24     * The currently selected item, associative array with id and type.
25     * Populated from (in this order):
26     * $_REQUEST['current_ns']
27     * $_REQUEST['current_id']
28     * $ns
29     * $ID
30     */
31    protected $current_item;
32    protected $who = '';
33    protected $usersgroups = [];
34    protected $specials = [];
35
36    /**
37     * return prompt for admin menu
38     */
39    public function getMenuText($language)
40    {
41        return $this->getLang('admin_acl');
42    }
43
44    /**
45     * return sort order for position in admin menu
46     */
47    public function getMenuSort()
48    {
49        return 1;
50    }
51
52    /**
53     * handle user request
54     *
55     * Initializes internal vars and handles modifications
56     *
57     * @author Andreas Gohr <andi@splitbrain.org>
58     */
59    public function handle()
60    {
61        global $AUTH_ACL;
62        global $ID;
63        global $auth;
64        global $config_cascade;
65        global $INPUT;
66
67        // fresh 1:1 copy without replacements
68        $AUTH_ACL = file($config_cascade['acl']['default']);
69
70        // namespace given?
71        if ($INPUT->str('ns') == '*') {
72            $this->ns = '*';
73        } else {
74            $this->ns = cleanID($INPUT->str('ns'));
75        }
76
77        if ($INPUT->str('current_ns')) {
78            $this->current_item = ['id' => cleanID($INPUT->str('current_ns')), 'type' => 'd'];
79        } elseif ($INPUT->str('current_id')) {
80            $this->current_item = ['id' => cleanID($INPUT->str('current_id')), 'type' => 'f'];
81        } elseif ($this->ns) {
82            $this->current_item = ['id' => $this->ns, 'type' => 'd'];
83        } else {
84            $this->current_item = ['id' => $ID, 'type' => 'f'];
85        }
86
87        // user or group choosen?
88        $who = trim($INPUT->str('acl_w'));
89        if ($INPUT->str('acl_t') == '__g__' && $who) {
90            $this->who = '@' . ltrim($auth->cleanGroup($who), '@');
91        } elseif ($INPUT->str('acl_t') == '__u__' && $who) {
92            $this->who = ltrim($who, '@');
93            if ($this->who != '%USER%' && $this->who != '%GROUP%') { #keep wildcard as is
94                $this->who = $auth->cleanUser($this->who);
95            }
96        } elseif (
97            $INPUT->str('acl_t') &&
98            $INPUT->str('acl_t') != '__u__' &&
99            $INPUT->str('acl_t') != '__g__'
100        ) {
101            $this->who = $INPUT->str('acl_t');
102        } elseif ($who) {
103            $this->who = $who;
104        }
105
106        // handle modifications
107        if ($INPUT->has('cmd') && checkSecurityToken()) {
108            $cmd = $INPUT->extract('cmd')->str('cmd');
109
110            // scope for modifications
111            if ($this->ns) {
112                if ($this->ns == '*') {
113                    $scope = '*';
114                } else {
115                    $scope = $this->ns . ':*';
116                }
117            } else {
118                $scope = $ID;
119            }
120
121            if ($cmd == 'save' && $scope && $this->who && $INPUT->has('acl')) {
122                // handle additions or single modifications
123                $this->deleteACL($scope, $this->who);
124                $this->addOrUpdateACL($scope, $this->who, $INPUT->int('acl'));
125            } elseif ($cmd == 'del' && $scope && $this->who) {
126                // handle single deletions
127                $this->deleteACL($scope, $this->who);
128            } elseif ($cmd == 'update') {
129                $acl = $INPUT->arr('acl');
130
131                // handle update of the whole file
132                foreach ($INPUT->arr('del') as $where => $names) {
133                    // remove all rules marked for deletion
134                    foreach ($names as $who)
135                        unset($acl[$where][$who]);
136                }
137                // prepare lines
138                $lines = [];
139                // keep header
140                foreach ($AUTH_ACL as $line) {
141                    if ($line[0] == '#') {
142                        $lines[] = $line;
143                    } else {
144                        break;
145                    }
146                }
147                // re-add all rules
148                foreach ($acl as $where => $opt) {
149                    foreach ($opt as $who => $perm) {
150                        if ($who[0] == '@') {
151                            if ($who != '@ALL') {
152                                $who = '@' . ltrim($auth->cleanGroup($who), '@');
153                            }
154                        } elseif ($who != '%USER%' && $who != '%GROUP%') { #keep wildcard as is
155                            $who = $auth->cleanUser($who);
156                        }
157                        $who = auth_nameencode($who, true);
158                        $perm = (int)$perm;
159                        $lines[] = "$where\t$who\t$perm\n";
160                    }
161                }
162                // save it
163                io_saveFile($config_cascade['acl']['default'], implode('', $lines));
164            }
165
166            // reload ACL config
167            $AUTH_ACL = file($config_cascade['acl']['default']);
168        }
169
170        // initialize ACL array
171        $this->initAclConfig();
172    }
173
174    /**
175     * ACL Output function
176     *
177     * print a table with all significant permissions for the
178     * current id
179     *
180     * @author  Frank Schubert <frank@schokilade.de>
181     * @author  Andreas Gohr <andi@splitbrain.org>
182     */
183    public function html()
184    {
185        echo '<div id="acl_manager">';
186        echo '<h1>' . $this->getLang('admin_acl') . '</h1>';
187        echo '<div class="level1">';
188
189        echo '<div id="acl__tree">';
190        $this->makeExplorer();
191        echo '</div>';
192
193        echo '<div id="acl__detail">';
194        $this->printDetail();
195        echo '</div>';
196        echo '</div>';
197
198        echo '<div class="clearer"></div>';
199        echo '<h2>' . $this->getLang('current') . '</h2>';
200        echo '<div class="level2">';
201        $this->printAclTable();
202        echo '</div>';
203
204        echo '<div class="footnotes"><div class="fn">';
205        echo '<sup><a id="fn__1" class="fn_bot" href="#fnt__1">1)</a></sup>';
206        echo '<div class="content">' . $this->getLang('p_include') . '</div>';
207        echo '</div></div>';
208
209        echo '</div>';
210    }
211
212    /**
213     * returns array with set options for building links
214     *
215     * @author Andreas Gohr <andi@splitbrain.org>
216     */
217    protected function getLinkOptions($addopts = null)
218    {
219        $opts = ['do' => 'admin', 'page' => 'acl'];
220        if ($this->ns) $opts['ns'] = $this->ns;
221        if ($this->who) $opts['acl_w'] = $this->who;
222
223        if (is_null($addopts)) return $opts;
224        return array_merge($opts, $addopts);
225    }
226
227    /**
228     * Display a tree menu to select a page or namespace
229     *
230     * @author Andreas Gohr <andi@splitbrain.org>
231     */
232    protected function makeExplorer()
233    {
234        global $conf;
235        global $ID;
236        global $lang;
237
238        $ns = $this->ns;
239        if (empty($ns)) {
240            $ns = dirname(str_replace(':', '/', $ID));
241            if ($ns == '.') $ns = '';
242        } elseif ($ns == '*') {
243            $ns = '';
244        }
245        $ns = utf8_encodeFN(str_replace(':', '/', $ns));
246
247        $data = $this->makeTree($ns);
248
249        // wrap a list with the root level around the other namespaces
250        array_unshift($data, [
251            'level' => 0,
252            'id' => '*',
253            'type' => 'd',
254            'open' => 'true',
255            'label' => '[' . $lang['mediaroot'] . ']'
256        ]);
257
258        echo html_buildlist(
259            $data,
260            'acl',
261            $this->makeTreeItem(...),
262            $this->makeListItem(...)
263        );
264    }
265
266    /**
267     * get a combined list of media and page files
268     *
269     * also called via AJAX
270     *
271     * @param string $folder an already converted filesystem folder of the current namespace
272     * @param string $limit limit the search to this folder
273     * @return array
274     */
275    public function makeTree($folder, $limit = '')
276    {
277        global $conf;
278
279        // read tree structure from pages and media
280        $data = [];
281        search($data, $conf['datadir'], 'search_index', ['ns' => $folder], $limit);
282        $media = [];
283        search($media, $conf['mediadir'], 'search_index', ['ns' => $folder, 'nofiles' => true], $limit);
284        $data = array_merge($data, $media);
285        unset($media);
286
287        // combine by sorting and removing duplicates
288        usort($data, $this->treeSort(...));
289        $count = count($data);
290        if ($count > 0) for ($i = 1; $i < $count; $i++) {
291            if ($data[$i - 1]['id'] == $data[$i]['id'] && $data[$i - 1]['type'] == $data[$i]['type']) {
292                unset($data[$i]);
293                $i++;  // duplicate found, next $i can't be a duplicate, so skip forward one
294            }
295        }
296        return $data;
297    }
298
299    /**
300     * usort callback
301     *
302     * Sorts the combined trees of media and page files
303     */
304    public function treeSort($a, $b)
305    {
306        // handle the trivial cases first
307        if ($a['id'] == '') return -1;
308        if ($b['id'] == '') return 1;
309        // split up the id into parts
310        $a_ids = explode(':', $a['id']);
311        $b_ids = explode(':', $b['id']);
312        // now loop through the parts
313        while (count($a_ids) && count($b_ids)) {
314            // compare each level from upper to lower
315            // until a non-equal component is found
316            $cur_result = Sort::strcmp(array_shift($a_ids), array_shift($b_ids));
317            if ($cur_result) {
318                // if one of the components is the last component and is a file
319                // and the other one is either of a deeper level or a directory,
320                // the file has to come after the deeper level or directory
321                if ($a_ids === [] && $a['type'] == 'f' && (count($b_ids) || $b['type'] == 'd')) return 1;
322                if ($b_ids === [] && $b['type'] == 'f' && (count($a_ids) || $a['type'] == 'd')) return -1;
323                return $cur_result;
324            }
325        }
326        // The two ids seem to be equal. One of them might however refer
327        // to a page, one to a namespace, the namespace needs to be first.
328        if ($a_ids === [] && $b_ids === []) {
329            if ($a['type'] == $b['type']) return 0;
330            if ($a['type'] == 'f') return 1;
331            return -1;
332        }
333        // Now the empty part is either a page in the parent namespace
334        // that obviously needs to be after the namespace
335        // Or it is the namespace that contains the other part and should be
336        // before that other part.
337        if ($a_ids === []) return ($a['type'] == 'd') ? -1 : 1;
338        if ($b_ids === []) return ($b['type'] == 'd') ? 1 : -1;
339        return 0; //shouldn't happen
340    }
341
342    /**
343     * Display the current ACL for selected where/who combination with
344     * selectors and modification form
345     *
346     * @author Andreas Gohr <andi@splitbrain.org>
347     */
348    protected function printDetail()
349    {
350        global $ID;
351
352        echo '<form action="' . wl() . '" method="post" accept-charset="utf-8"><div class="no">';
353
354        echo '<div id="acl__user">';
355        echo $this->getLang('acl_perms') . ' ';
356        $inl = $this->makeSelect();
357        echo sprintf(
358            '<input type="text" name="acl_w" class="edit" value="%s" />',
359            ($inl) ? '' : hsc(ltrim($this->who, '@'))
360        );
361        echo '<button type="submit">' . $this->getLang('btn_select') . '</button>';
362        echo '</div>';
363
364        echo '<div id="acl__info">';
365        $this->printInfo();
366        echo '</div>';
367
368        echo '<input type="hidden" name="ns" value="' . hsc($this->ns) . '" />';
369        echo '<input type="hidden" name="id" value="' . hsc($ID) . '" />';
370        echo '<input type="hidden" name="do" value="admin" />';
371        echo '<input type="hidden" name="page" value="acl" />';
372        echo '<input type="hidden" name="sectok" value="' . getSecurityToken() . '" />';
373        echo '</div></form>';
374    }
375
376    /**
377     * Print info and editor
378     *
379     * also loaded via Ajax
380     */
381    public function printInfo()
382    {
383        global $ID;
384
385        if ($this->who) {
386            $current = $this->getExactPermisson();
387
388            // explain current permissions
389            $this->printExplanation($current);
390            // load editor
391            $this->printAclEditor($current);
392        } else {
393            echo '<p>';
394            if ($this->ns) {
395                printf($this->getLang('p_choose_ns'), hsc($this->ns));
396            } else {
397                printf($this->getLang('p_choose_id'), hsc($ID));
398            }
399            echo '</p>';
400
401            echo $this->locale_xhtml('help');
402        }
403    }
404
405    /**
406     * Display the ACL editor
407     *
408     * @author Andreas Gohr <andi@splitbrain.org>
409     */
410    protected function printAclEditor($current)
411    {
412        global $lang;
413
414        echo '<fieldset>';
415        if (is_null($current)) {
416            echo '<legend>' . $this->getLang('acl_new') . '</legend>';
417        } else {
418            echo '<legend>' . $this->getLang('acl_mod') . '</legend>';
419        }
420
421        echo $this->makeCheckboxes($current, empty($this->ns), 'acl');
422
423        if (is_null($current)) {
424            echo '<button type="submit" name="cmd[save]">' . $lang['btn_save'] . '</button>';
425        } else {
426            echo '<button type="submit" name="cmd[save]">' . $lang['btn_update'] . '</button>';
427            echo '<button type="submit" name="cmd[del]">' . $lang['btn_delete'] . '</button>';
428        }
429
430        echo '</fieldset>';
431    }
432
433    /**
434     * Explain the currently set permissions in plain english/$lang
435     *
436     * @author Andreas Gohr <andi@splitbrain.org>
437     */
438    protected function printExplanation($current)
439    {
440        global $ID;
441        global $auth;
442
443        $who = $this->who;
444        $ns = $this->ns;
445
446        // prepare where to check
447        if ($ns) {
448            if ($ns == '*') {
449                $check = '*';
450            } else {
451                $check = $ns . ':*';
452            }
453        } else {
454            $check = $ID;
455        }
456
457        // prepare who to check
458        if ($who[0] == '@') {
459            $user = '';
460            $groups = [ltrim($who, '@')];
461        } else {
462            $user = $who;
463            $info = $auth->getUserData($user);
464            if ($info === false) {
465                $groups = [];
466            } else {
467                $groups = $info['grps'];
468            }
469        }
470
471        // check the permissions
472        $perm = auth_aclcheck($check, $user, $groups);
473
474        // build array of named permissions
475        $names = [];
476        if ($perm) {
477            if ($ns) {
478                if ($perm >= AUTH_DELETE) $names[] = $this->getLang('acl_perm16');
479                if ($perm >= AUTH_UPLOAD) $names[] = $this->getLang('acl_perm8');
480                if ($perm >= AUTH_CREATE) $names[] = $this->getLang('acl_perm4');
481            }
482            if ($perm >= AUTH_EDIT) $names[] = $this->getLang('acl_perm2');
483            if ($perm >= AUTH_READ) $names[] = $this->getLang('acl_perm1');
484            $names = array_reverse($names);
485        } else {
486            $names[] = $this->getLang('acl_perm0');
487        }
488
489        // print permission explanation
490        echo '<p>';
491        if ($user) {
492            if ($ns) {
493                printf($this->getLang('p_user_ns'), hsc($who), hsc($ns), implode(', ', $names));
494            } else {
495                printf($this->getLang('p_user_id'), hsc($who), hsc($ID), implode(', ', $names));
496            }
497        } elseif ($ns) {
498            printf($this->getLang('p_group_ns'), hsc(ltrim($who, '@')), hsc($ns), implode(', ', $names));
499        } else {
500            printf($this->getLang('p_group_id'), hsc(ltrim($who, '@')), hsc($ID), implode(', ', $names));
501        }
502        echo '</p>';
503
504        // add note if admin
505        if ($perm == AUTH_ADMIN) {
506            echo '<p>' . $this->getLang('p_isadmin') . '</p>';
507        } elseif (is_null($current)) {
508            echo '<p>' . $this->getLang('p_inherited') . '</p>';
509        }
510    }
511
512
513    /**
514     * Item formatter for the tree view
515     *
516     * User function for html_buildlist()
517     *
518     * @author Andreas Gohr <andi@splitbrain.org>
519     */
520    public function makeTreeItem($item)
521    {
522        $ret = '';
523        // what to display
524        if (!empty($item['label'])) {
525            $base = $item['label'];
526        } else {
527            $base = ':' . $item['id'];
528            $base = substr($base, strrpos($base, ':') + 1);
529        }
530
531        // highlight?
532        if (($item['type'] == $this->current_item['type'] && $item['id'] == $this->current_item['id'])) {
533            $cl = ' cur';
534        } else {
535            $cl = '';
536        }
537
538        // namespace or page?
539        if ($item['type'] == 'd') {
540            if ($item['open']) {
541                $img = DOKU_BASE . 'lib/images/minus.gif';
542                $alt = '−';
543            } else {
544                $img = DOKU_BASE . 'lib/images/plus.gif';
545                $alt = '+';
546            }
547            $ret .= '<img src="' . $img . '" alt="' . $alt . '" />';
548            $ret .= '<a href="' .
549                wl('', $this->getLinkOptions(['ns' => $item['id'], 'sectok' => getSecurityToken()])) .
550                '" class="idx_dir' . $cl . '">';
551            $ret .= $base;
552            $ret .= '</a>';
553        } else {
554            $ret .= '<a href="' .
555                wl('', $this->getLinkOptions(['id' => $item['id'], 'ns' => '', 'sectok' => getSecurityToken()])) .
556                '" class="wikilink1' . $cl . '">';
557            $ret .= noNS($item['id']);
558            $ret .= '</a>';
559        }
560        return $ret;
561    }
562
563    /**
564     * List Item formatter
565     *
566     * @param array $item
567     * @return string
568     */
569    public function makeListItem($item)
570    {
571        return '<li class="level' . $item['level'] . ' ' .
572            ($item['open'] ? 'open' : 'closed') . '">';
573    }
574
575
576    /**
577     * Get current ACL settings as multidim array
578     *
579     * @author Andreas Gohr <andi@splitbrain.org>
580     */
581    public function initAclConfig()
582    {
583        global $AUTH_ACL;
584        global $conf;
585        $acl_config = [];
586        $usersgroups = [];
587
588        // get special users and groups
589        $this->specials[] = '@ALL';
590        $this->specials[] = '@' . $conf['defaultgroup'];
591        if ($conf['manager'] != '!!not set!!') {
592            $this->specials = array_merge(
593                $this->specials,
594                array_map(
595                    trim(...),
596                    explode(',', $conf['manager'])
597                )
598            );
599        }
600        $this->specials = array_filter($this->specials);
601        $this->specials = array_unique($this->specials);
602        Sort::sort($this->specials);
603
604        foreach ($AUTH_ACL as $line) {
605            $line = trim(preg_replace('/#.*$/', '', $line)); //ignore comments
606            if (!$line) continue;
607
608            $acl = preg_split('/[ \t]+/', $line);
609            //0 is pagename, 1 is user, 2 is acl
610
611            $acl[1] = rawurldecode($acl[1]);
612            $acl_config[$acl[0]][$acl[1]] = $acl[2];
613
614            // store non-special users and groups for later selection dialog
615            $ug = $acl[1];
616            if (in_array($ug, $this->specials)) continue;
617            $usersgroups[] = $ug;
618        }
619
620        $usersgroups = array_unique($usersgroups);
621        Sort::sort($usersgroups);
622        Sort::ksort($acl_config);
623        foreach (array_keys($acl_config) as $pagename) {
624            Sort::ksort($acl_config[$pagename]);
625        }
626
627        $this->acl = $acl_config;
628        $this->usersgroups = $usersgroups;
629    }
630
631    /**
632     * Display all currently set permissions in a table
633     *
634     * @author Andreas Gohr <andi@splitbrain.org>
635     */
636    protected function printAclTable()
637    {
638        global $lang;
639        global $ID;
640
641        echo '<form action="' . wl() . '" method="post" accept-charset="utf-8"><div class="no">';
642        if ($this->ns) {
643            echo '<input type="hidden" name="ns" value="' . hsc($this->ns) . '" />';
644        } else {
645            echo '<input type="hidden" name="id" value="' . hsc($ID) . '" />';
646        }
647        echo '<input type="hidden" name="acl_w" value="' . hsc($this->who) . '" />';
648        echo '<input type="hidden" name="do" value="admin" />';
649        echo '<input type="hidden" name="page" value="acl" />';
650        echo '<input type="hidden" name="sectok" value="' . getSecurityToken() . '" />';
651        echo '<div class="table">';
652        echo '<table class="inline">';
653        echo '<tr>';
654        echo '<th>' . $this->getLang('where') . '</th>';
655        echo '<th>' . $this->getLang('who') . '</th>';
656        echo '<th>' . $this->getLang('perm') . '<sup><a id="fnt__1" class="fn_top" href="#fn__1">1)</a></sup></th>';
657        echo '<th>' . $lang['btn_delete'] . '</th>';
658        echo '</tr>';
659        foreach ($this->acl as $where => $set) {
660            foreach ($set as $who => $perm) {
661                echo '<tr>';
662                echo '<td>';
663                if (str_ends_with($where, '*')) {
664                    echo '<span class="aclns">' . hsc($where) . '</span>';
665                    $ispage = false;
666                } else {
667                    echo '<span class="aclpage">' . hsc($where) . '</span>';
668                    $ispage = true;
669                }
670                echo '</td>';
671
672                echo '<td>';
673                if ($who[0] == '@') {
674                    echo '<span class="aclgroup">' . hsc($who) . '</span>';
675                } else {
676                    echo '<span class="acluser">' . hsc($who) . '</span>';
677                }
678                echo '</td>';
679
680                echo '<td>';
681                echo $this->makeCheckboxes($perm, $ispage, 'acl[' . $where . '][' . $who . ']');
682                echo '</td>';
683
684                echo '<td class="check">';
685                echo '<input type="checkbox" name="del[' . hsc($where) . '][]" value="' . hsc($who) . '" />';
686                echo '</td>';
687                echo '</tr>';
688            }
689        }
690
691        echo '<tr>';
692        echo '<th class="action" colspan="4">';
693        echo '<button type="submit" name="cmd[update]">' . $lang['btn_update'] . '</button>';
694        echo '</th>';
695        echo '</tr>';
696        echo '</table>';
697        echo '</div>';
698        echo '</div></form>';
699    }
700
701    /**
702     * Returns the permission which were set for exactly the given user/group
703     * and page/namespace. Returns null if no exact match is available
704     *
705     * @author Andreas Gohr <andi@splitbrain.org>
706     */
707    protected function getExactPermisson()
708    {
709        global $ID;
710        if ($this->ns) {
711            if ($this->ns == '*') {
712                $check = '*';
713            } else {
714                $check = $this->ns . ':*';
715            }
716        } else {
717            $check = $ID;
718        }
719
720        if (isset($this->acl[$check][$this->who])) {
721            return $this->acl[$check][$this->who];
722        } else {
723            return null;
724        }
725    }
726
727    /**
728     * adds new acl-entry to conf/acl.auth.php
729     *
730     * @author  Frank Schubert <frank@schokilade.de>
731     */
732    public function addOrUpdateACL($acl_scope, $acl_user, $acl_level)
733    {
734        global $config_cascade;
735
736        // first make sure we won't end up with 2 lines matching this user and scope. See issue #1115
737        $this->deleteACL($acl_scope, $acl_user);
738        $acl_user = auth_nameencode($acl_user, true);
739
740        // max level for pagenames is edit
741        if (!str_contains($acl_scope, '*')) {
742            if ($acl_level > AUTH_EDIT) $acl_level = AUTH_EDIT;
743        }
744
745        $new_acl = "$acl_scope\t$acl_user\t$acl_level\n";
746
747        return io_saveFile($config_cascade['acl']['default'], $new_acl, true);
748    }
749
750    /**
751     * remove acl-entry from conf/acl.auth.php
752     *
753     * @author  Frank Schubert <frank@schokilade.de>
754     */
755    public function deleteACL($acl_scope, $acl_user)
756    {
757        global $config_cascade;
758        $acl_user = auth_nameencode($acl_user, true);
759
760        $acl_pattern = '^' . preg_quote($acl_scope, '/') . '[ \t]+' . $acl_user . '[ \t]+[0-8].*$';
761
762        return io_deleteFromFile($config_cascade['acl']['default'], "/$acl_pattern/", true);
763    }
764
765    /**
766     * print the permission radio boxes
767     *
768     * @author  Frank Schubert <frank@schokilade.de>
769     * @author  Andreas Gohr <andi@splitbrain.org>
770     */
771    protected function makeCheckboxes($setperm, $ispage, $name)
772    {
773        global $lang;
774
775        static $label = 0; //number labels
776        $ret = '';
777
778        if ($ispage && $setperm > AUTH_EDIT) $setperm = AUTH_EDIT;
779
780        foreach ([AUTH_NONE, AUTH_READ, AUTH_EDIT, AUTH_CREATE, AUTH_UPLOAD, AUTH_DELETE] as $perm) {
781            ++$label;
782
783            //general checkbox attributes
784            $atts = [
785                'type' => 'radio',
786                'id' => 'pbox' . $label,
787                'name' => $name,
788                'value' => $perm
789            ];
790            //dynamic attributes
791            if (!is_null($setperm) && $setperm == $perm) $atts['checked'] = 'checked';
792            if ($ispage && $perm > AUTH_EDIT) {
793                $atts['disabled'] = 'disabled';
794                $class = ' class="disabled"';
795            } else {
796                $class = '';
797            }
798
799            //build code
800            $ret .= '<label for="pbox' . $label . '"' . $class . '>';
801            $ret .= '<input ' . buildAttributes($atts) . ' />&#160;';
802            $ret .= $this->getLang('acl_perm' . $perm);
803            $ret .= '</label>';
804        }
805        return $ret;
806    }
807
808    /**
809     * Print a user/group selector (reusing already used users and groups)
810     *
811     * @author  Andreas Gohr <andi@splitbrain.org>
812     */
813    protected function makeSelect()
814    {
815        $inlist = false;
816        $usel = '';
817        $gsel = '';
818
819        if (
820            $this->who &&
821            !in_array($this->who, $this->usersgroups) &&
822            !in_array($this->who, $this->specials)
823        ) {
824            if ($this->who[0] == '@') {
825                $gsel = ' selected="selected"';
826            } else {
827                $usel = ' selected="selected"';
828            }
829        } else {
830            $inlist = true;
831        }
832
833        echo '<select name="acl_t" class="edit">';
834        echo '  <option value="__g__" class="aclgroup"' . $gsel . '>' . $this->getLang('acl_group') . '</option>';
835        echo '  <option value="__u__"  class="acluser"' . $usel . '>' . $this->getLang('acl_user') . '</option>';
836        if (!empty($this->specials)) {
837            echo '  <optgroup label="&#160;">';
838            foreach ($this->specials as $ug) {
839                if ($ug == $this->who) {
840                    $sel = ' selected="selected"';
841                    $inlist = true;
842                } else {
843                    $sel = '';
844                }
845
846                if ($ug[0] == '@') {
847                    echo '  <option value="' . hsc($ug) . '" class="aclgroup"' . $sel . '>' . hsc($ug) . '</option>';
848                } else {
849                    echo '  <option value="' . hsc($ug) . '" class="acluser"' . $sel . '>' . hsc($ug) . '</option>';
850                }
851            }
852            echo '  </optgroup>';
853        }
854        if (!empty($this->usersgroups)) {
855            echo '  <optgroup label="&#160;">';
856            foreach ($this->usersgroups as $ug) {
857                if ($ug == $this->who) {
858                    $sel = ' selected="selected"';
859                    $inlist = true;
860                } else {
861                    $sel = '';
862                }
863
864                if ($ug[0] == '@') {
865                    echo '  <option value="' . hsc($ug) . '" class="aclgroup"' . $sel . '>' . hsc($ug) . '</option>';
866                } else {
867                    echo '  <option value="' . hsc($ug) . '" class="acluser"' . $sel . '>' . hsc($ug) . '</option>';
868                }
869            }
870            echo '  </optgroup>';
871        }
872        echo '</select>';
873        return $inlist;
874    }
875}
876