From 322f649d7750453325f8f830e5c7f3c5c2db33cb Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Fri, 29 Jul 2011 13:16:13 +0200 Subject: [PATCH] Improve free/busy UI according to inputs from Georg --- plugins/calendar/calendar.php | 7 ++- plugins/calendar/calendar_ui.js | 58 ++++++++++++++++-- .../calendar/drivers/kolab/kolab_driver.php | 4 ++ plugins/calendar/localization/en_US.inc | 1 + plugins/calendar/skins/default/calendar.css | 28 +++++++++ plugins/calendar/skins/default/iehacks.css | 8 ++- .../skins/default/images/freebusy-colors.gif | Bin 0 -> 408 bytes .../skins/default/images/freebusy-colors.png | Bin 0 -> 631 bytes 8 files changed, 99 insertions(+), 7 deletions(-) create mode 100644 plugins/calendar/skins/default/images/freebusy-colors.gif create mode 100644 plugins/calendar/skins/default/images/freebusy-colors.png diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index 4d506ffc..ae7b2048 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -1395,8 +1395,8 @@ class calendar extends rcube_plugin } } - // let this information be cached for 15min - send_future_expire_header(900); + // let this information be cached for 5min + send_future_expire_header(300); echo $status; exit; @@ -1441,6 +1441,9 @@ class calendar extends rcube_plugin $t = $t_end; } + // 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)); exit; } diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js index 0b2b7049..2baeb49d 100644 --- a/plugins/calendar/calendar_ui.js +++ b/plugins/calendar/calendar_ui.js @@ -673,7 +673,7 @@ function rcube_calendar_ui(settings) fb_start.setHours(0); fb_start.setMinutes(0); fb_start.setSeconds(0); fb_start.setMilliseconds(0); fb_end.setTime(fb_start.getTime() + DAY_MS); - freebusy_data = {}; + freebusy_data = { required:{}, all:{} }; freebusy_ui.loading = 1; // prevent render_freebusy_grid() to load data yet freebusy_ui.numdays = allday.checked ? 7 : Math.ceil(duration * 2 / 86400); freebusy_ui.interval = allday.checked ? 360 : 60; @@ -690,6 +690,10 @@ function rcube_calendar_ui(settings) list_html += '
' + dispname + '
'; } + // add total row + list_html += '
 
