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