Branch 'roundcubemail-plugins-kolab-3.0' - 43 commits - plugins/calendar plugins/kolab_activesync plugins/kolab_addressbook plugins/kolab_auth plugins/kolab_config plugins/kolab_delegation plugins/kolab_folders plugins/kolab_zpush plugins/libcalendaring plugins/libkolab plugins/logon_page plugins/odfviewer plugins/tasklist plugins/tinymce_config

Aleksander Machniak machniak at kolabsys.com
Fri Jun 21 12:17:16 CEST 2013


 plugins/calendar/calendar.php                                  |   77 +-
 plugins/calendar/calendar_ui.js                                |   22 
 plugins/calendar/drivers/calendar_driver.php                   |   14 
 plugins/calendar/drivers/database/database_driver.php          |   11 
 plugins/calendar/drivers/kolab/kolab_calendar.php              |  131 +++
 plugins/calendar/drivers/kolab/kolab_driver.php                |   90 +-
 plugins/calendar/lib/calendar_ical.php                         |   57 +
 plugins/calendar/lib/calendar_ui.php                           |   11 
 plugins/calendar/localization/et_EE.inc                        |  360 +++++-----
 plugins/calendar/skins/larry/templates/calendar.html           |    2 
 plugins/kolab_activesync/kolab_activesync.php                  |    4 
 plugins/kolab_activesync/kolab_activesync_ui.php               |    6 
 plugins/kolab_activesync/localization/de_CH.inc                |    2 
 plugins/kolab_activesync/localization/de_DE.inc                |    2 
 plugins/kolab_activesync/localization/en_US.inc                |    2 
 plugins/kolab_activesync/localization/es_ES.inc                |    2 
 plugins/kolab_activesync/localization/et_EE.inc                |    2 
 plugins/kolab_activesync/localization/fr_FR.inc                |    2 
 plugins/kolab_activesync/localization/ja_JP.inc                |    2 
 plugins/kolab_activesync/localization/nl_NL.inc                |    2 
 plugins/kolab_activesync/localization/pl_PL.inc                |   54 -
 plugins/kolab_activesync/localization/ru_RU.inc                |    2 
 plugins/kolab_activesync/package.xml                           |    2 
 plugins/kolab_addressbook/kolab_addressbook.php                |   13 
 plugins/kolab_addressbook/lib/kolab_addressbook_ui.php         |    9 
 plugins/kolab_addressbook/lib/rcube_kolab_contacts.php         |   20 
 plugins/kolab_auth/kolab_auth.php                              |    2 
 plugins/kolab_config/kolab_config.php                          |    2 
 plugins/kolab_delegation/kolab_delegation.php                  |    8 
 plugins/kolab_delegation/kolab_delegation_engine.php           |    9 
 plugins/kolab_folders/kolab_folders.php                        |   86 ++
 plugins/kolab_zpush/kolab_zpush_ui.php                         |    6 
 plugins/libcalendaring/libcalendaring.php                      |   80 +-
 plugins/libkolab/lib/kolab_format_contact.php                  |   17 
 plugins/libkolab/lib/kolab_format_event.php                    |   84 ++
 plugins/libkolab/lib/kolab_format_xcal.php                     |   23 
 plugins/libkolab/lib/kolab_storage.php                         |    3 
 plugins/libkolab/lib/kolab_storage_cache.php                   |   51 -
 plugins/libkolab/lib/kolab_storage_folder.php                  |   88 ++
 plugins/logon_page/logon_page.php                              |    2 
 plugins/odfviewer/odfviewer.php                                |    2 
 plugins/tasklist/drivers/database/tasklist_database_driver.php |    6 
 plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php       |    2 
 plugins/tasklist/drivers/tasklist_driver.php                   |    2 
 plugins/tasklist/skins/larry/templates/mainview.html           |    2 
 plugins/tasklist/tasklist.js                                   |   13 
 plugins/tasklist/tasklist.php                                  |    4 
 plugins/tinymce_config/tinymce_config.php                      |    3 
 48 files changed, 889 insertions(+), 507 deletions(-)

New commits:
commit 8297464ab4273f5662f1ce47a0c75bf602494e8a
Author: Aleksander Machniak <machniak at kolabsys.com>
Date:   Tue Jun 11 14:52:10 2013 +0200

    Improve performance by reading max_allowed_packet variable (SHOW VARIABLES query) only if needed

diff --git a/plugins/libkolab/lib/kolab_storage_cache.php b/plugins/libkolab/lib/kolab_storage_cache.php
index bf0a7dd..e129015 100644
--- a/plugins/libkolab/lib/kolab_storage_cache.php
+++ b/plugins/libkolab/lib/kolab_storage_cache.php
@@ -35,7 +35,7 @@ class kolab_storage_cache
     private $synched = false;
     private $synclock = false;
     private $ready = false;
-    private $max_sql_packet = 1046576;  // 1 MB - 2000 bytes
+    private $max_sql_packet;
     private $max_sync_lock_time = 600;
     private $binary_cols = array('photo','pgppublickey','pkcs7publickey');
 
@@ -53,9 +53,6 @@ class kolab_storage_cache
         if ($this->enabled) {
             // remove sync-lock on script termination
             $rcmail->add_shutdown_function(array($this, '_sync_unlock'));
-
-            // read max_allowed_packet from mysql config
-            $this->max_sql_packet = min($this->db->get_variable('max_allowed_packet', 1048500), 4*1024*1024) - 2000;  // mysql limit or max 4 MB
         }
 
         if ($storage_folder)
@@ -630,7 +627,7 @@ class kolab_storage_cache
             $line = '(' . join(',', $values) . ')';
         }
 
