diff --git a/plugins/calendar/calendar.js b/plugins/calendar/calendar.js index d14c2b95..103e06c9 100644 --- a/plugins/calendar/calendar.js +++ b/plugins/calendar/calendar.js @@ -46,6 +46,21 @@ window.rcmail && rcmail.addEventListener('init', function(evt) { var day_clicked = day_clicked_ts = 0; var ignore_click = false; + // create a nice human-readable string for the date/time range + var event_date_text = function(event) { + var fromto, duration = event.end.getTime() / 1000 - event.start.getTime() / 1000; + if (event.allDay) + fromto = $.fullCalendar.formatDate(event.start, settings['date_format']) + (duration > 86400 ? ' — ' + $.fullCalendar.formatDate(event.end, settings['date_format']) : ''); + else if (duration < 86400 && event.start.getDay() == event.end.getDay()) + fromto = $.fullCalendar.formatDate(event.start, settings['date_format']) + ' ' + $.fullCalendar.formatDate(event.start, settings['time_format']) + ' — ' + + $.fullCalendar.formatDate(event.end, settings['time_format']); + else + fromto = $.fullCalendar.formatDate(event.start, settings['date_format']) + ' ' + $.fullCalendar.formatDate(event.start, settings['time_format']) + ' — ' + + $.fullCalendar.formatDate(event.end, settings['date_format']) + ' ' + $.fullCalendar.formatDate(event.end, settings['time_format']); + + return fromto; + }; + // event details dialog (show only) var event_show_dialog = function(event) { var $dialog = $("#eventshow"); @@ -59,17 +74,8 @@ window.rcmail && rcmail.addEventListener('init', function(evt) { if (event.description) $('#event-description').show().children('.event-text').html(nl2br(Q(event.description))); // TODO: format HTML with clickable links and stuff - // TODO: create a nice human-readable string for the date/time range - var fromto, duration = event.end.getTime() / 1000 - event.start.getTime() / 1000; - if (event.allDay) - fromto = $.fullCalendar.formatDate(event.start, settings['date_format']) + ' — ' + $.fullCalendar.formatDate(event.end, settings['date_format']); - else if (duration < 86400 && event.start.getDay() == event.end.getDay()) - fromto = $.fullCalendar.formatDate(event.start, settings['date_format']) + ' ' + $.fullCalendar.formatDate(event.start, settings['time_format']) + ' — ' - + $.fullCalendar.formatDate(event.end, settings['time_format']); - else - fromto = $.fullCalendar.formatDate(event.start, settings['date_format']) + ' ' + $.fullCalendar.formatDate(event.start, settings['time_format']) + ' — ' - + $.fullCalendar.formatDate(event.end, settings['date_format']) + ' ' + $.fullCalendar.formatDate(event.end, settings['time_format']); - $('#event-date').html(Q(fromto)).show(); + // render from-to in a nice human-readable way + $('#event-date').html(Q(event_date_text(event))).show(); if (event.recurrence && event.recurrence_text) $('#event-repeat').show().children('.event-text').html(Q(event.recurrence_text)); @@ -416,6 +422,47 @@ window.rcmail && rcmail.addEventListener('init', function(evt) { return false; }; + + // display a notification for the given pending alarms + this.display_alarms = function(alarms) { + // clear old alert first + $('#alarm-display').dialog('destroy'); + + var event_ids = []; + var display = $('
').attr('id', 'alarm-display'); + for (var html, alarm, i=0; i < alarms.length; i++) { + alarm = alarms[i]; + alarm.start = new Date(alarm.start); + alarm.end = new Date(alarm.end); + event_ids.push(alarm.id); + + html = '

' + Q(alarm.title) + '

'; + html += '
' + Q(alarm.location) + '
'; + html += '
' + Q(event_date_text(alarm)) + '
'; + $('
').addClass('alarm-item').html(html).appendTo(display); + } + + display.appendTo(document.body).dialog({ + modal: true, + resizable: true, + closeOnEscape: false, + dialogClass: 'alert', + title: rcmail.gettext('alarmtitle', 'calendar'), + buttons: { + "Snooze": function() { + $(this).dialog('close'); + }, + "Dismiss": function() { + $(this).dialog('close'); + } + }, + close: function() { + $(this).dialog('destroy'); + // TODO: submit dismissed event_ids to server + $(this).remove(); + } + }).data('event_ids', event_ids.join(',')); + }; // create list of event sources AKA calendars @@ -618,19 +665,19 @@ window.rcmail && rcmail.addEventListener('init', function(evt) { } // end rcube_calendar class - + // configure toobar buttons rcmail.register_command('plugin.addevent', function(){ cal.add_event(); }, true); // export events rcmail.register_command('plugin.export', function(){ rcmail.goto_url('plugin.export_events', { source:cal.selected_calendar }); }, true); rcmail.enable_command('plugin.export', true); + + // register callback commands + rcmail.addEventListener('plugin.display_alarms', function(alarms){ cal.display_alarms(alarms); }); // reload calendar - rcmail.addEventListener('plugin.reload_calendar', reload_calendar); - function reload_calendar() { - $('#calendar').fullCalendar('refetchEvents'); - } + rcmail.addEventListener('plugin.reload_calendar', function() { $('#calendar').fullCalendar('refetchEvents'); }); var formattime = function(hour, minutes) { diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index a4342a82..c84a45e0 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -74,6 +74,7 @@ class calendar extends rcube_plugin $this->register_action('plugin.load_events', array($this, 'load_events')); $this->register_action('plugin.event', array($this, 'event')); $this->register_action('plugin.export_events', array($this, 'export_events')); + $this->add_hook('keep_alive', array($this, 'keep_alive')); // set user's timezone if ($this->rc->config->get('timezone') === 'auto') @@ -360,6 +361,20 @@ class calendar extends rcube_plugin exit; } + /** + * Handler for keep-alive requests + * This will check for pending notifications and pass them to the client + */ + function keep_alive($attr) + { + $alarms = $this->driver->pending_alarms(time()); + #if ($alarms) + # $this->rc->output->command('plugin.display_alarms', $this->_alarms_output($alarms)); + } + + /** + * + */ function export_events() { $start = get_input_value('start', RCUBE_INPUT_GET); @@ -375,6 +390,9 @@ class calendar extends rcube_plugin exit; } + /** + * + */ function load_settings() { $settings = array(); @@ -478,6 +496,28 @@ class calendar extends rcube_plugin return json_encode($json); } + + /** + * Generate reduced and streamlined output for pending alarms + */ + private function _alarms_output($alarms) + { + $out = array(); + foreach ($alarms as $alarm) { + $out[] = array( + 'id' => $alarm['id'], + 'start' => $alarm['start'], + 'end' => $alarm['end'], + 'allDay' => ($event['all_day'] == 1)?true:false, + 'title' => $alarm['title'], + 'location' => $alarm['location'], + 'calendar' => $alarm['calendar'], + ); + } + + return $out; + } + /** * Render localized text for alarm settings */ diff --git a/plugins/calendar/localization/en_US.inc b/plugins/calendar/localization/en_US.inc index 835dead0..7cb773e1 100644 --- a/plugins/calendar/localization/en_US.inc +++ b/plugins/calendar/localization/en_US.inc @@ -65,6 +65,7 @@ $labels['trigger+D'] = 'days after'; $labels['addalarm'] = 'add alarm'; $labels['defaultalarmtype'] = 'Default reminder setting'; $labels['defaultalarmoffset'] = 'Default reminder time'; +$labels['alarmtitle'] = 'Upcoming events'; // event dialog tabs $labels['tabsummary'] = 'Summary'; diff --git a/plugins/calendar/skins/default/calendar.css b/plugins/calendar/skins/default/calendar.css index e9ea1c5d..281b90c1 100644 --- a/plugins/calendar/skins/default/calendar.css +++ b/plugins/calendar/skins/default/calendar.css @@ -341,6 +341,20 @@ a.dropdown-link:after { min-height: 20em; } +.alarm-item { + margin: 0.4em 0 1em 0; +} + +.alarm-item .event-title { + font-size: 14px; + margin: 0.1em 0 0.3em 0; +} + +.alarm-item div.event-section { + margin-top: 0.1em; + margin-bottom: 0.3em; +} + .ui-dialog-buttonset a.dropdown-link { margin-right: 1em; }