Sync v3.5.7

This commit is contained in:
Aleksander Machniak 2021-05-09 09:09:57 +02:00
parent 3d195270da
commit e11e26ed24
17 changed files with 13548 additions and 12520 deletions

File diff suppressed because it is too large Load diff

View file

@ -563,6 +563,11 @@ function rcube_calendar_ui(settings)
} }
}); });
var close_func = function(e) {
rcmail.command('menu-close', 'eventoptionsmenu', null, e);
$('.libcal-rsvp-replymode').hide();
};
// open jquery UI dialog // open jquery UI dialog
$dialog.dialog({ $dialog.dialog({
modal: true, modal: true,
@ -575,21 +580,12 @@ function rcube_calendar_ui(settings)
$dialog.parent().find('button:not(.ui-dialog-titlebar-close,.delete)').first().focus(); $dialog.parent().find('button:not(.ui-dialog-titlebar-close,.delete)').first().focus();
}, 5); }, 5);
}, },
beforeClose: function(e) {
rcmail.command('menu-close', 'eventoptionsmenu', null, e);
},
close: function(e) { close: function(e) {
$dialog.dialog('destroy').attr('aria-hidden', 'true').hide(); close_func(e);
$('.libcal-rsvp-replymode').hide(); $dialog.dialog('close');
},
dragStart: function(e) {
rcmail.command('menu-close', 'eventoptionsmenu', null, e);
$('.libcal-rsvp-replymode').hide();
},
resizeStart: function(e) {
rcmail.command('menu-close', 'eventoptionsmenu', null, e);
$('.libcal-rsvp-replymode').hide();
}, },
dragStart: close_func,
resizeStart: close_func,
buttons: buttons, buttons: buttons,
minWidth: 320, minWidth: 320,
width: 420 width: 420
@ -611,12 +607,12 @@ function rcube_calendar_ui(settings)
.attr({href: '#', 'class': 'dropdown-link btn btn-link options', 'data-popup-pos': 'top'}) .attr({href: '#', 'class': 'dropdown-link btn btn-link options', 'data-popup-pos': 'top'})
.text(rcmail.gettext('eventoptions','calendar')) .text(rcmail.gettext('eventoptions','calendar'))
.click(function(e) { .click(function(e) {
return rcmail.command('menu-open','eventoptionsmenu', this, e) return rcmail.command('menu-open','eventoptionsmenu', this, e);
}) })
.appendTo($dialog.parent().find('.ui-dialog-buttonset')); .appendTo($dialog.parent().find('.ui-dialog-buttonset'));
} }
rcmail.enable_command('event-history', calendar.history) rcmail.enable_command('event-history', calendar.history);
rcmail.triggerEvent('calendar-event-dialog', {dialog: $dialog}); rcmail.triggerEvent('calendar-event-dialog', {dialog: $dialog});
}; };
@ -626,12 +622,14 @@ function rcube_calendar_ui(settings)
{ {
var cutype = $(this).attr('data-cutype'), var cutype = $(this).attr('data-cutype'),
mailto = this.href.substr(7); mailto = this.href.substr(7);
if (rcmail.env.calendar_resources && cutype == 'RESOURCE') { if (rcmail.env.calendar_resources && cutype == 'RESOURCE') {
event_resources_dialog(mailto); event_resources_dialog(mailto);
} }
else { else {
rcmail.command('compose', mailto, e ? e.target : null, e); rcmail.command('compose', mailto, e ? e.target : null, e);
} }
return false; return false;
}; };
@ -3179,9 +3177,9 @@ function rcube_calendar_ui(settings)
if (v.role != 'ORGANIZER') { if (v.role != 'ORGANIZER') {
v.status = 'NEEDS-ACTION'; v.status = 'NEEDS-ACTION';
} }
}) });
event_edit_dialog('new', copy); setTimeout(function() { event_edit_dialog('new', copy); }, 50);
} }
}; };

View file

