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