diff --git a/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php b/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php index 5609f6cb..359d8ada 100644 --- a/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php +++ b/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php @@ -518,8 +518,6 @@ class rcube_kolab_contacts extends rcube_addressbook if (!$existing) { // generate new Kolab contact item $object = $this->_from_rcube_contact($save_data); - $object['uid'] = kolab_format::generate_uid(); - $saved = $this->storagefolder->save($object, 'contact'); if (!$saved) { @@ -554,7 +552,7 @@ class rcube_kolab_contacts extends rcube_addressbook { $updated = false; if ($old = $this->storagefolder->get_object($this->_id2uid($id))) { - $object = array_merge($old, $this->_from_rcube_contact($save_data)); + $object = $this->_from_rcube_contact($save_data, $old); $saved = $this->storagefolder->save($object, 'contact', $uid); if (!$saved) { @@ -1030,9 +1028,9 @@ class rcube_kolab_contacts extends rcube_addressbook } /** - * Map fields from Roundcube format to internal Kolab_Format + * Map fields from Roundcube format to internal kolab_format_contact properties */ - private function _from_rcube_contact($contact) + private function _from_rcube_contact($contact, $old = array()) { if (!$contact['uid'] && $contact['ID']) $contact['uid'] = $this->_id2uid($contact['ID']); @@ -1085,7 +1083,14 @@ class rcube_kolab_contacts extends rcube_addressbook $contact['_attachments']['photo.attachment'] = false; } - return $contact; + // copy meta data (starting with _) from old object + foreach ((array)$old as $key => $val) { + if (!isset($contact[$key]) && $key[0] == '_') + $contact[$key] = $val; + } + + // add empty values for some fields which can be removed in the UI + return $contact + array('nickname' => '', 'birthday' => '', 'anniversary' => '', 'freebusyurl' => ''); } } diff --git a/plugins/libkolab/lib/kolab_format.php b/plugins/libkolab/lib/kolab_format.php index 9c6fea0a..1fe8e611 100644 --- a/plugins/libkolab/lib/kolab_format.php +++ b/plugins/libkolab/lib/kolab_format.php @@ -26,6 +26,9 @@ abstract class kolab_format { public static $timezone; + protected $obj; + protected $data; + /** * Factory method to instantiate a kolab_format object of the given type */ @@ -42,28 +45,18 @@ abstract class kolab_format return PEAR::raiseError(sprintf("Failed to load Kolab Format wrapper for type %s", $type)); } - /** - * Generate random UID for Kolab objects - * - * @return string UUID with a unique MD5 value - */ - public static function generate_uid() - { - return 'urn:uuid:' . md5(uniqid(mt_rand(), true)); - } - /** * 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 object The libkolabxml date/time object or null on error + * @return object The libkolabxml date/time object */ public static function get_datetime($datetime, $tz = null, $dateonly = false) { if (!$tz) $tz = self::$timezone; - $result = null; + $result = new cDateTime(); if (is_numeric($datetime)) $datetime = new DateTime('@'.$datetime, $tz); @@ -71,7 +64,6 @@ abstract class kolab_format $datetime = new DateTime($datetime, $tz); if (is_a($datetime, 'DateTime')) { - $result = new cDateTime(); $result->setDate($datetime->format('Y'), $datetime->format('n'), $datetime->format('j')); if (!$dateonly) @@ -141,11 +133,34 @@ abstract class kolab_format public static function array2vector($arr) { $vec = new vectors; - foreach ((array)$arr as $val) - $vec->push($val); + foreach ((array)$arr as $val) { + if (strlen($val)) + $vec->push($val); + } return $vec; } + /** + * Save the last generated UID to the object properties. + * Should be called after kolabformat::writeXXXX(); + */ + protected function update_uid() + { + // get generated UID + if (!$this->data['uid']) { + $this->data['uid'] = kolabformat::getSerializedUID(); + $this->obj->setUid($this->data['uid']); + } + } + + /** + * Direct getter for object properties + */ + function __get($var) + { + return $this->data[$var]; + } + /** * Load Kolab object data from the given XML block * diff --git a/plugins/libkolab/lib/kolab_format_contact.php b/plugins/libkolab/lib/kolab_format_contact.php index 48260b34..9d56b83b 100644 --- a/plugins/libkolab/lib/kolab_format_contact.php +++ b/plugins/libkolab/lib/kolab_format_contact.php @@ -24,8 +24,10 @@ class kolab_format_contact extends kolab_format 'work' => Address::Work, ); - private $data; - private $obj; + private $gendermap = array( + 'female' => Contact::Female, + 'male' => Contact::Male, + ); // old Kolab 2 format field map private $kolab2_fieldmap = array( @@ -98,7 +100,9 @@ class kolab_format_contact extends kolab_format */ public function write() { - return kolabformat::writeContact($this->obj); + $xml = kolabformat::writeContact($this->obj); + parent::update_uid(); + return $xml; } /** @@ -109,18 +113,16 @@ class kolab_format_contact extends kolab_format public function set(&$object) { // set some automatic values if missing - if (empty($object['uid'])) - $object['uid'] = self::generate_uid(); - if (false && !$this->obj->created()) { if (!empty($object['created'])) $object['created'] = new DateTime('now', self::$timezone); $this->obj->setCreated(self::get_datetime($object['created'])); } - // do the hard work of setting object values - $this->obj->setUid($object['uid']); + if (!empty($object['uid'])) + $this->obj->setUid($object['uid']); + // do the hard work of setting object values $nc = new NameComponents; $nc->setSurnames(self::array2vector($object['surname'])); $nc->setGiven(self::array2vector($object['firstname'])); @@ -130,7 +132,7 @@ class kolab_format_contact extends kolab_format $this->obj->setNameComponents($nc); $this->obj->setName($object['name']); - if ($object['nickname']) + if (isset($object['nickname'])) $this->obj->setNickNames(self::array2vector($object['nickname'])); // organisation related properties (affiliation) @@ -191,15 +193,15 @@ class kolab_format_contact extends kolab_format } $this->obj->setTelephones($tels); - if ($object['gender']) - $this->obj->setGender($object['gender'] == 'female' ? Contact::Female : Contact::Male); - if ($object['notes']) + if (isset($object['gender'])) + $this->obj->setGender($this->gendermap[$object['gender']] ? $this->gendermap[$object['gender']] : Contact::NotSet); + if (isset($object['notes'])) $this->obj->setNote($object['notes']); - if ($object['freebusyurl']) + if (isset($object['freebusyurl'])) $this->obj->setFreeBusyUrl($object['freebusyurl']); - if ($object['birthday']) + if (isset($object['birthday'])) $this->obj->setBDay(self::get_datetime($object['birthday'], null, true)); - if ($object['anniversary']) + if (isset($object['anniversary'])) $this->obj->setAnniversary(self::get_datetime($object['anniversary'], null, true)); if (!empty($object['photo'])) { @@ -304,8 +306,9 @@ class kolab_format_contact extends kolab_format 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'; + $gendermap = array_flip($this->gendermap); + if (($g = $this->obj->gender()) && $gendermap[$g]) + $object['gender'] = $gendermap[$g]; if ($this->obj->photoMimetype()) $object['photo'] = $this->obj->photo(); diff --git a/plugins/libkolab/lib/kolab_format_distributionlist.php b/plugins/libkolab/lib/kolab_format_distributionlist.php index d6297815..b064718c 100644 --- a/plugins/libkolab/lib/kolab_format_distributionlist.php +++ b/plugins/libkolab/lib/kolab_format_distributionlist.php @@ -4,9 +4,6 @@ class kolab_format_distributionlist extends kolab_format { public $CTYPE = 'application/vcard+xml'; - - private $data; - private $obj; function __construct() { @@ -30,17 +27,17 @@ class kolab_format_distributionlist extends kolab_format */ public function write() { - return kolabformat::writeDistlist($this->obj); + $xml = kolabformat::writeDistlist($this->obj); + parent::update_uid(); + return $xml; } public function set(&$object) { // set some automatic values if missing - if (empty($object['uid'])) - $object['uid'] = self::generate_uid(); + if (!empty($object['uid'])) + $this->obj->setUid($object['uid']); - // do the hard work of setting object values - $this->obj->setUid($object['uid']); $this->obj->setName($object['name']); $members = new vectormember; diff --git a/plugins/libkolab/lib/kolab_format_event.php b/plugins/libkolab/lib/kolab_format_event.php index d8ab2764..00035e93 100644 --- a/plugins/libkolab/lib/kolab_format_event.php +++ b/plugins/libkolab/lib/kolab_format_event.php @@ -4,9 +4,6 @@ class kolab_format_event extends kolab_format { public $CTYPE = 'application/calendar+xml'; - - private $data; - private $obj; function __construct() { @@ -20,7 +17,9 @@ class kolab_format_event extends kolab_format public function write() { - return kolabformat::writeEvent($this->obj); + $xml = kolabformat::writeEvent($this->obj); + parent::update_uid(); + return $xml; } public function set(&$object) diff --git a/plugins/libkolab/lib/kolab_storage_folder.php b/plugins/libkolab/lib/kolab_storage_folder.php index 4dd29a22..01e465fd 100644 --- a/plugins/libkolab/lib/kolab_storage_folder.php +++ b/plugins/libkolab/lib/kolab_storage_folder.php @@ -44,6 +44,7 @@ class kolab_storage_folder private $imap; private $info; private $owner; + private $objcache = array(); private $uid2msg = array(); @@ -239,7 +240,8 @@ class kolab_storage_folder * * @param string The IMAP message UID to fetch * @param string The object type expected - * @return array Hash array representing the Kolab object + * @param string The folder name where the message is stored + * @return mixed Hash array representing the Kolab object, a kolab_format instance or false if not found */ private function read_object($msguid, $type = null, $folder = null) { @@ -247,6 +249,10 @@ class kolab_storage_folder if (!$folder) $folder = $this->name; $ctype= self::KTYPE_PREFIX . $type; + // requested message not in local cache + if ($this->objcache[$msguid]) + return $this->objcache[$msguid]; + $this->imap->set_folder($folder); $message = new rcube_message($msguid); $attachments = array(); @@ -296,10 +302,16 @@ class kolab_storage_folder } if ($format->is_valid()) { + if ($formatobj) + return $format; + $object = $format->to_array(); $object['_msguid'] = $msguid; $object['_mailbox'] = $this->name; $object['_attachments'] = $attachments; + $object['_formatobj'] = $format; + + $this->objcache[$msguid] = $object; return $object; } @@ -425,11 +437,21 @@ class kolab_storage_folder */ private function build_message(&$object, $type) { - $format = kolab_format::factory($type); + // load old object to preserve data we don't understand/process + if (is_object($object['_formatobj'])) + $format = $object['_formatobj']; + else if ($object['_msguid'] && ($old = $this->read_object($object['_msguid'], $type, $object['_mailbox']))) + $format = $old['_formatobj']; + + // create new kolab_format instance + if (!$format) + $format = kolab_format::factory($type); + $format->set($object); $xml = $format->write(); + $object['uid'] = $format->uid; // get read UID from format - if (!$format->is_valid()) { + if (!$format->is_valid() || empty($object['uid'])) { return false; } @@ -450,7 +472,7 @@ class kolab_storage_folder $mime->headers($headers); $mime->setTXTBody('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 http://www.kolab.org/kolab2-clients.html\n\n"); + . "For a list of such email clients please visit http://www.kolab.org/\n\n"); $mime->addAttachment($xml, $format->CTYPE,