Merge branch 'master' into dev/elastic

Conflicts:
	plugins/calendar/calendar_ui.js
This commit is contained in:
Aleksander Machniak 2018-03-16 09:25:28 +01:00
commit e52bc0a158
9 changed files with 171 additions and 124 deletions

View file

@ -2027,6 +2027,10 @@ class calendar extends rcube_plugin
*/
private function write_preprocess(&$event, $action)
{
// Remove double timezone specification (T2313)
$event['start'] = preg_replace('/\s*\(.*\)/', '', $event['start']);
$event['end'] = preg_replace('/\s*\(.*\)/', '', $event['end']);
// convert dates into DateTime objects in user's current timezone
$event['start'] = new DateTime($event['start'], $this->timezone);
$event['end'] = new DateTime($event['end'], $this->timezone);

View file

@ -3104,7 +3104,7 @@ function rcube_calendar_ui(settings)
else if (range > 0)
start = 'today -' + range + ' months';
rcmail.goto_url('export_events', { source:source, start:start, attachments:attachmt?1:0 });
rcmail.goto_url('export_events', { source:source, start:start, attachments:attachmt?1:0 }, false);
}
$dialog.dialog("close");
}
@ -3135,7 +3135,7 @@ function rcube_calendar_ui(settings)
this.event_download = function(event)
{
if (event && event.id) {
rcmail.goto_url('export_events', { source:event.calendar, id:event.id, attachments:1 });
rcmail.goto_url('export_events', { source:event.calendar, id:event.id, attachments:1 }, false);
}
};

View file

