From e73d7b53f08bbd075e2ab70c19de809a24fad42d Mon Sep 17 00:00:00 2001 From: Michael M Slusarz Date: Fri, 15 Oct 2010 16:02:55 -0600 Subject: [PATCH] Abstract prefs storage code into Horde_Prefs_Storage. Jan and I talked about using a stack system for retrieving prefs data from storage. After discussion, determined that cache storage won't work in the stack system for several reasons. However, it may be possible to implement otherwise. For example, in Horde, there would be 2 stack drivers. The first is a read-only driver that returns the default values from the prefs.php files. The second would be the persistent storage driver. --- framework/Core/lib/Horde/Core/Factory/Prefs.php | 11 +- .../Core/lib/Horde/Core/Prefs/Cache/Session.php | 73 ---- framework/Core/lib/Horde/Core/Prefs/Session.php | 66 ---- .../Core/lib/Horde/Core/Prefs/Storage/Session.php | 59 +++ framework/Core/lib/Horde/Registry.php | 4 +- framework/Core/package.xml | 6 +- framework/Prefs/lib/Horde/Prefs.php | 288 ++++++++------- framework/Prefs/lib/Horde/Prefs/Cache.php | 61 --- framework/Prefs/lib/Horde/Prefs/File.php | 209 ----------- framework/Prefs/lib/Horde/Prefs/Session.php | 68 ---- framework/Prefs/lib/Horde/Prefs/Storage.php | 69 ++++ framework/Prefs/lib/Horde/Prefs/Storage/File.php | 155 ++++++++ .../Prefs/lib/Horde/Prefs/{ => Storage}/Imsp.php | 85 ++--- .../Prefs/lib/Horde/Prefs/{ => Storage}/Kolab.php | 13 +- .../lib/Horde/Prefs/{ => Storage}/KolabImap.php | 218 +++++------ .../Prefs/lib/Horde/Prefs/{ => Storage}/Ldap.php | 409 +++++++++------------ .../lib/Horde/Prefs/{Cache => Storage}/Null.php | 8 +- .../lib/Horde/Prefs/{Cache => Storage}/Session.php | 16 +- .../Prefs/lib/Horde/Prefs/{ => Storage}/Sql.php | 111 ++---- framework/Prefs/package.xml | 47 +-- framework/Prefs/test/Horde/Prefs/bug_2838.phpt | 21 -- 21 files changed, 797 insertions(+), 1200 deletions(-) delete mode 100644 framework/Core/lib/Horde/Core/Prefs/Cache/Session.php delete mode 100644 framework/Core/lib/Horde/Core/Prefs/Session.php create mode 100644 framework/Core/lib/Horde/Core/Prefs/Storage/Session.php delete mode 100644 framework/Prefs/lib/Horde/Prefs/Cache.php delete mode 100644 framework/Prefs/lib/Horde/Prefs/File.php delete mode 100644 framework/Prefs/lib/Horde/Prefs/Session.php create mode 100644 framework/Prefs/lib/Horde/Prefs/Storage.php create mode 100644 framework/Prefs/lib/Horde/Prefs/Storage/File.php rename framework/Prefs/lib/Horde/Prefs/{ => Storage}/Imsp.php (51%) rename framework/Prefs/lib/Horde/Prefs/{ => Storage}/Kolab.php (65%) rename framework/Prefs/lib/Horde/Prefs/{ => Storage}/KolabImap.php (53%) rename framework/Prefs/lib/Horde/Prefs/{ => Storage}/Ldap.php (63%) rename framework/Prefs/lib/Horde/Prefs/{Cache => Storage}/Null.php (71%) rename framework/Prefs/lib/Horde/Prefs/{Cache => Storage}/Session.php (71%) rename framework/Prefs/lib/Horde/Prefs/{ => Storage}/Sql.php (60%) delete mode 100644 framework/Prefs/test/Horde/Prefs/bug_2838.phpt diff --git a/framework/Core/lib/Horde/Core/Factory/Prefs.php b/framework/Core/lib/Horde/Core/Factory/Prefs.php index b9bce66e1..320f7b247 100644 --- a/framework/Core/lib/Horde/Core/Factory/Prefs.php +++ b/framework/Core/lib/Horde/Core/Factory/Prefs.php @@ -63,7 +63,8 @@ class Horde_Core_Factory_Prefs * Return the Horde_Prefs:: instance. * * @param string $scope The scope for this set of preferences. - * @param array $opts See Horde_Prefs::factory(). Additional options: + * @param array $opts See Horde_Prefs::__construct(). Additional + * options: *
      * 'session' - (boolean) Use the session driver.
      *             DEFAULT: false
@@ -75,7 +76,7 @@ class Horde_Core_Factory_Prefs
     {
         if (empty($GLOBALS['conf']['prefs']['driver']) ||
             !empty($opts['session'])) {
-            $driver = 'Horde_Core_Prefs_Session';
+            $driver = 'Horde_Core_Prefs_Storage_Session';
             $params = array();
             unset($opts['session']);
         } else {
@@ -84,7 +85,7 @@ class Horde_Core_Factory_Prefs
         }
 
         $opts = array_merge(array(
-            'cache' => 'Horde_Core_Prefs_Cache_Session',
+            'cache' => 'Horde_Core_Prefs_Storage_Session',
             'charset' => 'UTF-8',
             'logger' => $this->_injector->getInstance('Horde_Log_Logger'),
             'password' => '',
@@ -122,7 +123,7 @@ class Horde_Core_Factory_Prefs
             }
 
             try {
-                $this->_instances[$sig] = Horde_Prefs::factory($driver, $scope, $opts, $params);
+                $this->_instances[$sig] = new Horde_Prefs($driver, $scope, $opts, $params);
             } catch (Horde_Prefs_Exception $e) {
                 if (!$GLOBALS['session']['horde:no_prefs']) {
                     $GLOBALS['session']['horde:no_prefs'] = true;
@@ -130,7 +131,7 @@ class Horde_Core_Factory_Prefs
                         $GLOBALS['notification']->push($this->_coreDict->t("The preferences backend is currently unavailable and your preferences have not been loaded. You may continue to use the system with default preferences."));
                     }
                 }
-                $this->_instances[$sig] = Horde_Prefs::factory('Horde_Core_Prefs_Session', $scope);
+                $this->_instances[$sig] = new Horde_Prefs('Horde_Core_Prefs_Storage_Session', $scope, $opts);
             }
         }
 
diff --git a/framework/Core/lib/Horde/Core/Prefs/Cache/Session.php b/framework/Core/lib/Horde/Core/Prefs/Cache/Session.php
deleted file mode 100644
index 2ae9f263c..000000000
--- a/framework/Core/lib/Horde/Core/Prefs/Cache/Session.php
+++ /dev/null
@@ -1,73 +0,0 @@
-
- * @category Horde
- * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
- * @package  Core
- */
-class Horde_Core_Prefs_Cache_Session extends Horde_Prefs_Cache
-{
-    /**
-     * Session key.
-     *
-     * @var string
-     */
-    protected $_key;
-
-    /**
-     */
-    public function __construct($user)
-    {
-        parent::__construct($user);
-
-        $this->_key = 'horde:prefs_' . $this->_user . '/';
-    }
-
-    /**
-     */
-    public function get($scope)
-    {
-        global $session;
-
-        return isset($session[$this->_key . $scope])
-            ? $session[$this->_key . $scope]
-            : false;
-    }
-
-    /**
-     */
-    public function update($scope, $prefs)
-    {
-        if (($cached = $this->get($scope)) === false) {
-            $cached = array();
-        }
-        $cached = array_merge($cached, $prefs);
-        $GLOBALS['session'][$this->_key . $scope] = $cached;
-    }
-
-    /**
-     */
-    public function clear($scope = null, $pref = null)
-    {
-        global $session;
-
-        if (is_null($scope)) {
-            unset($session[$this->_key]);
-        } elseif (is_null($pref)) {
-            unset($session[$this->_key . $scope]);
-        } elseif ((($cached = $this->get($scope)) !== false) &&
-                  isset($cached[$pref])) {
-            unset($cached[$pref]);
-            $session[$this->_key . $scope] = $cached;
-        }
-    }
-
-}
diff --git a/framework/Core/lib/Horde/Core/Prefs/Session.php b/framework/Core/lib/Horde/Core/Prefs/Session.php
deleted file mode 100644
index d5a28983c..000000000
--- a/framework/Core/lib/Horde/Core/Prefs/Session.php
+++ /dev/null
@@ -1,66 +0,0 @@
-
- * @category Horde
- * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
- * @package  Core
- */
-class Horde_Core_Prefs_Session extends Horde_Prefs
-{
-    /**
-     * Retrieves the requested set of preferences from the current session.
-     *
-     * @param string $scope  Scope specifier.
-     */
-    protected function _retrieve($scope)
-    {
-        global $session;
-
-        if (isset($session['horde:prefs_session/' . $scope])) {
-            $this->_scopes[$scope] = $session['horde:prefs_session/' . $scope];
-        }
-    }
-
-    /**
-     * Stores preferences in the current session.
-     */
-    public function store()
-    {
-        // Copy the current preferences into the session variable.
-        foreach ($this->_scopes as $scope => $prefs) {
-            foreach (array_keys($prefs) as $pref_name) {
-                // Clean the pref since it was just saved.
-                $prefs[$pref_name]['m'] &= ~Horde_Prefs::DIRTY;
-            }
-
-            $session['horde:prefs_session/' . $scope] = $prefs;
-        }
-    }
-
-    /**
-     * Perform cleanup operations.
-     *
-     * @param boolean $all  Cleanup all Horde preferences.
-     */
-    public function cleanup($all = false)
-    {
-        global $session;
-
-        // Perform a Horde-wide cleanup?
-        if ($all) {
-            unset($session['horde:prefs_session/']);
-        } else {
-            unset($session['horde:prefs_session/' . $this->_scope]);
-        }
-
-        parent::cleanup($all);
-    }
-
-}
diff --git a/framework/Core/lib/Horde/Core/Prefs/Storage/Session.php b/framework/Core/lib/Horde/Core/Prefs/Storage/Session.php
new file mode 100644
index 000000000..406053bd1
--- /dev/null
+++ b/framework/Core/lib/Horde/Core/Prefs/Storage/Session.php
@@ -0,0 +1,59 @@
+
+ * @category Horde
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Core
+ */
+class Horde_Core_Prefs_Storage_Session extends Horde_Prefs_Storage
+{
+    const SESS_KEY = 'horde:prefs_session/';
+
+    /**
+     */
+    public function get($scope)
+    {
+        global $session;
+
+        return isset($session[self::SESS_KEY . $scope])
+            ? $session[self::SESS_KEY . $scope]
+            : false;
+    }
+
+    /**
+     */
+    public function store($prefs)
+    {
+        foreach ($prefs as $scope => $vals) {
+            if (($old_vals = $this->get($scope)) === false) {
+                $old_vals = array();
+            }
+            $GLOBALS['session'][self::SESS_KEY . $scope] = array_merge($old_vals, $vals);
+        }
+    }
+
+    /**
+     */
+    public function remove($scope = null, $pref = null)
+    {
+        global $session;
+
+        if (is_null($scope)) {
+            unset($session[self::SESS_KEY]);
+        } elseif (is_null($pref)) {
+            unset($session[self::SESS_KEY . $this->_scope]);
+        } elseif ((($vals = $this->get($scope)) !== false) &&
+                  isset($vals[$pref])) {
+            unset($vals[$pref]);
+            $session[self::SESS_KEY . $scope] = $vals;
+        }
+    }
+
+}
diff --git a/framework/Core/lib/Horde/Registry.php b/framework/Core/lib/Horde/Registry.php
index e61dabe8b..9da47e4bc 100644
--- a/framework/Core/lib/Horde/Registry.php
+++ b/framework/Core/lib/Horde/Registry.php
@@ -1344,10 +1344,10 @@ class Horde_Registry
                 'user' => $this->getAuth()
             );
         } else {
-            /* If there is no logged in user, return an empty Horde_Prefs::
+            /* If there is no logged in user, return an empty Horde_Prefs
              * object with just default preferences. */
             $opts = array(
-                'cache' => 'Horde_Prefs_Cache_Null',
+                'cache' => null,
                 'session' => true
             );
         }
