Remove dependency on kolabcalendaring/kolabformat in CalDAV driver, various PHP8 support fixes

This commit is contained in:
Aleksander Machniak 2022-12-05 15:07:23 +01:00
parent a9531c9336
commit 0530881f4b
19 changed files with 202 additions and 122 deletions

View file

@ -23,6 +23,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#[AllowDynamicProperties]
class calendar extends rcube_plugin
{
const FREEBUSY_UNKNOWN = 0;
@ -42,6 +43,7 @@ class calendar extends rcube_plugin
public $timezone;
public $timezone_offset;
public $gmt_offset;
public $dst_active;
public $ui;
public $defaults = [
@ -1049,8 +1051,9 @@ $("#rcmfd_new_category").keypress(function(event) {
$old = $this->driver->get_event($event);
// load main event if savemode is 'all' or if deleting 'future' events
if (($event['_savemode'] == 'all' || ($event['_savemode'] == 'future' && $action == 'remove' && empty($event['_decline'])))
&& !empty($old['recurrence_id'])
if (!empty($old['recurrence_id'])
&& !empty($event['_savemode'])
&& ($event['_savemode'] == 'all' || ($event['_savemode'] == 'future' && $action == 'remove' && empty($event['_decline'])))
) {
$old['id'] = $old['recurrence_id'];
$old = $this->driver->get_event($old);
@ -1447,7 +1450,7 @@ $("#rcmfd_new_category").keypress(function(event) {
// $success is a new event ID
if ($success !== true) {
// send update notification on the main event
if ($event['_savemode'] == 'future' && !empty($event['_notify'])
if (!empty($event['_savemode']) && $event['_savemode'] == 'future' && !empty($event['_notify'])
&& !empty($old['attendees']) && !empty($old['recurrence_id'])
) {
$master = $this->driver->get_event(['id' => $old['recurrence_id'], 'calendar' => $old['calendar']], 0, true);
@ -1462,7 +1465,7 @@ $("#rcmfd_new_category").keypress(function(event) {
}
// delete old reference if saved as new
if ($event['_savemode'] == 'future' || $event['_savemode'] == 'new') {
if (!empty($event['_savemode']) && ($event['_savemode'] == 'future' || $event['_savemode'] == 'new')) {
$old = null;
}
@ -1472,7 +1475,7 @@ $("#rcmfd_new_category").keypress(function(event) {
// send out notifications
if (!empty($event['_notify']) && (!empty($event['attendees']) || !empty($old['attendees']))) {
$_savemode = $event['_savemode'];
$_savemode = $event['_savemode'] ?? null;
// send notification for the main event when savemode is 'all'
if ($action != 'remove' && $_savemode == 'all'
@ -2131,7 +2134,7 @@ $("#rcmfd_new_category").keypress(function(event) {
}
// mapping url => vurl, allday => allDay because of the fullcalendar client script
$event['vurl'] = $event['url'];
$event['vurl'] = $event['url'] ?? null;
$event['allDay'] = !empty($event['allday']);
unset($event['url']);
unset($event['allday']);
@ -2153,9 +2156,9 @@ $("#rcmfd_new_category").keypress(function(event) {
// 'changed' might be empty for event recurrences (Bug #2185)
'changed' => !empty($event['changed']) ? $this->lib->adjust_timezone($event['changed'])->format('c') : null,
'created' => !empty($event['created']) ? $this->lib->adjust_timezone($event['created'])->format('c') : null,
'title' => strval($event['title']),
'description' => strval($event['description']),
'location' => strval($event['location']),
'title' => strval($event['title'] ?? null),
'description' => strval($event['description'] ?? null),
'location' => strval($event['location'] ?? null),
] + $event;
}
@ -2460,7 +2463,7 @@ $("#rcmfd_new_category").keypress(function(event) {
}
if ($rsvp === null) {
$rsvp = !$old || $event['sequence'] > $old['sequence'];
$rsvp = !$old || ($event['sequence'] ?? 0) > ($old['sequence'] ?? 0);
}
$itip = $this->load_itip();
@ -3990,11 +3993,15 @@ $("#rcmfd_new_category").keypress(function(event) {
*/
public function find_first_occurrence($event)
{
// Make sure libkolab plugin is loaded in case of Kolab driver
// Make sure libkolab/libcalendaring plugins are loaded
$this->load_driver();
// Use libkolab to compute recurring events (and libkolab plugin)
if (class_exists('kolabformat') && class_exists('kolabcalendaring') && class_exists('kolab_date_recurrence')) {
$driver_name = $this->rc->config->get('calendar_driver', 'database');
// Use kolabcalendaring/kolabformat to compute recurring events only with the Kolab driver
if ($driver_name == 'kolab' && class_exists('kolabformat') && class_exists('kolabcalendaring')
&& class_exists('kolab_date_recurrence')
) {
$object = kolab_format::factory('event', 3.0);
$object->set($event);

View file

@ -163,12 +163,8 @@ class caldav_calendar extends kolab_storage_dav_folder
}
if (!empty($master)) {
// check for match in top-level exceptions (aka loose single occurrences)
if (!empty($master['_formatobj']) && ($instance = $master['_formatobj']->get_instance($instance_id))) {
$this->events[$id] = $this->_to_driver_event($instance, false, true, $master);
}
// check for match on the first instance already
else if (!empty($master['_instance']) && $master['_instance'] == $instance_id) {
if (!empty($master['_instance']) && $master['_instance'] == $instance_id) {
$this->events[$id] = $master;
}
else if (!empty($master['recurrence'])) {
@ -580,12 +576,6 @@ class caldav_calendar extends kolab_storage_dav_folder
*/
public function get_recurring_events($event, $start, $end = null, $event_id = null, $limit = null)
{
$object = $event['_formatobj'];
if (!is_object($object)) {
return [];
}
// determine a reasonable end date if none given
if (!$end) {
$end = clone $event['start'];
@ -641,7 +631,8 @@ class caldav_calendar extends kolab_storage_dav_folder
}
// Check first occurrence, it might have been moved
if ($first = $exdata[$event['start']->format('Ymd')]) {
if (!empty($exdata[$event['start']->format('Ymd')])) {
$first = $exdata[$event['start']->format('Ymd')];
// return it only if not already in the result, but in the requested period
if (!($event['start'] <= $end && $event['end'] >= $start)
&& ($first['start'] <= $end && $first['end'] >= $start)
@ -655,7 +646,7 @@ class caldav_calendar extends kolab_storage_dav_folder
}
// use libkolab to compute recurring events
$recurrence = new kolab_date_recurrence($object);
$recurrence = libcalendaring::get_recurrence($event);
$i = 0;
while ($next_event = $recurrence->next_instance()) {
@ -663,9 +654,7 @@ class caldav_calendar extends kolab_storage_dav_folder
$instance_id = $next_event['start']->format($recurrence_id_format);
// use this event data for future recurring instances
if (!empty($futuredata[$datestr])) {
$overlay_data = $futuredata[$datestr];
}
$overlay_data = $futuredata[$datestr] ?? null;
$rec_id = $event['uid'] . '-' . $instance_id;
$exception = !empty($exdata[$datestr]) ? $exdata[$datestr] : $overlay_data;
@ -762,17 +751,10 @@ class caldav_calendar extends kolab_storage_dav_folder
// clean up exception data
if (!empty($record['recurrence']) && !empty($record['recurrence']['EXCEPTIONS'])) {
array_walk($record['recurrence']['EXCEPTIONS'], function(&$exception) {
unset($exception['_mailbox'], $exception['_msguid'], $exception['_formatobj'], $exception['_attachments']);
unset($exception['_attachments']);
});
}
// Load the given event data into a libkolabxml container
// it's needed for recurrence resolving, which uses libcalendaring
// TODO: Drop dependency on libkolabxml?
$event_xml = new kolab_format_event();
$event_xml->set($record);
$record['_formatobj'] = $event_xml;
return $record;
}
@ -829,10 +811,7 @@ class caldav_calendar extends kolab_storage_dav_folder
// clean up exception data
if (!empty($event['exceptions'])) {
array_walk($event['exceptions'], function(&$exception) use ($cleanup_fn) {
unset($exception['_mailbox'], $exception['_msguid'], $exception['_formatobj']);
$cleanup_fn($exception);
});
array_walk($event['exceptions'], $cleanup_fn);
}
// copy meta data (starting with _) from old object

View file

@ -34,6 +34,8 @@ class caldav_driver extends kolab_driver
public $alarm_types = ['DISPLAY', 'AUDIO'];
public $categoriesimmutable = true;
protected $scheduling_properties = ['start', 'end', 'location'];
/**
* Default constructor
*/
@ -530,11 +532,6 @@ class caldav_driver extends kolab_driver
*/
public function get_recurring_events($event, $start, $end = null)
{
// load the given event data into a libkolabxml container
$event_xml = new kolab_format_event();
$event_xml->set($event);
$event['_formatobj'] = $event_xml;
$this->_read_calendars();
$storage = reset($this->calendars);
@ -546,13 +543,8 @@ class caldav_driver extends kolab_driver
*/
protected function get_recurrence_count($event, $dtstart)
{
// load the given event data into a libkolabxml container
$event_xml = new kolab_format_event();
$event_xml->set($event);
$event['_formatobj'] = $event_xml;
// use libkolab to compute recurring events
$recurrence = new kolab_date_recurrence($event['_formatobj']);
$recurrence = libcalendaring::get_recurrence($event);
$count = 0;
while (($next_event = $recurrence->next_instance()) && $next_event['start'] <= $dtstart && $count < 1000) {
@ -562,6 +554,98 @@ class caldav_driver extends kolab_driver
return $count;
}
/**
* Determine whether the current change affects scheduling and reset attendee status accordingly
*/
protected function check_scheduling(&$event, $old, $update = true)
{
// skip this check when importing iCal/iTip events
if (isset($event['sequence']) || !empty($event['_method'])) {
return false;
}
// iterate through the list of properties considered 'significant' for scheduling
$reschedule = $this->is_rescheduling_needed($event, $old);
// reset all attendee status to needs-action (#4360)
if ($update && $reschedule && !empty($event['attendees'])) {
$is_organizer = false;
$emails = $this->cal->get_user_emails();
$attendees = $event['attendees'];
foreach ($attendees as $i => $attendee) {
if ($attendee['role'] == 'ORGANIZER'
&& !empty($attendee['email'])
&& in_array(strtolower($attendee['email']), $emails)
) {
$is_organizer = true;
}
else if ($attendee['role'] != 'ORGANIZER'
&& $attendee['role'] != 'NON-PARTICIPANT'
&& $attendee['status'] != 'DELEGATED'
) {
$attendees[$i]['status'] = 'NEEDS-ACTION';
$attendees[$i]['rsvp'] = true;
}
}
// update attendees only if I'm the organizer
if ($is_organizer || (!empty($event['organizer']) && in_array(strtolower($event['organizer']['email']), $emails))) {
$event['attendees'] = $attendees;
}
}
return $reschedule;
}
/**
* Identify changes considered relevant for scheduling
*
* @param array Hash array with NEW object properties
* @param array Hash array with OLD object properties
*
* @return bool True if changes affect scheduling, False otherwise
*/
protected function is_rescheduling_needed($object, $old = null)
{
$reschedule = false;
foreach ($this->scheduling_properties as $prop) {
$a = $old[$prop] ?? null;
$b = $object[$prop] ?? null;
if (!empty($object['allday'])
&& ($prop == 'start' || $prop == 'end')
&& $a instanceof DateTimeInterface
&& $b instanceof DateTimeInterface
) {
$a = $a->format('Y-m-d');
$b = $b->format('Y-m-d');
}
if ($prop == 'recurrence' && is_array($a) && is_array($b)) {
unset($a['EXCEPTIONS'], $b['EXCEPTIONS']);
$a = array_filter($a);
$b = array_filter($b);
// advanced rrule comparison: no rescheduling if series was shortened
if ($a['COUNT'] && $b['COUNT'] && $b['COUNT'] < $a['COUNT']) {
unset($a['COUNT'], $b['COUNT']);
}
else if ($a['UNTIL'] && $b['UNTIL'] && $b['UNTIL'] < $a['UNTIL']) {
unset($a['UNTIL'], $b['UNTIL']);
}
}
if ($a != $b) {
$reschedule = true;
break;
}
}
return $reschedule;
}
/**
* Callback function to produce driver-specific calendar create/edit form
*

View file

@ -709,7 +709,8 @@ class kolab_calendar extends kolab_storage_folder_api
}
// Check first occurrence, it might have been moved
if ($first = $exdata[$event['start']->format('Ymd')]) {
if (!empty($exdata[$event['start']->format('Ymd')])) {
$first = $exdata[$event['start']->format('Ymd')];
// return it only if not already in the result, but in the requested period
if (!($event['start'] <= $end && $event['end'] >= $start)
&& ($first['start'] <= $end && $first['end'] >= $start)
@ -731,9 +732,7 @@ class kolab_calendar extends kolab_storage_folder_api
$instance_id = $next_event['start']->format($recurrence_id_format);
// use this event data for future recurring instances
if (!empty($futuredata[$datestr])) {
$overlay_data = $futuredata[$datestr];
}
$overlay_data = $futuredata[$datestr] ?? null;
$rec_id = $event['uid'] . '-' . $instance_id;
$exception = !empty($exdata[$datestr]) ? $exdata[$datestr] : $overlay_data;

View file

@ -1297,8 +1297,8 @@ class kolab_driver extends calendar_driver
// when saving an instance in 'all' mode, copy recurrence exceptions over
if (!empty($old['recurrence_id'])) {
$event['recurrence']['EXCEPTIONS'] = $master['recurrence']['EXCEPTIONS'];
$event['recurrence']['EXDATE'] = $master['recurrence']['EXDATE'];
$event['recurrence']['EXCEPTIONS'] = $master['recurrence']['EXCEPTIONS'] ?? [];
$event['recurrence']['EXDATE'] = $master['recurrence']['EXDATE'] ?? [];
}
else if (!empty($master['_instance'])) {
$event['_instance'] = $master['_instance'];
@ -1388,7 +1388,7 @@ class kolab_driver extends calendar_driver
/**
* Determine whether the current change affects scheduling and reset attendee status accordingly
*/
public function check_scheduling(&$event, $old, $update = true)
protected function check_scheduling(&$event, $old, $update = true)
{
// skip this check when importing iCal/iTip events
if (isset($event['sequence']) || !empty($event['_method'])) {
@ -2235,7 +2235,9 @@ class kolab_driver extends calendar_driver
foreach ($event['attendees'] as $attendee) {
if (in_array($attendee['email'], $user_emails)) {
$partstat = $attendee['status'];
if (!empty($attendee['status'])) {
$partstat = $attendee['status'];
}
break;
}
}

View file

@ -1,7 +1,5 @@
<?php
require_once realpath(__DIR__ . '/../../libcalendaring/lib/libcalendaring_itip.php');
/**
* iTIP functions for the Calendar plugin
*
@ -28,6 +26,8 @@ require_once realpath(__DIR__ . '/../../libcalendaring/lib/libcalendaring_itip.p
*/
class calendar_itip extends libcalendaring_itip
{
protected $db_itipinvitations;
/**
* Constructor to set text domain to calendar
*/

View file

@ -332,7 +332,7 @@ class carddav_contacts extends rcube_addressbook
$this->sortindex = array_merge($this->sortindex, $local_sortindex);
}
}
else if (is_array($this->filter['ids'])) {
else if (isset($this->filter['ids']) && is_array($this->filter['ids'])) {
$ids = $this->filter['ids'];
if (count($ids)) {
$uids = array_map([$this, 'id2uid'], $this->filter['ids']);
@ -1109,21 +1109,23 @@ class carddav_contacts extends rcube_addressbook
switch ($this->sort_col) {
case 'name':
$str = $rec['name'] . $rec['prefix'];
$str = ($rec['name'] ?? '') . ($rec['prefix'] ?? '');
case 'firstname':
$str .= $rec['firstname'] . $rec['middlename'] . $rec['surname'];
$str .= ($rec['firstname'] ?? '') . ($rec['middlename'] ?? '') . ($rec['surname'] ?? '');
break;
case 'surname':
$str = $rec['surname'] . $rec['firstname'] . $rec['middlename'];
$str = ($rec['surname'] ?? '') . ($rec['firstname'] ?? '') . ($rec['middlename'] ?? '');
break;
default:
$str = $rec[$this->sort_col];
$str = $rec[$this->sort_col] ?? '';
break;
}
$str .= is_array($rec['email']) ? $rec['email'][0] : $rec['email'];
if (!empty($rec['email'])) {
$str .= is_array($rec['email']) ? $rec['email'][0] : $rec['email'];
}
return mb_strtolower($str);
}

View file

@ -37,6 +37,7 @@ class kolab_addressbook extends rcube_plugin
private $sources;
private $rc;
private $ui;
private $recurrent = false;
const GLOBAL_FIRST = 0;
const PERSONAL_FIRST = 1;
@ -221,8 +222,8 @@ class kolab_addressbook extends rcube_plugin
$out .= $kolab . $spec;
$this->rc->output->set_env('contactgroups', array_filter($jsdata, function($src){ return $src['type'] == 'group'; }));
$this->rc->output->set_env('address_sources', array_filter($jsdata, function($src){ return $src['type'] != 'group'; }));
$this->rc->output->set_env('contactgroups', array_filter($jsdata, function($src){ return isset($src['type']) && $src['type'] == 'group'; }));
$this->rc->output->set_env('address_sources', array_filter($jsdata, function($src){ return !isset($src['type']) || $src['type'] != 'group'; }));
$args['content'] = html::tag('ul', $args, $out, html::$common_attrib);
return $args;
@ -276,27 +277,27 @@ class kolab_addressbook extends rcube_plugin
{
$current = rcube_utils::get_input_value('_source', rcube_utils::INPUT_GPC);
if (!$source['virtual']) {
if (empty($source['virtual'])) {
$jsdata[$id] = $source;
$jsdata[$id]['name'] = html_entity_decode($source['name'], ENT_NOQUOTES, RCUBE_CHARSET);
}
// set class name(s)
$classes = array('addressbook');
if ($source['group'])
if (!empty($source['group']))
$classes[] = $source['group'];
if ($current === $id)
$classes[] = 'selected';
if ($source['readonly'])
if (!empty($source['readonly']))
$classes[] = 'readonly';
if ($source['virtual'])
if (!empty($source['virtual']))
$classes[] = 'virtual';
if ($source['class_name'])
if (!empty($source['class_name']))
$classes[] = $source['class_name'];
$name = !empty($source['listname']) ? $source['listname'] : (!empty($source['name']) ? $source['name'] : $id);
$label_id = 'kabt:' . $id;
$inner = ($source['virtual'] ?
$inner = (!empty($source['virtual']) ?
html::a(array('tabindex' => '0'), $name) :
html::a(array(
'href' => $this->rc->url(array('_source' => $id)),
@ -331,12 +332,12 @@ class kolab_addressbook extends rcube_plugin
return html::div(null, $inner);
}
$out .= html::tag('li', array(
$out = html::tag('li', array(
'id' => 'rcmli' . rcube_utils::html_identifier($id, true),
'class' => join(' ', $classes),
'noclose' => true,
),
html::div($source['subscribed'] ? 'subscribed' : null, $inner)
html::div(!empty($source['subscribed']) ? 'subscribed' : null, $inner)
);
$groupdata = array('out' => '', 'jsdata' => $jsdata, 'source' => $id);

View file

@ -89,7 +89,6 @@ class kolab_addressbook_ui
$content = html::tag('li', $idx ? null : array('class' => 'separator_above'),
$this->plugin->api->output->button(array(
'label' => 'kolab_addressbook.'.str_replace('-', '', $command),
'domain' => $this->ID,
'class' => str_replace('-', ' ', $command) . ' disabled',
'classact' => str_replace('-', ' ', $command) . ' active',
'command' => $command,

View file

@ -52,8 +52,6 @@ class libcalendaring_recurrence
$this->duration = $event['start']->diff($event['end']);
}
$event['start']->_dateonly = !empty($event['allday']);
$this->init($event['recurrence'], $event['start']);
}
}
@ -67,7 +65,7 @@ class libcalendaring_recurrence
public function init($recurrence, $start)
{
$this->start = $start;
$this->dateonly = !empty($start->_dateonly);
$this->dateonly = !empty($start->_dateonly) || !empty($this->event['allday']);
$this->recurrence = $recurrence;
$event = [
@ -160,6 +158,7 @@ class libcalendaring_recurrence
$end->add(new DateInterval('P100Y'));
}
*/
return isset($end) ? $this->toDateTime($end) : false;
}

View file

@ -582,8 +582,8 @@ class libcalendaring_vcalendar implements Iterator
if (substr($value, 0, 4) == 'http' && !strpos($value, ':attachment:')) {
$event['links'][] = $value;
}
else if (strlen($value) && strtoupper($params['VALUE']) == 'BINARY') {
$attachment = self::map_keys($params, array('FMTTYPE' => 'mimetype', 'X-LABEL' => 'name', 'X-APPLE-FILENAME' => 'name'));
else if (is_string($value) && strlen($value) && !empty($params['VALUE']) && strtoupper($params['VALUE']) == 'BINARY') {
$attachment = self::map_keys($params, ['FMTTYPE' => 'mimetype', 'X-LABEL' => 'name', 'X-APPLE-FILENAME' => 'name']);
$attachment['data'] = $value;
$attachment['size'] = strlen($value);
$event['attachments'][] = $attachment;

View file

@ -36,7 +36,7 @@ class libcalendaring extends rcube_plugin
public $gmt_offset;
public $dst_active;
public $timezone_offset;
public $ical_parts = array();
public $ical_parts = [];
public $ical_message;
public $defaults = array(
@ -174,10 +174,10 @@ class libcalendaring extends rcube_plugin
/**
* Load recurrence computation engine
*/
public static function get_recurrence()
public static function get_recurrence($object = null)
{
$self = self::get_instance();
return new libcalendaring_recurrence($self);
return new libcalendaring_recurrence($self, $object);
}
/**
@ -1295,7 +1295,7 @@ class libcalendaring extends rcube_plugin
public static function identify_recurrence_instance(&$object)
{
// for savemode=all, remove recurrence instance identifiers
if (!empty($object['_savemode']) && $object['_savemode'] == 'all' && $object['recurrence']) {
if (!empty($object['_savemode']) && $object['_savemode'] == 'all' && !empty($object['recurrence'])) {
unset($object['_instance'], $object['recurrence_date']);
}
// set instance and 'savemode' according to recurrence-id

View file

@ -739,7 +739,7 @@ abstract class kolab_format
*/
public static function merge_attachments(&$object, $old)
{
$object['_attachments'] = (array) $old['_attachments'];
$object['_attachments'] = isset($old['_attachments']) && is_array($old['_attachments']) ? $old['_attachments'] : [];
// delete existing attachment(s)
if (!empty($object['deleted_attachments'])) {
@ -771,7 +771,7 @@ abstract class kolab_format
else {
// find attachment by name, so we can update it if exists
// and make sure there are no duplicates
foreach ((array) $object['_attachments'] as $cid => $att) {
foreach ($object['_attachments'] as $cid => $att) {
if ($att && $attachment['name'] == $att['name']) {
$key = $cid;
}

View file

@ -49,6 +49,7 @@ class kolab_storage_cache
protected $limit = null;
protected $error = 0;
protected $server_timezone;
protected $sync_start;
/**
@ -769,6 +770,7 @@ class kolab_storage_cache
public function select($query = array(), $uids = false, $fast = false)
{
$result = $uids ? array() : new kolab_storage_dataset($this);
$count = null;
// read from local cache DB (assume it to be synchronized)
if ($this->ready) {
@ -923,8 +925,12 @@ class kolab_storage_cache
{
if (!empty($sortcols)) {
$sortcols = array_map(function($v) {
list($column, $order) = explode(' ', $v, 2);
return "`$column`" . ($order ? " $order" : '');
$v = trim($v);
if (strpos($v, ' ')) {
list($column, $order) = explode(' ', $v, 2);
return "`{$column}` {$order}";
}
return "`{$v}`";
}, (array) $sortcols);
$this->order_by = join(', ', $sortcols);
@ -1257,13 +1263,15 @@ class kolab_storage_cache
$write_query = "UPDATE `{$this->folders_table}` SET `synclock` = ? WHERE `folder_id` = ? AND `synclock` = ?";
$max_lock_time = $this->_max_sync_lock_time();
$sync_lock = intval($this->metadata['synclock'] ?? 0);
// wait if locked (expire locks after 10 minutes) ...
// ... or if setting lock fails (another process meanwhile set it)
while (
(intval($this->metadata['synclock']) + $max_lock_time > time()) ||
(($res = $this->db->query($write_query, time(), $this->folder_id, intval($this->metadata['synclock']))) &&
!($affected = $this->db->affected_rows($res)))
($sync_lock + $max_lock_time > time()) ||
(($res = $this->db->query($write_query, time(), $this->folder_id, $sync_lock))
&& !($affected = $this->db->affected_rows($res))
)
) {
usleep(500000);
$this->metadata = $this->db->fetch_assoc($this->db->query($read_query, $this->folder_id));

View file

@ -39,7 +39,7 @@ class kolab_storage_cache_event extends kolab_storage_cache
$sql_data['dtend'] = $this->_convert_datetime($object['end']);
// extend date range for recurring events
if ($object['recurrence'] && $object['_formatobj']) {
if ($object['recurrence']) {
$recurrence = new kolab_date_recurrence($object['_formatobj']);
$dtend = $recurrence->end() ?: new DateTime('now +100 years');
$sql_data['dtend'] = $this->_convert_datetime($dtend);

View file

@ -46,9 +46,6 @@ class kolab_storage_dav
*/
public function setup()
{
$rcmail = rcube::get_instance();
$this->config = $rcmail->config;
$this->dav = new kolab_dav_client($this->url);
}

View file

@ -159,6 +159,7 @@ class kolab_storage_dav_cache extends kolab_storage_cache
// Fetch new objects and store in DB
if (!empty($new_index)) {
$i = 0;
foreach (array_chunk($new_index, $chunk_size, true) as $chunk) {
$objects = $this->folder->dav->getData($this->folder->href, $this->folder->get_dav_type(), $chunk);
@ -412,6 +413,7 @@ class kolab_storage_dav_cache extends kolab_storage_cache
public function select($query = [], $uids = false, $fast = false)
{
$result = $uids ? [] : new kolab_storage_dataset($this);
$count = null;
$this->_read_folder_data();

View file

@ -36,21 +36,20 @@ class kolab_storage_dav_cache_contact extends kolab_storage_dav_cache
protected function _serialize($object)
{
$sql_data = parent::_serialize($object);
$sql_data['type'] = $object['_type'] ?: 'contact';
$sql_data['type'] = !empty($object['_type']) ? $object['_type'] : 'contact';
if ($sql_data['type'] == 'group' || (!empty($object['kind']) && $object['kind'] == 'group')) {
$sql_data['type'] = 'group';
}
// columns for sorting
$sql_data['name'] = rcube_charset::clean($object['name'] . $object['prefix']);
$sql_data['firstname'] = rcube_charset::clean($object['firstname'] . $object['middlename'] . $object['surname']);
$sql_data['surname'] = rcube_charset::clean($object['surname'] . $object['firstname'] . $object['middlename']);
$sql_data['name'] = rcube_charset::clean(($object['name'] ?? '') . ($object['prefix'] ?? ''));
$sql_data['firstname'] = rcube_charset::clean(($object['firstname'] ?? '') . ($object['middlename'] ?? '') . ($object['surname'] ?? ''));
$sql_data['surname'] = rcube_charset::clean(($object['surname'] ?? '') . ($object['firstname'] ?? '') . ($object['middlename'] ?? ''));
$sql_data['email'] = '';
foreach ($object as $colname => $value) {
list($col, $field) = explode(':', $colname);
if ($col == 'email' && !empty($value)) {
if (!empty($value) && ($colname == 'email' || strpos($colname, 'email:') === 0)) {
$sql_data['email'] = is_array($value) ? $value[0] : $value;
break;
}
@ -84,14 +83,14 @@ class kolab_storage_dav_cache_contact extends kolab_storage_dav_cache
$data = '';
foreach ($object as $colname => $value) {
list($col, $field) = explode(':', $colname);
list($col, $field) = strpos($colname, ':') ? explode(':', $colname) : [$colname, null];
$val = '';
$val = null;
if (in_array($col, $this->fulltext_cols)) {
$val = is_array($value) ? join(' ', $value) : $value;
}
if (strlen($val)) {
if (is_string($val) && strlen($val)) {
$data .= $val . ' ';
}
}

View file

@ -41,19 +41,14 @@ class kolab_storage_dav_cache_event extends kolab_storage_dav_cache
// extend date range for recurring events
if (!empty($object['recurrence'])) {
if (empty($object['_formatobj'])) {
$event_xml = new kolab_format_event();
$event_xml->set($object);
$object['_formatobj'] = $event_xml;
}
$recurrence = new kolab_date_recurrence($object['_formatobj']);
$recurrence = libcalendaring::get_recurrence($object);
$dtend = $recurrence->end() ?: new DateTime('now +100 years');
$sql_data['dtend'] = $this->_convert_datetime($dtend);
}
// extend start/end dates to spawn all exceptions
if (is_array($object['exceptions'])) {
// FIXME: This should be done via libcalendaring_recurrence use above?
if (!empty($object['exceptions']) && is_array($object['exceptions'])) {
foreach ($object['exceptions'] as $exception) {
if ($exception['start'] instanceof DateTimeInterface) {
$exstart = $this->_convert_datetime($exception['start']);
@ -86,12 +81,18 @@ class kolab_storage_dav_cache_event extends kolab_storage_dav_cache
$data = '';
foreach ($this->fulltext_cols as $colname) {
list($col, $field) = explode(':', $colname);
list($col, $field) = strpos($colname, ':') ? explode(':', $colname) : [$colname, null];
if (empty($object[$col])) {
continue;
}
if ($field) {
$a = [];
foreach ((array) $object[$col] as $attr) {
$a[] = $attr[$field];
if (!empty($attr[$field])) {
$a[] = $attr[$field];
}
}
$val = join(' ', $a);
}
@ -99,14 +100,15 @@ class kolab_storage_dav_cache_event extends kolab_storage_dav_cache
$val = is_array($object[$col]) ? join(' ', $object[$col]) : $object[$col];
}
if (strlen($val))
if (is_string($val) && strlen($val)) {
$data .= $val . ' ';
}
}
$words = rcube_utils::normalize_string($data, true);
// collect words from recurrence exceptions
if (is_array($object['exceptions'])) {
if (!empty($object['exceptions']) && is_array($object['exceptions'])) {
foreach ($object['exceptions'] as $exception) {
$words = array_merge($words, $this->get_words($exception));
}
@ -137,14 +139,14 @@ class kolab_storage_dav_cache_event extends kolab_storage_dav_cache
}
// collect tags from recurrence exceptions
if (is_array($object['exceptions'])) {
if (!empty($object['exceptions']) && is_array($object['exceptions'])) {
foreach ($object['exceptions'] as $exception) {
$tags = array_merge($tags, $this->get_tags($exception));
}
}
if (!empty($object['status'])) {
$tags[] = 'x-status:' . strtolower($object['status']);
$tags[] = 'x-status:' . strtolower($object['status']);
}
return array_unique($tags);