diff --git a/framework/Kolab_Cli/lib/Horde/Kolab/Cli/Module/Data.php b/framework/Kolab_Cli/lib/Horde/Kolab/Cli/Module/Data.php index d9eef93dd2e..e2838e55d11 100644 --- a/framework/Kolab_Cli/lib/Horde/Kolab/Cli/Module/Data.php +++ b/framework/Kolab_Cli/lib/Horde/Kolab/Cli/Module/Data.php @@ -141,6 +141,11 @@ public function run($cli, $options, $arguments, &$world) (string) $world['storage']->getData($folder_name)->getStamp() ); break; + case 'complete': + $data = $world['storage']->getData($folder_name); + $complete = $data->fetchComplete($arguments[3]); + $cli->writeln($complete[1]->toString(array('headers' => $complete[0]))); + break; case 'part': $data = $world['storage']->getData($folder_name); $part = $data->fetchPart($arguments[3], $arguments[4]); diff --git a/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Data/Base.php b/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Data/Base.php index 0e50a023a6a..87018dbb908 100644 --- a/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Data/Base.php +++ b/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Data/Base.php @@ -190,13 +190,79 @@ public function create($object, $raw = false) ); } + /** + * Modify an existing object. + * + * @param array $object The array that holds the updated object data. + * @param boolean $raw True if the data to be stored has been provided in + * raw format. + * + * @return NULL + * + * @throws Horde_Kolab_Storage_Exception In case an error occured while + * saving the data. + */ + public function modify($object, $raw = false) + { + if (!isset($object['uid'])) { + throw new Horde_Kolab_Storage_Exception( + 'The provided object data contains no ID value!' + ); + } + try { + $obid = $this->getBackendId($object['uid']); + } catch (Horde_Kolab_Storage_Exception $e) { + throw new Horde_Kolab_Storage_Exception( + sprintf( + Horde_Kolab_Storage_Translation::t( + 'The message with ID %s does not exist. This probably means that the Kolab object has been modified by somebody else since you retrieved the object from the backend. Original error: %s' + ), + $object['uid'], + 0, + $e + ) + ); + } + $this->_driver->getParser() + ->modify( + $this->_folder->getPath(), + $object, + $obid, + array( + 'type' => $this->getType(), + 'version' => $this->_version, + 'raw' => $raw + ) + ); + } + + /** + * Retrieves the complete message for the given UID. + * + * @param string $uid The message UID. + * + * @return array The message encapsuled as an array that contains a + * Horde_Mime_Headers and a Horde_Mime_Part object. + */ + public function fetchComplete($uid) + { + if (!method_exists($this->_driver, 'fetchComplete')) { + throw new Horde_Kolab_Storage_Exception( + 'The backend does not support the "fetchComplete" method!' + ); + } + return $this->_driver->fetchComplete( + $this->_folder->getPath(), $uid + ); + } + /** * Retrieves the body part for the given UID and mime part ID. * * @param string $uid The message UID. * @param string $id The mime part ID. * - * @return @TODO + * @return resource The message part as stream resource. */ public function fetchPart($uid, $id) { diff --git a/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Data/Format/Mime.php b/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Data/Format/Mime.php index f66dea28d3e..65d8a770408 100644 --- a/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Data/Format/Mime.php +++ b/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Data/Format/Mime.php @@ -238,4 +238,26 @@ public function createKolabPart($object, array $options) return $kolab; } + /** + * Modify a Kolab groupware object. + * + * @param Horde_Kolab_Storage_Driver_Modifiable $modifiable The modifiable object. + * @param array $object The updated object. + * @param array $options Additional options. + * + * @return NULL + */ + public function modify( + Horde_Kolab_Storage_Data_Modifiable $modifiable, + $object, + array $options + ) { + $mime_id = $this->matchMimeId( + $options['type'], $modifiable->getStructure()->contentTypeMap() + ); + $modifiable->setPart( + $mime_id, $this->createKolabPart($object, $options) + ); + $modifiable->store(); + } } diff --git a/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Data/Modifiable.php b/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Data/Modifiable.php new file mode 100644 index 00000000000..7dd1fe73058 --- /dev/null +++ b/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Data/Modifiable.php @@ -0,0 +1,69 @@ + + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_Storage + */ + +/** + * A modifiable message object. + * + * Copyright 2011 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_Storage + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_Storage + */ +class Horde_Kolab_Storage_Data_Modifiable +{ + /** + * Constructor. + * + * @param Horde_Kolab_Storage_Driver $driver The backend driver. + * @param string $folder The folder this object belongs to. + * @param array $object The MIME parsed message elements. + */ + public function __construct($driver, $folder, $object) + { + $this->_driver = $driver; + $this->_folder = $folder; + $this->_object = $object; + } + + public function getStructure() + { + return $this->_object[1]; + } + + public function setPart($mime_id, $new_part) + { + $this->_object[1]->getPart(0)->setContents(''); + $this->_object[1]->alterPart($mime_id, $new_part); + $this->_object[1]->buildMimeIds(); + } + + public function store() + { + return $this->_driver->appendMessage( + $this->_folder, + $this->_object[1]->toString( + array( + 'canonical' => true, + 'stream' => true, + 'headers' => $this->_object[0] + ) + ) + ); + } +} \ No newline at end of file diff --git a/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Data/Parser/Structure.php b/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Data/Parser/Structure.php index 91d290a818a..7855b440cf4 100644 --- a/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Data/Parser/Structure.php +++ b/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Data/Parser/Structure.php @@ -111,6 +111,9 @@ public function fetch($folder, $obids, $options = array()) } //@todo: deal with exceptions $objects[$obid] = $this->getFormat()->parse($folder, $obid, $structure['structure'], $options); + if ($this->_driver->hasCatenateSupport()) { + $objects[$obid]['__structure'] = $structure['structure']; + } $this->_fetchAttachments($objects[$obid], $folder, $obid, $options); } return $objects; @@ -182,16 +185,42 @@ private function _completeOptions(&$options) * format. * * - * @return NULL + * @return string The ID of the new object or true in case the backend does + * not support this return value. */ public function create($folder, $object, $options = array()) { - $this->_driver->appendMessage( + return $this->_driver->appendMessage( $folder, $this->createObject($object, $options) ); } + /** + * Modify an existing object in the specified folder. + * + * @param string $folder The folder to use. + * @param array $object The object. + * @param string $obid The object ID in the backend. + * @param array $options Additional options for storing. + *
+     *  'type'    - Required argument specifying the object type that should be
+     *              stored.
+     *  'version' - Optional argument specifying the version of the object
+     *              format.
+     * 
+ * + * @return string The ID of the modified object or true in case the backend + * does not support this return value. + */ + public function modify($folder, $object, $obid, $options = array()) + { + $modifiable = $this->_driver->getModifiable($folder, $obid, $object); + $new_uid = $this->_format->modify($modifiable, $object, $options); + $this->_driver->deleteMessages($folder, array($obid)); + return $new_uid; + } + /** * Create a new MIME representation for the object. * diff --git a/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver.php b/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver.php index 4d57779a539..ae0ecce390c 100644 --- a/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver.php +++ b/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver.php @@ -92,6 +92,13 @@ public function getId(); */ public function getParameters(); + /** + * Checks if the backend supports CATENATE. + * + * @return boolean True if the backend supports CATENATE. + */ + public function hasCatenateSupport(); + /** List functionality */ diff --git a/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver/Base.php b/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver/Base.php index abad986b275..4d7621769a1 100644 --- a/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver/Base.php +++ b/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver/Base.php @@ -278,6 +278,32 @@ protected function decodeListKeys(array $list) return $result; } + /** + * Checks if the backend supports CATENATE. + * + * @return boolean True if the backend supports CATENATE. + */ + public function hasCatenateSupport() + { + return false; + } + + /** + * Return a modifiable message object. + * + * @param string $folder The folder to access. + * @param string $obid The backend ID of the object to retrieve from the folder. + * @param array $object The object data. + * + * @return Horde_Kolab_Storage_Driver_Modifiable The modifiable message object. + */ + public function getModifiable($folder, $obid, $object) + { + return new Horde_Kolab_Storage_Data_Modifiable( + $this, $folder, $this->fetchComplete($folder, $obid) + ); + } + /** * Retrieve the namespace information for this connection. * diff --git a/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver/Cclient.php b/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver/Cclient.php index 9bcdcc99619..855d82063a5 100644 --- a/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver/Cclient.php +++ b/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver/Cclient.php @@ -563,6 +563,41 @@ public function getUids($folder) return $uids; } + /** + * Retrieves a complete message. + * + * @param string $folder The folder to fetch the messages from. + * @param array $uid The message UID. + * + * @return array The message encapsuled as an array that contains a + * Horde_Mime_Headers and a Horde_Mime_Part object. + */ + public function fetchComplete($folder, $uid) + { + $this->select($folder); + + $headers = @imap_fetchheader($this->getBackend(), $uid, FT_UID | FT_PREFETCHTEXT); + $body = @imap_body($this->getBackend(), $uid, FT_UID); + if ($headers && $body) { + return array( + Horde_Mime_Headers::parseHeaders($headers), + Horde_Mime_Part::parseMessage($body) + ); + } else { + throw new Horde_Kolab_Storage_Exception( + sprintf( + Horde_Kolab_Storage_Translation::t( + "Failed fetching message %s in folder %s%s. Error: %s" + ), + $this->_getBaseMbox(), + $uid, + $folder, + imap_last_error() + ) + ); + } + } + /** * Retrieves the messages for the given message ids. * diff --git a/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver/Imap.php b/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver/Imap.php index 5d8a23ee650..5ae29c2f828 100644 --- a/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver/Imap.php +++ b/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver/Imap.php @@ -308,6 +308,33 @@ public function getUids($folder) return $uids->ids; } + /** + * Retrieves a complete message. + * + * @param string $folder The folder to fetch the messages from. + * @param array $uid The message UID. + * + * @return array The message encapsuled as an array that contains a + * Horde_Mime_Headers and a Horde_Mime_Part object. + */ + public function fetchComplete($folder, $uid) + { + $query = new Horde_Imap_Client_Fetch_Query(); + $query->headerText(); + $query->bodyText(); + + $ret = $this->getBackend()->fetch( + $folder, + $query, + array('ids' => new Horde_Imap_Client_Ids($uid)) + ); + + return array( + $ret[$uid]->getHeaderText(0, Horde_Imap_Client_Data_Fetch::HEADER_PARSE), + Horde_Mime_Part::parseMessage($ret[$uid]->getBodyText()) + ); + } + /** * Retrieves the messages for the given message ids. * diff --git a/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver/Mock.php b/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver/Mock.php index a01dac4296f..75a9cddd7c3 100644 --- a/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver/Mock.php +++ b/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver/Mock.php @@ -621,6 +621,23 @@ public function fetchStructure($folder, $uids) ); } + /** + * Retrieves a complete message. + * + * @param string $folder The folder to fetch the messages from. + * @param array $uid The message UID. + * + * @return array The message encapsuled as an array that contains a + * Horde_Mime_Headers and a Horde_Mime_Part object. + */ + public function fetchComplete($folder, $uid) + { + return $this->_data->fetchComplete( + $this->_convertToInternal($folder), + $uid + ); + } + /** * Retrieves a bodypart for the given message ID and mime part ID. * diff --git a/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver/Mock/Data.php b/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver/Mock/Data.php index b40ce8fa4de..2fba40b72e7 100644 --- a/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver/Mock/Data.php +++ b/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver/Mock/Data.php @@ -207,6 +207,27 @@ public function _notDeleted($message) || !($message['flags'] & self::FLAG_DELETED); } + public function fetchComplete($folder, $uid) + { + $this->select($folder); + if (isset($this->_selected['mails'][$uid]['stream'])) { + rewind($this->_selected['mails'][$uid]['stream']); + $msg = stream_get_contents($this->_selected['mails'][$uid]['stream']); + return array( + Horde_Mime_Headers::parseHeaders($msg), + Horde_Mime_Part::parseMessage($msg) + ); + } else { + throw new Horde_Kolab_Storage_Exception( + sprintf( + 'No message %s in folder %s!', + $uid, + $folder + ) + ); + } + } + /** * Retrieves the messages for the given message ids. * diff --git a/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver/Pear.php b/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver/Pear.php index 4163578e7cd..9e6cc1d11ef 100644 --- a/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver/Pear.php +++ b/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver/Pear.php @@ -342,6 +342,32 @@ public function getUids($folder) return $uids; } + /** + * Retrieves a complete message. + * + * @param string $folder The folder to fetch the messages from. + * @param array $uid The message UID. + * + * @return array The message encapsuled as an array that contains a + * Horde_Mime_Headers and a Horde_Mime_Part object. + */ + public function fetchComplete($folder, $uid) + { + $this->select($folder); + return array( + Horde_Mime_Headers::parseHeaders( + Horde_Kolab_Storage_Exception_Pear::catchError( + $this->getBackend()->getRawHeaders($uid, '', true) + ) + ), + Horde_Mime_Part::parseMessage( + Horde_Kolab_Storage_Exception_Pear::catchError( + $this->getBackend()->getBody($uid, true) + ) + ) + ); + } + /** * Retrieves the messages for the given message ids. * diff --git a/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver/Rcube.php b/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver/Rcube.php index 8c39b4b6478..1a2e64494db 100644 --- a/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver/Rcube.php +++ b/framework/Kolab_Storage/lib/Horde/Kolab/Storage/Driver/Rcube.php @@ -477,6 +477,41 @@ public function getUids($folder) return $uids; } + /** + * Retrieves a complete message. + * + * @param string $folder The folder to fetch the messages from. + * @param array $uid The message UID. + * + * @return array The message encapsuled as an array that contains a + * Horde_Mime_Headers and a Horde_Mime_Part object. + */ + public function fetchComplete($folder, $uid) + { + $msg = $this->getBackend()->handlePartBody( + $this->encodePath($folder), $uid, true, '', null, false + ); + if ($this->getBackend()->errornum != 0) { + throw new Horde_Kolab_Storage_Exception( + sprintf( + Horde_Kolab_Storage_Translation::t( + "Failed retrieving message %s in folder %s. Error: %s" + ), + $uid, + $folder, + $this->getBackend()->error + ) + ); + } + + return array( + Horde_Mime_Headers::parseHeaders($msg), + Horde_Mime_Part::parseMessage( + Horde_Mime_Part::getRawPartText($msg, 'body', 0) + ) + ); + } + /** * Retrieves the messages for the given message ids. * diff --git a/framework/Kolab_Storage/package.xml b/framework/Kolab_Storage/package.xml index 530cbb24a75..a6b2fc55707 100644 --- a/framework/Kolab_Storage/package.xml +++ b/framework/Kolab_Storage/package.xml @@ -1,5 +1,5 @@ - + Horde_Kolab_Storage pear.horde.org Kolab_Storage @@ -23,8 +23,8 @@ jan@horde.org yes - 2011-03-08 - + 2011-03-14 + 1.0.0beta1 1.0.0 @@ -78,6 +78,7 @@ + @@ -533,7 +534,6 @@ - @@ -645,6 +645,7 @@ + @@ -786,7 +787,6 @@ - @@ -924,5 +924,20 @@ * First alpha release for Horde 4. + + + 1.0.0beta1 + 1.0.0 + + + beta + beta + + 2011-03-14 + LGPL + +* + + diff --git a/framework/Kolab_Storage/test/Horde/Kolab/Storage/Unit/Data/BaseTest.php b/framework/Kolab_Storage/test/Horde/Kolab/Storage/Unit/Data/BaseTest.php index 83b94482119..428acd2f2f5 100644 --- a/framework/Kolab_Storage/test/Horde/Kolab/Storage/Unit/Data/BaseTest.php +++ b/framework/Kolab_Storage/test/Horde/Kolab/Storage/Unit/Data/BaseTest.php @@ -342,4 +342,35 @@ public function testMoveObject() ); } + /** + * @expectedException Horde_Kolab_Storage_Exception + */ + public function testModifyWithoutUid() + { + $store = $this->getMessageStorage(); + $data = $store->getData('INBOX/Notes'); + $data->create(array('desc' => 'test', 'uid' => 'UID')); + $data->modify(array('desc' => 'test')); + } + + /** + * @expectedException Horde_Kolab_Storage_Exception + */ + public function testModifyWithIncorrectUid() + { + $store = $this->getMessageStorage(); + $data = $store->getData('INBOX/Notes'); + $data->create(array('desc' => 'test', 'uid' => 'UID')); + $data->modify(array('desc' => 'test', 'uid' => 'NOSUCHUID')); + } + + public function testModify() + { + $store = $this->getMessageStorage(); + $data = $store->getData('INBOX/Notes'); + $data->create(array('desc' => 'test', 'uid' => 'UID')); + $data->modify(array('desc' => 'modified', 'uid' => 'UID')); + $object = $data->getObject('UID'); + $this->assertEquals('modified', $object['desc']); + } }