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