Branch 'roundcubemail-plugins-kolab-3.0' - 38 commits - plugins/calendar plugins/kolab_activesync plugins/kolab_addressbook plugins/kolab_auth plugins/kolab_delegation plugins/kolab_folders plugins/libcalendaring plugins/libkolab plugins/owncloud plugins/tasklist .tx/config

Aleksander Machniak machniak at kolabsys.com
Fri Jun 21 11:24:39 CEST 2013


 .tx/config                                                     |    2 
 plugins/calendar/calendar.php                                  |   51 +
 plugins/calendar/calendar_ui.js                                |  120 ++-
 plugins/calendar/drivers/database/database_driver.php          |   20 
 plugins/calendar/drivers/kolab/kolab_driver.php                |   15 
 plugins/calendar/lib/calendar_ical.php                         |   33 
 plugins/calendar/lib/calendar_recurrence.php                   |    8 
 plugins/calendar/localization/cs_CZ.inc                        |  381 +++++-----
 plugins/calendar/localization/de_CH.inc                        |    2 
 plugins/calendar/localization/de_DE.inc                        |    2 
 plugins/calendar/localization/en.inc                           |  230 ------
 plugins/calendar/localization/en_US.inc                        |    2 
 plugins/calendar/localization/fr_FR.inc                        |    2 
 plugins/calendar/skins/classic/iehacks.css                     |    8 
 plugins/calendar/skins/classic/templates/calendar.html         |    6 
 plugins/calendar/skins/classic/templates/eventedit.html        |    4 
 plugins/calendar/skins/larry/calendar.css                      |   30 
 plugins/calendar/skins/larry/iehacks.css                       |   44 -
 plugins/calendar/skins/larry/templates/calendar.html           |    9 
 plugins/calendar/skins/larry/templates/eventedit.html          |    6 
 plugins/kolab_activesync/kolab_activesync.js                   |    4 
 plugins/kolab_activesync/localization/en.inc                   |   33 
 plugins/kolab_addressbook/lib/rcube_kolab_contacts.php         |   57 -
 plugins/kolab_addressbook/localization/en.inc                  |   47 -
 plugins/kolab_auth/localization/en.inc                         |    5 
 plugins/kolab_delegation/localization/en.inc                   |   30 
 plugins/kolab_folders/localization/en.inc                      |   26 
 plugins/libcalendaring/libcalendaring.js                       |    6 
 plugins/libcalendaring/localization/cs_CZ.inc                  |   30 
 plugins/libcalendaring/localization/fr_FR.inc                  |   30 
 plugins/libkolab/lib/kolab_format.php                          |   61 +
 plugins/libkolab/lib/kolab_format_contact.php                  |   64 +
 plugins/libkolab/lib/kolab_format_distributionlist.php         |   19 
 plugins/libkolab/lib/kolab_format_event.php                    |   14 
 plugins/libkolab/lib/kolab_format_file.php                     |   17 
 plugins/libkolab/lib/kolab_format_journal.php                  |   20 
 plugins/libkolab/lib/kolab_format_note.php                     |   20 
 plugins/libkolab/lib/kolab_format_task.php                     |    4 
 plugins/libkolab/lib/kolab_format_xcal.php                     |   44 -
 plugins/libkolab/lib/kolab_storage_folder.php                  |   36 
 plugins/owncloud/localization/en.inc                           |    6 
 plugins/tasklist/drivers/database/tasklist_database_driver.php |    2 
 plugins/tasklist/jquery.tagedit.js                             |    2 
 plugins/tasklist/localization/cs_CZ.inc                        |   68 +
 plugins/tasklist/localization/de_CH.inc                        |    2 
 plugins/tasklist/localization/de_DE.inc                        |   97 +-
 plugins/tasklist/localization/en.inc                           |   68 -
 plugins/tasklist/localization/en_US.inc                        |    2 
 plugins/tasklist/skins/larry/templates/mainview.html           |   10 
 plugins/tasklist/skins/larry/templates/taskedit.html           |   12 
 plugins/tasklist/tasklist.js                                   |    7 
 51 files changed, 834 insertions(+), 984 deletions(-)

New commits:
commit 326be4e92b978c7a3b9bc058a9edebdf0cd6be3b
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Mar 28 18:06:45 2013 +0100

    Avoid duplicate entries for attendees that are already listed as organizer

diff --git a/plugins/libkolab/lib/kolab_format_xcal.php b/plugins/libkolab/lib/kolab_format_xcal.php
index b975332..68f4d0d 100644
--- a/plugins/libkolab/lib/kolab_format_xcal.php
+++ b/plugins/libkolab/lib/kolab_format_xcal.php
@@ -124,13 +124,15 @@ abstract class kolab_format_xcal extends kolab_format
         for ($i=0; $i < $attvec->size(); $i++) {
             $attendee = $attvec->get($i);
             $cr = $attendee->contact();
-            $object['attendees'][] = array(
-                'role' => $role_map[$attendee->role()],
-                'status' => $part_status_map[$attendee->partStat()],
-                'rsvp' => $attendee->rsvp(),
-                'email' => $cr->email(),
-                'name' => $cr->name(),
-            );
+            if ($cr->email() != $object['organizer']['email']) {
+                $object['attendees'][] = array(
+                    'role' => $role_map[$attendee->role()],
+                    'status' => $part_status_map[$attendee->partStat()],
+                    'rsvp' => $attendee->rsvp(),
+                    'email' => $cr->email(),
+                    'name' => $cr->name(),
+                );
+            }
         }
 
         // read recurrence rule
@@ -238,7 +240,7 @@ abstract class kolab_format_xcal extends kolab_format
             if ($attendee['role'] == 'ORGANIZER') {
                 $object['organizer'] = $attendee;
             }
