Remove dependency on kolabcalendaring/kolabformat in CalDAV driver, various PHP8 support fixes
This commit is contained in:
parent
a9531c9336
commit
0530881f4b
19 changed files with 202 additions and 122 deletions
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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 . ' ';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue