').addClass('alarm-item').html(html).appendTo(display);
+
+ adismiss = $('
').html(rcmail.gettext('dismiss','calendar')).click(function(){
+ me.dismiss_link = $(this);
+ me.dismiss_alarm(me.dismiss_link.data('id'), 0);
+ });
+ asnooze = $('
').html(rcmail.gettext('snooze','calendar')).click(function(){
+ me.snooze_dropdown($(this));
+ });
+ actions = $('
').addClass('alarm-actions').append(adismiss.data('id', alarm.id)).append(asnooze.data('id', alarm.id));
+
+ $('
').addClass('alarm-item').html(html).append(actions).appendTo(this.alarm_dialog);
}
- display.appendTo(document.body).dialog({
- modal: true,
+ var buttons = {};
+ buttons[rcmail.gettext('dismissall','calendar')] = function() {
+ // submit dismissed event_ids to server
+ me.dismiss_alarm(me.alarm_ids.join(','), 0);
+ $(this).dialog('close');
+ };
+
+ this.alarm_dialog.appendTo(document.body).dialog({
+ modal: false,
resizable: true,
closeOnEscape: false,
- dialogClass: 'alert',
+ dialogClass: 'alarm',
title: rcmail.gettext('alarmtitle', 'calendar'),
- buttons: {
- "Snooze": function() {
- $(this).dialog('close');
- },
- "Dismiss": function() {
- $(this).dialog('close');
- }
- },
+ buttons: buttons,
close: function() {
- $(this).dialog('destroy');
- // TODO: submit dismissed event_ids to server
- $(this).remove();
+ $('#alarm-snooze-dropdown').hide();
+ $(this).dialog('destroy').remove();
+ me.alarm_dialog = null;
+ me.alarm_ids = null;
+ },
+ drag: function(event, ui) {
+ $('#alarm-snooze-dropdown').hide();
}
- }).data('event_ids', event_ids.join(','));
+ });
+ this.alarm_ids = event_ids;
+ };
+
+ // show a drop-down menu with a selection of snooze times
+ this.snooze_dropdown = function(link)
+ {
+ if (!this.snooze_popup) {
+ this.snooze_popup = $('#alarm-snooze-dropdown');
+ $('#alarm-snooze-dropdown a').click(function(e){
+ var time = String(this.href).replace(/.+#/, '');
+ me.dismiss_alarm($('#alarm-snooze-dropdown').data('id'), time);
+ return false;
+ });
+ }
+
+ // hide visible popup
+ if (this.snooze_popup.is(':visible') && this.snooze_popup.data('id') == link.data('id')) {
+ this.snooze_popup.hide();
+ this.dismiss_link = null;
+ }
+ else { // open popup below the clicked link
+ var pos = link.offset();
+ pos.top += link.height() + 2;
+ this.snooze_popup.data('id', link.data('id')).css({ top:Math.floor(pos.top)+'px', left:Math.floor(pos.left)+'px' }).show();
+ this.dismiss_link = link;
+ }
+ };
+
+ // dismiss or snooze alarms for the given event
+ this.dismiss_alarm = function(id, snooze)
+ {
+ $('#alarm-snooze-dropdown').hide();
+ rcmail.http_post('plugin.event', { action:'dismiss', e:{ id:id, snooze:snooze } });
+
+ // remove dismissed alarm from list
+ if (this.dismiss_link) {
+ this.dismiss_link.closest('div.alarm-item').hide();
+ var new_ids = jQuery.grep(this.alarm_ids, function(v){ return v != id; });
+ if (new_ids.length)
+ this.alarm_ids = new_ids;
+ else
+ this.alarm_dialog.dialog('close');
+ }
+
+ this.dismiss_link = null;
};
@@ -588,24 +652,22 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
}
// send move request to server
var data = {
- action: 'move',
id: event.id,
start: event.start.getTime()/1000,
end: event.end.getTime()/1000,
allday: allDay?1:0
};
- rcmail.http_post('plugin.event', { e:data });
+ rcmail.http_post('plugin.event', { action:'move', e:data });
},
// callback for event resizing
eventResize : function(event, delta) {
// send resize request to server
var data = {
- action: 'resize',
id: event.id,
start: event.start.getTime()/1000,
end: event.end.getTime()/1000,
};
- rcmail.http_post('plugin.event', { e:data });
+ rcmail.http_post('plugin.event', { action:'resize', e:data });
}
});
diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
index 07d25cac..884fbb60 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -111,6 +111,9 @@ class calendar extends rcube_plugin
}
}
+ /**
+ * Render the main calendar view from skin template
+ */
function calendar_view()
{
$this->rc->output->set_pagetitle($this->gettext('calendar'));
@@ -128,6 +131,7 @@ class calendar extends rcube_plugin
$this->register_handler('plugin.freebusy_select', array($this->ui, 'freebusy_select'));
$this->register_handler('plugin.priority_select', array($this->ui, 'priority_select'));
$this->register_handler('plugin.alarm_select', array($this->ui, 'alarm_select'));
+ $this->register_handler('plugin.snooze_select', array($this->ui, 'snooze_select'));
$this->register_handler('plugin.recurrence_form', array($this->ui, 'recurrence_form'));
$this->rc->output->set_env('calendar_settings', $this->load_settings());
@@ -321,39 +325,56 @@ class calendar extends rcube_plugin
return $p;
}
+ /**
+ * Dispatcher for event actions initiated by the client
+ */
function event()
{
+ $action = get_input_value('action', RCUBE_INPUT_POST);
$event = get_input_value('e', RCUBE_INPUT_POST);
- $success = false;
+ $success = $reload = false;
- switch ($event['action']) {
+ switch ($action) {
case "new":
- // create UID for new event
- $events['uid'] = strtoupper(md5(time() . uniqid(rand())) . '-' . substr(md5($this->rc->user->get_username()), 0, 16));
- $success = $this->driver->new_event($event);
- break;
+ // create UID for new event
+ $events['uid'] = strtoupper(md5(time() . uniqid(rand())) . '-' . substr(md5($this->rc->user->get_username()), 0, 16));
+ $success = $this->driver->new_event($event);
+ $reload = true;
+ break;
case "edit":
- $success = $this->driver->edit_event($event);
- break;
+ $success = $this->driver->edit_event($event);
+ $reload = true;
+ break;
case "resize":
- $success = $this->driver->resize_event($event);
- break;
+ $success = $this->driver->resize_event($event);
+ $reload = true;
+ break;
case "move":
- $success = $this->driver->move_event($event);
- break;
+ $success = $this->driver->move_event($event);
+ $reload = true;
+ break;
case "remove":
- $success = $this->driver->remove_event($event);
- break;
+ $success = $this->driver->remove_event($event);
+ $reload = true;
+ break;
+ case "dismiss":
+ foreach (explode(',', $event['id']) as $id)
+ $success |= $this->driver->dismiss_alarm($id, $event['snooze']);
+ break;
}
- if ($success) {
- $this->rc->output->command('plugin.reload_calendar', array());
- }
- else {
+ if (!$success) {
$this->rc->output->show_message('calendar.errorsaving', 'error');
}
+ else if ($reload) {
+ $this->rc->output->command('plugin.reload_calendar', array());
+ }
}
+ /**
+ * Handler for load-requests from fullcalendar
+ * This will return pure JSON formatted output
+ */
function load_events()
{
$events = $this->driver->load_events(get_input_value('start', RCUBE_INPUT_GET), get_input_value('end', RCUBE_INPUT_GET), get_input_value('source', RCUBE_INPUT_GET));
@@ -368,8 +389,8 @@ class calendar extends rcube_plugin
function keep_alive($attr)
{
$alarms = $this->driver->pending_alarms(time());
- #if ($alarms)
- # $this->rc->output->command('plugin.display_alarms', $this->_alarms_output($alarms));
+ if ($alarms)
+ $this->rc->output->command('plugin.display_alarms', $this->_alarms_output($alarms));
}
/**
@@ -523,7 +544,7 @@ class calendar extends rcube_plugin
*/
private function _alarms_text($alarm)
{
- list($action, $trigger) = explode(':', $alarm);
+ list($trigger, $action) = explode(':', $alarm);
$text = '';
switch ($action) {
diff --git a/plugins/calendar/drivers/calendar_driver.php b/plugins/calendar/drivers/calendar_driver.php
index db0592e1..40f235ba 100644
--- a/plugins/calendar/drivers/calendar_driver.php
+++ b/plugins/calendar/drivers/calendar_driver.php
@@ -28,6 +28,7 @@ abstract class calendar_driver
public $alarms = false;
public $attendees = false;
public $attachments = false;
+ public $alarm_types = array('DISPLAY');
/**
* Get a list of available calendars from this source
@@ -150,7 +151,7 @@ abstract class calendar_driver
* @param string Event identifier
* @param integer Suspend the alarm for this number of seconds
*/
- abstract function confirm_alarm($event_id, $snooze = 0);
+ abstract function dismiss_alarm($event_id, $snooze = 0);
/**
* Save an attachment related to the given event
diff --git a/plugins/calendar/drivers/database/database_driver.php b/plugins/calendar/drivers/database/database_driver.php
index 08d83e4f..2a7d309f 100644
--- a/plugins/calendar/drivers/database/database_driver.php
+++ b/plugins/calendar/drivers/database/database_driver.php
@@ -29,6 +29,7 @@ class database_driver extends calendar_driver
public $alarms = true;
public $attendees = true;
public $attachments = true;
+ public $alarm_types = array('DISPLAY','EMAIL');
private $rc;
private $cal;
@@ -229,7 +230,7 @@ class database_driver extends calendar_driver
// compute absolute time to notify the user
if ($event['alarms']) {
- list($action, $trigger) = explode(':', $event['alarms']);
+ list($trigger, $action) = explode(':', $event['alarms']);
$notify = calendar::parse_alaram_value($trigger);
if (!empty($notify[1])){ // offset
$mult = 1;
@@ -249,7 +250,8 @@ class database_driver extends calendar_driver
$notify_at = $notify[0];
}
- $event['notifyat'] = date('Y-m-d H:i:s', $notify_at);
+ if ($notify_at > time())
+ $event['notifyat'] = date('Y-m-d H:i:s', $notify_at);
}
else
$event['notifyat'] = null;
@@ -466,17 +468,12 @@ class database_driver extends calendar_driver
/**
* Feedback after showing/sending an alarm notification
*
- * @see Driver:confirm_alarm()
+ * @see Driver:dismiss_alarm()
*/
- public function confirm_alarm($event_id, $snooze = 0)
+ public function dismiss_alarm($event_id, $snooze = 0)
{
- // set new notifyat time
- if ($snooze > 0) {
- $event = $this->get_event($event_id);
- $notify_at = date('Y-m-d H:i:s', strtotime($event['notifyat']) + $snooze);
- }
- else // unset notifyat value
- $notify_at = null;
+ // set new notifyat time or unset if not snoozed
+ $notify_at = $snooze > 0 ? date('Y-m-d H:i:s', time() + $snooze) : null;
$query = $this->rc->db->query(sprintf(
"UPDATE " . $this->db_events . "
@@ -487,6 +484,7 @@ class database_driver extends calendar_driver
$notify_at,
$event_id
);
+
return $this->rc->db->affected_rows($query);
}
diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php
index 1a23da31..a4205bdc 100644
--- a/plugins/calendar/drivers/kolab/kolab_driver.php
+++ b/plugins/calendar/drivers/kolab/kolab_driver.php
@@ -236,9 +236,9 @@ class kolab_driver extends calendar_driver
/**
* Feedback after showing/sending an alarm notification
*
- * @see Driver:confirm_alarm()
+ * @see Driver:dismiss_alarm()
*/
- public function confirm_alarm($event_id, $snooze = 0)
+ public function dismiss_alarm($event_id, $snooze = 0)
{
// TBD.
return false;
diff --git a/plugins/calendar/lib/calendar_ui.php b/plugins/calendar/lib/calendar_ui.php
index afb622a2..4dbe335a 100644
--- a/plugins/calendar/lib/calendar_ui.php
+++ b/plugins/calendar/lib/calendar_ui.php
@@ -209,9 +209,9 @@ class calendar_ui
{
unset($attrib['name']);
$select_type = new html_select(array('name' => 'alarmtype[]', 'class' => 'edit-alarm-type'));
- $select_type->add(
- array($this->calendar->gettext('none'), $this->calendar->gettext('alarmdisplayoption'), $this->calendar->gettext('alarmemailoption')),
- array('','DISPLAY','EMAIL'));
+ $select_type->add($this->calendar->gettext('none'), '');
+ foreach ($this->calendar->driver->alarm_types as $type)
+ $select_type->add($this->calendar->gettext(strtolower("alarm{$type}option")), $type);
$input_value = new html_inputfield(array('name' => 'alarmvalue[]', 'class' => 'edit-alarm-value', 'size' => 3));
$input_date = new html_inputfield(array('name' => 'alarmdate[]', 'class' => 'edit-alarm-date', 'size' => 10));
@@ -241,6 +241,29 @@ class calendar_ui
return $html;
}
+ function snooze_select($attrib = array())
+ {
+ $steps = array(
+ 5 => 'repeatinmin',
+ 10 => 'repeatinmin',
+ 15 => 'repeatinmin',
+ 20 => 'repeatinmin',
+ 30 => 'repeatinmin',
+ 60 => 'repeatinhr',
+ 120 => 'repeatinhrs',
+ 1440 => 'repeattomorrow',
+ 10080 => 'repeatinweek',
+ );
+
+ $items = array();
+ foreach ($steps as $n => $label) {
+ $items[] = html::tag('li', null, html::a(array('href' => "#" . ($n * 60), 'class' => 'active'),
+ $this->calendar->gettext(array('name' => $label, 'vars' => array('min' => $n % 60, 'hrs' => intval($n / 60))))));
+ }
+
+ return html::tag('ul', $attrib, join("\n", $items));
+ }
+
/**
* Generate the form for recurrence settings
*/
diff --git a/plugins/calendar/localization/en_US.inc b/plugins/calendar/localization/en_US.inc
index 7cb773e1..bf274acb 100644
--- a/plugins/calendar/localization/en_US.inc
+++ b/plugins/calendar/localization/en_US.inc
@@ -65,6 +65,14 @@ $labels['trigger+D'] = 'days after';
$labels['addalarm'] = 'add alarm';
$labels['defaultalarmtype'] = 'Default reminder setting';
$labels['defaultalarmoffset'] = 'Default reminder time';
+$labels['dismissall'] = 'Dismiss all';
+$labels['dismiss'] = 'Dismiss';
+$labels['snooze'] = 'Snooze';
+$labels['repeatinmin'] = 'Repeat in $min minutes';
+$labels['repeatinhr'] = 'Repeat in 1 hour';
+$labels['repeatinhrs'] = 'Repeat in $hrs hours';
+$labels['repeattomorrow'] = 'Repeat tomorrow';
+$labels['repeatinweek'] = 'Repeat in a week';
$labels['alarmtitle'] = 'Upcoming events';
// event dialog tabs
diff --git a/plugins/calendar/skins/default/calendar.css b/plugins/calendar/skins/default/calendar.css
index 281b90c1..68f5b542 100644
--- a/plugins/calendar/skins/default/calendar.css
+++ b/plugins/calendar/skins/default/calendar.css
@@ -355,6 +355,26 @@ a.dropdown-link:after {
margin-bottom: 0.3em;
}
+.alarm-item .alarm-actions {
+ margin-top: 0.4em;
+}
+
+.alarm-item div.alarm-actions a {
+ color: #CC0000;
+ margin-right: 0.8em;
+ text-decoration: none;
+}
+
+a.alarm-action-snooze:after {
+ content: ' ▼';
+ font-size: 10px;
+ color: #666;
+}
+
+#alarm-snooze-dropdown {
+ z-index: 5000;
+}
+
.ui-dialog-buttonset a.dropdown-link {
margin-right: 1em;
}
diff --git a/plugins/calendar/skins/default/templates/calendar.html b/plugins/calendar/skins/default/templates/calendar.html
index 1b0a4e0f..7302caaf 100644
--- a/plugins/calendar/skins/default/templates/calendar.html
+++ b/plugins/calendar/skins/default/templates/calendar.html
@@ -153,6 +153,9 @@