'; + list_html += '
' + rcmail.gettext('reqallattendees','calendar') + '
'; + $('#schedule-attendees-list').html(list_html); // enable/disable buttons @@ -781,6 +785,10 @@ function rcube_calendar_ui(settings) times_html += '' + slots_row + ''; } + // add line for all/required attendees + times_html += ' '; + times_html += '' + slots_row + ''; + var table = $('#schedule-freebusy-times'); table.children('thead').html(dates_row + times_row); table.children('tbody').html(times_html); @@ -905,9 +913,10 @@ function rcube_calendar_ui(settings) { var start = new Date(from.getTime() - DAY_MS * 2); // start 1 days before event var end = new Date(start.getTime() + DAY_MS * 14); // load 14 days + freebusy_ui.numrequired = 0; // load free-busy information for every attendee - var domid, email + var domid, email; for (var i=0; i < event_attendees.length; i++) { if ((email = event_attendees[i].email)) { domid = String(email).replace(rcmail.identifier_expr, ''); @@ -918,16 +927,37 @@ function rcube_calendar_ui(settings) type: 'GET', dataType: 'json', url: rcmail.url('freebusy-times'), - data: { email:email, start:date2unixtime(start), end:date2unixtime(end), interval:interval, _remote: 1 }, - success: function(data){ + data: { email:email, start:date2unixtime(start), end:date2unixtime(end), interval:interval, _remote:1 }, + success: function(data) { freebusy_ui.loading--; + // find attendee + var attendee = null; + for (var i=0; i < event_attendees.length; i++) { + if (event_attendees[i].email == data.email) { + attendee = event_attendees[i]; + break; + } + } + // copy data to member var + var req = attendee.role != 'OPT-PARTICIPANT'; var ts = data.start - 0; freebusy_data.start = ts; freebusy_data[data.email] = {}; for (var i=0; i < data.slots.length; i++) { freebusy_data[data.email][ts] = data.slots[i]; + + // set totals + if (!freebusy_data.required[ts]) + freebusy_data.required[ts] = [0,0,0,0]; + if (req) + freebusy_data.required[ts][data.slots[i]]++; + + 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; @@ -941,6 +971,10 @@ function rcube_calendar_ui(settings) update_freebusy_display(data.email); } }); + + // count required attendees + if (event_attendees[i].role != 'OPT-PARTICIPANT') + freebusy_ui.numrequired++; } } }; @@ -951,12 +985,28 @@ function rcube_calendar_ui(settings) var status_classes = ['unknown','free','busy','tentative','out-of-office']; 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]; if (fbdata && fbdata[ts] && row.length) { row.children().each(function(i, cell){ 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 all_status = freebusy_data.all[ts][2] ? 'busy' : 'unknown'; + req_status = freebusy_data.required[ts][2] ? 'busy' : 'free'; + for (var j=0; j < status_classes.length; j++) { + if (freebusy_ui.numrequired && freebusy_data.required[ts][j] >= freebusy_ui.numrequired) + req_status = status_classes[j]; + if (freebusy_data.all[ts][j] == event_attendees.length) + all_status = status_classes[j]; + } + + cell.className = cell.className.replace('unknown', req_status + ' all-' + all_status); + } + ts += freebusy_ui.interval * 60; }); } diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php index 62a55e03..a5002bab 100644 --- a/plugins/calendar/drivers/kolab/kolab_driver.php +++ b/plugins/calendar/drivers/kolab/kolab_driver.php @@ -806,6 +806,10 @@ class kolab_driver extends calendar_driver if (($fbstart = $fb->getStart()) && $start < $fbstart) { array_unshift($result, array($start, $fbstart, calendar::FREEBUSY_UNKNOWN)); } + // pad period till $end with status 'unknown' + if (($fbend = $fb->getEnd()) && $fbend < $end) { + $result[] = array($fbend, $end, calendar::FREEBUSY_UNKNOWN); + } return $result; } } diff --git a/plugins/calendar/localization/en_US.inc b/plugins/calendar/localization/en_US.inc index 7599c010..d24252a7 100644 --- a/plugins/calendar/localization/en_US.inc +++ b/plugins/calendar/localization/en_US.inc @@ -106,6 +106,7 @@ $labels['scheduletime'] = 'Find availability'; $labels['sendinvitations'] = 'Send invitations'; $labels['sendnotifications'] = 'Notify attendees about modifications'; $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'; diff --git a/plugins/calendar/skins/default/calendar.css b/plugins/calendar/skins/default/calendar.css index 6c2dae6f..95a8f322 100644 --- a/plugins/calendar/skins/default/calendar.css +++ b/plugins/calendar/skins/default/calendar.css @@ -602,6 +602,22 @@ td.topalign { background: #f0b400; } +#schedule-freebusy-times td.all-busy, +#schedule-freebusy-times td.all-tentative, +#schedule-freebusy-times td.all-out-of-office { + background-image: url('images/freebusy-colors.png'); + background-position: top right; + background-repeat: no-repeat; +} + +#schedule-freebusy-times td.all-tentative { + background-position: right -40px; +} + +#schedule-freebusy-times td.all-out-of-office { + background-position: right -80px; +} + #edit-attendees-legend { margin-top: 3em; margin-bottom: 0.5em; @@ -704,6 +720,18 @@ td.topalign { background: url('images/loading-small.gif') 1px 50% no-repeat; } +.attendees-list .total { + background: none; + padding-left: 4px; + font-weight: bold; +} + +.attendees-list .spacer, +#schedule-freebusy-times tr.spacer td { + background: 0; + font-size: 50%; +} + #schedule-freebusy-times { border-collapse: collapse; } diff --git a/plugins/calendar/skins/default/iehacks.css b/plugins/calendar/skins/default/iehacks.css index 31f82d78..59469034 100644 --- a/plugins/calendar/skins/default/iehacks.css +++ b/plugins/calendar/skins/default/iehacks.css @@ -51,4 +51,10 @@ html #calendartoolbar a.buttonPas { #eventfreebusy .schedule-buttons, #edit-attendees-form #edit-attendee-schedule { right: 0.6em; -} \ No newline at end of file +} + +#schedule-freebusy-times td.all-busy, +#schedule-freebusy-times td.all-tentative, +#schedule-freebusy-times td.all-out-of-office { + background-image: url('images/freebusy-colors.gif'); +} diff --git a/plugins/calendar/skins/default/images/freebusy-colors.gif b/plugins/calendar/skins/default/images/freebusy-colors.gif new file mode 100644 index 0000000000000000000000000000000000000000..699f4b9cb0c763086a579147211a949ae62e518b GIT binary patch literal 408 zcmV;J0cZY4Nk%w1VJHB20FeU#_p$(8Zr{}!0RR90A^8LW00093EC2ui04M->00092 zoR6u??GFct^Z`+`x>$SOc@-Fn5LucHnyQ-s$E|y?Fum9}j_5ltkqLh|UkqqG7KhB4 zCMlyqqpB%YWW7F{T;izP{eZ)nEVDcYf79d>TkU?gS8_PLK7Zl!y}dnQ_Lk!}rbn0; zSl4%0Xpo5K$jBBXnFe$T6oBavpt-rw>G_EeDmpq)YWkU~ipkoV`8xXAxk?M`a$9?; zyUWw-O9LFNGE6LDe5{eIT*2HN@eKVSJ>5`k4T_C>z0Ixd?d+XxFdiCho}GTqxjyaA z4i3)%Un*a(P@kHhzkks04;4Ur0tcQm81R%r3k!QpXea@|!-WzhHmqn-A;yepqc(Ef zQyV&ut~#y9cy-jS&U%UessIBD%_uQGawv<9^TQ zL#vESX_awOz$Kgva1AE`+`@$e_i&-W99$SM7Z(C7!O?-GI4ZCPM+4U4C_oDi4z%K+ zKo1-Y=!Js-J#p?pZ=5R-fpY_*a4tY3&J0B3Oh5*%8py&`0hzdBARAW%jKF0Bqi|Wk zNZc+k8n+2lz^wvRaEm}C+zhCOn*bGYBTyAL0F`kmjrvYBDa|I%BZa@^y1&G9%foPlw$iP(tS-2`76ITpmcfhY1K#4KfJ-xP=P`?%_g#Ik+%jE-nOEf};aVaa3Rpjs~p7 zQGga49B9QsfgU&*&OxEW9lHvuZ*MxZJ#rRjTJT*|)y0|4)uSqBM$ R2VwvK002ovPDHLkV1fXt{W|~v literal 0 HcmV?d00001