xref: /dokuwiki/lib/plugins/acl/admin.php (revision 11e2ce226d64ac98b82ddc93a81eea66160bcc21)
1*11e2ce22Schris<?php
2*11e2ce22Schris/**
3*11e2ce22Schris * ACL administration functions
4*11e2ce22Schris *
5*11e2ce22Schris * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6*11e2ce22Schris * @author     Frank Schubert <frank@schokilade.de>
7*11e2ce22Schris */
8*11e2ce22Schrisif(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
9*11e2ce22Schrisif(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
10*11e2ce22Schrisrequire_once(DOKU_PLUGIN.'admin.php');
11*11e2ce22Schris
12*11e2ce22Schris/**
13*11e2ce22Schris * All DokuWiki plugins to extend the admin function
14*11e2ce22Schris * need to inherit from this class
15*11e2ce22Schris */
16*11e2ce22Schrisclass admin_plugin_acl extends DokuWiki_Admin_Plugin {
17*11e2ce22Schris
18*11e2ce22Schris    /**
19*11e2ce22Schris     * return some info
20*11e2ce22Schris     */
21*11e2ce22Schris    function getInfo(){
22*11e2ce22Schris      return array(
23*11e2ce22Schris        'author' => 'Frank Schubert',
24*11e2ce22Schris        'email'  => 'frank@schokilade.de',
25*11e2ce22Schris        'date'   => '2005-08-08',
26*11e2ce22Schris        'name'   => 'ACL',
27*11e2ce22Schris        'desc'   => 'Manage Page Access Control Lists',
28*11e2ce22Schris        'url'    => 'http://wiki.splitbrain.org/wiki:acl',
29*11e2ce22Schris      );
30*11e2ce22Schris    }
31*11e2ce22Schris
32*11e2ce22Schris    /**
33*11e2ce22Schris     * return prompt for admin menu
34*11e2ce22Schris     */
35*11e2ce22Schris    function getMenuText($language) {
36*11e2ce22Schris        global $lang;
37*11e2ce22Schris        return $lang['admin_acl'];
38*11e2ce22Schris    }
39*11e2ce22Schris
40*11e2ce22Schris    /**
41*11e2ce22Schris     * return sort order for position in admin menu
42*11e2ce22Schris     */
43*11e2ce22Schris    function getMenuSort() {
44*11e2ce22Schris      return 1;
45*11e2ce22Schris    }
46*11e2ce22Schris
47*11e2ce22Schris    /**
48*11e2ce22Schris     * handle user request
49*11e2ce22Schris     */
50*11e2ce22Schris    function handle() {
51*11e2ce22Schris      global $AUTH_ACL;
52*11e2ce22Schris
53*11e2ce22Schris      $cmd   = $_REQUEST['acl_cmd'];
54*11e2ce22Schris      $scope = $_REQUEST['acl_scope'];
55*11e2ce22Schris      $type  = $_REQUEST['acl_type'];
56*11e2ce22Schris      $user  = $_REQUEST['acl_user'];
57*11e2ce22Schris      $perm  = $_REQUEST['acl_perm'];
58*11e2ce22Schris
59*11e2ce22Schris      if(is_array($perm)){
60*11e2ce22Schris        //use the maximum
61*11e2ce22Schris        sort($perm);
62*11e2ce22Schris        $perm = array_pop($perm);
63*11e2ce22Schris      }else{
64*11e2ce22Schris        $perm = 0;
65*11e2ce22Schris      }
66*11e2ce22Schris
67*11e2ce22Schris      //sanitize
68*11e2ce22Schris      $user  = cleanID($user);
69*11e2ce22Schris      if($type == '@') $user = '@'.$user;
70*11e2ce22Schris      if($user == '@all') $user = '@ALL'; //special group! (now case insensitive)
71*11e2ce22Schris      $perm  = (int) $perm;
72*11e2ce22Schris      if($perm > AUTH_DELETE) $perm = AUTH_DELETE;
73*11e2ce22Schris      //FIXME sanitize scope!!!
74*11e2ce22Schris
75*11e2ce22Schris      //nothing to do?
76*11e2ce22Schris      if(empty($cmd) || empty($scope) || empty($user)) return;
77*11e2ce22Schris
78*11e2ce22Schris
79*11e2ce22Schris      if($cmd == 'save'){
80*11e2ce22Schris        $this->admin_acl_del($scope, $user);
81*11e2ce22Schris        $this->admin_acl_add($scope, $user, $perm);
82*11e2ce22Schris      }elseif($cmd == 'delete'){
83*11e2ce22Schris        $this->admin_acl_del($scope, $user);
84*11e2ce22Schris      }
85*11e2ce22Schris
86*11e2ce22Schris      // reload ACL config
87*11e2ce22Schris      $AUTH_ACL = file(DOKU_CONF.'acl.auth.php');
88*11e2ce22Schris    }
89*11e2ce22Schris
90*11e2ce22Schris    /**
91*11e2ce22Schris     * ACL Output function
92*11e2ce22Schris     *
93*11e2ce22Schris     * print a table with all significant permissions for the
94*11e2ce22Schris     * current id
95*11e2ce22Schris     *
96*11e2ce22Schris     * @author  Frank Schubert <frank@schokilade.de>
97*11e2ce22Schris     * @author  Andreas Gohr <andi@splitbrain.org>
98*11e2ce22Schris     */
99*11e2ce22Schris    function html() {
100*11e2ce22Schris      global $ID;
101*11e2ce22Schris
102*11e2ce22Schris      print p_locale_xhtml('admin_acl');
103*11e2ce22Schris
104*11e2ce22Schris      ptln('<div class="acladmin">');
105*11e2ce22Schris      ptln('<table class="inline">');
106*11e2ce22Schris
107*11e2ce22Schris      //new
108*11e2ce22Schris      $this->admin_acl_html_new();
109*11e2ce22Schris
110*11e2ce22Schris      //current config
111*11e2ce22Schris      $acls = $this->get_acl_config($ID);
112*11e2ce22Schris      foreach ($acls as $id => $acl){
113*11e2ce22Schris        $this->admin_acl_html_current($id,$acl);
114*11e2ce22Schris      }
115*11e2ce22Schris
116*11e2ce22Schris      ptln('</table>');
117*11e2ce22Schris      ptln('</div>');
118*11e2ce22Schris    }
119*11e2ce22Schris
120*11e2ce22Schris
121*11e2ce22Schris    /**
122*11e2ce22Schris     * Get matching ACL lines for a page
123*11e2ce22Schris     *
124*11e2ce22Schris     * $ID is pagename, reads matching lines from $AUTH_ACL,
125*11e2ce22Schris     * also reads acls from namespace
126*11e2ce22Schris     * returns multi-array with key=pagename and value=array(user, acl)
127*11e2ce22Schris     *
128*11e2ce22Schris     * @todo    Fix comment to make sense
129*11e2ce22Schris     * @todo    should this moved to auth.php?
130*11e2ce22Schris     * @todo    can this be combined with auth_aclcheck to avoid duplicate code?
131*11e2ce22Schris     * @author  Frank Schubert <frank@schokilade.de>
132*11e2ce22Schris     */
133*11e2ce22Schris    function get_acl_config($id){
134*11e2ce22Schris      global $AUTH_ACL;
135*11e2ce22Schris
136*11e2ce22Schris      $acl_config=array();
137*11e2ce22Schris
138*11e2ce22Schris      // match exact name
139*11e2ce22Schris      $matches = preg_grep('/^'.$id.'\s+.*/',$AUTH_ACL);
140*11e2ce22Schris      if(count($matches)){
141*11e2ce22Schris        foreach($matches as $match){
142*11e2ce22Schris          $match = preg_replace('/#.*$/','',$match); //ignore comments
143*11e2ce22Schris          $acl   = preg_split('/\s+/',$match);
144*11e2ce22Schris          //0 is pagename, 1 is user, 2 is acl
145*11e2ce22Schris          $acl_config[$acl[0]][] = array( 'name' => $acl[1], 'perm' => $acl[2]);
146*11e2ce22Schris        }
147*11e2ce22Schris      }
148*11e2ce22Schris
149*11e2ce22Schris      $specific_found=array();
150*11e2ce22Schris      // match ns
151*11e2ce22Schris      while(($id=getNS($id)) !== false){
152*11e2ce22Schris        $matches = preg_grep('/^'.$id.':\*\s+.*/',$AUTH_ACL);
153*11e2ce22Schris        if(count($matches)){
154*11e2ce22Schris          foreach($matches as $match){
155*11e2ce22Schris            $match = preg_replace('/#.*$/','',$match); //ignore comments
156*11e2ce22Schris            $acl   = preg_split('/\s+/',$match);
157*11e2ce22Schris            //0 is pagename, 1 is user, 2 is acl
158*11e2ce22Schris            $acl_config[$acl[0]][] = array( 'name' => $acl[1], 'perm' => $acl[2]);
159*11e2ce22Schris            $specific_found[]=$acl[1];
160*11e2ce22Schris          }
161*11e2ce22Schris        }
162*11e2ce22Schris      }
163*11e2ce22Schris
164*11e2ce22Schris      //include *-config
165*11e2ce22Schris      $matches = preg_grep('/^\*\s+.*/',$AUTH_ACL);
166*11e2ce22Schris      if(count($matches)){
167*11e2ce22Schris        foreach($matches as $match){
168*11e2ce22Schris          $match = preg_replace('/#.*$/','',$match); //ignore comments
169*11e2ce22Schris          $acl   = preg_split('/\s+/',$match);
170*11e2ce22Schris          // only include * for this user if not already found in ns
171*11e2ce22Schris          if(!in_array($acl[1], $specific_found)){
172*11e2ce22Schris            //0 is pagename, 1 is user, 2 is acl
173*11e2ce22Schris            $acl_config[$acl[0]][] = array( 'name' => $acl[1], 'perm' => $acl[2]);
174*11e2ce22Schris          }
175*11e2ce22Schris        }
176*11e2ce22Schris      }
177*11e2ce22Schris
178*11e2ce22Schris      //sort
179*11e2ce22Schris      //FIXME: better sort algo: first sort by key, then sort by first value
180*11e2ce22Schris      krsort($acl_config, SORT_STRING);
181*11e2ce22Schris
182*11e2ce22Schris      return($acl_config);
183*11e2ce22Schris    }
184*11e2ce22Schris
185*11e2ce22Schris
186*11e2ce22Schris    /**
187*11e2ce22Schris     * adds new acl-entry to conf/acl.auth.php
188*11e2ce22Schris     *
189*11e2ce22Schris     * @author  Frank Schubert <frank@schokilade.de>
190*11e2ce22Schris     */
191*11e2ce22Schris    function admin_acl_add($acl_scope, $acl_user, $acl_level){
192*11e2ce22Schris      $acl_config = join("",file(DOKU_CONF.'acl.auth.php'));
193*11e2ce22Schris
194*11e2ce22Schris      // max level for pagenames is edit
195*11e2ce22Schris      if(strpos($acl_scope,'*') === false) {
196*11e2ce22Schris        if($acl_level > AUTH_EDIT) $acl_level = AUTH_EDIT;
197*11e2ce22Schris      }
198*11e2ce22Schris
199*11e2ce22Schris      $new_acl = "$acl_scope\t$acl_user\t$acl_level\n";
200*11e2ce22Schris
201*11e2ce22Schris      $new_config = $acl_config.$new_acl;
202*11e2ce22Schris
203*11e2ce22Schris      return io_saveFile(DOKU_CONF.'acl.auth.php', $new_config);
204*11e2ce22Schris    }
205*11e2ce22Schris
206*11e2ce22Schris    /**
207*11e2ce22Schris     * remove acl-entry from conf/acl.auth.php
208*11e2ce22Schris     *
209*11e2ce22Schris     * @author  Frank Schubert <frank@schokilade.de>
210*11e2ce22Schris     */
211*11e2ce22Schris    function admin_acl_del($acl_scope, $acl_user){
212*11e2ce22Schris      $acl_config = file(DOKU_CONF.'acl.auth.php');
213*11e2ce22Schris
214*11e2ce22Schris      $acl_pattern = '^'.preg_quote($acl_scope,'/').'\s+'.$acl_user.'\s+[0-8].*$';
215*11e2ce22Schris
216*11e2ce22Schris      // save all non!-matching #FIXME invert is available from 4.2.0 only!
217*11e2ce22Schris      $new_config = preg_grep("/$acl_pattern/", $acl_config, PREG_GREP_INVERT);
218*11e2ce22Schris
219*11e2ce22Schris      return io_saveFile(DOKU_CONF.'acl.auth.php', join('',$new_config));
220*11e2ce22Schris    }
221*11e2ce22Schris
222*11e2ce22Schris    // --- HTML OUTPUT FUNCTIONS BELOW --- //
223*11e2ce22Schris
224*11e2ce22Schris    /**
225*11e2ce22Schris     * print tablerows with the current permissions for one id
226*11e2ce22Schris     *
227*11e2ce22Schris     * @author  Frank Schubert <frank@schokilade.de>
228*11e2ce22Schris     * @author  Andreas Gohr <andi@splitbrain.org>
229*11e2ce22Schris     */
230*11e2ce22Schris    function admin_acl_html_dropdown($id){
231*11e2ce22Schris      global $lang;
232*11e2ce22Schris      $cur = $id;
233*11e2ce22Schris      $ret = '';
234*11e2ce22Schris      $opt = array();
235*11e2ce22Schris
236*11e2ce22Schris      //prepare all options
237*11e2ce22Schris
238*11e2ce22Schris      // current page
239*11e2ce22Schris      $opt[] = array('key'=> $id, 'val'=> $id.' ('.$lang['page'].')');
240*11e2ce22Schris
241*11e2ce22Schris      // additional namespaces
242*11e2ce22Schris      while(($id=getNS($id)) !== false){
243*11e2ce22Schris        $opt[] = array('key'=> $id.':*', 'val'=> $id.':* ('.$lang['namespace'].')');
244*11e2ce22Schris      }
245*11e2ce22Schris
246*11e2ce22Schris      // the top namespace
247*11e2ce22Schris      $opt[] = array('key'=> '*', 'val'=> '* ('.$lang['namespace'].')');
248*11e2ce22Schris
249*11e2ce22Schris      // set sel on second entry (current namespace)
250*11e2ce22Schris      $opt[1]['sel'] = ' selected="selected"';
251*11e2ce22Schris
252*11e2ce22Schris      // flip options
253*11e2ce22Schris      $opt = array_reverse($opt);
254*11e2ce22Schris
255*11e2ce22Schris      // create HTML
256*11e2ce22Schris      $att = array( 'name'  => 'acl_scope',
257*11e2ce22Schris                    'class' => 'edit',
258*11e2ce22Schris                    'title' => $lang['page'].'/'.$lang['namespace']);
259*11e2ce22Schris      $ret .= '<select '.html_attbuild($att).'>';
260*11e2ce22Schris      foreach($opt as $o){
261*11e2ce22Schris        $ret .= '<option value="'.$o['key'].'"'.$o['sel'].'>'.$o['val'].'</option>';
262*11e2ce22Schris      }
263*11e2ce22Schris      $ret .= '</select>';
264*11e2ce22Schris
265*11e2ce22Schris      return $ret;
266*11e2ce22Schris    }
267*11e2ce22Schris
268*11e2ce22Schris    /**
269*11e2ce22Schris     * print tablerows with the current permissions for one id
270*11e2ce22Schris     *
271*11e2ce22Schris     * @author  Frank Schubert <frank@schokilade.de>
272*11e2ce22Schris     * @author  Andreas Gohr <andi@splitbrain.org>
273*11e2ce22Schris     */
274*11e2ce22Schris    function admin_acl_html_new(){
275*11e2ce22Schris      global $lang;
276*11e2ce22Schris      global $ID;
277*11e2ce22Schris
278*11e2ce22Schris      // table headers
279*11e2ce22Schris      ptln('<tr>',2);
280*11e2ce22Schris      ptln('  <th class="leftalign" colspan="3">'.$lang['acl_new'].'</th>',2);
281*11e2ce22Schris      ptln('</tr>',2);
282*11e2ce22Schris
283*11e2ce22Schris      ptln('<tr>',2);
284*11e2ce22Schris
285*11e2ce22Schris      ptln('<td class="centeralign" colspan="3">',4);
286*11e2ce22Schris
287*11e2ce22Schris      ptln('  <form method="post" action="'.wl($ID).'">',4);
288*11e2ce22Schris      ptln('    <input type="hidden" name="do"   value="admin" />',4);
289*11e2ce22Schris      ptln('    <input type="hidden" name="page" value="acl" />',4);
290*11e2ce22Schris      ptln('    <input type="hidden" name="acl_cmd" value="save" />',4);
291*11e2ce22Schris
292*11e2ce22Schris      //scope select
293*11e2ce22Schris      ptln($lang['acl_perms'],4);
294*11e2ce22Schris      ptln($this->admin_acl_html_dropdown($ID),4);
295*11e2ce22Schris
296*11e2ce22Schris      $att = array( 'name'  => 'acl_type',
297*11e2ce22Schris                    'class' => 'edit',
298*11e2ce22Schris                    'title' => $lang['acl_user'].'/'.$lang['acl_group']);
299*11e2ce22Schris      ptln('    <select '.html_attbuild($att).'>',4);
300*11e2ce22Schris      ptln('      <option value="@">'.$lang['acl_group'].'</option>',4);
301*11e2ce22Schris      ptln('      <option value="">'.$lang['acl_user'].'</option>',4);
302*11e2ce22Schris      ptln('    </select>',4);
303*11e2ce22Schris
304*11e2ce22Schris      $att = array( 'name'  => 'acl_user',
305*11e2ce22Schris                    'type'  => 'text',
306*11e2ce22Schris                    'class' => 'edit',
307*11e2ce22Schris                    'title' => $lang['acl_user'].'/'.$lang['acl_group']);
308*11e2ce22Schris      ptln('    <input '.html_attbuild($att).' />',4);
309*11e2ce22Schris      ptln('    <br />');
310*11e2ce22Schris
311*11e2ce22Schris      ptln(     $this->admin_acl_html_checkboxes(0,false),8);
312*11e2ce22Schris
313*11e2ce22Schris      ptln('    <input type="submit" class="edit" value="'.$lang['btn_save'].'" />',4);
314*11e2ce22Schris      ptln('  </form>');
315*11e2ce22Schris      ptln('</tr>',2);
316*11e2ce22Schris    }
317*11e2ce22Schris
318*11e2ce22Schris    /**
319*11e2ce22Schris     * print tablerows with the current permissions for one id
320*11e2ce22Schris     *
321*11e2ce22Schris     * @author  Frank Schubert <frank@schokilade.de>
322*11e2ce22Schris     * @author  Andreas Gohr <andi@splitbrain.org>
323*11e2ce22Schris     */
324*11e2ce22Schris    function admin_acl_html_current($id,$permissions){
325*11e2ce22Schris      global $lang;
326*11e2ce22Schris      global $ID;
327*11e2ce22Schris
328*11e2ce22Schris      //is it a page?
329*11e2ce22Schris      if(substr($id,-1) == '*'){
330*11e2ce22Schris        $ispage = false;
331*11e2ce22Schris      }else{
332*11e2ce22Schris        $ispage = true;
333*11e2ce22Schris      }
334*11e2ce22Schris
335*11e2ce22Schris      // table headers
336*11e2ce22Schris      ptln('  <tr>');
337*11e2ce22Schris      ptln('    <th class="leftalign" colspan="3">');
338*11e2ce22Schris      ptln($lang['acl_perms'],6);
339*11e2ce22Schris      if($ispage){
340*11e2ce22Schris        ptln($lang['page'],6);
341*11e2ce22Schris      }else{
342*11e2ce22Schris        ptln($lang['namespace'],6);
343*11e2ce22Schris      }
344*11e2ce22Schris      ptln('<em>'.$id.'</em>',6);
345*11e2ce22Schris      ptln('    </th>');
346*11e2ce22Schris      ptln('  </tr>');
347*11e2ce22Schris
348*11e2ce22Schris      sort($permissions);
349*11e2ce22Schris
350*11e2ce22Schris      foreach ($permissions as $conf){
351*11e2ce22Schris        //userfriendly group/user display
352*11e2ce22Schris        if(substr($conf['name'],0,1)=="@"){
353*11e2ce22Schris          $group = $lang['acl_group'];
354*11e2ce22Schris          $name  = substr($conf['name'],1);
355*11e2ce22Schris          $type  = '@';
356*11e2ce22Schris        }else{
357*11e2ce22Schris          $group = $lang['acl_user'];
358*11e2ce22Schris          $name  = $conf['name'];
359*11e2ce22Schris          $type  = '';
360*11e2ce22Schris        }
361*11e2ce22Schris
362*11e2ce22Schris        ptln('<tr>',2);
363*11e2ce22Schris        ptln('<td class="leftalign">'.$group.' '.$name.'</td>',4);
364*11e2ce22Schris
365*11e2ce22Schris        // update form
366*11e2ce22Schris        ptln('<td class="centeralign">',4);
367*11e2ce22Schris        ptln('  <form method="post" action="'.wl($ID).'">',4);
368*11e2ce22Schris        ptln('    <input type="hidden" name="do"   value="admin" />',4);
369*11e2ce22Schris        ptln('    <input type="hidden" name="page" value="acl" />',4);
370*11e2ce22Schris        ptln('    <input type="hidden" name="acl_cmd"   value="save" />',4);
371*11e2ce22Schris        ptln('    <input type="hidden" name="acl_scope" value="'.formtext($id).'" />',4);
372*11e2ce22Schris        ptln('    <input type="hidden" name="acl_type" value="'.$type.'" />',4);
373*11e2ce22Schris        ptln('    <input type="hidden" name="acl_user"  value="'.formtext($name).'" />',4);
374*11e2ce22Schris        ptln(     $this->admin_acl_html_checkboxes($conf['perm'],$ispage),8);
375*11e2ce22Schris        ptln('    <input type="submit" class="edit" value="'.$lang['btn_update'].'" />',4);
376*11e2ce22Schris        ptln('  </form>');
377*11e2ce22Schris        ptln('</td>',4);
378*11e2ce22Schris
379*11e2ce22Schris
380*11e2ce22Schris        // deletion form
381*11e2ce22Schris
382*11e2ce22Schris        $ask  = $lang['del_confirm'].'\\n';
383*11e2ce22Schris        $ask .= $id.'  '.$conf['name'].'  '.$conf['perm'];
384*11e2ce22Schris        ptln('<td class="centeralign">',4);
385*11e2ce22Schris        ptln('  <form method="post" action="'.wl($ID).'" onsubmit="return confirm(\''.$ask.'\')">',4);
386*11e2ce22Schris        ptln('    <input type="hidden" name="do"        value="admin" />',4);
387*11e2ce22Schris        ptln('    <input type="hidden" name="page"      value="acl" />',4);
388*11e2ce22Schris        ptln('    <input type="hidden" name="acl_cmd"   value="delete" />',4);
389*11e2ce22Schris        ptln('    <input type="hidden" name="acl_scope" value="'.formtext($id).'" />',4);
390*11e2ce22Schris        ptln('    <input type="hidden" name="acl_type" value="'.$type.'" />',4);
391*11e2ce22Schris        ptln('    <input type="hidden" name="acl_user"  value="'.formtext($name).'" />',4);
392*11e2ce22Schris        ptln('    <input type="submit" class="edit" value="'.$lang['btn_delete'].'" />',4);
393*11e2ce22Schris        ptln('  </form>',4);
394*11e2ce22Schris        ptln('</td>',4);
395*11e2ce22Schris
396*11e2ce22Schris        ptln('</tr>',2);
397*11e2ce22Schris      }
398*11e2ce22Schris
399*11e2ce22Schris    }
400*11e2ce22Schris
401*11e2ce22Schris
402*11e2ce22Schris    /**
403*11e2ce22Schris     * print the permission checkboxes
404*11e2ce22Schris     *
405*11e2ce22Schris     * @author  Frank Schubert <frank@schokilade.de>
406*11e2ce22Schris     * @author  Andreas Gohr <andi@splitbrain.org>
407*11e2ce22Schris     */
408*11e2ce22Schris    function admin_acl_html_checkboxes($setperm,$ispage){
409*11e2ce22Schris      global $lang;
410*11e2ce22Schris
411*11e2ce22Schris      static $label = 0; //number labels
412*11e2ce22Schris      $ret = '';
413*11e2ce22Schris
414*11e2ce22Schris      foreach(array(AUTH_READ,AUTH_EDIT,AUTH_CREATE,AUTH_UPLOAD,AUTH_DELETE) as $perm){
415*11e2ce22Schris        $label += 1;
416*11e2ce22Schris
417*11e2ce22Schris        //general checkbox attributes
418*11e2ce22Schris        $atts = array( 'type'  => 'checkbox',
419*11e2ce22Schris                       'id'    => 'pbox'.$label,
420*11e2ce22Schris                       'name'  => 'acl_perm[]',
421*11e2ce22Schris                       'value' => $perm );
422*11e2ce22Schris        //dynamic attributes
423*11e2ce22Schris        if($setperm >= $perm) $atts['checked']  = 'checked';
424*11e2ce22Schris    #        if($perm > AUTH_READ) $atts['onchange'] = #FIXME JS to autoadd lower perms
425*11e2ce22Schris        if($ispage && $perm > AUTH_EDIT) $atts['disabled'] = 'disabled';
426*11e2ce22Schris
427*11e2ce22Schris        //build code
428*11e2ce22Schris        $ret .= '<label for="pbox'.$label.'" title="'.$lang['acl_perm'.$perm].'">';
429*11e2ce22Schris        $ret .= '<input '.html_attbuild($atts).' />';
430*11e2ce22Schris        $ret .= $lang['acl_perm'.$perm];
431*11e2ce22Schris        $ret .= "</label>\n";
432*11e2ce22Schris      }
433*11e2ce22Schris      return $ret;
434*11e2ce22Schris    }
435*11e2ce22Schris
436*11e2ce22Schris}