From c51ba8d51dcedbc1036e4e15f86956915f50574c Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Tue, 31 May 2011 17:12:59 +0200 Subject: [PATCH] Handle exclusions in recurrences, improve textual description of recurrence rules --- plugins/calendar/calendar.php | 56 +++++++++++++++++-- plugins/calendar/drivers/calendar_driver.php | 1 + .../drivers/database/database_driver.php | 6 ++ plugins/calendar/lib/calendar_ui.php | 2 +- plugins/calendar/localization/en_US.inc | 2 +- 5 files changed, 59 insertions(+), 8 deletions(-) diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index 2b80e782..037e89e9 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -589,23 +589,34 @@ class calendar extends rcube_plugin private function _recurrence_text($rrule) { // TODO: finish this - $text = sprintf('%s %d ', $this->gettext('every'), $rrule['INTERVAL']); + $freq = sprintf('%s %d ', $this->gettext('every'), $rrule['INTERVAL']); + $details = ''; switch ($rrule['FREQ']) { case 'DAILY': - $text .= $this->gettext('days'); + $freq .= $this->gettext('days'); break; case 'WEEKLY': - $text .= $this->gettext('weeks'); + $freq .= $this->gettext('weeks'); break; case 'MONTHLY': - $text .= $this->gettext('months'); + $freq .= $this->gettext('months'); break; case 'YEARY': - $text .= $this->gettext('years'); + $freq .= $this->gettext('years'); break; } - return $text; + if ($rrule['INTERVAL'] == 1) + $freq = $this->gettext(strtolower($rrule['FREQ'])); + + if ($rrule['COUNT']) + $until = $this->gettext(array('name' => 'forntimes', 'vars' => array('nr' => $rrule['COUNT']))); + else if ($rrule['UNTIL']) + $until = $this->gettext('recurrencend') . ' ' . format_date($rrule['UNTIL'], self::to_php_date_format($this->rc->config->get('calendar_date_format'))); + else + $until = $this->gettext('forever'); + + return rtrim($freq . $details . ', ' . $until); } /** @@ -637,11 +648,44 @@ class calendar extends rcube_plugin case 'UNTIL': $val = gmdate('Ymd\THis', $val); break; + case 'EXDATE': + foreach ((array)$val as $i => $ex) + $val[$i] = gmdate('Ymd\THis', $ex); + $val = join(',', $val); + break; } $rrule .= $k . '=' . $val . ';'; } return $rrule; } + + /** + * Convert from fullcalendar date format to PHP date() format string + */ + private static function to_php_date_format($from) + { + // "dd.MM.yyyy HH:mm:ss" => "d.m.Y H:i:s" + return strtr($from, array( + 'yyyy' => 'Y', + 'yy' => 'y', + 'MMMM' => 'F', + 'MMM' => 'M', + 'MM' => 'm', + 'M' => 'n', + 'dddd' => 'l', + 'ddd' => 'D', + 'dd' => 'd', + 'HH' => 'H', + 'hh' => 'h', + 'mm' => 'i', + 'ss' => 's', + 'TT' => 'A', + 'tt' => 'a', + 'T' => 'A', + 't' => 'a', + 'u' => 'c', + )); + } } diff --git a/plugins/calendar/drivers/calendar_driver.php b/plugins/calendar/drivers/calendar_driver.php index d4a5e318..044a361b 100644 --- a/plugins/calendar/drivers/calendar_driver.php +++ b/plugins/calendar/drivers/calendar_driver.php @@ -42,6 +42,7 @@ * 'UNTIL' => , * 'COUNT' => 1..n, // number of times * // + more properties (see http://www.kanzaki.com/docs/ical/recur.html) + * 'EXDATE' => array(), // list of s of exception Dates/Times * ), * 'recurrence_id' => 'ID of the recurrence group', // usually the ID of the starting event * 'categories' => 'Event category', diff --git a/plugins/calendar/drivers/database/database_driver.php b/plugins/calendar/drivers/database/database_driver.php index 45623aa0..0378b35a 100644 --- a/plugins/calendar/drivers/database/database_driver.php +++ b/plugins/calendar/drivers/database/database_driver.php @@ -223,6 +223,7 @@ class database_driver extends calendar_driver { // compose vcalendar-style recurrencue rule from structured data $rrule = $event['recurrence'] ? calendar::to_rrule($event['recurrence']) : ''; + $event['_exdates'] = (array)$event['recurrence']['EXDATE']; $event['recurrence'] = rtrim($rrule, ';'); $event['free_busy'] = intval($this->free_busy_map[strtolower($event['free_busy'])]); $event['allday'] = $event['allday'] ? 1 : 0; @@ -288,6 +289,9 @@ class database_driver extends calendar_driver $recurrence = new Horde_Date_Recurrence($event['start']); $recurrence->fromRRule20($event['recurrence']); + foreach ((array)$event['_exdates'] as $exdate) + $recurrence->addException(date('Y', $exdate), date('n', $exdate), date('j', $exdate)); + $duration = $event['end'] - $event['start']; $next = new Horde_Date($event['start']); while ($next = $recurrence->nextActiveRecurrence(array('year' => $next->year, 'month' => $next->month, 'mday' => $next->mday + 1, 'hour' => $next->hour, 'min' => $next->min, 'sec' => $next->sec))) { @@ -483,6 +487,8 @@ class database_driver extends calendar_driver $rr[2] = intval($rr[2]); else if ($rr[1] == 'UNTIL') $rr[2] = strtotime($rr[2]); + else if ($rr[1] == 'EXDATE') + $rr[2] = array_map('strtotime', explode(',', $rr[2])); $event['recurrence'][$rr[1]] = $rr[2]; } } diff --git a/plugins/calendar/lib/calendar_ui.php b/plugins/calendar/lib/calendar_ui.php index 939aa38f..461a92ba 100644 --- a/plugins/calendar/lib/calendar_ui.php +++ b/plugins/calendar/lib/calendar_ui.php @@ -362,7 +362,7 @@ class calendar_ui $table = new html_table(array('cols' => 2, 'border' => 0, 'cellpadding' => 0, 'class' => 'formtable')); - $table->add('label', $this->calendar->gettext('recurrencend')); + $table->add('label', ucfirst($this->calendar->gettext('recurrencend'))); $table->add(null, html::label(null, $radio->show('', array('value' => '', 'id' => 'edit-recurrence-repeat-forever')) . ' ' . $this->calendar->gettext('forever'))); diff --git a/plugins/calendar/localization/en_US.inc b/plugins/calendar/localization/en_US.inc index 54bfe5b0..ec8947e8 100644 --- a/plugins/calendar/localization/en_US.inc +++ b/plugins/calendar/localization/en_US.inc @@ -108,7 +108,7 @@ $labels['until'] = 'the'; $labels['each'] = 'Each'; $labels['onevery'] = 'On every'; $labels['forever'] = 'forever'; -$labels['recurrencend'] = 'Until'; +$labels['recurrencend'] = 'until'; $labels['forntimes'] = 'for $nr time(s)'; $labels['first'] = 'first'; $labels['second'] = 'second';