Import Horde_Perms from CVS HEAD.
authorMichael M Slusarz <slusarz@curecanti.org>
Fri, 20 Nov 2009 17:26:28 +0000 (10:26 -0700)
committerMichael M Slusarz <slusarz@curecanti.org>
Sun, 22 Nov 2009 18:09:53 +0000 (11:09 -0700)
21 files changed:
framework/Auth/lib/Horde/Auth.php
framework/Core/lib/Horde/Menu.php
framework/Core/lib/Horde/Registry.php
framework/Kolab_Storage/lib/Horde/Kolab/Storage/Folder.php
framework/Kolab_Storage/lib/Horde/Kolab/Storage/Permission.php
framework/Kolab_Storage/lib/Horde/Kolab/Storage/Perms.php
framework/Kolab_Storage/test/Horde/Kolab/Storage/PermsTest.php
framework/Kolab_Storage/test/Horde/Kolab/Storage/Scenario.php
framework/Kolab_Storage/test/Horde/Kolab/Storage/StorageTest.php
framework/Notification/test/Horde/Notification/Class/Notification/Listener/MobileTest.php
framework/Notification/test/Horde/Notification/Class/Notification/Listener/StatusTest.php
framework/Perms/lib/Horde/Perms.php [new file with mode: 0644]
framework/Perms/lib/Horde/Perms/Datatree.php [new file with mode: 0644]
framework/Perms/lib/Horde/Perms/Exception.php [new file with mode: 0644]
framework/Perms/lib/Horde/Perms/Permission.php [new file with mode: 0644]
framework/Perms/lib/Horde/Perms/Permission/DataTreeObject.php [new file with mode: 0644]
framework/Perms/lib/Horde/Perms/Permission/SqlObject.php [new file with mode: 0644]
framework/Perms/lib/Horde/Perms/Sql.php [new file with mode: 0644]
framework/Perms/lib/Horde/Perms/Ui.php [new file with mode: 0644]
framework/Perms/package.xml [new file with mode: 0644]
framework/Rpc/lib/Horde/Rpc/Webdav.php

index f630758..2a836ec 100644 (file)
@@ -831,9 +831,8 @@ class Horde_Auth
      *
      * @param string $permission  Allow users with this permission admin access
      *                            in the current context.
-     * @param integer $permlevel  The level of permissions to check for
-     *                            (PERMS_EDIT, PERMS_DELETE, etc). Defaults
-     *                            to PERMS_EDIT.
+     * @param integer $permlevel  The level of permissions to check for.
+     *                            Defaults to Horde_Perms::EDIT.
      * @param string $user        The user to check. Defaults to
      *                            self::getAuth().
      *
@@ -854,7 +853,7 @@ class Horde_Auth
 
         if (!is_null($permission)) {
             if (is_null($permlevel)) {
-                $permlevel = PERMS_EDIT;
+                $permlevel = Horde_Perms::EDIT;
             }
             return $GLOBALS['perms']->hasPermission($permission, $user, $permlevel);
         }
