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