-
+
diff --git a/plugins/kolab_files/skins/larry/ui.js b/plugins/kolab_files/skins/larry/ui.js
index a0a3c494..a5e359bd 100644
--- a/plugins/kolab_files/skins/larry/ui.js
+++ b/plugins/kolab_files/skins/larry/ui.js
@@ -103,12 +103,12 @@ function kolab_files_upload_input(button)
file.attr({name: 'file[]', type: 'file', multiple: 'multiple', size: 5})
.change(function() { rcmail.files_upload('#filesuploadform'); })
// opacity:0 does the trick, display/visibility doesn't work
- .css({opacity: 0, cursor: 'pointer', position: 'absolute', top: '10000px', left: '10000px'});
+ .css({opacity: 0, cursor: 'pointer', outline: 'none', position: 'absolute', top: '10000px', left: '10000px'});
- // In FF we need to move the browser file-input's button under the cursor
+ // In FF and IE we need to move the browser file-input's button under the cursor
// Thanks to the size attribute above we know the length of the input field
- if (bw.mz)
- file.css({marginLeft: '-75px'});
+ if (bw.mz || bw.ie)
+ file.css({marginLeft: '-80px'});
// Note: now, I observe problem with cursor style on FF < 4 only
link.css({overflow: 'hidden', cursor: 'pointer'})
diff --git a/plugins/libkolab/lib/kolab_format_event.php b/plugins/libkolab/lib/kolab_format_event.php
index 2b3f0c8f..ec97767c 100644
--- a/plugins/libkolab/lib/kolab_format_event.php
+++ b/plugins/libkolab/lib/kolab_format_event.php
@@ -192,7 +192,7 @@ class kolab_format_event extends kolab_format_xcal
}
// read exception event objects
- if (($exceptions = $this->obj->exceptions()) && $exceptions->size()) {
+ if (($exceptions = $this->obj->exceptions()) && is_object($exceptions) && $exceptions->size()) {
for ($i=0; $i < $exceptions->size(); $i++) {
if (($exobj = $exceptions->get($i))) {
$exception = new kolab_format_event($exobj);
diff --git a/plugins/libkolab/lib/kolab_storage_folder.php b/plugins/libkolab/lib/kolab_storage_folder.php
index 7e10f2e6..dd0e8d2d 100644
--- a/plugins/libkolab/lib/kolab_storage_folder.php
+++ b/plugins/libkolab/lib/kolab_storage_folder.php
@@ -548,9 +548,9 @@ class kolab_storage_folder
// detect old Kolab 2.0 format
if (strpos($xmlhead, '<' . $xmltype) !== false && strpos($xmlhead, 'xmlns=') === false)
- $format_version = 2.0;
+ $format_version = '2.0';
else
- $format_version = 3.0; // assume 3.0
+ $format_version = '3.0'; // assume 3.0
}
// get Kolab format handler for the given type
@@ -588,7 +588,6 @@ class kolab_storage_folder
return false;
}
-
/**
* Save an object in this folder.
*
@@ -659,6 +658,11 @@ class kolab_storage_folder
}
}
+ // save recurrence exceptions as individual objects due to lack of support in Kolab v2 format
+ if (kolab_storage::$version == '2.0' && $object['recurrence']['EXCEPTIONS']) {
+ $this->save_recurrence_exceptions($object, $type);
+ }
+
// check IMAP BINARY extension support for 'file' objects
// allow configuration to workaround bug in Cyrus < 2.4.17
$rcmail = rcube::get_instance();
@@ -666,6 +670,12 @@ class kolab_storage_folder
// generate and save object message
if ($raw_msg = $this->build_message($object, $type, $binary)) {
+ // resolve old msguid before saving
+ if ($uid && empty($object['_msguid']) && ($msguid = $this->cache->uid2msguid($uid))) {
+ $object['_msguid'] = $msguid;
+ $object['_mailbox'] = $this->name;
+ }
+
if (is_array($raw_msg)) {
$result = $this->imap->save_message($this->name, $raw_msg[0], $raw_msg[1], true, null, null, $binary);
@unlink($raw_msg[0]);
@@ -679,10 +689,6 @@ class kolab_storage_folder
$this->imap->delete_message($object['_msguid'], $object['_mailbox']);
$this->cache->set($object['_msguid'], false, $object['_mailbox']);
}
- else if ($result && $uid && ($msguid = $this->cache->uid2msguid($uid))) {
- $this->imap->delete_message($msguid, $this->name);
- $this->cache->set($object['_msguid'], false);
- }
// update cache with new UID
if ($result) {
@@ -694,6 +700,68 @@ class kolab_storage_folder
return $result;
}
+ /**
+ * Save recurrence exceptions as individual objects.
+ * The Kolab v2 format doesn't allow us to save fully embedded exception objects.
+ *
+ * @param array Hash array with event properties
+ * @param string Object type
+ */
+ private function save_recurrence_exceptions(&$object, $type = null)
+ {
+ if ($object['recurrence']['EXCEPTIONS']) {
+ $exdates = array();
+ foreach ((array)$object['recurrence']['EXDATE'] as $exdate) {
+ $key = is_a($exdate, 'DateTime') ? $exdate->format('Y-m-d') : strval($exdate);
+ $exdates[$key] = 1;
+ }
+
+ // save every exception as individual object
+ foreach((array)$object['recurrence']['EXCEPTIONS'] as $exception) {
+ $exception['uid'] = self::recurrence_exception_uid($object['uid'], $exception['start']->format('Ymd'));
+ $exception['sequence'] = $object['sequence'] + 1;
+
+ if ($exception['thisandfuture']) {
+ $exception['recurrence'] = $object['recurrence'];
+
+ // adjust the recurrence duration of the exception
+ if ($object['recurrence']['COUNT']) {
+ $recurrence = new kolab_date_recurrence($object['_formatobj']);
+ if ($end = $recurrence->end()) {
+ unset($exception['recurrence']['COUNT']);
+ $exception['recurrence']['UNTIL'] = new DateTime('@'.$end);
+ }
+ }
+
+ // set UNTIL date if we have a thisandfuture exception
+ $untildate = clone $exception['start'];
+ $untildate->sub(new DateInterval('P1D'));
+ $object['recurrence']['UNTIL'] = $untildate;
+ unset($object['recurrence']['COUNT']);
+ }
+ else {
+ if (!$exdates[$exception['start']->format('Y-m-d')])
+ $object['recurrence']['EXDATE'][] = clone $exception['start'];
+ unset($exception['recurrence']);
+ }
+
+ unset($exception['recurrence']['EXCEPTIONS'], $exception['_formatobj'], $exception['_msguid']);
+ $this->save($exception, $type, $exception['uid']);
+ }
+
+ unset($object['recurrence']['EXCEPTIONS']);
+ }
+ }
+
+ /**
+ * Generate an object UID with the given recurrence-ID in a way that it is
+ * unique (the original UID is not a substring) but still recoverable.
+ */
+ private static function recurrence_exception_uid($uid, $recurrence_id)
+ {
+ $offset = -2;
+ return substr($uid, 0, $offset) . '-' . $recurrence_id . '-' . substr($uid, $offset);
+ }
/**
* Delete the specified object from this folder.