index 036d176..746409c 100644 (file)
@@ -263,7 +263,7 @@ class Horde_Menu
 
         if (isset($conf['menu']['apps']) && is_array($conf['menu']['apps'])) {
             foreach ($conf['menu']['apps'] as $app) {
-                if ($registry->get('status', $app) != 'inactive' && $registry->hasPermission($app, PERMS_SHOW)) {
+                if ($registry->get('status', $app) != 'inactive' && $registry->hasPermission($app, Horde_Perms::SHOW)) {
                     try {
                         $this->add(Horde::url($registry->getInitialPage($app)), $registry->get('name', $app), $registry->get('icon', $app), '');
                     } catch (Horde_Exception $e) {}
index c762a78..f3ae178 100644 (file)
@@ -196,9 +196,9 @@ class Horde_Registry
             throw new Horde_Exception(_("This system is currently deactivated."));
         }
 
-        /* Create the global Perms object. */
+        /* Create the global permissions object. */
         // TODO: Remove(?)
-        $GLOBALS['perms'] = Perms::singleton();
+        $GLOBALS['perms'] = Horde_Perms::singleton();
     }
 
     /**
@@ -411,7 +411,7 @@ class Horde_Registry
      *                applications are defined returns an empty array.
      */
     public function listApps($filter = null, $assoc = false,
-                             $perms = PERMS_SHOW)
+                             $perms = Horde_Perms::SHOW)
     {
         $apps = array();
         if (is_null($filter)) {
@@ -860,7 +860,7 @@ class Horde_Registry
          *  - To all admins.
          *  - To all authenticated users if no permission is set on $app.
          *  - To anyone who is allowed by an explicit ACL on $app. */
-        if ($checkPerms && !$this->hasPermission($app, PERMS_READ)) {
+        if ($checkPerms && !$this->hasPermission($app, Horde_Perms::READ)) {
             if (!Horde_Auth::isAuthenticated(array('app' => $app))) {
                 throw new Horde_Exception('User is not authorized', self::AUTH_FAILURE);
             }
@@ -975,14 +975,14 @@ class Horde_Registry
      *
      * @return boolean  Whether access is allowed.
      */
-    public function hasPermission($app, $perms = PERMS_READ)
+    public function hasPermission($app, $perms = Horde_Perms::READ)
     {
         /* Always do isAuthenticated() check first. You can be an admin, but
          * application auth != Horde admin auth. And there can *never* be
          * non-SHOW access to an application that requires authentication. */
         if (!Horde_Auth::isAuthenticated(array('app' => $app)) &&
             Horde_Auth::requireAuth($app) &&
-            ($perms != PERMS_SHOW)) {
+            ($perms != Horde_Perms::SHOW)) {
             return false;
         }
 
index cdf6c27..d3c9cf7 100644 (file)
@@ -1200,7 +1200,7 @@ class Horde_Kolab_Storage_Folder
      * Checks to see if a user has a given permission.
      *
      * @param string $userid       The userid of the user.
-     * @param integer $permission  A PERMS_* constant to test for.
+     * @param integer $permission  A Horde_Perms::* constant to test for.
      * @param string $creator      The creator of the shared object.
      *
      * @return boolean|PEAR_Error  Whether or not $userid has $permission.
@@ -1233,8 +1233,9 @@ class Horde_Kolab_Storage_Folder
             } else {
                 $perms = array(
                     'users' => array(
-                        Horde_Auth::getAuth() => PERMS_SHOW | PERMS_READ |
-                        PERMS_EDIT | PERMS_DELETE));
+                        Horde_Auth::getAuth() => Horde_Perms::ALL
+                    )
+                );
             }
             $this->_perms = new Horde_Kolab_Storage_Permission($this, $perms);
         }
index eea928e..54276b7 100644 (file)
@@ -19,7 +19,6 @@ require_once 'Horde/Autoloader.php';
 /**
  * Packages that aren't autoloadable yet
  */
-require_once 'Horde/Perms.php';
 require_once 'Horde/Group.php';
 
 /**
@@ -156,16 +155,16 @@ class Horde_Kolab_Storage_Permission extends Horde_Permission
             for ($i = 0, $j = strlen($rights); $i < $j; $i++) {
                 switch ($rights[$i]) {
                 case 'l':
-                    $result |= PERMS_SHOW;
+                    $result |= Horde_Perms::SHOW;
                     break;
                 case 'r':
-                    $result |= PERMS_READ;
+                    $result |= Horde_Perms::READ;
                     break;
                 case 'i':
-                    $result |= PERMS_EDIT;
+                    $result |= Horde_Perms::EDIT;
                     break;
                 case 'd':
-                    $result |= PERMS_DELETE;
+                    $result |= Horde_Perms::DELETE;
                     break;
                 }
             }
@@ -302,16 +301,16 @@ class Horde_Kolab_Storage_Permission extends Horde_Permission
     {
         // Convert the horde permission style to IMAP permissions
         $result = $user == $this->_folder->getOwner() ? 'a' : '';
-        if ($perms & PERMS_SHOW) {
+        if ($perms & Horde_Perms::SHOW) {
             $result .= 'l';
         }
-        if ($perms & PERMS_READ) {
+        if ($perms & Horde_Perms::READ) {
             $result .= 'r';
         }
-        if ($perms & PERMS_EDIT) {
+        if ($perms & Horde_Perms::EDIT) {
             $result .= 'iswc';
         }
-        if ($perms & PERMS_DELETE) {
+        if ($perms & Horde_Perms::DELETE) {
             $result .= 'd';
         }
 
index e722da9..ab9dcb2 100644 (file)
@@ -1,14 +1,11 @@
 <?php
 /**
  * @package Kolab_Storage
- *
- * $Horde: framework/Kolab_Storage/lib/Horde/Kolab/Storage/Perms.php,v 1.3 2009/01/06 17:49:27 jan Exp $
  */
 
 /**
  * Packages that aren't autoloadable yet
  */
-require_once 'Horde/Perms.php';
 require_once 'Horde/Group.php';
 
 /**
@@ -16,8 +13,6 @@ require_once 'Horde/Group.php';
  * Permission handling and the IMAP permission system used on the
  * Kolab server.
  *
- * $Horde: framework/Kolab_Storage/lib/Horde/Kolab/Storage/Perms.php,v 1.3 2009/01/06 17:49:27 jan Exp $
- *
  * Copyright 2006-2009 The Horde Project (http://www.horde.org/)
  *
  * See the enclosed file COPYING for license information (LGPL). If you
@@ -143,16 +138,16 @@ class Horde_Permission_Kolab extends Horde_Permission
             for ($i = 0, $j = strlen($rights); $i < $j; $i++) {
                 switch ($rights[$i]) {
                 case 'l':
-                    $result |= PERMS_SHOW;
+                    $result |= Horde_Perms::SHOW;
                     break;
                 case 'r':
-                    $result |= PERMS_READ;
+                    $result |= Horde_Perms::READ;
                     break;
                 case 'i':
-                    $result |= PERMS_EDIT;
+                    $result |= Horde_Perms::EDIT;
                     break;
                 case 'd':
-                    $result |= PERMS_DELETE;
+                    $result |= Horde_Perms::DELETE;
                     break;
                 }
             }
@@ -307,16 +302,16 @@ class Horde_Permission_Kolab extends Horde_Permission
     {
         // Convert the horde permission style to IMAP permissions
         $result = $user == $this->_folder->getOwner() ? 'a' : '';
-        if ($perms & PERMS_SHOW) {
+        if ($perms & Horde_Perms::SHOW) {
             $result .= 'l';
         }
-        if ($perms & PERMS_READ) {
+        if ($perms & Horde_Perms::READ) {
             $result .= 'r';
         }
-        if ($perms & PERMS_EDIT) {
+        if ($perms & Horde_Perms::EDIT) {
             $result .= 'iswc';
         }
-        if ($perms & PERMS_DELETE) {
+        if ($perms & Horde_Perms::DELETE) {
             $result .= 'd';
         }
 
index 5fcdafe..b5b681f 100644 (file)
@@ -43,11 +43,7 @@ class Horde_Kolab_Storage_PermsTest extends PHPUnit_Framework_TestCase
         $this->assertEquals(array(), $perms->get('perm'));
         $permissions =  array('users' =>
                               array(
-                                  'wrobel' =>
-                                  PERMS_SHOW |
-                                  PERMS_READ |
-                                  PERMS_EDIT |
-                                  PERMS_DELETE
+                                  'wrobel' => Horde_Perms::ALL
                               ));
         $perms = new Horde_Kolab_Storage_Permission($folder, $permissions);
         $this->assertTrue(is_array($perms->get('perm')));
@@ -107,9 +103,9 @@ class Horde_Kolab_Storage_PermsTest extends PHPUnit_Framework_TestCase
         unset($data['guest']);
         unset($data['default']);
         unset($data['users']['viewer']);
-        $data['users']['editor'] = PERMS_SHOW | PERMS_READ | PERMS_EDIT | PERMS_DELETE;
-        $data['users']['test'] = PERMS_SHOW | PERMS_READ;
-        $data['groups']['group'] = PERMS_SHOW | PERMS_READ;
+        $data['users']['editor'] = Horde_Perms::ALL;
+        $data['users']['test'] = Horde_Perms::SHOW | Horde_Perms::READ;
+        $data['groups']['group'] = Horde_Perms::SHOW | Horde_Perms::READ;
         $perms->setData($data);
         $perms->save();
         $this->assertNotContains('anyone', array_keys($folder->acl));
@@ -129,7 +125,7 @@ class Horde_Kolab_Storage_PermsTest extends PHPUnit_Framework_TestCase
 
         $folder = new DummyFolder(array(), 'wrobel');
         $hperms = new Horde_Permission('test');
-        $hperms->addUserPermission('wrobel', PERMS_SHOW, false);
+        $hperms->addUserPermission('wrobel', Horde_Perms::SHOW, false);
         $perms = new Horde_Kolab_Storage_Permission($folder, $hperms->data);
         $perms->save();
         $this->assertEquals('al', $folder->acl['wrobel']);
index dd96c61..a3af0a7 100644 (file)
@@ -2,8 +2,6 @@
 /**
  * Base for PHPUnit scenarios.
  *
- * $Horde: framework/Kolab_Storage/lib/Horde/Kolab/Test/Storage.php,v 1.9 2009/06/24 23:39:23 slusarz Exp $
- *
  * PHP version 5
  *
  * @category Kolab
@@ -16,8 +14,6 @@
 /**
  * Base for PHPUnit scenarios.
  *
- * $Horde: framework/Kolab_Storage/lib/Horde/Kolab/Test/Storage.php,v 1.9 2009/06/24 23:39:23 slusarz Exp $
- *
  * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
  *
  * See the enclosed file COPYING for license information (LGPL). If you
@@ -317,8 +313,7 @@ EOD;
         $this->prepareNotification();
 
         if (!isset($GLOBALS['perms'])) {
-            include_once 'Horde/Perms.php';
-            $GLOBALS['perms'] = Perms::singleton();
+            $GLOBALS['perms'] = Horde_Perms::singleton();
         }
 
         /** Provide the horde registry */
index 6b89c46..029178e 100644 (file)
@@ -54,12 +54,12 @@ class Horde_Kolab_Storage_StorageTest extends Horde_Kolab_Storage_Scenario
 
         $folder = $this->prepareNewFolder($storage1, 'Contacts', 'contact', true);
         $perms  = $folder->getPermission();
-        $perms->addUserPermission('test@example.org', PERMS_SHOW);
+        $perms->addUserPermission('test@example.org', Horde_Perms::SHOW);
         $perms->save();
 
         $folder = $this->prepareNewFolder($storage1, 'Calendar', 'event', true);
         $perms  = $folder->getPermission();
-        $perms->addUserPermission('test@example.org', PERMS_SHOW);
+        $perms->addUserPermission('test@example.org', Horde_Perms::SHOW);
         $perms->save();
 
         /** Prepare a Kolab test storage */
index 9716ecd..03db317 100644 (file)
@@ -42,8 +42,6 @@ class Horde_Notification_Class_Notification_Listener_MobileTest extends PHPUnit_
             $this->markTestSkipped('The Horde_Mobile package is not installed!');
         }
 
-        /** Loading Horde/Registry.php requires the PERMS_* constants */
-        require_once 'Horde/Perms.php';
         /**
          * The listener pulls the registry from global scope to get the image
          * directory.
index 2d1b77f..f7b3d44 100644 (file)
@@ -38,8 +38,6 @@ class Horde_Notification_Class_Notification_Listener_StatusTest extends PHPUnit_
             $this->markTestSkipped('The Horde_Perms package is not installed!');
         }
 
-        /** Loading Horde/Registry.php requires the PERMS_* constants */
-        require_once 'Horde/Perms.php';
         /**
          * The listener pulls the registry from global scope to get the image
          * directory.
diff --git a/framework/Perms/lib/Horde/Perms.php b/framework/Perms/lib/Horde/Perms.php
new file mode 100644 (file)
index 0000000..83e4555
--- /dev/null
@@ -0,0 +1,516 @@
+<?php
+/**
+ * The Horde_Perms:: class provides the Horde permissions system.
+ *
+ * Copyright 2001-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author   Chuck Hagenbuch <chuck@horde.org>
+ * @author   Jan Schneider <jan@horde.org>
+ * @category Horde
+ * @package  Horde_Perms
+ */
+class Horde_Perms
+{
+    /* Existence of object is known - object is shown to user. */
+    const SHOW = 2;
+    /* Contents of the object can be read. */
+    const READ = 4;
+    /* Contents of the object can be edited. */
+    const EDIT = 8;
+    /* The object can be deleted. */
+    const DELETE = 16;
+
+    /* A bitmask of all possible permission values. Useful for
+     * removeXxxPermission(), unsetPerm(), etc.
+     * 30 = SHOW | READ | EDIT | DELETE */
+    const ALL = 30;
+
+    /* The root permission. */
+    const ROOT = -1;
+
+    /**
+     * Caches information about application permissions.
+     *
+     * @var array
+     */
+    protected $_appPerms;
+
+    /**
+     * Singleton instance.
+     *
+     * @var array
+     */
+    static protected $_instance = null;
+
+    /**
+     * Cache for integerToArray().
+     *
+     * @var array
+     */
+    static protected $_itaCache = array();
+
+    /**
+     * Attempts to return a concrete instance based on $driver.
+     *
+     * @param string $driver  The type of the concrete subclass to return.
+     *                        The class name is based on the perms driver
+     *                        ($driver).  The code is dynamically included.
+     * @param array $params   A hash containing any additional configuration
+     *                        or connection parameters a subclass might need.
+     *
+     * @return Horde_Perms  The newly created concrete instance.
+     * @throws Horde_Perms_Exception
+     */
+    static public function factory($driver = null, $params = null)
+    {
+        if (is_null($params)) {
+            $params = Horde::getDriverConfig('perms', $driver);
+        }
+
+        if (is_null($driver)) {
+            $perms = new Perms($params);
+        } else {
+            $class = 'Horde_Perms_' . ucfirst(basename($driver));
+            if (!class_exists($class)) {
+                throw new Horde_Perms_Exception('Bad permissions class name: ' . $class);
+            }
+
+            $perms = new $class($params);
+        }
+
+        return $perms;
+    }
+
+    /**
+     * Attempts to return a reference to a concrete instance.
+     * It will only create a new instance if no instance currently exists.
+     *
+     * This method must be invoked as: $var = Horde_Perms::singleton()
+     *
+     * @return Horde_Perms  The concrete reference.
+     * @throws Horde_Perms_Exception
+     */
+    static public function singleton()
+    {
+        if (is_null(self::$_instance)) {
+            $perm_driver = $perm_params = null;
+            if (empty($GLOBALS['conf']['perms']['driver'])) {
+                $perm_driver = empty($GLOBALS['conf']['datatree']['driver'])
+                    ? null
+                    : 'datatree';
+            } else {
+                $perm_driver = $GLOBALS['conf']['perms']['driver'];
+                $perm_params = Horde::getDriverConfig('perms', $perm_driver);
+            }
+
+            self::$_instance = self::factory($perm_driver, $perm_params);
+        }
+
+        return self::$_instance;
+    }
+
+    /**
+     * Returns the available permissions for a given level.
+     *
+     * @param string $name  The permission's name.
+     *
+     * @return array  An array of available permissions and their titles or
+     *                false if not sub permissions exist for this level.
+     * @throws Horde_Perms_Exception
+     */
+    public function getAvailable($name)
+    {
+        if ($name == self::ROOT) {
+            $name = '';
+        }
+
+        if (empty($name)) {
+            /* No name passed, so top level permissions are requested. These
+             * can only be applications. */
+            $apps = $GLOBALS['registry']->listApps(array('notoolbar', 'active', 'hidden'), true);
+            foreach (array_keys($apps) as $app) {
+                $apps[$app] = $GLOBALS['registry']->get('name', $app) . ' (' . $app . ')';
+            }
+            asort($apps);
+
+            return $apps;
+        }
+
+        /* Name has been passed, explode the name to get all the levels in
+         * permission being requisted, with the app as the first level. */
+        $levels = explode(':', $name);
+
+        /* First level is always app. */
+        $app = $levels[0];
+
+        /* Return empty if no app defined API method for providing
+         * permission information. */
+        if (!$GLOBALS['registry']->hasAppMethod($app, 'perms')) {
+            return false;
+        }
+
+        /* Call the app's permission function to return the permissions
+         * specific to this app. */
+        $perms = $this->getApplicationPermissions($app);
+
+        /* Get the part of the app's permissions based on the permission
+         * name requested. */
+        $children = Horde_Array::getElement($perms['tree'], $levels);
+        if (($children === false) ||
+            !is_array($children) ||
+            !count($children)) {
+            /* No array of children available for this permission name. */
+            return false;
+        }
+
+        $perms_list = array();
+        foreach ($children as $perm_key => $perm_val) {
+            $perms_list[$perm_key] = $perms['title'][$name . ':' . $perm_key];
+        }
+
+        return $perms_list;
+    }
+
+    /**
+     * Returns the short name of an object, the last portion of the full name.
+     *
+     * @param string $name  The name of the object.
+     *
+     * @return string  The object's short name.
+     */
+    static public function getShortName($name)
+    {
+        /* If there are several components to the name, explode and
+         * get the last one, otherwise just return the name. */
+        if (strpos($name, ':') !== false) {
+            $tmp = explode(':', $name);
+            return array_pop($tmp);
+        }
+
+        return $name;
+    }
+
+    /**
+     * Given a permission name, returns the title for that permission by
+     * looking it up in the applications's permission api.
+     *
+     * @param string $name  The permissions's name.
+     *
+     * @return string  The title for the permission.
+     */
+    public function getTitle($name)
+    {
+        if ($name === self::ROOT) {
+            return _("All Permissions");
+        }
+
+        $levels = explode(':', $name);
+        if (count($levels) == 1) {
+            return $GLOBALS['registry']->get('name', $name) . ' (' . $name . ')';
+        }
+        $perm = array_pop($levels);
+
+        /* First level is always app. */
+        $app = $levels[0];
+
+        /* Return empty if no app defined API method for providing permission
+         * information. */
+        if (!$GLOBALS['registry']->hasAppMethod($app, 'perms')) {
+            return $this->getShortName($name);
+        }
+
+        $app_perms = $this->getApplicationPermissions($app);
+
+        return isset($app_perms['title'][$name])
+            ? $app_perms['title'][$name] . ' (' . $this->getShortName($name) . ')'
+            : $this->getShortName($name);
+    }
+
+    /**
+     * Returns information about permissions implemented by an application.
+     *
+     * @param string $app  An application name.
+     *
+     * @return array  Hash with permissions information.
+     */
+    public function getApplicationPermissions($app)
+    {
+        if (!isset($this->_appPerms[$app])) {
+            try {
+                $this->_appPerms[$app] = $GLOBALS['registry']->callAppMethod($app, 'perms');
+            } catch (Horde_Exception $e) {
+                $this->_appPerms[$app] = array();
+            }
+        }
+
+        return $this->_appPerms[$app];
+    }
+
+    /**
+     * Returns a new permissions object.
+     *
+     * @param string $name  The permission's name.
+     *
+     * @return Horde_Perms_Permission  A new permissions object.
+     * @throws Horde_Perms_Exception
+     */
+    public function newPermission($name)
+    {
+        throw new Horde_Perms_Exception('The administrator needs to configure a permanent permissions backend.');
+    }
+
+    /**
+     * Returns an object corresponding to the named permission, with the users
+     * and other data retrieved appropriately.
+     *
+     * @param string $name  The name of the permission to retrieve.
+     *
+     * @return Horde_Perms_Permission  A permissions object.
+     * @throws Horde_Perms_Exception
+     */
+    public function getPermission($name)
+    {
+        throw new Horde_Perms_Exception('The administrator needs to configure a permanent permissions backend.');
+    }
+
+    /**
+     * Returns an object corresponding to the given unique ID, with the users
+     * and other data retrieved appropriately.
+     *
+     * @param integer $cid  The unique ID of the permission to retrieve.
+     *
+     * @return Horde_Perms_Permission  A permissions object.
+     * @throws Horde_Perms_Exception
+     */
+    public function getPermissionById($cid)
+    {
+        throw new Horde_Perms_Exception('The administrator needs to configure a permanent permissions backend.');
+    }
+
+    /**
+     * Adds a permission to the permissions system. The permission must first
+     * be created with newPermission(), and have any initial users added to
+     * it, before this function is called.
+     *
+     * @param Horde_Perms_Permission $perm  The permissions object.
+     *
+     * @throws Horde_Perms_Exception
+     */
+    public function addPermission($perm)
+    {
+        throw new Horde_Perms_Exception('The administrator needs to configure a permanent permissions backend.');
+    }
+
+    /**
+     * Removes a permission from the permissions system permanently.
+     *
+     * @param Horde_Perms_Permission $perm  The permission to remove.
+     * @param boolean $force                Force to remove every child.
+     *
+     * @throws Horde_Perms_Exception
+     */
+    public function removePermission($perm, $force = false)
+    {
+        throw new Horde_Perms_Exception('The administrator needs to configure a permanent permissions backend.');
+    }
+
+    /**
+     * Finds out what rights the given user has to this object.
+     *
+     * @param mixed $permission  The full permission name of the object to
+     *                           check the permissions of, or the
+     *                           Horde_Permissions object.
+     * @param string $user       The user to check for. Defaults to the current
+     *                           user.
+     * @param string $creator    The user who created the event.
+     *
+     * @return mixed  A bitmask of permissions the user has, false if there
+     *                are none.
+     */
+    public function getPermissions($permission, $user = null, $creator = null)
+    {
+        if (is_string($permission)) {
+            try {
+                $permission = $this->getPermission($permission);
+            } catch (Horde_Perms_Exception $e) {
+                Horde::logMessage($e, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+                return false;
+            }
+        }
+
+        if (is_null($user)) {
+            $user = Horde_Auth::getAuth();
+        }
+
+        // If this is a guest user, only check guest permissions.
+        if (empty($user)) {
+            return $permission->getGuestPermissions();
+        }
+
+        // If $creator was specified, check creator permissions.
+        // If the user is the creator of the event see if there are creator
+        // permissions.
+        if (!is_null($creator) &&
+            strlen($user) &&
+            ($user === $creator) &&
+            (($perms = $permission->getCreatorPermissions()) !== null)) {
+            return $perms;
+        }
+
+        // Check user-level permissions.
+        $userperms = $permission->getUserPermissions();
+        if (isset($userperms[$user])) {
+            return $userperms[$user];
+        }
+
+        // If no user permissions are found, try group permissions.
+        if (isset($permission->data['groups']) &&
+            is_array($permission->data['groups']) &&
+            count($permission->data['groups'])) {
+            require_once 'Horde/Group.php';
+            $groups = Group::singleton();
+
+            $composite_perm = null;
+            $type = $permission->get('type');
+            foreach ($permission->data['groups'] as $group => $perm) {
+                if ($groups->userIsInGroup($user, $group)) {
+                    if (is_null($composite_perm)) {
+                        $composite_perm = ($type == 'matrix') ? 0 : array();
+                    }
+
+                    if ($type == 'matrix') {
+                        $composite_perm |= $perm;
+                    } else {
+                        $composite_perm[] = $perm;
+                    }
+                }
+            }
+
+            if (!is_null($composite_perm)) {
+                return $composite_perm;
+            }
+        }
+
+        // If there are default permissions, return them.
+        if (($perms = $permission->getDefaultPermissions()) !== null) {
+            return $perms;
+        }
+
+        // Otherwise, deny all permissions to the object.
+        return false;
+    }
+
+    /**
+     * Returns the unique identifier of this permission.
+     *
+     * @param Horde_Perms_Permission $permission  The permission object to get
+     *                                            the ID of.
+     *
+     * @return integer  The unique id.
+     * @throws Horde_Perms_Exception
+     */
+    public function getPermissionId($permission)
+    {
+        throw new Horde_Perms_Exception('The administrator needs to configure a permanent permissions backend.');
+    }
+
+    /**
+     * Finds out if the user has the specified rights to the given object.
+     *
+     * @param string $permission  The permission to check.
+     * @param string $user        The user to check for.
+     * @param integer $perm       The permission level that needs to be checked
+     *                            for.
+     * @param string $creator     The creator of the event
+     *
+     * @return boolean  Whether the user has the specified permissions.
+     */
+    public function hasPermission($permission, $user, $perm, $creator = null)
+    {
+        return ($this->getPermissions($permission, $user, $creator) & $perm);
+    }
+
+    /**
+     * Checks if a permission exists in the system.
+     *
+     * @param string $permission  The permission to check.
+     *
+     * @return boolean  True if the permission exists.
+     */
+    public function exists($permission)
+    {
+        return false;
+    }
+
+    /**
+     * Returns a list of parent permissions.
+     *
+     * @param string $child  The name of the child to retrieve parents for.
+     *
+     * @return array  A hash with all parents in a tree format.
+     * @throws Horde_Perms_Exception
+     */
+    public function getParents($child)
+    {
+        throw new Horde_Perms_Exception('The administrator needs to configure a permanent permissions backend.');
+    }
+
+    /**
+     * Returns all permissions of the system in a tree format.
+     *
+     * @return array  A hash with all permissions in a tree format.
+     */
+    public function getTree()
+    {
+        return array();
+    }
+
+    /**
+     * Returns an hash of the available permissions.
+     *
+     * @return array  The available permissions as a hash.
+     */
+    static public function getPermsArray()
+    {
+        return array(
+            self::SHOW => _("Show"),
+            self::READ => _("Read"),
+            self::EDIT => _("Edit"),
+            self::DELETE => _("Delete")
+        );
+    }
+
+    /**
+     * Given an integer value of permissions returns an array representation
+     * of the integer.
+     *
+     * @param integer $int  The integer representation of permissions.
+     *
+     * @return TODO
+     */
+    static public function integerToArray($int)
+    {
+        if (isset(self::$_itaCache[$int])) {
+            return self::$_itaCache[$int];
+        }
+
+        self::$_itaCache[$int] = array();
+
+        /* Get the available perms array. */
+        $perms = self::getPermsArray();
+
+        /* Loop through each perm and check if its value is included in the
+         * integer representation. */
+        foreach ($perms as $val => $label) {
+            if ($int & $val) {
+                self::$_itaCache[$int][$val] = true;
+            }
+        }
+
+        return self::$_itaCache[$int];
+    }
+
+}
diff --git a/framework/Perms/lib/Horde/Perms/Datatree.php b/framework/Perms/lib/Horde/Perms/Datatree.php
new file mode 100644 (file)
index 0000000..e0c697b
--- /dev/null
@@ -0,0 +1,250 @@
+<?php
+/**
+ * The Horde_Perms_Datatree:: class provides a DataTree driver for the Horde
+ * permissions system.
+ *
+ * Copyright 2001-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author   Chuck Hagenbuch <chuck@horde.org>
+ * @author   Jan Schneider <jan@horde.org>
+ * @category Horde
+ * @package  Horde_Perms
+ */
+class Horde_Perms_Datatree extends Horde_Perms
+{
+    /**
+     * Pointer to a DataTree instance to manage the different permissions.
+     *
+     * @var DataTree
+     */
+    protected $_datatree;
+
+    /**
+     * Pointer to a Horde_Cache instance.
+     *
+     * @var Horde_Cache
+     */
+    protected $_cache;
+
+    /**
+     * Cache for getPermission().
+     *
+     * @var array
+     */
+    protected $_permsCache = array();
+
+    /**
+     * Constructor.
+     *
+     * @throws Horde_Exception
+     */
+    public function __construct()
+    {
+        global $conf;
+
+        if (empty($conf['datatree']['driver'])) {
+            throw new Horde_Exception('You must configure a DataTree backend.');
+        }
+
+        $driver = $conf['datatree']['driver'];
+        $this->_datatree = DataTree::singleton($driver,
+                                               array_merge(Horde::getDriverConfig('datatree', $driver),
+                                                           array('group' => 'horde.perms')));
+
+        $this->_cache = Horde_Cache::singleton($GLOBALS['conf']['cache']['driver'],
+                                               Horde::getDriverConfig('cache', $GLOBALS['conf']['cache']['driver']));
+
+        parent::__construct();
+    }
+
+    /**
+     * Returns a new permissions object.
+     *
+     * @param string $name  The permission's name.
+     *
+     * @return DataTreeObject_Permissions  A new permissions object.
+     */
+    public function newPermission($name)
+    {
+        $type = 'matrix';
+        $params = null;
+
+        if ($pos = strpos($name, ':')) {
+            try {
+                $info = $this->getApplicationPermissions(substr($name, 0, $pos));
+                if (isset($info['type']) && isset($info['type'][$name])) {
+                    $type = $info['type'][$name];
+                }
+
+                if (isset($info['params']) && isset($info['params'][$name])) {
+                    $params = $info['params'][$name];
+                }
+            } catch (Horde_Perms_Exception $e) {}
+        }
+
+        $perm = new Horde_Perms_Permission_DataTreeObject($name, $type, $params);
+        $perm->setDataTree($this->_datatree);
+
+        return $perm;
+    }
+
+    /**
+     * Returns a permission object corresponding to the named permission,
+     * with the users and other data retrieved appropriately.
+     *
+     * @param string $name  The name of the permission to retrieve.
+     *
+     * @return TODO
+     */
+    public function getPermission($name)
+    {
+        if (isset($this->_permsCache[$name])) {
+            return $this->_permsCache[$name];
+        }
+
+        $perm = $this->_cache->get('perm_' . $name, $GLOBALS['conf']['cache']['default_lifetime']);
+        if ($perm === false) {
+            $perm = $this->_datatree->getObject($name, 'Horde_Perms_Permission_DataTreeObject');
+            $this->_cache->set('perm_' . $name, serialize($perm), $GLOBALS['conf']['cache']['default_lifetime']);
+            $this->_permsCache[$name] = $perm;
+        } else {
+            $this->_permsCache[$name] = unserialize($perm);
+        }
+
+        return $this->_permsCache[$name];
+    }
+
+    /**
+     * Returns a permission object corresponding to the given unique ID,
+     * with the users and other data retrieved appropriately.
+     *
+     * @param integer $cid  The unique ID of the permission to retrieve.
+     */
+    public function getPermissionById($cid)
+    {
+        return ($cid == Horde_Perms::ROOT)
+            ? $this->newPermission(Horde_Perms::ROOT)
+            : $this->_datatree->getObjectById($cid, 'Horde_Perms_Permission_DataTreeObject');
+    }
+
+    /**
+     * Adds a permission to the permissions system. The permission must first
+     * be created with newPermission(), and have any initial users added to
+     * it, before this function is called.
+     *
+     * @param Horde_Perms_Permission_DataTreeObject $perm  The new perm
+     *                                                     object.
+     * @throws Horde_Perms_Exception
+     */
+    public function addPermission($perm)
+    {
+        if (!($perm instanceof Horde_Perms_Permission_DataTreeObject)) {
+            throw Horde_Perms_Exception('Permissions must be Horde_Perms_Permission_DataTreeObject objects.');
+        }
+
+        $name = $perm->getName();
+        if (empty($name)) {
+            throw Horde_Perms_Exception('Permission names must be non-empty.');
+        }
+        $this->_cache->expire('perm_' . $name);
+        $this->_cache->expire('perm_exists_' . $name);
+
+        return $this->_datatree->add($perm);
+    }
+
+    /**
+     * Removes a permission from the permissions system permanently.
+     *
+     * @param Horde_Perms_Permission_DataTreeObject $perm  The permission to
+     *                                                     remove.
+     * @param boolean $force                               Force to remove
+     *                                                     every child.
+     */
+    public function removePermission($perm, $force = false)
+    {
+        if (!($perm instanceof Horde_Perms_Permission_DataTreeObject)) {
+            throw Horde_Perms_Exception('Permissions must be Horde_Perms_Permission_DataTreeObject objects.');
+        }
+
+        $keys = $this->_datatree->get(DATATREE_FORMAT_FLAT, $perm->name, true);
+        foreach ($keys as $key) {
+            $this->_cache->expire('perm_' . $key);
+            $this->_cache->expire('perm_exists_' . $key);
+        }
+
+        return $this->_datatree->remove($perm->name, $force);
+    }
+
+    /**
+     * Returns the unique identifier of this permission.
+     *
+     * @param Horde_Perms_Permission_DataTreeObject $perm  The permission
+     *                                                     object to get the
+     *                                                     ID of.
+     *
+     * @return integer  The unique id.
+     */
+    public function getPermissionId($permission)
+    {
+        return $this->_datatree->getId($permission->getName());
+    }
+
+    /**
+     * Checks if a permission exists in the system.
+     *
+     * @param string $permission  The permission to check.
+     *
+     * @return boolean  True if the permission exists.
+     */
+    public function exists($permission)
+    {
+        $key = 'perm_exists_' . $permission;
+        $exists = $this->_cache->get($key, $GLOBALS['conf']['cache']['default_lifetime']);
+        if ($exists === false) {
+            $exists = $this->_datatree->exists($permission);
+            $this->_cache->set($key, (string)$exists);
+        }
+
+        return (bool)$exists;
+    }
+
+    /**
+     * Returns a list of parent permissions.
+     *
+     * @param string $child  The name of the child to retrieve parents for.
+     *
+     * @return array  A hash with all parents in a tree format.
+     */
+    public function getParents($child)
+    {
+        return $this->_datatree->getParents($child);
+    }
+
+    /**
+     * Returns a child's direct parent ID.
+     *
+     * @param mixed $child  Either the object, an array containing the
+     *                      path elements, or the object name for which
+     *                      to look up the parent's ID.
+     *
+     * @return mixed  The unique ID of the parent or PEAR_Error on error.
+     */
+    public function getParent($child)
+    {
+        return $this->_datatree->getParent($child);
+    }
+
+    /**
+     * Returns all permissions of the system in a tree format.
+     *
+     * @return array  A hash with all permissions in a tree format.
+     */
+    public function getTree()
+    {
+        return $this->_datatree->get(DATATREE_FORMAT_FLAT, Horde_Perms::ROOT, true);
+    }
+
+}
diff --git a/framework/Perms/lib/Horde/Perms/Exception.php b/framework/Perms/lib/Horde/Perms/Exception.php
new file mode 100644 (file)
index 0000000..39b532c
--- /dev/null
@@ -0,0 +1,16 @@
+<?php
+/**
+ * Exception handler for the Horde_Perms package.
+ *
+ * Copyright 2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author   Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @package  Horde_Perms
+ */
+class Horde_Perms_Exception extends Horde_Exception
+{
+}
diff --git a/framework/Perms/lib/Horde/Perms/Permission.php b/framework/Perms/lib/Horde/Perms/Permission.php
new file mode 100644 (file)
index 0000000..8c1ad69
--- /dev/null
@@ -0,0 +1,592 @@
+<?php
+/**
+ * Instance of a single permissioning object.
+ *
+ * Copyright 2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author   Chuck Hagenbuch <chuck@horde.org>
+ * @author   Jan Schneider <jan@horde.org>
+ * @category Horde
+ * @package  Horde_Perms
+ */
+class Horde_Perms_Permission
+{
+    // TODO
+    public $data;
+
+    // TODO
+    public $name;
+
+    /**
+     * Constructor.
+     *
+     * @param string $name   The name of the perm.
+     * @param string $type   The permission type.
+     * @param array $params  A hash with any parameters that the permission
+     *                       type needs.
+     */
+    public function __construct($name, $type = 'matrix', $params = null)
+    {
+        $this->setName($name);
+        $this->data['type'] = $type;
+        if (is_array($params)) {
+            $this->data['params'] = $params;
+        }
+    }
+
+    /**
+     * Gets one of the attributes of the object, or null if it isn't defined.
+     *
+     * @param string $attribute  The attribute to get.
+     *
+     * @return mixed  The value of the attribute, or null.
+     */
+    public function get($attribute)
+    {
+        if (isset($this->data[$attribute])) {
+            return $this->data[$attribute];
+        }
+
+        return ($attribute == 'type') ? 'matrix' : null;
+    }
+
+    /**
+     * Get permission name.
+     *
+     * @return string  Permission name.
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * Set permission name
+     *
+     * @param string $name  Permission name.
+     */
+    public function setName($name)
+    {
+        $this->name = $name;
+    }
+
+    /**
+     * Get permission details.
+     *
+     * @return array  Permission details.
+     */
+    public function getData()
+    {
+        return $this->data;
+    }
+
+    /**
+     * Set permission details.
+     *
+     * @param string $data  Permission details.
+     */
+    public function setData($data)
+    {
+        $this->data = $data;
+    }
+
+    /**
+     * Updates the permissions based on data passed in the array.
+     *
+     * @param array $perms  An array containing the permissions which are to
+     *                      be updated.
+     */
+    public function updatePermissions($perms)
+    {
+        $type = $this->get('type');
+
+        if ($type == 'matrix') {
+            /* Array of permission types to iterate through. */
+            $perm_types = Horde_Perms::getPermsArray();
+        }
+
+        foreach ($perms as $perm_class => $perm_values) {
+            switch ($perm_class) {
+            case 'default':
+            case 'guest':
+            case 'creator':
+                if ($type == 'matrix') {
+                    foreach ($perm_types as $val => $label) {
+                        if (!empty($perm_values[$val])) {
+                            $this->setPerm($perm_class, $val, false);
+                        } else {
+                            $this->unsetPerm($perm_class, $val, false);
+                        }
+                    }
+                } elseif (!empty($perm_values)) {
+                    $this->setPerm($perm_class, $perm_values, false);
+                } else {
+                    $this->unsetPerm($perm_class, null, false);
+                }
+                break;
+
+            case 'u':
+            case 'g':
+                $permId = array('class' => $perm_class == 'u' ? 'users' : 'groups');
+                /* Figure out what names that are stored in this permission
+                 * class have not been submitted for an update, ie. have been
+                 * removed entirely. */
+                $current_names = isset($this->data[$permId['class']])
+                    ? array_keys($this->data[$permId['class']])
+                    : array();
+                $updated_names = array_keys($perm_values);
+                $removed_names = array_diff($current_names, $updated_names);
+
+                /* Remove any names that have been completely unset. */
+                foreach ($removed_names as $name) {
+                    unset($this->data[$permId['class']][$name]);
+                }
+
+                /* If nothing to actually update finish with this case. */
+                if (is_null($perm_values)) {
+                    continue;
+                }
+
+                /* Loop through the names and update permissions for each. */
+                foreach ($perm_values as $name => $name_values) {
+                    $permId['name'] = $name;
+
+                    if ($type == 'matrix') {
+                        foreach ($perm_types as $val => $label) {
+                            if (!empty($name_values[$val])) {
+                                $this->setPerm($permId, $val, false);
+                            } else {
+                                $this->unsetPerm($permId, $val, false);
+                            }
+                        }
+                    } elseif (!empty($name_values)) {
+                        $this->setPerm($permId, $name_values, false);
+                    } else {
+                        $this->unsetPerm($permId, null, false);
+                    }
+                }
+                break;
+            }
+        }
+    }
+
+    /**
+     * TODO
+     */
+    public function setPerm($permId, $permission, $update = true)
+    {
+        if (is_array($permId)) {
+            if (empty($permId['name'])) {
+                return;
+            }
+            if ($this->get('type') == 'matrix' &&
+                isset($this->data[$permId['class']][$permId['name']])) {
+                $this->data[$permId['class']][$permId['name']] |= $permission;
+            } else {
+                $this->data[$permId['class']][$permId['name']] = $permission;
+            }
+        } else {
+            if ($this->get('type') == 'matrix' &&
+                isset($this->data[$permId])) {
+                $this->data[$permId] |= $permission;
+            } else {
+                $this->data[$permId] = $permission;
+            }
+        }
+
+        if ($update) {
+            $this->save();
+        }
+    }
+
+    /**
+     * TODO
+     */
+    public function unsetPerm($permId, $permission, $update = true)
+    {
+        if (is_array($permId)) {
+            if (empty($permId['name'])) {
+                return;
+            }
+
+            if ($this->get('type') == 'matrix') {
+                if (isset($this->data[$permId['class']][$permId['name']])) {
+                    $this->data[$permId['class']][$permId['name']] &= ~$permission;
+                    if (empty($this->data[$permId['class']][$permId['name']])) {
+                        unset($this->data[$permId['class']][$permId['name']]);
+                    }
+                } else {
+                    $update = false;
+                }
+            } else {
+                unset($this->data[$permId['class']][$permId['name']]);
+            }
+        } else {
+            if ($this->get('type') == 'matrix') {
+                if (isset($this->data[$permId])) {
+                    $this->data[$permId] &= ~$permission;
+                } else {
+                    $update = false;
+                }
+            } else {
+                unset($this->data[$permId]);
+            }
+        }
+
+        if ($update) {
+            $this->save();
+        }
+    }
+
+    /**
+     * Grants a user additional permissions to this object.
+     *
+     * @param string $uer          The user to grant additional permissions
+     *                             to.
+     * @param integer $permission  The permission (DELETE, etc.) to add.
+     * @param boolean $update      Whether to automatically update the
+     *                             backend.
+     */
+    public function addUserPermission($user, $permission, $update = true)
+    {
+        if (empty($user)) {
+            return;
+        }
+
+        if ($this->get('type') == 'matrix' &&
+            isset($this->data['users'][$user])) {
+            $this->data['users'][$user] |= $permission;
+        } else {
+            $this->data['users'][$user] = $permission;
+        }
+
+        if ($update) {
+            $this->save();
+        }
+    }
+
+    /**
+     * Grants guests additional permissions to this object.
+     *
+     * @param integer $permission  The permission (DELETE, etc.) to add.
+     * @param boolean $update      Whether to automatically update the
+     *                             backend.
+     */
+    public function addGuestPermission($permission, $update = true)
+    {
+        if ($this->get('type') == 'matrix' &&
+            isset($this->data['guest'])) {
+            $this->data['guest'] |= $permission;
+        } else {
+            $this->data['guest'] = $permission;
+        }
+
+        if ($update) {
+            $this->save();
+        }
+    }
+
+    /**
+     * Grants creators additional permissions to this object.
+     *
+     * @param integer $permission  The permission (DELETE, etc.) to add.
+     * @param boolean $update      Whether to automatically update the
+     *                             backend.
+     */
+    public function addCreatorPermission($permission, $update = true)
+    {
+        if ($this->get('type') == 'matrix' &&
+            isset($this->data['creator'])) {
+            $this->data['creator'] |= $permission;
+        } else {
+            $this->data['creator'] = $permission;
+        }
+
+        if ($update) {
+            $this->save();
+        }
+    }
+
+    /**
+     * Grants additional default permissions to this object.
+     *
+     * @param integer $permission  The permission (DELETE, etc.) to add.
+     * @param boolean $update      Whether to automatically update the
+     *                             backend.
+     */
+    public function addDefaultPermission($permission, $update = true)
+    {
+        if ($this->get('type') == 'matrix' &&
+            isset($this->data['default'])) {
+            $this->data['default'] |= $permission;
+        } else {
+            $this->data['default'] = $permission;
+        }
+
+        if ($update) {
+            $this->save();
+        }
+    }
+
+    /**
+     * Grants a group additional permissions to this object.
+     *
+     * @param integer $groupId     The id of the group to grant additional
+     *                             permissions to.
+     * @param integer $permission  The permission (DELETE, etc.) to add.
+     * @param boolean $update      Whether to automatically update the
+     *                             backend.
+     */
+    public function addGroupPermission($groupId, $permission, $update = true)
+    {
+        if (empty($groupId)) {
+            return;
+        }
+
+        if ($this->get('type') == 'matrix' &&
+            isset($this->data['groups'][$groupId])) {
+            $this->data['groups'][$groupId] |= $permission;
+        } else {
+            $this->data['groups'][$groupId] = $permission;
+        }
+
+        if ($update) {
+            $this->save();
+        }
+    }
+
+    /**
+     * Removes a permission that a user currently has on this object.
+     *
+     * @param string $user         The user to remove the permission from.
+     * @param integer $permission  The permission (DELETE, etc.) to
+     *                             remove.
+     * @param boolean $update      Whether to automatically update the
+     *                             backend.
+     */
+    public function removeUserPermission($user, $permission, $update = true)
+    {
+        if (empty($user) || !isset($this->data['users'][$user])) {
+            return;
+        }
+
+        if ($this->get('type') == 'matrix') {
+            $this->data['users'][$user] &= ~$permission;
+            if (empty($this->data['users'][$user])) {
+                unset($this->data['users'][$user]);
+            }
+        } else {
+            unset($this->data['users'][$user]);
+        }
+
+        if ($update) {
+            $this->save();
+        }
+    }
+
+    /**
+     * Removes a permission that guests currently have on this object.
+     *
+     * @param integer $permission  The permission (DELETE, etc.) to
+     *                             remove.
+     * @param boolean $update      Whether to automatically update the
+     *                             backend.
+     */
+    public function removeGuestPermission($permission, $update = true)
+    {
+        if (!isset($this->data['guest'])) {
+            return;
+        }
+
+        if ($this->get('type') == 'matrix') {
+            $this->data['guest'] &= ~$permission;
+        } else {
+            unset($this->data['guest']);
+        }
+
+        if ($update) {
+            $this->save();
+        }
+    }
+
+    /**
+     * Removes a permission that creators currently have on this object.
+     *
+     * @param integer $permission  The permission (DELETE, etc.) to
+     *                             remove.
+     * @param boolean $update      Whether to automatically update the
+     *                             backend.
+     */
+    public function removeCreatorPermission($permission, $update = true)
+    {
+        if (!isset($this->data['creator'])) {
+            return;
+        }
+
+        if ($this->get('type') == 'matrix') {
+            $this->data['creator'] &= ~$permission;
+        } else {
+            unset($this->data['creator']);
+        }
+
+        if ($update) {
+            $this->save();
+        }
+    }
+
+    /**
+     * Removes a default permission on this object.
+     *
+     * @param integer $permission  The permission (DELETE, etc.) to
+     *                             remove.
+     * @param boolean $update      Whether to automatically update the
+     *                             backend.
+     */
+    public function removeDefaultPermission($permission, $update = true)
+    {
+        if (!isset($this->data['default'])) {
+            return;
+        }
+
+        if ($this->get('type') == 'matrix') {
+            $this->data['default'] &= ~$permission;
+        } else {
+            unset($this->data['default']);
+        }
+
+        if ($update) {
+            $this->save();
+        }
+    }
+
+    /**
+     * Removes a permission that a group currently has on this object.
+     *
+     * @param integer $groupId     The id of the group to remove the
+     *                             permission from.
+     * @param integer $permission  The permission (DELETE, etc.) to
+     *                             remove.
+     * @param boolean $update      Whether to automatically update the
+     *                             backend.
+     */
+    public function removeGroupPermission($groupId, $permission,
+                                          $update = true)
+    {
+        if (empty($groupId) || !isset($this->data['groups'][$groupId])) {
+            return;
+        }
+
+        if ($this->get('type') == 'matrix') {
+            $this->data['groups'][$groupId] &= ~$permission;
+            if (empty($this->data['groups'][$groupId])) {
+                unset($this->data['groups'][$groupId]);
+            }
+        } else {
+            unset($this->data['groups'][$groupId]);
+        }
+
+        if ($update) {
+            $this->save();
+        }
+    }
+
+    /**
+     * Returns an array of all user permissions on this object.
+     *
+     * @param integer $perm  List only users with this permission level.
+     *                       Defaults to all users.
+     *
+     * @return array  All user permissions for this object, indexed by user.
+     */
+    public function getUserPermissions($perm = null)
+    {
+        if (!isset($this->data['users']) || !is_array($this->data['users'])) {
+            return array();
+        } elseif (!$perm) {
+            return $this->data['users'];
+        }
+
+        $users = array();
+        foreach ($this->data['users'] as $user => $uperm) {
+            if ($uperm & $perm) {
+                $users[$user] = $uperm;
+            }
+        }
+
+        return $users;
+    }
+
+    /**
+     * Returns the guest permissions on this object.
+     *
+     * @return integer  The guest permissions on this object.
+     */
+    public function getGuestPermissions()
+    {
+        return empty($this->data['guest'])
+            ? null
+            : $this->data['guest'];
+    }
+
+    /**
+     * Returns the creator permissions on this object.
+     *
+     * @return integer  The creator permissions on this object.
+     */
+    public function getCreatorPermissions()
+    {
+        return empty($this->data['creator'])
+            ? null
+            : $this->data['creator'];
+    }
+
+    /**
+     * Returns the default permissions on this object.
+     *
+     * @return integer  The default permissions on this object.
+     */
+    public function getDefaultPermissions()
+    {
+        return empty($this->data['default'])
+            ? null
+            : $this->data['default'];
+    }
+
+    /**
+     * Returns an array of all group permissions on this object.
+     *
+     * @param integer $perm  List only users with this permission level.
+     *                       Defaults to all users.
+     *
+     * @return array  All group permissions for this object, indexed by group.
+     */
+    public function getGroupPermissions($perm = null)
+    {
+        if (!isset($this->data['groups']) ||
+            !is_array($this->data['groups'])) {
+            return array();
+        } elseif (!$perm) {
+            return $this->data['groups'];
+        }
+
+        $groups = array();
+        foreach ($this->data['groups'] as $group => $gperm) {
+            if ($gperm & $perm) {
+                $groups[$group] = $gperm;
+            }
+        }
+
+        return $groups;
+    }
+
+    /**
+     * TODO
+     */
+    public function save()
+    {
+    }
+
+}
diff --git a/framework/Perms/lib/Horde/Perms/Permission/DataTreeObject.php b/framework/Perms/lib/Horde/Perms/Permission/DataTreeObject.php
new file mode 100644 (file)
index 0000000..a81cd6c
--- /dev/null
@@ -0,0 +1,562 @@
+<?php
+/**
+ * Extension of the DataTreeObject class for storing Permission information in
+ * the DataTree driver. If you want to store specialized Permission
+ * information, you should extend this class instead of extending
+ * DataTreeObject directly.
+ *
+ * @TODO This class duplicates most of the functionality of the
+ * Horde_Permission class. However, because for BC/DataTree reasons it
+ * must extend DataTreeObject, we can't remove these methods yet.
+ *
+ * @author   Chuck Hagenbuch <chuck@horde.org>
+ * @author   Jan Schneider <jan@horde.org>
+ * @category Horde
+ * @package  Horde_Perms
+ */
+class Horde_Perms_Permission_DataTreeObject extends DataTreeObject
+{
+    /**
+     * Constructor. Just makes sure to call the parent constructor so that
+     * the perm's name is set properly.
+     *
+     * @param string $name   The name of the perm.
+     * @param string $type   The permission type.
+     * @param array $params  A hash with any parameters that the permission
+     *                       type needs.
+     */
+    public function __construct($name, $type = 'matrix', $params = null)
+    {
+        parent::DataTreeObject($name);
+
+        $this->data['type'] = $type;
+        if (is_array($params)) {
+            $this->data['params'] = $params;
+        }
+    }
+
+    /**
+     * Gets one of the attributes of the object, or null if it isn't defined.
+     *
+     * @param string $attribute  The attribute to get.
+     *
+     * @return mixed  The value of the attribute, or null.
+     */
+    public function get($attribute)
+    {
+        $value = parent::get($attribute);
+
+        return (is_null($value) && $attribute == 'type')
+            ? 'matrix'
+            : $value;
+    }
+
+    /**
+     * Updates the permissions based on data passed in the array.
+     *
+     * @param array $perms  An array containing the permissions which are to
+     *                      be updated.
+     */
+    public function updatePermissions($perms)
+    {
+        $type = $this->get('type');
+
+        if ($type == 'matrix') {
+            /* Array of permission types to iterate through. */
+            $perm_types = Horde_Perms::getPermsArray();
+        }
+
+        foreach ($perms as $perm_class => $perm_values) {
+            switch ($perm_class) {
+            case 'default':
+            case 'guest':
+            case 'creator':
+                if ($type == 'matrix') {
+                    foreach ($perm_types as $val => $label) {
+                        if (!empty($perm_values[$val])) {
+                            $this->setPerm($perm_class, $val, false);
+                        } else {
+                            $this->unsetPerm($perm_class, $val, false);
+                        }
+                    }
+                } elseif (!empty($perm_values)) {
+                    $this->setPerm($perm_class, $perm_values, false);
+                } else {
+                    $this->unsetPerm($perm_class, null, false);
+                }
+                break;
+
+            case 'u':
+            case 'g':
+                $permId = array('class' => $perm_class == 'u' ? 'users' : 'groups');
+                /* Figure out what names that are stored in this permission
+                 * class have not been submitted for an update, ie. have been
+                 * removed entirely. */
+                $current_names = isset($this->data[$permId['class']])
+                    ? array_keys($this->data[$permId['class']])
+                    : array();
+                $updated_names = array_keys($perm_values);
+                $removed_names = array_diff($current_names, $updated_names);
+
+                /* Remove any names that have been completely unset. */
+                foreach ($removed_names as $name) {
+                    unset($this->data[$permId['class']][$name]);
+                }
+
+                /* If nothing to actually update finish with this case. */
+                if (is_null($perm_values)) {
+                    continue;
+                }
+
+                /* Loop through the names and update permissions for each. */
+                foreach ($perm_values as $name => $name_values) {
+                    $permId['name'] = $name;
+
+                    if ($type == 'matrix') {
+                        foreach ($perm_types as $val => $label) {
+                            if (!empty($name_values[$val])) {
+                                $this->setPerm($permId, $val, false);
+                            } else {
+                                $this->unsetPerm($permId, $val, false);
+                            }
+                        }
+                    } elseif (!empty($name_values)) {
+                        $this->setPerm($permId, $name_values, false);
+                    } else {
+                        $this->unsetPerm($permId, null, false);
+                    }
+                }
+                break;
+            }
+        }
+    }
+
+    /**
+     * TODO
+     */
+    public function setPerm($permId, $permission, $update = true)
+    {
+        if (is_array($permId)) {
+            if (empty($permId['name'])) {
+                return;
+            }
+            if ($this->get('type') == 'matrix' &&
+                isset($this->data[$permId['class']][$permId['name']])) {
+                $this->data[$permId['class']][$permId['name']] |= $permission;
+            } else {
+                $this->data[$permId['class']][$permId['name']] = $permission;
+            }
+        } else {
+            if ($this->get('type') == 'matrix' &&
+                isset($this->data[$permId])) {
+                $this->data[$permId] |= $permission;
+            } else {
+                $this->data[$permId] = $permission;
+            }
+        }
+
+        if ($update) {
+            $this->save();
+        }
+    }
+
+    /**
+     * TODO
+     */
+    public function unsetPerm($permId, $permission, $update = true)
+    {
+        if (is_array($permId)) {
+            if (empty($permId['name'])) {
+                return;
+            }
+            if ($this->get('type') == 'matrix') {
+                if (isset($this->data[$permId['class']][$permId['name']])) {
+                    $this->data[$permId['class']][$permId['name']] &= ~$permission;
+                    if (empty($this->data[$permId['class']][$permId['name']])) {
+                        unset($this->data[$permId['class']][$permId['name']]);
+                    }
+                    if ($update) {
+                        $this->save();
+                    }
+                }
+            } else {
+                unset($this->data[$permId['class']][$permId['name']]);
+                if ($update) {
+                    $this->save();
+                }
+            }
+        } else {
+            if ($this->get('type') == 'matrix') {
+                if (isset($this->data[$permId])) {
+                    $this->data[$permId] &= ~$permission;
+                    if ($update) {
+                        $this->save();
+                    }
+                }
+            } else {
+                unset($this->data[$permId]);
+                if ($update) {
+                    $this->save();
+                }
+            }
+        }
+    }
+
+    /**
+     * Grants a user additional permissions to this object.
+     *
+     * @param string $user         The user to grant additional permissions
+     *                             to.
+     * @param integer $permission  The permission (DELETE, etc.) to add.
+     * @param boolean $update      Whether to automatically update the
+     *                             backend.
+     */
+    public function addUserPermission($user, $permission, $update = true)
+    {
+        if (empty($user)) {
+            return;
+        }
+
+        if ($this->get('type') == 'matrix' &&
+            isset($this->data['users'][$user])) {
+            $this->data['users'][$user] |= $permission;
+        } else {
+            $this->data['users'][$user] = $permission;
+        }
+
+        if ($update) {
+            $this->save();
+        }
+    }
+
+    /**
+     * Grants guests additional permissions to this object.
+     *
+     * @param integer $permission  The permission (DELETE, etc.) to add.
+     * @param boolean $update      Whether to automatically update the
+     *                             backend.
+     */
+    public function addGuestPermission($permission, $update = true)
+    {
+        if ($this->get('type') == 'matrix' &&
+            isset($this->data['guest'])) {
+            $this->data['guest'] |= $permission;
+        } else {
+            $this->data['guest'] = $permission;
+        }
+
+        if ($update) {
+            $this->save();
+        }
+    }
+
+    /**
+     * Grants creators additional permissions to this object.
+     *
+     * @param integer $permission  The permission (DELETE, etc.) to add.
+     * @param boolean $update      Whether to automatically update the
+     *                             backend.
+     */
+    public function addCreatorPermission($permission, $update = true)
+    {
+        if ($this->get('type') == 'matrix' &&
+            isset($this->data['creator'])) {
+            $this->data['creator'] |= $permission;
+        } else {
+            $this->data['creator'] = $permission;
+        }
+
+        if ($update) {
+            $this->save();
+        }
+    }
+
+    /**
+     * Grants additional default permissions to this object.
+     *
+     * @param integer $permission  The permission (DELETE, etc.) to add.
+     * @param boolean $update      Whether to automatically update the
+     *                             backend.
+     */
+    public function addDefaultPermission($permission, $update = true)
+    {
+        if ($this->get('type') == 'matrix' &&
+            isset($this->data['default'])) {
+            $this->data['default'] |= $permission;
+        } else {
+            $this->data['default'] = $permission;
+        }
+
+        if ($update) {
+            $this->save();
+        }
+    }
+
+    /**
+     * Grants a group additional permissions to this object.
+     *
+     * @param integer $groupId     The id of the group to grant additional
+     *                             permissions to.
+     * @param integer $permission  The permission (DELETE, etc.) to add.
+     * @param boolean $update      Whether to automatically update the
+     *                             backend.
+     */
+    public function addGroupPermission($groupId, $permission, $update = true)
+    {
+        if (empty($groupId)) {
+            return;
+        }
+
+        if ($this->get('type') == 'matrix' &&
+            isset($this->data['groups'][$groupId])) {
+            $this->data['groups'][$groupId] |= $permission;
+        } else {
+            $this->data['groups'][$groupId] = $permission;
+        }
+
+        if ($update) {
+            $this->save();
+        }
+    }
+
+    /**
+     * Removes a permission that a user currently has on this object.
+     *
+     * @param string $user         The user to remove the permission from.
+     * @param integer $permission  The permission (DELETE, etc.) to
+     *                             remove.
+     * @param boolean $update      Whether to automatically update the
+     *                             backend.
+     */
+    public function removeUserPermission($user, $permission, $update = true)
+    {
+        if (empty($user) || !isset($this->data['users'][$user])) {
+            return;
+        }
+
+        if ($this->get('type') == 'matrix') {
+            $this->data['users'][$user] &= ~$permission;
+            if (empty($this->data['users'][$user])) {
+                unset($this->data['users'][$user]);
+            }
+        } else {
+            unset($this->data['users'][$user]);
+        }
+
+        if ($update) {
+            $this->save();
+        }
+    }
+
+    /**
+     * Removes a permission that guests currently have on this object.
+     *
+     * @param integer $permission  The permission (DELETE, etc.) to remove.
+     * @param boolean $update      Whether to automatically update the
+     *                             backend.
+     */
+    public function removeGuestPermission($permission, $update = true)
+    {
+        if (!isset($this->data['guest'])) {
+            return;
+        }
+
+        if ($this->get('type') == 'matrix') {
+            $this->data['guest'] &= ~$permission;
+        } else {
+            unset($this->data['guest']);
+        }
+
+        if ($update) {
+            $this->save();
+        }
+    }
+
+    /**
+     * Removes a permission that creators currently have on this object.
+     *
+     * @param integer $permission  The permission (DELETE, etc.) to remove.
+     * @param boolean $update      Whether to automatically update the
+     *                             backend.
+     */
+    public function removeCreatorPermission($permission, $update = true)
+    {
+        if (!isset($this->data['creator'])) {
+            return;
+        }
+
+        if ($this->get('type') == 'matrix') {
+            $this->data['creator'] &= ~$permission;
+        } else {
+            unset($this->data['creator']);
+        }
+
+        if ($update) {
+            $this->save();
+        }
+    }
+
+    /**
+     * Removes a default permission on this object.
+     *
+     * @param integer $permission  The permission (DELETE, etc.) to remove.
+     * @param boolean $update      Whether to automatically update the
+     *                             backend.
+     */
+    public function removeDefaultPermission($permission, $update = true)
+    {
+        if (!isset($this->data['default'])) {
+            return;
+        }
+
+        if ($this->get('type') == 'matrix') {
+            $this->data['default'] &= ~$permission;
+        } else {
+            unset($this->data['default']);
+        }
+
+        if ($update) {
+            $this->save();
+        }
+    }
+
+    /**
+     * Removes a permission that a group currently has on this object.
+     *
+     * @param integer $groupId     The id of the group to remove the
+     *                             permission from.
+     * @param integer $permission  The permission (DELETE, etc.) to remove.
+     * @param boolean $update      Whether to automatically update the
+     *                             backend.
+     */
+    public function removeGroupPermission($groupId, $permission,
+                                          $update = true)
+    {
+        if (empty($groupId) || !isset($this->data['groups'][$groupId])) {
+            return;
+        }
+
+        if ($this->get('type') == 'matrix') {
+            $this->data['groups'][$groupId] &= ~$permission;
+            if (empty($this->data['groups'][$groupId])) {
+                unset($this->data['groups'][$groupId]);
+            }
+        } else {
+            unset($this->data['groups'][$groupId]);
+        }
+
+        if ($update) {
+            $this->save();
+        }
+    }
+
+    /**
+     * Returns an array of all user permissions on this object.
+     *
+     * @param integer $perm  List only users with this permission level.
+     *                       Defaults to all users.
+     *
+     * @return array  All user permissions for this object, indexed by user.
+     */
+    public function getUserPermissions($perm = null)
+    {
+        if (!isset($this->data['users']) || !is_array($this->data['users'])) {
+            return array();
+        } elseif (!$perm) {
+            return $this->data['users'];
+        }
+
+        $users = array();
+        foreach ($this->data['users'] as $user => $uperm) {
+            if ($uperm & $perm) {
+                $users[$user] = $uperm;
+            }
+        }
+
+        return $users;
+    }
+
+    /**
+     * Returns the guest permissions on this object.
+     *
+     * @return integer  The guest permissions on this object.
+     */
+    public function getGuestPermissions()
+    {
+        return empty($this->data['guest'])
+            ? null
+            : $this->data['guest'];
+    }
+
+    /**
+     * Returns the creator permissions on this object.
+     *
+     * @return integer  The creator permissions on this object.
+     */
+    public function getCreatorPermissions()
+    {
+        return empty($this->data['creator'])
+            ? null
+            : $this->data['creator'];
+    }
+
+    /**
+     * Returns the default permissions on this object.
+     *
+     * @return integer  The default permissions on this object.
+     */
+    public function getDefaultPermissions()
+    {
+        return empty($this->data['default'])
+            ? null
+            : $this->data['default'];
+    }
+
+    /**
+     * Returns an array of all group permissions on this object.
+     *
+     * @param integer $perm  List only users with this permission level.
+     *                       Defaults to all users.
+     *
+     * @return array  All group permissions for this object, indexed by group.
+     */
+    public function getGroupPermissions($perm = null)
+    {
+        if (!isset($this->data['groups']) ||
+            !is_array($this->data['groups'])) {
+            return array();
+        } elseif (!$perm) {
+            return $this->data['groups'];
+        }
+
+        $groups = array();
+        foreach ($this->data['groups'] as $group => $gperm) {
+            if ($gperm & $perm) {
+                $groups[$group] = $gperm;
+            }
+        }
+
+        return $groups;
+    }
+
+    /**
+     * Saves any changes to this object to the backend permanently. New
+     * objects are added instead.
+     *
+     * @throws Horde_Perms_Exception
+     */
+    public function save()
+    {
+        $name = $this->getName();
+        if (empty($name)) {
+            throw new Horde_Perms_Exception('Permission names must be non-empty');
+        }
+
+        parent::save();
+
+        $cache = Horde_Cache::singleton($GLOBALS['conf']['cache']['driver'], Horde::getDriverConfig('cache', $GLOBALS['conf']['cache']['driver']));
+        $cache->expire('perm_' . $name);
+        $cache->expire('perm_exists_' . $name);
+    }
+
+}
diff --git a/framework/Perms/lib/Horde/Perms/Permission/SqlObject.php b/framework/Perms/lib/Horde/Perms/Permission/SqlObject.php
new file mode 100644 (file)
index 0000000..0a50456
--- /dev/null
@@ -0,0 +1,85 @@
+<?php
+/**
+ * Extension of the Horde_Permission class for storing permission
+ * information in the SQL driver.
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author   Duck <duck@obala.net>
+ * @category Horde
+ * @package  Horde_Perms
+ */
+class Horde_Perms_Permission_SqlObject extends Horde_Permission
+{
+    /**
+     * The string permission id.
+     *
+     * @var string
+     */
+    protected $_id;
+
+    /**
+     * Database handle for saving changes.
+     *
+     * @var DB
+     */
+    protected $_write_db;
+
+    /**
+     * Associates a DB object with this share.
+     *
+     * @param DB $write_db  The DB object.
+     */
+    public function setSqlOb($write_db)
+    {
+        $this->_write_db = $write_db;
+    }
+
+    /**
+     * Get permission ID.
+     *
+     * @return TODO
+     */
+    public function getId()
+    {
+        return $this->_id;
+    }
+
+    /**
+     * Set permission id.
+     *
+     * @param string $id  Permission ID.
+     */
+    public function setId($id)
+    {
+        $this->_id = $id;
+    }
+
+    /**
+     * Saves any changes to this object to the backend permanently. New
+     * objects are added instead.
+     *
+     * @throws Horde_Perms_Exception
+     */
+    public function save()
+    {
+        $name = $this->getName();
+        if (empty($name)) {
+            throw new Horde_Perms_Exception('Permission names must be non-empty');
+        }
+        $query = 'UPDATE horde_perms SET perm_data = ? WHERE perm_id = ?';
+        $params = array(serialize($this->data), $this->getId());
+        $result = $this->_write_db->query($query, $params);
+        if ($result instanceof PEAR_Error) {
+            throw new Horde_Perms_Exception($result);
+        }
+
+        $cache = Horde_Cache::singleton($GLOBALS['conf']['cache']['driver'], Horde::getDriverConfig('cache', $GLOBALS['conf']['cache']['driver']));
+        $cache->expire('perm_sql_' . $name);
+        $cache->expire('perm_sql_exists_' . $name);
+    }
+
+}
diff --git a/framework/Perms/lib/Horde/Perms/Sql.php b/framework/Perms/lib/Horde/Perms/Sql.php
new file mode 100644 (file)
index 0000000..f04e251
--- /dev/null
@@ -0,0 +1,455 @@
+<?php
+/**
+ * The Horde_Perms_Sql:: class provides a SQL driver for the Horde
+ * permissions system.
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author   Duck <duck@obala.net>
+ * @category Horde
+ * @package  Horde_Perms
+ */
+class Horde_Perms_Sql extends Horde_Perms
+{
+    /**
+     * Boolean indicating whether or not we're connected to the SQL server.
+     *
+     * @var boolean
+     */
+    protected $_connected = false;
+
+    /**
+     * Handle for the current database connection.
+     *
+     * @var DB
+     */
+    protected $_db;
+
+    /**
+     * Handle for the current database connection, used for writing. Defaults
+     * to the same handle as $db if a separate write database is not required.
+     *
+     * @var DB
+     */
+    protected $_write_db;
+
+    /**
+     * Pointer to a Horde_Cache instance
+     *
+     * @var Horde_Cache
+     */
+    protected $_cache;
+
+    /**
+     * Cache of previously retrieved permissions.
+     *
+     * @var array
+     */
+    protected $_permsCache = array();
+
+    /**
+     * Constructor.
+     */
+    public function __construct()
+    {
+        $this->_cache = Horde_Cache::singleton($GLOBALS['conf']['cache']['driver'], Horde::getDriverConfig('cache', $GLOBALS['conf']['cache']['driver']));
+    }
+
+    /**
+     * Returns a new permissions object.
+     *
+     * @param string $name  The permission's name.
+     *
+     * @return Horde_Perms_Permission_SqlObject  A new permissions object.
+     */
+    public function newPermission($name)
+    {
+        $type = 'matrix';
+        $params = null;
+
+        if ($pos = strpos($name, ':')) {
+            try {
+                $info = $this->getApplicationPermissions(substr($name, 0, $pos));
+                if (isset($info['type']) && isset($info['type'][$name])) {
+                    $type = $info['type'][$name];
+                }
+
+                if (isset($info['params']) && isset($info['params'][$name])) {
+                    $params = $info['params'][$name];
+                }
+            } catch (Horde_Perms_Exception $e) {}
+        }
+
+        return new Horde_Perms_Permission_SqlObject($name, $type, $params);
+    }
+
+    /**
+     * Returns an object corresponding to the named permission, with the
+     * users and other data retrieved appropriately.
+     *
+     * @param string $name  The name of the permission to retrieve.
+     *
+     * @return Horde_Perms_Permission_SqlObject  TODO
+     * @throw Horde_Perms_Exception
+     */
+    public function getPermission($name)
+    {
+        if (isset($this->_permsCache[$name])) {
+            return $this->_permsCache[$name];
+        }
+
+        $this->_connect();
+
+        $perm = $this->_cache->get('perm_sql' . $name, $GLOBALS['conf']['cache']['default_lifetime']);
+        if (empty($perm)) {
+            $query = 'SELECT perm_id, perm_data FROM horde_perms WHERE perm_name = ?';
+            $result = $this->_db->getRow($query, array($name), DB_FETCHMODE_ASSOC);
+
+            if ($result instanceof PEAR_Error) {
+                throw new Horde_Perms_Exception($result);
+            } elseif (empty($result)) {
+                throw new Horde_Perms_Exception('Does not exist');
+            }
+
+            $object = new Horde_Perms_Permission_SqlObject($name);
+            $object->setId($result['perm_id']);
+            $object->setData(unserialize($result['perm_data']));
+
+            $this->_cache->set('perm_sql' . $name, serialize($object));
+
+            $this->_permsCache[$name] = $object;
+        } else {
+            $this->_permsCache[$name] = unserialize($perm);
+        }
+
+        $this->_permsCache[$name]->setSQLOb($this->_write_db);
+
+        return $this->_permsCache[$name];
+    }
+
+    /**
+     * Returns a permission object corresponding to the given unique ID,
+     * with the users and other data retrieved appropriately.
+     *
+     * @param integer $id  The unique ID of the permission to retrieve.
+     *
+     * @return Horde_Perms_Permission_SqlObject  TODO
+     * @throws Horde_Perms_Exception
+     */
+    public function getPermissionById($id)
+    {
+        $this->_connect();
+
+        if ($id == Horde_Perms::ROOT || empty($id)) {
+            $object = $this->newPermission(Horde_Perms::ROOT);
+        } else {
+            $query = 'SELECT perm_name, perm_data FROM horde_perms WHERE perm_id = ?';
+            $result = $this->_db->getRow($query, array($id), DB_FETCHMODE_ASSOC);
+
+            if ($result instanceof PEAR_Error) {
+                throw new Horde_Perms_Exception($result);
+            } elseif (empty($result)) {
+                throw new Horde_Perms_Exception('Does not exist');
+            }
+
+            $object = new Horde_Perms_Permission_SqlObject($result['perm_name']);
+            $object->setId($id);
+            $object->setData(unserialize($result['perm_data']));
+            $object->setSQLOb($this->_write_db);
+        }
+
+        return $object;
+    }
+
+    /**
+     * Adds a permission to the permissions system. The permission must first
+     * be created with newPermission(), and have any initial users added to
+     * it, before this function is called.
+     *
+     * @param Horde_Perms_Permission_SqlObject $perm  The perm object.
+     *
+     * @return TODO
+     * @throws Horde_Perms_Exception
+     */
+    public function addPermission($perm)
+    {
+        if (!($perm instanceof Horde_Perms_Permission_SqlObject)) {
+            throw new Horde_Perms_Exception('Permissions must be a Horde_Perms_Permission_SqlObject object.');
+        }
+
+        $name = $perm->getName();
+        if (empty($name)) {
+            throw new Horde_Perms_Exception('Permission name must be non-empty.');
+        }
+
+        $this->_cache->expire('perm_sql' . $name);
+        $this->_cache->expire('perm_sql_exists_' . $name);
+
+        $this->_connect();
+        $id = $this->_write_db->nextId('horde_perms');
+
+        // remove root from the name
+        if (substr($name, 0, 3) == (Horde_Perms::ROOT . ':')) {
+            $name = substr($name, 3);
+        }
+
+        // build parents
+        $parents = '';
+        if (($pos = strrpos($name, ':')) !== false) {
+            $parent_name = substr($name, 0, $pos);
+            $query = 'SELECT perm_id, perm_parents FROM horde_perms WHERE perm_name = ?';
+            $result = $this->_db->getRow($query, array($parent_name), DB_FETCHMODE_ASSOC);
+            if (!empty($result)) {
+                $parents = $result['perm_parents'] . ':' . $result['perm_id'];
+            }
+        }
+
+        $query = 'INSERT INTO horde_perms (perm_id, perm_name, perm_parents) VALUES (?, ?, ?)';
+        $perm->setId($id);
+
+        $result = $this->_write_db->query($query, array($id, $name, $parents));
+        if ($result instanceof PEAR_Error) {
+            throw new Horde_Perms_Exception($result);
+        }
+
+        $perm->setSQLOb($this->_write_db);
+        $perm->save();
+
+        return $id;
+    }
+
+    /**
+     * Removes a permission from the permissions system permanently.
+     *
+     * @param Horde_Perms_Permission_SqlObject $perm  The permission to
+     *                                                remove.
+     * @param boolean $force                          Force to remove ever
+     *                                                child.
+     *
+     * @return TODO
+     * @throws Horde_Perms_Exception
+     */
+    public function removePermission($perm, $force = false)
+    {
+        if (!($perm instanceof Horde_Perms_Permissions_SqlObject)) {
+            throw new Horde_Perms_Exception('Permissions must be Horde_Perms_Permission_SqlObject objects.');
+        }
+
+        $name = $perm->getName();
+        $this->_cache->expire('perm_sql' . $name);
+        $this->_cache->expire('perm_sql_exists_' . $name);
+
+        $this->_connect();
+        $query = 'DELETE FROM horde_perms WHERE perm_name = ?';
+        $result = $this->_write_db->query($query, array($name));
+        if ($result instanceof PEAR_Error) {
+            throw new Horde_Perms_Exception($result);
+        } elseif ($force) {
+            return $result;
+        }
+
+        $query = 'DELETE FROM horde_perms WHERE perm_name LIKE ?';
+        return $this->_write_db->query($query, array($name . ':%'));
+    }
+
+    /**
+     * Returns the unique identifier of this permission.
+     *
+     * @param Horde_Perms_Permission_SqlObject $perm  The permission object to
+     *                                                 get the ID of.
+     *
+     * @return integer  The unique id.
+     */
+    public function getPermissionId($permission)
+    {
+        if ($permission->getName() == Horde_Perms::ROOT) {
+            return Horde_Perms::ROOT;
+        }
+
+        $this->_connect();
+        $query = 'SELECT perm_id FROM horde_perms WHERE perm_name = ?';
+        return $this->_db->getOne($query, array($permission->getName()));
+    }
+
+    /**
+     * Checks if a permission exists in the system.
+     *
+     * @param string $permission  The permission to check.
+     *
+     * @return boolean  True if the permission exists.
+     * @throws Horde_Perms_Exception
+     */
+    public function exists($permission)
+    {
+        $key = 'perm_sql_exists_' . $permission;
+        $exists = $this->_cache->get($key, $GLOBALS['conf']['cache']['default_lifetime']);
+        if ($exists === false) {
+            $this->_connect();
+            $query = 'SELECT COUNT(*) FROM horde_perms WHERE perm_name = ?';
+            $exists = $this->_db->getOne($query, array($permission));
+            if ($exists instanceof PEAR_Error) {
+                throw new Horde_Perms_Exception($exists);
+            }
+
+            $this->_cache->set($key, (string)$exists);
+        }
+
+        return (bool)$exists;
+    }
+
+    /**
+     * Returns a child's direct parent ID.
+     *
+     * @param mixed $child  The object name for which to look up the parent's
+     *                      ID.
+     *
+     * @return integer  The unique ID of the parent.
+     * @throws Horde_Perms_Exception
+     */
+    public function getParent($child)
+    {
+        $this->_connect();
+        $query = 'SELECT perm_parents FROM horde_perms WHERE perm_name = ?';
+        $parents = $this->_db->getOne($query, array($child));
+
+        if ($parents instanceof PEAR_Error) {
+            throw new Horde_Perms_Exception($parents);
+        }
+
+        if (empty($parents)) {
+            return Horde_Perms::ROOT;
+        }
+
+        $parents = explode(':', $parents);
+        return array_pop($parents);
+    }
+
+    /**
+     * Returns a list of parent permissions.
+     *
+     * @param string $child  The name of the child to retrieve parents for.
+     *
+     * @return array  A hash with all parents in a tree format.
+     * @throws Horde_Perms_Exception
+     */
+    public function getParents($child)
+    {
+        $this->_connect();
+        $query = 'SELECT perm_parents FROM horde_perms WHERE perm_name = ?';
+        $result = $this->_db->getOne($query, array($child));
+        if ($result instanceof PEAR_Error) {
+            throw new Horde_Perms_Exception($result);
+        } elseif (empty($result)) {
+            throw new Horde_Perms_Exception('Does not exist');
+        }
+
+        return $this->_getParents($result);
+    }
+
+    /**
+     * TODO
+     */
+    protected function _getParents($parents)
+    {
+        if (empty($parents)) {
+            return array(Horde_Perms::ROOT => true);
+        }
+
+        $pname = $parents;
+        $parents = substr($parents, 0, strrpos($parents, ':'));
+
+        return array($pname => $this->_getParents($parents));
+    }
+
+    /**
+     * Returns all permissions of the system in a tree format.
+     *
+     * @return array  A hash with all permissions in a tree format.
+     * @throws Horde_Perms_Exception
+     */
+    public function getTree()
+    {
+        $this->_connect();
+        $query = 'SELECT perm_id, perm_name FROM horde_perms ORDER BY perm_name ASC';
+        $tree = $this->_db->getAssoc($query);
+        if ($tree instanceof PEAR_Error) {
+            throw new Horde_Perms_Exception($tree);
+        }
+
+        $tree[Horde_Perms::ROOT] = Horde_Perms::ROOT;
+        return $tree;
+    }
+
+    /**
+     * Attempts to open a connection to the sql server.
+     *
+     * @throws Horde_Perms_Exception
+     */
+    protected function _connect()
+    {
+        if ($this->_connected) {
+            return;
+        }
+
+        $_params = $GLOBALS['conf']['sql'];
+        if (!isset($_params['database'])) {
+            $_params['database'] = '';
+        }
+        if (!isset($_params['username'])) {
+            $_params['username'] = '';
+        }
+        if (!isset($_params['hostspec'])) {
+            $_params['hostspec'] = '';
+        }
+
+        /* Connect to the sql server using the supplied parameters. */
+        $this->_write_db = DB::connect($_params,
+                                       array('persistent' => !empty($_params['persistent']),
+                                             'ssl' => !empty($this->_params['ssl'])));
+        if ($this->_write_db instanceof PEAR_Error) {
+            throw new Horde_Perms_Exception($this->_write_db);
+        }
+
+        /* Set DB portability options. */
+        switch ($this->_write_db->phptype) {
+        case 'mssql':
+            $this->_write_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM);
+            break;
+
+        default:
+            $this->_write_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS);
+            break;
+        }
+
+        /* Check if we need to set up the read DB connection seperately. */
+        if (!empty($_params['splitread'])) {
+            $params = array_merge($_params, $_params['read']);
+            $this->_db = DB::connect($params,
+                                     array('persistent' => !empty($params['persistent']),
+                                           'ssl' => !empty($params['ssl'])));
+            if ($this->_db instanceof PEAR_Error) {
+                throw new Horde_Perms_Exception($this->_db);
+            }
+
+            /* Set DB portability options. */
+            switch ($this->_db->phptype) {
+            case 'mssql':
+                $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM);
+                break;
+
+            default:
+                $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS);
+                break;
+            }
+        } else {
+            /* Default to the same DB handle for the writer too. */
+            $this->_db = $this->_write_db;
+        }
+
+        $this->_connected = true;
+    }
+
+}
diff --git a/framework/Perms/lib/Horde/Perms/Ui.php b/framework/Perms/lib/Horde/Perms/Ui.php
new file mode 100644 (file)
index 0000000..35cb123
--- /dev/null
@@ -0,0 +1,538 @@
+<?php
+/**
+ * The Horde_Perms_Ui:: class provides UI methods for the Horde permissions
+ * system.
+ *
+ * Copyright 2001-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author   Chuck Hagenbuch <chuck@horde.org>
+ * @category Horde
+ * @package  Horde_Perms
+ */
+class Horde_Perms_Ui
+{
+    /**
+     * The Horde_Perms object we're displaying UI stuff for.
+     *
+     * @var Horde_Perms
+     */
+    protected $_perms;
+
+    /**
+     * The Horde_Form object that will be used for displaying the edit form.
+     *
+     * @var Horde_Form
+     */
+    protected $_form = null;
+
+    /**
+     * The Horde_Variables object used in Horde_Form.
+     *
+     * @var Horde_Variables
+     */
+    protected $_vars = null;
+
+    /**
+     * The permission type.
+     *
+     * @var string
+     */
+    protected $_type = 'matrix';
+
+    /**
+     * Constructor.
+     *
+     * @param Horde_Perms $perms  The object to display UI stuff for.
+     */
+    public function __construct($perms)
+    {
+        $this->_perms = $perms;
+    }
+
+    /**
+     * Return a Horde_Tree representation of the permissions tree.
+     *
+     * @return string  The html showing the permissions as a Horde_Tree.
+     * @throws Horde_Perms_Exception
+     */
+    public function renderTree($current = Horde_Perms::ROOT)
+    {
+        global $registry;
+
+        /* Get the perms tree. */
+        $nodes = $this->_perms->getTree();
+
+        $icondir = array('icondir' => $GLOBALS['registry']->getImageDir());
+        $perms_node = $icondir + array('icon' => 'perms.png');
+        $add = Horde::applicationUrl('admin/perms/addchild.php');
+        $add_img = Horde::img('add_perm.png', _("Add Permission"));
+        $edit = Horde::applicationUrl('admin/perms/edit.php');
+        $delete = Horde::applicationUrl('admin/perms/delete.php');
+        $edit_img = Horde::img('edit.png', _("Edit Permission"));
+        $delete_img = Horde::img('delete.png', _("Delete Permission"));
+        $blank_img = Horde::img('blank.gif', '', array('width' => 16, 'height' => 16));
+
+        /* Set up the tree. */
+        $tree = Horde_Tree::singleton('perms_ui', 'javascript');
+        $tree->setOption(array('alternate' => true, 'hideHeaders' => true));
+        $tree->setHeader(array(array('width' => '50%')));
+
+        foreach ($nodes as $perm_id => $node) {
+            $node_class = ($current == $perm_id)
+                ? array('class' => 'selected')
+                : array();
+            if ($perm_id == Horde_Perms::ROOT) {
+                $add_link = Horde::link(
+                    Horde_Util::addParameter($add, 'perm_id', $perm_id),
+                    _("Add New Permission"))
+                    . $add_img . '</a>';
+                $base_node_params = $icondir +
+                    array('icon' => 'administration.png');
+
+                $tree->addNode($perm_id, null, _("All Permissions"), 0, true,
+                               $base_node_params + $node_class,
+                               array($add_link));
+            } else {
+                $parent_id = $this->_perms->getParent($node);
+
+                $perms_extra = array();
+                $parents = explode(':', $node);
+
+                if (!in_array($parents[0], $GLOBALS['registry']->listApps())) {
+                    // This backend has permissions for an application that is
+                    // not installed.  Perhaps the application has been removed
+                    // or the backend is shared with other Horde installations.
+                    // Skip this app and do not include it in the tree.
+                    continue;
+                }
+
+                try {
+                    $app_perms = $this->_perms->getApplicationPermissions($parents[0]);
+                } catch (Horde_Perms_Exception $e) {
+                    $GLOBALS['notification']->push($e);
+                    continue;
+                }
+
+                if (isset($app_perms['tree']) &&
+                    is_array(Horde_Array::getElement($app_perms['tree'], $parents))) {
+                    $add_link = Horde::link(
+                        Horde_Util::addParameter($add, 'perm_id', $perm_id),
+                        _("Add Child Permission"))
+                        . $add_img . '</a>';
+                    $perms_extra[] = $add_link;
+                } else {
+                    $perms_extra[] = $blank_img;
+                }
+
+                $edit_link = Horde::link(
+                    Horde_Util::addParameter($edit, 'perm_id', $perm_id),
+                    _("Edit Permission"))
+                    . $edit_img . '</a>';
+                $perms_extra[] = $edit_link;
+                $delete_link = Horde::link(
+                    Horde_Util::addParameter($delete, 'perm_id', $perm_id),
+                    _("Delete Permission"))
+                    . $delete_img . '</a>';
+                $perms_extra[] = $delete_link;
+                $name = $this->_perms->getTitle($node);
+
+                $expanded = isset($nodes[$current]) &&
+                    strpos($nodes[$current], $node) === 0 &&
+                    $nodes[$current] != $node;
+                $tree->addNode($perm_id, $parent_id, $name,
+                               substr_count($node, ':') + 1, $expanded,
+                               $perms_node + $node_class, $perms_extra);
+            }
+        }
+
+        $tree->sort('label');
+
+        return $tree->renderTree();
+    }
+
+    /**
+     * Set an existing form object to use for the edit form.
+     *
+     * @param Horde_Form $form  An existing Horde_Form object to use.
+     */
+    public function setForm(&$form)
+    {
+        $this->_form = $form;
+    }
+
+    /**
+     * Set an existing vars object to use for the edit form.
+     *
+     * @param Horde_Variables $vars  An existing Horde_Variables object to
+     *                               use.
+     */
+    public function setVars($vars)
+    {
+        $this->_vars = $vars;
+    }
+
+    /**
+     * Create a form to add a permission.
+     *
+     * @param Horde_Perms_Permission $permission  Permission
+     * @param string $force_choice                If the permission to be
+     *                                            added can be one of many,
+     *                                            setting this will force the
+     *                                            choice to one particular.
+     */
+    public function setupAddForm($permission, $force_choice = null)
+    {
+        /* Initialise form if required. */
+        $this->_formInit();
+
+        $this->_form->setTitle(sprintf(_("Add a child permission to \"%s\""), $this->_perms->getTitle($permission->getName())));
+        $this->_form->setButtons(_("Add"));
+        $this->_vars->set('perm_id', $this->_perms->getPermissionId($permission));
+        $this->_form->addHidden('', 'perm_id', 'text', false);
+
+        /* Set up the actual child adding field. */
+        $child_perms = $this->_perms->getAvailable($permission->getName());
+        if ($child_perms === false) {
+            /* False, so no childs are to be added below this level. */
+            $this->_form->addVariable(_("Permission"), 'child', 'invalid', true, false, null, array(_("No children can be added to this permission.")));
+        } elseif (is_array($child_perms)) {
+            if (!empty($force_choice)) {
+                /* Choice array available, but choice being forced. */
+                $this->_vars->set('child', $force_choice);
+                $this->_form->addVariable(_("Permissions"), 'child', 'enum', true, true, null, array($child_perms));
+            } else {
+                /* Choice array available, so set up enum field. */
+                $this->_form->addVariable(_("Permissions"), 'child', 'enum', true, false, null, array($child_perms));
+            }
+        }
+    }
+
+    /**
+     * Function to validate any add form input.
+     *
+     * @param TODO $info  TODO
+     *
+     * @return mixed  Either false if the form does not validate correctly or
+     *                an array with all the form values.
+     */
+    public function validateAddForm($info)
+    {
+        if (!$this->_form->validate($this->_vars)) {
+            return false;
+        }
+
+        $this->_form->getInfo($this->_vars, $info);
+        return true;
+    }
+
+    /**
+     * Create a permission editing form.
+     *
+     * @param Horde_Perms_Permission $permission  TODO
+     */
+    public function setupEditForm($permission)
+    {
+        global $registry;
+
+        /* Initialise form if required. */
+        $this->_formInit();
+
+        $this->_form->setButtons(_("Update"), true);
+        $perm_id = $this->_perms->getPermissionId($permission);
+        $this->_form->addHidden('', 'perm_id', 'text', false);
+
+        /* Get permission configuration. */
+        $this->_type = $permission->get('type');
+        $params = $permission->get('params');
+
+        /* Default permissions. */
+        $perm_val = $permission->getDefaultPermissions();
+        $this->_form->setSection('default', _("All Authenticated Users"), Horde::img('perms.png', '', '', $registry->getImageDir('horde')), false);
+
+        /* We MUST use 'deflt' for the variable name because 'default' is a
+         * reserved word in JavaScript. */
+        if ($this->_type == 'matrix') {
+            /* Set up the columns for the permissions matrix. */
+            $cols = Horde_Perms::getPermsArray();
+
+            /* Define a single matrix row for default perms. */
+            $matrix = array(Horde_Perms::integerToArray($perm_val));
+            $this->_form->addVariable('', 'deflt', 'matrix', false, false, null, array($cols, array(0 => ''), $matrix));
+        } else {
+            $var = $this->_form->addVariable('', 'deflt', $this->_type, false, false, null, $params);
+            $var->setDefault($perm_val);
+        }
+
+        /* Guest permissions. */
+        $perm_val = $permission->getGuestPermissions();
+        $this->_form->setSection('guest', _("Guest Permissions"), '', false);
+
+        if ($this->_type == 'matrix') {
+            /* Define a single matrix row for guest perms. */
+            $matrix = array(Horde_Perms::integerToArray($perm_val));
+            $this->_form->addVariable('', 'guest', 'matrix', false, false, null, array($cols, array(0 => ''), $matrix));
+        } else {
+            $var = $this->_form->addVariable('', 'guest', $this->_type, false, false, null, $params);
+            $var->setDefault($perm_val);
+        }
+
+        /* Object creator permissions. */
+        $perm_val = $permission->getCreatorPermissions();
+        $this->_form->setSection('creator', _("Creator Permissions"), Horde::img('user.png', '', '', $registry->getImageDir('horde')), false);
+
+        if ($this->_type == 'matrix') {
+            /* Define a single matrix row for creator perms. */
+            $matrix = array(Horde_Perms::integerToArray($perm_val));
+            $this->_form->addVariable('', 'creator', 'matrix', false, false, null, array($cols, array(0 => ''), $matrix));
+        } else {
+            $var = $this->_form->addVariable('', 'creator', $this->_type, false, false, null, $params);
+            $var->setDefault($perm_val);
+        }
+
+        /* Users permissions. */
+        $perm_val = $permission->getUserPermissions();
+        $this->_form->setSection('users', _("Individual Users"), Horde::img('user.png', '', '', $registry->getImageDir('horde')), false);
+        $auth = Horde_Auth::singleton($GLOBALS['conf']['auth']['driver']);
+        if ($auth->hasCapability('list')) {
+            /* The auth driver has list capabilities so set up an array which
+             * the matrix field type will recognise to set up an enum box for
+             * adding new users to the permissions matrix. */
+            $new_users = array();
+
+            try {
+                $user_list = $auth->listUsers();
+                sort($user_list);
+                foreach ($user_list as $user) {
+                    if (!isset($perm_val[$user])) {
+                        $new_users[$user] = $user;
+                    }
+                }
+            } catch (Horde_Auth_Exception $e) {
+                $new_users = true;
+            }
+        } else {
+            /* No list capabilities, setting to true so that the matrix field
+             * type will offer a text input box for adding new users. */
+            $new_users = true;
+        }
+
+        if ($this->_type == 'matrix') {
+            /* Set up the matrix array, breaking up each permission integer
+             * into an array.  The keys of this array will be the row
+             * headers. */
+            $rows = array();
+            $matrix = array();
+            foreach ($perm_val as $u_id => $u_perms) {
+                $rows[$u_id] = $u_id;
+                $matrix[$u_id] = Horde_Perms::integerToArray($u_perms);
+            }
+            $this->_form->addVariable('', 'u', 'matrix', false, false, null, array($cols, $rows, $matrix, $new_users));
+        } else {
+            if ($new_users) {
+                if (is_array($new_users)) {
+                    $u_n = Horde_Util::getFormData('u_n');
+                    $u_n = empty($u_n['u']) ? null : $u_n['u'];
+                    $user_html = '<select name="u_n[u]"><option value="">' . _("-- select --") . '</option>';
+                    foreach ($new_users as $new_user) {
+                        $user_html .= '<option value="' . $new_user . '"';
+                        $user_html .= $u_n == $new_user ? ' selected="selected"' : '';
+                        $user_html .= '>' . htmlspecialchars($new_user) . '</option>';
+                    }
+                    $user_html .= '</select>';
+                } else {
+                    $user_html = '<input type="text" name="u_n[u]" />';
+                }
+                $this->_form->addVariable($user_html, 'u_n[v]', $this->_type, false, false, null, $params);
+            }
+            foreach ($perm_val as $u_id => $u_perms) {
+                $var = $this->_form->addVariable($u_id, 'u_v[' . $u_id . ']', $this->_type, false, false, null, $params);
+                $var->setDefault($u_perms);
+            }
+        }
+
+        /* Groups permissions. */
+        $perm_val = $permission->getGroupPermissions();
+        $this->_form->setSection('groups', _("Groups"), Horde::img('group.png', '', '', $registry->getImageDir('horde')), false);
+        require_once 'Horde/Group.php';
+        $groups = Group::singleton();
+        $group_list = $groups->listGroups();
+        if ($group_list instanceof PEAR_Error) {
+            $GLOBALS['notification']->push($group_list);
+            $group_list = array();
+        }
+
+        if (!empty($group_list)) {
+            /* There is an available list of groups so set up an array which
+             * the matrix field type will recognise to set up an enum box for
+             * adding new groups to the permissions matrix. */
+            $new_groups = array();
+            foreach ($group_list as $groupId => $group) {
+                if (!isset($perm_val[$groupId])) {
+                    $new_groups[$groupId] = $group;
+                }
+            }
+        } else {
+            /* Do not offer a text box to add new groups. */
+            $new_groups = false;
+        }
+
+        if ($this->_type == 'matrix') {
+            /* Set up the matrix array, break up each permission integer into
+             * an array. The keys of this array will be the row headers. */
+            $rows = array();
+            $matrix = array();
+            foreach ($perm_val as $g_id => $g_perms) {
+                $rows[$g_id] = isset($group_list[$g_id]) ? $group_list[$g_id] : $g_id;
+                $matrix[$g_id] = Horde_Perms::integerToArray($g_perms);
+            }
+            $this->_form->addVariable('', 'g', 'matrix', false, false, null, array($cols, $rows, $matrix, $new_groups));
+        } else {
+            if ($new_groups) {
+                if (is_array($new_groups)) {
+                    $g_n = Horde_Util::getFormData('g_n');
+                    $g_n = empty($g_n['g']) ? null : $g_n['g'];
+                    $group_html = '<select name="g_n[g]"><option value="">' . _("-- select --") . '</option>';
+                    foreach ($new_groups as $groupId => $group) {
+                        $group_html .= '<option value="' . $groupId . '"';
+                        $group_html .= $g_n == $groupId ? ' selected="selected"' : '';
+                        $group_html .= '>' . htmlspecialchars($group) . '</option>';
+                    }
+                    $group_html .= '</select>';
+                } else {
+                    $group_html = '<input type="text" name="g_n[g]" />';
+                }
+                $this->_form->addVariable($group_html, 'g_n[v]', $this->_type, false, false, null, $params);
+            }
+            foreach ($perm_val as $g_id => $g_perms) {
+                $var = &$this->_form->addVariable(isset($group_list[$g_id]) ? $group_list[$g_id] : $g_id, 'g_v[' . $g_id . ']', $this->_type, false, false, null, $params);
+                $var->setDefault($g_perms);
+            }
+        }
+
+        /* Set form title. */
+        $this->_form->setTitle(sprintf(_("Edit permissions for \"%s\""), $this->_perms->getTitle($permission->getName())));
+    }
+
+    /**
+     * Function to validate any edit form input.
+     *
+     * @return mixed  Either false if the form does not validate correctly or
+     *                an array with all the form values.
+     */
+    public function validateEditForm(&$info)
+    {
+        if (!$this->_form->validate($this->_vars)) {
+            return false;
+        }
+
+        $this->_form->getInfo($this->_vars, $info);
+
+        if ($this->_type == 'matrix') {
+            /* Collapse the array for default/guest/creator. */
+            $info['deflt'] = isset($info['deflt'][0])
+                ? $info['deflt'][0]
+                : null;
+            $info['guest'] = isset($info['guest'][0])
+                ? $info['guest'][0]
+                : null;
+            $info['creator'] = isset($info['creator'][0])
+                ? $info['creator'][0]
+                : null;
+        } else {
+            $u_n = $this->_vars->get('u_n');
+            $info['u'] = array();
+            if (!empty($u_n['u'])) {
+                $info['u'][$u_n['u']] = $info['u_n']['v'];
+            }
+            unset($info['u_n']);
+            if (isset($info['u_v'])) {
+                $info['u'] += $info['u_v'];
+                unset($info['u_v']);
+            }
+            $g_n = $this->_vars->get('g_n');
+            $info['g'] = array();
+            if (!empty($g_n['g'])) {
+                $info['g'][$g_n['g']] = $info['g_n']['v'];
+            }
+            unset($info['g_n']);
+            if (isset($info['g_v'])) {
+                $info['g'] += $info['g_v'];
+                unset($info['g_v']);
+            }
+        }
+        $info['default'] = $info['deflt'];
+        unset($info['deflt']);
+
+        return true;
+    }
+
+    /**
+     * Create a permission deleting form.
+     *
+     * @param Horde_Perms_Permission $permission  A permissions object.
+     */
+    public function setupDeleteForm($permission)
+    {
+        /* Initialise form if required. */
+        $this->_formInit();
+
+        $this->_form->setTitle(sprintf(_("Delete permissions for \"%s\""), $this->_perms->getTitle($permission->getName())));
+        $this->_form->setButtons(array(_("Delete"), _("Do not delete")));
+        $this->_form->addHidden('', 'perm_id', 'text', false);
+        $this->_form->addVariable(sprintf(_("Delete permissions for \"%s\" and any sub-permissions?"), $this->_perms->getTitle($permission->getName())), 'prompt', 'description', false);
+    }
+
+    /**
+     * Function to validate any delete form input.
+     *
+     * @param TODO $info  TODO
+     *
+     * @return mixed  If the delete button confirmation has been pressed return
+     *                true, if any other submit button has been pressed return
+     *                false. If form did not validate return null.
+     */
+    public function validateDeleteForm($info)
+    {
+        $form_submit = $this->_vars->get('submitbutton');
+
+        if ($form_submit == _("Delete")) {
+            if ($this->_form->validate($this->_vars)) {
+                $this->_form->getInfo($this->_vars, $info);
+                return true;
+            }
+        } elseif (!empty($form_submit)) {
+            return false;
+        }
+
+        return null;
+    }
+
+    /**
+     * Renders the edit form.
+     */
+    public function renderForm($form_script = 'edit.php')
+    {
+        $renderer = new Horde_Form_Renderer();
+        $this->_form->renderActive($renderer, $this->_vars, $form_script, 'post');
+    }
+
+    /**
+     * Creates any form objects if they have not been initialised yet.
+     */
+    protected function _formInit()
+    {
+        if (is_null($this->_vars)) {
+            /* No existing vars set, get them now. */
+            $this->_vars = Horde_Variables::getDefaultVariables();
+        }
+
+        if (!($this->_form instanceof Horde_Form)) {
+            /* No existing valid form object set so set up a new one. */
+            $this->_form = new Horde_Form($this->_vars);
+        }
+    }
+
+}
diff --git a/framework/Perms/package.xml b/framework/Perms/package.xml
new file mode 100644 (file)
index 0000000..96bdf93
--- /dev/null
@@ -0,0 +1,149 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<package packagerversion="1.4.9" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
+http://pear.php.net/dtd/tasks-1.0.xsd
+http://pear.php.net/dtd/package-2.0
+http://pear.php.net/dtd/package-2.0.xsd">
+ <name>Perms</name>
+ <channel>pear.horde.org</channel>
+ <extends>Horde_Perms</extends>
+ <summary>Horde Permissions System</summary>
+ <description>The Perms package provides an interface to the Horde permissions system.</description>
+ <lead>
+  <name>Chuck Hagenbuch</name>
+  <user>chuck</user>
+  <email>chuck@horde.org</email>
+  <active>yes</active>
+ </lead>
+ <lead>
+  <name>Jan Schneider</name>
+  <user>jan</user>
+  <email>jan@horde.org</email>
+  <active>yes</active>
+ </lead>
+ <date>2009-11-20</date>
+ <version>
+  <release>0.2.0</release>
+  <api>0.2.0</api>
+ </version>
+ <stability>
+  <release>beta</release>
+  <api>beta</api>
+ </stability>
+ <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
+ <notes>* Initial Horde 4 package.
+ </notes>
+ <contents>
+  <dir name="/">
+   <dir name="lib">
+    <dir name="Horde">
+     <dir name="Perms">
+      <dir name="Permission">
+       <file name="DataTreeObject.php" role="php" />
+       <file name="SqlObject.php" role="php" />
+      </dir> <!-- /lib/Horde/Perms/Permission -->
+      <file name="Datatree.php" role="php" />
+      <file name="Exception.php" role="php" />
+      <file name="Permission.php" role="php" />
+      <file name="Sql.php" role="php" />
+      <file name="Ui.php" role="php" />
+     </dir> <!-- /lib/Horde/Perms -->
+     <file name="Perms.php" role="php" />
+    </dir> <!-- /lib/Horde -->
+   </dir> <!-- /lib -->
+  </dir> <!-- / -->
+ </contents>
+ <dependencies>
+  <required>
+   <php>
+    <min>5.2.0</min>
+   </php>
+   <pearinstaller>
+    <min>1.7.0</min>
+   </pearinstaller>
+   <package>
+    <name>Group</name>
+    <channel>pear.horde.org</channel>
+   </package>
+   <package>
+    <name>Util</name>
+    <channel>pear.horde.org</channel>
+   </package>
+   <extension>
+    <name>gettext</name>
+   </extension>
+  </required>
+  <optional>
+   <package>
+    <name>Horde_DataTree</name>
+    <channel>pear.horde.org</channel>
+   </package>
+   <package>
+    <name>Horde_Tree</name>
+    <channel>pear.horde.org</channel>
+   </package>
+  </optional>
+ </dependencies>
+ <phprelease>
+  <filelist>
+   <install name="lib/Horde/Perms/Datatree.php" as="Horde/Perms/Datatree.php" />
+   <install name="lib/Horde/Perms/Exception.php" as="Horde/Perms/Exception.php" />
+   <install name="lib/Horde/Perms/Permission.php" as="Horde/Perms/Permission.php" />
+   <install name="lib/Horde/Perms/Permission/DataTreeObject.php" as="Horde/Perms/Permission/DataTreeObject.php" />
+   <install name="lib/Horde/Perms/Permission/SqlObject.php" as="Horde/Perms/Permission/SqlObject.php" />
+   <install name="lib/Horde/Perms/Sql.php" as="Horde/Perms/Sql.php" />
+   <install name="lib/Horde/Perms/Ui.php" as="Horde/Perms/Ui.php" />
+   <install name="lib/Horde/Perms.php" as="Horde/Perms.php" />
+  </filelist>
+ </phprelease>
+ <changelog>
+  <release>
+   <version>
+    <release>0.1.0</release>
+    <api>0.1.0</api>
+   </version>
+   <stability>
+    <release>beta</release>
+    <api>beta</api>
+   </stability>
+   <date>2008-08-20</date>
+   <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
+   <notes>* Cache permissions with Horde_Cache (duck@obala.net).
+   * Fixed special permissions with more than one user or group (Bug #2058).
+   * Added a getParent() call instead of accessing the _datatree member directly. (duck@obala.net, Request #6150).
+   * Added beta SQL Permissions driver (duck@obala.net, Request #6150).
+   * Automatically expand the node of the currently open permission.
+   * Only show Add Permission icons if there are sub-permissions to add.
+   * Make sure Horde_Array is available.
+   * Handle and display errors if they occur in the Perms backend.
+   </notes>
+  </release>
+  <release>
+   <version>
+    <release>0.0.2</release>
+    <api>0.0.2</api>
+   </version>
+   <stability>
+    <release>alpha</release>
+    <api>alpha</api>
+   </stability>
+   <date>2006-05-08</date>
+   <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
+   <notes>Converted to package.xml 2.0 for pear.horde.org
+   </notes>
+  </release>
+  <release>
+   <version>
+    <release>0.0.1</release>
+    <api>0.0.1</api>
+   </version>
+   <stability>
+    <release>alpha</release>
+    <api>alpha</api>
+   </stability>
+   <date>2003-07-05</date>
+   <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
+   <notes>Initial release as a PEAR package
+   </notes>
+  </release>
+ </changelog>
+</package>
index 55c7fa9..9f1c4fa 100644 (file)
@@ -509,7 +509,7 @@ class Horde_Rpc_Webdav extends Horde_Rpc
             $list[] = array('path' => $path,
                             'props' => $this->_getProps($options['props'], $root));
 
-            $apps = $registry->listApps(null, false, PERMS_READ);
+            $apps = $registry->listApps(null, false, Horde_Perms::READ);
             if (is_a($apps, 'PEAR_Error')) {
                 Horde::logMessage($apps, __FILE__, __LINE__, PEAR_LOG_ERR);
                 return $apps;