From be0e774998de22b71fe7e65103fa3b32b3e62337 Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Sat, 23 Jul 2011 12:18:29 +0200 Subject: [PATCH] Support free/busy types (e.g. tentative, out-of-office) --- plugins/calendar/calendar.php | 10 ++++++---- plugins/calendar/calendar_ui.js | 19 +++++++++++++------ .../calendar/drivers/kolab/kolab_driver.php | 16 ++++++++++++++-- plugins/calendar/localization/en_US.inc | 1 + plugins/calendar/skins/default/calendar.css | 7 ++++++- .../default/templates/freebusylegend.html | 1 + 6 files changed, 41 insertions(+), 13 deletions(-) diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index 76464c26..7c9cffae 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -29,6 +29,7 @@ class calendar extends rcube_plugin const FREEBUSY_UNKNOWN = 0; const FREEBUSY_FREE = 1; const FREEBUSY_BUSY = 2; + const FREEBUSY_TENTATIVE = 3; const FREEBUSY_OOF = 4; public $task = '?(?!login|logout).*'; @@ -1206,6 +1207,7 @@ class calendar extends rcube_plugin if (!$start) $start = time(); if (!$end) $end = $start + 3600; + $fbtypemap = array(calendar::FREEBUSY_FREE => 'FREE', calendar::FREEBUSY_BUSY => 'BUSY', calendar::FREEBUSY_TENTATIVE => 'TENTATIVE', calendar::FREEBUSY_OOF => 'OUT-OF-OFFICE'); $status = 'UNKNOWN'; // if the backend has free-busy information @@ -1214,9 +1216,9 @@ class calendar extends rcube_plugin $status = 'FREE'; foreach ($fblist as $slot) { - list($from, $to) = $slot; + list($from, $to, $type) = $slot; if ($from <= $end && $to > $start) { - $status = 'BUSY'; + $status = $type && $fbtypemap[$type] ? $fbtypemap[$type] : 'BUSY'; break; } } @@ -1256,9 +1258,9 @@ class calendar extends rcube_plugin if (is_array($fblist)) { $status = self::FREEBUSY_FREE; foreach ($fblist as $slot) { - list($from, $to) = $slot; + list($from, $to, $type) = $slot; if ($from <= $t_end && $to > $t) { - $status = self::FREEBUSY_BUSY; + $status = isset($type) ? $type : self::FREEBUSY_BUSY; break; } } diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js index 6530a012..b691d31c 100644 --- a/plugins/calendar/calendar_ui.js +++ b/plugins/calendar/calendar_ui.js @@ -46,6 +46,7 @@ function rcube_calendar_ui(settings) var attendees_list; var freebusy_ui = {}; var freebusy_data = {}; + var freebusy_needsupdate; // general datepicker settings var datepicker_settings = { @@ -330,6 +331,7 @@ function rcube_calendar_ui(settings) var $dialog = $("#eventedit"); var calendar = event.calendar && me.calendars[event.calendar] ? me.calendars[event.calendar] : { editable:action=='new' }; me.selected_event = event; + freebusy_needsupdate = false; // reset dialog first, enable/disable fields according to editable state $('#eventtabs').get(0).reset(); @@ -799,8 +801,8 @@ function rcube_calendar_ui(settings) var allday = $('#edit-allday').get(0); me.selected_event.start = parse_datetime(allday.checked ? '00:00' : $('#edit-starttime').val(), $('#edit-startdate').val()); me.selected_event.end = parse_datetime(allday.checked ? '23:59' : $('#edit-endtime').val(), $('#edit-enddate').val()); - if (me.selected_event.attendees) - update_freebusy_status(me.selected_event); + if (event_attendees) + freebusy_needsupdate = true; $('#edit-startdate').data('duration', Math.round((me.selected_event.end.getTime() - me.selected_event.start.getTime()) / 1000)); } }; @@ -902,10 +904,12 @@ function rcube_calendar_ui(settings) var update_freebusy_status = function(event) { var icons = attendees_list.find('img.availabilityicon'); - for (var i=0; i < event.attendees.length; i++) { - if (icons.get(i) && event.attendees[i].email && event.attendees[i].status != 'ACCEPTED') - check_freebusy_status(icons.get(i), event.attendees[i].email, event); + for (var i=0; i < event_attendees.length; i++) { + if (icons.get(i) && event_attendees[i].email && event_attendees[i].status != 'ACCEPTED') + check_freebusy_status(icons.get(i), event_attendees[i].email, event); } + + freebusy_needsupdate = false; }; // load free-busy status from server and update icon accordingly @@ -1563,8 +1567,11 @@ function rcube_calendar_ui(settings) // init event dialog $('#eventtabs').tabs({ show: function(event, ui) { - if (ui.panel.id == 'event-tab-3') + if (ui.panel.id == 'event-tab-3') { $('#edit-attendee-name').select(); + if (freebusy_needsupdate && me.selected_event) + update_freebusy_status(me.selected_event); + } } }); $('#edit-enddate, input.edit-alarm-date').datepicker(datepicker_settings); diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php index 2b22320e..3fd03989 100644 --- a/plugins/calendar/drivers/kolab/kolab_driver.php +++ b/plugins/calendar/drivers/kolab/kolab_driver.php @@ -693,9 +693,19 @@ class kolab_driver extends calendar_driver if (empty($email)/* || $end < time()*/) return false; + // map vcalendar fbtypes to internal values + $fbtypemap = array( + 'FREE' => calendar::FREEBUSY_FREE, + 'BUSY-TENTATIVE' => calendar::FREEBUSY_TENTATIVE, + 'X-OUT-OF-OFFICE' => calendar::FREEBUSY_OOF, + 'OOF' => calendar::FREEBUSY_OOF); + // ask kolab server first $fbdata = @file_get_contents(rcube_kolab::get_freebusy_url($email)); - + + if (!$fbdata) + $fbdata = file_get_contents('http://localhost/roundcube/kolab/sample.ifb'); + // get free-busy url from contacts if (!$fbdata) { $fburl = null; @@ -722,10 +732,12 @@ class kolab_driver extends calendar_driver $fbcal->parsevCalendar($fbdata); if ($fb = $fbcal->findComponent('vfreebusy')) { $result = array(); + $params = $fb->getExtraParams(); foreach ($fb->getBusyPeriods() as $from => $to) { if ($to == null) // no information, assume free break; - $result[] = array($from, $to); + $type = $params[$from]['FBTYPE']; + $result[] = array($from, $to, isset($fbtypemap[$type]) ? $fbtypemap[$type] : calendar::FREEBUSY_BUSY); } return $result; diff --git a/plugins/calendar/localization/en_US.inc b/plugins/calendar/localization/en_US.inc index a4a8a509..ab4bf5c4 100644 --- a/plugins/calendar/localization/en_US.inc +++ b/plugins/calendar/localization/en_US.inc @@ -95,6 +95,7 @@ $labels['roleresource'] = 'Resource'; $labels['availfree'] = 'Free'; $labels['availbusy'] = 'Busy'; $labels['availunknown'] = 'Unknown'; +$labels['availtentative'] = 'Tentative'; $labels['availoutofoffice'] = 'Out of Office'; $labels['scheduletime'] = 'Available times'; $labels['sendnotifications'] = 'Send notifications'; diff --git a/plugins/calendar/skins/default/calendar.css b/plugins/calendar/skins/default/calendar.css index 47e47e8c..308b002f 100644 --- a/plugins/calendar/skins/default/calendar.css +++ b/plugins/calendar/skins/default/calendar.css @@ -566,7 +566,7 @@ td.topalign { } .availability img.availabilityicon.loading { - background: url('images/loading-small.gif') top left no-repeat; + background: url('images/loading-small.gif') middle middle no-repeat; } #schedule-freebusy-times td.unknown, @@ -584,6 +584,11 @@ td.topalign { background: #c00; } +#schedule-freebusy-times td.tentative, +.availability img.availabilityicon.tentative { + background: #66d; +} + #schedule-freebusy-times td.out-of-office, .availability img.availabilityicon.out-of-office { background: #f0b400; diff --git a/plugins/calendar/skins/default/templates/freebusylegend.html b/plugins/calendar/skins/default/templates/freebusylegend.html index c3d0a86e..5cc01a18 100644 --- a/plugins/calendar/skins/default/templates/freebusylegend.html +++ b/plugins/calendar/skins/default/templates/freebusylegend.html @@ -1,6 +1,7 @@
+