Replace Horde with Sabre in libcalendaring_recurrence
This commit is contained in:
parent
ee732ffb07
commit
e4271ccfd8
11 changed files with 182 additions and 3282 deletions
|
@ -3994,7 +3994,6 @@ $("#rcmfd_new_category").keypress(function(event) {
|
|||
$this->load_driver();
|
||||
|
||||
// Use libkolab to compute recurring events (and libkolab plugin)
|
||||
// Horde-based fallback has many bugs
|
||||
if (class_exists('kolabformat') && class_exists('kolabcalendaring') && class_exists('kolab_date_recurrence')) {
|
||||
$object = kolab_format::factory('event', 3.0);
|
||||
$object->set($event);
|
||||
|
@ -4002,9 +4001,8 @@ $("#rcmfd_new_category").keypress(function(event) {
|
|||
$recurrence = new kolab_date_recurrence($object);
|
||||
}
|
||||
else {
|
||||
// fallback to libcalendaring (Horde-based) recurrence implementation
|
||||
require_once(__DIR__ . '/lib/calendar_recurrence.php');
|
||||
$recurrence = new calendar_recurrence($this, $event);
|
||||
// fallback to libcalendaring recurrence implementation
|
||||
$recurrence = new libcalendaring_recurrence($this->lib, $event);
|
||||
}
|
||||
|
||||
return $recurrence->first_occurrence();
|
||||
|
|
|
@ -486,11 +486,8 @@ abstract class calendar_driver
|
|||
$events = [];
|
||||
|
||||
if (!empty($event['recurrence'])) {
|
||||
// include library class
|
||||
require_once(dirname(__FILE__) . '/../lib/calendar_recurrence.php');
|
||||
|
||||
$rcmail = rcmail::get_instance();
|
||||
$recurrence = new calendar_recurrence($rcmail->plugins->get_plugin('calendar'), $event);
|
||||
$rcmail = rcmail::get_instance();
|
||||
$recurrence = new libcalendaring_recurrence($rcmail->plugins->get_plugin('calendar')->lib, $event);
|
||||
$recurrence_id_format = libcalendaring::recurrence_id_format($event);
|
||||
|
||||
// determine a reasonable end date if none given
|
||||
|
|
|
@ -808,10 +808,7 @@ class database_driver extends calendar_driver
|
|||
|
||||
// create new fake entries
|
||||
if (!empty($event['recurrence'])) {
|
||||
// include library class
|
||||
require_once($this->cal->home . '/lib/calendar_recurrence.php');
|
||||
|
||||
$recurrence = new calendar_recurrence($this->cal, $event);
|
||||
$recurrence = new libcalendaring_recurrence($this->cal->lib, $event);
|
||||
$count = 0;
|
||||
$event['allday'] = $event['all_day'];
|
||||
$duration = $event['start']->diff($event['end']);
|
||||
|
|
|
@ -1,89 +0,0 @@
|
|||
<?php
|
||||
|
||||
require_once realpath(__DIR__ . '/../../libcalendaring/lib/libcalendaring_recurrence.php');
|
||||
|
||||
/**
|
||||
* Recurrence computation class for the Calendar plugin
|
||||
*
|
||||
* Uitility class to compute instances of recurring events.
|
||||
*
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2012-2014, Kolab Systems AG <contact@kolabsys.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
class calendar_recurrence extends libcalendaring_recurrence
|
||||
{
|
||||
private $event;
|
||||
private $duration;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*
|
||||
* @param calendar $cal The calendar plugin instance
|
||||
* @param array $event The event object to operate on
|
||||
*/
|
||||
function __construct($cal, $event)
|
||||
{
|
||||
parent::__construct($cal->lib);
|
||||
|
||||
$this->event = $event;
|
||||
|
||||
if (is_object($event['start']) && is_object($event['end'])) {
|
||||
$this->duration = $event['start']->diff($event['end']);
|
||||
}
|
||||
|
||||
$event['start']->_dateonly = !empty($event['allday']);
|
||||
|
||||
$this->init($event['recurrence'], $event['start']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of libcalendaring_recurrence::next()
|
||||
*
|
||||
* @return mixed DateTime object or False if recurrence ended
|
||||
*/
|
||||
public function next_start()
|
||||
{
|
||||
return $this->next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next recurring instance of this event
|
||||
*
|
||||
* @return mixed Array with event properties or False if recurrence ended
|
||||
*/
|
||||
public function next_instance()
|
||||
{
|
||||
if ($next_start = $this->next()) {
|
||||
$next = $this->event;
|
||||
$next['start'] = $next_start;
|
||||
|
||||
if ($this->duration) {
|
||||
$next['end'] = clone $next_start;
|
||||
$next['end']->add($this->duration);
|
||||
}
|
||||
|
||||
$next['recurrence_date'] = clone $next_start;
|
||||
$next['_instance'] = libcalendaring::recurrence_instance_identifier($next, $this->event['allday']);
|
||||
|
||||
unset($next['_formatobj']);
|
||||
|
||||
return $next;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,13 +1,15 @@
|
|||
<?php
|
||||
|
||||
use Sabre\VObject\Recur\EventIterator;
|
||||
|
||||
/**
|
||||
* Recurrence computation class for shared use
|
||||
* Recurrence computation class for shared use.
|
||||
*
|
||||
* Uitility class to compute reccurrence dates from the given rules
|
||||
* Uitility class to compute recurrence dates from the given rules.
|
||||
*
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
* @author Aleksander Machniak <machniak@apheleia-it.ch
|
||||
*
|
||||
* Copyright (C) 2012-2014, Kolab Systems AG <contact@kolabsys.com>
|
||||
* Copyright (C) 2012-2022, Apheleia IT AG <contact@apheleia-it.ch>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
|
@ -26,24 +28,34 @@ class libcalendaring_recurrence
|
|||
{
|
||||
protected $lib;
|
||||
protected $start;
|
||||
protected $next;
|
||||
protected $engine;
|
||||
protected $recurrence;
|
||||
protected $dateonly = false;
|
||||
protected $hour = 0;
|
||||
protected $event;
|
||||
protected $duration;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*
|
||||
* @param calendar The calendar plugin instance
|
||||
* @param libcalendaring $lib The libcalendaring plugin instance
|
||||
* @param array $event The event object to operate on
|
||||
*/
|
||||
function __construct($lib)
|
||||
function __construct($lib, $event = null)
|
||||
{
|
||||
// use Horde classes to compute recurring instances
|
||||
// TODO: replace with something that has less than 6'000 lines of code
|
||||
require_once(__DIR__ . '/Horde_Date_Recurrence.php');
|
||||
$this->lib = $lib;
|
||||
$this->event = $event;
|
||||
|
||||
$this->lib = $lib;
|
||||
if (!empty($event)) {
|
||||
if (!empty($event['start']) && is_object($event['start'])
|
||||
&& !empty($event['end']) && is_object($event['end'])
|
||||
) {
|
||||
$this->duration = $event['start']->diff($event['end']);
|
||||
}
|
||||
|
||||
$event['start']->_dateonly = !empty($event['allday']);
|
||||
|
||||
$this->init($event['recurrence'], $event['start']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -52,121 +64,117 @@ class libcalendaring_recurrence
|
|||
* @param array The recurrence properties
|
||||
* @param DateTime The recurrence start date
|
||||
*/
|
||||
public function init($recurrence, $start = null)
|
||||
public function init($recurrence, $start)
|
||||
{
|
||||
$this->start = $start;
|
||||
$this->dateonly = !empty($start->_dateonly);
|
||||
$this->recurrence = $recurrence;
|
||||
|
||||
$this->engine = new Horde_Date_Recurrence($start);
|
||||
$this->engine->fromRRule20(libcalendaring::to_rrule($recurrence));
|
||||
$event = [
|
||||
'uid' => '1',
|
||||
'allday' => $this->dateonly,
|
||||
'recurrence' => $recurrence,
|
||||
'start' => $start,
|
||||
// TODO: END/DURATION ???
|
||||
// TODO: moved occurrences ???
|
||||
];
|
||||
|
||||
$this->set_start($start);
|
||||
$vcalendar = new libcalendaring_vcalendar($this->lib->timezone);
|
||||
|
||||
if (!empty($recurrence['EXDATE'])) {
|
||||
foreach ((array) $recurrence['EXDATE'] as $exdate) {
|
||||
if ($exdate instanceof DateTimeInterface) {
|
||||
$this->engine->addException($exdate->format('Y'), $exdate->format('n'), $exdate->format('j'));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($recurrence['RDATE'])) {
|
||||
foreach ((array) $recurrence['RDATE'] as $rdate) {
|
||||
if ($rdate instanceof DateTimeInterface) {
|
||||
$this->engine->addRDate($rdate->format('Y'), $rdate->format('n'), $rdate->format('j'));
|
||||
}
|
||||
}
|
||||
}
|
||||
$ve = $vcalendar->toSabreComponent($event);
|
||||
|
||||
$this->engine = new EventIterator($ve, null, $this->lib->timezone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for (new) recurrence start date
|
||||
*
|
||||
* @param DateTime The recurrence start date
|
||||
*/
|
||||
public function set_start($start)
|
||||
{
|
||||
$this->start = $start;
|
||||
$this->dateonly = !empty($start->_dateonly);
|
||||
$this->next = new Horde_Date($start, $this->lib->timezone->getName());
|
||||
$this->hour = $this->next->hour;
|
||||
$this->engine->setRecurStart($this->next);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get date/time of the next occurence of this event
|
||||
* Get date/time of the next occurence of this event, and push the iterator.
|
||||
*
|
||||
* @return DateTime|false object or False if recurrence ended
|
||||
*/
|
||||
public function next()
|
||||
public function next_start()
|
||||
{
|
||||
$time = false;
|
||||
$after = clone $this->next;
|
||||
$after->mday = $after->mday + 1;
|
||||
|
||||
if ($this->next && ($next = $this->engine->nextActiveRecurrence($after))) {
|
||||
// avoid endless loops if recurrence computation fails
|
||||
if (!$next->after($this->next)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// fix time for all-day events
|
||||
if ($this->dateonly) {
|
||||
$next->hour = $this->hour;
|
||||
$next->min = 0;
|
||||
}
|
||||
|
||||
$this->next = $next;
|
||||
|
||||
$time = $this->toDateTime($next);
|
||||
try {
|
||||
$this->engine->next();
|
||||
$current = $this->engine->getDtStart();
|
||||
}
|
||||
catch (Exception $e) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
return $time;
|
||||
return $current ? $this->toDateTime($current) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the end date of the occurence of this recurrence cycle
|
||||
* Get the next recurring instance of this event
|
||||
*
|
||||
* @return DateTime|bool End datetime of the last occurence or False if recurrence exceeds limit
|
||||
* @return array|false Array with event properties or False if recurrence ended
|
||||
*/
|
||||
public function next_instance()
|
||||
{
|
||||
if ($next_start = $this->next_start()) {
|
||||
$next = $this->event;
|
||||
$next['start'] = $next_start;
|
||||
|
||||
if ($this->duration) {
|
||||
$next['end'] = clone $next_start;
|
||||
$next['end']->add($this->duration);
|
||||
}
|
||||
|
||||
$next['recurrence_date'] = clone $next_start;
|
||||
$next['_instance'] = libcalendaring::recurrence_instance_identifier($next, !empty($this->event['allday']));
|
||||
|
||||
unset($next['_formatobj']);
|
||||
|
||||
return $next;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the date of the end of the last occurrence of this recurrence cycle
|
||||
*
|
||||
* @return DateTime|false End datetime of the last occurrence or False if there's no end date
|
||||
*/
|
||||
public function end()
|
||||
{
|
||||
// recurrence end date is given
|
||||
if ($this->recurrence['UNTIL'] instanceof DateTimeInterface) {
|
||||
if (isset($this->recurrence['UNTIL']) && $this->recurrence['UNTIL'] instanceof DateTimeInterface) {
|
||||
return $this->recurrence['UNTIL'];
|
||||
}
|
||||
|
||||
// take the last RDATE entry if set
|
||||
if (is_array($this->recurrence['RDATE']) && !empty($this->recurrence['RDATE'])) {
|
||||
$last = end($this->recurrence['RDATE']);
|
||||
if ($last instanceof DateTimeInterface) {
|
||||
return $last;
|
||||
if (!$this->engine->isInfinite()) {
|
||||
// run through all items till we reach the end
|
||||
try {
|
||||
foreach ($this->engine as $end) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
catch (Exception $e) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
// run through all items till we reach the end
|
||||
if ($this->recurrence['COUNT']) {
|
||||
$last = $this->start;
|
||||
$this->next = new Horde_Date($this->start, $this->lib->timezone->getName());
|
||||
while (($next = $this->next()) && $c < 1000) {
|
||||
$last = $next;
|
||||
$c++;
|
||||
}
|
||||
else if (isset($this->event['end']) && $this->event['end'] instanceof DateTimeInterface) {
|
||||
// determine a reasonable end date if none given
|
||||
$end = clone $this->event['end'];
|
||||
$end->add(new DateInterval('P100Y'));
|
||||
}
|
||||
|
||||
return $last;
|
||||
return isset($end) ? $this->toDateTime($end, false) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find date/time of the first occurrence (excluding start date)
|
||||
*
|
||||
* @return DateTime|null First occurrence
|
||||
*/
|
||||
public function first_occurrence()
|
||||
{
|
||||
$start = clone $this->start;
|
||||
$orig_start = clone $this->start;
|
||||
$r = $this->recurrence;
|
||||
$interval = !empty($r['INTERVAL']) ? intval($r['INTERVAL']) : 1;
|
||||
$frequency = isset($this->recurrence['FREQ']) ? $this->recurrence['FREQ'] : null;
|
||||
$start = clone $this->start;
|
||||
$interval = $this->recurrence['INTERVAL'] ?? 1;
|
||||
$freq = $this->recurrence['FREQ'] ?? null;
|
||||
|
||||
switch ($frequency) {
|
||||
switch ($freq) {
|
||||
case 'WEEKLY':
|
||||
if (empty($this->recurrence['BYDAY'])) {
|
||||
return $start;
|
||||
|
@ -191,63 +199,80 @@ class libcalendaring_recurrence
|
|||
$start->sub(new DateInterval("P{$interval}Y"));
|
||||
break;
|
||||
|
||||
case 'DAILY':
|
||||
if (!empty($this->recurrence['BYMONTH'])) {
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return $start;
|
||||
}
|
||||
|
||||
$r = $this->recurrence;
|
||||
$r['INTERVAL'] = $interval;
|
||||
if (!empty($r['COUNT'])) {
|
||||
$recurrence = $this->recurrence;
|
||||
|
||||
if (!empty($recurrence['COUNT'])) {
|
||||
// Increase count so we do not stop the loop to early
|
||||
$r['COUNT'] += 100;
|
||||
$recurrence['COUNT'] += 100;
|
||||
}
|
||||
|
||||
// Create recurrence that starts in the past
|
||||
$recurrence = new self($this->lib);
|
||||
$recurrence->init($r, $start);
|
||||
$self = new self($this->lib);
|
||||
$self->init($recurrence, $start);
|
||||
|
||||
// TODO: This method does not work the same way as the kolab_date_recurrence based on
|
||||
// kolabcalendaring. I.e. if an event start date does not match the recurrence rule
|
||||
// it will be returned, kolab_date_recurrence will return the next occurrence in such a case
|
||||
// which is the intended result of this function.
|
||||
// See some commented out test cases in tests/RecurrenceTest.php
|
||||
|
||||
// find the first occurrence
|
||||
$found = false;
|
||||
while ($next = $recurrence->next()) {
|
||||
while ($next = $self->next_start()) {
|
||||
$start = $next;
|
||||
if ($next >= $orig_start) {
|
||||
if ($next >= $this->start) {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$found) {
|
||||
rcube::raise_error(array(
|
||||
'file' => __FILE__,
|
||||
'line' => __LINE__,
|
||||
'message' => sprintf("Failed to find a first occurrence. Start: %s, Recurrence: %s",
|
||||
$orig_start->format(DateTime::ISO8601), json_encode($r)),
|
||||
), true);
|
||||
rcube::raise_error(
|
||||
[
|
||||
'file' => __FILE__, 'line' => __LINE__,
|
||||
'message' => sprintf("Failed to find a first occurrence. Start: %s, Recurrence: %s",
|
||||
$this->start->format(DateTime::ISO8601), json_encode($recurrence)),
|
||||
],
|
||||
true
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$start = $this->toDateTime($start);
|
||||
|
||||
return $start;
|
||||
return $this->toDateTime($start);
|
||||
}
|
||||
|
||||
private function toDateTime($date)
|
||||
/**
|
||||
* Convert any DateTime into libcalendaring_datetime
|
||||
*/
|
||||
protected function toDateTime($date, $useStart = true)
|
||||
{
|
||||
if ($date Instanceof Horde_Date) {
|
||||
$date = $date->toDateTime();
|
||||
}
|
||||
|
||||
if ($date instanceof DateTimeInterface) {
|
||||
$date = libcalendaring_datetime::createFromFormat(
|
||||
'Y-m-d\\TH:i:s',
|
||||
$date->format('Y-m-d\\TH:i:s'),
|
||||
$date->getTimezone()
|
||||
// Sabre will loose timezone on all-day events, use the event start's timezone
|
||||
$this->start->getTimezone()
|
||||
);
|
||||
}
|
||||
|
||||
$date->_dateonly = $this->dateonly;
|
||||
|
||||
if ($useStart && $this->dateonly) {
|
||||
// Sabre sets time to 00:00:00 for all-day events,
|
||||
// let's copy the time from the event's start
|
||||
$date->setTime((int) $this->start->format('H'), (int) $this->start->format('i'), (int) $this->start->format('s'));
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1043,6 +1043,23 @@ class libcalendaring_vcalendar implements Iterator
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts internal event representation to Sabre component
|
||||
*
|
||||
* @param array Event
|
||||
* @param callable Callback function to fetch attachment contents, false if no attachment export
|
||||
*
|
||||
* @return Sabre\VObject\Component\VEvent Sabre component
|
||||
*/
|
||||
public function toSabreComponent($object, $get_attachment = false)
|
||||
{
|
||||
$vcal = new VObject\Component\VCalendar();
|
||||
|
||||
$this->_to_ical($object, $vcal, $get_attachment);
|
||||
|
||||
return $vcal->getBaseComponent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a valid iCal format block from the given event
|
||||
*
|
||||
|
@ -1097,11 +1114,14 @@ class libcalendaring_vcalendar implements Iterator
|
|||
$ve->add($recurrence_id);
|
||||
}
|
||||
|
||||
$ve->add('SUMMARY', $event['title']);
|
||||
if (!empty($event['title'])) {
|
||||
$ve->add('SUMMARY', $event['title']);
|
||||
}
|
||||
|
||||
if (!empty($event['location'])) {
|
||||
$ve->add($this->is_apple() ? new vobject_location_property($cal, 'LOCATION', $event['location']) : $cal->create('LOCATION', $event['location']));
|
||||
}
|
||||
|
||||
if (!empty($event['description'])) {
|
||||
$ve->add('DESCRIPTION', strtr($event['description'], array("\r\n" => "\n", "\r" => "\n"))); // normalize line endings
|
||||
}
|
||||
|
|
|
@ -250,23 +250,24 @@ class RecurrenceTest extends PHPUnit\Framework\TestCase
|
|||
}
|
||||
|
||||
/**
|
||||
* Test for libcalendaring_recurrence::next()
|
||||
* Test for libcalendaring_recurrence::next_instance()
|
||||
*/
|
||||
function test_next_instance()
|
||||
{
|
||||
date_default_timezone_set('America/New_York');
|
||||
|
||||
$start = new libcalendaring_datetime('2017-08-31 11:00:00', new DateTimeZone('Europe/Berlin'));
|
||||
$start->_dateonly = true;
|
||||
$event = [
|
||||
'start' => $start,
|
||||
'recurrence' => ['FREQ' => 'WEEKLY', 'INTERVAL' => '1'],
|
||||
'allday' => true,
|
||||
];
|
||||
|
||||
$recurrence = $this->plugin->get_recurrence();
|
||||
$recurrence = new libcalendaring_recurrence($this->plugin, $event);
|
||||
$next = $recurrence->next_instance();
|
||||
|
||||
$recurrence->init(['FREQ' => 'WEEKLY', 'INTERVAL' => '1'], $start);
|
||||
|
||||
$next = $recurrence->next();
|
||||
|
||||
$this->assertEquals($start->format('2017-09-07 H:i:s'), $next->format('Y-m-d H:i:s'), 'Same time');
|
||||
$this->assertEquals($start->getTimezone()->getName(), $next->getTimezone()->getName(), 'Same timezone');
|
||||
$this->assertTrue($next->_dateonly, '_dateonly flag');
|
||||
$this->assertEquals($start->format('2017-09-07 H:i:s'), $next['start']->format('Y-m-d H:i:s'), 'Same time');
|
||||
$this->assertEquals($start->getTimezone()->getName(), $next['start']->getTimezone()->getName(), 'Same timezone');
|
||||
$this->assertTrue($next['start']->_dateonly, '_dateonly flag');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,7 +119,7 @@ class kolab_date_recurrence
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the end date of the occurence of this recurrence cycle
|
||||
* Get the end date of the last occurence of this recurrence cycle
|
||||
*
|
||||
* @return DateTime|bool End datetime of the last event or False if recurrence exceeds limit
|
||||
*/
|
||||
|
@ -152,6 +152,8 @@ class kolab_date_recurrence
|
|||
|
||||
/**
|
||||
* Find date/time of the first occurrence
|
||||
*
|
||||
* @return DateTime|null First occurrence
|
||||
*/
|
||||
public function first_occurrence()
|
||||
{
|
||||
|
|
|
@ -701,7 +701,7 @@ class tasklist extends rcube_plugin
|
|||
$rec['recurrence'] = $this->lib->from_client_recurrence($rec['recurrence'], $refdate);
|
||||
|
||||
// translate count into an absolute end date.
|
||||
// why? because when shifting completed tasks to the next recurrence,
|
||||
// why? because when shifting completed tasks to the next occurrence,
|
||||
// the initial start date to count from gets lost.
|
||||
if (!empty($rec['recurrence']['COUNT'])) {
|
||||
$engine = libcalendaring::get_recurrence();
|
||||
|
@ -831,7 +831,7 @@ class tasklist extends rcube_plugin
|
|||
|
||||
$date = new DateTime($rec[$date_key] . ' ' . $rec[$time_key], $this->timezone);
|
||||
$engine->init($rrule, $date);
|
||||
if ($next = $engine->next()) {
|
||||
if ($next = $engine->next_start()) {
|
||||
$updates[$date_key] = $next->format('Y-m-d');
|
||||
if (!empty($rec[$time_key]))
|
||||
$updates[$time_key] = $next->format('H:i');
|
||||
|
@ -846,7 +846,7 @@ class tasklist extends rcube_plugin
|
|||
foreach ($rec['valarms'] as $i => $alarm) {
|
||||
if ($alarm['trigger'] instanceof DateTime) {
|
||||
$engine->init($rrule, $alarm['trigger']);
|
||||
if ($next = $engine->next()) {
|
||||
if ($next = $engine->next_start()) {
|
||||
$alarm['trigger'] = $next;
|
||||
}
|
||||
}
|
||||
|
@ -1382,7 +1382,7 @@ class tasklist extends rcube_plugin
|
|||
|
||||
// check task occurrences (stop next week)
|
||||
// FIXME: is there a faster way of doing this?
|
||||
while ($date = $engine->next()) {
|
||||
while ($date = $engine->next_start()) {
|
||||
$date = $date->format('Y-m-d');
|
||||
|
||||
// break iteration asap
|
||||
|
|
Loading…
Add table
Reference in a new issue