diff --git a/framework/Core/package.xml b/framework/Core/package.xml
index f781d4aae..59554f775 100644
--- a/framework/Core/package.xml
+++ b/framework/Core/package.xml
@@ -184,14 +184,13 @@ Application Framework.
        
        
       
-       
+       
         
         
        
         
         
        
-       
        
        
       
@@ -482,9 +481,8 @@ Application Framework.
    
    
    
-   
    
-   
+   
    
    
    
diff --git a/framework/Prefs/lib/Horde/Prefs.php b/framework/Prefs/lib/Horde/Prefs.php
index 449445730..ad8379ef3 100644
--- a/framework/Prefs/lib/Horde/Prefs.php
+++ b/framework/Prefs/lib/Horde/Prefs.php
@@ -19,43 +19,37 @@ class Horde_Prefs implements ArrayAccess
     /** Preference is administratively locked. */
     const LOCKED = 1;
 
-    /** Preference value has been changed. */
-    const DIRTY = 4;
-
     /** Preference value is the application default.
      *  DEFAULT is a reserved PHP constant. */
-    const PREFS_DEFAULT = 8;
+    const PREFS_DEFAULT = 2;
 
     /**
-     * Connection parameters.
+     * Caching object.
      *
-     * @var array
+     * @var Horde_Prefs_Storage
      */
-    protected $_params = array();
+    protected $_cache;
 
     /**
-     * String containing the name of the current scope. This is used
-     * to differentiate between sets of preferences.  By default, preferences
-     * belong to the "global" (Horde) scope.
+     * Translation provider.
      *
-     * @var string
+     * @var Horde_Translation
      */
-    protected $_scope = 'horde';
+    protected $_dict;
 
     /**
-     * Preferences list.  Stored by scope name.  Each preference has the
-     * following format:
-     * 
-     * [pref_name] => array(
-     *     [d] => (string) Default value
-     *     [m] => (integer) Pref mask
-     *     [v] => (string) Current pref value
-     * )
-     * 
+ * List of dirty prefs. * * @var array */ - protected $_scopes = array(); + protected $_dirty = array(); + + /** + * Hash holding preferences with hook functions defined. + * + * @var array + */ + protected $_hooks = array(); /** * General library options. @@ -63,46 +57,50 @@ class Horde_Prefs implements ArrayAccess * @var array */ protected $_opts = array( - 'cache' => 'Horde_Prefs_Cache_Null', + 'cache' => null, 'logger' => null, 'password' => '', 'sizecallback' => null, + 'storage' => null, 'user' => '' ); /** - * Caching object. + * String containing the name of the current scope. This is used + * to differentiate between sets of preferences. By default, preferences + * belong to the "global" (Horde) scope. * - * @var Horde_Prefs_Cache + * @var string */ - protected $_cache; + protected $_scope = 'horde'; /** - * Hash holding preferences with hook functions defined. + * Preferences list. Stored by scope name. Each preference has the + * following format: + *
+     * [pref_name] => array(
+     *     [d] => (string) Default value
+     *     [m] => (integer) Pref mask
+     *     [v] => (string) Current pref value
+     * )
+     * 
* * @var array */ - protected $_hooks = array(); - - /** - * Translation provider. - * - * @var Horde_Translation - */ - protected $_dict; + protected $_scopes = array(); /** - * List of dirty prefs. + * The storage driver. * - * @var array + * @var Horde_Prefs_Storage */ - protected $_dirty = array(); + protected $_storage; /** - * Attempts to return a concrete instance based on $driver. + * Constructor. * - * @param string $driver Either a driver name, or the full class name to - * use (class must extend Horde_Auth_Base). + * @param string $driver THe storage driver name. Either a driver name, + * or the full class name to use. * @param string $scope The scope for this set of preferences. * @param array $opts Additional confguration options: *
@@ -129,56 +127,57 @@ class Horde_Prefs implements ArrayAccess
      * @param array $params   A hash containing any additional configuration
      *                        or connection parameters a subclass might need.
      *
-     * @return Horde_Prefs  The newly created concrete instance.
-     * @throws Horde_Prefs_Exception
+     * @throws InvalidArgumentException
      */
