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