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