@ -4,7 +4,7 @@
"description": "Calendar plugin", "description": "Calendar plugin",
"homepage": "https://git.kolab.org/diffusion/RPK/", "homepage": "https://git.kolab.org/diffusion/RPK/",
"license": "AGPLv3", "license": "AGPLv3",
"version": "3.5.2", "version": "3.5.7",
"authors": [ "authors": [
{ {
"name": "Thomas Bruederli", "name": "Thomas Bruederli",
@ -24,7 +24,7 @@
} }
], ],
"require": { "require": {
"php": ">=5.3.0", "php": ">=5.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

@ -111,23 +111,24 @@ abstract class calendar_driver
public $attachments = false; public $attachments = false;
public $undelete = false; public $undelete = false;
public $history = false; public $history = false;
public $categoriesimmutable = false; public $alarm_types = ['DISPLAY'];
public $alarm_types = array('DISPLAY');
public $alarm_absolute = true; public $alarm_absolute = true;
public $categoriesimmutable = false;
public $last_error; public $last_error;
protected $default_categories = array( protected $default_categories = [
'Personal' => 'c0c0c0', 'Personal' => 'c0c0c0',
'Work' => 'ff0000', 'Work' => 'ff0000',
'Family' => '00ff00', 'Family' => '00ff00',
'Holiday' => 'ff6600', 'Holiday' => 'ff6600',
); ];
/** /**
* Get a list of available calendars from this source * Get a list of available calendars from this source
* *
* @param integer Bitmask defining filter criterias. * @param int $filter Bitmask defining filter criterias.
* See FILTER_* constants for possible values. * See FILTER_* constants for possible values.
*
* @return array List of calendars * @return array List of calendars
*/ */
abstract function list_calendars($filter = 0); abstract function list_calendars($filter = 0);
@ -135,10 +136,11 @@ abstract class calendar_driver
/** /**
* Create a new calendar assigned to the current user * Create a new calendar assigned to the current user
* *
* @param array Hash array with calendar properties * @param array $prop Hash array with calendar properties
* name: Calendar name * name: Calendar name
* color: The color of the calendar * color: The color of the calendar
* showalarms: True if alarms are enabled * showalarms: True if alarms are enabled
*
* @return mixed ID of the calendar on success, False on error * @return mixed ID of the calendar on success, False on error
*/ */
abstract function create_calendar($prop); abstract function create_calendar($prop);
@ -146,39 +148,43 @@ abstract class calendar_driver
/** /**
* Update properties of an existing calendar * Update properties of an existing calendar
* *
* @param array Hash array with calendar properties * @param array $prop Hash array with calendar properties
* id: Calendar Identifier * id: Calendar Identifier
* name: Calendar name * name: Calendar name
* color: The color of the calendar * color: The color of the calendar
* showalarms: True if alarms are enabled (if supported) * showalarms: True if alarms are enabled (if supported)
* @return boolean True on success, Fales on failure *
* @return bool True on success, Fales on failure
*/ */
abstract function edit_calendar($prop); abstract function edit_calendar($prop);
/** /**
* Set active/subscribed state of a calendar * Set active/subscribed state of a calendar
* *
* @param array Hash array with calendar properties * @param array $prop Hash array with calendar properties
* id: Calendar Identifier * id: Calendar Identifier
* active: True if calendar is active, false if not * active: True if calendar is active, false if not
* @return boolean True on success, Fales on failure *
* @return bool True on success, Fales on failure
*/ */
abstract function subscribe_calendar($prop); abstract function subscribe_calendar($prop);
/** /**
* Delete the given calendar with all its contents * Delete the given calendar with all its contents
* *
* @param array Hash array with calendar properties * @param array $prop Hash array with calendar properties
* id: Calendar Identifier * id: Calendar Identifier
* @return boolean True on success, Fales on failure *
* @return bool True on success, Fales on failure
*/ */
abstract function delete_calendar($prop); abstract function delete_calendar($prop);
/** /**
* Search for shared or otherwise not listed calendars the user has access * Search for shared or otherwise not listed calendars the user has access
* *
* @param string Search string * @param string $query Search string
* @param string Section/source to search * @param string $source Section/source to search
*
* @return array List of calendars * @return array List of calendars
*/ */
abstract function search_calendars($query, $source); abstract function search_calendars($query, $source);
@ -186,7 +192,8 @@ abstract class calendar_driver
/** /**
* Add a single event to the database * Add a single event to the database
* *
* @param array Hash array with event properties (see header of this file) * @param array $event Hash array with event properties (see header of this file)
*
* @return mixed New event ID on success, False on error * @return mixed New event ID on success, False on error
*/ */
abstract function new_event($event); abstract function new_event($event);
@ -194,18 +201,20 @@ abstract class calendar_driver
/** /**
* Update an event entry with the given data * Update an event entry with the given data
* *
* @param array Hash array with event properties (see header of this file) * @param array $event Hash array with event properties (see header of this file)
* @return boolean True on success, False on error *
* @return bool True on success, False on error
*/ */
abstract function edit_event($event); abstract function edit_event($event);
/** /**
* Extended event editing with possible changes to the argument * Extended event editing with possible changes to the argument
* *
* @param array Hash array with event properties * @param array &$event Hash array with event properties
* @param string New participant status * @param string $status New participant status
* @param array List of hash arrays with updated attendees * @param array $attendees List of hash arrays with updated attendees
* @return boolean True on success, False on error *
* @return bool True on success, False on error
*/ */
public function edit_rsvp(&$event, $status, $attendees) public function edit_rsvp(&$event, $status, $attendees)
{ {
@ -215,9 +224,10 @@ abstract class calendar_driver
/** /**
* Update the participant status for the given attendee * Update the participant status for the given attendee
* *
* @param array Hash array with event properties * @param array &$event Hash array with event properties
* @param array List of hash arrays each represeting an updated attendee * @param array $attendees List of hash arrays each represeting an updated attendee
* @return boolean True on success, False on error *
* @return bool True on success, False on error
*/ */
public function update_attendees(&$event, $attendees) public function update_attendees(&$event, $attendees)
{ {
@ -227,45 +237,47 @@ abstract class calendar_driver
/** /**
* Move a single event * Move a single event
* *
* @param array Hash array with event properties: * @param array $event Hash array with event properties:
* id: Event identifier * id: Event identifier
* start: Event start date/time as DateTime object * start: Event start date/time as DateTime object
* end: Event end date/time as DateTime object * end: Event end date/time as DateTime object
* allday: Boolean flag if this is an all-day event * allday: Boolean flag if this is an all-day event
* @return boolean True on success, False on error *
* @return bool True on success, False on error
*/ */
abstract function move_event($event); abstract function move_event($event);
/** /**
* Resize a single event * Resize a single event
* *
* @param array Hash array with event properties: * @param array $event Hash array with event properties:
* id: Event identifier * id: Event identifier
* start: Event start date/time as DateTime object with timezone * start: Event start date/time as DateTime object with timezone
* end: Event end date/time as DateTime object with timezone * end: Event end date/time as DateTime object with timezone
* @return boolean True on success, False on error *
* @return bool True on success, False on error
*/ */
abstract function resize_event($event); abstract function resize_event($event);
/** /**
* Remove a single event from the database * Remove a single event from the database
* *
* @param array Hash array with event properties: * @param array $event Hash array with event properties:
* id: Event identifier * id: Event identifier
* @param boolean Remove event irreversible (mark as deleted otherwise, * @param bool $force Remove event irreversible (mark as deleted otherwise,
* if supported by the backend) * if supported by the backend)
* *
* @return boolean True on success, False on error * @return bool True on success, False on error
*/ */
abstract function remove_event($event, $force = true); abstract function remove_event($event, $force = true);
/** /**
* Restores a single deleted event (if supported) * Restores a single deleted event (if supported)
* *
* @param array Hash array with event properties: * @param array $event Hash array with event properties:
* id: Event identifier * id: Event identifier
* *
* @return boolean True on success, False on error * @return bool True on success, False on error
*/ */
public function restore_event($event) public function restore_event($event)
{ {
@ -275,14 +287,14 @@ abstract class calendar_driver
/** /**
* Return data of a single event * Return data of a single event
* *
* @param mixed UID string or hash array with event properties: * @param mixed $event UID string or hash array with event properties:
* id: Event identifier * id: Event identifier
* uid: Event UID * uid: Event UID
* _instance: Instance identifier in combination with uid (optional) * _instance: Instance identifier in combination with uid (optional)
* calendar: Calendar identifier (optional) * calendar: Calendar identifier (optional)
* @param integer Bitmask defining the scope to search events in. * @param int $scope Bitmask defining the scope to search events in.
* See FILTER_* constants for possible values. * See FILTER_* constants for possible values.
* @param boolean If true, recurrence exceptions shall be added * @param bool $full If true, recurrence exceptions shall be added
* *
* @return array Event object as hash array * @return array Event object as hash array
*/ */
@ -291,12 +303,13 @@ abstract class calendar_driver
/** /**
* Get events from source. * Get events from source.
* *
* @param integer Date range start (unix timestamp) * @param int $start Date range start (unix timestamp)
* @param integer Date range end (unix timestamp) * @param int $end Date range end (unix timestamp)
* @param string Search query (optional) * @param string $query Search query (optional)
* @param mixed List of calendar IDs to load events from (either as array or comma-separated string) * @param mixed $calendars List of calendar IDs to load events from (either as array or comma-separated string)
* @param boolean Include virtual/recurring events (optional) * @param bool $virtual Include virtual/recurring events (optional)
* @param integer Only list events modified since this time (unix timestamp) * @param int $modifiedsince Only list events modified since this time (unix timestamp)
*
* @return array A list of event objects (see header of this file for struct of an event) * @return array A list of event objects (see header of this file for struct of an event)
*/ */
abstract function load_events($start, $end, $query = null, $calendars = null, $virtual = 1, $modifiedsince = null); abstract function load_events($start, $end, $query = null, $calendars = null, $virtual = 1, $modifiedsince = null);
@ -304,9 +317,10 @@ abstract class calendar_driver
/** /**
* Get number of events in the given calendar * Get number of events in the given calendar
* *
* @param mixed List of calendar IDs to count events (either as array or comma-separated string) * @param mixed $calendars List of calendar IDs to count events (either as array or comma-separated string)
* @param integer Date range start (unix timestamp) * @param int $start Date range start (unix timestamp)
* @param integer Date range end (unix timestamp) * @param int $end Date range end (unix timestamp)
*
* @return array Hash array with counts grouped by calendar ID * @return array Hash array with counts grouped by calendar ID
*/ */
abstract function count_events($calendars, $start, $end = null); abstract function count_events($calendars, $start, $end = null);
@ -314,8 +328,9 @@ abstract class calendar_driver
/** /**
* Get a list of pending alarms to be displayed to the user * Get a list of pending alarms to be displayed to the user
* *
* @param integer Current time (unix timestamp) * @param int $time Current time (unix timestamp)
* @param mixed List of calendar IDs to show alarms for (either as array or comma-separated string) * @param mixed $calendars List of calendar IDs to show alarms for (either as array or comma-separated string)
*
* @return array A list of alarms, each encoded as hash array: * @return array A list of alarms, each encoded as hash array:
* id: Event identifier * id: Event identifier
* uid: Unique identifier of this event * uid: Unique identifier of this event
@ -331,30 +346,33 @@ abstract class calendar_driver
* (User) feedback after showing an alarm notification * (User) feedback after showing an alarm notification
* This should mark the alarm as 'shown' or snooze it for the given amount of time * This should mark the alarm as 'shown' or snooze it for the given amount of time
* *
* @param string Event identifier * @param string $event_id Event identifier
* @param integer Suspend the alarm for this number of seconds * @param int $snooze Suspend the alarm for this number of seconds
*/ */
abstract function dismiss_alarm($event_id, $snooze = 0); abstract function dismiss_alarm($event_id, $snooze = 0);
/** /**
* Check the given event object for validity * Check the given event object for validity
* *
* @param array Event object as hash array * @param array $event Event object as hash array
*
* @return boolean True if valid, false if not * @return boolean True if valid, false if not
*/ */
public function validate($event) public function validate($event)
{ {
$valid = true; $valid = true;
if (!is_object($event['start']) || !is_a($event['start'], 'DateTime')) if (empty($event['start']) || !is_object($event['start']) || !is_a($event['start'], 'DateTime')) {
$valid = false; $valid = false;
if (!is_object($event['end']) || !is_a($event['end'], 'DateTime')) }
if (empty($event['end']) || !is_object($event['end']) || !is_a($event['end'], 'DateTime')) {
$valid = false; $valid = false;
}
return $valid; return $valid;
} }
/** /**
* Get list of event's attachments. * Get list of event's attachments.
* Drivers can return list of attachments as event property. * Drivers can return list of attachments as event property.
@ -443,9 +461,9 @@ abstract class calendar_driver
/** /**
* Fetch free/busy information from a person within the given range * Fetch free/busy information from a person within the given range
* *
* @param string E-mail address of attendee * @param string $email E-mail address of attendee
* @param integer Requested period start date/time as unix timestamp * @param int $start Requested period start date/time as unix timestamp
* @param integer Requested period end date/time as unix timestamp * @param int $end Requested period end date/time as unix timestamp
* *
* @return array List of busy timeslots within the requested range * @return array List of busy timeslots within the requested range
*/ */
@ -457,16 +475,17 @@ abstract class calendar_driver
/** /**
* Create instances of a recurring event * Create instances of a recurring event
* *
* @param array Hash array with event properties * @param array $event Hash array with event properties
* @param object DateTime Start date of the recurrence window * @param DateTime $start Start date of the recurrence window
* @param object DateTime End date of the recurrence window * @param DateTime $end End date of the recurrence window
*
* @return array List of recurring event instances * @return array List of recurring event instances
*/ */
public function get_recurring_events($event, $start, $end = null) public function get_recurring_events($event, $start, $end = null)
{ {
$events = array(); $events = [];
if ($event['recurrence']) { if (!empty($event['recurrence'])) {
// include library class // include library class
require_once(dirname(__FILE__) . '/../lib/calendar_recurrence.php'); require_once(dirname(__FILE__) . '/../lib/calendar_recurrence.php');
@ -551,7 +570,7 @@ abstract class calendar_driver
/** /**
* Return full data of a specific revision of an event * Return full data of a specific revision of an event
* *
* @param mixed UID string or hash array with event properties: * @param mixed $event UID string or hash array with event properties:
* id: Event identifier * id: Event identifier
* calendar: Calendar identifier * calendar: Calendar identifier
* @param mixed $rev Revision number * @param mixed $rev Revision number
@ -568,7 +587,7 @@ abstract class calendar_driver
* Command the backend to restore a certain revision of an event. * Command the backend to restore a certain revision of an event.
* This shall replace the current event with an older version. * This shall replace the current event with an older version.
* *
* @param mixed UID string or hash array with event properties: * @param mixed $event UID string or hash array with event properties:
* id: Event identifier * id: Event identifier
* calendar: Calendar identifier * calendar: Calendar identifier
* @param mixed $rev Revision number * @param mixed $rev Revision number
@ -580,19 +599,18 @@ abstract class calendar_driver
return false; return false;
} }
/** /**
* Callback function to produce driver-specific calendar create/edit form * Callback function to produce driver-specific calendar create/edit form
* *
* @param string Request action 'form-edit|form-new' * @param string $action Request action 'form-edit|form-new'
* @param array Calendar properties (e.g. id, color) * @param array $calendar Calendar properties (e.g. id, color)
* @param array Edit form fields * @param array $formfields Edit form fields
* *
* @return string HTML content of the form * @return string HTML content of the form
*/ */
public function calendar_form($action, $calendar, $formfields) public function calendar_form($action, $calendar, $formfields)
{ {
$table = new html_table(array('cols' => 2, 'class' => 'propform')); $table = new html_table(['cols' => 2, 'class' => 'propform']);
foreach ($formfields as $col => $colprop) { foreach ($formfields as $col => $colprop) {
$label = !empty($colprop['label']) ? $colprop['label'] : $rcmail->gettext("$domain.$col"); $label = !empty($colprop['label']) ? $colprop['label'] : $rcmail->gettext("$domain.$col");
@ -610,17 +628,18 @@ abstract class calendar_driver
* This is a default implementation using Roundcube's address book API. * This is a default implementation using Roundcube's address book API.
* It can be overriden with a more optimized version by the individual drivers. * It can be overriden with a more optimized version by the individual drivers.
* *
* @param integer Event's new start (unix timestamp) * @param int $start Event's new start (unix timestamp)
* @param integer Event's new end (unix timestamp) * @param int $end Event's new end (unix timestamp)
* @param string Search query (optional) * @param string $search Search query (optional)
* @param integer Only list events modified since this time (unix timestamp) * @param int $modifiedsince Only list events modified since this time (unix timestamp)
*
* @return array A list of event records * @return array A list of event records
*/ */
public function load_birthday_events($start, $end, $search = null, $modifiedsince = null) public function load_birthday_events($start, $end, $search = null, $modifiedsince = null)
{ {
// ignore update requests for simplicity reasons // ignore update requests for simplicity reasons
if (!empty($modifiedsince)) { if (!empty($modifiedsince)) {
return array(); return [];
} }
// convert to DateTime for comparisons // convert to DateTime for comparisons
@ -630,7 +649,7 @@ abstract class calendar_driver
$year = $start->format('Y'); $year = $start->format('Y');
$year2 = $end->format('Y'); $year2 = $end->format('Y');
$events = array(); $events = [];
$search = mb_strtolower($search); $search = mb_strtolower($search);
$rcmail = rcmail::get_instance(); $rcmail = rcmail::get_instance();
$cache = $rcmail->get_cache('calendar.birthdays', 'db', 3600); $cache = $rcmail->get_cache('calendar.birthdays', 'db', 3600);
@ -643,6 +662,7 @@ abstract class calendar_driver
// let the user select the address books to consider in prefs // let the user select the address books to consider in prefs
$selected_sources = $rcmail->config->get('calendar_birthday_adressbooks'); $selected_sources = $rcmail->config->get('calendar_birthday_adressbooks');
$sources = $selected_sources ?: array_keys($rcmail->get_address_sources(false, true)); $sources = $selected_sources ?: array_keys($rcmail->get_address_sources(false, true));
foreach ($sources as $source) { foreach ($sources as $source) {
$abook = $rcmail->get_address_book($source); $abook = $rcmail->get_address_book($source);
@ -651,14 +671,19 @@ abstract class calendar_driver
continue; continue;
} }
// skip collected recipients/senders addressbooks
if (is_a($abook, 'rcube_addresses')) {
continue;
}
$abook->set_pagesize(10000); $abook->set_pagesize(10000);
// check for cached results // check for cached results
$cache_records = array(); $cache_records = [];
$cached = $cache->get($source); $cached = $cache->get($source);
// iterate over (cached) contacts // iterate over (cached) contacts
foreach (($cached ?: $abook->search('*', '', 2, true, true, array('birthday'))) as $contact) { foreach (($cached ?: $abook->search('*', '', 2, true, true, ['birthday'])) as $contact) {
$event = self::parse_contact($contact, $source); $event = self::parse_contact($contact, $source);
if (empty($event)) { if (empty($event)) {
@ -667,11 +692,11 @@ abstract class calendar_driver
// add stripped record to cache // add stripped record to cache
if (empty($cached)) { if (empty($cached)) {
$cache_records[] = array( $cache_records[] = [
'ID' => $contact['ID'], 'ID' => $contact['ID'],
'name' => $event['_displayname'], 'name' => $event['_displayname'],
'birthday' => $event['start']->format('Y-m-d'), 'birthday' => $event['start']->format('Y-m-d'),
); ];
} }
// filter by search term (only name is involved here) // filter by search term (only name is involved here)
@ -701,9 +726,12 @@ abstract class calendar_driver
// if this is not the first occurence modify event details // if this is not the first occurence modify event details
// but not when this is "all birthdays feed" request // but not when this is "all birthdays feed" request
if ($year2 - $year < 10 && ($age = ($this_year - $byear))) { if ($year2 - $year < 10 && ($age = ($this_year - $byear))) {
$event['description'] = $rcmail->gettext(array('name' => 'birthdayage', 'vars' => array('age' => $age)), 'calendar'); $label = ['name' => 'birthdayage', 'vars' => ['age' => $age]];
$event['description'] = $rcmail->gettext($label, 'calendar');
$event['start'] = $bday; $event['start'] = $bday;
$event['end'] = clone $bday; $event['end'] = clone $bday;
unset($event['recurrence']); unset($event['recurrence']);
} }
@ -727,7 +755,7 @@ abstract class calendar_driver
public function get_birthday_event($id) public function get_birthday_event($id)
{ {
// decode $id // decode $id
list(,$source,$contact_id,$year) = explode(':', rcube_ldap::dn_decode($id)); list(, $source, $contact_id, $year) = explode(':', rcube_ldap::dn_decode($id));
$rcmail = rcmail::get_instance(); $rcmail = rcmail::get_instance();
@ -744,7 +772,7 @@ abstract class calendar_driver
* @param array $contact Contact data * @param array $contact Contact data
* @param string $source Addressbook source ID * @param string $source Addressbook source ID
* *
* @return array Birthday event data * @return array|null Birthday event data
*/ */
public static function parse_contact($contact, $source) public static function parse_contact($contact, $source)
{ {
@ -752,7 +780,7 @@ abstract class calendar_driver
return; return;
} }
if (is_array($contact['birthday'])) { if (!empty($contact['birthday']) && is_array($contact['birthday'])) {
$contact['birthday'] = reset($contact['birthday']); $contact['birthday'] = reset($contact['birthday']);
} }
@ -768,22 +796,25 @@ abstract class calendar_driver
$bday->_dateonly = true; $bday->_dateonly = true;
} }
catch (Exception $e) { catch (Exception $e) {
rcube::raise_error(array( rcube::raise_error([
'code' => 600, 'type' => 'php', 'code' => 600,
'file' => __FILE__, 'line' => __LINE__, 'file' => __FILE__,
'message' => 'BIRTHDAY PARSE ERROR: ' . $e->getMessage()), 'line' => __LINE__,
true, false); 'message' => 'BIRTHDAY PARSE ERROR: ' . $e->getMessage()
],
true, false
);
return; return;
} }
$rcmail = rcmail::get_instance(); $rcmail = rcmail::get_instance();
$birthyear = $bday->format('Y'); $birthyear = $bday->format('Y');
$display_name = rcube_addressbook::compose_display_name($contact); $display_name = rcube_addressbook::compose_display_name($contact);
$label = array('name' => 'birthdayeventtitle', 'vars' => array('name' => $display_name)); $label = ['name' => 'birthdayeventtitle', 'vars' => ['name' => $display_name]];
$event_title = $rcmail->gettext($label, 'calendar'); $event_title = $rcmail->gettext($label, 'calendar');
$uid = rcube_ldap::dn_encode('bday:' . $source . ':' . $contact['ID'] . ':' . $birthyear); $uid = rcube_ldap::dn_encode('bday:' . $source . ':' . $contact['ID'] . ':' . $birthyear);
$event = array( return [
'id' => $uid, 'id' => $uid,
'uid' => $uid, 'uid' => $uid,
'calendar' => self::BIRTHDAY_CALENDAR_ID, 'calendar' => self::BIRTHDAY_CALENDAR_ID,
@ -792,19 +823,17 @@ abstract class calendar_driver
'allday' => true, 'allday' => true,
'start' => $bday, 'start' => $bday,
'end' => clone $bday, 'end' => clone $bday,
'recurrence' => array('FREQ' => 'YEARLY', 'INTERVAL' => 1), 'recurrence' => ['FREQ' => 'YEARLY', 'INTERVAL' => 1],
'free_busy' => 'free', 'free_busy' => 'free',
'_displayname' => $display_name, '_displayname' => $display_name,
); ];
return $event;
} }
/** /**
* Store alarm dismissal for birtual birthay events * Store alarm dismissal for birtual birthay events
* *
* @param string Event identifier * @param string $event_id Event identifier
* @param integer Suspend the alarm for this number of seconds * @param int $snooze Suspend the alarm for this number of seconds
*/ */
public function dismiss_birthday_alarm($event_id, $snooze = 0) public function dismiss_birthday_alarm($event_id, $snooze = 0)
{ {
@ -814,7 +843,7 @@ abstract class calendar_driver
// compute new notification time or disable if not snoozed // compute new notification time or disable if not snoozed
$notifyat = $snooze > 0 ? time() + $snooze : null; $notifyat = $snooze > 0 ? time() + $snooze : null;
$cache->set($event_id, array('snooze' => $snooze, 'notifyat' => $notifyat)); $cache->set($event_id, ['snooze' => $snooze, 'notifyat' => $notifyat]);
return true; return true;
} }
@ -822,7 +851,8 @@ abstract class calendar_driver
/** /**
* Handler for user_delete plugin hook * Handler for user_delete plugin hook
* *
* @param array Hash array with hook arguments * @param array $args Hash array with hook arguments
*
* @return array Return arguments for plugin hooks * @return array Return arguments for plugin hooks
*/ */
public function user_delete($args) public function user_delete($args)

View file

@ -61,3 +61,4 @@ CREATE TABLE events (
INSERT INTO events (event_id, calendar_id, recurrence_id, uid, created, changed, sequence, start, end, recurrence, title, description, location, categories, all_day, free_busy, priority, sensitivity, alarms, attendees, notifyat) INSERT INTO events (event_id, calendar_id, recurrence_id, uid, created, changed, sequence, start, end, recurrence, title, description, location, categories, all_day, free_busy, priority, sensitivity, alarms, attendees, notifyat)
SELECT event_id, calendar_id, recurrence_id, uid, created, changed, sequence, start, end, recurrence, title, description, location, categories, all_day, free_busy, priority, sensitivity, alarms, attendees, notifyat FROM temp_events; SELECT event_id, calendar_id, recurrence_id, uid, created, changed, sequence, start, end, recurrence, title, description, location, categories, all_day, free_busy, priority, sensitivity, alarms, attendees, notifyat FROM temp_events;
DROP TABLE temp_events;

View file

@ -65,3 +65,4 @@ INSERT INTO events (event_id, calendar_id, recurrence_id, uid, created, changed,
SELECT event_id, calendar_id, recurrence_id, uid, created, changed, sequence, start, end, recurrence, title, description, location, categories, url, all_day, free_busy, priority, sensitivity, alarms, attendees, notifyat SELECT event_id, calendar_id, recurrence_id, uid, created, changed, sequence, start, end, recurrence, title, description, location, categories, url, all_day, free_busy, priority, sensitivity, alarms, attendees, notifyat
FROM temp_events; FROM temp_events;
DROP TABLE temp_events;

View file

@ -136,7 +136,7 @@ class database_driver extends calendar_driver
$hidden = array_filter(explode(',', $this->rc->config->get('hidden_calendars', ''))); $hidden = array_filter(explode(',', $this->rc->config->get('hidden_calendars', '')));
$id = self::BIRTHDAY_CALENDAR_ID; $id = self::BIRTHDAY_CALENDAR_ID;
if (!$active || !in_array($id, $hidden)) { if (empty($active) || !in_array($id, $hidden)) {
$calendars[$id] = array( $calendars[$id] = array(
'id' => $id, 'id' => $id,
'name' => $this->cal->gettext('birthdays'), 'name' => $this->cal->gettext('birthdays'),
@ -172,7 +172,7 @@ class database_driver extends calendar_driver
$this->rc->user->ID, $this->rc->user->ID,
$prop['name'], $prop['name'],
strval($prop['color']), strval($prop['color']),
$prop['showalarms'] ? 1 : 0 !empty($prop['showalarms']) ? 1 : 0
); );
if ($result) { if ($result) {
@ -321,24 +321,24 @@ class database_driver extends calendar_driver
. " VALUES (?, $now, $now, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", . " VALUES (?, $now, $now, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
$event['calendar'], $event['calendar'],
strval($event['uid']), strval($event['uid']),
intval($event['recurrence_id']), isset($event['recurrence_id']) ? intval($event['recurrence_id']) : 0,
strval($event['_instance']), isset($event['_instance']) ? strval($event['_instance']) : '',
intval($event['isexception']), isset($event['isexception']) ? intval($event['isexception']) : 0,
$event['start']->format(self::DB_DATE_FORMAT), $event['start']->format(self::DB_DATE_FORMAT),
$event['end']->format(self::DB_DATE_FORMAT), $event['end']->format(self::DB_DATE_FORMAT),
intval($event['all_day']), intval($event['all_day']),
$event['_recurrence'], $event['_recurrence'],
strval($event['title']), strval($event['title']),
strval($event['description']), isset($event['description']) ? strval($event['description']) : '',
strval($event['location']), isset($event['location']) ? strval($event['location']) : '',
join(',', (array)$event['categories']), isset($event['categories']) ? join(',', (array) $event['categories']) : '',
strval($event['url']), isset($event['url']) ? strval($event['url']) : '',
intval($event['free_busy']), intval($event['free_busy']),
intval($event['priority']), intval($event['priority']),
intval($event['sensitivity']), intval($event['sensitivity']),
strval($event['status']), isset($event['status']) ? strval($event['status']) : '',
$event['attendees'], $event['attendees'],
$event['alarms'], isset($event['alarms']) ? $event['alarms'] : null,
$event['notifyat'] $event['notifyat']
); );
@ -381,7 +381,7 @@ class database_driver extends calendar_driver
// increment sequence number // increment sequence number
if (empty($event['sequence']) && $reschedule) { if (empty($event['sequence']) && $reschedule) {
$event['sequence'] = max($event['sequence'], $old['sequence']) + 1; $event['sequence'] = $old['sequence'] + 1;
} }
// modify a recurring event, check submitted savemode to do the right things // modify a recurring event, check submitted savemode to do the right things
@ -389,11 +389,12 @@ class database_driver extends calendar_driver
$master = $old['recurrence_id'] ? $this->get_event(array('id' => $old['recurrence_id'])) : $old; $master = $old['recurrence_id'] ? $this->get_event(array('id' => $old['recurrence_id'])) : $old;
// keep saved exceptions (not submitted by the client) // keep saved exceptions (not submitted by the client)
if ($old['recurrence']['EXDATE']) { if (!empty($old['recurrence']['EXDATE'])) {
$event['recurrence']['EXDATE'] = $old['recurrence']['EXDATE']; $event['recurrence']['EXDATE'] = $old['recurrence']['EXDATE'];
} }
switch ($event['_savemode']) { $savemode = isset($event['_savemode']) ? $event['_savemode'] : null;
switch ($savemode) {
case 'new': case 'new':
$event['uid'] = $this->cal->generate_uid(); $event['uid'] = $this->cal->generate_uid();
return $this->new_event($event); return $this->new_event($event);
@ -582,10 +583,12 @@ class database_driver extends calendar_driver
// iterate through the list of properties considered 'significant' for scheduling // iterate through the list of properties considered 'significant' for scheduling
foreach (self::$scheduling_properties as $prop) { foreach (self::$scheduling_properties as $prop) {
$a = $old[$prop]; $a = isset($old[$prop]) ? $old[$prop] : null;
$b = $event[$prop]; $b = isset($event[$prop]) ? $event[$prop] : null;
if ($event['allday'] && ($prop == 'start' || $prop == 'end') && $a instanceof DateTime && $b instanceof DateTime) { if (!empty($event['allday']) && ($prop == 'start' || $prop == 'end')
&& $a instanceof DateTime && $b instanceof DateTime
) {
$a = $a->format('Y-m-d'); $a = $a->format('Y-m-d');
$b = $b->format('Y-m-d'); $b = $b->format('Y-m-d');
} }
@ -596,10 +599,10 @@ class database_driver extends calendar_driver
$b = array_filter($b); $b = array_filter($b);
// advanced rrule comparison: no rescheduling if series was shortened // advanced rrule comparison: no rescheduling if series was shortened
if ($a['COUNT'] && $b['COUNT'] && $b['COUNT'] < $a['COUNT']) { if (!empty($a['COUNT']) && !empty($b['COUNT']) && $b['COUNT'] < $a['COUNT']) {
unset($a['COUNT'], $b['COUNT']); unset($a['COUNT'], $b['COUNT']);
} }
else if ($a['UNTIL'] && $b['UNTIL'] && $b['UNTIL'] < $a['UNTIL']) { else if (!empty($a['UNTIL']) && !empty($b['UNTIL']) && $b['UNTIL'] < $a['UNTIL']) {
unset($a['UNTIL'], $b['UNTIL']); unset($a['UNTIL'], $b['UNTIL']);
} }
} }
@ -652,24 +655,24 @@ class database_driver extends calendar_driver
} }
// compose vcalendar-style recurrencue rule from structured data // compose vcalendar-style recurrencue rule from structured data
$rrule = $event['recurrence'] ? libcalendaring::to_rrule($event['recurrence']) : ''; $rrule = !empty($event['recurrence']) ? libcalendaring::to_rrule($event['recurrence']) : '';
$sensitivity = strtolower($event['sensitivity']);
$free_busy = strtolower($event['free_busy']);
$event['_recurrence'] = rtrim($rrule, ';'); $event['_recurrence'] = rtrim($rrule, ';');
$event['free_busy'] = intval($this->free_busy_map[strtolower($event['free_busy'])]); $event['free_busy'] = isset($this->free_busy_map[$free_busy]) ? $this->free_busy_map[$free_busy] : null;
$event['sensitivity'] = intval($this->sensitivity_map[strtolower($event['sensitivity'])]); $event['sensitivity'] = isset($this->sensitivity_map[$sensitivity]) ? $this->sensitivity_map[$sensitivity] : null;
$event['all_day'] = !empty($event['allday']) ? 1 : 0;
if ($event['free_busy'] == 'tentative') { if ($event['free_busy'] == 'tentative') {
$event['status'] = 'TENTATIVE'; $event['status'] = 'TENTATIVE';
} }
if (isset($event['allday'])) {
$event['all_day'] = $event['allday'] ? 1 : 0;
}
// compute absolute time to notify the user // compute absolute time to notify the user
$event['notifyat'] = $this->_get_notification($event); $event['notifyat'] = $this->_get_notification($event);
if (is_array($event['valarms'])) { if (!empty($event['valarms'])) {
$event['alarms'] = $this->serialize_alarms($event['valarms']); $event['alarms'] = $this->serialize_alarms($event['valarms']);
} }
@ -689,7 +692,7 @@ class database_driver extends calendar_driver
*/ */
private function _get_notification($event) private function _get_notification($event)
{ {
if ($event['valarms'] && $event['start'] > new DateTime()) { if (!empty($event['valarms']) && $event['start'] > new DateTime()) {
$alarm = libcalendaring::get_next_alarm($event); $alarm = libcalendaring::get_next_alarm($event);
if ($alarm['time'] && in_array($alarm['action'], $this->alarm_types)) { if ($alarm['time'] && in_array($alarm['action'], $this->alarm_types)) {
@ -714,26 +717,23 @@ class database_driver extends calendar_driver
); );
foreach ($set_cols as $col) { foreach ($set_cols as $col) {
if (is_object($event[$col]) && is_a($event[$col], 'DateTime')) { if (!empty($event[$col]) && is_a($event[$col], 'DateTime')) {
$sql_args[$col] = $event[$col]->format(self::DB_DATE_FORMAT); $sql_args[$col] = $event[$col]->format(self::DB_DATE_FORMAT);
} }
else if (is_array($event[$col])) {
$sql_args[$col] = join(',', $event[$col]);
}
else if (array_key_exists($col, $event)) { else if (array_key_exists($col, $event)) {
$sql_args[$col] = $event[$col]; $sql_args[$col] = is_array($event[$col]) ? join(',', $event[$col]) : $event[$col];
} }
} }
if ($event['_recurrence']) { if (!empty($event['_recurrence'])) {
$sql_args['recurrence'] = $event['_recurrence']; $sql_args['recurrence'] = $event['_recurrence'];
} }
if ($event['_instance']) { if (!empty($event['_instance'])) {
$sql_args['instance'] = $event['_instance']; $sql_args['instance'] = $event['_instance'];
} }
if ($event['_fromcalendar'] && $event['_fromcalendar'] != $event['calendar']) { if (!empty($event['_fromcalendar']) && $event['_fromcalendar'] != $event['calendar']) {
$sql_args['calendar_id'] = $event['calendar']; $sql_args['calendar_id'] = $event['calendar'];
} }
@ -763,7 +763,7 @@ class database_driver extends calendar_driver
} }
// remove attachments // remove attachments
if ($success && !empty($event['deleted_attachments'])) { if ($success && !empty($event['deleted_attachments']) && is_array($event['deleted_attachments'])) {
foreach ($event['deleted_attachments'] as $attachment) { foreach ($event['deleted_attachments'] as $attachment) {
$this->remove_attachment($attachment, $event['id']); $this->remove_attachment($attachment, $event['id']);
} }
@ -822,7 +822,7 @@ class database_driver extends calendar_driver
// skip exceptions // skip exceptions
// TODO: merge updated data from master event // TODO: merge updated data from master event
if ($exdata[$datestr]) { if (!empty($exdata[$datestr])) {
continue; continue;
} }
@ -831,7 +831,7 @@ class database_driver extends calendar_driver
$next_end->add($duration); $next_end->add($duration);
$notify_at = $this->_get_notification(array( $notify_at = $this->_get_notification(array(
'alarms' => $event['alarms'], 'alarms' => !empty($event['alarms']) ? $event['alarms'] : null,
'start' => $next_start, 'start' => $next_start,
'end' => $next_end, 'end' => $next_end,
'status' => $event['status'] 'status' => $event['status']
@ -860,13 +860,13 @@ class database_driver extends calendar_driver
} }
// stop adding events for inifinite recurrence after 20 years // stop adding events for inifinite recurrence after 20 years
if (++$count > 999 || (!$recurrence->recurEnd && !$recurrence->recurCount && $next_start->format('Y') > date('Y') + 20)) { if (++$count > 999 || (empty($recurrence->recurEnd) && empty($recurrence->recurCount) && $next_start->format('Y') > date('Y') + 20)) {
break; break;
} }
} }
// remove all exceptions after recurrence end // remove all exceptions after recurrence end
if ($next_end && !empty($exceptions)) { if (!empty($next_end) && !empty($exceptions)) {
$this->rc->db->query( $this->rc->db->query(
"DELETE FROM `{$this->db_events}`" "DELETE FROM `{$this->db_events}`"
. " WHERE `recurrence_id` = ? AND `isexception` = 1 AND `start` > ?" . " WHERE `recurrence_id` = ? AND `isexception` = 1 AND `start` > ?"
@ -1025,11 +1025,11 @@ class database_driver extends calendar_driver
*/ */
public function get_event($event, $scope = 0, $full = false) public function get_event($event, $scope = 0, $full = false)
{ {
$id = is_array($event) ? ($event['id'] ?: $event['uid']) : $event; $id = is_array($event) ? (!empty($event['id']) ? $event['id'] : $event['uid']) : $event;
$cal = is_array($event) ? $event['calendar'] : null; $cal = is_array($event) && !empty($event['calendar']) ? $event['calendar'] : null;
$col = is_array($event) && is_numeric($id) ? 'event_id' : 'uid'; $col = is_array($event) && is_numeric($id) ? 'event_id' : 'uid';
if ($this->cache[$id]) { if (!empty($this->cache[$id])) {
return $this->cache[$id]; return $this->cache[$id];
} }
@ -1039,15 +1039,15 @@ class database_driver extends calendar_driver
} }
$where_add = ''; $where_add = '';
if (is_array($event) && !$event['id'] && !empty($event['_instance'])) { if (is_array($event) && empty($event['id']) && !empty($event['_instance'])) {
$where_add = " AND e.instance = " . $this->rc->db->quote($event['_instance']); $where_add = " AND e.instance = " . $this->rc->db->quote($event['_instance']);
} }
if ($scope & self::FILTER_ACTIVE) { if ($scope & self::FILTER_ACTIVE) {
$calendars = $this->calendars; $calendars = [];
foreach ($calendars as $idx => $cal) { foreach ($this->calendars as $idx => $cal) {
if (!$cal['active']) { if (!empty($cal['active'])) {
unset($calendars[$idx]); $calendars[] = $idx;
} }
} }
$cals = join(',', $calendars); $cals = join(',', $calendars);
@ -1099,11 +1099,12 @@ class database_driver extends calendar_driver
// compose (slow) SQL query for searching // compose (slow) SQL query for searching
// FIXME: improve searching using a dedicated col and normalized values // FIXME: improve searching using a dedicated col and normalized values
$sql_add = '';
if ($query) { if ($query) {
foreach (array('title','location','description','categories','attendees') as $col) { foreach (array('title','location','description','categories','attendees') as $col) {
$sql_query[] = $this->rc->db->ilike($col, '%'.$query.'%'); $sql_query[] = $this->rc->db->ilike($col, '%'.$query.'%');
} }
$sql_add = " AND (" . join(' OR ', $sql_query) . ")"; $sql_add .= " AND (" . join(' OR ', $sql_query) . ")";
} }
if (!$virtual) { if (!$virtual) {
@ -1155,7 +1156,7 @@ class database_driver extends calendar_driver
// add events from the address books birthday calendar // add events from the address books birthday calendar
if (in_array(self::BIRTHDAY_CALENDAR_ID, $calendars) && empty($query)) { if (in_array(self::BIRTHDAY_CALENDAR_ID, $calendars) && empty($query)) {
$events = array_merge($events, $this->load_birthday_events($start, $end, $search, $modifiedsince)); $events = array_merge($events, $this->load_birthday_events($start, $end, null, $modifiedsince));
} }
return $events; return $events;
@ -1229,7 +1230,7 @@ class database_driver extends calendar_driver
} }
} }
if ($event['_attachments'] > 0) { if (!empty($event['_attachments'])) {
$event['attachments'] = (array)$this->list_attachments($event); $event['attachments'] = (array)$this->list_attachments($event);
} }
@ -1398,7 +1399,7 @@ class database_driver extends calendar_driver
. "SELECT `event_id` FROM `{$this->db_events}`" . "SELECT `event_id` FROM `{$this->db_events}`"
. " WHERE `event_id` = ? AND `calendar_id` IN ({$this->calendar_ids}))", . " WHERE `event_id` = ? AND `calendar_id` IN ({$this->calendar_ids}))",
$id, $id,
$event['recurrence_id'] ? $event['recurrence_id'] : $event['id'] !empty($event['recurrence_id']) ? $event['recurrence_id'] : $event['id']
); );
if ($result && ($arr = $this->rc->db->fetch_assoc($result))) { if ($result && ($arr = $this->rc->db->fetch_assoc($result))) {

View file

@ -33,34 +33,39 @@ class kolab_calendar extends kolab_storage_folder_api
public $alarms = false; public $alarms = false;
public $history = false; public $history = false;
public $subscriptions = true; public $subscriptions = true;
public $categories = array(); public $categories = [];
public $storage; public $storage;
public $type = 'event'; public $type = 'event';
protected $cal; protected $cal;
protected $events = array(); protected $events = [];
protected $search_fields = array('title', 'description', 'location', 'attendees', 'categories'); protected $search_fields = ['title', 'description', 'location', 'attendees', 'categories'];
/** /**
* Factory method to instantiate a kolab_calendar object * Factory method to instantiate a kolab_calendar object
* *
* @param string Calendar ID (encoded IMAP folder name) * @param string Calendar ID (encoded IMAP folder name)
* @param object calendar plugin object * @param object Calendar plugin object
* @return object kolab_calendar instance *
* @return kolab_calendar Self instance
*/ */
public static function factory($id, $calendar) public static function factory($id, $calendar)
{ {
$imap = $calendar->rc->get_storage(); $imap = $calendar->rc->get_storage();
$imap_folder = kolab_storage::id_decode($id); $imap_folder = kolab_storage::id_decode($id);
$info = $imap->folder_info($imap_folder, true); $info = $imap->folder_info($imap_folder, true);
if (empty($info) || $info['noselect'] || strpos(kolab_storage::folder_type($imap_folder), 'event') !== 0) {
if (
empty($info)
|| !empty($info['noselect'])
|| strpos(kolab_storage::folder_type($imap_folder), 'event') !== 0
) {
return new kolab_user_calendar($imap_folder, $calendar); return new kolab_user_calendar($imap_folder, $calendar);
} }
else {
return new kolab_calendar($imap_folder, $calendar); return new kolab_calendar($imap_folder, $calendar);
} }
}
/** /**
* Default constructor * Default constructor
@ -90,24 +95,26 @@ class kolab_calendar extends kolab_storage_folder_api
$rights = $this->storage->get_myrights(); $rights = $this->storage->get_myrights();
if ($rights && !PEAR::isError($rights)) { if ($rights && !PEAR::isError($rights)) {
$this->rights = $rights; $this->rights = $rights;
if (strpos($rights, 't') !== false || strpos($rights, 'd') !== false) if (strpos($rights, 't') !== false || strpos($rights, 'd') !== false) {
$this->editable = strpos($rights, 'i');; $this->editable = strpos($rights, 'i');;
} }
} }
}
// user-specific alarms settings win // user-specific alarms settings win
$prefs = $this->cal->rc->config->get('kolab_calendars', array()); $prefs = $this->cal->rc->config->get('kolab_calendars', []);
if (isset($prefs[$this->id]['showalarms'])) if (isset($prefs[$this->id]['showalarms'])) {
$this->alarms = $prefs[$this->id]['showalarms']; $this->alarms = $prefs[$this->id]['showalarms'];
else if (isset($prefs[$old_id]['showalarms'])) }
else if (isset($prefs[$old_id]['showalarms'])) {
$this->alarms = $prefs[$old_id]['showalarms']; $this->alarms = $prefs[$old_id]['showalarms'];
} }
}
$this->default = $this->storage->default; $this->default = $this->storage->default;
$this->subtype = $this->storage->subtype; $this->subtype = $this->storage->subtype;
} }
/** /**
* Getter for the IMAP folder name * Getter for the IMAP folder name
* *
@ -126,7 +133,6 @@ class kolab_calendar extends kolab_storage_folder_api
return null; return null;
} }
/** /**
* Return color to display this calendar * Return color to display this calendar
*/ */
@ -138,10 +144,11 @@ class kolab_calendar extends kolab_storage_folder_api
} }
// calendar color is stored in user prefs (temporary solution) // calendar color is stored in user prefs (temporary solution)
$prefs = $this->cal->rc->config->get('kolab_calendars', array()); $prefs = $this->cal->rc->config->get('kolab_calendars', []);
if (!empty($prefs[$this->id]) && !empty($prefs[$this->id]['color'])) if (!empty($prefs[$this->id]) && !empty($prefs[$this->id]['color'])) {
return $prefs[$this->id]['color']; return $prefs[$this->id]['color'];
}
return $default ?: 'cc0000'; return $default ?: 'cc0000';
} }
@ -152,18 +159,17 @@ class kolab_calendar extends kolab_storage_folder_api
public function get_caldav_url() public function get_caldav_url()
{ {
if ($template = $this->cal->rc->config->get('calendar_caldav_url', null)) { if ($template = $this->cal->rc->config->get('calendar_caldav_url', null)) {
return strtr($template, array( return strtr($template, [
'%h' => $_SERVER['HTTP_HOST'], '%h' => $_SERVER['HTTP_HOST'],
'%u' => urlencode($this->cal->rc->get_user_name()), '%u' => urlencode($this->cal->rc->get_user_name()),
'%i' => urlencode($this->storage->get_uid()), '%i' => urlencode($this->storage->get_uid()),
'%n' => urlencode($this->name), '%n' => urlencode($this->name),
)); ]);
} }
return false; return false;
} }
/** /**
* Update properties of this calendar folder * Update properties of this calendar folder
* *
@ -192,12 +198,12 @@ class kolab_calendar extends kolab_storage_folder_api
$master_id = preg_replace('/-\d{8}(T\d{6})?$/', '', $id); $master_id = preg_replace('/-\d{8}(T\d{6})?$/', '', $id);
// directly access storage object // directly access storage object
if (!$this->events[$id] && $master_id == $id && ($record = $this->storage->get_object($id))) { if (empty($this->events[$id]) && $master_id == $id && ($record = $this->storage->get_object($id))) {
$this->events[$id] = $this->_to_driver_event($record, true); $this->events[$id] = $this->_to_driver_event($record, true);
} }
// maybe a recurring instance is requested // maybe a recurring instance is requested
if (!$this->events[$id] && $master_id != $id) { if (empty($this->events[$id]) && $master_id != $id) {
$instance_id = substr($id, strlen($master_id) + 1); $instance_id = substr($id, strlen($master_id) + 1);
if ($record = $this->storage->get_object($master_id)) { if ($record = $this->storage->get_object($master_id)) {
@ -206,20 +212,21 @@ class kolab_calendar extends kolab_storage_folder_api
if ($master) { if ($master) {
// check for match in top-level exceptions (aka loose single occurrences) // check for match in top-level exceptions (aka loose single occurrences)
if ($master['_formatobj'] && ($instance = $master['_formatobj']->get_instance($instance_id))) { if (!empty($master['_formatobj']) && ($instance = $master['_formatobj']->get_instance($instance_id))) {
$this->events[$id] = $this->_to_driver_event($instance, false, true, $master); $this->events[$id] = $this->_to_driver_event($instance, false, true, $master);
} }
// check for match on the first instance already // check for match on the first instance already
else if ($master['_instance'] && $master['_instance'] == $instance_id) { else if (!empty($master['_instance']) && $master['_instance'] == $instance_id) {
$this->events[$id] = $master; $this->events[$id] = $master;
} }
else if (is_array($master['recurrence'])) { else if (!empty($master['recurrence'])) {
$start_date = $master['start'];
// For performance reasons we'll get only the specific instance // For performance reasons we'll get only the specific instance
if (($date = substr($id, strlen($master_id) + 1, 8)) && strlen($date) == 8 && is_numeric($date)) { if (($date = substr($id, strlen($master_id) + 1, 8)) && strlen($date) == 8 && is_numeric($date)) {
$start_date = new DateTime($date . 'T000000', $master['start']->getTimezone()); $start_date = new DateTime($date . 'T000000', $master['start']->getTimezone());
} }
$this->get_recurring_events($record, $start_date ?: $master['start'], null, $id, 1); $this->get_recurring_events($record, $start_date, null, $id, 1);
} }
} }
} }
@ -233,8 +240,9 @@ class kolab_calendar extends kolab_storage_folder_api
*/ */
public function get_attachment_body($id, $event) public function get_attachment_body($id, $event)
{ {
if (!$this->ready) if (!$this->ready) {
return false; return false;
}
$data = $this->storage->get_attachment($event['id'], $id); $data = $this->storage->get_attachment($event['id'], $id);
@ -250,15 +258,16 @@ class kolab_calendar extends kolab_storage_folder_api
} }
/** /**
* @param integer Event's new start (unix timestamp) * @param int Event's new start (unix timestamp)
* @param integer Event's new end (unix timestamp) * @param int Event's new end (unix timestamp)
* @param string Search query (optional) * @param string Search query (optional)
* @param boolean Include virtual events (optional) * @param bool Include virtual events (optional)
* @param array Additional parameters to query storage * @param array Additional parameters to query storage
* @param array Additional query to filter events * @param array Additional query to filter events
*
* @return array A list of event records * @return array A list of event records
*/ */
public function list_events($start, $end, $search = null, $virtual = 1, $query = array(), $filter_query = null) public function list_events($start, $end, $search = null, $virtual = 1, $query = [], $filter_query = null)
{ {
// convert to DateTime for comparisons // convert to DateTime for comparisons
// #5190: make the range a little bit wider // #5190: make the range a little bit wider
@ -280,40 +289,38 @@ class kolab_calendar extends kolab_storage_folder_api
$user_emails = $this->cal->get_user_emails(); $user_emails = $this->cal->get_user_emails();
// query Kolab storage // query Kolab storage
$query[] = array('dtstart', '<=', $end); $query[] = ['dtstart', '<=', $end];
$query[] = array('dtend', '>=', $start); $query[] = ['dtend', '>=', $start];
if (is_array($filter_query)) { if (is_array($filter_query)) {
$query = array_merge($query, $filter_query); $query = array_merge($query, $filter_query);
} }
$words = [];
$partstat_exclude = [];
$events = [];
if (!empty($search)) { if (!empty($search)) {
$search = mb_strtolower($search); $search = mb_strtolower($search);
$words = rcube_utils::tokenize_string($search, 1); $words = rcube_utils::tokenize_string($search, 1);
foreach (rcube_utils::normalize_string($search, true) as $word) { foreach (rcube_utils::normalize_string($search, true) as $word) {
$query[] = array('words', 'LIKE', $word); $query[] = ['words', 'LIKE', $word];
} }
} }
else {
$words = array();
}
// set partstat filter to skip pending and declined invitations // set partstat filter to skip pending and declined invitations
if (empty($filter_query) && $this->cal->rc->config->get('kolab_invitation_calendars') if (empty($filter_query)
&& $this->cal->rc->config->get('kolab_invitation_calendars')
&& $this->get_namespace() != 'other' && $this->get_namespace() != 'other'
) { ) {
$partstat_exclude = array('NEEDS-ACTION','DECLINED'); $partstat_exclude = ['NEEDS-ACTION', 'DECLINED'];
}
else {
$partstat_exclude = array();
} }
$events = array();
foreach ($this->storage->select($query) as $record) { foreach ($this->storage->select($query) as $record) {
$event = $this->_to_driver_event($record, !$virtual, false); $event = $this->_to_driver_event($record, !$virtual, false);
// remember seen categories // remember seen categories
if ($event['categories']) { if (!empty($event['categories'])) {
$cat = is_array($event['categories']) ? $event['categories'][0] : $event['categories']; $cat = is_array($event['categories']) ? $event['categories'][0] : $event['categories'];
$this->categories[$cat]++; $this->categories[$cat]++;
} }
@ -322,6 +329,7 @@ class kolab_calendar extends kolab_storage_folder_api
if ($event['start'] <= $end && $event['end'] >= $start) { if ($event['start'] <= $end && $event['end'] >= $start) {
unset($event['_attendees']); unset($event['_attendees']);
$add = true; $add = true;
// skip the first instance of a recurring event if listed in exdate // skip the first instance of a recurring event if listed in exdate
if ($virtual && !empty($event['recurrence']['EXDATE'])) { if ($virtual && !empty($event['recurrence']['EXDATE'])) {
$event_date = $event['start']->format('Ymd'); $event_date = $event['start']->format('Ymd');
@ -339,28 +347,33 @@ class kolab_calendar extends kolab_storage_folder_api
} }
// find and merge exception for the first instance // find and merge exception for the first instance
if ($virtual && !empty($event['recurrence']) && is_array($event['recurrence']['EXCEPTIONS'])) { if ($virtual && !empty($event['recurrence']) && !empty($event['recurrence']['EXCEPTIONS'])) {
foreach ($event['recurrence']['EXCEPTIONS'] as $exception) { foreach ($event['recurrence']['EXCEPTIONS'] as $exception) {
if ($event['_instance'] == $exception['_instance']) { if ($event['_instance'] == $exception['_instance']) {
unset($exception['calendar'], $exception['className'], $exception['_folder_id']); unset($exception['calendar'], $exception['className'], $exception['_folder_id']);
// clone date objects from main event before adjusting them with exception data // clone date objects from main event before adjusting them with exception data
if (is_object($event['start'])) $event['start'] = clone $record['start']; if (is_object($event['start'])) {
if (is_object($event['end'])) $event['end'] = clone $record['end']; $event['start'] = clone $record['start'];
}
if (is_object($event['end'])) {
$event['end'] = clone $record['end'];
}
kolab_driver::merge_exception_data($event, $exception); kolab_driver::merge_exception_data($event, $exception);
} }
} }
} }
if ($add) if ($add) {
$events[] = $event; $events[] = $event;
} }
}
// resolve recurring events // resolve recurring events
if ($record['recurrence'] && $virtual == 1) { if (!empty($record['recurrence']) && $virtual == 1) {
$events = array_merge($events, $this->get_recurring_events($record, $start, $end)); $events = array_merge($events, $this->get_recurring_events($record, $start, $end));
} }
// add top-level exceptions (aka loose single occurrences) // add top-level exceptions (aka loose single occurrences)
else if (is_array($record['exceptions'])) { else if (!empty($record['exceptions'])) {
foreach ($record['exceptions'] as $ex) { foreach ($record['exceptions'] as $ex) {
$component = $this->_to_driver_event($ex, false, false, $record); $component = $this->_to_driver_event($ex, false, false, $record);
if ($component['start'] <= $end && $component['end'] >= $start) { if ($component['start'] <= $end && $component['end'] >= $start) {
@ -385,9 +398,12 @@ class kolab_calendar extends kolab_storage_folder_api
} }
// partstat filter // partstat filter
if (count($partstat_exclude) && is_array($event['attendees'])) { if (count($partstat_exclude) && !empty($event['attendees'])) {
foreach ($event['attendees'] as $attendee) { foreach ($event['attendees'] as $attendee) {
if (in_array($attendee['email'], $user_emails) && in_array($attendee['status'], $partstat_exclude)) { if (
in_array($attendee['email'], $user_emails)
&& in_array($attendee['status'], $partstat_exclude)
) {
return false; return false;
} }
} }
@ -409,11 +425,11 @@ class kolab_calendar extends kolab_storage_folder_api
/** /**
* Get number of events in the given calendar * Get number of events in the given calendar
* *
* @param integer Date range start (unix timestamp) * @param int Date range start (unix timestamp)
* @param integer Date range end (unix timestamp) * @param int Date range end (unix timestamp)
* @param array Additional query to filter events * @param array Additional query to filter events
* *
* @return integer Count * @return int Count
*/ */
public function count_events($start, $end = null, $filter_query = null) public function count_events($start, $end = null, $filter_query = null)
{ {
@ -434,16 +450,17 @@ class kolab_calendar extends kolab_storage_folder_api
} }
// query Kolab storage // query Kolab storage
$query[] = array('dtend', '>=', $start); $query[] = ['dtend', '>=', $start];
if ($end) if ($end) {
$query[] = array('dtstart', '<=', $end); $query[] = ['dtstart', '<=', $end];
}
// add query to exclude pending/declined invitations // add query to exclude pending/declined invitations
if (empty($filter_query)) { if (empty($filter_query)) {
foreach ($this->cal->get_user_emails() as $email) { foreach ($this->cal->get_user_emails() as $email) {
$query[] = array('tags', '!=', 'x-partstat:' . $email . ':needs-action'); $query[] = ['tags', '!=', 'x-partstat:' . $email . ':needs-action'];
$query[] = array('tags', '!=', 'x-partstat:' . $email . ':declined'); $query[] = ['tags', '!=', 'x-partstat:' . $email . ':declined'];
} }
} }
else if (is_array($filter_query)) { else if (is_array($filter_query)) {
@ -459,15 +476,16 @@ class kolab_calendar extends kolab_storage_folder_api
* *
* @see calendar_driver::new_event() * @see calendar_driver::new_event()
* *
* @return mixed The created record ID on success, False on error * @return array|false The created record ID on success, False on error
*/ */
public function insert_event($event) public function insert_event($event)
{ {
if (!is_array($event)) if (!is_array($event)) {
return false; return false;
}
// email links are stored separately // email links are stored separately
$links = $event['links']; $links = !empty($event['links']) ? $event['links'] : [];
unset($event['links']); unset($event['links']);
//generate new event from RC input //generate new event from RC input
@ -475,11 +493,12 @@ class kolab_calendar extends kolab_storage_folder_api
$saved = $this->storage->save($object, 'event'); $saved = $this->storage->save($object, 'event');
if (!$saved) { if (!$saved) {
rcube::raise_error(array( rcube::raise_error([
'code' => 600, 'type' => 'php', 'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
'file' => __FILE__, 'line' => __LINE__, 'message' => "Error saving event object to Kolab server"
'message' => "Error saving event object to Kolab server"), ],
true, false); true, false
);
$saved = false; $saved = false;
} }
else { else {
@ -488,7 +507,7 @@ class kolab_calendar extends kolab_storage_folder_api
$object['links'] = $links; $object['links'] = $links;
} }
$this->events = array($event['uid'] => $this->_to_driver_event($object, true)); $this->events = [$event['uid'] => $this->_to_driver_event($object, true)];
} }
return $saved; return $saved;
@ -499,28 +518,31 @@ class kolab_calendar extends kolab_storage_folder_api
* *
* @see calendar_driver::new_event() * @see calendar_driver::new_event()
* *
* @return boolean True on success, False on error * @return bool True on success, False on error
*/ */
public function update_event($event, $exception_id = null) public function update_event($event, $exception_id = null)
{ {
$updated = false; $updated = false;
$old = $this->storage->get_object($event['uid'] ?: $event['id']); $old = $this->storage->get_object(!empty($event['uid']) ? $event['uid'] : $event['id']);
if (!$old || PEAR::isError($old))
if (!$old || PEAR::isError($old)) {
return false; return false;
}
// email links are stored separately // email links are stored separately
$links = $event['links']; $links = !empty($event['links']) ? $event['links'] : [];
unset($event['links']); unset($event['links']);
$object = $this->_from_driver_event($event, $old); $object = $this->_from_driver_event($event, $old);
$saved = $this->storage->save($object, 'event', $old['uid']); $saved = $this->storage->save($object, 'event', $old['uid']);
if (!$saved) { if (!$saved) {
rcube::raise_error(array( rcube::raise_error([
'code' => 600, 'type' => 'php', 'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
'file' => __FILE__, 'line' => __LINE__, 'message' => "Error saving event object to Kolab server"
'message' => "Error saving event object to Kolab server"), ],
true, false); true, false
);
} }
else { else {
// save links in configuration.relation object // save links in configuration.relation object
@ -529,7 +551,7 @@ class kolab_calendar extends kolab_storage_folder_api
} }
$updated = true; $updated = true;
$this->events = array($event['uid'] => $this->_to_driver_event($object, true)); $this->events = [$event['uid'] => $this->_to_driver_event($object, true)];
// refresh local cache with recurring instances // refresh local cache with recurring instances
if ($exception_id) { if ($exception_id) {
@ -545,17 +567,19 @@ class kolab_calendar extends kolab_storage_folder_api
* *
* @see calendar_driver::remove_event() * @see calendar_driver::remove_event()
* *
* @return boolean True on success, False on error * @return bool True on success, False on error
*/ */
public function delete_event($event, $force = true) public function delete_event($event, $force = true)
{ {
$deleted = $this->storage->delete($event['uid'] ?: $event['id'], $force); $deleted = $this->storage->delete(!empty($event['uid']) ? $event['uid'] : $event['id'], $force);
if (!$deleted) { if (!$deleted) {
rcube::raise_error(array( rcube::raise_error([
'code' => 600, 'file' => __FILE__, 'line' => __LINE__, 'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
'message' => sprintf("Error deleting event object '%s' from Kolab server", $event['id'])), 'message' => sprintf("Error deleting event object '%s' from Kolab server", $event['id'])
true, false); ],
true, false
);
} }
return $deleted; return $deleted;
@ -566,7 +590,7 @@ class kolab_calendar extends kolab_storage_folder_api
* *
* @see calendar_driver::undelete_event() * @see calendar_driver::undelete_event()
* *
* @return boolean True on success, False on error * @return bool True on success, False on error
*/ */
public function restore_event($event) public function restore_event($event)
{ {
@ -576,12 +600,13 @@ class kolab_calendar extends kolab_storage_folder_api
if ($this->storage->undelete($uid)) { if ($this->storage->undelete($uid)) {
return true; return true;
} }
else {
rcube::raise_error(array( rcube::raise_error([
'code' => 600, 'file' => __FILE__, 'line' => __LINE__, 'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
'message' => sprintf("Error undeleting the event object '%s' from the Kolab server", $event['id'])), 'message' => sprintf("Error undeleting the event object '%s' from the Kolab server", $event['id'])
true, false); ],
} true, false
);
return false; return false;
} }
@ -617,14 +642,17 @@ class kolab_calendar extends kolab_storage_folder_api
*/ */
public function get_recurring_events($event, $start, $end = null, $event_id = null, $limit = null) public function get_recurring_events($event, $start, $end = null, $event_id = null, $limit = null)
{ {
$object = $event['_formatobj']; if (empty($event['_formatobj'])) {
if (!$object) { $rec = $this->storage->get_object(!empty($event['uid']) ? $event['uid'] : $event['id']);
$rec = $this->storage->get_object($event['uid'] ?: $event['id']);
$object = $rec['_formatobj']; $object = $rec['_formatobj'];
} }
else {
$object = $event['_formatobj'];
}
if (!is_object($object)) if (!is_object($object)) {
return array(); return [];
}
// determine a reasonable end date if none given // determine a reasonable end date if none given
if (!$end) { if (!$end) {
@ -632,27 +660,32 @@ class kolab_calendar extends kolab_storage_folder_api
$end->add(new DateInterval('P100Y')); $end->add(new DateInterval('P100Y'));
} }
// read recurrence exceptions first
$events = [];
$exdata = [];
$futuredata = [];
$recurrence_id_format = libcalendaring::recurrence_id_format($event);
if (!empty($event['recurrence'])) {
// copy the recurrence rule from the master event (to be used in the UI) // copy the recurrence rule from the master event (to be used in the UI)
$recurrence_rule = $event['recurrence']; $recurrence_rule = $event['recurrence'];
unset($recurrence_rule['EXCEPTIONS'], $recurrence_rule['EXDATE']); unset($recurrence_rule['EXCEPTIONS'], $recurrence_rule['EXDATE']);
// read recurrence exceptions first if (!empty($event['recurrence']['EXCEPTIONS'])) {
$events = array();
$exdata = array();
$futuredata = array();
$recurrence_id_format = libcalendaring::recurrence_id_format($event);
if (is_array($event['recurrence']['EXCEPTIONS'])) {
foreach ($event['recurrence']['EXCEPTIONS'] as $exception) { foreach ($event['recurrence']['EXCEPTIONS'] as $exception) {
if (!$exception['_instance']) if (empty($exception['_instance'])) {
$exception['_instance'] = libcalendaring::recurrence_instance_identifier($exception, $event['allday']); $exception['_instance'] = libcalendaring::recurrence_instance_identifier($exception, !empty($event['allday']));
}
$rec_event = $this->_to_driver_event($exception, false, false, $event); $rec_event = $this->_to_driver_event($exception, false, false, $event);
$rec_event['id'] = $event['uid'] . '-' . $exception['_instance']; $rec_event['id'] = $event['uid'] . '-' . $exception['_instance'];
$rec_event['isexception'] = 1; $rec_event['isexception'] = 1;
// found the specifically requested instance: register exception (single occurrence wins) // found the specifically requested instance: register exception (single occurrence wins)
if ($rec_event['id'] == $event_id && (!$this->events[$event_id] || $this->events[$event_id]['thisandfuture'])) { if (
$rec_event['id'] == $event_id
&& (empty($this->events[$event_id]) || !empty($this->events[$event_id]['thisandfuture']))
) {
$rec_event['recurrence'] = $recurrence_rule; $rec_event['recurrence'] = $recurrence_rule;
$rec_event['recurrence_id'] = $event['uid']; $rec_event['recurrence_id'] = $event['uid'];
$this->events[$rec_event['id']] = $rec_event; $this->events[$rec_event['id']] = $rec_event;
@ -660,18 +693,19 @@ class kolab_calendar extends kolab_storage_folder_api
// remember this exception's date // remember this exception's date
$exdate = substr($exception['_instance'], 0, 8); $exdate = substr($exception['_instance'], 0, 8);
if (!$exdata[$exdate] || $exdata[$exdate]['thisandfuture']) { if (empty($exdata[$exdate]) || !empty($exdata[$exdate]['thisandfuture'])) {
$exdata[$exdate] = $rec_event; $exdata[$exdate] = $rec_event;
} }
if ($rec_event['thisandfuture']) { if (!empty($rec_event['thisandfuture'])) {
$futuredata[$exdate] = $rec_event; $futuredata[$exdate] = $rec_event;
} }
} }
} }
}
// found the specifically requested instance, exiting... // found the specifically requested instance, exiting...
if ($event_id && !empty($this->events[$event_id])) { if ($event_id && !empty($this->events[$event_id])) {
return array($this->events[$event_id]); return [$this->events[$event_id]];
} }
// Check first occurrence, it might have been moved // Check first occurrence, it might have been moved
@ -697,11 +731,12 @@ class kolab_calendar extends kolab_storage_folder_api
$instance_id = $next_event['start']->format($recurrence_id_format); $instance_id = $next_event['start']->format($recurrence_id_format);
// use this event data for future recurring instances // use this event data for future recurring instances
if ($futuredata[$datestr]) if (!empty($futuredata[$datestr])) {
$overlay_data = $futuredata[$datestr]; $overlay_data = $futuredata[$datestr];
}
$rec_id = $event['uid'] . '-' . $instance_id; $rec_id = $event['uid'] . '-' . $instance_id;
$exception = $exdata[$datestr] ?: $overlay_data; $exception = !empty($exdata[$datestr]) ? $exdata[$datestr] : $overlay_data;
$event_start = $next_event['start']; $event_start = $next_event['start'];
$event_end = $next_event['end']; $event_end = $next_event['end'];
@ -719,8 +754,10 @@ class kolab_calendar extends kolab_storage_folder_api
$rec_event['_instance'] = $instance_id; $rec_event['_instance'] = $instance_id;
$rec_event['_count'] = $i + 1; $rec_event['_count'] = $i + 1;
if ($exception) // copy data from exception if ($exception) {
// copy data from exception
kolab_driver::merge_exception_data($rec_event, $exception); kolab_driver::merge_exception_data($rec_event, $exception);
}
$rec_event['id'] = $rec_id; $rec_event['id'] = $rec_id;
$rec_event['recurrence_id'] = $event['uid']; $rec_event['recurrence_id'] = $event['uid'];
@ -737,13 +774,16 @@ class kolab_calendar extends kolab_storage_folder_api
return $events; return $events;
} }
} }
else if ($next_event['start'] > $end) // stop loop if out of range else if ($next_event['start'] > $end) {
// stop loop if out of range
break; break;
}
// avoid endless recursion loops // avoid endless recursion loops
if (++$i > 100000) if (++$i > 100000) {
break; break;
} }
}
return $events; return $events;
} }
@ -769,7 +809,7 @@ class kolab_calendar extends kolab_storage_folder_api
} }
if ($ns == 'other' || !$this->cal->rc->config->get('kolab_invitation_calendars')) { if ($ns == 'other' || !$this->cal->rc->config->get('kolab_invitation_calendars')) {
$record = kolab_driver::add_partstat_class($record, array('NEEDS-ACTION', 'DECLINED'), $this->get_owner()); $record = kolab_driver::add_partstat_class($record, ['NEEDS-ACTION', 'DECLINED'], $this->get_owner());
// 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
@ -778,15 +818,15 @@ class kolab_calendar extends kolab_storage_folder_api
// 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 && $record['recurrence'] && !$record['recurrence_id'] && !$record['_instance']) { if (!$noinst && !empty($record['recurrence']) && 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 (is_a($record['recurrence_date'], 'DateTime')) { else if (isset($record['recurrence_date']) && is_a($record['recurrence_date'], 'DateTime')) {
$record['_instance'] = $record['recurrence_date']->format($recurrence_id_format); $record['_instance'] = $record['recurrence_date']->format($recurrence_id_format);
} }
// clean up exception data // clean up exception data
if ($record['recurrence'] && is_array($record['recurrence']['EXCEPTIONS'])) { if (!empty($record['recurrence']) && !empty($record['recurrence']['EXCEPTIONS'])) {
array_walk($record['recurrence']['EXCEPTIONS'], function(&$exception) { array_walk($record['recurrence']['EXCEPTIONS'], function(&$exception) {
unset($exception['_mailbox'], $exception['_msguid'], $exception['_formatobj'], $exception['_attachments']); unset($exception['_mailbox'], $exception['_msguid'], $exception['_formatobj'], $exception['_attachments']);
}); });
@ -799,24 +839,24 @@ class kolab_calendar extends kolab_storage_folder_api
* Convert the given event record into a data structure that can be passed to Kolab_Storage backend for saving * Convert the given event record into a data structure that can be passed to Kolab_Storage backend for saving
* (opposite of self::_to_driver_event()) * (opposite of self::_to_driver_event())
*/ */
private function _from_driver_event($event, $old = array()) private function _from_driver_event($event, $old = [])
{ {
// set current user as ORGANIZER // set current user as ORGANIZER
if ($identity = $this->cal->rc->user->list_emails(true)) { if ($identity = $this->cal->rc->user->list_emails(true)) {
$event['attendees'] = (array) $event['attendees']; $event['attendees'] = !empty($event['attendees']) ? $event['attendees'] : [];
$found = false; $found = false;
// there can be only resources on attendees list (T1484) // there can be only resources on attendees list (T1484)
// let's check the existence of an organizer // let's check the existence of an organizer
foreach ($event['attendees'] as $attendee) { foreach ($event['attendees'] as $attendee) {
if ($attendee['role'] == 'ORGANIZER') { if (!empty($attendee['role']) && $attendee['role'] == 'ORGANIZER') {
$found = true; $found = true;
break; break;
} }
} }
if (!$found) { if (!$found) {
$event['attendees'][] = array('role' => 'ORGANIZER', 'name' => $identity['name'], 'email' => $identity['email']); $event['attendees'][] = ['role' => 'ORGANIZER', 'name' => $identity['name'], 'email' => $identity['email']];
} }
$event['_owner'] = $identity['email']; $event['_owner'] = $identity['email'];
@ -824,12 +864,12 @@ class kolab_calendar extends kolab_storage_folder_api
// remove EXDATE values if RDATE is given // remove EXDATE values if RDATE is given
if (!empty($event['recurrence']['RDATE'])) { if (!empty($event['recurrence']['RDATE'])) {
$event['recurrence']['EXDATE'] = array(); $event['recurrence']['EXDATE'] = [];
} }
// remove recurrence information (e.g. EXDATES and EXCEPTIONS) entirely // remove recurrence information (e.g. EXDATES and EXCEPTIONS) entirely
if ($event['recurrence'] && empty($event['recurrence']['FREQ']) && empty($event['recurrence']['RDATE'])) { if (!empty($event['recurrence']) && empty($event['recurrence']['FREQ']) && empty($event['recurrence']['RDATE'])) {
$event['recurrence'] = array(); $event['recurrence'] = [];
} }
// keep 'comment' from initial itip invitation // keep 'comment' from initial itip invitation
@ -847,7 +887,7 @@ class kolab_calendar extends kolab_storage_folder_api
$cleanup_fn($event); $cleanup_fn($event);
// clean up exception data // clean up exception data
if (is_array($event['exceptions'])) { if (!empty($event['exceptions'])) {
array_walk($event['exceptions'], function(&$exception) use ($cleanup_fn) { array_walk($event['exceptions'], function(&$exception) use ($cleanup_fn) {
unset($exception['_mailbox'], $exception['_msguid'], $exception['_formatobj']); unset($exception['_mailbox'], $exception['_msguid'], $exception['_formatobj']);
$cleanup_fn($exception); $cleanup_fn($exception);
@ -855,10 +895,11 @@ class kolab_calendar extends kolab_storage_folder_api
} }
// copy meta data (starting with _) from old object // copy meta data (starting with _) from old object
foreach ((array)$old as $key => $val) { foreach ((array) $old as $key => $val) {
if (!isset($event[$key]) && $key[0] == '_') if (!isset($event[$key]) && $key[0] == '_') {
$event[$key] = $val; $event[$key] = $val;
} }
}
return $event; return $event;
} }
@ -870,9 +911,14 @@ class kolab_calendar extends kolab_storage_folder_api
{ {
$hits = 0; $hits = 0;
foreach ($this->search_fields as $col) { foreach ($this->search_fields as $col) {
$sval = is_array($event[$col]) ? self::_complex2string($event[$col]) : $event[$col]; if (empty($event[$col])) {
if (empty($sval))
continue; continue;
}
$sval = is_array($event[$col]) ? self::_complex2string($event[$col]) : $event[$col];
if (empty($sval)) {
continue;
}
// do a simple substring matching (to be improved) // do a simple substring matching (to be improved)
$val = mb_strtolower($sval); $val = mb_strtolower($sval);
@ -890,7 +936,7 @@ class kolab_calendar extends kolab_storage_folder_api
*/ */
private static function _complex2string($prop) private static function _complex2string($prop)
{ {
static $ignorekeys = array('role','status','rsvp'); static $ignorekeys = ['role', 'status', 'rsvp'];
$out = ''; $out = '';
if (is_array($prop)) { if (is_array($prop)) {
@ -909,5 +955,4 @@ class kolab_calendar extends kolab_storage_folder_api
return rtrim($out); return rtrim($out);
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -30,8 +30,8 @@ class kolab_invitation_calendar
public $editable = false; public $editable = false;
public $attachments = false; public $attachments = false;
public $subscriptions = false; public $subscriptions = false;
public $partstats = array('unknown'); public $partstats = ['unknown'];
public $categories = array(); public $categories = [];
public $name = 'Invitations'; public $name = 'Invitations';
@ -45,23 +45,26 @@ class kolab_invitation_calendar
switch ($this->id) { switch ($this->id) {
case kolab_driver::INVITATIONS_CALENDAR_PENDING: case kolab_driver::INVITATIONS_CALENDAR_PENDING:
$this->partstats = array('NEEDS-ACTION'); $this->partstats = ['NEEDS-ACTION'];
$this->name = $this->cal->gettext('invitationspending'); $this->name = $this->cal->gettext('invitationspending');
if (!empty($_REQUEST['_quickview']))
if (!empty($_REQUEST['_quickview'])) {
$this->partstats[] = 'TENTATIVE'; $this->partstats[] = 'TENTATIVE';
}
break; break;
case kolab_driver::INVITATIONS_CALENDAR_DECLINED: case kolab_driver::INVITATIONS_CALENDAR_DECLINED:
$this->partstats = array('DECLINED'); $this->partstats = ['DECLINED'];
$this->name = $this->cal->gettext('invitationsdeclined'); $this->name = $this->cal->gettext('invitationsdeclined');
break; break;
} }
// user-specific alarms settings win // user-specific alarms settings win
$prefs = $this->cal->rc->config->get('kolab_calendars', array()); $prefs = $this->cal->rc->config->get('kolab_calendars', []);
if (isset($prefs[$this->id]['showalarms'])) if (isset($prefs[$this->id]['showalarms'])) {
$this->alarms = $prefs[$this->id]['showalarms']; $this->alarms = $prefs[$this->id]['showalarms'];
} }
}
/** /**
* Getter for a nice and human readable name for this calendar * Getter for a nice and human readable name for this calendar
@ -128,10 +131,11 @@ class kolab_invitation_calendar
public function get_color() public function get_color()
{ {
// calendar color is stored in local user prefs // calendar color is stored in local user prefs
$prefs = $this->cal->rc->config->get('kolab_calendars', array()); $prefs = $this->cal->rc->config->get('kolab_calendars', []);
if (!empty($prefs[$this->id]) && !empty($prefs[$this->id]['color'])) if (!empty($prefs[$this->id]) && !empty($prefs[$this->id]['color'])) {
return $prefs[$this->id]['color']; return $prefs[$this->id]['color'];
}
return 'ffffff'; return 'ffffff';
} }
@ -147,12 +151,12 @@ class kolab_invitation_calendar
/** /**
* Check activation status of this folder * Check activation status of this folder
* *
* @return boolean True if enabled, false if not * @return bool True if enabled, false if not
*/ */
public function is_active() public function is_active()
{ {
$prefs = $this->cal->rc->config->get('kolab_calendars', array()); // read local prefs $prefs = $this->cal->rc->config->get('kolab_calendars', []); // read local prefs
return (bool)$prefs[$this->id]['active']; return !empty($prefs[$this->id]['active']);
} }
/** /**
@ -190,7 +194,7 @@ class kolab_invitation_calendar
public function get_recurring_events($event, $start, $end = null, $event_id = null, $limit = null) public function get_recurring_events($event, $start, $end = null, $event_id = null, $limit = null)
{ {
// forward call to the actual storage folder // forward call to the actual storage folder
if ($event['_folder_id']) { if (!empty($event['_folder_id'])) {
$cal = $this->cal->driver->get_calendar($event['_folder_id']); $cal = $this->cal->driver->get_calendar($event['_folder_id']);
if ($cal && $cal->ready) { if ($cal && $cal->ready) {
return $cal->get_recurring_events($event, $start, $end, $event_id, $limit); return $cal->get_recurring_events($event, $start, $end, $event_id, $limit);
@ -227,39 +231,45 @@ class kolab_invitation_calendar
} }
/** /**
* @param integer Event's new start (unix timestamp) * @param int Event's new start (unix timestamp)
* @param integer Event's new end (unix timestamp) * @param int Event's new end (unix timestamp)
* @param string Search query (optional) * @param string Search query (optional)
* @param boolean Include virtual events (optional) * @param bool Include virtual events (optional)
* @param array Additional parameters to query storage * @param array Additional parameters to query storage
* *
* @return array A list of event records * @return array A list of event records
*/ */
public function list_events($start, $end, $search = null, $virtual = 1, $query = array()) public function list_events($start, $end, $search = null, $virtual = 1, $query = [])
{ {
// get email addresses of the current user // get email addresses of the current user
$user_emails = $this->cal->get_user_emails(); $user_emails = $this->cal->get_user_emails();
$subquery = array(); $subquery = [];
foreach ($user_emails as $email) { foreach ($user_emails as $email) {
foreach ($this->partstats as $partstat) { foreach ($this->partstats as $partstat) {
$subquery[] = array('tags', '=', 'x-partstat:' . $email . ':' . strtolower($partstat)); $subquery[] = ['tags', '=', 'x-partstat:' . $email . ':' . strtolower($partstat)];
} }
} }
$events = [];
// aggregate events from all calendar folders // aggregate events from all calendar folders
$events = array();
foreach (kolab_storage::list_folders('', '*', 'event', null) as $foldername) { foreach (kolab_storage::list_folders('', '*', 'event', null) as $foldername) {
$cal = $this->_get_calendar($foldername); $cal = $this->_get_calendar($foldername);
if (!$cal || $cal->get_namespace() == 'other') if (!$cal || $cal->get_namespace() == 'other') {
continue; continue;
}
foreach ($cal->list_events($start, $end, $search, 1, $query, array(array($subquery, 'OR'))) as $event) { foreach ($cal->list_events($start, $end, $search, 1, $query, [[$subquery, 'OR']]) as $event) {
$match = false; $match = false;
// post-filter events to match out partstats // post-filter events to match out partstats
if (is_array($event['attendees'])) { if (!empty($event['attendees'])) {
foreach ($event['attendees'] as $attendee) { foreach ($event['attendees'] as $attendee) {
if (in_array($attendee['email'], $user_emails) && in_array($attendee['status'], $this->partstats)) { if (
in_array($attendee['email'], $user_emails)
&& in_array($attendee['status'], $this->partstats)
) {
$match = true; $match = true;
break; break;
} }
@ -267,7 +277,8 @@ class kolab_invitation_calendar
} }
if ($match) { if ($match) {
$events[$event['id'] ?: $event['uid']] = $this->_mod_event($event, $cal->id); $uid = !empty($event['id']) ? $event['id'] : $event['uid'];
$events[$uid] = $this->_mod_event($event, $cal->id);
} }
} }
@ -281,34 +292,36 @@ class kolab_invitation_calendar
/** /**
* Get number of events in the given calendar * Get number of events in the given calendar
* *
* @param integer Date range start (unix timestamp) * @param int Date range start (unix timestamp)
* @param integer Date range end (unix timestamp) * @param int Date range end (unix timestamp)
* @param array Additional query to filter events * @param array Additional query to filter events
* *
* @return integer Count * @return int Count
*/ */
public function count_events($start, $end = null, $filter = null) public function count_events($start, $end = null, $filter = null)
{ {
// get email addresses of the current user // get email addresses of the current user
$user_emails = $this->cal->get_user_emails(); $user_emails = $this->cal->get_user_emails();
$subquery = array(); $subquery = [];
foreach ($user_emails as $email) { foreach ($user_emails as $email) {
foreach ($this->partstats as $partstat) { foreach ($this->partstats as $partstat) {
$subquery[] = array('tags', '=', 'x-partstat:' . $email . ':' . strtolower($partstat)); $subquery[] = ['tags', '=', 'x-partstat:' . $email . ':' . strtolower($partstat)];
} }
} }
$filter = array( $filter = [
array('tags','!=','x-status:cancelled'), ['tags', '!=', 'x-status:cancelled'],
array($subquery, 'OR') [$subquery, 'OR']
); ];
// aggregate counts from all calendar folders // aggregate counts from all calendar folders
$count = 0; $count = 0;
foreach (kolab_storage::list_folders('', '*', 'event', null) as $foldername) { foreach (kolab_storage::list_folders('', '*', 'event', null) as $foldername) {
$cal = $this->_get_calendar($foldername); $cal = $this->_get_calendar($foldername);
if (!$cal || $cal->get_namespace() == 'other') if (!$cal || $cal->get_namespace() == 'other') {
continue; continue;
}
$count += $cal->count_events($start, $end, $filter); $count += $cal->count_events($start, $end, $filter);
} }
@ -363,7 +376,7 @@ class kolab_invitation_calendar
public function update_event($event, $exception_id = null) public function update_event($event, $exception_id = null)
{ {
// forward call to the actual storage folder // forward call to the actual storage folder
if ($event['_folder_id']) { if (!empty($event['_folder_id'])) {
$cal = $this->cal->driver->get_calendar($event['_folder_id']); $cal = $this->cal->driver->get_calendar($event['_folder_id']);
if ($cal && $cal->ready) { if ($cal && $cal->ready) {
return $cal->update_event($event, $exception_id); return $cal->update_event($event, $exception_id);
@ -381,7 +394,7 @@ class kolab_invitation_calendar
public function delete_event($event, $force = true) public function delete_event($event, $force = true)
{ {
// forward call to the actual storage folder // forward call to the actual storage folder
if ($event['_folder_id']) { if (!empty($event['_folder_id'])) {
$cal = $this->cal->driver->get_calendar($event['_folder_id']); $cal = $this->cal->driver->get_calendar($event['_folder_id']);
if ($cal && $cal->ready) { if ($cal && $cal->ready) {
return $cal->delete_event($event, $force); return $cal->delete_event($event, $force);
@ -399,7 +412,7 @@ class kolab_invitation_calendar
public function restore_event($event) public function restore_event($event)
{ {
// forward call to the actual storage folder // forward call to the actual storage folder
if ($event['_folder_id']) { if (!empty($event['_folder_id'])) {
$cal = $this->cal->driver->get_calendar($event['_folder_id']); $cal = $this->cal->driver->get_calendar($event['_folder_id']);
if ($cal && $cal->ready) { if ($cal && $cal->ready) {
return $cal->restore_event($event); return $cal->restore_event($event);

View file

@ -29,8 +29,8 @@ class kolab_user_calendar extends kolab_calendar
public $attachments = false; public $attachments = false;
public $subscriptions = false; public $subscriptions = false;
protected $userdata = array(); protected $userdata = [];
protected $timeindex = array(); protected $timeindex = [];
/** /**
@ -50,7 +50,8 @@ class kolab_user_calendar extends kolab_calendar
$this->storage = $user_or_folder; $this->storage = $user_or_folder;
$this->userdata = $this->storage->ldaprec; $this->userdata = $this->storage->ldaprec;
} }
else { // get user record from LDAP else {
// get user record from LDAP
$this->storage = new kolab_storage_folder_user($user_or_folder); $this->storage = new kolab_storage_folder_user($user_or_folder);
$this->userdata = $this->storage->ldaprec; $this->userdata = $this->storage->ldaprec;
} }
@ -66,11 +67,12 @@ class kolab_user_calendar extends kolab_calendar
$this->parent = ''; // user calendars are top level $this->parent = ''; // user calendars are top level
// user-specific alarms settings win // user-specific alarms settings win
$prefs = $this->cal->rc->config->get('kolab_calendars', array()); $prefs = $this->cal->rc->config->get('kolab_calendars', []);
if (isset($prefs[$this->id]['showalarms'])) if (isset($prefs[$this->id]['showalarms'])) {
$this->alarms = $prefs[$this->id]['showalarms']; $this->alarms = $prefs[$this->id]['showalarms'];
} }
} }
}
/** /**
* Getter for a nice and human readable name for this calendar * Getter for a nice and human readable name for this calendar
@ -79,7 +81,11 @@ class kolab_user_calendar extends kolab_calendar
*/ */
public function get_name() public function get_name()
{ {
return $this->userdata['displayname'] ?: ($this->userdata['name'] ?: $this->userdata['mail']); if (!empty($this->userdata['displayname'])) {
return $this->userdata['displayname'];
}
return !empty($this->userdata['name']) ? $this->userdata['name'] : $this->userdata['mail'];
} }
/** /**
@ -99,7 +105,15 @@ class kolab_user_calendar extends kolab_calendar
*/ */
public function get_title() public function get_title()
{ {
return trim($this->userdata['displayname'] . '; ' . $this->userdata['mail'], '; '); $title = [];
if (!empty($this->userdata['displayname'])) {
$title[] = $this->userdata['displayname'];
}
$title[] = $this->userdata['mail'];
return implode('; ', $title);
} }
/** /**
@ -128,10 +142,11 @@ class kolab_user_calendar extends kolab_calendar
public function get_color($default = null) public function get_color($default = null)
{ {
// calendar color is stored in local user prefs // calendar color is stored in local user prefs
$prefs = $this->cal->rc->config->get('kolab_calendars', array()); $prefs = $this->cal->rc->config->get('kolab_calendars', []);
if (!empty($prefs[$this->id]) && !empty($prefs[$this->id]['color'])) if (!empty($prefs[$this->id]) && !empty($prefs[$this->id]['color'])) {
return $prefs[$this->id]['color']; return $prefs[$this->id]['color'];
}
return $default ?: 'cc0000'; return $default ?: 'cc0000';
} }
@ -172,7 +187,7 @@ class kolab_user_calendar extends kolab_calendar
public function get_event($id) public function get_event($id)
{ {
// TODO: implement this // TODO: implement this
return $this->events[$id]; return isset($this->events[$id]) ? $this->events[$id] : null;
} }
/** /**
@ -181,11 +196,11 @@ class kolab_user_calendar extends kolab_calendar
*/ */
public function get_attachment_body($id, $event) public function get_attachment_body($id, $event)
{ {
if (!$event['calendar'] && ($ev = $this->get_event($event['id']))) { if (empty($event['calendar']) && ($ev = $this->get_event($event['id']))) {
$event['calendar'] = $ev['calendar']; $event['calendar'] = $ev['calendar'];
} }
if ($event['calendar'] && ($cal = $this->cal->get_calendar($event['calendar']))) { if (!empty($event['calendar']) && ($cal = $this->cal->get_calendar($event['calendar']))) {
return $cal->get_attachment_body($id, $event); return $cal->get_attachment_body($id, $event);
} }
@ -193,16 +208,16 @@ class kolab_user_calendar extends kolab_calendar
} }
/** /**
* @param integer Event's new start (unix timestamp) * @param int Event's new start (unix timestamp)
* @param integer Event's new end (unix timestamp) * @param int Event's new end (unix timestamp)
* @param string Search query (optional) * @param string Search query (optional)
* @param boolean Include virtual events (optional) * @param bool Include virtual events (optional)
* @param array Additional parameters to query storage * @param array Additional parameters to query storage
* @param array Additional query to filter events * @param array Additional query to filter events
* *
* @return array A list of event records * @return array A list of event records
*/ */
public function list_events($start, $end, $search = null, $virtual = 1, $query = array(), $filter_query = null) public function list_events($start, $end, $search = null, $virtual = 1, $query = [], $filter_query = null)
{ {
// convert to DateTime for comparisons // convert to DateTime for comparisons
try { try {
@ -219,6 +234,7 @@ class kolab_user_calendar extends kolab_calendar
} }
$limit_changed = null; $limit_changed = null;
if (!empty($query)) { if (!empty($query)) {
foreach ($query as $q) { foreach ($query as $q) {
if ($q[0] == 'changed' && $q[1] == '>=') { if ($q[0] == 'changed' && $q[1] == '>=') {
@ -232,7 +248,7 @@ class kolab_user_calendar extends kolab_calendar
foreach (kolab_storage::list_user_folders($this->userdata, 'event', 2) as $foldername) { foreach (kolab_storage::list_user_folders($this->userdata, 'event', 2) as $foldername) {
$cal = new kolab_calendar($foldername, $this->cal); $cal = new kolab_calendar($foldername, $this->cal);
foreach ($cal->list_events($start, $end, $search, 1) as $event) { foreach ($cal->list_events($start, $end, $search, 1) as $event) {
$uid = $event['id'] ?: $event['uid']; $uid = !empty($event['id']) ? $event['id'] : $event['uid'];
$this->events[$uid] = $event; $this->events[$uid] = $event;
$this->timeindex[$this->time_key($event)] = $uid; $this->timeindex[$this->time_key($event)] = $uid;
} }
@ -244,11 +260,14 @@ class kolab_user_calendar extends kolab_calendar
$this->fetch_freebusy($limit_changed); $this->fetch_freebusy($limit_changed);
} }
$events = array(); $events = [];
foreach ($this->events as $event) { foreach ($this->events as $event) {
// list events in requested time window // list events in requested time window
if ($event['start'] <= $end_dt && $event['end'] >= $start_dt && if (
(!$limit_changed || !$event['changed'] || $event['changed'] >= $limit_changed)) { $event['start'] <= $end_dt
&& $event['end'] >= $start_dt
&& (!$limit_changed || empty($event['changed']) || $event['changed'] >= $limit_changed)
) {
$events[] = $event; $events[] = $event;
} }
} }
@ -262,8 +281,8 @@ class kolab_user_calendar extends kolab_calendar
/** /**
* Get number of events in the given calendar * Get number of events in the given calendar
* *
* @param integer Date range start (unix timestamp) * @param int Date range start (unix timestamp)
* @param integer Date range end (unix timestamp) * @param int Date range end (unix timestamp)
* @param array Additional query to filter events * @param array Additional query to filter events
* *
* @return integer Count * @return integer Count
@ -281,10 +300,10 @@ class kolab_user_calendar extends kolab_calendar
{ {
// ask kolab server first // ask kolab server first
try { try {
$request_config = array( $request_config = [
'store_body' => true, 'store_body' => true,
'follow_redirects' => true, 'follow_redirects' => true,
); ];
$request = libkolab::http_request(kolab_storage::get_freebusy_url($this->userdata['mail']), 'GET', $request_config); $request = libkolab::http_request(kolab_storage::get_freebusy_url($this->userdata['mail']), 'GET', $request_config);
$response = $request->send(); $response = $request->send();
@ -294,70 +313,72 @@ class kolab_user_calendar extends kolab_calendar
$response = $request->send(); $response = $request->send();
} }
if ($response->getStatus() == 200) if ($response->getStatus() == 200) {
$fbdata = $response->getBody(); $fbdata = $response->getBody();
}
unset($request, $response); unset($request, $response);
} }
catch (Exception $e) { catch (Exception $e) {
rcube::raise_error(array( rcube::raise_error([
'code' => 900, 'code' => 900, 'file' => __FILE__, 'line' => __LINE__,
'type' => 'php', 'message' => "Error fetching free/busy information: " . $e->getMessage()
'file' => __FILE__, ],
'line' => __LINE__, true, false
'message' => "Error fetching free/busy information: " . $e->getMessage()), );
true, false);
return false; return false;
} }
$statusmap = array( $statusmap = [
'FREE' => 'free', 'FREE' => 'free',
'BUSY' => 'busy', 'BUSY' => 'busy',
'BUSY-TENTATIVE' => 'tentative', 'BUSY-TENTATIVE' => 'tentative',
'X-OUT-OF-OFFICE' => 'outofoffice', 'X-OUT-OF-OFFICE' => 'outofoffice',
'OOF' => 'outofoffice', 'OOF' => 'outofoffice',
); ];
$titlemap = array(
$titlemap = [
'FREE' => $this->cal->gettext('availfree'), 'FREE' => $this->cal->gettext('availfree'),
'BUSY' => $this->cal->gettext('availbusy'), 'BUSY' => $this->cal->gettext('availbusy'),
'BUSY-TENTATIVE' => $this->cal->gettext('availtentative'), 'BUSY-TENTATIVE' => $this->cal->gettext('availtentative'),
'X-OUT-OF-OFFICE' => $this->cal->gettext('availoutofoffice'), 'X-OUT-OF-OFFICE' => $this->cal->gettext('availoutofoffice'),
); ];
// rcube::console('_fetch_freebusy', kolab_storage::get_freebusy_url($this->userdata['mail']), $fbdata); // rcube::console('_fetch_freebusy', kolab_storage::get_freebusy_url($this->userdata['mail']), $fbdata);
// parse free-busy information
$count = 0; $count = 0;
if ($fbdata) {
// parse free-busy information
if (!empty($fbdata)) {
$ical = $this->cal->get_ical(); $ical = $this->cal->get_ical();
$ical->import($fbdata); $ical->import($fbdata);
if ($fb = $ical->freebusy) { if ($fb = $ical->freebusy) {
// consider 'changed >= X' queries // consider 'changed >= X' queries
if ($limit_changed && $fb['created'] && $fb['created'] < $limit_changed) { if ($limit_changed && !empty($fb['created']) && $fb['created'] < $limit_changed) {
return 0; return 0;
} }
foreach ($fb['periods'] as $tuple) { foreach ($fb['periods'] as $tuple) {
list($from, $to, $type) = $tuple; list($from, $to, $type) = $tuple;
$event = array( $event = [
'uid' => md5($this->id . $from->format('U') . '/' . $to->format('U')), 'uid' => md5($this->id . $from->format('U') . '/' . $to->format('U')),
'calendar' => $this->id, 'calendar' => $this->id,
'changed' => $fb['created'] ?: new DateTime(), 'changed' => !empty($fb['created']) ? $fb['created'] : new DateTime(),
'title' => $this->get_name() . ' ' . ($titlemap[$type] ?: $type), 'title' => $this->get_name() . ' ' . (!empty($titlemap[$type]) ? $titlemap[$type] : $type),
'start' => $from, 'start' => $from,
'end' => $to, 'end' => $to,
'free_busy' => $statusmap[$type] ?: 'busy', 'free_busy' => !empty($statusmap[$type]) ? $statusmap[$type] : 'busy',
'className' => 'fc-type-freebusy', 'className' => 'fc-type-freebusy',
'organizer' => array( 'organizer' => [
'email' => $this->userdata['mail'], 'email' => $this->userdata['mail'],
'name' => $this->userdata['displayname'], 'name' => isset($this->userdata['displayname']) ? $this->userdata['displayname'] : null,
), ],
); ];
// avoid duplicate entries // avoid duplicate entries
$key = $this->time_key($event); $key = $this->time_key($event);
if (!$this->timeindex[$key]) { if (empty($this->timeindex[$key])) {
$this->events[$event['uid']] = $event; $this->events[$event['uid']] = $event;
$this->timeindex[$key] = $event['uid']; $this->timeindex[$key] = $event['uid'];
$count++; $count++;
@ -393,7 +414,7 @@ class kolab_user_calendar extends kolab_calendar
* Update a specific event record * Update a specific event record
* *
* @see calendar_driver::new_event() * @see calendar_driver::new_event()
* @return boolean True on success, False on error * @return bool True on success, False on error
*/ */
public function update_event($event, $exception_id = null) public function update_event($event, $exception_id = null)
{ {
@ -404,7 +425,7 @@ class kolab_user_calendar extends kolab_calendar
* Delete an event record * Delete an event record
* *
* @see calendar_driver::remove_event() * @see calendar_driver::remove_event()
* @return boolean True on success, False on error * @return bool True on success, False on error
*/ */
public function delete_event($event, $force = true) public function delete_event($event, $force = true)
{ {
@ -415,7 +436,7 @@ class kolab_user_calendar extends kolab_calendar
* Restore deleted event record * Restore deleted event record
* *
* @see calendar_driver::undelete_event() * @see calendar_driver::undelete_event()
* @return boolean True on success, False on error * @return bool True on success, False on error
*/ */
public function restore_event($event) public function restore_event($event)
{ {

View file

@ -41,13 +41,15 @@ class resources_driver_ldap extends resources_driver
/** /**
* Fetch resource objects to be displayed for booking * Fetch resource objects to be displayed for booking
* *
* @param string Search query (optional) * @param string $query Search query (optional)
* @param int $num Max size of the result
*
* @return array List of resource records available for booking * @return array List of resource records available for booking
*/ */
public function load_resources($query = null, $num = 5000) public function load_resources($query = null, $num = 5000)
{ {
if (!($ldap = $this->connect())) { if (!($ldap = $this->connect())) {
return array(); return [];
} }
// TODO: apply paging // TODO: apply paging
@ -72,7 +74,8 @@ class resources_driver_ldap extends resources_driver
/** /**
* Return properties of a single resource * Return properties of a single resource
* *
* @param string Unique resource identifier * @param string $id Unique resource identifier
*
* @return array Resource object as hash array * @return array Resource object as hash array
*/ */
public function get_resource($dn) public function get_resource($dn)
@ -93,7 +96,8 @@ class resources_driver_ldap extends resources_driver
/** /**
* Return properties of a resource owner * Return properties of a resource owner
* *
* @param string Owner identifier * @param string $dn Owner identifier
*
* @return array Resource object as hash array * @return array Resource object as hash array
*/ */
public function get_resource_owner($dn) public function get_resource_owner($dn)
@ -116,15 +120,15 @@ class resources_driver_ldap extends resources_driver
{ {
$rec['ID'] = rcube_ldap::dn_decode($rec['ID']); $rec['ID'] = rcube_ldap::dn_decode($rec['ID']);
$attributes = array(); $attributes = [];
foreach ((array) $rec['attributes'] as $sattr) { foreach ((array) $rec['attributes'] as $sattr) {
$sattr = trim($sattr); $sattr = trim($sattr);
if ($sattr && $sattr[0] === '{') { if (!empty($sattr) && $sattr[0] === '{') {
$attr = @json_decode($sattr, true); $attr = @json_decode($sattr, true);
$attributes += $attr; $attributes += $attr;
} }
else if ($sattr && empty($rec['description'])) { else if (!empty($sattr) && empty($rec['description'])) {
$rec['description'] = $sattr; $rec['description'] = $sattr;
} }
} }
@ -133,7 +137,7 @@ class resources_driver_ldap extends resources_driver
// force $rec['members'] to be an array // force $rec['members'] to be an array
if (!empty($rec['members']) && !is_array($rec['members'])) { if (!empty($rec['members']) && !is_array($rec['members'])) {
$rec['members'] = array($rec['members']); $rec['members'] = [$rec['members']];
} }
// remove unused cruft // remove unused cruft
@ -150,5 +154,4 @@ class resources_driver_ldap extends resources_driver
return $this->ldap->ready ? $this->ldap : null; return $this->ldap->ready ? $this->ldap : null;
} }
} }

View file

@ -39,7 +39,8 @@ abstract class resources_driver
/** /**
* Fetch resource objects to be displayed for booking * Fetch resource objects to be displayed for booking
* *
* @param string Search query (optional) * @param string $query Search query (optional)
*
* @return array List of resource records available for booking * @return array List of resource records available for booking
*/ */
abstract public function load_resources($query = null); abstract public function load_resources($query = null);
@ -47,7 +48,8 @@ abstract class resources_driver
/** /**
* Return properties of a single resource * Return properties of a single resource
* *
* @param string Unique resource identifier * @param string $id Unique resource identifier
*
* @return array Resource object as hash array * @return array Resource object as hash array
*/ */
abstract public function get_resource($id); abstract public function get_resource($id);
@ -55,7 +57,8 @@ abstract class resources_driver
/** /**
* Return properties of a resource owner * Return properties of a resource owner
* *
* @param string Owner identifier * @param string $id Owner identifier
*
* @return array Resource object as hash array * @return array Resource object as hash array
*/ */
public function get_resource_owner($id) public function get_resource_owner($id)
@ -69,20 +72,23 @@ abstract class resources_driver
* The default implementation extracts the resource's email address * The default implementation extracts the resource's email address
* and fetches free-busy data using the calendar backend driver. * and fetches free-busy data using the calendar backend driver.
* *
* @param integer Event's new start (unix timestamp) * @param string $id Calendar identifier
* @param integer Event's new end (unix timestamp) * @param int $start Event's new start (unix timestamp)
* @param int $end Event's new end (unix timestamp)
*
* @return array A list of event objects (see calendar_driver specification) * @return array A list of event objects (see calendar_driver specification)
*/ */
public function get_resource_calendar($id, $start, $end) public function get_resource_calendar($id, $start, $end)
{ {
$events = array(); $events = [];
$rec = $this->get_resource($id); $rec = $this->get_resource($id);
if ($rec && !empty($rec['email']) && $this->cal->driver) {
$fbtypemap = array( if ($rec && !empty($rec['email']) && !empty($this->cal->driver)) {
$fbtypemap = [
calendar::FREEBUSY_BUSY => 'busy', calendar::FREEBUSY_BUSY => 'busy',
calendar::FREEBUSY_TENTATIVE => 'tentative', calendar::FREEBUSY_TENTATIVE => 'tentative',
calendar::FREEBUSY_OOF => 'outofoffice', calendar::FREEBUSY_OOF => 'outofoffice',
); ];
// if the backend has free-busy information // if the backend has free-busy information
$fblist = $this->cal->driver->get_freebusy_list($rec['email'], $start, $end); $fblist = $this->cal->driver->get_freebusy_list($rec['email'], $start, $end);
@ -92,16 +98,16 @@ abstract class resources_driver
if ($type == calendar::FREEBUSY_FREE || $type == calendar::FREEBUSY_UNKNOWN) { if ($type == calendar::FREEBUSY_FREE || $type == calendar::FREEBUSY_UNKNOWN) {
continue; continue;
} }
if ($from < $end && $to > $start) { if ($from < $end && $to > $start) {
$event = array( $events[] = [
'id' => sha1($id . $from . $to), 'id' => sha1($id . $from . $to),
'title' => $rec['name'], 'title' => $rec['name'],
'start' => new DateTime('@' . $from), 'start' => new DateTime('@' . $from),
'end' => new DateTime('@' . $to), 'end' => new DateTime('@' . $to),
'status' => $fbtypemap[$type], 'status' => $fbtypemap[$type],
'calendar' => '_resource', 'calendar' => '_resource',
); ];
$events[] = $event;
} }
} }
} }

View file

@ -46,8 +46,9 @@ class calendar_itip extends libcalendaring_itip
$status = parent::get_itip_status($event, $existing); $status = parent::get_itip_status($event, $existing);
// don't ask for deleting events when declining // don't ask for deleting events when declining
if ($this->rc->config->get('kolab_invitation_calendars')) if ($this->rc->config->get('kolab_invitation_calendars')) {
$status['saved'] = false; $status['saved'] = false;
}
return $status; return $status;
} }
@ -55,7 +56,8 @@ class calendar_itip extends libcalendaring_itip
/** /**
* Find invitation record by token * Find invitation record by token
* *
* @param string Invitation token * @param string $token Invitation token
*
* @return mixed Invitation record as hash array or False if not found * @return mixed Invitation record as hash array or False if not found
*/ */
public function get_invitation($token) public function get_invitation($token)
@ -65,6 +67,7 @@ class calendar_itip extends libcalendaring_itip
if ($result && ($rec = $this->rc->db->fetch_assoc($result))) { if ($result && ($rec = $this->rc->db->fetch_assoc($result))) {
$rec['event'] = unserialize($rec['event']); $rec['event'] = unserialize($rec['event']);
$rec['attendee'] = $parts['attendee']; $rec['attendee'] = $parts['attendee'];
return $rec; return $rec;
} }
} }
@ -75,16 +78,17 @@ class calendar_itip extends libcalendaring_itip
/** /**
* Update the attendee status of the given invitation record * Update the attendee status of the given invitation record
* *
* @param array Invitation record as fetched with calendar_itip::get_invitation() * @param array $invitation Invitation record as fetched with calendar_itip::get_invitation()
* @param string Attendee email address * @param string $email Attendee email address
* @param string New attendee status * @param string $newstatus New attendee status
*/ */
public function update_invitation($invitation, $email, $newstatus) public function update_invitation($invitation, $email, $newstatus)
{ {
if (is_string($invitation)) if (is_string($invitation)) {
$invitation = $this->get_invitation($invitation); $invitation = $this->get_invitation($invitation);
}
if ($invitation['token'] && $invitation['event']) { if (!empty($invitation['token']) && !empty($invitation['event'])) {
// update attendee record in event data // update attendee record in event data
foreach ($invitation['event']['attendees'] as $i => $attendee) { foreach ($invitation['event']['attendees'] as $i => $attendee) {
if ($attendee['role'] == 'ORGANIZER') { if ($attendee['role'] == 'ORGANIZER') {
@ -92,72 +96,82 @@ class calendar_itip extends libcalendaring_itip
} }
else if ($attendee['email'] == $email) { else if ($attendee['email'] == $email) {
// nothing to be done here // nothing to be done here
if ($attendee['status'] == $newstatus) if ($attendee['status'] == $newstatus) {
return true; return true;
}
$invitation['event']['attendees'][$i]['status'] = $newstatus; $invitation['event']['attendees'][$i]['status'] = $newstatus;
$this->sender = $attendee; $this->sender = $attendee;
} }
} }
$invitation['event']['changed'] = new DateTime(); $invitation['event']['changed'] = new DateTime();
// send iTIP REPLY message to organizer // send iTIP REPLY message to organizer
if ($organizer) { if (!empty($organizer)) {
$status = strtolower($newstatus); $status = strtolower($newstatus);
if ($this->send_itip_message($invitation['event'], 'REPLY', $organizer, 'itipsubject' . $status, 'itipmailbody' . $status)) if ($this->send_itip_message($invitation['event'], 'REPLY', $organizer, 'itipsubject' . $status, 'itipmailbody' . $status)) {
$this->rc->output->command('display_message', $this->plugin->gettext(array('name' => 'sentresponseto', 'vars' => array('mailto' => $organizer['name'] ? $organizer['name'] : $organizer['email']))), 'confirmation'); $mailto = !empty($organizer['name']) ? $organizer['name'] : $organizer['email'];
else $message = $this->plugin->gettext([
'name' => 'sentresponseto',
'vars' => ['mailto' => $mailto]
]);
$this->rc->output->command('display_message', $message, 'confirmation');
}
else {
$this->rc->output->command('display_message', $this->plugin->gettext('itipresponseerror'), 'error'); $this->rc->output->command('display_message', $this->plugin->gettext('itipresponseerror'), 'error');
} }
}
// update record in DB // update record in DB
$query = $this->rc->db->query( $query = $this->rc->db->query(
"UPDATE $this->db_itipinvitations "UPDATE $this->db_itipinvitations SET `event` = ? WHERE `token` = ?",
SET `event` = ?
WHERE `token` = ?",
self::serialize_event($invitation['event']), self::serialize_event($invitation['event']),
$invitation['token'] $invitation['token']
); );
if ($this->rc->db->affected_rows($query)) if ($this->rc->db->affected_rows($query)) {
return true; return true;
} }
}
return false; return false;
} }
/** /**
* Create iTIP invitation token for later replies via URL * Create iTIP invitation token for later replies via URL
* *
* @param array Hash array with event properties * @param array $event Hash array with event properties
* @param string Attendee email address * @param string $attendee Attendee email address
*
* @return string Invitation token * @return string Invitation token
*/ */
public function store_invitation($event, $attendee) public function store_invitation($event, $attendee)
{ {
static $stored = array(); static $stored = [];
if (!$event['uid'] || !$attendee) if (empty($event['uid']) || !$attendee) {
return false; return false;
}
// generate token for this invitation // generate token for this invitation
$token = $this->generate_token($event, $attendee); $token = $this->generate_token($event, $attendee);
$base = substr($token, 0, 40); $base = substr($token, 0, 40);
// already stored this // already stored this
if ($stored[$base]) if (!empty($stored[$base])) {
return $token; return $token;
}
// delete old entry // delete old entry
$this->rc->db->query("DELETE FROM $this->db_itipinvitations WHERE `token` = ?", $base); $this->rc->db->query("DELETE FROM $this->db_itipinvitations WHERE `token` = ?", $base);
$event_uid = $event['uid'] . ($event['_instance'] ? '-' . $event['_instance'] : ''); $event_uid = $event['uid'] . (!empty($event['_instance']) ? '-' . $event['_instance'] : '');
$query = $this->rc->db->query( $query = $this->rc->db->query(
"INSERT INTO $this->db_itipinvitations "INSERT INTO $this->db_itipinvitations"
(`token`, `event_uid`, `user_id`, `event`, `expires`) . " (`token`, `event_uid`, `user_id`, `event`, `expires`)"
VALUES(?, ?, ?, ?, ?)", . " VALUES(?, ?, ?, ?, ?)",
$base, $base,
$event_uid, $event_uid,
$this->rc->user->ID, $this->rc->user->ID,
@ -176,17 +190,16 @@ class calendar_itip extends libcalendaring_itip
/** /**
* Mark invitations for the given event as cancelled * Mark invitations for the given event as cancelled
* *
* @param array Hash array with event properties * @param array $event Hash array with event properties
*/ */
public function cancel_itip_invitation($event) public function cancel_itip_invitation($event)
{ {
$event_uid = $event['uid'] . ($event['_instance'] ? '-' . $event['_instance'] : ''); $event_uid = $event['uid'] . (!empty($event['_instance']) ? '-' . $event['_instance'] : '');
// flag invitation record as cancelled // flag invitation record as cancelled
$this->rc->db->query( $this->rc->db->query(
"UPDATE $this->db_itipinvitations "UPDATE $this->db_itipinvitations SET `cancelled` = 1"
SET `cancelled` = 1 . " WHERE `event_uid` = ? AND `user_id` = ?",
WHERE `event_uid` = ? AND `user_id` = ?",
$event_uid, $event_uid,
$this->rc->user->ID $this->rc->user->ID
); );
@ -195,12 +208,12 @@ class calendar_itip extends libcalendaring_itip
/** /**
* Generate an invitation request token for the given event and attendee * Generate an invitation request token for the given event and attendee
* *
* @param array Event hash array * @param array $event Event hash array
* @param string Attendee email address * @param string $attendee Attendee email address
*/ */
public function generate_token($event, $attendee) public function generate_token($event, $attendee)
{ {
$event_uid = $event['uid'] . ($event['_instance'] ? '-' . $event['_instance'] : ''); $event_uid = $event['uid'] . (!empty($event['_instance']) ? '-' . $event['_instance'] : '');
$base = sha1($event_uid . ';' . $this->rc->user->ID); $base = sha1($event_uid . ';' . $this->rc->user->ID);
$mail = base64_encode($attendee); $mail = base64_encode($attendee);
$hash = substr(md5($base . $mail . $this->rc->config->get('des_key')), 0, 6); $hash = substr(md5($base . $mail . $this->rc->config->get('des_key')), 0, 6);
@ -211,7 +224,8 @@ class calendar_itip extends libcalendaring_itip
/** /**
* Decode the given iTIP request token and return its parts * Decode the given iTIP request token and return its parts
* *
* @param string Request token to decode * @param string $token Request token to decode
*
* @return mixed Hash array with parts or False if invalid * @return mixed Hash array with parts or False if invalid
*/ */
public function decode_token($token) public function decode_token($token)
@ -220,7 +234,7 @@ class calendar_itip extends libcalendaring_itip
// validate and return parts // validate and return parts
if ($mail && $hash && $hash == substr(md5($base . $mail . $this->rc->config->get('des_key')), 0, 6)) { if ($mail && $hash && $hash == substr(md5($base . $mail . $this->rc->config->get('des_key')), 0, 6)) {
return array('base' => $base, 'attendee' => base64_decode($mail)); return ['base' => $base, 'attendee' => base64_decode($mail)];
} }
return false; return false;
@ -232,9 +246,13 @@ class calendar_itip extends libcalendaring_itip
private static function serialize_event($event) private static function serialize_event($event)
{ {
$ev = $event; $ev = $event;
if (!empty($ev['description'])) {
$ev['description'] = abbreviate_string($ev['description'], 100); $ev['description'] = abbreviate_string($ev['description'], 100);
unset($ev['attachments']);
return serialize($ev);
} }
unset($ev['attachments']);
return serialize($ev);
}
} }

View file

@ -32,8 +32,8 @@ class calendar_recurrence extends libcalendaring_recurrence
/** /**
* Default constructor * Default constructor
* *
* @param object calendar The calendar plugin instance * @param calendar $cal The calendar plugin instance
* @param array The event object to operate on * @param array $event The event object to operate on
*/ */
function __construct($cal, $event) function __construct($cal, $event)
{ {
@ -41,10 +41,12 @@ class calendar_recurrence extends libcalendaring_recurrence
$this->event = $event; $this->event = $event;
if (is_object($event['start']) && is_object($event['end'])) if (is_object($event['start']) && is_object($event['end'])) {
$this->duration = $event['start']->diff($event['end']); $this->duration = $event['start']->diff($event['end']);
}
$event['start']->_dateonly = !empty($event['allday']);
$event['start']->_dateonly |= $event['allday'];
$this->init($event['recurrence'], $event['start']); $this->init($event['recurrence'], $event['start']);
} }
@ -84,5 +86,4 @@ class calendar_recurrence extends libcalendaring_recurrence
return false; return false;
} }
} }

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff