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}