Support reminders relative to end/due date (RELATED=END) (T656)
Conflicts: plugins/libcalendaring/libvcalendar.php
This commit is contained in:
parent
dc0b4bd39a
commit
326e4ad9ad
9 changed files with 109 additions and 44 deletions
|
@ -337,9 +337,10 @@ function rcube_libcalendaring(settings)
|
|||
$(this).parent().find('span.edit-alarm-values')[(this.selectedIndex>0?'show':'hide')]();
|
||||
});
|
||||
$(prefix+' select.edit-alarm-offset').change(function(){
|
||||
var val = $(this).val();
|
||||
$(this).parent().find('.edit-alarm-date, .edit-alarm-time')[val == '@' ? 'show' : 'hide']();
|
||||
$(this).parent().find('.edit-alarm-value').prop('disabled', val === '@' || val === '0');
|
||||
var val = $(this).val(), parent = $(this).parent();
|
||||
parent.find('.edit-alarm-date, .edit-alarm-time')[val == '@' ? 'show' : 'hide']();
|
||||
parent.find('.edit-alarm-value').prop('disabled', val === '@' || val === '0');
|
||||
parent.find('.edit-alarm-related')[val == '@' ? 'hide' : 'show']();
|
||||
});
|
||||
|
||||
$(prefix+' .edit-alarm-date').removeClass('hasDatepicker').removeAttr('id').datepicker(datepicker_settings);
|
||||
|
@ -389,6 +390,7 @@ function rcube_libcalendaring(settings)
|
|||
}
|
||||
|
||||
$('select.edit-alarm-type', domnode).val(alarm.action);
|
||||
$('select.edit-alarm-related', domnode).val(/END/i.test(alarm.related) ? 'end' : 'start');
|
||||
|
||||
if (String(alarm.trigger).match(/@(\d+)/)) {
|
||||
var ondate = this.fromunixtime(parseInt(RegExp.$1));
|
||||
|
@ -417,7 +419,11 @@ function rcube_libcalendaring(settings)
|
|||
var valarms = [];
|
||||
|
||||
$(prefix + ' .edit-alarm-item').each(function(i, elem) {
|
||||
var val, offset, alarm = { action: $('select.edit-alarm-type', elem).val() };
|
||||
var val, offset, alarm = {
|
||||
action: $('select.edit-alarm-type', elem).val(),
|
||||
related: $('select.edit-alarm-related', elem).val()
|
||||
};
|
||||
|
||||
if (alarm.action) {
|
||||
offset = $('select.edit-alarm-offset', elem).val();
|
||||
if (offset == '@') {
|
||||
|
|
|
@ -328,8 +328,10 @@ class libcalendaring extends rcube_plugin
|
|||
$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));
|
||||
$input_time = new html_inputfield(array('name' => 'alarmtime[]', 'class' => 'edit-alarm-time', 'size' => 6));
|
||||
$select_type = new html_select(array('name' => 'alarmtype[]', 'class' => 'edit-alarm-type', 'id' => $attrib['id']));
|
||||
$select_offset = new html_select(array('name' => 'alarmoffset[]', 'class' => 'edit-alarm-offset'));
|
||||
$select_type = new html_select(array('name' => 'alarmtype[]', 'class' => 'edit-alarm-type', 'id' => $attrib['id']));
|
||||
$select_offset = new html_select(array('name' => 'alarmoffset[]', 'class' => 'edit-alarm-offset'));
|
||||
$select_related = new html_select(array('name' => 'alarmrelated[]', 'class' => 'edit-alarm-related'));
|
||||
$object_type = $attrib['_type'] ?: 'event';
|
||||
|
||||
$select_type->add($this->gettext('none'), '');
|
||||
foreach ($alarm_types as $type)
|
||||
|
@ -342,6 +344,9 @@ class libcalendaring extends rcube_plugin
|
|||
if ($absolute_time)
|
||||
$select_offset->add($this->gettext('trigger@'), '@');
|
||||
|
||||
$select_related->add($this->gettext('relatedstart'), 'start');
|
||||
$select_related->add($this->gettext('relatedend' . $object_type), 'end');
|
||||
|
||||
// pre-set with default values from user settings
|
||||
$preset = self::parse_alarm_value($this->rc->config->get('calendar_default_alarm_offset', '-15M'));
|
||||
$hidden = array('style' => 'display:none');
|
||||
|
@ -350,6 +355,7 @@ class libcalendaring extends rcube_plugin
|
|||
html::span(array('class' => 'edit-alarm-values', 'style' => 'display:none'),
|
||||
$input_value->show($preset[0]) . ' ' .
|
||||
$select_offset->show($preset[1]) . ' ' .
|
||||
$select_related->show() . ' ' .
|
||||
$input_date->show('', $hidden) . ' ' .
|
||||
$input_time->show('', $hidden)
|
||||
)
|
||||
|
@ -527,10 +533,11 @@ class libcalendaring extends rcube_plugin
|
|||
}
|
||||
else {
|
||||
$trigger = $alarm['trigger'];
|
||||
$action = $alarm['action'];
|
||||
$action = $alarm['action'];
|
||||
$related = $alarm['related'];
|
||||
}
|
||||
|
||||
$text = '';
|
||||
$text = '';
|
||||
$rcube = rcube::get_instance();
|
||||
|
||||
switch ($action) {
|
||||
|
@ -558,11 +565,15 @@ class libcalendaring extends rcube_plugin
|
|||
));
|
||||
}
|
||||
else if ($val = self::parse_alarm_value($trigger)) {
|
||||
$r = strtoupper($related ?: 'start') == 'END' ? 'end' : '';
|
||||
// TODO: for all-day events say 'on date of event at XX' ?
|
||||
if ($val[0] == 0)
|
||||
$text .= ' ' . $rcube->gettext('libcalendaring.triggerattime');
|
||||
else
|
||||
$text .= ' ' . intval($val[0]) . ' ' . $rcube->gettext('libcalendaring.trigger' . $val[1]);
|
||||
if ($val[0] == 0) {
|
||||
$text .= ' ' . $rcube->gettext('libcalendaring.triggerattime' . $r);
|
||||
}
|
||||
else {
|
||||
$label = 'libcalendaring.trigger' . $r . $val[1];
|
||||
$text .= ' ' . intval($val[0]) . ' ' . $rcube->gettext($label);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
|
@ -601,7 +612,7 @@ class libcalendaring extends rcube_plugin
|
|||
}
|
||||
}
|
||||
|
||||
$expires = new DateTime('now - 12 hours');
|
||||
$expires = new DateTime('now - 12 hours');
|
||||
$alarm_id = $rec['id']; // alarm ID eq. record ID by default to keep backwards compatibility
|
||||
|
||||
// handle multiple alarms
|
||||
|
@ -613,8 +624,7 @@ class libcalendaring extends rcube_plugin
|
|||
$notify_time = $alarm['trigger'];
|
||||
}
|
||||
else if (is_string($alarm['trigger'])) {
|
||||
// $refdate = $alarm['trigger'][0] == '+' ? $rec['end'] : $rec['start'];
|
||||
$refdate = $rec['start'];
|
||||
$refdate = $alarm['related'] == 'END' ? $rec['end'] : $rec['start'];
|
||||
|
||||
// abort if no reference date is available to compute notification time
|
||||
if (!is_a($refdate, 'DateTime'))
|
||||
|
@ -624,7 +634,7 @@ class libcalendaring extends rcube_plugin
|
|||
|
||||
try {
|
||||
$interval = new DateInterval(trim($alarm['trigger'], '+-'));
|
||||
$interval->invert = $alarm['trigger'][0] != '+';
|
||||
$interval->invert = $alarm['trigger'][0] == '-';
|
||||
$notify_time = clone $refdate;
|
||||
$notify_time->add($interval);
|
||||
}
|
||||
|
|
|
@ -611,18 +611,23 @@ class libvcalendar implements Iterator
|
|||
|
||||
// find alarms
|
||||
foreach ($ve->select('VALARM') as $valarm) {
|
||||
$action = 'DISPLAY';
|
||||
$action = 'DISPLAY';
|
||||
$trigger = null;
|
||||
$alarm = array();
|
||||
$alarm = array();
|
||||
|
||||
foreach ($valarm->children as $prop) {
|
||||
$value = strval($prop);
|
||||
|
||||
switch ($prop->name) {
|
||||
case 'TRIGGER':
|
||||
if ($prop['VALUE'] == 'DATE-TIME') {
|
||||
$trigger = '@' . $prop->getDateTime()->format('U');
|
||||
$alarm['trigger'] = $prop->getDateTime();
|
||||
foreach ($prop->parameters as $param) {
|
||||
if ($param->name == 'VALUE' && $param->value == 'DATE-TIME') {
|
||||
$trigger = '@' . $prop->getDateTime()->format('U');
|
||||
$alarm['trigger'] = $prop->getDateTime();
|
||||
}
|
||||
else if ($param->name == 'RELATED') {
|
||||
$alarm['related'] = $param->value;
|
||||
}
|
||||
}
|
||||
if (!$trigger && ($values = libcalendaring::parse_alarm_value($value))) {
|
||||
$trigger = $values[2];
|
||||
|
@ -1111,7 +1116,11 @@ class libvcalendar implements Iterator
|
|||
$va->add($this->datetime_prop($cal, 'TRIGGER', $alarm['trigger'], true, null, true));
|
||||
}
|
||||
else {
|
||||
$va->add('TRIGGER', $alarm['trigger']);
|
||||
$alarm_props = array();
|
||||
if (strtoupper($alarm['related']) == 'END') {
|
||||
$alarm_props['RELATED'] = 'END';
|
||||
}
|
||||
$va->add('TRIGGER', $alarm['trigger'], $alarm_props);
|
||||
}
|
||||
|
||||
if ($alarm['action'] == 'EMAIL') {
|
||||
|
@ -1388,5 +1397,4 @@ class vobject_location_property extends VObject\Property\Text
|
|||
// iCalendar
|
||||
'REQUEST-STATUS',
|
||||
);
|
||||
|
||||
}
|
||||
|
|
|
@ -29,8 +29,18 @@ $labels['trigger-D'] = 'days before';
|
|||
$labels['trigger+M'] = 'minutes after';
|
||||
$labels['trigger+H'] = 'hours after';
|
||||
$labels['trigger+D'] = 'days after';
|
||||
$labels['triggerend-M'] = 'minutes before end';
|
||||
$labels['triggerend-H'] = 'hours before end';
|
||||
$labels['triggerend-D'] = 'days before end';
|
||||
$labels['triggerend+M'] = 'minutes after end';
|
||||
$labels['triggerend+H'] = 'hours after end';
|
||||
$labels['triggerend+D'] = 'days after end';
|
||||
$labels['trigger0'] = 'on time';
|
||||
$labels['triggerattime'] = 'at time';
|
||||
$labels['triggerattime'] = 'at start time';
|
||||
$labels['triggerattimeend'] = 'at end time';
|
||||
$labels['relatedstart'] = 'start';
|
||||
$labels['relatedendevent'] = 'end';
|
||||
$labels['relatedendtask'] = 'due time';
|
||||
$labels['addalarm'] = 'Add alarm';
|
||||
$labels['removealarm'] = 'Remove alarm';
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ class libcalendaring_test extends PHPUnit_Framework_TestCase
|
|||
$date = date('Ymd', strtotime('today + 2 days'));
|
||||
$event = array(
|
||||
'start' => new DateTime($date . 'T160000Z'),
|
||||
'end' => new DateTime($date . 'T180000Z'),
|
||||
'end' => new DateTime($date . 'T200000Z'),
|
||||
'valarms' => array(
|
||||
array(
|
||||
'trigger' => '-PT10M',
|
||||
|
@ -76,7 +76,7 @@ class libcalendaring_test extends PHPUnit_Framework_TestCase
|
|||
$this->assertEquals($event['valarms'][0]['action'], $alarm['action']);
|
||||
$this->assertEquals(strtotime($date . 'T155000Z'), $alarm['time']);
|
||||
|
||||
// alarm 1 hour after before event
|
||||
// alarm 1 hour after event start
|
||||
$event['valarms'] = array(
|
||||
array(
|
||||
'trigger' => '+PT1H',
|
||||
|
@ -84,8 +84,30 @@ class libcalendaring_test extends PHPUnit_Framework_TestCase
|
|||
);
|
||||
$alarm = libcalendaring::get_next_alarm($event);
|
||||
$this->assertEquals('DISPLAY', $alarm['action']);
|
||||
$this->assertEquals(strtotime($date . 'T170000Z'), $alarm['time']);
|
||||
|
||||
// alarm 1 hour before event end
|
||||
$event['valarms'] = array(
|
||||
array(
|
||||
'trigger' => '-PT1H',
|
||||
'related' => 'END',
|
||||
),
|
||||
);
|
||||
$alarm = libcalendaring::get_next_alarm($event);
|
||||
$this->assertEquals('DISPLAY', $alarm['action']);
|
||||
$this->assertEquals(strtotime($date . 'T190000Z'), $alarm['time']);
|
||||
|
||||
// alarm 1 hour after event end
|
||||
$event['valarms'] = array(
|
||||
array(
|
||||
'trigger' => 'PT1H',
|
||||
'related' => 'END',
|
||||
),
|
||||
);
|
||||
$alarm = libcalendaring::get_next_alarm($event);
|
||||
$this->assertEquals('DISPLAY', $alarm['action']);
|
||||
$this->assertEquals(strtotime($date . 'T210000Z'), $alarm['time']);
|
||||
|
||||
// ignore past alarms
|
||||
$event['start'] = new DateTime('today 22:00:00');
|
||||
$event['end'] = new DateTime('today 23:00:00');
|
||||
|
@ -159,6 +181,4 @@ class libcalendaring_test extends PHPUnit_Framework_TestCase
|
|||
$this->assertRegExp('/BYDAY='.$rrule['BYDAY'].'/', $s, "Recurrence BYDAY");
|
||||
$this->assertRegExp('/UNTIL=20250501T160000Z/', $s, "Recurrence End date (in UTC)");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -203,6 +203,7 @@ class libvcalendar_test extends PHPUnit_Framework_TestCase
|
|||
|
||||
$this->assertEquals('DISPLAY', $event['valarms'][0]['action'], "Full alarm item (action)");
|
||||
$this->assertEquals('-PT12H', $event['valarms'][0]['trigger'], "Full alarm item (trigger)");
|
||||
$this->assertEquals('END', $event['valarms'][0]['related'], "Full alarm item (related)");
|
||||
|
||||
// alarm trigger with 0 values
|
||||
$events = $ical->import_from_file(__DIR__ . '/resources/alarms.ics', 'UTF-8');
|
||||
|
@ -216,6 +217,7 @@ class libvcalendar_test extends PHPUnit_Framework_TestCase
|
|||
$this->assertEquals('-PT30M', $alarm[3], "Unified alarm string (stripped zero-values)");
|
||||
|
||||
$this->assertEquals('DISPLAY', $event['valarms'][0]['action'], "First alarm action");
|
||||
$this->assertEquals('', $event['valarms'][0]['related'], "First alarm related property");
|
||||
$this->assertEquals('This is the first event reminder', $event['valarms'][0]['description'], "First alarm text");
|
||||
|
||||
$this->assertEquals(3, count($event['valarms']), "List all VALARM blocks");
|
||||
|
@ -354,7 +356,7 @@ class libvcalendar_test extends PHPUnit_Framework_TestCase
|
|||
$this->assertEquals(100, $completed['complete'], "Task percent complete value");
|
||||
|
||||
$ics = $ical->export(array($completed));
|
||||
$this->assertRegExp('/COMPLETED:[0-9TZ]+/', $ics, "Export COMPLETED property");
|
||||
$this->assertRegExp('/COMPLETED(;VALUE=DATE-TIME)?:[0-9TZ]+/', $ics, "Export COMPLETED property");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -410,7 +412,7 @@ class libvcalendar_test extends PHPUnit_Framework_TestCase
|
|||
$this->assertRegExp('/EXDATE.*:20131218/', $ics, "Export Recurrence EXDATE");
|
||||
|
||||
$this->assertContains('BEGIN:VALARM', $ics, "Export VALARM");
|
||||
$this->assertContains('TRIGGER:-PT12H', $ics, "Export Alarm trigger");
|
||||
$this->assertContains('TRIGGER;RELATED=END:-PT12H', $ics, "Export Alarm trigger");
|
||||
|
||||
$this->assertRegExp('/ATTACH.*;VALUE=BINARY/', $ics, "Embed attachment");
|
||||
$this->assertRegExp('/ATTACH.*;ENCODING=BASE64/', $ics, "Attachment B64 encoding");
|
||||
|
|
|
@ -36,7 +36,7 @@ RRULE:FREQ=MONTHLY;INTERVAL=1;UNTIL=20140718T215959Z;BYDAY=3WE
|
|||
EXDATE;TZID=Europe/Zurich:20131218T080000
|
||||
EXDATE;TZID=Europe/Zurich:20140415T080000
|
||||
BEGIN:VALARM
|
||||
TRIGGER:-PT12H
|
||||
TRIGGER;RELATED=END:-PT12H
|
||||
ACTION:DISPLAY
|
||||
END:VALARM
|
||||
END:VEVENT
|
||||
|
|
|
@ -231,12 +231,12 @@ abstract class kolab_format_xcal extends kolab_format
|
|||
$object['valarms'] = array();
|
||||
for ($i=0; $i < $valarms->size(); $i++) {
|
||||
$alarm = $valarms->get($i);
|
||||
$type = $alarm_types[$alarm->type()];
|
||||
$type = $alarm_types[$alarm->type()];
|
||||
|
||||
if ($type == 'DISPLAY' || $type == 'EMAIL' || $type == 'AUDIO') { // only some alarms are supported
|
||||
$valarm = array(
|
||||
'action' => $type,
|
||||
'summary' => $alarm->summary(),
|
||||
'action' => $type,
|
||||
'summary' => $alarm->summary(),
|
||||
'description' => $alarm->description(),
|
||||
);
|
||||
|
||||
|
@ -254,12 +254,14 @@ abstract class kolab_format_xcal extends kolab_format
|
|||
}
|
||||
|
||||
if ($start = self::php_datetime($alarm->start())) {
|
||||
$object['alarms'] = '@' . $start->format('U');
|
||||
$object['alarms'] = '@' . $start->format('U');
|
||||
$valarm['trigger'] = $start;
|
||||
}
|
||||
else if ($offset = $alarm->relativeStart()) {
|
||||
$prefix = $alarm->relativeTo() == kolabformat::End ? '+' : '-';
|
||||
$value = $time = '';
|
||||
$prefix = $offset->isNegative() ? '-' : '+';
|
||||
$value = '';
|
||||
$time = '';
|
||||
|
||||
if ($w = $offset->weeks()) $value .= $w . 'W';
|
||||
else if ($d = $offset->days()) $value .= $d . 'D';
|
||||
else if ($h = $offset->hours()) $time .= $h . 'H';
|
||||
|
@ -269,23 +271,29 @@ abstract class kolab_format_xcal extends kolab_format
|
|||
// assume 'at event time'
|
||||
if (empty($value) && empty($time)) {
|
||||
$prefix = '';
|
||||
$time = '0S';
|
||||
$time = '0S';
|
||||
}
|
||||
|
||||
$object['alarms'] = $prefix . $value . $time;
|
||||
$object['alarms'] = $prefix . $value . $time;
|
||||
$valarm['trigger'] = $prefix . 'P' . $value . ($time ? 'T' . $time : '');
|
||||
|
||||
if ($alarm->relativeTo() == kolabformat::End) {
|
||||
$valarm['related'] == 'END';
|
||||
}
|
||||
}
|
||||
|
||||
// read alarm duration and repeat properties
|
||||
if (($duration = $alarm->duration()) && $duration->isValid()) {
|
||||
$value = $time = '';
|
||||
|
||||
if ($w = $duration->weeks()) $value .= $w . 'W';
|
||||
else if ($d = $duration->days()) $value .= $d . 'D';
|
||||
else if ($h = $duration->hours()) $time .= $h . 'H';
|
||||
else if ($m = $duration->minutes()) $time .= $m . 'M';
|
||||
else if ($s = $duration->seconds()) $time .= $s . 'S';
|
||||
|
||||
$valarm['duration'] = 'P' . $value . ($time ? 'T' . $time : '');
|
||||
$valarm['repeat'] = $alarm->numrepeat();
|
||||
$valarm['repeat'] = $alarm->numrepeat();
|
||||
}
|
||||
|
||||
$object['alarms'] .= ':' . $type; // legacy property
|
||||
|
@ -508,9 +516,8 @@ abstract class kolab_format_xcal extends kolab_format
|
|||
}
|
||||
else {
|
||||
try {
|
||||
$prefix = $valarm['trigger'][0];
|
||||
$period = new DateInterval(preg_replace('/[^0-9PTWDHMS]/', '', $valarm['trigger']));
|
||||
$duration = new Duration($period->d, $period->h, $period->i, $period->s, $prefix == '-');
|
||||
$period = new DateInterval(preg_replace('/[^0-9PTWDHMS]/', '', $valarm['trigger']));
|
||||
$duration = new Duration($period->d, $period->h, $period->i, $period->s, $valarm['trigger'][0] == '-');
|
||||
}
|
||||
catch (Exception $e) {
|
||||
// skip alarm with invalid trigger values
|
||||
|
@ -518,7 +525,8 @@ abstract class kolab_format_xcal extends kolab_format
|
|||
continue;
|
||||
}
|
||||
|
||||
$alarm->setRelativeStart($duration, $prefix == '-' ? kolabformat::Start : kolabformat::End);
|
||||
$related = strtoupper($valarm['related']) == 'END' ? kolabformat::End : kolabformat::Start;
|
||||
$alarm->setRelativeStart($duration, $related);
|
||||
}
|
||||
|
||||
if ($valarm['duration']) {
|
||||
|
|
|
@ -355,6 +355,7 @@ class tasklist_ui
|
|||
*/
|
||||
function alarm_select($attrib = array())
|
||||
{
|
||||
$attrib['_type'] = 'task';
|
||||
return $this->plugin->lib->alarm_select($attrib, $this->plugin->driver->alarm_types, $this->plugin->driver->alarm_absolute);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue