Sync v3.5.7
This commit is contained in:
parent
3d195270da
commit
e11e26ed24
17 changed files with 13548 additions and 12520 deletions
7312
calendar.php
7312
calendar.php
File diff suppressed because it is too large
Load diff
|
@ -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
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
|
@ -575,21 +580,12 @@ function rcube_calendar_ui(settings)
|
|||
$dialog.parent().find('button:not(.ui-dialog-titlebar-close,.delete)').first().focus();
|
||||
}, 5);
|
||||
},
|
||||
beforeClose: function(e) {
|
||||
rcmail.command('menu-close', 'eventoptionsmenu', null, e);
|
||||
},
|
||||
close: function(e) {
|
||||
$dialog.dialog('destroy').attr('aria-hidden', 'true').hide();
|
||||
$('.libcal-rsvp-replymode').hide();
|
||||
},
|
||||
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();
|
||||
close_func(e);
|
||||
$dialog.dialog('close');
|
||||
},
|
||||
dragStart: close_func,
|
||||
resizeStart: close_func,
|
||||
buttons: buttons,
|
||||
minWidth: 320,
|
||||
width: 420
|
||||
|
@ -611,12 +607,12 @@ function rcube_calendar_ui(settings)
|
|||
.attr({href: '#', 'class': 'dropdown-link btn btn-link options', 'data-popup-pos': 'top'})
|
||||
.text(rcmail.gettext('eventoptions','calendar'))
|
||||
.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'));
|
||||
}
|
||||
|
||||
rcmail.enable_command('event-history', calendar.history)
|
||||
rcmail.enable_command('event-history', calendar.history);
|
||||
|
||||
rcmail.triggerEvent('calendar-event-dialog', {dialog: $dialog});
|
||||
};
|
||||
|
@ -626,12 +622,14 @@ function rcube_calendar_ui(settings)
|
|||
{
|
||||
var cutype = $(this).attr('data-cutype'),
|
||||
mailto = this.href.substr(7);
|
||||
|
||||
if (rcmail.env.calendar_resources && cutype == 'RESOURCE') {
|
||||
event_resources_dialog(mailto);
|
||||
}
|
||||
else {
|
||||
rcmail.command('compose', mailto, e ? e.target : null, e);
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
|
@ -3179,9 +3177,9 @@ function rcube_calendar_ui(settings)
|
|||
if (v.role != 'ORGANIZER') {
|
||||
v.status = 'NEEDS-ACTION';
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
event_edit_dialog('new', copy);
|
||||
setTimeout(function() { event_edit_dialog('new', copy); }, 50);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"description": "Calendar plugin",
|
||||
"homepage": "https://git.kolab.org/diffusion/RPK/",
|
||||
"license": "AGPLv3",
|
||||
"version": "3.5.2",
|
||||
"version": "3.5.7",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Thomas Bruederli",
|
||||
|
@ -24,7 +24,7 @@
|
|||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.0",
|
||||
"php": ">=5.4.0",
|
||||
"roundcube/plugin-installer": ">=0.1.3",
|
||||
"kolab/libcalendaring": ">=3.4.0",
|
||||
"kolab/libkolab": ">=3.4.0"
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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)
|
||||
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;
|
||||
|
|
|
@ -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
|
||||
FROM temp_events;
|
||||
|
||||
DROP TABLE temp_events;
|
||||
|
|
|
@ -136,7 +136,7 @@ class database_driver extends calendar_driver
|
|||
$hidden = array_filter(explode(',', $this->rc->config->get('hidden_calendars', '')));
|
||||
$id = self::BIRTHDAY_CALENDAR_ID;
|
||||
|
||||
if (!$active || !in_array($id, $hidden)) {
|
||||
if (empty($active) || !in_array($id, $hidden)) {
|
||||
$calendars[$id] = array(
|
||||
'id' => $id,
|
||||
'name' => $this->cal->gettext('birthdays'),
|
||||
|
@ -172,7 +172,7 @@ class database_driver extends calendar_driver
|
|||
$this->rc->user->ID,
|
||||
$prop['name'],
|
||||
strval($prop['color']),
|
||||
$prop['showalarms'] ? 1 : 0
|
||||
!empty($prop['showalarms']) ? 1 : 0
|
||||
);
|
||||
|
||||
if ($result) {
|
||||
|
@ -321,24 +321,24 @@ class database_driver extends calendar_driver
|
|||
. " VALUES (?, $now, $now, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
$event['calendar'],
|
||||
strval($event['uid']),
|
||||
intval($event['recurrence_id']),
|
||||
strval($event['_instance']),
|
||||
intval($event['isexception']),
|
||||
isset($event['recurrence_id']) ? intval($event['recurrence_id']) : 0,
|
||||
isset($event['_instance']) ? strval($event['_instance']) : '',
|
||||
isset($event['isexception']) ? intval($event['isexception']) : 0,
|
||||
$event['start']->format(self::DB_DATE_FORMAT),
|
||||
$event['end']->format(self::DB_DATE_FORMAT),
|
||||
intval($event['all_day']),
|
||||
$event['_recurrence'],
|
||||
strval($event['title']),
|
||||
strval($event['description']),
|
||||
strval($event['location']),
|
||||
join(',', (array)$event['categories']),
|
||||
strval($event['url']),
|
||||
isset($event['description']) ? strval($event['description']) : '',
|
||||
isset($event['location']) ? strval($event['location']) : '',
|
||||
isset($event['categories']) ? join(',', (array) $event['categories']) : '',
|
||||
isset($event['url']) ? strval($event['url']) : '',
|
||||
intval($event['free_busy']),
|
||||
intval($event['priority']),
|
||||
intval($event['sensitivity']),
|
||||
strval($event['status']),
|
||||
isset($event['status']) ? strval($event['status']) : '',
|
||||
$event['attendees'],
|
||||
$event['alarms'],
|
||||
isset($event['alarms']) ? $event['alarms'] : null,
|
||||
$event['notifyat']
|
||||
);
|
||||
|
||||
|
@ -381,7 +381,7 @@ class database_driver extends calendar_driver
|
|||
|
||||
// increment sequence number
|
||||
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
|
||||
|
@ -389,11 +389,12 @@ class database_driver extends calendar_driver
|
|||
$master = $old['recurrence_id'] ? $this->get_event(array('id' => $old['recurrence_id'])) : $old;
|
||||
|
||||
// keep saved exceptions (not submitted by the client)
|
||||
if ($old['recurrence']['EXDATE']) {
|
||||
if (!empty($old['recurrence']['EXDATE'])) {
|
||||
$event['recurrence']['EXDATE'] = $old['recurrence']['EXDATE'];
|
||||
}
|
||||
|
||||
switch ($event['_savemode']) {
|
||||
$savemode = isset($event['_savemode']) ? $event['_savemode'] : null;
|
||||
switch ($savemode) {
|
||||
case 'new':
|
||||
$event['uid'] = $this->cal->generate_uid();
|
||||
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
|
||||
foreach (self::$scheduling_properties as $prop) {
|
||||
$a = $old[$prop];
|
||||
$b = $event[$prop];
|
||||
$a = isset($old[$prop]) ? $old[$prop] : null;
|
||||
$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');
|
||||
$b = $b->format('Y-m-d');
|
||||
}
|
||||
|
@ -596,10 +599,10 @@ class database_driver extends calendar_driver
|
|||
$b = array_filter($b);
|
||||
|
||||
// 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']);
|
||||
}
|
||||
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']);
|
||||
}
|
||||
}
|
||||
|
@ -652,24 +655,24 @@ class database_driver extends calendar_driver
|
|||
}
|
||||
|
||||
// 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['free_busy'] = intval($this->free_busy_map[strtolower($event['free_busy'])]);
|
||||
$event['sensitivity'] = intval($this->sensitivity_map[strtolower($event['sensitivity'])]);
|
||||
$event['free_busy'] = isset($this->free_busy_map[$free_busy]) ? $this->free_busy_map[$free_busy] : null;
|
||||
$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') {
|
||||
$event['status'] = 'TENTATIVE';
|
||||
}
|
||||
|
||||
if (isset($event['allday'])) {
|
||||
$event['all_day'] = $event['allday'] ? 1 : 0;
|
||||
}
|
||||
|
||||
// compute absolute time to notify the user
|
||||
$event['notifyat'] = $this->_get_notification($event);
|
||||
|
||||
if (is_array($event['valarms'])) {
|
||||
if (!empty($event['valarms'])) {
|
||||
$event['alarms'] = $this->serialize_alarms($event['valarms']);
|
||||
}
|
||||
|
||||
|
@ -689,7 +692,7 @@ class database_driver extends calendar_driver
|
|||
*/
|
||||
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);
|
||||
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
else if (is_array($event[$col])) {
|
||||
$sql_args[$col] = join(',', $event[$col]);
|
||||
}
|
||||
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'];
|
||||
}
|
||||
|
||||
if ($event['_instance']) {
|
||||
if (!empty($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'];
|
||||
}
|
||||
|
||||
|
@ -763,7 +763,7 @@ class database_driver extends calendar_driver
|
|||
}
|
||||
|
||||
// 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) {
|
||||
$this->remove_attachment($attachment, $event['id']);
|
||||
}
|
||||
|
@ -822,7 +822,7 @@ class database_driver extends calendar_driver
|
|||
|
||||
// skip exceptions
|
||||
// TODO: merge updated data from master event
|
||||
if ($exdata[$datestr]) {
|
||||
if (!empty($exdata[$datestr])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -831,7 +831,7 @@ class database_driver extends calendar_driver
|
|||
$next_end->add($duration);
|
||||
|
||||
$notify_at = $this->_get_notification(array(
|
||||
'alarms' => $event['alarms'],
|
||||
'alarms' => !empty($event['alarms']) ? $event['alarms'] : null,
|
||||
'start' => $next_start,
|
||||
'end' => $next_end,
|
||||
'status' => $event['status']
|
||||
|
@ -860,13 +860,13 @@ class database_driver extends calendar_driver
|
|||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// remove all exceptions after recurrence end
|
||||
if ($next_end && !empty($exceptions)) {
|
||||
if (!empty($next_end) && !empty($exceptions)) {
|
||||
$this->rc->db->query(
|
||||
"DELETE FROM `{$this->db_events}`"
|
||||
. " 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)
|
||||
{
|
||||
$id = is_array($event) ? ($event['id'] ?: $event['uid']) : $event;
|
||||
$cal = is_array($event) ? $event['calendar'] : null;
|
||||
$id = is_array($event) ? (!empty($event['id']) ? $event['id'] : $event['uid']) : $event;
|
||||
$cal = is_array($event) && !empty($event['calendar']) ? $event['calendar'] : null;
|
||||
$col = is_array($event) && is_numeric($id) ? 'event_id' : 'uid';
|
||||
|
||||
if ($this->cache[$id]) {
|
||||
if (!empty($this->cache[$id])) {
|
||||
return $this->cache[$id];
|
||||
}
|
||||
|
||||
|
@ -1039,15 +1039,15 @@ class database_driver extends calendar_driver
|
|||
}
|
||||
|
||||
$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']);
|
||||
}
|
||||
|
||||
if ($scope & self::FILTER_ACTIVE) {
|
||||
$calendars = $this->calendars;
|
||||
foreach ($calendars as $idx => $cal) {
|
||||
if (!$cal['active']) {
|
||||
unset($calendars[$idx]);
|
||||
$calendars = [];
|
||||
foreach ($this->calendars as $idx => $cal) {
|
||||
if (!empty($cal['active'])) {
|
||||
$calendars[] = $idx;
|
||||
}
|
||||
}
|
||||
$cals = join(',', $calendars);
|
||||
|
@ -1099,11 +1099,12 @@ class database_driver extends calendar_driver
|
|||
|
||||
// compose (slow) SQL query for searching
|
||||
// FIXME: improve searching using a dedicated col and normalized values
|
||||
$sql_add = '';
|
||||
if ($query) {
|
||||
foreach (array('title','location','description','categories','attendees') as $col) {
|
||||
$sql_query[] = $this->rc->db->ilike($col, '%'.$query.'%');
|
||||
}
|
||||
$sql_add = " AND (" . join(' OR ', $sql_query) . ")";
|
||||
$sql_add .= " AND (" . join(' OR ', $sql_query) . ")";
|
||||
}
|
||||
|
||||
if (!$virtual) {
|
||||
|
@ -1155,7 +1156,7 @@ class database_driver extends calendar_driver
|
|||
|
||||
// add events from the address books birthday calendar
|
||||
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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -1398,7 +1399,7 @@ class database_driver extends calendar_driver
|
|||
. "SELECT `event_id` FROM `{$this->db_events}`"
|
||||
. " WHERE `event_id` = ? AND `calendar_id` IN ({$this->calendar_ids}))",
|
||||
$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))) {
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -23,389 +23,402 @@
|
|||
|
||||
class kolab_invitation_calendar
|
||||
{
|
||||
public $id = '__invitation__';
|
||||
public $ready = true;
|
||||
public $alarms = false;
|
||||
public $rights = 'lrsv';
|
||||
public $editable = false;
|
||||
public $attachments = false;
|
||||
public $subscriptions = false;
|
||||
public $partstats = array('unknown');
|
||||
public $categories = array();
|
||||
public $name = 'Invitations';
|
||||
public $id = '__invitation__';
|
||||
public $ready = true;
|
||||
public $alarms = false;
|
||||
public $rights = 'lrsv';
|
||||
public $editable = false;
|
||||
public $attachments = false;
|
||||
public $subscriptions = false;
|
||||
public $partstats = ['unknown'];
|
||||
public $categories = [];
|
||||
public $name = 'Invitations';
|
||||
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public function __construct($id, $calendar)
|
||||
{
|
||||
$this->cal = $calendar;
|
||||
$this->id = $id;
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public function __construct($id, $calendar)
|
||||
{
|
||||
$this->cal = $calendar;
|
||||
$this->id = $id;
|
||||
|
||||
switch ($this->id) {
|
||||
case kolab_driver::INVITATIONS_CALENDAR_PENDING:
|
||||
$this->partstats = array('NEEDS-ACTION');
|
||||
$this->name = $this->cal->gettext('invitationspending');
|
||||
if (!empty($_REQUEST['_quickview']))
|
||||
$this->partstats[] = 'TENTATIVE';
|
||||
break;
|
||||
switch ($this->id) {
|
||||
case kolab_driver::INVITATIONS_CALENDAR_PENDING:
|
||||
$this->partstats = ['NEEDS-ACTION'];
|
||||
$this->name = $this->cal->gettext('invitationspending');
|
||||
|
||||
case kolab_driver::INVITATIONS_CALENDAR_DECLINED:
|
||||
$this->partstats = array('DECLINED');
|
||||
$this->name = $this->cal->gettext('invitationsdeclined');
|
||||
break;
|
||||
}
|
||||
|
||||
// user-specific alarms settings win
|
||||
$prefs = $this->cal->rc->config->get('kolab_calendars', array());
|
||||
if (isset($prefs[$this->id]['showalarms']))
|
||||
$this->alarms = $prefs[$this->id]['showalarms'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for a nice and human readable name for this calendar
|
||||
*
|
||||
* @return string Name of this calendar
|
||||
*/
|
||||
public function get_name()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the IMAP folder owner
|
||||
*
|
||||
* @return string Name of the folder owner
|
||||
*/
|
||||
public function get_owner()
|
||||
{
|
||||
return $this->cal->rc->get_user_name();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function get_title()
|
||||
{
|
||||
return $this->get_name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the name of the namespace to which the IMAP folder belongs
|
||||
*
|
||||
* @return string Name of the namespace (personal, other, shared)
|
||||
*/
|
||||
public function get_namespace()
|
||||
{
|
||||
return 'x-special';
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the top-end calendar folder name (not the entire path)
|
||||
*
|
||||
* @return string Name of this calendar
|
||||
*/
|
||||
public function get_foldername()
|
||||
{
|
||||
return $this->get_name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the Cyrus mailbox identifier corresponding to this folder
|
||||
*
|
||||
* @return string Mailbox ID
|
||||
*/
|
||||
public function get_mailbox_id()
|
||||
{
|
||||
// this is a virtual collection and has no concrete mailbox ID
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return color to display this calendar
|
||||
*/
|
||||
public function get_color()
|
||||
{
|
||||
// calendar color is stored in local user prefs
|
||||
$prefs = $this->cal->rc->config->get('kolab_calendars', array());
|
||||
|
||||
if (!empty($prefs[$this->id]) && !empty($prefs[$this->id]['color']))
|
||||
return $prefs[$this->id]['color'];
|
||||
|
||||
return 'ffffff';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose an URL for CalDAV access to this calendar (if configured)
|
||||
*/
|
||||
public function get_caldav_url()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check activation status of this folder
|
||||
*
|
||||
* @return boolean True if enabled, false if not
|
||||
*/
|
||||
public function is_active()
|
||||
{
|
||||
$prefs = $this->cal->rc->config->get('kolab_calendars', array()); // read local prefs
|
||||
return (bool)$prefs[$this->id]['active'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update properties of this calendar folder
|
||||
*
|
||||
* @see calendar_driver::edit_calendar()
|
||||
*/
|
||||
public function update(&$prop)
|
||||
{
|
||||
// don't change anything.
|
||||
// let kolab_driver save props in local prefs
|
||||
return $prop['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for a single event object
|
||||
*/
|
||||
public function get_event($id)
|
||||
{
|
||||
// redirect call to kolab_driver::get_event()
|
||||
$event = $this->cal->driver->get_event($id, calendar_driver::FILTER_WRITEABLE);
|
||||
|
||||
if (is_array($event)) {
|
||||
$event = $this->_mod_event($event, $event['calendar']);
|
||||
}
|
||||
|
||||
return $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create instances of a recurring event
|
||||
*
|
||||
* @see kolab_calendar::get_recurring_events()
|
||||
*/
|
||||
public function get_recurring_events($event, $start, $end = null, $event_id = null, $limit = null)
|
||||
{
|
||||
// forward call to the actual storage folder
|
||||
if ($event['_folder_id']) {
|
||||
$cal = $this->cal->driver->get_calendar($event['_folder_id']);
|
||||
if ($cal && $cal->ready) {
|
||||
return $cal->get_recurring_events($event, $start, $end, $event_id, $limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attachment body
|
||||
*
|
||||
* @see calendar_driver::get_attachment_body()
|
||||
*/
|
||||
public function get_attachment_body($id, $event)
|
||||
{
|
||||
// find the actual folder this event resides in
|
||||
if (!empty($event['_folder_id'])) {
|
||||
$cal = $this->cal->driver->get_calendar($event['_folder_id']);
|
||||
}
|
||||
else {
|
||||
$cal = null;
|
||||
foreach (kolab_storage::list_folders('', '*', 'event', null) as $foldername) {
|
||||
$cal = $this->_get_calendar($foldername);
|
||||
if ($cal->ready && $cal->storage && $cal->get_event($event['id'])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($cal && $cal->storage) {
|
||||
return $cal->get_attachment_body($id, $event);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param integer Event's new start (unix timestamp)
|
||||
* @param integer Event's new end (unix timestamp)
|
||||
* @param string Search query (optional)
|
||||
* @param boolean Include virtual events (optional)
|
||||
* @param array Additional parameters to query storage
|
||||
*
|
||||
* @return array A list of event records
|
||||
*/
|
||||
public function list_events($start, $end, $search = null, $virtual = 1, $query = array())
|
||||
{
|
||||
// get email addresses of the current user
|
||||
$user_emails = $this->cal->get_user_emails();
|
||||
$subquery = array();
|
||||
foreach ($user_emails as $email) {
|
||||
foreach ($this->partstats as $partstat) {
|
||||
$subquery[] = array('tags', '=', 'x-partstat:' . $email . ':' . strtolower($partstat));
|
||||
}
|
||||
}
|
||||
|
||||
// aggregate events from all calendar folders
|
||||
$events = array();
|
||||
foreach (kolab_storage::list_folders('', '*', 'event', null) as $foldername) {
|
||||
$cal = $this->_get_calendar($foldername);
|
||||
if (!$cal || $cal->get_namespace() == 'other')
|
||||
continue;
|
||||
|
||||
foreach ($cal->list_events($start, $end, $search, 1, $query, array(array($subquery, 'OR'))) as $event) {
|
||||
$match = false;
|
||||
|
||||
// post-filter events to match out partstats
|
||||
if (is_array($event['attendees'])) {
|
||||
foreach ($event['attendees'] as $attendee) {
|
||||
if (in_array($attendee['email'], $user_emails) && in_array($attendee['status'], $this->partstats)) {
|
||||
$match = true;
|
||||
break;
|
||||
if (!empty($_REQUEST['_quickview'])) {
|
||||
$this->partstats[] = 'TENTATIVE';
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case kolab_driver::INVITATIONS_CALENDAR_DECLINED:
|
||||
$this->partstats = ['DECLINED'];
|
||||
$this->name = $this->cal->gettext('invitationsdeclined');
|
||||
break;
|
||||
}
|
||||
|
||||
if ($match) {
|
||||
$events[$event['id'] ?: $event['uid']] = $this->_mod_event($event, $cal->id);
|
||||
// user-specific alarms settings win
|
||||
$prefs = $this->cal->rc->config->get('kolab_calendars', []);
|
||||
if (isset($prefs[$this->id]['showalarms'])) {
|
||||
$this->alarms = $prefs[$this->id]['showalarms'];
|
||||
}
|
||||
}
|
||||
|
||||
// merge list of event categories (really?)
|
||||
$this->categories += $cal->categories;
|
||||
}
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of events in the given calendar
|
||||
*
|
||||
* @param integer Date range start (unix timestamp)
|
||||
* @param integer Date range end (unix timestamp)
|
||||
* @param array Additional query to filter events
|
||||
*
|
||||
* @return integer Count
|
||||
*/
|
||||
public function count_events($start, $end = null, $filter = null)
|
||||
{
|
||||
// get email addresses of the current user
|
||||
$user_emails = $this->cal->get_user_emails();
|
||||
$subquery = array();
|
||||
foreach ($user_emails as $email) {
|
||||
foreach ($this->partstats as $partstat) {
|
||||
$subquery[] = array('tags', '=', 'x-partstat:' . $email . ':' . strtolower($partstat));
|
||||
}
|
||||
/**
|
||||
* Getter for a nice and human readable name for this calendar
|
||||
*
|
||||
* @return string Name of this calendar
|
||||
*/
|
||||
public function get_name()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
$filter = array(
|
||||
array('tags','!=','x-status:cancelled'),
|
||||
array($subquery, 'OR')
|
||||
);
|
||||
|
||||
// aggregate counts from all calendar folders
|
||||
$count = 0;
|
||||
foreach (kolab_storage::list_folders('', '*', 'event', null) as $foldername) {
|
||||
$cal = $this->_get_calendar($foldername);
|
||||
if (!$cal || $cal->get_namespace() == 'other')
|
||||
continue;
|
||||
|
||||
$count += $cal->count_events($start, $end, $filter);
|
||||
/**
|
||||
* Getter for the IMAP folder owner
|
||||
*
|
||||
* @return string Name of the folder owner
|
||||
*/
|
||||
public function get_owner()
|
||||
{
|
||||
return $this->cal->rc->get_user_name();
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get calendar object instance (that maybe already initialized)
|
||||
*/
|
||||
private function _get_calendar($folder_name)
|
||||
{
|
||||
$id = kolab_storage::folder_id($folder_name, true);
|
||||
return $this->cal->driver->get_calendar($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to modify some event properties
|
||||
*/
|
||||
private function _mod_event($event, $calendar_id = null)
|
||||
{
|
||||
// set classes according to PARTSTAT
|
||||
$event = kolab_driver::add_partstat_class($event, $this->partstats);
|
||||
|
||||
if (strpos($event['className'], 'fc-invitation-') !== false) {
|
||||
$event['calendar'] = $this->id;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function get_title()
|
||||
{
|
||||
return $this->get_name();
|
||||
}
|
||||
|
||||
// add pointer to original calendar folder
|
||||
if ($calendar_id) {
|
||||
$event['_folder_id'] = $calendar_id;
|
||||
/**
|
||||
* Getter for the name of the namespace to which the IMAP folder belongs
|
||||
*
|
||||
* @return string Name of the namespace (personal, other, shared)
|
||||
*/
|
||||
public function get_namespace()
|
||||
{
|
||||
return 'x-special';
|
||||
}
|
||||
|
||||
return $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new event record
|
||||
*
|
||||
* @see kolab_calendar::insert_event()
|
||||
*/
|
||||
public function insert_event($event)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a specific event record
|
||||
*
|
||||
* @see kolab_calendar::update_event()
|
||||
*/
|
||||
public function update_event($event, $exception_id = null)
|
||||
{
|
||||
// forward call to the actual storage folder
|
||||
if ($event['_folder_id']) {
|
||||
$cal = $this->cal->driver->get_calendar($event['_folder_id']);
|
||||
if ($cal && $cal->ready) {
|
||||
return $cal->update_event($event, $exception_id);
|
||||
}
|
||||
/**
|
||||
* Getter for the top-end calendar folder name (not the entire path)
|
||||
*
|
||||
* @return string Name of this calendar
|
||||
*/
|
||||
public function get_foldername()
|
||||
{
|
||||
return $this->get_name();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an event record
|
||||
*
|
||||
* @see kolab_calendar::delete_event()
|
||||
*/
|
||||
public function delete_event($event, $force = true)
|
||||
{
|
||||
// forward call to the actual storage folder
|
||||
if ($event['_folder_id']) {
|
||||
$cal = $this->cal->driver->get_calendar($event['_folder_id']);
|
||||
if ($cal && $cal->ready) {
|
||||
return $cal->delete_event($event, $force);
|
||||
}
|
||||
/**
|
||||
* Getter for the Cyrus mailbox identifier corresponding to this folder
|
||||
*
|
||||
* @return string Mailbox ID
|
||||
*/
|
||||
public function get_mailbox_id()
|
||||
{
|
||||
// this is a virtual collection and has no concrete mailbox ID
|
||||
return null;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Return color to display this calendar
|
||||
*/
|
||||
public function get_color()
|
||||
{
|
||||
// calendar color is stored in local user prefs
|
||||
$prefs = $this->cal->rc->config->get('kolab_calendars', []);
|
||||
|
||||
/**
|
||||
* Restore deleted event record
|
||||
*
|
||||
* @see kolab_calendar::restore_event()
|
||||
*/
|
||||
public function restore_event($event)
|
||||
{
|
||||
// forward call to the actual storage folder
|
||||
if ($event['_folder_id']) {
|
||||
$cal = $this->cal->driver->get_calendar($event['_folder_id']);
|
||||
if ($cal && $cal->ready) {
|
||||
return $cal->restore_event($event);
|
||||
}
|
||||
if (!empty($prefs[$this->id]) && !empty($prefs[$this->id]['color'])) {
|
||||
return $prefs[$this->id]['color'];
|
||||
}
|
||||
|
||||
return 'ffffff';
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Compose an URL for CalDAV access to this calendar (if configured)
|
||||
*/
|
||||
public function get_caldav_url()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check activation status of this folder
|
||||
*
|
||||
* @return bool True if enabled, false if not
|
||||
*/
|
||||
public function is_active()
|
||||
{
|
||||
$prefs = $this->cal->rc->config->get('kolab_calendars', []); // read local prefs
|
||||
return !empty($prefs[$this->id]['active']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update properties of this calendar folder
|
||||
*
|
||||
* @see calendar_driver::edit_calendar()
|
||||
*/
|
||||
public function update(&$prop)
|
||||
{
|
||||
// don't change anything.
|
||||
// let kolab_driver save props in local prefs
|
||||
return $prop['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for a single event object
|
||||
*/
|
||||
public function get_event($id)
|
||||
{
|
||||
// redirect call to kolab_driver::get_event()
|
||||
$event = $this->cal->driver->get_event($id, calendar_driver::FILTER_WRITEABLE);
|
||||
|
||||
if (is_array($event)) {
|
||||
$event = $this->_mod_event($event, $event['calendar']);
|
||||
}
|
||||
|
||||
return $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create instances of a recurring event
|
||||
*
|
||||
* @see kolab_calendar::get_recurring_events()
|
||||
*/
|
||||
public function get_recurring_events($event, $start, $end = null, $event_id = null, $limit = null)
|
||||
{
|
||||
// forward call to the actual storage folder
|
||||
if (!empty($event['_folder_id'])) {
|
||||
$cal = $this->cal->driver->get_calendar($event['_folder_id']);
|
||||
if ($cal && $cal->ready) {
|
||||
return $cal->get_recurring_events($event, $start, $end, $event_id, $limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attachment body
|
||||
*
|
||||
* @see calendar_driver::get_attachment_body()
|
||||
*/
|
||||
public function get_attachment_body($id, $event)
|
||||
{
|
||||
// find the actual folder this event resides in
|
||||
if (!empty($event['_folder_id'])) {
|
||||
$cal = $this->cal->driver->get_calendar($event['_folder_id']);
|
||||
}
|
||||
else {
|
||||
$cal = null;
|
||||
foreach (kolab_storage::list_folders('', '*', 'event', null) as $foldername) {
|
||||
$cal = $this->_get_calendar($foldername);
|
||||
if ($cal->ready && $cal->storage && $cal->get_event($event['id'])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($cal && $cal->storage) {
|
||||
return $cal->get_attachment_body($id, $event);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int Event's new start (unix timestamp)
|
||||
* @param int Event's new end (unix timestamp)
|
||||
* @param string Search query (optional)
|
||||
* @param bool Include virtual events (optional)
|
||||
* @param array Additional parameters to query storage
|
||||
*
|
||||
* @return array A list of event records
|
||||
*/
|
||||
public function list_events($start, $end, $search = null, $virtual = 1, $query = [])
|
||||
{
|
||||
// get email addresses of the current user
|
||||
$user_emails = $this->cal->get_user_emails();
|
||||
$subquery = [];
|
||||
|
||||
foreach ($user_emails as $email) {
|
||||
foreach ($this->partstats as $partstat) {
|
||||
$subquery[] = ['tags', '=', 'x-partstat:' . $email . ':' . strtolower($partstat)];
|
||||
}
|
||||
}
|
||||
|
||||
$events = [];
|
||||
|
||||
// aggregate events from all calendar folders
|
||||
foreach (kolab_storage::list_folders('', '*', 'event', null) as $foldername) {
|
||||
$cal = $this->_get_calendar($foldername);
|
||||
if (!$cal || $cal->get_namespace() == 'other') {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($cal->list_events($start, $end, $search, 1, $query, [[$subquery, 'OR']]) as $event) {
|
||||
$match = false;
|
||||
|
||||
// post-filter events to match out partstats
|
||||
if (!empty($event['attendees'])) {
|
||||
foreach ($event['attendees'] as $attendee) {
|
||||
if (
|
||||
in_array($attendee['email'], $user_emails)
|
||||
&& in_array($attendee['status'], $this->partstats)
|
||||
) {
|
||||
$match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($match) {
|
||||
$uid = !empty($event['id']) ? $event['id'] : $event['uid'];
|
||||
$events[$uid] = $this->_mod_event($event, $cal->id);
|
||||
}
|
||||
}
|
||||
|
||||
// merge list of event categories (really?)
|
||||
$this->categories += $cal->categories;
|
||||
}
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of events in the given calendar
|
||||
*
|
||||
* @param int Date range start (unix timestamp)
|
||||
* @param int Date range end (unix timestamp)
|
||||
* @param array Additional query to filter events
|
||||
*
|
||||
* @return int Count
|
||||
*/
|
||||
public function count_events($start, $end = null, $filter = null)
|
||||
{
|
||||
// get email addresses of the current user
|
||||
$user_emails = $this->cal->get_user_emails();
|
||||
$subquery = [];
|
||||
|
||||
foreach ($user_emails as $email) {
|
||||
foreach ($this->partstats as $partstat) {
|
||||
$subquery[] = ['tags', '=', 'x-partstat:' . $email . ':' . strtolower($partstat)];
|
||||
}
|
||||
}
|
||||
|
||||
$filter = [
|
||||
['tags', '!=', 'x-status:cancelled'],
|
||||
[$subquery, 'OR']
|
||||
];
|
||||
|
||||
// aggregate counts from all calendar folders
|
||||
$count = 0;
|
||||
foreach (kolab_storage::list_folders('', '*', 'event', null) as $foldername) {
|
||||
$cal = $this->_get_calendar($foldername);
|
||||
if (!$cal || $cal->get_namespace() == 'other') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$count += $cal->count_events($start, $end, $filter);
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get calendar object instance (that maybe already initialized)
|
||||
*/
|
||||
private function _get_calendar($folder_name)
|
||||
{
|
||||
$id = kolab_storage::folder_id($folder_name, true);
|
||||
return $this->cal->driver->get_calendar($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to modify some event properties
|
||||
*/
|
||||
private function _mod_event($event, $calendar_id = null)
|
||||
{
|
||||
// set classes according to PARTSTAT
|
||||
$event = kolab_driver::add_partstat_class($event, $this->partstats);
|
||||
|
||||
if (strpos($event['className'], 'fc-invitation-') !== false) {
|
||||
$event['calendar'] = $this->id;
|
||||
}
|
||||
|
||||
// add pointer to original calendar folder
|
||||
if ($calendar_id) {
|
||||
$event['_folder_id'] = $calendar_id;
|
||||
}
|
||||
|
||||
return $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new event record
|
||||
*
|
||||
* @see kolab_calendar::insert_event()
|
||||
*/
|
||||
public function insert_event($event)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a specific event record
|
||||
*
|
||||
* @see kolab_calendar::update_event()
|
||||
*/
|
||||
public function update_event($event, $exception_id = null)
|
||||
{
|
||||
// forward call to the actual storage folder
|
||||
if (!empty($event['_folder_id'])) {
|
||||
$cal = $this->cal->driver->get_calendar($event['_folder_id']);
|
||||
if ($cal && $cal->ready) {
|
||||
return $cal->update_event($event, $exception_id);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an event record
|
||||
*
|
||||
* @see kolab_calendar::delete_event()
|
||||
*/
|
||||
public function delete_event($event, $force = true)
|
||||
{
|
||||
// forward call to the actual storage folder
|
||||
if (!empty($event['_folder_id'])) {
|
||||
$cal = $this->cal->driver->get_calendar($event['_folder_id']);
|
||||
if ($cal && $cal->ready) {
|
||||
return $cal->delete_event($event, $force);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore deleted event record
|
||||
*
|
||||
* @see kolab_calendar::restore_event()
|
||||
*/
|
||||
public function restore_event($event)
|
||||
{
|
||||
// forward call to the actual storage folder
|
||||
if (!empty($event['_folder_id'])) {
|
||||
$cal = $this->cal->driver->get_calendar($event['_folder_id']);
|
||||
if ($cal && $cal->ready) {
|
||||
return $cal->restore_event($event);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,402 +23,423 @@
|
|||
|
||||
class kolab_user_calendar extends kolab_calendar
|
||||
{
|
||||
public $id = 'unknown';
|
||||
public $ready = false;
|
||||
public $editable = false;
|
||||
public $attachments = false;
|
||||
public $subscriptions = false;
|
||||
public $id = 'unknown';
|
||||
public $ready = false;
|
||||
public $editable = false;
|
||||
public $attachments = false;
|
||||
public $subscriptions = false;
|
||||
|
||||
protected $userdata = array();
|
||||
protected $timeindex = array();
|
||||
protected $userdata = [];
|
||||
protected $timeindex = [];
|
||||
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public function __construct($user_or_folder, $calendar)
|
||||
{
|
||||
$this->cal = $calendar;
|
||||
$this->imap = $calendar->rc->get_storage();
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public function __construct($user_or_folder, $calendar)
|
||||
{
|
||||
$this->cal = $calendar;
|
||||
$this->imap = $calendar->rc->get_storage();
|
||||
|
||||
// full user record is provided
|
||||
if (is_array($user_or_folder)) {
|
||||
$this->userdata = $user_or_folder;
|
||||
$this->storage = new kolab_storage_folder_user($this->userdata['kolabtargetfolder'], '', $this->userdata);
|
||||
}
|
||||
else if ($user_or_folder instanceof kolab_storage_folder_user) {
|
||||
$this->storage = $user_or_folder;
|
||||
$this->userdata = $this->storage->ldaprec;
|
||||
}
|
||||
else { // get user record from LDAP
|
||||
$this->storage = new kolab_storage_folder_user($user_or_folder);
|
||||
$this->userdata = $this->storage->ldaprec;
|
||||
}
|
||||
|
||||
$this->ready = !empty($this->userdata['kolabtargetfolder']);
|
||||
$this->storage->type = 'event';
|
||||
|
||||
if ($this->ready) {
|
||||
// ID is derrived from the user's kolabtargetfolder attribute
|
||||
$this->id = kolab_storage::folder_id($this->userdata['kolabtargetfolder'], true);
|
||||
$this->imap_folder = $this->userdata['kolabtargetfolder'];
|
||||
$this->name = $this->storage->name;
|
||||
$this->parent = ''; // user calendars are top level
|
||||
|
||||
// user-specific alarms settings win
|
||||
$prefs = $this->cal->rc->config->get('kolab_calendars', array());
|
||||
if (isset($prefs[$this->id]['showalarms']))
|
||||
$this->alarms = $prefs[$this->id]['showalarms'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for a nice and human readable name for this calendar
|
||||
*
|
||||
* @return string Name of this calendar
|
||||
*/
|
||||
public function get_name()
|
||||
{
|
||||
return $this->userdata['displayname'] ?: ($this->userdata['name'] ?: $this->userdata['mail']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the IMAP folder owner
|
||||
*
|
||||
* @param bool Return a fully qualified owner name (unused)
|
||||
*
|
||||
* @return string Name of the folder owner
|
||||
*/
|
||||
public function get_owner($fully_qualified = false)
|
||||
{
|
||||
return $this->userdata['mail'];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function get_title()
|
||||
{
|
||||
return trim($this->userdata['displayname'] . '; ' . $this->userdata['mail'], '; ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the name of the namespace to which the IMAP folder belongs
|
||||
*
|
||||
* @return string Name of the namespace (personal, other, shared)
|
||||
*/
|
||||
public function get_namespace()
|
||||
{
|
||||
return 'other user';
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the top-end calendar folder name (not the entire path)
|
||||
*
|
||||
* @return string Name of this calendar
|
||||
*/
|
||||
public function get_foldername()
|
||||
{
|
||||
return $this->get_name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return color to display this calendar
|
||||
*/
|
||||
public function get_color($default = null)
|
||||
{
|
||||
// calendar color is stored in local user prefs
|
||||
$prefs = $this->cal->rc->config->get('kolab_calendars', array());
|
||||
|
||||
if (!empty($prefs[$this->id]) && !empty($prefs[$this->id]['color']))
|
||||
return $prefs[$this->id]['color'];
|
||||
|
||||
return $default ?: 'cc0000';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose an URL for CalDAV access to this calendar (if configured)
|
||||
*/
|
||||
public function get_caldav_url()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check subscription status of this folder
|
||||
*
|
||||
* @return boolean True if subscribed, false if not
|
||||
*/
|
||||
public function is_subscribed()
|
||||
{
|
||||
return $this->storage->is_subscribed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update properties of this calendar folder
|
||||
*
|
||||
* @see calendar_driver::edit_calendar()
|
||||
*/
|
||||
public function update(&$prop)
|
||||
{
|
||||
// don't change anything.
|
||||
// let kolab_driver save props in local prefs
|
||||
return $prop['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for a single event object
|
||||
*/
|
||||
public function get_event($id)
|
||||
{
|
||||
// TODO: implement this
|
||||
return $this->events[$id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attachment body
|
||||
* @see calendar_driver::get_attachment_body()
|
||||
*/
|
||||
public function get_attachment_body($id, $event)
|
||||
{
|
||||
if (!$event['calendar'] && ($ev = $this->get_event($event['id']))) {
|
||||
$event['calendar'] = $ev['calendar'];
|
||||
}
|
||||
|
||||
if ($event['calendar'] && ($cal = $this->cal->get_calendar($event['calendar']))) {
|
||||
return $cal->get_attachment_body($id, $event);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param integer Event's new start (unix timestamp)
|
||||
* @param integer Event's new end (unix timestamp)
|
||||
* @param string Search query (optional)
|
||||
* @param boolean Include virtual events (optional)
|
||||
* @param array Additional parameters to query storage
|
||||
* @param array Additional query to filter events
|
||||
*
|
||||
* @return array A list of event records
|
||||
*/
|
||||
public function list_events($start, $end, $search = null, $virtual = 1, $query = array(), $filter_query = null)
|
||||
{
|
||||
// convert to DateTime for comparisons
|
||||
try {
|
||||
$start_dt = new DateTime('@'.$start);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$start_dt = new DateTime('@0');
|
||||
}
|
||||
try {
|
||||
$end_dt = new DateTime('@'.$end);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$end_dt = new DateTime('today +10 years');
|
||||
}
|
||||
|
||||
$limit_changed = null;
|
||||
if (!empty($query)) {
|
||||
foreach ($query as $q) {
|
||||
if ($q[0] == 'changed' && $q[1] == '>=') {
|
||||
try { $limit_changed = new DateTime('@'.$q[2]); }
|
||||
catch (Exception $e) { /* ignore */ }
|
||||
// full user record is provided
|
||||
if (is_array($user_or_folder)) {
|
||||
$this->userdata = $user_or_folder;
|
||||
$this->storage = new kolab_storage_folder_user($this->userdata['kolabtargetfolder'], '', $this->userdata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// aggregate all calendar folders the user shares (but are not activated)
|
||||
foreach (kolab_storage::list_user_folders($this->userdata, 'event', 2) as $foldername) {
|
||||
$cal = new kolab_calendar($foldername, $this->cal);
|
||||
foreach ($cal->list_events($start, $end, $search, 1) as $event) {
|
||||
$uid = $event['id'] ?: $event['uid'];
|
||||
$this->events[$uid] = $event;
|
||||
$this->timeindex[$this->time_key($event)] = $uid;
|
||||
}
|
||||
}
|
||||
|
||||
// get events from the user's free/busy feed (for quickview only)
|
||||
$fbview = $this->cal->rc->config->get('calendar_include_freebusy_data', 1);
|
||||
if ($fbview && ($fbview == 1 || !empty($_REQUEST['_quickview'])) && empty($search)) {
|
||||
$this->fetch_freebusy($limit_changed);
|
||||
}
|
||||
|
||||
$events = array();
|
||||
foreach ($this->events as $event) {
|
||||
// list events in requested time window
|
||||
if ($event['start'] <= $end_dt && $event['end'] >= $start_dt &&
|
||||
(!$limit_changed || !$event['changed'] || $event['changed'] >= $limit_changed)) {
|
||||
$events[] = $event;
|
||||
}
|
||||
}
|
||||
|
||||
// avoid session race conditions that will loose temporary subscriptions
|
||||
$this->cal->rc->session->nowrite = true;
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of events in the given calendar
|
||||
*
|
||||
* @param integer Date range start (unix timestamp)
|
||||
* @param integer Date range end (unix timestamp)
|
||||
* @param array Additional query to filter events
|
||||
*
|
||||
* @return integer Count
|
||||
*/
|
||||
public function count_events($start, $end = null, $filter_query = null)
|
||||
{
|
||||
// not implemented
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to fetch free/busy data for the user and turn it into calendar data
|
||||
*/
|
||||
private function fetch_freebusy($limit_changed = null)
|
||||
{
|
||||
// ask kolab server first
|
||||
try {
|
||||
$request_config = array(
|
||||
'store_body' => true,
|
||||
'follow_redirects' => true,
|
||||
);
|
||||
$request = libkolab::http_request(kolab_storage::get_freebusy_url($this->userdata['mail']), 'GET', $request_config);
|
||||
$response = $request->send();
|
||||
|
||||
// authentication required
|
||||
if ($response->getStatus() == 401) {
|
||||
$request->setAuth($this->cal->rc->user->get_username(), $this->cal->rc->decrypt($_SESSION['password']));
|
||||
$response = $request->send();
|
||||
}
|
||||
|
||||
if ($response->getStatus() == 200)
|
||||
$fbdata = $response->getBody();
|
||||
|
||||
unset($request, $response);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
rcube::raise_error(array(
|
||||
'code' => 900,
|
||||
'type' => 'php',
|
||||
'file' => __FILE__,
|
||||
'line' => __LINE__,
|
||||
'message' => "Error fetching free/busy information: " . $e->getMessage()),
|
||||
true, false);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$statusmap = array(
|
||||
'FREE' => 'free',
|
||||
'BUSY' => 'busy',
|
||||
'BUSY-TENTATIVE' => 'tentative',
|
||||
'X-OUT-OF-OFFICE' => 'outofoffice',
|
||||
'OOF' => 'outofoffice',
|
||||
);
|
||||
$titlemap = array(
|
||||
'FREE' => $this->cal->gettext('availfree'),
|
||||
'BUSY' => $this->cal->gettext('availbusy'),
|
||||
'BUSY-TENTATIVE' => $this->cal->gettext('availtentative'),
|
||||
'X-OUT-OF-OFFICE' => $this->cal->gettext('availoutofoffice'),
|
||||
);
|
||||
|
||||
// rcube::console('_fetch_freebusy', kolab_storage::get_freebusy_url($this->userdata['mail']), $fbdata);
|
||||
|
||||
// parse free-busy information
|
||||
$count = 0;
|
||||
if ($fbdata) {
|
||||
$ical = $this->cal->get_ical();
|
||||
$ical->import($fbdata);
|
||||
if ($fb = $ical->freebusy) {
|
||||
// consider 'changed >= X' queries
|
||||
if ($limit_changed && $fb['created'] && $fb['created'] < $limit_changed) {
|
||||
return 0;
|
||||
else if ($user_or_folder instanceof kolab_storage_folder_user) {
|
||||
$this->storage = $user_or_folder;
|
||||
$this->userdata = $this->storage->ldaprec;
|
||||
}
|
||||
else {
|
||||
// get user record from LDAP
|
||||
$this->storage = new kolab_storage_folder_user($user_or_folder);
|
||||
$this->userdata = $this->storage->ldaprec;
|
||||
}
|
||||
|
||||
foreach ($fb['periods'] as $tuple) {
|
||||
list($from, $to, $type) = $tuple;
|
||||
$event = array(
|
||||
'uid' => md5($this->id . $from->format('U') . '/' . $to->format('U')),
|
||||
'calendar' => $this->id,
|
||||
'changed' => $fb['created'] ?: new DateTime(),
|
||||
'title' => $this->get_name() . ' ' . ($titlemap[$type] ?: $type),
|
||||
'start' => $from,
|
||||
'end' => $to,
|
||||
'free_busy' => $statusmap[$type] ?: 'busy',
|
||||
'className' => 'fc-type-freebusy',
|
||||
'organizer' => array(
|
||||
'email' => $this->userdata['mail'],
|
||||
'name' => $this->userdata['displayname'],
|
||||
),
|
||||
);
|
||||
$this->ready = !empty($this->userdata['kolabtargetfolder']);
|
||||
$this->storage->type = 'event';
|
||||
|
||||
// avoid duplicate entries
|
||||
$key = $this->time_key($event);
|
||||
if (!$this->timeindex[$key]) {
|
||||
$this->events[$event['uid']] = $event;
|
||||
$this->timeindex[$key] = $event['uid'];
|
||||
$count++;
|
||||
}
|
||||
if ($this->ready) {
|
||||
// ID is derrived from the user's kolabtargetfolder attribute
|
||||
$this->id = kolab_storage::folder_id($this->userdata['kolabtargetfolder'], true);
|
||||
$this->imap_folder = $this->userdata['kolabtargetfolder'];
|
||||
$this->name = $this->storage->name;
|
||||
$this->parent = ''; // user calendars are top level
|
||||
|
||||
// user-specific alarms settings win
|
||||
$prefs = $this->cal->rc->config->get('kolab_calendars', []);
|
||||
if (isset($prefs[$this->id]['showalarms'])) {
|
||||
$this->alarms = $prefs[$this->id]['showalarms'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
/**
|
||||
* Getter for a nice and human readable name for this calendar
|
||||
*
|
||||
* @return string Name of this calendar
|
||||
*/
|
||||
public function get_name()
|
||||
{
|
||||
if (!empty($this->userdata['displayname'])) {
|
||||
return $this->userdata['displayname'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to build a key for the absolute time slot the given event convers
|
||||
*/
|
||||
private function time_key($event)
|
||||
{
|
||||
return sprintf('%s/%s', $event['start']->format('U'), is_object($event['end']) ? $event['end']->format('U') : '0');
|
||||
}
|
||||
return !empty($this->userdata['name']) ? $this->userdata['name'] : $this->userdata['mail'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new event record
|
||||
*
|
||||
* @see calendar_driver::new_event()
|
||||
*
|
||||
* @return mixed The created record ID on success, False on error
|
||||
*/
|
||||
public function insert_event($event)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Getter for the IMAP folder owner
|
||||
*
|
||||
* @param bool Return a fully qualified owner name (unused)
|
||||
*
|
||||
* @return string Name of the folder owner
|
||||
*/
|
||||
public function get_owner($fully_qualified = false)
|
||||
{
|
||||
return $this->userdata['mail'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a specific event record
|
||||
*
|
||||
* @see calendar_driver::new_event()
|
||||
* @return boolean True on success, False on error
|
||||
*/
|
||||
public function update_event($event, $exception_id = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function get_title()
|
||||
{
|
||||
$title = [];
|
||||
|
||||
/**
|
||||
* Delete an event record
|
||||
*
|
||||
* @see calendar_driver::remove_event()
|
||||
* @return boolean True on success, False on error
|
||||
*/
|
||||
public function delete_event($event, $force = true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!empty($this->userdata['displayname'])) {
|
||||
$title[] = $this->userdata['displayname'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore deleted event record
|
||||
*
|
||||
* @see calendar_driver::undelete_event()
|
||||
* @return boolean True on success, False on error
|
||||
*/
|
||||
public function restore_event($event)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
$title[] = $this->userdata['mail'];
|
||||
|
||||
return implode('; ', $title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the name of the namespace to which the IMAP folder belongs
|
||||
*
|
||||
* @return string Name of the namespace (personal, other, shared)
|
||||
*/
|
||||
public function get_namespace()
|
||||
{
|
||||
return 'other user';
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the top-end calendar folder name (not the entire path)
|
||||
*
|
||||
* @return string Name of this calendar
|
||||
*/
|
||||
public function get_foldername()
|
||||
{
|
||||
return $this->get_name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return color to display this calendar
|
||||
*/
|
||||
public function get_color($default = null)
|
||||
{
|
||||
// calendar color is stored in local user prefs
|
||||
$prefs = $this->cal->rc->config->get('kolab_calendars', []);
|
||||
|
||||
if (!empty($prefs[$this->id]) && !empty($prefs[$this->id]['color'])) {
|
||||
return $prefs[$this->id]['color'];
|
||||
}
|
||||
|
||||
return $default ?: 'cc0000';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose an URL for CalDAV access to this calendar (if configured)
|
||||
*/
|
||||
public function get_caldav_url()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check subscription status of this folder
|
||||
*
|
||||
* @return boolean True if subscribed, false if not
|
||||
*/
|
||||
public function is_subscribed()
|
||||
{
|
||||
return $this->storage->is_subscribed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update properties of this calendar folder
|
||||
*
|
||||
* @see calendar_driver::edit_calendar()
|
||||
*/
|
||||
public function update(&$prop)
|
||||
{
|
||||
// don't change anything.
|
||||
// let kolab_driver save props in local prefs
|
||||
return $prop['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for a single event object
|
||||
*/
|
||||
public function get_event($id)
|
||||
{
|
||||
// TODO: implement this
|
||||
return isset($this->events[$id]) ? $this->events[$id] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attachment body
|
||||
* @see calendar_driver::get_attachment_body()
|
||||
*/
|
||||
public function get_attachment_body($id, $event)
|
||||
{
|
||||
if (empty($event['calendar']) && ($ev = $this->get_event($event['id']))) {
|
||||
$event['calendar'] = $ev['calendar'];
|
||||
}
|
||||
|
||||
if (!empty($event['calendar']) && ($cal = $this->cal->get_calendar($event['calendar']))) {
|
||||
return $cal->get_attachment_body($id, $event);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int Event's new start (unix timestamp)
|
||||
* @param int Event's new end (unix timestamp)
|
||||
* @param string Search query (optional)
|
||||
* @param bool Include virtual events (optional)
|
||||
* @param array Additional parameters to query storage
|
||||
* @param array Additional query to filter events
|
||||
*
|
||||
* @return array A list of event records
|
||||
*/
|
||||
public function list_events($start, $end, $search = null, $virtual = 1, $query = [], $filter_query = null)
|
||||
{
|
||||
// convert to DateTime for comparisons
|
||||
try {
|
||||
$start_dt = new DateTime('@'.$start);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$start_dt = new DateTime('@0');
|
||||
}
|
||||
try {
|
||||
$end_dt = new DateTime('@'.$end);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$end_dt = new DateTime('today +10 years');
|
||||
}
|
||||
|
||||
$limit_changed = null;
|
||||
|
||||
if (!empty($query)) {
|
||||
foreach ($query as $q) {
|
||||
if ($q[0] == 'changed' && $q[1] == '>=') {
|
||||
try { $limit_changed = new DateTime('@'.$q[2]); }
|
||||
catch (Exception $e) { /* ignore */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// aggregate all calendar folders the user shares (but are not activated)
|
||||
foreach (kolab_storage::list_user_folders($this->userdata, 'event', 2) as $foldername) {
|
||||
$cal = new kolab_calendar($foldername, $this->cal);
|
||||
foreach ($cal->list_events($start, $end, $search, 1) as $event) {
|
||||
$uid = !empty($event['id']) ? $event['id'] : $event['uid'];
|
||||
$this->events[$uid] = $event;
|
||||
$this->timeindex[$this->time_key($event)] = $uid;
|
||||
}
|
||||
}
|
||||
|
||||
// get events from the user's free/busy feed (for quickview only)
|
||||
$fbview = $this->cal->rc->config->get('calendar_include_freebusy_data', 1);
|
||||
if ($fbview && ($fbview == 1 || !empty($_REQUEST['_quickview'])) && empty($search)) {
|
||||
$this->fetch_freebusy($limit_changed);
|
||||
}
|
||||
|
||||
$events = [];
|
||||
foreach ($this->events as $event) {
|
||||
// list events in requested time window
|
||||
if (
|
||||
$event['start'] <= $end_dt
|
||||
&& $event['end'] >= $start_dt
|
||||
&& (!$limit_changed || empty($event['changed']) || $event['changed'] >= $limit_changed)
|
||||
) {
|
||||
$events[] = $event;
|
||||
}
|
||||
}
|
||||
|
||||
// avoid session race conditions that will loose temporary subscriptions
|
||||
$this->cal->rc->session->nowrite = true;
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of events in the given calendar
|
||||
*
|
||||
* @param int Date range start (unix timestamp)
|
||||
* @param int Date range end (unix timestamp)
|
||||
* @param array Additional query to filter events
|
||||
*
|
||||
* @return integer Count
|
||||
*/
|
||||
public function count_events($start, $end = null, $filter_query = null)
|
||||
{
|
||||
// not implemented
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to fetch free/busy data for the user and turn it into calendar data
|
||||
*/
|
||||
private function fetch_freebusy($limit_changed = null)
|
||||
{
|
||||
// ask kolab server first
|
||||
try {
|
||||
$request_config = [
|
||||
'store_body' => true,
|
||||
'follow_redirects' => true,
|
||||
];
|
||||
$request = libkolab::http_request(kolab_storage::get_freebusy_url($this->userdata['mail']), 'GET', $request_config);
|
||||
$response = $request->send();
|
||||
|
||||
// authentication required
|
||||
if ($response->getStatus() == 401) {
|
||||
$request->setAuth($this->cal->rc->user->get_username(), $this->cal->rc->decrypt($_SESSION['password']));
|
||||
$response = $request->send();
|
||||
}
|
||||
|
||||
if ($response->getStatus() == 200) {
|
||||
$fbdata = $response->getBody();
|
||||
}
|
||||
|
||||
unset($request, $response);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
rcube::raise_error([
|
||||
'code' => 900, 'file' => __FILE__, 'line' => __LINE__,
|
||||
'message' => "Error fetching free/busy information: " . $e->getMessage()
|
||||
],
|
||||
true, false
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$statusmap = [
|
||||
'FREE' => 'free',
|
||||
'BUSY' => 'busy',
|
||||
'BUSY-TENTATIVE' => 'tentative',
|
||||
'X-OUT-OF-OFFICE' => 'outofoffice',
|
||||
'OOF' => 'outofoffice',
|
||||
];
|
||||
|
||||
$titlemap = [
|
||||
'FREE' => $this->cal->gettext('availfree'),
|
||||
'BUSY' => $this->cal->gettext('availbusy'),
|
||||
'BUSY-TENTATIVE' => $this->cal->gettext('availtentative'),
|
||||
'X-OUT-OF-OFFICE' => $this->cal->gettext('availoutofoffice'),
|
||||
];
|
||||
|
||||
// rcube::console('_fetch_freebusy', kolab_storage::get_freebusy_url($this->userdata['mail']), $fbdata);
|
||||
|
||||
$count = 0;
|
||||
|
||||
// parse free-busy information
|
||||
if (!empty($fbdata)) {
|
||||
$ical = $this->cal->get_ical();
|
||||
$ical->import($fbdata);
|
||||
if ($fb = $ical->freebusy) {
|
||||
// consider 'changed >= X' queries
|
||||
if ($limit_changed && !empty($fb['created']) && $fb['created'] < $limit_changed) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
foreach ($fb['periods'] as $tuple) {
|
||||
list($from, $to, $type) = $tuple;
|
||||
$event = [
|
||||
'uid' => md5($this->id . $from->format('U') . '/' . $to->format('U')),
|
||||
'calendar' => $this->id,
|
||||
'changed' => !empty($fb['created']) ? $fb['created'] : new DateTime(),
|
||||
'title' => $this->get_name() . ' ' . (!empty($titlemap[$type]) ? $titlemap[$type] : $type),
|
||||
'start' => $from,
|
||||
'end' => $to,
|
||||
'free_busy' => !empty($statusmap[$type]) ? $statusmap[$type] : 'busy',
|
||||
'className' => 'fc-type-freebusy',
|
||||
'organizer' => [
|
||||
'email' => $this->userdata['mail'],
|
||||
'name' => isset($this->userdata['displayname']) ? $this->userdata['displayname'] : null,
|
||||
],
|
||||
];
|
||||
|
||||
// avoid duplicate entries
|
||||
$key = $this->time_key($event);
|
||||
if (empty($this->timeindex[$key])) {
|
||||
$this->events[$event['uid']] = $event;
|
||||
$this->timeindex[$key] = $event['uid'];
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to build a key for the absolute time slot the given event convers
|
||||
*/
|
||||
private function time_key($event)
|
||||
{
|
||||
return sprintf('%s/%s', $event['start']->format('U'), is_object($event['end']) ? $event['end']->format('U') : '0');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new event record
|
||||
*
|
||||
* @see calendar_driver::new_event()
|
||||
*
|
||||
* @return mixed The created record ID on success, False on error
|
||||
*/
|
||||
public function insert_event($event)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a specific event record
|
||||
*
|
||||
* @see calendar_driver::new_event()
|
||||
* @return bool True on success, False on error
|
||||
*/
|
||||
public function update_event($event, $exception_id = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an event record
|
||||
*
|
||||
* @see calendar_driver::remove_event()
|
||||
* @return bool True on success, False on error
|
||||
*/
|
||||
public function delete_event($event, $force = true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore deleted event record
|
||||
*
|
||||
* @see calendar_driver::undelete_event()
|
||||
* @return bool True on success, False on error
|
||||
*/
|
||||
public function restore_event($event)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,72 +41,76 @@ class resources_driver_ldap extends resources_driver
|
|||
/**
|
||||
* Fetch resource objects to be displayed for booking
|
||||
*
|
||||
* @param string Search query (optional)
|
||||
* @return array List of resource records available for booking
|
||||
* @param string $query Search query (optional)
|
||||
* @param int $num Max size of the result
|
||||
*
|
||||
* @return array List of resource records available for booking
|
||||
*/
|
||||
public function load_resources($query = null, $num = 5000)
|
||||
{
|
||||
if (!($ldap = $this->connect())) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// TODO: apply paging
|
||||
$ldap->set_pagesize($num);
|
||||
|
||||
if (isset($query)) {
|
||||
$results = $ldap->search('*', $query, 0, true, true);
|
||||
}
|
||||
else {
|
||||
$results = $ldap->list_records();
|
||||
}
|
||||
|
||||
if ($results instanceof ArrayAccess) {
|
||||
foreach ($results as $i => $rec) {
|
||||
$results[$i] = $this->decode_resource($rec);
|
||||
if (!($ldap = $this->connect())) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
// TODO: apply paging
|
||||
$ldap->set_pagesize($num);
|
||||
|
||||
if (isset($query)) {
|
||||
$results = $ldap->search('*', $query, 0, true, true);
|
||||
}
|
||||
else {
|
||||
$results = $ldap->list_records();
|
||||
}
|
||||
|
||||
if ($results instanceof ArrayAccess) {
|
||||
foreach ($results as $i => $rec) {
|
||||
$results[$i] = $this->decode_resource($rec);
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return properties of a single resource
|
||||
*
|
||||
* @param string Unique resource identifier
|
||||
* @param string $id Unique resource identifier
|
||||
*
|
||||
* @return array Resource object as hash array
|
||||
*/
|
||||
public function get_resource($dn)
|
||||
{
|
||||
$rec = null;
|
||||
$rec = null;
|
||||
|
||||
if ($ldap = $this->connect()) {
|
||||
$rec = $ldap->get_record(rcube_ldap::dn_encode($dn), true);
|
||||
if ($ldap = $this->connect()) {
|
||||
$rec = $ldap->get_record(rcube_ldap::dn_encode($dn), true);
|
||||
|
||||
if (!empty($rec)) {
|
||||
$rec = $this->decode_resource($rec);
|
||||
if (!empty($rec)) {
|
||||
$rec = $this->decode_resource($rec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $rec;
|
||||
return $rec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return properties of a resource owner
|
||||
*
|
||||
* @param string Owner identifier
|
||||
* @return array Resource object as hash array
|
||||
* @param string $dn Owner identifier
|
||||
*
|
||||
* @return array Resource object as hash array
|
||||
*/
|
||||
public function get_resource_owner($dn)
|
||||
{
|
||||
$owner = null;
|
||||
$owner = null;
|
||||
|
||||
if ($ldap = $this->connect()) {
|
||||
$owner = $ldap->get_record(rcube_ldap::dn_encode($dn), true);
|
||||
$owner['ID'] = rcube_ldap::dn_decode($owner['ID']);
|
||||
unset($owner['_raw_attrib'], $owner['_type']);
|
||||
}
|
||||
if ($ldap = $this->connect()) {
|
||||
$owner = $ldap->get_record(rcube_ldap::dn_encode($dn), true);
|
||||
$owner['ID'] = rcube_ldap::dn_decode($owner['ID']);
|
||||
unset($owner['_raw_attrib'], $owner['_type']);
|
||||
}
|
||||
|
||||
return $owner;
|
||||
return $owner;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -114,41 +118,40 @@ class resources_driver_ldap extends resources_driver
|
|||
*/
|
||||
private function decode_resource($rec)
|
||||
{
|
||||
$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) {
|
||||
$sattr = trim($sattr);
|
||||
if ($sattr && $sattr[0] === '{') {
|
||||
$attr = @json_decode($sattr, true);
|
||||
$attributes += $attr;
|
||||
foreach ((array) $rec['attributes'] as $sattr) {
|
||||
$sattr = trim($sattr);
|
||||
if (!empty($sattr) && $sattr[0] === '{') {
|
||||
$attr = @json_decode($sattr, true);
|
||||
$attributes += $attr;
|
||||
}
|
||||
else if (!empty($sattr) && empty($rec['description'])) {
|
||||
$rec['description'] = $sattr;
|
||||
}
|
||||
}
|
||||
else if ($sattr && empty($rec['description'])) {
|
||||
$rec['description'] = $sattr;
|
||||
|
||||
$rec['attributes'] = $attributes;
|
||||
|
||||
// force $rec['members'] to be an array
|
||||
if (!empty($rec['members']) && !is_array($rec['members'])) {
|
||||
$rec['members'] = [$rec['members']];
|
||||
}
|
||||
}
|
||||
|
||||
$rec['attributes'] = $attributes;
|
||||
// remove unused cruft
|
||||
unset($rec['_raw_attrib']);
|
||||
|
||||
// force $rec['members'] to be an array
|
||||
if (!empty($rec['members']) && !is_array($rec['members'])) {
|
||||
$rec['members'] = array($rec['members']);
|
||||
}
|
||||
|
||||
// remove unused cruft
|
||||
unset($rec['_raw_attrib']);
|
||||
|
||||
return $rec;
|
||||
return $rec;
|
||||
}
|
||||
|
||||
private function connect()
|
||||
{
|
||||
if (!isset($this->ldap)) {
|
||||
$this->ldap = new rcube_ldap($this->rc->config->get('calendar_resources_directory'), true);
|
||||
}
|
||||
if (!isset($this->ldap)) {
|
||||
$this->ldap = new rcube_ldap($this->rc->config->get('calendar_resources_directory'), true);
|
||||
}
|
||||
|
||||
return $this->ldap->ready ? $this->ldap : null;
|
||||
return $this->ldap->ready ? $this->ldap : null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,87 +26,93 @@
|
|||
*/
|
||||
abstract class resources_driver
|
||||
{
|
||||
protected $cal;
|
||||
protected $cal;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
function __construct($cal)
|
||||
{
|
||||
$this->cal = $cal;
|
||||
}
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
function __construct($cal)
|
||||
{
|
||||
$this->cal = $cal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch resource objects to be displayed for booking
|
||||
*
|
||||
* @param string Search query (optional)
|
||||
* @return array List of resource records available for booking
|
||||
*/
|
||||
abstract public function load_resources($query = null);
|
||||
/**
|
||||
* Fetch resource objects to be displayed for booking
|
||||
*
|
||||
* @param string $query Search query (optional)
|
||||
*
|
||||
* @return array List of resource records available for booking
|
||||
*/
|
||||
abstract public function load_resources($query = null);
|
||||
|
||||
/**
|
||||
* Return properties of a single resource
|
||||
*
|
||||
* @param string Unique resource identifier
|
||||
* @return array Resource object as hash array
|
||||
*/
|
||||
abstract public function get_resource($id);
|
||||
/**
|
||||
* Return properties of a single resource
|
||||
*
|
||||
* @param string $id Unique resource identifier
|
||||
*
|
||||
* @return array Resource object as hash array
|
||||
*/
|
||||
abstract public function get_resource($id);
|
||||
|
||||
/**
|
||||
* Return properties of a resource owner
|
||||
*
|
||||
* @param string Owner identifier
|
||||
* @return array Resource object as hash array
|
||||
*/
|
||||
public function get_resource_owner($id)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Return properties of a resource owner
|
||||
*
|
||||
* @param string $id Owner identifier
|
||||
*
|
||||
* @return array Resource object as hash array
|
||||
*/
|
||||
public function get_resource_owner($id)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get event data to display a resource's calendar
|
||||
*
|
||||
* The default implementation extracts the resource's email address
|
||||
* and fetches free-busy data using the calendar backend driver.
|
||||
*
|
||||
* @param integer Event's new start (unix timestamp)
|
||||
* @param integer Event's new end (unix timestamp)
|
||||
* @return array A list of event objects (see calendar_driver specification)
|
||||
*/
|
||||
public function get_resource_calendar($id, $start, $end)
|
||||
{
|
||||
$events = array();
|
||||
$rec = $this->get_resource($id);
|
||||
if ($rec && !empty($rec['email']) && $this->cal->driver) {
|
||||
$fbtypemap = array(
|
||||
calendar::FREEBUSY_BUSY => 'busy',
|
||||
calendar::FREEBUSY_TENTATIVE => 'tentative',
|
||||
calendar::FREEBUSY_OOF => 'outofoffice',
|
||||
);
|
||||
/**
|
||||
* Get event data to display a resource's calendar
|
||||
*
|
||||
* The default implementation extracts the resource's email address
|
||||
* and fetches free-busy data using the calendar backend driver.
|
||||
*
|
||||
* @param string $id Calendar identifier
|
||||
* @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)
|
||||
*/
|
||||
public function get_resource_calendar($id, $start, $end)
|
||||
{
|
||||
$events = [];
|
||||
$rec = $this->get_resource($id);
|
||||
|
||||
// if the backend has free-busy information
|
||||
$fblist = $this->cal->driver->get_freebusy_list($rec['email'], $start, $end);
|
||||
if (is_array($fblist)) {
|
||||
foreach ($fblist as $slot) {
|
||||
list($from, $to, $type) = $slot;
|
||||
if ($type == calendar::FREEBUSY_FREE || $type == calendar::FREEBUSY_UNKNOWN) {
|
||||
continue;
|
||||
}
|
||||
if ($from < $end && $to > $start) {
|
||||
$event = array(
|
||||
'id' => sha1($id . $from . $to),
|
||||
'title' => $rec['name'],
|
||||
'start' => new DateTime('@' . $from),
|
||||
'end' => new DateTime('@' . $to),
|
||||
'status' => $fbtypemap[$type],
|
||||
'calendar' => '_resource',
|
||||
);
|
||||
$events[] = $event;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($rec && !empty($rec['email']) && !empty($this->cal->driver)) {
|
||||
$fbtypemap = [
|
||||
calendar::FREEBUSY_BUSY => 'busy',
|
||||
calendar::FREEBUSY_TENTATIVE => 'tentative',
|
||||
calendar::FREEBUSY_OOF => 'outofoffice',
|
||||
];
|
||||
|
||||
return $events;
|
||||
}
|
||||
// if the backend has free-busy information
|
||||
$fblist = $this->cal->driver->get_freebusy_list($rec['email'], $start, $end);
|
||||
if (is_array($fblist)) {
|
||||
foreach ($fblist as $slot) {
|
||||
list($from, $to, $type) = $slot;
|
||||
if ($type == calendar::FREEBUSY_FREE || $type == calendar::FREEBUSY_UNKNOWN) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($from < $end && $to > $start) {
|
||||
$events[] = [
|
||||
'id' => sha1($id . $from . $to),
|
||||
'title' => $rec['name'],
|
||||
'start' => new DateTime('@' . $from),
|
||||
'end' => new DateTime('@' . $to),
|
||||
'status' => $fbtypemap[$type],
|
||||
'calendar' => '_resource',
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $events;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,213 +28,231 @@ require_once realpath(__DIR__ . '/../../libcalendaring/lib/libcalendaring_itip.p
|
|||
*/
|
||||
class calendar_itip extends libcalendaring_itip
|
||||
{
|
||||
/**
|
||||
* Constructor to set text domain to calendar
|
||||
*/
|
||||
function __construct($plugin, $domain = 'calendar')
|
||||
{
|
||||
parent::__construct($plugin, $domain);
|
||||
/**
|
||||
* Constructor to set text domain to calendar
|
||||
*/
|
||||
function __construct($plugin, $domain = 'calendar')
|
||||
{
|
||||
parent::__construct($plugin, $domain);
|
||||
|
||||
$this->db_itipinvitations = $this->rc->db->table_name('itipinvitations', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for calendar/itip-status requests
|
||||
*/
|
||||
public function get_itip_status($event, $existing = null)
|
||||
{
|
||||
$status = parent::get_itip_status($event, $existing);
|
||||
|
||||
// don't ask for deleting events when declining
|
||||
if ($this->rc->config->get('kolab_invitation_calendars'))
|
||||
$status['saved'] = false;
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find invitation record by token
|
||||
*
|
||||
* @param string Invitation token
|
||||
* @return mixed Invitation record as hash array or False if not found
|
||||
*/
|
||||
public function get_invitation($token)
|
||||
{
|
||||
if ($parts = $this->decode_token($token)) {
|
||||
$result = $this->rc->db->query("SELECT * FROM $this->db_itipinvitations WHERE `token` = ?", $parts['base']);
|
||||
if ($result && ($rec = $this->rc->db->fetch_assoc($result))) {
|
||||
$rec['event'] = unserialize($rec['event']);
|
||||
$rec['attendee'] = $parts['attendee'];
|
||||
return $rec;
|
||||
}
|
||||
$this->db_itipinvitations = $this->rc->db->table_name('itipinvitations', true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the attendee status of the given invitation record
|
||||
*
|
||||
* @param array Invitation record as fetched with calendar_itip::get_invitation()
|
||||
* @param string Attendee email address
|
||||
* @param string New attendee status
|
||||
*/
|
||||
public function update_invitation($invitation, $email, $newstatus)
|
||||
{
|
||||
if (is_string($invitation))
|
||||
$invitation = $this->get_invitation($invitation);
|
||||
|
||||
if ($invitation['token'] && $invitation['event']) {
|
||||
// update attendee record in event data
|
||||
foreach ($invitation['event']['attendees'] as $i => $attendee) {
|
||||
if ($attendee['role'] == 'ORGANIZER') {
|
||||
$organizer = $attendee;
|
||||
/**
|
||||
* Handler for calendar/itip-status requests
|
||||
*/
|
||||
public function get_itip_status($event, $existing = null)
|
||||
{
|
||||
$status = parent::get_itip_status($event, $existing);
|
||||
|
||||
// don't ask for deleting events when declining
|
||||
if ($this->rc->config->get('kolab_invitation_calendars')) {
|
||||
$status['saved'] = false;
|
||||
}
|
||||
else if ($attendee['email'] == $email) {
|
||||
// nothing to be done here
|
||||
if ($attendee['status'] == $newstatus)
|
||||
return true;
|
||||
|
||||
$invitation['event']['attendees'][$i]['status'] = $newstatus;
|
||||
$this->sender = $attendee;
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find invitation record by token
|
||||
*
|
||||
* @param string $token Invitation token
|
||||
*
|
||||
* @return mixed Invitation record as hash array or False if not found
|
||||
*/
|
||||
public function get_invitation($token)
|
||||
{
|
||||
if ($parts = $this->decode_token($token)) {
|
||||
$result = $this->rc->db->query("SELECT * FROM $this->db_itipinvitations WHERE `token` = ?", $parts['base']);
|
||||
if ($result && ($rec = $this->rc->db->fetch_assoc($result))) {
|
||||
$rec['event'] = unserialize($rec['event']);
|
||||
$rec['attendee'] = $parts['attendee'];
|
||||
|
||||
return $rec;
|
||||
}
|
||||
}
|
||||
}
|
||||
$invitation['event']['changed'] = new DateTime();
|
||||
|
||||
// send iTIP REPLY message to organizer
|
||||
if ($organizer) {
|
||||
$status = strtolower($newstatus);
|
||||
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');
|
||||
else
|
||||
$this->rc->output->command('display_message', $this->plugin->gettext('itipresponseerror'), 'error');
|
||||
}
|
||||
|
||||
// update record in DB
|
||||
$query = $this->rc->db->query(
|
||||
"UPDATE $this->db_itipinvitations
|
||||
SET `event` = ?
|
||||
WHERE `token` = ?",
|
||||
self::serialize_event($invitation['event']),
|
||||
$invitation['token']
|
||||
);
|
||||
|
||||
if ($this->rc->db->affected_rows($query))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the attendee status of the given invitation record
|
||||
*
|
||||
* @param array $invitation Invitation record as fetched with calendar_itip::get_invitation()
|
||||
* @param string $email Attendee email address
|
||||
* @param string $newstatus New attendee status
|
||||
*/
|
||||
public function update_invitation($invitation, $email, $newstatus)
|
||||
{
|
||||
if (is_string($invitation)) {
|
||||
$invitation = $this->get_invitation($invitation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create iTIP invitation token for later replies via URL
|
||||
*
|
||||
* @param array Hash array with event properties
|
||||
* @param string Attendee email address
|
||||
* @return string Invitation token
|
||||
*/
|
||||
public function store_invitation($event, $attendee)
|
||||
{
|
||||
static $stored = array();
|
||||
|
||||
if (!$event['uid'] || !$attendee)
|
||||
return false;
|
||||
|
||||
// generate token for this invitation
|
||||
$token = $this->generate_token($event, $attendee);
|
||||
$base = substr($token, 0, 40);
|
||||
|
||||
// already stored this
|
||||
if ($stored[$base])
|
||||
return $token;
|
||||
if (!empty($invitation['token']) && !empty($invitation['event'])) {
|
||||
// update attendee record in event data
|
||||
foreach ($invitation['event']['attendees'] as $i => $attendee) {
|
||||
if ($attendee['role'] == 'ORGANIZER') {
|
||||
$organizer = $attendee;
|
||||
}
|
||||
else if ($attendee['email'] == $email) {
|
||||
// nothing to be done here
|
||||
if ($attendee['status'] == $newstatus) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// delete old entry
|
||||
$this->rc->db->query("DELETE FROM $this->db_itipinvitations WHERE `token` = ?", $base);
|
||||
$invitation['event']['attendees'][$i]['status'] = $newstatus;
|
||||
$this->sender = $attendee;
|
||||
}
|
||||
}
|
||||
|
||||
$event_uid = $event['uid'] . ($event['_instance'] ? '-' . $event['_instance'] : '');
|
||||
$invitation['event']['changed'] = new DateTime();
|
||||
|
||||
$query = $this->rc->db->query(
|
||||
"INSERT INTO $this->db_itipinvitations
|
||||
(`token`, `event_uid`, `user_id`, `event`, `expires`)
|
||||
VALUES(?, ?, ?, ?, ?)",
|
||||
$base,
|
||||
$event_uid,
|
||||
$this->rc->user->ID,
|
||||
self::serialize_event($event),
|
||||
date('Y-m-d H:i:s', $event['end']->format('U') + 86400 * 2)
|
||||
);
|
||||
|
||||
if ($this->rc->db->affected_rows($query)) {
|
||||
$stored[$base] = 1;
|
||||
return $token;
|
||||
// send iTIP REPLY message to organizer
|
||||
if (!empty($organizer)) {
|
||||
$status = strtolower($newstatus);
|
||||
if ($this->send_itip_message($invitation['event'], 'REPLY', $organizer, 'itipsubject' . $status, 'itipmailbody' . $status)) {
|
||||
$mailto = !empty($organizer['name']) ? $organizer['name'] : $organizer['email'];
|
||||
$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');
|
||||
}
|
||||
}
|
||||
|
||||
// update record in DB
|
||||
$query = $this->rc->db->query(
|
||||
"UPDATE $this->db_itipinvitations SET `event` = ? WHERE `token` = ?",
|
||||
self::serialize_event($invitation['event']),
|
||||
$invitation['token']
|
||||
);
|
||||
|
||||
if ($this->rc->db->affected_rows($query)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark invitations for the given event as cancelled
|
||||
*
|
||||
* @param array Hash array with event properties
|
||||
*/
|
||||
public function cancel_itip_invitation($event)
|
||||
{
|
||||
$event_uid = $event['uid'] . ($event['_instance'] ? '-' . $event['_instance'] : '');
|
||||
/**
|
||||
* Create iTIP invitation token for later replies via URL
|
||||
*
|
||||
* @param array $event Hash array with event properties
|
||||
* @param string $attendee Attendee email address
|
||||
*
|
||||
* @return string Invitation token
|
||||
*/
|
||||
public function store_invitation($event, $attendee)
|
||||
{
|
||||
static $stored = [];
|
||||
|
||||
// flag invitation record as cancelled
|
||||
$this->rc->db->query(
|
||||
"UPDATE $this->db_itipinvitations
|
||||
SET `cancelled` = 1
|
||||
WHERE `event_uid` = ? AND `user_id` = ?",
|
||||
$event_uid,
|
||||
$this->rc->user->ID
|
||||
);
|
||||
}
|
||||
if (empty($event['uid']) || !$attendee) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an invitation request token for the given event and attendee
|
||||
*
|
||||
* @param array Event hash array
|
||||
* @param string Attendee email address
|
||||
*/
|
||||
public function generate_token($event, $attendee)
|
||||
{
|
||||
$event_uid = $event['uid'] . ($event['_instance'] ? '-' . $event['_instance'] : '');
|
||||
$base = sha1($event_uid . ';' . $this->rc->user->ID);
|
||||
$mail = base64_encode($attendee);
|
||||
$hash = substr(md5($base . $mail . $this->rc->config->get('des_key')), 0, 6);
|
||||
|
||||
return "$base.$mail.$hash";
|
||||
}
|
||||
// generate token for this invitation
|
||||
$token = $this->generate_token($event, $attendee);
|
||||
$base = substr($token, 0, 40);
|
||||
|
||||
/**
|
||||
* Decode the given iTIP request token and return its parts
|
||||
*
|
||||
* @param string Request token to decode
|
||||
* @return mixed Hash array with parts or False if invalid
|
||||
*/
|
||||
public function decode_token($token)
|
||||
{
|
||||
list($base, $mail, $hash) = explode('.', $token);
|
||||
|
||||
// validate and return parts
|
||||
if ($mail && $hash && $hash == substr(md5($base . $mail . $this->rc->config->get('des_key')), 0, 6)) {
|
||||
return array('base' => $base, 'attendee' => base64_decode($mail));
|
||||
// already stored this
|
||||
if (!empty($stored[$base])) {
|
||||
return $token;
|
||||
}
|
||||
|
||||
// delete old entry
|
||||
$this->rc->db->query("DELETE FROM $this->db_itipinvitations WHERE `token` = ?", $base);
|
||||
|
||||
$event_uid = $event['uid'] . (!empty($event['_instance']) ? '-' . $event['_instance'] : '');
|
||||
|
||||
$query = $this->rc->db->query(
|
||||
"INSERT INTO $this->db_itipinvitations"
|
||||
. " (`token`, `event_uid`, `user_id`, `event`, `expires`)"
|
||||
. " VALUES(?, ?, ?, ?, ?)",
|
||||
$base,
|
||||
$event_uid,
|
||||
$this->rc->user->ID,
|
||||
self::serialize_event($event),
|
||||
date('Y-m-d H:i:s', $event['end']->format('U') + 86400 * 2)
|
||||
);
|
||||
|
||||
if ($this->rc->db->affected_rows($query)) {
|
||||
$stored[$base] = 1;
|
||||
return $token;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to serialize the given event for storing in invitations table
|
||||
*/
|
||||
private static function serialize_event($event)
|
||||
{
|
||||
$ev = $event;
|
||||
$ev['description'] = abbreviate_string($ev['description'], 100);
|
||||
unset($ev['attachments']);
|
||||
return serialize($ev);
|
||||
}
|
||||
/**
|
||||
* Mark invitations for the given event as cancelled
|
||||
*
|
||||
* @param array $event Hash array with event properties
|
||||
*/
|
||||
public function cancel_itip_invitation($event)
|
||||
{
|
||||
$event_uid = $event['uid'] . (!empty($event['_instance']) ? '-' . $event['_instance'] : '');
|
||||
|
||||
// flag invitation record as cancelled
|
||||
$this->rc->db->query(
|
||||
"UPDATE $this->db_itipinvitations SET `cancelled` = 1"
|
||||
. " WHERE `event_uid` = ? AND `user_id` = ?",
|
||||
$event_uid,
|
||||
$this->rc->user->ID
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an invitation request token for the given event and attendee
|
||||
*
|
||||
* @param array $event Event hash array
|
||||
* @param string $attendee Attendee email address
|
||||
*/
|
||||
public function generate_token($event, $attendee)
|
||||
{
|
||||
$event_uid = $event['uid'] . (!empty($event['_instance']) ? '-' . $event['_instance'] : '');
|
||||
$base = sha1($event_uid . ';' . $this->rc->user->ID);
|
||||
$mail = base64_encode($attendee);
|
||||
$hash = substr(md5($base . $mail . $this->rc->config->get('des_key')), 0, 6);
|
||||
|
||||
return "$base.$mail.$hash";
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the given iTIP request token and return its parts
|
||||
*
|
||||
* @param string $token Request token to decode
|
||||
*
|
||||
* @return mixed Hash array with parts or False if invalid
|
||||
*/
|
||||
public function decode_token($token)
|
||||
{
|
||||
list($base, $mail, $hash) = explode('.', $token);
|
||||
|
||||
// validate and return parts
|
||||
if ($mail && $hash && $hash == substr(md5($base . $mail . $this->rc->config->get('des_key')), 0, 6)) {
|
||||
return ['base' => $base, 'attendee' => base64_decode($mail)];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to serialize the given event for storing in invitations table
|
||||
*/
|
||||
private static function serialize_event($event)
|
||||
{
|
||||
$ev = $event;
|
||||
|
||||
if (!empty($ev['description'])) {
|
||||
$ev['description'] = abbreviate_string($ev['description'], 100);
|
||||
}
|
||||
|
||||
unset($ev['attachments']);
|
||||
|
||||
return serialize($ev);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,63 +26,64 @@ require_once realpath(__DIR__ . '/../../libcalendaring/lib/libcalendaring_recurr
|
|||
*/
|
||||
class calendar_recurrence extends libcalendaring_recurrence
|
||||
{
|
||||
private $event;
|
||||
private $duration;
|
||||
private $event;
|
||||
private $duration;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*
|
||||
* @param object calendar The calendar plugin instance
|
||||
* @param array The event object to operate on
|
||||
*/
|
||||
function __construct($cal, $event)
|
||||
{
|
||||
parent::__construct($cal->lib);
|
||||
/**
|
||||
* Default constructor
|
||||
*
|
||||
* @param calendar $cal The calendar plugin instance
|
||||
* @param array $event The event object to operate on
|
||||
*/
|
||||
function __construct($cal, $event)
|
||||
{
|
||||
parent::__construct($cal->lib);
|
||||
|
||||
$this->event = $event;
|
||||
$this->event = $event;
|
||||
|
||||
if (is_object($event['start']) && is_object($event['end']))
|
||||
$this->duration = $event['start']->diff($event['end']);
|
||||
if (is_object($event['start']) && is_object($event['end'])) {
|
||||
$this->duration = $event['start']->diff($event['end']);
|
||||
}
|
||||
|
||||
$event['start']->_dateonly |= $event['allday'];
|
||||
$this->init($event['recurrence'], $event['start']);
|
||||
}
|
||||
$event['start']->_dateonly = !empty($event['allday']);
|
||||
|
||||
/**
|
||||
* Alias of libcalendaring_recurrence::next()
|
||||
*
|
||||
* @return mixed DateTime object or False if recurrence ended
|
||||
*/
|
||||
public function next_start()
|
||||
{
|
||||
return $this->next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next recurring instance of this event
|
||||
*
|
||||
* @return mixed Array with event properties or False if recurrence ended
|
||||
*/
|
||||
public function next_instance()
|
||||
{
|
||||
if ($next_start = $this->next()) {
|
||||
$next = $this->event;
|
||||
$next['start'] = $next_start;
|
||||
|
||||
if ($this->duration) {
|
||||
$next['end'] = clone $next_start;
|
||||
$next['end']->add($this->duration);
|
||||
}
|
||||
|
||||
$next['recurrence_date'] = clone $next_start;
|
||||
$next['_instance'] = libcalendaring::recurrence_instance_identifier($next, $this->event['allday']);
|
||||
|
||||
unset($next['_formatobj']);
|
||||
|
||||
return $next;
|
||||
$this->init($event['recurrence'], $event['start']);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Alias of libcalendaring_recurrence::next()
|
||||
*
|
||||
* @return mixed DateTime object or False if recurrence ended
|
||||
*/
|
||||
public function next_start()
|
||||
{
|
||||
return $this->next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next recurring instance of this event
|
||||
*
|
||||
* @return mixed Array with event properties or False if recurrence ended
|
||||
*/
|
||||
public function next_instance()
|
||||
{
|
||||
if ($next_start = $this->next()) {
|
||||
$next = $this->event;
|
||||
$next['start'] = $next_start;
|
||||
|
||||
if ($this->duration) {
|
||||
$next['end'] = clone $next_start;
|
||||
$next['end']->add($this->duration);
|
||||
}
|
||||
|
||||
$next['recurrence_date'] = clone $next_start;
|
||||
$next['_instance'] = libcalendaring::recurrence_instance_identifier($next, $this->event['allday']);
|
||||
|
||||
unset($next['_formatobj']);
|
||||
|
||||
return $next;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
1668
lib/calendar_ui.php
1668
lib/calendar_ui.php
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue