]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/PagePerm.php
change rename action from access perm change to edit: allow the signed in user to...
[SourceForge/phpwiki.git] / lib / PagePerm.php
1 <?php // -*-php-*-
2 rcs_id('$Id: PagePerm.php,v 1.42 2007-08-25 18:03:34 rurban Exp $');
3 /*
4  Copyright 2004 $ThePhpWikiProgrammingTeam
5
6  This file is part of PhpWiki.
7
8  PhpWiki is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 2 of the License, or
11  (at your option) any later version.
12
13  PhpWiki is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  GNU General Public License for more details.
17
18  You should have received a copy of the GNU General Public License
19  along with PhpWiki; if not, write to the Free Software
20  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 /**
24    Permissions per page and action based on current user, 
25    ownership and group membership implemented with ACL's (Access Control Lists),
26    opposed to the simplier unix like ugo:rwx system.
27    The previous system was only based on action and current user. (lib/main.php)
28
29    Permissions may be inherited from its parent pages, a optional the 
30    optional master page ("."), and predefined default permissions, if "." 
31    is not defined.
32    Pagenames starting with "." have special default permissions.
33    For Authentication see WikiUserNew.php, WikiGroup.php and main.php
34    Page Permissions are in PhpWiki since v1.3.9 and enabled since v1.4.0
35
36    This file might replace the following functions from main.php:
37      Request::_notAuthorized($require_level)
38        display the denied message and optionally a login form 
39        to gain higher privileges
40      Request::getActionDescription($action)
41        helper to localize the _notAuthorized message per action, 
42        when login is tried.
43      Request::getDisallowedActionDescription($action)
44        helper to localize the _notAuthorized message per action, 
45        when it aborts
46      Request::requiredAuthority($action)
47        returns the needed user level
48        has a hook for plugins on POST
49      Request::requiredAuthorityForAction($action)
50        just returns the level per action, will be replaced with the 
51        action + page pair
52
53      The defined main.php actions map to simplier access types:
54        browse => view
55        edit   => edit
56        create => edit or create
57        remove => remove
58        rename => change
59        store prefs => change
60        list in PageList => list
61 */
62
63 /* Symbolic special ACL groups. Untranslated to be stored in page metadata*/
64 define('ACL_EVERY',        '_EVERY');
65 define('ACL_ANONYMOUS',    '_ANONYMOUS');
66 define('ACL_BOGOUSER',     '_BOGOUSER');
67 define('ACL_HASHOMEPAGE',  '_HASHOMEPAGE');
68 define('ACL_SIGNED',       '_SIGNED');
69 define('ACL_AUTHENTICATED','_AUTHENTICATED');
70 define('ACL_ADMIN',        '_ADMIN');
71 define('ACL_OWNER',        '_OWNER');
72 define('ACL_CREATOR',      '_CREATOR');
73
74 // Return an page permissions array for this page.
75 // To provide ui helpers to view and change page permissions:
76 //   <tr><th>Group</th><th>Access</th><th>Allow or Forbid</th></tr>
77 //   <tr><td>$group</td><td>_($access)</td><td> [ ] </td></tr>
78 function pagePermissions($pagename) {
79     global $request;
80     $page = $request->getPage($pagename);
81     // Page not found (new page); returned inherited permissions, to be displayed in gray
82     if (! $page->exists() ) {
83         if ($pagename == '.') // stop recursion
84             return array('default', new PagePermission());
85         else {
86             return array('inherited', pagePermissions(getParentPage($pagename)));
87         }
88     } elseif ($perm = getPagePermissions($page)) {
89         return array('page', $perm);
90     // or no permissions defined; returned inherited permissions, to be displayed in gray
91     } elseif ($pagename == '.') { // stop recursion in pathological case. 
92         // "." defined, without any acl
93         return array('default', new PagePermission());
94     } else {
95         return array('inherited', pagePermissions(getParentPage($pagename)));
96     }
97 }
98
99 function pagePermissionsSimpleFormat($perm_tree, $owner, $group=false) {
100     list($type,$perm) = pagePermissionsAcl($perm_tree[0], $perm_tree);
101     /*
102     $type = $perm_tree[0];
103     $perm = pagePermissionsAcl($perm_tree);
104     if (is_object($perm_tree[1]))
105         $perm = $perm_tree[1];
106     elseif (is_array($perm_tree[1])) {
107         $perm_tree = pagePermissionsSimpleFormat($perm_tree[1],$owner,$group);
108         if (isa($perm_tree[1],'pagepermission'))
109             $perm = $perm_tree[1];
110         elseif (isa($perm_tree,'htmlelement'))
111             return $perm_tree;
112     }
113     */
114     if ($type == 'page')
115         return HTML::tt(HTML::strong($perm->asRwxString($owner, $group)));
116     elseif ($type == 'default')
117         return HTML::tt($perm->asRwxString($owner, $group));
118     elseif ($type == 'inherited') {
119         return HTML::tt(array('class'=>'inherited', 'style'=>'color:#aaa;'),
120                         $perm->asRwxString($owner, $group));
121     }
122 }
123
124 function pagePermissionsAcl($type,$perm_tree) {
125     $perm = $perm_tree[1];
126     while (!is_object($perm)) {
127         $perm_tree = pagePermissionsAcl($type, $perm);
128         $perm = $perm_tree[1];
129     }
130     return array($type,$perm);
131 }
132
133 // view => who
134 // edit => who
135 function pagePermissionsAclFormat($perm_tree, $editable=false) {
136     list($type,$perm) = pagePermissionsAcl($perm_tree[0], $perm_tree);
137     if ($editable)
138         return $perm->asEditableTable($type);
139     else
140         return $perm->asTable($type);
141 }
142
143 /** 
144  * Check the permissions for the current action.
145  * Walk down the inheritance tree. Collect all permissions until 
146  * the minimum required level is gained, which is not 
147  * overruled by more specific forbid rules.
148  * Todo: cache result per access and page in session?
149  */
150 function requiredAuthorityForPage ($action) {
151     global $request;
152     $auth = _requiredAuthorityForPagename(action2access($action),
153                                           $request->getArg('pagename'));
154     assert($auth !== -1);
155     if ($auth)
156         return $request->_user->_level;
157     else
158         return WIKIAUTH_UNOBTAINABLE;
159 }
160
161 // Translate action or plugin to the simplier access types:
162 function action2access ($action) {
163     global $request;
164     switch ($action) {
165     case 'browse':
166     case 'viewsource':
167     case 'diff':
168     case 'select':
169     case 'xmlrpc':
170     case 'search':
171     case 'pdf':
172     case 'captcha':
173         return 'view';
174     case 'zip':
175     case 'ziphtml':
176     case 'dumpserial':
177     case 'dumphtml':
178         return 'dump';
179     // invent a new access-perm massedit? or switch back to change, or keep it at edit?
180     case _("PhpWikiAdministration")."/"._("Rename"):
181     case _("PhpWikiAdministration")."/"._("Replace"):
182     case 'replace':
183     case 'rename':
184     case 'revert':
185     case 'edit':
186         return 'edit';
187     case 'create':
188         $page = $request->getPage();
189         if (!$page->exists())
190             return 'create';
191         else
192             return 'view'; 
193         break;
194     case 'upload':
195     case 'loadfile': 
196         // probably create/edit but we cannot check all page permissions, can we?
197     case 'remove':
198     case 'lock':
199     case 'unlock':
200     case 'upgrade':
201     case 'chown':
202     case 'setacl':
203             return 'change';
204     default:
205         //Todo: Plugins should be able to override its access type
206         if (isWikiWord($action))
207             return 'view';
208         else
209             return 'change';
210         break;
211     }
212 }
213
214 // Recursive helper to do the real work.
215 // Using a simple perm cache for page-access pairs.
216 // Maybe page-(current+edit+change?)action pairs will help
217 function _requiredAuthorityForPagename($access, $pagename) {
218     static $permcache = array();
219     
220     if (array_key_exists($pagename, $permcache)
221         and array_key_exists($access, $permcache[$pagename]))
222         return $permcache[$pagename][$access];
223         
224     global $request;
225     $page = $request->getPage($pagename);
226     // Page not found; check against default permissions
227     if (! $page->exists() ) {
228         $perm = new PagePermission();
229         $result = ($perm->isAuthorized($access, $request->_user) === true);
230         $permcache[$pagename][$access] = $result;
231         return $result;
232     }
233     // no ACL defined; check for special dotfile or walk down
234     if (! ($perm = getPagePermissions($page))) { 
235         if ($pagename == '.') {
236             $perm = new PagePermission();
237             if ($perm->isAuthorized('change', $request->_user)) {
238                 // warn the user to set ACL of ".", if he has permissions to do so.
239                 trigger_error(". (dotpage == rootpage for inheriting pageperm ACLs) exists without any ACL!\n".
240                               "Please do ?action=setacl&pagename=.", E_USER_WARNING);
241             }
242             $result = ($perm->isAuthorized($access, $request->_user) === true);
243             $permcache[$pagename][$access] = $result;
244             return $result;
245         } elseif ($pagename[0] == '.') {
246             $perm = new PagePermission(PagePermission::dotPerms());
247             $result = ($perm->isAuthorized($access, $request->_user) === true);
248             $permcache[$pagename][$access] = $result;
249             return $result;
250         }
251         return _requiredAuthorityForPagename($access, getParentPage($pagename));
252     }
253     // ACL defined; check if isAuthorized returns true or false or undecided
254     $authorized = $perm->isAuthorized($access, $request->_user);
255     if ($authorized !== -1) { // interestingly true is also -1
256         $permcache[$pagename][$access] = $authorized;
257         return $authorized;
258     } elseif ($pagename == '.') {
259         return false;
260     } else {    
261         return _requiredAuthorityForPagename($access, getParentPage($pagename));
262     }
263 }
264
265 /**
266  * @param  string $pagename   page from which the parent page is searched.
267  * @return string parent      pagename or the (possibly pseudo) dot-pagename.
268  */
269 function getParentPage($pagename) {
270     if (isSubPage($pagename)) {
271         return subPageSlice($pagename, 0);
272     } else {
273         return '.';
274     }
275 }
276
277 // Read the ACL from the page
278 // Done: Not existing pages should NOT be queried. 
279 // Check the parent page instead and don't take the default ACL's
280 function getPagePermissions ($page) {
281     if ($hash = $page->get('perm'))  // hash => object
282         return new PagePermission(unserialize($hash));
283     else 
284         return false;
285 }
286
287 // Store the ACL in the page
288 function setPagePermissions ($page,$perm) {
289     $perm->store($page);
290 }
291
292 function getAccessDescription($access) {
293     static $accessDescriptions;
294     if (! $accessDescriptions) {
295         $accessDescriptions = array(
296                                     'list'     => _("List this page and all subpages"),
297                                     'view'     => _("View this page and all subpages"),
298                                     'edit'     => _("Edit this page and all subpages"),
299                                     'create'   => _("Create a new (sub)page"),
300                                     'dump'     => _("Download the page contents"),
301                                     'change'   => _("Change page attributes"),
302                                     'remove'   => _("Remove this page"),
303                                     );
304     }
305     if (in_array($access, array_keys($accessDescriptions)))
306         return $accessDescriptions[$access];
307     else
308         return $access;
309 }
310
311 // from php.net docs
312 function array_diff_assoc_recursive($array1, $array2) {
313     foreach ($array1 as $key => $value) {
314          if (is_array($value)) {
315              if (!is_array($array2[$key])) {
316                  $difference[$key] = $value;
317              } else {
318                  $new_diff = array_diff_assoc_recursive($value, $array2[$key]);
319                  if ($new_diff != false) {
320                      $difference[$key] = $new_diff;
321                  } 
322              }
323          } elseif(!isset($array2[$key]) || $array2[$key] != $value) {
324              $difference[$key] = $value;
325          }
326     }
327     return !isset($difference) ? 0 : $difference;
328 }
329
330 /**
331  * The ACL object per page. It is stored in a page, but can also 
332  * be merged with ACL's from other pages or taken from the master (pseudo) dot-file.
333  *
334  * A hash of "access" => "requires" pairs.
335  *   "access"   is a shortcut for common actions, which map to main.php actions
336  *   "requires" required username or groupname or any special group => true or false
337  *
338  * Define any special rules here, like don't list dot-pages.
339  */ 
340 class PagePermission {
341     var $perm;
342
343     function PagePermission($hash = array()) {
344         $this->_group = &$GLOBALS['request']->getGroup();
345         if (is_array($hash) and !empty($hash)) {
346             $accessTypes = $this->accessTypes();
347             foreach ($hash as $access => $requires) {
348                 if (in_array($access, $accessTypes))
349                     $this->perm[$access] = $requires;
350                 else
351                     trigger_error(sprintf(_("Unsupported ACL access type %s ignored."), $access),
352                                   E_USER_WARNING);
353             }
354         } else {
355             // set default permissions, the so called dot-file acl's
356             $this->perm = $this->defaultPerms();
357         }
358         return $this;
359     }
360
361     /**
362      * The workhorse to check the user against the current ACL pairs.
363      * Must translate the various special groups to the actual users settings 
364      * (userid, group membership).
365      */
366     function isAuthorized($access, $user) {
367         if (!empty($this->perm{$access})) {
368             $allow = -1;
369             foreach ($this->perm[$access] as $group => $bool) {
370                 if ($this->isMember($user, $group)) {
371                     return $bool;
372                 } elseif ($allow == -1) { // not a member and undecided: check other groups
373                     $allow = !$bool;
374                 }
375             }
376         }
377         return $allow; // undecided
378     }
379
380     /**
381      * Translate the various special groups to the actual users settings 
382      * (userid, group membership).
383      */
384     function isMember($user, $group) {
385         global $request;
386         if ($group === ACL_EVERY) return true;
387         if (!isset($this->_group)) $member =& $request->getGroup();
388         else $member =& $this->_group;
389         //$user = & $request->_user;
390         if ($group === ACL_ADMIN)   // WIKI_ADMIN or member of _("Administrators")
391             return $user->isAdmin() or 
392                    ($user->isAuthenticated() and 
393                    $member->isMember(GROUP_ADMIN));
394         if ($group === ACL_ANONYMOUS) 
395             return ! $user->isSignedIn();
396         if ($group === ACL_BOGOUSER)
397             if (ENABLE_USER_NEW)
398                 return isa($user,'_BogoUser') or 
399                       (isWikiWord($user->_userid) and $user->_level >= WIKIAUTH_BOGO);
400             else return isWikiWord($user->UserName());
401         if ($group === ACL_HASHOMEPAGE)
402             return $user->hasHomePage();
403         if ($group === ACL_SIGNED)
404             return $user->isSignedIn();
405         if ($group === ACL_AUTHENTICATED)
406             return $user->isAuthenticated();
407         if ($group === ACL_OWNER) {
408             if (!$user->isAuthenticated()) return false;
409             $page = $request->getPage();
410             $owner = $page->getOwner();
411             return ($owner === $user->UserName() 
412                     or $member->isMember($owner));
413         }
414         if ($group === ACL_CREATOR) {
415             if (!$user->isAuthenticated()) return false;
416             $page = $request->getPage();
417             $creator = $page->getCreator();
418             return ($creator === $user->UserName() 
419                     or $member->isMember($creator));
420         }
421         /* Or named groups or usernames.
422            Note: We don't seperate groups and users here. 
423            Users overrides groups with the same name. 
424         */
425         return $user->UserName() === $group or
426                $member->isMember($group);
427     }
428
429     /**
430      * returns hash of default permissions.
431      * check if the page '.' exists and returns this instead.
432      */
433     function defaultPerms() {
434         //Todo: check for the existance of '.' and take this instead.
435         //Todo: honor more config.ini auth settings here
436         $perm = array('view'   => array(ACL_EVERY => true),
437                       'edit'   => array(ACL_EVERY => true),
438                       'create' => array(ACL_EVERY => true),
439                       'list'   => array(ACL_EVERY => true),
440                       'remove' => array(ACL_ADMIN => true,
441                                         ACL_OWNER => true),
442                       'change' => array(ACL_ADMIN => true,
443                                         ACL_OWNER => true));
444         if (ZIPDUMP_AUTH)
445             $perm['dump'] = array(ACL_ADMIN => true,
446                                   ACL_OWNER => true);
447         else
448             $perm['dump'] = array(ACL_EVERY => true);
449         if (defined('REQUIRE_SIGNIN_BEFORE_EDIT') && REQUIRE_SIGNIN_BEFORE_EDIT)
450             $perm['edit'] = array(ACL_SIGNED => true);
451         // view:
452         if (!ALLOW_ANON_USER) {
453             if (!ALLOW_USER_PASSWORDS) 
454                 $perm['view'] = array(ACL_SIGNED => true);
455             else                
456                 $perm['view'] = array(ACL_AUTHENTICATED => true);
457             $perm['view'][ACL_BOGOUSER] = ALLOW_BOGO_LOGIN ? true : false;
458         }
459         // edit:
460         if (!ALLOW_ANON_EDIT) {
461             if (!ALLOW_USER_PASSWORDS) 
462                 $perm['edit'] = array(ACL_SIGNED => true);
463             else                
464                 $perm['edit'] = array(ACL_AUTHENTICATED => true);
465             $perm['edit'][ACL_BOGOUSER] = ALLOW_BOGO_LOGIN ? true : false;
466             $perm['create'] = $perm['edit'];
467         }
468         return $perm;
469     }
470
471     /**
472      * FIXME: check valid groups and access
473      */
474     function sanify() {
475         foreach ($this->perm as $access => $groups) {
476             foreach ($groups as $group => $bool) {
477                 $this->perm[$access][$group] = (boolean) $bool;
478             }
479         }
480     }
481
482     /**
483      * do a recursive comparison
484      */
485     function equal($otherperm) {
486         $diff = array_diff_assoc_recursive($this->perm, $otherperm);
487         return empty($diff);
488     }
489     
490     /**
491      * returns list of all supported access types.
492      */
493     function accessTypes() {
494         return array_keys(PagePermission::defaultPerms());
495     }
496
497     /**
498      * special permissions for dot-files, beginning with '.'
499      * maybe also for '_' files?
500      */
501     function dotPerms() {
502         $def = array(ACL_ADMIN => true,
503                      ACL_OWNER => true);
504         $perm = array();
505         foreach (PagePermission::accessTypes() as $access) {
506             $perm[$access] = $def;
507         }
508         return $perm;
509     }
510
511     /**
512      *  dead code. not needed inside the object. see getPagePermissions($page)
513      */
514     function retrieve($page) {
515         $hash = $page->get('perm');
516         if ($hash)  // hash => object
517             $perm = new PagePermission(unserialize($hash));
518         else 
519             $perm = new PagePermission();
520         $perm->sanify();
521         return $perm;
522     }
523
524     function store($page) {
525         // object => hash
526         $this->sanify();
527         return $page->set('perm',serialize($this->perm));
528     }
529
530     function groupName ($group) {
531         if ($group[0] == '_') return constant("GROUP".$group);
532         else return $group;
533     }
534     
535     /* type: page, default, inherited */
536     function asTable($type) {
537         $table = HTML::table();
538         foreach ($this->perm as $access => $perms) {
539             $td = HTML::table(array('class' => 'cal','valign' => 'top'));
540             foreach ($perms as $group => $bool) {
541                 $td->pushContent(HTML::tr(HTML::td(array('align'=>'right'),$group),
542                                                    HTML::td($bool ? '[X]' : '[ ]')));
543             }
544             $table->pushContent(HTML::tr(array('valign' => 'top'),
545                                          HTML::td($access),HTML::td($td)));
546         }
547         if ($type == 'default')
548             $table->setAttr('style','border: dotted thin black; background-color:#eee;');
549         elseif ($type == 'inherited')
550             $table->setAttr('style','border: dotted thin black; background-color:#ddd;');
551         elseif ($type == 'page')
552             $table->setAttr('style','border: solid thin black; font-weight: bold;');
553         return $table;
554     }
555     
556     /* type: page, default, inherited */
557     function asEditableTable($type) {
558         global $WikiTheme;
559         if (!isset($this->_group)) { 
560             $this->_group =& $GLOBALS['request']->getGroup();
561         }
562         $table = HTML::table();
563         $table->pushContent(HTML::tr(
564                                      HTML::th(array('align' => 'left'),
565                                               _("Access")),
566                                      HTML::th(array('align'=>'right'),
567                                               _("Group/User")),
568                                      HTML::th(_("Grant")),
569                                      HTML::th(_("Del/+")),
570                                      HTML::th(_("Description"))));
571         
572         $allGroups = $this->_group->_specialGroups();
573         foreach ($this->_group->getAllGroupsIn() as $group) {
574             if (!in_array($group,$this->_group->specialGroups()))
575                 $allGroups[] = $group;
576         }
577         //array_unique(array_merge($this->_group->getAllGroupsIn(),
578         $deletesrc = $WikiTheme->_findData('images/delete.png');
579         $addsrc = $WikiTheme->_findData('images/add.png');
580         $nbsp = HTML::raw('&nbsp;');
581         foreach ($this->perm as $access => $groups) {
582             //$permlist = HTML::table(array('class' => 'cal','valign' => 'top'));
583             $first_only = true;
584             $newperm = HTML::input(array('type' => 'checkbox',
585                                          'name' => "acl[_new_perm][$access]",
586                                          'value' => 1));
587             $addbutton = HTML::input(array('type' => 'checkbox',
588                                            'name' => "acl[_add_group][$access]",
589                                            //'src'  => $addsrc,
590                                            //'alt'   => "Add",
591                                            'title' => _("Add this ACL"),
592                                            'value' => 1));
593             $newgroup = HTML::select(array('name' => "acl[_new_group][$access]",
594                                            'style'=> 'text-align: right;',
595                                            'size' => 1));
596             foreach ($allGroups as $groupname) {
597                 if (!isset($groups[$groupname]))
598                     $newgroup->pushContent(HTML::option(array('value' => $groupname),
599                                                         $this->groupName($groupname)));
600             }
601             if (empty($groups)) {
602                 $addbutton->setAttr('checked','checked');
603                 $newperm->setAttr('checked','checked');
604                 $table->pushContent(
605                     HTML::tr(array('valign' => 'top'),
606                              HTML::td(HTML::strong($access.":")),
607                              HTML::td($newgroup),
608                              HTML::td($nbsp,$newperm),
609                              HTML::td($nbsp,$addbutton),
610                              HTML::td(HTML::em(getAccessDescription($access)))));
611             }
612             foreach ($groups as $group => $bool) {
613                 $checkbox = HTML::input(array('type' => 'checkbox',
614                                               'name' => "acl[$access][$group]",
615                                               'title' => _("Allow / Deny"),
616                                               'value' => 1));
617                 if ($bool) $checkbox->setAttr('checked','checked');
618                 $checkbox = HTML(HTML::input(array('type' => 'hidden',
619                                                    'name' => "acl[$access][$group]",
620                                                    'value' => 0)),
621                                  $checkbox);
622                 $deletebutton = HTML::input(array('type' => 'checkbox',
623                                                   'name' => "acl[_del_group][$access][$group]",
624                                                   'style' => 'background: #aaa url('.$deletesrc.')',
625                                                   //'src'  => $deletesrc,
626                                                   //'alt'   => "Del",
627                                                   'title' => _("Delete this ACL"),
628                                                   'value' => 1));
629                 if ($first_only) {
630                     $table->pushContent(
631                         HTML::tr(
632                                  HTML::td(HTML::strong($access.":")),
633                                  HTML::td(array('class' => 'cal-today','align'=>'right'),
634                                           HTML::strong($this->groupName($group))),
635                                  HTML::td(array('align'=>'center'),$nbsp,$checkbox),
636                                  HTML::td(array('align'=>'right','style' => 'background: #aaa url('.$deletesrc.') no-repeat'),$deletebutton),
637                                  HTML::td(HTML::em(getAccessDescription($access)))));
638                     $first_only = false;
639                 } else {
640                     $table->pushContent(
641                         HTML::tr(
642                                  HTML::td(),
643                                  HTML::td(array('class' => 'cal-today','align'=>'right'),
644                                           HTML::strong($this->groupName($group))),
645                                  HTML::td(array('align'=>'center'),$nbsp,$checkbox),
646                                  HTML::td(array('align'=>'right','style' => 'background: #aaa url('.$deletesrc.') no-repeat'),$deletebutton),
647                                  HTML::td()));
648                 }
649             }
650             if (!empty($groups))
651                 $table->pushContent(
652                     HTML::tr(array('valign' => 'top'),
653                              HTML::td(array('align'=>'right'),_("add ")),
654                              HTML::td($newgroup),
655                              HTML::td(array('align'=>'center'),$nbsp,$newperm),
656                              HTML::td(array('align'=>'right','style' => 'background: #ccc url('.$addsrc.') no-repeat'),$addbutton),
657                              HTML::td(HTML::small(_("Check to add this ACL")))));
658         }
659         if ($type == 'default')
660             $table->setAttr('style','border: dotted thin black; background-color:#eee;');
661         elseif ($type == 'inherited')
662             $table->setAttr('style','border: dotted thin black; background-color:#ddd;');
663         elseif ($type == 'page')
664             $table->setAttr('style','border: solid thin black; font-weight: bold;');
665         return $table;
666     }
667
668     // Print ACL as lines of [+-]user[,group,...]:access[,access...]
669     // Seperate acl's by "; " or whitespace
670     // See http://opag.ca/wiki/HelpOnAccessControlLists
671     // As used by WikiAdminSetAclSimple
672     function asAclLines() {
673         $s = ''; $line = '';
674         $this->sanify();
675         foreach ($this->perm as $access => $groups) {
676             // unify groups for same access+bool
677             //    view:CREATOR,-OWNER,
678             $line = $access.':';
679             foreach ($groups as $group => $bool) {
680                 $line .= ($bool?'':'-').$group.",";
681             }
682             if (substr($line,-1) == ',')
683                 $s .= substr($line,0,-1)."; ";
684         }
685         if (substr($s,-2) == '; ')
686             $s = substr($s,0,-2);
687         return $s;
688     }
689
690
691     // This is just a bad hack for testing.
692     // Simplify the ACL to a unix-like "rwx------+" string
693     // See getfacl(8)
694     function asRwxString($owner,$group=false) {
695         global $request;
696         // simplify object => rwxrw---x+ string as in cygwin (+ denotes additional ACLs)
697         $perm =& $this->perm;
698         // get effective user and group
699         $s = '---------+';
700         if (isset($perm['view'][$owner]) or 
701             (isset($perm['view'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
702             $s[0] = 'r';
703         if (isset($perm['edit'][$owner]) or 
704             (isset($perm['edit'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
705             $s[1] = 'w';
706         if (isset($perm['change'][$owner]) or 
707             (isset($perm['change'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
708             $s[2] = 'x';
709         if (!empty($group)) {
710             if (isset($perm['view'][$group]) or 
711                 (isset($perm['view'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
712                 $s[3] = 'r';
713             if (isset($perm['edit'][$group]) or 
714                 (isset($perm['edit'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
715                 $s[4] = 'w';
716             if (isset($perm['change'][$group]) or 
717                 (isset($perm['change'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
718                 $s[5] = 'x';
719         }
720         if (isset($perm['view'][ACL_EVERY]) or 
721             (isset($perm['view'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
722             $s[6] = 'r';
723         if (isset($perm['edit'][ACL_EVERY]) or 
724             (isset($perm['edit'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
725             $s[7] = 'w';
726         if (isset($perm['change'][ACL_EVERY]) or 
727             (isset($perm['change'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
728             $s[8] = 'x';
729         return $s;
730     }
731 }
732
733 // $Log: not supported by cvs2svn $
734 // Revision 1.41  2007/07/14 12:03:25  rurban
735 // fix for mult. group membership: not a member and undecided: check other groups
736 //
737 // Revision 1.40  2005/10/29 14:16:58  rurban
738 // unify message
739 //
740 // Revision 1.39  2005/05/06 16:57:54  rurban
741 // support captcha
742 //
743 // Revision 1.38  2004/11/30 17:48:38  rurban
744 // just comments
745 //
746 // Revision 1.37  2004/11/23 13:06:30  rurban
747 // several fixes and suggestions by Charles Corrigan:
748 // * fix GROUP_BOGO_USER check
749 // * allow group pages to have the link to the user page in [ ] brackets
750 // * fix up the implementation of GroupWikiPage::getMembersOf and allow the
751 //   user page to be linked in [ ] brackets
752 // * added _OWNER and _CREATOR to special wikigroups
753 // * check against those two for group membership also, not only the user.
754 //
755 // Revision 1.36  2004/11/21 11:59:16  rurban
756 // remove final \n to be ob_cache independent
757 //
758 // Revision 1.35  2004/11/15 15:56:40  rurban
759 // don't load PagePerm on ENABLE_PAGEPERM = false to save memory. Move mayAccessPage() to main.php
760 //
761 // Revision 1.34  2004/11/01 10:43:55  rurban
762 // seperate PassUser methods into seperate dir (memory usage)
763 // fix WikiUser (old) overlarge data session
764 // remove wikidb arg from various page class methods, use global ->_dbi instead
765 // ...
766 //
767 // Revision 1.33  2004/09/26 11:47:52  rurban
768 // fix another reecursion loop when . exists: deny if ACL not defined; implement pageperm cache
769 //
770 // Revision 1.32  2004/09/25 18:56:09  rurban
771 // avoid recursion bug on setacl for "."
772 //
773 // Revision 1.31  2004/09/25 18:34:45  rurban
774 // fix and warn on too restrictive ACL handling without ACL in existing . (dotpage)
775 //
776 // Revision 1.30  2004/09/25 16:24:02  rurban
777 // fix interesting PagePerm problem: -1 == true
778 //
779 // Revision 1.29  2004/07/03 08:04:19  rurban
780 // fixed implicit PersonalPage login (e.g. on edit), fixed to check against create ACL on create, not edit
781 //
782 // Revision 1.28  2004/06/25 14:29:17  rurban
783 // WikiGroup refactoring:
784 //   global group attached to user, code for not_current user.
785 //   improved helpers for special groups (avoid double invocations)
786 // new experimental config option ENABLE_XHTML_XML (fails with IE, and document.write())
787 // fixed a XHTML validation error on userprefs.tmpl
788 //
789 // Revision 1.27  2004/06/16 10:38:58  rurban
790 // Disallow refernces in calls if the declaration is a reference
791 // ("allow_call_time_pass_reference clean").
792 //   PhpWiki is now allow_call_time_pass_reference = Off clean,
793 //   but several external libraries may not.
794 //   In detail these libs look to be affected (not tested):
795 //   * Pear_DB odbc
796 //   * adodb oracle
797 //
798 // Revision 1.26  2004/06/14 11:31:36  rurban
799 // renamed global $Theme to $WikiTheme (gforge nameclash)
800 // inherit PageList default options from PageList
801 //   default sortby=pagename
802 // use options in PageList_Selectable (limit, sortby, ...)
803 // added action revert, with button at action=diff
804 // added option regex to WikiAdminSearchReplace
805 //
806 // Revision 1.25  2004/06/08 13:51:57  rurban
807 // some comments only
808 //
809 // Revision 1.24  2004/06/08 10:54:46  rurban
810 // better acl dump representation, read back acl and owner
811 //
812 // Revision 1.23  2004/06/08 10:05:11  rurban
813 // simplified admin action shortcuts
814 //
815 // Revision 1.22  2004/06/07 22:44:14  rurban
816 // added simplified chown, setacl actions
817 //
818 // Revision 1.21  2004/06/07 22:28:03  rurban
819 // add acl field to mimified dump
820 //
821 // Revision 1.20  2004/06/07 18:39:03  rurban
822 // support for SetAclSimple
823 //
824 // Revision 1.19  2004/06/06 17:12:28  rurban
825 // fixed PagePerm non-object problem (mayAccessPage), also bug #967150
826 //
827 // Revision 1.18  2004/05/27 17:49:05  rurban
828 // renamed DB_Session to DbSession (in CVS also)
829 // added WikiDB->getParam and WikiDB->getAuthParam method to get rid of globals
830 // remove leading slash in error message
831 // added force_unlock parameter to File_Passwd (no return on stale locks)
832 // fixed adodb session AffectedRows
833 // added FileFinder helpers to unify local filenames and DATA_PATH names
834 // editpage.php: new edit toolbar javascript on ENABLE_EDIT_TOOLBAR
835 //
836 // Revision 1.17  2004/05/16 23:10:44  rurban
837 // update_locale wrongly resetted LANG, which broke japanese.
838 // japanese now correctly uses EUC_JP, not utf-8.
839 // more charset and lang headers to help the browser.
840 //
841 // Revision 1.16  2004/05/16 22:32:53  rurban
842 // setacl icons
843 //
844 // Revision 1.15  2004/05/16 22:07:35  rurban
845 // check more config-default and predefined constants
846 // various PagePerm fixes:
847 //   fix default PagePerms, esp. edit and view for Bogo and Password users
848 //   implemented Creator and Owner
849 //   BOGOUSERS renamed to BOGOUSER
850 // fixed syntax errors in signin.tmpl
851 //
852 // Revision 1.14  2004/05/15 22:54:49  rurban
853 // fixed important WikiDB bug with DEBUG > 0: wrong assertion
854 // improved SetAcl (works) and PagePerms, some WikiGroup helpers.
855 //
856 // Revision 1.13  2004/05/15 19:48:33  rurban
857 // fix some too loose PagePerms for signed, but not authenticated users
858 //  (admin, owner, creator)
859 // no double login page header, better login msg.
860 // moved action_pdf to lib/pdf.php
861 //
862 // Revision 1.12  2004/05/04 22:34:25  rurban
863 // more pdf support
864 //
865 // Revision 1.11  2004/05/02 21:26:38  rurban
866 // limit user session data (HomePageHandle and auth_dbi have to invalidated anyway)
867 //   because they will not survive db sessions, if too large.
868 // extended action=upgrade
869 // some WikiTranslation button work
870 // revert WIKIAUTH_UNOBTAINABLE (need it for main.php)
871 // some temp. session debug statements
872 //
873 // Revision 1.10  2004/04/29 22:32:56  zorloc
874 // Slightly more elegant fix.  Instead of WIKIAUTH_FORBIDDEN, the current user's level + 1 is returned on a false.
875 //
876 // Revision 1.9  2004/04/29 17:18:19  zorloc
877 // Fixes permission failure issues.  With PagePermissions and Disabled Actions when user did not have permission WIKIAUTH_FORBIDDEN was returned.  In WikiUser this was ok because WIKIAUTH_FORBIDDEN had a value of 11 -- thus no user could perform that action.  But WikiUserNew has a WIKIAUTH_FORBIDDEN value of -1 -- thus a user without sufficent permission to do anything.  The solution is a new high value permission level (WIKIAUTH_UNOBTAINABLE) to be the default level for access failure.
878 //
879 // Revision 1.8  2004/03/14 16:24:35  rurban
880 // authenti(fi)cation spelling
881 //
882 // Revision 1.7  2004/02/28 22:25:07  rurban
883 // First PagePerm implementation:
884 //
885 // $WikiTheme->setAnonEditUnknownLinks(false);
886 //
887 // Layout improvement with dangling links for mostly closed wiki's:
888 // If false, only users with edit permissions will be presented the
889 // special wikiunknown class with "?" and Tooltip.
890 // If true (default), any user will see the ?, but will be presented
891 // the PrintLoginForm on a click.
892 //
893 // Revision 1.6  2004/02/24 15:20:05  rurban
894 // fixed minor warnings: unchecked args, POST => Get urls for sortby e.g.
895 //
896 // Revision 1.5  2004/02/23 21:30:25  rurban
897 // more PagePerm stuff: (working against 1.4.0)
898 //   ACL editing and simplification of ACL's to simple rwx------ string
899 //   not yet working.
900 //
901 // Revision 1.4  2004/02/12 13:05:36  rurban
902 // Rename functional for PearDB backend
903 // some other minor changes
904 // SiteMap comes with a not yet functional feature request: includepages (tbd)
905 //
906 // Revision 1.3  2004/02/09 03:58:12  rurban
907 // for now default DB_SESSION to false
908 // PagePerm:
909 //   * not existing perms will now query the parent, and not
910 //     return the default perm
911 //   * added pagePermissions func which returns the object per page
912 //   * added getAccessDescription
913 // WikiUserNew:
914 //   * added global ->prepare (not yet used) with smart user/pref/member table prefixing.
915 //   * force init of authdbh in the 2 db classes
916 // main:
917 //   * fixed session handling (not triple auth request anymore)
918 //   * don't store cookie prefs with sessions
919 // stdlib: global obj2hash helper from _AuthInfo, also needed for PagePerm
920 //
921 // Revision 1.2  2004/02/08 13:17:48  rurban
922 // This should be the functionality. Needs testing and some minor todos.
923 //
924 // Revision 1.1  2004/02/08 12:29:30  rurban
925 // initial version, not yet hooked into lib/main.php
926 //
927 //
928
929 // Local Variables:
930 // mode: php
931 // tab-width: 8
932 // c-basic-offset: 4
933 // c-hanging-comment-ender-p: nil
934 // indent-tabs-mode: nil
935 // End:
936 ?>