diff --git a/.tx/config b/.tx/config index 54caf13a..e3eee2ea 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/calendar_ui.js b/plugins/calendar/calendar_ui.js index c80b8941..40997764 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) @@ -534,7 +535,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/database/database_driver.php b/plugins/calendar/drivers/database/database_driver.php index 958e8ca9..7669350d 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 439bda7c..12d60c28 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; @@ -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 1bd8a977..025f655f 100644 --- a/plugins/calendar/localization/de_CH.inc +++ b/plugins/calendar/localization/de_CH.inc @@ -183,7 +183,7 @@ $labels['attendeupdateesuccess'] = 'Teilnehmerstatus erfolgreich aktualisiert'; $labels['itipsendsuccess'] = 'Einladung an Teilnehmer versendet.'; $labels['itipresponseerror'] = 'Die Antwort auf diese Einladung konnte nicht versendet werden'; $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 ea1d0a74..8d0f2bb4 100644 --- a/plugins/calendar/localization/de_DE.inc +++ b/plugins/calendar/localization/de_DE.inc @@ -183,7 +183,7 @@ $labels['attendeupdateesuccess'] = 'Teilnehmerstatus erfolgreich aktualisiert'; $labels['itipsendsuccess'] = 'Einladung an Teilnehmer versendet.'; $labels['itipresponseerror'] = 'Die Antwort auf diese Einladung konnte nicht versendet werden'; $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.inc b/plugins/calendar/localization/en.inc deleted file mode 100644 index 6cc76d7a..00000000 --- a/plugins/calendar/localization/en.inc +++ /dev/null @@ -1,230 +0,0 @@ - diff --git a/plugins/calendar/localization/en_US.inc b/plugins/calendar/localization/en_US.inc index 6cc76d7a..4164d892 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'; diff --git a/plugins/kolab_activesync/localization/en.inc b/plugins/kolab_activesync/localization/en.inc deleted file mode 100644 index 2399b13e..00000000 --- a/plugins/kolab_activesync/localization/en.inc +++ /dev/null @@ -1,33 +0,0 @@ -
In order to register a device, please connect it to the server first, using the instructions in the Wiki. 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 a66426f4..00000000 --- a/plugins/kolab_addressbook/localization/en.inc +++ /dev/null @@ -1,47 +0,0 @@ - diff --git a/plugins/kolab_auth/localization/en.inc b/plugins/kolab_auth/localization/en.inc deleted file mode 100644 index e1adb3f2..00000000 --- a/plugins/kolab_auth/localization/en.inc +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/plugins/kolab_delegation/localization/en.inc b/plugins/kolab_delegation/localization/en.inc deleted file mode 100644 index c445f7f9..00000000 --- a/plugins/kolab_delegation/localization/en.inc +++ /dev/null @@ -1,30 +0,0 @@ - diff --git a/plugins/kolab_folders/localization/en.inc b/plugins/kolab_folders/localization/en.inc deleted file mode 100644 index 856f59d5..00000000 --- a/plugins/kolab_folders/localization/en.inc +++ /dev/null @@ -1,26 +0,0 @@ - diff --git a/plugins/libkolab/lib/kolab_format_event.php b/plugins/libkolab/lib/kolab_format_event.php index 2b3f0c8f..ec97767c 100644 --- a/plugins/libkolab/lib/kolab_format_event.php +++ b/plugins/libkolab/lib/kolab_format_event.php @@ -192,7 +192,7 @@ class kolab_format_event extends kolab_format_xcal } // read exception event objects - if (($exceptions = $this->obj->exceptions()) && $exceptions->size()) { + if (($exceptions = $this->obj->exceptions()) && is_object($exceptions) && $exceptions->size()) { for ($i=0; $i < $exceptions->size(); $i++) { if (($exobj = $exceptions->get($i))) { $exception = new kolab_format_event($exobj); diff --git a/plugins/libkolab/lib/kolab_storage_folder.php b/plugins/libkolab/lib/kolab_storage_folder.php index 7e10f2e6..dd0e8d2d 100644 --- a/plugins/libkolab/lib/kolab_storage_folder.php +++ b/plugins/libkolab/lib/kolab_storage_folder.php @@ -548,9 +548,9 @@ class kolab_storage_folder // detect old Kolab 2.0 format if (strpos($xmlhead, '<' . $xmltype) !== false && strpos($xmlhead, 'xmlns=') === false) - $format_version = 2.0; + $format_version = '2.0'; else - $format_version = 3.0; // assume 3.0 + $format_version = '3.0'; // assume 3.0 } // get Kolab format handler for the given type @@ -588,7 +588,6 @@ class kolab_storage_folder return false; } - /** * Save an object in this folder. * @@ -659,6 +658,11 @@ class kolab_storage_folder } } + // save recurrence exceptions as individual objects due to lack of support in Kolab v2 format + if (kolab_storage::$version == '2.0' && $object['recurrence']['EXCEPTIONS']) { + $this->save_recurrence_exceptions($object, $type); + } + // check IMAP BINARY extension support for 'file' objects // allow configuration to workaround bug in Cyrus < 2.4.17 $rcmail = rcube::get_instance(); @@ -666,6 +670,12 @@ class kolab_storage_folder // generate and save object message if ($raw_msg = $this->build_message($object, $type, $binary)) { + // resolve old msguid before saving + if ($uid && empty($object['_msguid']) && ($msguid = $this->cache->uid2msguid($uid))) { + $object['_msguid'] = $msguid; + $object['_mailbox'] = $this->name; + } + if (is_array($raw_msg)) { $result = $this->imap->save_message($this->name, $raw_msg[0], $raw_msg[1], true, null, null, $binary); @unlink($raw_msg[0]); @@ -679,10 +689,6 @@ class kolab_storage_folder $this->imap->delete_message($object['_msguid'], $object['_mailbox']); $this->cache->set($object['_msguid'], false, $object['_mailbox']); } - else if ($result && $uid && ($msguid = $this->cache->uid2msguid($uid))) { - $this->imap->delete_message($msguid, $this->name); - $this->cache->set($object['_msguid'], false); - } // update cache with new UID if ($result) { @@ -694,6 +700,68 @@ class kolab_storage_folder return $result; } + /** + * Save recurrence exceptions as individual objects. + * The Kolab v2 format doesn't allow us to save fully embedded exception objects. + * + * @param array Hash array with event properties + * @param string Object type + */ + private function save_recurrence_exceptions(&$object, $type = null) + { + if ($object['recurrence']['EXCEPTIONS']) { + $exdates = array(); + foreach ((array)$object['recurrence']['EXDATE'] as $exdate) { + $key = is_a($exdate, 'DateTime') ? $exdate->format('Y-m-d') : strval($exdate); + $exdates[$key] = 1; + } + + // save every exception as individual object + foreach((array)$object['recurrence']['EXCEPTIONS'] as $exception) { + $exception['uid'] = self::recurrence_exception_uid($object['uid'], $exception['start']->format('Ymd')); + $exception['sequence'] = $object['sequence'] + 1; + + if ($exception['thisandfuture']) { + $exception['recurrence'] = $object['recurrence']; + + // adjust the recurrence duration of the exception + if ($object['recurrence']['COUNT']) { + $recurrence = new kolab_date_recurrence($object['_formatobj']); + if ($end = $recurrence->end()) { + unset($exception['recurrence']['COUNT']); + $exception['recurrence']['UNTIL'] = new DateTime('@'.$end); + } + } + + // set UNTIL date if we have a thisandfuture exception + $untildate = clone $exception['start']; + $untildate->sub(new DateInterval('P1D')); + $object['recurrence']['UNTIL'] = $untildate; + unset($object['recurrence']['COUNT']); + } + else { + if (!$exdates[$exception['start']->format('Y-m-d')]) + $object['recurrence']['EXDATE'][] = clone $exception['start']; + unset($exception['recurrence']); + } + + unset($exception['recurrence']['EXCEPTIONS'], $exception['_formatobj'], $exception['_msguid']); + $this->save($exception, $type, $exception['uid']); + } + + unset($object['recurrence']['EXCEPTIONS']); + } + } + + /** + * Generate an object UID with the given recurrence-ID in a way that it is + * unique (the original UID is not a substring) but still recoverable. + */ + private static function recurrence_exception_uid($uid, $recurrence_id) + { + $offset = -2; + return substr($uid, 0, $offset) . '-' . $recurrence_id . '-' . substr($uid, $offset); + } /** * Delete the specified object from this folder. diff --git a/plugins/owncloud/localization/en.inc b/plugins/owncloud/localization/en.inc deleted file mode 100644 index f5cb8fb8..00000000 --- a/plugins/owncloud/localization/en.inc +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/plugins/tasklist/localization/de_CH.inc b/plugins/tasklist/localization/de_CH.inc index 4fbb9768..4a6b3263 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 e8c0ada7..1cbd2c0d 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.inc b/plugins/tasklist/localization/en.inc deleted file mode 100644 index 7d5415ab..00000000 --- a/plugins/tasklist/localization/en.inc +++ /dev/null @@ -1,68 +0,0 @@ - -
- - - -
+
+ + + +
diff --git a/plugins/tasklist/skins/larry/templates/taskedit.html b/plugins/tasklist/skins/larry/templates/taskedit.html index f67d20ae..1c9aa4ed 100644 --- a/plugins/tasklist/skins/larry/templates/taskedit.html +++ b/plugins/tasklist/skins/larry/templates/taskedit.html @@ -19,18 +19,18 @@
-
- -   - - -
 
+
+ +   + + +