]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/PagePerm.php
Whitespace only
[SourceForge/phpwiki.git] / lib / PagePerm.php
1 <?php // -*-php-*-
2 // $Id$
3 /*
4  * Copyright 2004,2007 $ThePhpWikiProgrammingTeam
5  * Copyright 2009-2010 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 along
20  * with PhpWiki; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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")."/"._("SearchReplace"):
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
238     // Exceptions:
239     if (FUSIONFORGE) {
240         if ($pagename != '.' && isset($request->_user->_is_external) && $request->_user->_is_external && ! $page->get('external')) {
241             $permcache[$pagename][$access] = 0;
242             return 0;
243         }
244     }
245     if ((READONLY or $request->_dbi->readonly)
246         and in_array($access, array('edit','create','change')))
247     {
248         return 0;
249     }
250
251     // Page not found; check against default permissions
252     if (! $page->exists() ) {
253         $perm = new PagePermission();
254         $result = ($perm->isAuthorized($access, $request->_user) === true);
255         $permcache[$pagename][$access] = $result;
256         return $result;
257     }
258     // no ACL defined; check for special dotfile or walk down
259     if (! ($perm = getPagePermissions($page))) {
260         if ($pagename == '.') {
261             $perm = new PagePermission();
262             if ($perm->isAuthorized('change', $request->_user)) {
263                 // warn the user to set ACL of ".", if he has permissions to do so.
264                 trigger_error(". (dotpage == rootpage for inheriting pageperm ACLs) exists without any ACL!\n".
265                               "Please do ?action=setacl&pagename=.", E_USER_WARNING);
266             }
267             $result = ($perm->isAuthorized($access, $request->_user) === true);
268             $permcache[$pagename][$access] = $result;
269             return $result;
270         } elseif ($pagename[0] == '.') {
271             $perm = new PagePermission(PagePermission::dotPerms());
272             $result = ($perm->isAuthorized($access, $request->_user) === true);
273             $permcache[$pagename][$access] = $result;
274             return $result;
275         }
276         return _requiredAuthorityForPagename($access, getParentPage($pagename));
277     }
278     // ACL defined; check if isAuthorized returns true or false or undecided
279     $authorized = $perm->isAuthorized($access, $request->_user);
280     if ($authorized !== -1) { // interestingly true is also -1
281         $permcache[$pagename][$access] = $authorized;
282         return $authorized;
283     } elseif ($pagename == '.') {
284         return false;
285     } else {
286         return _requiredAuthorityForPagename($access, getParentPage($pagename));
287     }
288 }
289
290 /**
291  * @param  string $pagename   page from which the parent page is searched.
292  * @return string parent      pagename or the (possibly pseudo) dot-pagename.
293  */
294 function getParentPage($pagename) {
295     if (isSubPage($pagename)) {
296         return subPageSlice($pagename, 0);
297     } else {
298         return '.';
299     }
300 }
301
302 // Read the ACL from the page
303 // Done: Not existing pages should NOT be queried.
304 // Check the parent page instead and don't take the default ACL's
305 function getPagePermissions ($page) {
306     if ($hash = $page->get('perm'))  // hash => object
307         return new PagePermission(unserialize($hash));
308     else
309         return false;
310 }
311
312 // Store the ACL in the page
313 function setPagePermissions ($page,$perm) {
314     $perm->store($page);
315 }
316
317 function getAccessDescription($access) {
318     static $accessDescriptions;
319     if (! $accessDescriptions) {
320         $accessDescriptions = array(
321                                     'list'     => _("List this page and all subpages"),
322                                     'view'     => _("View this page and all subpages"),
323                                     'edit'     => _("Edit this page and all subpages"),
324                                     'create'   => _("Create a new (sub)page"),
325                                     'dump'     => _("Download page contents"),
326                                     'change'   => _("Change page attributes"),
327                                     'remove'   => _("Remove this page"),
328                                     'purge'    => _("Purge this page"),
329                                     );
330     }
331     if (in_array($access, array_keys($accessDescriptions)))
332         return $accessDescriptions[$access];
333     else
334         return $access;
335 }
336
337 /**
338  * The ACL object per page. It is stored in a page, but can also
339  * be merged with ACL's from other pages or taken from the master (pseudo) dot-file.
340  *
341  * A hash of "access" => "requires" pairs.
342  *   "access"   is a shortcut for common actions, which map to main.php actions
343  *   "requires" required username or groupname or any special group => true or false
344  *
345  * Define any special rules here, like don't list dot-pages.
346  */
347 class PagePermission {
348     var $perm;
349
350     function PagePermission($hash = array()) {
351         $this->_group = &$GLOBALS['request']->getGroup();
352         if (is_array($hash) and !empty($hash)) {
353             $accessTypes = $this->accessTypes();
354             foreach ($hash as $access => $requires) {
355                 if (in_array($access, $accessTypes))
356                     $this->perm[$access] = $requires;
357                 else
358                     trigger_error(sprintf(_("Unsupported ACL access type %s ignored."), $access),
359                                   E_USER_WARNING);
360             }
361         } else {
362             // set default permissions, the so called dot-file acl's
363             $this->perm = $this->defaultPerms();
364         }
365         return $this;
366     }
367
368     /**
369      * The workhorse to check the user against the current ACL pairs.
370      * Must translate the various special groups to the actual users settings
371      * (userid, group membership).
372      */
373     function isAuthorized($access, $user) {
374         $allow = -1;
375         if (!empty($this->perm{$access})) {
376             foreach ($this->perm[$access] as $group => $bool) {
377                 if ($this->isMember($user, $group)) {
378                     return $bool;
379                 } elseif ($allow == -1) { // not a member and undecided: check other groups
380                     $allow = !$bool;
381                 }
382             }
383         }
384         return $allow; // undecided
385     }
386
387     /**
388      * Translate the various special groups to the actual users settings
389      * (userid, group membership).
390      */
391     function isMember($user, $group) {
392         global $request;
393         if ($group === ACL_EVERY) return true;
394         if (!isset($this->_group)) $member =& $request->getGroup();
395         else $member =& $this->_group;
396         //$user = & $request->_user;
397         if ($group === ACL_ADMIN)   // WIKI_ADMIN or member of _("Administrators")
398             return $user->isAdmin() or
399                    ($user->isAuthenticated() and
400                    $member->isMember(GROUP_ADMIN));
401         if ($group === ACL_ANONYMOUS)
402             return ! $user->isSignedIn();
403         if ($group === ACL_BOGOUSER)
404             if (ENABLE_USER_NEW)
405                 return isa($user,'_BogoUser') or
406                       (isWikiWord($user->_userid) and $user->_level >= WIKIAUTH_BOGO);
407             else return isWikiWord($user->UserName());
408         if ($group === ACL_HASHOMEPAGE)
409             return $user->hasHomePage();
410         if ($group === ACL_SIGNED)
411             return $user->isSignedIn();
412         if ($group === ACL_AUTHENTICATED)
413             return $user->isAuthenticated();
414         if ($group === ACL_OWNER) {
415             if (!$user->isAuthenticated()) return false;
416             $page = $request->getPage();
417             $owner = $page->getOwner();
418             return ($owner === $user->UserName()
419                     or $member->isMember($owner));
420         }
421         if ($group === ACL_CREATOR) {
422             if (!$user->isAuthenticated()) return false;
423             $page = $request->getPage();
424             $creator = $page->getCreator();
425             return ($creator === $user->UserName()
426                     or $member->isMember($creator));
427         }
428         /* Or named groups or usernames.
429            Note: We don't seperate groups and users here.
430            Users overrides groups with the same name.
431         */
432         return $user->UserName() === $group or
433                $member->isMember($group);
434     }
435
436     /**
437      * returns hash of default permissions.
438      * check if the page '.' exists and returns this instead.
439      */
440     function defaultPerms() {
441         //Todo: check for the existance of '.' and take this instead.
442         //Todo: honor more config.ini auth settings here
443         $perm = array('view'   => array(ACL_EVERY => true),
444                       'edit'   => array(ACL_EVERY => true),
445                       'create' => array(ACL_EVERY => true),
446                       'list'   => array(ACL_EVERY => true),
447                       'remove' => array(ACL_ADMIN => true,
448                                         ACL_OWNER => true),
449                       'purge'  => array(ACL_ADMIN => true,
450                                         ACL_OWNER => true),
451                       'dump'   => array(ACL_ADMIN => true,
452                                         ACL_OWNER => true),
453                       'change' => array(ACL_ADMIN => true,
454                                         ACL_OWNER => true));
455         if (ZIPDUMP_AUTH)
456             $perm['dump'] = array(ACL_ADMIN => true,
457                                   ACL_OWNER => true);
458         elseif (INSECURE_ACTIONS_LOCALHOST_ONLY) {
459             if (is_localhost())
460                 $perm['dump'] = array(ACL_EVERY => true);
461             else
462                 $perm['dump'] = array(ACL_ADMIN => true);
463         }
464         else
465             $perm['dump'] = array(ACL_EVERY => true);
466         if (defined('REQUIRE_SIGNIN_BEFORE_EDIT') && REQUIRE_SIGNIN_BEFORE_EDIT)
467             $perm['edit'] = array(ACL_SIGNED => true);
468         // view:
469         if (!ALLOW_ANON_USER) {
470             if (!ALLOW_USER_PASSWORDS)
471                 $perm['view'] = array(ACL_SIGNED => true);
472             else
473                 $perm['view'] = array(ACL_AUTHENTICATED => true);
474             $perm['view'][ACL_BOGOUSER] = ALLOW_BOGO_LOGIN ? true : false;
475         }
476         // edit:
477         if (!ALLOW_ANON_EDIT) {
478             if (!ALLOW_USER_PASSWORDS)
479                 $perm['edit'] = array(ACL_SIGNED => true);
480             else
481                 $perm['edit'] = array(ACL_AUTHENTICATED => true);
482             $perm['edit'][ACL_BOGOUSER] = ALLOW_BOGO_LOGIN ? true : false;
483             $perm['create'] = $perm['edit'];
484         }
485         return $perm;
486     }
487
488     /**
489      * FIXME: check valid groups and access
490      */
491     function sanify() {
492         foreach ($this->perm as $access => $groups) {
493             foreach ($groups as $group => $bool) {
494                 $this->perm[$access][$group] = (boolean) $bool;
495             }
496         }
497     }
498
499     /**
500      * do a recursive comparison
501      */
502     function equal($otherperm) {
503         // The equal function seems to be unable to detect removed perm.
504         // Use case is when a rule is removed.
505         return (print_r($this->perm, true) === print_r($otherperm, true));
506     }
507
508     /**
509      * returns list of all supported access types.
510      */
511     function accessTypes() {
512         return array_keys(PagePermission::defaultPerms());
513     }
514
515     /**
516      * special permissions for dot-files, beginning with '.'
517      * maybe also for '_' files?
518      */
519     function dotPerms() {
520         $def = array(ACL_ADMIN => true,
521                      ACL_OWNER => true);
522         $perm = array();
523         foreach (PagePermission::accessTypes() as $access) {
524             $perm[$access] = $def;
525         }
526         return $perm;
527     }
528
529     /**
530      *  dead code. not needed inside the object. see getPagePermissions($page)
531      */
532     function retrieve($page) {
533         $hash = $page->get('perm');
534         if ($hash)  // hash => object
535             $perm = new PagePermission(unserialize($hash));
536         else
537             $perm = new PagePermission();
538         $perm->sanify();
539         return $perm;
540     }
541
542     function store($page) {
543         // object => hash
544         $this->sanify();
545         return $page->set('perm',serialize($this->perm));
546     }
547
548     function groupName ($group) {
549         if ($group[0] == '_') return constant("GROUP".$group);
550         else return $group;
551     }
552
553     /* type: page, default, inherited */
554     function asTable($type) {
555         $table = HTML::table();
556         foreach ($this->perm as $access => $perms) {
557             $td = HTML::table(array('class' => 'cal'));
558             foreach ($perms as $group => $bool) {
559                 $td->pushContent(HTML::tr(HTML::td(array('align'=>'right'),$group),
560                                                    HTML::td($bool ? '[X]' : '[ ]')));
561             }
562             $table->pushContent(HTML::tr(array('valign' => 'top'),
563                                          HTML::td($access),HTML::td($td)));
564         }
565         if ($type == 'default')
566             $table->setAttr('style','border: dotted thin black; background-color:#eee;');
567         elseif ($type == 'inherited')
568             $table->setAttr('style','border: dotted thin black; background-color:#ddd;');
569         elseif ($type == 'page')
570             $table->setAttr('style','border: solid thin black; font-weight: bold;');
571         return $table;
572     }
573
574     /* type: page, default, inherited */
575     function asEditableTable($type) {
576         global $WikiTheme;
577         if (!isset($this->_group)) {
578             $this->_group =& $GLOBALS['request']->getGroup();
579         }
580         $table = HTML::table();
581         $table->pushContent(HTML::tr(
582                                      HTML::th(array('align' => 'left'),
583                                               _("Access")),
584                                      HTML::th(array('align'=>'right'),
585                                               _("Group/User")),
586                                      HTML::th(_("Grant")),
587                                      HTML::th(_("Del/+")),
588                                      HTML::th(_("Description"))));
589
590         $allGroups = $this->_group->_specialGroups();
591         foreach ($this->_group->getAllGroupsIn() as $group) {
592             if (!in_array($group,$this->_group->specialGroups()))
593                 $allGroups[] = $group;
594         }
595         //array_unique(array_merge($this->_group->getAllGroupsIn(),
596         $deletesrc = $WikiTheme->_findData('images/delete.png');
597         $addsrc = $WikiTheme->_findData('images/add.png');
598         $nbsp = HTML::raw('&nbsp;');
599         foreach ($this->perm as $access => $groups) {
600             //$permlist = HTML::table(array('class' => 'cal'));
601             $first_only = true;
602             $newperm = HTML::input(array('type' => 'checkbox',
603                                          'name' => "acl[_new_perm][$access]",
604                                          'value' => 1));
605             $addbutton = HTML::input(array('type' => 'checkbox',
606                                            'name' => "acl[_add_group][$access]",
607                                            //'src'  => $addsrc,
608                                            //'alt'   => "Add",
609                                            'title' => _("Add this ACL"),
610                                            'value' => 1));
611             $newgroup = HTML::select(array('name' => "acl[_new_group][$access]",
612                                            'style'=> 'text-align: right;',
613                                            'size' => 1));
614             foreach ($allGroups as $groupname) {
615                 if (!isset($groups[$groupname]))
616                     $newgroup->pushContent(HTML::option(array('value' => $groupname),
617                                                         $this->groupName($groupname)));
618             }
619             if (empty($groups)) {
620                 $addbutton->setAttr('checked','checked');
621                 $newperm->setAttr('checked','checked');
622                 $table->pushContent(
623                     HTML::tr(array('valign' => 'top'),
624                              HTML::td(HTML::strong($access.":")),
625                              HTML::td($newgroup),
626                              HTML::td($nbsp,$newperm),
627                              HTML::td($nbsp,$addbutton),
628                              HTML::td(HTML::em(getAccessDescription($access)))));
629             }
630             foreach ($groups as $group => $bool) {
631                 $checkbox = HTML::input(array('type' => 'checkbox',
632                                               'name' => "acl[$access][$group]",
633                                               'title' => _("Allow / Deny"),
634                                               'value' => 1));
635                 if ($bool) $checkbox->setAttr('checked','checked');
636                 $checkbox = HTML(HTML::input(array('type' => 'hidden',
637                                                    'name' => "acl[$access][$group]",
638                                                    'value' => 0)),
639                                  $checkbox);
640                 $deletebutton = HTML::input(array('type' => 'checkbox',
641                                                   'name' => "acl[_del_group][$access][$group]",
642                                                   'style' => 'background: #aaa url('.$deletesrc.')',
643                                                   //'src'  => $deletesrc,
644                                                   //'alt'   => "Del",
645                                                   'title' => _("Delete this ACL"),
646                                                   'value' => 1));
647                 if ($first_only) {
648                     $table->pushContent(
649                         HTML::tr(
650                                  HTML::td(HTML::strong($access.":")),
651                                  HTML::td(array('class' => 'cal-today','align'=>'right'),
652                                           HTML::strong($this->groupName($group))),
653                                  HTML::td(array('align'=>'center'),$nbsp,$checkbox),
654                                  HTML::td(array('align'=>'right','style' => 'background: #aaa url('.$deletesrc.') no-repeat'),$deletebutton),
655                                  HTML::td(HTML::em(getAccessDescription($access)))));
656                     $first_only = false;
657                 } else {
658                     $table->pushContent(
659                         HTML::tr(
660                                  HTML::td(),
661                                  HTML::td(array('class' => 'cal-today','align'=>'right'),
662                                           HTML::strong($this->groupName($group))),
663                                  HTML::td(array('align'=>'center'),$nbsp,$checkbox),
664                                  HTML::td(array('align'=>'right','style' => 'background: #aaa url('.$deletesrc.') no-repeat'),$deletebutton),
665                                  HTML::td()));
666                 }
667             }
668             if (!empty($groups))
669                 $table->pushContent(
670                     HTML::tr(array('valign' => 'top'),
671                              HTML::td(array('align'=>'right'),_("add ")),
672                              HTML::td($newgroup),
673                              HTML::td(array('align'=>'center'),$nbsp,$newperm),
674                              HTML::td(array('align'=>'right','style' => 'background: #ccc url('.$addsrc.') no-repeat'),$addbutton),
675                              HTML::td(HTML::small(_("Check to add this ACL")))));
676         }
677         if ($type == 'default')
678             $table->setAttr('style','border: dotted thin black; background-color:#eee;');
679         elseif ($type == 'inherited')
680             $table->setAttr('style','border: dotted thin black; background-color:#ddd;');
681         elseif ($type == 'page')
682             $table->setAttr('style','border: solid thin black; font-weight: bold;');
683         return $table;
684     }
685
686     // Print ACL as lines of [+-]user[,group,...]:access[,access...]
687     // Seperate acl's by "; " or whitespace
688     // See http://opag.ca/wiki/HelpOnAccessControlLists
689     // As used by WikiAdminSetAclSimple
690     function asAclLines() {
691         $s = ''; $line = '';
692         $this->sanify();
693         foreach ($this->perm as $access => $groups) {
694             // unify groups for same access+bool
695             //    view:CREATOR,-OWNER,
696             $line = $access.':';
697             foreach ($groups as $group => $bool) {
698                 $line .= ($bool?'':'-').$group.",";
699             }
700             if (substr($line,-1) == ',')
701                 $s .= substr($line,0,-1)."; ";
702         }
703         if (substr($s,-2) == '; ')
704             $s = substr($s,0,-2);
705         return $s;
706     }
707
708     // This is just a bad hack for testing.
709     // Simplify the ACL to a unix-like "rwx------+" string
710     // See getfacl(8)
711     function asRwxString($owner,$group=false) {
712         global $request;
713         // simplify object => rwxrw---x+ string as in cygwin (+ denotes additional ACLs)
714         $perm =& $this->perm;
715         // get effective user and group
716         $s = '---------+';
717         if (isset($perm['view'][$owner]) or
718             (isset($perm['view'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
719             $s[0] = 'r';
720         if (isset($perm['edit'][$owner]) or
721             (isset($perm['edit'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
722             $s[1] = 'w';
723         if (isset($perm['change'][$owner]) or
724             (isset($perm['change'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
725             $s[2] = 'x';
726         if (!empty($group)) {
727             if (isset($perm['view'][$group]) or
728                 (isset($perm['view'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
729                 $s[3] = 'r';
730             if (isset($perm['edit'][$group]) or
731                 (isset($perm['edit'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
732                 $s[4] = 'w';
733             if (isset($perm['change'][$group]) or
734                 (isset($perm['change'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
735                 $s[5] = 'x';
736         }
737         if (isset($perm['view'][ACL_EVERY]) or
738             (isset($perm['view'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
739             $s[6] = 'r';
740         if (isset($perm['edit'][ACL_EVERY]) or
741             (isset($perm['edit'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
742             $s[7] = 'w';
743         if (isset($perm['change'][ACL_EVERY]) or
744             (isset($perm['change'][ACL_AUTHENTICATED]) and $request->_user->isAuthenticated()))
745             $s[8] = 'x';
746         return $s;
747     }
748 }
749
750 // Local Variables:
751 // mode: php
752 // tab-width: 8
753 // c-basic-offset: 4
754 // c-hanging-comment-ender-p: nil
755 // indent-tabs-mode: nil
756 // End:
757 ?>