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