[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/lib/plugins/acl/ -> admin.php (source)

   1  <?php
   2  use 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   */
  17  class admin_plugin_acl extends DokuWiki_Admin_Plugin
  18  {
  19      public $acl = null;
  20      protected $ns  = null;
  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 = null;
  30      protected $who = '';
  31      protected $usersgroups = array();
  32      protected $specials = array();
  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 = array('id' => cleanID($INPUT->str('current_ns')), 'type' => 'd');
  77          } elseif ($INPUT->str('current_id')) {
  78              $this->current_item = array('id' => cleanID($INPUT->str('current_id')), 'type' => 'f');
  79          } elseif ($this->ns) {
  80              $this->current_item = array('id' => $this->ns, 'type' => 'd');
  81          } else {
  82              $this->current_item = array('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 = array();
 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'], join('', $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 = array(
 215                      'do'=>'admin',
 216                      'page'=>'acl',
 217                  );
 218          if ($this->ns) $opts['ns'] = $this->ns;
 219          if ($this->who) $opts['acl_w'] = $this->who;
 220  
 221          if (is_null($addopts)) return $opts;
 222          return array_merge($opts, $addopts);
 223      }
 224  
 225      /**
 226       * Display a tree menu to select a page or namespace
 227       *
 228       * @author Andreas Gohr <andi@splitbrain.org>
 229       */
 230      protected function makeExplorer()
 231      {
 232          global $conf;
 233          global $ID;
 234          global $lang;
 235  
 236          $ns  = $this->ns;
 237          if (empty($ns)) {
 238              $ns = dirname(str_replace(':', '/', $ID));
 239              if ($ns == '.') $ns ='';
 240          } elseif ($ns == '*') {
 241              $ns ='';
 242          }
 243          $ns  = utf8_encodeFN(str_replace(':', '/', $ns));
 244  
 245          $data = $this->makeTree($ns);
 246  
 247          // wrap a list with the root level around the other namespaces
 248          array_unshift($data, array( 'level' => 0, 'id' => '*', 'type' => 'd',
 249                     'open' =>'true', 'label' => '['.$lang['mediaroot'].']'));
 250  
 251          echo html_buildlist(
 252              $data,
 253              'acl',
 254              array($this, 'makeTreeItem'),
 255              array($this, 'makeListItem')
 256          );
 257      }
 258  
 259      /**
 260       * get a combined list of media and page files
 261       *
 262       * also called via AJAX
 263       *
 264       * @param string $folder an already converted filesystem folder of the current namespace
 265       * @param string $limit limit the search to this folder
 266       * @return array
 267       */
 268      public function makeTree($folder, $limit = '')
 269      {
 270          global $conf;
 271  
 272          // read tree structure from pages and media
 273          $data = array();
 274          search($data, $conf['datadir'], 'search_index', array('ns' => $folder), $limit);
 275          $media = array();
 276          search($media, $conf['mediadir'], 'search_index', array('ns' => $folder, 'nofiles' => true), $limit);
 277          $data = array_merge($data, $media);
 278          unset($media);
 279  
 280          // combine by sorting and removing duplicates
 281          usort($data, array($this, 'treeSort'));
 282          $count = count($data);
 283          if ($count>0) for ($i=1; $i<$count; $i++) {
 284              if ($data[$i-1]['id'] == $data[$i]['id'] && $data[$i-1]['type'] == $data[$i]['type']) {
 285                  unset($data[$i]);
 286                  $i++;  // duplicate found, next $i can't be a duplicate, so skip forward one
 287              }
 288          }
 289          return $data;
 290      }
 291  
 292      /**
 293       * usort callback
 294       *
 295       * Sorts the combined trees of media and page files
 296       */
 297      public function treeSort($a, $b)
 298      {
 299          // handle the trivial cases first
 300          if ($a['id'] == '') return -1;
 301          if ($b['id'] == '') return 1;
 302          // split up the id into parts
 303          $a_ids = explode(':', $a['id']);
 304          $b_ids = explode(':', $b['id']);
 305          // now loop through the parts
 306          while (count($a_ids) && count($b_ids)) {
 307              // compare each level from upper to lower
 308              // until a non-equal component is found
 309              $cur_result = Sort::strcmp(array_shift($a_ids), array_shift($b_ids));
 310              if ($cur_result) {
 311                  // if one of the components is the last component and is a file
 312                  // and the other one is either of a deeper level or a directory,
 313                  // the file has to come after the deeper level or directory
 314                  if (empty($a_ids) && $a['type'] == 'f' && (count($b_ids) || $b['type'] == 'd')) return 1;
 315                  if (empty($b_ids) && $b['type'] == 'f' && (count($a_ids) || $a['type'] == 'd')) return -1;
 316                  return $cur_result;
 317              }
 318          }
 319          // The two ids seem to be equal. One of them might however refer
 320          // to a page, one to a namespace, the namespace needs to be first.
 321          if (empty($a_ids) && empty($b_ids)) {
 322              if ($a['type'] == $b['type']) return 0;
 323              if ($a['type'] == 'f') return 1;
 324              return -1;
 325          }
 326          // Now the empty part is either a page in the parent namespace
 327          // that obviously needs to be after the namespace
 328          // Or it is the namespace that contains the other part and should be
 329          // before that other part.
 330          if (empty($a_ids)) return ($a['type'] == 'd') ? -1 : 1;
 331          if (empty($b_ids)) return ($b['type'] == 'd') ? 1 : -1;
 332          return 0; //shouldn't happen
 333      }
 334  
 335      /**
 336       * Display the current ACL for selected where/who combination with
 337       * selectors and modification form
 338       *
 339       * @author Andreas Gohr <andi@splitbrain.org>
 340       */
 341      protected function printDetail()
 342      {
 343          global $ID;
 344  
 345          echo '<form action="'.wl().'" method="post" accept-charset="utf-8"><div class="no">'.NL;
 346  
 347          echo '<div id="acl__user">';
 348          echo $this->getLang('acl_perms').' ';
 349          $inl =  $this->makeSelect();
 350          echo '<input type="text" name="acl_w" class="edit" value="'.(($inl)?'':hsc(ltrim($this->who, '@'))).'" />'.NL;
 351          echo '<button type="submit">'.$this->getLang('btn_select').'</button>'.NL;
 352          echo '</div>'.NL;
 353  
 354          echo '<div id="acl__info">';
 355          $this->printInfo();
 356          echo '</div>';
 357  
 358          echo '<input type="hidden" name="ns" value="'.hsc($this->ns).'" />'.NL;
 359          echo '<input type="hidden" name="id" value="'.hsc($ID).'" />'.NL;
 360          echo '<input type="hidden" name="do" value="admin" />'.NL;
 361          echo '<input type="hidden" name="page" value="acl" />'.NL;
 362          echo '<input type="hidden" name="sectok" value="'.getSecurityToken().'" />'.NL;
 363          echo '</div></form>'.NL;
 364      }
 365  
 366      /**
 367       * Print info and editor
 368       *
 369       * also loaded via Ajax
 370       */
 371      public function printInfo()
 372      {
 373          global $ID;
 374  
 375          if ($this->who) {
 376              $current = $this->getExactPermisson();
 377  
 378              // explain current permissions
 379              $this->printExplanation($current);
 380              // load editor
 381              $this->printAclEditor($current);
 382          } else {
 383              echo '<p>';
 384              if ($this->ns) {
 385                  printf($this->getLang('p_choose_ns'), hsc($this->ns));
 386              } else {
 387                  printf($this->getLang('p_choose_id'), hsc($ID));
 388              }
 389              echo '</p>';
 390  
 391              echo $this->locale_xhtml('help');
 392          }
 393      }
 394  
 395      /**
 396       * Display the ACL editor
 397       *
 398       * @author Andreas Gohr <andi@splitbrain.org>
 399       */
 400      protected function printAclEditor($current)
 401      {
 402          global $lang;
 403  
 404          echo '<fieldset>';
 405          if (is_null($current)) {
 406              echo '<legend>'.$this->getLang('acl_new').'</legend>';
 407          } else {
 408              echo '<legend>'.$this->getLang('acl_mod').'</legend>';
 409          }
 410  
 411          echo $this->makeCheckboxes($current, empty($this->ns), 'acl');
 412  
 413          if (is_null($current)) {
 414              echo '<button type="submit" name="cmd[save]">'.$lang['btn_save'].'</button>'.NL;
 415          } else {
 416              echo '<button type="submit" name="cmd[save]">'.$lang['btn_update'].'</button>'.NL;
 417              echo '<button type="submit" name="cmd[del]">'.$lang['btn_delete'].'</button>'.NL;
 418          }
 419  
 420          echo '</fieldset>';
 421      }
 422  
 423      /**
 424       * Explain the currently set permissions in plain english/$lang
 425       *
 426       * @author Andreas Gohr <andi@splitbrain.org>
 427       */
 428      protected function printExplanation($current)
 429      {
 430          global $ID;
 431          global $auth;
 432  
 433          $who = $this->who;
 434          $ns  = $this->ns;
 435  
 436          // prepare where to check
 437          if ($ns) {
 438              if ($ns == '*') {
 439                  $check='*';
 440              } else {
 441                  $check=$ns.':*';
 442              }
 443          } else {
 444              $check = $ID;
 445          }
 446  
 447          // prepare who to check
 448          if ($who[0] == '@') {
 449              $user   = '';
 450              $groups = array(ltrim($who, '@'));
 451          } else {
 452              $user = $who;
 453              $info = $auth->getUserData($user);
 454              if ($info === false) {
 455                  $groups = array();
 456              } else {
 457                  $groups = $info['grps'];
 458              }
 459          }
 460  
 461          // check the permissions
 462          $perm = auth_aclcheck($check, $user, $groups);
 463  
 464          // build array of named permissions
 465          $names = array();
 466          if ($perm) {
 467              if ($ns) {
 468                  if ($perm >= AUTH_DELETE) $names[] = $this->getLang('acl_perm16');
 469                  if ($perm >= AUTH_UPLOAD) $names[] = $this->getLang('acl_perm8');
 470                  if ($perm >= AUTH_CREATE) $names[] = $this->getLang('acl_perm4');
 471              }
 472              if ($perm >= AUTH_EDIT) $names[] = $this->getLang('acl_perm2');
 473              if ($perm >= AUTH_READ) $names[] = $this->getLang('acl_perm1');
 474              $names = array_reverse($names);
 475          } else {
 476              $names[] = $this->getLang('acl_perm0');
 477          }
 478  
 479          // print permission explanation
 480          echo '<p>';
 481          if ($user) {
 482              if ($ns) {
 483                  printf($this->getLang('p_user_ns'), hsc($who), hsc($ns), join(', ', $names));
 484              } else {
 485                  printf($this->getLang('p_user_id'), hsc($who), hsc($ID), join(', ', $names));
 486              }
 487          } else {
 488              if ($ns) {
 489                  printf($this->getLang('p_group_ns'), hsc(ltrim($who, '@')), hsc($ns), join(', ', $names));
 490              } else {
 491                  printf($this->getLang('p_group_id'), hsc(ltrim($who, '@')), hsc($ID), join(', ', $names));
 492              }
 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(array('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(array('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=array();
 578          $usersgroups = array();
 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 (array(AUTH_NONE,AUTH_READ,AUTH_EDIT,AUTH_CREATE,AUTH_UPLOAD,AUTH_DELETE) as $perm) {
 773              $label += 1;
 774  
 775              //general checkbox attributes
 776              $atts = array( 'type'  => 'radio',
 777                             'id'    => 'pbox'.$label,
 778                             'name'  => $name,
 779                             'value' => $perm );
 780              //dynamic attributes
 781              if (!is_null($setperm) && $setperm == $perm) $atts['checked']  = 'checked';
 782              if ($ispage && $perm > AUTH_EDIT) {
 783                  $atts['disabled'] = 'disabled';
 784                  $class = ' class="disabled"';
 785              } else {
 786                  $class = '';
 787              }
 788  
 789              //build code
 790              $ret .= '<label for="pbox'.$label.'"'.$class.'>';
 791              $ret .= '<input '.buildAttributes($atts).' />&#160;';
 792              $ret .= $this->getLang('acl_perm'.$perm);
 793              $ret .= '</label>'.NL;
 794          }
 795          return $ret;
 796      }
 797  
 798      /**
 799       * Print a user/group selector (reusing already used users and groups)
 800       *
 801       * @author  Andreas Gohr <andi@splitbrain.org>
 802       */
 803      protected function makeSelect()
 804      {
 805          $inlist = false;
 806          $usel = '';
 807          $gsel = '';
 808  
 809          if ($this->who &&
 810             !in_array($this->who, $this->usersgroups) &&
 811             !in_array($this->who, $this->specials)) {
 812              if ($this->who[0] == '@') {
 813                  $gsel = ' selected="selected"';
 814              } else {
 815                  $usel = ' selected="selected"';
 816              }
 817          } else {
 818              $inlist = true;
 819          }
 820  
 821          echo '<select name="acl_t" class="edit">'.NL;
 822          echo '  <option value="__g__" class="aclgroup"'.$gsel.'>'.$this->getLang('acl_group').'</option>'.NL;
 823          echo '  <option value="__u__"  class="acluser"'.$usel.'>'.$this->getLang('acl_user').'</option>'.NL;
 824          if (!empty($this->specials)) {
 825              echo '  <optgroup label="&#160;">'.NL;
 826              foreach ($this->specials as $ug) {
 827                  if ($ug == $this->who) {
 828                      $sel    = ' selected="selected"';
 829                      $inlist = true;
 830                  } else {
 831                      $sel = '';
 832                  }
 833  
 834                  if ($ug[0] == '@') {
 835                          echo '  <option value="'.hsc($ug).'" class="aclgroup"'.$sel.'>'.hsc($ug).'</option>'.NL;
 836                  } else {
 837                          echo '  <option value="'.hsc($ug).'" class="acluser"'.$sel.'>'.hsc($ug).'</option>'.NL;
 838                  }
 839              }
 840              echo '  </optgroup>'.NL;
 841          }
 842          if (!empty($this->usersgroups)) {
 843              echo '  <optgroup label="&#160;">'.NL;
 844              foreach ($this->usersgroups as $ug) {
 845                  if ($ug == $this->who) {
 846                      $sel    = ' selected="selected"';
 847                      $inlist = true;
 848                  } else {
 849                      $sel = '';
 850                  }
 851  
 852                  if ($ug[0] == '@') {
 853                          echo '  <option value="'.hsc($ug).'" class="aclgroup"'.$sel.'>'.hsc($ug).'</option>'.NL;
 854                  } else {
 855                          echo '  <option value="'.hsc($ug).'" class="acluser"'.$sel.'>'.hsc($ug).'</option>'.NL;
 856                  }
 857              }
 858              echo '  </optgroup>'.NL;
 859          }
 860          echo '</select>'.NL;
 861          return $inlist;
 862      }
 863  }