]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/PagePerm.php
Do not use array_diff_assoc_recursive
[SourceForge/phpwiki.git] / lib / PagePerm.php
1 <?php // -*-php-*-
2 rcs_id('$Id$');
3 /*
4  Copyright 2004,2007 $ThePhpWikiProgrammingTeam
5  Copyright 2009 Marc-Etienne Vargenau, Alcatel-Lucent
6
7  This file is part of PhpWiki.
8
9  PhpWiki is free software; you can redistribute it and/or modify
10  it under the terms of the GNU General Public License as published by
11  the Free Software Foundation; either version 2 of the License, or
12  (at your option) any later version.
13
14  PhpWiki is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  GNU General Public License for more details.
18
19  You should have received a copy of the GNU General Public License
20  along with PhpWiki; if not, write to the Free Software
21  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 /**
25    Permissions per page and action based on current user, 
26    ownership and group membership implemented with ACL's (Access Control Lists),
27    opposed to the simplier unix-like ugo:rwx system.
28    The previous system was only based on action and current user. (lib/main.php)
29
30    Permissions may be inherited from its parent pages, a optional the 
31    optional master page ("."), and predefined default permissions, if "." 
32    is not defined.
33    Pagenames starting with "." have special default permissions.
34    For Authentication see WikiUserNew.php, WikiGroup.php and main.php
35    Page Permissions are in PhpWiki since v1.3.9 and enabled since v1.4.0
36
37    This file might replace the following functions from main.php:
38      Request::_notAuthorized($require_level)
39        display the denied message and optionally a login form 
40        to gain higher privileges
41      Request::getActionDescription($action)
42        helper to localize the _notAuthorized message per action, 
43        when login is tried.
44      Request::getDisallowedActionDescription($action)
45        helper to localize the _notAuthorized message per action, 
46        when it aborts
47      Request::requiredAuthority($action)
48        returns the needed user level
49        has a hook for plugins on POST
50      Request::requiredAuthorityForAction($action)
51        just returns the level per action, will be replaced with the 
52        action + page pair
53
54      The defined main.php actions map to simplier access types:
55        browse => view
56        edit   => edit
57        create => edit or create
58        remove => remove
59        rename => change
60        store prefs => change
61        list in PageList => list
62 */
63
64 /* Symbolic special ACL groups. Untranslated to be stored in page metadata*/
65 define('ACL_EVERY',        '_EVERY');
66 define('ACL_ANONYMOUS',    '_ANONYMOUS');
67 define('ACL_BOGOUSER',     '_BOGOUSER');
68 define('ACL_HASHOMEPAGE',  '_HASHOMEPAGE');
69 define('ACL_SIGNED',       '_SIGNED');
70 define('ACL_AUTHENTICATED','_AUTHENTICATED');
71 define('ACL_ADMIN',        '_ADMIN');
72 define('ACL_OWNER',        '_OWNER');
73 define('ACL_CREATOR',      '_CREATOR');
74
75 // Return an page permissions array for this page.
76 // To provide ui helpers to view and change page permissions:
77 //   <tr><th>Group</th><th>Access</th><th>Allow or Forbid</th></tr>
78 //   <tr><td>$group</td><td>_($access)</td><td> [ ] </td></tr>
79 function pagePermissions($pagename) {
80     global $request;
81     $page = $request->getPage($pagename);
82     // Page not found (new page); returned inherited permissions, to be displayed in gray
83     if (! $page->exists() ) {
84         if ($pagename == '.') // stop recursion
85             return array('default', new PagePermission());
86         else {
87             return array('inherited', pagePermissions(getParentPage($pagename)));
88         }
89     } elseif ($perm = getPagePermissions($page)) {
90         return array('page', $perm);
91     // or no permissions defined; returned inherited permissions, to be displayed in gray
92     } elseif ($pagename == '.') { // stop recursion in pathological case. 
93         // "." defined, without any acl
94         return array('default', new PagePermission());
95     } else {
96         return array('inherited', pagePermissions(getParentPage($pagename)));
97     }
98 }
99
100 function pagePermissionsSimpleFormat($perm_tree, $owner, $group=false) {
101     list($type,$perm) = pagePermissionsAcl($perm_tree[0], $perm_tree);
102     /*
103     $type = $perm_tree[0];
104     $perm = pagePermissionsAcl($perm_tree);
105     if (is_object($perm_tree[1]))
106         $perm = $perm_tree[1];
107     elseif (is_array($perm_tree[1])) {
108         $perm_tree = pagePermissionsSimpleFormat($perm_tree[1],$owner,$group);
109         if (isa($perm_tree[1],'pagepermission'))
110             $perm = $perm_tree[1];
111         elseif (isa($perm_tree,'htmlelement'))
112             return $perm_tree;
113     }
114     */
115     if ($type == 'page')
116         return HTML::tt(HTML::strong($perm->asRwxString($owner, $group)));
117     elseif ($type == 'default')
118         return HTML::tt($perm->asRwxString($owner, $group));
119     elseif ($type == 'inherited') {
120         return HTML::tt(array('class'=>'inherited', 'style'=>'color:#aaa;'),
121                         $perm->asRwxString($owner, $group));
122     }
123 }
124
125 function pagePermissionsAcl($type,$perm_tree) {
126     $perm = $perm_tree[1];
127     while (!is_object($perm)) {
128         $perm_tree = pagePermissionsAcl($type, $perm);
129         $perm = $perm_tree[1];
130     }
131     return array($type,$perm);
132 }
133
134 // view => who
135 // edit => who
136 function pagePermissionsAclFormat($perm_tree, $editable=false) {
137     list($type,$perm) = pagePermissionsAcl($perm_tree[0], $perm_tree);
138     if ($editable)
139         return $perm->asEditableTable($type);
140     else
141         return $perm->asTable($type);
142 }
143
144 /** 
145  * Check the permissions for the current action.
146  * Walk down the inheritance tree. Collect all permissions until 
147  * the minimum required level is gained, which is not 
148  * overruled by more specific forbid rules.
149  * Todo: cache result per access and page in session?
150  */
151 function requiredAuthorityForPage ($action) {
152     global $request;
153     $auth = _requiredAuthorityForPagename(action2access($action),
154                                           $request->getArg('pagename'));
155     assert($auth !== -1);
156     if ($auth)
157         return $request->_user->_level;
158     else
159         return WIKIAUTH_UNOBTAINABLE;
160 }
161
162 // Translate action or plugin to the simplier access types:
163 function action2access ($action) {
164     global $request;
165     switch ($action) {
166     case 'browse':
167     case 'viewsource':
168     case 'diff':
169     case 'select':
170     case 'search':
171     case 'pdf':
172     case 'captcha':
173     case 'zip':
174         return 'view';
175
176     case 'dumpserial':
177         if (INSECURE_ACTIONS_LOCALHOST_ONLY and is_localhost())
178             return 'dump';
179         else
180             return 'view';
181
182     // performance and security relevant
183     case 'xmlrpc':
184     case 'soap':
185     case 'ziphtml':
186     case 'dumphtml':
187         return 'dump';
188
189     // invent a new access-perm massedit? or switch back to change, or keep it at edit?
190     case _("PhpWikiAdministration")."/"._("Rename"):
191     case _("PhpWikiAdministration")."/"._("Replace"):
192     case 'replace':
193     case 'rename':
194     case 'revert':
195     case 'edit':
196         return 'edit';
197     case 'create':
198         $page = $request->getPage();
199         if (!$page->exists())
200             return 'create';
201         else
202             return 'view'; 
203         break;
204     case 'upload':
205     case 'loadfile': 
206         // probably create/edit but we cannot check all page permissions, can we?
207     case 'remove':
208     case 'purge':
209     case 'lock':
210     case 'unlock':
211     case 'upgrade':
212     case 'chown':
213     case 'setacl':
214         return 'change';
215     default:
216         //Todo: Plugins should be able to override its access type
217         if (isWikiWord($action))
218             return 'view';
219         else
220             return 'change';
221         break;
222     }
223 }
224
225 // Recursive helper to do the real work.
226 // Using a simple perm cache for page-access pairs.
227 // Maybe page-(current+edit+change?)action pairs will help
228 function _requiredAuthorityForPagename($access, $pagename) {
229     static $permcache = array();
230     
231     if (array_key_exists($pagename, $permcache)
232         and array_key_exists($access, $permcache[$pagename]))
233         return $permcache[$pagename][$access];
234         
235     global $request;
236     $page = $request->getPage($pagename);
237     // Page not found; check against default permissions
238     if (! $page->exists() ) {
239         $perm = new PagePermission();
240         $result = ($perm->isAuthorized($access, $request->_user) === true);
241         $permcache[$pagename][$access] = $result;
242         return $result;
243     }
244     // no ACL defined; check for special dotfile or walk down
245     if (! ($perm = getPagePermissions($page))) { 
246         if ($pagename == '.') {
247             $perm = new PagePermission();
248             if ($perm->isAuthorized('change', $request->_user)) {
249                 // warn the user to set ACL of ".", if he has permissions to do so.
250                 trigger_error(". (dotpage == rootpage for inheriting pageperm ACLs) exists without any ACL!\n".
251                               "Please do ?action=setacl&pagename=.", E_USER_WARNING);
252             }
253             $result = ($perm->isAuthorized($access, $request->_user) === true);
254             $permcache[$pagename][$access] = $result;
255             return $result;
256         } elseif ($pagename[0] == '.') {
257             $perm = new PagePermission(PagePermission::dotPerms());
258             $result = ($perm->isAuthorized($access, $request->_user) === true);
259             $permcache[$pagename][$access] = $result;
260             return $result;
261         }
262         return _requiredAuthorityForPagename($access, getParentPage($pagename));
263     }
264     // ACL defined; check if isAuthorized returns true or false or undecided
265     $authorized = $perm->isAuthorized($access, $request->_user);
266     if ($authorized !== -1) { // interestingly true is also -1
267         $permcache[$pagename][$access] = $authorized;
268         return $authorized;
269     } elseif ($pagename == '.') {
270         return false;
271     } else {    
272         return _requiredAuthorityForPagename($access, getParentPage($pagename));
273     }
274 }
275
276 /**
277  * @param  string $pagename   page from which the parent page is searched.
278  * @return string parent      pagename or the (possibly pseudo) dot-pagename.
279  */
280 function getParentPage($pagename) {
281     if (isSubPage($pagename)) {
282         return subPageSlice($pagename, 0);
283     } else {
284         return '.';
285     }
286 }
287
288 // Read the ACL from the page
289 // Done: Not existing pages should NOT be queried. 
290 // Check the parent page instead and don't take the default ACL's
291 function getPagePermissions ($page) {
292     if ($hash = $page->get('perm'))  // hash => object
293         return new PagePermission(unserialize($hash));
294     else 
295         return false;
296 }
297
298 // Store the ACL in the page
299 function setPagePermissions ($page,$perm) {
300     $perm->store($page);
301 }
302
303 function getAccessDescription($access) {
304     static $accessDescriptions;
305     if (! $accessDescriptions) {
306         $accessDescriptions = array(
307                                     'list'     => _("List this page and all subpages"),
308                                     'view'     => _("View this page and all subpages"),
309                                     'edit'     => _("Edit this page and all subpages"),
310                                     'create'   => _("Create a new (sub)page"),
311                                     'dump'     => _("Download page contents"),
312                                     'change'   => _("Change page attributes"),
313                                     'remove'   => _("Remove this page"),
314                                     'purge'    => _("Purge this page"),
315                                     );
316     }
317     if (in_array($access, array_keys($accessDescriptions)))
318         return $accessDescriptions[$access];
319     else
320         return $access;
321 }
322
323 // from php.net docs
324 function array_diff_assoc_recursive($array1, $array2) {
325     foreach ($array1 as $key => $value) {
326          if (is_array($value)) {
327              if (!is_array($array2[$key])) {
328                  $difference[$key] = $value;
329              } else {
330                  $new_diff = array_diff_assoc_recursive($value, $array2[$key]);
331                  if ($new_diff != false) {
332                      $difference[$key] = $new_diff;
333                  } 
334              }
335          } elseif(!isset($array2[$key]) || $array2[$key] != $value) {
336              $difference[$key] = $value;
337          }
338     }
339     return !isset($difference) ? 0 : $difference;
340 }
341
342 /**
343  * The ACL object per page. It is stored in a page, but can also 
344  * be merged with ACL's from other pages or taken from the master (pseudo) dot-file.
345  *
346  * A hash of "access" => "requires" pairs.
347  *   "access"   is a shortcut for common actions, which map to main.php actions
348  *   "requires" required username or groupname or any special group => true or false
349  *
350  * Define any special rules here, like don't list dot-pages.
351  */ 
352 class PagePermission {
353     var $perm;
354
355     function PagePermission($hash = array()) {
356         $this->_group = &$GLOBALS['request']->getGroup();
357         if (is_array($hash) and !empty($hash)) {
358             $accessTypes = $this->accessTypes();
359             foreach ($hash as $access => $requires) {
360                 if (in_array($access, $accessTypes))
361                     $this->perm[$access] = $requires;
362                 else
363                     trigger_error(sprintf(_("Unsupported ACL access type %s ignored."), $access),
364                                   E_USER_WARNING);
365             }
366         } else {
367             // set default permissions, the so called dot-file acl's
368             $this->perm = $this->defaultPerms();
369         }
370         return $this;
371     }
372
373     /**
374      * The workhorse to check the user against the current ACL pairs.
375      * Must translate the various special groups to the actual users settings 
376      * (userid, group membership).
377      */
378     function isAuthorized($access, $user) {
379         if (!empty($this->perm{$access})) {
380             $allow = -1;
381             foreach ($this->perm[$access] as $group => $bool) {
382                 if ($this->isMember($user, $group)) {
383                     return $bool;
384                 } elseif ($allow == -1) { // not a member and undecided: check other groups
385                     $allow = !$bool;
386                 }
387             }
388         }
389         return $allow; // undecided
390     }
391
392     /**
393      * Translate the various special groups to the actual users settings 
394      * (userid, group membership).
395      */
396     function isMember($user, $group) {
397         global $request;
398         if ($group === ACL_EVERY) return true;
399         if (!isset($this->_group)) $member =& $request->getGroup();
400         else $member =& $this->_group;
401         //$user = & $request->_user;
402         if ($group === ACL_ADMIN)   // WIKI_ADMIN or member of _("Administrators")
403             return $user->isAdmin() or 
404                    ($user->isAuthenticated() and 
405                    $member->isMember(GROUP_ADMIN));
406         if ($group === ACL_ANONYMOUS) 
407             return ! $user->isSignedIn();
408         if ($group === ACL_BOGOUSER)
409             if (ENABLE_USER_NEW)
410                 return isa($user,'_BogoUser') or 
411                       (isWikiWord($user->_userid) and $user->_level >= WIKIAUTH_BOGO);
412             else return isWikiWord($user->UserName());
413         if ($group === ACL_HASHOMEPAGE)
414             return $user->hasHomePage();
415         if ($group === ACL_SIGNED)
416             return $user->isSignedIn();
417         if ($group === ACL_AUTHENTICATED)
418             return $user->isAuthenticated();
419         if ($group === ACL_OWNER) {
420             if (!$user->isAuthenticated()) return false;
421             $page = $request->getPage();
422             $owner = $page->getOwner();
423             return ($owner === $user->UserName() 
424                     or $member->isMember($owner));
425         }
426         if ($group === ACL_CREATOR) {
427             if (!$user->isAuthenticated()) return false;
428             $page = $request->getPage();
429             $creator = $page->getCreator();
430             return ($creator === $user->UserName() 
431                     or $member->isMember($creator));
432         }
433         /* Or named groups or usernames.
434            Note: We don't seperate groups and users here. 
435            Users overrides groups with the same name. 
436         */
437         return $user->UserName() === $group or
438                $member->isMember($group);
439     }
440
441     /**
442      * returns hash of default permissions.
443      * check if the page '.' exists and returns this instead.
444      */
445     function defaultPerms() {
446         //Todo: check for the existance of '.' and take this instead.
447         //Todo: honor more config.ini auth settings here
448         $perm = array('view'   => array(ACL_EVERY => true),
449                       'edit'   => array(ACL_EVERY => true),
450                       'create' => array(ACL_EVERY => true),
451                       'list'   => array(ACL_EVERY => true),
452                       'remove' => array(ACL_ADMIN => true,
453                                         ACL_OWNER => true),
454                       'purge'  => array(ACL_ADMIN => true,
455                                         ACL_OWNER => true),
456                       'dump'   => array(ACL_ADMIN => true,
457                                         ACL_OWNER => true),
458                       'change' => array(ACL_ADMIN => true,
459                                         ACL_OWNER => true));
460         if (ZIPDUMP_AUTH)
461             $perm['dump'] = array(ACL_ADMIN => true,
462                                   ACL_OWNER => true);
463         elseif (INSECURE_ACTIONS_LOCALHOST_ONLY) {
464             if (is_localhost())
465                 $perm['dump'] = array(ACL_EVERY => true);
466             else
467                 $perm['dump'] = array(ACL_ADMIN => true);
468         }
469         else
470             $perm['dump'] = array(ACL_EVERY => true);
471         if (defined('REQUIRE_SIGNIN_BEFORE_EDIT') && REQUIRE_SIGNIN_BEFORE_EDIT)
472             $perm['edit'] = array(ACL_SIGNED => true);
473         // view:
474         if (!ALLOW_ANON_USER) {
475             if (!ALLOW_USER_PASSWORDS) 
476                 $perm['view'] = array(ACL_SIGNED => true);
477             else                
478                 $perm['view'] = array(ACL_AUTHENTICATED => true);
479             $perm['view'][ACL_BOGOUSER] = ALLOW_BOGO_LOGIN ? true : false;
480         }
481         // edit:
482         if (!ALLOW_ANON_EDIT) {
483             if (!ALLOW_USER_PASSWORDS) 
484                 $perm['edit'] = array(ACL_SIGNED => true);
485             else                
486                 $perm['edit'] = array(ACL_AUTHENTICATED => true);
487             $perm['edit'][ACL_BOGOUSER] = ALLOW_BOGO_LOGIN ? true : false;
488             $perm['create'] = $perm['edit'];
489         }
490         return $perm;
491     }
492
493     /**
494      * FIXME: check valid groups and access
495      */
496     function sanify() {
497         foreach ($this->perm as $access => $groups) {
498             foreach ($groups as $group => $bool) {
499                 $this->perm[$access][$group] = (boolean) $bool;
500             }
501         }
502     }
503
504     /**
505      * do a recursive comparison
506      */
507     function equal($otherperm) {
508         // The equal function seems to be unable to detect removed perm.
509         // Use case is when a rule is removed.
510         return (print_r($this->perm, true) === print_r($otherperm, true));
511
512 //      $diff = array_diff_assoc_recursive($this->perm, $otherperm);
513 //        return empty($diff);
514     }
515     
516     /**
517      * returns list of all supported access types.
518      */
519     function accessTypes() {
520         return array_keys(PagePermission::defaultPerms());
521     }
522
523     /**
524      * special permissions for dot-files, beginning with '.'
525      * maybe also for '_' files?
526      */
527     function dotPerms() {
528         $def = array(ACL_ADMIN => true,
529                      ACL_OWNER => true);
530         $perm = array();
531         foreach (PagePermission::accessTypes() as $access) {
532             $perm[$access] = $def;
533         }
534         return $perm;
535     }
536
537     /**
538      *  dead code. not needed inside the object. see getPagePermissions($page)
539      */
540     function retrieve($page) {
541         $hash = $page->get('perm');
542         if ($hash)  // hash => object
543             $perm = new PagePermission(unserialize($hash));
544         else 
545             $perm = new PagePermission();
546         $perm->sanify();
547         return $perm;
548     }
549
550     function store($page) {
551         // object => hash
552         $this->sanify();
553         return $page->set('perm',serialize($this->perm));
554     }
555
556     function groupName ($group) {
557         if ($group[0] == '_') return constant("GROUP".$group);
558         else return $group;
559     }
560     
561     /* type: page, default, inherited */
562     function asTable($type) {
563         $table = HTML::table();
564         foreach ($this->perm as $access => $perms) {
565             $td = HTML::table(array('class' => 'cal'));
566             foreach ($perms as $group => $bool) {
567                 $td->pushContent(HTML::tr(HTML::td(array('align'=>'right'),$group),
568                                                    HTML::td($bool ? '[X]' : '[ ]')));
569             }
570             $table->pushContent(HTML::tr(array('valign' => 'top'),
571                                          HTML::td($access),HTML::td($td)));
572         }
573         if ($type == 'default')
574             $table->setAttr('style','border: dotted thin black; background-color:#eee;');
575         elseif ($type == 'inherited')
576             $table->setAttr('style','border: dotted thin black; background-color:#ddd;');
577         elseif ($type == 'page')
578             $table->setAttr('style','border: solid thin black; font-weight: bold;');
579         return $table;
580     }
581     
582     /* type: page, default, inherited */
583     function asEditableTable($type) {
584         global $WikiTheme;
585         if (!isset($this->_group)) { 
586             $this->_group =& $GLOBALS['request']->getGroup();
587         }
588         $table = HTML::table();
589         $table->pushContent(HTML::tr(
590                                      HTML::th(array('align' => 'left'),
591                                               _("Access")),
592                                      HTML::th(array('align'=>'right'),
593                                               _("Group/User")),
594                                      HTML::th(_("Grant")),
595                                      HTML::th(_("Del/+")),
596                                      HTML::th(_("Description"))));
597         
598         $allGroups = $this->_group->_specialGroups();
599         foreach ($this->_group->getAllGroupsIn() as $group) {
600             if (!in_array($group,$this->_group->specialGroups()))
601                 $allGroups[] = $group;
602         }
603         //array_unique(array_merge($this->_group->getAllGroupsIn(),
604         $deletesrc = $WikiTheme->_findData('images/delete.png');
605         $addsrc = $WikiTheme->_findData('images/add.png');
606         $nbsp = HTML::raw('&nbsp;');
607         foreach ($this->perm as $access => $groups) {
608             //$permlist = HTML::table(array('class' => 'cal'));
609             $first_only = true;
610             $newperm = HTML::input(array('type' => 'checkbox',
611                                          'name' => "acl[_new_perm][$access]",
612                                          'value' => 1));
613             $addbutton = HTML::input(array('type' => 'checkbox',
614                                            'name' => "acl[_add_group][$access]",
615                                            //'src'  => $addsrc,
616                                            //'alt'   => "Add",
617                                            'title' => _("Add this ACL"),
618                                            'value' => 1));
619             $newgroup = HTML::select(array('name' => "acl[_new_group][$access]",
620                                            'style'=> 'text-align: right;',
621                                            'size' => 1));
622             foreach ($allGroups as $groupname) {
623                 if (!isset($groups[$groupname]))
624                     $newgroup->pushContent(HTML::option(array('value' => $groupname),
625                                                         $this->groupName($groupname)));
626             }
627             if (empty($groups)) {
628                 $addbutton->setAttr('checked','checked');
629                 $newperm->setAttr('checked','checked');
630                 $table->pushContent(
631                     HTML::tr(array('valign' => 'top'),
632                              HTML::td(HTML::strong($access.":")),
633                              HTML::td($newgroup),
634                              HTML::td($nbsp,$newperm),
635                              HTML::td($nbsp,$addbutton),
636                              HTML::td(HTML::em(getAccessDescription($access)))));
637             }
638             foreach ($groups as $group => $bool) {
639                 $checkbox = HTML::input(array('type' => 'checkbox',
640                                               'name' => "acl[$access][$group]",
641                                               'title' => _("Allow / Deny"),
642                                               'value' => 1));
643                 if ($bool) $checkbox->setAttr('checked','checked');
644                 $checkbox = HTML(HTML::input(array('type' => 'hidden',
645                                                    'name' => "acl[$access][$group]",
646                                                    'value' => 0)),
647                                  $checkbox);
648                 $deletebutton = HTML::input(array('type' => 'checkbox',
649                                                   'name' => "acl[_del_group][$access][$group]",
650                                                   'style' => 'background: #aaa url('.$deletesrc.')',
651                                                   //'src'  => $deletesrc,
652                                                   //'alt'   => "Del",
653                                                   'title' => _("Delete this ACL"),
654                                                   'value' => 1));
655                 if ($first_only) {
656                     $table->pushContent(
657                         HTML::tr(
658                                  HTML::td(HTML::strong($access.":")),
659                                  HTML::td(array('class' => 'cal-today','align'=>'right'),
660                                           HTML::strong($this->groupName($group))),
661                                  HTML::td(array('align'=>'center'),$nbsp,$checkbox),
662                                  HTML::td(array('align'=>'right','style' => 'background: #aaa url('.$deletesrc.') no-repeat'),$deletebutton),
663                                  HTML::td(HTML::em(getAccessDescription($access)))));
664                     $first_only = false;
665                 } else {
666                     $table->pushContent(
667                         HTML::tr(
668                                  HTML::td(),
669                                  HTML::td(array('class' => 'cal-today','align'=>'right'),
670                                           HTML::strong($this->groupName($group))),
671                                  HTML::td(array('align'=>'center'),$nbsp,$checkbox),
672                                  HTML::td(array('align'=>'right','style' => 'background: #aaa url('.$deletesrc.') no-repeat'),$deletebutton),
673                                  HTML::td()));
674                 }
675             }
676             if (!empty($groups))
677                 $table->pushContent(
678                     HTML::tr(array('valign' => 'top'),
679                              HTML::td(array('align'=>'right'),_("add ")),
680                              HTML::td($newgroup),
681                              HTML::td(array('align'=>'center'),$nbsp,$newperm),
682                              HTML::td(array('align'=>'right','style' => 'background: #ccc url('.$addsrc.') no-repeat'),$addbutton),
683                              HTML::td(HTML::small(_("Check to add this ACL")))));
684         }
685         if ($type == 'default')
686             $table->setAttr('style','border: dotted thin black; background-color:#eee;');
687         elseif ($type == 'inherited')
688             $table->setAttr('style','border: dotted thin black; background-color:#ddd;');
689         elseif ($type == 'page')
690             $table->setAttr('style','border: solid thin black; font-weight: bold;');
691         return $table;
692     }
693
694     // Print ACL as lines of [+-]user[,group,...]:access[,access...]
695     // Seperate acl's by "; " or whitespace
696     // See http://opag.ca/wiki/HelpOnAccessControlLists
697     // As used by WikiAdminSetAclSimple
698     function asAclLines() {
699         $s = ''; $line = '';
700         $this->sanify();
701         foreach ($this->perm as $access => $groups) {
702             // unify groups for same access+bool
703             //    view:CREATOR,-OWNER,
704             $line = $access.':';
705             foreach ($groups as $group => $bool) {
706                 $line .= ($bool?'':'-').$group.",";
707             }
708             if (substr($line,-1) == ',')
709                 $s .= substr($line,0,-1)."; ";
710         }
711         if (substr($s,-2) == '; ')
712             $s = substr($s,0,-2);
713         return $s;
714     }
715
716     // Print ACL as group followed by actions allowed for the group
717     function asAclGroupLines() {
718
719         $s = '';
720         $perm =& $this->perm;
721         $actions = array("view", "edit", "create", "list", "remove", "purge", "dump", "change");
722         $groups = array(ACL_EVERY, ACL_ANONYMOUS, ACL_BOGOUSER, ACL_HASHOMEPAGE, ACL_SIGNED, ACL_AUTHENTICATED, ACL_ADMIN, ACL_OWNER, ACL_CREATOR);
723
724         foreach ($groups as $group) {
725             $none = true;
726             foreach ($actions as $action) {
727                 if (isset($perm[$action][$group])) {
728                     if ($none) {
729                         $none = false;
730                         $s .= "$group:";
731                     }
732                     if ($perm[$action][$group]) {
733                         $s .= " $action";
734                     } else {
735                         $s .= " -$action";
736                     }
737                 }
738             }
739             if (!($none)) {
740                 $s .= "; ";
741             }
742         }
743         return $s;
744     }
745
746     // This is just a bad hack for testing.
747     // Simplify the ACL to a unix-like "rwx------+" string
748     // See getfacl(8)
749     function asRwxString($owner,$group=false) {
750         global $request;
751         // simplify object => rwxrw---x+ string as in cygwin (+ denotes additional ACLs)
752         $perm =& $this->perm;
753         // get effective user and group
754         $s = '---------+';
755         if (isset($perm['view'][$owner]) or 
756             (isset($perm['view'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
757             $s[0] = 'r';
758         if (isset($perm['edit'][$owner]) or 
759             (isset($perm['edit'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
760             $s[1] = 'w';
761         if (isset($perm['change'][$owner]) or 
762             (isset($perm['change'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
763             $s[2] = 'x';
764         if (!empty($group)) {
765             if (isset($perm['view'][$group]) or 
766                 (isset($perm['view'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
767                 $s[3] = 'r';
768             if (isset($perm['edit'][$group]) or 
769                 (isset($perm['edit'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
770                 $s[4] = 'w';
771             if (isset($perm['change'][$group]) or 
772                 (isset($perm['change'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
773                 $s[5] = 'x';
774         }
775         if (isset($perm['view'][ACL_EVERY]) or 
776             (isset($perm['view'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
777             $s[6] = 'r';
778         if (isset($perm['edit'][ACL_EVERY]) or 
779             (isset($perm['edit'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
780             $s[7] = 'w';
781         if (isset($perm['change'][ACL_EVERY]) or 
782             (isset($perm['change'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
783             $s[8] = 'x';
784         return $s;
785     }
786 }
787
788 // Local Variables:
789 // mode: php
790 // tab-width: 8
791 // c-basic-offset: 4
792 // c-hanging-comment-ender-p: nil
793 // indent-tabs-mode: nil
794 // End:
795 ?>