Convert Group to H4 conventions
authorMichael M Slusarz <slusarz@curecanti.org>
Tue, 1 Jun 2010 08:46:27 +0000 (02:46 -0600)
committerMichael M Slusarz <slusarz@curecanti.org>
Wed, 2 Jun 2010 03:42:57 +0000 (21:42 -0600)
Still needs a bunch of work, but now it should at least autoload.
Also, switch to Horde_Db usage for the Sql driver.

60 files changed:
ansel/lib/Storage.php
ansel/perms.php
folks/config/hooks.php.dist
folks/perms.php
framework/Ajax/lib/Horde/Ajax/Application/Base.php
framework/Core/lib/Horde/Core/Factory/KolabStorage.php
framework/Core/lib/Horde/Core/Perms/Ui.php
framework/Group/Group.php [deleted file]
framework/Group/Group/contactlists.php [deleted file]
framework/Group/Group/hooks.php [deleted file]
framework/Group/Group/kolab.php [deleted file]
framework/Group/Group/ldap.php [deleted file]
framework/Group/Group/mock.php [deleted file]
framework/Group/Group/sql.php [deleted file]
framework/Group/lib/Horde/Group.php [new file with mode: 0644]
framework/Group/lib/Horde/Group/ContactListObject.php [new file with mode: 0644]
framework/Group/lib/Horde/Group/Contactlists.php [new file with mode: 0644]
framework/Group/lib/Horde/Group/DataTreeObject.php [new file with mode: 0644]
framework/Group/lib/Horde/Group/Exception.php [new file with mode: 0644]
framework/Group/lib/Horde/Group/Hooks.php [new file with mode: 0644]
framework/Group/lib/Horde/Group/Kolab.php [new file with mode: 0644]
framework/Group/lib/Horde/Group/KolabObject.php [new file with mode: 0644]
framework/Group/lib/Horde/Group/Ldap.php [new file with mode: 0644]
framework/Group/lib/Horde/Group/LdapObject.php [new file with mode: 0644]
framework/Group/lib/Horde/Group/Mock.php [new file with mode: 0644]
framework/Group/lib/Horde/Group/Sql.php [new file with mode: 0644]
framework/Group/lib/Horde/Group/SqlObject.php [new file with mode: 0644]
framework/Group/package.xml
framework/Perms/lib/Horde/Perms.php
framework/Share/lib/Horde/Share/Datatree.php
framework/Share/lib/Horde/Share/Sql.php
framework/Share/lib/Horde/Share/Sql/Hierarchical.php
horde/admin/groups.php
horde/lib/Api.php
horde/scripts/sql/horde_groups.mysql.sql
horde/scripts/sql/horde_groups.oci8.sql
horde/scripts/sql/horde_groups.pgsql.sql
horde/scripts/sql/horde_groups.sql
horde/scripts/upgrades/2010-06-01_horde_groups_autoincrement.mysql.sql [new file with mode: 0644]
horde/scripts/upgrades/2010-06-01_horde_groups_autoincrement.oci8.sql [new file with mode: 0644]
horde/scripts/upgrades/2010-06-01_horde_groups_autoincrement.pgsql.sql [new file with mode: 0644]
horde/scripts/upgrades/convert_datatree_groups_to_sql.php
horde/services/shares/edit.php
kronolith/lib/Api.php
kronolith/lib/Kronolith.php
kronolith/perms.php
kronolith/templates/chunks/calendar.php
nag/lib/Api.php
nag/lib/Forms/task.php
nag/lib/Nag.php
turba/config/sources.php.dist
turba/lib/Driver/Group.php
whups/lib/Application.php
whups/lib/Forms/AddComment.php
whups/lib/Forms/CreateTicket.php
whups/lib/Forms/EditTicket.php
whups/lib/Forms/Query.php
whups/lib/Whups.php
whups/ticket/queue.php
whups/ticket/type.php