-            else {
+            else if ($attendee['email'] != $object['organizer']['email']) {
                 $cr = new ContactReference(ContactReference::EmailReference, $attendee['email']);
                 $cr->setName($attendee['name']);
 


commit 0b5e5c5733832a13b9677c8a86da56bbc4828525
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Mar 27 11:15:14 2013 +0100

    Fix wrong participant status mapping for event attendees (#1722)

diff --git a/plugins/libkolab/lib/kolab_format_xcal.php b/plugins/libkolab/lib/kolab_format_xcal.php
index 6dcd328..b975332 100644
--- a/plugins/libkolab/lib/kolab_format_xcal.php
+++ b/plugins/libkolab/lib/kolab_format_xcal.php
@@ -244,7 +244,7 @@ abstract class kolab_format_xcal extends kolab_format
 
                 $att = new Attendee;
                 $att->setContact($cr);
-                $att->setPartStat($this->status_map[$attendee['status']]);
+                $att->setPartStat($this->part_status_map[$attendee['status']]);
                 $att->setRole($this->role_map[$attendee['role']] ? $this->role_map[$attendee['role']] : kolabformat::Required);
                 $att->setRSVP((bool)$attendee['rsvp']);
 


commit cb74b9ca4c12d5ea75f7aeaadbb0ab4a0f0c9bd4
Author: Aleksander Machniak <machniak at kolabsys.com>
Date:   Fri Mar 22 13:02:21 2013 +0100

    Fix title (undefined label) on more actions button

diff --git a/plugins/calendar/skins/classic/templates/calendar.html b/plugins/calendar/skins/classic/templates/calendar.html
index 0725987..80255ff 100644
--- a/plugins/calendar/skins/classic/templates/calendar.html
+++ b/plugins/calendar/skins/classic/templates/calendar.html
@@ -21,7 +21,7 @@
       </div>
       <div class="boxfooter">
         <roundcube:button command="calendar-create" type="link" title="calendar.createcalendar" class="buttonPas addgroup" classAct="button addgroup" content=" " />
-        <roundcube:button name="calendaroptionslink" id="calendaroptionslink" type="link" title="calendaractions" class="button groupactions" onclick="rcmail_ui.show_popup('calendaroptions');return false" content=" " />
+        <roundcube:button name="calendaroptionslink" id="calendaroptionslink" type="link" title="moreactions" class="button groupactions" onclick="rcmail_ui.show_popup('calendaroptions');return false" content=" " />
       </div>
     </div>
   </div>
diff --git a/plugins/calendar/skins/larry/templates/calendar.html b/plugins/calendar/skins/larry/templates/calendar.html
index a814345..458bd2a 100644
--- a/plugins/calendar/skins/larry/templates/calendar.html
+++ b/plugins/calendar/skins/larry/templates/calendar.html
@@ -26,7 +26,7 @@
 			<roundcube:object name="plugin.calendar_list" id="calendarslist" class="listing" />
 			</div>
 			<div class="boxfooter">
-				<roundcube:button command="calendar-create" type="link" title="calendar.createcalendar" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" content="+" /><roundcube:button name="calendaroptionslink" id="calendaroptionsmenulink" type="link" title="calendar.calendaractions" class="listbutton groupactions" onclick="UI.show_popup('calendaroptionsmenu', undefined, { above:true });return false" innerClass="inner" content="⚙" />
+				<roundcube:button command="calendar-create" type="link" title="calendar.createcalendar" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" content="+" /><roundcube:button name="calendaroptionslink" id="calendaroptionsmenulink" type="link" title="moreactions" class="listbutton groupactions" onclick="UI.show_popup('calendaroptionsmenu', undefined, { above:true });return false" innerClass="inner" content="⚙" />
 			</div>
 		</div>
 	</div>


commit 9fc4a17451e2e700de8e29e68033bc5479f68ca0
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Mar 21 10:02:35 2013 +0100

    Adapt libkolab and kolab_addressbook to support type parameters for email addresses

diff --git a/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php b/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php
index 6e32bc7..9e3ea91 100644
--- a/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php
+++ b/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php
@@ -44,7 +44,7 @@ class rcube_kolab_contacts extends rcube_addressbook
       'jobtitle'     => array('limit' => 1),
       'organization' => array('limit' => 1),
       'department'   => array('limit' => 1),
-      'email'        => array('subtypes' => null),
+      'email'        => array('subtypes' => array('home','work','other')),
       'phone'        => array(),
       'address'      => array('subtypes' => array('home','work','office')),
       'website'      => array('subtypes' => array('homepage','blog')),
@@ -1035,21 +1035,15 @@ class rcube_kolab_contacts extends rcube_addressbook
     {
         $record['ID'] = $this->_uid2id($record['uid']);
 
-        if (is_array($record['phone'])) {
-            $phones = $record['phone'];
-            unset($record['phone']);
-            foreach ((array)$phones as $i => $phone) {
-                $key = 'phone' . ($phone['type'] ? ':' . $phone['type'] : '');
-                $record[$key][] = $phone['number'];
-            }
-        }
-
-        if (is_array($record['website'])) {
-            $urls = $record['website'];
-            unset($record['website']);
-            foreach ((array)$urls as $i => $url) {
-                $key = 'website' . ($url['type'] ? ':' . $url['type'] : '');
-                $record[$key][] = $url['url'];
+        // convert email, website, phone values
+        foreach (array('email'=>'address', 'website'=>'url', 'phone'=>'number') as $col => $propname) {
+            if (is_array($record[$col])) {
+                $values = $record[$col];
+                unset($record[$col]);
+                foreach ((array)$values as $i => $val) {
+                    $key = $col . ($val['type'] ? ':' . $val['type'] : '');
+                    $record[$key][] = $val[$propname];
+                }
             }
         }
 
@@ -1093,31 +1087,22 @@ class rcube_kolab_contacts extends rcube_addressbook
         else if (!$contact['uid'] && $old['uid'])
             $contact['uid'] = $old['uid'];
 
-        $contact['email'] = array_filter($this->get_col_values('email', $contact, true));
         $contact['im']    = array_filter($this->get_col_values('im', $contact, true));
 
-        $websites  = array();
-        $phones    = array();
-        $addresses = array();
-
-        foreach ($this->get_col_values('website', $contact) as $type => $values) {
-            foreach ((array)$values as $url) {
-                if (!empty($url)) {
-                    $websites[] = array('url' => $url, 'type' => $type);
-                }
-            }
-            unset($contact['website:'.$type]);
-        }
-
-        foreach ($this->get_col_values('phone', $contact) as $type => $values) {
-            foreach ((array)$values as $phone) {
-                if (!empty($phone)) {
-                    $phones[] = array('number' => $phone, 'type' => $type);
+        // convert email, website, phone values
+        foreach (array('email'=>'address', 'website'=>'url', 'phone'=>'number') as $col => $propname) {
+            $contact[$col] = array();
+            foreach ($this->get_col_values($col, $contact) as $type => $values) {
+                foreach ((array)$values as $val) {
+                    if (!empty($val)) {
+                        $contact[$col][] = array($propname => $val, 'type' => $type);
+                    }
                 }
+                unset($contact[$col.':'.$type]);
             }
-            unset($contact['phone:'.$type]);
         }
 
+        $addresses = array();
         foreach ($this->get_col_values('address', $contact) as $type => $values) {
             foreach ((array)$values as $adr) {
                 // skip empty address
@@ -1138,8 +1123,6 @@ class rcube_kolab_contacts extends rcube_addressbook
             unset($contact['address:'.$type]);
         }
 
-        $contact['website'] = $websites;
-        $contact['phone']   = $phones;
         $contact['address'] = $addresses;
 
         // copy meta data (starting with _) from old object
diff --git a/plugins/libkolab/lib/kolab_format_contact.php b/plugins/libkolab/lib/kolab_format_contact.php
index 2f8bd14..b350559 100644
--- a/plugins/libkolab/lib/kolab_format_contact.php
+++ b/plugins/libkolab/lib/kolab_format_contact.php
@@ -47,6 +47,12 @@ class kolab_format_contact extends kolab_format
         'other'   => Telephone::Textphone,
     );
 
+    public $emailtypes = array(
+        'home' => Email::Home,
+        'work' => Email::Work,
+        'other' => Email::Other,
+    );
+
     public $addresstypes = array(
         'home' => Address::Home,
         'work' => Address::Work,
@@ -125,10 +131,21 @@ class kolab_format_contact extends kolab_format
         }
         $org->setRelateds($rels);
 
-        // email, im, url
-        $this->obj->setEmailAddresses(self::array2vector($object['email']));
+        // im, email, url
         $this->obj->setIMaddresses(self::array2vector($object['im']));
 
+        if (class_exists('vectoremail')) {
+            $vemails = new vectoremail;
+            foreach ((array)$object['email'] as $email) {
+                $type = $this->emailtypes[$email['type']];
+                $vemails->push(new Email($email['address'], intval($type)));
+            }
+        }
+        else {
+            $vemails = self::array2vector(array_map(function($v){ return $v['address']; }, $object['email']));
+        }
+        $this->obj->setEmailAddresses($vemails);
+
         $vurls = new vectorurl;
         foreach ((array)$object['website'] as $url) {
             $type = $url['type'] == 'blog' ? Url::Blog : Url::NoType;
@@ -288,8 +305,19 @@ class kolab_format_contact extends kolab_format
             $this->read_relateds($org->relateds(), $object);
         }
 
-        $object['email']   = self::vector2array($this->obj->emailAddresses());
-        $object['im']      = self::vector2array($this->obj->imAddresses());
+        $object['im'] = self::vector2array($this->obj->imAddresses());
+
+        $emails = $this->obj->emailAddresses();
+        if ($emails instanceof vectoremail) {
+            $emailtypes = array_flip($this->emailtypes);
+            for ($i=0; $i < $emails->size(); $i++) {
+                $email = $emails->get($i);
+                $object['email'][] = array('address' => $email->address(), 'type' => $emailtypes[$email->types()]);
+            }
+        }
+        else {
+            $object['email'] = self::vector2array($emails);
+        }
 
         $urls = $this->obj->urls();
         for ($i=0; $i < $urls->size(); $i++) {


commit 8d3efd344d3310911b51f643ce8395a50799a58d
Author: Aleksander Machniak <machniak at kolabsys.com>
Date:   Fri Jun 21 11:22:42 2013 +0200

    Added Czech translations
    
    Conflicts:
    
    	plugins/calendar/localization/cs_CZ.inc

diff --git a/plugins/calendar/localization/cs_CZ.inc b/plugins/calendar/localization/cs_CZ.inc
index 5d4b950..dbc1a52 100644
--- a/plugins/calendar/localization/cs_CZ.inc
+++ b/plugins/calendar/localization/cs_CZ.inc
@@ -2,229 +2,234 @@
 
 $labels = array();
 
-// preferences
+// config
 $labels['default_view'] = 'Výchozí pohled';
 $labels['time_format'] = 'Formát data';
 $labels['timeslots'] = 'Slotů na hodinu';
 $labels['first_day'] = 'První den v týdnu';
-$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';
+
+// preferences
+$labels['default_view'] = 'Výchozí pohled';
+$labels['time_format'] = 'Formát času';
+$labels['timeslots'] = 'Slotů na hodinu';
+$labels['first_day'] = 'První den týdne';
+$labels['first_hour'] = 'První hodina k zobrazení';
+$labels['workinghours'] = 'Pracovní hodiny';
+$labels['add_category'] = 'Přidat kategorii';
+$labels['remove_category'] = 'Odstranit kategorii';
+$labels['defaultcalendar'] = 'Vytvářet nové události v';
+$labels['eventcoloring'] = 'Barvy událostí';
+$labels['coloringmode0'] = 'Podle kalendáře';
+$labels['coloringmode1'] = 'Podle kategorie';
+$labels['coloringmode2'] = 'Kalendář pro orámování, kategorie pro obsah';
+$labels['coloringmode3'] = 'Kategorie pro orámování, kalendář pro obsah';
 
 // calendar
 $labels['calendar'] = 'Kalendář';
-$labels['calendars'] = 'Calendars';
+$labels['calendars'] = 'Kalendáře';
 $labels['category'] = 'Kategorie';
-$labels['categories'] = 'Categories';
-$labels['createcalendar'] = 'Create new calendar';
-$labels['editcalendar'] = 'Edit calendar properties';
-$labels['name'] = 'Name';
-$labels['color'] = 'Color';
+$labels['categories'] = 'Kategorie';
+$labels['createcalendar'] = 'Vytvořit nový kalendář';
+$labels['editcalendar'] = 'Upravit vlastnosti kalendáře';
+$labels['name'] = 'Název';
+$labels['color'] = 'Barva';
 $labels['day'] = 'Den';
 $labels['week'] = 'Týden';
 $labels['month'] = 'Měsíc';
 $labels['agenda'] = 'Agenda';
-$labels['new'] = 'New';
+$labels['new'] = 'Nová';
 $labels['new_event'] = 'Nová událost';
-$labels['edit_event'] = 'Editovat událost';
-$labels['edit'] = 'Edit';
+$labels['edit_event'] = 'Upravit událost';
+$labels['edit'] = 'Upravit';
 $labels['save'] = 'Uložit';
 $labels['remove'] = 'Odstranit';
 $labels['cancel'] = 'Storno';
-$labels['select'] = 'Select';
-$labels['print'] = 'Print';
-$labels['printtitle'] = 'Print calendars';
+$labels['select'] = 'Vybrat';
+$labels['print'] = 'Tisk';
+$labels['printtitle'] = 'Vytisknout kalendáře';
 $labels['title'] = 'Souhrn';
 $labels['description'] = 'Popis';
 $labels['all-day'] = 'celý den';
 $labels['export'] = 'Exportovat do ICS';
-$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['exporttitle'] = 'Exportovat souhrn do ICS';
+$labels['location'] = 'Místo';
+$labels['date'] = 'Datum';
+$labels['start'] = 'Začátek';
+$labels['end'] = 'Konec';
+$labels['selectdate'] = 'Vyberte datum';
+$labels['freebusy'] = 'Zobrazovat časový úsek jako';
+$labels['free'] = 'volno';
+$labels['busy'] = 'obsazeno';
+$labels['outofoffice'] = 'mimo kancelář';
+$labels['tentative'] = 'nezávazně';
+$labels['priority'] = 'Priorita';
+$labels['sensitivity'] = 'Soukromí';
+$labels['public'] = 'veřejné';
+$labels['private'] = 'soukromé';
+$labels['confidential'] = 'důvěrné';
+$labels['alarms'] = 'Připomenutí';
+$labels['generated'] = 'vygenerováno';
+$labels['printdescriptions'] = 'Vytisknout popisy';
+$labels['parentcalendar'] = 'Vložit dovnitř';
+$labels['searchearlierdates'] = '« Hledat dřívější události';
+$labels['searchlaterdates'] = 'Hledat pozdější události »';
+$labels['andnmore'] = 'dalších $nr...';
+$labels['togglerole'] = 'Klikněte k přepnutí role';
+$labels['createfrommail'] = 'Uložit jako událost';
+$labels['importevents'] = 'Importovat události';
+$labels['importrange'] = 'Události od';
+$labels['onemonthback'] = '1 měsíc nazpátek';
+$labels['nmonthsback'] = '$nr měsíců nazpátek';
+$labels['showurl'] = 'Ukázat URL kalendáře';
+$labels['showurldescription'] = 'Tuto adresu použijte pro přístup (jen ke čtení) ke kalendáři z jiných aplikací. Můžete ji zkopírovat a vložit do jakéhokoli kalendářového softwaru, který podporuje formát iCal.';
 
 // 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'] = 'Rozsah k zobrazení:';
+$labels['listsections'] = 'Rozdělit na:';
+$labels['smartsections'] = 'Chytré sekce';
+$labels['until'] = 'do';
+$labels['today'] = 'Dnes';
+$labels['tomorrow'] = 'Zítra';
+$labels['thisweek'] = 'Tento týden';
+$labels['nextweek'] = 'Příští týden';
+$labels['thismonth'] = 'Tento měsíc';
+$labels['nextmonth'] = 'Příští měsíc';
+$labels['weekofyear'] = 'Týden';
+$labels['pastevents'] = 'Minulost';
+$labels['futureevents'] = 'Budoucnost';
 
 // alarm/reminder settings
-$labels['showalarms'] = 'Show alarms';
-$labels['defaultalarmtype'] = 'Default reminder setting';
-$labels['defaultalarmoffset'] = 'Default reminder time';
+$labels['showalarms'] = 'Zobrazit upozornění';
+$labels['defaultalarmtype'] = 'Výchozí nastavení připomenutí';
+$labels['defaultalarmoffset'] = 'Výchozí čas připomenutí';
 
 // attendees
-$labels['attendee'] = 'Participant';
+$labels['attendee'] = 'Účastník';
 $labels['role'] = 'Role';
-$labels['availability'] = 'Avail.';
-$labels['confirmstate'] = 'Status';
-$labels['addattendee'] = 'Add participant';
-$labels['roleorganizer'] = 'Organizer';
-$labels['rolerequired'] = 'Required';
-$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['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';
-$labels['eventupdatesubjectempty'] = 'An event that concerns you has been updated';
-$labels['eventupdatemailbody'] = "*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees\n\nPlease find attached an iCalendar file with the updated event details which you can import to your calendar application.";
-$labels['eventcancelsubject'] = '"$title" has been canceled';
-$labels['eventcancelmailbody'] = "*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees\n\nThe event has been cancelled by \$organizer.\n\nPlease find attached an iCalendar file with the updated event details.";
+$labels['availability'] = 'Dost.';
+$labels['confirmstate'] = 'Stav';
+$labels['addattendee'] = 'Přidat účastníka';
+$labels['roleorganizer'] = 'Organizátor';
+$labels['rolerequired'] = 'Povinný';
+$labels['roleoptional'] = 'Nepovinný';
+$labels['roleresource'] = 'Prostředek';
+$labels['availfree'] = 'volno';
+$labels['availbusy'] = 'obsazeno';
+$labels['availunknown'] = 'neznámý';
+$labels['availtentative'] = 'nezávazně';
+$labels['availoutofoffice'] = 'mimo kancelář';
+$labels['scheduletime'] = 'Najít dostupnost';
+$labels['sendinvitations'] = 'Poslat pozvánky';
+$labels['sendnotifications'] = 'Uvědomit účastníky o změnách';
+$labels['sendcancellation'] = 'Uvědomit účastníky o zrušení události';
+$labels['onlyworkinghours'] = 'Najít dostupnost v mé pracovní době';
+$labels['reqallattendees'] = 'Povinní/všichni účastníci';
+$labels['prevslot'] = 'Předchozí slot';
+$labels['nextslot'] = 'Další slot';
+$labels['noslotfound'] = 'Nelze najít volný slot';
+$labels['invitationsubject'] = 'Byl(a) jste pozván(a) na událost "$title"';
+$labels['invitationmailbody'] = "*\$title*\n\nKdy: \$date\n\nPozváni: \$attendees\n\nPodrobnosti o události najdete v přiloženém souboru typu iCalendar. Můžete si ho naimportovat do kalendářového programu.";
+$labels['invitationattendlinks'] = "Pokud váš poštovní klient nepodporuje pozvánky iTip, použijte prosím následující odkaz k potvrzení nebo odmítnutí pozvání:\n\$url";
+$labels['eventupdatesubject'] = 'Událost "$title" byla aktualizována';
+$labels['eventupdatesubjectempty'] = 'Událost, která se vás týká, byla aktualizována';
+$labels['eventupdatemailbody'] = "*\$title*\n\nKdy: \$date\n\nPozváni: \$attendees\n\nPodrobnosti o aktualizované události najdete v přiloženém souboru typu iCalendar. Můžete si ho naimportovat do kalendářového programu.";
+$labels['eventcancelsubject'] = 'Událost "$title" byla zrušena';
+$labels['eventcancelmailbody'] = "*\$title*\n\nKdy: \$date\n\nPozváni: \$attendees\n\nUdálost byla zrušena organizátorem (\$organizer).\n\nPodrobnosti najdete v přiloženém souboru ve formátu iCalendar.";
 
 // invitation handling
-$labels['itipinvitation'] = 'Invitation to';
-$labels['itipupdate'] = 'Update of';
-$labels['itipcancellation'] = 'Cancelled:';
-$labels['itipreply'] = 'Reply to';
-$labels['itipaccepted'] = 'Accept';
-$labels['itiptentative'] = 'Maybe';
-$labels['itipdeclined'] = 'Decline';
-$labels['itipsubjectaccepted'] = '"$title" has been accepted by $name';
-$labels['itipsubjecttentative'] = '"$title" has been tentatively accepted by $name';
-$labels['itipsubjectdeclined'] = '"$title" has been declined by $name';
-$labels['itipmailbodyaccepted'] = "\$sender has accepted the invitation to the following event:\n\n*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees";
-$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['itipinvitation'] = 'Pozvání na událost';
+$labels['itipupdate'] = 'Aktualizace události';
+$labels['itipcancellation'] = 'Zrušeno:';
+$labels['itipreply'] = 'Odpověď na';
+$labels['itipaccepted'] = 'Potvrdit';
+$labels['itiptentative'] = 'Možná';
+$labels['itipdeclined'] = 'Odmítnout';
+$labels['itipsubjectaccepted'] = '$name potvrdil(a) účas na události "$title"';
+$labels['itipsubjecttentative'] = '$name nezávazně potvrdil(a) účast na události "$title"';
+$labels['itipsubjectdeclined'] = '$name odmítl(a) účast na události "$title"';
+$labels['itipmailbodyaccepted'] = "\$sender přijal(a) pozvání na tuto událost:\n\n*\$title*\n\nKdy: \$date\n\nPozváni: \$attendees";
+$labels['itipmailbodytentative'] = "\$sender nezávazně přijal(a) pozvání na tuto událost:\n\n*\$title*\n\nKdy: \$date\n\nPozváni: \$attendees";
+$labels['itipmailbodydeclined'] = "\$sender odmítl(a) pozvání na tuto událost:\n\n*\$title*\n\nKdy: \$date\n\nPozváni: \$attendees";
+$labels['itipdeclineevent'] = 'Opravdu chcete odmítnout pozvání na tuto událost?';
+$labels['importtocalendar'] = 'Uložit do kalendáře';
+$labels['removefromcalendar'] = 'Odstranit z kalendáře';
+$labels['updateattendeestatus'] = 'Aktualizovat stav účastníka';
+$labels['acceptinvitation'] = 'Chcete přijmout toto pozvání (potvrdit účast)?';
+$labels['youhaveaccepted'] = 'Přijal(a) jste toto pozvání';
+$labels['youhavetentative'] = 'Nezávazně jste přijal(a) toto pozvání';
+$labels['youhavedeclined'] = 'Odmítl(a) jste toto pozvání';
+$labels['notanattendee'] = 'Nejste na seznamu účastníků této události';
+$labels['eventcancelled'] = 'Tato událost byla zrušena';
+$labels['saveincalendar'] = 'uložit do';
 
 // event dialog tabs
 $labels['tabsummary'] = 'Souhrn';
-$labels['tabrecurrence'] = 'Recurrence';
-$labels['tabattendees'] = 'Participants';
-$labels['tabattachments'] = 'Attachments';
-$labels['tabsharing'] = 'Sharing';
+$labels['tabrecurrence'] = 'Opakování';
+$labels['tabattendees'] = 'Účastníci';
+$labels['tabattachments'] = 'Přílohy';
+$labels['tabsharing'] = 'Sdílení';
 
 // 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'] = 'Opravdu chcete smazat tuto událost?';
+$labels['deletecalendarconfirm'] = 'Opravdu chcete smazat tento kalendář se všemi událostmi?';
+$labels['savingdata'] = 'Ukládám data...';
+$labels['errorsaving'] = 'Nelze uložit změny.';
+$labels['operationfailed'] = 'Požavovaná operace selhala.';
+$labels['invalideventdates'] = 'Vložená data nejsou platná! Zkontrolujte prosím zadávané údaje.';
+$labels['invalidcalendarproperties'] = 'Neplatné vlastnosti kalendáře! Vložte prosím platné jméno.';
+$labels['searchnoresults'] = 'Ve vybraných kalendářích nebyly nalezeny žádné události.';
+$labels['successremoval'] = 'Událost byla úspěšně smazána.';
+$labels['successrestore'] = 'Událost byla úspěšně obnovena.';
+$labels['errornotifying'] = 'Nelze odeslat notifikace účastníkům události';
+$labels['errorimportingevent'] = 'Nelze naimportovat událost';
+$labels['newerversionexists'] = 'Existuje již novější verze této události! Operace byla zrušena.';
+$labels['nowritecalendarfound'] = 'Nebyl nalezen žádný kalendář, do kterého by šlo uložit tuto událost.';
+$labels['importedsuccessfully'] = 'Událost byla úspěšně přidána do kalendáře \'$calendar\'';
+$labels['attendeupdateesuccess'] = 'Stav účastníka byl úspěšně aktualizován';
+$labels['itipsendsuccess'] = 'Pozvánky byly rozeslány účastníkům.';
+$labels['itipresponseerror'] = 'Nelze odeslat odpověď na tuto pozvánku';
+$labels['itipinvalidrequest'] = 'Tato pozvánka již není platná';
+$labels['sentresponseto'] = 'Odpověď na pozvánku byla úspěšně odeslána na adresu $mailto';
+$labels['localchangeswarning'] = 'Chystáte se provést změny, které se projeví jen ve vašem vlastním kalendáři a nelze je poslat organizátorovi události.';
+$labels['importsuccess'] = 'Úspěšně importováno $nr událostí';
+$labels['importnone'] = 'Nebyly nalezeny žádné události k importu';
+$labels['importerror'] = 'Při importu došlo k chybě';
+$labels['aclnorights'] = 'Nemáte administrátorská práva k tomuto kalendáři.';
 
 // 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['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['repeat'] = 'Opakování';
+$labels['frequency'] = 'Opakovat';
+$labels['never'] = 'nikdy';
+$labels['daily'] = 'dennÄ›';
+$labels['weekly'] = 'týdně';
+$labels['monthly'] = 'měsíčně';
+$labels['yearly'] = 'ročně';
+$labels['every'] = 'Každý';
+$labels['days'] = 'den (dny)';
+$labels['weeks'] = 'týden (týdny)';
+$labels['months'] = 'měsíc(e/ů)';
+$labels['years'] = 'rok(y/ů) v:';
+$labels['bydays'] = ' ';
+$labels['untildate'] = 'do';
+$labels['each'] = 'Každý';
+$labels['onevery'] = 'Vždy v';
+$labels['onsamedate'] = 'Ve stejné datum';
+$labels['forever'] = 'trvale';
+$labels['recurrencend'] = 'do';
+$labels['forntimes'] = 'jen $nrkrát';
+$labels['first'] = 'první';
+$labels['second'] = 'druhý';
+$labels['third'] = 'třetí';
+$labels['fourth'] = 'čtvrtý';
+$labels['last'] = 'poslední';
+$labels['dayofmonth'] = 'Den v měsíci';
 
-$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'] = 'Změnit událost';
+$labels['removeeventconfirm'] = 'Odstranit událost';
+$labels['changerecurringeventwarning'] = 'Toto je opakovaná událost. Chcete upravit jen toto konání, toto a všechna následující konání, úplně všechna konání nebo uložit událost jako novou?';
+$labels['removerecurringeventwarning'] = 'Toto je opakovaná událost. Chcete odstranit jen toto konání, toto a následující konání nebo úplně všechna konání této události?';
+$labels['currentevent'] = 'Aktuální';
+$labels['futurevents'] = 'Budoucí';
+$labels['allevents'] = 'VÅ¡echny';
+$labels['saveasnew'] = 'Uložit jako novou';
 
-?>
diff --git a/plugins/libcalendaring/localization/cs_CZ.inc b/plugins/libcalendaring/localization/cs_CZ.inc
new file mode 100755
index 0000000..0515e19
--- /dev/null
+++ b/plugins/libcalendaring/localization/cs_CZ.inc
@@ -0,0 +1,30 @@
+<?php
+
+$labels = array();
+
+$labels['alarmemail'] = 'Poslat e-mail';
+$labels['alarmdisplay'] = 'Zobrazit zprávu';
+$labels['alarmdisplayoption'] = 'Zpráva';
+$labels['alarmemailoption'] = 'E-mail';
+$labels['alarmat'] = '$datetime';
+$labels['trigger@'] = 'dne';
+$labels['trigger-M'] = 'minut před';
+$labels['trigger-H'] = 'hodin před';
+$labels['trigger-D'] = 'dnů před';
+$labels['trigger+M'] = 'minut po';
+$labels['trigger+H'] = 'hodin po';
+$labels['trigger+D'] = 'dnů po';
+$labels['addalarm'] = 'přidat upozornění';
+
+$labels['alarmtitle'] = 'Blížící se události';
+$labels['dismissall'] = 'Zrušit vše';
+$labels['dismiss'] = 'Zrušit';
+$labels['snooze'] = 'Odložit';
+$labels['repeatinmin'] = 'Zopakovat za $min minut';
+$labels['repeatinhr'] = 'Zopakovat za 1 hodinu';
+$labels['repeatinhrs'] = 'Zopakovat za $hrs hodin';
+$labels['repeattomorrow'] = 'Zopakovat zítra';
+$labels['repeatinweek'] = 'Zopakovat za týden';
+
+$labels['showmore'] = 'Ukázat víc...';
+
diff --git a/plugins/tasklist/localization/cs_CZ.inc b/plugins/tasklist/localization/cs_CZ.inc
new file mode 100644
index 0000000..1ad5b0b
--- /dev/null
+++ b/plugins/tasklist/localization/cs_CZ.inc
@@ -0,0 +1,68 @@
+<?php
+
+$labels = array();
+$labels['navtitle'] = 'Úkoly';
+$labels['lists'] = 'Seznamy úkolů';
+$labels['list'] = 'Seznam úkolů';
+$labels['tags'] = 'Štítky';
+
+$labels['newtask'] = 'Nový úkol';
+$labels['createnewtask'] = 'Vytvořit nový úkol (např. sobota, posekat trávník)';
+$labels['createfrommail'] = 'Uložit úkol';
+$labels['mark'] = 'Označit';
+$labels['unmark'] = 'Odznačit';
+$labels['edit'] = 'Upravit';
+$labels['delete'] = 'Smazat';
+$labels['title'] = 'Titulek';
+$labels['description'] = 'Popis';
+$labels['datetime'] = 'Konec';
+$labels['start'] = 'Začátek';
+$labels['alarms'] = 'Připomenutí';
+
+$labels['all'] = 'VÅ¡echny';
+$labels['flagged'] = 'Označkované';
+$labels['complete'] = 'Dokončené';
+$labels['overdue'] = 'Zpožděné';
+$labels['today'] = 'Dnes';
+$labels['tomorrow'] = 'Zítra';
+$labels['next7days'] = 'Příštích 7 dnů';
+$labels['later'] = 'Později';
+$labels['nodate'] = 'Žádné datum';
+$labels['removetag'] = 'Odstranit';
+
+$labels['taskdetails'] = 'Podrobnosti';
+$labels['newtask'] = 'Nový úkol';
+$labels['edittask'] = 'Upravit úkol';
+$labels['save'] = 'Uložit';
+$labels['cancel'] = 'Storno';
+$labels['addsubtask'] = 'Přidat podúkol';
+$labels['deletetask'] = 'Smazat úkol';
+$labels['deletethisonly'] = 'Smazat pouze tento úkol';
+$labels['deletewithchilds'] = 'Smazat se všemi podúkoly';
+
+$labels['tabsummary'] = 'Souhrn';
+$labels['tabrecurrence'] = 'Opakování';
+$labels['tabattachments'] = 'Přílohy';
+$labels['tabsharing'] = 'Sdílení';
+
+$labels['editlist'] = 'Upravit seznam';
+$labels['createlist'] = 'Přidat seznam';
+$labels['listactions'] = 'Možnosti seznamu...';
+$labels['listname'] = 'Název';
+$labels['showalarms'] = 'Ukázat upozornění';
+$labels['import'] = 'Import';
+
+// date words
+$labels['on'] = ' ';
+$labels['at'] = ' ';
+$labels['this'] = 'tento';
+$labels['next'] = 'příští';
+
+// messages
+$labels['savingdata'] = 'Ukládám data...';
+$labels['errorsaving'] = 'Nelze uložit data.';
+$labels['notasksfound'] = 'Nebyly nalezeny žádné úkoly vyhovující daným kritériím';
+$labels['invalidstartduedates'] = 'Počáteční datum nesmí být pozdější než koncové datum.';
+$labels['deletetasktconfirm'] = 'Opravdu chcete smazat tento úkol?';
+$labels['deleteparenttasktconfirm'] = 'Opravdu chcete smazat tento úkol a všechny jeho podúkoly?';
+$labels['deletelistconfirm'] = 'Opravdu chcete smazat tento seznam včetně všech úkolů?';


commit 0768490fff69c2521cc57fe0e66a173c25b44fe4
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Mar 14 09:43:29 2013 +0100

    Mention the correct RFC section for iCal value escaping

diff --git a/plugins/calendar/lib/calendar_ical.php b/plugins/calendar/lib/calendar_ical.php
index a8769bc..e4c8e74 100644
--- a/plugins/calendar/lib/calendar_ical.php
+++ b/plugins/calendar/lib/calendar_ical.php
@@ -465,7 +465,7 @@ class calendar_ical
   }
 
   /**
-   * Escape values according to RFC 2426 2.5
+   * Escape values according to RFC 2445 4.3.11
    */
   private function escape($str)
   {


commit 39707d3458188ab26bd883e52192fcc68304f1fe
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Mar 14 09:30:12 2013 +0100

    Fix iCal newline escaping (#1694) + typo

diff --git a/plugins/calendar/lib/calendar_ical.php b/plugins/calendar/lib/calendar_ical.php
index 6d7474e..a8769bc 100644
--- a/plugins/calendar/lib/calendar_ical.php
+++ b/plugins/calendar/lib/calendar_ical.php
@@ -364,7 +364,7 @@ class calendar_ical
       
       foreach ($events as $event) {
         $vevent = "BEGIN:VEVENT" . self::EOL;
-        $vevent .= "UID:" . self::escpape($event['uid']) . 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;
@@ -379,21 +379,21 @@ class calendar_ical
           $vevent .= $this->format_datetime("DTSTART", $event['start'], false) . self::EOL;
           $vevent .= $this->format_datetime("DTEND",   $event['end'], false) . self::EOL;
         }
-        $vevent .= "SUMMARY:" . self::escpape($event['title']) . self::EOL;
-        $vevent .= "DESCRIPTION:" . self::escpape($event['description']) . self::EOL;
+        $vevent .= "SUMMARY:" . self::escape($event['title']) . self::EOL;
+        $vevent .= "DESCRIPTION:" . self::escape($event['description']) . self::EOL;
         
         if (!empty($event['attendees'])){
           $vevent .= $this->_get_attendees($event['attendees']);
         }
 
         if (!empty($event['location'])) {
-          $vevent .= "LOCATION:" . self::escpape($event['location']) . self::EOL;
+          $vevent .= "LOCATION:" . self::escape($event['location']) . self::EOL;
         }
         if ($event['recurrence']) {
           $vevent .= "RRULE:" . libcalendaring::to_rrule($event['recurrence'], self::EOL) . self::EOL;
         }
         if(!empty($event['categories'])) {
-          $vevent .= "CATEGORIES:" . self::escpape(strtoupper($event['categories'])) . self::EOL;
+          $vevent .= "CATEGORIES:" . self::escape(strtoupper($event['categories'])) . self::EOL;
         }
         if ($event['sensitivity'] > 0) {
           $vevent .= "CLASS:" . ($event['sensitivity'] == 2 ? 'CONFIDENTIAL' : 'PRIVATE') . self::EOL;
@@ -405,7 +405,7 @@ class calendar_ical
           $vevent .= "BEGIN:VALARM\n";
           if ($val[1]) $vevent .= "TRIGGER:" . preg_replace('/^([-+])(.+)/', '\\1PT\\2', $trigger) . self::EOL;
           else         $vevent .= "TRIGGER;VALUE=DATE-TIME:" . gmdate('Ymd\THis\Z', $val[0]) . self::EOL;
-          if ($action) $vevent .= "ACTION:" . self::escpape(strtoupper($action)) . self::EOL;
+          if ($action) $vevent .= "ACTION:" . self::escape(strtoupper($action)) . self::EOL;
           $vevent .= "END:VALARM\n";
         }
         
@@ -421,7 +421,7 @@ class calendar_ical
           $vevent .= "STATUS:TENTATIVE" . self::EOL;
         
         foreach ((array)$event['x-custom'] as $prop)
-          $vevent .= $prop[0] . ':' . $this->escpape($prop[1]) . self::EOL;
+          $vevent .= $prop[0] . ':' . self::escape($prop[1]) . self::EOL;
         
         // TODO: export attachments
         
@@ -459,7 +459,7 @@ class calendar_ical
       // <ATTR>;TZID=Europe/Zurich:20120706T210000
       $tz = $dt->getTimezone();
       $tzname = $tz ? $tz->getName() : null;
-      $tzid = $tzname && $tzname != 'UTC' && $tzname != '+00:00' ? ';TZID=' . $tzname : '';
+      $tzid = $tzname && $tzname != 'UTC' && $tzname != '+00:00' ? ';TZID=' . self::escape($tzname) : '';
       return $attr . $tzid . ':' . $dt->format('Ymd\THis' . ($tzid ? '' : '\Z'));
     }
   }
@@ -467,9 +467,9 @@ class calendar_ical
   /**
    * Escape values according to RFC 2426 2.5
    */
-  private function escpape($str)
+  private function escape($str)
   {
-    return strtr($str, array('\\' => '\\\\', ';' => '\\;', ',' => '\\,'));
+    return strtr($str, array('\\' => '\\\\', "\n" => '\n', ';' => '\;', ',' => '\,'));
   }
 
   /**


commit 2068b37422d53c5a6b20aec2c4f23a7026119a8d
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Mar 14 09:28:53 2013 +0100

    Don't set empty attendees array for move/resize actions; otherwise attendees are deleted

diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
index a70cbdd..f2cca86 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -1286,9 +1286,10 @@ class calendar extends rcube_plugin
     $event['attachments'] = $attachments;
 
     // check for organizer in attendees
-    if (!$event['attendees'])
-      $event['attendees'] = array();
     if ($action == 'new' || $action == 'edit') {
+      if (!$event['attendees'])
+        $event['attendees'] = array();
+
       $emails = $this->get_user_emails();
       $organizer = $owner = false;
       foreach ((array)$event['attendees'] as $i => $attendee) {


commit 7b7b037073f59b75e5316135be3188fd60722d25
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Mar 13 15:30:12 2013 +0100

    Don't display local-change warning if calendar owner is organizer; Better wording for wanring text (#1693)

diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js
index 95e1d53..c25cf68 100644
--- a/plugins/calendar/calendar_ui.js
+++ b/plugins/calendar/calendar_ui.js
@@ -170,19 +170,20 @@ function rcube_calendar_ui(settings)
     };
     
     // check if the current user is an attendee of this event
-    var is_attendee = function(event, role)
+    var is_attendee = function(event, role, email)
     {
+      var emails = email ? ';'+email : 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 && settings.identity.emails.indexOf(';'+event.attendees[i].email) >= 0)
+        if ((!role || event.attendees[i].role == role) && event.attendees[i].email && emails.indexOf(';'+event.attendees[i].email) >= 0)
           return event.attendees[i];
       }
       return false;
     };
     
     // check if the current user is the organizer
-    var is_organizer = function(event)
+    var is_organizer = function(event, email)
     {
-      return is_attendee(event, 'ORGANIZER') || !event.id;
+      return is_attendee(event, 'ORGANIZER', email) || !event.id;
     };
 
     var load_attachment = function(event, att)
@@ -533,7 +534,7 @@ function rcube_calendar_ui(settings)
       event_attendees = [];
       attendees_list = $('#edit-attendees-table > tbody').html('');
       $('#edit-attendees-notify')[(notify.checked && organizer ? 'show' : 'hide')]();
-      $('#edit-localchanges-warning')[(has_attendees(event) && !organizer ? 'show' : 'hide')]();
+      $('#edit-localchanges-warning')[(has_attendees(event) && !(organizer || (calendar.owner && is_organizer(event, calendar.owner))) ? 'show' : 'hide')]();
 
       var load_attendees_tab = function()
       {
diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php
index 7be31e2..019b6d6 100644
--- a/plugins/calendar/drivers/kolab/kolab_driver.php
+++ b/plugins/calendar/drivers/kolab/kolab_driver.php
@@ -128,6 +128,7 @@ class kolab_driver extends calendar_driver
         'class_name' => $cal->get_namespace(),
         'default'  => $cal->storage->default,
         'active'   => $cal->storage->is_active(),
+        'owner'    => $cal->get_owner(),
       );
     }
 
diff --git a/plugins/calendar/localization/de_CH.inc b/plugins/calendar/localization/de_CH.inc
index 0479bd0..20311a2 100644
--- a/plugins/calendar/localization/de_CH.inc
+++ b/plugins/calendar/localization/de_CH.inc
@@ -184,7 +184,7 @@ $labels['itipsendsuccess'] = 'Einladung an Teilnehmer versendet.';
 $labels['itipresponseerror'] = 'Die Antwort auf diese Einladung konnte nicht versendet werden';
 $labels['itipinvalidrequest'] = 'Diese Einladung ist nicht mehr gültig';
 $labels['sentresponseto'] = 'Antwort auf diese Einladung erfolgreich an $mailto gesendet';
-$labels['localchangeswarning'] = 'Die Änderungen an diesem Termin können nur in Ihrem persönlichen Kalender gespeichert werden.';
+$labels['localchangeswarning'] = 'Änderungen an diesem Termin werden nur in Ihrem Kalender gespeichert und nicht an den Organisator des Termins gesendet.';
 $labels['importsuccess'] = 'Es wurden $nr Termine erfolgreich importiert';
 $labels['importnone'] = 'Keine Termine zum Importieren gefunden';
 $labels['importerror'] = 'Fehler beim Importieren';
diff --git a/plugins/calendar/localization/de_DE.inc b/plugins/calendar/localization/de_DE.inc
index 5ad1563..a3b0058 100644
--- a/plugins/calendar/localization/de_DE.inc
+++ b/plugins/calendar/localization/de_DE.inc
@@ -184,7 +184,7 @@ $labels['itipsendsuccess'] = 'Einladung an Teilnehmer versendet.';
 $labels['itipresponseerror'] = 'Die Antwort auf diese Einladung konnte nicht versendet werden';
 $labels['itipinvalidrequest'] = 'Diese Einladung ist nicht mehr gültig.';
 $labels['sentresponseto'] = 'Antwort auf diese Einladung erfolgreich an $mailto gesendet';
-$labels['localchangeswarning'] = 'Die Änderungen an diesem Termin können nur in Ihrem persönlichen Kalender gespeichert werden.';
+$labels['localchangeswarning'] = 'Änderungen an diesem Termin werden nur in Ihrem Kalender gespeichert und nicht an den Organisator des Termins gesendet.';
 $labels['importsuccess'] = 'Es wurden $nr Termine erfolgreich importiert';
 $labels['importnone'] = 'Keine Termine zum Importieren gefunden';
 $labels['importerror'] = 'Fehler beim Importieren';
diff --git a/plugins/calendar/localization/en_US.inc b/plugins/calendar/localization/en_US.inc
index 6cc76d7..4164d89 100644
--- a/plugins/calendar/localization/en_US.inc
+++ b/plugins/calendar/localization/en_US.inc
@@ -184,7 +184,7 @@ $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['localchangeswarning'] = 'You are about to make changes that will only be reflected on your calendar and not be sent to the organizer of the event.';
 $labels['importsuccess'] = 'Successfully imported $nr events';
 $labels['importnone'] = 'No events found to be imported';
 $labels['importerror'] = 'An error occured while importing';


commit 6cc6f2e33220a3e5f311b702c1729b35124d9508
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Mar 13 10:45:29 2013 +0100

    Cleanup Transifex localization files. en => en_US

diff --git a/.tx/config b/.tx/config
index 94f402b..034cd6d 100644
--- a/.tx/config
+++ b/.tx/config
@@ -1,6 +1,6 @@
 [main]
 host = https://www.transifex.com
-lang_map = de: de_DE, es: es_ES, fr: fr_FR, ja: ja_JP, nl: nl_NL
+lang_map = en: en_US, de: de_DE, es: es_ES, fr: fr_FR, ja: ja_JP, nl: nl_NL
 type = PHP_ALT_ARRAY
 
 [kolab.calendar]
diff --git a/plugins/calendar/localization/en.inc b/plugins/calendar/localization/en.inc
deleted file mode 100644
index 6cc76d7..0000000
--- a/plugins/calendar/localization/en.inc
+++ /dev/null
@@ -1,230 +0,0 @@
-<?php
-
-$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';
-
-// calendar
-$labels['calendar'] = 'Calendar';
-$labels['calendars'] = 'Calendars';
-$labels['category'] = 'Category';
-$labels['categories'] = 'Categories';
-$labels['createcalendar'] = 'Create new calendar';
-$labels['editcalendar'] = 'Edit calendar properties';
-$labels['name'] = 'Name';
-$labels['color'] = 'Color';
-$labels['day'] = 'Day';
-$labels['week'] = 'Week';
-$labels['month'] = 'Month';
-$labels['agenda'] = 'Agenda';
-$labels['new'] = 'New';
-$labels['new_event'] = 'New event';
-$labels['edit_event'] = 'Edit event';
-$labels['edit'] = 'Edit';
-$labels['save'] = 'Save';
-$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.';
-
-// 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';
-
-// alarm/reminder settings
-$labels['showalarms'] = 'Show alarms';
-$labels['defaultalarmtype'] = 'Default reminder setting';
-$labels['defaultalarmoffset'] = 'Default reminder time';
-
-// attendees
-$labels['attendee'] = 'Participant';
-$labels['role'] = 'Role';
-$labels['availability'] = 'Avail.';
-$labels['confirmstate'] = 'Status';
-$labels['addattendee'] = 'Add participant';
-$labels['roleorganizer'] = 'Organizer';
-$labels['rolerequired'] = 'Required';
-$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['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';
-$labels['eventupdatesubjectempty'] = 'An event that concerns you has been updated';
-$labels['eventupdatemailbody'] = "*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees\n\nPlease find attached an iCalendar file with the updated event details which you can import to your calendar application.";
-$labels['eventcancelsubject'] = '"$title" has been canceled';
-$labels['eventcancelmailbody'] = "*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees\n\nThe event has been cancelled by \$organizer.\n\nPlease find attached an iCalendar file with the updated event details.";
-
-// invitation handling
-$labels['itipinvitation'] = 'Invitation to';
-$labels['itipupdate'] = 'Update of';
-$labels['itipcancellation'] = 'Cancelled:';
-$labels['itipreply'] = 'Reply to';
-$labels['itipaccepted'] = 'Accept';
-$labels['itiptentative'] = 'Maybe';
-$labels['itipdeclined'] = 'Decline';
-$labels['itipsubjectaccepted'] = '"$title" has been accepted by $name';
-$labels['itipsubjecttentative'] = '"$title" has been tentatively accepted by $name';
-$labels['itipsubjectdeclined'] = '"$title" has been declined by $name';
-$labels['itipmailbodyaccepted'] = "\$sender has accepted the invitation to the following event:\n\n*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees";
-$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';
-
-// event dialog tabs
-$labels['tabsummary'] = 'Summary';
-$labels['tabrecurrence'] = 'Recurrence';
-$labels['tabattendees'] = 'Participants';
-$labels['tabattachments'] = 'Attachments';
-$labels['tabsharing'] = 'Sharing';
-
-// 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.';
-
-// 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['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['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';
-
-?>
diff --git a/plugins/kolab_activesync/localization/en.inc b/plugins/kolab_activesync/localization/en.inc
deleted file mode 100644
index 2399b13..0000000
--- a/plugins/kolab_activesync/localization/en.inc
+++ /dev/null
@@ -1,33 +0,0 @@
-<?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'] = '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['savingdata'] = 'Saving data...';
-$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';
-
-?>
diff --git a/plugins/kolab_addressbook/localization/en.inc b/plugins/kolab_addressbook/localization/en.inc
deleted file mode 100644
index a66426f..0000000
--- a/plugins/kolab_addressbook/localization/en.inc
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-
-$labels = array();
-$labels['initials'] = 'Initials';
-$labels['profession'] = 'Profession';
-$labels['officelocation'] = 'Office location';
-$labels['children'] = 'Children';
-$labels['pgppublickey'] = 'PGP public key';
-$labels['pkcs7publickey'] = 'S/MIME public key';
-$labels['freebusyurl'] = 'Free-busy URL';
-$labels['typebusiness'] = 'Business';
-$labels['typebusinessfax'] = 'Business Fax';
-$labels['typecompany'] = 'Company';
-$labels['typeprimary'] = 'Primary';
-$labels['typetelex'] = 'Telex';
-$labels['typeradio'] = 'Radio';
-$labels['typeisdn'] = 'ISDN';
-$labels['typettytdd'] = 'TTY/TDD';
-$labels['typecallback'] = 'Callback';
-$labels['settings'] = 'Settings';
-
-$labels['bookcreate'] = 'Create address book';
-$labels['bookedit'] = 'Edit address book';
-$labels['bookdelete'] = 'Delete address book';
-$labels['bookproperties'] = 'Address book properties';
-$labels['bookname'] = 'Book name';
-$labels['parentbook'] = 'Superior book';
-
-$labels['addressbookprio'] = 'Address book(s) selection/behaviour';
-$labels['personalfirst'] = 'Personal address book(s) first';
-$labels['globalfirst'] = 'Global address book(s) first';
-$labels['personalonly'] = 'Personal address book(s) only';
-$labels['globalonly'] = 'Global address book(s) only';
-
-$messages['bookdeleteconfirm']  = 'Do you really want to delete the selected address book and all contacts in it?';
-$messages['bookdeleting'] = 'Deleting address book...';
-$messages['booksaving'] = 'Saving address book...';
-$messages['bookdeleted'] = 'Address book deleted successfully.';
-$messages['bookupdated'] = 'Address book updated successfully.';
-$messages['bookcreated'] = 'Address book created successfully.';
-$messages['bookdeleteerror'] = 'An error occured while deleting address book.';
-$messages['bookupdateerror'] = 'An error occured while updating address book.';
-$messages['bookcreateerror'] = 'An error occured while creating address book.';
-$messages['nobooknamewarning'] = 'Please, enter address book name.';
-$messages['noemailnamewarning'] = 'Please, enter email address or contact name.';
-
-?>
diff --git a/plugins/kolab_auth/localization/en.inc b/plugins/kolab_auth/localization/en.inc
deleted file mode 100644
index e1adb3f..0000000
--- a/plugins/kolab_auth/localization/en.inc
+++ /dev/null
@@ -1,5 +0,0 @@
-<?php
-
-$labels['loginas'] = 'Login As';
-
-?>
diff --git a/plugins/kolab_delegation/localization/en.inc b/plugins/kolab_delegation/localization/en.inc
deleted file mode 100644
index c445f7f..0000000
--- a/plugins/kolab_delegation/localization/en.inc
+++ /dev/null
@@ -1,30 +0,0 @@
-<?php
-
-$labels = array();
-
-$labels['tabtitle'] = 'Delegation';
-$labels['delegates'] = 'Delegates';
-$labels['delegate'] = 'Delegate';
-$labels['mail'] = 'Email';
-$labels['contact'] = 'Address Books';
-$labels['event'] = 'Calendars';
-$labels['task'] = 'Tasks';
-$labels['note'] = 'Notes';
-$labels['yes'] = 'Yes';
-$labels['no'] = 'No';
-
-$labels['adddelegate'] = 'Add delegate';
-$labels['deletedelegate'] = 'Delete delegate';
-
-$labels['savingdata'] = 'Saving data...';
-$labels['delegatedeleteconfirm'] = 'Do you really want to delete this delegate?';
-$labels['delegateremoveacl'] = 'remove access rights on folders assigned to this user';
-$labels['deleteconfirm'] = 'Confirmation';
-$labels['deletesuccess'] = 'The delegate was successfully removed.';
-$labels['deleteerror'] = 'Could not remove delegate.';
-$labels['updatesuccess'] = 'The delegate was successfully updated.';
-$labels['updateerror'] = 'Could not update delegate.';
-$labels['createsuccess'] = 'The delegate was successfully added.';
-$labels['createerror'] = 'Could not add delegate.';
-
-?>
diff --git a/plugins/kolab_folders/localization/en.inc b/plugins/kolab_folders/localization/en.inc
deleted file mode 100644
index 856f59d..0000000
--- a/plugins/kolab_folders/localization/en.inc
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-$labels = array();
-
-$labels['folderctype'] = 'Content type';
-$labels['foldertypemail'] = 'Mail';
-$labels['foldertypeevent'] = 'Calendar'; // Events?
-$labels['foldertypejournal'] = 'Journal';
-$labels['foldertypetask'] = 'Tasks';
-$labels['foldertypenote'] = 'Notes';
-$labels['foldertypecontact'] = 'Contacts';
-$labels['foldertypeconfiguration'] = 'Configuration';
-$labels['foldertypefile'] = 'Files';
-$labels['foldertypefreebusy'] = 'Free-Busy';
-
-$labels['default'] = 'Default';
-$labels['inbox'] = 'Inbox';
-$labels['drafts'] = 'Drafts';
-$labels['sentitems'] = 'Sent';
-$labels['outbox'] = 'Outbox';
-$labels['wastebasket'] = 'Trash';
-$labels['junkemail'] = 'Junk';
-
-$messages['defaultfolderexists'] = 'There is already default folder of specified type';
-
-?>
diff --git a/plugins/owncloud/localization/en.inc b/plugins/owncloud/localization/en.inc
deleted file mode 100644
index f5cb8fb..0000000
--- a/plugins/owncloud/localization/en.inc
+++ /dev/null
@@ -1,6 +0,0 @@
-<?php
-
-$labels = array();
-$labels['owncloud'] = 'Files';
-
-?>
diff --git a/plugins/tasklist/localization/en.inc b/plugins/tasklist/localization/en.inc
deleted file mode 100644
index 7d5415a..0000000
--- a/plugins/tasklist/localization/en.inc
+++ /dev/null
@@ -1,68 +0,0 @@
-<?php
-
-$labels = array();
-$labels['navtitle'] = 'Tasks';
-$labels['lists'] = 'Tasklists';
-$labels['list'] = 'Tasklist';
-$labels['tags'] = 'Tags';
-
-$labels['newtask'] = 'New Task';
-$labels['createnewtask'] = 'Create new Task (e.g. Saturday, Mow the lawn)';
-$labels['createfrommail'] = 'Save as task';
-$labels['mark'] = 'Mark';
-$labels['unmark'] = 'Unmark';
-$labels['edit'] = 'Edit';
-$labels['delete'] = 'Delete';
-$labels['title'] = 'Title';
-$labels['description'] = 'Description';
-$labels['datetime'] = 'Date/Time';
-$labels['start'] = 'Start';
-$labels['alarms'] = 'Reminder';
-
-$labels['all'] = 'All';
-$labels['flagged'] = 'Flagged';
-$labels['complete'] = 'Complete';
-$labels['overdue'] = 'Overdue';
-$labels['today'] = 'Today';
-$labels['tomorrow'] = 'Tomorrow';
-$labels['next7days'] = 'Next 7 days';
-$labels['later'] = 'Later';
-$labels['nodate'] = 'no date';
-$labels['removetag'] = 'Remove';
-
-$labels['taskdetails'] = 'Details';
-$labels['newtask'] = 'New Task';
-$labels['edittask'] = 'Edit Task';
-$labels['save'] = 'Save';
-$labels['cancel'] = 'Cancel';
-$labels['addsubtask'] = 'Add subtask';
-$labels['deletetask'] = 'Delete task';
-$labels['deletethisonly'] = 'Delete this task only';
-$labels['deletewithchilds'] = 'Delete with all subtasks';
-
-$labels['tabsummary'] = 'Summary';
-$labels['tabrecurrence'] = 'Recurrence';
-$labels['tabattachments'] = 'Attachments';
-$labels['tabsharing'] = 'Sharing';
-
-$labels['editlist'] = 'Edit list';
-$labels['createlist'] = 'Add list';
-$labels['listactions'] = 'List options...';
-$labels['listname'] = 'Name';
-$labels['showalarms'] = 'Show alarms';
-$labels['import'] = 'Import';
-
-// date words
-$labels['on'] = 'on';
-$labels['at'] = 'at';
-$labels['this'] = 'this';
-$labels['next'] = 'next';
-
-// mesages
-$labels['savingdata'] = 'Saving data...';
-$labels['errorsaving'] = 'Failed to save data.';
-$labels['notasksfound'] = 'No tasks found for the given criteria';
-$labels['invalidstartduedates'] = 'Start date must not be greater than due date.';
-$labels['deletetasktconfirm'] = 'Do you really want to delete this task?';
-$labels['deleteparenttasktconfirm'] = 'Do you really want to delete this task and all its subtasks?';
-$labels['deletelistconfirm'] = 'Do you really want to delete this list with all its tasks?';


commit cb37ba3978a603332794a81877886918ce40db06
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Mar 13 10:43:48 2013 +0100

    Better name and placement of the due date field (#1692); copied de_CH localization to de_DE

diff --git a/plugins/tasklist/localization/de_CH.inc b/plugins/tasklist/localization/de_CH.inc
index 857df01..740b0db 100644
--- a/plugins/tasklist/localization/de_CH.inc
+++ b/plugins/tasklist/localization/de_CH.inc
@@ -15,7 +15,7 @@ $labels['edit'] = 'Bearbeiten';
 $labels['delete'] = 'Löschen';
 $labels['title'] = 'Titel';
 $labels['description'] = 'Beschreibung';
-$labels['datetime'] = 'Datum/Zeit';
+$labels['datetime'] = 'Fällig';
 $labels['start'] = 'Beginn';
 $labels['alarms'] = 'Erinnerung';
 
diff --git a/plugins/tasklist/localization/de_DE.inc b/plugins/tasklist/localization/de_DE.inc
index e8c0ada..1cbd2c0 100644
--- a/plugins/tasklist/localization/de_DE.inc
+++ b/plugins/tasklist/localization/de_DE.inc
@@ -2,67 +2,68 @@
 
 $labels = array();
 $labels['navtitle'] = 'Aufgaben';
-$labels['lists'] = 'Tasklists';
-$labels['list'] = 'Tasklist';
+$labels['lists'] = 'Aufgabenlisten';
+$labels['list'] = 'Liste';
 $labels['tags'] = 'Tags';
 
-$labels['newtask'] = 'New Task';
-$labels['createnewtask'] = 'Create new Task (e.g. Saturday, Mow the lawn)';
-$labels['createfrommail'] = 'Save as task';
-$labels['mark'] = 'Mark';
-$labels['unmark'] = 'Unmark';
-$labels['edit'] = 'Edit';
+$labels['newtask'] = 'Neue Aufgabe';
+$labels['createnewtask'] = 'Neue Aufgabe eingeben (z.B. Samstag, Rasenmähen)';
+$labels['createfrommail'] = 'Als Aufgabe speichern';
+$labels['mark'] = 'Markieren';
+$labels['unmark'] = 'Markierung aufheben';
+$labels['edit'] = 'Bearbeiten';
 $labels['delete'] = 'Löschen';
 $labels['title'] = 'Titel';
-$labels['description'] = 'Description';
-$labels['datetime'] = 'Date/Time';
-$labels['start'] = 'Start';
-$labels['alarms'] = 'Reminder';
+$labels['description'] = 'Beschreibung';
+$labels['datetime'] = 'Fällig';
+$labels['start'] = 'Beginn';
+$labels['alarms'] = 'Erinnerung';
 
-$labels['all'] = 'All';
-$labels['flagged'] = 'Flagged';
-$labels['complete'] = 'Complete';
-$labels['overdue'] = 'Overdue';
-$labels['today'] = 'Today';
-$labels['tomorrow'] = 'Tomorrow';
-$labels['next7days'] = 'Next 7 days';
-$labels['later'] = 'Later';
-$labels['nodate'] = 'no date';
-$labels['removetag'] = 'Remove';
+$labels['all'] = 'Alle';
+$labels['flagged'] = 'Markiert';
+$labels['complete'] = 'Erledigt';
+$labels['overdue'] = 'Überfällig';
+$labels['today'] = 'Heute';
+$labels['tomorrow'] = 'Morgen';
+$labels['next7days'] = 'Nächste 7 Tage';
+$labels['later'] = 'Später';
+$labels['nodate'] = 'kein Datum';
+$labels['removetag'] = 'Löschen';
 
 $labels['taskdetails'] = 'Details';
-$labels['newtask'] = 'New Task';
-$labels['edittask'] = 'Edit Task';
+$labels['newtask'] = 'Neue Aufgabe';
+$labels['edittask'] = 'Aufgabe bearbeiten';
 $labels['save'] = 'Speichern';
-$labels['cancel'] = 'Cancel';
-$labels['addsubtask'] = 'Add subtask';
-$labels['deletetask'] = 'Delete task';
-$labels['deletethisonly'] = 'Delete this task only';
-$labels['deletewithchilds'] = 'Delete with all subtasks';
+$labels['cancel'] = 'Abbrechen';
+$labels['addsubtask'] = 'Neue Teilaufgabe';
+$labels['deletetask'] = 'Aufgabe löschen';
+$labels['deletethisonly'] = 'Nur diese Aufgabe löschen';
+$labels['deletewithchilds'] = 'Mit allen Teilaufhaben löschen';
 
-$labels['tabsummary'] = 'Summary';
-$labels['tabrecurrence'] = 'Recurrence';
-$labels['tabattachments'] = 'Attachments';
-$labels['tabsharing'] = 'Sharing';
 
-$labels['editlist'] = 'Edit list';
-$labels['createlist'] = 'Add list';
-$labels['listactions'] = 'List options...';
+$labels['tabsummary'] = 'Ãœbersicht';
+$labels['tabrecurrence'] = 'Wiederholung';
+$labels['tabattachments'] = 'Anhänge';
+$labels['tabsharing'] = 'Freigabe';
+
+$labels['editlist'] = 'Liste bearbeiten';
+$labels['createlist'] = 'Neue Liste';
+$labels['listactions'] = 'Listenoptionen...';
 $labels['listname'] = 'Name';
-$labels['showalarms'] = 'Show alarms';
-$labels['import'] = 'Import';
+$labels['showalarms'] = 'Erinnerungen anzeigen';
+$labels['import'] = 'Importieren';
 
 // date words
-$labels['on'] = 'on';
-$labels['at'] = 'at';
-$labels['this'] = 'this';
-$labels['next'] = 'next';
+$labels['on'] = 'am';
+$labels['at'] = 'um';
+$labels['this'] = 'diesen';
+$labels['next'] = 'nächsten';
 
 // mesages
 $labels['savingdata'] = 'Daten werden gespeichert...';
-$labels['errorsaving'] = 'Failed to save data.';
-$labels['notasksfound'] = 'No tasks found for the given criteria';
-$labels['invalidstartduedates'] = 'Start date must not be greater than due date.';
-$labels['deletetasktconfirm'] = 'Do you really want to delete this task?';
-$labels['deleteparenttasktconfirm'] = 'Do you really want to delete this task and all its subtasks?';
-$labels['deletelistconfirm'] = 'Do you really want to delete this list with all its tasks?';
+$labels['errorsaving'] = 'Fehler beim Speichern.';
+$labels['notasksfound'] = 'Für die aktuellen Kriterien wurden keine Aufgaben gefunden.';
+$labels['invalidstartduedates'] = 'Beginn der Aufgabe darf nicht größer als das Enddatum sein.';
+$labels['deletetasktconfirm'] = 'Möchten Sie diese Aufgabe wirklich löschen?';
+$labels['deleteparenttasktconfirm'] = 'Möchten Sie diese Aufgabe inklusive aller Teilaufgaben wirklich löschen?';
+$labels['deletelistconfirm'] = 'Möchten Sie diese Liste mit allen Aufgaben wirklich löschen?';
diff --git a/plugins/tasklist/localization/en_US.inc b/plugins/tasklist/localization/en_US.inc
index 7d5415a..8a31be3 100644
--- a/plugins/tasklist/localization/en_US.inc
+++ b/plugins/tasklist/localization/en_US.inc
@@ -15,7 +15,7 @@ $labels['edit'] = 'Edit';
 $labels['delete'] = 'Delete';
 $labels['title'] = 'Title';
 $labels['description'] = 'Description';
-$labels['datetime'] = 'Date/Time';
+$labels['datetime'] = 'Due';
 $labels['start'] = 'Start';
 $labels['alarms'] = 'Reminder';
 
diff --git a/plugins/tasklist/skins/larry/templates/mainview.html b/plugins/tasklist/skins/larry/templates/mainview.html
index 4b8b49a..93914e1 100644
--- a/plugins/tasklist/skins/larry/templates/mainview.html
+++ b/plugins/tasklist/skins/larry/templates/mainview.html
@@ -103,16 +103,16 @@
 		<label><roundcube:label name="tasklist.tags" /></label>
 		<span class="task-text"></span>
 	</div>
-	<div id="task-date" class="form-section">
-		<label><roundcube:label name="tasklist.datetime" /></label>
-		<span class="task-text"></span>
-		<span id="task-time"></span>
-	</div>
 	<div id="task-start" class="form-section">
 		<label><roundcube:label name="tasklist.start" /></label>
 		<span class="task-text"></span>
 		<span id="task-starttime"></span>
 	</div>
+	<div id="task-date" class="form-section">
+		<label><roundcube:label name="tasklist.datetime" /></label>
+		<span class="task-text"></span>
+		<span id="task-time"></span>
+	</div>
 	<div id="task-alarm" class="form-section">
 		<label><roundcube:label name="tasklist.alarms" /></label>
 		<span class="task-text"></span>
diff --git a/plugins/tasklist/skins/larry/templates/taskedit.html b/plugins/tasklist/skins/larry/templates/taskedit.html
index f67d20a..1c9aa4e 100644
--- a/plugins/tasklist/skins/larry/templates/taskedit.html
+++ b/plugins/tasklist/skins/larry/templates/taskedit.html
@@ -20,17 +20,17 @@
 				<roundcube:object name="plugin.tags_editline" id="taskedit-tagline" class="tagedit" tabindex="3" />
 			</div>
 			<div class="form-section">
-				<label for="taskedit-date"><roundcube:label name="tasklist.datetime" /></label>
-				<input type="text" name="date" size="10" id="taskedit-date" tabindex="20" />  
-				<input type="text" name="time" size="6" id="taskedit-time" tabindex="21" />
-				<a href="#nodate" style="margin-left:1em" class="edit-nodate" rel="#taskedit-date,#taskedit-time"><roundcube:label name="tasklist.nodate" /></a>
-			</div>
-			<div class="form-section">
 				<label for="taskedit-startdate"><roundcube:label name="tasklist.start" /></label>
 				<input type="text" name="startdate" size="10" id="taskedit-startdate" tabindex="23" />  
 				<input type="text" name="starttime" size="6" id="taskedit-starttime" tabindex="24" />
 				<a href="#nodate" style="margin-left:1em" class="edit-nodate" rel="#taskedit-startdate,#taskedit-starttime"><roundcube:label name="tasklist.nodate" /></a>
 			</div>
+			<div class="form-section">
+				<label for="taskedit-date"><roundcube:label name="tasklist.datetime" /></label>
+				<input type="text" name="date" size="10" id="taskedit-date" tabindex="20" />  
+				<input type="text" name="time" size="6" id="taskedit-time" tabindex="21" />
+				<a href="#nodate" style="margin-left:1em" class="edit-nodate" rel="#taskedit-date,#taskedit-time"><roundcube:label name="tasklist.nodate" /></a>
+			</div>
 			<div class="form-section" id="taskedit-alarms">
 				<label for="taskedit-alarm"><roundcube:label name="tasklist.alarms" /></label>
 				<roundcube:object name="plugin.alarm_select" />


commit 93b83e6df1dd36632271674d8dc9e855f1e4fc7f
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Mar 13 10:26:59 2013 +0100

    Remove alarm type 'email' until we have a cron job that actually sends out such reminders

diff --git a/plugins/calendar/drivers/database/database_driver.php b/plugins/calendar/drivers/database/database_driver.php
index 958e8ca..7669350 100644
--- a/plugins/calendar/drivers/database/database_driver.php
+++ b/plugins/calendar/drivers/database/database_driver.php
@@ -34,7 +34,7 @@ class database_driver extends calendar_driver
   public $attendees = true;
   public $freebusy = false;
   public $attachments = true;
-  public $alarm_types = array('DISPLAY','EMAIL');
+  public $alarm_types = array('DISPLAY');
 
   private $rc;
   private $cal;
diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php
index 1c6a578..7be31e2 100644
--- a/plugins/calendar/drivers/kolab/kolab_driver.php
+++ b/plugins/calendar/drivers/kolab/kolab_driver.php
@@ -33,7 +33,7 @@ class kolab_driver extends calendar_driver
   public $freebusy = true;
   public $attachments = true;
   public $undelete = true;
-  public $alarm_types = array('DISPLAY','EMAIL');
+  public $alarm_types = array('DISPLAY');
   public $categoriesimmutable = true;
 
   private $rc;


commit c2c20b692cb9324feadfd5815d5046fdbeae23f6
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Mar 7 18:02:36 2013 +0100

    Read/write catgories property for contact objects

diff --git a/plugins/libkolab/lib/kolab_format_contact.php b/plugins/libkolab/lib/kolab_format_contact.php
index d78a82d..2f8bd14 100644
--- a/plugins/libkolab/lib/kolab_format_contact.php
+++ b/plugins/libkolab/lib/kolab_format_contact.php
@@ -97,6 +97,7 @@ class kolab_format_contact extends kolab_format
         $nc->setSuffixes(self::array2vector($object['suffix']));
         $this->obj->setNameComponents($nc);
         $this->obj->setName($object['name']);
+        $this->obj->setCategories(self::array2vector($object['categories']));
 
         if (isset($object['nickname']))
             $this->obj->setNickNames(self::array2vector($object['nickname']));
@@ -275,6 +276,7 @@ class kolab_format_contact extends kolab_format
         $object['suffix']     = join(' ', self::vector2array($nc->suffixes()));
         $object['nickname']   = join(' ', self::vector2array($this->obj->nickNames()));
         $object['profession'] = join(' ', self::vector2array($this->obj->titles()));
+        $object['categories'] = self::vector2array($this->obj->categories());
 
         // organisation related properties (affiliation)
         $orgs = $this->obj->affiliations();


commit 34cd551370617b0bafbb44a3958685288189c485
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Mar 6 15:47:30 2013 +0100

    Fix task reminder saving/rendering (#1680)

diff --git a/plugins/tasklist/drivers/database/tasklist_database_driver.php b/plugins/tasklist/drivers/database/tasklist_database_driver.php
index fe2733c..ae2b5e2 100644
--- a/plugins/tasklist/drivers/database/tasklist_database_driver.php
+++ b/plugins/tasklist/drivers/database/tasklist_database_driver.php
@@ -656,7 +656,7 @@ class tasklist_database_driver extends tasklist_driver
     private function _get_notification($task)
     {
         if ($task['alarms'] && $task['complete'] < 1 || strpos($task['alarms'], '@') !== false) {
-            $alarm = libcalendaring::get_next_alarm($task);
+            $alarm = libcalendaring::get_next_alarm($task, 'task');
 
         if ($alarm['time'] && $alarm['action'] == 'DISPLAY')
           return date('Y-m-d H:i:s', $alarm['time']);
diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js
index f3c26fd..7a10484 100644
--- a/plugins/tasklist/tasklist.js
+++ b/plugins/tasklist/tasklist.js
@@ -100,7 +100,6 @@ function rcube_tasklist_ui(settings)
     var Q = this.quote_html;
     var text2html = this.text2html;
     var event_date_text = this.event_date_text;
-    var format_datetime = this.format_datetime;
     var parse_datetime = this.parse_datetime;
     var date2unixtime = this.date2unixtime;
     var fromunixtime = this.fromunixtime;
@@ -1019,8 +1018,8 @@ function rcube_tasklist_ui(settings)
               if (alarm[0].match(/@(\d+)/)) {
                   var ondate = fromunixtime(parseInt(RegExp.$1));
                   $('#taskedit select.edit-alarm-offset').val('@');
-                  $('#taskedit input.edit-alarm-date').val(format_datetime(ondate, 1));
-                  $('#taskedit input.edit-alarm-time').val(format_datetime(ondate, 2));
+                  $('#taskedit input.edit-alarm-date').val(me.format_datetime(ondate, 1));
+                  $('#taskedit input.edit-alarm-time').val(me.format_datetime(ondate, 2));
               }
               else if (alarm[0].match(/([-+])(\d+)([MHD])/)) {
                   $('#taskedit input.edit-alarm-value').val(RegExp.$2);


commit e35d2a3a52af59a7c60957885cb95abd7e69f26f
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Mar 6 15:37:19 2013 +0100

    Fix invalid class reference (#1679)

diff --git a/plugins/tasklist/drivers/database/tasklist_database_driver.php b/plugins/tasklist/drivers/database/tasklist_database_driver.php
index 56f4cf7..fe2733c 100644
--- a/plugins/tasklist/drivers/database/tasklist_database_driver.php
+++ b/plugins/tasklist/drivers/database/tasklist_database_driver.php
@@ -656,7 +656,7 @@ class tasklist_database_driver extends tasklist_driver
     private function _get_notification($task)
     {
         if ($task['alarms'] && $task['complete'] < 1 || strpos($task['alarms'], '@') !== false) {
-            $alarm = calendarlibcalendaring::get_next_alarm($task);
+            $alarm = libcalendaring::get_next_alarm($task);
 
         if ($alarm['time'] && $alarm['action'] == 'DISPLAY')
           return date('Y-m-d H:i:s', $alarm['time']);


commit 5fa81b2041c4e576aee53bdf66d1d25ce9380c32
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Mar 6 15:25:45 2013 +0100

    Correctly quote procted column names such as 'start' and 'end' (#1675)

diff --git a/plugins/calendar/drivers/database/database_driver.php b/plugins/calendar/drivers/database/database_driver.php
index 5a518f7..958e8ca 100644
--- a/plugins/calendar/drivers/database/database_driver.php
+++ b/plugins/calendar/drivers/database/database_driver.php
@@ -240,8 +240,10 @@ class database_driver extends calendar_driver
       $event = $this->_save_preprocess($event);
       $query = $this->rc->db->query(sprintf(
         "INSERT INTO " . $this->db_events . "
-         (calendar_id, created, changed, uid, start, end, all_day, recurrence, title, description, location, categories, free_busy, priority, sensitivity, attendees, alarms, notifyat)
+         (calendar_id, created, changed, uid, %s, %s, all_day, recurrence, title, description, location, categories, free_busy, priority, sensitivity, attendees, alarms, notifyat)
          VALUES (?, %s, %s, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+          $this->rc->db->quote_identifier('start'),
+          $this->rc->db->quote_identifier('end'),
           $this->rc->db->now(),
           $this->rc->db->now()
         ),
@@ -341,9 +343,10 @@ class database_driver extends calendar_driver
                 $sqlresult = $this->rc->db->query(sprintf(
                   "SELECT event_id FROM " . $this->db_events . "
                    WHERE calendar_id IN (%s)
-                   AND start >= ?
+                   AND %s >= ?
                    AND recurrence_id=?",
-                  $this->calendar_ids
+                  $this->calendar_ids,
+                  $this->rc->db->quote_identifier('start')
                   ),
                   $fromdate->format(self::DB_DATE_FORMAT),
                   $master['id']);
@@ -538,9 +541,11 @@ class database_driver extends calendar_driver
         $notify_at = $this->_get_notification(array('alarms' => $event['alarms'], 'start' => $next_start, 'end' => $next_end));
         $query = $this->rc->db->query(sprintf(
           "INSERT INTO " . $this->db_events . "
-           (calendar_id, recurrence_id, created, changed, uid, start, end, all_day, recurrence, title, description, location, categories, free_busy, priority, sensitivity, alarms, notifyat)
+           (calendar_id, recurrence_id, created, changed, uid, %s, %s, all_day, recurrence, title, description, location, categories, free_busy, priority, sensitivity, alarms, notifyat)
             SELECT calendar_id, ?, %s, %s, uid, ?, ?, all_day, recurrence, title, description, location, categories, free_busy, priority, sensitivity, alarms, ?
             FROM  " . $this->db_events . " WHERE event_id=? AND calendar_id IN (" . $this->calendar_ids . ")",
+            $this->rc->db->quote_identifier('start'),
+            $this->rc->db->quote_identifier('end'),
             $this->rc->db->now(),
             $this->rc->db->now()
           ),
@@ -636,7 +641,7 @@ class database_driver extends calendar_driver
             $query = $this->rc->db->query(
               "DELETE FROM " . $this->db_events . "
                WHERE calendar_id IN (" . $this->calendar_ids . ")
-               AND start >= ?
+               AND " . $this->rc->db->quote_identifier('start') . " >= ?
                AND recurrence_id=?",
               $fromdate->format(self::DB_DATE_FORMAT),
               $master['id']
@@ -836,9 +841,10 @@ class database_driver extends calendar_driver
       $result = $this->rc->db->query(sprintf(
         "SELECT * FROM " . $this->db_events . "
          WHERE calendar_id IN (%s)
-         AND notifyat <= %s AND end > %s",
+         AND notifyat <= %s AND %s > %s",
          join(',', $calendar_ids),
          $this->rc->db->fromunixtime($time),
+         $this->rc->db->quote_identifier('end'),
          $this->rc->db->fromunixtime($time)
        ));
 


commit 67fbd4d6f088a0210f340073f0bfcdf2db11d32e
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Mar 6 15:23:31 2013 +0100

    Fix javascript errors about invalid method calls

diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js
index dbd8b47..95e1d53 100644
--- a/plugins/calendar/calendar_ui.js
+++ b/plugins/calendar/calendar_ui.js
@@ -2487,8 +2487,8 @@ function rcube_calendar_ui(settings)
       widget.children().each(function(){
         li = $(this);
         html = li.children().first().html().replace(/\s+\(.+\)$/, '').replace(amregex, '0:$1').replace(pmregex, '1:$1');
-        if (html == val)
-          menu.activate($.Event({ type:'keypress' }), li);
+        if (html.indexOf(val) == 0)
+          menu._scrollIntoView(li);
       });
     };
 


commit e1286178e85393f918841256100f803ed4d61bb7
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Mar 6 12:56:15 2013 +0100

    Avoid javascript errors while free/busy data isn't fully loaded

diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js
index 66b126b..dbd8b47 100644
--- a/plugins/calendar/calendar_ui.js
+++ b/plugins/calendar/calendar_ui.js
@@ -1251,6 +1251,10 @@ function rcube_calendar_ui(settings)
     // attempt to find a time slot where all attemdees are available
     var freebusy_find_slot = function(dir)
     {
+      // exit if free-busy data isn't available yet
+      if (!freebusy_data || !freebusy_data.start)
+        return false;
+
       var event = me.selected_event,
         eventstart = clone_date(event.start, event.allDay ? 1 : 0).getTime(),  // calculate with integers
         eventend = clone_date(event.end, event.allDay ? 2 : 0).getTime(),


commit 6e484c5934a03455e1b8031f9ea2a6e6ceb78725
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Mar 6 12:43:56 2013 +0100

    Avoid rendering the next day in free/busy grid (can happen on DST transitions)

diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js
index 5cd1d7d..66b126b 100644
--- a/plugins/calendar/calendar_ui.js
+++ b/plugins/calendar/calendar_ui.js
@@ -933,6 +933,7 @@ function rcube_calendar_ui(settings)
         curdate.setTime(t);
         datestr = fc.fullCalendar('formatDate', curdate, date_format);
         if (datestr != lastdate) {
+          if (lastdate && !allday) break;
           dates_row += '<th colspan="' + dayslots + '" class="boxtitle date' + $.fullCalendar.formatDate(curdate, 'ddMMyyyy') + '">' + Q(datestr) + '</th>';
           lastdate = datestr;
         }


commit b03f2bfbff89f2b67b0905f3fe0b1022e2d067ab
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Mar 6 12:30:12 2013 +0100

    Minor calendar UI improvements: make date fields wide enough; display client's current timezone name

diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
index c2c7371..a70cbdd 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -275,6 +275,7 @@ class calendar extends rcube_plugin
     // initialize attendees autocompletion
     rcube_autocomplete_init();
 
+    $this->rc->output->set_env('timezone', $this->timezone->getName());
     $this->rc->output->set_env('calendar_driver', $this->rc->config->get('calendar_driver'), false);
     $this->rc->output->set_env('mscolors', $this->driver->get_color_values());
     $this->rc->output->set_env('identities-selector', $this->ui->identity_select(array('id' => 'edit-identities-list')));
diff --git a/plugins/calendar/skins/classic/templates/calendar.html b/plugins/calendar/skins/classic/templates/calendar.html
index 6e70a16..0725987 100644
--- a/plugins/calendar/skins/classic/templates/calendar.html
+++ b/plugins/calendar/skins/classic/templates/calendar.html
@@ -106,12 +106,12 @@
   <div style="float:left; width:28em">
     <div class="form-section">
       <label for="schedule-startdate"><roundcube:label name="calendar.start" /></label>
-      <input type="text" name="startdate" size="10" id="schedule-startdate" disabled="true" />  
+      <input type="text" name="startdate" size="11" id="schedule-startdate" disabled="true" />  
       <input type="text" name="starttime" size="6" id="schedule-starttime" disabled="true" />
     </div>
     <div class="form-section">
       <label for="schedule-enddate"><roundcube:label name="calendar.end" /></label>
-      <input type="text" name="enddate" size="10" id="schedule-enddate" disabled="true" />  
+      <input type="text" name="enddate" size="11" id="schedule-enddate" disabled="true" />  
       <input type="text" name="endtime" size="6"  id="schedule-endtime" disabled="true" />
     </div>
   </div>
diff --git a/plugins/calendar/skins/classic/templates/eventedit.html b/plugins/calendar/skins/classic/templates/eventedit.html
index 3548478..a5ace0d 100644
--- a/plugins/calendar/skins/classic/templates/eventedit.html
+++ b/plugins/calendar/skins/classic/templates/eventedit.html
@@ -26,12 +26,12 @@
       <div class="event-section">
         <label style="float:right;padding-right:0.5em"><input type="checkbox" name="allday" id="edit-allday" value="1" /><roundcube:label name="calendar.all-day" /></label>
         <label for="edit-startdate"><roundcube:label name="calendar.start" /></label>
-        <input type="text" name="startdate" size="10" id="edit-startdate" />  
+        <input type="text" name="startdate" size="11" id="edit-startdate" />  
         <input type="text" name="starttime" size="6" id="edit-starttime" />
       </div>
       <div class="event-section">
         <label for="edit-enddate"><roundcube:label name="calendar.end" /></label>
-        <input type="text" name="enddate" size="10" id="edit-enddate" />  
+        <input type="text" name="enddate" size="11" id="edit-enddate" />  
         <input type="text" name="endtime" size="6"  id="edit-endtime" />
       </div>
       <div class="event-section" id="edit-alarms">
diff --git a/plugins/calendar/skins/larry/calendar.css b/plugins/calendar/skins/larry/calendar.css
index 8d6162b..9e617e7 100644
--- a/plugins/calendar/skins/larry/calendar.css
+++ b/plugins/calendar/skins/larry/calendar.css
@@ -125,6 +125,14 @@ div.sidebarclosed {
 	border-bottom-color: #ababab;
 }
 
+#calendar .timezonedisplay {
+	position: absolute;
+	bottom: 9px;
+	right: 8px;
+	font-size: 0.85em;
+	color: #666;
+}
+
 #print {
 	width: 680px;
 }
diff --git a/plugins/calendar/skins/larry/templates/calendar.html b/plugins/calendar/skins/larry/templates/calendar.html
index 3f914f8..a814345 100644
--- a/plugins/calendar/skins/larry/templates/calendar.html
+++ b/plugins/calendar/skins/larry/templates/calendar.html
@@ -41,6 +41,7 @@
 	<div id="calendar">
 		<roundcube:object name="plugin.angenda_options" class="boxfooter" id="agendaoptions" />
 		<roundcube:object name="message" id="message" class="statusbar" />
+		<div class="timezonedisplay"><roundcube:var name="env:timezone" /></div>
 	</div>
 </div>
 
@@ -119,13 +120,13 @@
 	<div style="float:left; width:28em">
 		<div class="form-section">
 			<label for="schedule-startdate"><roundcube:label name="calendar.start" /></label>
-			<input type="text" name="startdate" size="10" id="schedule-startdate" disabled="true" />  
+			<input type="text" name="startdate" size="11" id="schedule-startdate" disabled="true" />  
 			<input type="text" name="starttime" size="6" id="schedule-starttime" disabled="true" />
 		</div>
 		<div class="form-section">
 			<label for="schedule-enddate"><roundcube:label name="calendar.end" /></label>
-			<input type="text" name="enddate" size="10" id="schedule-enddate" disabled="true" />  
-			<input type="text" name="endtime" size="6"	id="schedule-endtime" disabled="true" />
+			<input type="text" name="enddate" size="11" id="schedule-enddate" disabled="true" />  
+			<input type="text" name="endtime" size="6" id="schedule-endtime" disabled="true" />
 		</div>
 	</div>
 	<div style="float:left">
diff --git a/plugins/calendar/skins/larry/templates/eventedit.html b/plugins/calendar/skins/larry/templates/eventedit.html
index 841baf7..6784891 100644
--- a/plugins/calendar/skins/larry/templates/eventedit.html
+++ b/plugins/calendar/skins/larry/templates/eventedit.html
@@ -23,13 +23,13 @@
 			<div class="event-section">
 				<label style="float:right;padding-right:0.5em"><input type="checkbox" name="allday" id="edit-allday" value="1" /><roundcube:label name="calendar.all-day" /></label>
 				<label for="edit-startdate"><roundcube:label name="calendar.start" /></label>
-				<input type="text" name="startdate" size="10" id="edit-startdate" />  
+				<input type="text" name="startdate" size="11" id="edit-startdate" />  
 				<input type="text" name="starttime" size="6" id="edit-starttime" />
 			</div>
 			<div class="event-section">
 				<label for="edit-enddate"><roundcube:label name="calendar.end" /></label>
-				<input type="text" name="enddate" size="10" id="edit-enddate" />  
-				<input type="text" name="endtime" size="6"	id="edit-endtime" />
+				<input type="text" name="enddate" size="11" id="edit-enddate" />  
+				<input type="text" name="endtime" size="6" id="edit-endtime" />
 			</div>
 			<div class="event-section" id="edit-alarms">
 				<label for="edit-alarm"><roundcube:label name="calendar.alarms" /></label>


commit 6a2ebfa3719d365b434efcad87d9e2f2ecd1c0a1
Author: Thomas Bruederli <thomas at roundcube.net>
Date:   Wed Mar 6 11:45:40 2013 +0100

    Fix free/busy finder for all-day events

diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js
index d9e855c..5cd1d7d 100644
--- a/plugins/calendar/calendar_ui.js
+++ b/plugins/calendar/calendar_ui.js
@@ -1231,10 +1231,10 @@ function rcube_calendar_ui(settings)
     {
       // fix all-day evebt times
       if (me.selected_event.allDay) {
+        var numdays = Math.floor((me.selected_event.end.getTime() - me.selected_event.start.getTime()) / DAY_MS);
         start.setHours(12);
         start.setMinutes(0);
-        if (end.getHours() == 0)
-          end.setHours(-1);
+        end.setTime(start.getTime() + numdays * DAY_MS);
         end.setHours(13);
         end.setMinutes(0);
       }
@@ -1251,13 +1251,13 @@ function rcube_calendar_ui(settings)
     var freebusy_find_slot = function(dir)
     {
       var event = me.selected_event,
-        eventstart = event.start.getTime(),  // calculate with integers
-        eventend = event.end.getTime(),
-        duration = eventend - eventstart,
+        eventstart = clone_date(event.start, event.allDay ? 1 : 0).getTime(),  // calculate with integers
+        eventend = clone_date(event.end, event.allDay ? 2 : 0).getTime(),
+        duration = eventend - eventstart - (event.allDay ? HOUR_MS : 0),  // make sure we don't cross day borders on DST change
         sinterval = freebusy_data.interval * 60000,
         intvlslots = 1,
         numslots = Math.ceil(duration / sinterval),
-        checkdate, slotend, email, ts, slot, slotdate = new Date(), slotenddate = new Date();
+        checkdate, slotend, email, ts, slot, slotdate = new Date();
 
       // shift event times to next possible slot
       eventstart += sinterval * intvlslots * dir;
@@ -1268,9 +1268,13 @@ function rcube_calendar_ui(settings)
       for (slot = dir > 0 ? freebusy_data.start.getTime() : freebusy_data.end.getTime() - sinterval;
             (dir > 0 && slot < freebusy_data.end.getTime()) || (dir < 0 && slot >= freebusy_data.start.getTime());
             slot += sinterval * dir) {
-        slotend = slot + sinterval;
         slotdate.setTime(slot);
-        slotenddate.setTime(slotend);
+        // fix slot if just crossed a DST change
+        if (event.allDay) {
+          fix_date(slotdate);
+          slot = slotdate.getTime();
+        }
+        slotend = slot + sinterval;
 
         if ((dir > 0 && slotend <= eventstart) || (dir < 0 && slot >= eventend))  // skip
           continue;


commit af73e0c6902fba55074cf1cfd73afe4261d8822a
Author: Thomas Bruederli <thomas at roundcube.net>
Date:   Wed Mar 6 10:57:56 2013 +0100

    Make free/busy finder DST aware (#1676)

diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
index 5f4cc6b..c2c7371 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -1382,6 +1382,16 @@ class calendar extends rcube_plugin
     $start = get_input_value('start', RCUBE_INPUT_GPC);
     $end = get_input_value('end', RCUBE_INPUT_GPC);
     
+    // convert dates into unix timestamps
+    if (!empty($start) && !is_numeric($start)) {
+      $dts = new DateTime($start, $this->timezone);
+      $start = $dts->format('U');
+    }
+    if (!empty($end) && !is_numeric($end)) {
+      $dte = new DateTime($end, $this->timezone);
+      $end = $dte->format('U');
+    }
+    
     if (!$start) $start = time();
     if (!$end) $end = $start + 3600;
     
@@ -1419,11 +1429,27 @@ class calendar extends rcube_plugin
     $start = get_input_value('start', RCUBE_INPUT_GPC);
     $end = get_input_value('end', RCUBE_INPUT_GPC);
     $interval = intval(get_input_value('interval', RCUBE_INPUT_GPC));
-    
+    $strformat = $interval > 60 ? 'Ymd' : 'YmdHis';
+
+    // convert dates into unix timestamps
+    if (!empty($start) && !is_numeric($start)) {
+      $dts = new DateTime($start, $this->timezone);
+      $start = $dts->format('U');
+    }
+    if (!empty($end) && !is_numeric($end)) {
+      $dte = new DateTime($end, $this->timezone);
+      $end = $dte->format('U');
+    }
+
     if (!$start) $start = time();
     if (!$end)   $end = $start + 86400 * 30;
     if (!$interval) $interval = 60;  // 1 hour
     
+    if (!$dte) {
+      $dts = new DateTime('@'.$start);
+      $dts->setTimezone($this->timezone);
+    }
+    
     $fblist = $this->driver->get_freebusy_list($email, $start, $end);
     $slots = array();
     
@@ -1431,7 +1457,9 @@ class calendar extends rcube_plugin
     for ($s = 0, $t = $start; $t <= $end; $s++) {
       $status = self::FREEBUSY_UNKNOWN;
       $t_end = $t + $interval * 60;
-        
+      $dt = new DateTime('@'.$t);
+      $dt->setTimezone($this->timezone);
+
       // determine attendee's status
       if (is_array($fblist)) {
         $status = self::FREEBUSY_FREE;
@@ -1446,13 +1474,24 @@ class calendar extends rcube_plugin
       }
       
       $slots[$s] = $status;
+      $times[$s] = intval($dt->format($strformat));
       $t = $t_end;
     }
     
+    $dte = new DateTime('@'.$t_end);
+    $dte->setTimezone($this->timezone);
+    
     // let this information be cached for 5min
     send_future_expire_header(300);
     
-    echo json_encode(array('email' => $email, 'start' => intval($start), 'end' => intval($t_end), 'interval' => $interval, 'slots' => $slots));
+    echo json_encode(array(
+      'email' => $email,
+      'start' => $dts->format('c'),
+      'end'   => $dte->format('c'),
+      'interval' => $interval,
+      'slots' => $slots,
+      'times' => $times,
+    ));
     exit;
   }
   
diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js
index 24d7bd6..d9e855c 100644
--- a/plugins/calendar/calendar_ui.js
+++ b/plugins/calendar/calendar_ui.js
@@ -74,6 +74,7 @@ function rcube_calendar_ui(settings)
     var parse_datetime = this.parse_datetime;
     var date2unixtime = this.date2unixtime;
     var fromunixtime = this.fromunixtime;
+    var parseISO8601 = this.parseISO8601;
     var init_alarms_edit = this.init_alarms_edit;
 
 
@@ -122,6 +123,15 @@ function rcube_calendar_ui(settings)
       return d;
     };
 
+    // fix date if jumped over a DST change
+    var fix_date = function(date)
+    {
+      if (date.getHours() == 23)
+        date.setTime(date.getTime() + HOUR_MS);
+      else if (date.getHours() > 0)
+        date.setHours(0);
+    };
+
     // turn the given date into an ISO 8601 date string understandable by PHPs strtotime()
     var date2servertime = function(date)
     {
@@ -129,6 +139,11 @@ function rcube_calendar_ui(settings)
           + 'T'+zeropad(date.getHours())+':'+zeropad(date.getMinutes())+':'+zeropad(date.getSeconds());
     }
 
+    var date2timestring = function(date, dateonly)
+    {
+      return date2servertime(date).replace(/[^0-9]/g, '').substr(0, (dateonly ? 8 : 14));
+    }
+
     var zeropad = function(num)
     {
         return (num < 10 ? '0' : '') + num;
@@ -469,7 +484,7 @@ function rcube_calendar_ui(settings)
         recurrence = $('#edit-recurrence-frequency').val(event.recurrence ? event.recurrence.FREQ : '').change();
         interval = $('#eventedit select.edit-recurrence-interval').val(event.recurrence ? event.recurrence.INTERVAL : 1);
         rrtimes = $('#edit-recurrence-repeat-times').val(event.recurrence ? event.recurrence.COUNT : 1);
-        rrenddate = $('#edit-recurrence-enddate').val(event.recurrence && event.recurrence.UNTIL ? $.fullCalendar.formatDate($.fullCalendar.parseISO8601(event.recurrence.UNTIL), settings['date_format']) : '');
+        rrenddate = $('#edit-recurrence-enddate').val(event.recurrence && event.recurrence.UNTIL ? $.fullCalendar.formatDate(parseISO8601(event.recurrence.UNTIL), settings['date_format']) : '');
         $('#eventedit input.edit-recurrence-until:checked').prop('checked', false);
       
         var weekdays = ['SU','MO','TU','WE','TH','FR','SA'];
@@ -893,10 +908,13 @@ function rcube_calendar_ui(settings)
     {
       if (delta) {
         freebusy_ui.start.setTime(freebusy_ui.start.getTime() + DAY_MS * delta);
+        fix_date(freebusy_ui.start);
+        
         // skip weekends if in workinhoursonly-mode
         if (Math.abs(delta) == 1 && freebusy_ui.workinhoursonly) {
           while (is_weekend(freebusy_ui.start))
             freebusy_ui.start.setTime(freebusy_ui.start.getTime() + DAY_MS * delta);
+          fix_date(freebusy_ui.start);
         }
         
         freebusy_ui.end = new Date(freebusy_ui.start.getTime() + DAY_MS * freebusy_ui.numdays);
@@ -965,7 +983,7 @@ function rcube_calendar_ui(settings)
       
       // if we have loaded free-busy data, show it
       if (!freebusy_ui.loading) {
-        if (date2unixtime(freebusy_ui.start) < freebusy_data.start || date2unixtime(freebusy_ui.end) > freebusy_data.end || freebusy_ui.interval != freebusy_data.interval) {
+        if (freebusy_ui.start < freebusy_data.start || freebusy_ui.end > freebusy_data.end || freebusy_ui.interval != freebusy_data.interval) {
           load_freebusy_data(freebusy_ui.start, freebusy_ui.interval);
         }
         else {
@@ -1069,12 +1087,13 @@ function rcube_calendar_ui(settings)
     // fetch free-busy information for each attendee from server
     var load_freebusy_data = function(from, interval)
     {
-      var start = new Date(from.getTime() - DAY_MS * 2);  // start 1 days before event
+      var start = new Date(from.getTime() - DAY_MS * 2);  // start 2 days before event
+      fix_date(start);
       var end = new Date(start.getTime() + DAY_MS * Math.max(14, freebusy_ui.numdays + 7));   // load min. 14 days
       freebusy_ui.numrequired = 0;
       freebusy_data.all = [];
       freebusy_data.required = [];
-      
+
       // load free-busy information for every attendee
       var domid, email;
       for (var i=0; i < event_attendees.length; i++) {
@@ -1087,7 +1106,7 @@ function rcube_calendar_ui(settings)
             type: 'GET',
             dataType: 'json',
             url: rcmail.url('freebusy-times'),
-            data: { email:email, start:date2unixtime(clone_date(start, 1)), end:date2unixtime(clone_date(end, 2)), interval:interval, _remote:1 },
+            data: { email:email, start:date2servertime(clone_date(start, 1)), end:date2servertime(clone_date(end, 2)), interval:interval, _remote:1 },
             success: function(data) {
               freebusy_ui.loading--;
               
@@ -1101,11 +1120,11 @@ function rcube_calendar_ui(settings)
               }
               
               // copy data to member var
-              var req = attendee.role != 'OPT-PARTICIPANT';
-              var ts = data.start - 0;
-              freebusy_data.start = ts;
+              var ts, req = attendee.role != 'OPT-PARTICIPANT';
+              freebusy_data.start = parseISO8601(data.start);
               freebusy_data[data.email] = {};
               for (var i=0; i < data.slots.length; i++) {
+                ts = data.times[i] + '';
                 freebusy_data[data.email][ts] = data.slots[i];
                 
                 // set totals
@@ -1117,10 +1136,8 @@ function rcube_calendar_ui(settings)
                 if (!freebusy_data.all[ts])
                   freebusy_data.all[ts] = [0,0,0,0];
                 freebusy_data.all[ts][data.slots[i]]++;
-                
-                ts += data.interval * 60;
               }
-              freebusy_data.end = ts;
+              freebusy_data.end = parseISO8601(data.end);
               freebusy_data.interval = data.interval;
 
               // hide loading indicator
@@ -1177,13 +1194,18 @@ function rcube_calendar_ui(settings)
       var domid = String(email).replace(rcmail.identifier_expr, '');
       var row = $('#fbrow' + domid);
       var rowall = $('#fbrowall').children();
-      var ts = date2unixtime(freebusy_ui.start);
-      var fbdata = freebusy_data[email];
-      
+      var dateonly = freebusy_ui.interval > 60,
+        t, ts = date2timestring(freebusy_ui.start, dateonly),
+        curdate = new Date(),
+        fbdata = freebusy_data[email];
+
       if (fbdata && fbdata[ts] !== undefined && row.length) {
+        t = freebusy_ui.start.getTime();
         row.children().each(function(i, cell){
+          curdate.setTime(t);
+          ts = date2timestring(curdate, dateonly);
           cell.className = cell.className.replace('unknown', fbdata[ts] ? status_classes[fbdata[ts]] : 'unknown');
-          
+
           // also update total row if all data was loaded
           if (freebusy_ui.loading == 0 && freebusy_data.all[ts] && (cell = rowall.get(i))) {
             var workinghours = cell.className.indexOf('workinghours') >= 0;
@@ -1199,7 +1221,7 @@ function rcube_calendar_ui(settings)
             cell.className = (workinghours ? 'workinghours ' : 'offhours ') + req_status +  ' all-' + all_status;
           }
           
-          ts += freebusy_ui.interval * 60;
+          t += freebusy_ui.interval * 60000;
         });
       }
     };
@@ -1207,9 +1229,12 @@ function rcube_calendar_ui(settings)
     // write changed event date/times back to form fields
     var update_freebusy_dates = function(start, end)
     {
+      // fix all-day evebt times
       if (me.selected_event.allDay) {
         start.setHours(12);
         start.setMinutes(0);
+        if (end.getHours() == 0)
+          end.setHours(-1);
         end.setHours(13);
         end.setMinutes(0);
       }
@@ -1226,13 +1251,13 @@ function rcube_calendar_ui(settings)
     var freebusy_find_slot = function(dir)
     {
       var event = me.selected_event,
-        eventstart = date2unixtime(event.start),  // calculate with unixtimes
-        eventend = date2unixtime(event.end),
+        eventstart = event.start.getTime(),  // calculate with integers
+        eventend = event.end.getTime(),
         duration = eventend - eventstart,
-        sinterval = freebusy_data.interval * 60,
+        sinterval = freebusy_data.interval * 60000,
         intvlslots = 1,
         numslots = Math.ceil(duration / sinterval),
-        checkdate, slotend, email, curdate;
+        checkdate, slotend, email, ts, slot, slotdate = new Date(), slotenddate = new Date();
 
       // shift event times to next possible slot
       eventstart += sinterval * intvlslots * dir;
@@ -1240,27 +1265,32 @@ function rcube_calendar_ui(settings)
 
       // iterate through free-busy slots and find candidates
       var candidatecount = 0, candidatestart = candidateend = success = false;
-      for (var slot = dir > 0 ? freebusy_data.start : freebusy_data.end - sinterval; (dir > 0 && slot < freebusy_data.end) || (dir < 0 && slot >= freebusy_data.start); slot += sinterval * dir) {
+      for (slot = dir > 0 ? freebusy_data.start.getTime() : freebusy_data.end.getTime() - sinterval;
+            (dir > 0 && slot < freebusy_data.end.getTime()) || (dir < 0 && slot >= freebusy_data.start.getTime());
+            slot += sinterval * dir) {
         slotend = slot + sinterval;
+        slotdate.setTime(slot);
+        slotenddate.setTime(slotend);
+
         if ((dir > 0 && slotend <= eventstart) || (dir < 0 && slot >= eventend))  // skip
           continue;
-        
+
         // respect workingours setting
         if (freebusy_ui.workinhoursonly) {
-          curdate = fromunixtime(dir > 0 || !candidateend ? slot : (candidateend - duration));
-          if (is_weekend(curdate) || (freebusy_data.interval <= 60 && !is_workinghour(curdate))) {  // skip off-hours
+          if (is_weekend(slotdate) || (freebusy_data.interval <= 60 && !is_workinghour(slotdate))) {  // skip off-hours
             candidatestart = candidateend = false;
             candidatecount = 0;
             continue;
           }
         }
-        
+
         if (!candidatestart)
           candidatestart = slot;
-        
+
         // check freebusy data for all attendees
+        ts = date2timestring(slotdate, freebusy_data.interval > 60);
         for (var i=0; i < event_attendees.length; i++) {
-          if (freebusy_ui.attendees[i].role != 'OPT-PARTICIPANT' && (email = freebusy_ui.attendees[i].email) && freebusy_data[email] && freebusy_data[email][slot] > 1) {
+          if (freebusy_ui.attendees[i].role != 'OPT-PARTICIPANT' && (email = freebusy_ui.attendees[i].email) && freebusy_data[email] && freebusy_data[email][ts] > 1) {
             candidatestart = candidateend = false;
             break;
           }
@@ -1281,12 +1311,12 @@ function rcube_calendar_ui(settings)
         // if candidate is big enough, this is it!
         if (candidatecount == numslots) {
           if (dir > 0) {
-            event.start = fromunixtime(candidatestart);
-            event.end = fromunixtime(candidatestart + duration);
+            event.start.setTime(candidatestart);
+            event.end.setTime(candidatestart + duration);
           }
           else {
-            event.end = fromunixtime(candidateend);
-            event.start = fromunixtime(candidateend - duration);
+            event.end.setTime(candidateend);
+            event.start.setTime(candidateend - duration);
           }
           success = true;
           break;
@@ -1456,7 +1486,7 @@ function rcube_calendar_ui(settings)
         type: 'GET',
         dataType: 'html',
         url: rcmail.url('freebusy-status'),
-        data: { email:email, start:date2unixtime(clone_date(event.start, event.allDay?1:0)), end:date2unixtime(clone_date(event.end, event.allDay?2:0)), _remote: 1 },
+        data: { email:email, start:date2servertime(clone_date(event.start, event.allDay?1:0)), end:date2servertime(clone_date(event.end, event.allDay?2:0)), _remote: 1 },
         success: function(status){
           icon.removeClass('loading').addClass(String(status).toLowerCase());
         },
diff --git a/plugins/libcalendaring/libcalendaring.js b/plugins/libcalendaring/libcalendaring.js
index 35897d7..b2ae597 100644
--- a/plugins/libcalendaring/libcalendaring.js
+++ b/plugins/libcalendaring/libcalendaring.js
@@ -110,7 +110,7 @@ function rcube_libcalendaring(settings)
      * Convert an ISO 8601 formatted date string from the server into a Date object.
      * Timezone information will be ignored, the server already provides dates in user's timezone.
      */
-    function parseISO8601(s)
+    this.parseISO8601 = function(s)
     {
         // force d to be on check's YMD, for daylight savings purposes
         var fixDate = function(d, check) {
@@ -319,8 +319,8 @@ function rcube_libcalendaring(settings)
         var actions, adismiss, asnooze, alarm, html, event_ids = [];
         for (var i=0; i < alarms.length; i++) {
             alarm = alarms[i];
-            alarm.start = parseISO8601(alarm.start);
-            alarm.end = parseISO8601(alarm.end);
+            alarm.start = this.parseISO8601(alarm.start);
+            alarm.end = this.parseISO8601(alarm.end);
             event_ids.push(alarm.id);
 
             html = '<h3 class="event-title">' + Q(alarm.title) + '</h3>';


commit 6aacc89876228687ca03233a26a79ef022f19283
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Mar 6 10:19:22 2013 +0100

    Improve behavior when deleting recurring events (#1677)

diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php
index 2b3694a..1c6a578 100644
--- a/plugins/calendar/drivers/kolab/kolab_driver.php
+++ b/plugins/calendar/drivers/kolab/kolab_driver.php
@@ -430,6 +430,13 @@ class kolab_driver extends calendar_driver
           // removing 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'));
+
+            // no future instances found: delete the master event (bug #1677)
+            if (!$recurring['start']) {
+              $success = $storage->delete_event($master, $force);
+              break;
+            }
+
             $master['start'] = $recurring['start'];
             $master['end'] = $recurring['end'];
             if ($master['recurrence']['COUNT'])
@@ -449,6 +456,11 @@ class kolab_driver extends calendar_driver
             $master['recurrence']['UNTIL'] = clone $event['start'];
             $master['recurrence']['UNTIL']->sub(new DateInterval('P1D'));
             unset($master['recurrence']['COUNT']);
+
+            // if all future instances are deleted, remove recurrence rule entirely (bug #1677)
+            if ($master['recurrence']['UNTIL']->format('Ymd') == $master['start']->format('Ymd'))
+              $master['recurrence'] = array();
+
             $success = $storage->update_event($master);
             break;
           }


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

    Adapt fix for #349

diff --git a/plugins/libkolab/lib/kolab_storage_folder.php b/plugins/libkolab/lib/kolab_storage_folder.php
index b3fe9fe..0c12423 100644
--- a/plugins/libkolab/lib/kolab_storage_folder.php
+++ b/plugins/libkolab/lib/kolab_storage_folder.php
@@ -932,7 +932,11 @@ class kolab_storage_folder
         case 'event':
             if ($this->get_namespace() == 'personal') {
                 $result = $this->trigger_url(
-                    sprintf('%s/trigger/%s/%s.pfb', kolab_storage::get_freebusy_server(), $owner, $this->imap->mod_folder($this->name)),
+                    sprintf('%s/trigger/%s/%s.pfb',
+                        kolab_storage::get_freebusy_server(),
+                        urlencode($owner),
+                        urlencode($this->imap->mod_folder($this->name))
+                    ),
                     $this->imap->options['user'],
                     $this->imap->options['password']
                 );


commit 79ce7fd64c5cbdb595f692ec60ea17e1760de4f1
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Feb 28 15:26:56 2013 +0100

    Fix overflowing calendar widget in French localization (#1665)

diff --git a/plugins/calendar/skins/larry/calendar.css b/plugins/calendar/skins/larry/calendar.css
index 5072ce2..8d6162b 100644
--- a/plugins/calendar/skins/larry/calendar.css
+++ b/plugins/calendar/skins/larry/calendar.css
@@ -920,31 +920,21 @@ a.dropdown-link:after {
 .ui-dialog-buttonset a.dropdown-link {
 	margin-right: 1em;
 }
-/*
-.ui-datepicker-calendar .ui-datepicker-today .ui-state-default {
-	border-color: #cccccc;
-	background: #ffffcc;
-	color: #000;
+
+#calendarsidebar .ui-datepicker-calendar {
+	table-layout: fixed;
 }
-*/
+
 .ui-datepicker-calendar .ui-datepicker-week-col {
 	border: 0;
 	color: #999;
 	font-size: 90%;
 	text-align: right;
 	padding-right: 6px;
-}
-/*
-.ui-datepicker th {
-    padding: 0.3em 0;
-    font-size: 10px;
+	width: 20px;
+	overflow: hidden;
 }
 
-.ui-datepicker td span,
-.ui-datepicker td a {
-	padding-left: 0.1em;
-}
-*/
 .ui-autocomplete {
 	max-height: 160px;
 	overflow-y: auto;


commit 88d95b24a118d3ea18bd2edf47cec3e5fbc7ddaf
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Feb 28 15:25:47 2013 +0100

    Fix javascript errors trown by CSS expressions

diff --git a/plugins/calendar/skins/classic/iehacks.css b/plugins/calendar/skins/classic/iehacks.css
index 2b6d6be..42bd736 100644
--- a/plugins/calendar/skins/classic/iehacks.css
+++ b/plugins/calendar/skins/classic/iehacks.css
@@ -5,13 +5,13 @@
 	height: expression(Math.max(300, parseInt(document.documentElement.clientHeight)-100)+'px');
 }
 
-#sidebar,
-#sidebartoggle {
+#calendarsidebar,
+#calendarsidebartoggle {
 	height: expression((parseInt(this.parentNode.offsetHeight)-37)+'px');
 }
 
 #calendar {
-	width: expression((parseInt(this.parentNode.offsetWidth)-parseInt(document.getElementById('sidebartoggle').offsetWidth)-parseInt(document.getElementById('sidebartoggle').offsetLeft)-4)+'px');
+	width: expression((parseInt(this.parentNode.offsetWidth)-parseInt(document.getElementById('calendarsidebartoggle').offsetWidth)-parseInt(document.getElementById('calendarsidebartoggle').offsetLeft)-4)+'px');
 	height: expression(parseInt(this.parentNode.offsetHeight)+'px');
 }
 
diff --git a/plugins/calendar/skins/larry/iehacks.css b/plugins/calendar/skins/larry/iehacks.css
index 529b029..d6e122d 100644
--- a/plugins/calendar/skins/larry/iehacks.css
+++ b/plugins/calendar/skins/larry/iehacks.css
@@ -1,17 +1,17 @@
 /* CSS hacks for IE 7 */
 
-#sidebar,
-#sidebartoggle {
+#calendarsidebar,
+#calendarsidebartoggle {
 	height: expression((parseInt(this.parentNode.offsetHeight)-37)+'px');
 }
 
 #calendar {
-	width: expression((parseInt(this.parentNode.offsetWidth)-parseInt(document.getElementById('sidebartoggle').offsetWidth)-parseInt(document.getElementById('sidebartoggle').offsetLeft)-4)+'px');
+	width: expression((parseInt(this.parentNode.offsetWidth)-parseInt(document.getElementById('calendarsidebartoggle').offsetWidth)-parseInt(document.getElementById('calendarsidebartoggle').offsetLeft)-4)+'px');
 	height: expression((parseInt(this.parentNode.offsetHeight)-30)+'px');
 }
 
 #calendars {
-	height: expression((parseInt(this.parentNode.offsetHeight)-280)+'px');
+	height: expression((parseInt(this.parentNode.offsetHeight)-240)+'px');
 }
 
 #agendaoptions {


commit b21abe6618b226f640fa12bd28f615f725ca4af1
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Feb 28 15:04:03 2013 +0100

    Fix IE css hacks for Larry and Kolab skin

diff --git a/plugins/calendar/skins/larry/iehacks.css b/plugins/calendar/skins/larry/iehacks.css
index 0993754..529b029 100644
--- a/plugins/calendar/skins/larry/iehacks.css
+++ b/plugins/calendar/skins/larry/iehacks.css
@@ -1,10 +1,5 @@
 /* CSS hacks for IE 7 */
 
-#main {
-	width: expression(Math.max(300, parseInt(document.documentElement.clientWidth)-10)+'px');
-	height: expression(Math.max(300, parseInt(document.documentElement.clientHeight)-100)+'px');
-}
-
 #sidebar,
 #sidebartoggle {
 	height: expression((parseInt(this.parentNode.offsetHeight)-37)+'px');
@@ -12,11 +7,11 @@
 
 #calendar {
 	width: expression((parseInt(this.parentNode.offsetWidth)-parseInt(document.getElementById('sidebartoggle').offsetWidth)-parseInt(document.getElementById('sidebartoggle').offsetLeft)-4)+'px');
-	height: expression(parseInt(this.parentNode.offsetHeight)+'px');
+	height: expression((parseInt(this.parentNode.offsetHeight)-30)+'px');
 }
 
 #calendars {
-	height: expression((parseInt(this.parentNode.offsetHeight)-220)+'px');
+	height: expression((parseInt(this.parentNode.offsetHeight)-280)+'px');
 }
 
 #agendaoptions {
@@ -31,19 +26,36 @@
 	filter: alpha(opacity=40);
 }
 
-#datepicker .ui-widget-header {
-	width: 102%;
+.calendarmain .fc-day-content {
+	cursor: default;
+}
+
+.calendarmain .fc-view-table col.fc-event-date {
+	width: 8em;
 }
 
-.fc-day-content {
-	cursor: default;
+.calendarmain .fc-view-table col.fc-event-time {
+	width: 9em;
 }
 
-.fc-header-title h2 {
+.calendarmain .fc-header-title h2 {
 	font-size: 16px;
 }
 
-.fc-event-temp .fc-event-bg {
+.calendarmain .fc-header-left {
+	width: 248px;
+}
+
+.calendarmain .fc-header-center {
+	width: auto;
+}
+
+.calendarmain .fc-header-right {
+	width: 144px;
+	white-space: nowrap;
+}
+
+.calendarmain .fc-event-temp .fc-event-bg {
 	display: none; /* nested opacity filters while dragging don't work */
 }
 


commit 481ef522132ab1abdfcaa7010a0066721521b7ac
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Feb 28 14:20:41 2013 +0100

    Fix javascript syntax to work on IE7

diff --git a/plugins/kolab_activesync/kolab_activesync.js b/plugins/kolab_activesync/kolab_activesync.js
index c28a877..c5fb6d1 100644
--- a/plugins/kolab_activesync/kolab_activesync.js
+++ b/plugins/kolab_activesync/kolab_activesync.js
@@ -108,7 +108,7 @@ function kolab_activesync_config()
     var data = {
       cmd: 'save',
       id: rcmail.env.active_device,
-      devicealias: $('#config-device-alias').val(),
+      devicealias: $('#config-device-alias').val()
 //      syncmode: $('#config-device-mode option:selected').val(),
 //      laxpic: $('#config-device-laxpic').get(0).checked ? 1 : 0
     };
@@ -137,7 +137,7 @@ function kolab_activesync_config()
       parent.window.activesync_object.update_list(p.id, p.alias);
 
     // device deleted
-    if (p.success && p.id && p.delete) {
+    if (p.success && p.id && p['delete']) {
       active_device = null;
       device_select();
       devicelist.remove_row(p.id);


commit 5dc65cbd7ed2aab58eeefb161a6f35de1b346d60
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Feb 28 14:19:58 2013 +0100

    Fix background image in calendars listing

diff --git a/plugins/calendar/skins/classic/iehacks.css b/plugins/calendar/skins/classic/iehacks.css
index 831266f..2b6d6be 100644
--- a/plugins/calendar/skins/classic/iehacks.css
+++ b/plugins/calendar/skins/classic/iehacks.css
@@ -36,7 +36,7 @@ html #calendartoolbar a.buttonPas {
 	filter: alpha(opacity=40);
 }
 
-#calendarslist li span {
+#calendarslist li span.handle {
 	background-image: url(images/calendars.gif);
 }
 


commit ee8e315df086ccb5f340a2b76b78fbf9eb955dfd
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Feb 27 19:48:01 2013 +0100

    Fix placeholder in French translations

diff --git a/plugins/calendar/localization/fr_FR.inc b/plugins/calendar/localization/fr_FR.inc
index dfe9c59..cb4e3f8 100644
--- a/plugins/calendar/localization/fr_FR.inc
+++ b/plugins/calendar/localization/fr_FR.inc
@@ -123,7 +123,7 @@ $labels['nextslot'] = 'Créneau suivant';
 $labels['noslotfound'] = 'Impossible de trouver un créneau disponible';
 $labels['invitationsubject'] = 'Vous avez invité à "$title"';
 $labels['invitationmailbody'] = "*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees\n\nVous trouverez ci-joint un fichier iCalendar avec tous les détails de l'évènement que vous pourrez importer dans votre agenda électronique.";
-$labels['invitationattendlinks'] = "Dans le cas où votre application de messagerie ne gère pas les demandes \"iTip\". Vous pouvez utiliser ce lien pour accepter ou refuser l'invitation : n$url";
+$labels['invitationattendlinks'] = "Dans le cas où votre application de messagerie ne gère pas les demandes \"iTip\". Vous pouvez utiliser ce lien pour accepter ou refuser l'invitation :\n\$url";
 $labels['eventupdatesubject'] = '"$title" a été modifié';
 $labels['eventupdatesubjectempty'] = 'Un évènement vous concernant a été modifié';
 $labels['eventupdatemailbody'] = "*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees\n\nVous trouverez ci-joint un fichier iCalendar avec tous les modifications de l'évènement que vous pourrez importer dans votre agenda électronique.";


commit b111a369e3485b751fa8a2c010f1a358210d62e7
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Feb 27 17:05:10 2013 +0100

    Expose URI attachments as 'links' for event objects

diff --git a/plugins/libkolab/lib/kolab_format_event.php b/plugins/libkolab/lib/kolab_format_event.php
index 489d16e..bb2b3b7 100644
--- a/plugins/libkolab/lib/kolab_format_event.php
+++ b/plugins/libkolab/lib/kolab_format_event.php
@@ -86,6 +86,13 @@ class kolab_format_event extends kolab_format_xcal
             $attach->setUri('cid:' . $cid, $attr['mimetype']);
             $vattach->push($attach);
         }
+
+        foreach ((array)$object['links'] as $link) {
+            $attach = new Attachment;
+            $attach->setUri($link, null);
+            $vattach->push($attach);
+        }
+
         $this->obj->setAttachments($vattach);
 
         // cache this data
@@ -152,6 +159,9 @@ class kolab_format_event extends kolab_format_xcal
                     'content'  => $data,
                 );
             }
+            else if (substr($attach->uri(), 0, 4) == 'http') {
+                $object['links'][] = $attach->uri();
+            }
         }
 
         $this->data = $object;


commit c7164cbc22a3099d674218fec701c565eb98dc35
Author: Torsten Grote <t at grobox.de>
Date:   Wed Feb 27 15:33:16 2013 +0100

    added French translation for libcalendaring

diff --git a/plugins/libcalendaring/localization/fr_FR.inc b/plugins/libcalendaring/localization/fr_FR.inc
new file mode 100644
index 0000000..c33f29e
--- /dev/null
+++ b/plugins/libcalendaring/localization/fr_FR.inc
@@ -0,0 +1,30 @@
+<?php
+
+$labels = array();
+
+$labels['alarmemail'] = 'Envoyer un email';
+$labels['alarmdisplay'] = 'Voir le message';
+$labels['alarmdisplayoption'] = 'Message';
+$labels['alarmemailoption'] = 'Email';
+$labels['alarmat'] = 'à $datetime';
+$labels['trigger@'] = 'à la date';
+$labels['trigger-M'] = 'minutes avant';
+$labels['trigger-H'] = 'heures avant';
+$labels['trigger-D'] = 'jours avant';
+$labels['trigger+M'] = 'minutes après';
+$labels['trigger+H'] = 'heures après';
+$labels['trigger+D'] = 'jours après';
+$labels['addalarm'] = 'ajouter alarme';
+
+$labels['alarmtitle'] = 'Evénements à venir';
+$labels['dismissall'] = 'Tout supprimer';
+$labels['dismiss'] = 'Abandonner';
+$labels['snooze'] = 'En pause';
+$labels['repeatinmin'] = 'Répéter dans $min minutes';
+$labels['repeatinhr'] = 'Répéter dans 1 heure';
+$labels['repeatinhrs'] = 'Répéter dans $hrs heures';
+$labels['repeattomorrow'] = 'Répéter demain';
+$labels['repeatinweek'] = 'Répéter dans une semaine';
+
+$labels['showmore'] = 'Afficher plus...';
+


commit b13b5b5806d64936d19a48d37fe4da5bd3b8cc68
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Feb 27 12:16:27 2013 +0100

    Fix selecting tags from autocompletion (#1329)

diff --git a/plugins/tasklist/jquery.tagedit.js b/plugins/tasklist/jquery.tagedit.js
index 7c3a5e6..d70cb32 100755
--- a/plugins/tasklist/jquery.tagedit.js
+++ b/plugins/tasklist/jquery.tagedit.js
@@ -455,7 +455,7 @@
 				// If there is an entry for that already in the autocomplete, don't use it (Check could be case sensitive or not)
 				for (var i = 0; i < result.length; i++) {
 					var label = typeof result[i] == 'string' ? result[i] : result[i].label;
-					if (options.checkNewEntriesCaseSensitive == true)
+					if (options.checkNewEntriesCaseSensitive == false)
 						label = label.toLowerCase();
 					if (label == compareValue) {
 						isNew = false;
diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js
index daf157e..f3c26fd 100644
--- a/plugins/tasklist/tasklist.js
+++ b/plugins/tasklist/tasklist.js
@@ -1077,7 +1077,7 @@ function rcube_tasklist_ui(settings)
                 }
             }
 
-            $('input[name="tags[]"]', rcmail.gui_objects.edittagline).each(function(i,elem){
+            $('input[type="hidden"]', rcmail.gui_objects.edittagline).each(function(i,elem){
                 if (elem.value)
                     me.selected_task.tags.push(elem.value);
             });


commit cad6551c5d60556e458bfcd95a8dc8631c2c0356
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Feb 27 10:04:05 2013 +0100

    Fix computation of yearly recurring events using the Horde_Date_Recurrence class (database backend)

diff --git a/plugins/calendar/lib/calendar_recurrence.php b/plugins/calendar/lib/calendar_recurrence.php
index bf534f5..d4a3641 100644
--- a/plugins/calendar/lib/calendar_recurrence.php
+++ b/plugins/calendar/lib/calendar_recurrence.php
@@ -70,7 +70,13 @@ class calendar_recurrence
   public function next_start()
   {
     $time = false;
-    if ($this->next && ($next = $this->engine->nextActiveRecurrence(array('year' => $this->next->year, 'month' => $this->next->month, 'mday' => $this->next->mday + 1, 'hour' => $this->next->hour, 'min' => $this->next->min, 'sec' => $this->next->sec)))) {
+    $after = clone $this->next;
+    $after->mday = $after->mday + 1;
+    if ($this->next && ($next = $this->engine->nextActiveRecurrence($after))) {
+      if (!$next->after($this->next)) {
+        // avoid endless loops if recurrence computation fails
+        return false;
+      }
       if ($this->event['allday']) {
         $next->hour = $this->hour;  # fix time for all-day events
         $next->min = 0;


commit 2423d5ba31f614640c3af9e5b979d5f4b2226ba2
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Feb 27 09:49:07 2013 +0100

    Gracefully handle buggy messages with either missing or duplicated X-Kolab-Type headers

diff --git a/plugins/libkolab/lib/kolab_storage_folder.php b/plugins/libkolab/lib/kolab_storage_folder.php
index 2811886..b3fe9fe 100644
--- a/plugins/libkolab/lib/kolab_storage_folder.php
+++ b/plugins/libkolab/lib/kolab_storage_folder.php
@@ -458,12 +458,40 @@ class kolab_storage_folder
         $this->imap->set_folder($folder);
 
         $headers = $this->imap->get_message_headers($msguid);
+        $message = null;
 
         // Message doesn't exist?
         if (empty($headers)) {
             return false;
         }
 
+        // extract the X-Kolab-Type header from the XML attachment part if missing
+        if (empty($headers->others['x-kolab-type'])) {
+            $message = new rcube_message($msguid);
+            foreach ((array)$message->attachments as $part) {
+                if (strpos($part->mimetype, kolab_format::KTYPE_PREFIX) === 0) {
+                    $headers->others['x-kolab-type'] = $part->mimetype;
+                    break;
+                }
+            }
+        }
+        // fix buggy messages stating the X-Kolab-Type header twice
+        else if (is_array($headers->others['x-kolab-type'])) {
+            $headers->others['x-kolab-type'] = reset($headers->others['x-kolab-type']);
+        }
+
+        // no object type header found: abort
+        if (empty($headers->others['x-kolab-type'])) {
+            rcube::raise_error(array(
+                'code' => 600,
+                'type' => 'php',
+                'file' => __FILE__,
+                'line' => __LINE__,
+                'message' => "No X-Kolab-Type information found in message $msguid ($this->name).",
+            ), true);
+            return false;
+        }
+
         $object_type = kolab_format::mime2object_type($headers->others['x-kolab-type']);
         $content_type  = kolab_format::KTYPE_PREFIX . $object_type;
 
@@ -471,7 +499,7 @@ class kolab_storage_folder
         if ($type != '*' && $object_type != $type)
             return false;
 
-        $message = new rcube_message($msguid);
+        if (!$message) $message = new rcube_message($msguid);
         $attachments = array();
 
         // get XML part


commit 83dc7e097f852b132e6d646b9cdf923964a6fa49
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Feb 21 17:41:41 2013 +0100

    Support import/export of custom properties

diff --git a/plugins/calendar/lib/calendar_ical.php b/plugins/calendar/lib/calendar_ical.php
index 0fea74e..6d7474e 100644
--- a/plugins/calendar/lib/calendar_ical.php
+++ b/plugins/calendar/lib/calendar_ical.php
@@ -9,7 +9,7 @@
  * @author Bogomil "Bogo" Shopov <shopov at kolabsys.com>
  *
  * Copyright (C) 2010, Lazlo Westerhof <hello at lazlo.me>
- * Copyright (C) 2011, Kolab Systems AG <contact at kolabsys.com>
+ * Copyright (C) 2013, Kolab Systems AG <contact at kolabsys.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -258,6 +258,10 @@ class calendar_ical
           else if (in_array($attr['value'], array('FREE', 'BUSY', 'TENTATIVE')))
             $event['free_busy'] = strtolower($attr['value']);
           break;
+
+        default:
+          if (substr($attr['name'], 0, 2) == 'X-')
+            $event['x-custom'][] = array($attr['name'], $attr['value']);
       }
     }
 
@@ -416,6 +420,9 @@ class calendar_ical
         else if ($event['free_busy'] == 'tentative')
           $vevent .= "STATUS:TENTATIVE" . self::EOL;
         
+        foreach ((array)$event['x-custom'] as $prop)
+          $vevent .= $prop[0] . ':' . $this->escpape($prop[1]) . self::EOL;
+        
         // TODO: export attachments
         
         $vevent .= "END:VEVENT" . self::EOL;


commit 410bc481477c14fe396d58ece5a5fe5090dc97ea
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Feb 21 17:33:17 2013 +0100

    Fix typos and read STATUS:CANCELLED

diff --git a/plugins/calendar/lib/calendar_ical.php b/plugins/calendar/lib/calendar_ical.php
index cec2040..0fea74e 100644
--- a/plugins/calendar/lib/calendar_ical.php
+++ b/plugins/calendar/lib/calendar_ical.php
@@ -205,7 +205,9 @@ class calendar_ical
         
         case 'STATUS':
           if ($attr['value'] == 'TENTATIVE')
-            $event['free_busy'] == 'tentative';
+            $event['free_busy'] = 'tentative';
+          else if ($attr['value'] == 'CANCELLED')
+            $event['cancelled'] = true;
           break;
         
         case 'PRIORITY':


commit f286c67e02773617e55036b5dfcacde666a26857
Author: Aleksander Machniak <machniak at kolabsys.com>
Date:   Fri Jun 21 11:04:12 2013 +0200

    Small refactoring: use base class methods to read/set common properties; add support for custom properties in all objects
    
    Conflicts:
    
    	plugins/libkolab/lib/kolab_format.php

diff --git a/plugins/libkolab/lib/kolab_format.php b/plugins/libkolab/lib/kolab_format.php
index ccc7e46..cb77e18 100644
--- a/plugins/libkolab/lib/kolab_format.php
+++ b/plugins/libkolab/lib/kolab_format.php
@@ -381,19 +381,68 @@ abstract class kolab_format
      *
      * @param array  Object data as hash array
      */
-    abstract public function set(&$object);
+    public function set(&$object)
+    {
+        $this->init();
 
-    /**
-     *
-     */
-    abstract public function is_valid();
+        if (!empty($object['uid']))
+            $this->obj->setUid($object['uid']);
+
+        // set some automatic values if missing
+        if (method_exists($this->obj, 'setCreated') && !$this->obj->created()) {
+            if (empty($object['created']))
+                $object['created'] = new DateTime('now', self::$timezone);
+            $this->obj->setCreated(self::get_datetime($object['created']));
+        }
+
+        $object['changed'] = new DateTime('now', self::$timezone);
+        $this->obj->setLastModified(self::get_datetime($object['changed'], new DateTimeZone('UTC')));
+
+        // Save custom properties of the given object
+        if (!empty($object['x-custom'])) {
+            $vcustom = new vectorcs;
+            foreach ($object['x-custom'] as $cp) {
+                if (is_array($cp))
+                    $vcustom->push(new CustomProperty($cp[0], $cp[1]));
+            }
+            $this->obj->setCustomProperties($vcustom);
+        }
+    }
 
     /**
      * Convert the Kolab object into a hash array data structure
      *
      * @return array  Kolab object data as hash array
      */
-    abstract public function to_array();
+    public function to_array($data = array())
+    {
+        $this->init();
+
+        // read object properties into local data object
+        $object = array(
+            'uid'     => $this->obj->uid(),
+            'changed' => self::php_datetime($this->obj->lastModified()),
+        );
+
+        // not all container support the created property
+        if (method_exists($this->obj, 'created')) {
+            $object['created'] = self::php_datetime($this->obj->created());
+        }
+
+        // read custom properties
+        $vcustom = $this->obj->customProperties();
+        for ($i=0; $i < $vcustom->size(); $i++) {
+            $cp = $vcustom->get($i);
+            $object['x-custom'][] = array($cp->identifier, $cp->value);
+        }
+
+        return $object;
+    }
+
+    /**
+     * Object validation method to be implemented by derived classes
+     */
+    abstract public function is_valid();
 
     /**
      * Callback for kolab_storage_cache to get object specific tags to cache
diff --git a/plugins/libkolab/lib/kolab_format_contact.php b/plugins/libkolab/lib/kolab_format_contact.php
index 39e579c..d78a82d 100644
--- a/plugins/libkolab/lib/kolab_format_contact.php
+++ b/plugins/libkolab/lib/kolab_format_contact.php
@@ -85,20 +85,8 @@ class kolab_format_contact extends kolab_format
      */
     public function set(&$object)
     {
-        $this->init();
-
-        // set some automatic values if missing
-        if (false && !$this->obj->created()) {
-            if (!empty($object['created']))
-                $object['created'] = new DateTime('now', self::$timezone);
-            $this->obj->setCreated(self::get_datetime($object['created']));
-        }
-
-        if (!empty($object['uid']))
-            $this->obj->setUid($object['uid']);
-
-        $object['changed'] = new DateTime('now', self::$timezone);
-        $this->obj->setLastModified(self::get_datetime($object['changed'], new DateTimeZone('UTC')));
+        // set common object properties
+        parent::set($object);
 
         // do the hard work of setting object values
         $nc = new NameComponents;
@@ -274,14 +262,10 @@ class kolab_format_contact extends kolab_format
         if (!empty($this->data))
             return $this->data;
 
-        $this->init();
+        // read common object props into local data object
+        $object = parent::to_array();
 
-        // read object properties into local data object
-        $object = array(
-            'uid'       => $this->obj->uid(),
-            'name'      => $this->obj->name(),
-            'changed'   => self::php_datetime($this->obj->lastModified()),
-        );
+        $object['name'] = $this->obj->name();
 
         $nc = $this->obj->nameComponents();
         $object['surname']    = join(' ', self::vector2array($nc->surnames()));
diff --git a/plugins/libkolab/lib/kolab_format_distributionlist.php b/plugins/libkolab/lib/kolab_format_distributionlist.php
index 5622fc5..b966700 100644
--- a/plugins/libkolab/lib/kolab_format_distributionlist.php
+++ b/plugins/libkolab/lib/kolab_format_distributionlist.php
@@ -39,14 +39,8 @@ class kolab_format_distributionlist extends kolab_format
      */
     public function set(&$object)
     {
-        $this->init();
-
-        // set some automatic values if missing
-        if (!empty($object['uid']))
-            $this->obj->setUid($object['uid']);
-
-        $object['changed'] = new DateTime('now', self::$timezone);
-        $this->obj->setLastModified(self::get_datetime($object['changed'], new DateTimeZone('UTC')));
+        // set common object properties
+        parent::set($object);
 
         $this->obj->setName($object['name']);
 
@@ -91,12 +85,11 @@ class kolab_format_distributionlist extends kolab_format
         if (!empty($this->data))
             return $this->data;
 
-        $this->init();
+        // read common object props into local data object
+        $object = parent::to_array();
 
-        // read object properties
-        $object = array(
-            'uid'       => $this->obj->uid(),
-            'changed'   => self::php_datetime($this->obj->lastModified()),
+        // add object properties
+        $object += array(
             'name'      => $this->obj->name(),
             'member'    => array(),
             '_type'     => 'distribution-list',
diff --git a/plugins/libkolab/lib/kolab_format_event.php b/plugins/libkolab/lib/kolab_format_event.php
index 4ef6735..489d16e 100644
--- a/plugins/libkolab/lib/kolab_format_event.php
+++ b/plugins/libkolab/lib/kolab_format_event.php
@@ -61,8 +61,6 @@ class kolab_format_event extends kolab_format_xcal
      */
     public function set(&$object)
     {
-        $this->init();
-
         // set common xcal properties
         parent::set($object);
 
@@ -114,8 +112,6 @@ class kolab_format_event extends kolab_format_xcal
         if (!empty($this->data))
             return $this->data;
 
-        $this->init();
-
         // read common xcal props
         $object = parent::to_array();
 
diff --git a/plugins/libkolab/lib/kolab_format_file.php b/plugins/libkolab/lib/kolab_format_file.php
index b3ab158..e73622a 100644
--- a/plugins/libkolab/lib/kolab_format_file.php
+++ b/plugins/libkolab/lib/kolab_format_file.php
@@ -49,14 +49,9 @@ class kolab_format_file extends kolab_format
      */
     public function set(&$object)
     {
-        $this->init();
+        // set common object properties
+        parent::set($object);
 
-        // set some automatic values if missing
-        if (!empty($object['uid']))
-            $this->obj->setUid($object['uid']);
-
-        $object['changed'] = new DateTime('now', self::$timezone);
-        $this->obj->setLastModified(self::get_datetime($object['changed'], new DateTimeZone('UTC')));
         $this->obj->setClassification($this->sensitivity_map[$object['sensitivity']]);
         $this->obj->setCategories(self::array2vector($object['categories']));
 
@@ -109,15 +104,13 @@ class kolab_format_file extends kolab_format
         if (!empty($this->data))
             return $this->data;
 
-        $this->init();
+        // read common object props into local data object
+        $object = parent::to_array();
 
         $sensitivity_map = array_flip($this->sensitivity_map);
 
         // read object properties
-        $object = array(
-            'uid'         => $this->obj->uid(),
-            'created'     => self::php_datetime($this->obj->created()),
-            'changed'     => self::php_datetime($this->obj->lastModified()),
+        $object += array(
             'sensitivity' => $sensitivity_map[$this->obj->classification()],
             'categories'  => self::vector2array($this->obj->categories()),
             'notes'       => $this->obj->note(),
diff --git a/plugins/libkolab/lib/kolab_format_journal.php b/plugins/libkolab/lib/kolab_format_journal.php
index daab1ac..7e02244 100644
--- a/plugins/libkolab/lib/kolab_format_journal.php
+++ b/plugins/libkolab/lib/kolab_format_journal.php
@@ -39,14 +39,8 @@ class kolab_format_journal extends kolab_format
      */
     public function set(&$object)
     {
-        $this->init();
-
-        // set some automatic values if missing
-        if (!empty($object['uid']))
-            $this->obj->setUid($object['uid']);
-
-        $object['changed'] = new DateTime('now', self::$timezone);
-        $this->obj->setLastModified(self::get_datetime($object['changed'], new DateTimeZone('UTC')));
+        // set common object properties
+        parent::set($object);
 
         // TODO: set object propeties
 
@@ -74,14 +68,8 @@ class kolab_format_journal extends kolab_format
         if (!empty($this->data))
             return $this->data;
 
-        $this->init();
-
-        // read object properties
-        $object = array(
-            'uid'     => $this->obj->uid(),
-            'created' => self::php_datetime($this->obj->created()),
-            'changed' => self::php_datetime($this->obj->lastModified()),
-        );
+        // read common object props into local data object
+        $object = parent::to_array();
 
 
         // TODO: read object properties
diff --git a/plugins/libkolab/lib/kolab_format_note.php b/plugins/libkolab/lib/kolab_format_note.php
index 5af9343..849c61a 100644
--- a/plugins/libkolab/lib/kolab_format_note.php
+++ b/plugins/libkolab/lib/kolab_format_note.php
@@ -39,14 +39,8 @@ class kolab_format_note extends kolab_format
      */
     public function set(&$object)
     {
-        $this->init();
-
-        // set some automatic values if missing
-        if (!empty($object['uid']))
-            $this->obj->setUid($object['uid']);
-
-        $object['changed'] = new DateTime('now', self::$timezone);
-        $this->obj->setLastModified(self::get_datetime($object['changed'], new DateTimeZone('UTC')));
+        // set common object properties
+        parent::set($object);
 
         // TODO: set object propeties
 
@@ -74,14 +68,8 @@ class kolab_format_note extends kolab_format
         if (!empty($this->data))
             return $this->data;
 
-        $this->init();
-
-        // read object properties
-        $object = array(
-            'uid'       => $this->obj->uid(),
-            'created'   => self::php_datetime($this->obj->created()),
-            'changed'   => self::php_datetime($this->obj->lastModified()),
-        );
+        // read common object props into local data object
+        $object = parent::to_array();
 
 
         // TODO: read object properties
diff --git a/plugins/libkolab/lib/kolab_format_task.php b/plugins/libkolab/lib/kolab_format_task.php
index 77870c7..e44eebf 100644
--- a/plugins/libkolab/lib/kolab_format_task.php
+++ b/plugins/libkolab/lib/kolab_format_task.php
@@ -38,8 +38,6 @@ class kolab_format_task extends kolab_format_xcal
      */
     public function set(&$object)
     {
-        $this->init();
-
         // set common xcal properties
         parent::set($object);
 
@@ -79,8 +77,6 @@ class kolab_format_task extends kolab_format_xcal
         if (!empty($this->data))
             return $this->data;
 
-        $this->init();
-
         // read common xcal props
         $object = parent::to_array();
 
diff --git a/plugins/libkolab/lib/kolab_format_xcal.php b/plugins/libkolab/lib/kolab_format_xcal.php
index 3e1a721..6dcd328 100644
--- a/plugins/libkolab/lib/kolab_format_xcal.php
+++ b/plugins/libkolab/lib/kolab_format_xcal.php
@@ -92,13 +92,13 @@ abstract class kolab_format_xcal extends kolab_format
      */
     public function to_array()
     {
+        // read common object props
+        $object = parent::to_array();
+
         $status_map = array_flip($this->status_map);
         $sensitivity_map = array_flip($this->sensitivity_map);
 
-        $object = array(
-            'uid'         => $this->obj->uid(),
-            'created'     => self::php_datetime($this->obj->created()),
-            'changed'     => self::php_datetime($this->obj->lastModified()),
+        $object += array(
             'sequence'    => intval($this->obj->sequence()),
             'title'       => $this->obj->summary(),
             'location'    => $this->obj->location(),
@@ -214,20 +214,12 @@ abstract class kolab_format_xcal extends kolab_format
      */
     public function set(&$object)
     {
-        $is_new = !$this->obj->uid();
+        $this->init();
 
-        // set some automatic values if missing
-        if (!$this->obj->created()) {
-            if (!empty($object['created']))
-                $object['created'] = new DateTime('now', self::$timezone);
-            $this->obj->setCreated(self::get_datetime($object['created']));
-        }
-
-        if (!empty($object['uid']))
-            $this->obj->setUid($object['uid']);
+        $is_new = !$this->obj->uid();
 
-        $object['changed'] = new DateTime('now', self::$timezone);
-        $this->obj->setLastModified(self::get_datetime($object['changed'], new DateTimeZone('UTC')));
+        // set common object properties
+        parent::set($object);
 
         // increment sequence on updates
         $object['sequence'] = !$is_new ? $this->obj->sequence()+1 : 0;





More information about the commits mailing list