-    static public function factory($driver, $scope, array $opts = array(),
-                                   array $params = array())
+    public function __construct($driver, $scope, array $opts,
+                                array $params = array())
     {
-        /* Base drivers (in Auth/ directory). */
-        $class = __CLASS__ . '_' . $driver;
-        if (!class_exists($class)) {
-            /* Explicit class name, */
-            $class = $driver;
-            if (!class_exists($class)) {
-                throw new Horde_Prefs_Exception(__CLASS__ . ': class definition not found - ' . $class);
-            }
+        if (!isset($opts['charset'])) {
+            throw new InvalidArgumentException(__CLASS__ . ': Missing charset parameter.');
         }
 
-        $prefs = new $class($scope, $opts, $params);
-        $prefs->retrieve($scope);
+        $this->_opts = array_merge($this->_opts, $opts);
+
+        $this->_cache = $this->_getStorage($this->_opts['cache']);
+        $this->_dict = isset($this->_opts['translation'])
+            ? $this->_opts['translation']
+            : new Horde_Translation_Gettext('Horde_Prefs', dirname(__FILE__) . '/../../locale');
+        $this->_scope = $scope;
+        $this->_storage = $this->_getStorage($driver, $params);
+
+        register_shutdown_function(array($this, 'store'));
 
-        return $prefs;
+        $this->retrieve($scope);
     }
 
     /**
-     * Constructor.
+     * Instantiate storage driver.
      *
-     * @param string $scope  The scope for this set of preferences.
-     * @param array $opts    See factory() for list of options.
-     * @param array $params  A hash containing any additional configuration
-     *                       or connection parameters a subclass might need.
+     * @param string $driver  Storage driver name.
+     * @param array $params   Storage driver parameters.
      *
-     * @throws InvalidArgumentException
+     * @return Horde_Prefs_Storage  The storage object.
+     * @throws Horde_Prefs_Exception
      */
-    protected function __construct($scope, $opts, $params)
+    protected function _getStorage($driver, $params = array())
     {
-        foreach (array('charset') as $val) {
-            if (!isset($opts[$val])) {
-                throw new InvalidArgumentException('Missing ' . $val . ' parameter.');
+        if (is_null($driver)) {
+            $class = __CLASS__ . '_Storage_Null';
+        } else {
+            /* Built-in drivers (in Storage/ directory). */
+            $class = __CLASS__ . '_Storage_' . $driver;
+            if (!class_exists($class)) {
+                /* Explicit class name, */
+                $class = $driver;
+                if (!class_exists($class)) {
+                    throw new Horde_Prefs_Exception(__CLASS__ . ': class definition not found - ' . $class);
+                }
             }
         }
 
-        $this->_opts = array_merge($this->_opts, $opts);
-        $this->_params = $params;
-        $this->_scope = $scope;
-
-        $this->_cache = new $this->_opts['cache']($this->getUser());
-        $this->_dict = isset($this->_opts['translation'])
-            ? $this->_opts['translation']
-            : new Horde_Translation_Gettext('Horde_Prefs', dirname(__FILE__) . '/../../locale');
+        $params['user'] = $this->getUser();
 
-        register_shutdown_function(array($this, 'store'));
+        return new $class($params);
     }
 
     /**
@@ -228,13 +227,23 @@ class Horde_Prefs implements ArrayAccess
      */
     public function remove($pref)
     {
-        // FIXME not updated yet - not removed from backend.
         $scope = $this->_getPrefScope($pref);
         unset(
             $this->_dirty[$scope][$pref],
             $this->_scopes[$scope][$pref]
         );
-        $this->_cache->clear($scope, $pref);
+
+        try {
+            $this->_storage->remove($scope, $pref);
+        } catch (Horde_Prefs_Exception $e) {
+            // TODO: logging
+        }
+
+        try {
+            $this->_cache->remove($scope, $pref);
+        } catch (Horde_Prefs_Exception $e) {
+            // TODO: logging
+        }
     }
 
     /**
@@ -249,7 +258,7 @@ class Horde_Prefs implements ArrayAccess
      *
      * @return boolean  True if the value was successfully set, false on a
      *                  failure.
-     * @throws Horde_Exception
+     * @throws Horde_Prefs_Exception
      */
     public function setValue($pref, $val, $convert = true)
     {
@@ -260,11 +269,13 @@ class Horde_Prefs implements ArrayAccess
             return false;
         }
 
-        $result = $this->_setValue($pref, $val, true, $convert);
+        $result = $this->_setValue($pref, $val, $convert);
 
         if ($result && $this->isDirty($pref)) {
-            $this->_cache->update($scope, array(
-                $pref => $this->_scopes[$scope][$pref]
+            $this->_cache->store(array(
+                $scope => array(
+                    $pref => $this->_scopes[$scope][$pref]
+                )
             ));
 
             /* If this preference has a change hook, call it now. */
@@ -291,8 +302,6 @@ class Horde_Prefs implements ArrayAccess
      *
      * @param string $pref      The name of the preference to modify.
      * @param string $val       The new value for this preference.
-     * @param boolean $dirty    True if we should mark the new value as
-     *                          dirty (changed).
      * @param boolean $convert  If true the preference value gets converted
      *                          from the current charset to the backend's
      *                          charset.
@@ -300,7 +309,7 @@ class Horde_Prefs implements ArrayAccess
      * @return boolean  True if the value was successfully set, false on a
      *                  failure.
      */
-    protected function _setValue($pref, $val, $dirty = true, $convert = true)
+    protected function _setValue($pref, $val, $convert = true)
     {
         if ($convert) {
             $val = $this->convertToDriver($val);
@@ -326,14 +335,16 @@ class Horde_Prefs implements ArrayAccess
 
         // Assign the new value, unset the "default" bit, and set the
         // "dirty" bit.
-        if (empty($this->_scopes[$scope][$pref]['m'])) {
-            $this->_scopes[$scope][$pref]['m'] = 0;
+        $ptr = &$this->_scopes[$scope][$pref];
+        if (empty($ptr['m'])) {
+            $ptr['m'] = 0;
         }
-        $this->_scopes[$scope][$pref]['v'] = $val;
-        $this->setDefault($pref, false);
-        if ($dirty) {
-            $this->setDirty($pref, true);
+        if (!isset($ptr['d'])) {
+            $ptr['d'] = $ptr['v'];
         }
+        $ptr['v'] = $val;
+        $this->setDefault($pref, false);
+        $this->setDirty($pref, true);
 
         if ($this->_opts['logger']) {
             $this->_opts['logger']->log(__CLASS__ . ': Storing preference value (' . $pref . ')', 'DEBUG');
@@ -405,8 +416,8 @@ class Horde_Prefs implements ArrayAccess
     /**
      * Modifies the "dirty" bit for the given preference.
      *
-     * @param string $pref      The name of the preference to modify.
-     * @param boolean $bool     The new boolean value for the "dirty" bit.
+     * @param string $pref   The name of the preference to modify.
+     * @param boolean $bool  The new boolean value for the "dirty" bit.
      */
     public function setDirty($pref, $bool)
     {
@@ -417,8 +428,6 @@ class Horde_Prefs implements ArrayAccess
         } else {
             unset($this->_dirty[$scope][$pref]);
         }
-
-        $this->_setMask($pref, $bool, self::DIRTY);
     }
 
     /**
@@ -430,7 +439,8 @@ class Horde_Prefs implements ArrayAccess
      */
     public function isDirty($pref)
     {
-        return $this->_getMask($pref, self::DIRTY);
+        $scope = $this->_getPrefScope($pref);
+        return isset($this->_dirty[$scope][$pref]);
     }
 
     /**
@@ -455,14 +465,13 @@ class Horde_Prefs implements ArrayAccess
     {
         $scope = $this->_getPrefScope($pref);
 
-        return empty($this->_scopes[$scope][$pref]['d'])
-            ? ''
-            : $this->_scopes[$scope][$pref]['d'];
+        return isset($this->_scopes[$scope][$pref]['d'])
+            ? $this->_scopes[$scope][$pref]['d']
+            : '';
     }
 
     /**
-     * Determines if the current preference value is the default
-     * value from prefs.php or a user defined value
+     * Determines if the current preference value is the default value.
      *
      * @param string $pref  The name of the preference to check.
      *
@@ -527,7 +536,7 @@ class Horde_Prefs implements ArrayAccess
     }
 
     /**
-     * Retrieves preferences for the current scope + the 'horde' scope.
+     * Retrieves preferences for the current scope.
      *
      * @param string $scope  Optional scope specifier - if not present the
      *                       current scope will be used.
@@ -559,40 +568,56 @@ class Horde_Prefs implements ArrayAccess
         // Basic initialization so _something_ is always set.
         $this->_scopes[$scope] = array();
 
-        // Always set defaults to pick up new default values, etc.
         $this->_setDefaults($scope);
 
         // Now check the prefs cache for existing values.
-        if (($cached = $this->_cache->get($scope)) !== false) {
-            $this->_scopes[$scope] = $cached;
-            return;
+        try {
+            if (($cached = $this->_cache->get($scope)) !== false) {
+                $this->_scopes[$scope] = $cached;
+                return;
+            }
+        } catch (Horde_Prefs_Exception $e) {}
+
+        if (($prefs = $this->_storage->get($scope)) !== false) {
+            foreach ($prefs as $name => $val) {
+                if ($this->isDefault($name)) {
+                    $this->_scopes[$scope][$name]['d'] = $this->_scopes[$scope][$name]['v'];
+
+                } elseif (!isset($this->_scopes[$scope][$name])) {
+                    $this->_scopes[$scope][$name] = array(
+                        'm' => 0
+                    );
+                }
+                $this->_scopes[$scope][$name]['v'] = $val;
+                $this->setDefault($name, false);
+            }
         }
 
-        $this->_retrieve($scope);
         $this->_callHooks($scope);
 
         /* Update the cache. */
-        $this->_cache->update($scope, $this->_scopes[$scope]);
+        $this->_cache->store(array($scope => $this->_scopes[$scope]));
     }
 
     /**
      * This function will be run at the end of every request as a shutdown
      * function (registered by the constructor).  All prefs with the
-     * dirty bit set will be saved to the storage backend at this time; thus,
-     * there is no need to manually call $prefs->store() every time a
-     * preference is changed.
+     * dirty bit set will be saved to the storage backend.
      */
     public function store()
     {
-    }
+        if (!empty($this->_dirty)) {
+            try {
+                $this->_storage->store($this->_dirty);
 
-    /**
-     * TODO
-     *
-     * @throws Horde_Exception
-     */
-    protected function _retrieve()
-    {
+                /* Clear the dirty flag. */
+                foreach ($this->_dirty as $k => $v) {
+                    foreach (array_keys($v) as $name) {
+                        $this->setDirty($name, false);
+                    }
+                }
+            } catch (Horde_Prefs_Exception $e) {}
+        }
     }
 
     /**
@@ -609,22 +634,21 @@ class Horde_Prefs implements ArrayAccess
             $this->_dirty = $this->_scopes = array();
 
             /* Destroy the contents of the preferences cache. */
-            $this->_cache->clear();
+            try {
+                $this->_cache->remove();
+            } catch (Horde_Prefs_Exception $e) {}
         } else {
+            $scope = $this->getScope();
+
+            $this->_dirty[$scope] = $this->_scopes[$scope] = array();
             /* Remove this scope from the preferences cache. */
-            $this->_cache->clear($this->getScope());
+            try {
+                $this->_cache->remove($scope);
+            } catch (Horde_Prefs_Exception $e) {}
         }
     }
 
     /**
-     * Clears all preferences from the backend.
-     */
-    public function clear()
-    {
-        $this->cleanup(true);
-    }
-
-    /**
      * Converts a value from the driver's charset to the specified charset.
      *
      * @param mixed $value  A value to convert.
@@ -678,16 +702,12 @@ class Horde_Prefs implements ArrayAccess
 
             $name = str_replace('.', '_', $name);
 
-            $mask = 0;
-            $mask &= ~self::DIRTY;
-            $mask |= self::PREFS_DEFAULT;
-
+            $mask = self::PREFS_DEFAULT;
             if (!empty($pref['locked'])) {
                 $mask |= self::LOCKED;
             }
 
             $this->_scopes[$scope][$name] = array(
-                'd' => $pref['value'],
                 'm' => $mask,
                 'v' => $pref['value']
             );
@@ -703,8 +723,6 @@ class Horde_Prefs implements ArrayAccess
      * preferences that have hooks to the result of the hook.
      *
      * @param string $scope  The preferences scope to call hooks for.
-     *
-     * @throws Horde_Exception
      */
     protected function _callHooks($scope)
     {
diff --git a/framework/Prefs/lib/Horde/Prefs/Cache.php b/framework/Prefs/lib/Horde/Prefs/Cache.php
deleted file mode 100644
index 469eea2e7..000000000
--- a/framework/Prefs/lib/Horde/Prefs/Cache.php
+++ /dev/null
@@ -1,61 +0,0 @@
-
- * @category Horde
- * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
- * @package  Prefs
- */
-abstract class Horde_Prefs_Cache
-{
-    /**
-     * The username.
-     *
-     * @var string
-     */
-    protected $_user;
-
-    /**
-     * Constructor.
-     *
-     * @param string $user  The current username.
-     */
-    public function __construct($user)
-    {
-        $this->_user = $user;
-    }
-
-    /**
-     * Gets a cache entry.
-     *
-     * @param string $scope  The scope of the prefs to get.
-     *
-     * @return mixed  Prefs array on success, false on failure.
-     */
-    abstract public function get($scope);
-
-    /**
-     * Updates a cache entry.
-     *
-     * @param string $scope  The scope of the prefs being updated.
-     * @param array $prefs   The preferences to update.
-     */
-    abstract public function update($scope, $prefs);
-
-    /**
-     * Clear cache entries.
-     *
-     * @param string $scope  The scope of the prefs to clear. If null, clears
-     *                       entire cache.
-     * @param string $scope  The pref to clear. If null, clears the entire
-     *                       scope.
-     */
-    abstract public function clear($scope = null, $pref = null);
-
-}
diff --git a/framework/Prefs/lib/Horde/Prefs/File.php b/framework/Prefs/lib/Horde/Prefs/File.php
deleted file mode 100644
index 5a58004d7..000000000
--- a/framework/Prefs/lib/Horde/Prefs/File.php
+++ /dev/null
@@ -1,209 +0,0 @@
-
- * @category Horde
- * @package  Prefs
- */
-class Horde_Prefs_File extends Horde_Prefs_Base
-{
-    /**
-     * Current version number of the data format
-     *
-     * @var int
-     */
-    protected $_version = 2;
-
-    /**
-     * Directory to store the preferences
-     *
-     * @var string
-     */
-    protected $_dirname;
-
-    /**
-     * Full path to the current preference file
-     *
-     * @var string
-     */
-    protected $_fullpath;
-
-    /**
-     * Cached unserialized data of all scopes
-     *
-     * @var array
-     */
-    protected $_fileCache = null;
-
-    /**
-     * Constructor.
-     *
-     * @param string $scope  The scope for this set of preferences.
-     * @param array $opts    See factory() for list of options.
-     * @param array $params  Additional parameters:
-     * 
-     * 'directory' - (string) [REQUIRED] Preference storage directory.
-     * 
- * - * @throws InvalidArgumentException - */ - public function __construct($scope, $opts, $params); - { - parent::__construct($scope, $opts, $params); - - // Sanity check for directory - if (empty($params['directory']) || !is_dir($params['directory'])) { - throw new InvalidArgumentException('Preference storage directory is not available.'); - } - if (!is_writable($params['directory'])) { - throw new InvalidArgumentException(sprintf('Directory %s is not writeable.', $params['directory'])); - } - - $this->_dirname = $params['directory']; - $this->_fullpath = $this->_dirname . '/' . basename($user) . '.prefs'; - } - - /** - * Retrieves the requested set of preferences from the current session. - * - * @param string $scope Scope specifier. - * - * @throws Horde_Prefs_Exception - */ - protected function _retrieve($scope) - { - if (is_null($this->_dirname)) { - return; - } - - if (is_null($this->_fileCache)) { - // Try to read - $this->_fileCache = $this->_readCache(); - if (is_null($this->_fileCache)) { - return; - } - - // Check version number. We can call format transformations hooks - // in the future. - if (!is_array($this->_fileCache) || - !array_key_exists('__file_version', $this->_fileCache) || - !($this->_fileCache['__file_version'] == $this->_version)) { - if ($this->_fileCache['__file_version'] == 1) { - $this->transformV1V2(); - } else { - throw new Horde_Prefs_Exception(sprintf('Wrong version number found: %s (should be %d)', $this->_fileCache['__file_version'], $this->_version)); - } - } - } - - // Check if the scope exists - if (empty($scope) || !array_key_exists($scope, $this->_fileCache)) { - return; - } - - // Merge config values - foreach ($this->_fileCache[$scope] as $name => $val) { - if (isset($this->_scopes[$scope][$name])) { - $this->_scopes[$scope][$name]['v'] = $val; - $this->_scopes[$scope][$name]['m'] &= ~self::PREFS_DEFAULT; - } else { - // This is a shared preference. - $this->_scopes[$scope][$name] = array('v' => $val, - 'm' => 0, - 'd' => null); - } - } - } - - /** - * Read data from disk. - * - * @return mixed Data array on success or null on error. - */ - protected function _readCache() - { - return file_exists($this->_fullpath) - ? unserialize(file_get_contents($this->_fullpath)) - : null; - } - - /** - * Transforms the broken version 1 format into version 2. - */ - public function transformV1V2() - { - $version2 = array('__file_version' => 2); - foreach ($this->_fileCache as $scope => $prefs) { - if ($scope != '__file_version') { - foreach ($prefs as $name => $pref) { - /* Default values should not have been stored by the - * driver. They are being set via the prefs.php files. */ - if (!($pref['m'] & self::PREFS_DEFAULT)) { - $version2[$scope][$name] = $pref['v']; - } - } - } - } - $this->_fileCache = $version2; - } - - /** - * Write data to disk - * - * @return boolean True on success. - */ - protected function _writeCache() - { - $tmp_file = Horde_Util::getTempFile('PrefsFile', true, $this->_dirname); - - $data = serialize($this->_fileCache); - - if (file_put_contents($tmp_file, $data) === false) { - return false; - } - - return @rename($tmp_file, $this->_fullpath); - } - - /** - * Stores preferences in the current session. - * - * @throws Horde_Prefs_Exception - */ - public function store() - { - if (is_null($this->_dirname) || empty($this->_dirty)) { - return; - } - - // Read in all existing preferences, if any. - $this->_retrieve(''); - if (!is_array($this->_fileCache)) { - $this->_fileCache = array('__file_version' => $this->_version); - } - - // Update all values from dirty scope - foreach ($this->_dirty as $scope => $prefs) { - foreach ($prefs as $name => $pref) { - // Don't store locked preferences. - if (!($this->_scopes[$scope][$name]['m'] & self::LOCKED)) { - $this->_fileCache[$scope][$name] = $pref['v']; - - // Clean the pref since it was just saved. - $this->_scopes[$scope][$name]['m'] &= ~self::DIRTY; - } - } - } - - if ($this->_writeCache() == false) { - throw new Horde_Prefs_Exception(sprintf('Write of preferences to %s failed', $this->_filename)); - } - } - -} diff --git a/framework/Prefs/lib/Horde/Prefs/Session.php b/framework/Prefs/lib/Horde/Prefs/Session.php deleted file mode 100644 index 9bc62bef4..000000000 --- a/framework/Prefs/lib/Horde/Prefs/Session.php +++ /dev/null @@ -1,68 +0,0 @@ - - * @category Horde - * @package Prefs - */ -class Horde_Prefs_Session extends Horde_Prefs -{ - /** - * Retrieves the requested set of preferences from the current session. - * - * @param string $scope Scope specifier. - */ - protected function _retrieve($scope) - { - if (isset($_SESSION['horde_prefs'][$scope])) { - $this->_scopes[$scope] = $_SESSION['horde_prefs'][$scope]; - } - } - - /** - * Stores preferences in the current session. - */ - public function store() - { - // Create and register the preferences array, if necessary. - if (!isset($_SESSION['horde_prefs'])) { - $_SESSION['horde_prefs'] = array(); - } - - // Copy the current preferences into the session variable. - foreach ($this->_scopes as $scope => $prefs) { - $pref_keys = array_keys($prefs); - foreach ($pref_keys as $pref_name) { - // Clean the pref since it was just saved. - $prefs[$pref_name]['m'] &= ~Horde_Prefs::DIRTY; - } - - $_SESSION['horde_prefs'][$scope] = $prefs; - } - } - - /** - * Perform cleanup operations. - * - * @param boolean $all Cleanup all Horde preferences. - */ - public function cleanup($all = false) - { - // Perform a Horde-wide cleanup? - if ($all) { - unset($_SESSION['horde_prefs']); - } else { - unset($_SESSION['horde_prefs'][$this->_scope]); - $_SESSION['horde_prefs']['_filled'] = false; - } - - parent::cleanup($all); - } - -} diff --git a/framework/Prefs/lib/Horde/Prefs/Storage.php b/framework/Prefs/lib/Horde/Prefs/Storage.php new file mode 100644 index 000000000..2655dde75 --- /dev/null +++ b/framework/Prefs/lib/Horde/Prefs/Storage.php @@ -0,0 +1,69 @@ + + * @category Horde + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @package Prefs + */ +abstract class Horde_Prefs_Storage +{ + /** + * Configuration parameters. + * + * @var string + */ + protected $_params = array(); + + /** + * Constructor. + * + * @param array $params Configuration parameters. + *
+     * 'user' - (string) The current username.
+     * 
+ */ + public function __construct(array $params = array()) + { + $this->_params = $params; + } + + /** + * Retrieves the requested preferences scope from the storage backend. + * + * @param string $scope Scope specifier. + * + * @return mixed Keys are pref names, values are pref values. Returns + * false if no data is available. + * @throws Horde_Db_Exception + */ + abstract public function get($scope); + + /** + * Stores preferences in the storage backend. + * + * @param array $prefs The preference list. + * + * @throws Horde_Db_Exception + */ + abstract public function store($prefs); + + /** + * Removes preferences from the backend. + * + * @param string $scope The scope of the prefs to clear. If null, clears + * entire cache. + * @param string $pref The pref to clear. If null, clears the entire + * scope. + * + * @throws Horde_Db_Exception + */ + abstract public function remove($scope = null, $pref = null); + +} diff --git a/framework/Prefs/lib/Horde/Prefs/Storage/File.php b/framework/Prefs/lib/Horde/Prefs/Storage/File.php new file mode 100644 index 000000000..1815f9aef --- /dev/null +++ b/framework/Prefs/lib/Horde/Prefs/Storage/File.php @@ -0,0 +1,155 @@ + + * @author Michael Slusarz + * @category Horde + * @package Prefs + */ +class Horde_Prefs_Storage_File extends Horde_Prefs_Storage +{ + /* Current version number of the data format */ + const VERSION = 2; + + /** + * Cached unserialized data of all scopes. + * + * @var array + */ + protected $_fileCache = null; + + /** + * Full path to the current preference file. + * + * @var string + */ + protected $_fullpath; + + /** + * Constructor. + * + * @param array $params Configuration parameters: + *
+     * 'directory' - (string) [REQUIRED] Preference storage directory.
+     * 
+ * + * @throws InvalidArgumentException + */ + public function __construct(array $params = array()) + { + // Sanity check for directory + if (empty($params['directory']) || !is_dir($params['directory'])) { + throw new InvalidArgumentException('Preference storage directory is not available.'); + } + if (!is_writable($params['directory'])) { + throw new InvalidArgumentException(sprintf('Directory %s is not writeable.', $params['directory'])); + } + + parent::__construct($scope, $opts, $params); + + $this->_fullpath = $this->params['directory'] . '/' . basename($this->_params['user']) . '.prefs'; + } + + /** + */ + public function get($scope) + { + if (!isset($this->_params['directory'])) { + return false; + } + + if (is_null($this->_fileCache)) { + // Try to read + if (!file_exists($this->_fullpath)) { + return false; + } + $this->_fileCache = unserialize(file_get_contents($this->_fullpath)); + + // Check version number. We can call format transformations hooks + // in the future. + if (!is_array($this->_fileCache) || + !array_key_exists('__file_version', $this->_fileCache) || + !($this->_fileCache['__file_version'] == self::VERSION)) { + if ($this->_fileCache['__file_version'] == 1) { + $this->transformV1V2(); + } else { + throw new Horde_Prefs_Exception(sprintf('Wrong version number found: %s (should be %d)', $this->_fileCache['__file_version'], self::VERSION)); + } + } + } + + // Check if the scope exists + if (empty($scope) || !isset($this->_fileCache[$scope])) { + return false; + } + + $ret = array(); + + foreach ($this->_fileCache[$scope] as $name => $val) { + $ret[$name] = $val; + } + + return $ret; + } + + /** + */ + public function store($prefs) + { + if (!isset($this->_params['directory'])) { + return; + } + + // Read in all existing preferences, if any. + $this->get(''); + if (!is_array($this->_fileCache)) { + $this->_fileCache = array('__file_version' => self::VERSION); + } + + foreach ($prefs as $scope => $p) { + foreach ($p as $name => $val) { + $this->_fileCache[$scope][$name] = $pref['v']; + } + } + + $tmp_file = Horde_Util::getTempFile('PrefsFile', true, $this->_params['directory']); + + if ((file_put_contents($tmp_file, serialize($this->_fileCache)) === false) || + (@rename($tmp_file, $this->_fullpath) === false)) { + throw new Horde_Prefs_Exception(sprintf('Write of preferences to %s failed', $this->_filename)); + } + } + + /** + */ + public function remove($scope = null, $pref = null) + { + // TODO + } + + /* Helper functions. */ + + /** + * Transforms the broken version 1 format into version 2. + */ + public function transformV1V2() + { + $version2 = array('__file_version' => 2); + foreach ($this->_fileCache as $scope => $prefs) { + if ($scope != '__file_version') { + foreach ($prefs as $name => $pref) { + $version2[$scope][$name] = $pref['v']; + } + } + } + + $this->_fileCache = $version2; + } + +} diff --git a/framework/Prefs/lib/Horde/Prefs/Imsp.php b/framework/Prefs/lib/Horde/Prefs/Storage/Imsp.php similarity index 51% rename from framework/Prefs/lib/Horde/Prefs/Imsp.php rename to framework/Prefs/lib/Horde/Prefs/Storage/Imsp.php index e3d5efc3c..805cff294 100644 --- a/framework/Prefs/lib/Horde/Prefs/Imsp.php +++ b/framework/Prefs/lib/Horde/Prefs/Storage/Imsp.php @@ -11,16 +11,9 @@ * @category Horde * @package Prefs */ -class Horde_Prefs_Imsp extends Horde_Prefs_Base +class Horde_Prefs_Storage_Imsp extends Horde_Prefs_Storage { /** - * Handle for the IMSP server connection. - * - * @var Net_IMSP - */ - protected $_imsp; - - /** * Boolean indicating whether or not we're connected to the IMSP server. * * @var boolean @@ -28,62 +21,41 @@ class Horde_Prefs_Imsp extends Horde_Prefs_Base protected $_connected = false; /** - * Retrieves the requested set of preferences from the IMSP server. - * - * @param string $scope Scope specifier. + * Handle for the IMSP server connection. * - * @throws Horde_Prefs_Exception + * @var Net_IMSP + */ + protected $_imsp; + + /** */ - protected function _retrieve($scope) + public function get($scope) { $this->_connect(); $prefs = $this->_imsp->get($scope . '.*'); if ($prefs instanceof PEAR_Error) { - if ($this->_opts['logger']) { - $this->_opts['logger']->log($prefs, 'ERR'); - } - return; + throw new Horde_Prefs_Exception($prefs); } + $ret = array(); + foreach ($prefs as $name => $val) { $name = str_replace($scope . '.', '', $name); if ($val != '-') { - if (isset($this->_scopes[$scope][$name])) { - $this->_scopes[$scope][$name]['v'] = $val; - $this->_scopes[$scope][$name]['m'] &= ~self::PREFS_DEFAULT; - } else { - // This is a shared preference. - $this->_scopes[$scope][$name] = array('v' => $val, - 'm' => 0, - 'd' => null); - } + $ret[$name] = $val; } } } /** - * Stores all dirty prefs to IMSP server. */ - public function store() + public function store($prefs) { - // Get the list of preferences that have changed. If there are - // none, no need to hit the backend. - if (empty($this->_dirty)) { - return; - } - $this->_connect(); - foreach ($this->_dirty as $scope => $prefs) { - $updated = array(); - - foreach ($prefs as $name => $pref) { - // Don't store locked preferences. - if ($this->_scopes[$scope][$name]['m'] & self::LOCKED) { - continue; - } - + foreach ($prefs as $scope => $p) { + foreach ($p as $name => $pref) { $value = $pref['v']; if (empty($value)) { $value = '-'; @@ -91,24 +63,22 @@ class Horde_Prefs_Imsp extends Horde_Prefs_Base $result = $this->_imsp->set($scope . '.' . $name, $value); if ($result instanceof PEAR_Error) { - if ($this->_opts['logger']) { - $this->_opts['logger']->log($result, 'ERR'); - } - return; + throw new Horde_Prefs_Exception($result); } - - // Clean the pref since it was just saved. - $this->_scopes[$scope][$name]['m'] &= ~self::DIRTY; - - $updated[$name] = $this->_scopes[$scope][$name]; } - - // Update the cache for this scope. - $this->_cache->update($scope, $updated); } } /** + */ + public function remove($scope = null, $pref = null) + { + // TODO + } + + /* Helper functions. */ + + /** * Attempts to set up a connection to the IMSP server. * * @throws Horde_Prefs_Exception @@ -131,14 +101,9 @@ class Horde_Prefs_Imsp extends Horde_Prefs_Base $this->_imsp = Net_IMSP::factory('Options', $this->_params); $result = $this->_imsp->init(); if ($result instanceof PEAR_Error) { - if ($this->_opts['logger']) { - $this->_opts['logger']->log($result, 'ERR'); - } throw new Horde_Prefs_Exception($result); } - // TODO - //$this->_imsp->setLogger($GLOBALS['conf']['log']); $this->_connected = true; } diff --git a/framework/Prefs/lib/Horde/Prefs/Kolab.php b/framework/Prefs/lib/Horde/Prefs/Storage/Kolab.php similarity index 65% rename from framework/Prefs/lib/Horde/Prefs/Kolab.php rename to framework/Prefs/lib/Horde/Prefs/Storage/Kolab.php index 2d4cae3c8..8b1fcd6b3 100644 --- a/framework/Prefs/lib/Horde/Prefs/Kolab.php +++ b/framework/Prefs/lib/Horde/Prefs/Storage/Kolab.php @@ -1,7 +1,7 @@ @@ -13,17 +13,14 @@ * @category Horde * @package Prefs */ -class Horde_Prefs_Kolab extends Horde_Prefs_Ldap +class Horde_Prefs_Storage_Kolab extends Horde_Prefs_Storage_Ldap { /** * Constructor. * - * @param string $scope The scope for this set of preferences. - * @param array $opts See factory() for list of options. - * @param array $params A hash containing any additional configuration - * or connection parameters a subclass might need. + * @param array $params Configuration parameters. */ - protected function __construct($scope, $opts, $params) + public function __construct(array $params = array()) { require_once 'Horde/Kolab.php'; $params = array( @@ -37,7 +34,7 @@ class Horde_Prefs_Kolab extends Horde_Prefs_Ldap 'uid' => 'mail' ); - parent::__construct($scope, $opts, $params); + parent::__construct($params); } } diff --git a/framework/Prefs/lib/Horde/Prefs/KolabImap.php b/framework/Prefs/lib/Horde/Prefs/Storage/KolabImap.php similarity index 53% rename from framework/Prefs/lib/Horde/Prefs/KolabImap.php rename to framework/Prefs/lib/Horde/Prefs/Storage/KolabImap.php index 3e0a5eb18..cac031eca 100644 --- a/framework/Prefs/lib/Horde/Prefs/KolabImap.php +++ b/framework/Prefs/lib/Horde/Prefs/Storage/KolabImap.php @@ -11,9 +11,16 @@ * @category Horde * @package Prefs */ -class Horde_Prefs_KolabImap extends Horde_Prefs_Base +class Horde_Prefs_Storage_KolabImap extends Horde_Prefs_Storage { /** + * Handle for the current Kolab connection. + * + * @var Kolab + */ + protected $_connection; + + /** * ID of the config default share * * @var string @@ -21,11 +28,82 @@ class Horde_Prefs_KolabImap extends Horde_Prefs_Base protected $_share; /** - * Handle for the current Kolab connection. - * - * @var Kolab */ - protected $_connection; + public function get($scope) + { + $this->_connect(); + + $pref = $this->_getPref($scope); + + if (is_null($pref)) { + /* No preferences saved yet. */ + return false; + } + + $ret = array(); + + foreach ($pref['pref'] as $prefstr) { + // If the string doesn't contain a colon delimiter, skip it. + if (strpos($prefstr, ':') !== false) { + // Split the string into its name:value components. + list($name, $val) = explode(':', $prefstr, 2); + $ret[$name] = base64_decode($val); + } + } + } + + /** + */ + public function store($prefs) + { + $this->_connect(); + + // Build a hash of the preferences and their values that need + // to be stored on the IMAP server. Because we have to update + // all of the values of a multi-value entry wholesale, we + // can't just pick out the dirty preferences; we must update + // every scope that has dirty preferences. + foreach ($prefs as $scope => $vals) { + $new_values = array(); + foreach ($vals as $name => $pref) { + $new_values[] = $name . ':' . base64_encode($pref['v']); + } + + $pref = $this->_getPref($scope); + + if (is_null($pref)) { + $old_uid = null; + $prefs_uid = $this->_connection->_storage->generateUID(); + } else { + $old_uid = $pref['uid']; + $prefs_uid = $pref['uid']; + } + + $object = array( + 'uid' => $prefs_uid, + 'application' => $scope, + 'pref' => $new_values + ); + + $result = $this->_connection->_storage->save($object, $old_uid); + if ($result instanceof PEAR_Error) { + throw new Horde_Prefs_Exception($result); + } + } + } + + /** + */ + public function remove($scope = null, $pref = null) + { + if (is_null($scope)) { + $this->_connection->deleteAll(); + } else { + // TODO + } + } + + /* Helper functions. */ /** * Opens a connection to the Kolab server. @@ -41,9 +119,6 @@ class Horde_Prefs_KolabImap extends Horde_Prefs_Base $shares = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Share')->create('h-prefs'); $default = $shares->getDefaultShare(); if ($default instanceof PEAR_Error) { - if ($this->_opts['logger']) { - $this->_opts['logger']->log($default, 'ERR'); - } throw new Horde_Prefs_Exception($default); } $this->_share = $default->getName(); @@ -51,17 +126,11 @@ class Horde_Prefs_KolabImap extends Horde_Prefs_Base require_once 'Horde/Kolab.php'; $connection = new Kolab('h-prefs'); if ($connection instanceof PEAR_Error) { - if ($this->_opts['logger']) { - $this->_opts['logger']->log($connection, 'ERR'); - } throw new Horde_Prefs_Exception($connection); } $result = $this->_connection->open($this->_share, 1); if ($result instanceof PEAR_Error) { - if ($this->_opts['logger']) { - $this->_opts['logger']->log($result, 'ERR'); - } throw new Horde_Prefs_Exception($result); } @@ -69,48 +138,6 @@ class Horde_Prefs_KolabImap extends Horde_Prefs_Base } /** - * Retrieves the requested set of preferences from the user's config folder. - * - * @param string $scope Scope specifier. - * - * @throws Horde_Prefs_Exception - */ - protected function _retrieve($scope) - { - $this->_connect(); - - try { - $pref = $this->_getPref($scope); - } catch (Horde_Prefs_Exception $e) { - return; - } - - if (is_null($pref)) { - /* No preferences saved yet */ - return; - } - - foreach ($pref['pref'] as $prefstr) { - // If the string doesn't contain a colon delimiter, skip it. - if (strpos($prefstr, ':') === false) { - continue; - } - - // Split the string into its name:value components. - list($name, $val) = explode(':', $prefstr, 2); - if (isset($this->_scopes[$scope][$name])) { - $this->_scopes[$scope][$name]['v'] = base64_decode($val); - $this->_scopes[$scope][$name]['m'] &= ~self::PREFS_DEFAULT; - } else { - // This is a shared preference. - $this->_scopes[$scope][$name] = array('v' => base64_decode($val), - 'm' => 0, - 'd' => null); - } - } - } - - /** * Retrieves the requested preference from the user's config folder. * * @param string $scope Scope specifier. @@ -124,9 +151,6 @@ class Horde_Prefs_KolabImap extends Horde_Prefs_Base $prefs = $this->_connection->getObjects(); if ($prefs instanceof PEAR_Error) { - if ($this->_opts['logger']) { - $this->_opts['logger']->log($prefs, 'ERR'); - } throw new Horde_Prefs_Exception($prefs); } @@ -139,81 +163,5 @@ class Horde_Prefs_KolabImap extends Horde_Prefs_Base return null; } - /** - * Stores preferences to the Kolab server. - * - * @throws Horde_Prefs_Exception - */ - public function store() - { - // Get the list of preferences that have changed. If there are - // none, no need to hit the backend. - if (empty($this->_dirty)) { - return; - } - - $this->_connect(); - - // Build a hash of the preferences and their values that need - // to be stored on the IMAP server. Because we have to update - // all of the values of a multi-value entry wholesale, we - // can't just pick out the dirty preferences; we must update - // every scope that has dirty preferences. - foreach (array_keys($this->_dirty) as $scope) { - $new_values = array(); - foreach ($this->_scopes[$scope] as $name => $pref) { - // Don't store locked preferences. - if (!($pref['m'] & self::LOCKED)) { - $new_values[] = $name . ':' . base64_encode($pref['v']); - } - } - - try { - $pref = $this->_getPref($scope); - } catch (Horde_Prefs_Exception $e) { - return; - } - - if (is_null($pref)) { - $old_uid = null; - $prefs_uid = $this->_connection->_storage->generateUID(); - } else { - $old_uid = $pref['uid']; - $prefs_uid = $pref['uid']; - } - - $object = array( - 'uid' => $prefs_uid, - 'application' => $scope, - 'pref' => $new_values - ); - - $result = $this->_connection->_storage->save($object, $old_uid); - if ($result instanceof PEAR_Error) { - if ($this->_opts['logger']) { - $this->_opts['logger']->log($result, 'ERR'); - } - return; - } - } - - // Clean the preferences since they were just saved. - foreach ($this->_dirty as $scope => $prefs) { - foreach ($prefs as $name => $pref) { - $this->_scopes[$scope][$name]['m'] &= ~self::DIRTY; - } - - // Update the cache for this scope. - $this->_cache->update($scope, $prefs); - } - } - - /** - * Clears all preferences from the kolab_imap backend. - */ - public function clear() - { - return $this->_connection->deleteAll(); - } } diff --git a/framework/Prefs/lib/Horde/Prefs/Ldap.php b/framework/Prefs/lib/Horde/Prefs/Storage/Ldap.php similarity index 63% rename from framework/Prefs/lib/Horde/Prefs/Ldap.php rename to framework/Prefs/lib/Horde/Prefs/Storage/Ldap.php index b2cf3d3b3..84598c94b 100644 --- a/framework/Prefs/lib/Horde/Prefs/Ldap.php +++ b/framework/Prefs/lib/Horde/Prefs/Storage/Ldap.php @@ -1,6 +1,6 @@ * @author Ben Klang + * @author Michael Slusarz * @category Horde * @package Prefs */ -class Horde_Prefs_Ldap extends Horde_Prefs +class Horde_Prefs_Storage_Ldap extends Horde_Prefs_Storage { /** * Handle for the current LDAP connection. @@ -38,9 +39,7 @@ class Horde_Prefs_Ldap extends Horde_Prefs /** * Constructor. * - * @param string $scope The scope for this set of preferences. - * @param array $opts See factory() for list of options. - * @param array $params Additional configuration options: + * @param array $params Configuration options: *
      * basedn - (string) [REQUIRED] The base DN for the LDAP server.
      * hostspec - (string) [REQUIRED] The hostname of the LDAP server.
@@ -62,22 +61,170 @@ class Horde_Prefs_Ldap extends Horde_Prefs
      *           DEFAULT: NONE (system default will be used)
      * 
*/ - protected function __construct($scope, $opts, $params) + public function __construct(array $params = array()) { /* If a valid server port has not been specified, set the default. */ - if (!isset($params['port']) || !is_int($params['port'])) { + if (!isset($params['port']) || !is_integer($params['port'])) { $params['port'] = 389; } - parent::__construct($scope, $opts, $params); + parent::__construct($params); } /** + */ + public function get($scope) + { + $this->_connect(); + + // Search for the multi-valued field containing the array of + // preferences. + $search = @ldap_search($this->_connection, $this->_params['basedn'], + $this->_params['uid'] . '=' . $this->params['user'], + array($scope . 'Prefs')); + if ($search === false) { + throw new Horde_Prefs_Exception(sprintf('Error while searching for the user\'s prefs: [%d]: %s', @ldap_errno($this->_connection), @ldap_error($this->_connection))); + } + + $result = @ldap_get_entries($this->_connection, $search); + if ($result === false) { + throw new Horde_Prefs_Exception(sprintf('Error while retrieving LDAP search results for the user\'s prefs: [%d]: %s', @ldap_errno($this->_connection), @ldap_error($this->_connection))); + } + + // Preferences are stored as colon-separated name:value pairs. + // Each pair is stored as its own attribute off of the multi- + // value attribute named in: $scope . 'Prefs' + + // ldap_get_entries() converts attribute indexes to lowercase. + $field = Horde_String::lower($scope . 'prefs'); + $prefs = isset($result[0][$field]) + ? $result[0][$field] + : array(); + + $ret = array(); + + foreach ($prefs as $prefstr) { + // If the string doesn't contain a colon delimiter, skip it. + if (strpos($prefstr, ':') !== false) { + // Split the string into its name:value components. + list($name, $val) = explode(':', $prefstr, 2); + $ret[$name] = base64_decode($val); + } + } + + return $ret; + } + + /** + */ + public function store($prefs) + { + $this->_connect(); + + // Build a hash of the preferences and their values that need + // to be stored on the LDAP server. Because we have to update + // all of the values of a multi-value entry wholesale, we + // can't just pick out the dirty preferences; we must update + // every scope that has dirty preferences. + $new_values = array(); + foreach ($prefs as $scope => $vals) { + foreach ($vals as $name => $pref) { + $new_values[$scope . 'Prefs'][] = $name . ':' . base64_encode($pref['v']); + } + } + + // Entries must have the objectclasses 'top' and 'hordeperson' + // to successfully store LDAP prefs. Check for both of them, + // and add them if necessary. + $search = @ldap_search($this->_connection, $this->_params['basedn'], + $this->_params['uid'] . '=' . $this->prefs['user'], + array('objectclass')); + if ($search === false) { + throw new Horde_Prefs_Exception(sprintf('Error searching the directory for required objectClasses: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection))); + } + + $result = @ldap_get_entries($this->_connection, $search); + if ($result === false) { + throw new Horde_Prefs_Exception(sprintf('Error retrieving results while checking for required objectClasses: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection))); + } + + if ($result['count'] > 0) { + $top = false; + $hordeperson = false; + + for ($i = 0; $i < $result[0]['objectclass']['count']; $i++) { + if ($result[0]['objectclass'][$i] == 'top') { + $top = true; + } elseif ($result[0]['objectclass'][$i] == 'hordePerson') { + $hordeperson = true; + } + } + + // Add any missing objectclasses. + if (!$top) { + @ldap_mod_add($this->_connection, $this->_dn, array('objectclass' => 'top')); + } + + if (!$hordeperson) { + @ldap_mod_add($this->_connection, $this->_dn, array('objectclass' => 'hordePerson')); + } + } + + // Send the hash to the LDAP server. + $result = @ldap_mod_replace($this->_connection, $this->_dn, + $new_values); + if ($result === false) { + throw new Horde_Prefs_Exception(sprintf('Unable to modify user\'s objectClass for preferences: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection))); + } + } + + /** + */ + public function remove($scope = null, $pref = null) + { + // TODO: Implement scope/pref-level removal + if (!is_null($scope) || !is_null($pref)) { + throw new Horde_Prefs_Exception('Removal not supported.'); + } + + $this->_connect(); + + // TODO: Move to horde/Core + $attrs = $GLOBALS['registry']->listApps(array('inactive', 'active', 'hidden', 'notoolbar', 'admin')); + foreach ($attrs as $key => $val) { + $attrs[$key] = $val . 'Prefs'; + } + + $search = @ldap_read($this->_connection, $this->_dn, + 'objectClass=hordePerson', $attrs, 1); + if ($search === false) { + throw new Horde_Prefs_Exception(sprintf('Error while getting preferenes from LDAP: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection))); + } + + $result = @ldap_get_entries($this->_connection, $search); + if ($result === false) { + throw new Horde_Prefs_Exception(sprintf('Error while retrieving results from LDAP: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection))); + } + + $attrs = array(); + for ($i = 0; $i < $result[0]['count']; $i++) { + $attrs[$result[0][$i]] = array(); + } + $result = @ldap_mod_del($this->_connection, $this->_dn, $attrs); + if ($result === false) { + throw new Horde_Prefs_Exception(sprintf('Unable to clear user\'s preferences: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection))); + } + } + + /* LDAP Helper functions. + * TODO: Use Horde_LDAP */ + + /** * Opens a connection to the LDAP server. * * @throws Horde_Prefs_Exception */ - function _connect() + protected function _connect() { if ($this->_connected) { return; @@ -94,10 +241,7 @@ class Horde_Prefs_Ldap extends Horde_Prefs /* Connect to the LDAP server anonymously. */ $conn = ldap_connect($this->_params['hostspec'], $this->_params['port']); if (!$conn) { - if ($this->_opts['logger']) { - $this->_opts['logger']->log(sprintf('Failed to open an LDAP connection to %s.', $this->_params['hostspec']), 'ERR'); - } - throw new Horde_Prefs_Exception('Internal LDAP error. Details have been logged for the administrator.'); + throw new Horde_Prefs_Exception(sprintf('Failed to open an LDAP connection to %s.', $this->_params['hostspec'])); } /* Set the LDAP protocol version. */ @@ -105,18 +249,13 @@ class Horde_Prefs_Ldap extends Horde_Prefs $result = @ldap_set_option($conn, LDAP_OPT_PROTOCOL_VERSION, $this->_params['version']); if ($result === false) { - if ($this->_opts['logger']) { - $this->_opts['logger']->log(sprintf('Set LDAP protocol version to %d failed: [%d] %s', $this->_params['version'], @ldap_errno($conn), @ldap_error($conn)), 'WARN'); - } - throw new Horde_Prefs_Exception('Internal LDAP error. Details have been logged for the administrator.'); + throw new Horde_Prefs_Exception(sprintf('Set LDAP protocol version to %d failed: [%d] %s', $this->_params['version'], @ldap_errno($conn), @ldap_error($conn))); } } /* Start TLS if we're using it. */ - if (!empty($this->_params['tls']) && - !@ldap_start_tls($conn) && - $this->_opts['logger']) { - $this->_opts['logger']->log(sprintf('STARTTLS failed: [%d] %s', @ldap_errno($this->_ds), @ldap_error($this->_ds)), 'ERR'); + if (!empty($this->_params['tls'])) { + @ldap_start_tls($conn); } /* If necessary, bind to the LDAP server as the user with search @@ -125,10 +264,7 @@ class Horde_Prefs_Ldap extends Horde_Prefs $bind = @ldap_bind($conn, $this->_params['searchdn'], $this->_params['searchpw']); if ($bind === false) { - if ($this->_opts['logger']) { - $this->_opts['logger']->log(sprintf('Bind to server %s:%d with DN %s failed: [%d] %s', $this->_params['hostspec'], $this->_params['port'], $this->_params['searchdn'], @ldap_errno($conn), @ldap_error($conn)), 'ERR'); - } - throw new Horde_Prefs_Exception('Internal LDAP error. Details have been logged for the administrator.', @ldap_errno($conn)); + throw new Horde_Prefs_Exception(sprintf('Bind to server %s:%d with DN %s failed: [%d] %s', $this->_params['hostspec'], $this->_params['port'], $this->_params['searchdn'], @ldap_errno($conn), @ldap_error($conn))); } } @@ -136,10 +272,7 @@ class Horde_Prefs_Ldap extends Horde_Prefs if (function_exists('ldap_set_rebind_proc')) { $result = @ldap_set_rebind_proc($conn, array($this, 'rebindProc')); if ($result === false) { - if ($this->_opts['logger']) { - $this->_opts['logger']->log(sprintf('Setting referral callback failed: [%d] %s', @ldap_errno($conn), @ldap_error($conn)), 'WARN'); - } - throw new Horde_Prefs_Exception($this->_dict->t("Internal LDAP error. Details have been logged for the administrator."), @ldap_errno($conn)); + throw new Horde_Prefs_Exception(sprintf('Setting referral callback failed: [%d] %s', @ldap_errno($conn), @ldap_error($conn))); } } @@ -148,27 +281,18 @@ class Horde_Prefs_Ldap extends Horde_Prefs /* Search for the user's full DN. */ $search = @ldap_search($this->_connection, $this->_params['basedn'], - $this->_params['uid'] . '=' . $this->getUser(), array('dn')); + $this->_params['uid'] . '=' . $this->params['user'], array('dn')); if ($search === false) { - if ($this->_opts['logger']) { - $this->_opts['logger']->log(sprintf('Error while searching the directory for the user\'s DN: [%d]: %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)), 'ERR'); - } - throw new Horde_Prefs_Exception('Internal LDAP error. Details have been logged for the administrator.', @ldap_errno($conn)); + throw new Horde_Prefs_Exception(sprintf('Error while searching the directory for the user\'s DN: [%d]: %s', @ldap_errno($this->_connection), @ldap_error($this->_connection))); } $result = @ldap_get_entries($this->_connection, $search); if ($result === false) { - if ($this->_opts['logger']) { - $this->_opts['logger']->log(sprintf('Error while retrieving LDAP search results for the user\'s DN: [%d]: %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)), 'ERR'); - } - throw new Horde_Prefs_Exception('Internal LDAP error. Details have been logged for the administrator.', @ldap_errno($this->_connection)); + throw new Horde_Prefs_Exception(sprintf('Error while retrieving LDAP search results for the user\'s DN: [%d]: %s', @ldap_errno($this->_connection), @ldap_error($this->_connection))); } if ($result['count'] != 1) { - if ($this->_opts['logger']) { - $this->_opts['logger']->log('Zero or more than one DN returned from search; unable to determine user\'s correct DN.', 'ERR'); - } - throw new Horde_Prefs_Exception('Internal LDAP error. Details have been logged for the administrator.'); + throw new Horde_Prefs_Exception('Zero or more than one DN returned from search; unable to determine user\'s correct DN.'); } $this->_dn = $result[0]['dn']; @@ -194,10 +318,7 @@ class Horde_Prefs_Ldap extends Horde_Prefs } if ($result === false) { - if ($this->_opts['logger']) { - $this->_opts['logger']->log(sprintf('Error rebinding for prefs writing: [%d]: %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)), 'ERR'); - } - throw new Horde_Prefs_Exception('Internal LDAP error. Details have been logged for the administrator.', @ldap_errno($this->_connection)); + throw new Horde_Prefs_Exception(sprintf('Error rebinding for prefs writing: [%d]: %s', @ldap_errno($this->_connection), @ldap_error($this->_connection))); } // We now have a ready-to-use connection. @@ -251,201 +372,5 @@ class Horde_Prefs_Ldap extends Horde_Prefs return 0; } - /** - * Retrieves the requested set of preferences from the user's LDAP entry. - * - * @param string $scope Scope specifier. - */ - function _retrieve($scope) - { - $this->_connect(); - - // Search for the multi-valued field containing the array of - // preferences. - $search = @ldap_search($this->_connection, $this->_params['basedn'], - $this->_params['uid'] . '=' . $this->getUser(), - array($scope . 'Prefs')); - if ($search === false) { - if ($this->_opts['logger']) { - $this->_opts['logger']->log(sprintf('Error while searching for the user\'s prefs: [%d]: %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)), 'ERR'); - } - return; - } - - $result = @ldap_get_entries($this->_connection, $search); - if ($result === false) { - if ($this->_opts['logger']) { - $this->_opts['logger']->log(sprintf('Error while retrieving LDAP search results for the user\'s prefs: [%d]: %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)), 'ERR'); - } - return; - } - - // Preferences are stored as colon-separated name:value pairs. - // Each pair is stored as its own attribute off of the multi- - // value attribute named in: $scope . 'Prefs' - - // ldap_get_entries() converts attribute indexes to lowercase. - $field = Horde_String::lower($scope . 'prefs'); - $prefs = isset($result[0][$field]) - ? $result[0][$field] - : array(); - - foreach ($prefs as $prefstr) { - // If the string doesn't contain a colon delimiter, skip it. - if (strpos($prefstr, ':') === false) { - continue; - } - - // Split the string into its name:value components. - list($name, $val) = explode(':', $prefstr, 2); - if (isset($this->_scopes[$scope][$name])) { - $this->_scopes[$scope][$name]['v'] = base64_decode($val); - $this->_scopes[$scope][$name]['m'] &= ~self::PREFS_DEFAULT; - } else { - // This is a shared preference. - $this->_scopes[$scope][$name] = array('v' => base64_decode($val), - 'm' => 0, - 'd' => null); - } - } - } - - /** - * Stores preferences to the LDAP server. - * - * @throws Horde_Prefs_Exception - */ - public function store() - { - // Get the list of preferences that have changed. If there are - // none, no need to hit the backend. - if (empty($this->_dirty)) { - return; - } - - $this->_connect(); - - // Build a hash of the preferences and their values that need - // to be stored on the LDAP server. Because we have to update - // all of the values of a multi-value entry wholesale, we - // can't just pick out the dirty preferences; we must update - // every scope that has dirty preferences. - $new_values = array(); - foreach (array_keys($this->_dirty) as $scope) { - foreach ($this->_scopes[$scope] as $name => $pref) { - // Don't store locked preferences. - if (!($pref['m'] & self::LOCKED)) { - $new_values[$scope . 'Prefs'][] = - $name . ':' . base64_encode($pref['v']); - } - } - } - - // Entries must have the objectclasses 'top' and 'hordeperson' - // to successfully store LDAP prefs. Check for both of them, - // and add them if necessary. - $search = @ldap_search($this->_connection, $this->_params['basedn'], - $this->_params['uid'] . '=' . $this->getUser(), - array('objectclass')); - if ($search === false) { - if ($this->_opts['logger']) { - $this->_opts['logger']->log(sprintf('Error searching the directory for required objectClasses: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)), 'ERR'); - } - return; - } - - $result = @ldap_get_entries($this->_connection, $search); - if ($result === false) { - if ($this->_opts['logger']) { - $this->_opts['logger']->log(sprintf('Error retrieving results while checking for required objectClasses: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)), 'ERR'); - } - return; - } - - if ($result['count'] > 0) { - $top = false; - $hordeperson = false; - - for ($i = 0; $i < $result[0]['objectclass']['count']; $i++) { - if ($result[0]['objectclass'][$i] == 'top') { - $top = true; - } elseif ($result[0]['objectclass'][$i] == 'hordePerson') { - $hordeperson = true; - } - } - - // Add any missing objectclasses. - if (!$top) { - @ldap_mod_add($this->_connection, $this->_dn, array('objectclass' => 'top')); - } - - if (!$hordeperson) { - @ldap_mod_add($this->_connection, $this->_dn, array('objectclass' => 'hordePerson')); - } - } - - // Send the hash to the LDAP server. - $result = @ldap_mod_replace($this->_connection, $this->_dn, - $new_values); - if ($result === false) { - if ($this->_opts['logger']) { - $this->_opts['logger']->log(sprintf('Unable to modify user\'s objectClass for preferences: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)), 'ERR'); - } - return; - } - - // Clean the preferences since they were just saved. - foreach ($this->_dirty as $scope => $prefs) { - foreach ($prefs as $name => $pref) { - $this->_scopes[$scope][$name]['m'] &= ~self::DIRTY; - } - - // Update the cache for this scope. - $this->_cache->update($scope, $prefs); - } - } - - /** - * Clears all preferences from the LDAP backend. - * - * @throws Horde_Prefs_Exception - */ - public function clear() - { - $this->_connect(); - - $attrs = $GLOBALS['registry']->listApps(array('inactive', 'active', 'hidden', 'notoolbar', 'admin')); - foreach ($attrs as $key => $val) { - $attrs[$key] = $val . 'Prefs'; - } - - $search = @ldap_read($this->_connection, $this->_dn, - 'objectClass=hordePerson', $attrs, 1); - if ($search === false) { - if ($this->_opts['logger']) { - $this->_opts['logger']->log(sprintf('Error while getting preferenes from LDAP: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)), 'ERR'); - } - return; - } - - $result = @ldap_get_entries($this->_connection, $search); - if ($result === false) { - if ($this->_opts['logger']) { - $this->_opts['logger']->log(sprintf('Error while retrieving results from LDAP: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)), 'ERR'); - } - return; - } - - $attrs = array(); - for ($i = 0; $i < $result[0]['count']; $i++) { - $attrs[$result[0][$i]] = array(); - } - $result = @ldap_mod_del($this->_connection, $this->_dn, $attrs); - if (($result === false) && $this->_opts['logger']) { - $this->_opts['logger']->log(sprintf('Unable to clear user\'s preferences: [%d] %s', @ldap_errno($this->_connection), @ldap_error($this->_connection)), 'ERR'); - } - - $this->cleanup(true); - } } diff --git a/framework/Prefs/lib/Horde/Prefs/Cache/Null.php b/framework/Prefs/lib/Horde/Prefs/Storage/Null.php similarity index 71% rename from framework/Prefs/lib/Horde/Prefs/Cache/Null.php rename to framework/Prefs/lib/Horde/Prefs/Storage/Null.php index 9737ba2a1..b082fded2 100644 --- a/framework/Prefs/lib/Horde/Prefs/Cache/Null.php +++ b/framework/Prefs/lib/Horde/Prefs/Storage/Null.php @@ -1,6 +1,6 @@ _key][$scope] = isset($_SESSION[$this->_key][$scope]) - ? array_merge($_SESSION[$this->_key][$scope], $prefs) - : array(); + foreach ($prefs as $scope => $vals) { + $_SESSION[$this->_key][$scope] = isset($_SESSION[$this->_key][$scope]) + ? array_merge($_SESSION[$this->_key][$scope], $vals) + : array(); + } } /** */ - public function clear($scope = null, $pref = null) + public function remove($scope = null, $pref = null) { if (is_null($scope)) { unset($_SESSION[$this->_key]); diff --git a/framework/Prefs/lib/Horde/Prefs/Sql.php b/framework/Prefs/lib/Horde/Prefs/Storage/Sql.php similarity index 60% rename from framework/Prefs/lib/Horde/Prefs/Sql.php rename to framework/Prefs/lib/Horde/Prefs/Storage/Sql.php index d853d9d2f..f3d0b0345 100644 --- a/framework/Prefs/lib/Horde/Prefs/Sql.php +++ b/framework/Prefs/lib/Horde/Prefs/Storage/Sql.php @@ -1,10 +1,6 @@ + * @author Michael Slusarz * @category Horde * @license http://www.fsf.org/copyleft/lgpl.html LGPL * @package Prefs */ -class Horde_Prefs_Sql extends Horde_Prefs +class Horde_Prefs_Storage_Sql extends Horde_Prefs_Storage { /** * Handle for the current database connection. @@ -28,10 +25,7 @@ class Horde_Prefs_Sql extends Horde_Prefs /** * Constructor. * - * @param string $scope The scope for this set of preferences. - * @param array $opts See factory() for list of options. - * @param array $params A hash containing any additional configuration - * or connection parameters a subclass might need. + * @param array $params Configuration parameters. *
      * 'db' - (Horde_Db_Adapter) [REQUIRED] The DB instance.
      * 'table' - (string) The name of the prefs table.
@@ -40,7 +34,7 @@ class Horde_Prefs_Sql extends Horde_Prefs
      *
      * @throws InvalidArgumentException
      */
-    protected function __construct($scope, $opts, $params)
+    public function __construct(array $params = array())
     {
         if (!isset($params['db'])) {
             throw new InvalidArgumentException('Missing db parameter.');
@@ -52,31 +46,26 @@ class Horde_Prefs_Sql extends Horde_Prefs
             'table' => 'horde_prefs'
         ), $params);
 
-        parent::__construct($scope, $opts, $params);
+        parent::__construct($params);
     }
 
     /**
-     * Retrieves the requested set of preferences from the user's database
-     * entry.
-     *
-     * @param string $scope  Scope specifier.
      */
-    protected function _retrieve($scope)
+    public function get($scope)
     {
         $query = 'SELECT pref_scope, pref_name, pref_value FROM ' .
             $this->_params['table'] . ' ' .
             'WHERE pref_uid = ? AND pref_scope = ?';
-        $values = array($this->getUser(), $scope);
+        $values = array($this->_params['user'], $scope);
 
         try {
             $result = $this->_db->selectAll($query, $values);
         } catch (Horde_Db_Exception $e) {
-            if ($this->_opts['logger']) {
-                $this->_opts['logger']->log('No preferences were retrieved.', 'DEBUG');
-            }
-            return;
+            throw Horde_Prefs_Exception($e);
         }
 
+        $ret = array();
+
         foreach ($result as $row) {
             $name = trim($row['pref_name']);
 
@@ -92,48 +81,30 @@ class Horde_Prefs_Sql extends Horde_Prefs
                 break;
             }
 
-            if (isset($this->_scopes[$scope][$name])) {
-                $this->_scopes[$scope][$name]['m'] &= ~self::PREFS_DEFAULT;
-                $this->_scopes[$scope][$name]['v'] = $row['pref_value'];
-            } else {
-                // This is a shared preference.
-                $this->_scopes[$scope][$name] = array(
-                    'd' => null,
-                    'm' => 0,
-                    'v' => $row['pref_value']
-                );
-            }
+            $ret[$name] = $row['pref_value'];
         }
+
+        return $ret;
     }
 
     /**
-     * Stores preferences to the SQL server.
-     *
-     * @throws Horde_Exception
      */
-    public function store()
+    public function store($prefs)
     {
         // For each preference, check for an existing table row and
         // update it if it's there, or create a new one if it's not.
-        foreach ($this->_dirty as $scope => $prefs) {
-            $updated = array();
-
-            foreach ($prefs as $name => $pref) {
-                // Don't store locked preferences.
-                if ($this->_scopes[$scope][$name]['m'] & self::LOCKED) {
-                    continue;
-                }
-
+        foreach ($prefs as $scope => $p) {
+            foreach ($p as $name => $pref) {
                 // Does a row already exist for this preference?
                 $query = 'SELECT 1 FROM ' . $this->_params['table'] .
                     ' WHERE pref_uid = ? AND pref_name = ?' .
                     ' AND pref_scope = ?';
-                $values = array($this->getUser(), $name, $scope);
+                $values = array($this->_params['user'], $name, $scope);
 
                 try {
                     $check = $this->_db->selectValue($query, $values);
                 } catch (Horde_Db_Exception $e) {
-                    return;
+                    throw Horde_Prefs_Exception($e);
                 }
 
                 $value = strval(isset($pref['v']) ? $pref['v'] : null);
@@ -151,7 +122,7 @@ class Horde_Prefs_Sql extends Horde_Prefs
                         '(pref_uid, pref_scope, pref_name, pref_value) VALUES' .
                         '(?, ?, ?, ?)';
                     $values = array(
-                        $this->getUser(),
+                        $this->_params['user'],
                         $scope,
                         $name,
                         $value
@@ -160,7 +131,7 @@ class Horde_Prefs_Sql extends Horde_Prefs
                     try {
                         $this->_db->insert($query, $values);
                     } catch (Horde_Db_Exception $e) {
-                        return;
+                        throw Horde_Prefs_Exception($e);
                     }
                 } else {
                     // Update the existing row.
@@ -171,7 +142,7 @@ class Horde_Prefs_Sql extends Horde_Prefs
                         ' AND pref_scope = ?';
                     $values = array(
                         $value,
-                        $this->getUser(),
+                        $this->_params['user'],
                         $name,
                         $scope
                     );
@@ -179,40 +150,36 @@ class Horde_Prefs_Sql extends Horde_Prefs
                     try {
                         $this->_db->update($query, $values);
                     } catch (Horde_Db_Exception $e) {
-                        return;
+                        throw Horde_Prefs_Exception($e);
                     }
                 }
-
-                // Clean the pref since it was just saved.
-                $this->_scopes[$scope][$name]['m'] &= ~self::DIRTY;
-
-                $updated[$name] = $this->_scopes[$scope][$name];
             }
-
-            // Update the cache for this scope.
-            $this->_cache->update($scope, $updated);
         }
     }
 
     /**
-     * Clears all preferences from the backend.
-     *
-     * @throws Horde_Exception
      */
-    public function clear()
+    public function remove($scope = null, $pref = null)
     {
-        // Build the SQL query.
         $query = 'DELETE FROM ' . $this->_params['table'] .
-            ' WHERE pref_uid = ?';
-        $values = array($this->getUser());
+                 ' WHERE pref_uid = ?';
+        $values = array($this->_params['user']);
+
+        if (!is_null($scope)) {
+            $query .= ' AND pref_scope = ?';
+            $values[] = $scope;
+
+            if (!is_null($pref)) {
+                $query .= ' AND pref_name = ?';
+                $values[] = $pref;
+            }
+        }
 
-        // Execute the query.
         try {
             $this->_db->delete($query, $values);
-        } catch (Horde_Db_Exception $e) {}
-
-        // Cleanup.
-        parent::clear();
+        } catch (Horde_Db_Exception $e) {
+            throw Horde_Prefs_Exception($e);
+        }
     }
 
 }
diff --git a/framework/Prefs/package.xml b/framework/Prefs/package.xml
index 093806518..95c4cf2e2 100644
--- a/framework/Prefs/package.xml
+++ b/framework/Prefs/package.xml
@@ -24,7 +24,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
   beta
  
  LGPL
- * Abstract caching code into Horde_Prefs_Cache.
+ * Abstract storage code into Horde_Prefs_Storage.
  * Add array access API to Horde_Prefs.
  * Remove dependency on horde/Core.
  * Use Horde_Db as backend for Sql driver.
@@ -36,32 +36,24 @@ http://pear.php.net/dtd/package-2.0.xsd">
    
     
      
-      
+      
+       
+       
+       
+       
+       
        
        
-       
-      
+       
+       
       
       
-      
       
-      
-      
-      
-      
-      
-      
+      
       
      
      
     
-   
-    
-     
-      
-      
-     
-    
   
@@ -97,19 +89,18 @@ http://pear.php.net/dtd/package-2.0.xsd"> - - - - - - - - - - + + + + + + + + + diff --git a/framework/Prefs/test/Horde/Prefs/bug_2838.phpt b/framework/Prefs/test/Horde/Prefs/bug_2838.phpt deleted file mode 100644 index a8899fa14..000000000 --- a/framework/Prefs/test/Horde/Prefs/bug_2838.phpt +++ /dev/null @@ -1,21 +0,0 @@ ---TEST-- -Test for Bug #2838, overwriting of preferences when multiple scopes are retrieved. ---FILE-- - 'none')); - -$prefs = Horde_Prefs::factory('session', 'horde', 'testuser', 'testpw'); -$prefs->retrieve('imp'); -$prefs->setValue('last_login', 'test'); -echo $prefs->getValue('last_login') . "\n"; - -$prefs->retrieve('ingo'); -echo $prefs->getValue('last_login') . "\n"; - -?> ---EXPECT-- -test -test -- 2.11.0