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