From 7d6e1fd78742d01ffbc360a9164a2d0ccae5489a Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 7 May 2019 10:24:46 +0200 Subject: [PATCH 01/14] Calendar: Fix broken UI when using unsupported (old) view type 'table' (Bifrost#T206587) The default view could have been stored in user preferences. After fullCalendar upgrade we switched to 'list' view instead of 'table'. --- plugins/calendar/calendar.php | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index 1e590c19..f24a81ae 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -1764,20 +1764,25 @@ class calendar extends rcube_plugin $settings = array(); // configuration + $settings['default_view'] = (string) $this->rc->config->get('calendar_default_view', $this->defaults['calendar_default_view']); + $settings['timeslots'] = (int) $this->rc->config->get('calendar_timeslots', $this->defaults['calendar_timeslots']); + $settings['first_day'] = (int) $this->rc->config->get('calendar_first_day', $this->defaults['calendar_first_day']); + $settings['first_hour'] = (int) $this->rc->config->get('calendar_first_hour', $this->defaults['calendar_first_hour']); + $settings['work_start'] = (int) $this->rc->config->get('calendar_work_start', $this->defaults['calendar_work_start']); + $settings['work_end'] = (int) $this->rc->config->get('calendar_work_end', $this->defaults['calendar_work_end']); + $settings['agenda_range'] = (int) $this->rc->config->get('calendar_agenda_range', $this->defaults['calendar_agenda_range']); + $settings['event_coloring'] = (int) $this->rc->config->get('calendar_event_coloring', $this->defaults['calendar_event_coloring']); + $settings['time_indicator'] = (int) $this->rc->config->get('calendar_time_indicator', $this->defaults['calendar_time_indicator']); + $settings['invite_shared'] = (int) $this->rc->config->get('calendar_allow_invite_shared', $this->defaults['calendar_allow_invite_shared']); + $settings['itip_notify'] = (int) $this->rc->config->get('calendar_itip_send_option', $this->defaults['calendar_itip_send_option']); + $settings['show_weekno'] = (int) $this->rc->config->get('calendar_show_weekno', $this->defaults['calendar_show_weekno']); $settings['default_calendar'] = $this->rc->config->get('calendar_default_calendar'); - $settings['default_view'] = (string)$this->rc->config->get('calendar_default_view', $this->defaults['calendar_default_view']); - $settings['timeslots'] = (int)$this->rc->config->get('calendar_timeslots', $this->defaults['calendar_timeslots']); - $settings['first_day'] = (int)$this->rc->config->get('calendar_first_day', $this->defaults['calendar_first_day']); - $settings['first_hour'] = (int)$this->rc->config->get('calendar_first_hour', $this->defaults['calendar_first_hour']); - $settings['work_start'] = (int)$this->rc->config->get('calendar_work_start', $this->defaults['calendar_work_start']); - $settings['work_end'] = (int)$this->rc->config->get('calendar_work_end', $this->defaults['calendar_work_end']); - $settings['agenda_range'] = (int)$this->rc->config->get('calendar_agenda_range', $this->defaults['calendar_agenda_range']); - $settings['event_coloring'] = (int)$this->rc->config->get('calendar_event_coloring', $this->defaults['calendar_event_coloring']); - $settings['time_indicator'] = (int)$this->rc->config->get('calendar_time_indicator', $this->defaults['calendar_time_indicator']); - $settings['invite_shared'] = (int)$this->rc->config->get('calendar_allow_invite_shared', $this->defaults['calendar_allow_invite_shared']); - $settings['invitation_calendars'] = (bool)$this->rc->config->get('kolab_invitation_calendars', false); - $settings['itip_notify'] = (int)$this->rc->config->get('calendar_itip_send_option', $this->defaults['calendar_itip_send_option']); - $settings['show_weekno'] = (int)$this->rc->config->get('calendar_show_weekno', $this->defaults['calendar_show_weekno']); + $settings['invitation_calendars'] = (bool) $this->rc->config->get('kolab_invitation_calendars', false); + + // 'table' view has been replaced by 'list' view + if ($settings['default_view'] == 'table') { + $settings['default_view'] = 'list'; + } // get user identity to create default attendee if ($this->ui->screen == 'calendar') { From f847df8e50b83142f6c0c3dc70e70654d8bd76fc Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 14 May 2019 09:49:20 +0200 Subject: [PATCH 02/14] Fix broken resource details when kolabDescAttribute contains non-JSON-formatted value (Bifrost#T209152) We'll display such string as a resource description (if not specified otherwise). --- .../drivers/ldap/resources_driver_ldap.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/plugins/calendar/drivers/ldap/resources_driver_ldap.php b/plugins/calendar/drivers/ldap/resources_driver_ldap.php index c377393d..bd2d610c 100644 --- a/plugins/calendar/drivers/ldap/resources_driver_ldap.php +++ b/plugins/calendar/drivers/ldap/resources_driver_ldap.php @@ -116,17 +116,21 @@ class resources_driver_ldap extends resources_driver { $rec['ID'] = rcube_ldap::dn_decode($rec['ID']); - if (is_array($rec['attributes']) && $rec['attributes'][0]) { - $attributes = array(); + $attributes = array(); - foreach ($rec['attributes'] as $sattr) { + foreach ((array) $rec['attributes'] as $sattr) { + $sattr = trim($sattr); + if ($sattr && $sattr[0] === '{') { $attr = @json_decode($sattr, true); $attributes += $attr; } - - $rec['attributes'] = $attributes; + else if ($sattr && empty($rec['description'])) { + $rec['description'] = $sattr; + } } + $rec['attributes'] = $attributes; + // force $rec['members'] to be an array if (!empty($rec['members']) && !is_array($rec['members'])) { $rec['members'] = array($rec['members']); From f848609ea1e4ccbf29ef377698fb1fc84ee81c20 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 14 May 2019 11:04:27 +0200 Subject: [PATCH 03/14] Elastic: Improve event/task reminders (alarms) input --- .../skins/elastic/templates/eventedit.html | 8 +-- plugins/libcalendaring/libcalendaring.php | 2 +- .../skins/elastic/include/calendar.less | 4 ++ .../skins/elastic/include/libcalendaring.less | 54 +++++++++---------- .../skins/elastic/templates/taskedit.html | 8 +-- 5 files changed, 39 insertions(+), 37 deletions(-) diff --git a/plugins/calendar/skins/elastic/templates/eventedit.html b/plugins/calendar/skins/elastic/templates/eventedit.html index d0c97e16..68b5f901 100644 --- a/plugins/calendar/skins/elastic/templates/eventedit.html +++ b/plugins/calendar/skins/elastic/templates/eventedit.html @@ -33,11 +33,11 @@
-
+
- - - + + +
diff --git a/plugins/libcalendaring/libcalendaring.php b/plugins/libcalendaring/libcalendaring.php index ff795fb3..6c33aba1 100644 --- a/plugins/libcalendaring/libcalendaring.php +++ b/plugins/libcalendaring/libcalendaring.php @@ -344,7 +344,7 @@ class libcalendaring extends rcube_plugin { unset($attrib['name']); - $input_value = new html_inputfield(array('name' => 'alarmvalue[]', 'class' => 'edit-alarm-value form-control input-group-prepend', 'size' => 3)); + $input_value = new html_inputfield(array('name' => 'alarmvalue[]', 'class' => 'edit-alarm-value form-control', 'size' => 3)); $input_date = new html_inputfield(array('name' => 'alarmdate[]', 'class' => 'edit-alarm-date form-control', 'size' => 10)); $input_time = new html_inputfield(array('name' => 'alarmtime[]', 'class' => 'edit-alarm-time form-control', 'size' => 6)); $select_type = new html_select(array('name' => 'alarmtype[]', 'class' => 'edit-alarm-type form-control', 'id' => $attrib['id'])); diff --git a/plugins/libkolab/skins/elastic/include/calendar.less b/plugins/libkolab/skins/elastic/include/calendar.less index 2171e659..fe1d61f0 100644 --- a/plugins/libkolab/skins/elastic/include/calendar.less +++ b/plugins/libkolab/skins/elastic/include/calendar.less @@ -1255,6 +1255,10 @@ body.task-calendar { } } +#rcmfd_alarmvalue { + max-width: 80px; +} + @media screen and (max-width: @screen-width-small) { #agendaoptions { padding-top: .5rem; diff --git a/plugins/libkolab/skins/elastic/include/libcalendaring.less b/plugins/libkolab/skins/elastic/include/libcalendaring.less index 3b0ef9c4..d0fff594 100644 --- a/plugins/libkolab/skins/elastic/include/libcalendaring.less +++ b/plugins/libkolab/skins/elastic/include/libcalendaring.less @@ -389,18 +389,30 @@ & + .edit-alarm-item { margin-top: .25rem; } + + &.first .delete-alarm, + &:not(.first) .add-alarm { + display: none; + } } .edit-alarm-buttons { a { - line-height: 1; - padding: .375em .25em; - margin: 0 .25rem; + border-top-right-radius: .25rem !important; + border-bottom-right-radius: .25rem !important; &:before { margin: 0; width: 1em; } + + &:focus { + box-shadow: none; + } + + &.add:before { + content: @fa-var-plus; + } } .inner { @@ -408,16 +420,14 @@ } } - .edit-alarm-item.first .delete-alarm { - display: none; - } - - .edit-alarm-item:not(.first) .add-alarm { - display: none; - } - .edit-alarm-type { flex: 1; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + + &:focus { + z-index: 2; + } } .edit-alarm-set { @@ -426,26 +436,14 @@ } .edit-alarm-values { - margin-left: .25rem; flex: 5; - &.offset-default { - select.edit-alarm-related { - border-radius: 0 .25rem .25rem 0; - } + .form-control { + border-radius: 0; } - &.offset-ontime { - select.edit-alarm-offset { - border-radius: .25rem 0 0 .25rem; - } - select.edit-alarm-related { - border-radius: 0 .25rem .25rem 0; - } - } - &.offset-ondate { - select.edit-alarm-offset { - border-radius: .25rem 0 0 .25rem; - } + + :first-child { + border-left: 0; } } diff --git a/plugins/tasklist/skins/elastic/templates/taskedit.html b/plugins/tasklist/skins/elastic/templates/taskedit.html index 19699082..d36b30e5 100644 --- a/plugins/tasklist/skins/elastic/templates/taskedit.html +++ b/plugins/tasklist/skins/elastic/templates/taskedit.html @@ -34,11 +34,11 @@
-
+
- - - + + +
From 985d88fed2b8ede49d90e2f5b877e50bc6664152 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 21 May 2019 11:45:37 +0200 Subject: [PATCH 04/14] Tasks: Fix folder edit/create issues - fix setting unwanted and invalid folder color annotation - fix saving "Show reminders" setting - fix javascript error after editing a folder --- plugins/tasklist/tasklist.js | 4 +--- plugins/tasklist/tasklist_ui.php | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js index 2e14256f..d93c6992 100644 --- a/plugins/tasklist/tasklist.js +++ b/plugins/tasklist/tasklist.js @@ -3050,8 +3050,6 @@ function rcube_tasklist_ui(settings) data = form.serializeJSON(); if (list.id) data.id = list.id; - if (form.find('#taskedit-tasklistname').is(':checked')) - data.showalarms = 1; saving_lock = rcmail.set_busy(true, 'tasklist.savingdata'); rcmail.http_post('tasklist', {action: (list.id ? 'edit' : 'new'), l: data}); @@ -3172,7 +3170,7 @@ function rcube_tasklist_ui(settings) me.tasklists[prop.id] = prop; $(li).find('input').first().val(prop.id); $(li).find('.listname').first().html(Q(prop.name)); - tasklists_widget.update(id, { id:prop.id, html:li.children().first() }); + tasklists_widget.update(id, {id: prop.id, html: $(li).children().first()}); } } diff --git a/plugins/tasklist/tasklist_ui.php b/plugins/tasklist/tasklist_ui.php index fd999898..f4c262ec 100644 --- a/plugins/tasklist/tasklist_ui.php +++ b/plugins/tasklist/tasklist_ui.php @@ -362,7 +362,7 @@ class tasklist_ui 'showalarms' => array( 'id' => 'taskedit-showalarms', 'label' => $this->plugin->gettext('showalarms'), - 'value' => html::tag('input', array('id' => 'taskedit-showalarms', 'name' => 'color', 'type' => 'checkbox')), + 'value' => html::tag('input', array('id' => 'taskedit-showalarms', 'name' => 'showalarms', 'type' => 'checkbox', 'value' => 1)), ), ); From 5e63cef9d564794ac7797a0bdce26061fad2f0c2 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 22 May 2019 09:19:22 +0200 Subject: [PATCH 05/14] Remove old TODO file --- plugins/calendar/TODO | 48 ---------------------------- plugins/calendar/config.inc.php.dist | 2 +- 2 files changed, 1 insertion(+), 49 deletions(-) delete mode 100644 plugins/calendar/TODO diff --git a/plugins/calendar/TODO b/plugins/calendar/TODO deleted file mode 100644 index b1a08d7e..00000000 --- a/plugins/calendar/TODO +++ /dev/null @@ -1,48 +0,0 @@ -+ Edit: 3.12: Subject -+ Edit: 3.13: Location -+ Edit: 3.14: Start / End / All Day -+ Edit: 3.15: Show time as: Busy, Free, Out of office -+ Edit: 3.16: Reminder set -+ Edit: 3.17: Priority: High/Low -+ Edit: 3.18: Recurrence (in line with Kontact) -+ Edit: 3.19: Attachment Upload -+ Edit: 3.20: Print -+ Add/Manage Attendees - + Edit: 3.21: Required / Optional / Resource specification - + Edit: 3.22: Conflict Handling (Free/Busy Check for attendees) -+ View: 3.3: Display modes (agenda / day / week / month) - + Day / Week / Month - + List (Agenda) view - + Add selection for date range - - Individual days selection -+ Show list of calendars in a (hideable) drawer - + View: 3.1: Folder list - + View: 3.2: Add / Remove / Rename / Share Folders - + View: 3.6: Combined calendar view (Turn calendars on/off) - + View: 3.7: Small month overview calendar -+ View: 3.5: Search - - Filter by categories (similar to mail) -+ View: 3.9: Alter event with drag/drop -+ Option: 4.12: Set default reminder time -+ Option: 3.23: Specify folder for new event (prefs) -+ Option: Set date/time format in prefs -+ Receive: 1.20: Invitation handling - - Jump to calendar view from mail ("Show event") - - Allow to re-send invitations - - Implement iTIP delegation - -+ View: 3.4: Fish-Eye View For Busy Days -+ View: 3.8: Color according to calendar and category (similar to Kontact) - -+ Support for multiple calendars (replace categories) -+ Allow user to create/edit/delete calendars -+ Colors for calendars should be user-configurable -+ ICS parser/generator (http://code.google.com/p/qcal/) - -- Script to send event alarms by email (in cronjob) -- Export *with* attachments -- Remember last visited view -- Create/manage invdividual views -+ Importing ICS files (upload, drag & drop) - - diff --git a/plugins/calendar/config.inc.php.dist b/plugins/calendar/config.inc.php.dist index 6ffdd581..970feb75 100644 --- a/plugins/calendar/config.inc.php.dist +++ b/plugins/calendar/config.inc.php.dist @@ -25,7 +25,7 @@ +-------------------------------------------------------------------------+ */ -// backend type (database, google, kolab) +// backend type (database, kolab) $config['calendar_driver'] = "database"; // default calendar view (agendaDay, agendaWeek, month) From d42ca55bfc1e3ebb3502ad8d55e54bc9b80f28f7 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 22 May 2019 09:29:24 +0200 Subject: [PATCH 06/14] README update --- plugins/calendar/README | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/plugins/calendar/README b/plugins/calendar/README index 238dd380..78c84a96 100644 --- a/plugins/calendar/README +++ b/plugins/calendar/README @@ -6,14 +6,10 @@ server as backends for calendar and event storage. For both drivers, some initialization of the local database is necessary. To do so, execute the SQL commands in drivers//SQL/.initial.sql -The client-side calendar UI relies on the "fullcalendar" project by Adam Arshaw -with extensions made for the use in Roundcube. All changes are published in -an official fork at https://github.com/roundcube/fullcalendar - For some general calendar-based operations such as alarms handling or iCal -parsing/exporting this plugins requires the `libcalendaring` plugin which -is also part of the Kolab Roundcube Plugins repository. Make sure that plugin -is installed and configured correctly. +parsing/exporting and UI widgets/style this plugins requires the `libcalendaring` +and `libkolab` plugins which are also part of the Kolab Roundcube Plugins repository. +Make sure these plugins are installed and configured correctly. For recurring event computation, some utility classes from the Horde project are used. They are packaged in a slightly modified version with this plugin. @@ -27,7 +23,7 @@ library plugins. Thus in order to run the calendar plugin, you also need the following plugins installed: * libcalendaring [1] -* libkolab [1] (when using the 'kolab' driver) +* libkolab [1] INSTALLATION @@ -44,6 +40,7 @@ driver. $ cd //plugins $ cp -r /tmp/roundcubemail-plugins-kolab/plugins/calendar . $ cp -r /tmp/roundcubemail-plugins-kolab/plugins/libcalendaring . + $ cp -r /tmp/roundcubemail-plugins-kolab/plugins/libkolab . 2. Create calendar plugin configuration @@ -53,11 +50,15 @@ driver. 3. Initialize the calendar database tables - $ mysql roundcubemail < drivers/database/SQL/mysql.initial.sql - -4. Enable the calendar plugin - $ cd ../../ + $ bin/initdb.sh --dir=plugins/calendar/drivers/database/SQL + +4. Build css styles for the Elastic skin + + $ lessc --relative-urls -x plugins/libkolab/skins/elastic/libkolab.less > plugins/libkolab/skins/elastic/libkolab.min.css + +5. Enable the calendar plugin + $ edit config/config.inc.php Add 'calendar' to the list of active plugins: @@ -68,5 +69,4 @@ Add 'calendar' to the list of active plugins: ); - [1] https://git.kolab.org/diffusion/RPK/ From 915035e9bad5c339c5ca49efb9bd784ce0d34219 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Mon, 27 May 2019 15:12:14 +0200 Subject: [PATCH 07/14] Small code simplification --- plugins/calendar/calendar_ui.js | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js index 02807837..b0688c3f 100644 --- a/plugins/calendar/calendar_ui.js +++ b/plugins/calendar/calendar_ui.js @@ -3609,25 +3609,19 @@ function rcube_calendar_ui(settings) } var id = this.value; - if (me.calendars[id]) { // add or remove event source on click - var action; - if (this.checked) { - action = 'addEventSource'; - me.calendars[id].active = true; - } - else { - action = 'removeEventSource'; - me.calendars[id].active = false; - } + // add or remove event source on click + if (me.calendars[id]) { // adjust checked state of original list item if (calendars_list.is_search()) { calendars_list.container.find('input[value="'+id+'"]').prop('checked', this.checked); } + me.calendars[id].active = this.checked; + // add/remove event source - fc.fullCalendar(action, me.calendars[id]); - rcmail.http_post('calendar', { action:'subscribe', c:{ id:id, active:me.calendars[id].active?1:0 } }); + fc.fullCalendar(this.checked ? 'addEventSource' : 'removeEventSource', me.calendars[id]); + rcmail.http_post('calendar', {action: 'subscribe', c: {id: id, active: this.checked ? 1 : 0}}); } }) .on('keypress', 'input[type=checkbox]', function(e) { From de8e018c4a2a5b218810fe4b1b8d1d68b3ad727b Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 28 May 2019 13:54:00 +0200 Subject: [PATCH 08/14] Fix kolab_alarms table schema for MariaDB (Bifrost#T163148) Error: Specified key was too long; max key length is 767 bytes, is caused by missing charset specification for the table. Maria's default charset is utf8mb4 making the varchar(255) field too big for a key. --- plugins/calendar/drivers/kolab/SQL/mysql.initial.sql | 2 +- plugins/calendar/drivers/kolab/SQL/mysql/2012080600.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/calendar/drivers/kolab/SQL/mysql.initial.sql b/plugins/calendar/drivers/kolab/SQL/mysql.initial.sql index d5009616..f672fb74 100644 --- a/plugins/calendar/drivers/kolab/SQL/mysql.initial.sql +++ b/plugins/calendar/drivers/kolab/SQL/mysql.initial.sql @@ -14,7 +14,7 @@ CREATE TABLE IF NOT EXISTS `kolab_alarms` ( PRIMARY KEY(`alarm_id`,`user_id`), CONSTRAINT `fk_kolab_alarms_user_id` FOREIGN KEY (`user_id`) REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE -) /*!40000 ENGINE=INNODB */; +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; CREATE TABLE IF NOT EXISTS `itipinvitations` ( `token` VARCHAR(64) NOT NULL, diff --git a/plugins/calendar/drivers/kolab/SQL/mysql/2012080600.sql b/plugins/calendar/drivers/kolab/SQL/mysql/2012080600.sql index 5c9f1aec..71ddfe7f 100644 --- a/plugins/calendar/drivers/kolab/SQL/mysql/2012080600.sql +++ b/plugins/calendar/drivers/kolab/SQL/mysql/2012080600.sql @@ -8,4 +8,4 @@ CREATE TABLE `kolab_alarms` ( PRIMARY KEY(`event_id`), CONSTRAINT `fk_kolab_alarms_user_id` FOREIGN KEY (`user_id`) REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE -) /*!40000 ENGINE=INNODB */; +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; From 2f75ab5b39dc8025dcc1ec247c80d6b1c3f63df9 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 31 May 2019 09:19:04 +0000 Subject: [PATCH 09/14] T5414: Fix wrong all-day event date in invitation box --- plugins/calendar/calendar.php | 8 +++--- plugins/libcalendaring/libcalendaring.php | 4 +-- plugins/libcalendaring/libvcalendar.php | 30 ++++++++++++++++++----- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index f24a81ae..37e86a43 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -2924,11 +2924,11 @@ class calendar extends rcube_plugin $append = ''; // prepare a small agenda preview to be filled with actual event data on async request - if ($ical_objects->method == 'REQUEST') { + if ($ical_objects->method == 'REQUEST' && $event['start']) { + $date = $this->rc->format_date($event['start'], $this->rc->config->get('date_format'), empty($event['start']->_dateonly)); $append = html::div('calendar-agenda-preview', - html::tag('h3', 'preview-title', $this->gettext('agenda') . ' ' . - html::span('date', $this->rc->format_date($event['start'], $this->rc->config->get('date_format'))) - ) . '%before%' . $this->mail_agenda_event_row($event, 'current') . '%after%'); + html::tag('h3', 'preview-title', $this->gettext('agenda') . ' ' . html::span('date', $date)) + . '%before%' . $this->mail_agenda_event_row($event, 'current') . '%after%'); } $html .= html::div('calendar-invitebox invitebox boxinformation', diff --git a/plugins/libcalendaring/libcalendaring.php b/plugins/libcalendaring/libcalendaring.php index 6c33aba1..e9a4c286 100644 --- a/plugins/libcalendaring/libcalendaring.php +++ b/plugins/libcalendaring/libcalendaring.php @@ -315,8 +315,8 @@ class libcalendaring extends rcube_plugin $time_format = self::to_php_date_format($this->rc->config->get('calendar_time_format', $this->defaults['calendar_time_format'])); if ($event['allday']) { - $fromto = $this->rc->format_date($event['start'], $date_format); - if (($todate = $this->rc->format_date($event['end'], $date_format)) != $fromto) + $fromto = $this->rc->format_date($event['start'], $date_format, false); + if (($todate = $this->rc->format_date($event['end'], $date_format, false)) != $fromto) $fromto .= ' - ' . $todate; } else if ($duration < 86400 && $event['start']->format('d') == $event['end']->format('d')) { diff --git a/plugins/libcalendaring/libvcalendar.php b/plugins/libcalendaring/libvcalendar.php index b5e5e5a9..4f53494a 100644 --- a/plugins/libcalendaring/libvcalendar.php +++ b/plugins/libcalendaring/libvcalendar.php @@ -430,7 +430,7 @@ class libvcalendar implements Iterator case 'DTEND': case 'DUE': $propmap = array('DTSTART' => 'start', 'DTEND' => 'end', 'DUE' => 'due'); - $event[$propmap[$prop->name]] = self::convert_datetime($prop); + $event[$propmap[$prop->name]] = self::convert_datetime($prop); break; case 'TRANSP': @@ -694,16 +694,14 @@ class libvcalendar implements Iterator // assign current timezone to event start/end if ($event['start'] instanceof DateTime) { - if ($this->timezone) - $event['start']->setTimezone($this->timezone); + $this->_apply_timezone($event['start']); } else { unset($event['start']); } if ($event['end'] instanceof DateTime) { - if ($this->timezone) - $event['end']->setTimezone($this->timezone); + $this->_apply_timezone($event['end']); } else { unset($event['end']); @@ -728,6 +726,27 @@ class libvcalendar implements Iterator return $event; } + /** + * Apply user timezone to DateTime object + */ + private function _apply_timezone(&$date) + { + if (empty($this->timezone)) { + return; + } + + // For date-only we'll keep the date and time intact + if ($date->_dateonly) { + $dt = new DateTime(null, $this->timezone); + $dt->setDate($date->format('Y'), $date->format('n'), $date->format('j')); + $dt->setTime($date->format('G'), $date->format('i'), 0); + $date = $dt; + } + else { + $date->setTimezone($this->timezone); + } + } + /** * Parse the given vfreebusy component into an array representation */ @@ -816,7 +835,6 @@ class libvcalendar implements Iterator if (empty($prop)) { return $as_array ? array() : null; } - else if ($prop instanceof VObject\Property\iCalendar\DateTime) { if (count($prop->getDateTimes()) > 1) { $dt = array(); From 8d04cec2f17813be79b015bf606bb500049f4f57 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 31 May 2019 11:30:17 +0000 Subject: [PATCH 10/14] T5414: Fix Check Calendar button pointing to a wrong date --- plugins/calendar/calendar.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index 37e86a43..b1147c26 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -2921,13 +2921,14 @@ class calendar extends rcube_plugin // get prepared inline UI for this event object if ($ical_objects->method) { - $append = ''; + $append = ''; + $date_str = $this->rc->format_date($event['start'], $this->rc->config->get('date_format'), empty($event['start']->_dateonly)); + $date = new DateTime($event['start']->format('Y-m-d') . ' 12:00:00', new DateTimeZone('UTC')); // prepare a small agenda preview to be filled with actual event data on async request - if ($ical_objects->method == 'REQUEST' && $event['start']) { - $date = $this->rc->format_date($event['start'], $this->rc->config->get('date_format'), empty($event['start']->_dateonly)); + if ($ical_objects->method == 'REQUEST') { $append = html::div('calendar-agenda-preview', - html::tag('h3', 'preview-title', $this->gettext('agenda') . ' ' . html::span('date', $date)) + html::tag('h3', 'preview-title', $this->gettext('agenda') . ' ' . html::span('date', $date_str)) . '%before%' . $this->mail_agenda_event_row($event, 'current') . '%after%'); } @@ -2938,7 +2939,7 @@ class calendar extends rcube_plugin $ical_objects->mime_id . ':' . $idx, 'calendar', rcube_utils::anytodatetime($ical_objects->message_date), - $this->rc->url(array('task' => 'calendar')) . '&view=agendaDay&date=' . $event['start']->format('U') + $this->rc->url(array('task' => 'calendar')) . '&view=agendaDay&date=' . $date->format('U') ) . $append ); } From d87235e20e22da1cf68d0708ffa1fd5ae204c6a3 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 31 May 2019 11:55:33 +0000 Subject: [PATCH 11/14] Calendar: Limit number of event occurrences when "checking an itip event in calendar" --- plugins/calendar/calendar.php | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index b1147c26..6f1cf586 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -1361,7 +1361,7 @@ class calendar extends rcube_plugin public function itip_events($msgref) { $path = explode('/', $msgref); - $msg = array_pop($path); + $msg = array_pop($path); $mbox = join('/', $path); list($uid, $mime_id) = explode('#', $msg); $events = array(); @@ -1377,23 +1377,27 @@ class calendar extends rcube_plugin } } */ - $event['id'] = $event['uid']; + $event['id'] = $event['uid']; $event['temporary'] = true; - $event['readonly'] = true; - $event['calendar'] = '--invitation--itip'; + $event['readonly'] = true; + $event['calendar'] = '--invitation--itip'; $event['className'] = 'fc-invitation-' . strtolower($partstat); - $event['_mbox'] = $mbox; - $event['_uid'] = $uid; - $event['_part'] = $mime_id; + $event['_mbox'] = $mbox; + $event['_uid'] = $uid; + $event['_part'] = $mime_id; $events[] = $this->_client_event($event, true); // add recurring instances if (!empty($event['recurrence'])) { - foreach ($this->driver->get_recurring_events($event, $event['start']) as $recurring) { + // Some installations can't handle all occurrences (aborting the request w/o an error in log) + $end = clone $event['start']; + $end->add(new DateInterval($event['recurrence']['FREQ'] == 'DAILY' ? 'P1Y' : 'P10Y')); + + foreach ($this->driver->get_recurring_events($event, $event['start'], $end) as $recurring) { $recurring['temporary'] = true; - $recurring['readonly'] = true; - $recurring['calendar'] = '--invitation--itip'; + $recurring['readonly'] = true; + $recurring['calendar'] = '--invitation--itip'; $events[] = $this->_client_event($recurring, true); } } From 98239ee764fe87caa0cc4a5b2b9d4200ea39f078 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 31 May 2019 12:00:38 +0000 Subject: [PATCH 12/14] Elastic: Fix position of counter badge on calendars list --- plugins/libkolab/skins/elastic/libkolab.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/libkolab/skins/elastic/libkolab.less b/plugins/libkolab/skins/elastic/libkolab.less index d6bb6859..bc30161d 100644 --- a/plugins/libkolab/skins/elastic/libkolab.less +++ b/plugins/libkolab/skins/elastic/libkolab.less @@ -207,7 +207,7 @@ right: 0; min-width: 2em; line-height: 1.4rem; - margin: (@listing-line-height - 1.4rem)/2; + margin: (@listing-line-height - 1.4 * @page-font-size)/2; padding: 0 .3em; border-radius: .4em; background: @color-list-secondary; @@ -217,7 +217,7 @@ html.touch & { line-height: 2rem; - margin: (@listing-touch-line-height - 2rem)/2; + margin: (@listing-touch-line-height - 2 * @page-font-size)/2; } } } From 6358d0ebfacce420090c294f23d098fe38cc2871 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Sun, 2 Jun 2019 07:44:21 +0000 Subject: [PATCH 13/14] Calendar: Fix white on white color for itip event preview --- plugins/calendar/calendar_ui.js | 3 +-- plugins/libcalendaring/libcalendaring.js | 5 +++++ plugins/libkolab/skins/elastic/include/calendar.less | 12 +++--------- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js index b0688c3f..6285f6eb 100644 --- a/plugins/calendar/calendar_ui.js +++ b/plugins/calendar/calendar_ui.js @@ -3684,8 +3684,7 @@ function rcube_calendar_ui(settings) if (rcmail.env.itip_events && rcmail.env.itip_events.length) { me.calendars['--invitation--itip'] = { events: rcmail.env.itip_events, - color: '#fff', - textColor: '#333', + color: 'ffffff', editable: false, rights: 'lrs', attendees: true diff --git a/plugins/libcalendaring/libcalendaring.js b/plugins/libcalendaring/libcalendaring.js index a8a4d54d..0cd41260 100644 --- a/plugins/libcalendaring/libcalendaring.js +++ b/plugins/libcalendaring/libcalendaring.js @@ -348,6 +348,11 @@ function rcube_libcalendaring(settings) if (!color_map[color]) { color_map[color] = '#fff'; + // Convert 3-char to 6-char + if (/^#?([a-f0-9]{1})([a-f0-9]{1})([a-f0-9]{1})$/i.test(color)) { + color = '#' + RegExp.$1 + RegExp.$1 + RegExp.$2 + RegExp.$2 + RegExp.$3 + RegExp.$3; + } + if (/^#?([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/i.test(color)) { // use information about brightness calculation found at // http://javascriptrules.com/2009/08/05/css-color-brightness-contrast-using-javascript/ diff --git a/plugins/libkolab/skins/elastic/include/calendar.less b/plugins/libkolab/skins/elastic/include/calendar.less index fe1d61f0..fc1ff201 100644 --- a/plugins/libkolab/skins/elastic/include/calendar.less +++ b/plugins/libkolab/skins/elastic/include/calendar.less @@ -322,16 +322,10 @@ fieldset.categories .input-group { margin-top: 1.1rem; } - &.fc-invitation-needs-action { - border: 1px dashed #5757c7; - } - - &.fc-invitation-tentative { - border: 1px dashed #eb8900; - } - + &.fc-invitation-needs-action, + &.fc-invitation-tentative, &.fc-invitation-declined { - border: 1px dashed #c00; + border: 1px dashed #999; } &.fc-event-ns-other.fc-invitation-declined { From c1a1d59ade769ff36379738e241b5a6f44970afc Mon Sep 17 00:00:00 2001 From: "Jeroen van Meeuwen (Kolab Systems)" Date: Mon, 3 Jun 2019 11:14:19 +0200 Subject: [PATCH 14/14] Bump version to 3.4.6 --- plugins/calendar/composer.json | 2 +- plugins/libcalendaring/composer.json | 2 +- plugins/libkolab/composer.json | 2 +- plugins/tasklist/composer.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/calendar/composer.json b/plugins/calendar/composer.json index 6186274c..a580b2fd 100644 --- a/plugins/calendar/composer.json +++ b/plugins/calendar/composer.json @@ -4,7 +4,7 @@ "description": "Calendar plugin", "homepage": "https://git.kolab.org/diffusion/RPK/", "license": "AGPLv3", - "version": "3.4.5", + "version": "3.4.6", "authors": [ { "name": "Thomas Bruederli", diff --git a/plugins/libcalendaring/composer.json b/plugins/libcalendaring/composer.json index d0e4f3c2..80503b76 100644 --- a/plugins/libcalendaring/composer.json +++ b/plugins/libcalendaring/composer.json @@ -4,7 +4,7 @@ "description": "Library providing common functions for calendaring plugins", "homepage": "https://git.kolab.org/diffusion/RPK/", "license": "AGPLv3", - "version": "3.4.5", + "version": "3.4.6", "authors": [ { "name": "Thomas Bruederli", diff --git a/plugins/libkolab/composer.json b/plugins/libkolab/composer.json index a1f6532d..ecb6ca79 100644 --- a/plugins/libkolab/composer.json +++ b/plugins/libkolab/composer.json @@ -4,7 +4,7 @@ "description": "Plugin to setup a basic environment for the interaction with a Kolab server.", "homepage": "https://git.kolab.org/diffusion/RPK/", "license": "AGPLv3", - "version": "3.4.5", + "version": "3.4.6", "authors": [ { "name": "Thomas Bruederli", diff --git a/plugins/tasklist/composer.json b/plugins/tasklist/composer.json index a1b3844f..62d0cdee 100644 --- a/plugins/tasklist/composer.json +++ b/plugins/tasklist/composer.json @@ -4,7 +4,7 @@ "description": "Task management plugin", "homepage": "https://git.kolab.org/diffusion/RPK/", "license": "AGPLv3", - "version": "3.4.5", + "version": "3.4.6", "authors": [ { "name": "Thomas Bruederli",