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