From 8866e87886183c3204578e6a2eb62d1957af3918 Mon Sep 17 00:00:00 2001 From: Gunnar Wrobel Date: Wed, 24 Mar 2010 05:32:30 +0100 Subject: [PATCH] Start extracting an interface from the folder implementation. --- framework/Kolab_Storage/TODO | 8 + .../Kolab_Storage/lib/Horde/Kolab/Storage.php | 4 +- .../lib/Horde/Kolab/Storage/Folder.php | 1471 +------------------ .../lib/Horde/Kolab/Storage/Folder/Base.php | 1474 ++++++++++++++++++++ framework/Kolab_Storage/package.xml | 4 + .../test/Horde/Kolab/Storage/FolderTest.php | 4 +- .../test/Horde/Kolab/Storage/NamespaceTest.php | 2 +- .../test/Horde/Kolab/Storage/PermsTest.php | 2 +- 8 files changed, 1503 insertions(+), 1466 deletions(-) create mode 100644 framework/Kolab_Storage/TODO create mode 100644 framework/Kolab_Storage/lib/Horde/Kolab/Storage/Folder/Base.php diff --git a/framework/Kolab_Storage/TODO b/framework/Kolab_Storage/TODO new file mode 100644 index 000000000..5bafe3b7a --- /dev/null +++ b/framework/Kolab_Storage/TODO @@ -0,0 +1,8 @@ +===================================== + Kolab_Storage Development TODO List +===================================== + + - Remove character set conversion from the namespace handler + + - Do not hide the prefix of the shared namespace? + diff --git a/framework/Kolab_Storage/lib/Horde/Kolab/Storage.php b/framework/Kolab_Storage/lib/Horde/Kolab/Storage.php index 2a4bff406..47aa6fce6 100644 --- a/framework/Kolab_Storage/lib/Horde/Kolab/Storage.php +++ b/framework/Kolab_Storage/lib/Horde/Kolab/Storage.php @@ -302,7 +302,7 @@ class Horde_Kolab_Storage if (!isset($this->_folders[$folder])) { $result = $this->getConnection($folder); - $kf = new Horde_Kolab_Storage_Folder($result->name); + $kf = new Horde_Kolab_Storage_Folder_Base($result->name); $kf->restore($this, $result->connection); $this->_folders[$folder] = &$kf; } @@ -323,7 +323,7 @@ class Horde_Kolab_Storage } else { $connection = &$this->connections[$connection]; } - $folder = new Horde_Kolab_Storage_Folder(null); + $folder = new Horde_Kolab_Storage_Folder_Base(null); $folder->restore($this, $connection); return $folder; } diff --git a/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Folder.php b/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Folder.php index a11f43115..b44d930f4 100644 --- a/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Folder.php +++ b/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Folder.php @@ -1,16 +1,18 @@ + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_Storage */ -require_once 'Horde/Autoloader.php'; /** - * The Kolab_Folder class represents an IMAP folder on the Kolab - * server. + * The interface describing a Kolab folder. * * Copyright 2004-2010 The Horde Project (http://www.horde.org/) * @@ -22,1457 +24,6 @@ require_once 'Horde/Autoloader.php'; * @author Thomas Jarosch * @package Kolab_Storage */ -class Horde_Kolab_Storage_Folder +interface Horde_Kolab_Storage_Folder { - - /** - * The root of the Kolab annotation hierarchy, used on the various IMAP - * folder that are used by Kolab clients. - */ - const ANNOT_ROOT = '/shared/vendor/kolab/'; - - /** - * The annotation, as defined by the Kolab format spec, that is used to store - * information about what groupware format the folder contains. - */ - const ANNOT_FOLDER_TYPE = '/shared/vendor/kolab/folder-type'; - - /** - * Horde-specific annotations on the imap folder have this prefix. - */ - const ANNOT_SHARE_ATTR = '/shared/vendor/horde/share-'; - - /** - * Kolab specific free/busy relevance - */ - const FBRELEVANCE_ADMINS = 0; - const FBRELEVANCE_READERS = 1; - const FBRELEVANCE_NOBODY = 2; - - /** - * The folder name. - * - * @var string - */ - public $name; - - /** - * A new folder name if the folder should be renamed on the next - * save. - * - * @var string - */ - var $new_name; - - /** - * The connection specific for this folder. - * - * @var Horde_Kolab_Storage_Driver - */ - private $_connection; - - /** - * The handler for the list of Kolab folders. - * - * @var Kolab_storage - */ - var $_storage; - - /** - * The type of this folder. - * - * @var string - */ - var $_type; - - /** - * The complete folder type annotation (type + default). - * - * @var string - */ - var $_type_annotation; - - /** - * The owner of this folder. - * - * @var string - */ - var $_owner; - - /** - * The pure folder. - * - * @var string - */ - var $_subpath; - - /** - * Additional Horde folder attributes. - * - * @var array - */ - var $_attributes; - - /** - * Additional Kolab folder attributes. - * - * @var array - */ - var $_kolab_attributes; - - /** - * Is this a default folder? - * - * @var boolean - */ - var $_default; - - /** - * The title of this folder. - * - * @var string - */ - var $_title; - - /** - * The permission handler for the folder. - * - * @var Horde_Permission_Kolab - */ - var $_perms; - - /** - * Links to the data handlers for this folder. - * - * @var array - */ - var $_data; - - /** - * Links to the annotation data handlers for this folder. - * - * @var array - */ - var $_annotation_data; - - /** - * Indicate that the folder data has been modified from the - * outside and all Data handlers need to synchronize. - * - * @var boolean - */ - var $tainted = false; - - /** - * Creates a Kolab Folder representation. - * - * @param string $name Name of the folder - */ - function __construct($name = null) - { - $this->name = $name; - $this->__wakeup(); - } - - /** - * Initializes the object. - */ - function __wakeup() - { - if (!isset($this->_data)) { - $this->_data = array(); - } - - foreach($this->_data as $data) { - $data->setFolder($this); - } - - if (isset($this->_perms)) { - $this->_perms->setFolder($this); - } - } - - /** - * Returns the properties that need to be serialized. - * - * @return array List of serializable properties. - */ - function __sleep() - { - $properties = get_object_vars($this); - unset($properties['_storage']); - unset($properties['_connection']); - $properties = array_keys($properties); - return $properties; - } - - /** - * Restore the object after a deserialization. - * - * @param Horde_Kolab_Storage $storage The handler for the list of - * folders. - * @param Horde_Kolab_Storage_Driver $connection The storage connection. - */ - function restore( - Horde_Kolab_Storage &$storage, - Horde_Kolab_Storage_Driver &$connection - ) { - $this->_storage = $storage; - $this->_connection = $connection; - } - - /** - * Return the name of the folder. - * - * @return string The name of the folder. - */ - public function getName() - { - if (isset($this->name)) { - return $this->name; - } - if (!isset($this->name) && isset($this->new_name)) { - return $this->new_name; - } - } - - /** - * Set a new name for the folder. The new name will be realized - * when saving the folder. - * - * @param string $name The new folder name - */ - function setName($name) - { - $this->new_name = $this->_connection->getNamespace()->setName($name); - } - - /** - * Set a new IMAP folder name for the folder. The new name will be - * realized when saving the folder. - * - * @param string $name The new folder name. - */ - function setFolder($name) - { - $this->new_name = $name; - } - - /** - * Return the share ID of this folder. - * - * @return string The share ID of this folder. - */ - function getShareId() - { - $current_user = Horde_Auth::getAuth(); - if ($this->isDefault() && $this->getOwner() == $current_user) { - return $current_user; - } - return rawurlencode($this->name); - } - - /** - * Saves the folder. - * - * @param array $attributes An array of folder attributes. You can - * set any attribute but there are a few - * special ones like 'type', 'default', - * 'owner' and 'desc'. - * - * @return boolean|PEAR_Error True on success. - */ - function save($attributes = null) - { - if (!isset($this->name)) { - /* A new folder needs to be created */ - if (!isset($this->new_name)) { - throw new Horde_Kolab_Storage_Exception('Cannot create this folder! The name has not yet been set.', - Horde_Kolab_Storage_Exception::FOLDER_NAME_UNSET); - } - - if (isset($attributes['type'])) { - $this->_type = $attributes['type']; - unset($attributes['type']); - } else { - $this->_type = 'mail'; - } - - if (isset($attributes['default'])) { - $this->_default = $attributes['default']; - unset($attributes['default']); - } else { - $this->_default = false; - } - - $result = $this->_connection->exists($this->new_name); - if ($result) { - throw new Horde_Kolab_Storage_Exception(sprintf("Unable to add %s: destination folder already exists", - $this->new_name), - Horde_Kolab_Storage_Exception::FOLDER_EXISTS); - } - - $this->_connection->create($this->new_name); - - $this->name = $this->new_name; - $this->new_name = null; - - /* Initialize the new folder to default permissions */ - if (empty($this->_perms)) { - $this->getPermission(); - } - } else { - - $type = $this->getType(); - - if (isset($attributes['type'])) { - if ($attributes['type'] != $type) { - Horde::logMessage(sprintf('Cannot modify the type of a folder from %s to %s!', - $type, $attributes['type']), 'ERR'); - } - unset($attributes['type']); - } - - if (isset($attributes['default'])) { - $this->_default = $attributes['default']; - unset($attributes['default']); - } else { - $this->_default = $this->isDefault(); - } - - if (isset($this->new_name) - && $this->new_name != $this->name) { - /** The folder needs to be renamed */ - $result = $this->_connection->exists($this->new_name); - if ($result) { - throw new Horde_Kolab_Storage_Exception(sprintf(_("Unable to rename %s to %s: destination folder already exists"), - $name, $new_name)); - } - - $result = $this->_connection->rename($this->name, $this->new_name); - $this->_storage->removeFromCache($this); - - /** - * Trigger the old folder on an empty IMAP folder. - */ - try { - $this->_connection->create($this->name); - $this->_connection->setAnnotation(self::ANNOT_FOLDER_TYPE, - $this->_type, - $this->name); - $this->trigger($this->name); - $this->_connection->delete($this->name); - } catch (Exception $e) { - Horde::logMessage(sprintf('Failed handling the dummy folder: %s!', - $e->getMessage()), 'ERR'); - } - - $this->name = $this->new_name; - $this->new_name = null; - $this->_title = null; - $this->_owner = null; - } - } - - if (isset($attributes['owner'])) { - if ($attributes['owner'] != $this->getOwner()) { - Horde::logMessage(sprintf('Cannot modify the owner of a folder from %s to %s!', - $this->getOwner(), $attributes['owner']), 'ERR'); - } - unset($attributes['owner']); - } - - /** Handle the folder type */ - $folder_type = $this->_type . ($this->_default ? '.default' : ''); - if ($this->_type_annotation != $folder_type) { - try { - $result = $this->_setAnnotation(self::ANNOT_FOLDER_TYPE, $folder_type); - } catch (Exception $e) { - $this->_type = null; - $this->_default = false; - $this->_type_annotation = null; - throw $e; - } - } - - if (!empty($attributes)) { - if (!is_array($attributes)) { - $attributes = array($attributes); - } - foreach ($attributes as $key => $value) { - if ($key == 'params') { - $params = unserialize($value); - if (isset($params['xfbaccess'])) { - $result = $this->setXfbAccess($params['xfbaccess']); - if (is_a($result, 'PEAR_Error')) { - return $result; - } - } - if (isset($params['fbrelevance'])) { - $result = $this->setFbrelevance($params['fbrelevance']); - if (is_a($result, 'PEAR_Error')) { - return $result; - } - } - } - - // setAnnotation apparently does not suppoort UTF-8 nor any special characters - $store = base64_encode($value); - if ($key == 'desc') { - $entry = '/shared/comment'; - } else { - $entry = self::ANNOT_SHARE_ATTR . $key; - } - $result = $this->_setAnnotation($entry, $store); - if (is_a($result, 'PEAR_Error')) { - return $result; - } - } - $this->_attributes = $attributes; - } - - /** Now save the folder permissions */ - if (isset($this->_perms)) { - $this->_perms->save(); - } - - $this->_storage->addToCache($this); - - try { - $this->trigger(); - } catch (Horde_Kolab_Storage_Exception $e) { - Horde::logMessage(sprintf('Failed triggering folder %s! Error was: %s', - $this->name, $e->getMessage()), 'ERR'); - } - - return true; - } - - /** - * Delete this folder. - * - * @return boolean|PEAR_Error True if the operation succeeded. - */ - function delete() - { - $this->_connection->delete($this->name); - $this->_storage->removeFromCache($this); - return true; - } - - /** - * Returns the owner of the folder. - * - * @return string|PEAR_Error The owner of this folder. - */ - public function getOwner() - { - if (!isset($this->_owner)) { - $owner = $this->_connection->getNamespace()->getOwner($this->getName()); - /** - * @todo: Reconsider if this handling should really be done here - * rather than in a module nearer to the applications. - */ - switch ($owner) { - case Horde_Kolab_Storage_Namespace::PERSONAL: - $this->_owner = Horde_Auth::getAuth(); - break; - case Horde_Kolab_Storage_Namespace::SHARED: - $this->_owner = 'anonymous'; - break; - default: - list($prefix, $user) = explode(':', $owner, 2); - if (strpos($user, '@') === false) { - $domain = strstr(Horde_Auth::getAuth(), '@'); - if (!empty($domain)) { - $user .= $domain; - } - } - $this->_owner = $user; - break; - } - } - return $this->_owner; - } - - /** - * Returns the subpath of the folder. - * - * @param string $name Name of the folder that should be triggered. - * - * @return string|PEAR_Error The subpath of this folder. - */ - public function getSubpath($name = null) - { - if (!empty($name)) { - return $this->_connection->getNamespace()->getSubpath($name); - } - if (!isset($this->_subpath)) { - $this->_subpath = $this->_connection->getNamespace()->getSubpath($this->getName()); - } - return $this->_subpath; - } - - /** - * Returns a readable title for this folder. - * - * @return string The folder title. - */ - public function getTitle() - { - if (!isset($this->_title)) { - $this->_title = $this->_connection->getNamespace()->getTitle($this->getName()); - } - return $this->_title; - } - - /** - * The type of this folder. - * - * @return string|PEAR_Error The folder type. - */ - function getType() - { - if (!isset($this->_type)) { - try { - $type_annotation = $this->_getAnnotation(self::ANNOT_FOLDER_TYPE, - $this->name); - } catch (Exception $e) { - $this->_default = false; - throw $e; - } - if (empty($type_annotation)) { - $this->_default = false; - $this->_type = ''; - } else { - $type = explode('.', $type_annotation); - $this->_default = (!empty($type[1]) && $type[1] == 'default'); - $this->_type = $type[0]; - } - $this->_type_annotation = $type_annotation; - } - return $this->_type; - } - - /** - * Is this a default folder? - * - * @return boolean Boolean that indicates the default status. - */ - function isDefault() - { - if (!isset($this->_default)) { - /* This call also determines default status */ - $this->getType(); - } - return $this->_default; - } - - /** - * Returns one of the attributes of the folder, or an empty string - * if it isn't defined. - * - * @param string $attribute The attribute to retrieve. - * - * @return mixed The value of the attribute, an empty string or an - * error. - */ - function getAttribute($attribute) - { - if (!isset($this->_attributes[$attribute])) { - if ($attribute == 'desc') { - $entry = '/comment'; - } else { - $entry = self::ANNOT_SHARE_ATTR . $attribute; - } - $annotation = $this->_getAnnotation($entry, $this->name); - if (is_a($annotation, 'PEAR_Error')) { - return $annotation; - } - if (empty($annotation)) { - $this->_attributes[$attribute] = ''; - } else { - $this->_attributes[$attribute] = base64_decode($annotation); - } - } - return $this->_attributes[$attribute]; - } - - /** - * Returns one of the Kolab attributes of the folder, or an empty - * string if it isn't defined. - * - * @param string $attribute The attribute to retrieve. - * - * @return mixed The value of the attribute, an empty string or an - * error. - */ - function getKolabAttribute($attribute) - { - if (!isset($this->_kolab_attributes[$attribute])) { - $entry = KOLAB_ANNOT_ROOT . $attribute; - $annotation = $this->_getAnnotation($entry, $this->name); - if (is_a($annotation, 'PEAR_Error')) { - return $annotation; - } - if (empty($annotation)) { - $this->_kolab_attributes[$attribute] = ''; - } else { - $this->_kolab_attributes[$attribute] = $annotation; - } - } - return $this->_kolab_attributes[$attribute]; - } - - - /** - * Returns whether the folder exists. - * - * @return boolean|PEAR_Error True if the folder exists. - */ - function exists() - { - try { - return $this->_connection->exists($this->name); - } catch (Horde_Imap_Client_Exception $e) { - return false; - } - } - - /** - * Returns whether the folder is accessible. - * - * @return boolean|PEAR_Error True if the folder can be accessed. - */ - function accessible() - { - try { - return $this->_connection->select($this->name); - } catch (Horde_Imap_Client_Exception $e) { - return false; - } - } - - /** - * Retrieve a handler for the data in this folder. - * - * @param Kolab_List $list The handler for the list of folders. - * - * @return Horde_Kolab_Storage_Data The data handler. - */ - function &getData($object_type = null, $data_version = 1) - { - if (empty($object_type)) { - $object_type = $this->getType(); - if (is_a($object_type, 'PEAR_Error')) { - return $object_type; - } - } - - if ($this->tainted) { - foreach ($this->_data as $data) { - $data->synchronize(); - } - $this->tainted = false; - } - - $key = $object_type . '|' . $data_version; - if (!isset($this->_data[$key])) { - if ($object_type != 'annotation') { - $type = $this->getType(); - } else { - $type = 'annotation'; - } - $data = new Horde_Kolab_Storage_Data($type, $object_type, $data_version); - $data->setFolder($this); - $data->synchronize(); - $this->_data[$key] = &$data; - } - return $this->_data[$key]; - } - - /** - * Delete the specified message from this folder. - * - * @param string $id IMAP id of the message to be deleted. - * @param boolean $trigger Should the folder be triggered? - * - * @return boolean|PEAR_Error True if successful. - */ - function deleteMessage($id, $trigger = true) - { - // Select folder - $this->_connection->deleteMessages($this->name, $id); - $this->_connection->expunge($this->name); - - if ($trigger) { - try { - $result = $this->trigger(); - } catch (Horde_Kolab_Storage_Exception $e) { - Horde::logMessage(sprintf('Failed triggering folder %s! Error was: %s', - $this->name, $result->getMessage()), 'ERR'); - } - } - - return true; - } - - /** - * Move the specified message to the specified folder. - * - * @param string $id IMAP id of the message to be moved. - * @param string $folder Name of the receiving folder. - * - * @return boolean|PEAR_Error True if successful. - */ - function moveMessage($id, $folder) - { - // Select folder - $result = $this->_connection->select($this->name); - if (is_a($result, 'PEAR_Error')) { - return $result; - } - - $result = $this->_connection->moveMessage($this->name, $id, $folder); - if (is_a($result, 'PEAR_Error')) { - return $result; - } - - $result = $this->_connection->expunge($this->name); - if (is_a($result, 'PEAR_Error')) { - return $result; - } - - $result = $this->trigger(); - if (is_a($result, 'PEAR_Error')) { - Horde::logMessage(sprintf('Failed triggering folder %s! Error was: %s', - $this->name, $result->getMessage()), 'ERR'); - } - - return true; - } - - /** - * Move the specified message to the specified share. - * - * @param string $id IMAP id of the message to be moved. - * @param string $share Name of the receiving share. - * - * @return boolean|PEAR_Error True if successful. - */ - function moveMessageToShare($id, $share) - { - $folder = $this->_storage->getByShare($share, $this->getType()); - if (is_a($folder, 'PEAR_Error')) { - return $folder; - } - $folder->tainted = true; - - $success = $this->moveMessage($id, $folder->name); - - $result = $this->trigger(); - if (is_a($result, 'PEAR_Error')) { - Horde::logMessage(sprintf('Failed triggering folder %s! Error was: %s', - $this->name, $result->getMessage()), 'ERR'); - } - return $success; - } - - /** - * Retrieve the supported formats. - * - * @return array The names of the supported formats. - */ - function getFormats() - { - global $conf; - - if (empty($conf['kolab']['misc']['formats'])) { - $formats = array('XML'); - } else { - $formats = $conf['kolab']['misc']['formats']; - } - if (!is_array($formats)) { - $formats = array($formats); - } - if (!in_array('XML', $formats)) { - $formats[] = 'XML'; - } - return $formats; - } - - /** - * Save an object in this folder. - * - * @param array $object The array that holds the data of the object. - * @param int $data_version The format handler version. - * @param string $object_type The type of the kolab object. - * @param string $id The IMAP id of the old object if it - * existed before - * @param array $old_object The array that holds the current data of the - * object. - * - * @return boolean True on success. - */ - function saveObject(&$object, $data_version, $object_type, $id = null, - &$old_object = null) - { - // Select folder - $this->_connection->select($this->name); - - $new_headers = new Horde_Mime_Headers(); - $new_headers->setEOL("\r\n"); - - $formats = $this->getFormats(); - - $handlers = array(); - foreach ($formats as $type) { - $handlers[$type] = &Horde_Kolab_Format::factory($type, $object_type, - $data_version); - if (is_a($handlers[$type], 'PEAR_Error')) { - if ($type == 'XML') { - return $handlers[$type]; - } - Horde::logMessage(sprintf('Loading format handler "%s" failed: %s', - $type, $handlers[$type]->getMessage()), 'ERR'); - continue; - } - } - - if ($id != null) { - /** Update an existing kolab object */ - if (!in_array($id, $this->_connection->getUids($this->name))) { - return PEAR::raiseError(sprintf(_("The message with ID %s does not exist. This probably means that the Kolab object has been modified by somebody else while you were editing it. Your edits have been lost."), - $id)); - } - - /** Parse email and load Kolab format structure */ - $result = $this->parseMessage($id, $handlers['XML']->getMimeType(), - true, $formats); - if (is_a($result, 'PEAR_Error')) { - return $result; - } - list($old_message, $part_ids, $mime_message, $mime_headers) = $result; - if (is_a($old_message, 'PEAR_Error')) { - return $old_message; - } - - if (isset($object['_attachments']) && isset($old_object['_attachments'])) { - $attachments = array_keys($object['_attachments']); - foreach (array_keys($old_object['_attachments']) as $attachment) { - if (!in_array($attachment, $attachments)) { - foreach ($mime_message->getParts() as $part) { - if ($part->getName() === $attachment) { - foreach (array_keys($mime_message->_parts) as $key) { - if ($mime_message->_parts[$key]->getMimeId() == $part->getMimeId()) { - unset($mime_message->_parts[$key]); - break; - } - } - $mime_message->_generateIdMap($mime_message->_parts); - } - } - } - } - } - $object = array_merge($old_object, $object); - - if (isset($attachments)) { - foreach ($mime_message->getParts() as $part) { - $name = $part->getName(); - foreach ($attachments as $attachment) { - if ($name === $attachment) { - $object['_attachments'][$attachment]['id'] = $part->getMimeId(); - } - } - } - } - - /** Copy email header */ - if (!empty($mime_headers) && !$mime_headers === false) { - foreach ($mime_headers as $header => $value) { - $new_headers->addheader($header, $value); - } - } - } else { - $mime_message = $this->_prepareNewMessage($new_headers); - $mime_part_id = false; - } - - if (isset($object['_attachments'])) { - $attachments = array_keys($object['_attachments']); - foreach ($attachments as $attachment) { - $data = $object['_attachments'][$attachment]; - - if (!isset($data['content']) && !isset($data['path'])) { - /** - * There no new content and no new path. Do not rewrite the - * attachment. - */ - continue; - } - - $part = new Horde_Mime_Part(); - $part->setType(isset($data['type']) ? $data['type'] : null); - $part->setContents(isset($data['content']) ? $data['content'] : file_get_contents($data['path'])); - $part->setCharset(Horde_Nls::getCharset()); - $part->setTransferEncoding('quoted-printable'); - $part->setDisposition('attachment'); - $part->setName($attachment); - - if (!isset($data['id'])) { - $mime_message->addPart($part); - } else { - $mime_message->alterPart($data['id'], $part); - } - } - } - - foreach ($formats as $type) { - $new_content = $handlers[$type]->save($object); - if (is_a($new_content, 'PEAR_Error')) { - return $new_content; - } - - /** Update mime part */ - $part = new Horde_Mime_Part(); - $part->setType($handlers[$type]->getMimeType()); - $part->setContents($new_content); - $part->setCharset(Horde_Nls::getCharset()); - $part->setTransferEncoding('quoted-printable'); - $part->setDisposition($handlers[$type]->getDisposition()); - $part->setDispositionParameter('x-kolab-type', $type); - $part->setName($handlers[$type]->getName()); - - if (!isset($part_ids) || $part_ids[$type] === false) { - $mime_message->addPart($part); - } else { - $mime_message->alterPart($part_ids[$type], $part); - } - } - - $session = &Horde_Kolab_Session_Singleton::singleton(); - - // Update email headers - $new_headers->addHeader('From', $session->user_mail); - $new_headers->addHeader('To', $session->user_mail); - $new_headers->addHeader('Date', date('r')); - $new_headers->addHeader('X-Kolab-Type', $handlers['XML']->getMimeType()); - $new_headers->addHeader('Subject', $object['uid']); - $new_headers->addHeader('User-Agent', 'Horde::Kolab::Storage v0.2'); - $new_headers->addHeader('MIME-Version', '1.0'); - $mime_message->addMimeHeaders(array('headers' => $new_headers)); - - $msg = $new_headers->toString() . $mime_message->toString(array('canonical' => true, - 'headers' => false)); - - // delete old email? - if ($id != null) { - $this->_connection->deleteMessages($this->name, $id); - } - - // store new email - try { - $result = $this->_connection->appendMessage($this->name, $msg); - } catch (Horde_Kolab_Storage_Exception $e) { - if ($id != null) { - $this->_connection->undeleteMessages($id); - } - } - - // remove deleted object - if ($id != null) { - $this->_connection->expunge($this->name); - } - - try { - $this->trigger(); - } catch (Horde_Kolab_Storage_Exception $e) { - Horde::logMessage(sprintf('Failed triggering folder %s! Error was: %s', - $this->name, $result->getMessage()), 'ERR'); - } - - return true; - } - - /** - * Get an IMAP message and retrieve the Kolab Format object. - * - * @param int $id The message to retrieve. - * @param string $mime_type The mime type of the part to retrieve. - * @param boolean $parse_headers Should the heades be Mime parsed? - * @param array $formats The list of possible format parts. - * - * @return array|PEAR_Error An array that list the Kolab XML - * object text, the mime ID of the part - * with the XML object, the Mime parsed - * message and the Mime parsed headers if - * requested. - */ - function parseMessage($id, $mime_type, $parse_headers = true, - $formats = array('XML')) - { - $raw_headers = $this->_connection->getMessageHeader($this->name, $id); - if (is_a($raw_headers, 'PEAR_Error')) { - return PEAR::raiseError(sprintf(_("Failed retrieving the message with ID %s. Original error: %s."), - $id, $raw_headers->getMessage())); - } - - $body = $this->_connection->getMessageBody($this->name, $id); - if (is_a($body, 'PEAR_Error')) { - return PEAR::raiseError(sprintf(_("Failed retrieving the message with ID %s. Original error: %s."), - $id, $body->getMessage())); - } - - //@todo: not setting "forcemime" means the subparts get checked too. Seems incorrect. - $mime_message = Horde_Mime_Part::parseMessage($raw_headers . "\r" . $body, array('forcemime' => true)); - $parts = $mime_message->contentTypeMap(); - - $mime_headers = false; - $xml = false; - - // Read in a Kolab event object, if one exists - $part_ids['XML'] = array_search($mime_type, $parts); - if ($part_ids['XML'] !== false) { - if ($parse_headers) { - $mime_headers = Horde_Mime_Headers::parseHeaders($raw_headers); - $mime_headers->setEOL("\r\n"); - } - - $part = $mime_message->getPart($part_ids['XML']); - //@todo: Check what happened to this call - //$part->transferDecodeContents(); - $xml = $part->getContents(); - } - - $alternate_formats = array_diff(array('XML'), $formats); - if (!empty($alternate_formats)) { - foreach ($alternate_formats as $type) { - $part_ids[$type] = false; - } - foreach ($mime_message->getParts() as $part) { - $params = $part->getDispositionParameters(); - foreach ($alternate_formats as $type) { - if (isset($params['x-kolab-format']) - && $params['x-kolab-format'] == $type) { - $part_ids[$type] = $part->getMimeId(); - } - } - } - } - - $result = array($xml, $part_ids, $mime_message, $mime_headers); - return $result; - } - - /** - * Prepares a new kolab Groupeware message. - * - * @return string The Mime message - */ - function _prepareNewMessage() - { - $mime_message = new Horde_Mime_Part(); - $mime_message->setName('Kolab Groupware Data'); - $mime_message->setType('multipart/mixed'); - $kolab_text = sprintf(_("This is a Kolab Groupware object. To view this object you will need an email client that understands the Kolab Groupware format. For a list of such email clients please visit %s"), - 'http://www.kolab.org/kolab2-clients.html'); - $part = new Horde_Mime_Part(); - $part->setType('text/plain'); - $part->setName('Kolab Groupware Information'); - $part->setContents(Horde_String::wrap($kolab_text, 76, "\r\n", Horde_Nls::getCharset())); - $part->setCharset(Horde_Nls::getCharset()); - - $part->setTransferEncoding('quoted-printable'); - $mime_message->addPart($part); - return $mime_message; - } - - /** - * Report the status of this folder. - * - * @return array|PEAR_Error An array listing the validity ID, the - * next IMAP ID and an array of IMAP IDs. - */ - function getStatus() - { - // Select the folder to update uidnext - $this->_connection->select($this->name); - - $status = $this->_connection->status($this->name); - $uids = $this->_connection->getUids($this->name); - return array($status['uidvalidity'], $status['uidnext'], $uids); - } - - /** - * Triggers any required updates after changes within the - * folder. This is currently only required for handling free/busy - * information with Kolab. - * - * @param string $name Name of the folder that should be triggered. - * - * @return boolean|PEAR_Error True if successfull. - */ - function trigger($name = null) - { - $type = $this->getType(); - if (is_a($type, 'PEAR_Error')) { - return $type; - } - - $owner = $this->getOwner(); - if (is_a($owner, 'PEAR_Error')) { - return $owner; - } - - $subpath = $this->getSubpath($name); - if (is_a($subpath, 'PEAR_Error')) { - return $subpath; - } - - switch($type) { - case 'event': - $session = &Horde_Kolab_Session_Singleton::singleton(); - $url = sprintf('%s/trigger/%s/%s.pfb', - $session->freebusy_server, $owner, $subpath); - break; - default: - return true; - } - - $result = $this->triggerUrl($url); - if (is_a($result, 'PEAR_Error')) { - return PEAR::raiseError(sprintf(_("Failed triggering folder %s. Error was: %s"), - $this->name, $result->getMessage())); - } - return $result; - } - - /** - * Triggers a URL. - * - * @param string $url The URL to be triggered. - * - * @return boolean|PEAR_Error True if successfull. - */ - function triggerUrl($url) - { - global $conf; - - if (!empty($conf['kolab']['no_triggering'])) { - return true; - } - - $options['method'] = 'GET'; - $options['timeout'] = 5; - $options['allowRedirects'] = true; - - if (isset($conf['http']['proxy']) && !empty($conf['http']['proxy']['proxy_host'])) { - $options = array_merge($options, $conf['http']['proxy']); - } - - require_once 'HTTP/Request.php'; - $http = new HTTP_Request($url, $options); - $http->setBasicAuth(Horde_Auth::getAuth(), Horde_Auth::getCredential('password')); - @$http->sendRequest(); - if ($http->getResponseCode() != 200) { - return PEAR::raiseError(sprintf(_("Unable to trigger URL %s. Response: %s"), - $url, $http->getResponseCode())); - } - return true; - } - - /** - * Checks to see if a user has a given permission. - * - * @param string $userid The userid of the user. - * @param integer $permission A Horde_Perms::* constant to test for. - * @param string $creator The creator of the shared object. - * - * @return boolean|PEAR_Error Whether or not $userid has $permission. - */ - function hasPermission($userid, $permission, $creator = null) - { - if ($userid == $this->getOwner()) { - return true; - } - - $perm = &$this->getPermission(); - if (is_a($perm, 'PEAR_Error')) { - return $perm; - } - return $perm->hasPermission($userid, $permission, $creator); - } - - /** - * Returns the permissions from this storage object. - * - * @return Horde_Permission_Kolab The permissions on the share. - */ - function &getPermission() - { - if (!isset($this->_perms)) { - if ($this->exists()) { - // The permissions are unknown but the folder exists - // -> discover permissions - $perms = null; - } else { - $perms = array( - 'users' => array( - Horde_Auth::getAuth() => Horde_Perms::ALL - ) - ); - } - $this->_perms = new Horde_Kolab_Storage_Permission($this, $perms); - } - return $this->_perms; - } - - /** - * Sets the permissions on the share. - * - * @param Horde_Permission_Kolab $perms Permission object to store on the - * object. - * @param boolean $update Save the updated information? - * - * @return boolean|PEAR_Error True on success. - */ - function setPermission(&$perms, $update = true) - { - if (!is_a($perms, 'Horde_Perms_Permission')) { - return PEAR::raiseError('The permissions for this share must be specified as an instance of the Horde_Perms_Permission class!'); - } - - if (!is_a($perms, 'Horde_Kolab_Storage_Permission')) { - $this->_perms = new Horde_Kolab_Storage_Permission($this, $perms->data); - } else { - $this->_perms = $perms; - $this->_perms->setFolder($this); - } - - if ($update) { - return $this->save(); - } - - return true; - } - - /** - * Return the IMAP ACL of this folder. - * - * @return array|PEAR_Error An array with IMAP ACL. - */ - function getACL() - { - return $this->_connection->getACL($this->name); - } - - /** - * Set the IMAP ACL of this folder. - * - * @param $user The user for whom the ACL should be set. - * @param $acl The new ACL value. - * - * @return boolean|PEAR_Error True on success. - */ - function setACL($user, $acl) - { - $this->_connection->setACL($this->name, $user, $acl); - - if (!empty($this->_perms)) { - /** Refresh the cache after changing the permissions */ - $this->_perms->getPerm(); - } - - $result = $this->trigger(); - if (is_a($result, 'PEAR_Error')) { - Horde::logMessage(sprintf('Failed triggering folder %s! Error was: %s', - $this->name, $result->getMessage()), 'ERR'); - } - - return $result; - } - - /** - * Delete the IMAP ACL for a user on this folder. - * - * @param $user The user for whom the ACL should be deleted. - * - * @return boolean|PEAR_Error True on success. - */ - function deleteACL($user) - { - global $conf; - - if (!empty($conf['kolab']['imap']['no_acl'])) { - return true; - } - - $this->_connection->deleteACL($this->name, $user); - - $result = $this->trigger(); - if (is_a($result, 'PEAR_Error')) { - Horde::logMessage(sprintf('Failed triggering folder %s! Error was: %s', - $this->name, $result->getMessage()), 'ERR'); - } - - return $iresult; - } - - - /** - * Get annotation values on IMAP server that do not support - * METADATA. - * - * @return array|PEAR_Error The anotations of this folder. - */ - function _getAnnotationData() - { - $this->_annotation_data = $this->getData('annotation'); - } - - - /** - * Get an annotation value of this folder. - * - * @param $key The key of the annotation to retrieve. - * - * @return string|PEAR_Error The anotation value. - */ - function _getAnnotation($key) - { - global $conf; - - if (empty($conf['kolab']['imap']['no_annotations'])) { - return $this->_connection->getAnnotation($key, $this->name); - } - - if (!isset($this->_annotation_data)) { - $this->_getAnnotationData(); - } - $data = $this->_annotation_data->getObject('KOLAB_FOLDER_CONFIGURATION'); - if (is_a($data, 'PEAR_Error')) { - Horde::logMessage(sprintf('Error retrieving annotation data on folder %s: %s', - $this->name, $data->getMessage()), 'ERR'); - return ''; - } - if (isset($data[$key])) { - return $data[$key]; - } else { - return ''; - } - } - - /** - * Set an annotation value of this folder. - * - * @param $key The key of the annotation to change. - * @param $value The new value. - * - * @return boolean|PEAR_Error True on success. - */ - function _setAnnotation($key, $value) - { - if (empty($conf['kolab']['imap']['no_annotations'])) { - return $this->_connection->setAnnotation($key, $value, $this->name); - } - - if (!isset($this->_annotation_data)) { - $this->_getAnnotationData(); - } - $data = $this->_annotation_data->getObject('KOLAB_FOLDER_CONFIGURATION'); - if (is_a($data, 'PEAR_Error')) { - Horde::logMessage(sprintf('Error retrieving annotation data on folder %s: %s', - $this->name, $data->getMessage()), 'ERR'); - $data = array(); - $uid = null; - } else { - $uid = 'KOLAB_FOLDER_CONFIGURATION'; - } - $data[$key] = $value; - $data['uid'] = 'KOLAB_FOLDER_CONFIGURATION'; - return $this->_annotation_data->save($data, $uid); - } - - - - /** - * Get the free/busy relevance for this folder - * - * @return int Value containing the FB_RELEVANCE. - */ - function getFbrelevance() - { - $result = $this->getKolabAttribute('incidences-for'); - if (is_a($result, 'PEAR_Error') || empty($result)) { - return KOLAB_FBRELEVANCE_ADMINS; - } - switch ($result) { - case 'admins': - return KOLAB_FBRELEVANCE_ADMINS; - case 'readers': - return KOLAB_FBRELEVANCE_READERS; - case 'nobody': - return KOLAB_FBRELEVANCE_NOBODY; - default: - return KOLAB_FBRELEVANCE_ADMINS; - } - } - - /** - * Set the free/busy relevance for this folder - * - * @param int $relevance Value containing the FB_RELEVANCE - * - * @return mixed True on success or a PEAR_Error. - */ - function setFbrelevance($relevance) - { - switch ($relevance) { - case KOLAB_FBRELEVANCE_ADMINS: - $value = 'admins'; - break; - case KOLAB_FBRELEVANCE_READERS: - $value = 'readers'; - break; - case KOLAB_FBRELEVANCE_NOBODY: - $value = 'nobody'; - break; - default: - $value = 'admins'; - } - - return $this->_setAnnotation(KOLAB_ANNOT_ROOT . 'incidences-for', - $value); - } - - /** - * Get the extended free/busy access settings for this folder - * - * @return array Array containing the users with access to the - * extended information. - */ - function getXfbaccess() - { - $result = $this->getKolabAttribute('pxfb-readable-for'); - if (is_a($result, 'PEAR_Error') || empty($result)) { - return array(); - } - return explode(' ', $result); - } - - /** - * Set the extended free/busy access settings for this folder - * - * @param array $access Array containing the users with access to the - * extended information. - * - * @return mixed True on success or a PEAR_Error. - */ - function setXfbaccess($access) - { - $value = join(' ', $access); - return $this->_setAnnotation(KOLAB_ANNOT_ROOT . 'pxfb-readable-for', - $value); - } } diff --git a/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Folder/Base.php b/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Folder/Base.php new file mode 100644 index 000000000..a8b2dc564 --- /dev/null +++ b/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Folder/Base.php @@ -0,0 +1,1474 @@ + + * @author Gunnar Wrobel + * @author Thomas Jarosch + * @package Kolab_Storage + */ +class Horde_Kolab_Storage_Folder_Base +implements Horde_Kolab_Storage_Folder +{ + + /** + * The root of the Kolab annotation hierarchy, used on the various IMAP + * folder that are used by Kolab clients. + */ + const ANNOT_ROOT = '/shared/vendor/kolab/'; + + /** + * The annotation, as defined by the Kolab format spec, that is used to store + * information about what groupware format the folder contains. + */ + const ANNOT_FOLDER_TYPE = '/shared/vendor/kolab/folder-type'; + + /** + * Horde-specific annotations on the imap folder have this prefix. + */ + const ANNOT_SHARE_ATTR = '/shared/vendor/horde/share-'; + + /** + * Kolab specific free/busy relevance + */ + const FBRELEVANCE_ADMINS = 0; + const FBRELEVANCE_READERS = 1; + const FBRELEVANCE_NOBODY = 2; + + /** + * The folder name. + * + * @var string + */ + public $name; + + /** + * A new folder name if the folder should be renamed on the next + * save. + * + * @var string + */ + var $new_name; + + /** + * The connection specific for this folder. + * + * @var Horde_Kolab_Storage_Driver + */ + private $_connection; + + /** + * The handler for the list of Kolab folders. + * + * @var Kolab_storage + */ + var $_storage; + + /** + * The type of this folder. + * + * @var string + */ + var $_type; + + /** + * The complete folder type annotation (type + default). + * + * @var string + */ + var $_type_annotation; + + /** + * The owner of this folder. + * + * @var string + */ + var $_owner; + + /** + * The pure folder. + * + * @var string + */ + var $_subpath; + + /** + * Additional Horde folder attributes. + * + * @var array + */ + var $_attributes; + + /** + * Additional Kolab folder attributes. + * + * @var array + */ + var $_kolab_attributes; + + /** + * Is this a default folder? + * + * @var boolean + */ + var $_default; + + /** + * The title of this folder. + * + * @var string + */ + var $_title; + + /** + * The permission handler for the folder. + * + * @var Horde_Permission_Kolab + */ + var $_perms; + + /** + * Links to the data handlers for this folder. + * + * @var array + */ + var $_data; + + /** + * Links to the annotation data handlers for this folder. + * + * @var array + */ + var $_annotation_data; + + /** + * Indicate that the folder data has been modified from the + * outside and all Data handlers need to synchronize. + * + * @var boolean + */ + var $tainted = false; + + /** + * Creates a Kolab Folder representation. + * + * @param string $name Name of the folder + */ + function __construct($name = null) + { + $this->name = $name; + $this->__wakeup(); + } + + /** + * Initializes the object. + */ + function __wakeup() + { + if (!isset($this->_data)) { + $this->_data = array(); + } + + foreach($this->_data as $data) { + $data->setFolder($this); + } + + if (isset($this->_perms)) { + $this->_perms->setFolder($this); + } + } + + /** + * Returns the properties that need to be serialized. + * + * @return array List of serializable properties. + */ + function __sleep() + { + $properties = get_object_vars($this); + unset($properties['_storage']); + unset($properties['_connection']); + $properties = array_keys($properties); + return $properties; + } + + /** + * Restore the object after a deserialization. + * + * @param Horde_Kolab_Storage $storage The handler for the list of + * folders. + * @param Horde_Kolab_Storage_Driver $connection The storage connection. + */ + function restore( + Horde_Kolab_Storage &$storage, + Horde_Kolab_Storage_Driver &$connection + ) { + $this->_storage = $storage; + $this->_connection = $connection; + } + + /** + * Return the name of the folder. + * + * @return string The name of the folder. + */ + public function getName() + { + if (isset($this->name)) { + return $this->name; + } + if (!isset($this->name) && isset($this->new_name)) { + return $this->new_name; + } + } + + /** + * Set a new name for the folder. The new name will be realized + * when saving the folder. + * + * @param string $name The new folder name + */ + function setName($name) + { + $this->new_name = $this->_connection->getNamespace()->setName($name); + } + + /** + * Set a new IMAP folder name for the folder. The new name will be + * realized when saving the folder. + * + * @param string $name The new folder name. + */ + function setFolder($name) + { + $this->new_name = $name; + } + + /** + * Return the share ID of this folder. + * + * @return string The share ID of this folder. + */ + function getShareId() + { + $current_user = Horde_Auth::getAuth(); + if ($this->isDefault() && $this->getOwner() == $current_user) { + return $current_user; + } + return rawurlencode($this->name); + } + + /** + * Saves the folder. + * + * @param array $attributes An array of folder attributes. You can + * set any attribute but there are a few + * special ones like 'type', 'default', + * 'owner' and 'desc'. + * + * @return boolean|PEAR_Error True on success. + */ + function save($attributes = null) + { + if (!isset($this->name)) { + /* A new folder needs to be created */ + if (!isset($this->new_name)) { + throw new Horde_Kolab_Storage_Exception('Cannot create this folder! The name has not yet been set.', + Horde_Kolab_Storage_Exception::FOLDER_NAME_UNSET); + } + + if (isset($attributes['type'])) { + $this->_type = $attributes['type']; + unset($attributes['type']); + } else { + $this->_type = 'mail'; + } + + if (isset($attributes['default'])) { + $this->_default = $attributes['default']; + unset($attributes['default']); + } else { + $this->_default = false; + } + + $result = $this->_connection->exists($this->new_name); + if ($result) { + throw new Horde_Kolab_Storage_Exception(sprintf("Unable to add %s: destination folder already exists", + $this->new_name), + Horde_Kolab_Storage_Exception::FOLDER_EXISTS); + } + + $this->_connection->create($this->new_name); + + $this->name = $this->new_name; + $this->new_name = null; + + /* Initialize the new folder to default permissions */ + if (empty($this->_perms)) { + $this->getPermission(); + } + } else { + + $type = $this->getType(); + + if (isset($attributes['type'])) { + if ($attributes['type'] != $type) { + Horde::logMessage(sprintf('Cannot modify the type of a folder from %s to %s!', + $type, $attributes['type']), 'ERR'); + } + unset($attributes['type']); + } + + if (isset($attributes['default'])) { + $this->_default = $attributes['default']; + unset($attributes['default']); + } else { + $this->_default = $this->isDefault(); + } + + if (isset($this->new_name) + && $this->new_name != $this->name) { + /** The folder needs to be renamed */ + $result = $this->_connection->exists($this->new_name); + if ($result) { + throw new Horde_Kolab_Storage_Exception(sprintf(_("Unable to rename %s to %s: destination folder already exists"), + $name, $new_name)); + } + + $result = $this->_connection->rename($this->name, $this->new_name); + $this->_storage->removeFromCache($this); + + /** + * Trigger the old folder on an empty IMAP folder. + */ + try { + $this->_connection->create($this->name); + $this->_connection->setAnnotation(self::ANNOT_FOLDER_TYPE, + $this->_type, + $this->name); + $this->trigger($this->name); + $this->_connection->delete($this->name); + } catch (Exception $e) { + Horde::logMessage(sprintf('Failed handling the dummy folder: %s!', + $e->getMessage()), 'ERR'); + } + + $this->name = $this->new_name; + $this->new_name = null; + $this->_title = null; + $this->_owner = null; + } + } + + if (isset($attributes['owner'])) { + if ($attributes['owner'] != $this->getOwner()) { + Horde::logMessage(sprintf('Cannot modify the owner of a folder from %s to %s!', + $this->getOwner(), $attributes['owner']), 'ERR'); + } + unset($attributes['owner']); + } + + /** Handle the folder type */ + $folder_type = $this->_type . ($this->_default ? '.default' : ''); + if ($this->_type_annotation != $folder_type) { + try { + $result = $this->_setAnnotation(self::ANNOT_FOLDER_TYPE, $folder_type); + } catch (Exception $e) { + $this->_type = null; + $this->_default = false; + $this->_type_annotation = null; + throw $e; + } + } + + if (!empty($attributes)) { + if (!is_array($attributes)) { + $attributes = array($attributes); + } + foreach ($attributes as $key => $value) { + if ($key == 'params') { + $params = unserialize($value); + if (isset($params['xfbaccess'])) { + $result = $this->setXfbAccess($params['xfbaccess']); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + } + if (isset($params['fbrelevance'])) { + $result = $this->setFbrelevance($params['fbrelevance']); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + } + } + + // setAnnotation apparently does not suppoort UTF-8 nor any special characters + $store = base64_encode($value); + if ($key == 'desc') { + $entry = '/shared/comment'; + } else { + $entry = self::ANNOT_SHARE_ATTR . $key; + } + $result = $this->_setAnnotation($entry, $store); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + } + $this->_attributes = $attributes; + } + + /** Now save the folder permissions */ + if (isset($this->_perms)) { + $this->_perms->save(); + } + + $this->_storage->addToCache($this); + + try { + $this->trigger(); + } catch (Horde_Kolab_Storage_Exception $e) { + Horde::logMessage(sprintf('Failed triggering folder %s! Error was: %s', + $this->name, $e->getMessage()), 'ERR'); + } + + return true; + } + + /** + * Delete this folder. + * + * @return boolean|PEAR_Error True if the operation succeeded. + */ + function delete() + { + $this->_connection->delete($this->name); + $this->_storage->removeFromCache($this); + return true; + } + + /** + * Returns the owner of the folder. + * + * @return string|PEAR_Error The owner of this folder. + */ + public function getOwner() + { + if (!isset($this->_owner)) { + $owner = $this->_connection->getNamespace()->getOwner($this->getName()); + /** + * @todo: Reconsider if this handling should really be done here + * rather than in a module nearer to the applications. + */ + switch ($owner) { + case Horde_Kolab_Storage_Namespace::PERSONAL: + $this->_owner = Horde_Auth::getAuth(); + break; + case Horde_Kolab_Storage_Namespace::SHARED: + $this->_owner = 'anonymous'; + break; + default: + list($prefix, $user) = explode(':', $owner, 2); + if (strpos($user, '@') === false) { + $domain = strstr(Horde_Auth::getAuth(), '@'); + if (!empty($domain)) { + $user .= $domain; + } + } + $this->_owner = $user; + break; + } + } + return $this->_owner; + } + + /** + * Returns the subpath of the folder. + * + * @param string $name Name of the folder that should be triggered. + * + * @return string|PEAR_Error The subpath of this folder. + */ + public function getSubpath($name = null) + { + if (!empty($name)) { + return $this->_connection->getNamespace()->getSubpath($name); + } + if (!isset($this->_subpath)) { + $this->_subpath = $this->_connection->getNamespace()->getSubpath($this->getName()); + } + return $this->_subpath; + } + + /** + * Returns a readable title for this folder. + * + * @return string The folder title. + */ + public function getTitle() + { + if (!isset($this->_title)) { + $this->_title = $this->_connection->getNamespace()->getTitle($this->getName()); + } + return $this->_title; + } + + /** + * The type of this folder. + * + * @return string|PEAR_Error The folder type. + */ + function getType() + { + if (!isset($this->_type)) { + try { + $type_annotation = $this->_getAnnotation(self::ANNOT_FOLDER_TYPE, + $this->name); + } catch (Exception $e) { + $this->_default = false; + throw $e; + } + if (empty($type_annotation)) { + $this->_default = false; + $this->_type = ''; + } else { + $type = explode('.', $type_annotation); + $this->_default = (!empty($type[1]) && $type[1] == 'default'); + $this->_type = $type[0]; + } + $this->_type_annotation = $type_annotation; + } + return $this->_type; + } + + /** + * Is this a default folder? + * + * @return boolean Boolean that indicates the default status. + */ + function isDefault() + { + if (!isset($this->_default)) { + /* This call also determines default status */ + $this->getType(); + } + return $this->_default; + } + + /** + * Returns one of the attributes of the folder, or an empty string + * if it isn't defined. + * + * @param string $attribute The attribute to retrieve. + * + * @return mixed The value of the attribute, an empty string or an + * error. + */ + function getAttribute($attribute) + { + if (!isset($this->_attributes[$attribute])) { + if ($attribute == 'desc') { + $entry = '/comment'; + } else { + $entry = self::ANNOT_SHARE_ATTR . $attribute; + } + $annotation = $this->_getAnnotation($entry, $this->name); + if (is_a($annotation, 'PEAR_Error')) { + return $annotation; + } + if (empty($annotation)) { + $this->_attributes[$attribute] = ''; + } else { + $this->_attributes[$attribute] = base64_decode($annotation); + } + } + return $this->_attributes[$attribute]; + } + + /** + * Returns one of the Kolab attributes of the folder, or an empty + * string if it isn't defined. + * + * @param string $attribute The attribute to retrieve. + * + * @return mixed The value of the attribute, an empty string or an + * error. + */ + function getKolabAttribute($attribute) + { + if (!isset($this->_kolab_attributes[$attribute])) { + $entry = KOLAB_ANNOT_ROOT . $attribute; + $annotation = $this->_getAnnotation($entry, $this->name); + if (is_a($annotation, 'PEAR_Error')) { + return $annotation; + } + if (empty($annotation)) { + $this->_kolab_attributes[$attribute] = ''; + } else { + $this->_kolab_attributes[$attribute] = $annotation; + } + } + return $this->_kolab_attributes[$attribute]; + } + + + /** + * Returns whether the folder exists. + * + * @return boolean|PEAR_Error True if the folder exists. + */ + function exists() + { + try { + return $this->_connection->exists($this->name); + } catch (Horde_Imap_Client_Exception $e) { + return false; + } + } + + /** + * Returns whether the folder is accessible. + * + * @return boolean|PEAR_Error True if the folder can be accessed. + */ + function accessible() + { + try { + return $this->_connection->select($this->name); + } catch (Horde_Imap_Client_Exception $e) { + return false; + } + } + + /** + * Retrieve a handler for the data in this folder. + * + * @param Kolab_List $list The handler for the list of folders. + * + * @return Horde_Kolab_Storage_Data The data handler. + */ + function &getData($object_type = null, $data_version = 1) + { + if (empty($object_type)) { + $object_type = $this->getType(); + if (is_a($object_type, 'PEAR_Error')) { + return $object_type; + } + } + + if ($this->tainted) { + foreach ($this->_data as $data) { + $data->synchronize(); + } + $this->tainted = false; + } + + $key = $object_type . '|' . $data_version; + if (!isset($this->_data[$key])) { + if ($object_type != 'annotation') { + $type = $this->getType(); + } else { + $type = 'annotation'; + } + $data = new Horde_Kolab_Storage_Data($type, $object_type, $data_version); + $data->setFolder($this); + $data->synchronize(); + $this->_data[$key] = &$data; + } + return $this->_data[$key]; + } + + /** + * Delete the specified message from this folder. + * + * @param string $id IMAP id of the message to be deleted. + * @param boolean $trigger Should the folder be triggered? + * + * @return boolean|PEAR_Error True if successful. + */ + function deleteMessage($id, $trigger = true) + { + // Select folder + $this->_connection->deleteMessages($this->name, $id); + $this->_connection->expunge($this->name); + + if ($trigger) { + try { + $result = $this->trigger(); + } catch (Horde_Kolab_Storage_Exception $e) { + Horde::logMessage(sprintf('Failed triggering folder %s! Error was: %s', + $this->name, $result->getMessage()), 'ERR'); + } + } + + return true; + } + + /** + * Move the specified message to the specified folder. + * + * @param string $id IMAP id of the message to be moved. + * @param string $folder Name of the receiving folder. + * + * @return boolean|PEAR_Error True if successful. + */ + function moveMessage($id, $folder) + { + // Select folder + $result = $this->_connection->select($this->name); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + + $result = $this->_connection->moveMessage($this->name, $id, $folder); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + + $result = $this->_connection->expunge($this->name); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + + $result = $this->trigger(); + if (is_a($result, 'PEAR_Error')) { + Horde::logMessage(sprintf('Failed triggering folder %s! Error was: %s', + $this->name, $result->getMessage()), 'ERR'); + } + + return true; + } + + /** + * Move the specified message to the specified share. + * + * @param string $id IMAP id of the message to be moved. + * @param string $share Name of the receiving share. + * + * @return boolean|PEAR_Error True if successful. + */ + function moveMessageToShare($id, $share) + { + $folder = $this->_storage->getByShare($share, $this->getType()); + if (is_a($folder, 'PEAR_Error')) { + return $folder; + } + $folder->tainted = true; + + $success = $this->moveMessage($id, $folder->name); + + $result = $this->trigger(); + if (is_a($result, 'PEAR_Error')) { + Horde::logMessage(sprintf('Failed triggering folder %s! Error was: %s', + $this->name, $result->getMessage()), 'ERR'); + } + return $success; + } + + /** + * Retrieve the supported formats. + * + * @return array The names of the supported formats. + */ + function getFormats() + { + global $conf; + + if (empty($conf['kolab']['misc']['formats'])) { + $formats = array('XML'); + } else { + $formats = $conf['kolab']['misc']['formats']; + } + if (!is_array($formats)) { + $formats = array($formats); + } + if (!in_array('XML', $formats)) { + $formats[] = 'XML'; + } + return $formats; + } + + /** + * Save an object in this folder. + * + * @param array $object The array that holds the data of the object. + * @param int $data_version The format handler version. + * @param string $object_type The type of the kolab object. + * @param string $id The IMAP id of the old object if it + * existed before + * @param array $old_object The array that holds the current data of the + * object. + * + * @return boolean True on success. + */ + function saveObject(&$object, $data_version, $object_type, $id = null, + &$old_object = null) + { + // Select folder + $this->_connection->select($this->name); + + $new_headers = new Horde_Mime_Headers(); + $new_headers->setEOL("\r\n"); + + $formats = $this->getFormats(); + + $handlers = array(); + foreach ($formats as $type) { + $handlers[$type] = &Horde_Kolab_Format::factory($type, $object_type, + $data_version); + if (is_a($handlers[$type], 'PEAR_Error')) { + if ($type == 'XML') { + return $handlers[$type]; + } + Horde::logMessage(sprintf('Loading format handler "%s" failed: %s', + $type, $handlers[$type]->getMessage()), 'ERR'); + continue; + } + } + + if ($id != null) { + /** Update an existing kolab object */ + if (!in_array($id, $this->_connection->getUids($this->name))) { + return PEAR::raiseError(sprintf(_("The message with ID %s does not exist. This probably means that the Kolab object has been modified by somebody else while you were editing it. Your edits have been lost."), + $id)); + } + + /** Parse email and load Kolab format structure */ + $result = $this->parseMessage($id, $handlers['XML']->getMimeType(), + true, $formats); + if (is_a($result, 'PEAR_Error')) { + return $result; + } + list($old_message, $part_ids, $mime_message, $mime_headers) = $result; + if (is_a($old_message, 'PEAR_Error')) { + return $old_message; + } + + if (isset($object['_attachments']) && isset($old_object['_attachments'])) { + $attachments = array_keys($object['_attachments']); + foreach (array_keys($old_object['_attachments']) as $attachment) { + if (!in_array($attachment, $attachments)) { + foreach ($mime_message->getParts() as $part) { + if ($part->getName() === $attachment) { + foreach (array_keys($mime_message->_parts) as $key) { + if ($mime_message->_parts[$key]->getMimeId() == $part->getMimeId()) { + unset($mime_message->_parts[$key]); + break; + } + } + $mime_message->_generateIdMap($mime_message->_parts); + } + } + } + } + } + $object = array_merge($old_object, $object); + + if (isset($attachments)) { + foreach ($mime_message->getParts() as $part) { + $name = $part->getName(); + foreach ($attachments as $attachment) { + if ($name === $attachment) { + $object['_attachments'][$attachment]['id'] = $part->getMimeId(); + } + } + } + } + + /** Copy email header */ + if (!empty($mime_headers) && !$mime_headers === false) { + foreach ($mime_headers as $header => $value) { + $new_headers->addheader($header, $value); + } + } + } else { + $mime_message = $this->_prepareNewMessage($new_headers); + $mime_part_id = false; + } + + if (isset($object['_attachments'])) { + $attachments = array_keys($object['_attachments']); + foreach ($attachments as $attachment) { + $data = $object['_attachments'][$attachment]; + + if (!isset($data['content']) && !isset($data['path'])) { + /** + * There no new content and no new path. Do not rewrite the + * attachment. + */ + continue; + } + + $part = new Horde_Mime_Part(); + $part->setType(isset($data['type']) ? $data['type'] : null); + $part->setContents(isset($data['content']) ? $data['content'] : file_get_contents($data['path'])); + $part->setCharset(Horde_Nls::getCharset()); + $part->setTransferEncoding('quoted-printable'); + $part->setDisposition('attachment'); + $part->setName($attachment); + + if (!isset($data['id'])) { + $mime_message->addPart($part); + } else { + $mime_message->alterPart($data['id'], $part); + } + } + } + + foreach ($formats as $type) { + $new_content = $handlers[$type]->save($object); + if (is_a($new_content, 'PEAR_Error')) { + return $new_content; + } + + /** Update mime part */ + $part = new Horde_Mime_Part(); + $part->setType($handlers[$type]->getMimeType()); + $part->setContents($new_content); + $part->setCharset(Horde_Nls::getCharset()); + $part->setTransferEncoding('quoted-printable'); + $part->setDisposition($handlers[$type]->getDisposition()); + $part->setDispositionParameter('x-kolab-type', $type); + $part->setName($handlers[$type]->getName()); + + if (!isset($part_ids) || $part_ids[$type] === false) { + $mime_message->addPart($part); + } else { + $mime_message->alterPart($part_ids[$type], $part); + } + } + + $session = &Horde_Kolab_Session_Singleton::singleton(); + + // Update email headers + $new_headers->addHeader('From', $session->user_mail); + $new_headers->addHeader('To', $session->user_mail); + $new_headers->addHeader('Date', date('r')); + $new_headers->addHeader('X-Kolab-Type', $handlers['XML']->getMimeType()); + $new_headers->addHeader('Subject', $object['uid']); + $new_headers->addHeader('User-Agent', 'Horde::Kolab::Storage v0.2'); + $new_headers->addHeader('MIME-Version', '1.0'); + $mime_message->addMimeHeaders(array('headers' => $new_headers)); + + $msg = $new_headers->toString() . $mime_message->toString(array('canonical' => true, + 'headers' => false)); + + // delete old email? + if ($id != null) { + $this->_connection->deleteMessages($this->name, $id); + } + + // store new email + try { + $result = $this->_connection->appendMessage($this->name, $msg); + } catch (Horde_Kolab_Storage_Exception $e) { + if ($id != null) { + $this->_connection->undeleteMessages($id); + } + } + + // remove deleted object + if ($id != null) { + $this->_connection->expunge($this->name); + } + + try { + $this->trigger(); + } catch (Horde_Kolab_Storage_Exception $e) { + Horde::logMessage(sprintf('Failed triggering folder %s! Error was: %s', + $this->name, $result->getMessage()), 'ERR'); + } + + return true; + } + + /** + * Get an IMAP message and retrieve the Kolab Format object. + * + * @param int $id The message to retrieve. + * @param string $mime_type The mime type of the part to retrieve. + * @param boolean $parse_headers Should the heades be Mime parsed? + * @param array $formats The list of possible format parts. + * + * @return array|PEAR_Error An array that list the Kolab XML + * object text, the mime ID of the part + * with the XML object, the Mime parsed + * message and the Mime parsed headers if + * requested. + */ + function parseMessage($id, $mime_type, $parse_headers = true, + $formats = array('XML')) + { + $raw_headers = $this->_connection->getMessageHeader($this->name, $id); + if (is_a($raw_headers, 'PEAR_Error')) { + return PEAR::raiseError(sprintf(_("Failed retrieving the message with ID %s. Original error: %s."), + $id, $raw_headers->getMessage())); + } + + $body = $this->_connection->getMessageBody($this->name, $id); + if (is_a($body, 'PEAR_Error')) { + return PEAR::raiseError(sprintf(_("Failed retrieving the message with ID %s. Original error: %s."), + $id, $body->getMessage())); + } + + //@todo: not setting "forcemime" means the subparts get checked too. Seems incorrect. + $mime_message = Horde_Mime_Part::parseMessage($raw_headers . "\r" . $body, array('forcemime' => true)); + $parts = $mime_message->contentTypeMap(); + + $mime_headers = false; + $xml = false; + + // Read in a Kolab event object, if one exists + $part_ids['XML'] = array_search($mime_type, $parts); + if ($part_ids['XML'] !== false) { + if ($parse_headers) { + $mime_headers = Horde_Mime_Headers::parseHeaders($raw_headers); + $mime_headers->setEOL("\r\n"); + } + + $part = $mime_message->getPart($part_ids['XML']); + //@todo: Check what happened to this call + //$part->transferDecodeContents(); + $xml = $part->getContents(); + } + + $alternate_formats = array_diff(array('XML'), $formats); + if (!empty($alternate_formats)) { + foreach ($alternate_formats as $type) { + $part_ids[$type] = false; + } + foreach ($mime_message->getParts() as $part) { + $params = $part->getDispositionParameters(); + foreach ($alternate_formats as $type) { + if (isset($params['x-kolab-format']) + && $params['x-kolab-format'] == $type) { + $part_ids[$type] = $part->getMimeId(); + } + } + } + } + + $result = array($xml, $part_ids, $mime_message, $mime_headers); + return $result; + } + + /** + * Prepares a new kolab Groupeware message. + * + * @return string The Mime message + */ + function _prepareNewMessage() + { + $mime_message = new Horde_Mime_Part(); + $mime_message->setName('Kolab Groupware Data'); + $mime_message->setType('multipart/mixed'); + $kolab_text = sprintf(_("This is a Kolab Groupware object. To view this object you will need an email client that understands the Kolab Groupware format. For a list of such email clients please visit %s"), + 'http://www.kolab.org/kolab2-clients.html'); + $part = new Horde_Mime_Part(); + $part->setType('text/plain'); + $part->setName('Kolab Groupware Information'); + $part->setContents(Horde_String::wrap($kolab_text, 76, "\r\n", Horde_Nls::getCharset())); + $part->setCharset(Horde_Nls::getCharset()); + + $part->setTransferEncoding('quoted-printable'); + $mime_message->addPart($part); + return $mime_message; + } + + /** + * Report the status of this folder. + * + * @return array|PEAR_Error An array listing the validity ID, the + * next IMAP ID and an array of IMAP IDs. + */ + function getStatus() + { + // Select the folder to update uidnext + $this->_connection->select($this->name); + + $status = $this->_connection->status($this->name); + $uids = $this->_connection->getUids($this->name); + return array($status['uidvalidity'], $status['uidnext'], $uids); + } + + /** + * Triggers any required updates after changes within the + * folder. This is currently only required for handling free/busy + * information with Kolab. + * + * @param string $name Name of the folder that should be triggered. + * + * @return boolean|PEAR_Error True if successfull. + */ + function trigger($name = null) + { + $type = $this->getType(); + if (is_a($type, 'PEAR_Error')) { + return $type; + } + + $owner = $this->getOwner(); + if (is_a($owner, 'PEAR_Error')) { + return $owner; + } + + $subpath = $this->getSubpath($name); + if (is_a($subpath, 'PEAR_Error')) { + return $subpath; + } + + switch($type) { + case 'event': + $session = &Horde_Kolab_Session_Singleton::singleton(); + $url = sprintf('%s/trigger/%s/%s.pfb', + $session->freebusy_server, $owner, $subpath); + break; + default: + return true; + } + + $result = $this->triggerUrl($url); + if (is_a($result, 'PEAR_Error')) { + return PEAR::raiseError(sprintf(_("Failed triggering folder %s. Error was: %s"), + $this->name, $result->getMessage())); + } + return $result; + } + + /** + * Triggers a URL. + * + * @param string $url The URL to be triggered. + * + * @return boolean|PEAR_Error True if successfull. + */ + function triggerUrl($url) + { + global $conf; + + if (!empty($conf['kolab']['no_triggering'])) { + return true; + } + + $options['method'] = 'GET'; + $options['timeout'] = 5; + $options['allowRedirects'] = true; + + if (isset($conf['http']['proxy']) && !empty($conf['http']['proxy']['proxy_host'])) { + $options = array_merge($options, $conf['http']['proxy']); + } + + require_once 'HTTP/Request.php'; + $http = new HTTP_Request($url, $options); + $http->setBasicAuth(Horde_Auth::getAuth(), Horde_Auth::getCredential('password')); + @$http->sendRequest(); + if ($http->getResponseCode() != 200) { + return PEAR::raiseError(sprintf(_("Unable to trigger URL %s. Response: %s"), + $url, $http->getResponseCode())); + } + return true; + } + + /** + * Checks to see if a user has a given permission. + * + * @param string $userid The userid of the user. + * @param integer $permission A Horde_Perms::* constant to test for. + * @param string $creator The creator of the shared object. + * + * @return boolean|PEAR_Error Whether or not $userid has $permission. + */ + function hasPermission($userid, $permission, $creator = null) + { + if ($userid == $this->getOwner()) { + return true; + } + + $perm = &$this->getPermission(); + if (is_a($perm, 'PEAR_Error')) { + return $perm; + } + return $perm->hasPermission($userid, $permission, $creator); + } + + /** + * Returns the permissions from this storage object. + * + * @return Horde_Permission_Kolab The permissions on the share. + */ + function &getPermission() + { + if (!isset($this->_perms)) { + if ($this->exists()) { + // The permissions are unknown but the folder exists + // -> discover permissions + $perms = null; + } else { + $perms = array( + 'users' => array( + Horde_Auth::getAuth() => Horde_Perms::ALL + ) + ); + } + $this->_perms = new Horde_Kolab_Storage_Permission($this, $perms); + } + return $this->_perms; + } + + /** + * Sets the permissions on the share. + * + * @param Horde_Permission_Kolab $perms Permission object to store on the + * object. + * @param boolean $update Save the updated information? + * + * @return boolean|PEAR_Error True on success. + */ + function setPermission(&$perms, $update = true) + { + if (!is_a($perms, 'Horde_Perms_Permission')) { + return PEAR::raiseError('The permissions for this share must be specified as an instance of the Horde_Perms_Permission class!'); + } + + if (!is_a($perms, 'Horde_Kolab_Storage_Permission')) { + $this->_perms = new Horde_Kolab_Storage_Permission($this, $perms->data); + } else { + $this->_perms = $perms; + $this->_perms->setFolder($this); + } + + if ($update) { + return $this->save(); + } + + return true; + } + + /** + * Return the IMAP ACL of this folder. + * + * @return array|PEAR_Error An array with IMAP ACL. + */ + function getACL() + { + return $this->_connection->getACL($this->name); + } + + /** + * Set the IMAP ACL of this folder. + * + * @param $user The user for whom the ACL should be set. + * @param $acl The new ACL value. + * + * @return boolean|PEAR_Error True on success. + */ + function setACL($user, $acl) + { + $this->_connection->setACL($this->name, $user, $acl); + + if (!empty($this->_perms)) { + /** Refresh the cache after changing the permissions */ + $this->_perms->getPerm(); + } + + $result = $this->trigger(); + if (is_a($result, 'PEAR_Error')) { + Horde::logMessage(sprintf('Failed triggering folder %s! Error was: %s', + $this->name, $result->getMessage()), 'ERR'); + } + + return $result; + } + + /** + * Delete the IMAP ACL for a user on this folder. + * + * @param $user The user for whom the ACL should be deleted. + * + * @return boolean|PEAR_Error True on success. + */ + function deleteACL($user) + { + global $conf; + + if (!empty($conf['kolab']['imap']['no_acl'])) { + return true; + } + + $this->_connection->deleteACL($this->name, $user); + + $result = $this->trigger(); + if (is_a($result, 'PEAR_Error')) { + Horde::logMessage(sprintf('Failed triggering folder %s! Error was: %s', + $this->name, $result->getMessage()), 'ERR'); + } + + return $iresult; + } + + + /** + * Get annotation values on IMAP server that do not support + * METADATA. + * + * @return array|PEAR_Error The anotations of this folder. + */ + function _getAnnotationData() + { + $this->_annotation_data = $this->getData('annotation'); + } + + + /** + * Get an annotation value of this folder. + * + * @param $key The key of the annotation to retrieve. + * + * @return string|PEAR_Error The anotation value. + */ + function _getAnnotation($key) + { + global $conf; + + if (empty($conf['kolab']['imap']['no_annotations'])) { + return $this->_connection->getAnnotation($key, $this->name); + } + + if (!isset($this->_annotation_data)) { + $this->_getAnnotationData(); + } + $data = $this->_annotation_data->getObject('KOLAB_FOLDER_CONFIGURATION'); + if (is_a($data, 'PEAR_Error')) { + Horde::logMessage(sprintf('Error retrieving annotation data on folder %s: %s', + $this->name, $data->getMessage()), 'ERR'); + return ''; + } + if (isset($data[$key])) { + return $data[$key]; + } else { + return ''; + } + } + + /** + * Set an annotation value of this folder. + * + * @param $key The key of the annotation to change. + * @param $value The new value. + * + * @return boolean|PEAR_Error True on success. + */ + function _setAnnotation($key, $value) + { + if (empty($conf['kolab']['imap']['no_annotations'])) { + return $this->_connection->setAnnotation($key, $value, $this->name); + } + + if (!isset($this->_annotation_data)) { + $this->_getAnnotationData(); + } + $data = $this->_annotation_data->getObject('KOLAB_FOLDER_CONFIGURATION'); + if (is_a($data, 'PEAR_Error')) { + Horde::logMessage(sprintf('Error retrieving annotation data on folder %s: %s', + $this->name, $data->getMessage()), 'ERR'); + $data = array(); + $uid = null; + } else { + $uid = 'KOLAB_FOLDER_CONFIGURATION'; + } + $data[$key] = $value; + $data['uid'] = 'KOLAB_FOLDER_CONFIGURATION'; + return $this->_annotation_data->save($data, $uid); + } + + + + /** + * Get the free/busy relevance for this folder + * + * @return int Value containing the FB_RELEVANCE. + */ + function getFbrelevance() + { + $result = $this->getKolabAttribute('incidences-for'); + if (is_a($result, 'PEAR_Error') || empty($result)) { + return KOLAB_FBRELEVANCE_ADMINS; + } + switch ($result) { + case 'admins': + return KOLAB_FBRELEVANCE_ADMINS; + case 'readers': + return KOLAB_FBRELEVANCE_READERS; + case 'nobody': + return KOLAB_FBRELEVANCE_NOBODY; + default: + return KOLAB_FBRELEVANCE_ADMINS; + } + } + + /** + * Set the free/busy relevance for this folder + * + * @param int $relevance Value containing the FB_RELEVANCE + * + * @return mixed True on success or a PEAR_Error. + */ + function setFbrelevance($relevance) + { + switch ($relevance) { + case KOLAB_FBRELEVANCE_ADMINS: + $value = 'admins'; + break; + case KOLAB_FBRELEVANCE_READERS: + $value = 'readers'; + break; + case KOLAB_FBRELEVANCE_NOBODY: + $value = 'nobody'; + break; + default: + $value = 'admins'; + } + + return $this->_setAnnotation(KOLAB_ANNOT_ROOT . 'incidences-for', + $value); + } + + /** + * Get the extended free/busy access settings for this folder + * + * @return array Array containing the users with access to the + * extended information. + */ + function getXfbaccess() + { + $result = $this->getKolabAttribute('pxfb-readable-for'); + if (is_a($result, 'PEAR_Error') || empty($result)) { + return array(); + } + return explode(' ', $result); + } + + /** + * Set the extended free/busy access settings for this folder + * + * @param array $access Array containing the users with access to the + * extended information. + * + * @return mixed True on success or a PEAR_Error. + */ + function setXfbaccess($access) + { + $value = join(' ', $access); + return $this->_setAnnotation(KOLAB_ANNOT_ROOT . 'pxfb-readable-for', + $value); + } +} diff --git a/framework/Kolab_Storage/package.xml b/framework/Kolab_Storage/package.xml index 29013a09d..d0df99788 100644 --- a/framework/Kolab_Storage/package.xml +++ b/framework/Kolab_Storage/package.xml @@ -81,6 +81,9 @@ + + + @@ -202,6 +205,7 @@ + diff --git a/framework/Kolab_Storage/test/Horde/Kolab/Storage/FolderTest.php b/framework/Kolab_Storage/test/Horde/Kolab/Storage/FolderTest.php index 1df2fa86e..32531fb46 100644 --- a/framework/Kolab_Storage/test/Horde/Kolab/Storage/FolderTest.php +++ b/framework/Kolab_Storage/test/Horde/Kolab/Storage/FolderTest.php @@ -64,7 +64,7 @@ class Horde_Kolab_Storage_FolderTest extends Horde_Kolab_Storage_Scenario public function testConstruct() { $GLOBALS['language'] = 'de_DE'; - $folder = new Horde_Kolab_Storage_Folder( + $folder = new Horde_Kolab_Storage_Folder_Base( 'INBOX/Contacts', new Horde_Kolab_Storage_Namespace_Fixed() ); @@ -85,7 +85,7 @@ class Horde_Kolab_Storage_FolderTest extends Horde_Kolab_Storage_Scenario $connection->expects($this->once()) ->method('getNamespace') ->will($this->returnValue(new Horde_Kolab_Storage_Namespace_Fixed())); - $folder = new Horde_Kolab_Storage_Folder('INBOX/Contacts'); + $folder = new Horde_Kolab_Storage_Folder_Base('INBOX/Contacts'); $folder->restore($storage, $connection); $folder->setName('TestAÖÜ'); $this->assertEquals(Horde_String::convertCharset('INBOX/TestAÖÜ', Horde_Nls::getCharset(), 'UTF7-IMAP'), $folder->new_name); diff --git a/framework/Kolab_Storage/test/Horde/Kolab/Storage/NamespaceTest.php b/framework/Kolab_Storage/test/Horde/Kolab/Storage/NamespaceTest.php index ca0ffeda2..f408733b8 100644 --- a/framework/Kolab_Storage/test/Horde/Kolab/Storage/NamespaceTest.php +++ b/framework/Kolab_Storage/test/Horde/Kolab/Storage/NamespaceTest.php @@ -248,7 +248,7 @@ class Horde_Kolab_Storage_NamespaceTest extends PHPUnit_Framework_TestCase $this->_connection->expects($this->any()) ->method('getNamespace') ->will($this->returnValue($namespace)); - $folder = new Horde_Kolab_Storage_Folder($name); + $folder = new Horde_Kolab_Storage_Folder_Base($name); $folder->restore($this->_storage, $this->_connection); return $folder; } diff --git a/framework/Kolab_Storage/test/Horde/Kolab/Storage/PermsTest.php b/framework/Kolab_Storage/test/Horde/Kolab/Storage/PermsTest.php index 2699610a5..f5154aca5 100644 --- a/framework/Kolab_Storage/test/Horde/Kolab/Storage/PermsTest.php +++ b/framework/Kolab_Storage/test/Horde/Kolab/Storage/PermsTest.php @@ -147,7 +147,7 @@ class Horde_Kolab_Storage_PermsTest extends PHPUnit_Framework_TestCase * @license http://www.fsf.org/copyleft/lgpl.html LGPL * @link http://pear.horde.org/index.php?package=Kolab_Storage */ -class DummyFolder extends Horde_Kolab_Storage_Folder +class DummyFolder extends Horde_Kolab_Storage_Folder_Base { var $acl; var $_owner; -- 2.11.0