Use Sabre/VObject v4, Partial PHP8 support

This commit is contained in:
Aleksander Machniak 2022-11-29 15:54:43 +01:00
parent 48027bc26e
commit f5d95d5a92
31 changed files with 274 additions and 215 deletions

View file

@ -1312,7 +1312,7 @@ $("#rcmfd_new_category").keypress(function(event) {
if (!empty($change['date'])) { if (!empty($change['date'])) {
$dt = $lib->adjust_timezone($change['date']); $dt = $lib->adjust_timezone($change['date']);
if ($dt instanceof DateTime) { if ($dt instanceof DateTimeInterface) {
$change['date'] = $this->rc->format_date($dt, $dtformat, false); $change['date'] = $this->rc->format_date($dt, $dtformat, false);
} }
} }

View file

@ -24,7 +24,7 @@
} }
], ],
"require": { "require": {
"php": ">=5.4.0", "php": ">=7.4.0",
"roundcube/plugin-installer": ">=0.1.3", "roundcube/plugin-installer": ">=0.1.3",
"kolab/libcalendaring": ">=3.4.0", "kolab/libcalendaring": ">=3.4.0",
"kolab/libkolab": ">=3.4.0" "kolab/libkolab": ">=3.4.0"

View file

@ -745,12 +745,14 @@ class caldav_calendar extends kolab_storage_dav_folder
// Modify invitation status class name, when invitation calendars are disabled // Modify invitation status class name, when invitation calendars are disabled
// we'll use opacity only for declined/needs-action events // we'll use opacity only for declined/needs-action events
$record['className'] = str_replace('-invitation', '', $record['className']); $record['className'] = !empty($record['className']) ? str_replace('-invitation', '', $record['className']) : '';
} }
// add instance identifier to first occurrence (master event) // add instance identifier to first occurrence (master event)
$recurrence_id_format = libcalendaring::recurrence_id_format($master_event ? $master_event : $record); $recurrence_id_format = libcalendaring::recurrence_id_format($master_event ? $master_event : $record);
if (!$noinst && !empty($record['recurrence']) && empty($record['recurrence_id']) && empty($record['_instance'])) { if (!$noinst && !empty($record['recurrence']) && !empty($record['start'])
&& empty($record['recurrence_id']) && empty($record['_instance'])
) {
$record['_instance'] = $record['start']->format($recurrence_id_format); $record['_instance'] = $record['start']->format($recurrence_id_format);
} }
else if (isset($record['recurrence_date']) && is_a($record['recurrence_date'], 'DateTime')) { else if (isset($record['recurrence_date']) && is_a($record['recurrence_date'], 'DateTime')) {

View file

@ -136,6 +136,7 @@ class caldav_driver extends kolab_driver
$parents = array_keys($this->calendars); $parents = array_keys($this->calendars);
foreach ($folders as $id => $cal) { foreach ($folders as $id => $cal) {
$parent_id = null;
/* /*
$path = explode('/', $cal->name); $path = explode('/', $cal->name);

View file

@ -790,8 +790,8 @@ abstract class calendar_driver
try { try {
$bday = $contact['birthday']; $bday = $contact['birthday'];
if (!$bday instanceof DateTime) { if (!$bday instanceof DateTimeInterface) {
$bday = new DateTime($bday, new DateTimezone('UTC')); $bday = new DateTime($bday, new DateTimeZone('UTC'));
} }
$bday->_dateonly = true; $bday->_dateonly = true;
} }

View file

@ -587,7 +587,8 @@ class database_driver extends calendar_driver
$b = isset($event[$prop]) ? $event[$prop] : null; $b = isset($event[$prop]) ? $event[$prop] : null;
if (!empty($event['allday']) && ($prop == 'start' || $prop == 'end') if (!empty($event['allday']) && ($prop == 'start' || $prop == 'end')
&& $a instanceof DateTime && $b instanceof DateTime && $a instanceof DateTimeInterface
&& $b instanceof DateTimeInterface
) { ) {
$a = $a->format('Y-m-d'); $a = $a->format('Y-m-d');
$b = $b->format('Y-m-d'); $b = $b->format('Y-m-d');
@ -1464,7 +1465,7 @@ class database_driver extends calendar_driver
private function serialize_alarms($valarms) private function serialize_alarms($valarms)
{ {
foreach ((array)$valarms as $i => $alarm) { foreach ((array)$valarms as $i => $alarm) {
if ($alarm['trigger'] instanceof DateTime) { if ($alarm['trigger'] instanceof DateTimeInterface) {
$valarms[$i]['trigger'] = '@' . $alarm['trigger']->format('c'); $valarms[$i]['trigger'] = '@' . $alarm['trigger']->format('c');
} }
} }

View file

@ -1339,7 +1339,7 @@ class kolab_driver extends calendar_driver
$recurrence_id = rcube_utils::anytodatetime($exception['_instance'], $old['start']->getTimezone()); $recurrence_id = rcube_utils::anytodatetime($exception['_instance'], $old['start']->getTimezone());
} }
if ($recurrence_id instanceof DateTime) { if ($recurrence_id instanceof DateTimeInterface) {
$recurrence_id->add($date_shift); $recurrence_id->add($date_shift);
$event['recurrence']['EXCEPTIONS'][$i]['recurrence_date'] = $recurrence_id; $event['recurrence']['EXCEPTIONS'][$i]['recurrence_date'] = $recurrence_id;
$event['recurrence']['EXCEPTIONS'][$i]['_instance'] = $recurrence_id->format($recurrence_id_format); $event['recurrence']['EXCEPTIONS'][$i]['_instance'] = $recurrence_id->format($recurrence_id_format);
@ -1604,13 +1604,13 @@ class kolab_driver extends calendar_driver
public static function merge_exception_dates(&$event, $overlay) public static function merge_exception_dates(&$event, $overlay)
{ {
// compute date offset from the exception // compute date offset from the exception
if ($overlay['start'] instanceof DateTime && $overlay['recurrence_date'] instanceof DateTime) { if ($overlay['start'] instanceof DateTimeInterface && $overlay['recurrence_date'] instanceof DateTimeInterface) {
$date_offset = $overlay['recurrence_date']->diff($overlay['start']); $date_offset = $overlay['recurrence_date']->diff($overlay['start']);
} }
foreach (['start', 'end'] as $prop) { foreach (['start', 'end'] as $prop) {
$value = $overlay[$prop]; $value = $overlay[$prop];
if (isset($event[$prop]) && $event[$prop] instanceof DateTime) { if (isset($event[$prop]) && $event[$prop] instanceof DateTimeInterface) {
// set date value if overlay is an exception of the current instance // set date value if overlay is an exception of the current instance
if (substr($overlay['_instance'], 0, 8) == substr($event['_instance'], 0, 8)) { if (substr($overlay['_instance'], 0, 8) == substr($event['_instance'], 0, 8)) {
$event[$prop]->setDate(intval($value->format('Y')), intval($value->format('n')), intval($value->format('j'))); $event[$prop]->setDate(intval($value->format('Y')), intval($value->format('n')), intval($value->format('j')));

View file

@ -11,6 +11,6 @@ Provides utility functions for calendar-related modules such as
iCal parsing and exporting is done with the help of the Sabre VObject iCal parsing and exporting is done with the help of the Sabre VObject
library [1]. It needs to be insalled with Roundcube using composer: library [1]. It needs to be insalled with Roundcube using composer:
$ composer require "sabre/vobject" "~3.3.3" $ composer require "sabre/vobject" "~4.5.1"
[1]: http://sabre.io/vobject/ [1]: http://sabre.io/vobject/

View file

@ -24,8 +24,8 @@
} }
], ],
"require": { "require": {
"php": ">=5.4.0", "php": ">=7.4.0",
"roundcube/plugin-installer": ">=0.1.3", "roundcube/plugin-installer": ">=0.1.3",
"sabre/vobject": "~3.5.3" "sabre/vobject": "~4.5.1"
} }
} }

View file

@ -0,0 +1,29 @@
<?php
/**
* DateTime wrapper. Main reason for its existence is that
* you can't set undefined properties on DateTime without
* a deprecation warning on PHP >= 8.1
*
* @author Aleksander Machniak <machniak@apheleia-it.ch>
*
* Copyright (C) 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
* 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 libcalendaring_datetime extends DateTime
{
public $_dateonly = false;
}

View file

@ -472,7 +472,7 @@ class libcalendaring_itip
if ($key == 'allday') { if ($key == 'allday') {
$event[$key] = $event[$key] == 'true'; $event[$key] = $event[$key] == 'true';
} }
$value = $existing[$key] instanceof DateTime ? $existing[$key]->format('c') : $existing[$key]; $value = $existing[$key] instanceof DateTimeInterface ? $existing[$key]->format('c') : $existing[$key];
$num++; $num++;
$got += intval($value == $event[$key]); $got += intval($value == $event[$key]);
} }
@ -671,7 +671,7 @@ class libcalendaring_itip
// For replies we need more metadata // For replies we need more metadata
foreach (array('start', 'end', 'due', 'allday', 'recurrence', 'location') as $key) { foreach (array('start', 'end', 'due', 'allday', 'recurrence', 'location') as $key) {
if (isset($event[$key])) { if (isset($event[$key])) {
$metadata[$key] = $event[$key] instanceof DateTime ? $event[$key]->format('c') : $event[$key]; $metadata[$key] = $event[$key] instanceof DateTimeInterface ? $event[$key]->format('c') : $event[$key];
} }
} }
} }

View file

@ -35,7 +35,7 @@ class libcalendaring_recurrence
/** /**
* Default constructor * Default constructor
* *
* @param object calendar The calendar plugin instance * @param calendar The calendar plugin instance
*/ */
function __construct($lib) function __construct($lib)
{ {
@ -50,7 +50,7 @@ class libcalendaring_recurrence
* Initialize recurrence engine * Initialize recurrence engine
* *
* @param array The recurrence properties * @param array The recurrence properties
* @param object DateTime The recurrence start date * @param DateTime The recurrence start date
*/ */
public function init($recurrence, $start = null) public function init($recurrence, $start = null)
{ {
@ -80,7 +80,7 @@ class libcalendaring_recurrence
/** /**
* Setter for (new) recurrence start date * Setter for (new) recurrence start date
* *
* @param object DateTime The recurrence start date * @param DateTime The recurrence start date
*/ */
public function set_start($start) public function set_start($start)
{ {
@ -94,7 +94,7 @@ class libcalendaring_recurrence
/** /**
* Get date/time of the next occurence of this event * Get date/time of the next occurence of this event
* *
* @return mixed DateTime object or False if recurrence ended * @return DateTime|int|false object or False if recurrence ended
*/ */
public function next() public function next()
{ {
@ -127,14 +127,14 @@ class libcalendaring_recurrence
public function end() public function end()
{ {
// recurrence end date is given // recurrence end date is given
if ($this->recurrence['UNTIL'] instanceof DateTime) { if ($this->recurrence['UNTIL'] instanceof DateTimeInterface) {
return $this->recurrence['UNTIL']; return $this->recurrence['UNTIL'];
} }
// take the last RDATE entry if set // take the last RDATE entry if set
if (is_array($this->recurrence['RDATE']) && !empty($this->recurrence['RDATE'])) { if (is_array($this->recurrence['RDATE']) && !empty($this->recurrence['RDATE'])) {
$last = end($this->recurrence['RDATE']); $last = end($this->recurrence['RDATE']);
if ($last instanceof DateTime) { if ($last instanceof DateTimeInterface) {
return $last; return $last;
} }
} }

View file

@ -194,7 +194,7 @@ class libcalendaring extends rcube_plugin
$dt = rcube_utils::anytodatetime($dt); $dt = rcube_utils::anytodatetime($dt);
} }
if ($dt instanceof DateTime && empty($dt->_dateonly) && !$dateonly) { if ($dt instanceof DateTimeInterface && empty($dt->_dateonly) && !$dateonly) {
$dt->setTimezone($this->timezone); $dt->setTimezone($this->timezone);
} }
@ -518,7 +518,7 @@ class libcalendaring extends rcube_plugin
public static function to_client_alarms($valarms) public static function to_client_alarms($valarms)
{ {
return array_map(function($alarm) { return array_map(function($alarm) {
if ($alarm['trigger'] instanceof DateTime) { if ($alarm['trigger'] instanceof DateTimeInterface) {
$alarm['trigger'] = '@' . $alarm['trigger']->format('U'); $alarm['trigger'] = '@' . $alarm['trigger']->format('U');
} }
else if ($trigger = libcalendaring::parse_alarm_value($alarm['trigger'])) { else if ($trigger = libcalendaring::parse_alarm_value($alarm['trigger'])) {
@ -601,7 +601,7 @@ class libcalendaring extends rcube_plugin
break; break;
} }
if ($trigger instanceof DateTime) { if ($trigger instanceof DateTimeInterface) {
$text .= ' ' . $rcube->gettext(array( $text .= ' ' . $rcube->gettext(array(
'name' => 'libcalendaring.alarmat', 'name' => 'libcalendaring.alarmat',
'vars' => array('datetime' => $rcube->format_date($trigger)) 'vars' => array('datetime' => $rcube->format_date($trigger))
@ -681,7 +681,7 @@ class libcalendaring extends rcube_plugin
foreach ($rec['valarms'] as $alarm) { foreach ($rec['valarms'] as $alarm) {
$notify_time = null; $notify_time = null;
if ($alarm['trigger'] instanceof DateTime) { if ($alarm['trigger'] instanceof DateTimeInterface) {
$notify_time = $alarm['trigger']; $notify_time = $alarm['trigger'];
} }
else if (is_string($alarm['trigger'])) { else if (is_string($alarm['trigger'])) {
@ -1335,7 +1335,7 @@ class libcalendaring extends rcube_plugin
{ {
$instance_date = !empty($event['recurrence_date']) ? $event['recurrence_date'] : $event['start']; $instance_date = !empty($event['recurrence_date']) ? $event['recurrence_date'] : $event['start'];
if ($instance_date instanceof DateTime) { if ($instance_date instanceof DateTimeInterface) {
// According to RFC5545 (3.8.4.4) RECURRENCE-ID format should // According to RFC5545 (3.8.4.4) RECURRENCE-ID format should
// be date/date-time depending on the main event type, not the exception // be date/date-time depending on the main event type, not the exception
if ($allday === null) { if ($allday === null) {

View file

@ -24,6 +24,8 @@
use \Sabre\VObject; use \Sabre\VObject;
use \Sabre\VObject\DateTimeParser; use \Sabre\VObject\DateTimeParser;
require_once __DIR__ . '/lib/libcalendaring_datetime.php';
/** /**
* Class to parse and build vCalendar (iCalendar) files * Class to parse and build vCalendar (iCalendar) files
* *
@ -421,7 +423,7 @@ class libvcalendar implements Iterator
} }
// map other attributes to internal fields // map other attributes to internal fields
foreach ($ve->children as $prop) { foreach ($ve->children() as $prop) {
if (!($prop instanceof VObject\Property)) if (!($prop instanceof VObject\Property))
continue; continue;
@ -640,7 +642,7 @@ class libvcalendar implements Iterator
$trigger = null; $trigger = null;
$alarm = array(); $alarm = array();
foreach ($valarm->children as $prop) { foreach ($valarm->children() as $prop) {
$value = strval($prop); $value = strval($prop);
switch ($prop->name) { switch ($prop->name) {
@ -708,14 +710,14 @@ class libvcalendar implements Iterator
} }
// assign current timezone to event start/end // assign current timezone to event start/end
if (!empty($event['start']) && $event['start'] instanceof DateTime) { if (!empty($event['start']) && $event['start'] instanceof DateTimeInterface) {
$this->_apply_timezone($event['start']); $this->_apply_timezone($event['start']);
} }
else { else {
unset($event['start']); unset($event['start']);
} }
if (!empty($event['end']) && $event['end'] instanceof DateTime) { if (!empty($event['end']) && $event['end'] instanceof DateTimeInterface) {
$this->_apply_timezone($event['end']); $this->_apply_timezone($event['end']);
} }
else { else {
@ -752,7 +754,7 @@ class libvcalendar implements Iterator
// For date-only we'll keep the date and time intact // For date-only we'll keep the date and time intact
if (!empty($date->_dateonly)) { if (!empty($date->_dateonly)) {
$dt = new DateTime(null, $this->timezone); $dt = new libcalendaring_datetime(null, $this->timezone);
$dt->setDate($date->format('Y'), $date->format('n'), $date->format('j')); $dt->setDate($date->format('Y'), $date->format('n'), $date->format('j'));
$dt->setTime($date->format('G'), $date->format('i'), 0); $dt->setTime($date->format('G'), $date->format('i'), 0);
$date = $dt; $date = $dt;
@ -770,7 +772,7 @@ class libvcalendar implements Iterator
$this->freebusy = array('_type' => 'freebusy', 'periods' => array()); $this->freebusy = array('_type' => 'freebusy', 'periods' => array());
$seen = array(); $seen = array();
foreach ($ve->children as $prop) { foreach ($ve->children() as $prop) {
if (!($prop instanceof VObject\Property)) if (!($prop instanceof VObject\Property))
continue; continue;
@ -857,26 +859,29 @@ class libvcalendar implements Iterator
public static function convert_datetime($prop, $as_array = false) public static function convert_datetime($prop, $as_array = false)
{ {
if (empty($prop)) { if (empty($prop)) {
return $as_array ? array() : null; return $as_array ? [] : null;
} }
else if ($prop instanceof VObject\Property\iCalendar\DateTime) {
if ($prop instanceof VObject\Property\ICalendar\DateTime) {
if (count($prop->getDateTimes()) > 1) { if (count($prop->getDateTimes()) > 1) {
$dt = array(); $dt = [];
$dateonly = !$prop->hasTime(); $dateonly = !$prop->hasTime();
foreach ($prop->getDateTimes() as $item) { foreach ($prop->getDateTimes() as $item) {
$item = libcalendaring_datetime::createFromImmutable($item);
$item->_dateonly = $dateonly; $item->_dateonly = $dateonly;
$dt[] = $item; $dt[] = $item;
} }
} }
else { else {
$dt = $prop->getDateTime(); $dt = libcalendaring_datetime::createFromImmutable($prop->getDateTime());
if (!$prop->hasTime()) { if (!$prop->hasTime()) {
$dt->_dateonly = true; $dt->_dateonly = true;
} }
} }
} }
else if ($prop instanceof VObject\Property\iCalendar\Period) { else if ($prop instanceof VObject\Property\ICalendar\Period) {
$dt = array(); $dt = [];
foreach ($prop->getParts() as $val) { foreach ($prop->getParts() as $val) {
try { try {
list($start, $end) = explode('/', $val); list($start, $end) = explode('/', $val);
@ -891,20 +896,21 @@ class libvcalendar implements Iterator
else { else {
$end = DateTimeParser::parseDateTime($end); $end = DateTimeParser::parseDateTime($end);
} }
$dt[] = array($start, $end);
$dt[] = [libcalendaring_datetime::createFromImmutable($start), libcalendaring_datetime::createFromImmutable($end)];
} }
catch (Exception $e) { catch (Exception $e) {
// ignore single date parse errors // ignore single date parse errors
} }
} }
} }
else if ($prop instanceof \DateTime) { else if ($prop instanceof \DateTimeInterface) {
$dt = $prop; $dt = libcalendaring_datetime::createFromImmutable($prop);
} }
// force return value to array if requested // force return value to array if requested
if ($as_array && !is_array($dt)) { if ($as_array && !is_array($dt)) {
$dt = empty($dt) ? array() : array($dt); $dt = empty($dt) ? [] : [$dt];
} }
return $dt; return $dt;
@ -1082,7 +1088,7 @@ class libvcalendar implements Iterator
} }
// we're exporting a recurrence instance only // we're exporting a recurrence instance only
if (!$recurrence_id && !empty($event['recurrence_date']) && $event['recurrence_date'] instanceof DateTime) { if (!$recurrence_id && !empty($event['recurrence_date']) && $event['recurrence_date'] instanceof DateTimeInterface) {
$recurrence_id = $this->datetime_prop($cal, 'RECURRENCE-ID', $event['recurrence_date'], false, !empty($event['allday'])); $recurrence_id = $this->datetime_prop($cal, 'RECURRENCE-ID', $event['recurrence_date'], false, !empty($event['allday']));
if (!empty($event['thisandfuture'])) { if (!empty($event['thisandfuture'])) {
$recurrence_id->add('RANGE', 'THISANDFUTURE'); $recurrence_id->add('RANGE', 'THISANDFUTURE');
@ -1124,7 +1130,7 @@ class libvcalendar implements Iterator
// add EXDATEs each one per line (for Thunderbird Lightning) // add EXDATEs each one per line (for Thunderbird Lightning)
if (is_array($exdates)) { if (is_array($exdates)) {
foreach ($exdates as $exdate) { foreach ($exdates as $exdate) {
if ($exdate instanceof DateTime) { if ($exdate instanceof DateTimeInterface) {
$ve->add($this->datetime_prop($cal, 'EXDATE', $exdate)); $ve->add($this->datetime_prop($cal, 'EXDATE', $exdate));
} }
} }
@ -1186,7 +1192,7 @@ class libvcalendar implements Iterator
foreach ($event['valarms'] as $alarm) { foreach ($event['valarms'] as $alarm) {
$va = $cal->createComponent('VALARM'); $va = $cal->createComponent('VALARM');
$va->action = $alarm['action']; $va->action = $alarm['action'];
if ($alarm['trigger'] instanceof DateTime) { if ($alarm['trigger'] instanceof DateTimeInterface) {
$va->add($this->datetime_prop($cal, 'TRIGGER', $alarm['trigger'], true, null, true)); $va->add($this->datetime_prop($cal, 'TRIGGER', $alarm['trigger'], true, null, true));
} }
else { else {
@ -1228,7 +1234,7 @@ class libvcalendar implements Iterator
if (!empty($val[3])) { if (!empty($val[3])) {
$va->add('TRIGGER', $val[3]); $va->add('TRIGGER', $val[3]);
} }
else if ($val[0] instanceof DateTime) { else if ($val[0] instanceof DateTimeInterface) {
$va->add($this->datetime_prop($cal, 'TRIGGER', $val[0], true, null, true)); $va->add($this->datetime_prop($cal, 'TRIGGER', $val[0], true, null, true));
} }
$ve->add($va); $ve->add($va);

View file

@ -21,9 +21,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
class libcalendaring_test extends PHPUnit\Framework\TestCase class LibcalendaringTest extends PHPUnit\Framework\TestCase
{ {
function setUp() function setUp(): void
{ {
require_once __DIR__ . '/../libcalendaring.php'; require_once __DIR__ . '/../libcalendaring.php';
} }
@ -176,9 +176,9 @@ class libcalendaring_test extends PHPUnit\Framework\TestCase
$s = libcalendaring::to_rrule($rrule); $s = libcalendaring::to_rrule($rrule);
$this->assertRegExp('/FREQ='.$rrule['FREQ'].'/', $s, "Recurrence Frequence"); $this->assertMatchesRegularExpression('/FREQ='.$rrule['FREQ'].'/', $s, "Recurrence Frequence");
$this->assertRegExp('/INTERVAL='.$rrule['INTERVAL'].'/', $s, "Recurrence Interval"); $this->assertMatchesRegularExpression('/INTERVAL='.$rrule['INTERVAL'].'/', $s, "Recurrence Interval");
$this->assertRegExp('/BYDAY='.$rrule['BYDAY'].'/', $s, "Recurrence BYDAY"); $this->assertMatchesRegularExpression('/BYDAY='.$rrule['BYDAY'].'/', $s, "Recurrence BYDAY");
$this->assertRegExp('/UNTIL=20250501T160000Z/', $s, "Recurrence End date (in UTC)"); $this->assertMatchesRegularExpression('/UNTIL=20250501T160000Z/', $s, "Recurrence End date (in UTC)");
} }
} }

View file

@ -21,9 +21,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
class libvcalendar_test extends PHPUnit\Framework\TestCase class LibvcalendarTest extends PHPUnit\Framework\TestCase
{ {
function setUp() private $attachment_data;
function setUp(): void
{ {
require_once __DIR__ . '/../libvcalendar.php'; require_once __DIR__ . '/../libvcalendar.php';
require_once __DIR__ . '/../libcalendaring.php'; require_once __DIR__ . '/../libcalendaring.php';
@ -41,12 +43,12 @@ class libvcalendar_test extends PHPUnit\Framework\TestCase
$this->assertEquals(1, count($events)); $this->assertEquals(1, count($events));
$event = $events[0]; $event = $events[0];
$this->assertInstanceOf('DateTime', $event['created'], "'created' property is DateTime object"); $this->assertInstanceOf('DateTimeInterface', $event['created'], "'created' property is DateTime object");
$this->assertInstanceOf('DateTime', $event['changed'], "'changed' property is DateTime object"); $this->assertInstanceOf('DateTimeInterface', $event['changed'], "'changed' property is DateTime object");
$this->assertEquals('UTC', $event['created']->getTimezone()->getName(), "'created' date is in UTC"); $this->assertEquals('UTC', $event['created']->getTimezone()->getName(), "'created' date is in UTC");
$this->assertInstanceOf('DateTime', $event['start'], "'start' property is DateTime object"); $this->assertInstanceOf('DateTimeInterface', $event['start'], "'start' property is DateTime object");
$this->assertInstanceOf('DateTime', $event['end'], "'end' property is DateTime object"); $this->assertInstanceOf('DateTimeInterface', $event['end'], "'end' property is DateTime object");
$this->assertEquals('08-01', $event['start']->format('m-d'), "Start date is August 1st"); $this->assertEquals('08-01', $event['start']->format('m-d'), "Start date is August 1st");
$this->assertTrue($event['allday'], "All-day event flag"); $this->assertTrue($event['allday'], "All-day event flag");
@ -81,7 +83,7 @@ class libvcalendar_test extends PHPUnit\Framework\TestCase
{ {
$ical = new libvcalendar(); $ical = new libvcalendar();
$ical->fopen(__DIR__ . '/resources/multiple-rdate.ics', 'UTF-8'); $ical->fopen(__DIR__ . '/resources/multiple-rdate.ics', 'UTF-8');
$events = array(); $events = [];
foreach ($ical as $event) { foreach ($ical as $event) {
$events[] = $event; $events[] = $event;
} }
@ -98,7 +100,7 @@ class libvcalendar_test extends PHPUnit\Framework\TestCase
$event = $events[0]; $event = $events[0];
$this->assertEquals(1, count($events), "Import event data"); $this->assertEquals(1, count($events), "Import event data");
$this->assertInstanceOf('DateTime', $event['created'], "Created date field"); $this->assertInstanceOf('DateTimeInterface', $event['created'], "Created date field");
$this->assertFalse(array_key_exists('changed', $event), "No changed date field"); $this->assertFalse(array_key_exists('changed', $event), "No changed date field");
} }
@ -139,7 +141,7 @@ class libvcalendar_test extends PHPUnit\Framework\TestCase
$attachment = $event['attachments'][0]; $attachment = $event['attachments'][0];
$this->assertEquals('text/html', $attachment['mimetype'], "Attachment mimetype attribute"); $this->assertEquals('text/html', $attachment['mimetype'], "Attachment mimetype attribute");
$this->assertEquals('calendar.html', $attachment['name'], "Attachment filename (X-LABEL) attribute"); $this->assertEquals('calendar.html', $attachment['name'], "Attachment filename (X-LABEL) attribute");
$this->assertContains('<title>Kalender</title>', $attachment['data'], "Attachment content (decoded)"); $this->assertStringContainsString('<title>Kalender</title>', $attachment['data'], "Attachment content (decoded)");
// recurrence rules // recurrence rules
$events = $ical->import_from_file(__DIR__ . '/resources/recurring.ics', 'UTF-8'); $events = $ical->import_from_file(__DIR__ . '/resources/recurring.ics', 'UTF-8');
@ -150,10 +152,10 @@ class libvcalendar_test extends PHPUnit\Framework\TestCase
$this->assertEquals('MONTHLY', $rrule['FREQ'], "Recurrence frequency"); $this->assertEquals('MONTHLY', $rrule['FREQ'], "Recurrence frequency");
$this->assertEquals('1', $rrule['INTERVAL'], "Recurrence interval"); $this->assertEquals('1', $rrule['INTERVAL'], "Recurrence interval");
$this->assertEquals('3WE', $rrule['BYDAY'], "Recurrence frequency"); $this->assertEquals('3WE', $rrule['BYDAY'], "Recurrence frequency");
$this->assertInstanceOf('DateTime', $rrule['UNTIL'], "Recurrence end date"); $this->assertInstanceOf('DateTimeInterface', $rrule['UNTIL'], "Recurrence end date");
$this->assertEquals(2, count($rrule['EXDATE']), "Recurrence EXDATEs"); $this->assertEquals(2, count($rrule['EXDATE']), "Recurrence EXDATEs");
$this->assertInstanceOf('DateTime', $rrule['EXDATE'][0], "Recurrence EXDATE as DateTime"); $this->assertInstanceOf('DateTimeInterface', $rrule['EXDATE'][0], "Recurrence EXDATE as DateTime");
$this->assertTrue(is_array($rrule['EXCEPTIONS'])); $this->assertTrue(is_array($rrule['EXCEPTIONS']));
$this->assertEquals(1, count($rrule['EXCEPTIONS']), "Recurrence Exceptions"); $this->assertEquals(1, count($rrule['EXCEPTIONS']), "Recurrence Exceptions");
@ -161,7 +163,7 @@ class libvcalendar_test extends PHPUnit\Framework\TestCase
$exception = $rrule['EXCEPTIONS'][0]; $exception = $rrule['EXCEPTIONS'][0];
$this->assertEquals($event['uid'], $event['uid'], "Exception UID"); $this->assertEquals($event['uid'], $event['uid'], "Exception UID");
$this->assertEquals('Recurring Test (Exception)', $exception['title'], "Exception title"); $this->assertEquals('Recurring Test (Exception)', $exception['title'], "Exception title");
$this->assertInstanceOf('DateTime', $exception['start'], "Exception start"); $this->assertInstanceOf('DateTimeInterface', $exception['start'], "Exception start");
// categories, class // categories, class
$this->assertEquals('libcalendaring tests', join(',', (array)$event['categories']), "Event categories"); $this->assertEquals('libcalendaring tests', join(',', (array)$event['categories']), "Event categories");
@ -169,7 +171,7 @@ class libvcalendar_test extends PHPUnit\Framework\TestCase
// parse a recurrence chain instance // parse a recurrence chain instance
$events = $ical->import_from_file(__DIR__ . '/resources/recurrence-id.ics', 'UTF-8'); $events = $ical->import_from_file(__DIR__ . '/resources/recurrence-id.ics', 'UTF-8');
$this->assertEquals(1, count($events), "Fall back to Component::getComponents() when getBaseComponents() is empty"); $this->assertEquals(1, count($events), "Fall back to Component::getComponents() when getBaseComponents() is empty");
$this->assertInstanceOf('DateTime', $events[0]['recurrence_date'], "Recurrence-ID as date"); $this->assertInstanceOf('DateTimeInterface', $events[0]['recurrence_date'], "Recurrence-ID as date");
$this->assertTrue($events[0]['thisandfuture'], "Range=THISANDFUTURE"); $this->assertTrue($events[0]['thisandfuture'], "Range=THISANDFUTURE");
$this->assertEquals(count($events[0]['exceptions']), 1, "Second VEVENT as exception"); $this->assertEquals(count($events[0]['exceptions']), 1, "Second VEVENT as exception");
@ -218,16 +220,16 @@ class libvcalendar_test extends PHPUnit\Framework\TestCase
$this->assertEquals('EMAIL', $valarm['action'], "Second alarm item (action)"); $this->assertEquals('EMAIL', $valarm['action'], "Second alarm item (action)");
$this->assertEquals('-P1D', $valarm['trigger'], "Second alarm item (trigger)"); $this->assertEquals('-P1D', $valarm['trigger'], "Second alarm item (trigger)");
$this->assertEquals('This is the reminder message', $valarm['summary'], "Email alarm text"); $this->assertEquals('This is the reminder message', $valarm['summary'], "Email alarm text");
$this->assertInstanceOf('DateTime', $event['valarms'][2]['trigger'], "Absolute trigger date/time"); $this->assertInstanceOf('DateTimeInterface', $event['valarms'][2]['trigger'], "Absolute trigger date/time");
// test alarms export // test alarms export
$ics = $ical->export(array($event)); $ics = $ical->export([$event]);
$this->assertContains('ACTION:DISPLAY', $ics, "Display alarm block"); $this->assertStringContainsString('ACTION:DISPLAY', $ics, "Display alarm block");
$this->assertContains('ACTION:EMAIL', $ics, "Email alarm block"); $this->assertStringContainsString('ACTION:EMAIL', $ics, "Email alarm block");
$this->assertContains('DESCRIPTION:This is the first event reminder', $ics, "Alarm description"); $this->assertStringContainsString('DESCRIPTION:This is the first event reminder', $ics, "Alarm description");
$this->assertContains('SUMMARY:This is the reminder message', $ics, "Email alarm summary"); $this->assertStringContainsString('SUMMARY:This is the reminder message', $ics, "Email alarm summary");
$this->assertContains('ATTENDEE:mailto:reminder-recipient@example.org', $ics, "Email alarm recipient"); $this->assertStringContainsString('ATTENDEE:mailto:reminder-recipient@example.org', $ics, "Email alarm recipient");
$this->assertContains('TRIGGER;VALUE=DATE-TIME:20130812', $ics, "Date-Time trigger"); $this->assertStringContainsString('TRIGGER;VALUE=DATE-TIME:20130812', $ics, "Date-Time trigger");
} }
/** /**
@ -281,7 +283,7 @@ class libvcalendar_test extends PHPUnit\Framework\TestCase
$this->assertEquals("Kolab, Thomas", $event['attendees'][3]['name'], "Unescaped"); $this->assertEquals("Kolab, Thomas", $event['attendees'][3]['name'], "Unescaped");
$ics = $ical->export($events); $ics = $ical->export($events);
$this->assertContains('ATTENDEE;CN="Kolab, Thomas";PARTSTAT=', $ics, "Quoted attendee parameters"); $this->assertStringContainsString('ATTENDEE;CN="Kolab, Thomas";PARTSTAT=', $ics, "Quoted attendee parameters");
} }
/** /**
@ -294,8 +296,8 @@ class libvcalendar_test extends PHPUnit\Framework\TestCase
$event = $events[0]; $event = $events[0];
$this->assertEquals(9, count($event['recurrence']['RDATE'])); $this->assertEquals(9, count($event['recurrence']['RDATE']));
$this->assertInstanceOf('DateTime', $event['recurrence']['RDATE'][0]); $this->assertInstanceOf('DateTimeInterface', $event['recurrence']['RDATE'][0]);
$this->assertInstanceOf('DateTime', $event['recurrence']['RDATE'][1]); $this->assertInstanceOf('DateTimeInterface', $event['recurrence']['RDATE'][1]);
} }
/** /**
@ -307,8 +309,8 @@ class libvcalendar_test extends PHPUnit\Framework\TestCase
$ical->import_from_file(__DIR__ . '/resources/freebusy.ifb', 'UTF-8'); $ical->import_from_file(__DIR__ . '/resources/freebusy.ifb', 'UTF-8');
$freebusy = $ical->freebusy; $freebusy = $ical->freebusy;
$this->assertInstanceOf('DateTime', $freebusy['start'], "'start' property is DateTime object"); $this->assertInstanceOf('DateTimeInterface', $freebusy['start'], "'start' property is DateTime object");
$this->assertInstanceOf('DateTime', $freebusy['end'], "'end' property is DateTime object"); $this->assertInstanceOf('DateTimeInterface', $freebusy['end'], "'end' property is DateTime object");
$this->assertEquals(11, count($freebusy['periods']), "Number of freebusy periods defined"); $this->assertEquals(11, count($freebusy['periods']), "Number of freebusy periods defined");
$periods = $ical->get_busy_periods(); $periods = $ical->get_busy_periods();
$this->assertEquals(9, count($periods), "Number of busy periods found"); $this->assertEquals(9, count($periods), "Number of busy periods found");
@ -325,7 +327,7 @@ class libvcalendar_test extends PHPUnit\Framework\TestCase
$freebusy = $ical->freebusy; $freebusy = $ical->freebusy;
$this->assertEquals(0, count($freebusy['periods']), "Ignore 0-length freebudy periods"); $this->assertEquals(0, count($freebusy['periods']), "Ignore 0-length freebudy periods");
$this->assertContains('dummy', $freebusy['comment'], "Parse comment"); $this->assertStringContainsString('dummy', $freebusy['comment'], "Parse comment");
} }
function test_vtodo() function test_vtodo()
@ -334,8 +336,8 @@ class libvcalendar_test extends PHPUnit\Framework\TestCase
$tasks = $ical->import_from_file(__DIR__ . '/resources/vtodo.ics', 'UTF-8', true); $tasks = $ical->import_from_file(__DIR__ . '/resources/vtodo.ics', 'UTF-8', true);
$task = $tasks[0]; $task = $tasks[0];
$this->assertInstanceOf('DateTime', $task['start'], "'start' property is DateTime object"); $this->assertInstanceOf('DateTimeInterface', $task['start'], "'start' property is DateTime object");
$this->assertInstanceOf('DateTime', $task['due'], "'due' property is DateTime object"); $this->assertInstanceOf('DateTimeInterface', $task['due'], "'due' property is DateTime object");
$this->assertEquals('-1D:DISPLAY', $task['alarms'], "Taks alarm value"); $this->assertEquals('-1D:DISPLAY', $task['alarms'], "Taks alarm value");
$this->assertEquals('IN-PROCESS', $task['status'], "Task status property"); $this->assertEquals('IN-PROCESS', $task['status'], "Task status property");
$this->assertEquals(1, count($task['x-custom']), "Custom properties"); $this->assertEquals(1, count($task['x-custom']), "Custom properties");
@ -346,14 +348,12 @@ class libvcalendar_test extends PHPUnit\Framework\TestCase
$this->assertEquals('COMPLETED', $completed['status'], "Task status=completed when COMPLETED property is present"); $this->assertEquals('COMPLETED', $completed['status'], "Task status=completed when COMPLETED property is present");
$this->assertEquals(100, $completed['complete'], "Task percent complete value"); $this->assertEquals(100, $completed['complete'], "Task percent complete value");
$ics = $ical->export(array($completed)); $ics = $ical->export([$completed]);
$this->assertRegExp('/COMPLETED(;VALUE=DATE-TIME)?:[0-9TZ]+/', $ics, "Export COMPLETED property"); $this->assertMatchesRegularExpression('/COMPLETED(;VALUE=DATE-TIME)?:[0-9TZ]+/', $ics, "Export COMPLETED property");
} }
/** /**
* Test for iCal export from internal hash array representation * Test for iCal export from internal hash array representation
*
*
*/ */
function test_export() function test_export()
{ {
@ -372,46 +372,46 @@ class libvcalendar_test extends PHPUnit\Framework\TestCase
$event['start']->setTimezone(new DateTimezone('America/Montreal')); $event['start']->setTimezone(new DateTimezone('America/Montreal'));
$event['end']->setTimezone(new DateTimezone('Europe/Berlin')); $event['end']->setTimezone(new DateTimezone('Europe/Berlin'));
$ics = $ical->export(array($event), 'REQUEST', false, array($this, 'get_attachment_data'), true); $ics = $ical->export([$event], 'REQUEST', false, [$this, 'get_attachment_data'], true);
$this->assertContains('BEGIN:VCALENDAR', $ics, "VCALENDAR encapsulation BEGIN"); $this->assertStringContainsString('BEGIN:VCALENDAR', $ics, "VCALENDAR encapsulation BEGIN");
$this->assertContains('BEGIN:VTIMEZONE', $ics, "VTIMEZONE encapsulation BEGIN"); $this->assertStringContainsString('BEGIN:VTIMEZONE', $ics, "VTIMEZONE encapsulation BEGIN");
$this->assertContains('TZID:Europe/Berlin', $ics, "Timezone ID"); $this->assertStringContainsString('TZID:Europe/Berlin', $ics, "Timezone ID");
$this->assertContains('TZOFFSETFROM:+0100', $ics, "Timzone transition FROM"); $this->assertStringContainsString('TZOFFSETFROM:+0100', $ics, "Timzone transition FROM");
$this->assertContains('TZOFFSETTO:+0200', $ics, "Timzone transition TO"); $this->assertStringContainsString('TZOFFSETTO:+0200', $ics, "Timzone transition TO");
$this->assertContains('TZOFFSETFROM:-0400', $ics, "TZOFFSETFROM with negative offset (Bug T428)"); $this->assertStringContainsString('TZOFFSETFROM:-0400', $ics, "TZOFFSETFROM with negative offset (Bug T428)");
$this->assertContains('TZOFFSETTO:-0500', $ics, "TZOFFSETTO with negative offset (Bug T428)"); $this->assertStringContainsString('TZOFFSETTO:-0500', $ics, "TZOFFSETTO with negative offset (Bug T428)");
$this->assertContains('END:VTIMEZONE', $ics, "VTIMEZONE encapsulation END"); $this->assertStringContainsString('END:VTIMEZONE', $ics, "VTIMEZONE encapsulation END");
$this->assertContains('BEGIN:VEVENT', $ics, "VEVENT encapsulation BEGIN"); $this->assertStringContainsString('BEGIN:VEVENT', $ics, "VEVENT encapsulation BEGIN");
$this->assertSame(2, substr_count($ics, 'DTSTAMP'), "Duplicate DTSTAMP (T1148)"); $this->assertSame(2, substr_count($ics, 'DTSTAMP'), "Duplicate DTSTAMP (T1148)");
$this->assertContains('UID:ac6b0aee-2519-4e5c-9a25-48c57064c9f0', $ics, "Event UID"); $this->assertStringContainsString('UID:ac6b0aee-2519-4e5c-9a25-48c57064c9f0', $ics, "Event UID");
$this->assertContains('SEQUENCE:' . $event['sequence'], $ics, "Export Sequence number"); $this->assertStringContainsString('SEQUENCE:' . $event['sequence'], $ics, "Export Sequence number");
$this->assertContains('DESCRIPTION:*Exported by', $ics, "Export Description"); $this->assertStringContainsString('DESCRIPTION:*Exported by', $ics, "Export Description");
$this->assertContains('ORGANIZER;CN=Rolf Test:mailto:rolf@', $ics, "Export organizer"); $this->assertStringContainsString('ORGANIZER;CN=Rolf Test:mailto:rolf@', $ics, "Export organizer");
$this->assertRegExp('/ATTENDEE.*;ROLE=REQ-PARTICIPANT/', $ics, "Export Attendee ROLE"); $this->assertMatchesRegularExpression('/ATTENDEE.*;ROLE=REQ-PARTICIPANT/', $ics, "Export Attendee ROLE");
$this->assertRegExp('/ATTENDEE.*;PARTSTAT=NEEDS-ACTION/', $ics, "Export Attendee Status"); $this->assertMatchesRegularExpression('/ATTENDEE.*;PARTSTAT=NEEDS-ACTION/', $ics, "Export Attendee Status");
$this->assertRegExp('/ATTENDEE.*;RSVP=TRUE/', $ics, "Export Attendee RSVP"); $this->assertMatchesRegularExpression('/ATTENDEE.*;RSVP=TRUE/', $ics, "Export Attendee RSVP");
$this->assertRegExp('/:mailto:rolf2@/', $ics, "Export Attendee mailto:"); $this->assertMatchesRegularExpression('/:mailto:rolf2@/', $ics, "Export Attendee mailto:");
$rrule = $event['recurrence']; $rrule = $event['recurrence'];
$this->assertRegExp('/RRULE:.*FREQ='.$rrule['FREQ'].'/', $ics, "Export Recurrence Frequence"); $this->assertMatchesRegularExpression('/RRULE:.*FREQ='.$rrule['FREQ'].'/', $ics, "Export Recurrence Frequence");
$this->assertRegExp('/RRULE:.*INTERVAL='.$rrule['INTERVAL'].'/', $ics, "Export Recurrence Interval"); $this->assertMatchesRegularExpression('/RRULE:.*INTERVAL='.$rrule['INTERVAL'].'/', $ics, "Export Recurrence Interval");
$this->assertRegExp('/RRULE:.*UNTIL=20140718T215959Z/', $ics, "Export Recurrence End date"); $this->assertMatchesRegularExpression('/RRULE:.*UNTIL=20140718T215959Z/', $ics, "Export Recurrence End date");
$this->assertRegExp('/RRULE:.*BYDAY='.$rrule['BYDAY'].'/', $ics, "Export Recurrence BYDAY"); $this->assertMatchesRegularExpression('/RRULE:.*BYDAY='.$rrule['BYDAY'].'/', $ics, "Export Recurrence BYDAY");
$this->assertRegExp('/EXDATE.*:20131218/', $ics, "Export Recurrence EXDATE"); $this->assertMatchesRegularExpression('/EXDATE.*:20131218/', $ics, "Export Recurrence EXDATE");
$this->assertContains('BEGIN:VALARM', $ics, "Export VALARM"); $this->assertStringContainsString('BEGIN:VALARM', $ics, "Export VALARM");
$this->assertContains('TRIGGER;RELATED=END:-PT12H', $ics, "Export Alarm trigger"); $this->assertStringContainsString('TRIGGER;RELATED=END:-PT12H', $ics, "Export Alarm trigger");
$this->assertRegExp('/ATTACH.*;VALUE=BINARY/', $ics, "Embed attachment"); $this->assertMatchesRegularExpression('/ATTACH.*;VALUE=BINARY/', $ics, "Embed attachment");
$this->assertRegExp('/ATTACH.*;ENCODING=BASE64/', $ics, "Attachment B64 encoding"); $this->assertMatchesRegularExpression('/ATTACH.*;ENCODING=BASE64/', $ics, "Attachment B64 encoding");
$this->assertRegExp('!ATTACH.*;FMTTYPE=text/html!', $ics, "Attachment mimetype"); $this->assertMatchesRegularExpression('!ATTACH.*;FMTTYPE=text/html!', $ics, "Attachment mimetype");
$this->assertRegExp('!ATTACH.*;X-LABEL=calendar.html!', $ics, "Attachment filename with X-LABEL"); $this->assertMatchesRegularExpression('!ATTACH.*;X-LABEL=calendar.html!', $ics, "Attachment filename with X-LABEL");
$this->assertContains('END:VEVENT', $ics, "VEVENT encapsulation END"); $this->assertStringContainsString('END:VEVENT', $ics, "VEVENT encapsulation END");
$this->assertContains('END:VCALENDAR', $ics, "VCALENDAR encapsulation END"); $this->assertStringContainsString('END:VCALENDAR', $ics, "VCALENDAR encapsulation END");
} }
/** /**
@ -429,8 +429,8 @@ class libvcalendar_test extends PHPUnit\Framework\TestCase
$num = count($events); $num = count($events);
$ics = $ical->export($events, null, false); $ics = $ical->export($events, null, false);
$this->assertContains('BEGIN:VCALENDAR', $ics, "VCALENDAR encapsulation BEGIN"); $this->assertStringContainsString('BEGIN:VCALENDAR', $ics, "VCALENDAR encapsulation BEGIN");
$this->assertContains('END:VCALENDAR', $ics, "VCALENDAR encapsulation END"); $this->assertStringContainsString('END:VCALENDAR', $ics, "VCALENDAR encapsulation END");
$this->assertEquals($num, substr_count($ics, 'BEGIN:VEVENT'), "VEVENT encapsulation BEGIN"); $this->assertEquals($num, substr_count($ics, 'BEGIN:VEVENT'), "VEVENT encapsulation BEGIN");
$this->assertEquals($num, substr_count($ics, 'END:VEVENT'), "VEVENT encapsulation END"); $this->assertEquals($num, substr_count($ics, 'END:VEVENT'), "VEVENT encapsulation END");
} }
@ -460,7 +460,7 @@ class libvcalendar_test extends PHPUnit\Framework\TestCase
$exception2['end']->setDate(2013, 11, 13); $exception2['end']->setDate(2013, 11, 13);
$exception2['title'] = 'Recurring Exception'; $exception2['title'] = 'Recurring Exception';
$events[0]['recurrence']['EXCEPTIONS'] = array($exception1, $exception2); $events[0]['recurrence']['EXCEPTIONS'] = [$exception1, $exception2];
$ics = $ical->export($events, null, false); $ics = $ical->export($events, null, false);
@ -469,30 +469,30 @@ class libvcalendar_test extends PHPUnit\Framework\TestCase
$this->assertEquals($num, substr_count($ics, 'UID:'.$event['uid']), "Recurrence Exceptions with same UID"); $this->assertEquals($num, substr_count($ics, 'UID:'.$event['uid']), "Recurrence Exceptions with same UID");
$this->assertEquals($num, substr_count($ics, 'END:VEVENT'), "VEVENT encapsulation END"); $this->assertEquals($num, substr_count($ics, 'END:VEVENT'), "VEVENT encapsulation END");
$this->assertContains('RECURRENCE-ID;TZID=Europe/Zurich:20130814', $ics, "Recurrence-ID (1) being the exception date"); $this->assertStringContainsString('RECURRENCE-ID;TZID=Europe/Zurich:20130814', $ics, "Recurrence-ID (1) being the exception date");
$this->assertContains('RECURRENCE-ID;TZID=Europe/Zurich:20131113', $ics, "Recurrence-ID (2) being the exception date"); $this->assertStringContainsString('RECURRENCE-ID;TZID=Europe/Zurich:20131113', $ics, "Recurrence-ID (2) being the exception date");
$this->assertContains('SUMMARY:'.$exception2['title'], $ics, "Exception title"); $this->assertStringContainsString('SUMMARY:'.$exception2['title'], $ics, "Exception title");
} }
function test_export_valid_rrules() function test_export_valid_rrules()
{ {
$event = array( $event = [
'uid' => '1234567890', 'uid' => '1234567890',
'start' => new DateTime('now'), 'start' => new DateTime('now'),
'end' => new DateTime('now + 30min'), 'end' => new DateTime('now + 30min'),
'title' => 'test_export_valid_rrules', 'title' => 'test_export_valid_rrules',
'recurrence' => array( 'recurrence' => [
'FREQ' => 'DAILY', 'FREQ' => 'DAILY',
'COUNT' => 5, 'COUNT' => 5,
'EXDATE' => array(), 'EXDATE' => [],
'RDATE' => array(), 'RDATE' => [],
), ],
); ];
$ical = new libvcalendar(); $ical = new libvcalendar();
$ics = $ical->export(array($event), null, false, null, false); $ics = $ical->export([$event], null, false, null, false);
$this->assertNotContains('EXDATE=', $ics); $this->assertStringNotContainsString('EXDATE=', $ics);
$this->assertNotContains('RDATE=', $ics); $this->assertStringNotContainsString('RDATE=', $ics);
} }
/** /**
@ -504,7 +504,7 @@ class libvcalendar_test extends PHPUnit\Framework\TestCase
$events = $ical->import_from_file(__DIR__ . '/resources/multiple-rdate.ics', 'UTF-8'); $events = $ical->import_from_file(__DIR__ . '/resources/multiple-rdate.ics', 'UTF-8');
$ics = $ical->export($events, null, false); $ics = $ical->export($events, null, false);
$this->assertContains('RDATE:20140520T020000Z', $ics, "VALUE=PERIOD is translated into single DATE-TIME values"); $this->assertStringContainsString('RDATE:20140520T020000Z', $ics, "VALUE=PERIOD is translated into single DATE-TIME values");
} }
/** /**
@ -522,8 +522,8 @@ class libvcalendar_test extends PHPUnit\Framework\TestCase
ob_end_clean(); ob_end_clean();
$this->assertTrue($return, "Return true on successful writing"); $this->assertTrue($return, "Return true on successful writing");
$this->assertContains('BEGIN:VCALENDAR', $output, "VCALENDAR encapsulation BEGIN"); $this->assertStringContainsString('BEGIN:VCALENDAR', $output, "VCALENDAR encapsulation BEGIN");
$this->assertContains('END:VCALENDAR', $output, "VCALENDAR encapsulation END"); $this->assertStringContainsString('END:VCALENDAR', $output, "VCALENDAR encapsulation END");
$this->assertEquals($num, substr_count($output, 'BEGIN:VEVENT'), "VEVENT encapsulation BEGIN"); $this->assertEquals($num, substr_count($output, 'BEGIN:VEVENT'), "VEVENT encapsulation BEGIN");
$this->assertEquals($num, substr_count($output, 'END:VEVENT'), "VEVENT encapsulation END"); $this->assertEquals($num, substr_count($output, 'END:VEVENT'), "VEVENT encapsulation END");
} }
@ -537,10 +537,10 @@ class libvcalendar_test extends PHPUnit\Framework\TestCase
$utctime = $ical->datetime_prop($cal, 'DTSTART', new DateTime('2013-09-01 12:00:00', new DateTimeZone('UTC'))); $utctime = $ical->datetime_prop($cal, 'DTSTART', new DateTime('2013-09-01 12:00:00', new DateTimeZone('UTC')));
$asutctime = $ical->datetime_prop($cal, 'DTSTART', new DateTime('2013-09-01 12:00:00', new DateTimeZone('Europe/Berlin')), true); $asutctime = $ical->datetime_prop($cal, 'DTSTART', new DateTime('2013-09-01 12:00:00', new DateTimeZone('Europe/Berlin')), true);
$this->assertContains('TZID=Europe/Berlin', $localtime->serialize()); $this->assertStringContainsString('TZID=Europe/Berlin', $localtime->serialize());
$this->assertContains('VALUE=DATE', $localdate->serialize()); $this->assertStringContainsString('VALUE=DATE', $localdate->serialize());
$this->assertContains('20130901T120000Z', $utctime->serialize()); $this->assertStringContainsString('20130901T120000Z', $utctime->serialize());
$this->assertContains('20130901T100000Z', $asutctime->serialize()); $this->assertStringContainsString('20130901T100000Z', $asutctime->serialize());
} }
function test_get_vtimezone() function test_get_vtimezone()
@ -578,8 +578,8 @@ class libvcalendar_test extends PHPUnit\Framework\TestCase
// DateTimezone as input data // DateTimezone as input data
$vtz = libvcalendar::get_vtimezone(new DateTimezone('Pacific/Chatham')); $vtz = libvcalendar::get_vtimezone(new DateTimezone('Pacific/Chatham'));
$this->assertInstanceOf('\Sabre\VObject\Component', $vtz); $this->assertInstanceOf('\Sabre\VObject\Component', $vtz);
$this->assertContains('TZOFFSETFROM:+1245', $vtz->serialize()); $this->assertStringContainsString('TZOFFSETFROM:+1245', $vtz->serialize());
$this->assertContains('TZOFFSETTO:+1345', $vtz->serialize()); $this->assertStringContainsString('TZOFFSETTO:+1345', $vtz->serialize());
// Making sure VTIMEZOONE contains at least one STANDARD/DAYLIGHT component // Making sure VTIMEZOONE contains at least one STANDARD/DAYLIGHT component
// when there's only one transition in specified time period (T5626) // when there's only one transition in specified time period (T5626)

View file

@ -24,8 +24,9 @@
} }
], ],
"require": { "require": {
"php": ">=5.3.0", "php": ">=7.4.0",
"roundcube/plugin-installer": ">=0.1.3", "roundcube/plugin-installer": ">=0.1.3",
"kolab/libcalendaring": ">=3.4.0",
"pear/http_request2": "~2.3.0", "pear/http_request2": "~2.3.0",
"caxy/php-htmldiff": "~0.1.7" "caxy/php-htmldiff": "~0.1.7"
} }

View file

@ -4,7 +4,7 @@
* Recurrence computation class for xcal-based Kolab format objects * Recurrence computation class for xcal-based Kolab format objects
* *
* Utility class to compute instances of recurring events. * Utility class to compute instances of recurring events.
* It requires the libcalendaring PHP module to be installed and loaded. * It requires the libcalendaring PHP extension to be installed and loaded.
* *
* @version @package_version@ * @version @package_version@
* @author Thomas Bruederli <bruederli@kolabsys.com> * @author Thomas Bruederli <bruederli@kolabsys.com>
@ -24,6 +24,7 @@
* You should have received a copy of the GNU Affero General Public License * 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/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
class kolab_date_recurrence class kolab_date_recurrence
{ {
private /* EventCal */ $engine; private /* EventCal */ $engine;
@ -63,9 +64,9 @@ class kolab_date_recurrence
/** /**
* Get date/time of the next occurence of this event * Get date/time of the next occurence of this event
* *
* @param boolean Return a Unix timestamp instead of a DateTime object * @param bool Return a Unix timestamp instead of a DateTime object
* *
* @return mixed DateTime object/unix timestamp or False if recurrence ended * @return DateTime|int|false Object/unix timestamp or False if recurrence ended
*/ */
public function next_start($timestamp = false) public function next_start($timestamp = false)
{ {
@ -127,7 +128,7 @@ class kolab_date_recurrence
$event = $this->object->to_array(); $event = $this->object->to_array();
// recurrence end date is given // recurrence end date is given
if ($event['recurrence']['UNTIL'] instanceof DateTime) { if ($event['recurrence']['UNTIL'] instanceof DateTimeInterface) {
return $event['recurrence']['UNTIL']; return $event['recurrence']['UNTIL'];
} }
@ -139,7 +140,7 @@ class kolab_date_recurrence
} }
// determine a reasonable end date if none given // determine a reasonable end date if none given
if (!$event['recurrence']['COUNT'] && $event['end'] instanceof DateTime) { if (!$event['recurrence']['COUNT'] && $event['end'] instanceof DateTimeInterface) {
$end_dt = clone $event['end']; $end_dt = clone $event['end'];
$end_dt->add(new DateInterval('P100Y')); $end_dt->add(new DateInterval('P100Y'));

View file

@ -25,6 +25,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
require_once __DIR__ . '/../../libcalendaring/lib/libcalendaring_datetime.php';
abstract class kolab_format abstract class kolab_format
{ {
public static $timezone; public static $timezone;
@ -211,7 +213,7 @@ abstract class kolab_format
{ {
// use timezone information from datetime or global setting // use timezone information from datetime or global setting
if (!$tz && $tz !== false) { if (!$tz && $tz !== false) {
if ($datetime instanceof DateTime) if ($datetime instanceof DateTimeInterface)
$tz = $datetime->getTimezone(); $tz = $datetime->getTimezone();
if (!$tz) if (!$tz)
$tz = self::$timezone; $tz = self::$timezone;
@ -222,19 +224,19 @@ abstract class kolab_format
try { try {
// got a unix timestamp (in UTC) // got a unix timestamp (in UTC)
if (is_numeric($datetime)) { if (is_numeric($datetime)) {
$datetime = new DateTime('@'.$datetime, new DateTimeZone('UTC')); $datetime = new libcalendaring_datetime('@'.$datetime, new DateTimeZone('UTC'));
if ($tz) $datetime->setTimezone($tz); if ($tz) $datetime->setTimezone($tz);
} }
else if (is_string($datetime) && strlen($datetime)) { else if (is_string($datetime) && strlen($datetime)) {
$datetime = $tz ? new DateTime($datetime, $tz) : new DateTime($datetime); $datetime = $tz ? new libcalendaring_datetime($datetime, $tz) : new libcalendaring_datetime($datetime);
} }
else if ($datetime instanceof DateTime) { else if ($datetime instanceof DateTimeInterface) {
$datetime = clone $datetime; $datetime = clone $datetime;
} }
} }
catch (Exception $e) {} catch (Exception $e) {}
if ($datetime instanceof DateTime) { if ($datetime instanceof DateTimeInterface) {
if ($dest_tz instanceof DateTimeZone && $dest_tz !== $datetime->getTimezone()) { if ($dest_tz instanceof DateTimeZone && $dest_tz !== $datetime->getTimezone()) {
$datetime->setTimezone($dest_tz); $datetime->setTimezone($dest_tz);
$tz = $dest_tz; $tz = $dest_tz;
@ -272,7 +274,7 @@ abstract class kolab_format
* @param cDateTime The libkolabxml datetime object * @param cDateTime The libkolabxml datetime object
* @param DateTimeZone The timezone to convert the date to * @param DateTimeZone The timezone to convert the date to
* *
* @return DateTime PHP datetime instance * @return libcalendaring_datetime PHP datetime instance
*/ */
public static function php_datetime($cdt, $dest_tz = null) public static function php_datetime($cdt, $dest_tz = null)
{ {
@ -280,9 +282,12 @@ abstract class kolab_format
return null; return null;
} }
$d = new DateTime; $d = new libcalendaring_datetime(null, self::$timezone);
$d->setTimezone($dest_tz ?: self::$timezone);
if ($dest_tz) {
$d->setTimezone($dest_tz);
}
else {
try { try {
if ($tzs = $cdt->timezone()) { if ($tzs = $cdt->timezone()) {
$tz = new DateTimeZone($tzs); $tz = new DateTimeZone($tzs);
@ -293,6 +298,7 @@ abstract class kolab_format
} }
} }
catch (Exception $e) { } catch (Exception $e) { }
}
$d->setDate($cdt->year(), $cdt->month(), $cdt->day()); $d->setDate($cdt->year(), $cdt->month(), $cdt->day());

View file

@ -112,7 +112,7 @@ class kolab_format_event extends kolab_format_xcal
} }
} }
if ($object['recurrence_date'] && $object['recurrence_date'] instanceof DateTime) { if ($object['recurrence_date'] && $object['recurrence_date'] instanceof DateTimeInterface) {
if ($object['recurrence']) { if ($object['recurrence']) {
// unset recurrence_date for master events with rrule // unset recurrence_date for master events with rrule
$object['recurrence_date'] = null; $object['recurrence_date'] = null;
@ -236,9 +236,9 @@ class kolab_format_event extends kolab_format_xcal
$object = $this->to_array(); $object = $this->to_array();
$recurrence_id_format = libkolab::recurrence_id_format($object); $recurrence_id_format = libkolab::recurrence_id_format($object);
$instance_id = $recurrence_id instanceof DateTime ? $recurrence_id->format($recurrence_id_format) : strval($recurrence_id); $instance_id = $recurrence_id instanceof DateTimeInterface ? $recurrence_id->format($recurrence_id_format) : strval($recurrence_id);
if ($object['recurrence_date'] instanceof DateTime) { if ($object['recurrence_date'] instanceof DateTimeInterface) {
if ($object['recurrence_date']->format($recurrence_id_format) == $instance_id) { if ($object['recurrence_date']->format($recurrence_id_format) == $instance_id) {
$result = $object; $result = $object;
} }

View file

@ -123,7 +123,7 @@ class kolab_format_task extends kolab_format_xcal
*/ */
public function get_reference_date() public function get_reference_date()
{ {
if ($this->data['due'] && $this->data['due'] instanceof DateTime) { if ($this->data['due'] && $this->data['due'] instanceof DateTimeInterface) {
return $this->data['due']; return $this->data['due'];
} }

View file

@ -166,7 +166,7 @@ abstract class kolab_format_xcal extends kolab_format
} }
} }
if ($object['start'] instanceof DateTime) { if ($object['start'] instanceof DateTimeInterface) {
$start_tz = $object['start']->getTimezone(); $start_tz = $object['start']->getTimezone();
} }
@ -183,7 +183,7 @@ abstract class kolab_format_xcal extends kolab_format
} }
else if ($until = self::php_datetime($rr->end(), $start_tz)) { else if ($until = self::php_datetime($rr->end(), $start_tz)) {
$refdate = $this->get_reference_date(); $refdate = $this->get_reference_date();
if ($refdate && $refdate instanceof DateTime && !$refdate->_dateonly) { if ($refdate && $refdate instanceof DateTimeInterface && empty($refdate->_dateonly)) {
$until->setTime($refdate->format('G'), $refdate->format('i'), 0); $until->setTime($refdate->format('G'), $refdate->format('i'), 0);
} }
$object['recurrence']['UNTIL'] = $until; $object['recurrence']['UNTIL'] = $until;
@ -412,7 +412,7 @@ abstract class kolab_format_xcal extends kolab_format
$this->obj->setOrganizer($organizer); $this->obj->setOrganizer($organizer);
} }
if ($object['start'] instanceof DateTime) { if ($object['start'] instanceof DateTimeInterface) {
$start_tz = $object['start']->getTimezone(); $start_tz = $object['start']->getTimezone();
} }
@ -536,7 +536,7 @@ abstract class kolab_format_xcal extends kolab_format
$alarm = new Alarm(strval($valarm['summary'] ?: $object['title'])); $alarm = new Alarm(strval($valarm['summary'] ?: $object['title']));
} }
if (is_object($valarm['trigger']) && $valarm['trigger'] instanceof DateTime) { if (is_object($valarm['trigger']) && $valarm['trigger'] instanceof DateTimeInterface) {
$alarm->setStart(self::get_datetime($valarm['trigger'], new DateTimeZone('UTC'))); $alarm->setStart(self::get_datetime($valarm['trigger'], new DateTimeZone('UTC')));
} }
else if (preg_match('/^@([0-9]+)$/', $valarm['trigger'], $m)) { else if (preg_match('/^@([0-9]+)$/', $valarm['trigger'], $m)) {
@ -618,7 +618,7 @@ abstract class kolab_format_xcal extends kolab_format
*/ */
public function get_reference_date() public function get_reference_date()
{ {
if ($this->data['start'] && $this->data['start'] instanceof DateTime) { if ($this->data['start'] && $this->data['start'] instanceof DateTimeInterface) {
return $this->data['start']; return $this->data['start'];
} }
@ -719,7 +719,12 @@ abstract class kolab_format_xcal extends kolab_format
foreach ($this->_scheduling_properties ?: self::$scheduling_properties as $prop) { foreach ($this->_scheduling_properties ?: self::$scheduling_properties as $prop) {
$a = $old[$prop]; $a = $old[$prop];
$b = $object[$prop]; $b = $object[$prop];
if ($object['allday'] && ($prop == 'start' || $prop == 'end') && $a instanceof DateTime && $b instanceof DateTime) {
if ($object['allday']
&& ($prop == 'start' || $prop == 'end')
&& $a instanceof DateTimeInterface
&& $b instanceof DateTimeInterface
) {
$a = $a->format('Y-m-d'); $a = $a->format('Y-m-d');
$b = $b->format('Y-m-d'); $b = $b->format('Y-m-d');
} }

View file

@ -1051,7 +1051,7 @@ class kolab_storage_cache
foreach ($this->data_props as $prop) { foreach ($this->data_props as $prop) {
if (isset($object[$prop])) { if (isset($object[$prop])) {
$data[$prop] = $object[$prop]; $data[$prop] = $object[$prop];
if ($data[$prop] instanceof DateTime) { if ($data[$prop] instanceof DateTimeInterface) {
$data[$prop] = array( $data[$prop] = array(
'cl' => 'DateTime', 'cl' => 'DateTime',
'dt' => $data[$prop]->format('Y-m-d H:i:s'), 'dt' => $data[$prop]->format('Y-m-d H:i:s'),

View file

@ -72,7 +72,7 @@ class kolab_storage_dataset implements Iterator, ArrayAccess, Countable
/*** Implement PHP Countable interface ***/ /*** Implement PHP Countable interface ***/
public function count() public function count(): int
{ {
return count($this->index); return count($this->index);
} }
@ -80,7 +80,7 @@ class kolab_storage_dataset implements Iterator, ArrayAccess, Countable
/*** Implement PHP ArrayAccess interface ***/ /*** Implement PHP ArrayAccess interface ***/
public function offsetSet($offset, $value) public function offsetSet($offset, $value): void
{ {
if (is_string($value)) { if (is_string($value)) {
$uid = $value; $uid = $value;
@ -106,16 +106,17 @@ class kolab_storage_dataset implements Iterator, ArrayAccess, Countable
} }
} }
public function offsetExists($offset) public function offsetExists($offset): bool
{ {
return isset($this->index[$offset]); return isset($this->index[$offset]);
} }
public function offsetUnset($offset) public function offsetUnset($offset): void
{ {
unset($this->index[$offset]); unset($this->index[$offset]);
} }
#[ReturnTypeWillChange]
public function offsetGet($offset) public function offsetGet($offset)
{ {
if (isset($this->chunk[$offset])) { if (isset($this->chunk[$offset])) {
@ -163,28 +164,28 @@ class kolab_storage_dataset implements Iterator, ArrayAccess, Countable
/*** Implement PHP Iterator interface ***/ /*** Implement PHP Iterator interface ***/
#[ReturnTypeWillChange]
public function current() public function current()
{ {
return $this->offsetGet($this->iteratorkey); return $this->offsetGet($this->iteratorkey);
} }
public function key() public function key(): int
{ {
return $this->iteratorkey; return $this->iteratorkey;
} }
public function next() public function next(): void
{ {
$this->iteratorkey++; $this->iteratorkey++;
return $this->valid();
} }
public function rewind() public function rewind(): void
{ {
$this->iteratorkey = 0; $this->iteratorkey = 0;
} }
public function valid() public function valid(): bool
{ {
return !empty($this->index[$this->iteratorkey]); return !empty($this->index[$this->iteratorkey]);
} }

View file

@ -627,7 +627,7 @@ class kolab_storage_dav_cache extends kolab_storage_cache
foreach ($this->data_props as $prop) { foreach ($this->data_props as $prop) {
if (isset($object[$prop])) { if (isset($object[$prop])) {
$data[$prop] = $object[$prop]; $data[$prop] = $object[$prop];
if ($data[$prop] instanceof DateTime) { if ($data[$prop] instanceof DateTimeInterface) {
$data[$prop] = array( $data[$prop] = array(
'cl' => 'DateTime', 'cl' => 'DateTime',
'dt' => $data[$prop]->format('Y-m-d H:i:s'), 'dt' => $data[$prop]->format('Y-m-d H:i:s'),
@ -651,17 +651,11 @@ class kolab_storage_dav_cache extends kolab_storage_cache
*/ */
protected function _unserialize($sql_arr, $noread = false, $fast_mode = false) protected function _unserialize($sql_arr, $noread = false, $fast_mode = false)
{ {
if (!empty($sql_arr['data'])) { if (!empty($sql_arr['data']) && ($object = json_decode($sql_arr['data'], true))) {
if ($object = json_decode($sql_arr['data'], true)) {
$object['_type'] = $sql_arr['type'] ?: $this->folder->type;
$object['uid'] = $sql_arr['uid'];
$object['etag'] = $sql_arr['etag'];
}
}
if (!empty($fast_mode) && !empty($object)) {
foreach ($this->data_props as $prop) { foreach ($this->data_props as $prop) {
if (isset($object[$prop]) && is_array($object[$prop]) && $object[$prop]['cl'] == 'DateTime') { if (isset($object[$prop]) && is_array($object[$prop])
&& isset($object[$prop]['cl']) && $object[$prop]['cl'] == 'DateTime'
) {
$object[$prop] = new DateTime($object[$prop]['dt'], new DateTimeZone($object[$prop]['tz'])); $object[$prop] = new DateTime($object[$prop]['dt'], new DateTimeZone($object[$prop]['tz']));
} }
else if (!isset($object[$prop]) && isset($sql_arr[$prop])) { else if (!isset($object[$prop]) && isset($sql_arr[$prop])) {
@ -677,6 +671,12 @@ class kolab_storage_dav_cache extends kolab_storage_cache
$object['changed'] = new DateTime($sql_arr['changed']); $object['changed'] = new DateTime($sql_arr['changed']);
} }
$object['_type'] = !empty($sql_arr['type']) ? $sql_arr['type'] : $this->folder->type;
$object['uid'] = $sql_arr['uid'];
$object['etag'] = $sql_arr['etag'];
}
if (!empty($fast_mode) && !empty($object)) {
unset($object['_raw']); unset($object['_raw']);
} }
else if ($noread) { else if ($noread) {

View file

@ -29,16 +29,16 @@ class kolab_storage_dav_folder extends kolab_storage_folder
/** /**
* Object constructor * Object constructor
*/ */
public function __construct($dav, $attributes, $type_annotation = '') public function __construct($dav, $attributes, $type = '')
{ {
$this->attributes = $attributes; $this->attributes = $attributes;
$this->href = $this->attributes['href']; $this->href = $this->attributes['href'];
$this->id = md5($this->dav->url . '/' . $this->href); $this->id = md5($dav->url . '/' . $this->href);
$this->dav = $dav; $this->dav = $dav;
$this->valid = true; $this->valid = true;
list($this->type, $suffix) = explode('.', $type_annotation); list($this->type, $suffix) = strpos($type, '.') ? explode('.', $type) : [$type, ''];
$this->default = $suffix == 'default'; $this->default = $suffix == 'default';
$this->subtype = $this->default ? '' : $suffix; $this->subtype = $this->default ? '' : $suffix;

View file

@ -184,7 +184,7 @@ class libkolab extends rcube_plugin
array_walk($result['changes'], function(&$change) use ($dtformat, $rcmail) { array_walk($result['changes'], function(&$change) use ($dtformat, $rcmail) {
if ($change['date']) { if ($change['date']) {
$dt = rcube_utils::anytodatetime($change['date']); $dt = rcube_utils::anytodatetime($change['date']);
if ($dt instanceof DateTime) { if ($dt instanceof DateTimeInterface) {
$change['date'] = $rcmail->format_date($dt, $dtformat); $change['date'] = $rcmail->format_date($dt, $dtformat);
} }
} }

View file

@ -21,9 +21,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
class kolab_date_recurrence_test extends PHPUnit\Framework\TestCase class KolabDateRecurrenceTest extends PHPUnit\Framework\TestCase
{ {
function setUp() function setUp(): void
{ {
$rcube = rcmail::get_instance(); $rcube = rcmail::get_instance();
$rcube->plugins->load_plugin('libkolab', true, true); $rcube->plugins->load_plugin('libkolab', true, true);
@ -251,11 +251,11 @@ class kolab_date_recurrence_test extends PHPUnit\Framework\TestCase
date_default_timezone_set('America/New_York'); date_default_timezone_set('America/New_York');
$start = new DateTime('2017-08-31 11:00:00', new DateTimeZone('Europe/Berlin')); $start = new DateTime('2017-08-31 11:00:00', new DateTimeZone('Europe/Berlin'));
$event = array( $event = [
'start' => $start, 'start' => $start,
'recurrence' => array('FREQ' => 'WEEKLY', 'INTERVAL' => '1'), 'recurrence' => ['FREQ' => 'WEEKLY', 'INTERVAL' => '1'],
'allday' => true, 'allday' => true,
); ];
$object = kolab_format::factory('event', 3.0); $object = kolab_format::factory('event', 3.0);
$object->set($event); $object->set($event);
@ -265,6 +265,6 @@ class kolab_date_recurrence_test extends PHPUnit\Framework\TestCase
$this->assertEquals($start->format('2017-09-07 H:i:s'), $next['start']->format('Y-m-d H:i:s'), 'Same time'); $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->assertEquals($start->getTimezone()->getName(), $next['start']->getTimezone()->getName(), 'Same timezone');
$this->assertSame($next['start']->_dateonly, true, '_dateonly flag'); $this->assertSame(true, $next['start']->_dateonly, '_dateonly flag');
} }
} }

View file

@ -1,6 +1,6 @@
<?php <?php
class kolab_storage_config_test extends PHPUnit\Framework\TestCase class KolabStorageConfigTest extends PHPUnit\Framework\TestCase
{ {
private $params_personal = array( private $params_personal = array(
'folder' => 'Archive', 'folder' => 'Archive',
@ -29,7 +29,7 @@ class kolab_storage_config_test extends PHPUnit\Framework\TestCase
); );
private $url_other = 'imap:///user/lucy.white%40example.org/Mailings/378?message-id=%3C22448899%40example.org%3E&date=Tue%2C+14+Apr+2015+14%3A14%3A30+%2B0200&subject=Happy+Holidays'; private $url_other = 'imap:///user/lucy.white%40example.org/Mailings/378?message-id=%3C22448899%40example.org%3E&date=Tue%2C+14+Apr+2015+14%3A14%3A30+%2B0200&subject=Happy+Holidays';
public static function setUpBeforeClass() public static function setUpBeforeClass(): void
{ {
$rcube = rcmail::get_instance(); $rcube = rcmail::get_instance();
$rcube->plugins->load_plugin('libkolab', true, true); $rcube->plugins->load_plugin('libkolab', true, true);
@ -38,11 +38,14 @@ class kolab_storage_config_test extends PHPUnit\Framework\TestCase
return; return;
} }
// Unset mock'ed storage from the Roundcube core tests
$rcmail->storage = null;
if ($rcube->config->get('tests_username')) { if ($rcube->config->get('tests_username')) {
$authenticated = $rcube->login( $authenticated = $rcube->login(
$rcube->config->get('tests_username'), $rcube->config->get('tests_username'),
$rcube->config->get('tests_password'), $rcube->config->get('tests_password'),
$rcube->config->get('default_host'), $rcube->config->get('imap_host', $rcube->config->get('default_host')),
false false
); );

View file

@ -21,9 +21,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
class kolab_storage_folder_test extends PHPUnit\Framework\TestCase class KolabStorageFolderTest extends PHPUnit\Framework\TestCase
{ {
public static function setUpBeforeClass() public static function setUpBeforeClass(): void
{ {
// load libkolab plugin // load libkolab plugin
$rcmail = rcmail::get_instance(); $rcmail = rcmail::get_instance();
@ -33,11 +33,14 @@ class kolab_storage_folder_test extends PHPUnit\Framework\TestCase
return; return;
} }
// Unset mock'ed storage from the Roundcube core tests
$rcmail->storage = null;
if ($rcmail->config->get('tests_username')) { if ($rcmail->config->get('tests_username')) {
$authenticated = $rcmail->login( $authenticated = $rcmail->login(
$rcmail->config->get('tests_username'), $rcmail->config->get('tests_username'),
$rcmail->config->get('tests_password'), $rcmail->config->get('tests_password'),
$rcmail->config->get('default_host'), $rcmail->config->get('imap_host', $rcmail->config->get('default_host')),
false false
); );

View file

@ -18,7 +18,7 @@ Add these config options used by the libkolab tests:
$config['default_host'] = '<kolab-server>'; $config['default_host'] = '<kolab-server>';
// disable all plugins // disable all plugins
$config['plugins'] = array(); $config['plugins'] = [];
``` ```
WARNING WARNING