From 80fa73b895c84bc49768d80e80f3d8461b6623e9 Mon Sep 17 00:00:00 2001 From: Thomas B Date: Thu, 8 Mar 2012 10:21:21 +0100 Subject: [PATCH] Adapt date/time handling to recent changes in libkolabxml; forward attachment parts when saving a Kolab object in a new message --- .../lib/rcube_kolab_contacts.php | 12 +++- plugins/libkolab/lib/kolab_format.php | 45 ++++++++++++-- plugins/libkolab/lib/kolab_format_contact.php | 24 ++++---- plugins/libkolab/lib/kolab_storage_folder.php | 60 +++++++++++++++---- 4 files changed, 111 insertions(+), 30 deletions(-) diff --git a/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php b/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php index 08ba20a8..19bea06c 100644 --- a/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php +++ b/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php @@ -46,7 +46,7 @@ class rcube_kolab_contacts extends rcube_addressbook 'department' => array('limit' => 1), 'email' => array('subtypes' => null), 'phone' => array(), - 'address' => array('subtypes' => array('home','business')), + 'address' => array('subtypes' => array('home','work')), 'officelocation' => array('type' => 'text', 'size' => 40, 'maxlength' => 50, 'limit' => 1, 'label' => 'kolab_addressbook.officelocation', 'category' => 'main'), 'website' => array('subtypes' => null), @@ -1046,8 +1046,10 @@ class rcube_kolab_contacts extends rcube_addressbook } // photo is stored as separate attachment - if ($record['photo'] && ($att = $record['_attachments'][$record['photo']])) { - $out['photo'] = $att['content'] ? $att['content'] : $this->storagefolder->get_attachment($record['uid'], $att['key']); + if ($record['photo'] && strlen($record['photo']) < 255 && ($att = $record['_attachments'][$record['photo']])) { + // only fetch photo content if requested + if ($rcmail = rcmail::get_instance()->action == 'photo') + $record['photo'] = $att['content'] ? $att['content'] : $this->storagefolder->get_attachment($record['uid'], $att['key']); } // remove empty fields @@ -1112,6 +1114,10 @@ class rcube_kolab_contacts extends rcube_addressbook ); $contact['photo'] = $attkey; } + else if (isset($contact['photo']) && empty($contact['photo'])) { + // unset photo attachment + $contact['_attachments']['photo.attachment'] = false; + } return $contact; } diff --git a/plugins/libkolab/lib/kolab_format.php b/plugins/libkolab/lib/kolab_format.php index 928ee2f6..bcad59e7 100644 --- a/plugins/libkolab/lib/kolab_format.php +++ b/plugins/libkolab/lib/kolab_format.php @@ -53,25 +53,25 @@ abstract class kolab_format } /** - * Convert the given date/time value into a c_DateTime object + * Convert the given date/time value into a cDateTime object * * @param mixed Date/Time value either as unix timestamp, date string or PHP DateTime object * @param DateTimeZone The timezone the date/time is in. Use global default if empty * @param boolean True of the given date has no time component - * @return c_DateTime The libkolabxml date/time object or null on error + * @return object The libkolabxml date/time object or null on error */ - public static function getDateTime($datetime, $tz = null, $dateonly = false) + public static function get_datetime($datetime, $tz = null, $dateonly = false) { if (!$tz) $tz = self::$timezone; $result = null; if (is_numeric($datetime)) $datetime = new DateTime('@'.$datetime, $tz); - else if (is_string($datetime)) + else if (is_string($datetime) && strlen($datetime)) $datetime = new DateTime($datetime, $tz); if (is_a($datetime, 'DateTime')) { - $result = new KolabDateTime(); + $result = new cDateTime(); $result->setDate($datetime->format('Y'), $datetime->format('n'), $datetime->format('j')); if (!$dateonly) @@ -83,6 +83,41 @@ abstract class kolab_format return $result; } + /** + * Convert the given cDateTime into a PHP DateTime object + * + * @param object cDateTime The libkolabxml datetime object + * @return object DateTime PHP datetime instance + */ + public static function php_datetime($cdt) + { + if (!is_object($cdt) || !$cdt->isValid()) + return null; + + $d = new DateTime; + $d->setTimezone(self::$timezone); + + try { + if ($tzs = $cdt->timezone()) { + $tz = new DateTimeZone($tzs); + $d->setTimezone($tz); + } + } + catch (Exception $e) { } + + $d->setDate($cdt->year(), $cdt->month(), $cdt->day()); + + if ($cdt->isDateOnly()) { + $d->_dateonly = true; + $d->setTime(12, 0, 0); // set time to noon to avoid timezone troubles + } + else { + $d->setTime($cdt->hour(), $cdt->minute(), $cdt->second()); + } + + return $d; + } + /** * Convert a libkolabxml vector to a PHP array * diff --git a/plugins/libkolab/lib/kolab_format_contact.php b/plugins/libkolab/lib/kolab_format_contact.php index 4dbcaf8f..8ff60879 100644 --- a/plugins/libkolab/lib/kolab_format_contact.php +++ b/plugins/libkolab/lib/kolab_format_contact.php @@ -55,6 +55,7 @@ class kolab_format_contact extends kolab_format 'body' => 'notes', 'pgp-publickey' => 'pgppublickey', 'free-busy-url' => 'freebusyurl', + 'picture' => 'photo', ); private $kolab2_phonetypes = array( 'home1' => 'home', @@ -114,7 +115,7 @@ class kolab_format_contact extends kolab_format if (false && !$this->obj->created()) { if (!empty($object['created'])) $object['created'] = new DateTime('now', self::$timezone); - $this->obj->setCreated(self::getDateTime($object['created'])); + $this->obj->setCreated(self::get_datetime($object['created'])); } // do the hard work of setting object values @@ -196,10 +197,10 @@ class kolab_format_contact extends kolab_format $this->obj->setNote($object['notes']); if ($object['freebusyurl']) $this->obj->setFreeBusyUrl($object['freebusyurl']); -// if ($object['birthday']) -// $this->obj->setBDay(self::getDateTime($object['birthday'], null, true)); -// if ($object['anniversary']) -// $this->obj->setAnniversary(self::getDateTime($object['anniversary'], null, true)); + if ($object['birthday']) + $this->obj->setBDay(self::get_datetime($object['birthday'], null, true)); + if ($object['anniversary']) + $this->obj->setAnniversary(self::get_datetime($object['anniversary'], null, true)); // handle spouse, children, profession, initials, pgppublickey, etc. @@ -281,7 +282,13 @@ class kolab_format_contact extends kolab_format $object['notes'] = $this->obj->note(); $object['freebusyurl'] = $this->obj->freeBusyUrl(); - + + if ($bday = self::php_datetime($this->obj->bDay())) + $object['birthday'] = $bday->format('c'); + + if ($anniversary = self::php_datetime($this->obj->anniversary())) + $object['anniversary'] = $anniversary->format('c'); + if ($g = $this->obj->gender()) $object['gender'] = $g == Contact::Female ? 'female' : 'male'; @@ -327,11 +334,6 @@ class kolab_format_contact extends kolab_format } } - // photo is stored as separate attachment - if ($record['picture'] && ($att = $record['_attachments'][$record['picture']])) { - $object['photo'] = $att['content'] ? $att['content'] : $this->contactstorage->getAttachment($att['key']); - } - // remove empty fields $this->data = array_filter($object); } diff --git a/plugins/libkolab/lib/kolab_storage_folder.php b/plugins/libkolab/lib/kolab_storage_folder.php index d82c5c69..c47d41c5 100644 --- a/plugins/libkolab/lib/kolab_storage_folder.php +++ b/plugins/libkolab/lib/kolab_storage_folder.php @@ -202,16 +202,19 @@ class kolab_storage_folder * Fetch a Kolab object attachment which is stored in a separate part * of the mail MIME message that represents the Kolab record. * - * @param string Object's UID - * @param string The attachment key stored in the Kolab XML - * @return mixed The attachment content as binary string + * @param string Object's UID + * @param string The attachment's mime number + * @param string IMAP folder where message is stored; + * If set, that also implies that the given UID is an IMAP UID + * @return mixed The attachment content as binary string */ - public function get_attachment($uid, $key) + public function get_attachment($uid, $part, $mailbox = null) { - // TODO: implement this + if ($msguid = ($mailbox ? $uid : $this->uid2msguid($uid))) { + if ($mailbox) + $this->imap->set_folder($mailbox); - if ($msguid = $this->uid2msguid($uid)) { - $message = new rcube_message($msguid); + return $this->imap->get_message_part($msguid, $part); } return null; @@ -219,21 +222,34 @@ class kolab_storage_folder /** + * Fetch the mime message from the storage server and extract + * the Kolab groupware object from it * + * @param string The IMAP message UID to fetch + * @param string The object type expected + * @return array Hash array representing the Kolab object */ - private function read_object($msguid, $type = null) + private function read_object($msguid, $type = null, $folder = null) { if (!$type) $type = $this->type; + if (!$folder) $folder = $this->name; $ctype= self::KTYPE_PREFIX . $type; - $this->imap->set_folder($this->name); + $this->imap->set_folder($folder); $message = new rcube_message($msguid); + $attachments = array(); // get XML part foreach ((array)$message->attachments as $part) { - if ($part->mimetype == $ctype || preg_match('!application/([a-z]+\+)?xml!', $part->mimetype)) { + if (!$xml && ($part->mimetype == $ctype || preg_match('!application/([a-z]+\+)?xml!', $part->mimetype))) { $xml = $part->body ? $part->body : $message->get_part_content($part->mime_id); - break; + } + else if ($part->filename) { + $attachments[$part->filename] = array( + 'key' => $part->mime_id, + 'type' => $part->mimetype, + 'size' => $part->size, + ); } } @@ -271,6 +287,7 @@ class kolab_storage_folder $object = $format->to_array(); $object['_msguid'] = $msguid; $object['_mailbox'] = $this->name; + $object['_attachments'] = $attachments; return $object; } @@ -291,6 +308,15 @@ class kolab_storage_folder if (!$type) $type = $this->type; + // copy attachments from old message + if (!empty($object['_msguid']) && ($old = $this->read_object($object['_msguid'], $type, $object['_mailbox']))) { + foreach ($old['_attachments'] as $name => $att) { + if (!isset($object['_attachments'][$name])) { + $object['_attachments'][$name] = $old['_attachments'][$name]; + } + } + } + if ($raw_msg = $this->build_message($object, $type)) { $result = $this->imap->save_message($this->name, $raw_msg, '', false); @@ -410,6 +436,18 @@ class kolab_storage_folder '', RCMAIL_CHARSET ); + // save object attachments as separate parts + // TODO: optimize memory consumption by using tempfiles for transfer + foreach ((array)$object['_attachments'] as $name => $att) { + if (empty($att['content']) && !empty($att['key'])) { + $msguid = !empty($object['_msguid']) ? $object['_msguid'] : $object['uid']; + $att['content'] = $this->get_attachment($msguid, $att['key'], $object['_mailbox']); + } + if (!empty($att['content'])) { + $mime->addAttachment($att['content'], $att['type'], $name, false); + } + } + return $mime->getMessage(); }