Branch 'roundcubemail-plugins-kolab-format2-horde5' - plugins/calendar plugins/libcalendaring
Thomas Brüderli
bruederli at kolabsys.com
Wed Mar 6 10:58:41 CET 2013
plugins/calendar/calendar.php | 45 +++++++++++++-
plugins/calendar/calendar_ui.js | 94 ++++++++++++++++++++-----------
plugins/libcalendaring/libcalendaring.js | 6 -
3 files changed, 107 insertions(+), 38 deletions(-)
New commits:
commit 3475aa416fe0e90acbe8181ca8f5e462d50dffe2
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 f777cb3..7259c99 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -1365,6 +1365,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;
@@ -1402,11 +1412,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();
@@ -1414,7 +1440,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;
@@ -1429,13 +1457,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 1efc160..5342884 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;
@@ -463,7 +478,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'];
@@ -876,10 +891,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);
@@ -948,7 +966,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 {
@@ -1052,12 +1070,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++) {
@@ -1070,7 +1089,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--;
@@ -1084,11 +1103,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
@@ -1100,10 +1119,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
@@ -1160,13 +1177,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;
@@ -1182,7 +1204,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;
});
}
};
@@ -1190,9 +1212,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);
}
@@ -1209,13 +1234,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;
@@ -1223,27 +1248,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;
}
@@ -1264,12 +1294,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;
@@ -1432,7 +1462,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>';
More information about the commits
mailing list