index 13ab3dd..5ecda01 100644 (file)
@@ -221,9 +221,9 @@ class Ansel_Storage
             }
 
             if ($perms) {
-                $groups = Group::singleton();
+                $groups = Horde_Group::singleton();
                 $group_list = $groups->getGroupMemberships(Horde_Auth::getAuth());
-                if (!($group_list instanceof PEAR_Error) && count($group_list)) {
+                if (count($group_list)) {
                     foreach ($group_list as $group_id => $group_name) {
                         $perm->addGroupPermission($group_id, $perms, false);
                     }
index 84481ea..4306479 100644 (file)
@@ -18,9 +18,7 @@ $fieldsList = array(
 require_once dirname(__FILE__) . '/lib/Application.php';
 Horde_Registry::appInit('ansel');
 
-require_once 'Horde/Group.php';
-
-$groups = Group::singleton();
+$groups = Horde_Group::singleton();
 $auth = $injector->getInstance('Horde_Auth')->getOb();
 
 $form = null;
@@ -263,11 +261,12 @@ if ($auth->hasCapability('list')) {
     $userList = array();
 }
 
-$groupList = $groups->listGroups();
-asort($groupList);
-if ($groupList instanceof PEAR_Error) {
-    Horde::logMessage($groupList, 'NOTICE');
-    $groupList = array();
+$groupList = array();
+try {
+    $groupList = $groups->listGroups();
+    asort($groupList);
+} catch (Horde_Group_Exception $e) {
+    Horde::logMessage($e, 'NOTICE');
 }
 
 require $registry->get('templates', 'horde') . '/common-header.inc';
index e682c7b..01742e6 100644 (file)
@@ -207,8 +207,7 @@ class Folks_Hooks
 
     public function prelogin($app)
     {
-        require_once 'Horde/Group.php';
-        $group = Group::singleton();
+        $group = Horde_Group::singleton();
         $user_uid = Horde_Auth::getAuth();
 
         switch ($app) {
index 4bc2a90..958588a 100644 (file)
  */
 
 require_once dirname(__FILE__) . '/lib/base.php';
-require_once 'Horde/Group.php';
 
 $shares = $GLOBALS['injector']->getInstance('Horde_Share')->getScope();
-$groups = &Group::singleton();
+$groups = Horde_Group::singleton();
 $auth = $injector->getInstance('Horde_Auth')->getOb();
 
 $reload = false;
@@ -234,16 +233,15 @@ if ($auth->hasCapability('list')) {
     $userList = array();
 }
 
-if (!empty($conf['share']['any_group'])) {
-    $groupList = $groups->listGroups();
-} else {
-    $groupList = $groups->getGroupMemberships(Horde_Auth::getAuth(), true);
-}
-if (is_a($groupList, 'PEAR_Error')) {
-    Horde::logMessage($groupList, 'NOTICE');
-    $groupList = array();
+$groupList = array();
+try {
+    $groupList = empty($conf['share']['any_group'])
+        ? $groups->getGroupMemberships(Horde_Auth::getAuth(), true)
+        : $groups->listGroups();
+    asort($groupList);
+} catch (Horde_Group_Exception $e) {
+    Horde::logMessage($e, 'NOTICE');
 }
-asort($groupList);
 
 require FOLKS_TEMPLATES . '/common-header.inc';
 $notification->notify(array('listeners' => 'status'));
index 11b6379..800cdc7 100644 (file)
@@ -133,21 +133,19 @@ abstract class Horde_Ajax_Application_Base
      */
     public function listGroups()
     {
-        $result = new stdClass;
-        require_once 'Horde/Group.php';
-        $horde_groups = Group::singleton();
-        if (!empty($GLOBALS['conf']['share']['any_group'])) {
-            $groups = $horde_groups->listGroups();
-        } else {
-            $groups = $horde_groups->getGroupMemberships(Horde_Auth::getAuth(), true);
-        }
-        if ($groups) {
-            if ($groups instanceof PEAR_Error) {
-                $groups = array();
-            }
+        try {
+            $horde_groups = Horde_Group::singleton();
+            $groups = empty($GLOBALS['conf']['share']['any_group'])
+                ? $horde_groups->getGroupMemberships(Horde_Auth::getAuth(), true)
+                : $horde_groups->listGroups();
             asort($groups);
-            $result->groups = $groups;
+        } catch (Horde_Group_Exception $e) {
+            $groups = array();
         }
+
+        $result = new stdClass;
+        $result->groups = $groups;
+
         return $result;
     }
 
index 0babf6a..91b8a16 100644 (file)
@@ -103,12 +103,9 @@ class Horde_Core_Factory_KolabStorage
 
         $imap = Horde_Imap_Client::factory('socket', $params);
 
-        //@todo: The Group package needs to be converted to H4
-        require_once 'Horde/Group.php';
-
         $master = new Horde_Kolab_Storage_Driver_Imap(
             $imap,
-            Group::singleton()
+            Horde_Group::singleton()
         );
 
         return new Horde_Kolab_Storage(
index 617a8e0..d4cf222 100644 (file)
@@ -344,11 +344,11 @@ class Horde_Core_Perms_Ui
         /* Groups permissions. */
         $perm_val = $permission->getGroupPermissions();
         $this->_form->setSection('groups', _("Groups"), Horde::img('group.png'), false);
-        require_once 'Horde/Group.php';
-        $groups = Group::singleton();
-        $group_list = $groups->listGroups();
-        if ($group_list instanceof PEAR_Error) {
-            $GLOBALS['notification']->push($group_list);
+        try {
+            $groups = Horde_Group::singleton();
+            $group_list = $groups->listGroups();
+        } catch (Horde_Group_Exception $e) {
+            $GLOBALS['notification']->push($e);
             $group_list = array();
         }
 
diff --git a/framework/Group/Group.php b/framework/Group/Group.php
deleted file mode 100644 (file)
index 50097e7..0000000
+++ /dev/null
@@ -1,910 +0,0 @@
-<?php
-
-require_once 'Horde/DataTree.php';
-
-/** The parent Group node */
-define('GROUP_ROOT', -1);
-
-/**
- * The Group:: class provides the Horde groups system.
- *
- * Copyright 1999-2010 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  Stephane Huther <shuther1@free.fr>
- * @author  Chuck Hagenbuch <chuck@horde.org>
- * @package Horde_Group
- */
-class Group {
-
-    /**
-     * Group driver parameters
-     *
-     * @var array
-     */
-    var $_params;
-
-    /**
-     * Pointer to a DataTree instance to manage the different groups.
-     *
-     * @var DataTree
-     */
-    var $_datatree;
-
-    /**
-     * Cache of previously retrieved group objects.
-     *
-     * @var array
-     */
-    var $_groupCache = array();
-
-    /**
-     * Id-name-map of already cached group objects.
-     *
-     * @var array
-     */
-    var $_groupMap = array();
-
-    /**
-     * Id-name-hash of all existing groups.
-     *
-     * @var array
-     */
-    var $_groupList;
-
-    /**
-     * List of sub groups.
-     *
-     * @see listAllUsers()
-     * @var array
-     */
-    var $_subGroups = array();
-
-    /**
-     * Cache of parent groups.
-     *
-     * This is an array with group IDs as keys and the integer group id of the
-     * direct parent as values.
-     *
-     * @see getGroupParent
-     * @var array
-     */
-    var $_groupParents = array();
-
-    /**
-     * Cache of parent group trees.
-     *
-     * This is an array with group IDs as keys and id-name-hashes of all
-     * parents as values.
-     *
-     * @see getGroupParentList
-     * @var array
-     */
-    var $_groupParentList = array();
-
-    /**
-     * Cache of parents tree.
-     *
-     * @see getGroupParents()
-     * @var array
-     */
-    var $_parentTree = array();
-
-    /**
-     * Hash of groups of certain users.
-     *
-     * @see getGroupMemberShips()
-     * @var array
-     */
-    var $_userGroups;
-
-    /**
-     * Constructor.
-     */
-    function Group($params)
-    {
-        $this->_params = $params;
-        $this->__wakeup();
-    }
-
-    /**
-     * Initializes the object.
-     *
-     * @throws Horde_Exception
-     */
-    function __wakeup()
-    {
-        global $conf;
-
-        if (empty($conf['datatree']['driver'])) {
-            throw new Horde_Exception('You must configure a DataTree backend to use Groups.');
-        }
-
-        $driver = $conf['datatree']['driver'];
-        $this->_datatree = &DataTree::singleton($driver,
-                                                array_merge(Horde::getDriverConfig('datatree', $driver),
-                                                            array('group' => 'horde.groups')));
-
-        foreach (array_keys($this->_groupCache) as $name) {
-            $this->_groupCache[$name]->setGroupOb($this);
-            $this->_groupCache[$name]->setDataTree($this->_datatree);
-        }
-    }
-
-    /**
-     * Returns a new group object.
-     *
-     * @param string $name    The group's name.
-     * @param string $parent  The group's parent's name.
-     *
-     * @return DataTreeObject_Group  A new group object.
-     */
-    function &newGroup($name, $parent = GROUP_ROOT)
-    {
-        if (empty($name)) {
-            return PEAR::raiseError(_("Group names must be non-empty"));
-        }
-
-        if ($parent != GROUP_ROOT) {
-            $name = $this->getGroupName($parent) . ':' . DataTree::encodeName($name);
-        }
-
-        $group = new DataTreeObject_Group($name);
-        $group->setGroupOb($this);
-        return $group;
-    }
-
-    /**
-     * Returns a DataTreeObject_Group object corresponding to the named group,
-     * with the users and other data retrieved appropriately.
-     *
-     * @param string $name The name of the group to retrieve.
-     */
-    function &getGroup($name)
-    {
-        if (!isset($this->_groupCache[$name])) {
-            $this->_groupCache[$name] = &$this->_datatree->getObject($name, 'DataTreeObject_Group');
-            if (!is_a($this->_groupCache[$name], 'PEAR_Error')) {
-                $this->_groupCache[$name]->setGroupOb($this);
-                $this->_groupMap[$this->_groupCache[$name]->getId()] = $name;
-            }
-        }
-
-        return $this->_groupCache[$name];
-    }
-
-    /**
-     * Returns a DataTreeObject_Group object corresponding to the given unique
-     * ID, with the users and other data retrieved appropriately.
-     *
-     * @param integer $cid  The unique ID of the group to retrieve.
-     */
-    function &getGroupById($cid)
-    {
-        if (isset($this->_groupMap[$cid])) {
-            $group = $this->_groupCache[$this->_groupMap[$cid]];
-        } else {
-            $group = $this->_datatree->getObjectById($cid, 'DataTreeObject_Group');
-            if (!is_a($group, 'PEAR_Error')) {
-                $group->setGroupOb($this);
-                $name = $group->getName();
-                $this->_groupCache[$name] = &$group;
-                $this->_groupMap[$cid] = $name;
-            }
-        }
-
-        return $group;
-    }
-
-    /**
-     * Returns a globally unique ID for a group.
-     *
-     * @param DataTreeObject_Group $group  The group.
-     *
-     * @return string  A GUID referring to $group.
-     */
-    function getGUID($group)
-    {
-        return 'horde:group:' . $this->getGroupId($group);
-    }
-
-    /**
-     * Adds a group to the groups system. The group must first be created with
-     * Group::newGroup(), and have any initial users added to it, before this
-     * function is called.
-     *
-     * @param DataTreeObject_Group $group  The new group object.
-     *
-     * @throws Horde_History_Exception
-     * @throws InvalidArgumentException
-     */
-    function addGroup($group)
-    {
-        if (!is_a($group, 'DataTreeObject_Group')) {
-            return PEAR::raiseError('Groups must be DataTreeObject_Group objects or extend that class.');
-        }
-        $result = $this->_datatree->add($group);
-        if (is_a($result, 'PEAR_Error')) {
-            return $result;
-        }
-
-        $id = $group->getId();
-        $name = $group->getName();
-        $this->_groupCache[$name] = &$group;
-        $this->_groupMap[$id] = $name;
-        if (isset($this->_groupList)) {
-            $this->_groupList[$id] = $name;
-        }
-
-        /* Log the addition of the group in the history log. */
-        $GLOBALS['injector']->getInstance('Horde_History')->log($this->getGUID($group), array('action' => 'add'), true);
-
-        return $result;
-    }
-
-    /**
-     * Stores updated data - users, etc. - of a group to the backend system.
-     *
-     * @param DataTreeObject_Group $group  The group to update.
-     *
-     * @throws Horde_History_Exception
-     * @throws InvalidArgumentException
-     */
-    function updateGroup($group)
-    {
-        if (!is_a($group, 'DataTreeObject_Group')) {
-            return PEAR::raiseError('Groups must be DataTreeObject_Group objects or extend that class.');
-        }
-        $result = $this->_datatree->updateData($group);
-        if (is_a($result, 'PEAR_Error')) {
-            return $result;
-        }
-
-        $this->_groupCache[$group->getName()] = &$group;
-
-        /* Log the update of the group users on the history log. */
-        $history = $GLOBALS['injector']->getInstance('Horde_History');
-        $guid = $this->getGUID($group);
-        foreach ($group->getAuditLog() as $userId => $action) {
-            $history->log($guid, array('action' => $action, 'user' => $userId), true);
-        }
-        $group->clearAuditLog();
-
-        /* Log the group modification. */
-        $history->log($guid, array('action' => 'modify'), true);
-        return $result;
-    }
-
-    /**
-     * Removes a group from the groups system permanently.
-     *
-     * @param DataTreeObject_Group $group  The group to remove.
-     * @param boolean $force               Force to remove every child.
-     *
-     * @throws Horde_History_Exception
-     * @throws InvalidArgumentException
-     */
-    function removeGroup($group, $force = false)
-    {
-        if (!is_a($group, 'DataTreeObject_Group')) {
-            return PEAR::raiseError('Groups must be DataTreeObject_Group objects or extend that class.');
-        }
-
-        $id = $group->getId();
-        unset($this->_groupMap[$id]);
-        if (isset($this->_groupList)) {
-            unset($this->_groupList[$id]);
-        }
-        unset($this->_groupCache[$group->getName()]);
-
-        $GLOBALS['injector']->getInstance('Horde_History')->log($this->getGUID($group), array('action' => 'delete'), true);
-
-        return $this->_datatree->remove($group, $force);
-    }
-
-    /**
-     * Retrieves the name of a group.
-     *
-     * @param integer|DataTreeObject_Group $gid  The id of the group or the
-     *                                           group object to retrieve the
-     *                                           name for.
-     *
-     * @return string  The group's name.
-     */
-    function getGroupName($gid)
-    {
-        if (is_a($gid, 'DataTreeObject_Group')) {
-            $gid = $gid->getId();
-        }
-
-        if (isset($this->_groupMap[$gid])) {
-            return $this->_groupMap[$gid];
-        }
-        if (isset($this->_groupList[$gid])) {
-            return $this->_groupList[$gid];
-        }
-
-        return $this->_datatree->getName($gid);
-    }
-
-    /**
-     * Strips all parent references off of the given group name.
-     *
-     * @param string $group  Name of the group.
-     *
-     * @return The name of the group without parents.
-     */
-    function getGroupShortName($group)
-    {
-        return $this->_datatree->getShortName($group);
-    }
-
-    /**
-     * Retrieves the ID of a group.
-     *
-     * @param string|DataTreeObject_Group $group  The group name or object to
-     *                                            retrieve the ID for.
-     *
-     * @return integer  The group's ID.
-     */
-    function getGroupId($group)
-    {
-        if (is_a($group, 'DataTreeObject_Group')) {
-            $group = $group->getName();
-        }
-
-        $id = array_search($group, $this->_groupMap);
-        if ($id !== false) {
-            return $id;
-        }
-        if (isset($this->_groupList)) {
-            $id = array_search($group, $this->_groupList);
-            if ($id !== false) {
-                return $id;
-            }
-        }
-
-        return $this->_datatree->getId($group);
-    }
-
-    /**
-     * Check if a group exists in the system.
-     *
-     * @param string $group  The group to check.
-     *
-     * @return boolean  True if the group exists, false otherwise.
-     */
-    function exists($group)
-    {
-        if (isset($this->_groupCache[$group]) ||
-            (isset($this->_groupList) &&
-             array_search($group, $this->_groupList) !== false)) {
-            return true;
-        }
-
-        return $this->_datatree->exists($group);
-    }
-
-    /**
-     * Returns a tree of the parents of a child group.
-     *
-     * @param integer $gid  The id of the child group.
-     *
-     * @return array  The group parents tree, with groupnames as the keys.
-     */
-    function getGroupParents($gid)
-    {
-        if (!isset($this->_parentTree[$gid])) {
-            $name = $this->getGroupName($gid);
-            $parents = $this->_datatree->getParents($name);
-            if (is_a($parents, 'PEAR_Error')) {
-                return $parents;
-            }
-            $this->_parentTree[$gid] = $parents;
-        }
-
-        return $this->_parentTree[$gid];
-    }
-
-    /**
-     * Returns the single parent ID of the given group.
-     *
-     * @param integer $gid  The DataTree ID of the child group.
-     *
-     * @return integer  The parent of the given group.
-     */
-    function getGroupParent($gid)
-    {
-        if (!isset($this->_groupParents[$gid])) {
-            $parent = $this->_datatree->getParentById($gid);
-            if (is_a($parent, 'PEAR_Error')) {
-                return $parent;
-            }
-            $this->_groupParents[$gid] = $parent;
-        }
-
-        return $this->_groupParents[$gid];
-    }
-
-    /**
-     * Returns a flat list of the parents of a child group
-     *
-     * @param integer $gid  The id of the group.
-     *
-     * @return array  A flat list of all of the parents of $group, hashed in
-     *                $id => $name format.
-     */
-    function getGroupParentList($gid)
-    {
-        if (!isset($this->_groupParentList[$gid])) {
-            $parents = $this->_datatree->getParentList($gid);
-            if (is_a($parents, 'PEAR_Error')) {
-                return $parents;
-            }
-            $this->_groupParentList[$gid] = $parents;
-        }
-
-        return $this->_groupParentList[$gid];
-    }
-
-    /**
-     * Returns a list of all groups, in the format id => groupname.
-     *
-     * @param boolean $refresh  If true, the cached value is ignored and the
-     *                          group list is refreshed from the group backend.
-     *
-     * @return array  ID => groupname hash.
-     */
-    function listGroups($refresh = false)
-    {
-        if ($refresh || !isset($this->_groupList)) {
-            $this->_groupList = $this->_datatree->get(DATATREE_FORMAT_FLAT, GROUP_ROOT, true);
-            unset($this->_groupList[GROUP_ROOT]);
-        }
-
-        return $this->_groupList;
-    }
-
-    /**
-     * Get a list of every user that is a part of this group ONLY.
-     *
-     * @param integer $gid  The ID of the group.
-     *
-     * @return array  The user list.
-     */
-    function listUsers($gid)
-    {
-        $groupOb = &$this->getGroupById($gid);
-        if (is_a($groupOb, 'PEAR_Error')) {
-            return $groupOb;
-        }
-
-        if (!isset($groupOb->data['users']) ||
-            !is_array($groupOb->data['users'])) {
-            return array();
-        }
-
-        return array_keys($groupOb->data['users']);
-    }
-
-    /**
-     * Get a list of every user that is part of the specified group
-     * and any of its subgroups.
-     *
-     * @param integer $group  The ID of the parent group.
-     *
-     * @return array  The complete user list.
-     */
-    function listAllUsers($gid)
-    {
-        if (!isset($this->_subGroups[$gid])) {
-            // Get a list of every group that is a sub-group of $group.
-            $groups = $this->_datatree->get(DATATREE_FORMAT_FLAT, $this->getGroupName($gid), true);
-            if (is_a($groups, 'PEAR_Error')) {
-                return $groups;
-            }
-            $this->_subGroups[$gid] = array_keys($groups);
-        }
-
-        $users = array();
-        foreach ($this->_subGroups[$gid] as $groupId) {
-            $users = array_merge($users, $this->listUsers($groupId));
-        }
-        return array_values(array_flip(array_flip($users)));
-    }
-
-    /**
-     * Get a list of every group that $user is in.
-     *
-     * @param string  $user          The user to get groups for.
-     * @param boolean $parentGroups  Also return the parents of any groups?
-     *
-     * @return array  An array of all groups the user is in.
-     */
-    function getGroupMemberships($user, $parentGroups = false)
-    {
-        if (!isset($this->_userGroups[$user])) {
-            $criteria = array(
-                'AND' => array(
-                    array('field' => 'name', 'op' => '=', 'test' => 'user'),
-                    array('field' => 'key', 'op' => '=', 'test' => $user)));
-            $groups = $this->_datatree->getByAttributes($criteria);
-
-            if (is_a($groups, 'PEAR_Error')) {
-                return $groups;
-            }
-
-            if ($parentGroups) {
-                foreach ($groups as $id => $g) {
-                    $parents = $this->_datatree->getParentList($id);
-                    if (is_a($parents, 'PEAR_Error')) {
-                        return $parents;
-                    }
-                    $groups += $parents;
-                }
-            }
-
-            $this->_userGroups[$user] = $groups;
-        }
-
-        return $this->_userGroups[$user];
-    }
-
-    /**
-     * Say if a user is a member of a group or not.
-     *
-     * @param string $user        The name of the user.
-     * @param integer $gid        The ID of the group.
-     * @param boolean $subgroups  Return true if the user is in any subgroups
-     *                            of group with ID $gid, also.
-     *
-     * @return boolean
-     */
-    function userIsInGroup($user, $gid, $subgroups = true)
-    {
-        if (!$this->exists($this->getGroupName($gid))) {
-            return false;
-        } elseif ($subgroups) {
-            $groups = $this->getGroupMemberships($user, true);
-            if (is_a($groups, 'PEAR_Error')) {
-                Horde::logMessage($groups, 'ERR');
-                return false;
-            }
-
-            return !empty($groups[$gid]);
-        } else {
-            $users = $this->listUsers($gid);
-            if (is_a($users, 'PEAR_Error')) {
-                Horde::logMessage($users, 'ERR');
-                return false;
-            }
-            return in_array($user, $users);
-        }
-    }
-
-    /**
-     * Returns the nesting level of the given group. 0 is returned for any
-     * object directly below GROUP_ROOT.
-     *
-     * @param integer $gid  The DataTree ID of the group.
-     *
-     * @return The DataTree level of the group.
-     */
-    function getLevel($gid)
-    {
-        $name = $this->getGroupName($gid);
-        return substr_count($name, ':');
-    }
-
-    /**
-     * Stores the object in the session cache.
-     */
-    function shutdown()
-    {
-        $session = new Horde_SessionObjects();
-        $session->overwrite('horde_group', $this, false);
-    }
-
-    /**
-     * Returns the properties that need to be serialized.
-     *
-     * @return array  List of serializable properties.
-     */
-    function __sleep()
-    {
-        $properties = get_object_vars($this);
-        unset($properties['_datatree']);
-        $properties = array_keys($properties);
-        return $properties;
-    }
-
-    /**
-     * Attempts to return a concrete Group instance based on $driver.
-     *
-     * @param mixed $driver  The type of concrete Group subclass to return.
-     * @param array $params  A hash containing any additional configuration or
-     *                       connection parameters a subclass might need.
-     *
-     * @return Group  The newly created concrete Group instance, or a
-     *                PEAR_Error object on an error.
-     */
-    public static function factory($driver = '', $params = null)
-    {
-        if (is_null($params)) {
-            $params = Horde::getDriverConfig('group', $driver);
-        }
-
-        $class = Group::_loadDriver($driver);
-        if (class_exists($class)) {
-            $group = new $class($params);
-        } else {
-            $group = PEAR::raiseError('Class definition of ' . $class . ' not found.');
-        }
-
-        return $group;
-    }
-
-    /**
-     * Attempts to return a reference to a concrete Group instance.
-     * It will only create a new instance if no Group instance
-     * currently exists.
-     *
-     * This method must be invoked as: $var = &Group::singleton()
-     *
-     * @return Group  The concrete Group reference, or false on an error.
-     */
-    public static function singleton()
-    {
-        static $group;
-
-        if (isset($group)) {
-            return $group;
-        }
-
-        $group_driver = null;
-        $group_params = null;
-        $auth = $GLOBALS['injector']->getInstance('Horde_Auth')->getOb();
-        if ($auth->hasCapability('groups')) {
-            $group_driver = $auth->getDriver();
-            $group_params = $auth;
-        } elseif (!empty($GLOBALS['conf']['group']['driver']) &&
-                  $GLOBALS['conf']['group']['driver'] != 'datatree') {
-            $group_driver = $GLOBALS['conf']['group']['driver'];
-            $group_params = Horde::getDriverConfig('group', $group_driver);
-        }
-
-        Group::_loadDriver($group_driver);
-
-        $group = null;
-        if (!empty($GLOBALS['conf']['group']['cache'])) {
-            $session = new Horde_SessionObjects();
-            $group = $session->query('horde_group');
-        }
-
-        if (!$group) {
-            $group = Group::factory($group_driver, $group_params);
-        }
-
-        if (!empty($GLOBALS['conf']['group']['cache'])) {
-            register_shutdown_function(array(&$group, 'shutdown'));
-        }
-
-        return $group;
-    }
-
-    protected static function _loadDriver($driver)
-    {
-        if (!$driver) {
-            $class = 'Group';
-        } else {
-            $driver = basename($driver);
-            $class = 'Group_' . $driver;
-            if (!class_exists($class)) {
-                include 'Horde/Group/' . $driver . '.php';
-            }
-        }
-
-        return $class;
-    }
-
-}
-
-/**
- * Extension of the DataTreeObject class for storing Group information
- * in the Categories driver. If you want to store specialized Group
- * information, you should extend this class instead of extending
- * DataTreeObject directly.
- *
- * @author  Chuck Hagenbuch <chuck@horde.org>
- * @package Horde_Group
- */
-class DataTreeObject_Group extends DataTreeObject {
-
-    /**
-     * The Group object which this group is associated with - needed
-     * for updating data in the backend to make changes stick, etc.
-     *
-     * @var Group
-     */
-    var $_groupOb;
-
-    /**
-     * This variable caches the users added or removed from the group
-     * for History logging of user-groups relationship.
-     *
-     * @var array
-     */
-    var $_auditLog = array();
-
-    /**
-     * The DataTreeObject_Group constructor. Just makes sure to call
-     * the parent constructor so that the group's name is set
-     * properly.
-     *
-     * @param string $name  The name of the group.
-     */
-    function DataTreeObject_Group($name)
-    {
-        parent::DataTreeObject($name);
-    }
-
-    /**
-     * Returns the properties that need to be serialized.
-     *
-     * @return array  List of serializable properties.
-     */
-    function __sleep()
-    {
-        $properties = get_object_vars($this);
-        unset($properties['datatree'], $properties['_groupOb']);
-        $properties = array_keys($properties);
-        return $properties;
-    }
-
-    /**
-     * Associates a Group object with this group.
-     *
-     * @param Group $groupOb  The Group object.
-     */
-    function setGroupOb(&$groupOb)
-    {
-        $this->_groupOb = &$groupOb;
-    }
-
-    /**
-     * Fetch the ID of this group
-     *
-     * @return string The group's ID
-     */
-    function getId()
-    {
-        return $this->_groupOb->getGroupId($this);
-    }
-
-    /**
-     * Save any changes to this object to the backend permanently.
-     */
-    function save()
-    {
-        return $this->_groupOb->updateGroup($this);
-
-    }
-
-    /**
-     * Adds a user to this group, and makes sure that the backend is
-     * updated as well.
-     *
-     * @param string $username The user to add.
-     */
-    function addUser($username, $update = true)
-    {
-        $this->data['users'][$username] = 1;
-        $this->_auditLog[$username] = 'addUser';
-        if ($update && $this->_groupOb->exists($this->getName())) {
-            return $this->save();
-        }
-    }
-
-    /**
-     * Removes a user from this group, and makes sure that the backend
-     * is updated as well.
-     *
-     * @param string $username The user to remove.
-     */
-    function removeUser($username, $update = true)
-    {
-        unset($this->data['users'][$username]);
-        $this->_auditLog[$username] = 'deleteUser';
-        if ($update) {
-            return $this->save();
-        }
-    }
-
-    /**
-     * Get a list of every user that is a part of this group
-     * (and only this group)
-     *
-     * @return array  The user list
-     */
-    function listUsers()
-    {
-        return $this->_groupOb->listUsers($this->getId());
-    }
-
-    /**
-     * Get a list of every user that is a part of this group and
-     * any of it's subgroups
-     *
-     * @return array  The complete user list
-     */
-    function listAllUsers()
-    {
-        return $this->_groupOb->listAllUsers($this->getId());
-    }
-
-    /**
-     * Get all the users recently added or removed from the group.
-     */
-    function getAuditLog()
-    {
-        return $this->_auditLog;
-    }
-
-    /**
-     * Clears the audit log. To be called after group update.
-     */
-    function clearAuditLog()
-    {
-        $this->_auditLog = array();
-    }
-
-    /**
-     * Map this object's attributes from the data array into a format
-     * that we can store in the attributes storage backend.
-     *
-     * @return array  The attributes array.
-     */
-    function _toAttributes()
-    {
-        // Default to no attributes.
-        $attributes = array();
-
-        // Loop through all users, if any.
-        if (isset($this->data['users']) && is_array($this->data['users']) && count($this->data['users'])) {
-            foreach ($this->data['users'] as $user => $active) {
-                $attributes[] = array('name' => 'user',
-                                      'key' => $user,
-                                      'value' => $active);
-            }
-        }
-        $attributes[] = array('name' => 'email',
-                              'key' => '',
-                              'value' => $this->get('email'));
-
-        return $attributes;
-    }
-
-    /**
-     * Take in a list of attributes from the backend and map it to our
-     * internal data array.
-     *
-     * @param array $attributes  The list of attributes from the
-     *                           backend (attribute name, key, and value).
-     */
-    function _fromAttributes($attributes)
-    {
-        // Initialize data array.
-        $this->data['users'] = array();
-
-        foreach ($attributes as $attr) {
-            if ($attr['name'] == 'user') {
-                $this->data['users'][$attr['key']] = $attr['value'];
-            } else {
-                $this->data[$attr['name']] = $attr['value'];
-            }
-        }
-    }
-
-}
diff --git a/framework/Group/Group/contactlists.php b/framework/Group/Group/contactlists.php
deleted file mode 100644 (file)
index 52ea400..0000000
+++ /dev/null
@@ -1,721 +0,0 @@
-<?php
-/**
- * The Group_contactlists class provides a groups system based on Turba
- * contact lists. Only SQL sources are supported.
- *
- * Copyright 2008-2010 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 J. Rubinsky <mrubinsk@horde.org>
- * @package Horde_Group
- */
-class Group_contactlists extends Group {
-
-    /**
-     * A cache object
-     *
-     * @var Horde_Cache object
-     */
-    var $_cache = null;
-
-    /**
-     * Handles for the  database connections. Need one for each possible source.
-     *
-     * @var DB
-     */
-    var $_db = array();
-
-    /**
-     * Local copy of available address book sources that the group driver can
-     * use.
-     *
-     * @var array of Turba's cfgSource style entries.
-     */
-    var $_sources = array();
-
-    /**
-     * Local cache of retreived group entries from Turba storage.
-     *
-     * @var unknown_type
-     */
-    var $_listEntries = array();
-
-    /**
-     * Constructor.
-     */
-    function Group_contactlists($params)
-    {
-        // Get a list of all available Turba sources
-        $turba_sources = Horde::loadConfiguration('sources.php',
-                                                  'cfgSources', 'turba');
-
-        // We only support sql type sources.
-        foreach ($turba_sources as $key => $source) {
-            if ($source['type'] == 'sql') {
-                $this->_sources[$key] = $source;
-            }
-        }
-
-        $this->_cache = $GLOBALS['injector']->getInstance('Horde_Cache');
-    }
-
-    /**
-     * Initializes the object.
-     */
-    function __wakeup()
-    {
-    }
-
-    /**
-     * Returns the properties that need to be serialized.
-     *
-     * @return array  List of serializable properties.
-     */
-    function __sleep()
-    {
-    }
-
-    /**
-     * Stores the object in the session cache.
-     */
-    function shutdown()
-    {
-    }
-
-    /**
-     * Returns a new group object.
-     *
-     * @param string $name    The group's name.
-     * @param string $parent  The group's parent's name.
-     *
-     * @return PEAR_Error  This functionality is not supported in this driver.
-     */
-    function newGroup($name, $parent = GROUP_ROOT)
-    {
-        return PEAR::raiseError(_("Unsupported"));
-    }
-
-    /**
-     * Returns a Group object corresponding to the named group,
-     * with the users and other data retrieved appropriately.
-     *
-     * This is deprecated. Use getGroupById instead.
-     *
-     * @param string $name The name of the group to retrieve.
-     */
-    function getGroup($name)
-    {
-        return PEAR::raiseError(_("Deprecated. Use getGroupById() instead."));
-    }
-
-    /**
-     * Returns a ContactListObject_Group object corresponding to the given unique
-     * ID, with the users and other data retrieved appropriately.
-     *
-     * @param integer $cid  The unique ID of the group to retrieve.
-     */
-    function getGroupById($gid)
-    {
-
-        if (!empty($this->_groupCache[$gid])) {
-            return $this->_groupCache[$gid];
-        }
-        list($source, $id) = explode(':', $gid);
-        $entry = $this->_retrieveListEntry($gid);
-        if (is_a($entry, 'PEAR_Error')) {
-            return $entry;
-        } elseif (empty($entry)) {
-            return PEAR::raiseError($gid . ' does not exist');
-        }
-
-        $users = $this->_getAllMembers($gid);
-        if (is_a($users, 'PEAR_Error')) {
-            return $users;
-        }
-
-        $group = new ContactListObject_Group($entry[$this->_sources[$source]['map'][$this->_sources[$source]['list_name_field']]]);
-        $group->id = $gid;
-        $group->data['email'] = $entry[$this->_sources[$source]['map']['email']];
-        if (!empty($users)) {
-            $group->data['users'] = array_flip($users);
-        }
-
-        $group->setGroupOb($this);
-        $this->_groupCache[$gid] = $group;
-
-        return $group;
-    }
-
-    /**
-     * Adds a group to the groups system. The group must first be created with
-     * Group::newGroup(), and have any initial users added to it, before this
-     * function is called.
-     *
-     * @param ContactListObjectObject_Group $group  The new group object.
-     *
-     * @return PEAR_Error - unsupported
-     */
-    function addGroup($group)
-    {
-        return PEAR::raiseError(_("Unsupported"));
-    }
-
-    /**
-     * Stores updated data - users, etc. - of a group to the backend system.
-     *
-     * @param ContactListObject_Group $group  The group to update.
-     */
-    function updateGroup($group)
-    {
-        return PEAR::raiseError(_("Unsupported"));
-    }
-
-    /**
-     * Removes a group from the groups system permanently.
-     *
-     * @param ContactListObject_Group $group  The group to remove.
-     * @param boolean $force          Force to remove every child.
-     *
-     * @return PEAR_Error - unsupported.
-     */
-    function removeGroup($group, $force = false)
-    {
-        return PEAR::raiseError(_("Unsupported"));
-    }
-
-    /**
-     * Retrieves the name of a group.
-     *
-     * @param integer|ContactListObject_Group $gid  The id of the group or the
-     *                                      group object to retrieve the
-     *                                      name for.
-     *
-     * @return string  The group's name.
-     */
-    function getGroupName($gid)
-    {
-        static $beenHere;
-
-        if (strpos($gid, ':') === false) {
-            return PEAR::raiseError(sprintf(_("Group %s not found."), $gid));
-        }
-
-        if (is_a($gid, 'ContactListObject_Group')) {
-            $gid = $gid->getId();
-        }
-        if (!empty($this->_listEntries[$gid])) {
-            list($source, $id) = explode(':', $gid);
-            $beenHere = false;
-            return $this->_listEntries[$gid][$this->_sources[$source]['map'][$this->_sources[$source]['list_name_field']]];
-        }
-
-        $result = $this->_retrieveListEntry($gid);
-        if (is_a($result, 'PEAR_Error')) {
-            return $result;
-        }
-
-        // We should have the information cached now, try again..but protect
-        // against anything nasty...
-        if (!$beenHere) {
-            $beenHere = true;
-            return $this->getGroupName($gid);
-        }
-
-        return PEAR::raiseError(sprintf(_("Group %s not found."), $gid));
-    }
-
-    /**
-     * Strips all parent references off of the given group name.
-     *
-     * Not used in this driver...group display names are ONLY for display.
-     *
-     * @param string $group  Name of the group.
-     *
-     * @return The name of the group without parents.
-     */
-    function getGroupShortName($group)
-    {
-       return $group;
-    }
-
-    /**
-     * Retrieves the ID of a group, given the group object.
-     * Here for BC. Kinda silly, since if we have the object, we can just call
-     * getId() ourselves.
-     *
-     * @param ContactListObject_Group $group  The group object to retrieve the
-     *                                        ID for.
-     *
-     * @return integer  The group's ID.
-     */
-    function getGroupId($group)
-    {
-        if (is_a($group, 'ContactListObject_Group')) {
-            return $group->getId();
-        }
-
-        return PEAR::raiseError(_("Unsupported"));
-    }
-
-    /**
-     * Check if a group exists in the system.
-     *
-     * This must either be a noop or we need to somehow "uniqueify" the
-     * list's display name?
-     *
-     * @param string $group  The group name to check.
-     *
-     * @return boolean  True if the group exists, false otherwise.
-     */
-    function exists($group)
-    {
-        return true;
-    }
-
-    /**
-     * Returns a tree of the parents of a child group.
-     *
-     * @param integer $gid  The id of the child group.
-     *
-     * @return array  The group parents tree, with groupnames as the keys.
-     */
-    function getGroupParents($gid)
-    {
-        return array();
-    }
-
-
-    /**
-     * Returns the single parent ID of the given group.
-     *
-     * @param integer $gid  The ID of the child group.
-     *
-     * @return integer  The parent of the given group.
-     */
-    function getGroupParent($gid)
-    {
-        return GROUP_ROOT;
-    }
-
-    /**
-     * Returns a flat list of the parents of a child group
-     *
-     * @param integer $gid  The id of the group.
-     *
-     * @return array  A flat list of all of the parents of $group, hashed in
-     *                $id => $name format.
-     */
-    function getGroupParentList($gid)
-    {
-        return array();
-    }
-
-    /**
-     * Returns a list of all groups, in the format id => groupname.
-     * The groups returned represent only the groups visible to the current user
-     * only.
-     *
-     * @param boolean $refresh  If true, the cached value is ignored and the
-     *                          group list is refreshed from the group backend.
-     *
-     * @return array  ID => groupname hash.
-     */
-    function listGroups($refresh = false)
-    {
-        if (isset($this->_groupList) && !$refresh) {
-            return $this->_groupList;
-        }
-
-        // First, make sure we are connected to all sources
-        $this->_connect();
-
-        $groups = array();
-        $owners = array();
-        foreach ($this->_sources as $key => $source) {
-            if ($source['use_shares']) {
-                if (empty($contact_shares)) {
-                    $scope = $GLOBALS['registry']->hasInterface('contacts');
-                    $shares = $GLOBALS['injector']->getInstance('Horde_Share')->getScope($scope);
-                    $this->_contact_shares = $shares->listShares(Horde_Auth::getAuth(), Horde_Perms::SHOW, Horde_Auth::getAuth());
-                }
-                // Contruct a list of owner ids to use
-                foreach ($this->_contact_shares as $id => $share) {
-                    $params = @unserialize($share->get('params'));
-                    if ($params['source'] == $key) {
-                        $owners[] = $params['name'];
-                    }
-                }
-            } else {
-                $owners = array(Horde_Auth::getAuth());
-            }
-            $owner_ids = array();
-            foreach ($owners as $owner) {
-                $owner_ids[] = $this->_db[$key]->quote($owner);
-            }
-            $sql = 'SELECT ' . $source['map']['__key'] . ', ' . $source['map'][$source['list_name_field']]
-                . '  FROM ' . $source['params']['table'] . ' WHERE '
-                . $source['map']['__type'] . ' = \'Group\' AND '
-                . $source['map']['__owner'] . ' IN (' . implode(',', $owner_ids ) . ')';
-
-           $results = $this->_db[$key]->getAssoc($sql);
-           foreach ($results as $id => $name) {
-               $groups[$key . ':' . $id] = $name;
-           }
-        }
-        $this->_groupList = $groups;
-
-        return $this->_groupList;
-    }
-
-    /**
-     * Get a list of every user that is part of the specified group
-     * and any of its subgroups.
-     *
-     * @param integer $group  The ID of the parent group.
-     *
-     * @return array  The complete user list.
-     */
-    function listAllUsers($gid)
-    {
-        $members = $this->_getAllMembers($gid, true);
-        if (is_a($members, 'PEAR_Error')) {
-            return $members;
-        }
-
-        return array_values($members);
-    }
-
-    /**
-     * Returns a hash representing the list entry. Items are keyed by the
-     * backend specific keys.
-     *
-     * @param string $gid  The group id
-     * @return array | PEAR_Error
-     */
-    function _retrieveListEntry($gid)
-    {
-        if (!empty($this->_listEntries[$gid])) {
-            return $this->_listEntries[$gid];
-        }
-
-        list($source, $id) = explode(':', $gid);
-        if (empty($this->_sources[$source])) {
-            return array();
-        }
-
-        $this->_connect($source);
-        $sql = 'SELECT ' . $this->_sources[$source]['map']['__members'] . ','
-            . $this->_sources[$source]['map']['email'] . ','
-            . $this->_sources[$source]['map'][$this->_sources[$source]['list_name_field']]
-            . ' from ' . $this->_sources[$source]['params']['table'] . ' WHERE '
-            . $this->_sources[$source]['map']['__key'] . ' = ' . $this->_db[$source]->quote($id);
-
-        $results = $this->_db[$source]->getRow($sql,array(), DB_FETCHMODE_ASSOC);
-        if (is_a($results, 'PEAR_Error')) {
-            Horde::logMessage($results, 'ERR');
-        }
-        $this->_listEntries[$gid] = $results;
-
-        return $results;
-
-    }
-
-    /**
-     * TODO
-     */
-    function _getAllMembers($gid, $subGroups = false)
-    {
-        if (empty($gid) || strpos($gid, ':') === false) {
-            return PEAR::raiseError(sprintf(_("Unsupported group id: %s"), $gid));
-        }
-
-        list($source, $id) = explode(':', $gid);
-        $entry = $this->_retrieveListEntry($gid);
-        if (is_a($entry, 'PEAR_Error')) {
-            return $entry;
-        }
-        $members = @unserialize($entry[$this->_sources[$source]['map']['__members']]);
-        $users = array();
-
-        // TODO: optimize this to only query each table once
-        foreach ($members as $member) {
-
-            // Is this member from the same source or a different one?
-            if (strpos($member, ':') !== false) {
-                list($newSource, $uid) = explode(':', $member);
-                if (!empty($this->_contact_shares[$newSource])) {
-                    $params = @unserialize($this->_contact_shares[$newSource]->get('params'));
-                    $newSource = $params['source'];
-                    $member = $uid;
-                    $this->_connect($newSource);
-                } elseif (empty($this->_sources[$newSource])) {
-                    // Last chance, it's not in one of our non-share sources
-                    continue;
-                }
-            } else {
-                // Same source
-                $newSource = $source;
-            }
-
-            $sql = 'SELECT ' . $this->_sources[$newSource]['map']['email']
-                . ', ' . $this->_sources[$newSource]['map']['__type']
-                . ' FROM ' . $this->_sources[$newSource]['params']['table']
-                . ' WHERE ' . $this->_sources[$newSource]['map']['__key']
-                . ' = ' . $this->_db[$newSource]->quote($member);
-
-            $results = $this->_db[$newSource]->getRow($sql);
-            if (is_a($results, 'PEAR_Error')) {
-                Horde::logMessage($results, 'ERR');
-                return $results;
-            }
-
-            // Sub-Lists are treated as sub groups the best that we can...
-            if ($subGroups && $results[1] == 'Group') {
-                $this->_subGroups[$gid] = $newSource . ':' . $member;
-                $users = array_merge($users, $this->_getAllMembers($newSource . ':' . $member));
-            }
-            if (strlen($results[0])) {
-                // use a key to dump dups
-                $users[$results[0]] = $results[0];
-            }
-        }
-
-        return $users;
-    }
-
-    /**
-     * Returns ALL contact lists present in ALL sources that this driver knows
-     * about.
-     *
-     */
-    function _listAllLists()
-    {
-        // Clear the cache - we will rebuild it.
-        $this->_listEntries = array();
-
-        foreach ($this->_sources as $key => $source) {
-            $this->_connect($key);
-            $sql = 'SELECT ' . $source['map']['__key'] . ','
-            . $source['map']['__members'] . ','
-            . $source['map']['email'] . ','
-            . $source['map'][$source['list_name_field']]
-            . ' FROM ' . $source['params']['table'] . ' WHERE '
-            . $source['map']['__type'] . ' = \'Group\'';
-
-           $results = $this->_db[$key]->query($sql);
-           if (is_a($results, 'PEAR_Error')) {
-               return $results;
-           }
-           while ($row = $results->fetchRow(DB_FETCHMODE_ASSOC)) {
-                $this->_listEntries[$key . ':' . $row[$source['map']['__key']]] = $row;
-           }
-        }
-
-        return $this->_listEntries;
-    }
-
-    /**
-     * Get a list of every group that $user is in.
-     *
-     * @param string  $user          The user to get groups for.
-     * @param boolean $parentGroups  Also return the parents of any groups?
-     *
-     * @return array  An array of all groups the user is in.
-     */
-    function getGroupMemberships($user, $parentGroups = false)
-    {
-        if (($memberships = $this->_cache->get('Group_contactlists_memberships' . md5($user))) !== false) {
-            return unserialize($memberships);
-        }
-        $lists = $this->_listAllLists();
-        $memberships = array();
-        foreach (array_keys($lists) as $list) {
-            $members = $this->_getAllMembers($list, $parentGroups);
-            if (!empty($members[$user])) {
-                $memberships[] = $list;
-            }
-        }
-
-        $this->_cache->set('Group_contactlists_memberships' . md5($user), serialize($memberships));
-        return $memberships;
-
-    }
-
-    /**
-     * Say if a user is a member of a group or not.
-     *
-     * @param string $user        The name of the user.
-     * @param integer $gid        The ID of the group.
-     * @param boolean $subgroups  Return true if the user is in any subgroups
-     *                            of group with ID $gid, also.
-     *
-     * @return boolean
-     */
-    function userIsInGroup($user, $gid, $subgroups = true)
-    {
-        if (isset($_SESSION['horde']['groups']['i'][$user][$subgroups][$gid])) {
-            return $_SESSION['horde']['groups']['i'][$user][$subgroups][$gid];
-        }
-
-        $users = $this->_getAllMembers($gid, $subgroups);
-        if (is_a($users, 'PEAR_Error')) {
-            Horde::logMessage($users, 'ERR');
-            return false;
-        }
-        $result = !empty($users[$user]);
-        $_SESSION['horde']['groups']['i'][$user][$subgroups][$gid] = (bool)$result;
-        return (bool)$result;
-    }
-
-    /**
-     * Attempts to open a persistent connection to the sql server.
-     *
-     * @return boolean  True on success.
-     * @throws Horde_Exception
-     */
-    function _connect($source = null)
-    {
-        if (!is_null($source) && !empty($this->_db[$source])) {
-            return true;
-        }
-        /* Connect to the sql server using the supplied parameters. */
-        require_once 'DB.php';
-
-        if (is_null($source)) {
-            $sources = array_keys($this->_sources);
-        } else {
-            $sources = array($source);
-        }
-
-        foreach ($sources as $source) {
-            if (empty($this->_db[$source])) {
-                $this->_db[$source] = DB::connect($this->_sources[$source]['params'],
-                    array('persistent' => !empty($this->_sources[$source]['params']['persistent'])));
-                if (is_a($this->_db[$source], 'PEAR_Error')) {
-                    throw new Horde_Exception_Prior($this->_db[$source]);
-                }
-
-                /* Set DB portability options. */
-                switch ($this->_db[$source]->phptype) {
-                case 'mssql':
-                    $this->_db[$source]->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM);
-                    break;
-                default:
-                    $this->_db[$source]->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS);
-                }
-            }
-        }
-
-        return true;
-    }
-
-}
-
-/**
- * Extension of the DataTreeObject_Group class for storing Group information.
- *
- * @author  Michael J. Rubinsky <mrubinsk@horde.org>
- * @package Horde_Group
- */
-class ContactListObject_Group extends DataTreeObject_Group {
-
-    /**
-     * The unique name of this object.
-     * These names have the same requirements as other object names - they must
-     * be unique, etc.
-     *
-     * @var string
-     */
-    var $name;
-
-    /**
-     * The unique name of this object.
-     * These names have the same requirements as other object names - they must
-     * be unique, etc.
-     *
-     * @var integer
-     */
-    var $id;
-
-    /**
-     * Key-value hash that will be serialized.
-     *
-     * @see getData()
-     * @var array
-     */
-    var $data = array();
-
-    /**
-     * Constructor.
-     *
-     * @param string $name  The name of the group.
-     */
-    function ContactListObject_Group($name)
-    {
-        $this->name = $name;
-    }
-
-    /**
-     * Gets the ID of this object.
-     *
-     * @return string  The object's ID.
-     */
-    function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Gets the name of this object.
-     *
-     * @return string The object name.
-     */
-    function getName()
-    {
-        return $this->name;
-    }
-
-    /**
-     * 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.
-     */
-    function get($attribute)
-    {
-        return isset($this->data[$attribute])
-            ? $this->data[$attribute]
-            : null;
-    }
-
-    /**
-     * Sets one of the attributes of the object.
-     *
-     * @param string $attribute  The attribute to set.
-     * @param mixed $value       The value for $attribute.
-     */
-    function set($attribute, $value)
-    {
-        return PEAR::raiseError(_("Unsupported"));
-    }
-
-    /**
-     * Save group
-     */
-    function save()
-    {
-        return PEAR::raiseError(_("Unsupported"));
-    }
-
-    function removeUser($username, $update = true)
-    {
-        return PEAR::raiseError(_("Unsupported"));
-    }
-    function addUser($username, $update = true)
-    {
-       return PEAR::raiseError(_("Unsupported"));
-    }
-}
diff --git a/framework/Group/Group/hooks.php b/framework/Group/Group/hooks.php
deleted file mode 100644 (file)
index afb7b87..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-<?php
-/**
- * The Group_hooks:: class provides the Horde groups system with the
- * addition of adding support for hook functions to define if a user
- * is in a group.
- *
- * Copyright 2003-2010 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  Jason Rust <jrust@rustyparts.com>
- * @package Horde_Group
- */
-class Group_hooks extends Group {
-
-    var $_hookFunction = false;
-
-    /**
-     * Constructor.
-     */
-    function Group_hooks($params)
-    {
-        parent::Group($params);
-        Horde::loadConfiguration('hooks.php', null, 'horde');
-        $this->_hookFunction = function_exists('_group_hook');
-    }
-
-    /**
-     * Get a list of every group that $user is in.
-     *
-     * @param string  $user          The user to get groups for.
-     * @param boolean $parentGroups  Also return the parents of any groups?
-     *
-     * @return array  An array of all groups the user is in.
-     */
-    function getGroupMemberships($user, $parentGroups = false)
-    {
-        $memberships = parent::getGroupMemberships($user, $parentGroups);
-        if (!$this->_hookFunction) {
-            return $memberships;
-        }
-
-        $groups = $this->listGroups();
-        foreach ($groups as $gid => $groupName) {
-            if (empty($memberships[$gid]) && _group_hook($groupName, $user)) {
-                $memberships += array($gid => $groupName);
-            }
-
-            if ($parentGroups) {
-                $parents = $this->getGroupParentList($gid);
-                if (is_a($parents, 'PEAR_Error')) {
-                    return $parents;
-                }
-
-                $memberships += $parents;
-            }
-        }
-
-        return $memberships;
-    }
-
-    /**
-     * Say if a user is a member of a group or not.
-     *
-     * @param string  $user       The name of the user.
-     * @param integer $gid        The ID of the group.
-     * @param boolean $subgroups  Return true if the user is in any subgroups
-     *                            of $group, also.
-     *
-     * @return boolean
-     */
-    function userIsInGroup($user, $gid, $subgroups = true)
-    {
-        $inGroup = ($this->_hookFunction && _group_hook($this->getGroupName($gid), $user));
-        return ($inGroup || parent::userIsInGroup($user, $gid, $subgroups));
-    }
-
-}
diff --git a/framework/Group/Group/kolab.php b/framework/Group/Group/kolab.php
deleted file mode 100644 (file)
index d901e20..0000000
+++ /dev/null
@@ -1,494 +0,0 @@
-<?php
-
-require_once 'Horde/Group/ldap.php';
-
-/**
- * The Group_kolab class provides a Kolab backend for the Horde groups
- * system.
- *
- * FIXME: A better solution would be to let this class rely on
- *        Horde/Kolab/LDAP.php.
- *
- * Copyright 2005-2010 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  Gunnar Wrobel <wrobel@pardus.de>
- * @package Horde_Group
- */
-class Group_kolab extends Group_ldap {
-
-    /**
-     * A marker for fatal errors
-     */
-    var $_error;
-
-    /**
-     * Constructor.
-     */
-    function Group_kolab($params)
-    {
-        if (!function_exists('ldap_connect')) {
-            $this->_error = PEAR::raiseError(_("The Kolab group driver requires LDAP support."));
-        }
-
-        $this->_params = array();
-        $this->_params['hostspec'] = $GLOBALS['conf']['kolab']['ldap']['server'];
-        $this->_params['basedn'] = $GLOBALS['conf']['kolab']['ldap']['basedn'];
-        $this->_params['binddn'] = $GLOBALS['conf']['kolab']['ldap']['phpdn'];
-        $this->_params['password'] = $GLOBALS['conf']['kolab']['ldap']['phppw'];
-        $this->_params['version'] = 3;
-        $this->_params['gid'] = 'cn';
-        $this->_params['memberuid'] = 'member';
-        $this->_params['attrisdn'] = true;
-        $this->_params['filter_type'] = 'objectclass';
-        $this->_params['objectclass'] = 'kolabGroupOfNames';
-        $this->_params['newgroup_objectclass'] = 'kolabGroupOfNames';
-
-        $this->_filter = 'objectclass=' . $this->_params['objectclass'];
-
-        $this->__wakeup();
-    }
-
-    /**
-     * Initializes the object.
-     */
-    function __wakeup()
-    {
-        foreach (array_keys($this->_groupCache) as $name) {
-            $this->_groupCache[$name]->setGroupOb($this);
-        }
-    }
-
-    /**
-     * Returns the properties that need to be serialized.
-     *
-     * @return array  List of serializable properties.
-     */
-    function __sleep()
-    {
-        $properties = get_object_vars($this);
-        unset($properties['_datatree'], $properties['_ds']);
-        $properties = array_keys($properties);
-        return $properties;
-    }
-
-
-    /**
-     * Returns a new group object.
-     *
-     * @param string $name    The group's name.
-     * @param string $parent  The group's parent's name.
-     *
-     * @return Kolab_Group  A new group object.
-     */
-    function &newGroup($name)
-    {
-        return PEAR::raiseError(_("Unsupported"));
-    }
-
-    /**
-     * Adds a group to the groups system. The group must first be created with
-     * Group::newGroup(), and have any initial users added to it, before this
-     * function is called.
-     *
-     * @param Kolab_Group $group  The new group object.
-     */
-    function addGroup($group)
-    {
-        return PEAR::raiseError(_("Unsupported"));
-    }
-
-    /**
-     * Stores updated data - users, etc. - of a group to the backend system.
-     *
-     * @param Kolab_Group $group  The group to update.
-     */
-    function updateGroup($group)
-    {
-        return PEAR::raiseError(_("Unsupported"));
-    }
-
-    /**
-     * Removes a group from the groups system permanently.
-     *
-     * @param Kolab_Group $group  The group to remove.
-     * @param boolean $force               Force to remove every child.
-     */
-    function removeGroup($group, $force = false)
-    {
-        return PEAR::raiseError(_("Unsupported"));
-    }
-
-    /**
-     * Return a Kolab_Group object corresponding to the given dn, with the
-     * users and other data retrieved appropriately.
-     *
-     * @param string $dn  The dn of the group to retrieve.
-     *
-     * @return Kolab_Group  The requested group.
-     */
-    function &getGroupById($dn)
-    {
-        static $cache = array();
-
-        if (!isset($cache[$dn])) {
-
-            if (is_a($this->_error, 'PEAR_Error')) {
-                return $this->_error;
-            }
-
-            /* Connect to the LDAP server. */
-            $success = $this->_connect();
-            if (is_a($success, 'PEAR_Error')) {
-                return PEAR::raiseError($success->getMessage());
-            }
-
-            $search = @ldap_search($this->_ds, $dn, $this->_filter);
-            if (!$search) {
-                return PEAR::raiseError(_("Could not reach the LDAP server"));
-            }
-
-            $result = @ldap_get_entries($this->_ds, $search);
-            @ldap_close($this->_ds);
-            if (!is_array($result) || (count($result) <= 1)) {
-                return PEAR::raiseError(_("Empty result"));
-            }
-
-            $attributes = array();
-            for ($i = 0; $i < $result[0]['count']; $i++) {
-                if ($result[0][$result[0][$i]]['count'] > 1) {
-                    $attributes[$result[0][$i]] = array();
-                    for ($j = 0; $j < $result[0][$result[0][$i]]['count']; $j++) {
-                        $attributes[$result[0][$i]][] = $result[0][$result[0][$i]][$j];
-                    }
-                } else {
-                    $attributes[$result[0][$i]] = $result[0][$result[0][$i]][0];
-                }
-            }
-            $attributes['dn'] = $result[0]['dn'];
-
-            $group = new Kolab_Group($this->getGroupName($dn));
-            $group->_fromAttributes($attributes);
-            $group->setGroupOb($this);
-            $cache[$dn] = $group;
-        }
-
-        return $cache[$dn];
-    }
-
-
-    /**
-     * Retrieve the ID of the given group.
-     *
-     * NOTE: If given a group name, this function can be unreliable if more
-     * than one group exists with the same name.
-     *
-     * @param mixed $group   LDAP_Group object, or a group name (string)
-     *
-     * @return string  The group's ID.
-     */
-    function getGroupId($group)
-    {
-        static $cache = array();
-
-        if (is_a($group, 'Kolab_Group')) {
-            return $group->getDn();
-        }
-
-        if (!isset($cache[$group])) {
-
-            if (is_a($this->_error, 'PEAR_Error')) {
-                return $this->_error;
-            }
-
-            $this->_connect();
-            $search = @ldap_search($this->_ds, $this->_params['basedn'],
-                                   $this->_params['gid'] . '=' . $group,
-                                   array($this->_params['gid']));
-            if (!$search) {
-                return PEAR::raiseError(_("Could not reach the LDAP server"));
-            }
-
-            $result = @ldap_get_entries($this->_ds, $search);
-            @ldap_close($this->_ds);
-            if (!is_array($result) || (count($result) <= 1)) {
-                return PEAR::raiseError(_("Empty result"));
-            }
-            $cache[$group] = $result[0]['dn'];
-        }
-
-        return $cache[$group];
-    }
-
-    /**
-     * Get a list of the parents of a child group.
-     *
-     * @param string $dn  The fully qualified group dn
-     *
-     * @return array  Nested array of parents
-     */
-    function getGroupParents($dn)
-    {
-        return array();
-    }
-
-    /**
-     * Get the parent of the given group.
-     *
-     * @param string $dn  The dn of the child group.
-     *
-     * @return string  The dn of the parent group.
-     */
-    function getGroupParent($dn)
-    {
-        return null;
-    }
-
-    /**
-     * Get a list of parents all the way up to the root object for the given
-     * group.
-     *
-     * @param string $dn  The dn of the group.
-     *
-     * @return array  A flat list of all of the parents of the given group,
-     *                hashed in $dn => $name format.
-     */
-    function getGroupParentList($dn)
-    {
-        return array();
-    }
-
-    /**
-     * Tries to find a DN for a given kolab mail address.
-     *
-     * @param string $mail  The mail address to search for.
-     *
-     * @return string  The corresponding dn or false.
-     */
-    function dnForMail($mail)
-    {
-        $filter = '(&(objectclass=kolabInetOrgPerson)(mail=' . Horde_Ldap::quote($mail) . '))';
-        $search = @ldap_search($this->_ds, $this->_params['basedn'], $filter);
-        if (!$search) {
-            return PEAR::raiseError(_("Could not reach the LDAP server"));
-        }
-        $dn = @ldap_first_entry($this->_ds, $search);
-        if ($dn) {
-            return ldap_get_dn($this->_ds, $dn);
-        }
-        return PEAR::raiseError(sprintf(_("Error searching for user with the email address \"%s\"!"),
-                                        $mail));
-    }
-
-    /**
-     * Get a list of every group that the given user is a member of.
-     *
-     * @param string  $user          The user to get groups for.
-     * @param boolean $parentGroups  Also return the parents of any groups?
-     *
-     * @return array  An array of all groups the user is in.
-     */
-    function getGroupMemberships($user, $parentGroups = false)
-    {
-        static $cache = array();
-
-        if (empty($cache[$user])) {
-
-            if (is_a($this->_error, 'PEAR_Error')) {
-                return $this->_error;
-            }
-
-            /* Connect to the LDAP server. */
-            $success = $this->_connect();
-            if (is_a($success, 'PEAR_Error')) {
-                return PEAR::raiseError($success->getMessage());
-            }
-
-            $dn = $this->dnForMail($user);
-            if (is_a($dn, 'PEAR_Error')) {
-                return $dn;
-            }
-
-            // Set up search filter
-            $filter = '(' . $this->_params['memberuid'] . '=' . $dn . ')';
-
-            // Perform search
-            $search = @ldap_search($this->_ds, $this->_params['basedn'], $filter);
-            if (!$search) {
-                return PEAR::raiseError(_("Could not reach the LDAP server"));
-            }
-
-            $result = @ldap_get_entries($this->_ds, $search);
-            @ldap_close($this->_ds);
-            if (!is_array($result) || (count($result) <= 1)) {
-                return array();
-            }
-
-            $groups = array();
-            $current_charset = Horde_Nls::getCharset();
-            for ($i = 0; $i < $result['count']; $i++) {
-                $utf8_dn = Horde_String::convertCharset($result[$i]['dn'], 'UTF-8', $current_charset);
-                $groups[$utf8_dn] = $this->getGroupName($utf8_dn);
-            }
-
-            $cache[$user] = $groups;
-        }
-
-        return $cache[$user];
-    }
-
-}
-
-/**
- *
- *
- * @author  Ben Chavet <ben@horde.org>
- * @package Horde_Group
- */
-class Kolab_Group extends LDAP_Group {
-
-    /**
-     * Constructor.
-     *
-     * @param string $name    The name of this group.
-     * @param string $parent  The dn of the parent of this group.
-     */
-    function Kolab_Group($name, $parent = null)
-    {
-        $this->setName($name);
-    }
-
-    /**
-     * Fetch the ID of this group
-     *
-     * @return string The group's ID
-     */
-    function getId()
-    {
-        return $this->getDn();
-    }
-
-    /**
-     * Save any changes to this object to the backend permanently.
-     */
-    function save()
-    {
-        return PEAR::raiseError(_("Unsupported"));
-    }
-
-    /**
-     * Adds a user to this group, and makes sure that the backend is
-     * updated as well.
-     *
-     * @param string $username The user to add.
-     */
-    function addUser($username, $update = true)
-    {
-        return PEAR::raiseError(_("Unsupported"));
-    }
-
-
-    /**
-     * Removes a user from this group, and makes sure that the backend
-     * is updated as well.
-     *
-     * @param string $username The user to remove.
-     */
-    function removeUser($username, $update = true)
-    {
-        return PEAR::raiseError(_("Unsupported"));
-    }
-
-    /**
-     * Get all the users recently added or removed from the group.
-     */
-    function getAuditLog()
-    {
-        return array();
-    }
-
-    /**
-     * Clears the audit log. To be called after group update.
-     */
-    function clearAuditLog()
-    {
-    }
-
-    /**
-     * Sets the name of this object.
-     *
-     * @param string $name  The name to set this object's name to.
-     */
-    function getDn()
-    {
-        return $this->name . ',' . $GLOBALS['conf']['kolab']['ldap']['basedn'];
-    }
-
-    /**
-     * Take in a list of attributes from the backend and map it to our
-     * internal data array.
-     *
-     * @param array $attributes  The list of attributes from the backend.
-     */
-    function _fromAttributes($attributes = array())
-    {
-        $this->data['users'] = array();
-        foreach ($attributes as $key => $value) {
-            if (Horde_String::lower($key) == 'member') {
-                if (is_array($value)) {
-                    foreach ($value as $user) {
-                        $pattern = '/^cn=([^,]+).*$/';
-                        $results = array();
-                        preg_match($pattern, $user, $results);
-                        if (isset($results[1])) {
-                            $user = $results[1];
-                        }
-                        $this->data['users'][$user] = '1';
-                    }
-                } else {
-                    $pattern = '/^cn=([^,]+).*$/';
-                    $results = array();
-                    preg_match($pattern, $value, $results);
-                    if (isset($results[1])) {
-                        $value = $results[1];
-                    }
-                    $this->data['users'][$value] = '1';
-                }
-            } elseif ($key == 'mail') {
-                $this->data['email'] = $value;
-            } else {
-                $this->data[$key] = $value;
-            }
-        }
-    }
-
-    /**
-     * Map this object's attributes from the data array into a format that
-     * can be stored in an LDAP entry.
-     *
-     * @return array  The entry array.
-     */
-    function _toAttributes()
-    {
-        $attributes = array();
-        foreach ($this->data as $key => $value) {
-            if ($key == 'users') {
-                foreach ($value as $user => $membership) {
-                    $user = 'cn=' . $user . ',' . $GLOBALS['conf']['kolab']['ldap']['basedn'];
-                    $attributes['member'][] = $user;
-                }
-            } elseif ($key == 'email') {
-                if (!empty($value)) {
-                    $attributes['mail'] = $value;
-                }
-            } elseif ($key != 'dn' && $key != 'member') {
-                $attributes[$key] = !empty($value) ? $value : ' ';
-            }
-        }
-
-        return $attributes;
-    }
-
-}
diff --git a/framework/Group/Group/ldap.php b/framework/Group/Group/ldap.php
deleted file mode 100644 (file)
index 664539f..0000000
+++ /dev/null
@@ -1,858 +0,0 @@
-<?php
-/**
- * The Group_ldap class provides an LDAP backend for the Horde groups
- * system.
- *
- * Copyright 2005-2010 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  Ben Chavet <ben@horde.org>
- * @package Horde_Group
- */
-class Group_ldap extends Group {
-
-    /**
-     * LDAP connection handle
-     */
-    var $_ds;
-
-    /**
-     * Local copy of the global $conf['group']['params'] array. Simply
-     * for coding convenience.
-     */
-    var $_params;
-
-    /**
-     * Generated LDAP filter based on the config parameters
-     */
-    var $_filter;
-
-    /**
-     * Constructor.
-     */
-    function Group_ldap($params)
-    {
-        $this->_params = $GLOBALS['conf']['group']['params'];
-
-        $this->_params['gid'] = Horde_String::lower($this->_params['gid']);
-        $this->_params['memberuid'] = Horde_String::lower($this->_params['memberuid']);
-        foreach ($this->_params['newgroup_objectclass'] as $key => $val) {
-            $this->_params['newgroup_objectclass'][$key] = Horde_String::lower($val);
-        }
-
-        /* Generate LDAP search filter. */
-        if (!empty($this->_params['filter'])) {
-            $this->_filter = $this->_params['filter'];
-        } elseif (!is_array($this->_params['objectclass'])) {
-            $this->_filter = 'objectclass=' . $this->_params['objectclass'];
-        } else {
-            $this->_filter = '';
-            foreach ($this->_params['objectclass'] as $objectclass) {
-                $this->_filter = '(&' . $this->_filter;
-                $this->_filter .= '(objectclass=' . $objectclass . '))';
-            }
-        }
-
-        $this->_filter = Horde_String::lower($this->_filter);
-    }
-
-    /**
-     * Connects to the LDAP server.
-     *
-     * @return boolean  True or False based on success of connect and bind.
-     */
-    function _connect()
-    {
-        /* Connect to the LDAP server. */
-        $this->_ds = @ldap_connect($this->_params['hostspec']);
-        if (!$this->_ds) {
-            return PEAR::raiseError(_("Could not reach the LDAP server"));
-        }
-
-        if (!ldap_set_option($this->_ds, LDAP_OPT_PROTOCOL_VERSION,
-                             $this->_params['version'])) {
-            Horde::logMessage(
-                sprintf('Set LDAP protocol version to %d failed: [%d] %s',
-                        $this->_params['version'],
-                        ldap_errno($conn),
-                        ldap_error($conn),
-                        __FILE__, __LINE__));
-        }
-
-        /* Start TLS if we're using it. */
-        if (!empty($this->_params['tls'])) {
-            if (!@ldap_start_tls($this->_ds)) {
-                Horde::logMessage(
-                    sprintf('STARTTLS failed: [%d] %s',
-                            @ldap_errno($this->_ds),
-                            @ldap_error($this->_ds)),
-                    'ERR');
-            }
-        }
-
-        if (isset($this->_params['binddn'])) {
-            $bind = @ldap_bind($this->_ds, $this->_params['binddn'],
-                               $this->_params['password']);
-        } else {
-            $bind = @ldap_bind($this->_ds);
-        }
-
-        if (!$bind) {
-            return PEAR::raiseError(_("Could not bind to LDAP server"));
-        }
-
-        return true;
-    }
-
-    /**
-     * Recursively deletes $dn. $this->_ds MUST already be connected.
-     *
-     * @return mixed  True if delete was successful, PEAR_Error otherwise.
-     */
-    function _recursive_delete($dn)
-    {
-        $search = @ldap_list($this->_ds, $dn, 'objectclass=*', array(''));
-        if (!$search) {
-            return PEAR::raiseError(_("Could not reach the LDAP server"));
-        }
-
-        $children = @ldap_get_entries($this->_ds, $search);
-        for ($i = 0; $i < $children['count']; $i++) {
-            $result = $this->_recursive_delete($children[$i]['dn']);
-            if (!$result) {
-                return PEAR::raiseError(sprintf(_("Group_ldap: Unable to delete group \"%s\". This is what the server said: %s"), $this->getName($children[$i]['dn']), @ldap_error($this->_ds)));
-            }
-        }
-
-        $result = @ldap_delete($this->_ds, $dn);
-        if (!$result) {
-            return PEAR::raiseError(sprintf(_("Group_ldap: Unable to delete group \"%s\". This is what the server said: %s"), $dn, @ldap_error($this->_ds)));
-        }
-
-        return $result;
-    }
-
-    /**
-     * Searches existing groups for the highest gidnumber, and returns
-     * one higher.
-     */
-    function _nextGid()
-    {
-        /* Connect to the LDAP server. */
-        $success = $this->_connect();
-        if (is_a($success, 'PEAR_Error')) {
-            return PEAR::raiseError($success->getMessage());
-        }
-
-        $search = @ldap_search($this->_ds, $this->_params['basedn'], $this->_filter);
-        if (!$search) {
-            return PEAR::raiseError(_("Could not reach the LDAP server"));
-        }
-
-        $result = @ldap_get_entries($this->_ds, $search);
-        @ldap_close($this->_ds);
-
-        if (!is_array($result) || (count($result) <= 1)) {
-            return 1;
-        }
-
-        $nextgid = 0;
-        for ($i = 0; $i < $result['count']; $i++) {
-            if ($result[$i]['gidnumber'][0] > $nextgid) {
-                $nextgid = $result[$i]['gidnumber'][0];
-            }
-        }
-
-        return $nextgid + 1;
-    }
-
-    /**
-     * Return a new group object.
-     *
-     * @param string $name    The group's name.
-     * @param string $parent  The group's parent's ID (DN)
-     *
-     * @return LDAP_Group  A new group object.
-     * @throws Horde_Exception
-     */
-    function &newGroup($name, $parent = null)
-    {
-        if (empty($name)) {
-            return PEAR::raiseError(_("Group names must be non-empty"));
-        }
-
-        try {
-            $entry = Horde::callHook('groupldap', array($name, $parent));
-        } catch (Horde_Exception_HookNotSet $e) {
-            // Try this simple default and hope it works.
-            $entry[$this->_params['gid']] = $name;
-            $entry['objectclass'] = $this->_params['newgroup_objectclass'];
-            $entry['gidnumber'] = $this->_nextGid();
-        }
-
-        $group = new LDAP_Group($name, $parent);
-        $group->_fromAttributes($entry);
-        $group->setGroupOb($this);
-        return $group;
-    }
-
-    /**
-     * Return an LDAP_Group object corresponding to the named group, with the
-     * users and other data retrieved appropriately.
-     *
-     * @param string $name  The name of the group to retrieve.
-     *
-     * @return LDAP_Group  The requested group.
-     */
-    function &getGroup($name)
-    {
-        $dn = $this->getGroupId($name);
-        if (is_a($dn, 'PEAR_Error')) {
-            return PEAR::raiseError($dn->getMessage());
-        }
-        $group = &$this->getGroupById($dn);
-        return $group;
-    }
-
-    /**
-     * Return an LDAP_Object object corresponding to the given dn, with the
-     * users and other data retrieved appropriately.
-     *
-     * @param string $dn  The dn of the group to retrieve.
-     *
-     * @return LDAP_Object  The requested group.
-     */
-    function &getGroupById($dn)
-    {
-        static $cache = array();
-
-        if (!isset($cache[$dn])) {
-            /* Connect to the LDAP server. */
-            $success = $this->_connect();
-            if (is_a($success, 'PEAR_Error')) {
-                return PEAR::raiseError($success->getMessage());
-            }
-
-            $search = @ldap_search($this->_ds, $dn, $this->_filter);
-            if (!$search) {
-                return PEAR::raiseError(_("Could not reach the LDAP server"));
-            }
-
-            $result = @ldap_get_entries($this->_ds, $search);
-            @ldap_close($this->_ds);
-            if (!is_array($result) || (count($result) <= 1)) {
-                return PEAR::raiseError(_("Empty result"));
-            }
-
-            $attributes = array();
-            for ($i = 0; $i < $result[0]['count']; $i++) {
-                if ($result[0][$result[0][$i]]['count'] > 1) {
-                    $attributes[$result[0][$i]] = array();
-                    for ($j = 0; $j < $result[0][$result[0][$i]]['count']; $j++) {
-                        $attributes[$result[0][$i]][] = $result[0][$result[0][$i]][$j];
-                    }
-                } else {
-                    $attributes[$result[0][$i]] = $result[0][$result[0][$i]][0];
-                }
-            }
-            $attributes['dn'] = $result[0]['dn'];
-
-            $group = new LDAP_Group($this->getGroupName($dn));
-            $group->_fromAttributes($attributes);
-            $group->setGroupOb($this);
-            $cache[$dn] = $group;
-        }
-
-        return $cache[$dn];
-    }
-
-    /**
-     * Get a globally unique ID for a group.  This really just returns the dn
-     * for the group, but is included for compatibility with the Group class.
-     *
-     * @param LDAP_Object $group  The group.
-     *
-     * @return string  a GUID referring to $group.
-     */
-    function getGUID($group)
-    {
-        return $group->get('dn');
-    }
-
-    /**
-     * Add a group to the groups system.  The group must first be created with
-     * Group_ldap::newGroup(), and have any initial users added to it, before
-     * this function is called.
-     *
-     * @param LDAP_Group $group  The new group object.
-     *
-     * @return mixed  True if successful, PEAR_Error otherwise.
-     */
-    function addGroup($group)
-    {
-        if (!is_a($group, 'DataTreeObject_Group')) {
-            return PEAR::raiseError('Groups must be DataTreeObject_Group objects or extend that class.');
-        }
-
-        /* Connect to the LDAP server. */
-        $success = $this->_connect();
-        if (is_a($success, 'PEAR_Error')) {
-            return PEAR::raiseError($success->getMessage());
-        }
-
-        $dn = $group->get('dn');
-
-        $entry = $group->_toAttributes();
-        $success = @ldap_add($this->_ds, $dn, $entry);
-
-        if (!$success) {
-            return PEAR::raiseError(sprintf(_("Group_ldap: Unable to add group \"%s\". This is what the server said: "), $group->getName()) . @ldap_error($this->_ds));
-        }
-
-        @ldap_close($this->_ds);
-
-        return true;
-    }
-
-    /**
-     * Store updated data - users, etc. - of a group to the backend system.
-     *
-     * @param LDAP_Object $group  The group to update
-     *
-     * @return mixed  True on success, PEAR_Error otherwise.
-     *
-     * @throws Horde_History_Exception
-     * @throws InvalidArgumentException
-     */
-    function updateGroup($group)
-    {
-        if (!is_a($group, 'DataTreeObject_Group')) {
-            return PEAR::raiseError('Groups must be DataTreeObject_Group objects or extend that class.');
-        }
-
-        $entry = $group->_toAttributes();
-
-        /* Connect to the LDAP server. */
-        $success = $this->_connect();
-        if (is_a($success, 'PEAR_Error')) {
-            return PEAR::raiseError($success->getMessage());
-        }
-
-        // Do not attempt to change an LDAP object's objectClasses
-        unset($entry['objectclass']);
-
-        $result = @ldap_modify($this->_ds, $group->getId(), $entry);
-        if (!$result) {
-            return PEAR::raiseError(sprintf(_("Group_ldap: Unable to update group \"%s\". This is what the server said: %s"), $group->getName(), @ldap_error($this->_ds)));
-        }
-
-        @ldap_close($this->_ds);
-
-        /* Log the update of the group users on the history log. */
-        $history = $GLOBALS['injector']->getInstance('Horde_History');
-        $guid = $this->getGUID($group);
-        foreach ($group->getAuditLog() as $userId => $action) {
-            $history->log($guid, array('action' => $action, 'user' => $userId), true);
-        }
-        $group->clearAuditLog();
-
-        /* Log the group modification. */
-        $history->log($guid, array('action' => 'modify'), true);
-        return $result;
-    }
-
-    /**
-     * Remove a group from the groups system permanently.
-     *
-     * @param LDAP_Group $group  The group to remove.
-     * @param boolean $force     Recursively delete children groups if true.
-     *
-     * @return mixed  True on success, PEAR_Error otherwise.
-     */
-    function removeGroup($group, $force = false)
-    {
-        if (!is_a($group, 'DataTreeObject_Group')) {
-            return PEAR::raiseError('Groups must be DataTreeObject_Group objects or extend that class.');
-        }
-
-        $dn = $group->getId();
-
-        /* Connect to the LDAP server. */
-        $success = $this->_connect();
-        if (is_a($success, 'PEAR_Error')) {
-            return PEAR::raiseError($success->getMessage());
-        }
-
-        if ($force) {
-            return $this->_recursive_delete($dn);
-        } else {
-            $result = @ldap_delete($this->_ds, $dn);
-            if (!$result) {
-                return PEAR::raiseError(sprintf(_("Group_ldap: Unable to delete group \"%s\". This is what the server said: %s"), $dn, @ldap_error($this->_ds)));
-            }
-        }
-    }
-
-    /**
-     * Retrieve the name of a group.
-     *
-     * @param string $dn  The dn of the group to retrieve the name for.
-     *
-     * @return string  The group's name.
-     */
-    function getGroupName($dn)
-    {
-        $dn = Horde_String::convertCharset($dn, Horde_Nls::getCharset(), 'UTF-8');
-        $result = @ldap_explode_dn($dn, 1);
-        if ($result === false) {
-            return PEAR::raiseError(_("Invalid group ID passed (bad DN syntax)"));
-        }
-
-        return $result[0];
-    }
-
-    /**
-     * DataTreeObject full names include references to parents, but LDAP does
-     * not have this concept.  This function simply returns the $group
-     * parameter and is included for compatibility with the Group class.
-     *
-     * @param string $group  Group name.
-     *
-     * @return string  $group.
-     */
-    function getGroupShortName($group)
-    {
-        return $group;
-    }
-
-    /**
-     * Retrieve the ID of the given group.
-     *
-     * NOTE: If given a group name, this function can be unreliable if more
-     * than one group exists with the same name.
-     *
-     * @param mixed $group   LDAP_Group object, or a group name (string)
-     *
-     * @return string  The group's ID.
-     */
-    function getGroupId($group)
-    {
-        static $cache = array();
-
-        if (is_a($group, 'LDAP_Group')) {
-            return $group->get('dn');
-        }
-
-        if (!isset($cache[$group])) {
-            $this->_connect();
-            $search = @ldap_search($this->_ds, $this->_params['basedn'],
-                                   $this->_params['gid'] . '=' . $group,
-                                   array($this->_params['gid']));
-            if (!$search) {
-                return PEAR::raiseError(_("Could not reach the LDAP server"));
-            }
-
-            $result = @ldap_get_entries($this->_ds, $search);
-            @ldap_close($this->_ds);
-            if (!is_array($result) || (count($result) <= 1)) {
-                return PEAR::raiseError(_("Empty result"));
-            }
-            $cache[$group] = $result[0]['dn'];
-        }
-
-        return $cache[$group];
-    }
-
-    /**
-     * Check if a group exists in the system.
-     *
-     * @param string $group  The group name to check for.
-     *
-     * @return boolean  True if the group exists, False otherwise.
-     */
-    function exists($group)
-    {
-        static $cache = array();
-
-        if (!isset($cache[$group])) {
-            /* Connect to the LDAP server. */
-            $success = $this->_connect();
-            if (is_a($success, 'PEAR_Error')) {
-                return PEAR::raiseError($success->getMessage());
-            }
-
-            $groupDN = $this->getGroupId($group);
-            $group = $this->getGroupShortName($group);
-
-            $res = @ldap_compare($this->_ds, $groupDN, $this->_params['gid'], $group);
-            if ($res === false) {
-                return PEAR::raiseError(sprintf(_("Internal Error: An attribute must ALWAYS match itself: %s"), @ldap_error($this->_ds)));
-            }
-            // $res is True if the group exists, -1 if not, false never
-            $cache[$group] = ($res === true);
-        }
-
-        return $cache[$group];
-    }
-
-    /**
-     * Get a list of the parents of a child group.
-     *
-     * @param string $dn  The fully qualified group dn
-     *
-     * @return array  Nested array of parents
-     */
-    function getGroupParents($dn)
-    {
-        $parent = $this->getGroupParent($dn);
-        $parents = array(DATATREE_ROOT => 1);
-        while ($parent != DATATREE_ROOT) {
-            $parents = array($parent => $parents);
-            $parent = $this->getGroupParent($parent);
-        }
-        return $parents;
-    }
-
-    /**
-     * Get the parent of the given group.
-     *
-     * @param string $dn  The dn of the child group.
-     *
-     * @return string  The dn of the parent group.
-     */
-    function getGroupParent($dn)
-    {
-        $result = @ldap_explode_dn($dn, 0);
-        if ($result === false) {
-            return PEAR::raiseError(_("Invalid group ID passed (bad DN syntax)"));
-        }
-
-        unset($result['count']);
-        unset($result[0]);
-        $parent_dn = implode(',', $result);
-
-        if (Horde_String::lower($parent_dn) == Horde_String::lower($GLOBALS['conf']['group']['params']['basedn'])) {
-            return DATATREE_ROOT;
-        } else {
-            return $parent_dn;
-        }
-    }
-
-    /**
-     * Get a list of parents all the way up to the root object for the given
-     * group.
-     *
-     * @param string $dn  The dn of the group.
-     *
-     * @return array  A flat list of all of the parents of the given group,
-     *                hashed in $dn => $name format.
-     */
-    function getGroupParentList($dn)
-    {
-        $result = @ldap_explode_dn($dn, 0);
-        if ($result === false) {
-            return PEAR::raiseError(_("Invalid group ID passed (bad DN syntax)"));
-        }
-
-        $num = $result['count'];
-        unset($result['count']);
-        unset($result[0]);
-
-        $count = 0;
-        $parents = array();
-        $parent_dn = implode(',', $result);
-        while ($parent_dn != $this->_params['basedn'] && $count++ != $num) {
-            $parents[$parent_dn] = $this->getGroupName($parent_dn);
-            unset($result[$count]);
-            $parent_dn = implode(',', $result);
-        }
-        $parents[DATATREE_ROOT] = DATATREE_ROOT;
-
-        return $parents;
-    }
-
-    /**
-     * Get a list of every group, in the format dn => groupname.
-     *
-     * @param boolean $refresh  If true, the cached value is ignored and the
-     *                          group list is refreshed from the group backend.
-     *
-     * @return array  dn => groupname hash.
-     */
-    function listGroups($refresh = false)
-    {
-        static $groups;
-
-        if ($refresh || is_null($groups)) {
-            /* Connect to the LDAP server. */
-            $success = $this->_connect();
-            if (is_a($success, 'PEAR_Error')) {
-                return PEAR::raiseError($success->getMessage());
-            }
-
-            $search = @ldap_search($this->_ds, $this->_params['basedn'], $this->_filter, array($this->_params['gid']));
-            if (!$search) {
-                return PEAR::raiseError(_("Could not reach the LDAP server"));
-            }
-
-            @ldap_sort($this->_ds, $search, $this->_params['gid']);
-
-            $result = @ldap_get_entries($this->_ds, $search);
-            @ldap_close($this->_ds);
-            if (!is_array($result) || (count($result) <= 1)) {
-                return array();
-            }
-
-            $groups = array();
-            for ($i = 0; $i < $result['count']; $i++) {
-                $groups[$result[$i]['dn']] = $this->getGroupName($result[$i]['dn']);
-            }
-        }
-
-        return $groups;
-    }
-
-    /**
-     * Get a list of every user that is part of the specified group and any
-     * of its subgroups.
-     *
-     * @param string $dn  The dn of the parent group.
-     *
-     * @return array  The complete user list.
-     */
-    function listAllUsers($dn)
-    {
-        static $cache = array();
-
-        if (!isset($cache[$dn])) {
-            $success = $this->_connect();
-            if (is_a($success, 'PEAR_Error')) {
-                return PEAR::raiseError($success->getMessage());
-            }
-
-            $search = @ldap_search($this->_ds, $dn, $this->_filter);
-            if (!$search) {
-                return PEAR::raiseError(sprintf(_("Could not reach the LDAP server: %s"), @ldap_error($this->_ds)));
-            }
-
-            $result = @ldap_get_entries($this->_ds, $search);
-            @ldap_close($this->_ds);
-            if (!is_array($result) || (count($result) <= 1)) {
-                // Not an error, we just don't have any users in this group.
-                return array();
-            }
-
-            $users = array();
-            for ($i = 0; $i < $result['count']; $i++) {
-                $users = array_merge($users, $this->listUsers($result[$i]['dn']));
-            }
-
-            $cache[$dn] = array_keys(array_flip($users));
-        }
-
-        return $cache[$dn];
-    }
-
-    /**
-     * Get a list of every group that the given user is a member of.
-     *
-     * @param string  $user          The user to get groups for.
-     * @param boolean $parentGroups  Also return the parents of any groups?
-     *
-     * @return array  An array of all groups the user is in.
-     */
-    function getGroupMemberships($user, $parentGroups = false)
-    {
-        static $cache = array();
-
-        if (empty($cache[$user])) {
-            /* Connect to the LDAP server. */
-            $success = $this->_connect();
-            if (is_a($success, 'PEAR_Error')) {
-                return PEAR::raiseError($success->getMessage());
-            }
-
-            // Set up search filter
-            $filter = '(' . $this->_params['memberuid'] . '=';
-            if ($GLOBALS['conf']['group']['params']['attrisdn']) {
-                $filter .= $GLOBALS['conf']['auth']['params']['uid'] . '=';
-            }
-            $filter .= $user;
-            if ($GLOBALS['conf']['group']['params']['attrisdn']) {
-                $filter .= ',' . $GLOBALS['conf']['auth']['params']['basedn'];
-            }
-            $filter .= ')';
-
-            // Perform search
-            $search = @ldap_search($this->_ds, $this->_params['basedn'], $filter);
-            if (!$search) {
-                return PEAR::raiseError(_("Could not reach the LDAP server"));
-            }
-
-            $result = @ldap_get_entries($this->_ds, $search);
-            @ldap_close($this->_ds);
-            if (!is_array($result) || (count($result) <= 1)) {
-                return array();
-            }
-
-            $groups = array();
-            $current_charset = Horde_Nls::getCharset();
-            for ($i = 0; $i < $result['count']; $i++) {
-                $utf8_dn = Horde_String::convertCharset($result[$i]['dn'], 'UTF-8', $current_charset);
-                $groups[$utf8_dn] = $this->getGroupName($utf8_dn);
-            }
-
-            $cache[$user] = $groups;
-        }
-
-        return $cache[$user];
-    }
-
-    /**
-     * Returns the tree depth of the given group, relative to the base dn.
-     * 0 is returned for any object directly below the base dn.
-     *
-     * @param string $dn  The dn of the object.
-     *
-     * @return intenger  The tree depth of the group.
-     */
-    function getLevel($dn)
-    {
-        $base = @ldap_explode_dn($this->_params['basedn'], 0);
-        if ($base === false) {
-            return PEAR::raiseError(_("Invalid basedn configured"));
-        }
-
-        $group = @ldap_explode_dn($dn, 0);
-        if ($group === false) {
-            return PEAR::raiseError(_("Invalid group ID passed (bad DN syntax)"));
-        }
-
-        return $group['count'] - $base['count'] - 1;
-    }
-
-}
-
-/**
- * Extension of the DataTreeObject_Group class for storing group information
- * in an LDAP directory.
- *
- * @author  Ben Chavet <ben@horde.org>
- * @package Horde_Group
- */
-class LDAP_Group extends DataTreeObject_Group {
-
-    /**
-     * Constructor.
-     *
-     * @param string $name    The name of this group.
-     * @param string $parent  The dn of the parent of this group.
-     */
-    function LDAP_Group($name, $parent = null)
-    {
-        parent::DataTreeObject_Group($name);
-        if ($parent) {
-            $this->data['dn'] = Horde_String::lower($GLOBALS['conf']['group']['params']['gid']) . '=' . $name . ',' . $parent;
-        } else {
-            $this->data['dn'] = Horde_String::lower($GLOBALS['conf']['group']['params']['gid']) . '=' . $name .
-                ',' . Horde_String::lower($GLOBALS['conf']['group']['params']['basedn']);
-        }
-    }
-
-    /**
-     * Get a list of every user that is part of this group (and only
-     * this group).
-     *
-     * @return array  The user list.
-     */
-    function listUsers()
-    {
-        return $this->_groupOb->listUsers($this->data['dn']);
-    }
-
-    /**
-     * Get a list of every user that is a member of this group and any of
-     * it's subgroups.
-     *
-     * @return array  The complete user list.
-     */
-    function listAllUsers()
-    {
-        return $this->_groupOb->listAllUsers($this->data['dn']);
-    }
-
-    /**
-     * Take in a list of attributes from the backend and map it to our
-     * internal data array.
-     *
-     * @param array $attributes  The list of attributes from the backend.
-     */
-    function _fromAttributes($attributes = array())
-    {
-        $this->data['users'] = array();
-        foreach ($attributes as $key => $value) {
-            if (Horde_String::lower($key) == Horde_String::lower($GLOBALS['conf']['group']['params']['memberuid'])) {
-                if (is_array($value)) {
-                    foreach ($value as $user) {
-                        if ($GLOBALS['conf']['group']['params']['attrisdn']) {
-                            $pattern = '/^' . $GLOBALS['conf']['auth']['params']['uid'] . '=([^,]+).*$/';
-                            $results = array();
-                            preg_match($pattern, $user, $results);
-                            if (isset($results[1])) {
-                                $user = $results[1];
-                            }
-                        }
-                        $this->data['users'][$user] = '1';
-                    }
-                } else {
-                    if ($GLOBALS['conf']['group']['params']['attrisdn']) {
-                        $pattern = '/^' . $GLOBALS['conf']['auth']['params']['uid'] . '=([^,]+).*$/';
-                        $results = array();
-                        preg_match($pattern, $value, $results);
-                        if (isset($results[1])) {
-                            $value = $results[1];
-                        }
-                    }
-                    $this->data['users'][$value] = '1';
-                }
-            } elseif ($key == 'mail') {
-                $this->data['email'] = $value;
-            } else {
-                $this->data[$key] = $value;
-            }
-        }
-    }
-
-    /**
-     * Map this object's attributes from the data array into a format that
-     * can be stored in an LDAP entry.
-     *
-     * @return array  The entry array.
-     */
-    function _toAttributes()
-    {
-        $attributes = array();
-        foreach ($this->data as $key => $value) {
-            if ($key == 'users') {
-                foreach ($value as $user => $membership) {
-                    if ($GLOBALS['conf']['group']['params']['attrisdn']) {
-                        $user = $GLOBALS['conf']['auth']['params']['uid'] .
-                            '=' . $user . ',' . $GLOBALS['conf']['auth']['params']['basedn'];
-                    }
-                    $attributes[Horde_String::lower($GLOBALS['conf']['group']['params']['memberuid'])][] = $user;
-                }
-            } elseif ($key == 'email') {
-                if (!empty($value)) {
-                    $attributes['mail'] = $value;
-                }
-            } elseif ($key != 'dn' && $key != Horde_String::lower($GLOBALS['conf']['group']['params']['memberuid'])) {
-                $attributes[$key] = !empty($value) ? $value : ' ';
-            }
-        }
-
-        return $attributes;
-    }
-
-}
diff --git a/framework/Group/Group/mock.php b/framework/Group/Group/mock.php
deleted file mode 100644 (file)
index 5c57a1f..0000000
+++ /dev/null
@@ -1,280 +0,0 @@
-<?php
-/**
- * The Group:: class provides the Horde groups system.
- *
- * Copyright 2008-2010 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>
- * @package Horde_Group
- */
-class Group_mock extends Group {
-
-    /**
-     * Constructor.
-     */
-    function Group_mock()
-    {
-    }
-
-    /**
-     * Initializes the object.
-     */
-    function __wakeup()
-    {
-    }
-
-    /**
-     * Returns a new group object.
-     *
-     * @param string $name    The group's name.
-     * @param string $parent  The group's parent's name.
-     *
-     * @return DataTreeObject_Group  A new group object.
-     */
-    function &newGroup($name, $parent = GROUP_ROOT)
-    {
-        return PEAR::raiseError(_("Unsupported"));
-    }
-
-    /**
-     * Returns a DataTreeObject_Group object corresponding to the named group,
-     * with the users and other data retrieved appropriately.
-     *
-     * @param string $name The name of the group to retrieve.
-     */
-    function &getGroup($name)
-    {
-        return PEAR::raiseError(_("Unsupported"));
-    }
-
-    /**
-     * Returns a DataTreeObject_Group object corresponding to the given unique
-     * ID, with the users and other data retrieved appropriately.
-     *
-     * @param integer $cid  The unique ID of the group to retrieve.
-     */
-    function &getGroupById($cid)
-    {
-        return PEAR::raiseError(_("Unsupported"));
-    }
-
-    /**
-     * Adds a group to the groups system. The group must first be created with
-     * Group::newGroup(), and have any initial users added to it, before this
-     * function is called.
-     *
-     * @param DataTreeObject_Group $group  The new group object.
-     */
-    function addGroup($group)
-    {
-        return PEAR::raiseError(_("Unsupported"));
-    }
-
-    /**
-     * Stores updated data - users, etc. - of a group to the backend system.
-     *
-     * @param DataTreeObject_Group $group  The group to update.
-     */
-    function updateGroup($group)
-    {
-        return PEAR::raiseError(_("Unsupported"));
-    }
-
-    /**
-     * Removes a group from the groups system permanently.
-     *
-     * @param DataTreeObject_Group $group  The group to remove.
-     * @param boolean $force               Force to remove every child.
-     */
-    function removeGroup($group, $force = false)
-    {
-        return PEAR::raiseError(_("Unsupported"));
-    }
-
-    /**
-     * Retrieves the name of a group.
-     *
-     * @param integer|DataTreeObject_Group $gid  The id of the group or the
-     *                                           group object to retrieve the
-     *                                           name for.
-     *
-     * @return string  The group's name.
-     */
-    function getGroupName($gid)
-    {
-        return '';
-    }
-
-    /**
-     * Strips all parent references off of the given group name.
-     *
-     * @param string $group  Name of the group.
-     *
-     * @return The name of the group without parents.
-     */
-    function getGroupShortName($group)
-    {
-        return '';
-    }
-
-    /**
-     * Retrieves the ID of a group.
-     *
-     * @param string|DataTreeObject_Group $group  The group name or object to
-     *                                            retrieve the ID for.
-     *
-     * @return integer  The group's ID.
-     */
-    function getGroupId($group)
-    {
-        return '';
-    }
-
-    /**
-     * Check if a group exists in the system.
-     *
-     * @param string $group  The group to check.
-     *
-     * @return boolean  True if the group exists, false otherwise.
-     */
-    function exists($group)
-    {
-        return false;
-    }
-
-    /**
-     * Returns a tree of the parents of a child group.
-     *
-     * @param integer $gid  The id of the child group.
-     *
-     * @return array  The group parents tree, with groupnames as the keys.
-     */
-    function getGroupParents($gid)
-    {
-        return array();
-    }
-
-    /**
-     * Returns the single parent ID of the given group.
-     *
-     * @param integer $gid  The DataTree ID of the child group.
-     *
-     * @return integer  The parent of the given group.
-     */
-    function getGroupParent($gid)
-    {
-        return null;
-    }
-
-    /**
-     * Returns a flat list of the parents of a child group
-     *
-     * @param integer $gid  The id of the group.
-     *
-     * @return array  A flat list of all of the parents of $group, hashed in
-     *                $id => $name format.
-     */
-    function getGroupParentList($gid)
-    {
-        return array();
-    }
-
-    /**
-     * Returns a list of all groups, in the format id => groupname.
-     *
-     * @param boolean $refresh  If true, the cached value is ignored and the
-     *                          group list is refreshed from the group backend.
-     *
-     * @return array  ID => groupname hash.
-     */
-    function listGroups($refresh = false)
-    {
-        return array();
-    }
-
-    /**
-     * Get a list of every user that is a part of this group ONLY.
-     *
-     * @param integer $gid  The ID of the group.
-     *
-     * @return array  The user list.
-     */
-    function listUsers($gid)
-    {
-        return array();
-    }
-
-    /**
-     * Get a list of every user that is part of the specified group
-     * and any of its subgroups.
-     *
-     * @param integer $group  The ID of the parent group.
-     *
-     * @return array  The complete user list.
-     */
-    function listAllUsers($gid)
-    {
-        return array();
-    }
-
-    /**
-     * Get a list of every group that $user is in.
-     *
-     * @param string  $user          The user to get groups for.
-     * @param boolean $parentGroups  Also return the parents of any groups?
-     *
-     * @return array  An array of all groups the user is in.
-     */
-    function getGroupMemberships($user, $parentGroups = false)
-    {
-        return array();
-    }
-
-    /**
-     * Say if a user is a member of a group or not.
-     *
-     * @param string $user        The name of the user.
-     * @param integer $gid        The ID of the group.
-     * @param boolean $subgroups  Return true if the user is in any subgroups
-     *                            of group with ID $gid, also.
-     *
-     * @return boolean
-     */
-    function userIsInGroup($user, $gid, $subgroups = true)
-    {
-        return false;
-    }
-
-    /**
-     * Returns the nesting level of the given group. 0 is returned for any
-     * object directly below GROUP_ROOT.
-     *
-     * @param integer $gid  The ID of the group.
-     *
-     * @return The nesting level of the group.
-     */
-    function getLevel($gid)
-    {
-        return 0;
-    }
-
-    /**
-     * Stores the object in the session cache.
-     */
-    function shutdown()
-    {
-    }
-
-    /**
-     * Returns the properties that need to be serialized.
-     *
-     * @return array  List of serializable properties.
-     */
-    function __sleep()
-    {
-    }
-
-}
diff --git a/framework/Group/Group/sql.php b/framework/Group/Group/sql.php
deleted file mode 100644 (file)
index 442da7b..0000000
+++ /dev/null
@@ -1,891 +0,0 @@
-<?php
-/**
- * The Group:: class provides the Horde groups system.
- *
- * Copyright 1999-2010 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>
- * @package Horde_Group
- */
-class Group_sql extends Group {
-
-    /**
-     * Boolean indicating whether or not we're connected to the SQL server.
-     *
-     * @var boolean
-     */
-    var $_connected = false;
-
-    /**
-     * Handle for the current database connection.
-     *
-     * @var DB
-     */
-    var $_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
-     */
-    var $_write_db;
-
-    /**
-     * Constructor.
-     */
-    function Group_sql($params)
-    {
-        $this->_params = $params;
-    }
-
-    /**
-     * Initializes the object.
-     */
-    function __wakeup()
-    {
-    }
-
-    /**
-     * Returns the properties that need to be serialized.
-     *
-     * @return array  List of serializable properties.
-     */
-    function __sleep()
-    {
-    }
-
-    /**
-     * Stores the object in the session cache.
-     */
-    function shutdown()
-    {
-    }
-
-    /**
-     * Replace all occurences of ':' in an object name with '.'.
-     *
-     * @param string $name  The name of the object.
-     *
-     * @return string  The encoded name.
-     */
-    function encodeName($name)
-    {
-        return str_replace(':', '.', $name);
-    }
-
-    /**
-     * Returns a new group object.
-     *
-     * @param string $name    The group's name.
-     * @param string $parent  The group's parent's name.
-     *
-     * @return SQLObject_Group  A new group object.
-     */
-    function &newGroup($name, $parent = GROUP_ROOT)
-    {
-        if (empty($name)) {
-            return PEAR::raiseError(_("Group names must be non-empty"));
-        }
-
-        if ($parent != GROUP_ROOT) {
-            $name = $this->getGroupName($parent) . ':' . $this->encodeName($name);
-        }
-
-        $group = new SQLObject_Group($name);
-        $group->setGroupOb($this);
-        return $group;
-    }
-
-    /**
-     * Returns a SQLObject_Group object corresponding to the named group,
-     * with the users and other data retrieved appropriately.
-     *
-     * @param string $name The name of the group to retrieve.
-     */
-    function &getGroup($name)
-    {
-        if (!isset($this->_groupCache[$name])) {
-            $this->_connect();
-            $sql = 'SELECT group_uid, group_email FROM horde_groups WHERE group_name = ?';
-            $group = $this->_db->getRow($sql, array($name), DB_FETCHMODE_ASSOC);
-
-            if (is_a($group, 'PEAR_Error')) {
-                return $group;
-            } elseif (empty($group)) {
-                return PEAR::raiseError($name . ' does not exist');
-            }
-
-            $sql = 'SELECT user_uid FROM horde_groups_members '
-                . ' WHERE group_uid = ? ORDER BY user_uid ASC';
-            $users = $this->_db->getCol($sql, 0, array($group['group_uid']));
-            if (is_a($users, 'PEAR_Error')) {
-                return $users;
-            }
-
-            $object = new SQLObject_Group($name);
-            $object->id = $group['group_uid'];
-            $object->data['email'] = $group['group_email'];
-
-            if (!empty($users)) {
-                $object->data['users'] = array_flip($users);
-            }
-
-            $this->_groupCache[$name] = $object;
-            $this->_groupCache[$name]->setGroupOb($this);
-            $this->_groupMap[$this->_groupCache[$name]->getId()] = $name;
-        }
-
-        return $this->_groupCache[$name];
-    }
-
-    /**
-     * Returns a SQLObject_Group object corresponding to the given unique
-     * ID, with the users and other data retrieved appropriately.
-     *
-     * @param integer $cid  The unique ID of the group to retrieve.
-     */
-    function &getGroupById($cid)
-    {
-        if (isset($this->_groupMap[$cid])) {
-            return $this->_groupCache[$this->_groupMap[$cid]];
-        }
-
-        $this->_connect();
-        $sql = 'SELECT group_name, group_email FROM horde_groups WHERE group_uid = ?';
-        $row = $this->_db->getRow($sql, array($cid), DB_FETCHMODE_ASSOC);
-
-        if (is_a($row, 'PEAR_Error')) {
-            return $row;
-        } elseif (empty($row)) {
-            return PEAR::raiseError($cid . ' does not exist');
-        }
-
-        $sql = 'SELECT user_uid FROM horde_groups_members '
-            . ' WHERE group_uid = ? ORDER BY user_uid ASC';
-        $users = $this->_db->getCol($sql, 0, array($cid));
-        if (is_a($users, 'PEAR_Error')) {
-            return $users;
-        }
-
-        $group = new SQLObject_Group($row['group_name']);
-        $group->id = $cid;
-        $group->data['email'] = $row['group_email'];
-
-        if (!empty($users)) {
-            $group->data['users'] = array_flip($users);
-        }
-
-        $group->setGroupOb($this);
-        $name = $group->getName();
-        $this->_groupCache[$name] = &$group;
-        $this->_groupMap[$cid] = $name;
-
-        return $group;
-    }
-
-    /**
-     * Adds a group to the groups system. The group must first be created with
-     * Group::newGroup(), and have any initial users added to it, before this
-     * function is called.
-     *
-     * @param SQLObject_Group $group  The new group object.
-     *
-     * @throws Horde_History_Exception
-     * @throws InvalidArgumentException
-     */
-    function addGroup(&$group)
-    {
-        if (!is_a($group, 'SQLObject_Group')) {
-            return PEAR::raiseError('Groups must be SQLObject_Group objects or extend that class.');
-        }
-
-        $this->_connect();
-        $group->setGroupOb($this);
-        $name = $group->getName();
-
-        $email = isset($group->data['email']) ? $group->data['email'] : '';
-        $group_id = $this->_write_db->nextId('horde_groups');
-        if (is_a($group_id, 'PEAR_Error')) {
-            return $group_id;
-        }
-
-        $group->id = $group_id;
-        $query = 'INSERT INTO horde_groups (group_uid, group_name, group_parents, group_email) VALUES (?, ?, ?, ?)';
-        $result = $this->_write_db->query($query, array($group->id, $name, '', $email));
-        if (is_a($result, 'PEAR_Error')) {
-            return $result;
-        }
-
-        if (!empty($group->data['users'])) {
-            $query = 'INSERT INTO horde_groups_members (group_uid, user_uid)'
-                .' VALUES (' . (int)$group->id . ', ?)';
-            $sth = $this->_write_db->prepare($query);
-            $result = $this->_write_db->executeMultiple($sth, $group->data['users']);
-            if (is_a($result, 'PEAR_Error')) {
-                return $result;
-            }
-        }
-
-        $this->_groupCache[$name] = &$group;
-        $this->_groupMap[$group_id] = $name;
-        if (isset($this->_groupList)) {
-            $this->_groupList[$group_id] = $name;
-        }
-
-        /* Log the addition of the group in the history log. */
-        $GLOBALS['injector']->getInstance('Horde_History')->log($this->getGUID($group), array('action' => 'add'), true);
-
-        return $result;
-    }
-
-    /**
-     * Stores updated data - users, etc. - of a group to the backend system.
-     *
-     * @param SQLObject_Group $group  The group to update.
-     *
-     * @throws Horde_History_Exception
-     * @throws InvalidArgumentException
-     */
-    function updateGroup($group)
-    {
-        if (!is_a($group, 'SQLObject_Group')) {
-            return PEAR::raiseError('Groups must be SQLObject_Group objects or extend that class.');
-        }
-
-        $this->_connect();
-
-        $query = 'UPDATE horde_groups SET group_email = ? WHERE group_uid = ?';
-        $result = $this->_write_db->query($query, array($this->data['email'], $this->id));
-        if (is_a($result, 'PEAR_Error')) {
-            return $result;
-        }
-
-        $query = 'DELETE FROM horde_groups_members WHERE group_uid = ?';
-        $result = $this->_write_db->query($query, array($this->id));
-        if (is_a($result, 'PEAR_Error')) {
-            return $result;
-        }
-
-        $query = 'INSERT INTO horde_groups_members (group_uid, user_uid)'
-            .' VALUES (' . (int)$this->id . ', ?)';
-        $sth = $this->_write_db->prepare($query);
-        $result = $this->_groupOb->_write_db->executeMultiple($sth, $this->data['users']);
-        if (is_a($result, 'PEAR_Error')) {
-            return $result;
-        }
-
-        $this->_groupCache[$group->getName()] = &$group;
-
-        /* Log the update of the group users on the history log. */
-        $history = $GLOBALS['injector']->getInstance('Horde_History');
-        $guid = $this->getGUID($group);
-        foreach ($group->getAuditLog() as $userId => $action) {
-            $history->log($guid, array('action' => $action, 'user' => $userId), true);
-        }
-
-        $group->clearAuditLog();
-
-        /* Log the group modification. */
-        $history->log($guid, array('action' => 'modify'), true);
-        return $result;
-    }
-
-    /**
-     * Removes a group from the groups system permanently.
-     *
-     * @param SQLObject_Group $group  The group to remove.
-     * @param boolean $force               Force to remove every child.
-     *
-     * @throws Horde_History_Exception
-     * @throws InvalidArgumentException
-     */
-    function removeGroup($group, $force = false)
-    {
-        if (!is_a($group, 'SQLObject_Group')) {
-            return PEAR::raiseError('Groups must be SQLObject_Group objects or extend that class.');
-        }
-
-        $this->_connect();
-        $id = $group->getId();
-        $name = $group->getName();
-        unset($this->_groupMap[$id]);
-        if (isset($this->_groupList)) {
-            unset($this->_groupList[$id]);
-        }
-        unset($this->_groupCache[$name]);
-
-        $GLOBALS['injector']->getInstance('Horde_History')->log($this->getGUID($group), array('action' => 'delete'), true);
-
-        $query = 'DELETE FROM horde_groups_members WHERE group_uid = ?';
-        $result = $this->_write_db->query($query, array($id));
-        if (is_a($result, 'PEAR_Error')) {
-            return $result;
-        }
-
-        $query = 'DELETE FROM horde_groups WHERE group_uid = ?';
-        $result = $this->_write_db->query($query, array($id));
-        if (!$force || is_a($result, 'PEAR_Error')) {
-            return $result;
-        }
-
-        $query = 'DELETE FROM horde_groups WHERE group_name LIKE ?';
-        return $this->_write_db->query($query, array($name . ':%'));
-    }
-
-    /**
-     * Retrieves the name of a group.
-     *
-     * @param integer|SQLObject_Group $gid  The id of the group or the
-     *                                           group object to retrieve the
-     *                                           name for.
-     *
-     * @return string  The group's name.
-     */
-    function getGroupName($gid)
-    {
-        if (is_a($gid, 'SQLObject_Group')) {
-            $gid = $gid->getId();
-        }
-
-        if (isset($this->_groupMap[$gid])) {
-            return $this->_groupMap[$gid];
-        }
-        if (isset($this->_groupList[$gid])) {
-            return $this->_groupList[$gid];
-        }
-
-        $this->_connect();
-        $query = 'SELECT group_name FROM horde_groups WHERE group_uid = ?';
-        return $this->_db->getOne($query, $gid);
-    }
-
-    /**
-     * Strips all parent references off of the given group name.
-     *
-     * @param string $group  Name of the group.
-     *
-     * @return The name of the group without parents.
-     */
-    function getGroupShortName($group)
-    {
-        /* If there are several components to the name, explode and get the
-         * last one, otherwise just return the name. */
-        if (strpos($group, ':') !== false) {
-            $name = explode(':', $group);
-            return array_pop($name);
-        }
-
-        return $group;
-    }
-
-    /**
-     * Retrieves the ID of a group.
-     *
-     * @param string|SQLObject_Group $group  The group name or object to
-     *                                            retrieve the ID for.
-     *
-     * @return integer  The group's ID.
-     */
-    function getGroupId($group)
-    {
-        if (is_a($group, 'SQLObject_Group')) {
-            return $group->getId();
-        }
-
-        $id = array_search($group, $this->_groupMap);
-        if ($id !== false) {
-            return $id;
-        }
-        if (isset($this->_groupList)) {
-            $id = array_search($group, $this->_groupList);
-            if ($id !== false) {
-                return $id;
-            }
-        }
-
-        $this->_connect();
-        $query = 'SELECT group_uid FROM horde_groups WHERE group_name = ?';
-        return $this->_db->getOne($query, $group);
-    }
-
-    /**
-     * Check if a group exists in the system.
-     *
-     * @param string $group  The group to check.
-     *
-     * @return boolean  True if the group exists, false otherwise.
-     */
-    function exists($group)
-    {
-        if (isset($this->_groupCache[$group]) ||
-            (isset($this->_groupList) &&
-             array_search($group, $this->_groupList) !== false)) {
-            return true;
-        }
-
-        $this->_connect();
-        $query = 'SELECT COUNT(*) FROM horde_groups WHERE group_name = ?';
-        return (bool)$this->_db->getOne($query, $group);
-    }
-
-    /**
-     * Returns a tree of the parents of a child group.
-     *
-     * @param integer $gid  The id of the child group.
-     *
-     * @return array  The group parents tree, with groupnames as the keys.
-     */
-    function getGroupParents($gid)
-    {
-        if (!isset($this->_parentTree[$gid])) {
-            $name = $this->getGroupName($gid);
-            $this->_connect();
-            $parents = $this->_getGroupParents($name);
-            if (is_a($parents, 'PEAR_Error')) {
-                return $parents;
-            }
-
-            $this->_parentTree[$gid] = $parents;
-        }
-
-        return $this->_parentTree[$gid];
-    }
-
-    /**
-     * 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.
-     */
-    function _getGroupParents($child)
-    {
-        if (($pos = strrpos($child, ':')) !== false) {
-            $child = substr($child, 0, $pos);
-        }
-
-        return $this->_getParents($child);
-    }
-
-    /**
-     */
-    function _getParents($parents)
-    {
-        $mother = array();
-        if (!empty($parents)) {
-            $pname = $parents;
-            $parents = substr($parents, 0, strrpos($parents, ':'));
-            $mother[$pname] = $this->_getParents($parents);
-        } else {
-            return array(GROUP_ROOT => true);
-        }
-
-        return $mother;
-    }
-
-    /**
-     * Returns the single parent ID of the given group.
-     *
-     * @param integer $gid  The ID of the child group.
-     *
-     * @return integer  The parent of the given group.
-     */
-    function getGroupParent($gid)
-    {
-        if (!isset($this->_groupParents[$gid])) {
-            $this->_connect();
-
-            $name = $this->getGroupName($gid);
-            if (is_a($name, 'PEAR_Error')) {
-                return $name;
-            }
-
-            if (($pos = strrpos($name, ':')) !== false) {
-                $this->_groupParents[$gid] = $this->getGroupId(substr($name, 0, $pos));
-            } else {
-                $this->_groupParents[$gid] = GROUP_ROOT;
-            }
-        }
-
-        return $this->_groupParents[$gid];
-    }
-
-    /**
-     * Returns a flat list of the parents of a child group
-     *
-     * @param integer $gid  The id of the group.
-     *
-     * @return array  A flat list of all of the parents of $group, hashed in
-     *                $id => $name format.
-     */
-    function getGroupParentList($gid)
-    {
-        if (!isset($this->_groupParentList[$gid])) {
-            $name = $this->getGroupName($gid);
-            $pos = strpos($name, ':');
-            if ($pos == false) {
-                $this->_groupParentList[$gid] = array();
-                return $this->_groupParentList[$gid];
-            }
-
-            $parents = array();
-            while ($pos) {
-                $name = substr($name, 0, $pos);
-                $parents[] = $name;
-                $pos = strpos($name, ':');
-            }
-
-            $query = 'SELECT group_uid, group_name FROM horde_groups '
-                . ' WHERE group_name IN (' . str_repeat('?, ', count($parents) - 1) . '?) ';
-            $parents = $this->_db->getAssoc($query, false, $parents);
-            if (is_a($parents, 'PEAR_Error')) {
-                return $parents;
-            }
-
-            $this->_groupParentList[$gid] = $parents;
-        }
-
-        return $this->_groupParentList[$gid];
-    }
-
-    /**
-     * Returns a flat list of the parents of a child group
-     *
-     * @param integer $gid  The id of the group.
-     *
-     * @return array  A flat list of all of the parents of $group, hashed in
-     *                $id => $name format.
-     */
-    function _getGroupParentNameList($name)
-    {
-        $parents = array();
-
-        while ($pos) {
-            $name = substr($name, 0, $pos);
-            $parents[] = $name;
-            $pos = strpos($name, ':');
-        }
-
-        return $parents;
-    }
-
-    /**
-     * Returns a list of all groups, in the format id => groupname.
-     *
-     * @param boolean $refresh  If true, the cached value is ignored and the
-     *                          group list is refreshed from the group backend.
-     *
-     * @return array  ID => groupname hash.
-     */
-    function listGroups($refresh = false)
-    {
-        if ($refresh || !isset($this->_groupList)) {
-            $this->_connect();
-            $sql = 'SELECT group_uid, group_name FROM horde_groups ORDER BY group_uid';
-            $this->_groupList = $this->_db->getAssoc($sql);
-        }
-
-        return $this->_groupList;
-    }
-
-    /**
-     * Get a list of every user that is part of the specified group
-     * and any of its subgroups.
-     *
-     * @param integer $group  The ID of the parent group.
-     *
-     * @return array  The complete user list.
-     */
-    function listAllUsers($gid)
-    {
-        if (!isset($this->_subGroups[$gid])) {
-            // Get a list of every group that is a sub-group of $group.
-            $name = $this->getGroupName($gid);
-            $query = 'SELECT group_uid FROM horde_groups WHERE group_name LIKE ?';
-            $parents = $this->_db->getCol($query, 0, array($name .  ':%'));
-            $this->_subGroups[$gid] = $parents;
-            $this->_subGroups[$gid][] = $gid;
-        }
-
-        $users = array();
-        foreach ($this->_subGroups[$gid] as $groupId) {
-            $users = array_merge($users, $this->listUsers($groupId));
-        }
-
-        return array_values(array_flip(array_flip($users)));
-    }
-
-    /**
-     * Get a list of every group that $user is in.
-     *
-     * @param string  $user          The user to get groups for.
-     * @param boolean $parentGroups  Also return the parents of any groups?
-     *
-     * @return array  An array of all groups the user is in.
-     */
-    function getGroupMemberships($user, $parentGroups = false)
-    {
-        if (isset($_SESSION['horde']['groups']['m'][$user][$parentGroups])) {
-            return $_SESSION['horde']['groups']['m'][$user][$parentGroups];
-        }
-
-        $this->_connect();
-
-        $sql = 'SELECT g.group_uid AS group_uid, g.group_name AS group_name FROM horde_groups g, horde_groups_members m '
-            . ' WHERE m.user_uid = ? AND g.group_uid = m.group_uid ORDER BY g.group_name';
-        $result = $this->_db->query($sql, $user);
-        if (is_a($result, 'PEAR_Error')) {
-            return $result;
-        }
-
-        $groups = array();
-        while ($row = $result->fetchRow(DB_FETCHMODE_ASSOC)) {
-            $groups[(int)$row['group_uid']] = $this->getGroupShortName($row['group_name']);
-        }
-
-        if ($parentGroups) {
-            foreach ($groups as $id => $g) {
-                $parents = $this->getGroupParentList($id);
-                if (is_a($parents, 'PEAR_Error')) {
-                    return $parents;
-                }
-                $groups += $parents;
-            }
-        }
-
-        $_SESSION['horde']['groups']['m'][$user][$parentGroups] = $groups;
-        return $groups;
-    }
-
-    /**
-     * Say if a user is a member of a group or not.
-     *
-     * @param string $user        The name of the user.
-     * @param integer $gid        The ID of the group.
-     * @param boolean $subgroups  Return true if the user is in any subgroups
-     *                            of group with ID $gid, also.
-     *
-     * @return boolean
-     */
-    function userIsInGroup($user, $gid, $subgroups = true)
-    {
-        if (isset($_SESSION['horde']['groups']['i'][$user][$subgroups][$gid])) {
-            return $_SESSION['horde']['groups']['i'][$user][$subgroups][$gid];
-        }
-
-        if ($subgroups) {
-            $groups = $this->getGroupMemberships($user, true);
-            if (is_a($groups, 'PEAR_Error')) {
-                Horde::logMessage($groups, 'ERR');
-                return false;
-            }
-
-            $result = !empty($groups[$gid]);
-        } else {
-            $this->_connect();
-            $query = 'SELECT COUNT(*) FROM horde_groups_members WHERE group_uid = ? AND user_uid = ?';
-            $result = $this->_db->getOne($query, array($gid, $user));
-
-        }
-
-        $_SESSION['horde']['groups']['i'][$user][$subgroups][$gid] = (bool)$result;
-        return (bool)$result;
-    }
-
-    /**
-     * Attempts to open a persistent connection to the sql server.
-     *
-     * @return boolean  True on success.
-     * @throws Horde_Exception
-     */
-    function _connect()
-    {
-        if ($this->_connected) {
-            return true;
-        }
-        if (!isset($this->_params['database'])) {
-            $this->_params['database'] = '';
-        }
-        if (!isset($this->_params['username'])) {
-            $this->_params['username'] = '';
-        }
-        if (!isset($this->_params['hostspec'])) {
-            $this->_params['hostspec'] = '';
-        }
-
-        /* Connect to the sql server using the supplied parameters. */
-        require_once 'DB.php';
-        $this->_write_db = DB::connect($this->_params,
-                                       array('persistent' => !empty($this->_params['persistent']),
-                                             'ssl' => !empty($this->_params['ssl'])));
-        if (is_a($this->_write_db, 'PEAR_Error')) {
-            throw new Horde_Exception_Prior($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);
-        }
-
-        /* Check if we need to set up the read DB connection seperately. */
-        if (!empty($this->_params['splitread'])) {
-            $params = array_merge($this->_params, $this->_params['read']);
-            $this->_db = DB::connect($params,
-                                     array('persistent' => !empty($params['persistent']),
-                                           'ssl' => !empty($params['ssl'])));
-            if (is_a($this->_db, 'PEAR_Error')) {
-                throw new Horde_Exception_Prior($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);
-            }
-        } else {
-            /* Default to the same DB handle for the writer too. */
-            $this->_db = $this->_write_db;
-        }
-
-        $this->_connected = true;
-        return true;
-    }
-
-}
-
-/**
- * Extension of the SQLObject class for storing Group information
- * in the Categories driver. If you want to store specialized Group
- * information, you should extend this class instead of extending
- * SQLObject directly.
- *
- * @author  Duck <duck@obala.net>
- * @package Horde_Group
- */
-class SQLObject_Group extends DataTreeObject_Group {
-
-    /**
-     * The unique name of this object.
-     * These names have the same requirements as other object names - they must
-     * be unique, etc.
-     *
-     * @var string
-     */
-    var $name;
-
-    /**
-     * The unique name of this object.
-     * These names have the same requirements as other object names - they must
-     * be unique, etc.
-     *
-     * @var integer
-     */
-    var $id;
-
-    /**
-     * Key-value hash that will be serialized.
-     *
-     * @see getData()
-     * @var array
-     */
-    var $data = array();
-
-    /**
-     * The SQLObject_Group constructor. Just makes sure to call
-     * the parent constructor so that the group's name is set
-     * properly.
-     *
-     * @param string $name  The name of the group.
-     */
-    function SQLObject_Group($name)
-    {
-        $this->name = $name;
-    }
-
-    /**
-     * Gets the ID of this object.
-     *
-     * @return string  The object's ID.
-     */
-    function getId()
-    {
-        return $this->id;
-    }
-
-    /**
-     * Gets the name of this object.
-     *
-     * @return string The object name.
-     */
-    function getName()
-    {
-        return $this->name;
-    }
-
-    /**
-     * 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.
-     */
-    function get($attribute)
-    {
-        return isset($this->data[$attribute])
-            ? $this->data[$attribute]
-            : null;
-    }
-
-    /**
-     * Sets one of the attributes of the object.
-     *
-     * @param string $attribute  The attribute to set.
-     * @param mixed $value       The value for $attribute.
-     */
-    function set($attribute, $value)
-    {
-        $this->data[$attribute] = $value;
-    }
-
-    /**
-     * Save group
-     */
-    function save()
-    {
-        if (isset($this->data['email'])) {
-            $query = 'UPDATE horde_groups SET group_email = ? WHERE group_uid = ?';
-            $result = $this->_groupOb->_write_db->query($query, array($this->data['email'], $this->id));
-            if (is_a($result, 'PEAR_Error')) {
-                return $result;
-            }
-        }
-
-        $query = 'DELETE FROM horde_groups_members WHERE group_uid = ?';
-        $result = $this->_groupOb->_write_db->query($query, array($this->id));
-        if (is_a($result, 'PEAR_Error')) {
-            return $result;
-        }
-
-        if (!empty($this->data['users'])) {
-            $query = 'INSERT INTO horde_groups_members (group_uid, user_uid)'
-                .' VALUES (' . $this->_groupOb->_write_db->quote($this->id) . ', ?)';
-            $sth = $this->_groupOb->_write_db->prepare($query);
-            $result = $this->_groupOb->_write_db->executeMultiple($sth, array_keys($this->data['users']));
-            if (is_a($result, 'PEAR_Error')) {
-                return $result;
-            }
-        }
-    }
-
-}
diff --git a/framework/Group/lib/Horde/Group.php b/framework/Group/lib/Horde/Group.php
new file mode 100644 (file)
index 0000000..7f7d73f
--- /dev/null
@@ -0,0 +1,704 @@
+<?php
+/**
+ * The Horde_Group:: class provides the Horde groups system.
+ *
+ * Copyright 1999-2010 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   Stephane Huther <shuther1@free.fr>
+ * @author   Chuck Hagenbuch <chuck@horde.org>
+ * @category Horde
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Group
+ */
+class Horde_Group
+{
+    /** The parent Group node */
+    const ROOT = -1;
+
+    /**
+     * Group driver parameters
+     *
+     * @var array
+     */
+    protected $_params;
+
+    /**
+     * Pointer to a DataTree instance to manage the different groups.
+     *
+     * @var DataTree
+     */
+    protected $_datatree;
+
+    /**
+     * Cache of previously retrieved group objects.
+     *
+     * @var array
+     */
+    protected $_groupCache = array();
+
+    /**
+     * Id-name-map of already cached group objects.
+     *
+     * @var array
+     */
+    protected $_groupMap = array();
+
+    /**
+     * Id-name-hash of all existing groups.
+     *
+     * @var array
+     */
+    protected $_groupList;
+
+    /**
+     * List of sub groups.
+     *
+     * @see listAllUsers()
+     * @var array
+     */
+    protected $_subGroups = array();
+
+    /**
+     * Cache of parent groups.
+     *
+     * This is an array with group IDs as keys and the integer group id of the
+     * direct parent as values.
+     *
+     * @see getGroupParent()
+     * @var array
+     */
+    protected $_groupParents = array();
+
+    /**
+     * Cache of parent group trees.
+     *
+     * This is an array with group IDs as keys and id-name-hashes of all
+     * parents as values.
+     *
+     * @see getGroupParentList()
+     * @var array
+     */
+    protected $_groupParentList = array();
+
+    /**
+     * Cache of parents tree.
+     *
+     * @see getGroupParents()
+     * @var array
+     */
+    protected $_parentTree = array();
+
+    /**
+     * Hash of groups of certain users.
+     *
+     * @see getGroupMemberShips()
+     * @var array
+     */
+    protected $_userGroups;
+
+    /**
+     * Attempts to return a concrete Group instance based on $driver.
+     *
+     * @param mixed $driver  The type of concrete Group subclass to return.
+     * @param array $params  A hash containing any additional configuration or
+     *                       connection parameters a subclass might need.
+     *
+     * @return Horde_Group  The newly created concrete Group instance.
+     * @throws Horde_Group_Exception
+     */
+    static public function factory($driver = '', $params = null)
+    {
+        if (is_null($params)) {
+            $params = Horde::getDriverConfig('group', $driver);
+        }
+
+        $class = self::_loadDriver($driver);
+        if (class_exists($class)) {
+            return new $class($params);
+        }
+
+        throw new Horde_Group_Exception('Class definition of ' . $class . ' not found.');
+    }
+
+    /**
+     * Attempts to return a reference to a concrete Group instance.
+     * It will only create a new instance if no Group instance
+     * currently exists.
+     *
+     * @return Group  The concrete Group reference, or false on an error.
+     */
+    public static function singleton()
+    {
+        static $group;
+
+        if (isset($group)) {
+            return $group;
+        }
+
+        $group_driver = null;
+        $group_params = null;
+        $auth = $GLOBALS['injector']->getInstance('Horde_Auth')->getOb();
+        if ($auth->hasCapability('groups')) {
+            $group_driver = $auth->getDriver();
+            $group_params = $auth;
+        } elseif (!empty($GLOBALS['conf']['group']['driver']) &&
+                  $GLOBALS['conf']['group']['driver'] != 'datatree') {
+            $group_driver = $GLOBALS['conf']['group']['driver'];
+            $group_params = Horde::getDriverConfig('group', $group_driver);
+        }
+
+        self::_loadDriver($group_driver);
+
+        $group = null;
+        if (!empty($GLOBALS['conf']['group']['cache'])) {
+            $session = new Horde_SessionObjects();
+            $group = $session->query('horde_group');
+        }
+
+        if (!$group) {
+            $group = self::factory($group_driver, $group_params);
+        }
+
+        if (!empty($GLOBALS['conf']['group']['cache'])) {
+            register_shutdown_function(array($group, 'shutdown'));
+        }
+
+        return $group;
+    }
+
+    /**
+     */
+    protected static function _loadDriver($driver)
+    {
+        if (!$driver) {
+            $class = __CLASS__;
+        } else {
+            $driver = ucfirst(strtolower(basename($driver)));
+            $class = __CLASS__ . '_' . $driver;
+            if (!class_exists($class)) {
+            }
+        }
+
+        return $class;
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param array $params  Configuration parameters.
+     */
+    public function __construct(array $params = array())
+    {
+        $this->_params = $params;
+        $this->__wakeup();
+    }
+
+    /**
+     * Stores the object in the session cache.
+     */
+    public function shutdown()
+    {
+        $GLOBALS['injector']->getInstance('Horde_SessionObjects')->overwrite('horde_group', $this, false);
+    }
+
+    /**
+     * Returns the properties that need to be serialized.
+     *
+     * @return array  List of serializable properties.
+     */
+    public function __sleep()
+    {
+        return array_diff(array_keys(get_class_vars(__CLASS__)), array('_datatree'));
+    }
+    /**
+     * Initializes the object.
+     *
+     * @throws Horde_Group_Exception
+     */
+    public function __wakeup()
+    {
+        global $conf;
+
+        if (empty($conf['datatree']['driver'])) {
+            throw new Horde_Group_Exception('You must configure a DataTree backend to use Groups.');
+        }
+
+        $driver = $conf['datatree']['driver'];
+        $this->_datatree = DataTree::singleton($driver, array_merge(Horde::getDriverConfig('datatree', $driver), array('group' => 'horde.groups')));
+
+        foreach (array_keys($this->_groupCache) as $name) {
+            $this->_groupCache[$name]->setGroupOb($this);
+            $this->_groupCache[$name]->setDataTree($this->_datatree);
+        }
+    }
+
+    /**
+     * Returns a new group object.
+     *
+     * @param string $name    The group's name.
+     * @param string $parent  The group's parent's name.
+     *
+     * @return Horde_Group_DataTreeObject  A new group object.
+     * @throws Horde_Group_Exception
+     */
+    public function newGroup(string $name, $parent = self::ROOT)
+    {
+        if ($parent != self::ROOT) {
+            $name = $this->getGroupName($parent) . ':' . DataTree::encodeName($name);
+        }
+
+        $group = new Horde_Group_DataTreeObject($name);
+        $group->setGroupOb($this);
+
+        return $group;
+    }
+
+    /**
+     * Returns a group object corresponding to the named group, with the users
+     * and other data retrieved appropriately.
+     *
+     * @param string $name The name of the group to retrieve.
+     *
+     * @return Horde_Group_DataTreeObject  Group object.
+     */
+    public function getGroup($name)
+    {
+        if (!isset($this->_groupCache[$name])) {
+            $this->_groupCache[$name] = $this->_datatree->getObject($name, 'Horde_Group_DataTreeObject');
+            if (!($this->_groupCache[$name] instanceof  PEAR_Error)) {
+                $this->_groupCache[$name]->setGroupOb($this);
+                $this->_groupMap[$this->_groupCache[$name]->getId()] = $name;
+            }
+        }
+
+        return $this->_groupCache[$name];
+    }
+
+    /**
+     * Returns a group object corresponding to the given unique ID, with the
+     * users and other data retrieved appropriately.
+     *
+     * @param integer $cid  The unique ID of the group to retrieve.
+     *
+     * @return Horde_Group_DataTreeObject  Group object.
+     */
+    public function getGroupById($cid)
+    {
+        if (isset($this->_groupMap[$cid])) {
+            $group = $this->_groupCache[$this->_groupMap[$cid]];
+        } else {
+            $group = $this->_datatree->getObjectById($cid, 'Horde_Group_DataTreeObject');
+            if (!($group instanceof PEAR_Error)) {
+                $group->setGroupOb($this);
+                $name = $group->getName();
+                $this->_groupCache[$name] = &$group;
+                $this->_groupMap[$cid] = $name;
+            }
+        }
+
+        return $group;
+    }
+
+    /**
+     * Returns a globally unique ID for a group.
+     *
+     * @param DataTreeObject_Group $group  The group.
+     *
+     * @return string  A GUID referring to $group.
+     */
+    public function getGUID($group)
+    {
+        return 'horde:group:' . $this->getGroupId($group);
+    }
+
+    /**
+     * Adds a group to the groups system. The group must first be created with
+     * newGroup(), and have any initial users added to it, before this
+     * function is called.
+     *
+     * @param Horde_Group_DataTreeObject $group  The new group object.
+     *
+     * @throws Horde_Group_Exception
+     * @throws Horde_History_Exception
+     * @throws InvalidArgumentException
+     */
+    public function addGroup(Horde_Group_DataTreeObject $group)
+    {
+        $result = $this->_datatree->add($group);
+        if ($result instanceof PEAR_Error) {
+            throw new Horde_Group_Exception($result);
+        }
+
+        $id = $group->getId();
+        $name = $group->getName();
+        $this->_groupCache[$name] = &$group;
+        $this->_groupMap[$id] = $name;
+        if (isset($this->_groupList)) {
+            $this->_groupList[$id] = $name;
+        }
+
+        /* Log the addition of the group in the history log. */
+        $GLOBALS['injector']->getInstance('Horde_History')->log($this->getGUID($group), array('action' => 'add'), true);
+
+        return $result;
+    }
+
+    /**
+     * Stores updated data - users, etc. - of a group to the backend system.
+     *
+     * @param Horde_Group_DataTreeObject $group  The group to update.
+     *
+     * @throws Horde_History_Exception
+     * @throws InvalidArgumentException
+     */
+    public function updateGroup(Horde_Group_DataTreeObject $group)
+    {
+        $result = $this->_datatree->updateData($group);
+        if ($result instanceof PEAR_Error) {
+            return $result;
+        }
+
+        $this->_groupCache[$group->getName()] = &$group;
+
+        /* Log the update of the group users on the history log. */
+        $history = $GLOBALS['injector']->getInstance('Horde_History');
+        $guid = $this->getGUID($group);
+        foreach ($group->getAuditLog() as $userId => $action) {
+            $history->log($guid, array('action' => $action, 'user' => $userId), true);
+        }
+        $group->clearAuditLog();
+
+        /* Log the group modification. */
+        $history->log($guid, array('action' => 'modify'), true);
+
+        return $result;
+    }
+
+    /**
+     * Removes a group from the groups system permanently.
+     *
+     * @param Horde_Group_DataTreeObject $group  The group to remove.
+     * @param boolean $force                     Force to remove every child?
+     *
+     * @throws Horde_History_Exception
+     * @throws InvalidArgumentException
+     */
+    public function removeGroup(Horde_Group_DataTreeObject $group,
+                                $force = false)
+    {
+        $id = $group->getId();
+        unset($this->_groupMap[$id]);
+        if (isset($this->_groupList)) {
+            unset($this->_groupList[$id]);
+        }
+        unset($this->_groupCache[$group->getName()]);
+
+        $GLOBALS['injector']->getInstance('Horde_History')->log($this->getGUID($group), array('action' => 'delete'), true);
+
+        return $this->_datatree->remove($group, $force);
+    }
+
+    /**
+     * Retrieves the name of a group.
+     *
+     * @param integer|Horde_Group_DataTreeObject $gid  The id of the group or
+     *                                                 the group object to
+     *                                                 retrieve the name for.
+     *
+     * @return string  The group's name.
+     */
+    public function getGroupName($gid)
+    {
+        if ($gid instanceof Horde_Group_DataTreeObject) {
+            $gid = $gid->getId();
+        }
+
+        if (isset($this->_groupMap[$gid])) {
+            return $this->_groupMap[$gid];
+        }
+        if (isset($this->_groupList[$gid])) {
+            return $this->_groupList[$gid];
+        }
+
+        return $this->_datatree->getName($gid);
+    }
+
+    /**
+     * Strips all parent references off of the given group name.
+     *
+     * @param string $group  Name of the group.
+     *
+     * @return The name of the group without parents.
+     */
+    public function getGroupShortName($group)
+    {
+        return $this->_datatree->getShortName($group);
+    }
+
+    /**
+     * Retrieves the ID of a group.
+     *
+     * @param integer|Horde_Group_DataTreeObject $gid  The id of the group or
+     *                                                 the group object to
+     *                                                 retrieve the ID for.
+     *
+     * @return integer  The group's ID.
+     */
+    public function getGroupId($group)
+    {
+        if ($group instanceof Horde_Group_DataTreeObject) {
+            $group = $group->getName();
+        }
+
+        $id = array_search($group, $this->_groupMap);
+        if ($id !== false) {
+            return $id;
+        }
+
+        if (isset($this->_groupList)) {
+            $id = array_search($group, $this->_groupList);
+            if ($id !== false) {
+                return $id;
+            }
+        }
+
+        return $this->_datatree->getId($group);
+    }
+
+    /**
+     * Check if a group exists in the system.
+     *
+     * @param string $group  The group to check.
+     *
+     * @return boolean  True if the group exists, false otherwise.
+     */
+    public function exists($group)
+    {
+        if (isset($this->_groupCache[$group]) ||
+            (isset($this->_groupList) &&
+             array_search($group, $this->_groupList) !== false)) {
+            return true;
+        }
+
+        return $this->_datatree->exists($group);
+    }
+
+    /**
+     * Returns a tree of the parents of a child group.
+     *
+     * @param integer $gid  The id of the child group.
+     *
+     * @return array  The group parents tree, with groupnames as the keys.
+     * @throws Horde_Group_Exception
+     */
+    public function getGroupParents($gid)
+    {
+        if (!isset($this->_parentTree[$gid])) {
+            $name = $this->getGroupName($gid);
+            $parents = $this->_datatree->getParents($name);
+            if ($parents instanceof PEAR_Error) {
+                throw new Horde_Group_Exception($parents);
+            }
+            $this->_parentTree[$gid] = $parents;
+        }
+
+        return $this->_parentTree[$gid];
+    }
+
+    /**
+     * Returns the single parent ID of the given group.
+     *
+     * @param integer $gid  The DataTree ID of the child group.
+     *
+     * @return integer  The parent of the given group.
+     * @throws Horde_Group_Exception
+     */
+    public function getGroupParent($gid)
+    {
+        if (!isset($this->_groupParents[$gid])) {
+            $parent = $this->_datatree->getParentById($gid);
+            if ($parent instanceof PEAR_Error) {
+                throw new Horde_Group_Exception($parent);
+            }
+            $this->_groupParents[$gid] = $parent;
+        }
+
+        return $this->_groupParents[$gid];
+    }
+
+    /**
+     * Returns a flat list of the parents of a child group
+     *
+     * @param integer $gid  The id of the group.
+     *
+     * @return array  A flat list of all of the parents of $group, hashed in
+     *                $id => $name format.
+     * @throws Horde_Group_Exception
+     */
+    public function getGroupParentList($gid)
+    {
+        if (!isset($this->_groupParentList[$gid])) {
+            $parents = $this->_datatree->getParentList($gid);
+            if ($parents instanceof PEAR_Error) {
+                throw new Horde_Group_Exception($parents);
+            }
+            $this->_groupParentList[$gid] = $parents;
+        }
+
+        return $this->_groupParentList[$gid];
+    }
+
+    /**
+     * Returns a list of all groups, in the format id => groupname.
+     *
+     * @param boolean $refresh  If true, the cached value is ignored and the
+     *                          group list is refreshed from the group backend.
+     *
+     * @return array  ID => groupname hash.
+     */
+    public function listGroups($refresh = false)
+    {
+        if ($refresh || !isset($this->_groupList)) {
+            $this->_groupList = $this->_datatree->get(DATATREE_FORMAT_FLAT, GROUP_ROOT, true);
+            unset($this->_groupList[GROUP_ROOT]);
+        }
+
+        return $this->_groupList;
+    }
+
+    /**
+     * Get a list of every user that is a part of this group ONLY.
+     *
+     * @param integer $gid  The ID of the group.
+     *
+     * @return array  The user list.
+     * @throws Horde_Group_Exception
+     */
+    public function listUsers($gid)
+    {
+        $groupOb = $this->getGroupById($gid);
+
+        if (!isset($groupOb->data['users']) ||
+            !is_array($groupOb->data['users'])) {
+            return array();
+        }
+
+        return array_keys($groupOb->data['users']);
+    }
+
+    /**
+     * Get a list of every user that is part of the specified group
+     * and any of its subgroups.
+     *
+     * @param integer $group  The ID of the parent group.
+     *
+     * @return array  The complete user list.
+     * @throws Horde_Group_Exception
+     */
+    public function listAllUsers($gid)
+    {
+        if (!isset($this->_subGroups[$gid])) {
+            // Get a list of every group that is a sub-group of $group.
+            $groups = $this->_datatree->get(DATATREE_FORMAT_FLAT, $this->getGroupName($gid), true);
+            if ($groups instanceof PEAR_Error) {
+                throw new Horde_Group_Exception($groups);
+            }
+            $this->_subGroups[$gid] = array_keys($groups);
+        }
+
+        $users = array();
+        foreach ($this->_subGroups[$gid] as $groupId) {
+            $users = array_merge($users, $this->listUsers($groupId));
+        }
+
+        return array_values(array_flip(array_flip($users)));
+    }
+
+    /**
+     * Get a list of every group that $user is in.
+     *
+     * @param string  $user          The user to get groups for.
+     * @param boolean $parentGroups  Also return the parents of any groups?
+     *
+     * @return array  An array of all groups the user is in.
+     * @throws Horde_Group_Exception
+     */
+    public function getGroupMemberships($user, $parentGroups = false)
+    {
+        if (!isset($this->_userGroups[$user])) {
+            $criteria = array(
+                'AND' => array(
+                    array('field' => 'name', 'op' => '=', 'test' => 'user'),
+                    array('field' => 'key', 'op' => '=', 'test' => $user)));
+            $groups = $this->_datatree->getByAttributes($criteria);
+            if ($groups instanceof PEAR_Error) {
+                throw new Horde_Group_Exception($groups);
+            }
+
+            if ($parentGroups) {
+                foreach ($groups as $id => $g) {
+                    $parents = $this->_datatree->getParentList($id);
+                    if ($parents instanceof PEAR_Error) {
+                        throw new Horde_Group_Exception($parents);
+                    }
+                    $groups += $parents;
+                }
+            }
+
+            $this->_userGroups[$user] = $groups;
+        }
+
+        return $this->_userGroups[$user];
+    }
+
+    /**
+     * Say if a user is a member of a group or not.
+     *
+     * @param string $user        The name of the user.
+     * @param integer $gid        The ID of the group.
+     * @param boolean $subgroups  Return true if the user is in any subgroups
+     *                            of group with ID $gid, also.
+     *
+     * @return boolean
+     */
+    public function userIsInGroup($user, $gid, $subgroups = true)
+    {
+        if (!$this->exists($this->getGroupName($gid))) {
+            return false;
+        } elseif ($subgroups) {
+            try {
+                $groups = $this->getGroupMemberships($user, true);
+            } catch (Horde_Group_Exception $e) {
+                Horde::logMessage($e, 'ERR');
+                return false;
+            }
+
+            return !empty($groups[$gid]);
+        } else {
+            try {
+                $users = $this->listUsers($gid);
+            } catch (Horde_Group_Exception $e) {
+                Horde::logMessage($e, 'ERR');
+                return false;
+            }
+            return in_array($user, $users);
+        }
+    }
+
+    /**
+     * Returns the nesting level of the given group. 0 is returned for any
+     * object directly below self::ROOT.
+     *
+     * @param integer $gid  The DataTree ID of the group.
+     *
+     * @return The DataTree level of the group.
+     */
+    public function getLevel($gid)
+    {
+        return substr_count($this->getGroupName($gid), ':');
+    }
+
+}
diff --git a/framework/Group/lib/Horde/Group/ContactListObject.php b/framework/Group/lib/Horde/Group/ContactListObject.php
new file mode 100644 (file)
index 0000000..a95b997
--- /dev/null
@@ -0,0 +1,127 @@
+<?php
+/**
+ * Extension of the DataTreeObject_Group class for storing Group information.
+ *
+ * Copyright 2008-2010 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 J. Rubinsky <mrubinsk@horde.org>
+ * @category Horde
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Group
+ */
+class Horde_Group_ContactListObject extends DataTreeObject_Group
+{
+
+    /**
+     * The unique name of this object.
+     * These names have the same requirements as other object names - they
+     * must be unique, etc.
+     *
+     * @var string
+     */
+    protected $name;
+
+    /**
+     * The unique name of this object.
+     * These names have the same requirements as other object names - they
+     * must be unique, etc.
+     *
+     * @var integer
+     */
+    protected $id;
+
+    /**
+     * Key-value hash that will be serialized.
+     *
+     * @see getData()
+     * @var array
+     */
+    protected $data = array();
+
+    /**
+     * Constructor.
+     *
+     * @param string $name  The name of the group.
+     */
+    public function __construct($name)
+    {
+        $this->name = $name;
+    }
+
+    /**
+     * Gets the ID of this object.
+     *
+     * @return string  The object's ID.
+     */
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    /**
+     * Gets the name of this object.
+     *
+     * @return string The object name.
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * 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)
+    {
+        return isset($this->data[$attribute])
+            ? $this->data[$attribute]
+            : null;
+    }
+
+    /**
+     * Sets one of the attributes of the object.
+     *
+     * @param string $attribute  The attribute to set.
+     * @param mixed $value       The value for $attribute.
+     *
+     * @throws Horde_Group_Exception
+     */
+    public function set($attribute, $value)
+    {
+        throw new Horde_Group_Exception('Unsupported.');
+    }
+
+    /**
+     * Save group.
+     *
+     * @throws Horde_Group_Exception
+     */
+    public function save()
+    {
+        throw new Horde_Group_Exception('Unsupported.');
+    }
+
+    /**
+     * @throws Horde_Group_Exception
+     */
+    public function removeUser($username, $update = true)
+    {
+        throw new Horde_Group_Exception('Unsupported.');
+    }
+
+    /**
+     * @throws Horde_Group_Exception
+     */
+    function addUser($username, $update = true)
+    {
+        throw new Horde_Group_Exception('Unsupported.');
+    }
+
+}
diff --git a/framework/Group/lib/Horde/Group/Contactlists.php b/framework/Group/lib/Horde/Group/Contactlists.php
new file mode 100644 (file)
index 0000000..3bb70e2
--- /dev/null
@@ -0,0 +1,614 @@
+<?php
+/**
+ * The Group_contactlists class provides a groups system based on Turba
+ * contact lists. Only SQL sources are supported.
+ *
+ * Copyright 2008-2010 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 J. Rubinsky <mrubinsk@horde.org>
+ * @category Horde
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Group
+ */
+class Horde_Group_Contactlists extends Horde_Group
+{
+    /**
+     * A cache object
+     *
+     * @var Horde_Cache object
+     */
+    protected $_cache = null;
+
+    /**
+     * Handles for the database connections. Need one for each possible
+     * source.
+     *
+     * @var DB
+     */
+    protected $_db = array();
+
+    /**
+     * Local copy of available address book sources that the group driver can
+     * use.
+     *
+     * @var array of Turba's cfgSource style entries.
+     */
+    protected $_sources = array();
+
+    /**
+     * Local cache of retreived group entries from Turba storage.
+     *
+     * @var unknown_type
+     */
+    protected $_listEntries = array();
+
+    /**
+     * Constructor.
+     */
+    public function __construct(array $params = array())
+    {
+        // Get a list of all available Turba sources
+        $turba_sources = Horde::loadConfiguration('sources.php',
+                                                  'cfgSources', 'turba');
+
+        // We only support sql type sources.
+        foreach ($turba_sources as $key => $source) {
+            if ($source['type'] == 'sql') {
+                $this->_sources[$key] = $source;
+            }
+        }
+
+        $this->_cache = $GLOBALS['injector']->getInstance('Horde_Cache');
+    }
+
+    /**
+     * Initializes the object.
+     */
+    public function __wakeup()
+    {
+    }
+
+    /**
+     * Returns the properties that need to be serialized.
+     *
+     * @return array  List of serializable properties.
+     */
+    public function __sleep()
+    {
+    }
+
+    /**
+     * Stores the object in the session cache.
+     */
+    public function shutdown()
+    {
+    }
+
+    /**
+     * Returns a new group object.
+     *
+     * @param string $name    The group's name.
+     * @param string $parent  The group's parent's name.
+     *
+     * @throws Horde_Group_Exception
+     */
+    public function newGroup($name, $parent = GROUP_ROOT)
+    {
+        throw new Horde_Group_Exception('Unsupported.');
+    }
+
+    /**
+     * Returns a Group object corresponding to the named group,
+     * with the users and other data retrieved appropriately.
+     *
+     * This is deprecated. Use getGroupById() instead.
+     *
+     * @param string $name  The name of the group to retrieve.
+     * @throws Horde_Group_Exception
+     */
+    public function getGroup($name)
+    {
+        throw new Horde_Group_Exception('Deprecated. Use getGroupById() instead.');
+    }
+
+    /**
+     * Returns a Horde_Group_ContactListObject object corresponding to the
+     * given unique ID, with the users and other data retrieved
+     * appropriately.
+     *
+     * @param integer $cid  The unique ID of the group to retrieve.
+     *
+     * @return Horde_Group_ContactListObject
+     * @throws Horde_Group_Exception
+     */
+    public function getGroupById($gid)
+    {
+        if (!empty($this->_groupCache[$gid])) {
+            return $this->_groupCache[$gid];
+        }
+
+        list($source, $id) = explode(':', $gid);
+        $entry = $this->_retrieveListEntry($gid);
+        if (empty($entry)) {
+            throw new Horde_Group_Exception($gid . ' does not exist');
+        }
+
+        $users = $this->_getAllMembers($gid);
+
+        $group = new Horde_Group_ContactListObject($entry[$this->_sources[$source]['map'][$this->_sources[$source]['list_name_field']]]);
+        $group->id = $gid;
+        $group->data['email'] = $entry[$this->_sources[$source]['map']['email']];
+        if (!empty($users)) {
+            $group->data['users'] = array_flip($users);
+        }
+
+        $group->setGroupOb($this);
+        $this->_groupCache[$gid] = $group;
+
+        return $group;
+    }
+
+    /**
+     * Adds a group to the groups system. The group must first be created with
+     * newGroup(), and have any initial users added to it, before this
+     * function is called.
+     *
+     * @param Horde_Group_ContactListObject $group  The new group object.
+     * @throws Horde_Group_Exception
+     */
+    public function addGroup($group)
+    {
+        throw new Horde_Group_Exception('Unsupported.');
+    }
+
+    /**
+     * Stores updated data - users, etc. - of a group to the backend system.
+     *
+     * @param ContactListObject_Group $group  The group to update.
+     *
+     * @throws Horde_Group_Exception
+     */
+    public function updateGroup($group)
+    {
+        throw new Horde_Group_Exception('Unsupported.');
+    }
+
+    /**
+     * Removes a group from the groups system permanently.
+     *
+     * @param ContactListObject_Group $group  The group to remove.
+     * @param boolean $force                  Force to remove every child.
+     *
+     * @throws Horde_Group_Exception
+     */
+    public function removeGroup($group, $force = false)
+    {
+        throw new Horde_Group_Exception('Unsupported.');
+    }
+
+    /**
+     * Retrieves the name of a group.
+     *
+     * @param integer|Horde_Group_ContactListObject $gid  The id of the group
+     *                                                    or the group object
+     *                                                    to retrieve the name
+     *                                                    for.
+     *
+     * @return string  The group's name.
+     * @throws Horde_Group_Exception
+     */
+    public function getGroupName($gid)
+    {
+        static $beenHere;
+
+        if (strpos($gid, ':') === false) {
+            throw new Horde_Group_Exception(sprintf('Group %s not found.', $gid));
+        }
+
+        if ($gid instanceof Horde_Group_ContactListObject) {
+            $gid = $gid->getId();
+        }
+
+        if (!empty($this->_listEntries[$gid])) {
+            list($source, $id) = explode(':', $gid);
+            $beenHere = false;
+            return $this->_listEntries[$gid][$this->_sources[$source]['map'][$this->_sources[$source]['list_name_field']]];
+        }
+
+        $this->_retrieveListEntry($gid);
+
+        // We should have the information cached now, try again..but protect
+        // against anything nasty...
+        if (!$beenHere) {
+            $beenHere = true;
+            return $this->getGroupName($gid);
+        }
+
+        throw new Horde_Group_Exception(sprintf('Group %s not found.', $gid));
+    }
+
+    /**
+     * Strips all parent references off of the given group name.
+     * Not used in this driver...group display names are ONLY for display.
+     *
+     * @param string $group  Name of the group.
+     *
+     * @return string  The name of the group without parents.
+     */
+    public function getGroupShortName($group)
+    {
+       return $group;
+    }
+
+    /**
+     * Retrieves the ID of a group, given the group object.
+     * Here for BC. Kinda silly, since if we have the object, we can just call
+     * getId() ourselves.
+     *
+     * @param ContactListObject_Group $group  The group object to retrieve the
+     *                                        ID for.
+     *
+     * @return integer  The group's ID.
+     * @throws Horde_Group_Exception
+     */
+    public function getGroupId($group)
+    {
+        if ($group instanceof Horde_Group_ContactListObject) {
+            return $group->getId();
+        }
+
+        throw new Horde_Group_Exception('Unsupported.');
+    }
+
+    /**
+     * Check if a group exists in the system.
+     * This must either be a noop or we need to somehow "uniqueify" the
+     * list's display name?
+     *
+     * @param string $group  The group name to check.
+     *
+     * @return boolean  True if the group exists, false otherwise.
+     */
+    public function exists($group)
+    {
+        return true;
+    }
+
+    /**
+     * Returns a tree of the parents of a child group.
+     *
+     * @param integer $gid  The id of the child group.
+     *
+     * @return array  The group parents tree, with groupnames as the keys.
+     */
+    public function getGroupParents($gid)
+    {
+        return array();
+    }
+
+    /**
+     * Returns the single parent ID of the given group.
+     *
+     * @param integer $gid  The ID of the child group.
+     *
+     * @return integer  The parent of the given group.
+     */
+    public function getGroupParent($gid)
+    {
+        return self::ROOT;
+    }
+
+    /**
+     * Returns a flat list of the parents of a child group
+     *
+     * @param integer $gid  The id of the group.
+     *
+     * @return array  A flat list of all of the parents of $group, hashed in
+     *                $id => $name format.
+     */
+    public function getGroupParentList($gid)
+    {
+        return array();
+    }
+
+    /**
+     * Returns a list of all groups, in the format id => groupname.
+     * The groups returned represent only the groups visible to the current
+     * user only.
+     *
+     * @param boolean $refresh  If true, the cached value is ignored and the
+     *                          group list is refreshed from the group backend.
+     *
+     * @return array  ID => groupname hash.
+     */
+    public function listGroups($refresh = false)
+    {
+        if (isset($this->_groupList) && !$refresh) {
+            return $this->_groupList;
+        }
+
+        // First, make sure we are connected to all sources
+        $this->_connect();
+
+        $groups = $owners = array();
+
+        foreach ($this->_sources as $key => $source) {
+            if ($source['use_shares']) {
+                if (empty($contact_shares)) {
+                    $scope = $GLOBALS['registry']->hasInterface('contacts');
+                    $shares = $GLOBALS['injector']->getInstance('Horde_Share')->getScope($scope);
+                    $this->_contact_shares = $shares->listShares(Horde_Auth::getAuth(), Horde_Perms::SHOW, Horde_Auth::getAuth());
+                }
+                // Contruct a list of owner ids to use
+                foreach ($this->_contact_shares as $id => $share) {
+                    $params = @unserialize($share->get('params'));
+                    if ($params['source'] == $key) {
+                        $owners[] = $params['name'];
+                    }
+                }
+            } else {
+                $owners = array(Horde_Auth::getAuth());
+            }
+            $owner_ids = array();
+            foreach ($owners as $owner) {
+                $owner_ids[] = $this->_db[$key]->quote($owner);
+            }
+            $sql = 'SELECT ' . $source['map']['__key'] . ', ' . $source['map'][$source['list_name_field']]
+                . '  FROM ' . $source['params']['table'] . ' WHERE '
+                . $source['map']['__type'] . ' = \'Group\' AND '
+                . $source['map']['__owner'] . ' IN (' . implode(',', $owner_ids ) . ')';
+
+           $results = $this->_db[$key]->getAssoc($sql);
+           foreach ($results as $id => $name) {
+               $groups[$key . ':' . $id] = $name;
+           }
+        }
+        $this->_groupList = $groups;
+
+        return $this->_groupList;
+    }
+
+    /**
+     * Get a list of every user that is part of the specified group
+     * and any of its subgroups.
+     *
+     * @param integer $group  The ID of the parent group.
+     *
+     * @return array  The complete user list.
+     * @throws Horde_Group_Exception
+     */
+    public function listAllUsers($gid)
+    {
+        return array_values($this->_getAllMembers($gid, true));
+    }
+
+    /**
+     * Returns a hash representing the list entry. Items are keyed by the
+     * backend specific keys.
+     *
+     * @param string $gid  The group id.
+     *
+     * @return array
+     * @throws Horde_Group_Exception
+     */
+    protected function _retrieveListEntry($gid)
+    {
+        if (!empty($this->_listEntries[$gid])) {
+            return $this->_listEntries[$gid];
+        }
+
+        list($source, $id) = explode(':', $gid);
+        if (empty($this->_sources[$source])) {
+            return array();
+        }
+
+        $this->_connect($source);
+        $sql = 'SELECT ' . $this->_sources[$source]['map']['__members'] . ','
+            . $this->_sources[$source]['map']['email'] . ','
+            . $this->_sources[$source]['map'][$this->_sources[$source]['list_name_field']]
+            . ' from ' . $this->_sources[$source]['params']['table'] . ' WHERE '
+            . $this->_sources[$source]['map']['__key'] . ' = ' . $this->_db[$source]->quote($id);
+
+        $results = $this->_db[$source]->getRow($sql, array(), DB_FETCHMODE_ASSOC);
+        if ($results instanceof PEAR_Error) {
+            Horde::logMessage($results, 'ERR');
+            throw new Horde_Group_Exception($results);
+        }
+        $this->_listEntries[$gid] = $results;
+
+        return $results;
+    }
+
+    /**
+     * TODO
+     *
+     * @throws Horde_Group_Exception
+     */
+    protected function _getAllMembers($gid, $subGroups = false)
+    {
+        if (empty($gid) || strpos($gid, ':') === false) {
+            throw new Horde_Group_Exception(sprintf('Unsupported group id: %s', $gid));
+        }
+
+        list($source, $id) = explode(':', $gid);
+        $entry = $this->_retrieveListEntry($gid);
+        $members = @unserialize($entry[$this->_sources[$source]['map']['__members']]);
+        $users = array();
+
+        // TODO: optimize this to only query each table once
+        foreach ($members as $member) {
+            // Is this member from the same source or a different one?
+            if (strpos($member, ':') !== false) {
+                list($newSource, $uid) = explode(':', $member);
+                if (!empty($this->_contact_shares[$newSource])) {
+                    $params = @unserialize($this->_contact_shares[$newSource]->get('params'));
+                    $newSource = $params['source'];
+                    $member = $uid;
+                    $this->_connect($newSource);
+                } elseif (empty($this->_sources[$newSource])) {
+                    // Last chance, it's not in one of our non-share sources
+                    continue;
+                }
+            } else {
+                // Same source
+                $newSource = $source;
+            }
+
+            $sql = 'SELECT ' . $this->_sources[$newSource]['map']['email']
+                . ', ' . $this->_sources[$newSource]['map']['__type']
+                . ' FROM ' . $this->_sources[$newSource]['params']['table']
+                . ' WHERE ' . $this->_sources[$newSource]['map']['__key']
+                . ' = ' . $this->_db[$newSource]->quote($member);
+
+            $results = $this->_db[$newSource]->getRow($sql);
+            if ($results instanceof PEAR_Error) {
+                Horde::logMessage($results, 'ERR');
+                throw new Horde_Group_Exception($results);
+            }
+
+            // Sub-Lists are treated as sub groups the best that we can...
+            if ($subGroups && $results[1] == 'Group') {
+                $this->_subGroups[$gid] = $newSource . ':' . $member;
+                $users = array_merge($users, $this->_getAllMembers($newSource . ':' . $member));
+            }
+            if (strlen($results[0])) {
+                // use a key to dump dups
+                $users[$results[0]] = $results[0];
+            }
+        }
+
+        return $users;
+    }
+
+    /**
+     * Returns ALL contact lists present in ALL sources that this driver knows
+     * about.
+     *
+     * @throws Horde_Group_Exception
+     */
+    protected function _listAllLists()
+    {
+        // Clear the cache - we will rebuild it.
+        $this->_listEntries = array();
+
+        foreach ($this->_sources as $key => $source) {
+            $this->_connect($key);
+            $sql = 'SELECT ' . $source['map']['__key'] . ','
+            . $source['map']['__members'] . ','
+            . $source['map']['email'] . ','
+            . $source['map'][$source['list_name_field']]
+            . ' FROM ' . $source['params']['table'] . ' WHERE '
+            . $source['map']['__type'] . ' = \'Group\'';
+
+           $results = $this->_db[$key]->query($sql);
+           if ($results instanceof PEAR_Error) {
+               throw new Horde_Group_Exception($results);
+           }
+
+           while ($row = $results->fetchRow(DB_FETCHMODE_ASSOC)) {
+                $this->_listEntries[$key . ':' . $row[$source['map']['__key']]] = $row;
+           }
+        }
+
+        return $this->_listEntries;
+    }
+
+    /**
+     * Get a list of every group that $user is in.
+     *
+     * @param string  $user          The user to get groups for.
+     * @param boolean $parentGroups  Also return the parents of any groups?
+     *
+     * @return array  An array of all groups the user is in.
+     */
+    public function getGroupMemberships($user, $parentGroups = false)
+    {
+        if (($memberships = $this->_cache->get('Group_contactlists_memberships' . md5($user))) !== false) {
+            return unserialize($memberships);
+        }
+        $lists = $this->_listAllLists();
+        $memberships = array();
+        foreach (array_keys($lists) as $list) {
+            $members = $this->_getAllMembers($list, $parentGroups);
+            if (!empty($members[$user])) {
+                $memberships[] = $list;
+            }
+        }
+
+        $this->_cache->set('Group_contactlists_memberships' . md5($user), serialize($memberships));
+
+        return $memberships;
+    }
+
+    /**
+     * Say if a user is a member of a group or not.
+     *
+     * @param string $user        The name of the user.
+     * @param integer $gid        The ID of the group.
+     * @param boolean $subgroups  Return true if the user is in any subgroups
+     *                            of group with ID $gid, also.
+     *
+     * @return boolean
+     */
+    public function userIsInGroup($user, $gid, $subgroups = true)
+    {
+        if (isset($_SESSION['horde']['groups']['i'][$user][$subgroups][$gid])) {
+            return $_SESSION['horde']['groups']['i'][$user][$subgroups][$gid];
+        }
+
+        try {
+            $users = $this->_getAllMembers($gid, $subgroups);
+        } catch (Horde_Group_Exception $e) {
+            Horde::logMessage($e, 'ERR');
+            return false;
+        }
+
+        $result = (bool)!empty($users[$user]);
+        $_SESSION['horde']['groups']['i'][$user][$subgroups][$gid] = $result;
+
+        return $result;
+    }
+
+    /**
+     * Attempts to open a persistent connection to the sql server.
+     *
+     * @return boolean  True on success.
+     * @throws Horde_Group_Exception
+     */
+    protected function _connect($source = null)
+    {
+        if (!is_null($source) && !empty($this->_db[$source])) {
+            return;
+        }
+
+        $sources = is_null($source)
+            ? array_keys($this->_sources)
+            : array($source);
+
+        foreach ($sources as $source) {
+            if (empty($this->_db[$source])) {
+                $this->_db[$source] = DB::connect($this->_sources[$source]['params'],
+                    array('persistent' => !empty($this->_sources[$source]['params']['persistent'])));
+                if ($this->_db[$source] instanceof PEAR_Error) {
+                    throw new Horde_Group_Exception($this->_db[$source]);
+                }
+
+                /* Set DB portability options. */
+                switch ($this->_db[$source]->phptype) {
+                case 'mssql':
+                    $this->_db[$source]->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM);
+                    break;
+
+                default:
+                    $this->_db[$source]->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS);
+                }
+            }
+        }
+
+        return true;
+    }
+
+}
diff --git a/framework/Group/lib/Horde/Group/DataTreeObject.php b/framework/Group/lib/Horde/Group/DataTreeObject.php
new file mode 100644 (file)
index 0000000..cd8b8e1
--- /dev/null
@@ -0,0 +1,190 @@
+<?php
+/**
+ * Extension of the DataTreeObject class for storing Group information
+ * in the Categories driver. If you want to store specialized Group
+ * information, you should extend this class instead of extending
+ * DataTreeObject directly.
+ *
+ * Copyright 1999-2010 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
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Group
+ */
+class Horde_Group_DataTreeObject extends DataTreeObject
+{
+    /**
+     * The Group object which this group is associated with - needed
+     * for updating data in the backend to make changes stick, etc.
+     *
+     * @var Horde_Group
+     */
+    protected $_groupOb;
+
+    /**
+     * This variable caches the users added or removed from the group
+     * for History logging of user-groups relationship.
+     *
+     * @var array
+     */
+    protected $_auditLog = array();
+
+    /**
+     * Returns the properties that need to be serialized.
+     *
+     * @return array  List of serializable properties.
+     */
+    public function __sleep()
+    {
+        return array_diff(array_keys(get_class_vars(__CLASS__)), array('_datatree', '_groupOb'));
+    }
+
+    /**
+     * Associates a group object with this group.
+     *
+     * @param Horde_Group $groupOb  The group object.
+     */
+    public function setGroupOb($groupOb)
+    {
+        $this->_groupOb = $groupOb;
+    }
+
+    /**
+     * Fetch the ID of this group
+     *
+     * @return string The group's ID
+     */
+    public function getId()
+    {
+        return $this->_groupOb->getGroupId($this);
+    }
+
+    /**
+     * Save any changes to this object to the backend permanently.
+     */
+    public function save()
+    {
+        return $this->_groupOb->updateGroup($this);
+
+    }
+
+    /**
+     * Adds a user to this group, and makes sure that the backend is
+     * updated as well.
+     *
+     * @param string $username The user to add.
+     */
+    public function addUser($username, $update = true)
+    {
+        $this->data['users'][$username] = 1;
+        $this->_auditLog[$username] = 'addUser';
+        if ($update && $this->_groupOb->exists($this->getName())) {
+            return $this->save();
+        }
+    }
+
+    /**
+     * Removes a user from this group, and makes sure that the backend
+     * is updated as well.
+     *
+     * @param string $username The user to remove.
+     */
+    public function removeUser($username, $update = true)
+    {
+        unset($this->data['users'][$username]);
+        $this->_auditLog[$username] = 'deleteUser';
+        if ($update) {
+            return $this->save();
+        }
+    }
+
+    /**
+     * Get a list of every user that is a part of this group
+     * (and only this group)
+     *
+     * @return array  The user list.
+     */
+    public function listUsers()
+    {
+        return $this->_groupOb->listUsers($this->getId());
+    }
+
+    /**
+     * Get a list of every user that is a part of this group and
+     * any of it's subgroups
+     *
+     * @return array  The complete user list.
+     */
+    public function listAllUsers()
+    {
+        return $this->_groupOb->listAllUsers($this->getId());
+    }
+
+    /**
+     * Get all the users recently added or removed from the group.
+     */
+    public function getAuditLog()
+    {
+        return $this->_auditLog;
+    }
+
+    /**
+     * Clears the audit log. To be called after group update.
+     */
+    public function clearAuditLog()
+    {
+        $this->_auditLog = array();
+    }
+
+    /**
+     * Map this object's attributes from the data array into a format
+     * that we can store in the attributes storage backend.
+     *
+     * @return array  The attributes array.
+     */
+    protected function _toAttributes()
+    {
+        // Default to no attributes.
+        $attributes = array();
+
+        // Loop through all users, if any.
+        if (isset($this->data['users']) && is_array($this->data['users']) && count($this->data['users'])) {
+            foreach ($this->data['users'] as $user => $active) {
+                $attributes[] = array('name' => 'user',
+                                      'key' => $user,
+                                      'value' => $active);
+            }
+        }
+        $attributes[] = array('name' => 'email',
+                              'key' => '',
+                              'value' => $this->get('email'));
+
+        return $attributes;
+    }
+
+    /**
+     * Take in a list of attributes from the backend and map it to our
+     * internal data array.
+     *
+     * @param array $attributes  The list of attributes from the
+     *                           backend (attribute name, key, and value).
+     */
+    protected function _fromAttributes($attributes)
+    {
+        // Initialize data array.
+        $this->data['users'] = array();
+
+        foreach ($attributes as $attr) {
+            if ($attr['name'] == 'user') {
+                $this->data['users'][$attr['key']] = $attr['value'];
+            } else {
+                $this->data[$attr['name']] = $attr['value'];
+            }
+        }
+    }
+
+}
diff --git a/framework/Group/lib/Horde/Group/Exception.php b/framework/Group/lib/Horde/Group/Exception.php
new file mode 100644 (file)
index 0000000..e1265af
--- /dev/null
@@ -0,0 +1,17 @@
+<?php
+/**
+ * Exception handler for the horde/Group package.
+ *
+ * Copyright 2010 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
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Group
+ */
+class Horde_Group_Exception extends Horde_Exception_Prior
+{
+}
diff --git a/framework/Group/lib/Horde/Group/Hooks.php b/framework/Group/lib/Horde/Group/Hooks.php
new file mode 100644 (file)
index 0000000..83ca1f0
--- /dev/null
@@ -0,0 +1,81 @@
+<?php
+/**
+ * This class provides the Horde groups system with the addition of adding
+ * support for hook functions to define if a user is in a group.
+ *
+ * Copyright 2003-2010 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   Jason Rust <jrust@rustyparts.com>
+ * @category Horde
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Group
+ */
+class Horde_Group_Hooks extends Horde_Group
+{
+    /**
+     * @var boolean
+     */
+    protected $_hookFunction = false;
+
+    /**
+     * Constructor.
+     *
+     * @params array $params
+     */
+    public function __construct($params)
+    {
+        parent::__construct($params);
+        Horde::loadConfiguration('hooks.php', null, 'horde');
+        $this->_hookFunction = function_exists('_group_hook');
+    }
+
+    /**
+     * Get a list of every group that $user is in.
+     *
+     * @param string  $user          The user to get groups for.
+     * @param boolean $parentGroups  Also return the parents of any groups?
+     *
+     * @return array  An array of all groups the user is in.
+     * @throws Horde_Group_Exception
+     */
+    public function getGroupMemberships($user, $parentGroups = false)
+    {
+        $memberships = parent::getGroupMemberships($user, $parentGroups);
+        if (!$this->_hookFunction) {
+            return $memberships;
+        }
+
+        $groups = $this->listGroups();
+        foreach ($groups as $gid => $groupName) {
+            if (empty($memberships[$gid]) && _group_hook($groupName, $user)) {
+                $memberships += array($gid => $groupName);
+            }
+
+            if ($parentGroups) {
+                $memberships += $this->getGroupParentList($gid);
+            }
+        }
+
+        return $memberships;
+    }
+
+    /**
+     * Say if a user is a member of a group or not.
+     *
+     * @param string  $user       The name of the user.
+     * @param integer $gid        The ID of the group.
+     * @param boolean $subgroups  Return true if the user is in any subgroups
+     *                            of $group, also.
+     *
+     * @return boolean
+     */
+    public function userIsInGroup($user, $gid, $subgroups = true)
+    {
+        return ($this->_hookFunction && _group_hook($this->getGroupName($gid), $user)) ||
+                parent::userIsInGroup($user, $gid, $subgroups);
+    }
+
+}
diff --git a/framework/Group/lib/Horde/Group/Kolab.php b/framework/Group/lib/Horde/Group/Kolab.php
new file mode 100644 (file)
index 0000000..c42864b
--- /dev/null
@@ -0,0 +1,319 @@
+<?php
+/**
+ * This class provides a Kolab backend for the Horde groups system.
+ *
+ * FIXME: A better solution would be to let this class rely on
+ *        Horde/Kolab/LDAP.php.
+ *
+ * Copyright 2005-2010 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   Gunnar Wrobel <wrobel@pardus.de>
+ * @category Horde
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Group
+ */
+class Horde_Group_Kolab extends Horde_Group_Ldap
+{
+    /**
+     * Constructor.
+     *
+     * @throws Horde_Group_Exception
+     */
+    public function __construct($params)
+    {
+        if (!function_exists('ldap_connect')) {
+            throw new Horde_Group_Exception('The Kolab group driver requires LDAP support.');
+        }
+
+        $this->_params = array(
+            'hostspec' => $GLOBALS['conf']['kolab']['ldap']['server'],
+            'basedn' => $GLOBALS['conf']['kolab']['ldap']['basedn'],
+            'binddn' => $GLOBALS['conf']['kolab']['ldap']['phpdn'],
+            'password' => $GLOBALS['conf']['kolab']['ldap']['phppw'],
+            'version' => 3,
+            'gid' => 'cn',
+            'memberuid' => 'member',
+            'attrisdn' => true,
+            'filter_type' => 'objectclass',
+            'objectclass' => 'kolabGroupOfNames',
+            'newgroup_objectclass' => 'kolabGroupOfNames'
+        );
+
+        $this->_filter = 'objectclass=' . $this->_params['objectclass'];
+
+        $this->__wakeup();
+    }
+
+    /**
+     * Initializes the object.
+     */
+    public function __wakeup()
+    {
+        foreach (array_keys($this->_groupCache) as $name) {
+            $this->_groupCache[$name]->setGroupOb($this);
+        }
+    }
+
+    /**
+     * Returns the properties that need to be serialized.
+     *
+     * @return array  List of serializable properties.
+     */
+    public function __sleep()
+    {
+        return array_diff(array_keys(get_class_vars(__CLASS__)), array('_datatree', '_ds'));
+    }
+
+    /**
+     * Returns a new group object.
+     *
+     * @param string $name    The group's name.
+     * @param string $parent  The group's parent's name.
+     *
+     * @return Horde_Group_KolabObject  A new group object.
+     * @throws Horde_Group_Exception
+     */
+    public function newGroup($name)
+    {
+        throw new Horde_Group_Exception('Unsupported.');
+    }
+
+    /**
+     * Adds a group to the groups system. The group must first be created with
+     * newGroup(), and have any initial users added to it, before this
+     * function is called.
+     *
+     * @param Horde_Group_KolabObject $group  The new group object.
+     * @throws Horde_Group_Exception
+     */
+    public function addGroup($group)
+    {
+        throw new Horde_Group_Exception('Unsupported.');
+    }
+
+    /**
+     * Stores updated data - users, etc. - of a group to the backend system.
+     *
+     * @param Horde_Group_KolabObject $group  The group to update.
+     *
+     * @throws Horde_Group_Exception
+     */
+    public function updateGroup($group)
+    {
+        throw new Horde_Group_Exception('Unsupported.');
+    }
+
+    /**
+     * Removes a group from the groups system permanently.
+     *
+     * @param Horde_Group_KolabObject $group  The group to remove.
+     * @param boolean $force      Force to remove every child.
+     *
+     * @throws Horde_Group_Exception
+     */
+    public function removeGroup($group, $force = false)
+    {
+        throw new Horde_Group_Exception('Unsupported.');
+    }
+
+    /**
+     * Return a Horde_Group_KolabObject corresponding to the given dn, with the
+     * users and other data retrieved appropriately.
+     *
+     * @param string $dn  The dn of the group to retrieve.
+     *
+     * @return Horde_Group_KolabObject  The requested group.
+     * @throws Horde_Group_Exception
+     */
+    public function getGroupById($dn)
+    {
+        static $cache = array();
+
+        if (!isset($cache[$dn])) {
+            /* Connect to the LDAP server. */
+            $success = $this->_connect();
+
+            $search = @ldap_search($this->_ds, $dn, $this->_filter);
+            if (!$search) {
+                throw new Horde_Group_Exception('Could not reach the LDAP server');
+            }
+
+            $result = @ldap_get_entries($this->_ds, $search);
+            @ldap_close($this->_ds);
+            if (!is_array($result) || (count($result) <= 1)) {
+                throw new Horde_Group_Exception('Empty result');
+            }
+
+            $attributes = array();
+            for ($i = 0; $i < $result[0]['count']; $i++) {
+                if ($result[0][$result[0][$i]]['count'] > 1) {
+                    $attributes[$result[0][$i]] = array();
+                    for ($j = 0; $j < $result[0][$result[0][$i]]['count']; $j++) {
+                        $attributes[$result[0][$i]][] = $result[0][$result[0][$i]][$j];
+                    }
+                } else {
+                    $attributes[$result[0][$i]] = $result[0][$result[0][$i]][0];
+                }
+            }
+            $attributes['dn'] = $result[0]['dn'];
+
+            $group = new Horde_Group_KolabObject($this->getGroupName($dn));
+            $group->_fromAttributes($attributes);
+            $group->setGroupOb($this);
+            $cache[$dn] = $group;
+        }
+
+        return $cache[$dn];
+    }
+
+
+    /**
+     * Retrieve the ID of the given group.
+     *
+     * NOTE: If given a group name, this function can be unreliable if more
+     * than one group exists with the same name.
+     *
+     * @param mixed $group  LDAP_Group object, or a group name (string)
+     *
+     * @return string  The group's ID.
+     * @throws Horde_Group_Exception
+     */
+    public function getGroupId($group)
+    {
+        static $cache = array();
+
+        if ($group instanceof Horde_Group_KolabObject) {
+            return $group->getDn();
+        }
+
+        if (!isset($cache[$group])) {
+            $this->_connect();
+            $search = @ldap_search($this->_ds, $this->_params['basedn'],
+                                   $this->_params['gid'] . '=' . $group,
+                                   array($this->_params['gid']));
+            if (!$search) {
+                throw new Horde_Group_Exception('Could not reach the LDAP server');
+            }
+
+            $result = @ldap_get_entries($this->_ds, $search);
+            @ldap_close($this->_ds);
+            if (!is_array($result) || (count($result) <= 1)) {
+                throw new Horde_Group_Exception('Empty result');
+            }
+            $cache[$group] = $result[0]['dn'];
+        }
+
+        return $cache[$group];
+    }
+
+    /**
+     * Get a list of the parents of a child group.
+     *
+     * @param string $dn  The fully qualified group dn
+     *
+     * @return array  Nested array of parents
+     */
+    public function getGroupParents($dn)
+    {
+        return array();
+    }
+
+    /**
+     * Get the parent of the given group.
+     *
+     * @param string $dn  The dn of the child group.
+     *
+     * @return string  The dn of the parent group.
+     */
+    public function getGroupParent($dn)
+    {
+        return null;
+    }
+
+    /**
+     * Get a list of parents all the way up to the root object for the given
+     * group.
+     *
+     * @param string $dn  The dn of the group.
+     *
+     * @return array  A flat list of all of the parents of the given group,
+     *                hashed in $dn => $name format.
+     */
+    public function getGroupParentList($dn)
+    {
+        return array();
+    }
+
+    /**
+     * Tries to find a DN for a given kolab mail address.
+     *
+     * @param string $mail  The mail address to search for.
+     *
+     * @return string  The corresponding dn or false.
+     * @throws Horde_Group_Exception
+     */
+    public function dnForMail($mail)
+    {
+        $filter = '(&(objectclass=kolabInetOrgPerson)(mail=' . Horde_Ldap::quote($mail) . '))';
+        $search = @ldap_search($this->_ds, $this->_params['basedn'], $filter);
+        if (!$search) {
+            throw new Horde_Group_Exception('Could not reach the LDAP server');
+        }
+        $dn = @ldap_first_entry($this->_ds, $search);
+        if ($dn) {
+            return ldap_get_dn($this->_ds, $dn);
+        }
+
+        throw new Horde_Group_Exception(sprintf('Error searching for user with the email address "%s"!', $mail));
+    }
+
+    /**
+     * Get a list of every group that the given user is a member of.
+     *
+     * @param string  $user          The user to get groups for.
+     * @param boolean $parentGroups  Also return the parents of any groups?
+     *
+     * @return array  An array of all groups the user is in.
+     * @throws Horde_Group_Exception
+     */
+    public function getGroupMemberships($user, $parentGroups = false)
+    {
+        static $cache = array();
+
+        if (empty($cache[$user])) {
+            /* Connect to the LDAP server. */
+            $success = $this->_connect();
+            $dn = $this->dnForMail($user);
+
+            // Set up search filter
+            $filter = '(' . $this->_params['memberuid'] . '=' . $dn . ')';
+
+            // Perform search
+            $search = @ldap_search($this->_ds, $this->_params['basedn'], $filter);
+            if (!$search) {
+                throw new Horde_Group_Exception('Could not reach the LDAP server');
+            }
+
+            $result = @ldap_get_entries($this->_ds, $search);
+            @ldap_close($this->_ds);
+            if (!is_array($result) || (count($result) <= 1)) {
+                return array();
+            }
+
+            $groups = array();
+            $current_charset = Horde_Nls::getCharset();
+            for ($i = 0; $i < $result['count']; $i++) {
+                $utf8_dn = Horde_String::convertCharset($result[$i]['dn'], 'UTF-8', $current_charset);
+                $groups[$utf8_dn] = $this->getGroupName($utf8_dn);
+            }
+
+            $cache[$user] = $groups;
+        }
+
+        return $cache[$user];
+    }
+
+}
diff --git a/framework/Group/lib/Horde/Group/KolabObject.php b/framework/Group/lib/Horde/Group/KolabObject.php
new file mode 100644 (file)
index 0000000..c0264b9
--- /dev/null
@@ -0,0 +1,162 @@
+<?php
+/**
+ * Copyright 2005-2010 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   Ben Chavet <ben@horde.org>
+ * @category Horde
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Group
+ */
+class Horde_Group_KolabObject extends LDAP_Group
+{
+    /**
+     * Constructor.
+     *
+     * @param string $name    The name of this group.
+     * @param string $parent  The dn of the parent of this group.
+     */
+    public function __construct($name, $parent = null)
+    {
+        $this->setName($name);
+    }
+
+    /**
+     * Fetch the ID of this group
+     *
+     * @return string The group's ID
+     */
+    public function getId()
+    {
+        return $this->getDn();
+    }
+
+    /**
+     * Save any changes to this object to the backend permanently.
+     *
+     * @throws Horde_Group_Exception
+     */
+    public function save()
+    {
+        throw new Horde_Group_Exception('Unsupported.');
+    }
+
+    /**
+     * Adds a user to this group, and makes sure that the backend is
+     * updated as well.
+     *
+     * @param string $username The user to add.
+     *
+     * @throws Horde_Group_Exception
+     */
+    public function addUser($username, $update = true)
+    {
+        throw new Horde_Group_Exception('Unsupported.');
+    }
+
+    /**
+     * Removes a user from this group, and makes sure that the backend
+     * is updated as well.
+     *
+     * @param string $username The user to remove.
+     *
+     * @throws Horde_Group_Exception
+     */
+    public function removeUser($username, $update = true)
+    {
+        throw new Horde_Group_Exception('Unsupported.');
+    }
+
+    /**
+     * Get all the users recently added or removed from the group.
+     */
+    public function getAuditLog()
+    {
+        return array();
+    }
+
+    /**
+     * Clears the audit log. To be called after group update.
+     */
+    public function clearAuditLog()
+    {
+    }
+
+    /**
+     * Sets the name of this object.
+     *
+     * @param string $name  The name to set this object's name to.
+     */
+    public function getDn()
+    {
+        return $this->name . ',' . $GLOBALS['conf']['kolab']['ldap']['basedn'];
+    }
+
+    /**
+     * Take in a list of attributes from the backend and map it to our
+     * internal data array.
+     *
+     * @param array $attributes  The list of attributes from the backend.
+     */
+    protected function _fromAttributes($attributes = array())
+    {
+        $this->data['users'] = array();
+        foreach ($attributes as $key => $value) {
+            if (Horde_String::lower($key) == 'member') {
+                if (is_array($value)) {
+                    foreach ($value as $user) {
+                        $pattern = '/^cn=([^,]+).*$/';
+                        $results = array();
+                        preg_match($pattern, $user, $results);
+                        if (isset($results[1])) {
+                            $user = $results[1];
+                        }
+                        $this->data['users'][$user] = '1';
+                    }
+                } else {
+                    $pattern = '/^cn=([^,]+).*$/';
+                    $results = array();
+                    preg_match($pattern, $value, $results);
+                    if (isset($results[1])) {
+                        $value = $results[1];
+                    }
+                    $this->data['users'][$value] = '1';
+                }
+            } elseif ($key == 'mail') {
+                $this->data['email'] = $value;
+            } else {
+                $this->data[$key] = $value;
+            }
+        }
+    }
+
+    /**
+     * Map this object's attributes from the data array into a format that
+     * can be stored in an LDAP entry.
+     *
+     * @return array  The entry array.
+     */
+    protected function _toAttributes()
+    {
+        $attributes = array();
+        foreach ($this->data as $key => $value) {
+            if ($key == 'users') {
+                foreach ($value as $user => $membership) {
+                    $user = 'cn=' . $user . ',' . $GLOBALS['conf']['kolab']['ldap']['basedn'];
+                    $attributes['member'][] = $user;
+                }
+            } elseif ($key == 'email') {
+                if (!empty($value)) {
+                    $attributes['mail'] = $value;
+                }
+            } elseif ($key != 'dn' && $key != 'member') {
+                $attributes[$key] = !empty($value) ? $value : ' ';
+            }
+        }
+
+        return $attributes;
+    }
+
+}
diff --git a/framework/Group/lib/Horde/Group/Ldap.php b/framework/Group/lib/Horde/Group/Ldap.php
new file mode 100644 (file)
index 0000000..919114f
--- /dev/null
@@ -0,0 +1,692 @@
+<?php
+/**
+ * This class provides an LDAP backend for the Horde groups system.
+ *
+ * Copyright 2005-2010 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   Ben Chavet <ben@horde.org>
+ * @category Horde
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Group
+ */
+class Horde_Group_Ldap extends Horde_Group
+{
+    /**
+     * LDAP connection handle
+     */
+    protected $_ds;
+
+    /**
+     * Local copy of the global $conf['group']['params'] array. Simply
+     * for coding convenience.
+     */
+    protected $_params;
+
+    /**
+     * Generated LDAP filter based on the config parameters
+     */
+    protected $_filter;
+
+    /**
+     * Constructor.
+     */
+    public function __construct($params)
+    {
+        $this->_params = $GLOBALS['conf']['group']['params'];
+
+        $this->_params['gid'] = Horde_String::lower($this->_params['gid']);
+        $this->_params['memberuid'] = Horde_String::lower($this->_params['memberuid']);
+        foreach ($this->_params['newgroup_objectclass'] as $key => $val) {
+            $this->_params['newgroup_objectclass'][$key] = Horde_String::lower($val);
+        }
+
+        /* Generate LDAP search filter. */
+        if (!empty($this->_params['filter'])) {
+            $this->_filter = $this->_params['filter'];
+        } elseif (!is_array($this->_params['objectclass'])) {
+            $this->_filter = 'objectclass=' . $this->_params['objectclass'];
+        } else {
+            $this->_filter = '';
+            foreach ($this->_params['objectclass'] as $objectclass) {
+                $this->_filter = '(&' . $this->_filter;
+                $this->_filter .= '(objectclass=' . $objectclass . '))';
+            }
+        }
+
+        $this->_filter = Horde_String::lower($this->_filter);
+    }
+
+    /**
+     * Connects to the LDAP server.
+     *
+     * @throws Horde_Group_Exception
+     */
+    protected function _connect()
+    {
+        /* Connect to the LDAP server. */
+        $this->_ds = @ldap_connect($this->_params['hostspec']);
+        if (!$this->_ds) {
+            throw new Horde_Group_Exception('Could not reach the LDAP server');
+        }
+
+        if (!ldap_set_option($this->_ds, LDAP_OPT_PROTOCOL_VERSION,
+                             $this->_params['version'])) {
+            Horde::logMessage(
+                sprintf('Set LDAP protocol version to %d failed: [%d] %s',
+                        $this->_params['version'],
+                        ldap_errno($conn),
+                        ldap_error($conn),
+                        __FILE__, __LINE__));
+        }
+
+        /* Start TLS if we're using it. */
+        if (!empty($this->_params['tls'])) {
+            if (!@ldap_start_tls($this->_ds)) {
+                Horde::logMessage(
+                    sprintf('STARTTLS failed: [%d] %s',
+                            @ldap_errno($this->_ds),
+                            @ldap_error($this->_ds)),
+                    'ERR');
+            }
+        }
+
+        if (isset($this->_params['binddn'])) {
+            $bind = @ldap_bind($this->_ds, $this->_params['binddn'],
+                               $this->_params['password']);
+        } else {
+            $bind = @ldap_bind($this->_ds);
+        }
+
+        if (!$bind) {
+            throw new Horde_Group_Exception('Could not bind to LDAP server');
+        }
+    }
+
+    /**
+     * Recursively deletes $dn. $this->_ds MUST already be connected.
+     *
+     * @throws Horde_Group_Exception
+     */
+    protected function _recursive_delete($dn)
+    {
+        $search = @ldap_list($this->_ds, $dn, 'objectclass=*', array(''));
+        if (!$search) {
+            throw new Horde_Group_Exception('Could not reach the LDAP server');
+        }
+
+        $children = @ldap_get_entries($this->_ds, $search);
+        for ($i = 0; $i < $children['count']; $i++) {
+            $result = $this->_recursive_delete($children[$i]['dn']);
+            if (!$result) {
+                throw new Horde_Group_Exception(sprintf(__CLASS__ . ': Unable to delete group "%s". This is what the server said: %s', $this->getName($children[$i]['dn']), @ldap_error($this->_ds)));
+            }
+        }
+
+        if (!@ldap_delete($this->_ds, $dn)) {
+            throw new Horde_Group_Exception(sprintf(__CLASS__ . ': Unable to delete group "%s". This is what the server said: %s', $dn, @ldap_error($this->_ds)));
+        }
+    }
+
+    /**
+     * Searches existing groups for the highest gidnumber, and returns
+     * one higher.
+     *
+     * @return integer
+     *
+     * @throws Horde_Group_Exception
+     */
+    protected function _nextGid()
+    {
+        /* Connect to the LDAP server. */
+        $this->_connect();
+
+        $search = @ldap_search($this->_ds, $this->_params['basedn'], $this->_filter);
+        if (!$search) {
+            throw new Horde_Group_Exception('Could not reach the LDAP server');
+        }
+
+        $result = @ldap_get_entries($this->_ds, $search);
+        @ldap_close($this->_ds);
+
+        if (!is_array($result) || (count($result) <= 1)) {
+            return 1;
+        }
+
+        $nextgid = 0;
+        for ($i = 0; $i < $result['count']; $i++) {
+            if ($result[$i]['gidnumber'][0] > $nextgid) {
+                $nextgid = $result[$i]['gidnumber'][0];
+            }
+        }
+
+        return $nextgid + 1;
+    }
+
+    /**
+     * Return a new group object.
+     *
+     * @param string $name    The group's name.
+     * @param string $parent  The group's parent's ID (DN).
+     *
+     * @return Horde_Group_LdapObject  A new group object.
+     * @throws Horde_Exception
+     */
+    public function newGroup(string $name, $parent = null)
+    {
+        try {
+            $entry = Horde::callHook('groupldap', array($name, $parent));
+        } catch (Horde_Exception_HookNotSet $e) {
+            // Try this simple default and hope it works.
+            $entry[$this->_params['gid']] = $name;
+            $entry['objectclass'] = $this->_params['newgroup_objectclass'];
+            $entry['gidnumber'] = $this->_nextGid();
+        }
+
+        $group = new Horde_Group_LdapObject($name, $parent);
+        $group->_fromAttributes($entry);
+        $group->setGroupOb($this);
+
+        return $group;
+    }
+
+    /**
+     * Return a group object corresponding to the named group, with the
+     * users and other data retrieved appropriately.
+     *
+     * @param string $name  The name of the group to retrieve.
+     *
+     * @return Horde_Group_LdapObject  The requested group.
+     * @throws Horde_Group_Exception
+     */
+    public function getGroup($name)
+    {
+        return $this->getGroupById($this->getGroupId($name));
+    }
+
+    /**
+     * Return a group object corresponding to the given dn, with the
+     * users and other data retrieved appropriately.
+     *
+     * @param string $dn  The dn of the group to retrieve.
+     *
+     * @return Horde_Group_LdapObject  The requested group.
+     * @throws Horde_Group_Exception
+     */
+    public function getGroupById($dn)
+    {
+        static $cache = array();
+
+        if (!isset($cache[$dn])) {
+            /* Connect to the LDAP server. */
+            $this->_connect();
+
+            $search = @ldap_search($this->_ds, $dn, $this->_filter);
+            if (!$search) {
+                throw new Horde_Group_Exception('Could not reach the LDAP server');
+            }
+
+            $result = @ldap_get_entries($this->_ds, $search);
+            @ldap_close($this->_ds);
+            if (!is_array($result) || (count($result) <= 1)) {
+                throw new Horde_Group_Exception('Empty result');
+            }
+
+            $attributes = array();
+            for ($i = 0; $i < $result[0]['count']; $i++) {
+                if ($result[0][$result[0][$i]]['count'] > 1) {
+                    $attributes[$result[0][$i]] = array();
+                    for ($j = 0; $j < $result[0][$result[0][$i]]['count']; $j++) {
+                        $attributes[$result[0][$i]][] = $result[0][$result[0][$i]][$j];
+                    }
+                } else {
+                    $attributes[$result[0][$i]] = $result[0][$result[0][$i]][0];
+                }
+            }
+            $attributes['dn'] = $result[0]['dn'];
+
+            $group = new Horde_Group_LdapObject($this->getGroupName($dn));
+            $group->_fromAttributes($attributes);
+            $group->setGroupOb($this);
+            $cache[$dn] = $group;
+        }
+
+        return $cache[$dn];
+    }
+
+    /**
+     * Get a globally unique ID for a group.  This really just returns the dn
+     * for the group, but is included for compatibility with the Group class.
+     *
+     * @param Horde_Group_LdapObject $group  The group.
+     *
+     * @return string  A GUID referring to $group.
+     */
+    public function getGUID($group)
+    {
+        return $group->get('dn');
+    }
+
+    /**
+     * Add a group to the groups system.  The group must first be created with
+     * Group_ldap::newGroup(), and have any initial users added to it, before
+     * this function is called.
+     *
+     * @param Horde_Group_LdapObject $group  The new group object.
+     *
+     * @throws Horde_Group_Exception
+     */
+    public function addGroup(Horde_Group_DataTreeObject $group)
+    {
+        /* Connect to the LDAP server. */
+        $this->_connect();
+
+        $dn = $group->get('dn');
+
+        $entry = $group->_toAttributes();
+        $success = @ldap_add($this->_ds, $dn, $entry);
+
+        if (!$success) {
+            throw new Horde_Group_Exception(sprintf(__CLASS__ . ': Unable to add group "%s". This is what the server said: ', $group->getName()) . @ldap_error($this->_ds));
+        }
+
+        @ldap_close($this->_ds);
+    }
+
+    /**
+     * Store updated data - users, etc. - of a group to the backend system.
+     *
+     * @param Horde_Group_LdapObject $group  The group to update
+     *
+     * @throws Horde_Group_Exception
+     * @throws Horde_History_Exception
+     * @throws InvalidArgumentException
+     */
+    public function updateGroup(Horde_Group_DataTreeObject $group)
+    {
+        $entry = $group->_toAttributes();
+
+        /* Connect to the LDAP server. */
+        $this->_connect();
+
+        // Do not attempt to change an LDAP object's objectClasses
+        unset($entry['objectclass']);
+
+        $result = @ldap_modify($this->_ds, $group->getId(), $entry);
+        if (!$result) {
+            throw new Horde_Group_Exception(sprintf(__CLASS__ . ': Unable to update group "%s". This is what the server said: %s', $group->getName(), @ldap_error($this->_ds)));
+        }
+
+        @ldap_close($this->_ds);
+
+        /* Log the update of the group users on the history log. */
+        $history = $GLOBALS['injector']->getInstance('Horde_History');
+        $guid = $this->getGUID($group);
+        foreach ($group->getAuditLog() as $userId => $action) {
+            $history->log($guid, array('action' => $action, 'user' => $userId), true);
+        }
+        $group->clearAuditLog();
+
+        /* Log the group modification. */
+        $history->log($guid, array('action' => 'modify'), true);
+
+        return $result;
+    }
+
+    /**
+     * Remove a group from the groups system permanently.
+     *
+     * @param Horde_Group_LdapObject $group  The group to remove.
+     * @param boolean $force     Recursively delete children groups if true.
+     *
+     * @throws Horde_Group_Exception
+     */
+    public function removeGroup(Horde_Group_DataTreeObject $group,
+                                $force = false)
+    {
+        $dn = $group->getId();
+
+        /* Connect to the LDAP server. */
+        $this->_connect();
+
+        if ($force) {
+            return $this->_recursive_delete($dn);
+        }
+
+        if (!@ldap_delete($this->_ds, $dn)) {
+            throw new Horde_Group_Exception(sprintf(__CLASS__ . ': Unable to delete group "%s". This is what the server said: %s', $dn, @ldap_error($this->_ds)));
+        }
+    }
+
+    /**
+     * Retrieve the name of a group.
+     *
+     * @param string $dn  The dn of the group to retrieve the name for.
+     *
+     * @return string  The group's name.
+     * @throws Horde_Group_Exception
+     */
+    public function getGroupName($dn)
+    {
+        $dn = Horde_String::convertCharset($dn, Horde_Nls::getCharset(), 'UTF-8');
+        $result = @ldap_explode_dn($dn, 1);
+        if ($result === false) {
+            throw new Horde_Group_Exception('Invalid group ID passed (bad DN syntax)');
+        }
+
+        return $result[0];
+    }
+
+    /**
+     * DataTreeObject full names include references to parents, but LDAP does
+     * not have this concept.  This function simply returns the $group
+     * parameter and is included for compatibility with the Group class.
+     *
+     * @param string $group  Group name.
+     *
+     * @return string  $group.
+     */
+    public function getGroupShortName($group)
+    {
+        return $group;
+    }
+
+    /**
+     * Retrieve the ID of the given group.
+     *
+     * NOTE: If given a group name, this function can be unreliable if more
+     * than one group exists with the same name.
+     *
+     * @param mixed $group  Group object, or a group name (string).
+     *
+     * @return string  The group's ID.
+     * @throws Horde_Group_Exception
+     */
+    public function getGroupId($group)
+    {
+        static $cache = array();
+
+        if ($group instanceof Horde_Group_LdapObject) {
+            return $group->get('dn');
+        }
+
+        if (!isset($cache[$group])) {
+            $this->_connect();
+            $search = @ldap_search($this->_ds, $this->_params['basedn'],
+                                   $this->_params['gid'] . '=' . $group,
+                                   array($this->_params['gid']));
+            if (!$search) {
+                throw new Horde_Group_Exception('Could not reach the LDAP server');
+            }
+
+            $result = @ldap_get_entries($this->_ds, $search);
+            @ldap_close($this->_ds);
+            if (!is_array($result) || (count($result) <= 1)) {
+                throw new Horde_Group_Exception('Empty result');
+            }
+            $cache[$group] = $result[0]['dn'];
+        }
+
+        return $cache[$group];
+    }
+
+    /**
+     * Check if a group exists in the system.
+     *
+     * @param string $group  The group name to check for.
+     *
+     * @return boolean  True if the group exists, False otherwise.
+     * @throws Horde_Group_Exception
+     */
+    public function exists($group)
+    {
+        static $cache = array();
+
+        if (!isset($cache[$group])) {
+            /* Connect to the LDAP server. */
+            $this->_connect();
+
+            $groupDN = $this->getGroupId($group);
+            $group = $this->getGroupShortName($group);
+
+            $res = @ldap_compare($this->_ds, $groupDN, $this->_params['gid'], $group);
+            if ($res === false) {
+                throw new Horde_Group_Exception(sprintf('Internal Error: An attribute must ALWAYS match itself: %s', @ldap_error($this->_ds)));
+            }
+            // $res is True if the group exists, -1 if not, false never
+            $cache[$group] = ($res === true);
+        }
+
+        return $cache[$group];
+    }
+
+    /**
+     * Get a list of the parents of a child group.
+     *
+     * @param string $dn  The fully qualified group dn
+     *
+     * @return array  Nested array of parents
+     */
+    public function getGroupParents($dn)
+    {
+        $parent = $this->getGroupParent($dn);
+        $parents = array(DATATREE_ROOT => 1);
+        while ($parent != DATATREE_ROOT) {
+            $parents = array($parent => $parents);
+            $parent = $this->getGroupParent($parent);
+        }
+        return $parents;
+    }
+
+    /**
+     * Get the parent of the given group.
+     *
+     * @param string $dn  The dn of the child group.
+     *
+     * @return string  The dn of the parent group.
+     * @throws Horde_Group_Exception
+     */
+    public function getGroupParent($dn)
+    {
+        if (@ldap_explode_dn($dn, 0) === false) {
+            throw new Horde_Group_Exception('Invalid group ID passed (bad DN syntax)');
+        }
+
+        unset($result['count'], $result[0]);
+        $parent_dn = implode(',', $result);
+
+        return (Horde_String::lower($parent_dn) == Horde_String::lower($GLOBALS['conf']['group']['params']['basedn']))
+            ? DATATREE_ROOT
+            : $parent_dn;
+    }
+
+    /**
+     * Get a list of parents all the way up to the root object for the given
+     * group.
+     *
+     * @param string $dn  The dn of the group.
+     *
+     * @return array  A flat list of all of the parents of the given group,
+     *                hashed in $dn => $name format.
+     * @throws Horde_Group_Exception
+     */
+    public function getGroupParentList($dn)
+    {
+        if (@ldap_explode_dn($dn, 0) === false) {
+            throw new Horde_Group_Exception('Invalid group ID passed (bad DN syntax)');
+        }
+
+        $num = $result['count'];
+        unset($result['count'], $result[0]);
+
+        $count = 0;
+        $parents = array();
+        $parent_dn = implode(',', $result);
+        while ($parent_dn != $this->_params['basedn'] && $count++ != $num) {
+            $parents[$parent_dn] = $this->getGroupName($parent_dn);
+            unset($result[$count]);
+            $parent_dn = implode(',', $result);
+        }
+        $parents[DATATREE_ROOT] = DATATREE_ROOT;
+
+        return $parents;
+    }
+
+    /**
+     * Get a list of every group, in the format dn => groupname.
+     *
+     * @param boolean $refresh  If true, the cached value is ignored and the
+     *                          group list is refreshed from the group backend.
+     *
+     * @return array  dn => groupname hash.
+     * @throws Horde_Group_Exception
+     */
+    public function listGroups($refresh = false)
+    {
+        static $groups;
+
+        if ($refresh || is_null($groups)) {
+            /* Connect to the LDAP server. */
+            $this->_connect();
+
+            $search = @ldap_search($this->_ds, $this->_params['basedn'], $this->_filter, array($this->_params['gid']));
+            if (!$search) {
+                throw new Horde_Group_Exception('Could not reach the LDAP server');
+            }
+
+            @ldap_sort($this->_ds, $search, $this->_params['gid']);
+
+            $result = @ldap_get_entries($this->_ds, $search);
+            @ldap_close($this->_ds);
+            if (!is_array($result) || (count($result) <= 1)) {
+                return array();
+            }
+
+            $groups = array();
+            for ($i = 0; $i < $result['count']; $i++) {
+                $groups[$result[$i]['dn']] = $this->getGroupName($result[$i]['dn']);
+            }
+        }
+
+        return $groups;
+    }
+
+    /**
+     * Get a list of every user that is part of the specified group and any
+     * of its subgroups.
+     *
+     * @param string $dn  The dn of the parent group.
+     *
+     * @return array  The complete user list.
+     * @throws Horde_Group_Exception
+     */
+    public function listAllUsers($dn)
+    {
+        static $cache = array();
+
+        if (!isset($cache[$dn])) {
+            $this->_connect();
+
+            $search = @ldap_search($this->_ds, $dn, $this->_filter);
+            if (!$search) {
+                throw new Horde_Group_Exception(sprintf('Could not reach the LDAP server: %s', @ldap_error($this->_ds)));
+            }
+
+            $result = @ldap_get_entries($this->_ds, $search);
+            @ldap_close($this->_ds);
+            if (!is_array($result) || (count($result) <= 1)) {
+                // Not an error, we just don't have any users in this group.
+                return array();
+            }
+
+            $users = array();
+            for ($i = 0; $i < $result['count']; $i++) {
+                $users = array_merge($users, $this->listUsers($result[$i]['dn']));
+            }
+
+            $cache[$dn] = array_keys(array_flip($users));
+        }
+
+        return $cache[$dn];
+    }
+
+    /**
+     * Get a list of every group that the given user is a member of.
+     *
+     * @param string  $user          The user to get groups for.
+     * @param boolean $parentGroups  Also return the parents of any groups?
+     *
+     * @return array  An array of all groups the user is in.
+     * @throws Horde_Group_Exception
+     */
+    public function getGroupMemberships($user, $parentGroups = false)
+    {
+        static $cache = array();
+
+        if (empty($cache[$user])) {
+            /* Connect to the LDAP server. */
+            $this->_connect();
+
+            // Set up search filter
+            $filter = '(' . $this->_params['memberuid'] . '=';
+            if ($GLOBALS['conf']['group']['params']['attrisdn']) {
+                $filter .= $GLOBALS['conf']['auth']['params']['uid'] . '=';
+            }
+            $filter .= $user;
+            if ($GLOBALS['conf']['group']['params']['attrisdn']) {
+                $filter .= ',' . $GLOBALS['conf']['auth']['params']['basedn'];
+            }
+            $filter .= ')';
+
+            // Perform search
+            $search = @ldap_search($this->_ds, $this->_params['basedn'], $filter);
+            if (!$search) {
+                throw new Horde_Group_Exception('Could not reach the LDAP server');
+            }
+
+            $result = @ldap_get_entries($this->_ds, $search);
+            @ldap_close($this->_ds);
+            if (!is_array($result) || (count($result) <= 1)) {
+                return array();
+            }
+
+            $groups = array();
+            $current_charset = Horde_Nls::getCharset();
+            for ($i = 0; $i < $result['count']; $i++) {
+                $utf8_dn = Horde_String::convertCharset($result[$i]['dn'], 'UTF-8', $current_charset);
+                $groups[$utf8_dn] = $this->getGroupName($utf8_dn);
+            }
+
+            $cache[$user] = $groups;
+        }
+
+        return $cache[$user];
+    }
+
+    /**
+     * Returns the tree depth of the given group, relative to the base dn.
+     * 0 is returned for any object directly below the base dn.
+     *
+     * @param string $dn  The dn of the object.
+     *
+     * @return intenger  The tree depth of the group.
+     * @throws Horde_Group_Exception
+     */
+    public function getLevel($dn)
+    {
+        $base = @ldap_explode_dn($this->_params['basedn'], 0);
+        if ($base === false) {
+            throw new Horde_Group_Exception('Invalid basedn configured');
+        }
+
+        $group = @ldap_explode_dn($dn, 0);
+        if ($group === false) {
+            throw new Horde_Group_Exception('Invalid group ID passed (bad DN syntax)');
+        }
+
+        return $group['count'] - $base['count'] - 1;
+    }
+
+}
diff --git a/framework/Group/lib/Horde/Group/LdapObject.php b/framework/Group/lib/Horde/Group/LdapObject.php
new file mode 100644 (file)
index 0000000..0ccd667
--- /dev/null
@@ -0,0 +1,129 @@
+<?php
+/**
+ * Extension of the Horde_Group_DataTreeObject class for storing group
+ * information in an LDAP directory.
+ *
+ * Copyright 2005-2010 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   Ben Chavet <ben@horde.org>
+ * @category Horde
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Group
+ */
+class Horde_Group_LdapObject extends Horde_Group_DataTreeObject
+{
+    /**
+     * Constructor.
+     *
+     * @param string $name    The name of this group.
+     * @param string $parent  The dn of the parent of this group.
+     */
+    public function __construct($name, $parent = null)
+    {
+        parent::__construct($name);
+        if ($parent) {
+            $this->data['dn'] = Horde_String::lower($GLOBALS['conf']['group']['params']['gid']) . '=' . $name . ',' . $parent;
+        } else {
+            $this->data['dn'] = Horde_String::lower($GLOBALS['conf']['group']['params']['gid']) . '=' . $name .
+                ',' . Horde_String::lower($GLOBALS['conf']['group']['params']['basedn']);
+        }
+    }
+
+    /**
+     * Get a list of every user that is part of this group (and only
+     * this group).
+     *
+     * @return array  The user list.
+     */
+    public function listUsers()
+    {
+        return $this->_groupOb->listUsers($this->data['dn']);
+    }
+
+    /**
+     * Get a list of every user that is a member of this group and any of
+     * it's subgroups.
+     *
+     * @return array  The complete user list.
+     */
+    public function listAllUsers()
+    {
+        return $this->_groupOb->listAllUsers($this->data['dn']);
+    }
+
+    /**
+     * Take in a list of attributes from the backend and map it to our
+     * internal data array.
+     *
+     * @param array $attributes  The list of attributes from the backend.
+     */
+    protected function _fromAttributes($attributes = array())
+    {
+        $this->data['users'] = array();
+        foreach ($attributes as $key => $value) {
+            if (Horde_String::lower($key) == Horde_String::lower($GLOBALS['conf']['group']['params']['memberuid'])) {
+                if (is_array($value)) {
+                    foreach ($value as $user) {
+                        if ($GLOBALS['conf']['group']['params']['attrisdn']) {
+                            $pattern = '/^' . $GLOBALS['conf']['auth']['params']['uid'] . '=([^,]+).*$/';
+                            $results = array();
+                            preg_match($pattern, $user, $results);
+                            if (isset($results[1])) {
+                                $user = $results[1];
+                            }
+                        }
+                        $this->data['users'][$user] = '1';
+                    }
+                } else {
+                    if ($GLOBALS['conf']['group']['params']['attrisdn']) {
+                        $pattern = '/^' . $GLOBALS['conf']['auth']['params']['uid'] . '=([^,]+).*$/';
+                        $results = array();
+                        preg_match($pattern, $value, $results);
+                        if (isset($results[1])) {
+                            $value = $results[1];
+                        }
+                    }
+                    $this->data['users'][$value] = '1';
+                }
+            } elseif ($key == 'mail') {
+                $this->data['email'] = $value;
+            } else {
+                $this->data[$key] = $value;
+            }
+        }
+    }
+
+    /**
+     * Map this object's attributes from the data array into a format that
+     * can be stored in an LDAP entry.
+     *
+     * @return array  The entry array.
+     */
+    protected function _toAttributes()
+    {
+        $attributes = array();
+        foreach ($this->data as $key => $value) {
+            if ($key == 'users') {
+                foreach ($value as $user => $membership) {
+                    if ($GLOBALS['conf']['group']['params']['attrisdn']) {
+                        $user = $GLOBALS['conf']['auth']['params']['uid'] .
+                            '=' . $user . ',' . $GLOBALS['conf']['auth']['params']['basedn'];
+                    }
+                    $attributes[Horde_String::lower($GLOBALS['conf']['group']['params']['memberuid'])][] = $user;
+                }
+            } elseif ($key == 'email') {
+                if (!empty($value)) {
+                    $attributes['mail'] = $value;
+                }
+            } elseif ($key != 'dn' && $key != Horde_String::lower($GLOBALS['conf']['group']['params']['memberuid'])) {
+                $attributes[$key] = !empty($value) ? $value : ' ';
+            }
+        }
+
+        return $attributes;
+    }
+
+}
diff --git a/framework/Group/lib/Horde/Group/Mock.php b/framework/Group/lib/Horde/Group/Mock.php
new file mode 100644 (file)
index 0000000..a3500fb
--- /dev/null
@@ -0,0 +1,291 @@
+<?php
+/**
+ * Copyright 2008-2010 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
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Group
+ */
+class Horde_Group_Mock extends Horde_Group {
+
+    /**
+     * Constructor.
+     */
+    public function __construct()
+    {
+    }
+
+    /**
+     * Initializes the object.
+     */
+    public function __wakeup()
+    {
+    }
+
+    /**
+     * Returns a new group object.
+     *
+     * @param string $name    The group's name.
+     * @param string $parent  The group's parent's name.
+     *
+     * @return DataTreeObject_Group  A new group object.
+     * @throws Horde_Group_Exception
+     */
+    public function newGroup($name, $parent = GROUP_ROOT)
+    {
+        throw new Horde_Group_Exception('Unsupported.');
+    }
+
+    /**
+     * Returns a DataTreeObject_Group object corresponding to the named group,
+     * with the users and other data retrieved appropriately.
+     *
+     * @param string $name The name of the group to retrieve.
+     *
+     * @throws Horde_Group_Exception
+     */
+    public function getGroup($name)
+    {
+        throw new Horde_Group_Exception('Unsupported.');
+    }
+
+    /**
+     * Returns a group object corresponding to the given unique
+     * ID, with the users and other data retrieved appropriately.
+     *
+     * @param integer $cid  The unique ID of the group to retrieve.
+     *
+     * @throws Horde_Group_Exception
+     */
+    public function getGroupById($cid)
+    {
+        throw new Horde_Group_Exception('Unsupported.');
+    }
+
+    /**
+     * Adds a group to the groups system. The group must first be created with
+     * newGroup(), and have any initial users added to it, before this
+     * function is called.
+     *
+     * @param Horde_Group_DataTreeObject $group  The new group object.
+     *
+     * @throws Horde_Group_Exception
+     */
+    public function addGroup($group)
+    {
+        throw new Horde_Group_Exception('Unsupported.');
+    }
+
+    /**
+     * Stores updated data - users, etc. - of a group to the backend system.
+     *
+     * @param Horde_Group_DataTreeObject $group  The group to update.
+     *
+     * @throws Horde_Group_Exception
+     */
+    public function updateGroup($group)
+    {
+        throw new Horde_Group_Exception('Unsupported.');
+    }
+
+    /**
+     * Removes a group from the groups system permanently.
+     *
+     * @param Horde_Group_DataTreeObject $group  The group to remove.
+     * @param boolean $force               Force to remove every child.
+     *
+     * @throws Horde_Group_Exception
+     */
+    public function removeGroup($group, $force = false)
+    {
+        throw new Horde_Group_Exception('Unsupported.');
+    }
+
+    /**
+     * Retrieves the name of a group.
+     *
+     * @param integer|Horde_Group_DataTreeObject $gid  The id of the group or the
+     *                                           group object to retrieve the
+     *                                           name for.
+     *
+     * @return string  The group's name.
+     */
+    public function getGroupName($gid)
+    {
+        return '';
+    }
+
+    /**
+     * Strips all parent references off of the given group name.
+     *
+     * @param string $group  Name of the group.
+     *
+     * @return The name of the group without parents.
+     */
+    public function getGroupShortName($group)
+    {
+        return '';
+    }
+
+    /**
+     * Retrieves the ID of a group.
+     *
+     * @param string|Horde_Group_DataTreeObject $group  The group name or object to
+     *                                            retrieve the ID for.
+     *
+     * @return integer  The group's ID.
+     */
+    public function getGroupId($group)
+    {
+        return '';
+    }
+
+    /**
+     * Check if a group exists in the system.
+     *
+     * @param string $group  The group to check.
+     *
+     * @return boolean  True if the group exists, false otherwise.
+     */
+    public function exists($group)
+    {
+        return false;
+    }
+
+    /**
+     * Returns a tree of the parents of a child group.
+     *
+     * @param integer $gid  The id of the child group.
+     *
+     * @return array  The group parents tree, with groupnames as the keys.
+     */
+    public function getGroupParents($gid)
+    {
+        return array();
+    }
+
+    /**
+     * Returns the single parent ID of the given group.
+     *
+     * @param integer $gid  The DataTree ID of the child group.
+     *
+     * @return integer  The parent of the given group.
+     */
+    public function getGroupParent($gid)
+    {
+        return null;
+    }
+
+    /**
+     * Returns a flat list of the parents of a child group
+     *
+     * @param integer $gid  The id of the group.
+     *
+     * @return array  A flat list of all of the parents of $group, hashed in
+     *                $id => $name format.
+     */
+    public function getGroupParentList($gid)
+    {
+        return array();
+    }
+
+    /**
+     * Returns a list of all groups, in the format id => groupname.
+     *
+     * @param boolean $refresh  If true, the cached value is ignored and the
+     *                          group list is refreshed from the group backend.
+     *
+     * @return array  ID => groupname hash.
+     */
+    public function listGroups($refresh = false)
+    {
+        return array();
+    }
+
+    /**
+     * Get a list of every user that is a part of this group ONLY.
+     *
+     * @param integer $gid  The ID of the group.
+     *
+     * @return array  The user list.
+     */
+    public function listUsers($gid)
+    {
+        return array();
+    }
+
+    /**
+     * Get a list of every user that is part of the specified group
+     * and any of its subgroups.
+     *
+     * @param integer $group  The ID of the parent group.
+     *
+     * @return array  The complete user list.
+     */
+    public function listAllUsers($gid)
+    {
+        return array();
+    }
+
+    /**
+     * Get a list of every group that $user is in.
+     *
+     * @param string  $user          The user to get groups for.
+     * @param boolean $parentGroups  Also return the parents of any groups?
+     *
+     * @return array  An array of all groups the user is in.
+     */
+    public function getGroupMemberships($user, $parentGroups = false)
+    {
+        return array();
+    }
+
+    /**
+     * Say if a user is a member of a group or not.
+     *
+     * @param string $user        The name of the user.
+     * @param integer $gid        The ID of the group.
+     * @param boolean $subgroups  Return true if the user is in any subgroups
+     *                            of group with ID $gid, also.
+     *
+     * @return boolean
+     */
+    public function userIsInGroup($user, $gid, $subgroups = true)
+    {
+        return false;
+    }
+
+    /**
+     * Returns the nesting level of the given group. 0 is returned for any
+     * object directly below GROUP_ROOT.
+     *
+     * @param integer $gid  The ID of the group.
+     *
+     * @return The nesting level of the group.
+     */
+    public function getLevel($gid)
+    {
+        return 0;
+    }
+
+    /**
+     * Stores the object in the session cache.
+     */
+    public function shutdown()
+    {
+    }
+
+    /**
+     * Returns the properties that need to be serialized.
+     *
+     * @return array  List of serializable properties.
+     */
+    public function __sleep()
+    {
+    }
+
+}
diff --git a/framework/Group/lib/Horde/Group/Sql.php b/framework/Group/lib/Horde/Group/Sql.php
new file mode 100644 (file)
index 0000000..31ee5ab
--- /dev/null
@@ -0,0 +1,710 @@
+<?php
+/**
+ * Copyright 1999-2010 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
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Group
+ */
+class Horde_Group_Sql extends Horde_Group
+{
+    /**
+     * Handle for the current database connection.
+     *
+     * @var Horde_Db_Adapter_Base
+     */
+    public $db;
+
+    /**
+     * Constructor.
+     */
+    public function __construct($params)
+    {
+        $this->_params = $params;
+        $this->db = $GLOBALS['injector']->getInstance('Horde_Db')->getOb('horde', 'group');
+    }
+
+    /**
+     * Initializes the object.
+     */
+    public function __wakeup()
+    {
+    }
+
+    /**
+     * Returns the properties that need to be serialized.
+     *
+     * @return array  List of serializable properties.
+     */
+    public function __sleep()
+    {
+    }
+
+    /**
+     * Stores the object in the session cache.
+     */
+    public function shutdown()
+    {
+    }
+
+    /**
+     * Replace all occurences of ':' in an object name with '.'.
+     *
+     * @param string $name  The name of the object.
+     *
+     * @return string  The encoded name.
+     */
+    public function encodeName($name)
+    {
+        return str_replace(':', '.', $name);
+    }
+
+    /**
+     * Returns a new group object.
+     *
+     * @param string $name    The group's name.
+     * @param string $parent  The group's parent's name.
+     *
+     * @return Horde_Group_SqlObject  A new group object.
+     */
+    public function newGroup($name, $parent = self::ROOT)
+    {
+        if ($parent != self::ROOT) {
+            $name = $this->getGroupName($parent) . ':' . $this->encodeName($name);
+        }
+
+        $group = new Horde_Group_SqlObject($name);
+        $group->setGroupOb($this);
+
+        return $group;
+    }
+
+    /**
+     * Returns a group object corresponding to the named group,
+     * with the users and other data retrieved appropriately.
+     *
+     * @param string $name The name of the group to retrieve.
+     *
+     * @throws Horde_Group_Exception
+     */
+    public function getGroup($name)
+    {
+        if (!isset($this->_groupCache[$name])) {
+            $sql = 'SELECT group_uid, group_email FROM horde_groups WHERE group_name = ?';
+
+            try {
+                $group = $this->db->selectRow($sql, array($name));
+            } catch (Horde_Db_Exception $e) {
+                throw new Horde_Group_Exception($e);
+            }
+
+            if (empty($group)) {
+                throw new Horde_Group_Exception($name . ' does not exist');
+            }
+
+            $sql = 'SELECT user_uid FROM horde_groups_members '
+                . ' WHERE group_uid = ? ORDER BY user_uid ASC';
+
+            try {
+                $users = $this->db->selectValues($sql, array($group['group_uid']));
+            } catch (Horde_Db_Exception $e) {
+                throw new Horde_Group_Exception($e);
+            }
+
+            $object = new Horde_Group_SqlObject($name);
+            $object->id = $group['group_uid'];
+            $object->data['email'] = $group['group_email'];
+
+            if (!empty($users)) {
+                $object->data['users'] = array_flip($users);
+            }
+
+            $this->_groupCache[$name] = $object;
+            $this->_groupCache[$name]->setGroupOb($this);
+            $this->_groupMap[$this->_groupCache[$name]->getId()] = $name;
+        }
+
+        return $this->_groupCache[$name];
+    }
+
+    /**
+     * Returns a group object corresponding to the given unique
+     * ID, with the users and other data retrieved appropriately.
+     *
+     * @param integer $cid  The unique ID of the group to retrieve.
+     *
+     * @throws Horde_Group_Exception
+     */
+    public function getGroupById($cid)
+    {
+        if (isset($this->_groupMap[$cid])) {
+            return $this->_groupCache[$this->_groupMap[$cid]];
+        }
+
+        $sql = 'SELECT group_name, group_email FROM horde_groups WHERE group_uid = ?';
+
+        try {
+            $row = $this->db->selectOne($sql, array($cid));
+        } catch (Horde_Db_Exception $e) {
+            throw new Horde_Group_Exception($e);
+        }
+
+        if (empty($row)) {
+            throw new Horde_Group_Exception($cid . ' does not exist');
+        }
+
+        $sql = 'SELECT user_uid FROM horde_groups_members '
+            . ' WHERE group_uid = ? ORDER BY user_uid ASC';
+
+        try {
+            $users = $this->db->selectValues($sql, array($cid));
+        } catch (Horde_Db_Exception $e) {
+            throw new Horde_Group_Exception($e);
+        }
+
+        $group = new Horde_Group_SqlObject($row['group_name']);
+        $group->id = $cid;
+        $group->data['email'] = $row['group_email'];
+
+        if (!empty($users)) {
+            $group->data['users'] = array_flip($users);
+        }
+
+        $group->setGroupOb($this);
+        $name = $group->getName();
+        $this->_groupCache[$name] = $group;
+        $this->_groupMap[$cid] = $name;
+
+        return $group;
+    }
+
+    /**
+     * Adds a group to the groups system. The group must first be created with
+     * newGroup(), and have any initial users added to it, before this
+     * function is called.
+     *
+     * @param Horde_Group_SqlObject $group  The new group object.
+     *
+     * @throws Horde_Group_Exception
+     * @throws Horde_History_Exception
+     * @throws InvalidArgumentException
+     */
+    public function addGroup(Horde_Group_SqlObject $group)
+    {
+        $group->setGroupOb($this);
+        $name = $group->getName();
+
+        $email = isset($group->data['email']) ? $group->data['email'] : '';
+
+        $query = 'INSERT INTO horde_groups (group_name, group_parents, group_email) VALUES (?, ?, ?)';
+
+        try {
+            $result = $this->db->insert($query, array($name, '', $email));
+        } catch (Horde_Db_Exception $e) {
+            throw new Horde_Group_Exception($e);
+        }
+
+        $group->id = $result;
+
+        if (!empty($group->data['users'])) {
+            $query = 'INSERT INTO horde_groups_members (group_uid, user_uid)'
+                .' VALUES (?, ?)';
+            foreach ($group->data['users'] as $user) {
+                try {
+                    $this->db->insert($query, array($result, $user));
+                } catch (Horde_Db_Exception $e) {
+                    throw new Horde_Group_Exception($e);
+                }
+            }
+        }
+
+        $this->_groupCache[$name] = $group;
+        $this->_groupMap[$group_id] = $name;
+        if (isset($this->_groupList)) {
+            $this->_groupList[$group_id] = $name;
+        }
+
+        /* Log the addition of the group in the history log. */
+        $GLOBALS['injector']->getInstance('Horde_History')->log($this->getGUID($group), array('action' => 'add'), true);
+    }
+
+    /**
+     * Stores updated data - users, etc. - of a group to the backend system.
+     *
+     * @param Horde_Group_SqlObject $group  The group to update.
+     *
+     * @throws Horde_Group_Exception
+     * @throws Horde_History_Exception
+     * @throws InvalidArgumentException
+     */
+    public function updateGroup(Horde_Group_SqlObject $group)
+    {
+        $query = 'UPDATE horde_groups SET group_email = ? WHERE group_uid = ?';
+
+        try {
+            $this->db->update($query, array($this->data['email'], $this->id));
+        } catch (Horde_Db_Exception $e) {
+            throw new Horde_Group_Exception($e);
+        }
+
+        $query = 'DELETE FROM horde_groups_members WHERE group_uid = ?';
+
+        try {
+            $this->db->delete($query, array($this->id));
+        } catch (Horde_Db_Exception $e) {
+            throw new Horde_Group_Exception($e);
+        }
+
+        $query = 'INSERT INTO horde_groups_members (group_uid, user_uid)' .
+                 ' VALUES (?, ?)';
+        foreach ($this->data['users'] as $user) {
+            try {
+                $this->db->insert($query, array(intval($this->id), $user));
+            } catch (Horde_Db_Exception $e) {
+                throw new Horde_Group_Exception($e);
+            }
+        }
+
+        $this->_groupCache[$group->getName()] = &$group;
+
+        /* Log the update of the group users on the history log. */
+        $history = $GLOBALS['injector']->getInstance('Horde_History');
+        $guid = $this->getGUID($group);
+        foreach ($group->getAuditLog() as $userId => $action) {
+            $history->log($guid, array('action' => $action, 'user' => $userId), true);
+        }
+
+        $group->clearAuditLog();
+
+        /* Log the group modification. */
+        $history->log($guid, array('action' => 'modify'), true);
+
+        return $result;
+    }
+
+    /**
+     * Removes a group from the groups system permanently.
+     *
+     * @param Horde_Group_SqlObject $group  The group to remove.
+     * @param boolean $force               Force to remove every child.
+     *
+     * @throws Horde_Group_Exception
+     * @throws Horde_History_Exception
+     * @throws InvalidArgumentException
+     */
+    public function removeGroup(Horde_Group_SqlObject $group, $force = false)
+    {
+        $id = $group->getId();
+        $name = $group->getName();
+        unset($this->_groupMap[$id]);
+        if (isset($this->_groupList)) {
+            unset($this->_groupList[$id]);
+        }
+        unset($this->_groupCache[$name]);
+
+        $GLOBALS['injector']->getInstance('Horde_History')->log($this->getGUID($group), array('action' => 'delete'), true);
+
+        $query = 'DELETE FROM horde_groups_members WHERE group_uid = ?';
+
+        try {
+            $this->db->delete($query, array($id));
+        } catch (Horde_Db_Exception $e) {
+            throw new Horde_Group_Exception($e);
+        }
+
+        $query = 'DELETE FROM horde_groups WHERE group_uid = ?';
+        try {
+            $this->db->delete($query, array($id));
+        } catch (Horde_Db_Exception $e) {
+            throw new Horde_Group_Exception($e);
+        }
+
+        if ($force) {
+            $query = 'DELETE FROM horde_groups WHERE group_name LIKE ?';
+
+            try {
+                $this->db->delete($query, array($name . ':%'));
+            } catch (Horde_Db_Exception $e) {
+                throw new Horde_Group_Exception($e);
+            }
+        }
+    }
+
+    /**
+     * Retrieves the name of a group.
+     *
+     * @param integer|Horde_Group_SqlObject $gid  The id of the group or the
+     *                                            group object to retrieve the
+     *                                            name for.
+     *
+     * @return string  The group's name.
+     * @throws Horde_Group_Exception
+     */
+    public function getGroupName($gid)
+    {
+        if ($gid instanceof Horde_Group_SqlObject) {
+            $gid = $gid->getId();
+        }
+
+        if (isset($this->_groupMap[$gid])) {
+            return $this->_groupMap[$gid];
+        }
+        if (isset($this->_groupList[$gid])) {
+            return $this->_groupList[$gid];
+        }
+
+        $query = 'SELECT group_name FROM horde_groups WHERE group_uid = ?';
+
+        try {
+            return $this->db->selectValue($query, $gid);
+        } catch (Horde_Db_Exception $e) {
+            throw new Horde_Group_Exception($e);
+        }
+    }
+
+    /**
+     * Strips all parent references off of the given group name.
+     *
+     * @param string $group  Name of the group.
+     *
+     * @return The name of the group without parents.
+     */
+    public function getGroupShortName($group)
+    {
+        /* If there are several components to the name, explode and get the
+         * last one, otherwise just return the name. */
+        if (strpos($group, ':') !== false) {
+            $name = explode(':', $group);
+            return array_pop($name);
+        }
+
+        return $group;
+    }
+
+    /**
+     * Retrieves the ID of a group.
+     *
+     * @param string|Horde_Group_SqlObject $group  The group name or object to
+     *                                             retrieve the ID for.
+     *
+     * @return integer  The group's ID.
+     * @throws Horde_Group_Exception
+     */
+    public function getGroupId($group)
+    {
+        if ($group instanceof Horde_Group_SqlObject) {
+            return $group->getId();
+        }
+
+        $id = array_search($group, $this->_groupMap);
+        if ($id !== false) {
+            return $id;
+        }
+        if (isset($this->_groupList)) {
+            $id = array_search($group, $this->_groupList);
+            if ($id !== false) {
+                return $id;
+            }
+        }
+
+        $query = 'SELECT group_uid FROM horde_groups WHERE group_name = ?';
+
+        try {
+            return $this->db->selectValue($query, $group);
+        } catch (Horde_Db_Exception $e) {
+            throw new Horde_Group_Exception($e);
+        }
+    }
+
+    /**
+     * Check if a group exists in the system.
+     *
+     * @param string $group  The group to check.
+     *
+     * @return boolean  True if the group exists, false otherwise.
+     */
+    public function exists($group)
+    {
+        if (isset($this->_groupCache[$group]) ||
+            (isset($this->_groupList) &&
+             array_search($group, $this->_groupList) !== false)) {
+            return true;
+        }
+
+        $query = 'SELECT COUNT(*) FROM horde_groups WHERE group_name = ?';
+
+        try {
+            return (bool)$this->db->selectValue($query, $group);
+        } catch (Horde_Db_Exception $e) {
+            return false;
+        }
+    }
+
+    /**
+     * Returns a tree of the parents of a child group.
+     *
+     * @param integer $gid  The id of the child group.
+     *
+     * @return array  The group parents tree, with groupnames as the keys.
+     * @throws Horde_Group_Exception
+     */
+    function getGroupParents($gid)
+    {
+        if (!isset($this->_parentTree[$gid])) {
+            $name = $this->getGroupName($gid);
+            $this->_parentTree[$gid] = $this->_getGroupParents($name);
+        }
+
+        return $this->_parentTree[$gid];
+    }
+
+    /**
+     * 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.
+     */
+    protected function _getGroupParents($child)
+    {
+        if (($pos = strrpos($child, ':')) !== false) {
+            $child = substr($child, 0, $pos);
+        }
+
+        return $this->_getParents($child);
+    }
+
+    /**
+     */
+    protected function _getParents($parents)
+    {
+        $mother = array();
+        if (!empty($parents)) {
+            $pname = $parents;
+            $parents = substr($parents, 0, strrpos($parents, ':'));
+            $mother[$pname] = $this->_getParents($parents);
+        } else {
+            return array(self::ROOT => true);
+        }
+
+        return $mother;
+    }
+
+    /**
+     * Returns the single parent ID of the given group.
+     *
+     * @param integer $gid  The ID of the child group.
+     *
+     * @return integer  The parent of the given group.
+     */
+    public function getGroupParent($gid)
+    {
+        if (!isset($this->_groupParents[$gid])) {
+            $name = $this->getGroupName($gid);
+            if (($pos = strrpos($name, ':')) !== false) {
+                $this->_groupParents[$gid] = $this->getGroupId(substr($name, 0, $pos));
+            } else {
+                $this->_groupParents[$gid] = self::ROOT;
+            }
+        }
+
+        return $this->_groupParents[$gid];
+    }
+
+    /**
+     * Returns a flat list of the parents of a child group
+     *
+     * @param integer $gid  The id of the group.
+     *
+     * @return array  A flat list of all of the parents of $group, hashed in
+     *                $id => $name format.
+     *@throws Horde_Group_Exception
+     */
+    public function getGroupParentList($gid)
+    {
+        if (!isset($this->_groupParentList[$gid])) {
+            $name = $this->getGroupName($gid);
+            $pos = strpos($name, ':');
+            if ($pos == false) {
+                $this->_groupParentList[$gid] = array();
+                return $this->_groupParentList[$gid];
+            }
+
+            $parents = array();
+            while ($pos) {
+                $name = substr($name, 0, $pos);
+                $parents[] = $name;
+                $pos = strpos($name, ':');
+            }
+
+            $query = 'SELECT group_uid, group_name FROM horde_groups '
+                . ' WHERE group_name IN (' . str_repeat('?, ', count($parents) - 1) . '?) ';
+            try {
+                $this->_groupParentList[$gid] = $this->db->selectAssoc($query, $parents);
+            } catch (Horde_Db_Exception $e) {
+                throw new Horde_Group_Exception($e);
+            }
+        }
+
+        return $this->_groupParentList[$gid];
+    }
+
+    /**
+     * Returns a flat list of the parents of a child group
+     *
+     * @param integer $gid  The id of the group.
+     *
+     * @return array  A flat list of all of the parents of $group, hashed in
+     *                $id => $name format.
+     */
+    protected function _getGroupParentNameList($name)
+    {
+        $parents = array();
+
+        while ($pos) {
+            $name = substr($name, 0, $pos);
+            $parents[] = $name;
+            $pos = strpos($name, ':');
+        }
+
+        return $parents;
+    }
+
+    /**
+     * Returns a list of all groups, in the format id => groupname.
+     *
+     * @param boolean $refresh  If true, the cached value is ignored and the
+     *                          group list is refreshed from the group backend.
+     *
+     * @return array  ID => groupname hash.
+     * @throws Horde_Group_Exception
+     */
+    public function listGroups($refresh = false)
+    {
+        if ($refresh || !isset($this->_groupList)) {
+            $sql = 'SELECT group_uid, group_name FROM horde_groups ORDER BY group_uid';
+            try {
+                $this->_groupList = $this->db->selectAssoc($sql);
+            } catch (Horde_Db_Exception $e) {
+                throw new Horde_Group_Exception($e);
+            }
+        }
+
+        return $this->_groupList;
+    }
+
+    /**
+     * Get a list of every user that is part of the specified group
+     * and any of its subgroups.
+     *
+     * @param integer $group  The ID of the parent group.
+     *
+     * @return array  The complete user list.
+     * @throws Horde_Group_Exception
+     */
+    public function listAllUsers($gid)
+    {
+        if (!isset($this->_subGroups[$gid])) {
+            // Get a list of every group that is a sub-group of $group.
+            $name = $this->getGroupName($gid);
+            $query = 'SELECT group_uid FROM horde_groups WHERE group_name LIKE ?';
+
+            try {
+                $this->_subGroups[$gid] = $this->db->selectValues($query, array($name .  ':%'));
+            } catch (Horde_Db_Exception $e) {
+                throw new Horde_Group_Exception($e);
+            }
+            $this->_subGroups[$gid][] = $gid;
+        }
+
+        $users = array();
+        foreach ($this->_subGroups[$gid] as $groupId) {
+            $users = array_merge($users, $this->listUsers($groupId));
+        }
+
+        return array_values(array_flip(array_flip($users)));
+    }
+
+    /**
+     * Get a list of every group that $user is in.
+     *
+     * @param string  $user          The user to get groups for.
+     * @param boolean $parentGroups  Also return the parents of any groups?
+     *
+     * @return array  An array of all groups the user is in.
+     * @throws Horde_Group_Exception
+     */
+    public function getGroupMemberships($user, $parentGroups = false)
+    {
+        if (isset($_SESSION['horde']['groups']['m'][$user][$parentGroups])) {
+            return $_SESSION['horde']['groups']['m'][$user][$parentGroups];
+        }
+
+        $sql = 'SELECT g.group_uid AS group_uid, g.group_name AS group_name FROM horde_groups g, horde_groups_members m '
+            . ' WHERE m.user_uid = ? AND g.group_uid = m.group_uid ORDER BY g.group_name';
+        try {
+            $result = $this->db->selectAll($sql, $user);
+        } catch (Horde_Db_Exception $e) {
+            throw new Horde_Group_Exception($e);
+        }
+
+        $groups = array();
+        foreach ($result as $row) {
+            $groups[(int)$row['group_uid']] = $this->getGroupShortName($row['group_name']);
+        }
+
+        if ($parentGroups) {
+            foreach ($groups as $id => $g) {
+                $groups += $this->getGroupParentList($id);
+            }
+        }
+
+        $_SESSION['horde']['groups']['m'][$user][$parentGroups] = $groups;
+
+        return $groups;
+    }
+
+    /**
+     * Say if a user is a member of a group or not.
+     *
+     * @param string $user        The name of the user.
+     * @param integer $gid        The ID of the group.
+     * @param boolean $subgroups  Return true if the user is in any subgroups
+     *                            of group with ID $gid, also.
+     *
+     * @return boolean
+     */
+    public function userIsInGroup($user, $gid, $subgroups = true)
+    {
+        if (isset($_SESSION['horde']['groups']['i'][$user][$subgroups][$gid])) {
+            return $_SESSION['horde']['groups']['i'][$user][$subgroups][$gid];
+        }
+
+        if ($subgroups) {
+            try {
+                $groups = $this->getGroupMemberships($user, true);
+            } catch (Horde_Group_Exception $e) {
+                Horde::logMessage($e, 'ERR');
+                return false;
+            }
+
+            $result = !empty($groups[$gid]);
+        } else {
+            $query = 'SELECT COUNT(*) FROM horde_groups_members WHERE group_uid = ? AND user_uid = ?';
+            try {
+                $result = $this->db->selectValue($query, array($gid, $user));
+            } catch (Horde_Db_Exception $e) {
+                $result = false;
+            }
+
+        }
+
+        $_SESSION['horde']['groups']['i'][$user][$subgroups][$gid] = (bool)$result;
+        return (bool)$result;
+    }
+
+}
diff --git a/framework/Group/lib/Horde/Group/SqlObject.php b/framework/Group/lib/Horde/Group/SqlObject.php
new file mode 100644 (file)
index 0000000..e37be8f
--- /dev/null
@@ -0,0 +1,133 @@
+<?php
+/**
+ * Copyright 1999-2010 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
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Group
+ */
+class Horde_Group_SqlObject extends Horde_Group_DataTreeObject
+{
+    /**
+     * The unique name of this object.
+     * These names have the same requirements as other object names - they must
+     * be unique, etc.
+     *
+     * @var string
+     */
+    public $name;
+
+    /**
+     * The unique name of this object.
+     * These names have the same requirements as other object names - they must
+     * be unique, etc.
+     *
+     * @var integer
+     */
+    public $id;
+
+    /**
+     * Key-value hash that will be serialized.
+     *
+     * @see getData()
+     * @var array
+     */
+    public $data = array();
+
+    /**
+     * Constructor.
+     *
+     * @param string $name  The name of the group.
+     */
+    public function __construct($name)
+    {
+        $this->name = $name;
+    }
+
+    /**
+     * Gets the ID of this object.
+     *
+     * @return string  The object's ID.
+     */
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    /**
+     * Gets the name of this object.
+     *
+     * @return string The object name.
+     */
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    /**
+     * 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 get($attribute)
+    {
+        return isset($this->data[$attribute])
+            ? $this->data[$attribute]
+            : null;
+    }
+
+    /**
+     * Sets one of the attributes of the object.
+     *
+     * @param string $attribute  The attribute to set.
+     * @param mixed $value       The value for $attribute.
+     */
+    public set($attribute, $value)
+    {
+        $this->data[$attribute] = $value;
+    }
+
+    /**
+     * Save group
+     *
+     * @throws Horde_Group_Exception
+     */
+    public function save()
+    {
+        if (isset($this->data['email'])) {
+            $query = 'UPDATE horde_groups SET group_email = ? WHERE group_uid = ?';
+            try {
+                $this->_groupOb->db->update($query, array($this->data['email'], $this->id));
+            } catch (Horde_Db_Exception $e) {
+                throw new Horde_Group_Exception($e);
+            }
+        }
+
+        $query = 'DELETE FROM horde_groups_members WHERE group_uid = ?';
+
+        try {
+            $this->_groupOb->db->delete($query, array($this->id));
+        } catch (Horde_Db_Exception $e) {
+            throw new Horde_Group_Exception($e);
+        }
+
+        if (!empty($this->data['users'])) {
+            $query = 'INSERT INTO horde_groups_members (group_uid, user_uid)' .
+                     ' VALUES (?, ?)';
+            foreach ($this->data['users'] as $user) {
+                try {
+                    $this->db->insert($query, array(intval($this->id), $user));
+                } catch (Horde_Db_Exception $e) {
+                    throw new Horde_Group_Exception($e);
+                }
+            }
+        }
+    }
+
+}
index 37f50da..faa8e20 100644 (file)
@@ -20,72 +20,128 @@ http://pear.php.net/dtd/package-2.0.xsd">
   <email>jan@horde.org</email>
   <active>yes</active>
  </lead>
- <date>2008-09-16</date>
+ <date>2010-06-01</date>
  <version>
-  <release>0.1.0</release>
-  <api>0.1.0</api>
+  <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>* Added a mock driver for installations that don't need groups (Request #6157).
-* Added a beta SQL Group driver (Request #6175).
-* Removed unused renameGroup() function.
-* Fixed loading subclasses before unserializing session objects (Bug #4650)
-* Added caching.
-* Fixed getGroupParents().
-* Fixed listAllUsers().
-* Switched from hook functions for every group to a single hook function for all groups (Request #4324).
-* Added a Group driver for the Kolab groupware server.
-* Allow group members to be stored as DNs in LDAP driver (Bug #4131).
-* Significant changes to the LDAP Group driver (Bug #4135).
-* Fixed chicken and egg problem for creating the first LDAP group (Bug #4668).
-* Fixed nextgid calculation in the LDAP driver (Bug #4699).
-* UTF-8-encoded DNs in the LDAP Groups driver (Bugs #4692 and #4918).
+ <notes>* Throw exceptions, not PEAR_Errors.
+ * Initial Horde 4 release.
  </notes>
  <contents>
   <dir name="/">
-   <dir name="Group">
-    <file baseinstalldir="/Horde" name="hooks.php" role="php" />
-    <file baseinstalldir="/Horde" name="kolab.php" role="php" />
-    <file baseinstalldir="/Horde" name="ldap.php" role="php" />
-    <file baseinstalldir="/Horde" name="mock.php" role="php" />
-    <file baseinstalldir="/Horde" name="sql.php" role="php" />
-    <file baseinstalldir="/Horde" name="contactlists.php" role="php" />
-   </dir> <!-- /Group -->
-   <file baseinstalldir="/Horde" name="Group.php" role="php" />
+   <dir name="lib">
+    <dir name="Horde">
+     <dir name="Group">
+      <file name="ContactListObject.php" role="php" />
+      <file name="Contactlists.php" role="php" />
+      <file name="DataTreeObject.php" role="php" />
+      <file name="Exception.php" role="php" />
+      <file name="Hooks.php" role="php" />
+      <file name="Kolab.php" role="php" />
+      <file name="KolabObject.php" role="php" />
+      <file name="Ldap.php" role="php" />
+      <file name="LdapObject.php" role="php" />
+      <file name="Mock.php" role="php" />
+      <file name="Sql.php" role="php" />
+      <file name="SqlObject.php" role="php" />
+     </dir> <!-- /lib/Horde/Group -->
+     <file name="Group.php" role="php" />
+    </dir> <!-- /lib/Horde -->
+   </dir> <!-- /lib -->
   </dir> <!-- / -->
  </contents>
  <dependencies>
   <required>
    <php>
-    <min>4.3.0</min>
+    <min>5.2.0</min>
    </php>
    <pearinstaller>
-    <min>1.5.4</min>
+    <min>1.7.0</min>
    </pearinstaller>
    <package>
-    <name>Horde_Framework</name>
+   <name>Core</name>
     <channel>pear.horde.org</channel>
    </package>
    <package>
-    <name>Horde_DataTree</name>
+    <name>DataTree</name>
     <channel>pear.horde.org</channel>
    </package>
    <package>
-    <name>Auth</name>
+    <name>Exception</name>
     <channel>pear.horde.org</channel>
    </package>
-   <extension>
-    <name>gettext</name>
-   </extension>
   </required>
+   <package>
+    <name>Util</name>
+    <channel>pear.horde.org</channel>
+   </package>
+  <optional>
+   <package>
+    <name>Auth</name>
+    <channel>pear.horde.org</channel>
+   </package>
+   <package>
+    <name>Db</name>
+    <channel>pear.horde.org</channel>
+   </package>
+   <package>
+    <name>Ldap</name>
+    <channel>pear.horde.org</channel>
+   </package>
+  </optional>
  </dependencies>
- <phprelease />
+ <phprelease>
+  <filelist>
+   <install name="lib/Horde/Group/ContactListObject.php" as="Horde/Group/ContactListObject.php" />
+   <install name="lib/Horde/Group/Contactlists.php" as="Horde/Group/Contactlists.php" />
+   <install name="lib/Horde/Group/DataTreeObject.php" as="Horde/Group/DataTreeObject.php" />
+   <install name="lib/Horde/Group/Exception.php" as="Horde/Group/Exception.php" />
+   <install name="lib/Horde/Group/Hooks.php" as="Horde/Group/Hooks.php" />
+   <install name="lib/Horde/Group/Kolab.php" as="Horde/Group/Kolab.php" />
+   <install name="lib/Horde/Group/KolabObject.php" as="Horde/Group/KolabObject.php" />
+   <install name="lib/Horde/Group/Ldap.php" as="Horde/Group/Ldap.php" />
+   <install name="lib/Horde/Group/LdapObject.php" as="Horde/Group/LdapObject.php" />
+   <install name="lib/Horde/Group/Mock.php" as="Horde/Group/Mock.php" />
+   <install name="lib/Horde/Group/Sql.php" as="Horde/Group/Sql.php" />
+   <install name="lib/Horde/Group/SqlObject.php" as="Horde/Group/SqlObject.php" />
+   <install name="lib/Horde/Group.php" as="Horde/Group.php" />
+  </filelist>
+ </phprelease>
  <changelog>
   <release>
+   <date>2008-09-16</date>
+   <version>
+    <release>0.1.0</release>
+    <api>0.1.0</api>
+   </version>
+   <stability>
+    <release>beta</release>
+    <api>beta</api>
+   </stability>
+   <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
+   <notes>* Added a mock driver for installations that don't need groups (Request #6157).
+   * Added a beta SQL Group driver (Request #6175).
+   * Removed unused renameGroup() function.
+   * Fixed loading subclasses before unserializing session objects (Bug #4650)
+   * Added caching.
+   * Fixed getGroupParents().
+   * Fixed listAllUsers().
+   * Switched from hook functions for every group to a single hook function for all groups (Request #4324).
+   * Added a Group driver for the Kolab groupware server.
+   * Allow group members to be stored as DNs in LDAP driver (Bug #4131).
+   * Significant changes to the LDAP Group driver (Bug #4135).
+   * Fixed chicken and egg problem for creating the first LDAP group (Bug #4668).
+   * Fixed nextgid calculation in the LDAP driver (Bug #4699).
+   * UTF-8-encoded DNs in the LDAP Groups driver (Bugs #4692 and #4918).
+   </notes>
+  </release>
+  <release>
    <version>
     <release>0.0.2</release>
     <api>0.0.2</api>
index d589976..45f75e4 100644 (file)
@@ -366,8 +366,7 @@ class Horde_Perms
         if (isset($permission->data['groups']) &&
             is_array($permission->data['groups']) &&
             count($permission->data['groups'])) {
-            require_once 'Horde/Group.php';
-            $groups = Group::singleton();
+            $groups = Horde_Group::singleton();
 
             $composite_perm = null;
             $type = $permission->get('type');
index ccebe21..9e973da 100644 (file)
@@ -300,17 +300,18 @@ class Horde_Share_Datatree extends Horde_Share
 
             // If the user has any group memberships, check for those also.
             // @TODO: inject
-            require_once 'Horde/Group.php';
-            $group = &Group::singleton();
-            $groups = $group->getGroupMemberships($userid, true);
-            if (!($groups instanceof PEAR_Error) && $groups) {
-                // (name == perm_groups and key in ($groups) and val & $perm)
-                $criteria['OR'][] = array(
-                    'AND' => array(
-                        array('field' => 'name', 'op' => '=', 'test' => 'perm_groups'),
-                        array('field' => 'key', 'op' => 'IN', 'test' => array_keys($groups)),
-                        array('field' => 'value', 'op' => '&', 'test' => $perm)));
-            }
+            try {
+                $group = Horde_Group::singleton();
+                $groups = $group->getGroupMemberships($userid, true);
+                if ($groups) {
+                    // (name == perm_groups and key in ($groups) and val & $perm)
+                    $criteria['OR'][] = array(
+                        'AND' => array(
+                            array('field' => 'name', 'op' => '=', 'test' => 'perm_groups'),
+o                           array('field' => 'key', 'op' => 'IN', 'test' => array_keys($groups)),
+                            array('field' => 'value', 'op' => '&', 'test' => $perm)));
+                }
+            } catch (Horde_Group_Exception $e) {}
         } else {
             $criteria = array(
                 'AND' => array(
index def009f..d919537 100644 (file)
@@ -727,21 +727,22 @@ class Horde_Share_Sql extends Horde_Share
 
             // If the user has any group memberships, check for those also.
             // @TODO: Inject the group driver
-            require_once 'Horde/Group.php';
-            $group = Group::singleton();
-            $groups = $group->getGroupMemberships($userid, true);
-            if (!is_a($groups, 'PEAR_Error') && $groups) {
-                // (name == perm_groups and key in ($groups) and val & $perm)
-                $ids = array_keys($groups);
-                $group_ids = array();
-                foreach ($ids as $id) {
-                    $group_ids[] = $this->_db->quote((string)$id);
+            try {
+                $group = Horde_Group::singleton();
+                $groups = $group->getGroupMemberships($userid, true);
+                if ($groups) {
+                    // (name == perm_groups and key in ($groups) and val & $perm)
+                    $ids = array_keys($groups);
+                    $group_ids = array();
+                    foreach ($ids as $id) {
+                        $group_ids[] = $this->_db->quote((string)$id);
+                    }
+                    $query .= ' LEFT JOIN ' . $this->_table . '_groups g ON g.share_id = s.share_id';
+                    $where .= ' OR (g.group_uid IN (' . implode(',', $group_ids) . ')'
+                        . ' AND (' . Horde_SQL::buildClause($this->_db, 'g.perm', '&', $perm) . '))';
                 }
-                $query .= ' LEFT JOIN ' . $this->_table . '_groups g ON g.share_id = s.share_id';
-                $where .= ' OR (g.group_uid IN (' . implode(',', $group_ids) . ')'
-                    . ' AND (' . Horde_SQL::buildClause($this->_db, 'g.perm', '&', $perm) . '))';
-            } elseif ($groups instanceof PEAR_Error) {
-                Horde::logMessage($groups, 'ERR');
+            } catch (Horde_Group_Exception $e) {
+                Horde::logMessage($e, 'ERR');
             }
         } else {
             $where = '(' . Horde_SQL::buildClause($this->_db, 's.perm_guest', '&', $perm) . ')';
index e1d48e8..a379d1a 100644 (file)
@@ -8,7 +8,7 @@
  * @package Horde_Share
  */
 class Horde_Share_Sql_Hierarchical extends Horde_Share_Sql
-{    
+{
     /**
      * The Horde_Share_Object subclass to instantiate objects as
      *
@@ -214,20 +214,21 @@ class Horde_Share_Sql_Hierarchical extends Horde_Share_Sql
 
                 // If the user has any group memberships, check for those also.
                 // @TODO: Inject the group driver
-                require_once 'Horde/Group.php';
-                $group = &Group::singleton();
-                $groups = $group->getGroupMemberships($userid, true);
-                if (!($groups instanceof PEAR_Error) && $groups) {
-                    // (name == perm_groups and key in ($groups) and val & $perm)
-                    $ids = array_keys($groups);
-                    $group_ids = array();
-                    foreach ($ids as $id) {
-                        $group_ids[] = $this->_db->quote((string)$id);
+                try {
+                    $group = Horde_Group::singleton();
+                    $groups = $group->getGroupMemberships($userid, true);
+                    if ($groups) {
+                        // (name == perm_groups and key in ($groups) and val & $perm)
+                        $ids = array_keys($groups);
+                        $group_ids = array();
+                        foreach ($ids as $id) {
+                            $group_ids[] = $this->_db->quote((string)$id);
+                        }
+                        $query .= ' LEFT JOIN ' . $this->_table . '_groups AS g ON g.share_id = s.share_id';
+                        $where .= ' OR (g.group_uid IN (' . implode(',', $group_ids) . ')'
+                            . ' AND (' . Horde_SQL::buildClause($this->_db, 'g.perm', '&', $perm) . '))';
                     }
-                    $query .= ' LEFT JOIN ' . $this->_table . '_groups AS g ON g.share_id = s.share_id';
-                    $where .= ' OR (g.group_uid IN (' . implode(',', $group_ids) . ')'
-                        . ' AND (' . Horde_SQL::buildClause($this->_db, 'g.perm', '&', $perm) . '))';
-                }
+                } catch (Horde_Group_Exception $e) {}
             }
         }
 
index f5220a4..7982dc3 100644 (file)
@@ -11,8 +11,7 @@
 require_once dirname(__FILE__) . '/../lib/Application.php';
 Horde_Registry::appInit('horde', array('admin' => true));
 
-require_once 'Horde/Group.php';
-$groups = Group::singleton();
+$groups = Horde_Group::singleton();
 $auth = $injector->getInstance('Horde_Auth')->getOb();
 
 $form = null;
@@ -22,92 +21,99 @@ $cid = Horde_Util::getFormData('cid');
 
 switch ($actionID) {
 case 'addchild':
-    if ($cid == GROUP_ROOT) {
+    if ($cid == Horde_Group::ROOT) {
         $form = 'addchild.inc';
         $gname = _("All Groups");
     } else {
-        $group = &$groups->getGroupById($cid);
-        if (!is_a($group, 'PEAR_Error')) {
+        try {
+            $group = $groups->getGroupById($cid);
             $gname = $group->getShortName();
             $form = 'addchild.inc';
-        }
+        } catch (Horde_Group_Exception $e) {]
     }
     break;
 
 case 'addchildform':
     $parent = $cid;
-    if ($parent == GROUP_ROOT) {
-        $child = &$groups->newGroup(Horde_Util::getFormData('child'));
-    } else {
-        $child = &$groups->newGroup(Horde_Util::getFormData('child'), $parent);
-    }
-    if (is_a($child, 'PEAR_Error')) {
-        Horde::logMessage($child, 'ERR');
-        $notification->push(sprintf(_("Group was not created: %s."), $child->getMessage()), 'horde.error');
+    try {
+        $child = ($parent == Horde_Group::ROOT)
+            ? $groups->newGroup(Horde_Util::getFormData('child'))
+            : $groups->newGroup(Horde_Util::getFormData('child'), $parent);
+    } catch (Horde_Group_Exception $e) {
+        Horde::logMessage($e, 'ERR');
+        $notification->push(sprintf(_("Group was not created: %s."), $e->getMessage()), 'horde.error');
         break;
     }
 
-    $result = $groups->addGroup($child);
-    if (is_a($result, 'PEAR_Error')) {
-        Horde::logMessage($result, 'ERR');
-        $notification->push(sprintf(_("\"%s\" was not created: %s."), $child->getShortName(), $result->getMessage()), 'horde.error');
-    } else {
+    try {
+        $groups->addGroup($child);
         $notification->push(sprintf(_("\"%s\" was added to the groups system."), $child->getShortName()), 'horde.success');
         $group = $child;
         $form = 'edit.inc';
         $reload = true;
+    } catch (Horde_Group_Exception $e) {
+        Horde::logMessage($e, 'ERR');
+        $notification->push(sprintf(_("\"%s\" was not created: %s."), $child->getShortName(), $e->getMessage()), 'horde.error');
     }
     break;
 
 case 'delete':
-    $group = &$groups->getGroupById($cid);
-    if (!is_a($group, 'PEAR_Error')) {
+    try {
+        $group = $groups->getGroupById($cid);
         $form = 'delete.inc';
-    }
+    } catch (Horde_Group_Exception $e) {}
     break;
 
 case 'deleteform':
     if (Horde_Util::getFormData('confirm') == _("Delete")) {
-        $group = &$groups->getGroupById($cid);
-        if (is_a($group, 'PEAR_Error')) {
+        try {
+            $group = $groups->getGroupById($cid);
+        } catch (Horde_Group_Exception $e) {
             $notification->push(_("Attempt to delete a non-existent group."), 'horde.error');
-        } else {
-            $result = $groups->removeGroup($group, true);
-            if (is_a($result, 'PEAR_Error')) {
-                $notification->push(sprintf(_("Unable to delete \"%s\": %s."), $group->getShortName(), $result->getMessage()), 'horde.error');
-             } else {
-                $notification->push(sprintf(_("Successfully deleted \"%s\"."), $group->getShortName()), 'horde.success');
-                $cid = null;
-                $reload = true;
-            }
+            break;
+        }
+
+        try {
+            $groups->removeGroup($group, true);
+            $notification->push(sprintf(_("Successfully deleted \"%s\"."), $group->getShortName()), 'horde.success');
+            $cid = null;
+            $reload = true;
+        } catch (Horde_Group_Exception $e) {
+            $notification->push(sprintf(_("Unable to delete \"%s\": %s."), $group->getShortName(), $e->getMessage()), 'horde.error');
         }
     }
     break;
 
 case 'edit':
-    $group = &$groups->getGroupById($cid);
-    if (!is_a($group, 'PEAR_Error')) {
+    try {
+        $group = $groups->getGroupById($cid);
         $form = 'edit.inc';
-    } elseif (($category = Horde_Util::getFormData('category')) !== null) {
-        $group = &$groups->getGroup($category);
-        if (!is_a($group, 'PEAR_Error')) {
+        break;
+    } catch (Horde_Group_Exception $e) {}
+
+    if (($category = Horde_Util::getFormData('category')) !== null) {
+        try {
+            $group = $groups->getGroup($category);
             $form = 'edit.inc';
-        } elseif (Horde_Util::getFormData('autocreate')) {
+            break;
+        } catch (Horde_Group_Exception $e) {}
+
+        if (Horde_Util::getFormData('autocreate')) {
             $parent = Horde_Util::getFormData('parent');
-            $group = &$groups->newGroup($category);
-            $result = $groups->addGroup($group, $parent);
-            if (!is_a($result, 'PEAR_Error')) {
+            $group = $groups->newGroup($category);
+            try {
+                $groups->addGroup($group, $parent);
                 $form = 'edit.inc';
-            }
+            } catch (Horde_Group_Exception $e) {}
         }
     }
     break;
 
 case 'editform':
-    $group = &$groups->getGroupById($cid);
+    $group = $groups->getGroupById($cid);
 
     // make a copy of the group so we can restore it if there is an error.
-    $restore = $group;
+    $restore = clone $group;
 
     // Add any new users.
     $newuser = Horde_Util::getFormData('new_user');
@@ -133,14 +139,13 @@ case 'editform':
     $group->set('email', Horde_Util::getFormData('email'));
 
     // Save the group to the backend.
-    $result = $group->save();
-
-    if (is_a($result, 'PEAR_Error')) {
-        $notification->push($result->getMessage(), 'horde.error');
+    try {
+        $group->save();
+        $notification->push(sprintf(_("Updated \"%s\"."), $group->getShortName()), 'horde.success');
+    } catch (Horde_Group_Exception $e) {
+        $notification->push($e, 'horde.error');
         // restore backup copy
         $group = $restore;
-    } else {
-        $notification->push(sprintf(_("Updated \"%s\"."), $group->getShortName()), 'horde.success');
     }
 
     $form = 'edit.inc';
@@ -155,22 +160,26 @@ case 'addchild.inc':
 
 case 'edit.inc':
     /* Set up the lists. */
-    $users = $group->listUsers();
-    if (is_a($users, 'PEAR_Error')) {
-        $notification->push($users, 'horde.error');
+    try {
+        $users = $group->listUsers();
+    } catch (Horde_Group_Exception $e) {
+        $notification->push($e, 'horde.error');
         $users = array();
     }
-    $all_users = $group->listAllUsers();
-    if (is_a($all_users, 'PEAR_Error')) {
-        $notification->push($all_users, 'horde.error');
+
+    try {
+        $all_users = $group->listAllUsers();
+    } catch (Horde_Group_Exception $e) {
+        $notification->push($e, 'horde.error');
         $all_users = array();
     }
     $inherited_users = array_diff($all_users, $users);
 
     if ($auth->hasCapability('list')) {
-        $user_list = $auth->listUsers();
-        if (is_a($user_list, 'PEAR_Error')) {
-            $notification->push($user_list, 'horde.error');
+        try {
+            $user_list = $auth->listUsers();
+        } catch (Horde_Auth_Exception $e) {
+            $notification->push($e, 'horde.error');
             $user_list = array();
         }
         sort($user_list);
@@ -190,10 +199,7 @@ if (!empty($form)) {
 
 /* Get the perms tree. */
 $nodes = $groups->listGroups(true);
-if (is_a($nodes, 'PEAR_Error')) {
-    throw new Horde_Exception_Prior($nodes);
-}
-$nodes[GROUP_ROOT] = GROUP_ROOT;
+$nodes[Horde_Group::ROOT] = Horde_Group::ROOT;
 
 /* Set up some node params. */
 $spacer = '&nbsp;&nbsp;&nbsp;&nbsp;';
@@ -215,14 +221,11 @@ $tree->setHeader(array(array('width' => '50%')));
  * for the root node. */
 if ($cid > 0) {
     $cid_parents = $groups->getGroupParentList($cid);
-    if (is_a($cid_parents, 'PEAR_Error')) {
-        throw new Horde_Exception_Prior($cid_parents);
-    }
 }
 
 foreach ($nodes as $id => $node) {
     $node_params = ($cid == $id) ? array('class' => 'selected') : array();
-    if ($id == GROUP_ROOT) {
+    if ($id == Horde_Group::ROOT) {
         $add_link = Horde::link(Horde_Util::addParameter($add, 'cid', $id), _("Add a new group")) . $add_img . '</a>';
 
         $base_node_params = $icondir + array('icon' => 'administration.png');
index ab8a9cc..1b399d4 100644 (file)
@@ -220,39 +220,40 @@ class Horde_Api extends Horde_Registry_Api
         }
 
         /* Remove user from all groups */
-        require_once 'Horde/Group.php';
-        $groups = Group::singleton();
-        $allGroups = $groups->getGroupMemberships($user);
-        if (is_a($groups, 'PEAR_Error')) {
-            Horde::logMessage($allGroups, 'ERR');
-            $haveError = true;
-        } else {
+        $groups = Horde_Group::singleton();
+        try {
+            $allGroups = $groups->getGroupMemberships($user);
             foreach (array_keys($allGroups) as $id) {
                 $group = $groups->getGroupById($id);
                 $group->removeUser($user, true);
             }
+        } catch (Horde_Group_Exception $e) {
+            Horde::logMessage($e, 'ERR');
+            $haveError = true;
         }
 
         /* Remove the user from all application permissions */
         $perms = $GLOBALS['injector']->getInstance('Horde_Perms');
-        $tree = $perms->getTree();
-        if (is_a($tree, 'PEAR_Error')) {
-            Horde::logMessage($tree, 'ERR');
+        try {
+            $tree = $perms->getTree();
+        } catch (Horde_Perms_Exception $e) {
+            Horde::logMessage($e, 'ERR');
             $haveError = true;
-        } else {
-            foreach (array_keys($tree) as $id) {
+            $tree = array();
+        }
+
+        foreach (array_keys($tree) as $id) {
+            try {
                 $perm = $perms->getPermissionById($id);
-                if (is_a($perm, 'PEAR_Error')) {
-                    Horde::logMessage($perm, 'ERR');
-                    $haveError = true;
-                    continue;
-                }
                 if ($perms->getPermissions($perm, $user)) {
                     // The Horde_Perms::ALL is used if this is a matrix perm,
                     // otherwise it's ignored in the method and the entry is
                     // totally removed.
                     $perm->removeUserPermission($user, Horde_Perms::ALL, true);
                 }
+            } catch (Horde_Perms_Exception $e) {
+                Horde::logMessage($e, 'ERR');
+                $haveError = true;
             }
         }
 
@@ -314,19 +315,16 @@ class Horde_Api extends Horde_Registry_Api
             throw new Horde_Exception(_("You are not allowed to add groups."));
         }
 
-        require_once 'Horde/Group.php';
-        $groups = Group::singleton();
-
         if (empty($parent)) {
-            $parent = GROUP_ROOT;
-        }
-
-        if (is_a($group = &$groups->newGroup($name, $parent), 'PEAR_Error')) {
-            throw new Horde_Exception($group);
+            $parent = Horde_Group::ROOT;
         }
 
-        if (is_a($result = $groups->addGroup($group), 'PEAR_Error')) {
-            throw new Horde_Exception($result);
+        try {
+            $groups = Horde_Group::singleton();
+            $group = $groups->newGroup($name, $parent);
+            $groups->addGroup($group);
+        } catch (Horde_Group_Exception $e) {
+            throw new Horde_Exception($e);
         }
     }
 
@@ -343,15 +341,12 @@ class Horde_Api extends Horde_Registry_Api
             throw new Horde_Exception(_("You are not allowed to delete groups."));
         }
 
-        require_once 'Horde/Group.php';
-        $groups = Group::singleton();
-
-        if (is_a($group = &$groups->getGroup($name), 'PEAR_Error')) {
-            throw new Horde_Exception($group);
-        }
-
-        if (is_a($result = $groups->removeGroup($group, true), 'PEAR_Error')) {
-            throw new Horde_Exception($result);
+        try {
+            $groups = Horde_Group::singleton();
+            $group = $groups->getGroup($name);
+            $groups->removeGroup($group, true);
+        } catch (Horde_Group_Exception $e) {
+            throw new Horde_Exception($e);
         }
     }
 
@@ -369,15 +364,12 @@ class Horde_Api extends Horde_Registry_Api
             throw new Horde_Exception(_("You are not allowed to change groups."));
         }
 
-        require_once 'Horde/Group.php';
-        $groups = Group::singleton();
-
-        if (is_a($group = &$groups->getGroup($name), 'PEAR_Error')) {
-            throw new Horde_Exception($group);
-        }
-
-        if (is_a($result = $group->addUser($user), 'PEAR_Error')) {
-            throw new Horde_Exception($result);
+        try {
+            $groups = Horde_Group::singleton();
+            $group = $groups->getGroup($name);
+            $group->addUser($user);
+        } catch (Horde_Group_Exception $e) {
+            throw new Horde_Exception($e);
         }
     }
 
@@ -395,19 +387,15 @@ class Horde_Api extends Horde_Registry_Api
             throw new Horde_Exception(_("You are not allowed to change groups."));
         }
 
-        require_once 'Horde/Group.php';
-        $groups = Group::singleton();
-
-        if (is_a($group = &$groups->getGroup($name), 'PEAR_Error')) {
-            throw new Horde_Exception($group);
-        }
-
-        foreach ($users as $user) {
-            $group->addUser($user, false);
-        }
-
-        if (is_a($result = $group->save(), 'PEAR_Error')) {
-            throw new Horde_Exception($result);
+        try {
+            $groups = Horde_Group::singleton();
+            $group = $groups->getGroup($name);
+            foreach ($users as $user) {
+                $group->addUser($user, false);
+            }
+            $group->save();
+        } catch (Horde_Group_Exception $e) {
+            throw new Horde_Exception($e);
         }
     }
 
@@ -425,15 +413,12 @@ class Horde_Api extends Horde_Registry_Api
             throw new Horde_Exception(_("You are not allowed to change groups."));
         }
 
-        require_once 'Horde/Group.php';
-        $groups = Group::singleton();
-
-        if (is_a($group = &$groups->getGroup($name), 'PEAR_Error')) {
-            throw new Horde_Exception($group);
-        }
-
-        if (is_a($result = $group->removeUser($user), 'PEAR_Error')) {
-            throw new Horde_Exception($result);
+        try {
+            $groups = Horde_Group::singleton();
+            $group = $groups->getGroup($name);
+            $group->removeUser($user);
+        } catch (Horde_Group_Exception $e) {
+            throw new Horde_Exception($e);
         }
     }
 
@@ -451,21 +436,15 @@ class Horde_Api extends Horde_Registry_Api
             throw new Horde_Exception(_("You are not allowed to change groups."));
         }
 
-        require_once 'Horde/Group.php';
-        $groups = Group::singleton();
-
-        if (is_a($group = &$groups->getGroup($name), 'PEAR_Error')) {
-            throw new Horde_Exception($group);
-        }
-
-        foreach ($users as $user) {
-            if (is_a($result = $group->removeUser($user, false), 'PEAR_Error')) {
-                throw new Horde_Exception($result);
+        try {
+            $groups = Horde_Group::singleton();
+            $group = $groups->getGroup($name);
+            foreach ($users as $user) {
+                $group->removeUser($user, false);
             }
-        }
-
-        if (is_a($result = $group->save(), 'PEAR_Error')) {
-            throw new Horde_Exception($result);
+            $group->save();
+        } catch (Horde_Group_Exception $e) {
+            throw new Horde_Exception($e);
         }
     }
 
@@ -483,14 +462,13 @@ class Horde_Api extends Horde_Registry_Api
             throw new Horde_Exception(_("You are not allowed to list users of groups."));
         }
 
-        require_once 'Horde/Group.php';
-        $groups = Group::singleton();
-
-        if (is_a($group = &$groups->getGroup($name), 'PEAR_Error')) {
-            throw new Horde_Exception($group);
+        try {
+            $groups = Horde_Group::singleton();
+            $group = $groups->getGroup($name);
+            return $group->listUsers();
+        } catch (Horde_Group_Exception $e) {
+            throw new Horde_Exception($e);
         }
-
-        return $group->listUsers();
     }
 
     /* Shares. */
@@ -634,15 +612,17 @@ class Horde_Api extends Horde_Registry_Api
             throw new Horde_Exception(_("You are not allowed to change shares."));
         }
 
-        require_once 'Horde/Group.php';
         $shares = $GLOBALS['injector']->getInstance('Horde_Share')->getScope($scope);
-        $groups = Group::singleton();
 
         if (is_a($share = &$shares->getShare($shareName), 'PEAR_Error')) {
             throw new Horde_Exception($share);
         }
-        if (is_a($groupId = $groups->getGroupId($groupName), 'PEAR_Error')) {
-            throw new Horde_Exception($groupId);
+
+        try {
+            $groups = Horde_Group::singleton();
+            $groupId = $groups->getGroupId($groupName);
+        } catch (Horde_Group_Exception $e) {
+            throw new Horde_Exception($e);
         }
 
         $perm = &$share->getPermission();
@@ -701,16 +681,17 @@ class Horde_Api extends Horde_Registry_Api
             throw new Horde_Exception(_("You are not allowed to change shares."));
         }
 
-        require_once 'Horde/Group.php';
         $shares = $GLOBALS['injector']->getInstance('Horde_Share')->getScope($scope);
-        $groups = Group::singleton();
 
         if (is_a($share = &$shares->getShare($shareName), 'PEAR_Error')) {
             throw new Horde_Exception($share);
         }
 
-        if (is_a($groupId = $groups->getGroupId($groupName), 'PEAR_Error')) {
-            throw new Horde_Exception($groupId);
+        try {
+            $groups = Horde_Group::singleton();
+            $groupId = $groups->getGroupId($groupName);
+        } catch (Horde_Group_Exception $e) {
+            throw new Horde_Exception($e);
         }
 
         if (is_a($result = $share->removeGroup($groupId), 'PEAR_Error')) {
index 1b25018..868c0a7 100644 (file)
@@ -1,5 +1,5 @@
 CREATE TABLE horde_groups (
-    group_uid INT(10) UNSIGNED NOT NULL,
+    group_uid INT(11) NOT NULL AUTO_INCREMENT,
     group_name VARCHAR(255) NOT NULL,
     group_parents VARCHAR(255) NOT NULL,
     group_email VARCHAR(255),
@@ -8,8 +8,9 @@ CREATE TABLE horde_groups (
 );
 
 CREATE TABLE horde_groups_members (
-    group_uid INT(10) UNSIGNED NOT NULL,
+    group_uid INT(11) NOT NULL,
     user_uid VARCHAR(255) NOT NULL
 );
+
 CREATE INDEX group_uid_idx ON horde_groups_members (group_uid);
 CREATE INDEX user_uid_idx ON horde_groups_members (user_uid);
index 3f2c616..93025e8 100644 (file)
@@ -13,3 +13,11 @@ CREATE TABLE horde_groups_members (
 
 CREATE INDEX group_uid_idx ON horde_groups_members (group_uid);
 CREATE INDEX user_uid_idx ON horde_groups_members (user_uid);
+
+CREATE SEQUENCE horde_groups_uid_seq;
+CREATE TRIGGER horde_groups_uid_trigger
+BEFORE INSERT ON horde_groups
+FOR EACH ROW
+BEGIN
+    SELECT horde_groups_uid_seq.nextval INTO :new.group_uid FROM dual;
+END;
index b3d42ca..3ea74fa 100644 (file)
@@ -1,5 +1,5 @@
 CREATE TABLE horde_groups (
-    group_uid INTEGER NOT NULL,
+    group_uid SERIAL UNIQUE,
     group_name VARCHAR(255) NOT NULL UNIQUE,
     group_parents VARCHAR(255) NOT NULL,
     group_email VARCHAR(255),
@@ -10,5 +10,6 @@ CREATE TABLE horde_groups_members (
     group_uid INTEGER NOT NULL,
     user_uid VARCHAR(255) NOT NULL
 );
+
 CREATE INDEX group_uid_idx ON horde_groups_members (group_uid);
 CREATE INDEX user_uid_idx ON horde_groups_members (user_uid);
index 99d1b7b..e12b22e 100644 (file)
@@ -1,5 +1,5 @@
 CREATE TABLE horde_groups (
-    group_uid INTEGER NOT NULL,
+    group_uid INTEGER NOT NULL AUTO_INCREMENT,
     group_name VARCHAR(255) NOT NULL,
     group_parents VARCHAR(255) NOT NULL,
     group_email VARCHAR(255),
diff --git a/horde/scripts/upgrades/2010-06-01_horde_groups_autoincrement.mysql.sql b/horde/scripts/upgrades/2010-06-01_horde_groups_autoincrement.mysql.sql
new file mode 100644 (file)
index 0000000..bbcfa91
--- /dev/null
@@ -0,0 +1 @@
+ALTER TABLE horde_groups CHANGE COLUMN group_uid group_uid INT(11) NOT NULL AUTO_INCREMENT;
diff --git a/horde/scripts/upgrades/2010-06-01_horde_groups_autoincrement.oci8.sql b/horde/scripts/upgrades/2010-06-01_horde_groups_autoincrement.oci8.sql
new file mode 100644 (file)
index 0000000..00cd0b0
--- /dev/null
@@ -0,0 +1,7 @@
+CREATE SEQUENCE horde_group_uid_seq;
+CREATE TRIGGER horde_group_uid_trigger
+BEFORE INSERT ON horde_groups
+FOR EACH ROW
+BEGIN
+SELECT horde_group_uid_seq.nextval INTO :new.group_uid FROM dual;
+END;
diff --git a/horde/scripts/upgrades/2010-06-01_horde_groups_autoincrement.pgsql.sql b/horde/scripts/upgrades/2010-06-01_horde_groups_autoincrement.pgsql.sql
new file mode 100644 (file)
index 0000000..cd92611
--- /dev/null
@@ -0,0 +1,2 @@
+CREATE SEQUENCE horde_group_uid_seq;
+ALTER TABLE horde_groups ALTER COLUMN group_uid SET DEFAULT NEXTVAL('horde_group_uid_seq');
index 2db11d1..409fe1b 100755 (executable)
@@ -8,8 +8,7 @@
 require_once dirname(__FILE__) . '/../../lib/Application.php';
 Horde_Registry::appInit('horde', array('authentication' => 'none', 'cli' => true));
 
-require_once 'Horde/Group.php';
-$g = Group::factory();
+$g = Horde_Group::factory();
 
 $group_query = '
 INSERT INTO
index 7a96b05..edfe2bf 100644 (file)
@@ -11,8 +11,6 @@
 require_once dirname(__FILE__) . '/../../lib/Application.php';
 Horde_Registry::appInit('horde');
 
-require_once 'Horde/Group.php';
-
 // Exit if the user shouldn't be able to change share permissions.
 if (!empty($conf['share']['no_sharing'])) {
     throw new Horde_Exception('Permission denied.');
@@ -27,7 +25,7 @@ $fieldsList = array(
 
 $app = Horde_Util::getFormData('app');
 $shares = $GLOBALS['injector']->getInstance('Horde_Share')->getScope($app);
-$groups = Group::singleton();
+$groups = Horde_Group::singleton();
 $auth = $injector->getInstance('Horde_Auth')->getOb();
 if ($registry->hasMethod('shareHelp', $app)) {
     $help = $registry->callByPackage($app, 'shareHelp');
@@ -266,29 +264,27 @@ if (is_a($share, 'PEAR_Error')) {
     $title = sprintf(_("Edit permissions for \"%s\""), $share->get('name'));
 }
 
+$userList = array();
 if ($auth->hasCapability('list') &&
     ($conf['auth']['list_users'] == 'list' ||
      $conf['auth']['list_users'] == 'both')) {
-    $userList = $auth->listUsers();
-    if ($userList instanceof PEAR_Error) {
-        Horde::logMessage($userList, 'ERR');
-        $userList = array();
+    try {
+        $userList = $auth->listUsers();
+        sort($userList);
+    } catch (Horde_Auth_Exception $e) {
+        Horde::logMessage($e, 'ERR');
     }
-    sort($userList);
-} else {
-    $userList = array();
 }
 
-if (!empty($conf['share']['any_group'])) {
-    $groupList = $groups->listGroups();
-} else {
-    $groupList = $groups->getGroupMemberships(Horde_Auth::getAuth(), true);
-}
-if ($groupList instanceof PEAR_Error) {
-    Horde::logMessage($groupList, 'NOTICE');
+try {
+    $groupList = empty($conf['share']['any_group'])
+        ? $groups->getGroupMemberships(Horde_Auth::getAuth(), true)
+        : $groups->listGroups();
+    asort($groupList);
+} catch (Horde_Group_Exception $e) {
+    Horde::logMessage($e, 'NOTICE');
     $groupList = array();
 }
-asort($groupList);
 
 require HORDE_TEMPLATES . '/common-header.inc';
 $notification->notify(array('listeners' => 'status'));
index a83fdd2..2b1080e 100644 (file)
@@ -1133,14 +1133,12 @@ class Kronolith_Api extends Horde_Registry_Api
      */
     public function listAlarms($time, $user = null)
     {
-        require_once 'Horde/Group.php';
-
         $current_user = Horde_Auth::getAuth();
         if ((empty($user) || $user != $current_user) && !$GLOBALS['registry']->isAdmin()) {
             throw new Horde_Exception_PermissionDenied();
         }
 
-        $group = Group::singleton();
+        $group = Horde_Group::singleton();
         $alarm_list = array();
         $time = new Horde_Date($time);
         $calendars = is_null($user) ? array_keys($GLOBALS['kronolith_shares']->listAllShares()) : $GLOBALS['display_calendars'];
@@ -1158,10 +1156,9 @@ class Kronolith_Api extends Horde_Registry_Api
                 $users = $share->listUsers(Horde_Perms::READ);
                 $groups = $share->listGroups(Horde_Perms::READ);
                 foreach ($groups as $gid) {
-                    $group_users = $group->listUsers($gid);
-                    if (!($group_users instanceof PEAR_Error)) {
-                        $users = array_merge($users, $group_users);
-                    }
+                    try {
+                        $users = array_merge($users, $group->listUsers($gid));
+                    } catch (Horde_Group_Exception $e) {}
                 }
                 $users = array_unique($users);
             } else {
index 157f20a..8fae644 100644 (file)
@@ -1174,19 +1174,22 @@ class Kronolith
                     $perm_value = Horde_Perms::READ | Horde_Perms::SHOW | Horde_Perms::EDIT | Horde_Perms::DELETE;
                     break;
                 }
-                $groups = &Group::singleton();
-                $group_list = $groups->getGroupMemberships(Horde_Auth::getAuth());
-                if (!($group_list instanceof PEAR_Error) && count($group_list)) {
-                    $perm = $share->getPermission();
-                    // Add the default perm, not added otherwise
-                    $perm->addUserPermission(Horde_Auth::getAuth(), Horde_Perms::ALL, false);
-                    foreach ($group_list as $group_id => $group_name) {
-                        $perm->addGroupPermission($group_id, $perm_value, false);
+
+                try {
+                    $groups = Horde_Group::singleton();
+                    $group_list = $groups->getGroupMemberships(Horde_Auth::getAuth());
+                    if (count($group_list)) {
+                        $perm = $share->getPermission();
+                        // Add the default perm, not added otherwise
+                        $perm->addUserPermission(Horde_Auth::getAuth(), Horde_Perms::ALL, false);
+                        foreach ($group_list as $group_id => $group_name) {
+                            $perm->addGroupPermission($group_id, $perm_value, false);
+                        }
+                        $share->setPermission($perm);
+                        $share->save();
+                        $GLOBALS['notification']->push(sprintf(_("New calendar created and automatically shared with the following group(s): %s."), implode(', ', $group_list)), 'horde.success');
                     }
-                    $share->setPermission($perm);
-                    $share->save();
-                    $GLOBALS['notification']->push(sprintf(_("New calendar created and automatically shared with the following group(s): %s."), implode(', ', $group_list)), 'horde.success');
-                }
+                } catch (Horde_Group_Exception $e) {}
             }
 
             $GLOBALS['prefs']->setValue('display_cals', serialize($GLOBALS['display_calendars']));
@@ -1833,7 +1836,7 @@ class Kronolith
             // Notify users that have been added.
             if ($GLOBALS['conf']['share']['notify'] &&
                 !isset($current[$group]) && $has_perms) {
-                $groupOb = Group::singleton()->getGroupById($group);
+                $groupOb = Horde_Group::singleton()->getGroupById($group);
                 if (!empty($groupOb->data['email'])) {
                     try {
                         $message = Horde::callHook('shareGroupNotification', array($group, $share));
@@ -2261,9 +2264,7 @@ class Kronolith
             throw new Kronolith_Exception('Unknown event action: ' . $action);
         }
 
-        require_once 'Horde/Group.php';
-
-        $groups = Group::singleton();
+        $groups = Horde_Group::singleton();
         $calendar = $event->calendar;
         $recipients = array();
         try {
@@ -2287,15 +2288,14 @@ class Kronolith
         }
 
         foreach ($share->listGroups(Horde_Perms::READ) as $group) {
-            $group = $groups->getGroupById($group);
-            if ($group instanceof PEAR_Error) {
-                continue;
-            }
-            $group_users = $group->listAllUsers();
-            if ($group_users instanceof PEAR_Error) {
+            try {
+                $group = $groups->getGroupById($group);
+                $group_users = $group->listAllUsers();
+            } catch (Horde_Group_Exception $e) {
                 Horde::logMessage($group_users, 'ERR');
                 continue;
             }
+
             foreach ($group_users as $user) {
                 if (!isset($recipients[$user])) {
                     $recipients[$user] = self::_notificationPref($user, 'read', $calendar);
index 187e65e..c38d404 100644 (file)
 require_once dirname(__FILE__) . '/lib/Application.php';
 Horde_Registry::appInit('kronolith');
 
-require_once 'Horde/Group.php';
-
 // Exit if the user shouldn't be able to change share permissions.
 if (!empty($conf['share']['no_sharing'])) {
     throw new Horde_Exception('Permission denied.');
 }
 
 $shares = $GLOBALS['injector']->getInstance('Horde_Share')->getScope();
-$groups = Group::singleton();
+$groups = Horde_Group::singleton();
 $auth = $injector->getInstance('Horde_Auth')->getOb();
 
 $reload = false;
@@ -100,16 +98,15 @@ if ($auth->hasCapability('list') &&
     $userList = array();
 }
 
-if (!empty($conf['share']['any_group'])) {
-    $groupList = $groups->listGroups();
-} else {
-    $groupList = $groups->getGroupMemberships(Horde_Auth::getAuth(), true);
-}
-if ($groupList instanceof PEAR_Error) {
-    Horde::logMessage($groupList, 'NOTICE');
-    $groupList = array();
+$groupList = array();
+try {
+    $groupList = empty($conf['share']['any_group'])
+        ? $groups->getGroupMemberships(Horde_Auth::getAuth(), true)
+        : $groups->listGroups();
+    asort($groupList);
+} catch (Horde_Group_Exception $e) {
+    Horde::logMessage($e, 'NOTICE');
 }
-asort($groupList);
 
 require KRONOLITH_TEMPLATES . '/common-header.inc';
 $notification->notify(array('listeners' => 'status'));
index d79f7dd..7ce65a0 100644 (file)
@@ -1,16 +1,15 @@
 <?php
 $auth = $GLOBALS['injector']->getInstance('Horde_Auth')->getOb();
-require_once 'Horde/Group.php';
-$horde_groups = Group::singleton();
-if (!empty($GLOBALS['conf']['share']['any_group'])) {
-    $groups = $horde_groups->listGroups();
-} else {
-    $groups = $horde_groups->getGroupMemberships(Horde_Auth::getAuth(), true);
-}
-if ($groups instanceof PEAR_Error) {
-    $groups = array();
-}
-asort($groups);
+$horde_groups = Horde_Group::singleton();
+
+$groups = array();
+try {
+    $groups = empty($GLOBALS['conf']['share']['any_group'])
+        ? $horde_groups->getGroupMemberships(Horde_Auth::getAuth(), true)
+        : $horde_groups->listGroups();
+    asort($groups);
+} catch (Horde_Group_Exception $e) {}
+
 $file_upload = $GLOBALS['browser']->allowFileUploads();
 ?>
 <div id="kronolithCalendarDialog" class="kronolithDialog">
index 7cba2b9..7f83795 100644 (file)
@@ -1370,15 +1370,13 @@ class Nag_Api extends Horde_Registry_Api
      */
     public function listAlarms($time, $user = null)
     {
-        require_once 'Horde/Group.php';
-
         if ((empty($user) || $user != Horde_Auth::getAuth()) &&
             !$GLOBALS['registry']->isAdmin()) {
             return PEAR::raiseError(_("Permission Denied"));
         }
 
         $storage = Nag_Driver::singleton();
-        $group = Group::singleton();
+        $group = Horde_Group::singleton();
         $alarm_list = array();
         $tasklists = is_null($user) ? array_keys($GLOBALS['nag_shares']->listAllShares()) :  $GLOBALS['display_tasklists'];
 
index 7acbcb4..d9e998a 100644 (file)
@@ -53,8 +53,7 @@ class Nag_TaskForm extends Horde_Form {
         $users = $share->listUsers(Horde_Perms::READ);
         $groups = $share->listGroups(Horde_Perms::READ);
         if (count($groups)) {
-            require_once 'Horde/Group.php';
-            $horde_group = Group::singleton();
+            $horde_group = Horde_Group::singleton();
             foreach ($groups as $group) {
                 $users = array_merge($users,
                                      $horde_group->listAllUsers($group));
index 51899b1..caffe3c 100644 (file)
@@ -835,9 +835,7 @@ class Nag
             throw new Nag_Exception($e);
         }
 
-        require_once 'Horde/Group.php';
-
-        $groups = Group::singleton();
+        $groups = Horde_Group::singleton();
         $recipients = array();
         $identity = $GLOBALS['injector']->getInstance('Horde_Prefs_Identity')->getIdentity();
         $from = $identity->getDefaultFromAddress(true);
@@ -853,15 +851,14 @@ class Nag
             }
         }
         foreach ($share->listGroups(Horde_Perms::READ) as $group) {
-            $group = $groups->getGroupById($group);
-            if (is_a($group, 'PEAR_Error')) {
-                continue;
-            }
-            $group_users = $group->listAllUsers();
-            if (is_a($group_users, 'PEAR_Error')) {
-                Horde::logMessage($group_users, 'ERR');
+            try {
+                $group = $groups->getGroupById($group);
+                $group_users = $group->listAllUsers();
+            } catch (Horde_Group_Exception $e) {
+                Horde::logMessage($e, 'ERR');
                 continue;
             }
+
             foreach ($group_users as $user) {
                 if (empty($recipients[$user])) {
                     $recipients[$user] = Nag::_notificationPref($user, 'read', $task->tasklist);
index 4d770a0..e9886c5 100644 (file)
@@ -701,7 +701,6 @@ $cfgSources['favourites'] = array(
 //    'browse' => true,
 //);
 
-//require_once 'Horde/Group.php';
 //$_group_driver = &Group::singleton();
 //$_group_list = $_group_driver->getGroupMemberships(Horde_Auth::getAuth());
 //foreach ($_group_list as $_group_id => $_group_name) {
index 1941eb1..2251b52 100644 (file)
@@ -129,9 +129,7 @@ class Turba_Driver_Group extends Turba_Driver
 
     function _getAddressBook()
     {
-        require_once 'Horde/Group.php';
-
-        $groups = Group::singleton();
+        $groups = Horde_Group::singleton();
         $members = $groups->listAllUsers($this->_gid);
         $addressbook = array();
         foreach ($members as $member) {
index cb87d8f..400a488 100644 (file)
@@ -57,7 +57,6 @@ class Whups_Application extends Horde_Registry_Application
     protected function _init()
     {
         // TODO: Remove once they can be autoloaded
-        require_once 'Horde/Group.php';
         require_once 'Horde/Form.php';
         require_once 'Horde/Form/Renderer.php';
 
index 76ba7c7..a569eac 100644 (file)
@@ -25,7 +25,7 @@ class AddCommentForm extends Horde_Form {
         /* Group restrictions. */
         if ($GLOBALS['registry']->isAdmin(array('permission' => 'whups:admin')) ||
             $GLOBALS['injector']->getInstance('Horde_Perms')->hasPermission('whups:hiddenComments', Horde_Auth::getAuth(), Horde_Perms::EDIT)) {
-            $groups = &Group::singleton();
+            $groups = Horde_Group::singleton();
             $mygroups = $groups->getGroupMemberships(Horde_Auth::getAuth());
             if ($mygroups) {
                 foreach (array_keys($mygroups) as $gid) {
index 8f79a3b..aa8c877 100644 (file)
@@ -211,7 +211,7 @@ class CreateStep4Form extends Horde_Form {
         $this->addHidden('', 'deferred_attachment', 'text', false);
 
         /* Groups. */
-        $groups = &Group::singleton();
+        $groups = Horde_Group::singleton();
         if ($conf['prefs']['assign_all_groups']) {
             $mygroups = $groups->listGroups();
         } else {
index 65860df..040225e 100644 (file)
@@ -95,7 +95,7 @@ class EditTicketForm extends Horde_Form {
                 case 'owner':
                     if (Whups::hasPermission($vars->get('queue'), 'queue',
                                              'assign')) {
-                        $groups = &Group::singleton();
+                        $groups = Horde_Group::singleton();
                         if ($GLOBALS['conf']['prefs']['assign_all_groups']) {
                             $mygroups = $groups->listGroups();
                         } else {
@@ -172,7 +172,7 @@ class EditTicketForm extends Horde_Form {
                     }
 
                     /* Comment permissions. */
-                    $groups = &Group::singleton();
+                    $groups = Horde_Group::singleton();
                     $mygroups = $groups->getGroupMemberships(Horde_Auth::getAuth());
                     if ($mygroups) {
                         foreach (array_keys($mygroups) as $gid) {
index cf5ab9a..cc88c47 100644 (file)
@@ -89,9 +89,14 @@ class GroupCriterionForm extends Horde_Form {
 
         $this->addHidden('', 'edit', 'boolean', false);
 
-        $groups = &Group::singleton();
-        $grouplist = $groups->listGroups();
-        if (is_a($grouplist, 'PEAR_Error') || !count($grouplist)) {
+        try {
+            $groups = Horde_Group::singleton();
+            $grouplist = $groups->listGroups();
+        } catch (Horde_Group_Exception $e) {
+            $grouplist = array();
+        }
+
+        if (count($grouplist)) {
             $type_params = array(_("Could not find any groups."));
             $this->addVariable(_("Groups"), 'groups', 'invalid', false, false, null, $type_params);
         } else {
index 0f5aa89..81a6172 100644 (file)
@@ -503,7 +503,7 @@ class Whups {
     function getOwnerCriteria($user)
     {
         $criteria = array('user:' . $user);
-        $groups = &Group::singleton();
+        $groups = Horde_Group::singleton();
         $mygroups = $groups->getGroupMemberships(Horde_Auth::getAuth());
         foreach ($mygroups as $id => $group) {
             $criteria[] = 'group:' . $id;
@@ -578,15 +578,16 @@ class Whups {
                     $results[$user]['email'] = $identity->getValue('from_addr');
                 }
             } elseif ($type == 'group') {
-                $groups = &Group::singleton();
-                $group = $groups->getGroupById($user);
-                if (is_a($group, 'PEAR_Error')) {
-                    $results['user']['name'] = '';
-                    $results['user']['email'] = '';
-                } else {
+                try {
+                    $groups = Horde_Group::singleton();
+                    $group = $groups->getGroupById($user);
+
                     $results[$user]['user'] = $group->getShortName();
                     $results[$user]['name'] = $group->getShortName();
                     $results[$user]['email'] = $group->get('email');
+                } catch (Horde_Group_Exception $e) {
+                    $results['user']['name'] = '';
+                    $results['user']['email'] = '';
                 }
             }
         }
index ceaf835..a2a9ff9 100644 (file)
@@ -34,7 +34,7 @@ class SetQueueStep1Form extends Horde_Form {
         if ($GLOBALS['registry']->isAdmin(array('permission' => 'whups:admin', 'permlevel' => Horde_Perms::EDIT)) ||
             $GLOBALS['injector']->getInstance('Horde_Perms')->hasPermission('whups:hiddenComments',
                                              Horde_Auth::getAuth(), Horde_Perms::EDIT)) {
-            $groups = &Group::singleton();
+            $groups = Horde_Group::singleton();
             $mygroups = $groups->getGroupMemberships(Horde_Auth::getAuth());
             if ($mygroups) {
                 foreach (array_keys($mygroups) as $gid) {
index 51d845b..7c83ab2 100644 (file)
@@ -28,7 +28,7 @@ class SetTypeStep1Form extends Horde_Form {
         $this->addVariable(_("Comment"), 'newcomment', 'longtext', false);
 
         /* Group restrictions. */
-        $groups = &Group::singleton();
+        $groups = Horde_Group::singleton();
         $mygroups = $groups->getGroupMemberships(Horde_Auth::getAuth());
         if ($mygroups) {
             foreach (array_keys($mygroups) as $gid) {