@ -192,7 +192,7 @@ class libcalendaring_itip
}
}
return $p;
return $p;
}
/**
@ -365,128 +365,153 @@ class libcalendaring_itip
*/
public function get_itip_status($event, $existing = null)
{
$action = $event['rsvp'] ? 'rsvp' : '';
$status = $event['fallback'];
$latest = $rescheduled = false;
$html = '';
$action = $event['rsvp'] ? 'rsvp' : '';
$status = $event['fallback'];
$latest = $rescheduled = false;
$html = '';
if (is_numeric($event['changed']))
$event['changed'] = new DateTime('@'.$event['changed']);
if (is_numeric($event['changed'])) {
$event['changed'] = new DateTime('@'.$event['changed']);
}
// check if the given itip object matches the last state
if ($existing) {
$latest = (isset($event['sequence']) && intval($existing['sequence']) == intval($event['sequence'])) ||
// check if the given itip object matches the last state
if ($existing) {
$latest = (isset($event['sequence']) && intval($existing['sequence']) == intval($event['sequence'])) ||
(!isset($event['sequence']) && $existing['changed'] && $existing['changed'] >= $event['changed']);
}
}
// determine action for REQUEST
if ($event['method'] == 'REQUEST') {
$html = html::div('rsvp-status', $this->gettext('acceptinvitation'));
// determine action for REQUEST
if ($event['method'] == 'REQUEST') {
$html = html::div('rsvp-status', $this->gettext('acceptinvitation'));
if ($existing) {
$rsvp = $event['rsvp'];
$emails = $this->lib->get_user_emails();
foreach ($existing['attendees'] as $attendee) {
if ($attendee['email'] && in_array(strtolower($attendee['email']), $emails)) {
$status = strtoupper($attendee['status']);
break;
if ($existing) {
$rsvp = $event['rsvp'];
$emails = $this->lib->get_user_emails();
foreach ($existing['attendees'] as $attendee) {
if ($attendee['email'] && in_array(strtolower($attendee['email']), $emails)) {
$status = strtoupper($attendee['status']);
break;
}
}
// Detect re-sheduling
if (!$latest) {
// FIXME: This is probably to simplistic, or maybe we should just check
// attendee's RSVP flag in the new event?
$rescheduled = $existing['start'] != $event['start'] || $existing['end'] > $event['end'];
}
}
}
// Detect re-sheduling
if (!$latest) {
// FIXME: This is probably to simplistic, or maybe we should just check
// attendee's RSVP flag in the new event?
$rescheduled = $existing['start'] != $event['start'] || $existing['end'] > $event['end'];
}
}
else {
$rsvp = $event['rsvp'] && $this->rc->config->get('calendar_allow_itip_uninvited', true);
}
$status_lc = strtolower($status);
if ($status_lc == 'unknown' && !$this->rc->config->get('calendar_allow_itip_uninvited', true)) {
$html = html::div('rsvp-status', $this->gettext('notanattendee'));
$action = 'import';
}
else if (in_array($status_lc, $this->rsvp_status)) {
$status_text = $this->gettext(($latest ? 'youhave' : 'youhavepreviously') . $status_lc);
if ($existing && ($existing['sequence'] > $event['sequence'] || (!isset($event['sequence']) && $existing['changed'] && $existing['changed'] > $event['changed']))) {
$action = ''; // nothing to do here, outdated invitation
if ($status_lc == 'needs-action')
$status_text = $this->gettext('outdatedinvitation');
}
else if (!$existing && !$rsvp) {
$action = 'import';
}
else if ($rescheduled) {
$action = 'rsvp';
}
else if ($status_lc != 'needs-action') {
// check if there are any changes
if ($latest) {
$diff = $this->get_itip_diff($event, $existing);
$latest = empty($diff);
else {
$rsvp = $event['rsvp'] && $this->rc->config->get('calendar_allow_itip_uninvited', true);
}
$action = !$latest ? 'update' : '';
}
$status_lc = strtolower($status);
$html = html::div('rsvp-status ' . $status_lc, $status_text);
}
}
// determine action for REPLY
else if ($event['method'] == 'REPLY') {
// check whether the sender already is an attendee
if ($existing) {
$action = $this->rc->config->get('calendar_allow_itip_uninvited', true) ? 'accept' : '';
$listed = false;
foreach ($existing['attendees'] as $attendee) {
if ($attendee['role'] != 'ORGANIZER' && strcasecmp($attendee['email'], $event['attendee']) == 0) {
$status_lc = strtolower($status);
if (in_array($status_lc, $this->rsvp_status)) {
$html = html::div('rsvp-status ' . $status_lc, $this->gettext(array(
'name' => 'attendee' . $status_lc,
'vars' => array(
'delegatedto' => rcube::Q($event['delegated-to'] ?: ($attendee['delegated-to'] ?: '?')),
)
)));
}
$action = $attendee['status'] == $status || !$latest ? '' : 'update';
$listed = true;
break;
if ($status_lc == 'unknown' && !$this->rc->config->get('calendar_allow_itip_uninvited', true)) {
$html = html::div('rsvp-status', $this->gettext('notanattendee'));
$action = 'import';
}
}
else if (in_array($status_lc, $this->rsvp_status)) {
$status_text = $this->gettext(($latest ? 'youhave' : 'youhavepreviously') . $status_lc);
if (!$listed) {
$html = html::div('rsvp-status', $this->gettext('itipnewattendee'));
}
}
else {
$html = html::div('rsvp-status hint', $this->gettext('itipobjectnotfound'));
$action = '';
}
}
else if ($event['method'] == 'CANCEL') {
if (!$existing) {
$html = html::div('rsvp-status hint', $this->gettext('itipobjectnotfound'));
$action = '';
}
}
if ($existing && ($existing['sequence'] > $event['sequence']
|| (!isset($event['sequence']) && $existing['changed'] && $existing['changed'] > $event['changed']))
) {
$action = ''; // nothing to do here, outdated invitation
if ($status_lc == 'needs-action') {
$status_text = $this->gettext('outdatedinvitation');
}
}
else if (!$existing && !$rsvp) {
$action = 'import';
}
else if ($rescheduled) {
$action = 'rsvp';
}
else if ($status_lc != 'needs-action') {
// check if there are any changes
if ($latest) {
$diff = $this->get_itip_diff($event, $existing);
$latest = empty($diff);
}
return array(
'uid' => $event['uid'],
'id' => asciiwords($event['uid'], true),
'existing' => $existing ? true : false,
'saved' => $existing ? true : false,
'latest' => $latest,
'status' => $status,
'action' => $action,
'rescheduled' => $rescheduled,
'html' => $html,
);
$action = !$latest ? 'update' : '';
}
$html = html::div('rsvp-status ' . $status_lc, $status_text);
}
}
// determine action for REPLY
else if ($event['method'] == 'REPLY') {
// check whether the sender already is an attendee
if ($existing) {
// Relax checking if that is a reply to the latest version of the event
// We accept versions with older SEQUENCE but no significant changes (Bifrost#T78144)
if (!$latest) {
$num = $got = 0;
foreach (array('start', 'end', 'due', 'allday', 'recurrence', 'location') as $key) {
if (isset($existing[$key])) {
if ($key == 'allday') {
$event[$key] = $event[$key] == 'true';
}
$value = $existing[$key] instanceof DateTime ? $existing[$key]->format('c') : $existing[$key];
$num++;
$got += intval($value == $event[$key]);
}
}
$latest = $num === $got;
}
$action = $this->rc->config->get('calendar_allow_itip_uninvited', true) ? 'accept' : '';
$listed = false;
foreach ($existing['attendees'] as $attendee) {
if ($attendee['role'] != 'ORGANIZER' && strcasecmp($attendee['email'], $event['attendee']) == 0) {
$status_lc = strtolower($status);
if (in_array($status_lc, $this->rsvp_status)) {
$html = html::div('rsvp-status ' . $status_lc, $this->gettext(array(
'name' => 'attendee' . $status_lc,
'vars' => array(
'delegatedto' => rcube::Q($event['delegated-to'] ?: ($attendee['delegated-to'] ?: '?')),
)
)));
}
$action = $attendee['status'] == $status || !$latest ? '' : 'update';
$listed = true;
break;
}
}
if (!$listed) {
$html = html::div('rsvp-status', $this->gettext('itipnewattendee'));
}
}
else {
$html = html::div('rsvp-status hint', $this->gettext('itipobjectnotfound'));
$action = '';
}
}
else if ($event['method'] == 'CANCEL') {
if (!$existing) {
$html = html::div('rsvp-status hint', $this->gettext('itipobjectnotfound'));
$action = '';
}
}
return array(
'uid' => $event['uid'],
'id' => asciiwords($event['uid'], true),
'existing' => $existing ? true : false,
'saved' => $existing ? true : false,
'latest' => $latest,
'status' => $status,
'action' => $action,
'rescheduled' => $rescheduled,
'html' => $html,
);
}
protected function get_itip_diff($event, $existing)
@ -612,6 +637,13 @@ class libcalendaring_itip
$buttons[] = html::div(array('id' => 'update-'.$dom_id, 'style' => 'display:none'), $update_button);
$buttons[] = html::div(array('id' => 'accept-'.$dom_id, 'style' => 'display:none'), $accept_buttons);
// For replies we need more metadata
foreach (array('start', 'end', 'due', 'allday', 'recurrence', 'location') as $key) {
if (isset($event[$key])) {
$metadata[$key] = $event[$key] instanceof DateTime ? $event[$key]->format('c') : $event[$key];
}
}
}
// when receiving iTip REQUEST messages:
else if ($method == 'REQUEST') {

View file

@ -29,7 +29,7 @@ DROP TABLE IF EXISTS `kolab_cache_contact`;
CREATE TABLE `kolab_cache_contact` (
`folder_id` BIGINT UNSIGNED NOT NULL,
`msguid` BIGINT UNSIGNED NOT NULL,
`uid` VARCHAR(512) CHARACTER SET ascii NOT NULL,
`uid` VARCHAR(512) NOT NULL,
`created` DATETIME DEFAULT NULL,
`changed` DATETIME DEFAULT NULL,
`data` LONGTEXT NOT NULL,
@ -53,7 +53,7 @@ DROP TABLE IF EXISTS `kolab_cache_event`;
CREATE TABLE `kolab_cache_event` (
`folder_id` BIGINT UNSIGNED NOT NULL,
`msguid` BIGINT UNSIGNED NOT NULL,
`uid` VARCHAR(512) CHARACTER SET ascii NOT NULL,
`uid` VARCHAR(512) NOT NULL,
`created` DATETIME DEFAULT NULL,
`changed` DATETIME DEFAULT NULL,
`data` LONGTEXT NOT NULL,
@ -73,7 +73,7 @@ DROP TABLE IF EXISTS `kolab_cache_task`;
CREATE TABLE `kolab_cache_task` (
`folder_id` BIGINT UNSIGNED NOT NULL,
`msguid` BIGINT UNSIGNED NOT NULL,
`uid` VARCHAR(512) CHARACTER SET ascii NOT NULL,
`uid` VARCHAR(512) NOT NULL,
`created` DATETIME DEFAULT NULL,
`changed` DATETIME DEFAULT NULL,
`data` LONGTEXT NOT NULL,
@ -93,7 +93,7 @@ DROP TABLE IF EXISTS `kolab_cache_journal`;
CREATE TABLE `kolab_cache_journal` (
`folder_id` BIGINT UNSIGNED NOT NULL,
`msguid` BIGINT UNSIGNED NOT NULL,
`uid` VARCHAR(512) CHARACTER SET ascii NOT NULL,
`uid` VARCHAR(512) NOT NULL,
`created` DATETIME DEFAULT NULL,
`changed` DATETIME DEFAULT NULL,
`data` LONGTEXT NOT NULL,
@ -113,7 +113,7 @@ DROP TABLE IF EXISTS `kolab_cache_note`;
CREATE TABLE `kolab_cache_note` (
`folder_id` BIGINT UNSIGNED NOT NULL,
`msguid` BIGINT UNSIGNED NOT NULL,
`uid` VARCHAR(512) CHARACTER SET ascii NOT NULL,
`uid` VARCHAR(512) NOT NULL,
`created` DATETIME DEFAULT NULL,
`changed` DATETIME DEFAULT NULL,
`data` LONGTEXT NOT NULL,
@ -131,7 +131,7 @@ DROP TABLE IF EXISTS `kolab_cache_file`;
CREATE TABLE `kolab_cache_file` (
`folder_id` BIGINT UNSIGNED NOT NULL,
`msguid` BIGINT UNSIGNED NOT NULL,
`uid` VARCHAR(512) CHARACTER SET ascii NOT NULL,
`uid` VARCHAR(512) NOT NULL,
`created` DATETIME DEFAULT NULL,
`changed` DATETIME DEFAULT NULL,
`data` LONGTEXT NOT NULL,
@ -151,7 +151,7 @@ DROP TABLE IF EXISTS `kolab_cache_configuration`;
CREATE TABLE `kolab_cache_configuration` (
`folder_id` BIGINT UNSIGNED NOT NULL,
`msguid` BIGINT UNSIGNED NOT NULL,
`uid` VARCHAR(512) CHARACTER SET ascii NOT NULL,
`uid` VARCHAR(512) NOT NULL,
`created` DATETIME DEFAULT NULL,
`changed` DATETIME DEFAULT NULL,
`data` LONGTEXT NOT NULL,
@ -171,7 +171,7 @@ DROP TABLE IF EXISTS `kolab_cache_freebusy`;
CREATE TABLE `kolab_cache_freebusy` (
`folder_id` BIGINT UNSIGNED NOT NULL,
`msguid` BIGINT UNSIGNED NOT NULL,
`uid` VARCHAR(512) CHARACTER SET ascii NOT NULL,
`uid` VARCHAR(512) NOT NULL,
`created` DATETIME DEFAULT NULL,
`changed` DATETIME DEFAULT NULL,
`data` LONGTEXT NOT NULL,
@ -188,4 +188,4 @@ CREATE TABLE `kolab_cache_freebusy` (
/*!40014 SET FOREIGN_KEY_CHECKS=1 */;
REPLACE INTO `system` (`name`, `value`) VALUES ('libkolab-version', '2017071900');
REPLACE INTO `system` (`name`, `value`) VALUES ('libkolab-version', '2018021300');

View file

@ -0,0 +1,9 @@
-- accept utf8 in UID column
ALTER TABLE `kolab_cache_contact` MODIFY `uid` VARCHAR(512) CHARACTER SET utf8 NOT NULL;
ALTER TABLE `kolab_cache_event` MODIFY `uid` VARCHAR(512) CHARACTER SET utf8 NOT NULL;
ALTER TABLE `kolab_cache_task` MODIFY `uid` VARCHAR(512) CHARACTER SET utf8 NOT NULL;
ALTER TABLE `kolab_cache_journal` MODIFY `uid` VARCHAR(512) CHARACTER SET utf8 NOT NULL;
ALTER TABLE `kolab_cache_note` MODIFY `uid` VARCHAR(512) CHARACTER SET utf8 NOT NULL;
ALTER TABLE `kolab_cache_file` MODIFY `uid` VARCHAR(512) CHARACTER SET utf8 NOT NULL;
ALTER TABLE `kolab_cache_configuration` MODIFY `uid` VARCHAR(512) CHARACTER SET utf8 NOT NULL;
ALTER TABLE `kolab_cache_freebusy` MODIFY `uid` VARCHAR(512) CHARACTER SET utf8 NOT NULL;

View file

@ -183,4 +183,4 @@ CREATE TABLE "kolab_cache_freebusy" (
CREATE INDEX "kolab_cache_fb_uid2msguid" ON "kolab_cache_freebusy" ("folder_id", "uid", "msguid");
INSERT INTO "system" ("name", "value") VALUES ('libkolab-version', '2017071900');
INSERT INTO "system" ("name", "value") VALUES ('libkolab-version', '2018021300');

View file

@ -0,0 +1 @@
-- empty

View file

@ -156,4 +156,4 @@ CREATE TABLE kolab_cache_freebusy (
CREATE INDEX ix_freebusy_uid2msguid ON kolab_cache_freebusy(folder_id,uid,msguid);
INSERT INTO system (name, value) VALUES ('libkolab-version', '2017071900');
INSERT INTO system (name, value) VALUES ('libkolab-version', '2018021300');

View file

@ -0,0 +1 @@
-- empty