-        if ($buffer && (!$msguid || (strlen($buffer) + strlen($line) > $this->max_sql_packet))) {
+        if ($buffer && (!$msguid || (strlen($buffer) + strlen($line) > $this->max_sql_packet()))) {
             $result = $this->db->query(
                 "INSERT INTO kolab_cache ".
                 " (resource, type, msguid, uid, created, changed, data, xml, dtstart, dtend, tags, words)".
@@ -650,6 +647,20 @@ class kolab_storage_cache
     }
 
     /**
+     * Returns max_allowed_packet from mysql config
+     */
+    private function max_sql_packet()
+    {
+        if (!$this->max_sql_packet) {
+            // mysql limit or max 4 MB
+            $value = $this->db->get_variable('max_allowed_packet', 1048500);
+            $this->max_sql_packet = min($value, 4*1024*1024) - 2000;
+        }
+
+        return $this->max_sql_packet;
+    }
+
+    /**
      * Check lock record for this folder and wait if locked or set lock
      */
     private function _sync_lock()


commit 9321a10124d1fbba87f8a25114fd583e385c2b66
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Fri May 31 18:16:54 2013 +0200

    Fix contact fulltext indexing with new email subtypes

diff --git a/plugins/libkolab/lib/kolab_format_contact.php b/plugins/libkolab/lib/kolab_format_contact.php
index 3852fb7..9944c3d 100644
--- a/plugins/libkolab/lib/kolab_format_contact.php
+++ b/plugins/libkolab/lib/kolab_format_contact.php
@@ -31,7 +31,7 @@ class kolab_format_contact extends kolab_format
     protected $read_func = 'readContact';
     protected $write_func = 'writeContact';
 
-    public static $fulltext_cols = array('name', 'firstname', 'surname', 'middlename', 'email');
+    public static $fulltext_cols = array('name', 'firstname', 'surname', 'middlename', 'email:address');
 
     public $phonetypes = array(
         'home'    => Telephone::Home,
@@ -383,7 +383,18 @@ class kolab_format_contact extends kolab_format
     {
         $data = '';
         foreach (self::$fulltext_cols as $col) {
-            $val = is_array($this->data[$col]) ? join(' ', $this->data[$col]) : $this->data[$col];
+            list($col, $field) = explode(':', $colname);
+
+            if ($field) {
+                $a = array();
+                foreach ((array)$this->data[$col] as $attr)
+                    $a[] = $attr[$field];
+                $val = join(' ', $a);
+            }
+            else {
+                $val = is_array($this->data[$col]) ? join(' ', $this->data[$col]) : $this->data[$col];
+            }
+
             if (strlen($val))
                 $data .= $val . ' ';
         }


commit 8502e5f68f4378f9e32cf12ad7015304c114d20d
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Fri May 31 18:07:16 2013 +0200

    Only temporarily split alarms string into array (#1905)

diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js
index 530bb25..84215b0 100644
--- a/plugins/tasklist/tasklist.js
+++ b/plugins/tasklist/tasklist.js
@@ -1008,10 +1008,7 @@ function rcube_tasklist_ui(settings)
 
         // set alarm(s)
         if (rec.alarms || action != 'new') {
-            if (typeof rec.alarms == 'string')
-                rec.alarms = rec.alarms.split(';');
-
-          var valarms = rec.alarms || [''];
+          var valarms = (typeof rec.alarms == 'string' ? rec.alarms.split(';') : rec.alarms) || [''];
           for (var alarm, i=0; i < valarms.length; i++) {
               alarm = String(valarms[i]).split(':');
               if (!alarm[1] && alarm[0]) alarm[1] = 'DISPLAY';


commit 68e15d58df08444a6222cf7fd53604f4e43a4655
Author: Thomas Bruederli <thomas at roundcube.net>
Date:   Mon May 27 17:27:53 2013 +0200

    Replace recursive calls with while loop when waiting for sync-lock (#1637)

diff --git a/plugins/libkolab/lib/kolab_storage_cache.php b/plugins/libkolab/lib/kolab_storage_cache.php
index 2782428..bf0a7dd 100644
--- a/plugins/libkolab/lib/kolab_storage_cache.php
+++ b/plugins/libkolab/lib/kolab_storage_cache.php
@@ -36,6 +36,7 @@ class kolab_storage_cache
     private $synclock = false;
     private $ready = false;
     private $max_sql_packet = 1046576;  // 1 MB - 2000 bytes
+    private $max_sync_lock_time = 600;
     private $binary_cols = array('photo','pgppublickey','pkcs7publickey');
 
 
@@ -92,7 +93,7 @@ class kolab_storage_cache
             return;
 
         // increase time limit
-        @set_time_limit(500);
+        @set_time_limit($this->max_sync_lock_time);
 
         // lock synchronization for this folder or wait if locked
         $this->_sync_lock();
@@ -656,12 +657,9 @@ class kolab_storage_cache
         if (!$this->ready)
             return;
 
-        $sql_arr = $this->db->fetch_assoc($this->db->query(
-            "SELECT msguid AS locked, ".$this->db->unixtimestamp('created')." AS created FROM kolab_cache ".
-            "WHERE resource=? AND type=?",
-            $this->resource_uri,
-            'lock'
-        ));
+        $sql_query = "SELECT msguid AS locked, ".$this->db->unixtimestamp('created')." AS created FROM kolab_cache ".
+            "WHERE resource=? AND type=?";
+        $sql_arr = $this->db->fetch_assoc($this->db->query($sql_query, $this->resource_uri, 'lock'));
 
         // abort if database is not set-up
         if ($this->db->is_error()) {
@@ -671,6 +669,12 @@ class kolab_storage_cache
 
         $this->synclock = true;
 
+        // wait if locked (expire locks after 10 minutes)
+        while ($sql_arr && intval($sql_arr['locked']) > 0 && $sql_arr['created'] + $this->max_sync_lock_time > time()) {
+            usleep(500000);
+            $sql_arr = $this->db->fetch_assoc($this->db->query($sql_query, $this->resource_uri, 'lock'));
+        }
+
         // create lock record if not exists
         if (!$sql_arr) {
             $this->db->query(
@@ -681,12 +685,6 @@ class kolab_storage_cache
                 date('Y-m-d H:i:s')
             );
         }
-        // wait if locked (expire locks after 10 minutes)
-        else if (intval($sql_arr['locked']) > 0 && (time() - $sql_arr['created']) < 600) {
-            usleep(500000);
-            return $this->_sync_lock();
-        }
-        // set lock
         else {
             $this->db->query(
                 "UPDATE kolab_cache SET msguid=1, created=? ".


commit a94edccc0d95cad9563136b5834287b03806b2f1
Author: Aleksander Machniak <machniak at kolabsys.com>
Date:   Fri May 17 10:47:08 2013 +0200

    Fix xml structure

diff --git a/plugins/kolab_activesync/package.xml b/plugins/kolab_activesync/package.xml
index 09e8d66..6ef5f14 100644
--- a/plugins/kolab_activesync/package.xml
+++ b/plugins/kolab_activesync/package.xml
@@ -74,7 +74,7 @@
 			</package>
 			<package>
 				<name>jqueryui</name>
-				<channel>pear.roundcube.net</uri>
+				<channel>pear.roundcube.net</channel>
 			</package>
 		</required>
 	</dependencies>


commit ba7303572f1d5553900ee5f6789662fbd05f7ffc
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu May 16 14:51:32 2013 +0200

    Fix usage of KolabEvent url setter/getter

diff --git a/plugins/libkolab/lib/kolab_format_xcal.php b/plugins/libkolab/lib/kolab_format_xcal.php
index 4983e99..1f4c894 100644
--- a/plugins/libkolab/lib/kolab_format_xcal.php
+++ b/plugins/libkolab/lib/kolab_format_xcal.php
@@ -111,6 +111,7 @@ abstract class kolab_format_xcal extends kolab_format
             'title'       => $this->obj->summary(),
             'location'    => $this->obj->location(),
             'description' => $this->obj->description(),
+            'url'         => $this->obj->url(),
             'status'      => $status_map[$this->obj->status()],
             'sensitivity' => $sensitivity_map[$this->obj->classification()],
             'priority'    => $this->obj->priority(),
@@ -243,12 +244,7 @@ abstract class kolab_format_xcal extends kolab_format
         $this->obj->setPriority($object['priority']);
         $this->obj->setClassification($this->sensitivity_map[$object['sensitivity']]);
         $this->obj->setCategories(self::array2vector($object['categories']));
-
-        $vurls = new vectorurl;
-        foreach ((array)$object['url'] as $url) {
-            $vurls->push(new Url(strval($url)));
-        }
-        $this->obj->setUrls($vurls);
+        $this->obj->setUrl(strval($object['url']));
 
         // process event attendees
         $attendees = new vectorattendee;


commit 9f1e3927fb9ad8bf3dcc1871bab722db83c453f0
Author: Aleksander Machniak <machniak at kolabsys.com>
Date:   Fri Jun 21 12:08:30 2013 +0200

    Copy event organizer to attendees list if necessary
    
    Conflicts:
    
    	plugins/calendar/calendar.php

diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
index 6537c53..d6cfbec 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -1098,6 +1098,23 @@ class calendar extends rcube_plugin
       $event['attachments'][$k]['classname'] = rcube_utils::file2class($attachment['mimetype'], $attachment['name']);
     }
 
+    // check for organizer in attendees list
+    $organizer = null;
+    foreach ((array)$event['attendees'] as $i => $attendee) {
+      if ($attendee['role'] == 'ORGANIZER') {
+        $organizer = $attendee;
+        break;
+      }
+    }
+
+    if ($organizer === null && !empty($event['organizer'])) {
+      $organizer = $event['organizer'];
+      $organizer['role'] = 'ORGANIZER';
+      if (!is_array($event['attendees']))
+        $event['attendees'] = array();
+      array_unshift($event['attendees'], $organizer);
+    }
+
     return array(
       '_id'   => $event['calendar'] . ':' . $event['id'],  // unique identifier for fullcalendar
       'start' => $this->lib->adjust_timezone($event['start'])->format('c'),


commit 84439e3acf414cf1903cc112cab6767eaec4761b
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu May 16 14:20:25 2013 +0200

    Fix syntax error

diff --git a/plugins/libkolab/lib/kolab_format_xcal.php b/plugins/libkolab/lib/kolab_format_xcal.php
index 868316c..4983e99 100644
--- a/plugins/libkolab/lib/kolab_format_xcal.php
+++ b/plugins/libkolab/lib/kolab_format_xcal.php
@@ -246,7 +246,7 @@ abstract class kolab_format_xcal extends kolab_format
 
         $vurls = new vectorurl;
         foreach ((array)$object['url'] as $url) {
-            $vurls->push(new Url(strval($url));
+            $vurls->push(new Url(strval($url)));
         }
         $this->obj->setUrls($vurls);
 


commit 64afdf923ec93fd6ae689c3fa06ba10138929a7f
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu May 16 13:30:04 2013 +0200

    Add support for URL property in xcal based objects

diff --git a/plugins/libkolab/lib/kolab_format_xcal.php b/plugins/libkolab/lib/kolab_format_xcal.php
index f173a2e..868316c 100644
--- a/plugins/libkolab/lib/kolab_format_xcal.php
+++ b/plugins/libkolab/lib/kolab_format_xcal.php
@@ -244,6 +244,12 @@ abstract class kolab_format_xcal extends kolab_format
         $this->obj->setClassification($this->sensitivity_map[$object['sensitivity']]);
         $this->obj->setCategories(self::array2vector($object['categories']));
 
+        $vurls = new vectorurl;
+        foreach ((array)$object['url'] as $url) {
+            $vurls->push(new Url(strval($url));
+        }
+        $this->obj->setUrls($vurls);
+
         // process event attendees
         $attendees = new vectorattendee;
         foreach ((array)$object['attendees'] as $attendee) {


commit 9fe8791236a5f23c27b015d82a6b7b3c806a550c
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu May 16 09:23:48 2013 +0200

    Add support for CUTYPE parameter for event attendees

diff --git a/plugins/libkolab/lib/kolab_format_xcal.php b/plugins/libkolab/lib/kolab_format_xcal.php
index b37b727..f173a2e 100644
--- a/plugins/libkolab/lib/kolab_format_xcal.php
+++ b/plugins/libkolab/lib/kolab_format_xcal.php
@@ -43,6 +43,14 @@ abstract class kolab_format_xcal extends kolab_format
         'CHAIR' => kolabformat::Chair,
     );
 
+    protected $cutype_map = array(
+        'INDIVIDUAL' => kolabformat::CutypeIndividual,
+        'GROUP'      => kolabformat::CutypeGroup,
+        'ROOM'       => kolabformat::CutypeRoom,
+        'RESOURCE'   => kolabformat::CutypeResource,
+        'UNKNOWN'    => kolabformat::CutypeUnknown,
+    );
+
     protected $rrule_type_map = array(
         'MINUTELY' => RecurrenceRule::Minutely,
         'HOURLY' => RecurrenceRule::Hourly,
@@ -119,6 +127,7 @@ abstract class kolab_format_xcal extends kolab_format
         }
 
         $role_map = array_flip($this->role_map);
+        $cutype_map = array_flip($this->cutype_map);
         $part_status_map = array_flip($this->part_status_map);
         $attvec = $this->obj->attendees();
         for ($i=0; $i < $attvec->size(); $i++) {
@@ -127,6 +136,7 @@ abstract class kolab_format_xcal extends kolab_format
             if ($cr->email() != $object['organizer']['email']) {
                 $object['attendees'][] = array(
                     'role' => $role_map[$attendee->role()],
+                    'cutype' => $cutype_map[$attendee->cutype()],
                     'status' => $part_status_map[$attendee->partStat()],
                     'rsvp' => $attendee->rsvp(),
                     'email' => $cr->email(),
@@ -248,6 +258,7 @@ abstract class kolab_format_xcal extends kolab_format
                 $att->setContact($cr);
                 $att->setPartStat($this->part_status_map[$attendee['status']]);
                 $att->setRole($this->role_map[$attendee['role']] ? $this->role_map[$attendee['role']] : kolabformat::Required);
+                $att->setCutype($this->cutype_map[$attendee['cutype']] ? $this->cutype_map[$attendee['cutype']] : kolabformat::CutypeIndividual);
                 $att->setRSVP((bool)$attendee['rsvp']);
 
                 if ($att->isValid()) {


commit d34b1b769e88ea1ca367b31ffc743e84abddbdf1
Author: Thomas Bruederli <thomas at roundcube.net>
Date:   Mon May 13 09:14:57 2013 +0200

    Added Estonian translation for calendar (#1868)

diff --git a/plugins/calendar/localization/et_EE.inc b/plugins/calendar/localization/et_EE.inc
index 7589f38..edfba0c 100644
--- a/plugins/calendar/localization/et_EE.inc
+++ b/plugins/calendar/localization/et_EE.inc
@@ -3,125 +3,125 @@
 $labels = array();
 
 // preferences
-$labels['default_view'] = 'Default view';
-$labels['time_format'] = 'Time format';
-$labels['timeslots'] = 'Timeslots per hour';
-$labels['first_day'] = 'First weekday';
-$labels['first_hour'] = 'First hour to show';
-$labels['workinghours'] = 'Working hours';
-$labels['add_category'] = 'Add category';
-$labels['remove_category'] = 'Remove category';
-$labels['defaultcalendar'] = 'Create new events in';
-$labels['eventcoloring'] = 'Event coloring';
-$labels['coloringmode0'] = 'According to calendar';
-$labels['coloringmode1'] = 'According to category';
-$labels['coloringmode2'] = 'Calendar for outline, category for content';
-$labels['coloringmode3'] = 'Category for outline, calendar for content';
+$labels['default_view'] = 'Vaikevaade';
+$labels['time_format'] = 'Aja vorming';
+$labels['timeslots'] = 'Ajaühikuid päevas';
+$labels['first_day'] = 'Esimene päev';
+$labels['first_hour'] = 'Esimene tund';
+$labels['workinghours'] = 'Tööaeg';
+$labels['add_category'] = 'Lisa kategooria';
+$labels['remove_category'] = 'Kustuta kategooria';
+$labels['defaultcalendar'] = 'Loo uued sündmused';
+$labels['eventcoloring'] = 'Sündmuste värvid';
+$labels['coloringmode0'] = 'Vastavalt kalendrile';
+$labels['coloringmode1'] = 'Vastavalt kategooriale';
+$labels['coloringmode2'] = 'Kalender väljaspool, kategooria sisul';
+$labels['coloringmode3'] = 'Kategooria väljaspool, kalender sisul';
 
 // calendar
-$labels['calendar'] = 'Calendar';
-$labels['calendars'] = 'Calendars';
-$labels['category'] = 'Category';
-$labels['categories'] = 'Categories';
-$labels['createcalendar'] = 'Create new calendar';
-$labels['editcalendar'] = 'Edit calendar properties';
+$labels['calendar'] = 'Kalender';
+$labels['calendars'] = 'Kalendrid';
+$labels['category'] = 'Kategooria';
+$labels['categories'] = 'Kategooriad';
+$labels['createcalendar'] = 'Uus kalender';
+$labels['editcalendar'] = 'Muuda seadeid';
 $labels['name'] = 'Nimi';
-$labels['color'] = 'Color';
-$labels['day'] = 'Day';
-$labels['week'] = 'Week';
-$labels['month'] = 'Month';
+$labels['color'] = 'Värv';
+$labels['day'] = 'Päev';
+$labels['week'] = 'Nädal';
+$labels['month'] = 'Kuu';
 $labels['agenda'] = 'Agenda';
-$labels['new'] = 'New';
-$labels['new_event'] = 'New event';
-$labels['edit_event'] = 'Edit event';
-$labels['edit'] = 'Edit';
+$labels['new'] = 'Uus';
+$labels['new_event'] = 'Uus sündmus';
+$labels['edit_event'] = 'Muuda sündmust';
+$labels['edit'] = 'Redigeeri';
 $labels['save'] = 'Salvesta';
-$labels['remove'] = 'Remove';
-$labels['cancel'] = 'Cancel';
-$labels['select'] = 'Select';
-$labels['print'] = 'Print';
-$labels['printtitle'] = 'Print calendars';
-$labels['title'] = 'Summary';
-$labels['description'] = 'Description';
-$labels['all-day'] = 'all-day';
-$labels['export'] = 'Export';
-$labels['exporttitle'] = 'Export to iCalendar';
-$labels['location'] = 'Location';
-$labels['date'] = 'Date';
-$labels['start'] = 'Start';
-$labels['end'] = 'End';
-$labels['selectdate'] = 'Choose date';
-$labels['freebusy'] = 'Show me as';
-$labels['free'] = 'Free';
-$labels['busy'] = 'Busy';
-$labels['outofoffice'] = 'Out of Office';
-$labels['tentative'] = 'Tentative';
-$labels['priority'] = 'Priority';
-$labels['sensitivity'] = 'Privacy';
-$labels['public'] = 'public';
-$labels['private'] = 'private';
-$labels['confidential'] = 'confidential';
-$labels['alarms'] = 'Reminder';
-$labels['generated'] = 'generated at';
-$labels['printdescriptions'] = 'Print descriptions';
-$labels['parentcalendar'] = 'Insert inside';
-$labels['searchearlierdates'] = '« Search for earlier events';
-$labels['searchlaterdates'] = 'Search for later events »';
-$labels['andnmore'] = '$nr more...';
-$labels['togglerole'] = 'Click to toggle role';
-$labels['createfrommail'] = 'Save as event';
-$labels['importevents'] = 'Import events';
-$labels['importrange'] = 'Events from';
-$labels['onemonthback'] = '1 month back';
-$labels['nmonthsback'] = '$nr months back';
-$labels['showurl'] = 'Show calendar URL';
-$labels['showurldescription'] = 'Use the following address to access (read only) your calendar from other applications. You can copy and paste this into any calendar software that supports the iCal format.';
+$labels['remove'] = 'Kustuta';
+$labels['cancel'] = 'Tühista';
+$labels['select'] = 'Märgi';
+$labels['print'] = 'Trüki';
+$labels['printtitle'] = 'Trüki kalendrid';
+$labels['title'] = 'Kokkuvõte';
+$labels['description'] = 'Selgitus';
+$labels['all-day'] = 'kogu päev';
+$labels['export'] = 'Ekspordi';
+$labels['exporttitle'] = 'Ekspordi iCalendar';
+$labels['location'] = 'Askukoht';
+$labels['date'] = 'Kuupäev';
+$labels['start'] = 'Algus';
+$labels['end'] = 'Lõpp';
+$labels['selectdate'] = 'Vali kuupäev';
+$labels['freebusy'] = 'Näita mind';
+$labels['free'] = 'vabana';
+$labels['busy'] = 'hõivatuna';
+$labels['outofoffice'] = 'kontorist eemal';
+$labels['tentative'] = 'ei tea';
+$labels['priority'] = 'Prioriteet';
+$labels['sensitivity'] = 'Privaatsus';
+$labels['public'] = 'avalik';
+$labels['private'] = 'varjatud';
+$labels['confidential'] = 'konfidentsiaalne';
+$labels['alarms'] = 'Meeldetuletus';
+$labels['generated'] = 'loodud';
+$labels['printdescriptions'] = 'Väljuta kirjeldus';
+$labels['parentcalendar'] = 'Sisesta';
+$labels['searchearlierdates'] = '« Otsi varasemaid sündmusi';
+$labels['searchlaterdates'] = 'Otsi hilisemaid sündmusi »';
+$labels['andnmore'] = '$nr rohkem...';
+$labels['togglerole'] = 'Kliki rolli muutmiseks';
+$labels['createfrommail'] = 'Salvesta sündmusena';
+$labels['importevents'] = 'Impordi sündmused';
+$labels['importrange'] = 'Sündmused alates';
+$labels['onemonthback'] = '1 kuu tagasi';
+$labels['nmonthsback'] = '$nr kuud tagasi';
+$labels['showurl'] = 'Näita kalendi URLi';
+$labels['showurldescription'] = 'Kasuta seda aadressi kalendri lugemisõiguse andmiseks iCal vormingus.';
 
 // agenda view
-$labels['listrange'] = 'Range to display:';
-$labels['listsections'] = 'Divide into:';
-$labels['smartsections'] = 'Smart sections';
-$labels['until'] = 'until';
-$labels['today'] = 'Today';
-$labels['tomorrow'] = 'Tomorrow';
-$labels['thisweek'] = 'This week';
-$labels['nextweek'] = 'Next week';
-$labels['thismonth'] = 'This month';
-$labels['nextmonth'] = 'Next month';
-$labels['weekofyear'] = 'Week';
-$labels['pastevents'] = 'Past';
-$labels['futureevents'] = 'Future';
+$labels['listrange'] = 'Näita ajavahemikku:';
+$labels['listsections'] = 'Jaga:';
+$labels['smartsections'] = 'nutikad osad';
+$labels['until'] = 'kuni';
+$labels['today'] = 'täna';
+$labels['tomorrow'] = 'homme';
+$labels['thisweek'] = 'see nädal';
+$labels['nextweek'] = 'järgmine nädal';
+$labels['thismonth'] = 'Sel kuul';
+$labels['nextmonth'] = 'Järgmisel kuul';
+$labels['weekofyear'] = 'Nädal aastas';
+$labels['pastevents'] = 'Möödunud';
+$labels['futureevents'] = 'Tulevased';
 
 // alarm/reminder settings
-$labels['showalarms'] = 'Show alarms';
-$labels['defaultalarmtype'] = 'Default reminder setting';
-$labels['defaultalarmoffset'] = 'Default reminder time';
+$labels['showalarms'] = 'Näita meeldetuletusi';
+$labels['defaultalarmtype'] = 'Meeldetuletuse vaiketüüp';
+$labels['defaultalarmoffset'] = 'Meeldetuletuse vaikeaeg';
 
 // attendees
-$labels['attendee'] = 'Participant';
-$labels['role'] = 'Role';
-$labels['availability'] = 'Avail.';
-$labels['confirmstate'] = 'Status';
-$labels['addattendee'] = 'Add participant';
-$labels['roleorganizer'] = 'Organizer';
-$labels['rolerequired'] = 'Kohustuslik';
-$labels['roleoptional'] = 'Optional';
-$labels['roleresource'] = 'Resource';
-$labels['availfree'] = 'Free';
-$labels['availbusy'] = 'Busy';
-$labels['availunknown'] = 'Unknown';
-$labels['availtentative'] = 'Tentative';
-$labels['availoutofoffice'] = 'Out of Office';
-$labels['scheduletime'] = 'Find availability';
-$labels['sendinvitations'] = 'Send invitations';
-$labels['sendnotifications'] = 'Notify participants about modifications';
-$labels['sendcancellation'] = 'Notify participants about event cancellation';
-$labels['onlyworkinghours'] = 'Find availability within my working hours';
-$labels['reqallattendees'] = 'Required/all participants';
-$labels['prevslot'] = 'Previous Slot';
-$labels['nextslot'] = 'Next Slot';
-$labels['noslotfound'] = 'Unable to find a free time slot';
-$labels['invitationsubject'] = 'You\'ve been invited to "$title"';
+$labels['attendee'] = 'Ósavõtja';
+$labels['role'] = 'Roll';
+$labels['availability'] = 'Saadavus';
+$labels['confirmstate'] = 'Staatus';
+$labels['addattendee'] = 'Lisa osaleja';
+$labels['roleorganizer'] = 'Korraldaja';
+$labels['rolerequired'] = 'Vajalik';
+$labels['roleoptional'] = 'Teised';
+$labels['roleresource'] = 'Ressurss';
+$labels['availfree'] = 'Vaba';
+$labels['availbusy'] = 'Hõivatud';
+$labels['availunknown'] = 'Teadmata';
+$labels['availtentative'] = 'Tinglik';
+$labels['availoutofoffice'] = 'Kontorist eemal';
+$labels['scheduletime'] = 'Otsi saadavust';
+$labels['sendinvitations'] = 'Saada kutsed';
+$labels['sendnotifications'] = 'Anna osavõtjaile muutustest teada';
+$labels['sendcancellation'] = 'Anna osavõtjaile tühistamisest teada';
+$labels['onlyworkinghours'] = 'Leia vaba aeg minu tööajast';
+$labels['reqallattendees'] = 'Vajalikud/teised osavõtjad';
+$labels['prevslot'] = 'Eelnev aeg';
+$labels['nextslot'] = 'Järgnev aeg';
+$labels['noslotfound'] = 'Ei õnnestu leida vaba aega';
+$labels['invitationsubject'] = 'Olete kutsutud "$title"';
 $labels['invitationmailbody'] = "*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees\n\nPlease find attached an iCalendar file with all the event details which you can import to your calendar application.";
 $labels['invitationattendlinks'] = "In case your email client doesn't support iTip requests you can use the following link to either accept or decline this invitation:\n\$url";
 $labels['eventupdatesubject'] = '"$title" has been updated';
@@ -145,86 +145,86 @@ $labels['itipmailbodyaccepted'] = "\$sender has accepted the invitation to the f
 $labels['itipmailbodytentative'] = "\$sender has tentatively accepted the invitation to the following event:\n\n*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees";
 $labels['itipmailbodydeclined'] = "\$sender has declined the invitation to the following event:\n\n*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees";
 $labels['itipdeclineevent'] = 'Do you want to decline your invitation to this event?';
-$labels['importtocalendar'] = 'Save to my calendar';
-$labels['removefromcalendar'] = 'Remove from my calendar';
-$labels['updateattendeestatus'] = 'Update the participant\'s status';
-$labels['acceptinvitation'] = 'Do you accept this invitation?';
-$labels['youhaveaccepted'] = 'You have accepted this invitation';
-$labels['youhavetentative'] = 'You have tentatively accepted this invitation';
-$labels['youhavedeclined'] = 'You have declined this invitation';
-$labels['notanattendee'] = 'You\'re not listed as an attendee of this event';
-$labels['eventcancelled'] = 'The event has been cancelled';
-$labels['saveincalendar'] = 'save in';
+$labels['importtocalendar'] = 'Salvesta minu kalendrisse';
+$labels['removefromcalendar'] = 'Kustuta kalendrist';
+$labels['updateattendeestatus'] = 'Värskenda osavõtja staatust';
+$labels['acceptinvitation'] = 'Kas kutse vastu võtta?';
+$labels['youhaveaccepted'] = 'Oled kutse vastu võtnud';
+$labels['youhavetentative'] = 'Oled kutse tingimisi vastu võtnud';
+$labels['youhavedeclined'] = 'Oled kutsest keeldunud';
+$labels['notanattendee'] = 'Sa ei ole osavõtjate nimekirjas';
+$labels['eventcancelled'] = 'Sündmus on tühistatud';
+$labels['saveincalendar'] = 'salvesta';
 
 // event dialog tabs
-$labels['tabsummary'] = 'Summary';
-$labels['tabrecurrence'] = 'Recurrence';
-$labels['tabattendees'] = 'Participants';
-$labels['tabattachments'] = 'Attachments';
-$labels['tabsharing'] = 'Sharing';
+$labels['tabsummary'] = 'Kokkuvõte';
+$labels['tabrecurrence'] = 'Kordumine';
+$labels['tabattendees'] = 'Osavõtjad';
+$labels['tabattachments'] = 'Manused';
+$labels['tabsharing'] = 'Jagamine';
 
 // messages
-$labels['deleteventconfirm'] = 'Do you really want to delete this event?';
-$labels['deletecalendarconfirm'] = 'Do you really want to delete this calendar with all its events?';
-$labels['savingdata'] = 'Saving data...';
-$labels['errorsaving'] = 'Failed to save changes.';
-$labels['operationfailed'] = 'The requested operation failed.';
-$labels['invalideventdates'] = 'Invalid dates entered! Please check your input.';
-$labels['invalidcalendarproperties'] = 'Invalid calendar properties! Please set a valid name.';
-$labels['searchnoresults'] = 'No events found in the selected calendars.';
-$labels['successremoval'] = 'The event has been deleted successfully.';
-$labels['successrestore'] = 'The event has been restored successfully.';
-$labels['errornotifying'] = 'Failed to send notifications to event participants';
-$labels['errorimportingevent'] = 'Failed to import the event';
-$labels['newerversionexists'] = 'A newer version of this event already exists! Aborted.';
-$labels['nowritecalendarfound'] = 'No calendar found to save the event';
-$labels['importedsuccessfully'] = 'The event was successfully added to \'$calendar\'';
-$labels['attendeupdateesuccess'] = 'Successfully updated the participant\'s status';
-$labels['itipsendsuccess'] = 'Invitation sent to participants.';
-$labels['itipresponseerror'] = 'Failed to send the response to this event invitation';
-$labels['itipinvalidrequest'] = 'This invitation is no longer valid';
-$labels['sentresponseto'] = 'Successfully sent invitation response to $mailto';
-$labels['localchangeswarning'] = 'You are about to make changes that will only be reflected on your personal calendar';
-$labels['importsuccess'] = 'Successfully imported $nr events';
-$labels['importnone'] = 'No events found to be imported';
-$labels['importerror'] = 'An error occured while importing';
-$labels['aclnorights'] = 'You do not have administrator rights on this calendar.';
+$labels['deleteventconfirm'] = 'Kas tõsti kustutada see sündmus?';
+$labels['deletecalendarconfirm'] = 'Kas tõesti kustutada see kalender koos kõigi sündmustega?';
+$labels['savingdata'] = 'Andmete salvestamine...';
+$labels['errorsaving'] = 'Andmete salvestamine ebaõnnestus.';
+$labels['operationfailed'] = 'Operatsioon nurjus.';
+$labels['invalideventdates'] = 'Kuupäev on vigane! Kontrollige vormingut.';
+$labels['invalidcalendarproperties'] = 'Vigane kalendri atribuut! Valige sobiv atribuut.';
+$labels['searchnoresults'] = 'Sellest kalendrist ei leitud ühtki sündmust.';
+$labels['successremoval'] = 'Sündmus on edukalt kustutatud.';
+$labels['successrestore'] = 'Sündmus on edukalt taastatud.';
+$labels['errornotifying'] = 'Teate saatmine osavõtjaile ebaõnnestus.';
+$labels['errorimportingevent'] = 'Sündmuse import ebaõnnestus.';
+$labels['newerversionexists'] = 'Uuem versioon sündmusest on juba olemas! Täitmine peatatud.';
+$labels['nowritecalendarfound'] = 'Kirjutamiseks sobivat kalendrit ei leitud.';
+$labels['importedsuccessfully'] = 'Sündmus on edukalt lisatud kalendrisse \'$calendar\'';
+$labels['attendeupdateesuccess'] = 'Osavõtja staatus on uuendatud';
+$labels['itipsendsuccess'] = 'Kutse on saadetud osavõtjaile.';
+$labels['itipresponseerror'] = 'Vastuse saatmine kutsele ebaõnnestus.';
+$labels['itipinvalidrequest'] = 'See kutse pole enam kehtiv.';
+$labels['sentresponseto'] = 'Vastus kutsele on edukalt saadetud aadressile $mailto';
+$labels['localchangeswarning'] = 'Muutuseid on näha ainult teie isiklikus kalendris.';
+$labels['importsuccess'] = 'Edukalt imporditud $nr sündmust';
+$labels['importnone'] = 'Ühtki imporditavat sündmust ei leitud';
+$labels['importerror'] = 'Importimise käigus tekkis viga';
+$labels['aclnorights'] = 'Ei ole selle kalendri administreerimisõigusi.';
 
 // recurrence form
-$labels['repeat'] = 'Repeat';
-$labels['frequency'] = 'Repeat';
-$labels['never'] = 'never';
-$labels['daily'] = 'daily';
-$labels['weekly'] = 'weekly';
-$labels['monthly'] = 'monthly';
-$labels['yearly'] = 'annually';
-$labels['every'] = 'Every';
-$labels['days'] = 'day(s)';
-$labels['weeks'] = 'week(s)';
-$labels['months'] = 'month(s)';
-$labels['years'] = 'year(s) in:';
+$labels['repeat'] = 'Korda';
+$labels['frequency'] = 'Sagedus';
+$labels['never'] = 'mitte kunagi';
+$labels['daily'] = 'iga päev';
+$labels['weekly'] = 'nädalati';
+$labels['monthly'] = 'iga kuu';
+$labels['yearly'] = 'iga aasta';
+$labels['every'] = 'Iga';
+$labels['days'] = 'päevad';
+$labels['weeks'] = 'nädalad';
+$labels['months'] = 'kuud';
+$labels['years'] = 'aastad:';
 $labels['bydays'] = 'On';
 $labels['untildate'] = 'the';
-$labels['each'] = 'Each';
-$labels['onevery'] = 'On every';
-$labels['onsamedate'] = 'On the same date';
-$labels['forever'] = 'forever';
-$labels['recurrencend'] = 'until';
-$labels['forntimes'] = 'for $nr time(s)';
-$labels['first'] = 'first';
-$labels['second'] = 'second';
-$labels['third'] = 'third';
-$labels['fourth'] = 'fourth';
-$labels['last'] = 'last';
-$labels['dayofmonth'] = 'Day of month';
+$labels['each'] = 'Iga';
+$labels['onevery'] = 'Igal';
+$labels['onsamedate'] = 'samal kuupäeval';
+$labels['forever'] = 'alati';
+$labels['recurrencend'] = 'kuni';
+$labels['forntimes'] = ' $nr korda';
+$labels['first'] = 'esimesel';
+$labels['second'] = 'teisel';
+$labels['third'] = 'kolmandal';
+$labels['fourth'] = 'neljandal';
+$labels['last'] = 'viimasel';
+$labels['dayofmonth'] = 'Kuupäev';
 
-$labels['changeeventconfirm'] = 'Change event';
-$labels['removeeventconfirm'] = 'Remove event';
-$labels['changerecurringeventwarning'] = 'This is a recurring event. Would you like to edit the current event only, this and all future occurences, all occurences or save it as a new event?';
-$labels['removerecurringeventwarning'] = 'This is a recurring event. Would you like to remove the current event only, this and all future occurences or all occurences of this event?';
-$labels['currentevent'] = 'Current';
-$labels['futurevents'] = 'Future';
-$labels['allevents'] = 'All';
-$labels['saveasnew'] = 'Save as new';
+$labels['changeeventconfirm'] = 'Muuda sündmust';
+$labels['removeeventconfirm'] = 'Kustuta sündmus';
+$labels['changerecurringeventwarning'] = 'See on korduv sündmus. Kas muuta seda sündmust, seda ja kõiki järgnevaid, või salvestada uue sündmusena?';
+$labels['removerecurringeventwarning'] = 'See on korduv sündmus. Kas kustudada see sündmus, see ja kõik järgnevad sündmused?';
+$labels['currentevent'] = 'Praegune';
+$labels['futurevents'] = 'Tulevikusündmused';
+$labels['allevents'] = 'Kõik sündmused';
+$labels['saveasnew'] = 'Salvesta uuena';
 
 ?>


commit e466d526d08cc931b8503763269345b0114a4f1c
Author: Aleksander Machniak <machniak at kolabsys.com>
Date:   Sat May 11 09:27:24 2013 +0200

    Fix PHP warning when saving calendar settings in case all categories are removed from the list

diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
index f8451a1..6537c53 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -507,11 +507,13 @@ class calendar extends rcube_plugin
         foreach ($this->driver->list_categories() as $name => $color) {
           $old_categories[md5($name)] = $name;
         }
-        $categories = get_input_value('_categories', RCUBE_INPUT_POST);
-        $colors = get_input_value('_colors', RCUBE_INPUT_POST);
+
+        $categories = (array) get_input_value('_categories', RCUBE_INPUT_POST);
+        $colors     = (array) get_input_value('_colors', RCUBE_INPUT_POST);
+
         foreach ($categories as $key => $name) {
           $color = preg_replace('/^#/', '', strval($colors[$key]));
-        
+
           // rename categories in existing events -> driver's job
           if ($oldname = $old_categories[$key]) {
             $this->driver->replace_category($oldname, $name, $color);
@@ -519,7 +521,7 @@ class calendar extends rcube_plugin
           }
           else
             $this->driver->add_category($name, $color);
-        
+
           $new_categories[$name] = $color;
         }
 
@@ -527,7 +529,7 @@ class calendar extends rcube_plugin
         foreach ((array)$old_categories[$key] as $key => $name) {
           $this->driver->remove_category($name);
         }
-        
+
         $p['prefs']['calendar_categories'] = $new_categories;
       }
     }


commit 29347e419a8113ad0fe48ce02a12fd57d7463733
Author: Aleksander Machniak <machniak at kolabsys.com>
Date:   Sat May 11 09:24:42 2013 +0200

    Remove deprecated functions usage

diff --git a/plugins/libcalendaring/libcalendaring.php b/plugins/libcalendaring/libcalendaring.php
index 48a85b4..5185f17 100644
--- a/plugins/libcalendaring/libcalendaring.php
+++ b/plugins/libcalendaring/libcalendaring.php
@@ -137,16 +137,16 @@ class libcalendaring extends rcube_plugin
 
         // localization
         $settings['days'] = array(
-            rcube_label('sunday'),   rcube_label('monday'),
-            rcube_label('tuesday'),  rcube_label('wednesday'),
-            rcube_label('thursday'), rcube_label('friday'),
-            rcube_label('saturday')
+            $this->rc->gettext('sunday'),   $this->rc->gettext('monday'),
+            $this->rc->gettext('tuesday'),  $this->rc->gettext('wednesday'),
+            $this->rc->gettext('thursday'), $this->rc->gettext('friday'),
+            $this->rc->gettext('saturday')
         );
         $settings['days_short'] = array(
-            rcube_label('sun'), rcube_label('mon'),
-            rcube_label('tue'), rcube_label('wed'),
-            rcube_label('thu'), rcube_label('fri'),
-            rcube_label('sat')
+            $this->rc->gettext('sun'), $this->rc->gettext('mon'),
+            $this->rc->gettext('tue'), $this->rc->gettext('wed'),
+            $this->rc->gettext('thu'), $this->rc->gettext('fri'),
+            $this->rc->gettext('sat')
         );
         $settings['months'] = array(
             $this->rc->gettext('longjan'), $this->rc->gettext('longfeb'),
@@ -310,20 +310,25 @@ class libcalendaring extends rcube_plugin
         list($trigger, $action) = explode(':', $alarm);
 
         $text = '';
+        $rcube = rcube::get_instance();
+
         switch ($action) {
         case 'EMAIL':
-            $text = rcube_label('libcalendaring.alarmemail');
+            $text = $rcube->gettext('libcalendaring.alarmemail');
             break;
         case 'DISPLAY':
-            $text = rcube_label('libcalendaring.alarmdisplay');
+            $text = $rcube->gettext('libcalendaring.alarmdisplay');
             break;
         }
 
         if (preg_match('/@(\d+)/', $trigger, $m)) {
-            $text .= ' ' . rcube_label(array('name' => 'libcalendaring.alarmat', 'vars' => array('datetime' => format_date($m[1]))));
+            $text .= ' ' . $rcube->gettext(array(
+                'name' => 'libcalendaring.alarmat',
+                'vars' => array('datetime' => $rcube->format_date($m[1]))
+            ));
         }
         else if ($val = self::parse_alaram_value($trigger)) {
-            $text .= ' ' . intval($val[0]) . ' ' . rcube_label('libcalendaring.trigger' . $val[1]);
+            $text .= ' ' . intval($val[0]) . ' ' . $rcube->gettext('libcalendaring.trigger' . $val[1]);
         }
         else
             return false;
@@ -408,8 +413,8 @@ class libcalendaring extends rcube_plugin
      */
     public function alarms_action()
     {
-//        $action = get_input_value('action', RCUBE_INPUT_GPC);
-        $data  = get_input_value('data', RCUBE_INPUT_POST, true);
+//        $action = rcube_utils::get_input_value('action', rcube_utils::INPUT_GPC);
+        $data  = rcube_utils::get_input_value('data', rcube_utils::INPUT_POST, true);
 
         $data['ids'] = explode(',', $data['id']);
         $plugin = $this->rc->plugins->exec_hook('dismiss_alarms', $data);
@@ -477,11 +482,11 @@ class libcalendaring extends rcube_plugin
     {
         // Upload progress update
         if (!empty($_GET['_progress'])) {
-            rcube_upload_progress();
+            $this->rc->upload_progress();
         }
 
-        $recid = $id_prefix . get_input_value('_id', RCUBE_INPUT_GPC);
-        $uploadid = get_input_value('_uploadid', RCUBE_INPUT_GPC);
+        $recid = $id_prefix . rcube_utils::get_input_value('_id', rcube_utils::INPUT_GPC);
+        $uploadid = rcube_utils::get_input_value('_uploadid', rcube_utils::INPUT_GPC);
 
         if (!is_array($_SESSION[$session_key]) || $_SESSION[$session_key]['id'] != $recid) {
             $_SESSION[$session_key] = array();
@@ -502,7 +507,7 @@ class libcalendaring extends rcube_plugin
                     'path' => $filepath,
                     'size' => $_FILES['_attachments']['size'][$i],
                     'name' => $_FILES['_attachments']['name'][$i],
-                    'mimetype' => rc_mime_content_type($filepath, $_FILES['_attachments']['name'][$i], $_FILES['_attachments']['type'][$i]),
+                    'mimetype' => rcube_mime::file_content_type($filepath, $_FILES['_attachments']['name'][$i], $_FILES['_attachments']['type'][$i]),
                     'group' => $recid,
                 );
 
@@ -519,18 +524,18 @@ class libcalendaring extends rcube_plugin
                   if (($icon = $_SESSION[$session_key . '_deleteicon']) && is_file($icon)) {
                       $button = html::img(array(
                           'src' => $icon,
-                          'alt' => rcube_label('delete')
+                          'alt' => $this->rc->gettext('delete')
                       ));
                   }
                   else {
-                      $button = Q(rcube_label('delete'));
+                      $button = Q($this->rc->gettext('delete'));
                   }
 
                   $content = html::a(array(
                       'href' => "#delete",
                       'class' => 'delete',
                       'onclick' => sprintf("return %s.remove_from_attachment_list('rcmfile%s')", JS_OBJECT_NAME, $id),
-                      'title' => rcube_label('delete'),
+                      'title' => $this->rc->gettext('delete'),
                   ), $button);
 
                   $content .= Q($attachment['name']);
@@ -544,14 +549,14 @@ class libcalendaring extends rcube_plugin
               }
               else {  // upload failed
                   if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) {
-                    $msg = rcube_label(array('name' => 'filesizeerror', 'vars' => array(
+                    $msg = $this->rc->gettext(array('name' => 'filesizeerror', 'vars' => array(
                         'size' => show_bytes(parse_bytes(ini_get('upload_max_filesize'))))));
                   }
                   else if ($attachment['error']) {
                       $msg = $attachment['error'];
                   }
                   else {
-                      $msg = rcube_label('fileuploaderror');
+                      $msg = $this->rc->gettext('fileuploaderror');
                   }
 
                   $this->rc->output->command('display_message', $msg, 'error');
@@ -563,10 +568,10 @@ class libcalendaring extends rcube_plugin
             // if filesize exceeds post_max_size then $_FILES array is empty,
             // show filesizeerror instead of fileuploaderror
             if ($maxsize = ini_get('post_max_size'))
-                $msg = rcube_label(array('name' => 'filesizeerror', 'vars' => array(
+                $msg = $this->rc->gettext(array('name' => 'filesizeerror', 'vars' => array(
                     'size' => show_bytes(parse_bytes($maxsize)))));
             else
-                $msg = rcube_label('fileuploaderror');
+                $msg = $this->rc->gettext('fileuploaderror');
 
             $this->rc->output->command('display_message', $msg, 'error');
             $this->rc->output->command('remove_from_attachment_list', $uploadid);
@@ -660,7 +665,7 @@ class libcalendaring extends rcube_plugin
     public function attachment_loading_page()
     {
         $url = str_replace('&_preload=1', '', $_SERVER['REQUEST_URI']);
-        $message = rcube_label('loadingdata');
+        $message = $this->rc->gettext('loadingdata');
 
         header('Content-Type: text/html; charset=' . RCMAIL_CHARSET);
         print "<html>\n<head>\n"
@@ -691,13 +696,13 @@ class libcalendaring extends rcube_plugin
         $table = new html_table(array('cols' => 3));
 
         if (!empty($this->attachment['name'])) {
-            $table->add('title', Q(rcube_label('filename')));
+            $table->add('title', Q($this->rc->gettext('filename')));
             $table->add('header', Q($this->attachment['name']));
-            $table->add('download-link', html::a('?'.str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING']), Q(rcube_label('download'))));
+            $table->add('download-link', html::a('?'.str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING']), Q($this->rc->gettext('download'))));
         }
 
         if (!empty($this->attachment['size'])) {
-            $table->add('title', Q(rcube_label('filesize')));
+            $table->add('title', Q($this->rc->gettext('filesize')));
             $table->add('header', Q(show_bytes($this->attachment['size'])));
         }
 


commit 28138807bf2bb97856ebe0a916abc7ba60615e7f
Author: Aleksander Machniak <machniak at kolabsys.com>
Date:   Sat May 11 09:09:13 2013 +0200

    No need to set_env in ajax request (limits size of ajax response)

diff --git a/plugins/libcalendaring/libcalendaring.php b/plugins/libcalendaring/libcalendaring.php
index eb72e70..48a85b4 100644
--- a/plugins/libcalendaring/libcalendaring.php
+++ b/plugins/libcalendaring/libcalendaring.php
@@ -85,10 +85,11 @@ class libcalendaring extends rcube_plugin
 
         // include client scripts and styles
         if ($this->rc->output) {
-            $this->include_script('libcalendaring.js');
-            $this->rc->output->set_env('libcal_settings', $this->load_settings());
-
-            $this->include_stylesheet($this->local_skin_path() . '/libcal.css');
+            if ($this->rc->output->type == 'html') {
+                $this->rc->output->set_env('libcal_settings', $this->load_settings());
+                $this->include_script('libcalendaring.js');
+                $this->include_stylesheet($this->local_skin_path() . '/libcal.css');
+            }
 
             // add hook to display alarms
             $this->add_hook('refresh', array($this, 'refresh'));


commit db0e50ce28c876f3672235fbd974d6089e63373c
Author: Aleksander Machniak <machniak at kolabsys.com>
Date:   Wed May 8 14:22:18 2013 +0200

    Fix "incompat. declaration" error - revert part of 27e57c73352e1966918cdfe567c32c5a4844be82

diff --git a/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php b/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php
index 35312d9..5437056 100644
--- a/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php
+++ b/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php
@@ -245,11 +245,12 @@ class rcube_kolab_contacts extends rcube_addressbook
     /**
      * List the current set of contact records
      *
-     * @param  int    Only return this number of records, use negative values for tail
+     * @param array List of cols to show
+     * @param  int  Only return this number of records, use negative values for tail
      *
      * @return array  Indexed list of contact records, each a hash array
      */
-    public function list_records($subset = 0)
+    public function list_records($cols = null, $subset = 0)
     {
         $this->result = new rcube_result_set(0, ($this->list_page-1) * $this->page_size);;
 


commit 6c786252a077409cea0769fda4ec3f27337e8e16
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed May 8 09:07:29 2013 +0200

    Fix stupidy

diff --git a/plugins/calendar/drivers/database/database_driver.php b/plugins/calendar/drivers/database/database_driver.php
index e1de282..5f00b1d 100644
--- a/plugins/calendar/drivers/database/database_driver.php
+++ b/plugins/calendar/drivers/database/database_driver.php
@@ -534,6 +534,7 @@ class database_driver extends calendar_driver
       
       $recurrence = new calendar_recurrence($this->cal, $event);
 
+      $count = 0;
       $duration = $event['start']->diff($event['end']);
       while ($next_start = $recurrence->next_start()) {
         $next_start->setTimezone($this->server_timezone);
@@ -561,7 +562,6 @@ class database_driver extends calendar_driver
           break;
         
         // stop adding events for inifinite recurrence after 20 years
-        $count = 0;
         if (++$count > 999 || (!$recurrence->recurEnd && !$recurrence->recurCount && $next_start->format('Y') > date('Y') + 20))
           break;
       }


commit ef4a4efd884604da79efe6340e53bd9867bd8a55
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed May 8 08:55:58 2013 +0200

    Small fixes after static code analysis (#1851)

diff --git a/plugins/calendar/drivers/database/database_driver.php b/plugins/calendar/drivers/database/database_driver.php
index 3b949ad..e1de282 100644
--- a/plugins/calendar/drivers/database/database_driver.php
+++ b/plugins/calendar/drivers/database/database_driver.php
@@ -562,7 +562,7 @@ class database_driver extends calendar_driver
         
         // stop adding events for inifinite recurrence after 20 years
         $count = 0;
-        if (++$count > 999 || (!$recurrence->recurEnd && !$recurrence->recurCount && $next->year > date('Y') + 20))
+        if (++$count > 999 || (!$recurrence->recurEnd && !$recurrence->recurCount && $next_start->format('Y') > date('Y') + 20))
           break;
       }
     }
@@ -638,7 +638,7 @@ class database_driver extends calendar_driver
             $update_master = true;
             
             // delete this and all future instances
-            $fromdate = clone $old['start'];
+            $fromdate = clone $event['start'];
             $fromdate->setTimezone($this->server_timezone);
             $query = $this->rc->db->query(
               "DELETE FROM " . $this->db_events . "
diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php
index 0f564ae..ce6e0ec 100644
--- a/plugins/calendar/drivers/kolab/kolab_calendar.php
+++ b/plugins/calendar/drivers/kolab/kolab_calendar.php
@@ -379,7 +379,7 @@ class kolab_calendar
         rcube::raise_error(array(
           'code' => 600, 'type' => 'php',
           'file' => __FILE__, 'line' => __LINE__,
-          'message' => "Error undeleting the event object $uid from the Kolab server"),
+          'message' => "Error undeleting the event object $event[id] from the Kolab server"),
         true, false);
     }
 
@@ -646,7 +646,7 @@ class kolab_calendar
       if (is_array($prop)) {
           foreach ($prop as $key => $val) {
               if (is_numeric($key)) {
-                  $out .= self::_complex2string($val, $fields);
+                  $out .= self::_complex2string($val);
               }
               else if (!in_array($key, $ignorekeys)) {
                 $out .= $val . ' ';


commit 1ce64b4b8f5e28a3cc2706dad2ebd94034648dc6
Author: Aleksander Machniak <machniak at kolabsys.com>
Date:   Fri Jun 21 12:03:24 2013 +0200

    Fix undefined variable related errors caught in static code analysis
    
    Conflicts:
    
    	plugins/libkolab/lib/kolab_storage_cache.php

diff --git a/plugins/libkolab/lib/kolab_format_event.php b/plugins/libkolab/lib/kolab_format_event.php
index 38bd0ce..2db745c 100644
--- a/plugins/libkolab/lib/kolab_format_event.php
+++ b/plugins/libkolab/lib/kolab_format_event.php
@@ -166,7 +166,7 @@ class kolab_format_event extends kolab_format_xcal
         if ($status == kolabformat::StatusTentative)
           $object['free_busy'] = 'tentative';
         else if ($status == kolabformat::StatusCancelled)
-          $objec['cancelled'] = true;
+          $object['cancelled'] = true;
 
         // handle attachments
         $vattach = $this->obj->attachments();
@@ -246,16 +246,15 @@ class kolab_format_event extends kolab_format_xcal
      */
     private function compact_exception($exception, $master)
     {
-      static $forbidden = array('recurrence','organizer','attendees','sequence');
+      $forbidden = array('recurrence','organizer','attendees','sequence');
 
-      $out = $exception;
-      foreach ($exception as $prop => $val) {
-        if (in_array($prop, $forbidden)) {
-          unset($out[$prop]);
+      foreach ($forbidden as $prop) {
+        if (array_key_exists($prop, $exception)) {
+          unset($exception[$prop]);
         }
       }
 
-      return $out;
+      return $exception;
     }
 
     /**
diff --git a/plugins/libkolab/lib/kolab_format_xcal.php b/plugins/libkolab/lib/kolab_format_xcal.php
index 1534363..b37b727 100644
--- a/plugins/libkolab/lib/kolab_format_xcal.php
+++ b/plugins/libkolab/lib/kolab_format_xcal.php
@@ -103,7 +103,7 @@ abstract class kolab_format_xcal extends kolab_format
             'title'       => $this->obj->summary(),
             'location'    => $this->obj->location(),
             'description' => $this->obj->description(),
-            'status'      => $this->status_map[$this->obj->status()],
+            'status'      => $status_map[$this->obj->status()],
             'sensitivity' => $sensitivity_map[$this->obj->classification()],
             'priority'    => $this->obj->priority(),
             'categories'  => self::vector2array($this->obj->categories()),
diff --git a/plugins/libkolab/lib/kolab_storage.php b/plugins/libkolab/lib/kolab_storage.php
index a569af7..c736607 100644
--- a/plugins/libkolab/lib/kolab_storage.php
+++ b/plugins/libkolab/lib/kolab_storage.php
@@ -37,7 +37,6 @@ class kolab_storage
     private static $subscriptions;
     private static $states;
     private static $config;
-    private static $cache;
     private static $imap;
 
 
@@ -289,7 +288,7 @@ class kolab_storage
         else {
             // these characters are problematic e.g. when used in LIST/LSUB
             foreach (array($delimiter, '%', '*') as $char) {
-                if (strpos($folder, $delimiter) !== false) {
+                if (strpos($folder, $char) !== false) {
                     self::$last_error = 'forbiddencharacter';
                     return false;
                 }
diff --git a/plugins/libkolab/lib/kolab_storage_cache.php b/plugins/libkolab/lib/kolab_storage_cache.php
index ef4dd22..2782428 100644
--- a/plugins/libkolab/lib/kolab_storage_cache.php
+++ b/plugins/libkolab/lib/kolab_storage_cache.php
@@ -157,7 +157,7 @@ class kolab_storage_cache
     {
         // delegate to another cache instance
         if ($foldername && $foldername != $this->folder->name) {
-            return kolab_storage::get_folder($foldername)->cache->get($msguid, $object);
+            return kolab_storage::get_folder($foldername)->cache->get($msguid, $type);
         }
 
         // load object if not in memory
@@ -274,12 +274,12 @@ class kolab_storage_cache
      * @param string Entry's Object UID
      * @param string Target IMAP folder to move it to
      */
-    public function move($msguid, $objuid, $target_folder)
+    public function move($msguid, $uid, $target_folder)
     {
         $target = kolab_storage::get_folder($target_folder);
 
         // resolve new message UID in target folder
-        if ($new_msguid = $target->cache->uid2msguid($objuid)) {
+        if ($new_msguid = $target->cache->uid2msguid($uid)) {
             $this->db->query(
                 "UPDATE kolab_cache SET resource=?, msguid=? ".
                 "WHERE resource=? AND msguid=? AND type<>?",
diff --git a/plugins/libkolab/lib/kolab_storage_folder.php b/plugins/libkolab/lib/kolab_storage_folder.php
index 7e9c379..98f8511 100644
--- a/plugins/libkolab/lib/kolab_storage_folder.php
+++ b/plugins/libkolab/lib/kolab_storage_folder.php
@@ -54,7 +54,6 @@ class kolab_storage_folder
     private $idata;
     private $owner;
     private $resource_uri;
-    private $uid2msg = array();
 
 
     /**
@@ -160,7 +159,6 @@ class kolab_storage_folder
             break;
 
         default:
-            $owner = '';
             list($prefix, $user) = explode($this->imap->get_hierarchy_delimiter(), $info['name']);
             if (strpos($user, '@') === false) {
                 $domain = strstr($rcmail->get_user_name(), '@');
@@ -622,7 +620,7 @@ class kolab_storage_folder
         }
 
         // save contact photo to attachment for Kolab2 format
-        if (kolab_storage::$version == '2.0' && $object['photo'] && !$existing_photo) {
+        if (kolab_storage::$version == '2.0' && $object['photo']) {
             $attkey = 'kolab-picture.png';  // this file name is hard-coded in libkolab/kolabformatV2/contact.cpp
             $object['_attachments'][$attkey] = array(
                 'mimetype'=> rc_image_content_type($object['photo']),
@@ -830,7 +828,7 @@ class kolab_storage_folder
     public function move($uid, $target_folder)
     {
         if ($msguid = $this->cache->uid2msguid($uid)) {
-            if ($success = $this->imap->move_message($msguid, $target_folder, $this->name)) {
+            if ($this->imap->move_message($msguid, $target_folder, $this->name)) {
                 $this->cache->move($msguid, $uid, $target_folder);
                 return true;
             }
diff --git a/plugins/tasklist/drivers/database/tasklist_database_driver.php b/plugins/tasklist/drivers/database/tasklist_database_driver.php
index ae2b5e2..4d335ee 100644
--- a/plugins/tasklist/drivers/database/tasklist_database_driver.php
+++ b/plugins/tasklist/drivers/database/tasklist_database_driver.php
@@ -30,7 +30,6 @@ class tasklist_database_driver extends tasklist_driver
 
     private $rc;
     private $plugin;
-    private $cache = array();
     private $lists = array();
     private $list_ids = '';
 
@@ -177,12 +176,13 @@ class tasklist_database_driver extends tasklist_driver
     public function remove_list($prop)
     {
         $list_id = $prop['id'];
+
         if ($this->lists[$list_id]) {
             // delete all tasks linked with this list
             $this->rc->db->query(
                 "DELETE FROM " . $this->db_tasks . "
                  WHERE tasklist_id=?",
-                $lisr_id
+                $list_id
             );
 
             // delete list record
@@ -305,8 +305,6 @@ class tasklist_database_driver extends tasklist_driver
 
         $tasks = array();
         if (!empty($list_ids)) {
-            $datecol = $this->rc->db->quote_identifier('date');
-            $timecol = $this->rc->db->quote_identifier('time');
             $result = $this->rc->db->query(sprintf(
                 "SELECT * FROM " . $this->db_tasks . "
                  WHERE tasklist_id IN (%s)
diff --git a/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php b/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php
index a713311..078243a 100644
--- a/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php
+++ b/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php
@@ -68,7 +68,7 @@ class tasklist_kolab_driver extends tasklist_driver
         // convert to UTF8 and sort
         $names = array();
         $default_folder = null;
-        foreach ($this->folders as $i => $folder) {
+        foreach ($this->folders as $folder) {
             $names[$folder->name] = rcube_charset::convert($folder->name, 'UTF7-IMAP');
             $this->folders[$folder->name] = $folder;
             if ($folder->default)
diff --git a/plugins/tasklist/drivers/tasklist_driver.php b/plugins/tasklist/drivers/tasklist_driver.php
index 40d0d9b..cc9a409 100644
--- a/plugins/tasklist/drivers/tasklist_driver.php
+++ b/plugins/tasklist/drivers/tasklist_driver.php
@@ -266,7 +266,7 @@ abstract class tasklist_driver
     public function tasklist_edit_form($formfields)
     {
         $html = '';
-        foreach ($formfields as $prop => $field) {
+        foreach ($formfields as $field) {
             $html .= html::div('form-section',
                 html::label($field['id'], $field['label']) .
                 $field['value']);
diff --git a/plugins/tasklist/tasklist.php b/plugins/tasklist/tasklist.php
index 35c528c..6ebddc4 100644
--- a/plugins/tasklist/tasklist.php
+++ b/plugins/tasklist/tasklist.php
@@ -247,7 +247,7 @@ class tasklist extends rcube_plugin
             break;
 
         case 'collapse':
-            if ($collapsed = intval(get_input_value('collapsed', RCUBE_INPUT_GPC))) {
+            if (intval(get_input_value('collapsed', RCUBE_INPUT_GPC))) {
                 $this->collapsed_tasks[] = $rec['id'];
             }
             else {
@@ -383,7 +383,7 @@ class tasklist extends rcube_plugin
         }
 
         // alarms cannot work without a date
-        if ($rec['alarms'] && !$rec['date'] && !$rec['startdate'] && strpos($task['alarms'], '@') === false)
+        if ($rec['alarms'] && !$rec['date'] && !$rec['startdate'] && strpos($rec['alarms'], '@') === false)
             $rec['alarms'] = '';
 
         $attachments = array();


commit b9cd4b9afc675d51b5c37a873ae9ef4687761f97
Author: Aleksander Machniak <machniak at kolabsys.com>
Date:   Fri Jun 21 12:01:59 2013 +0200

    Fix undefined or unused variable errors caught in static code analysis
    
    Conflicts:
    
    	plugins/kolab_auth/kolab_auth.php
    	plugins/pdfviewer/pdfviewer.php

diff --git a/plugins/kolab_activesync/kolab_activesync.php b/plugins/kolab_activesync/kolab_activesync.php
index 0238f11..d0cd63c 100644
--- a/plugins/kolab_activesync/kolab_activesync.php
+++ b/plugins/kolab_activesync/kolab_activesync.php
@@ -82,9 +82,9 @@ class kolab_activesync extends rcube_plugin
             if (!$err) {
                 // iterate over folders list and update metadata if necessary
                 // old subscriptions
-                foreach ($this->folder_meta() as $folder => $meta) {
+                foreach (array_keys($this->folder_meta()) as $folder) {
                     $err |= !$this->folder_set($folder, $imei, intval($subscriptions[$folder]));
-                    unset($subsciptions[$folder]);
+                    unset($subscriptions[$folder]);
                 }
                 // new subscription
                 foreach ($subscriptions as $folder => $flag) {
diff --git a/plugins/kolab_activesync/kolab_activesync_ui.php b/plugins/kolab_activesync/kolab_activesync_ui.php
index a191255..5b97979 100644
--- a/plugins/kolab_activesync/kolab_activesync_ui.php
+++ b/plugins/kolab_activesync/kolab_activesync_ui.php
@@ -167,6 +167,7 @@ class kolab_activesync_ui
                 }
             }
 
+            $folder_id = 'rcmf' . html_identifier($folder);
             $names[] = $origname;
             $classes = array('mailbox');
 
@@ -175,9 +176,6 @@ class kolab_activesync_ui
                 $classes[] = $folder_class;
             }
 
-            $folder_id = 'rcmf' . html_identifier($folder);
-            $padding = str_repeat('    ', $level);
-
             $table->add_row();
             $table->add('subscription', $checkbox_sync->show(
                 !empty($subscribed[$folder]) ? $folder : null,
@@ -189,7 +187,7 @@ class kolab_activesync_ui
                     array('value' => $folder, 'id' => $folder_id.'_alarm')));
             }
 
-            $table->add(join(' ', $classes), html::label($folder_id, $padding . $foldername));
+            $table->add(join(' ', $classes), html::label($folder_id, $foldername));
         }
 
         return $table->show();
diff --git a/plugins/kolab_addressbook/kolab_addressbook.php b/plugins/kolab_addressbook/kolab_addressbook.php
index 82346f3..3f85fa7 100644
--- a/plugins/kolab_addressbook/kolab_addressbook.php
+++ b/plugins/kolab_addressbook/kolab_addressbook.php
@@ -173,7 +173,7 @@ class kolab_addressbook extends rcube_plugin
         }
 
         $kolab_sources = array();
-        foreach ($this->_list_sources() as $abook_id => $abook) {
+        foreach (array_keys($this->_list_sources()) as $abook_id) {
             if (!in_array($abook_id, $sources))
                 $kolab_sources[] = $abook_id;
         }
@@ -250,7 +250,7 @@ class kolab_addressbook extends rcube_plugin
 
             asort($names, SORT_LOCALE_STRING);
 
-            foreach ($names as $utf7name => $name) {
+            foreach (array_keys($names) as $utf7name) {
                 // create instance of rcube_contacts
                 $abook_id = kolab_storage::folder_id($utf7name);
                 $abook = new rcube_kolab_contacts($utf7name);
@@ -305,9 +305,10 @@ class kolab_addressbook extends rcube_plugin
 
     private function _sort_form_fields($contents)
     {
-      $block = array();
+      $block    = array();
       $contacts = reset($this->sources);
-      foreach ($contacts->coltypes as $col => $prop) {
+
+      foreach (array_keys($contacts->coltypes) as $col) {
           if (isset($contents[$col]))
               $block[$col] = $contents[$col];
       }
@@ -442,10 +443,10 @@ class kolab_addressbook extends rcube_plugin
             // create display name for the folder (see self::address_sources())
             if (strpos($folder, $delimiter)) {
                 $names = array();
-                foreach ($this->_list_sources() as $abook_id => $abook) {
+                foreach ($this->_list_sources() as $abook) {
                     $realname = $abook->get_realname();
                     // The list can be not updated yet, handle old folder name
-                    if ($type == 'update' && $realname == $oldfolder) {
+                    if ($type == 'update' && $realname == $prop['oldname']) {
                         $abook    = $kolab_folder;
                         $realname = $folder;
                     }
diff --git a/plugins/kolab_addressbook/lib/kolab_addressbook_ui.php b/plugins/kolab_addressbook/lib/kolab_addressbook_ui.php
index a1504fb..7546046 100644
--- a/plugins/kolab_addressbook/lib/kolab_addressbook_ui.php
+++ b/plugins/kolab_addressbook/lib/kolab_addressbook_ui.php
@@ -116,8 +116,9 @@ class kolab_addressbook_ui
 
         $hidden_fields[] = array('name' => '_source', 'value' => $folder);
 
-        $folder = rcube_charset::convert($folder, RCMAIL_CHARSET, 'UTF7-IMAP');
-        $delim  = $_SESSION['imap_delimiter'];
+        $folder  = rcube_charset::convert($folder, RCMAIL_CHARSET, 'UTF7-IMAP');
+        $storage = $this->rc->get_storage();
+        $delim   = $storage->get_hierarchy_delimiter();
 
         if ($this->rc->action == 'plugin.book-save') {
             // save error
@@ -144,7 +145,7 @@ class kolab_addressbook_ui
         if (strlen($folder)) {
             $hidden_fields[] = array('name' => '_oldname', 'value' => $folder);
 
-            $options = $this->rc->get_storage()->folder_info($folder);
+            $options = $storage->folder_info($folder);
         }
 
         $form   = array();
@@ -155,7 +156,7 @@ class kolab_addressbook_ui
         );
 
         if (!empty($options) && ($options['norename'] || $options['protected'])) {
-            $foldername = Q(str_replace($delimiter, ' » ', kolab_storage::object_name($folder)));
+            $foldername = Q(str_replace($delim, ' » ', kolab_storage::object_name($folder)));
         }
         else {
             $foldername = new html_inputfield(array('name' => '_name', 'id' => '_name', 'size' => 30));
diff --git a/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php b/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php
index 48b89be..35312d9 100644
--- a/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php
+++ b/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php
@@ -245,11 +245,11 @@ class rcube_kolab_contacts extends rcube_addressbook
     /**
      * List the current set of contact records
      *
-     * @param  array  List of cols to show
      * @param  int    Only return this number of records, use negative values for tail
+     *
      * @return array  Indexed list of contact records, each a hash array
      */
-    public function list_records($cols=null, $subset=0)
+    public function list_records($subset = 0)
     {
         $this->result = new rcube_result_set(0, ($this->list_page-1) * $this->page_size);;
 
@@ -808,8 +808,9 @@ class rcube_kolab_contacts extends rcube_addressbook
         $this->_fetch_groups(true);
         $list = $this->distlists[$gid];
 
-        foreach ((array)$list['member'] as $i => $member)
+        foreach ((array)$list['member'] as $member) {
             $exists[] = $member['ID'];
+        }
 
         // substract existing assignments from list
         $ids = array_diff($ids, $exists);
diff --git a/plugins/kolab_auth/kolab_auth.php b/plugins/kolab_auth/kolab_auth.php
index fc0158b..f511949 100644
--- a/plugins/kolab_auth/kolab_auth.php
+++ b/plugins/kolab_auth/kolab_auth.php
@@ -337,7 +337,7 @@ class kolab_auth extends rcube_plugin
             // check group
             if (!$isadmin && !empty($group)) {
                 $groups = $ldap->get_record_groups($record['ID']);
-                foreach ($groups as $g) {
+                foreach (array_keys($groups) as $g) {
                     if ($group == rcube_ldap::dn_decode($g)) {
                         $isadmin = true;
                         break;
diff --git a/plugins/kolab_config/kolab_config.php b/plugins/kolab_config/kolab_config.php
index 23188cf..e5f07ad 100644
--- a/plugins/kolab_config/kolab_config.php
+++ b/plugins/kolab_config/kolab_config.php
@@ -68,7 +68,7 @@ class kolab_config extends rcube_plugin
         $this->require_plugin('libkolab');
 
         $this->folders = kolab_storage::get_folders('configuration');
-        foreach ($this->folders as $i => $folder) {
+        foreach ($this->folders as $folder) {
             if ($folder->default) {
                 $this->default = $folder;
                 break;
diff --git a/plugins/kolab_delegation/kolab_delegation.php b/plugins/kolab_delegation/kolab_delegation.php
index 8ec1f8b..156e81f 100644
--- a/plugins/kolab_delegation/kolab_delegation.php
+++ b/plugins/kolab_delegation/kolab_delegation.php
@@ -350,7 +350,6 @@ class kolab_delegation extends rcube_plugin
         asort($list, SORT_LOCALE_STRING);
 
         foreach ($list as $id => $delegate) {
-            $name = $id;
             $table->add_row(array('id' => 'rcmrow' . $id));
             $table->add(null, Q($delegate));
         }
@@ -415,7 +414,6 @@ class kolab_delegation extends rcube_plugin
 
         $folder_data   = $engine->list_folders($delegate['uid']);
         $rights        = array();
-        $folders       = array();
         $folder_groups = array();
 
         foreach ($folder_data as $folder_name => $folder) {
@@ -471,6 +469,7 @@ class kolab_delegation extends rcube_plugin
                 }
             }
 
+            $folder_id = 'rcmf' . html_identifier($folder);
             $names[] = $origname;
             $classes = array('mailbox');
 
@@ -479,9 +478,6 @@ class kolab_delegation extends rcube_plugin
                 $classes[] = $folder_class;
             }
 
-            $folder_id = 'rcmf' . html_identifier($folder);
-            $padding = str_repeat('    ', $level);
-
             $table->add_row();
             $table->add('read', $checkbox_read->show(
                 $rights[$folder] >= kolab_delegation_engine::ACL_READ ? $folder : null,
@@ -490,7 +486,7 @@ class kolab_delegation extends rcube_plugin
                 $rights[$folder] >= kolab_delegation_engine::ACL_WRITE ? $folder : null,
                 array('value' => $folder, 'id' => $folder_id)));
 
-            $table->add(join(' ', $classes), html::label($folder_id, $padding . $foldername));
+            $table->add(join(' ', $classes), html::label($folder_id, $foldername));
         }
 
         return $table->show();
diff --git a/plugins/kolab_delegation/kolab_delegation_engine.php b/plugins/kolab_delegation/kolab_delegation_engine.php
index 7b35f41..26f6b38 100644
--- a/plugins/kolab_delegation/kolab_delegation_engine.php
+++ b/plugins/kolab_delegation/kolab_delegation_engine.php
@@ -60,10 +60,10 @@ class kolab_delegation_engine
         if (!is_array($delegate)) {
             $delegate = $this->delegate_get($delegate);
         }
-        $dn       = $delegate['ID'];
-        $list     = $this->list_delegates();
-        $user     = $this->user();
-        $ldap     = $this->ldap();
+
+        $dn   = $delegate['ID'];
+        $list = $this->list_delegates();
+        $user = $this->user();
 
         if (empty($delegate) || empty($dn)) {
             return false;
@@ -133,7 +133,6 @@ class kolab_delegation_engine
         $delegate = $this->delegate_get($dn);
         $list     = $this->list_delegates();
         $user     = $this->user();
-        $ldap     = $this->ldap();
 
         if (empty($delegate) || !isset($list[$dn])) {
             return false;
diff --git a/plugins/kolab_folders/kolab_folders.php b/plugins/kolab_folders/kolab_folders.php
index cf2bb77..12c0423 100644
--- a/plugins/kolab_folders/kolab_folders.php
+++ b/plugins/kolab_folders/kolab_folders.php
@@ -486,7 +486,6 @@ class kolab_folders extends rcube_plugin
         $storage     = $this->rc->get_storage();
         $namespace   = $storage->get_namespace();
         $defaults    = array();
-        $need_update = false;
         $prefix      = '';
 
         // Find personal namespace prefix
diff --git a/plugins/kolab_zpush/kolab_zpush_ui.php b/plugins/kolab_zpush/kolab_zpush_ui.php
index 3d2fbcc..0e8e6e9 100644
--- a/plugins/kolab_zpush/kolab_zpush_ui.php
+++ b/plugins/kolab_zpush/kolab_zpush_ui.php
@@ -143,7 +143,6 @@ class kolab_zpush_ui
             }
 
             $names[] = $origname;
-
             $classes = array('mailbox');
 
             if ($folder_class = $this->rc->folder_classname($folder)) {
@@ -152,9 +151,8 @@ class kolab_zpush_ui
             }
 
             $folder_id = 'rcmf' . html_identifier($folder);
-            $padding = str_repeat('    ', $level);
 
-            $table->add_row(array('class' => (($level+1) * $idx++) % 2 == 0 ? 'even' : 'odd'));
+            $table->add_row();
             $table->add('subscription', $checkbox_sync->show('', array('value' => $folder, 'id' => $folder_id)));
 
             if ($alarms)
@@ -162,7 +160,7 @@ class kolab_zpush_ui
             else
                 $table->add('alarm', '');
 
-            $table->add(join(' ', $classes), html::label($folder_id, $padding . Q($foldername)));
+            $table->add(join(' ', $classes), html::label($folder_id, Q($foldername)));
         }
 
         return $table->show();
diff --git a/plugins/libcalendaring/libcalendaring.php b/plugins/libcalendaring/libcalendaring.php
index 138c506..eb72e70 100644
--- a/plugins/libcalendaring/libcalendaring.php
+++ b/plugins/libcalendaring/libcalendaring.php
@@ -106,7 +106,7 @@ class libcalendaring extends rcube_plugin
     public function adjust_timezone($dt)
     {
         if (is_numeric($dt))
-            $dt = new DateTime('@'.$td);
+            $dt = new DateTime('@'.$dt);
         else if (is_string($dt))
             $dt = new DateTime($dt);
 
@@ -391,10 +391,10 @@ class libcalendaring extends rcube_plugin
         // collect pending alarms from all providers (e.g. calendar, tasks)
         $plugin = $this->rc->plugins->exec_hook('pending_alarms', array(
             'time' => time(),
-            'alarms' => $alarms,
+            'alarms' => array(),
         ));
 
-        if (!$plugin['abort'] && $plugin['alarms']) {
+        if (!$plugin['abort'] && !empty($plugin['alarms'])) {
             // make sure texts and env vars are available on client
             $this->add_texts('localization/', true);
             $this->rc->output->set_env('snooze_select', $this->snooze_select());
@@ -407,7 +407,7 @@ class libcalendaring extends rcube_plugin
      */
     public function alarms_action()
     {
-        $action = get_input_value('action', RCUBE_INPUT_GPC);
+//        $action = get_input_value('action', RCUBE_INPUT_GPC);
         $data  = get_input_value('data', RCUBE_INPUT_POST, true);
 
         $data['ids'] = explode(',', $data['id']);
diff --git a/plugins/logon_page/logon_page.php b/plugins/logon_page/logon_page.php
index 4c69099..8dee962 100644
--- a/plugins/logon_page/logon_page.php
+++ b/plugins/logon_page/logon_page.php
@@ -63,6 +63,6 @@ class logon_page extends rcube_plugin
             $rcmail->output->add_footer($html);
         }
 
-        return $arg;
+        return $args;
     }
 }
diff --git a/plugins/odfviewer/odfviewer.php b/plugins/odfviewer/odfviewer.php
index 950b2dd..39d88de 100644
--- a/plugins/odfviewer/odfviewer.php
+++ b/plugins/odfviewer/odfviewer.php
@@ -136,8 +136,6 @@ class odfviewer extends rcube_plugin
    */
   function gc_cleanup()
   {
-    $rcmail = rcube::get_instance();
-
     $tmp = unslashify($this->tempdir);
     $expire = mktime() - 172800;  // expire in 48 hours
 
diff --git a/plugins/tinymce_config/tinymce_config.php b/plugins/tinymce_config/tinymce_config.php
index e6e5828..07b14e6 100644
--- a/plugins/tinymce_config/tinymce_config.php
+++ b/plugins/tinymce_config/tinymce_config.php
@@ -42,6 +42,7 @@ class tinymce_config extends rcube_plugin
     $script = sprintf('$.extend(window.rcmail_editor_settings, %s);', json_encode($config));
 
     $rcmail->output->add_script($script, 'foot');
+
+    return $args;
   }
 }
-


commit 4d3e6b5493290ac742f445ad57f3ccaaf16f7d0d
Author: Aleksander Machniak <alec at alec.pl>
Date:   Tue May 7 11:31:30 2013 +0200

    Fix code bugs caught in static code analysis

diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
index 1517306..f8451a1 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -843,7 +843,7 @@ class calendar extends rcube_plugin
   {
     $this->load_driver();
     if ($alarms = $this->driver->pending_alarms($p['time'] ?: time())) {
-      foreach ($alarms as $i => $alarm) {
+      foreach ($alarms as $alarm) {
         $alarm['id'] = 'cal:' . $alarm['id'];  // prefix ID with cal:
         $p['alarms'][] = $alarm;
       }
@@ -886,7 +886,6 @@ class calendar extends rcube_plugin
     }
 
     $calendar = get_input_value('calendar', RCUBE_INPUT_GPC);
-    $uploadid = get_input_value('_uploadid', RCUBE_INPUT_GPC);
 
     // process uploaded file if there is no error
     $err = $_FILES['_data']['error'];
@@ -903,7 +902,7 @@ class calendar extends rcube_plugin
           continue;
 
         $event['calendar'] = $calendar;
-        if ($success = $this->driver->new_event($event)) {
+        if ($this->driver->new_event($event)) {
           $count++;
         }
         else
@@ -1161,9 +1160,10 @@ class calendar extends rcube_plugin
    */
   public function generate_randomdata()
   {
-    $num = $_REQUEST['_num'] ? intval($_REQUEST['_num']) : 100;
-    $cats = array_keys($this->driver->list_categories());
-    $cals = $this->driver->list_calendars(true);
+    $num   = $_REQUEST['_num'] ? intval($_REQUEST['_num']) : 100;
+    $cats  = array_keys($this->driver->list_categories());
+    $cals  = $this->driver->list_calendars(true);
+    $count = 0;
 
     while ($count++ < $num) {
       $start = round((time() + rand(-2600, 2600) * 1000) / 300) * 300;
@@ -1183,7 +1183,7 @@ class calendar extends rcube_plugin
       $title = '';
       $len = rand(2, 12);
       $words = explode(" ", "The Hough transform is named after Paul Hough who patented the method in 1962. It is a technique which can be used to isolate features of a particular shape within an image. Because it requires that the desired features be specified in some parametric form, the classical Hough transform is most commonly used for the de- tection of regular curves such as lines, circles, ellipses, etc. A generalized Hough transform can be employed in applications where a simple analytic description of a feature(s) is not possible. Due to the computational complexity of the generalized Hough algorithm, we restrict the main focus of this discussion to the classical Hough transform. Despite its domain restrictions, the classical Hough transform (hereafter referred to without the classical prefix ) retains many applications, as most manufac- tured parts (and many anatomical parts investigated in medical imagery) contain feature boundaries which can be described by regu
 lar curves. The main advantage of the Hough transform technique is that it is tolerant of gaps in feature boundary descriptions and is relatively unaffected by image noise.");
-      $chars = "!# abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890";
+//      $chars = "!# abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890";
       for ($i = 0; $i < $len; $i++)
         $title .= $words[rand(0,count($words)-1)] . " ";
       
@@ -1670,7 +1670,7 @@ class calendar extends rcube_plugin
     $itip_part = null;
 
     // check all message parts for .ics files
-    foreach ((array)$this->message->mime_parts as $idx => $part) {
+    foreach ((array)$this->message->mime_parts as $part) {
       if ($this->is_vcalendar($part)) {
         if ($part->ctype_parameters['method'])
           $itip_part = $part->mime_id;
@@ -1739,7 +1739,7 @@ class calendar extends rcube_plugin
           
           // check my status
           $status = 'unknown';
-          foreach ($event['attendees'] as $i => $attendee) {
+          foreach ($event['attendees'] as $attendee) {
             if ($attendee['email'] && in_array(strtolower($attendee['email']), $emails)) {
               $status = strtoupper($attendee['status']);
               break;
diff --git a/plugins/calendar/drivers/calendar_driver.php b/plugins/calendar/drivers/calendar_driver.php
index 43e1345..a9402e1 100644
--- a/plugins/calendar/drivers/calendar_driver.php
+++ b/plugins/calendar/drivers/calendar_driver.php
@@ -379,12 +379,12 @@ abstract class calendar_driver
   public function calendar_form($action, $calendar, $formfields)
   {
     $html = '';
-    foreach ($formfields as $prop => $field) {
+    foreach ($formfields as $field) {
       $html .= html::div('form-section',
         html::label($field['id'], $field['label']) .
         $field['value']);
     }
-    
+
     return $html;
   }
 
diff --git a/plugins/calendar/drivers/database/database_driver.php b/plugins/calendar/drivers/database/database_driver.php
index 7669350..3b949ad 100644
--- a/plugins/calendar/drivers/database/database_driver.php
+++ b/plugins/calendar/drivers/database/database_driver.php
@@ -236,9 +236,10 @@ class database_driver extends calendar_driver
         return false;
       if (!$event['calendar'])
         $event['calendar'] = reset(array_keys($this->calendars));
-      
+
       $event = $this->_save_preprocess($event);
-      $query = $this->rc->db->query(sprintf(
+
+      $this->rc->db->query(sprintf(
         "INSERT INTO " . $this->db_events . "
          (calendar_id, created, changed, uid, %s, %s, all_day, recurrence, title, description, location, categories, free_busy, priority, sensitivity, attendees, alarms, notifyat)
          VALUES (?, %s, %s, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
@@ -560,6 +561,7 @@ class database_driver extends calendar_driver
           break;
         
         // stop adding events for inifinite recurrence after 20 years
+        $count = 0;
         if (++$count > 999 || (!$recurrence->recurEnd && !$recurrence->recurCount && $next->year > date('Y') + 20))
           break;
       }
@@ -923,7 +925,6 @@ class database_driver extends calendar_driver
   public function list_attachments($event)
   {
     $attachments = array();
-    $event_id = $event['recurrence_id'] ? $event['recurrence_id'] : $event['event_id'];
 
     if (!empty($this->calendar_ids)) {
       $result = $this->rc->db->query(
diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php
index b9a6231..0f564ae 100644
--- a/plugins/calendar/drivers/kolab/kolab_calendar.php
+++ b/plugins/calendar/drivers/kolab/kolab_calendar.php
@@ -583,7 +583,7 @@ class kolab_calendar
     // in kolab_storage attachments are indexed by content-id
     $event['_attachments'] = array();
     if (is_array($event['attachments'])) {
-      foreach ($event['attachments'] as $idx => $attachment) {
+      foreach ($event['attachments'] as $attachment) {
         $key = null;
         // Roundcube ID has nothing to do with the storage ID, remove it
         if ($attachment['content']) {
diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php
index 560a0cd..f873166 100644
--- a/plugins/calendar/drivers/kolab/kolab_driver.php
+++ b/plugins/calendar/drivers/kolab/kolab_driver.php
@@ -83,7 +83,7 @@ class kolab_driver extends calendar_driver
 
     asort($names, SORT_LOCALE_STRING);
 
-    foreach ($names as $utf7name => $name) {
+    foreach (array_keys($names) as $utf7name) {
       $calendar = new kolab_calendar($utf7name, $this->cal);
       $this->calendars[$calendar->id] = $calendar;
       if (!$calendar->readonly)
@@ -691,7 +691,7 @@ class kolab_driver extends calendar_driver
       $calendars = explode(',', $calendars);
 
     $events = $categories = array();
-    foreach ($this->calendars as $cid => $calendar) {
+    foreach (array_keys($this->calendars) as $cid) {
       if ($calendars && !in_array($cid, $calendars))
         continue;
 
@@ -1042,7 +1042,7 @@ class kolab_driver extends calendar_driver
     // Disable folder name input
     if (!empty($options) && ($options['norename'] || $options['protected'])) {
       $input_name = new html_hiddenfield(array('name' => 'name', 'id' => 'calendar-name'));
-      $formfields['name']['value'] = Q(str_replace($delimiter, ' » ', kolab_storage::object_name($folder)))
+      $formfields['name']['value'] = Q(str_replace($delim, ' » ', kolab_storage::object_name($folder)))
         . $input_name->show($folder);
     }
 
@@ -1180,8 +1180,6 @@ class kolab_driver extends calendar_driver
       $color  = '';
     }
 
-    $hidden_fields[] = array('name' => 'oldname', 'value' => $folder);
-
     $storage = $this->rc->get_storage();
     $delim   = $storage->get_hierarchy_delimiter();
     $form   = array();
@@ -1192,7 +1190,7 @@ class kolab_driver extends calendar_driver
       $path_imap = implode($path_imap, $delim);
 
       $options = $storage->folder_info($folder);
-    
+
       // Allow plugins to modify the form content (e.g. with ACL form)
       $plugin = $this->rc->plugins->exec_hook('calendar_form_kolab',
         array('form' => $form, 'options' => $options, 'name' => $folder));
diff --git a/plugins/calendar/lib/calendar_ui.php b/plugins/calendar/lib/calendar_ui.php
index f06084c..61803e7 100644
--- a/plugins/calendar/lib/calendar_ui.php
+++ b/plugins/calendar/lib/calendar_ui.php
@@ -270,7 +270,7 @@ class calendar_ui
     $select         = new html_select($attrib);
     $identities     = $this->rc->user->list_identities();
 
-    foreach ($identities as $id => $ident) {
+    foreach ($identities as $ident) {
         $select->add(format_email_recipient($ident['email'], $ident['name']), $ident['identity_id']);
     }
 
@@ -285,7 +285,7 @@ class calendar_ui
     $attrib['name'] = 'categories';
     $select = new html_select($attrib);
     $select->add('---', '');
-    foreach ((array)$this->cal->driver->list_categories() as $cat => $color) {
+    foreach (array_keys((array)$this->cal->driver->list_categories()) as $cat) {
       $select->add($cat, $cat);
     }
 
@@ -530,7 +530,6 @@ class calendar_ui
     // Get max filesize, enable upload progress bar
     $max_filesize = rcube_upload_init();
 
-    $button = new html_inputfield(array('type' => 'button'));
     $input = new html_inputfield(array(
       'type' => 'file', 'name' => '_data', 'size' => $attrib['uploadfieldsize']));
 
@@ -543,12 +542,12 @@ class calendar_ui
         $this->cal->gettext('all'),
       ),
       array('1','2','6','12',0));
-    
+
     $html .= html::div('form-section',
       html::div(null, $input->show()) .
       html::div('hint', rcube_label(array('name' => 'maxuploadsize', 'vars' => array('size' => $max_filesize))))
     );
-    
+
     $html .= html::div('form-section',
       html::label('event-import-calendar', $this->cal->gettext('calendar')) .
       $this->calendar_select(array('name' => 'calendar', 'id' => 'event-import-calendar'))
@@ -641,7 +640,7 @@ class calendar_ui
     $formfields = array(
       'name' => array(
         'label' => $this->cal->gettext('name'),
-        'value' => $input_name->show($name),
+        'value' => $input_name->show($calendar['name']),
         'id' => 'calendar-name',
       ),
       'color' => array(


commit eaf1b043514ad563d46760dc5142c0a771e4e2be
Author: Aleksander Machniak <alec at alec.pl>
Date:   Tue May 7 10:58:39 2013 +0200

    Fix so default categories are used when categories aren't configured

diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
index 82f7b3c..1517306 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -58,13 +58,6 @@ class calendar extends rcube_plugin
     'calendar_time_indicator'  => true,
   );
 
-  private $default_categories = array(
-    'Personal' => 'c0c0c0',
-    'Work'     => 'ff0000',
-    'Family'   => '00ff00',
-    'Holiday'  => 'ff6600',
-  );
-  
   private $ics_parts = array();
 
 
diff --git a/plugins/calendar/drivers/calendar_driver.php b/plugins/calendar/drivers/calendar_driver.php
index 478a08c..43e1345 100644
--- a/plugins/calendar/drivers/calendar_driver.php
+++ b/plugins/calendar/drivers/calendar_driver.php
@@ -91,6 +91,13 @@ abstract class calendar_driver
   public $alarm_absolute = true;
   public $last_error;
 
+  protected $default_categories = array(
+    'Personal' => 'c0c0c0',
+    'Work'     => 'ff0000',
+    'Family'   => '00ff00',
+    'Holiday'  => 'ff6600',
+  );
+
   /**
    * Get a list of available calendars from this source
    *
@@ -328,7 +335,7 @@ abstract class calendar_driver
   public function list_categories()
   {
     $rcmail = rcube::get_instance();
-    return $rcmail->config->get('calendar_categories', array());
+    return $rcmail->config->get('calendar_categories', $this->default_categories);
   }
 
   /**
diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php
index ccf6a80..560a0cd 100644
--- a/plugins/calendar/drivers/kolab/kolab_driver.php
+++ b/plugins/calendar/drivers/kolab/kolab_driver.php
@@ -700,7 +700,7 @@ class kolab_driver extends calendar_driver
     }
     
     // add new categories to user prefs
-    $old_categories = $this->rc->config->get('calendar_categories', array());
+    $old_categories = $this->rc->config->get('calendar_categories', $this->default_categories);
     if ($newcats = array_diff(array_map('strtolower', array_keys($categories)), array_map('strtolower', array_keys($old_categories)))) {
       foreach ($newcats as $category)
         $old_categories[$category] = '';  // no color set yet
@@ -869,7 +869,7 @@ class kolab_driver extends calendar_driver
   public function list_categories()
   {
     // FIXME: complete list with categories saved in config objects (KEP:12)
-    return $this->rc->config->get('calendar_categories', array());
+    return $this->rc->config->get('calendar_categories', $this->default_categories);
   }
 
   /**


commit 26cd91260e388f9fe786439ee669c7e3595cb57c
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Fri May 3 10:41:26 2013 +0200

    Do case-insensitive checks whether the current user is an event attendee or organizer (#1838)

diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
index 1c6f9cd..82f7b3c 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -676,7 +676,7 @@ class calendar extends rcube_plugin
           foreach ($old['attendees'] as $i => $attendee) {
             if ($attendee['role'] == 'ORGANIZER')
               $organizer = $attendee;
-            else if ($attendee['email'] && in_array($attendee['email'], $emails)) {
+            else if ($attendee['email'] && in_array(strtolower($attendee['email']), $emails)) {
               $old['attendees'][$i]['status'] = 'DECLINED';
             }
           }
@@ -713,7 +713,7 @@ class calendar extends rcube_plugin
         if ($existing = $this->driver->get_event($event, true, false, true)) {
           $emails = $this->get_user_emails();
           foreach ($existing['attendees'] as $i => $attendee) {
-            if ($attendee['email'] && in_array($attendee['email'], $emails)) {
+            if ($attendee['email'] && in_array(strtolower($attendee['email']), $emails)) {
               $status = $attendee['status'];
               break;
             }
@@ -1063,7 +1063,7 @@ class calendar extends rcube_plugin
         $settings['identities'][$rec['identity_id']] = $rec['email'];
       }
       $identity['emails'][] = $this->rc->user->get_username();
-      $settings['identity'] = array('name' => $identity['name'], 'email' => $identity['email'], 'emails' => ';' . join(';', $identity['emails']));
+      $settings['identity'] = array('name' => $identity['name'], 'email' => strtolower($identity['email']), 'emails' => ';' . strtolower(join(';', $identity['emails'])));
     }
 
     return $settings;
@@ -1296,7 +1296,7 @@ class calendar extends rcube_plugin
       foreach ((array)$event['attendees'] as $i => $attendee) {
         if ($attendee['role'] == 'ORGANIZER')
           $organizer = $i;
-        if ($attendee['email'] == in_array($attendee['email'], $emails))
+        if ($attendee['email'] == in_array(strtolower($attendee['email']), $emails))
           $owner = $i;
         else if (!isset($attendee['rsvp']))
           $event['attendees'][$i]['rsvp'] = true;
@@ -1358,7 +1358,7 @@ class calendar extends rcube_plugin
     $sent = 0;
     foreach ((array)$event['attendees'] as $attendee) {
       // skip myself for obvious reasons
-      if (!$attendee['email'] || in_array($attendee['email'], $emails))
+      if (!$attendee['email'] || in_array(strtolower($attendee['email']), $emails))
         continue;
       
       // which template to use for mail text
@@ -1747,7 +1747,7 @@ class calendar extends rcube_plugin
           // check my status
           $status = 'unknown';
           foreach ($event['attendees'] as $i => $attendee) {
-            if ($attendee['email'] && in_array($attendee['email'], $emails)) {
+            if ($attendee['email'] && in_array(strtolower($attendee['email']), $emails)) {
               $status = strtoupper($attendee['status']);
               break;
             }
@@ -1856,7 +1856,7 @@ class calendar extends rcube_plugin
           if ($attendee['role'] == 'ORGANIZER') {
             $organizer = $attendee;
           }
-          else if ($attendee['email'] && in_array($attendee['email'], $emails)) {
+          else if ($attendee['email'] && in_array(strtolower($attendee['email']), $emails)) {
             $event['attendees'][$i]['status'] = strtoupper($status);
           }
         }
@@ -1940,7 +1940,7 @@ class calendar extends rcube_plugin
 
 
     // send iTip reply
-    if ($this->ical->method == 'REQUEST' && $organizer && !in_array($organizer['email'], $emails) && !$error_msg) {
+    if ($this->ical->method == 'REQUEST' && $organizer && !in_array(strtolower($organizer['email']), $emails) && !$error_msg) {
       $itip = $this->load_itip();
       if ($itip->send_itip_message($event, 'REPLY', $organizer, 'itipsubject' . $status, 'itipmailbody' . $status))
         $this->rc->output->command('display_message', $this->gettext(array('name' => 'sentresponseto', 'vars' => array('mailto' => $organizer['name'] ? $organizer['name'] : $organizer['email']))), 'confirmation');
@@ -2037,7 +2037,7 @@ class calendar extends rcube_plugin
   {
     $emails = array();
     $plugin = $this->rc->plugins->exec_hook('calendar_user_emails', array('emails' => $emails));
-    $emails = $plugin['emails'];
+    $emails = array_map('strtolower', $plugin['emails']);
 
     if ($plugin['abort']) {
       return $emails;
@@ -2045,7 +2045,7 @@ class calendar extends rcube_plugin
 
     $emails[] = $this->rc->user->get_username();
     foreach ($this->rc->user->list_identities() as $identity)
-      $emails[] = $identity['email'];
+      $emails[] = strtolower($identity['email']);
     
     return array_unique($emails);
   }
diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js
index 64210f8..4626414 100644
--- a/plugins/calendar/calendar_ui.js
+++ b/plugins/calendar/calendar_ui.js
@@ -166,15 +166,15 @@ function rcube_calendar_ui(settings)
     // check if the event has 'real' attendees, excluding the current user
     var has_attendees = function(event)
     {
-      return (event.attendees && event.attendees.length && (event.attendees.length > 1 || event.attendees[0].email != settings.identity.email));
+      return (event.attendees && event.attendees.length && (event.attendees.length > 1 || String(event.attendees[0].email).toLowerCase() != settings.identity.email));
     };
     
     // check if the current user is an attendee of this event
     var is_attendee = function(event, role, email)
     {
-      var emails = email ? ';'+email : settings.identity.emails;
+      var emails = email ? ';'+email.toLowerCase() : settings.identity.emails;
       for (var i=0; event.attendees && i < event.attendees.length; i++) {
-        if ((!role || event.attendees[i].role == role) && event.attendees[i].email && emails.indexOf(';'+event.attendees[i].email) >= 0)
+        if ((!role || event.attendees[i].role == role) && event.attendees[i].email && emails.indexOf(';'+event.attendees[i].email.toLowerCase()) >= 0)
           return event.attendees[i];
       }
       return false;
@@ -635,7 +635,7 @@ function rcube_calendar_ui(settings)
           data._identity = $('#edit-identities-list option:selected').val();
         
         // don't submit attendees if only myself is added as organizer
-        if (data.attendees.length == 1 && data.attendees[0].role == 'ORGANIZER' && data.attendees[0].email == settings.identity.email)
+        if (data.attendees.length == 1 && data.attendees[0].role == 'ORGANIZER' && String(data.attendees[0].email).toLowerCase() == settings.identity.email)
           data.attendees = [];
         
         // tell server to send notifications
@@ -1522,7 +1522,7 @@ function rcube_calendar_ui(settings)
         // update attendee status
         for (var data, i=0; i < me.selected_event.attendees.length; i++) {
           data = me.selected_event.attendees[i];
-          if (settings.identity.emails.indexOf(';'+data.email) >= 0)
+          if (settings.identity.emails.indexOf(';'+String(data.email).toLowerCase()) >= 0)
             data.status = response.toUpperCase();
         }
         event_show_dialog(me.selected_event);


commit 83cc9dc4c2e7704e1a734f4ba5254877ab6adc9d
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed May 1 15:25:37 2013 +0200

    Use the correct email type constant

diff --git a/plugins/libkolab/lib/kolab_format_contact.php b/plugins/libkolab/lib/kolab_format_contact.php
index b350559..3852fb7 100644
--- a/plugins/libkolab/lib/kolab_format_contact.php
+++ b/plugins/libkolab/lib/kolab_format_contact.php
@@ -50,7 +50,7 @@ class kolab_format_contact extends kolab_format
     public $emailtypes = array(
         'home' => Email::Home,
         'work' => Email::Work,
-        'other' => Email::Other,
+        'other' => Email::NoType,
     );
 
     public $addresstypes = array(


commit 6d3ac3cc1ebc8e7807e1a441f0935fbb8285394c
Author: Thomas Bruederli <thomas at roundcube.net>
Date:   Wed May 1 09:12:35 2013 +0200

    Fix searching by attendees in calendar events (#1774)

diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php
index 590bb55..b9a6231 100644
--- a/plugins/calendar/drivers/kolab/kolab_calendar.php
+++ b/plugins/calendar/drivers/kolab/kolab_calendar.php
@@ -37,7 +37,7 @@ class kolab_calendar
   private $cal;
   private $events = array();
   private $imap_folder = 'INBOX/Calendar';
-  private $search_fields = array('title', 'description', 'location', '_attendees');
+  private $search_fields = array('title', 'description', 'location', 'attendees');
   private $sensitivity_map = array('public', 'private', 'confidential');
 
 
@@ -227,7 +227,7 @@ class kolab_calendar
       if (!empty($search)) {
         $hit = false;
         foreach ($this->search_fields as $col) {
-          $sval = is_array($col) ? $event[$col[0]][$col[1]] : $event[$col];
+          $sval = is_array($event[$col]) ? self::_complex2string($event[$col]) : $event[$col];
           if (empty($sval))
             continue;
           
@@ -635,5 +635,29 @@ class kolab_calendar
     return $event;
   }
 
+  /**
+   * Convert a complex event attribute to a string value
+   */
+  private static function _complex2string($prop)
+  {
+      static $ignorekeys = array('role','status','rsvp');
+
+      $out = '';
+      if (is_array($prop)) {
+          foreach ($prop as $key => $val) {
+              if (is_numeric($key)) {
+                  $out .= self::_complex2string($val, $fields);
+              }
+              else if (!in_array($key, $ignorekeys)) {
+                $out .= $val . ' ';
+            }
+          }
+      }
+      else if (is_string($prop) || is_numeric($prop)) {
+          $out .= $prop . ' ';
+      }
+
+      return rtrim($out);
+  }
 
 }


commit 64336252a40e22be366d6e5693f9c57de1669166
Author: Aleksander Machniak <machniak at kolabsys.com>
Date:   Thu Apr 18 15:45:00 2013 +0200

    Fix bug where contacts were moved instead of copied. Internal date
    need to be cleared when converting from kolab to Roundcube format (Bug #1767)

diff --git a/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php b/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php
index 57dd4f5..48b89be 100644
--- a/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php
+++ b/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php
@@ -1074,7 +1074,12 @@ class rcube_kolab_contacts extends rcube_addressbook
             $record['pgppublickey'] = substr($record['pgppublickey'], 0, 140) . '...';
 
         // remove empty fields
-        return array_filter($record);
+        $record = array_filter($record);
+
+        // remove kolab_storage internal data
+        unset($record['_msguid'], $record['_formatobj'], $record['_mailbox'], $record['_type'], $record['_size']);
+
+        return $record;
     }
 
     /**


commit b35c18f0dff830380ff07935c0f62bdb38143a27
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Apr 18 15:28:03 2013 +0200

    Improve fix for wrong alarm settings (#1764)

diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js
index 000cc3c..64210f8 100644
--- a/plugins/calendar/calendar_ui.js
+++ b/plugins/calendar/calendar_ui.js
@@ -450,8 +450,9 @@ function rcube_calendar_ui(settings)
         if (typeof event.alarms == 'string')
           event.alarms = event.alarms.split(';');
         
-        for (var alarm, i=0; i < event.alarms.length; i++) {
-          alarm = String(event.alarms[i]).split(':');
+        var valarms = event.alarms || [''];
+        for (var alarm, i=0; i < valarms.length; i++) {
+          alarm = String(valarms[i]).split(':');
           if (!alarm[1] && alarm[0]) alarm[1] = 'DISPLAY';
           $('#eventedit select.edit-alarm-type').val(alarm[1]);
           
diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js
index 7fa17f6..530bb25 100644
--- a/plugins/tasklist/tasklist.js
+++ b/plugins/tasklist/tasklist.js
@@ -1011,8 +1011,9 @@ function rcube_tasklist_ui(settings)
             if (typeof rec.alarms == 'string')
                 rec.alarms = rec.alarms.split(';');
 
-          for (var alarm, i=0; i < rec.alarms.length; i++) {
-              alarm = String(rec.alarms[i]).split(':');
+          var valarms = rec.alarms || [''];
+          for (var alarm, i=0; i < valarms.length; i++) {
+              alarm = String(valarms[i]).split(':');
               if (!alarm[1] && alarm[0]) alarm[1] = 'DISPLAY';
               $('#taskedit select.edit-alarm-type').val(alarm[1]);
 


commit 5a890d8f6a5821b04a4aa0f3d6896b3a7b3dc283
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Apr 18 15:13:00 2013 +0200

    Don't apply default alarm settings when editing an object that has no alarm set (#1764)

diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js
index 10d6521..000cc3c 100644
--- a/plugins/calendar/calendar_ui.js
+++ b/plugins/calendar/calendar_ui.js
@@ -44,7 +44,7 @@ function rcube_calendar_ui(settings)
     var client_timezone = new Date().getTimezoneOffset();
     var day_clicked = day_clicked_ts = 0;
     var ignore_click = false;
-    var event_defaults = { free_busy:'busy' };
+    var event_defaults = { free_busy:'busy', alarms:'' };
     var event_attendees = [];
     var attendees_list;
     var freebusy_ui = { workinhoursonly:false, needsupdate:false };
@@ -446,7 +446,7 @@ function rcube_calendar_ui(settings)
       
       // set alarm(s)
       // TODO: support multiple alarm entries
-      if (event.alarms) {
+      if (event.alarms || action != 'new') {
         if (typeof event.alarms == 'string')
           event.alarms = event.alarms.split(';');
         
diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js
index 7a10484..7fa17f6 100644
--- a/plugins/tasklist/tasklist.js
+++ b/plugins/tasklist/tasklist.js
@@ -966,7 +966,8 @@ function rcube_tasklist_ui(settings)
         if (rcmail.busy || !list.editable || (action == 'edit' && (!rec || rec.readonly)))
             return false;
 
-        me.selected_task = $.extend({}, rec);  // clone task object
+        me.selected_task = $.extend({ alarms:'' }, rec);  // clone task object
+        rec =  me.selected_task;
 
         // assign temporary id
         if (!me.selected_task.id)
@@ -1006,7 +1007,7 @@ function rcube_tasklist_ui(settings)
         });
 
         // set alarm(s)
-        if (rec.alarms) {
+        if (rec.alarms || action != 'new') {
             if (typeof rec.alarms == 'string')
                 rec.alarms = rec.alarms.split(';');
 


commit 306451ca0791a994c750336b9a142846f1720220
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Apr 17 09:19:26 2013 +0200

    Little code cleanup

diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php
index 98041c8..590bb55 100644
--- a/plugins/calendar/drivers/kolab/kolab_calendar.php
+++ b/plugins/calendar/drivers/kolab/kolab_calendar.php
@@ -580,10 +580,8 @@ class kolab_calendar
    */
   private function _from_rcube_event($event, $old = array())
   {
-    $object = &$event;
-
     // in kolab_storage attachments are indexed by content-id
-    $object['_attachments'] = array();
+    $event['_attachments'] = array();
     if (is_array($event['attachments'])) {
       foreach ($event['attachments'] as $idx => $attachment) {
         $key = null;
@@ -600,15 +598,15 @@ class kolab_calendar
 
         // flagged for deletion => set to false
         if ($attachment['_deleted']) {
-          $object['_attachments'][$key] = false;
+          $event['_attachments'][$key] = false;
         }
         // replace existing entry
         else if ($key) {
-          $object['_attachments'][$key] = $attachment;
+          $event['_attachments'][$key] = $attachment;
         }
         // append as new attachment
         else {
-          $object['_attachments'][] = $attachment;
+          $event['_attachments'][] = $attachment;
         }
       }
 
@@ -625,6 +623,9 @@ class kolab_calendar
 
     $event['_owner'] = $identity['email'];
 
+    // remove some internal properties which should not be saved
+    unset($event['_savemode'], $event['_fromcalendar'], $event['_identity']);
+
     // copy meta data (starting with _) from old object
     foreach ((array)$old as $key => $val) {
       if (!isset($event[$key]) && $key[0] == '_')
diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php
index 6d3a252..ccf6a80 100644
--- a/plugins/calendar/drivers/kolab/kolab_driver.php
+++ b/plugins/calendar/drivers/kolab/kolab_driver.php
@@ -612,6 +612,9 @@ class kolab_driver extends calendar_driver
         $event['recurrence'] = array();
         $event['thisandfuture'] = $savemode == 'future';
 
+        // remove some internal properties which should not be saved
+        unset($event['_savemode'], $event['_fromcalendar'], $event['_identity']);
+
         // save properties to a recurrence exception instance
         if ($old['recurrence_id']) {
             $i = $old['_instance'] - 1;


commit 6719e1053dadb0d0fb6e74718233771becaf14db
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Apr 17 09:15:34 2013 +0200

    Set savemode to 'current' for recurrence exceptions (#1725)

diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js
index 4099776..10d6521 100644
--- a/plugins/calendar/calendar_ui.js
+++ b/plugins/calendar/calendar_ui.js
@@ -523,7 +523,7 @@ function rcube_calendar_ui(settings)
       
       // show warning if editing a recurring event
       if (event.id && event.recurrence) {
-        var sel = event.thisandfuture ? 'future' : 'all';
+        var sel = event.thisandfuture ? 'future' : (event.isexception ? 'current' : 'all');
         $('#edit-recurring-warning').show();
         $('input.edit-recurring-savemode[value="'+sel+'"]').prop('checked', true);
       }
diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php
index 764f619..98041c8 100644
--- a/plugins/calendar/drivers/kolab/kolab_calendar.php
+++ b/plugins/calendar/drivers/kolab/kolab_calendar.php
@@ -434,6 +434,7 @@ class kolab_calendar
         $rec_event['recurrence_id'] = $event['uid'];
         $rec_event['recurrence'] = $recurrence_rule;
         $rec_event['_instance'] = $i;
+        $rec_event['isexception'] = 1;
         $events[] = $rec_event;
 
         // found the specifically requested instance, exiting...


commit eb715970740a17e8f379edfcabe0d16155e2e1b1
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Mar 13 17:39:41 2013 +0100

    Fix saving of recurrence exceptions for v2: create unique UIDs, correctly save this-and-future instances with (modified) recurrence rule

diff --git a/plugins/libkolab/lib/kolab_storage_folder.php b/plugins/libkolab/lib/kolab_storage_folder.php
index c1e558f..7e9c379 100644
--- a/plugins/libkolab/lib/kolab_storage_folder.php
+++ b/plugins/libkolab/lib/kolab_storage_folder.php
@@ -719,22 +719,35 @@ class kolab_storage_folder
 
             // save every exception as individual object
             foreach((array)$object['recurrence']['EXCEPTIONS'] as $exception) {
-                $exception['uid'] = $object['uid'] . '-' . $exception['start']->format('Ymd');
-                $exception['recurrence-id'] = $exception['start']->format('Y-m-d');
+                $exception['uid'] = self::recurrence_exception_uid($object['uid'], $exception['start']->format('Ymd'));
                 $exception['sequence'] = $object['sequence'] + 1;
 
-                unset($exception['recurrence'], $exception['organizer'], $exception['attendees']);
-                $this->save($exception, $type, $exception['uid']);
-
-                // set UNTIL date if we have a thisandfuture exception
                 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'];
+                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']);
@@ -742,6 +755,16 @@ class kolab_storage_folder
     }
 
     /**
+     * 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.
      *
      * @param  mixed   $object  The Kolab object to delete or object UID


commit 6776c49a19e597bd94597df0ec6fc13a5580e079
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Mar 13 16:19:02 2013 +0100

    Keep libkolab API for event recurrence exceptions consistent for Kolab v2. The format doesn't allow to save exceptions inline, thus save them as individual events

diff --git a/plugins/libkolab/lib/kolab_format_event.php b/plugins/libkolab/lib/kolab_format_event.php
index baf14aa..38bd0ce 100644
--- a/plugins/libkolab/lib/kolab_format_event.php
+++ b/plugins/libkolab/lib/kolab_format_event.php
@@ -190,7 +190,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 0c12423..c1e558f 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
@@ -589,7 +589,6 @@ class kolab_storage_folder
         return false;
     }
 
-
     /**
      * Save an object in this folder.
      *
@@ -660,6 +659,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();
@@ -667,6 +671,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]);
@@ -680,10 +690,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) {
@@ -695,6 +701,45 @@ 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'] = $object['uid'] . '-' . $exception['start']->format('Ymd');
+                $exception['recurrence-id'] = $exception['start']->format('Y-m-d');
+                $exception['sequence'] = $object['sequence'] + 1;
+
+                unset($exception['recurrence'], $exception['organizer'], $exception['attendees']);
+                $this->save($exception, $type, $exception['uid']);
+
+                // set UNTIL date if we have a thisandfuture exception
+                if ($exception['thisandfuture']) {
+                    $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($object['recurrence']['EXCEPTIONS']);
+        }
+    }
 
     /**
      * Delete the specified object from this folder.


commit fb45f78d2b1bf6f3c5d68fc37ca855645889a20f
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Mar 6 15:14:51 2013 +0100

    Also adjust the timezone of existing DateTime objects

diff --git a/plugins/libcalendaring/libcalendaring.php b/plugins/libcalendaring/libcalendaring.php
index 0760277..138c506 100644
--- a/plugins/libcalendaring/libcalendaring.php
+++ b/plugins/libcalendaring/libcalendaring.php
@@ -109,8 +109,6 @@ class libcalendaring extends rcube_plugin
             $dt = new DateTime('@'.$td);
         else if (is_string($dt))
             $dt = new DateTime($dt);
-        else
-            return $dt;
 
         $dt->setTimezone($this->timezone);
         return $dt;


commit cf52ef65f00f48bea760332cd2f86f3bf1a1c05d
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Feb 28 14:18:08 2013 +0100

    Really skip this property

diff --git a/plugins/libcalendaring/libcalendaring.php b/plugins/libcalendaring/libcalendaring.php
index 5e30b73..0760277 100644
--- a/plugins/libcalendaring/libcalendaring.php
+++ b/plugins/libcalendaring/libcalendaring.php
@@ -729,7 +729,7 @@ class libcalendaring extends rcube_plugin
                 $val = join(',', (array)$val);
                 break;
             case 'EXCEPTIONS':
-                continue;
+                continue 2;
             }
             $rrule .= $k . '=' . $val . ';';
         }


commit e95f9bd7b6cd23e4c16fa6d0f8a54c279b4b9908
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Feb 28 10:44:15 2013 +0100

    Properly export recurrence exceptions to iCal

diff --git a/plugins/calendar/lib/calendar_ical.php b/plugins/calendar/lib/calendar_ical.php
index e4c8e74..f1716f4 100644
--- a/plugins/calendar/lib/calendar_ical.php
+++ b/plugins/calendar/lib/calendar_ical.php
@@ -347,27 +347,32 @@ class calendar_ical
    * @param  boolean Directly send data to stdout instead of returning
    * @return string  Events in iCalendar format (http://tools.ietf.org/html/rfc5545)
    */
-  public function export($events, $method = null, $write = false)
+  public function export($events, $method = null, $write = false, $recurrence_id = null)
   {
-      $ical = "BEGIN:VCALENDAR" . self::EOL;
-      $ical .= "VERSION:2.0" . self::EOL;
-      $ical .= "PRODID:-//Roundcube Webmail " . RCMAIL_VERSION . "//NONSGML Calendar//EN" . self::EOL;
-      $ical .= "CALSCALE:GREGORIAN" . self::EOL;
-      
-      if ($method)
-        $ical .= "METHOD:" . strtoupper($method) . self::EOL;
-        
-      if ($write) {
-        echo $ical;
-        $ical = '';
+      if (!$recurrence_id) {
+        $ical = "BEGIN:VCALENDAR" . self::EOL;
+        $ical .= "VERSION:2.0" . self::EOL;
+        $ical .= "PRODID:-//Roundcube Webmail " . RCMAIL_VERSION . "//NONSGML Calendar//EN" . self::EOL;
+        $ical .= "CALSCALE:GREGORIAN" . self::EOL;
+
+        if ($method)
+          $ical .= "METHOD:" . strtoupper($method) . self::EOL;
+
+        if ($write) {
+          echo $ical;
+          $ical = '';
+        }
       }
-      
+
       foreach ($events as $event) {
         $vevent = "BEGIN:VEVENT" . self::EOL;
         $vevent .= "UID:" . self::escape($event['uid']) . self::EOL;
         $vevent .= $this->format_datetime("DTSTAMP", $event['changed'] ?: new DateTime(), false, true) . self::EOL;
         if ($event['sequence'])
-            $vevent .= "SEQUENCE:" . intval($event['sequence']) . self::EOL;
+          $vevent .= "SEQUENCE:" . intval($event['sequence']) . self::EOL;
+        if ($recurrence_id)
+          $vevent .= $recurrence_id . self::EOL;
+        
         // correctly set all-day dates
         if ($event['allday']) {
           $event['end'] = clone $event['end'];
@@ -389,7 +394,7 @@ class calendar_ical
         if (!empty($event['location'])) {
           $vevent .= "LOCATION:" . self::escape($event['location']) . self::EOL;
         }
-        if ($event['recurrence']) {
+        if ($event['recurrence'] && !$recurrence_id) {
           $vevent .= "RRULE:" . libcalendaring::to_rrule($event['recurrence'], self::EOL) . self::EOL;
         }
         if(!empty($event['categories'])) {
@@ -426,18 +431,30 @@ class calendar_ical
         // TODO: export attachments
         
         $vevent .= "END:VEVENT" . self::EOL;
-        
+
+        // append recurrence exceptions
+        if ($event['recurrence']['EXCEPTIONS'] && !$recurrence_id) {
+          foreach ($event['recurrence']['EXCEPTIONS'] as $ex) {
+            $exdate = clone $event['start'];
+            $exdate->setDate($ex['start']->format('Y'), $ex['start']->format('n'), $ex['start']->format('j'));
+            $vevent .= $this->export(array($ex), null, false,
+              $this->format_datetime('RECURRENCE-ID', $exdate, $event['allday']));
+          }
+        }
+
         if ($write)
           echo rcube_vcard::rfc2425_fold($vevent);
         else
           $ical .= $vevent;
       }
       
-      $ical .= "END:VCALENDAR" . self::EOL;
+      if (!$recurrence_id) {
+        $ical .= "END:VCALENDAR" . self::EOL;
       
-      if ($write) {
-        echo $ical;
-        return true;
+        if ($write) {
+          echo $ical;
+          return true;
+        }
       }
 
       // fold lines to 75 chars


commit eeb55a32c0b876a6d215f446eea85707fa2d030b
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Feb 28 08:26:54 2013 +0100

    Don't attempt to serialize recurrence EXCEPTIONS

diff --git a/plugins/libcalendaring/libcalendaring.php b/plugins/libcalendaring/libcalendaring.php
index 35255b9..5e30b73 100644
--- a/plugins/libcalendaring/libcalendaring.php
+++ b/plugins/libcalendaring/libcalendaring.php
@@ -728,6 +728,8 @@ class libcalendaring extends rcube_plugin
                     $val[$i] = $ex->format('Ymd\THis');
                 $val = join(',', (array)$val);
                 break;
+            case 'EXCEPTIONS':
+                continue;
             }
             $rrule .= $k . '=' . $val . ';';
         }


commit a616fa629fd0fda66675571c580723d23fc60610
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Feb 14 16:17:02 2013 +0100

    Make this-and-future recurrence exceptions work

diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js
index c25cf68..4099776 100644
--- a/plugins/calendar/calendar_ui.js
+++ b/plugins/calendar/calendar_ui.js
@@ -523,8 +523,9 @@ function rcube_calendar_ui(settings)
       
       // show warning if editing a recurring event
       if (event.id && event.recurrence) {
+        var sel = event.thisandfuture ? 'future' : 'all';
         $('#edit-recurring-warning').show();
-        $('input.edit-recurring-savemode[value="all"]').prop('checked', true);
+        $('input.edit-recurring-savemode[value="'+sel+'"]').prop('checked', true);
       }
       else
         $('#edit-recurring-warning').hide();
diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php
index 53c47ad..764f619 100644
--- a/plugins/calendar/drivers/kolab/kolab_calendar.php
+++ b/plugins/calendar/drivers/kolab/kolab_calendar.php
@@ -422,23 +422,33 @@ class kolab_calendar
     $i = 0;
     $events = array();
     $exdates = array();
+    $futuredata = array();
     if (is_array($event['recurrence']['EXCEPTIONS'])) {
-        foreach ($event['recurrence']['EXCEPTIONS'] as $exception) {
-            $rec_event = $this->_to_rcube_event($exception);
-            $rec_event['id'] = $event['uid'] . '-' . ++$i;
-            $rec_event['recurrence_id'] = $event['uid'];
-            $rec_event['_instance'] = $i;
-            $events[] = $rec_event;
-
-            // found the specifically requested instance, exiting...
-            if ($rec_event['id'] == $event_id) {
-              $this->events[$rec_event['id']] = $rec_event;
-              return $events;
-            }
+      // copy the recurrence rule from the master event (to be used in the UI)
+      $recurrence_rule = $event['recurrence'];
+      unset($recurrence_rule['EXCEPTIONS'], $recurrence_rule['EXDATE']);
+
+      foreach ($event['recurrence']['EXCEPTIONS'] as $exception) {
+        $rec_event = $this->_to_rcube_event($exception);
+        $rec_event['id'] = $event['uid'] . '-' . ++$i;
+        $rec_event['recurrence_id'] = $event['uid'];
+        $rec_event['recurrence'] = $recurrence_rule;
+        $rec_event['_instance'] = $i;
+        $events[] = $rec_event;
 
-            // remember this exception's date
-            $exdates[$rec_event['start']->format('Y-m-d')] = $rec_event['id'];
+        // found the specifically requested instance, exiting...
+        if ($rec_event['id'] == $event_id) {
+          $this->events[$rec_event['id']] = $rec_event;
+          return $events;
         }
+
+        // remember this exception's date
+        $exdate = $rec_event['start']->format('Y-m-d');
+        $exdates[$exdate] = $rec_event['id'];
+        if ($rec_event['thisandfuture']) {
+          $futuredata[$exdate] = $rec_event;
+        }
+      }
     }
 
     // use libkolab to compute recurring events
@@ -446,20 +456,29 @@ class kolab_calendar
         $recurrence = new kolab_date_recurrence($object);
     }
     else {
-        // fallback to local recurrence implementation
-        require_once($this->cal->home . '/lib/calendar_recurrence.php');
-        $recurrence = new calendar_recurrence($this->cal, $event);
+      // fallback to local recurrence implementation
+      require_once($this->cal->home . '/lib/calendar_recurrence.php');
+      $recurrence = new calendar_recurrence($this->cal, $event);
     }
 
     while ($next_event = $recurrence->next_instance()) {
       // skip if there's an exception at this date
-      if ($exdates[$next_event['start']->format('Y-m-d')])
+      $datestr = $next_event['start']->format('Y-m-d');
+      if ($exdates[$datestr]) {
+        // use this event data for future recurring instances
+        if ($futuredata[$datestr])
+          $overlay_data = $futuredata[$datestr];
         continue;
+      }
 
       // add to output if in range
       $rec_id = $event['uid'] . '-' . ++$i;
       if (($next_event['start'] <= $end && $next_event['end'] >= $start) || ($event_id && $rec_id == $event_id)) {
         $rec_event = $this->_to_rcube_event($next_event);
+
+        if ($overlay_data)  // copy data from a 'this-and-future' exception
+          $this->_merge_event_data($rec_event, $overlay_data);
+
         $rec_event['id'] = $rec_id;
         $rec_event['recurrence_id'] = $event['uid'];
         $rec_event['_instance'] = $i;
@@ -483,6 +502,27 @@ class kolab_calendar
   }
 
   /**
+   * Merge certain properties from the overlay event to the base event object
+   *
+   * @param array The event object to be altered
+   * @param array The overlay event object to be merged over $event
+   */
+  private function _merge_event_data(&$event, $overlay)
+  {
+    static $forbidden = array('id','uid','created','changed','recurrence','organizer','attendees','sequence');
+
+    foreach ($overlay as $prop => $value) {
+      // adjust time of the recurring event instance
+      if ($prop == 'start' || $prop == 'end') {
+        if (is_object($event[$prop]) && is_a($event[$prop], 'DateTime'))
+          $event[$prop]->setTime($value->format('G'), intval($value->format('i')), intval($value->format('s')));
+      }
+      else if ($prop[0] != '_' && !in_array($prop, $forbidden))
+        $event[$prop] = $value;
+    }
+  }
+
+  /**
    * Convert from Kolab_Format to internal representation
    */
   private function _to_rcube_event($record)
diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php
index b840033..6d3a252 100644
--- a/plugins/calendar/drivers/kolab/kolab_driver.php
+++ b/plugins/calendar/drivers/kolab/kolab_driver.php
@@ -424,6 +424,14 @@ class kolab_driver extends calendar_driver
         $savemode = $event['_savemode'];
       }
 
+      // removing an exception instance
+      if ($event['recurrence_id']) {
+        $i = $event['_instance'] - 1;
+        if (!empty($master['recurrence']['EXCEPTIONS'][$i])) {
+          unset($master['recurrence']['EXCEPTIONS'][$i]);
+        }
+      }
+
       switch ($savemode) {
         case 'current':
           $_SESSION['calendar_restore_event_data'] = $master;
@@ -597,43 +605,12 @@ class kolab_driver extends calendar_driver
         
         $success = $storage->insert_event($event);
         break;
-        
+
+      case 'future':
       case 'current':
-        // save as exception to master event
+        // recurring instances shall not store recurrence rules
         $event['recurrence'] = array();
-        $master['recurrence']['EXCEPTIONS'][] = $event;
-#       $master['recurrence']['EXDATE'][] = $event['start'];
-        $success = $storage->update_event($master);
-        break;
-        
-      case 'future':
-        if ($master['id'] != $event['id']) {
-          // set until-date on master event
-          $master['recurrence']['UNTIL'] = clone $old['start'];
-          $master['recurrence']['UNTIL']->sub(new DateInterval('P1D'));
-          unset($master['recurrence']['COUNT']);
-          $storage->update_event($master);
-          
-          // save this instance as new recurring event
-          $event += $old;
-          $event['uid'] = $this->cal->generate_uid();
-          
-          // if recurrence COUNT, update value to the correct number of future occurences
-          if ($event['recurrence']['COUNT']) {
-            $event['recurrence']['COUNT'] -= $old['_instance'];
-          }
-          
-          // remove fixed weekday, will be re-set to the new weekday in kolab_calendar::insert_event()
-          if (strlen($event['recurrence']['BYDAY']) == 2)
-            unset($event['recurrence']['BYDAY']);
-          if ($master['recurrence']['BYMONTH'] == $master['start']->format('n'))
-            unset($event['recurrence']['BYMONTH']);
-          
-          $success = $storage->insert_event($event);
-          break;
-        }
-
-      default:  // 'all' is default
+        $event['thisandfuture'] = $savemode == 'future';
 
         // save properties to a recurrence exception instance
         if ($old['recurrence_id']) {
@@ -645,6 +622,12 @@ class kolab_driver extends calendar_driver
             }
         }
 
+        // save as new exception to master event
+        $master['recurrence']['EXCEPTIONS'][] = $event;
+        $success = $storage->update_event($master);
+        break;
+
+      default:  // 'all' is default
         $event['id'] = $master['id'];
         $event['uid'] = $master['uid'];
 
diff --git a/plugins/libkolab/lib/kolab_format_event.php b/plugins/libkolab/lib/kolab_format_event.php
index 42874b8..baf14aa 100644
--- a/plugins/libkolab/lib/kolab_format_event.php
+++ b/plugins/libkolab/lib/kolab_format_event.php
@@ -114,7 +114,7 @@ class kolab_format_event extends kolab_format_xcal
             foreach((array)$object['recurrence']['EXCEPTIONS'] as $exception) {
                 $exevent = new kolab_format_event;
                 $exevent->set($this->compact_exception($exception, $object));  // only save differing values
-                $exevent->obj->setRecurrenceID(self::get_datetime($exception['start'], null, true), false);
+                $exevent->obj->setRecurrenceID(self::get_datetime($exception['start'], null, true), (bool)$exception['thisandfuture']);
                 $vexceptions->push($exevent->obj);
             }
             $this->obj->setExceptions($vexceptions);
@@ -190,7 +190,7 @@ class kolab_format_event extends kolab_format_xcal
         }
 
         // read exception event objects
-        if ($exceptions = $this->obj->exceptions()) {
+        if (($exceptions = $this->obj->exceptions()) && $exceptions->size()) {
             for ($i=0; $i < $exceptions->size(); $i++) {
                 if (($exobj = $exceptions->get($i))) {
                     $exception = new kolab_format_event($exobj);
@@ -200,6 +200,10 @@ class kolab_format_event extends kolab_format_xcal
                 }
             }
         }
+        // this is an exception object
+        else if ($this->obj->recurrenceID()->isValid()) {
+          $object['thisandfuture'] = $this->obj->thisAndFuture();
+        }
 
         // merge with additional data, e.g. attachments from the message
         if ($data) {
@@ -238,24 +242,15 @@ class kolab_format_event extends kolab_format_xcal
     }
 
     /**
-     * Reduce the exception container to attributes which differ from the master event
+     * Remove some attributes from the exception container
      */
     private function compact_exception($exception, $master)
     {
-      static $mandatory = array('uid','created','start');
-      static $forbidden = array('recurrence','attendees','sequence');
+      static $forbidden = array('recurrence','organizer','attendees','sequence');
 
       $out = $exception;
       foreach ($exception as $prop => $val) {
-        if (in_array($prop, $mandatory))
-          continue;
-
-        if (is_object($exception[$prop]) && is_a($exception[$prop], 'DateTime'))
-          $equals = $exception[$prop] <> $master[$prop];
-        else
-          $equals = $exception[$prop] == $master[$prop];
-
-        if ($equals || in_array($prop, $forbidden)) {
+        if (in_array($prop, $forbidden)) {
           unset($out[$prop]);
         }
       }
@@ -268,10 +263,8 @@ class kolab_format_event extends kolab_format_xcal
      */
     private function expand_exception($exception, $master)
     {
-      static $forbidden = array('recurrence');
-
       foreach ($master as $prop => $value) {
-        if (empty($exception[$prop]) && !in_array($prop, $forbidden))
+        if (empty($exception[$prop]) && !empty($value))
           $exception[$prop] = $value;
       }
 


commit cdb43e6e7d12739cc537342611dffadea11710ef
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Jan 23 17:17:05 2013 +0100

    Skip regular recurrences on exception dates

diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php
index cfe46f9..53c47ad 100644
--- a/plugins/calendar/drivers/kolab/kolab_calendar.php
+++ b/plugins/calendar/drivers/kolab/kolab_calendar.php
@@ -421,6 +421,7 @@ class kolab_calendar
     // add recurrence exceptions to output
     $i = 0;
     $events = array();
+    $exdates = array();
     if (is_array($event['recurrence']['EXCEPTIONS'])) {
         foreach ($event['recurrence']['EXCEPTIONS'] as $exception) {
             $rec_event = $this->_to_rcube_event($exception);
@@ -429,10 +430,14 @@ class kolab_calendar
             $rec_event['_instance'] = $i;
             $events[] = $rec_event;
 
+            // found the specifically requested instance, exiting...
             if ($rec_event['id'] == $event_id) {
               $this->events[$rec_event['id']] = $rec_event;
               return $events;
             }
+
+            // remember this exception's date
+            $exdates[$rec_event['start']->format('Y-m-d')] = $rec_event['id'];
         }
     }
 
@@ -447,11 +452,12 @@ class kolab_calendar
     }
 
     while ($next_event = $recurrence->next_instance()) {
-      $rec_start = $next_event['start']->format('U');
-      $rec_end = $next_event['end']->format('U');
-      $rec_id = $event['uid'] . '-' . ++$i;
+      // skip if there's an exception at this date
+      if ($exdates[$next_event['start']->format('Y-m-d')])
+        continue;
 
       // add to output if in range
+      $rec_id = $event['uid'] . '-' . ++$i;
       if (($next_event['start'] <= $end && $next_event['end'] >= $start) || ($event_id && $rec_id == $event_id)) {
         $rec_event = $this->_to_rcube_event($next_event);
         $rec_event['id'] = $rec_id;


commit 29a8a9ae4dd21fd84890ff0f9d2b445ea37a702a
Author: Aleksander Machniak <machniak at kolabsys.com>
Date:   Fri Jun 21 11:46:48 2013 +0200

    Save changes in a recurring event as exception to the master event
    
    Conflicts:
    
    	plugins/libkolab/lib/kolab_format_event.php

diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
index f2cca86..1c6f9cd 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -1097,6 +1097,7 @@ class calendar extends rcube_plugin
       $event['recurrence_text'] = $this->_recurrence_text($event['recurrence']);
       if ($event['recurrence']['UNTIL'])
         $event['recurrence']['UNTIL'] = $this->lib->adjust_timezone($event['recurrence']['UNTIL'])->format('c');
+      unset($event['recurrence']['EXCEPTIONS']);
     }
 
     foreach ((array)$event['attachments'] as $k => $attachment) {
@@ -1110,7 +1111,7 @@ class calendar extends rcube_plugin
       'title'       => strval($event['title']),
       'description' => strval($event['description']),
       'location'    => strval($event['location']),
-      'className'   => ($addcss ? 'fc-event-cal-'.asciiwords($event['calendar'], true).' ' : '') . 'fc-event-cat-' . asciiwords(strtolower($event['categories']), true),
+      'className'   => ($addcss ? 'fc-event-cal-'.asciiwords($event['calendar'], true).' ' : '') . 'fc-event-cat-' . asciiwords(strtolower(join('-', (array)$event['categories'])), true),
       'allDay'      => ($event['allday'] == 1),
     ) + $event;
   }
diff --git a/plugins/calendar/drivers/calendar_driver.php b/plugins/calendar/drivers/calendar_driver.php
index a0d0c52..478a08c 100644
--- a/plugins/calendar/drivers/calendar_driver.php
+++ b/plugins/calendar/drivers/calendar_driver.php
@@ -46,6 +46,7 @@
  *           'COUNT' => 1..n,   // number of times
  *                      // + more properties (see http://www.kanzaki.com/docs/ical/recur.html)
  *          'EXDATE' => array(),  // list of DateTime objects of exception Dates/Times
+ *      'EXCEPTIONS' => array(<event>),  list of event objects which denote exceptions in the recurrence chain
  *    ),
  * 'recurrence_id' => 'ID of the recurrence group',   // usually the ID of the starting event
  *    'categories' => 'Event category',
diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php
index 45db638..cfe46f9 100644
--- a/plugins/calendar/drivers/kolab/kolab_calendar.php
+++ b/plugins/calendar/drivers/kolab/kolab_calendar.php
@@ -312,7 +312,7 @@ class kolab_calendar
    * @return boolean True on success, False on error
    */
 
-  public function update_event($event)
+  public function update_event($event, $exception_id = null)
   {
     $updated = false;
     $old = $this->storage->get_object($event['id']);
@@ -333,6 +333,11 @@ class kolab_calendar
     else {
       $updated = true;
       $this->events[$event['id']] = $this->_to_rcube_event($object);
+
+      // refresh local cache with recurring instances
+      if ($exception_id) {
+        $this->_get_recurring_events($object, $event['start'], $event['end'], $exception_id);
+      }
     }
 
     return $updated;
@@ -413,6 +418,24 @@ class kolab_calendar
       $end->add(new DateInterval($intvl));
     }
 
+    // add recurrence exceptions to output
+    $i = 0;
+    $events = array();
+    if (is_array($event['recurrence']['EXCEPTIONS'])) {
+        foreach ($event['recurrence']['EXCEPTIONS'] as $exception) {
+            $rec_event = $this->_to_rcube_event($exception);
+            $rec_event['id'] = $event['uid'] . '-' . ++$i;
+            $rec_event['recurrence_id'] = $event['uid'];
+            $rec_event['_instance'] = $i;
+            $events[] = $rec_event;
+
+            if ($rec_event['id'] == $event_id) {
+              $this->events[$rec_event['id']] = $rec_event;
+              return $events;
+            }
+        }
+    }
+
     // use libkolab to compute recurring events
     if (class_exists('kolabcalendaring')) {
         $recurrence = new kolab_date_recurrence($object);
@@ -423,8 +446,6 @@ class kolab_calendar
         $recurrence = new calendar_recurrence($this->cal, $event);
     }
 
-    $i = 0;
-    $events = array();
     while ($next_event = $recurrence->next_instance()) {
       $rec_start = $next_event['start']->format('U');
       $rec_end = $next_event['end']->format('U');
diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php
index 019b6d6..b840033 100644
--- a/plugins/calendar/drivers/kolab/kolab_driver.php
+++ b/plugins/calendar/drivers/kolab/kolab_driver.php
@@ -580,6 +580,8 @@ class kolab_driver extends calendar_driver
     // keep saved exceptions (not submitted by the client)
     if ($old['recurrence']['EXDATE'])
       $event['recurrence']['EXDATE'] = $old['recurrence']['EXDATE'];
+    if ($old['recurrence']['EXCEPTIONS'])
+      $event['recurrence']['EXCEPTIONS'] = $old['recurrence']['EXCEPTIONS'];
 
     switch ($savemode) {
       case 'new':
@@ -597,26 +599,11 @@ class kolab_driver extends calendar_driver
         break;
         
       case 'current':
-        // modifying the first instance => just move to next occurence
-        if ($master['id'] == $event['id']) {
-          $recurring = reset($storage->_get_recurring_events($event, $event['start'], null, $event['id'].'-1'));
-          $master['start'] = $recurring['start'];
-          $master['end'] = $recurring['end'];
-          if ($master['recurrence']['COUNT'])
-            $master['recurrence']['COUNT']--;
-        }
-        else {  // add exception to master event
-          $master['recurrence']['EXDATE'][] = $old['start'];
-		}
-
-        $storage->update_event($master);
-        
-        // insert new event for this occurence
-        $event += $old;
+        // save as exception to master event
         $event['recurrence'] = array();
-        unset($event['recurrence_id']);
-        $event['uid'] = $this->cal->generate_uid();
-        $success = $storage->insert_event($event);
+        $master['recurrence']['EXCEPTIONS'][] = $event;
+#       $master['recurrence']['EXDATE'][] = $event['start'];
+        $success = $storage->update_event($master);
         break;
         
       case 'future':
@@ -647,6 +634,17 @@ class kolab_driver extends calendar_driver
         }
 
       default:  // 'all' is default
+
+        // save properties to a recurrence exception instance
+        if ($old['recurrence_id']) {
+            $i = $old['_instance'] - 1;
+            if (!empty($master['recurrence']['EXCEPTIONS'][$i])) {
+                $master['recurrence']['EXCEPTIONS'][$i] = $event;
+                $success = $storage->update_event($master, $old['id']);
+                break;
+            }
+        }
+
         $event['id'] = $master['id'];
         $event['uid'] = $master['uid'];
 
diff --git a/plugins/libcalendaring/libcalendaring.php b/plugins/libcalendaring/libcalendaring.php
index c78173b..35255b9 100644
--- a/plugins/libcalendaring/libcalendaring.php
+++ b/plugins/libcalendaring/libcalendaring.php
@@ -109,6 +109,8 @@ class libcalendaring extends rcube_plugin
             $dt = new DateTime('@'.$td);
         else if (is_string($dt))
             $dt = new DateTime($dt);
+        else
+            return $dt;
 
         $dt->setTimezone($this->timezone);
         return $dt;
diff --git a/plugins/libkolab/lib/kolab_format_event.php b/plugins/libkolab/lib/kolab_format_event.php
index bb2b3b7..42874b8 100644
--- a/plugins/libkolab/lib/kolab_format_event.php
+++ b/plugins/libkolab/lib/kolab_format_event.php
@@ -30,6 +30,19 @@ class kolab_format_event extends kolab_format_xcal
     protected $read_func = 'readEvent';
     protected $write_func = 'writeEvent';
 
+    /**
+     * Default constructor
+     */
+    function __construct($data = null, $version = 3.0)
+    {
+        parent::__construct(is_string($data) ? $data : null, $version);
+
+        // got an Event object as argument
+        if (is_object($data) && is_a($data, $this->objclass)) {
+            $this->obj = $data;
+            $this->loaded = true;
+        }
+    }
 
     /**
      * Clones into an instance of libcalendaring's extended EventCal class
@@ -95,6 +108,18 @@ class kolab_format_event extends kolab_format_xcal
 
         $this->obj->setAttachments($vattach);
 
+        // save recurrence exceptions
+        if ($object['recurrence']['EXCEPTIONS']) {
+            $vexceptions = new vectorevent;
+            foreach((array)$object['recurrence']['EXCEPTIONS'] as $exception) {
+                $exevent = new kolab_format_event;
+                $exevent->set($this->compact_exception($exception, $object));  // only save differing values
+                $exevent->obj->setRecurrenceID(self::get_datetime($exception['start'], null, true), false);
+                $vexceptions->push($exevent->obj);
+            }
+            $this->obj->setExceptions($vexceptions);
+        }
+
         // cache this data
         $this->data = $object;
         unset($this->data['_formatobj']);
@@ -164,6 +189,30 @@ class kolab_format_event extends kolab_format_xcal
             }
         }
 
+        // read exception event objects
+        if ($exceptions = $this->obj->exceptions()) {
+            for ($i=0; $i < $exceptions->size(); $i++) {
+                if (($exobj = $exceptions->get($i))) {
+                    $exception = new kolab_format_event($exobj);
+                    if ($exception->is_valid()) {
+                        $object['recurrence']['EXCEPTIONS'][] = $this->expand_exception($exception->to_array(), $object);
+                    }
+                }
+            }
+        }
+
+        // merge with additional data, e.g. attachments from the message
+        if ($data) {
+            foreach ($data as $idx => $value) {
+                if (is_array($value)) {
+                    $object[$idx] = array_merge((array)$object[$idx], $value);
+                }
+                else {
+                    $object[$idx] = $value;
+                }
+            }
+        }
+
         $this->data = $object;
         return $this->data;
     }
@@ -188,4 +237,45 @@ class kolab_format_event extends kolab_format_xcal
         return $tags;
     }
 
+    /**
+     * Reduce the exception container to attributes which differ from the master event
+     */
+    private function compact_exception($exception, $master)
+    {
+      static $mandatory = array('uid','created','start');
+      static $forbidden = array('recurrence','attendees','sequence');
+
+      $out = $exception;
+      foreach ($exception as $prop => $val) {
+        if (in_array($prop, $mandatory))
+          continue;
+
+        if (is_object($exception[$prop]) && is_a($exception[$prop], 'DateTime'))
+          $equals = $exception[$prop] <> $master[$prop];
+        else
+          $equals = $exception[$prop] == $master[$prop];
+
+        if ($equals || in_array($prop, $forbidden)) {
+          unset($out[$prop]);
+        }
+      }
+
+      return $out;
+    }
+
+    /**
+     * Copy attributes not specified by the exception from the master event
+     */
+    private function expand_exception($exception, $master)
+    {
+      static $forbidden = array('recurrence');
+
+      foreach ($master as $prop => $value) {
+        if (empty($exception[$prop]) && !in_array($prop, $forbidden))
+          $exception[$prop] = $value;
+      }
+
+      return $exception;
+    }
+
 }
diff --git a/plugins/libkolab/lib/kolab_format_xcal.php b/plugins/libkolab/lib/kolab_format_xcal.php
index 68f4d0d..1534363 100644
--- a/plugins/libkolab/lib/kolab_format_xcal.php
+++ b/plugins/libkolab/lib/kolab_format_xcal.php
@@ -111,7 +111,7 @@ abstract class kolab_format_xcal extends kolab_format
         );
 
         // read organizer and attendees
-        if ($organizer = $this->obj->organizer()) {
+        if (($organizer = $this->obj->organizer()) && ($organizer->email() || $organizer->name())) {
             $object['organizer'] = array(
                 'email' => $organizer->email(),
                 'name' => $organizer->name(),
@@ -170,9 +170,9 @@ abstract class kolab_format_xcal extends kolab_format
                 $object['recurrence']['BYMONTH'] = join(',', self::vector2array($bymonth));
             }
 
-            if ($exceptions = $this->obj->exceptionDates()) {
-                for ($i=0; $i < $exceptions->size(); $i++) {
-                    if ($exdate = self::php_datetime($exceptions->get($i)))
+            if ($exdates = $this->obj->exceptionDates()) {
+                for ($i=0; $i < $exdates->size(); $i++) {
+                    if ($exdate = self::php_datetime($exdates->get($i)))
                         $object['recurrence']['EXDATE'][] = $exdate;
                 }
             }


commit 57dbd019ddc70468438b3e37743c2bfbdaac6116
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Jan 9 16:27:20 2013 +0100

    Use /this/ to reference asset files in plugin templates

diff --git a/plugins/calendar/skins/larry/templates/calendar.html b/plugins/calendar/skins/larry/templates/calendar.html
index 458bd2a..289c8b0 100644
--- a/plugins/calendar/skins/larry/templates/calendar.html
+++ b/plugins/calendar/skins/larry/templates/calendar.html
@@ -3,7 +3,7 @@
 <head>
 <title><roundcube:object name="pagetitle" /></title>
 <roundcube:include file="/includes/links.html" />
-<!--[if lte IE 7]><link rel="stylesheet" type="text/css" href="./plugins/calendar/$__skin_path/iehacks.css" /><![endif]-->
+<!--[if lte IE 7]><link rel="stylesheet" type="text/css" href="/this/iehacks.css" /><![endif]-->
 </head>
 <body class="calendarmain noscroll">
 
diff --git a/plugins/tasklist/skins/larry/templates/mainview.html b/plugins/tasklist/skins/larry/templates/mainview.html
index 93914e1..83d39bd 100644
--- a/plugins/tasklist/skins/larry/templates/mainview.html
+++ b/plugins/tasklist/skins/larry/templates/mainview.html
@@ -3,7 +3,7 @@
 <head>
 <title><roundcube:object name="pagetitle" /></title>
 <roundcube:include file="/includes/links.html" />
-<!--[if lte IE 8]><link rel="stylesheet" type="text/css" href="./plugins/tasklist/skins/larry/iehacks.css" /><![endif]-->
+<!--[if lte IE 8]><link rel="stylesheet" type="text/css" href="/this/iehacks.css" /><![endif]-->
 </head>
 <body class="tasklistview noscroll">
 


commit c20fb0173f76414cedc0dbec90d77d3b0d566f24
Author: Aleksander Machniak <machniak at kolabsys.com>
Date:   Fri Apr 12 19:05:45 2013 +0200

    Remove useless 'vcard' data on contact import - fixes problem with such data
    serialization in kolab_cache (Bug #1711)

diff --git a/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php b/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php
index 9e3ea91..57dd4f5 100644
--- a/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php
+++ b/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php
@@ -1139,6 +1139,9 @@ class rcube_kolab_contacts extends rcube_addressbook
             }
         }
 
+        // When importing contacts 'vcard' data is added, we don't need it (Bug #1711)
+        unset($contact['vcard']);
+
         // add empty values for some fields which can be removed in the UI
         return array_filter($contact) + array('nickname' => '', 'birthday' => '', 'anniversary' => '', 'freebusyurl' => '', 'photo' => $contact['photo']);
     }


commit 65d2e79c7a0509eec88cc6922a4d1658a9d5451b
Author: Aleksander Machniak <machniak at kolabsys.com>
Date:   Wed Apr 10 13:56:04 2013 +0200

    Update folder type annotation on special folder change (#1031)

diff --git a/plugins/kolab_folders/kolab_folders.php b/plugins/kolab_folders/kolab_folders.php
index 3f5170e..cf2bb77 100644
--- a/plugins/kolab_folders/kolab_folders.php
+++ b/plugins/kolab_folders/kolab_folders.php
@@ -54,6 +54,9 @@ class kolab_folders extends rcube_plugin
         $this->add_hook('folder_delete', array($this, 'folder_save'));
         $this->add_hook('folder_rename', array($this, 'folder_save'));
         $this->add_hook('folders_list', array($this, 'folders_list'));
+
+        // Special folders setting
+        $this->add_hook('preferences_save', array($this, 'prefs_save'));
     }
 
     /**
@@ -315,6 +318,86 @@ class kolab_folders extends rcube_plugin
     }
 
     /**
+     * Handler for user preferences save (preferences_save hook)
+     *
+     * @param array $args Hash array with hook parameters
+     *
+     * @return array Hash array with modified hook parameters
+     */
+    public function prefs_save($args)
+    {
+        if ($args['section'] != 'folders') {
+            return $args;
+        }
+
+        // Load configuration
+        $this->load_config();
+
+        // Check that configuration is not disabled
+        $dont_override  = (array) $this->rc->config->get('dont_override', array());
+
+        // special handling for 'default_folders'
+        if (in_array('default_folders', $dont_override)) {
+            return $args;
+        }
+
+        // map config option name to kolab folder type annotation
+        $opts = array(
+            'drafts_mbox' => 'mail.drafts',
+            'sent_mbox'   => 'mail.sentitems',
+            'junk_mbox'   => 'mail.junkemail',
+            'trash_mbox'  => 'mail.wastebasket',
+        );
+
+        // check if any of special folders has been changed
+        foreach ($opts as $opt_name => $type) {
+            $new = $args['prefs'][$opt_name];
+            $old = $this->rc->config->get($opt_name);
+            if ($new === $old) {
+                unset($opts[$opt_name]);
+            }
+        }
+
+        if (empty($opts)) {
+            return $args;
+        }
+
+        $storage    = $this->rc->get_storage();
+        $folderdata = $storage->get_metadata('*', array(kolab_storage::CTYPE_KEY_PRIVATE, kolab_storage::CTYPE_KEY));
+
+        if (!is_array($folderdata)) {
+             return $args;
+        }
+
+        $folderdata = array_map(array('kolab_storage', 'folder_select_metadata'), $folderdata);
+
+        foreach ($opts as $opt_name => $type) {
+            $foldername = $args['prefs'][$opt_name];
+            if (strlen($foldername)) {
+
+                // get all folders of specified type
+                $folders = array_intersect($folderdata, array($type));
+
+                // folder already annotated with specified type
+                if (!empty($folders[$foldername])) {
+                    continue;
+                }
+
+                // set type to the new folder
+                $this->set_folder_type($foldername, $type);
+
+                // unset old folder(s) type annotation
+                list($maintype, $subtype) = explode('.', $type);
+                foreach (array_keys($folders) as $folder) {
+                    $this->set_folder_type($folder, $maintype);
+                }
+            }
+        }
+
+        return $args;
+    }
+
+    /**
      * Checks if IMAP server supports any of METADATA, ANNOTATEMORE, ANNOTATEMORE2
      *
      * @return boolean


commit 17fa7e9ea117a23a718d62e2ee01d840d22a6adf
Author: Aleksander Machniak <machniak at kolabsys.com>
Date:   Fri Jun 21 11:29:34 2013 +0200

    Fix link to ActiveSync configuration wiki-page (Bug #1629)
    
    Conflicts:
    
    	plugins/kolab_activesync/localization/pl_PL.inc

diff --git a/plugins/kolab_activesync/localization/de_CH.inc b/plugins/kolab_activesync/localization/de_CH.inc
index e9aca2d..e2027c9 100644
--- a/plugins/kolab_activesync/localization/de_CH.inc
+++ b/plugins/kolab_activesync/localization/de_CH.inc
@@ -22,7 +22,7 @@ $labels['configuration'] = 'Konfiguration';
 $labels['deletedevice'] = 'Gerät löschen';
 $labels['imageformat'] = 'Bildformat';
 $labels['laxpiclabel'] = 'PNG- und GIF-Bilder erlauben';
-$labels['nodevices'] = 'Es sind noch keine Geräte registriert.<br/><br/>Um ein neues Gerät anzumelden, verbinden Sie dieses zuerst mit dem Server. Eine Anleitung dazu finden Sie im <a href="http://wiki.kolab.org/Z_push#Clients">Wiki</a>. Anschliessend laden Sie diese Seite neu und das Gerät wird hier aufgelistet.';
+$labels['nodevices'] = 'Es sind noch keine Geräte registriert.<br/><br/>Um ein neues Gerät anzumelden, verbinden Sie dieses zuerst mit dem Server. Eine Anleitung dazu finden Sie im <a href="http://wiki.kolab.org/Setup_ActiveSync">Wiki</a>. Anschliessend laden Sie diese Seite neu und das Gerät wird hier aufgelistet.';
 $labels['savingdata'] = 'Daten werden gespeichert...';
 $labels['savingerror'] = 'Fehler beim Speichern';
 $labels['notsupported'] = 'Ihr Server unterstützt keine Activesync-Konfiguration';
diff --git a/plugins/kolab_activesync/localization/de_DE.inc b/plugins/kolab_activesync/localization/de_DE.inc
index 237e37f..421f804 100644
--- a/plugins/kolab_activesync/localization/de_DE.inc
+++ b/plugins/kolab_activesync/localization/de_DE.inc
@@ -22,7 +22,7 @@ $labels['configuration'] = 'Konfiguration';
 $labels['deletedevice'] = 'Gerät löschen';
 $labels['imageformat'] = 'Bildformat';
 $labels['laxpiclabel'] = 'PNG- und GIF-Bilder erlauben';
-$labels['nodevices'] = 'Es sind noch keine Geräte registriert.<br/><br/>Um ein neues Gerät anzumelden, verbinden Sie dieses zuerst mit dem Server. Eine Anleitung dazu finden Sie im <a href="http://wiki.kolab.org/Z_push#Clients">Wiki</a>. Anschliessend laden Sie diese Seite neu und das Gerät wird hier aufgelistet.';
+$labels['nodevices'] = 'Es sind noch keine Geräte registriert.<br/><br/>Um ein neues Gerät anzumelden, verbinden Sie dieses zuerst mit dem Server. Eine Anleitung dazu finden Sie im <a href="http://wiki.kolab.org/Setup_ActiveSync">Wiki</a>. Anschliessend laden Sie diese Seite neu und das Gerät wird hier aufgelistet.';
 $labels['savingdata'] = 'Daten werden gespeichert...';
 $labels['savingerror'] = 'Fehler beim Speichern';
 $labels['notsupported'] = 'Ihr Server unterstützt keine Activesync-Konfiguration';
diff --git a/plugins/kolab_activesync/localization/en_US.inc b/plugins/kolab_activesync/localization/en_US.inc
index 2399b13..3eb5830 100644
--- a/plugins/kolab_activesync/localization/en_US.inc
+++ b/plugins/kolab_activesync/localization/en_US.inc
@@ -22,7 +22,7 @@ $labels['configuration'] = 'Configuration';
 $labels['deletedevice'] = 'Delete device';
 $labels['imageformat'] = 'Image format';
 $labels['laxpiclabel'] = 'Allow PNG and GIF images';
-$labels['nodevices'] = 'There are currently no devices registered.<br/><br/>In order to register a device, please connect it to the server first, using <a href="http://wiki.kolab.org/Z_push#Clients">the instructions in the Wiki</a>. Afterwards the device should become available for configuration here.';
+$labels['nodevices'] = 'There are currently no devices registered.<br/><br/>In order to register a device, please connect it to the server first, using <a href="http://wiki.kolab.org/Setup_ActiveSync">the instructions in the Wiki</a>. Afterwards the device should become available for configuration here.';
 $labels['savingdata'] = 'Saving data...';
 $labels['savingerror'] = 'Failed to save configuration';
 $labels['notsupported'] = 'Your server does not support metadata/annotations';
diff --git a/plugins/kolab_activesync/localization/es_ES.inc b/plugins/kolab_activesync/localization/es_ES.inc
index c809afc..b7eb366 100644
--- a/plugins/kolab_activesync/localization/es_ES.inc
+++ b/plugins/kolab_activesync/localization/es_ES.inc
@@ -22,7 +22,7 @@ $labels['configuration'] = 'Configuración';
 $labels['deletedevice'] = 'Delete device';
 $labels['imageformat'] = 'Image format';
 $labels['laxpiclabel'] = 'Allow PNG and GIF images';
-$labels['nodevices'] = 'There are currently no devices registered.<br/><br/>In order to register a device, please connect it to the server first, using <a href="http://wiki.kolab.org/Z_push#Clients">the instructions in the Wiki</a>. Afterwards the device should become available for configuration here.';
+$labels['nodevices'] = 'There are currently no devices registered.<br/><br/>In order to register a device, please connect it to the server first, using <a href="http://wiki.kolab.org/Setup_ActiveSync">the instructions in the Wiki</a>. Afterwards the device should become available for configuration here.';
 $labels['savingdata'] = 'Guardando datos...';
 $labels['savingerror'] = 'Failed to save configuration';
 $labels['notsupported'] = 'Your server does not support metadata/annotations';
diff --git a/plugins/kolab_activesync/localization/et_EE.inc b/plugins/kolab_activesync/localization/et_EE.inc
index 32fb9b9..6ea633d 100644
--- a/plugins/kolab_activesync/localization/et_EE.inc
+++ b/plugins/kolab_activesync/localization/et_EE.inc
@@ -22,7 +22,7 @@ $labels['configuration'] = 'Configuration';
 $labels['deletedevice'] = 'Delete device';
 $labels['imageformat'] = 'Image format';
 $labels['laxpiclabel'] = 'Allow PNG and GIF images';
-$labels['nodevices'] = 'There are currently no devices registered.<br/><br/>In order to register a device, please connect it to the server first, using <a href="http://wiki.kolab.org/Z_push#Clients">the instructions in the Wiki</a>. Afterwards the device should become available for configuration here.';
+$labels['nodevices'] = 'There are currently no devices registered.<br/><br/>In order to register a device, please connect it to the server first, using <a href="http://wiki.kolab.org/Setup_ActiveSync">the instructions in the Wiki</a>. Afterwards the device should become available for configuration here.';
 $labels['savingdata'] = 'Saving data...';
 $labels['savingerror'] = 'Failed to save configuration';
 $labels['notsupported'] = 'Your server does not support metadata/annotations';
diff --git a/plugins/kolab_activesync/localization/fr_FR.inc b/plugins/kolab_activesync/localization/fr_FR.inc
index 6dacc0b..01614d9 100644
--- a/plugins/kolab_activesync/localization/fr_FR.inc
+++ b/plugins/kolab_activesync/localization/fr_FR.inc
@@ -22,7 +22,7 @@ $labels['configuration'] = 'Configuration';
 $labels['deletedevice'] = 'Supprimer ce périphérique';
 $labels['imageformat'] = 'Format d\'image';
 $labels['laxpiclabel'] = 'Autoriser les images PNG et GIF';
-$labels['nodevices'] = 'Il n\'y a actuellement pas de périphérique enregistrés.<br/><br/>Pour enregistrer un périphérique, merci de d\'abord le connecter au serveur, en suivant <a href="http://wiki.kolab.org/Z_push#Clients">les instructions sur le Wiki</a>. Ensuite le périphérique sera accessible ici pour sa configuration.';
+$labels['nodevices'] = 'Il n\'y a actuellement pas de périphérique enregistrés.<br/><br/>Pour enregistrer un périphérique, merci de d\'abord le connecter au serveur, en suivant <a href="http://wiki.kolab.org/Setup_ActiveSync">les instructions sur le Wiki</a>. Ensuite le périphérique sera accessible ici pour sa configuration.';
 $labels['savingdata'] = 'Enregistrer...';
 $labels['savingerror'] = 'Échec de l\'enregistrement de la configuration';
 $labels['notsupported'] = 'Votre serveur ne supporte pas les métadonnées ou les annotations';
diff --git a/plugins/kolab_activesync/localization/ja_JP.inc b/plugins/kolab_activesync/localization/ja_JP.inc
index dd0e379..6b1588a 100644
--- a/plugins/kolab_activesync/localization/ja_JP.inc
+++ b/plugins/kolab_activesync/localization/ja_JP.inc
@@ -22,7 +22,7 @@ $labels['configuration'] = '設定';
 $labels['deletedevice'] = 'Delete device';
 $labels['imageformat'] = 'Image format';
 $labels['laxpiclabel'] = 'Allow PNG and GIF images';
-$labels['nodevices'] = 'There are currently no devices registered.<br/><br/>In order to register a device, please connect it to the server first, using <a href="http://wiki.kolab.org/Z_push#Clients">the instructions in the Wiki</a>. Afterwards the device should become available for configuration here.';
+$labels['nodevices'] = 'There are currently no devices registered.<br/><br/>In order to register a device, please connect it to the server first, using <a href="http://wiki.kolab.org/Setup_ActiveSync">the instructions in the Wiki</a>. Afterwards the device should become available for configuration here.';
 $labels['savingdata'] = 'Saving data...';
 $labels['savingerror'] = 'Failed to save configuration';
 $labels['notsupported'] = 'Your server does not support metadata/annotations';
diff --git a/plugins/kolab_activesync/localization/nl_NL.inc b/plugins/kolab_activesync/localization/nl_NL.inc
index 88ce6ea..a78afdc 100644
--- a/plugins/kolab_activesync/localization/nl_NL.inc
+++ b/plugins/kolab_activesync/localization/nl_NL.inc
@@ -22,7 +22,7 @@ $labels['configuration'] = 'Configuratie';
 $labels['deletedevice'] = 'Delete device';
 $labels['imageformat'] = 'Image format';
 $labels['laxpiclabel'] = 'Sta PNG en GIF plaatjes toe';
-$labels['nodevices'] = 'There are currently no devices registered.<br/><br/>In order to register a device, please connect it to the server first, using <a href="http://wiki.kolab.org/Z_push#Clients">the instructions in the Wiki</a>. Afterwards the device should become available for configuration here.';
+$labels['nodevices'] = 'There are currently no devices registered.<br/><br/>In order to register a device, please connect it to the server first, using <a href="http://wiki.kolab.org/Setup_ActiveSync">the instructions in the Wiki</a>. Afterwards the device should become available for configuration here.';
 $labels['savingdata'] = 'Data wordt opgeslagen...';
 $labels['savingerror'] = 'Failed to save configuration';
 $labels['notsupported'] = 'Your server does not support metadata/annotations';
diff --git a/plugins/kolab_activesync/localization/pl_PL.inc b/plugins/kolab_activesync/localization/pl_PL.inc
index 0bb213e..4d1c413 100644
--- a/plugins/kolab_activesync/localization/pl_PL.inc
+++ b/plugins/kolab_activesync/localization/pl_PL.inc
@@ -1,33 +1,31 @@
 <?php
 
 $labels = array();
-$labels['tabtitle'] = 'Activesync';
-$labels['devices'] = 'Devices';
-$labels['devicealias'] = 'Device name';
-$labels['syncmode'] = 'Sync Mode';
-$labels['modeauto'] = 'Determine automatically';
-$labels['modeflat'] = 'Force flat mode';
-$labels['modefolder'] = 'Force folder mode';
-$labels['synchronize'] = 'Synchronize';
-$labels['withalarms'] = 'With alarms';
-$labels['syncsettings'] = 'Synchronization settings';
-$labels['deviceconfig'] = 'Device configration';
-$labels['folderstosync'] = 'Folders to synchronize';
-$labels['mail'] = 'Email';
-$labels['contact'] = 'Address Books';
-$labels['event'] = 'Calendars';
-$labels['task'] = 'Tasks';
-$labels['note'] = 'Notes';
-$labels['configuration'] = 'Konfiguracja';
-$labels['deletedevice'] = 'Delete device';
-$labels['imageformat'] = 'Image format';
-$labels['laxpiclabel'] = 'Allow PNG and GIF images';
-$labels['nodevices'] = 'There are currently no devices registered.<br/><br/>In order to register a device, please connect it to the server first, using <a href="http://wiki.kolab.org/Z_push#Clients">the instructions in the Wiki</a>. Afterwards the device should become available for configuration here.';
-$labels['savingdata'] = 'ZapisujÄ™ dane...';
-$labels['savingerror'] = 'Failed to save configuration';
-$labels['notsupported'] = 'Your server does not support metadata/annotations';
-$labels['devicedeleteconfirm'] = 'Do you really want to delete the configuration for this device?';
-$labels['successfullydeleted'] = 'The device configuration was successfully removed';
-$labels['devicenotfound'] = 'Unable to read device configuration';
+$labels['tabtitle'] = 'ActiveSync';
+$labels['devices'] = 'UrzÄ…dzenia';
+$labels['devicealias'] = 'Nazwa urzÄ…dzenia';
+$labels['syncmode'] = 'Tryb synchronizacji';
+$labels['modeauto'] = 'Ustaw automatycznie';
+$labels['modeflat'] = 'Wymuś tryb płaski';
+$labels['modefolder'] = 'WymuÅ› tryb folderu';
+$labels['synchronize'] = 'Synchronizuj';
+$labels['withalarms'] = 'Z alarmami';
+$labels['syncsettings'] = 'Ustawienia synchronizacji';
+$labels['deviceconfig'] = 'Konfiguracja urzÄ…dzenia';
+$labels['folderstosync'] = 'Foldery do synchronizacji';
+$labels['mail'] = 'Poczta';
+$labels['contact'] = 'Książki adresowe';
+$labels['event'] = 'Kalendarze';
+$labels['task'] = 'Zadania';
+$labels['note'] = 'Notatki';
+$labels['deletedevice'] = 'Usuń urządzenie';
+$labels['imageformat'] = 'Format obrazka';
+$labels['laxpiclabel'] = 'Zezwalaj na obrazki PNG i GIF';
+$labels['nodevices'] = 'Obecnie brak zarejestrowanych urządzeń.<br/><br/>Aby zarejestrować urządzenie najpierw podłącz je do serwera według <a href="http://wiki.kolab.org/Setup_ActiveSync">instrukcji z Wiki</a>. Po tym procesie urządzenie powinno dać się skonfigurować tutaj.';
+$labels['savingdata'] = 'Zapisywanie danych...';
+$labels['savingerror'] = 'Nie udało się zapisać konfiguracji';
+$labels['notsupported'] = 'Twój serwer nie obsługuje metadanych (metadata/annotations).';
+$labels['devicedeleteconfirm'] = 'Czy na pewno chcesz usunąć konfigurację dla tego urządzenia?';
+$labels['successfullydeleted'] = 'Konfiguracja urządzenia została pomyślnie usunięta.';
 
 ?>
diff --git a/plugins/kolab_activesync/localization/ru_RU.inc b/plugins/kolab_activesync/localization/ru_RU.inc
index e27bc21..446e6db 100644
--- a/plugins/kolab_activesync/localization/ru_RU.inc
+++ b/plugins/kolab_activesync/localization/ru_RU.inc
@@ -22,7 +22,7 @@ $labels['configuration'] = 'Конфигурация';
 $labels['deletedevice'] = 'Удалить устройство';
 $labels['imageformat'] = 'Формат изображений';
 $labels['laxpiclabel'] = 'Разрешить PNG и GIF изображения';
-$labels['nodevices'] = 'У вас нет зарегистрированных устройств.<br/><br/>Для того, чтобы зарегистрировать устройство, подключите его к серверу, используя <a href="http://wiki.kolab.org/Z_push#Clients">инструкцию в Wiki</a>. После этого устройство должно стать доступным для конфигурации здесь.';
+$labels['nodevices'] = 'У вас нет зарегистрированных устройств.<br/><br/>Для того, чтобы зарегистрировать устройство, подключите его к серверу, используя <a href="http://wiki.kolab.org/Setup_ActiveSync">инструкцию в Wiki</a>. После этого устройство должно стать доступным для конфигурации здесь.';
 $labels['savingdata'] = 'Сохранение данных...';
 $labels['savingerror'] = 'Не удалось сохранить конфигурацию';
 $labels['notsupported'] = 'Ваш сервер не поддерживает метаданные/аннотации';


commit 62858d5be0bd49b178dae6be70ff455d1f43c06b
Author: Aleksander Machniak <machniak at kolabsys.com>
Date:   Tue Apr 9 18:50:47 2013 +0200

    Fix PHP warning when folder rights is not available (e.g. non-existing folder)

diff --git a/plugins/kolab_folders/kolab_folders.php b/plugins/kolab_folders/kolab_folders.php
index a45c108..3f5170e 100644
--- a/plugins/kolab_folders/kolab_folders.php
+++ b/plugins/kolab_folders/kolab_folders.php
@@ -178,7 +178,7 @@ class kolab_folders extends rcube_plugin
         // Don't allow changing type of shared folder, according to ACL
         if (strlen($mbox)) {
             $options = $storage->folder_info($mbox);
-            if ($options['namespace'] != 'personal' && !in_array('a', $options['rights'])) {
+            if ($options['namespace'] != 'personal' && !in_array('a', (array)$options['rights'])) {
                 if (in_array($ctype, $this->types)) {
                     $value = $this->gettext('foldertype'.$ctype);
                 }





More information about the commits mailing list