From: Gunnar Wrobel Date: Fri, 11 Sep 2009 09:50:16 +0000 (+0200) Subject: Started converting the Kolab Free/Busy application into a Horde MVC X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=47b9ccfffb10e89122fc0538fc0a77b6146ce7b8;p=horde.git Started converting the Kolab Free/Busy application into a Horde MVC based webapp. Also began to remove the use of singletons in order to facilitate unit testing the system. Horde_Kolab_FreeBusy now serves as Registry/ServiceLocator to the various services required. --- diff --git a/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy.php b/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy.php index 32112746e..d9f75a5cb 100644 --- a/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy.php +++ b/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy.php @@ -1,340 +1,320 @@ + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_FreeBusy */ -/** PEAR for raising errors */ -require_once 'PEAR.php'; - -/** View classes for the result */ -require_once 'Horde/Kolab/FreeBusy/View.php'; - -/** A class that handles access restrictions */ -require_once 'Horde/Kolab/FreeBusy/Access.php'; - /** - * How to use this class - * - * require_once 'config.php'; - * - * $fb = new Kolab_Freebusy(); - * - * $fb->trigger(); - * - * OR - * - * $fb->fetch(); + * The Horde_Kolab_FreeBusy class serves as Registry aka ServiceLocator for the + * Free/Busy application. It also provides the entry point into the the Horde + * MVC system and allows to dispatch a request. * - * $Horde: framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy.php,v 1.14 2009/07/14 00:28:33 mrubinsk Exp $ + * Copyright 2009 The Horde Project (http://www.horde.org/) * - * Copyright 2004-2008 Klarälvdalens Datakonsult AB + * See the enclosed file COPYING for license information (LGPL). If you did not + * receive this file, see + * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. * - * See the enclosed file COPYING for license information (LGPL). If you - * did not receive this file, see http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. - * - * @since Horde 3.2 - * @author Steffen Hansen - * @author Gunnar Wrobel - * @author Thomas Arendsen Hein - * @package Kolab_FreeBusy + * @category Kolab + * @package Kolab_FreeBusy + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_FreeBusy + * @since Horde 3.2 */ -class Horde_Kolab_FreeBusy { - +class Horde_Kolab_FreeBusy +{ /** - * Parameters provided to this class. + * Singleton value. * - * @var array + * @var Horde_Kolab_FreeBusy */ - var $_params; + static protected $instance; /** - * Link to the cache. + * The object representing the request. * - * @var Horde_Kolab_FreeBusy_Cache + * @var Horde_Controller_Request_Base */ - var $_cache; + private $_request; /** - * Setup the cache. + * The object representing the request<->controller mapping. + * + * @var Horde_Routes_Mapper */ - function _initCache() - { - global $conf; + private $_mapper; - /* Load the cache class now */ - require_once 'Horde/Kolab/FreeBusy/Cache.php'; - - /* Where is the cache data stored? */ - if (!empty($conf['fb']['cache_dir'])) { - $cache_dir = $conf['fb']['cache_dir']; - } else { - if (class_exists('Horde')) { - $cache_dir = Horde::getTempDir(); - } else { - $cache_dir = '/tmp'; - } - } + /** + * The request dispatcher. + * + * @var Horde_Controller_Dispatcher + */ + private $_dispatcher; - $this->_cache = new Horde_Kolab_FreeBusy_Cache($cache_dir); + /** + * Constructor. + * + * @param array $params The parameters required to initialize the + * application. + */ + public function __construct($params = array()) + { + $this->_params = $params; } /** - * Trigger regeneration of free/busy data in a calender. + * Returns a reference to the global Horde_Kolab_FreeBusy object, + * only creating it if it doesn't already exist. + * + * This method must be invoked as: + * $registry = Horde_Kolab_FreeBusy::singleton() + * + * We expect this method to be invoked *once* on application startup. At + * that point the parameters need to be set to correctly initialize the + * system. + * + * The singleton should then later be only used by the Controllers to access + * different system components (by using the MVC system we loose connection + * to this class within the controllers so we need global state here). + * + * @param array $params The parameters required to initialize the + * application. + *
+     * 'script'  - (string) Script name in relation to the document root.
+     *                      [optional]
+     *
+     * 'config'  - (array)  Indicates where to find configuration options.
+     *                      [optional]
+     *
+     *     'dir'      - (string) Configuration files can be found in this
+     *                           directory.
+     *
+     * 'request' - (array)  Options for the request object. [optional]
+     *
+     *     'class'    - (string) The class of request object to use (should
+     *                           obviously match the request type).
+     *     'params'   - (array)  Additional parameters to use on request
+     *                           object construction.
+     *
+     * 'mapper'  - (array)  Options for the mapper object. [optional]
+     *
+     *     'params'   - (array)  Additional parameters to use on mapper
+     *                           object construction.
+     *
+     * 'dispatch'- (array)  Options for the dispatcher object. [optional]
+     *
+     *     'controllerDir' - (string) The directory holding controllers.
+     *     'viewsDir'      - (string) The directory holding views.
+     *
+     * 
+ * + * @return Horde_Kolab_FreeBusy The Horde_Registry instance. */ - function &trigger() + static public function singleton($params = array()) { - global $conf; - - /* Get the folder name */ - $req_folder = Horde_Util::getFormData('folder', ''); - - Horde::logMessage(sprintf("Starting generation of partial free/busy data for folder %s", - $req_folder), __FILE__, __LINE__, PEAR_LOG_DEBUG); - - /* Validate folder access */ - $access = new Horde_Kolab_FreeBusy_Access(); - $result = $access->parseFolder($req_folder); - if (is_a($result, 'PEAR_Error')) { - $error = array('type' => FREEBUSY_ERROR_NOTFOUND, - 'error' => $result); - $view = new Horde_Kolab_FreeBusy_View_error($error); - return $view; + if (!isset(self::$instance)) { + self::$instance = new Horde_Kolab_FreeBusy($params); } - Horde::logMessage(sprintf("Partial free/busy data of owner %s on server %s requested by user %s.", - $access->owner, $access->freebusyserver, $access->user), - __FILE__, __LINE__, PEAR_LOG_DEBUG); - - /* Get the cache request variables */ - $req_cache = Horde_Util::getFormData('cache', false); - $req_extended = Horde_Util::getFormData('extended', false); - - /* Try to fetch the data if it is stored on a remote server */ - $result = $access->fetchRemote(true, $req_extended); - if (is_a($result, 'PEAR_Error')) { - $error = array('type' => FREEBUSY_ERROR_UNAUTHORIZED, - 'error' => $result); - $view = new Horde_Kolab_FreeBusy_View_error($error); - return $view; - } + return self::$instance; + } - $this->_initCache(); + /** + * Destroy the application context. + * + * @return NULL + */ + static public function destroy() + { + self::$instance = null; + } - if (!$req_cache) { - /* User wants to regenerate the cache */ + /** + * Inject the request object into the application context. + * + * @param Horde_Controller_Request_Base $request The object that should + * represent the current + * request. + * + * @return NULL + */ + public function setRequest(Horde_Controller_Request_Base $request) + { + $this->_request = $request; + } - /* Here we really need an authenticated IMAP user */ - $result = $access->authenticated(); - if (is_a($result, 'PEAR_Error')) { - $error = array('type' => FREEBUSY_ERROR_UNAUTHORIZED, - 'error' => $result); - $view = new Horde_Kolab_FreeBusy_View_error($error); - return $view; + /** + * Return the object representing the current request. + * + * @return Horde_Controller_Request_Base The current request. + * + * @throws Horde_Exception + */ + public function getRequest() + { + if (!isset($this->_request)) { + if (!empty($this->_params['request']['class'])) { + $request_class = $this->_params['request']['class']; + } else { + $request_class = 'Horde_Controller_Request_Http'; } - - if (empty($access->owner)) { - $message = sprintf(_("No such account %s!"), - htmlentities($access->req_owner)); - $error = array('type' => FREEBUSY_ERROR_NOTFOUND, - 'error' => PEAR::raiseError($message)); - $view = new Horde_Kolab_FreeBusy_View_error($error); - return $view; + if (!empty($this->_params['request']['params'])) { + $params = $this->_params['request']['params']; + } else { + $params = array(); } - - /* Update the cache */ - $result = $this->_cache->store($access); - if (is_a($result, 'PEAR_Error')) { - $error = array('type' => FREEBUSY_ERROR_NOTFOUND, - 'error' => $result); - $view = new Horde_Kolab_FreeBusy_View_error($error); - return $view; + // Set up our request and routing objects + $this->_request = new $request_class($params); + /** + * The HTTP request object would hide errors. Display them. + */ + if (isset($this->request->_exception)) { + throw $this->request->_exception; } } - /* Load the cache data */ - $vfb = $this->_cache->loadPartial($access, $req_extended); - if (is_a($vfb, 'PEAR_Error')) { - $error = array('type' => FREEBUSY_ERROR_NOTFOUND, - 'error' => $vfb); - $view = new Horde_Kolab_FreeBusy_View_error($error); - return $view; - } - - Horde::logMessage("Delivering partial free/busy data.", __FILE__, __LINE__, PEAR_LOG_DEBUG); - - /* Generate the renderer */ - $data = array('fb' => $vfb, 'name' => $access->owner . '.ifb'); - $view = new Horde_Kolab_FreeBusy_View_vfb($data); - - /* Finish up */ - Horde::logMessage("Free/busy generation complete.", __FILE__, __LINE__, PEAR_LOG_DEBUG); - - return $view; + return $this->_request; } /** - * Fetch the free/busy data for a user. + * Inject the mapper object into the application context. + * + * @param Horde_Route_Mapper $mapper The object that handles mapping. + * + * @return NULL */ - function &fetch() + public function setMapper(Horde_Route_Mapper $mapper) { - global $conf; - - /* Get the user requsted */ - $req_owner = Horde_Util::getFormData('uid'); - - Horde::logMessage(sprintf("Starting generation of free/busy data for user %s", - $req_owner), __FILE__, __LINE__, PEAR_LOG_DEBUG); - - /* Validate folder access */ - $access = new Horde_Kolab_FreeBusy_Access(); - $result = $access->parseOwner($req_owner); - if (is_a($result, 'PEAR_Error')) { - $error = array('type' => FREEBUSY_ERROR_NOTFOUND, 'error' => $result); - $view = new Horde_Kolab_FreeBusy_View_error($error); - return $view; - } - - Horde::logMessage(sprintf("Free/busy data of owner %s on server %s requested by user %s.", - $access->owner, $access->freebusyserver, $access->user), - __FILE__, __LINE__, PEAR_LOG_DEBUG); - - $req_extended = Horde_Util::getFormData('extended', false); - - /* Try to fetch the data if it is stored on a remote server */ - $result = $access->fetchRemote(false, $req_extended); - if (is_a($result, 'PEAR_Error')) { - $error = array('type' => FREEBUSY_ERROR_UNAUTHORIZED, 'error' => $result); - $view = new Horde_Kolab_FreeBusy_View_error($error); - return $view; - } - - $this->_initCache(); - - $result = $this->_cache->load($access, $req_extended); - if (is_a($result, 'PEAR_Error')) { - $error = array('type' => FREEBUSY_ERROR_NOTFOUND, 'error' => $result); - $view = new Horde_Kolab_FreeBusy_View_error($error); - return $view; - } - - Horde::logMessage("Delivering complete free/busy data.", __FILE__, __LINE__, PEAR_LOG_DEBUG); - - /* Generate the renderer */ - $data = array('fb' => $result, 'name' => $access->owner . '.vfb'); - $view = new Horde_Kolab_FreeBusy_View_vfb($data); - - /* Finish up */ - Horde::logMessage("Free/busy generation complete.", __FILE__, __LINE__, PEAR_LOG_DEBUG); - - return $view; + $this->_mapper = $mapper; } /** - * Regenerate the free/busy cache. + * Return the mapper. + * + * @return Horde_Route_Mapper The mapper. + * + * @throws Horde_Exception */ - function ®enerate($reporter) + public function getMapper() { - $access = new Horde_Kolab_FreeBusy_Access(); - $result = $access->authenticated(); - if (is_a($result, 'PEAR_Error')) { - return $result->getMessage(); - } - - /* Load the required Kolab libraries */ - require_once "Horde/Kolab/Storage/List.php"; - - $list = &Kolab_List::singleton(); - $calendars = $list->getByType('event'); - if (is_a($calendars, 'PEAR_Error')) { - return $calendars->getMessage(); - } - - $this->_initCache(); - - $lines = array(); + if (!isset($this->_mapper)) { + if (!empty($this->_params['mapper']['params'])) { + $params = $this->_params['mapper']['params']; + } else { + $params = array(); + } + $this->_mapper = new Horde_Routes_Mapper($params); - foreach ($calendars as $calendar) { /** - * We are using imap folders for our calendar list but - * the library expects us to follow the trigger format - * used by pfb.php + * Application routes are relative only to the application. Let the + * mapper know where they start. */ - $req_domain = explode('@', $calendar->name); - if (isset($req_domain[1])) { - $domain = $req_domain[1]; + if (!empty($this->_params['script'])) { + $this->_mapper->prefix = dirname($this->_params['script']); } else { - $domain = null; + $this->_mapper->prefix = dirname($_SERVER['PHP_SELF']); + } + + // Check for route definitions. + if (!empty($this->_params['config']['dir'])) { + $routeFile = $this->_params['config']['dir'] . '/routes.php'; } - $req_folder = explode('/', $req_domain[0]); - if ($req_folder[0] == 'user') { - unset($req_folder[0]); - $owner = $req_folder[1]; - unset($req_folder[1]); - } else if ($req_folder[0] == 'INBOX') { - $owner = $access->user; - unset($req_folder[0]); + if (empty($this->_params['config']['dir']) + || !file_exists($routeFile)) { + $this->_mapper->connect('*(id).:(type)', + array('controller' => 'freebusy', + 'action' => 'fetch', + 'requirements' => array('type' => '(i|x|v|p|px)fb') + )); + + $this->_mapper->connect('trigger/:id/:folder', + array('controller' => 'freebusy', + 'action' => 'trigger')); + + $this->_mapper->connect('delete/:id', + array('controller' => 'freebusy', + 'action' => 'delete')); + + $this->_mapper->connect('regenerate/:id', + array('controller' => 'freebusy', + 'action' => 'delete')); + } else { + // Load application routes. + include $routeFile; } + } - $trigger = $owner . ($domain ? '@' . $domain : '') . '/' . join('/', $req_folder); - $trigger = Horde_String::convertCharset($trigger, 'UTF7-IMAP', 'UTF-8'); + return $this->_mapper; + } - /* Validate folder access */ - $result = $access->parseFolder($trigger); - if (is_a($result, 'PEAR_Error')) { - $reporter->failure($calendar->name, $result->getMessage()); - continue; - } + /** + * Inject the dispatcher object into the application context. + * + * @param Horde_Controller_Dispatcher $dispatcher The object that handles + * dispatching. + * + * @return NULL + */ + public function setDispatcher(Horde_Controller_Dispatcher $dispatcher) + { + $this->_dispatcher = $dispatcher; + } - /* Hack for allowing manager access */ - if ($access->user == 'manager') { - $imapc = &Horde_Kolab_IMAP::singleton($GLOBALS['conf']['kolab']['imap']['server'], - $GLOBALS['conf']['kolab']['imap']['port']); - $result = $imapc->connect($access->user, Horde_Auth::getCredential('password')); - if (is_a($result, 'PEAR_Error')) { - $reporter->failure($calendar->name, $result->getMessage()); - continue; - } - $acl = $imapc->getACL($calendar->name); - if (is_a($acl, 'PEAR_Error')) { - $reporter->failure($calendar->name, $result->getMessage()); - continue; - } - $oldacl = ''; - if (isset($acl['manager'])) { - $oldacl = $acl['manager']; - } - $result = $imapc->setACL($calendar->name, 'manager', 'lrs'); - if (is_a($result, 'PEAR_Error')) { - $reporter->failure($calendar->name, $result->getMessage()); - continue; - } + /** + * Return the dispatcher. + * + * @return Horde_Controller_Dispatcher The dispatcher. + * + * @throws Horde_Exception + */ + public function getDispatcher() + { + if (!isset($this->_dispatcher)) { + if (empty($this->_params['dispatch']['controllerDir'])) { + $controllerDir = dirname(__FILE__) . '/FreeBusy/Controller'; + } else { + $controllerDir = $this->_params['dispatch']['controllerDir']; } - /* Update the cache */ - $result = $this->_cache->store($access); - if (is_a($result, 'PEAR_Error')) { - $reporter->failure($calendar->name, $result->getMessage()); - continue; + if (empty($this->_params['dispatch']['viewsDir'])) { + $viewsDir = dirname(__FILE__) . '/FreeBusy/View'; + } else { + $viewsDir = $this->_params['dispatch']['viewsDir']; } - /* Revert the acl */ - if ($access->user == 'manager' && $oldacl) { - $result = $imapc->setACL($calendar->name, 'manager', $oldacl); - if (is_a($result, 'PEAR_Error')) { - $reporter->failure($calendar->name, $result->getMessage()); - continue; - } - } + $context = array( + 'mapper' => $this->getMapper(), + 'controllerDir' => $controllerDir, + 'viewsDir' => $viewsDir, + // 'logger' => '', + ); - $reporter->success($calendar->name); + $this->_dispatcher = Horde_Controller_Dispatcher::singleton($context); + } + return $this->_dispatcher; + } + + /** + * Handle the current request. + * + * @return NULL + */ + public function dispatch() + { + try { + $this->getDispatcher()->dispatch($this->getRequest()); + } catch (Exception $e) { + //@todo: Error view + throw $e; } - return $lines; } } - - diff --git a/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Controller/FreebusyController.php b/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Controller/FreebusyController.php new file mode 100644 index 000000000..6902ea1de --- /dev/null +++ b/framework/Kolab_FreeBusy/lib/Horde/Kolab/FreeBusy/Controller/FreebusyController.php @@ -0,0 +1,347 @@ + + * @author Steffen Hansen + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_FreeBusy + */ + +/** + * The Autoloader allows us to omit "require/include" statements. + */ +require_once 'Horde/Autoloader.php'; + +/** + * The core Controller handling the different request types. + * + * Copyright 2004-2009 Klarälvdalens Datakonsult AB + * + * See the enclosed file COPYING for license information (LGPL). If you did not + * receive this file, see + * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * @category Kolab + * @package Kolab_FreeBusy + * @author Thomas Arendsen Hein + * @author Steffen Hansen + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_FreeBusy + * @since Horde 3.2 + */ +class FreeBusyController extends Horde_Controller_Base +{ + /** + * Parameters provided to this class. + * + * @var array + */ + var $_params; + + /** + * Link to the cache. + * + * @var Horde_Kolab_FreeBusy_Cache + */ + var $_cache; + + /** + * Setup the cache. + */ + function _initCache() + { + global $conf; + + /* Load the cache class now */ + require_once 'Horde/Kolab/FreeBusy/Cache.php'; + + /* Where is the cache data stored? */ + if (!empty($conf['fb']['cache_dir'])) { + $cache_dir = $conf['fb']['cache_dir']; + } else { + if (class_exists('Horde')) { + $cache_dir = Horde::getTempDir(); + } else { + $cache_dir = '/tmp'; + } + } + + $this->_cache = new Horde_Kolab_FreeBusy_Cache($cache_dir); + } + + /** + * Trigger regeneration of free/busy data in a calender. + * + * @return NULL + */ + function &trigger() + { + global $conf; + + /* Get the folder name */ + $req_folder = Horde_Util::getFormData('folder', ''); + + Horde::logMessage(sprintf("Starting generation of partial free/busy data for folder %s", + $req_folder), __FILE__, __LINE__, PEAR_LOG_DEBUG); + + /* Validate folder access */ + $access = new Horde_Kolab_FreeBusy_Access(); + $result = $access->parseFolder($req_folder); + if (is_a($result, 'PEAR_Error')) { + $error = array('type' => FREEBUSY_ERROR_NOTFOUND, + 'error' => $result); + $view = new Horde_Kolab_FreeBusy_View_error($error); + return $view; + } + + Horde::logMessage(sprintf("Partial free/busy data of owner %s on server %s requested by user %s.", + $access->owner, $access->freebusyserver, $access->user), + __FILE__, __LINE__, PEAR_LOG_DEBUG); + + /* Get the cache request variables */ + $req_cache = Horde_Util::getFormData('cache', false); + $req_extended = Horde_Util::getFormData('extended', false); + + /* Try to fetch the data if it is stored on a remote server */ + $result = $access->fetchRemote(true, $req_extended); + if (is_a($result, 'PEAR_Error')) { + $error = array('type' => FREEBUSY_ERROR_UNAUTHORIZED, + 'error' => $result); + $view = new Horde_Kolab_FreeBusy_View_error($error); + return $view; + } + + $this->_initCache(); + + if (!$req_cache) { + /* User wants to regenerate the cache */ + + /* Here we really need an authenticated IMAP user */ + $result = $access->authenticated(); + if (is_a($result, 'PEAR_Error')) { + $error = array('type' => FREEBUSY_ERROR_UNAUTHORIZED, + 'error' => $result); + $view = new Horde_Kolab_FreeBusy_View_error($error); + return $view; + } + + if (empty($access->owner)) { + $message = sprintf(_("No such account %s!"), + htmlentities($access->req_owner)); + $error = array('type' => FREEBUSY_ERROR_NOTFOUND, + 'error' => PEAR::raiseError($message)); + $view = new Horde_Kolab_FreeBusy_View_error($error); + return $view; + } + + /* Update the cache */ + $result = $this->_cache->store($access); + if (is_a($result, 'PEAR_Error')) { + $error = array('type' => FREEBUSY_ERROR_NOTFOUND, + 'error' => $result); + $view = new Horde_Kolab_FreeBusy_View_error($error); + return $view; + } + } + + /* Load the cache data */ + $vfb = $this->_cache->loadPartial($access, $req_extended); + if (is_a($vfb, 'PEAR_Error')) { + $error = array('type' => FREEBUSY_ERROR_NOTFOUND, + 'error' => $vfb); + $view = new Horde_Kolab_FreeBusy_View_error($error); + return $view; + } + + Horde::logMessage("Delivering partial free/busy data.", __FILE__, __LINE__, PEAR_LOG_DEBUG); + + /* Generate the renderer */ + $data = array('fb' => $vfb, 'name' => $access->owner . '.ifb'); + $view = new Horde_Kolab_FreeBusy_View_vfb($data); + + /* Finish up */ + Horde::logMessage("Free/busy generation complete.", __FILE__, __LINE__, PEAR_LOG_DEBUG); + + return $view; + } + + /** + * Fetch the free/busy data for a user. + * + * @return NULL + */ + function &fetch() + { + global $conf; + + /* Get the user requsted */ + $req_owner = Horde_Util::getFormData('uid'); + + Horde::logMessage(sprintf("Starting generation of free/busy data for user %s", + $req_owner), __FILE__, __LINE__, PEAR_LOG_DEBUG); + + /* Validate folder access */ + $access = new Horde_Kolab_FreeBusy_Access(); + $result = $access->parseOwner($req_owner); + if (is_a($result, 'PEAR_Error')) { + $error = array('type' => FREEBUSY_ERROR_NOTFOUND, 'error' => $result); + $view = new Horde_Kolab_FreeBusy_View_error($error); + return $view; + } + + Horde::logMessage(sprintf("Free/busy data of owner %s on server %s requested by user %s.", + $access->owner, $access->freebusyserver, $access->user), + __FILE__, __LINE__, PEAR_LOG_DEBUG); + + $req_extended = Horde_Util::getFormData('extended', false); + + /* Try to fetch the data if it is stored on a remote server */ + $result = $access->fetchRemote(false, $req_extended); + if (is_a($result, 'PEAR_Error')) { + $error = array('type' => FREEBUSY_ERROR_UNAUTHORIZED, 'error' => $result); + $view = new Horde_Kolab_FreeBusy_View_error($error); + return $view; + } + + $this->_initCache(); + + $result = $this->_cache->load($access, $req_extended); + if (is_a($result, 'PEAR_Error')) { + $error = array('type' => FREEBUSY_ERROR_NOTFOUND, 'error' => $result); + $view = new Horde_Kolab_FreeBusy_View_error($error); + return $view; + } + + Horde::logMessage("Delivering complete free/busy data.", __FILE__, __LINE__, PEAR_LOG_DEBUG); + + /* Generate the renderer */ + $data = array('fb' => $result, 'name' => $access->owner . '.vfb'); + $view = new Horde_Kolab_FreeBusy_View_vfb($data); + + /* Finish up */ + Horde::logMessage("Free/busy generation complete.", __FILE__, __LINE__, PEAR_LOG_DEBUG); + + return $view; + } + + /** + * Regenerate the free/busy cache data. + * + * @return NULL + */ + function ®enerate($reporter) + { + $access = new Horde_Kolab_FreeBusy_Access(); + $result = $access->authenticated(); + if (is_a($result, 'PEAR_Error')) { + return $result->getMessage(); + } + + /* Load the required Kolab libraries */ + require_once "Horde/Kolab/Storage/List.php"; + + $list = &Kolab_List::singleton(); + $calendars = $list->getByType('event'); + if (is_a($calendars, 'PEAR_Error')) { + return $calendars->getMessage(); + } + + $this->_initCache(); + + $lines = array(); + + foreach ($calendars as $calendar) { + /** + * We are using imap folders for our calendar list but + * the library expects us to follow the trigger format + * used by pfb.php + */ + $req_domain = explode('@', $calendar->name); + if (isset($req_domain[1])) { + $domain = $req_domain[1]; + } else { + $domain = null; + } + $req_folder = explode('/', $req_domain[0]); + if ($req_folder[0] == 'user') { + unset($req_folder[0]); + $owner = $req_folder[1]; + unset($req_folder[1]); + } else if ($req_folder[0] == 'INBOX') { + $owner = $access->user; + unset($req_folder[0]); + } + + $trigger = $owner . ($domain ? '@' . $domain : '') . '/' . join('/', $req_folder); + $trigger = Horde_String::convertCharset($trigger, 'UTF7-IMAP', 'UTF-8'); + + /* Validate folder access */ + $result = $access->parseFolder($trigger); + if (is_a($result, 'PEAR_Error')) { + $reporter->failure($calendar->name, $result->getMessage()); + continue; + } + + /* Hack for allowing manager access */ + if ($access->user == 'manager') { + $imapc = &Horde_Kolab_IMAP::singleton($GLOBALS['conf']['kolab']['imap']['server'], + $GLOBALS['conf']['kolab']['imap']['port']); + $result = $imapc->connect($access->user, Horde_Auth::getCredential('password')); + if (is_a($result, 'PEAR_Error')) { + $reporter->failure($calendar->name, $result->getMessage()); + continue; + } + $acl = $imapc->getACL($calendar->name); + if (is_a($acl, 'PEAR_Error')) { + $reporter->failure($calendar->name, $result->getMessage()); + continue; + } + $oldacl = ''; + if (isset($acl['manager'])) { + $oldacl = $acl['manager']; + } + $result = $imapc->setACL($calendar->name, 'manager', 'lrs'); + if (is_a($result, 'PEAR_Error')) { + $reporter->failure($calendar->name, $result->getMessage()); + continue; + } + } + + /* Update the cache */ + $result = $this->_cache->store($access); + if (is_a($result, 'PEAR_Error')) { + $reporter->failure($calendar->name, $result->getMessage()); + continue; + } + + /* Revert the acl */ + if ($access->user == 'manager' && $oldacl) { + $result = $imapc->setACL($calendar->name, 'manager', $oldacl); + if (is_a($result, 'PEAR_Error')) { + $reporter->failure($calendar->name, $result->getMessage()); + continue; + } + } + + $reporter->success($calendar->name); + + } + return $lines; + } + + /** + * Delete data for a specific user. + * + * @return NULL + */ + public function delete() + { + } +} \ No newline at end of file diff --git a/framework/Kolab_FreeBusy/package.xml b/framework/Kolab_FreeBusy/package.xml index 3fc93e916..212f160ee 100644 --- a/framework/Kolab_FreeBusy/package.xml +++ b/framework/Kolab_FreeBusy/package.xml @@ -64,6 +64,9 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + + @@ -79,8 +82,14 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + + + + + @@ -136,12 +145,15 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + + diff --git a/framework/Kolab_FreeBusy/test/Horde/Kolab/FreeBusy/DispatchTest.php b/framework/Kolab_FreeBusy/test/Horde/Kolab/FreeBusy/DispatchTest.php new file mode 100644 index 000000000..281874851 --- /dev/null +++ b/framework/Kolab_FreeBusy/test/Horde/Kolab/FreeBusy/DispatchTest.php @@ -0,0 +1,137 @@ + + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_FreeBusy + */ + +/** + * The Autoloader allows us to omit "require/include" statements. + */ +require_once 'Horde/Autoloader.php'; + +/** + * Test dispatching calls in the free/busy system. + * + * Copyright 2009 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. + * + * @category Kolab + * @package Kolab_FreeBusy + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_FreeBusy + */ +class Horde_Kolab_FreeBusy_DispatchTest extends Horde_Kolab_Test_FreeBusy +{ + + /** + * Test setup. + */ + public function setUp() + { + /** + * The controller automatically starts a session. But we don't want to + * send out cookie headers since we are running in PHPUnit. + */ + ini_set('session.use_cookies', 0); + ini_set('session.use_only_cookies', 0); + session_cache_limiter(null); + } + + /** + * Test destruction. + */ + public function tearDown() + { + Horde_Kolab_FreeBusy::destroy(); + } + + /** + * Test dispatching a "fetch" call. + * + * @return NULL + */ + public function testFetch() + { + $urls = array( + '/freebusy/test@example.com.ifb' => array( + 'ifb', 'test@example.com'), + '/freebusy/test@example.com.vfb' => array( + 'vfb', 'test@example.com'), + '/freebusy/test@example.com.xfb' => array( + 'xfb', 'test@example.com'), + '/freebusy/test@example.com.pfb' => array( + 'pfb', 'test@example.com'), + '/freebusy/test@example.com.pxfb' => array( + 'pxfb', 'test@example.com'), + '/freebusy/test@example.com.zfb' => false, + ); + + foreach ($urls as $key => $result) { + $params = array( + 'script' => '/freebusy/freebusy.php', + 'request' => array( + 'params' => array( + 'server' => array( + 'REQUEST_URI' => $key + ) + ) + ), + 'dispatch' => array( + 'controllerDir' => dirname(__FILE__) . '/Mock/Controller', + ) + ); + $application = Horde_Kolab_FreeBusy::singleton($params); + if (empty($result)) { + try { + $application->dispatch(); + } catch (Horde_Controller_Exception $e) { + $this->assertEquals('No routes match the path: "' . substr($key, 1) . '"', $e->getMessage()); + } + } else { + ob_start(); + $application->dispatch(); + $output = ob_get_contents(); + ob_end_clean(); + $this->assertEquals('fetched "' . $result[0] . '" data for user "' . $result[1] . '"', $output); + } + Horde_Kolab_FreeBusy::destroy(); + } + } + + /** + * Test dispatching a "trigger" call. + * + * @return NULL + */ + public function testTrigger() + { + } + + /** + * Test dispatching a "regenerate" call. + * + * @return NULL + */ + public function testRegenerate() + { + } + + /** + * Test dispatching a "delete" call. + * + * @return NULL + */ + public function testDelete() + { + } +} \ No newline at end of file diff --git a/framework/Kolab_FreeBusy/test/Horde/Kolab/FreeBusy/Mock/Controller/FreebusyController.php b/framework/Kolab_FreeBusy/test/Horde/Kolab/FreeBusy/Mock/Controller/FreebusyController.php new file mode 100644 index 000000000..7cb1bb626 --- /dev/null +++ b/framework/Kolab_FreeBusy/test/Horde/Kolab/FreeBusy/Mock/Controller/FreebusyController.php @@ -0,0 +1,76 @@ + + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_FreeBusy + */ + +/** + * The Autoloader allows us to omit "require/include" statements. + */ +require_once 'Horde/Autoloader.php'; + +/** + * A mockup for the main application controller. + * + * Copyright 2004-2009 Klarälvdalens Datakonsult AB + * + * See the enclosed file COPYING for license information (LGPL). If you did not + * receive this file, see + * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * @category Kolab + * @package Kolab_FreeBusy + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_FreeBusy + * @since Horde 3.2 + */ +class FreeBusyController extends Horde_Controller_Base +{ + /** + * Trigger regeneration of free/busy data in a calender. + * + * @return NULL + */ + public function trigger() + { + $this->renderText('triggered'); + } + + /** + * Fetch the free/busy data for a user. + * + * @return NULL + */ + public function fetch() + { + $this->renderText('fetched "' . $this->params->type . '" data for user "' . $this->params->id . '"'); + } + + /** + * Regenerate the free/busy cache data. + * + * @return NULL + */ + public function regenerate() + { + $this->renderText('regenerated'); + } + + /** + * Delete data for a specific user. + * + * @return NULL + */ + public function delete() + { + $this->renderText('deleted'); + } +} \ No newline at end of file diff --git a/framework/Kolab_FreeBusy/www/Horde/Kolab/FreeBusy/freebusy.php b/framework/Kolab_FreeBusy/www/Horde/Kolab/FreeBusy/freebusy.php index 0e2880227..a9b72b0a3 100644 --- a/framework/Kolab_FreeBusy/www/Horde/Kolab/FreeBusy/freebusy.php +++ b/framework/Kolab_FreeBusy/www/Horde/Kolab/FreeBusy/freebusy.php @@ -1,24 +1,30 @@ - * @author Gunnar Wrobel - * @author Thomas Arendsen Hein - * @package Kolab_FreeBusy + * See the enclosed file COPYING for license information (LGPL). If you did not + * receive this file, see + * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * PHP version 5 + * + * @category Kolab + * @package Kolab_FreeBusy + * @author Thomas Arendsen Hein + * @author Steffen Hansen + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_FreeBusy */ -/** Load the required free/busy library */ -require_once 'Horde/Kolab/FreeBusy.php'; - -/** Load the configuration */ -require_once 'config.php'; - -$fb = new Horde_Kolab_FreeBusy(); -$view = $fb->fetch(); -$view->render(); +/** + * The Autoloader allows us to omit "require/include" statements. + */ +require_once 'Horde/Autoloader.php'; +/** Dispatch the request. */ +$params = array('config' => array('dir' => dirname(__FILE__) . '/config')); +$application = Horde_Kolab_FreeBusy::singleton($params); +$application->dispatch();