Compare commits
No commits in common. "master" and "mail_import_select_calendar" have entirely different histories.
master
...
mail_impor
17 changed files with 220 additions and 332 deletions
81
README
Normal file
81
README
Normal file
|
@ -0,0 +1,81 @@
|
|||
A calendar module for Roundcube
|
||||
-------------------------------
|
||||
|
||||
This plugin currently supports a local database as well as a Kolab groupware
|
||||
server as backends for calendar and event storage. For both drivers, some
|
||||
initialization of the local database is necessary. To do so, execute the
|
||||
SQL commands in drivers/<yourchoice>/SQL/<yourdatabase>.initial.sql
|
||||
|
||||
For some general calendar-based operations such as alarms handling or iCal
|
||||
parsing/exporting and UI widgets/style this plugins requires the `libcalendaring`
|
||||
and `libkolab` plugins which are also part of the Kolab Roundcube Plugins repository.
|
||||
Make sure these plugins are installed and configured correctly.
|
||||
|
||||
For recurring event computation, some utility classes from the Horde project
|
||||
are used. They are packaged in a slightly modified version with this plugin.
|
||||
|
||||
|
||||
REQUIREMENTS
|
||||
------------
|
||||
|
||||
Some functions are shared with other plugins and therefore being moved to
|
||||
library plugins. Thus in order to run the calendar plugin, you also need the
|
||||
following plugins installed:
|
||||
|
||||
* kolab/libcalendaring [1]
|
||||
* kolab/libkolab [1]
|
||||
|
||||
|
||||
INSTALLATION
|
||||
------------
|
||||
|
||||
For a manual installation of the calendar plugin (and its dependencies),
|
||||
execute the following steps. This will set it up with the database backend
|
||||
driver.
|
||||
|
||||
1. Get the source from git
|
||||
|
||||
$ cd /tmp
|
||||
$ git clone https://git.kolab.org/diffusion/RPK/roundcubemail-plugins-kolab.git
|
||||
$ cd /<path-to-roundcube>/plugins
|
||||
$ cp -r /tmp/roundcubemail-plugins-kolab/plugins/calendar .
|
||||
$ cp -r /tmp/roundcubemail-plugins-kolab/plugins/libcalendaring .
|
||||
$ cp -r /tmp/roundcubemail-plugins-kolab/plugins/libkolab .
|
||||
|
||||
2. Create calendar plugin configuration
|
||||
|
||||
$ cd calendar/
|
||||
$ cp config.inc.php.dist config.inc.php
|
||||
$ edit config.inc.php
|
||||
|
||||
3. Initialize the calendar database tables
|
||||
|
||||
$ cd ../../
|
||||
$ bin/initdb.sh --dir=plugins/calendar/drivers/database/SQL
|
||||
|
||||
4. Build css styles for the Elastic skin
|
||||
|
||||
$ lessc --relative-urls -x plugins/libkolab/skins/elastic/libkolab.less > plugins/libkolab/skins/elastic/libkolab.min.css
|
||||
|
||||
5. Enable the calendar plugin
|
||||
|
||||
$ edit config/config.inc.php
|
||||
|
||||
Add 'calendar' to the list of active plugins:
|
||||
|
||||
$config['plugins'] = array(
|
||||
(...)
|
||||
'calendar',
|
||||
);
|
||||
|
||||
|
||||
IMPORTANT
|
||||
---------
|
||||
|
||||
This plugin doesn't work with the Classic skin of Roundcube because no
|
||||
templates are available for that skin.
|
||||
|
||||
Use Roundcube `skins_allowed` option to limit skins available to the user
|
||||
or remove incompatible skins from the skins folder.
|
||||
|
||||
[1] https://git.kolab.org/diffusion/RPK/
|
68
README.md
68
README.md
|
@ -1,68 +0,0 @@
|
|||
## TLDR
|
||||
Contrary to other CalDAV forks, this one is based on the original calendar kolab-roundcube-plugins-mirror/calendar (which means the calendar itself is most up to date) and adds CalDAV capability on top of it. As far as I am aware, it is the most up to date version with the most bugfixes (April 2022).
|
||||
(installation instructions are at the bottom)
|
||||
|
||||
## Why is this needed?
|
||||
Unfortunately, the current situation about CalDAV support in roundcube is quite confusing. There are several plugins (/Forks) around that have CalDAV support but from what I found, all of them are slightly buggy or do not work anymore.
|
||||
All of them are based on <https://gitlab.awesome-it.de/kolab/roundcube-plugins>, a very old Fork which (as far as I can tell) is based on a version of kolab-roundcube-plugins-mirror/calendar that is over 10 years old.
|
||||
None of these forks incorporate updates from the original calendar, meaning it is only a matter of time until they are not compatible with roundcube anymore. I tried to change as little as possible in the original codebase and only added caldav support as a new driver - which means new updates from the roundcube team should be easy to incorporate.
|
||||
|
||||
## History of other Forks so far
|
||||
|
||||
### kolab-roundcube-plugins-mirror/calendar :
|
||||
This is the original calendar that all other forks are based on. It is working very well and is actively maintained but unfortunately, it does not have caldav support
|
||||
|
||||
### [https://gitlab.awesome-it.de/kolab/roundcube-plugins](awesome-it) :
|
||||
This is the "original fork" of the calendar. A lot of work was put into it and caldav is almost fully implemented. Unfortunately it has a few bugs / problems and most of them were not fixed in any other forks:
|
||||
- The birthday calendar is not supported by the caldav driver.
|
||||
- While the backend (mostly) supports adding all calendars from a dav-url, the front-end does not. That makes calendar handling a bit clunky and confusing.
|
||||
- Calendar colors have to be set manually and can not be loaded from DAV.
|
||||
- Adding and Removing calendars directly in the external source is not supported.
|
||||
- It prepares the codebase so multiple drivers can be used. But as far as I can tell, this feature is not used in the code and also not really supported by the front-end. This means, that it still only uses one driver but as a result adds a lot of unnecessary changes to the original codebase.
|
||||
|
||||
|
||||
### fasterit/roundcube_calendar :
|
||||
A fork of awesome-it to make it work with blind-coder/rcmcardav (a CardDAV plugin) by packing the outdated version of sabre/DAV inside the plugin. But it hasn't been maintained and is still based on a very outdated version of kolab-roundcube-plugins-mirror/calendar.
|
||||
|
||||
### texxasrulez/calendar :
|
||||
This is a fork of awesome-it with a few bugfixes to make it work with roundcube 1.3 but its maintainer does not seem to be active anymore.
|
||||
It is the most current fork of the original CalDAV fork. But unfortunately, it is treated as its own project (which means that it doesn't have any updates from the original calendar) and is focused primarily on nextcloud (which I don't really understand since nextcloud is using CalDAV anyway).
|
||||
Also, on top of still having the original bugs included, it is also still based on an ancient sabre/DAV version.
|
||||
|
||||
### texxasrulez/caldav_calendar :
|
||||
That one confuses me. It is from texxasrulez as well and seems to be the basis of Texxas but was abandoned in favour of texxasrulez/calendar. But it seems to be only a few commits behind texxasrulez/calendar.
|
||||
|
||||
|
||||
### What is this fork doing differently?
|
||||
|
||||
All CalDAV forks are based on faster-it which has a very different codebase to the original calendar because of its unfinished "multiple-driver" support. That makes it very difficult to get updates from the original calendar.
|
||||
|
||||
So I decided to ditch the "multiple driver" support (which isnt used anywway) and keep most changes in the CalDAV driver itself to stay compatible with the original calendar. I also added a ton of updates:
|
||||
- Based on the most recent version of the calendar plugin.
|
||||
- Uses the most recent version of sabre/dav (4.1.5)
|
||||
- Only minor changes in the existing code base, meaning that future updates of the calendar plugin should be able to be merged quite easily.
|
||||
- Added support for the birthday calendar.
|
||||
- Changed the behaviour from "per calendar" to "per CalDAV source".
|
||||
- All calendars from a source will be automatically added.
|
||||
- Calendars can be created and deleted directly at the CalDAV source.
|
||||
- ics support included.
|
||||
|
||||
### Why does this need a fork of libcalendaring?
|
||||
The original libcalendaring still uses sabre/vobject 3.5.3
|
||||
In order to be compatible with other plugins (and because version 3.5.3 is ancient), I updated it to version 4.1.5
|
||||
The problem is, that sabre/vobject makes use of DateTimeImmutable which libcalendaring does not expect.
|
||||
It only needs minor changes to account for that, but unfortunately the roundcube-project does not accept pull requests...
|
||||
|
||||
### Installation
|
||||
I havent published this as a plugin yet, so you have to instruct composer to install directly from github. Run the following commands in the roundcubemail folder
|
||||
(If you get an error that the "API rate limit" has been exceeded and you need an GitHub OAuth token, just follow the instructions in the console - you will need a GitHub account).
|
||||
```
|
||||
cd /pathTo/roundcubemail
|
||||
|
||||
composer config repositories.calendar vcs https://github.com/JodliDev/calendar
|
||||
composer config repositories.libcalendaring vcs https://github.com/JodliDev/libcalendaring
|
||||
composer config minimum-stability dev
|
||||
composer require kolab/calendar
|
||||
|
||||
bin/initdb.sh --dir=plugins/calendar/drivers/caldav/SQL
|
||||
```
|
126
calendar.php
126
calendar.php
|
@ -1251,31 +1251,23 @@ $("#rcmfd_new_category").keypress(function(event) {
|
|||
$noreply = intval($noreply) || $status == 'needs-action' || $itip_sending === 0;
|
||||
$reload = $event['calendar'] != $ev['calendar'] || !empty($event['recurrence']) ? 2 : 1;
|
||||
$emails = $this->get_user_emails();
|
||||
$ownedResourceEmails = $this->owned_resources_emails();
|
||||
$organizer = null;
|
||||
$resourceConfirmation = false;
|
||||
|
||||
foreach ($event['attendees'] as $i => $attendee) {
|
||||
if ($attendee['role'] == 'ORGANIZER') {
|
||||
$organizer = $attendee;
|
||||
}
|
||||
else if (!empty($attendee['email']) && in_array_nocase($attendee['email'], $emails)) {
|
||||
else if (!empty($attendee['email']) && in_array(strtolower($attendee['email']), $emails)) {
|
||||
$reply_sender = $attendee['email'];
|
||||
}
|
||||
else if (!empty($attendee['cutype']) && $attendee['cutype'] == 'RESOURCE' && !empty($attendee['email']) && in_array_nocase($attendee['email'], $ownedResourceEmails)) {
|
||||
$resourceConfirmation = true;
|
||||
// Note on behalf of which resource this update is going to be sent out
|
||||
$event['_resource'] = $attendee['email'];
|
||||
}
|
||||
}
|
||||
|
||||
if (!$noreply) {
|
||||
$itip = $this->load_itip();
|
||||
$itip->set_sender_email($reply_sender);
|
||||
$event['thisandfuture'] = $event['_savemode'] == 'future';
|
||||
$bodytextprefix = $resourceConfirmation ? 'itipmailbodyresource' : 'itipmailbody';
|
||||
|
||||
if ($organizer && $itip->send_itip_message($event, 'REPLY', $organizer, 'itipsubject' . $status, $bodytextprefix . $status)) {
|
||||
if ($organizer && $itip->send_itip_message($event, 'REPLY', $organizer, 'itipsubject' . $status, 'itipmailbody' . $status)) {
|
||||
$mailto = !empty($organizer['name']) ? $organizer['name'] : $organizer['email'];
|
||||
$msg = $this->gettext(['name' => 'sentresponseto', 'vars' => ['mailto' => $mailto]]);
|
||||
|
||||
|
@ -2026,12 +2018,10 @@ $("#rcmfd_new_category").keypress(function(event) {
|
|||
}
|
||||
|
||||
$identity['emails'][] = $this->rc->user->get_username();
|
||||
$identity['ownedResources'] = $this->owned_resources_emails();
|
||||
$settings['identity'] = [
|
||||
'name' => $identity['name'],
|
||||
'email' => strtolower($identity['email']),
|
||||
'emails' => ';' . strtolower(join(';', $identity['emails'])),
|
||||
'ownedResources' => ';' . strtolower(join(';', $identity['ownedResources']))
|
||||
'emails' => ';' . strtolower(join(';', $identity['emails']))
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -2412,7 +2402,7 @@ $("#rcmfd_new_category").keypress(function(event) {
|
|||
}
|
||||
|
||||
// set new organizer identity
|
||||
if ($organizer !== false && !empty($identity)) {
|
||||
if ($organizer !== false && $identity) {
|
||||
$event['attendees'][$organizer]['name'] = $identity['name'];
|
||||
$event['attendees'][$organizer]['email'] = $identity['email'];
|
||||
}
|
||||
|
@ -2422,7 +2412,7 @@ $("#rcmfd_new_category").keypress(function(event) {
|
|||
unset($event['attendees'][$owner]['rsvp']);
|
||||
}
|
||||
// fallback to the selected identity
|
||||
else if ($organizer === false && !empty($identity)) {
|
||||
else if ($organizer === false && $identity) {
|
||||
$event['attendees'][] = [
|
||||
'role' => 'ORGANIZER',
|
||||
'name' => $identity['name'],
|
||||
|
@ -2889,21 +2879,6 @@ $("#rcmfd_new_category").keypress(function(event) {
|
|||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* List email addressed of owned resources
|
||||
*/
|
||||
private function owned_resources_emails()
|
||||
{
|
||||
$results = [];
|
||||
if ($directory = $this->resources_directory()) {
|
||||
foreach ($directory->load_resources($_SESSION['kolab_dn'], 5000, 'owner') as $rec) {
|
||||
$results[] = $rec['email'];
|
||||
}
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
|
||||
/**** Event invitation plugin hooks ****/
|
||||
|
||||
/**
|
||||
|
@ -3185,16 +3160,10 @@ $("#rcmfd_new_category").keypress(function(event) {
|
|||
*/
|
||||
private function mail_agenda_event_row($event, $class = '')
|
||||
{
|
||||
if (!empty($event['allday'])) {
|
||||
$time = $this->gettext('all-day');
|
||||
}
|
||||
else {
|
||||
$start = is_object($event['start']) ? clone $event['start'] : $event['start'];
|
||||
$end = is_object($event['end']) ? clone $event['end'] : $event['end'];
|
||||
|
||||
$time = $this->rc->format_date($start, $this->rc->config->get('time_format'))
|
||||
. ' - ' . $this->rc->format_date($end, $this->rc->config->get('time_format'));
|
||||
}
|
||||
$time = !empty($event['allday']) ? $this->gettext('all-day') :
|
||||
$this->rc->format_date($event['start'], $this->rc->config->get('time_format'))
|
||||
. ' - ' .
|
||||
$this->rc->format_date($event['end'], $this->rc->config->get('time_format'));
|
||||
|
||||
return html::div(rtrim('event-row ' . ($class ?: $event['className'])),
|
||||
html::span('event-date', $time)
|
||||
|
@ -3259,7 +3228,7 @@ $("#rcmfd_new_category").keypress(function(event) {
|
|||
// get prepared inline UI for this event object
|
||||
if ($ical_objects->method) {
|
||||
$append = '';
|
||||
$date_str = $this->rc->format_date(clone $event['start'], $this->rc->config->get('date_format'), empty($event['start']->_dateonly));
|
||||
$date_str = $this->rc->format_date($event['start'], $this->rc->config->get('date_format'), empty($event['start']->_dateonly));
|
||||
$date = new DateTime($event['start']->format('Y-m-d') . ' 12:00:00', new DateTimeZone('UTC'));
|
||||
|
||||
// prepare a small agenda preview to be filled with actual event data on async request
|
||||
|
@ -3456,38 +3425,39 @@ $("#rcmfd_new_category").keypress(function(event) {
|
|||
|
||||
// only update attendee status
|
||||
if ($event['_method'] == 'REPLY') {
|
||||
$existing_attendee_index = -1;
|
||||
// try to identify the attendee using the email sender address
|
||||
$existing_attendee = -1;
|
||||
$existing_attendee_emails = [];
|
||||
|
||||
foreach ($existing['attendees'] as $i => $attendee) {
|
||||
$existing_attendee_emails[] = $attendee['email'];
|
||||
if ($this->itip->compare_email($attendee['email'], $event['_sender'], $event['_sender_utf'])) {
|
||||
$existing_attendee = $i;
|
||||
}
|
||||
}
|
||||
|
||||
$event_attendee = null;
|
||||
$update_attendees = [];
|
||||
|
||||
if ($attendee = $this->itip->find_reply_attendee($event)) {
|
||||
$event_attendee = $attendee;
|
||||
$update_attendees[] = $attendee;
|
||||
$metadata['fallback'] = $attendee['status'];
|
||||
$metadata['attendee'] = $attendee['email'];
|
||||
$metadata['rsvp'] = !empty($attendee['rsvp']) || $attendee['role'] != 'NON-PARTICIPANT';
|
||||
foreach ($event['attendees'] as $attendee) {
|
||||
if ($this->itip->compare_email($attendee['email'], $event['_sender'], $event['_sender_utf'])) {
|
||||
$event_attendee = $attendee;
|
||||
$update_attendees[] = $attendee;
|
||||
$metadata['fallback'] = $attendee['status'];
|
||||
$metadata['attendee'] = $attendee['email'];
|
||||
$metadata['rsvp'] = !empty($attendee['rsvp']) || $attendee['role'] != 'NON-PARTICIPANT';
|
||||
|
||||
$existing_attendee_emails = [];
|
||||
|
||||
// Find the attendee to update
|
||||
foreach ($existing['attendees'] as $i => $existing_attendee) {
|
||||
$existing_attendee_emails[] = $existing_attendee['email'];
|
||||
if ($this->itip->compare_email($existing_attendee['email'], $attendee['email'])) {
|
||||
$existing_attendee_index = $i;
|
||||
if ($attendee['status'] != 'DELEGATED') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($attendee['status'] == 'DELEGATED') {
|
||||
//Also find and copy the delegatee
|
||||
$delegatee_email = $attendee['email'];
|
||||
$delegatees = array_filter($event['attendees'], function($attendee) use ($delegatee_email){ return $attendee['role'] != 'ORGANIZER' && $this->itip->compare_email($attendee['delegated-from'], $delegatee_email); });
|
||||
|
||||
if ($delegatee = $this->itip->find_attendee_by_email($event['attendees'], 'delegated-from', $attendee['email'])) {
|
||||
$update_attendees[] = $delegatee;
|
||||
if (!in_array_nocase($delegatee['email'], $existing_attendee_emails)) {
|
||||
$existing['attendees'][] = $delegated_attendee;
|
||||
}
|
||||
// also copy delegate attendee
|
||||
else if (!empty($attendee['delegated-from'])
|
||||
&& $this->itip->compare_email($attendee['delegated-from'], $event['_sender'], $event['_sender_utf'])
|
||||
) {
|
||||
$update_attendees[] = $attendee;
|
||||
if (!in_array_nocase($attendee['email'], $existing_attendee_emails)) {
|
||||
$existing['attendees'][] = $attendee;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3505,9 +3475,29 @@ $("#rcmfd_new_category").keypress(function(event) {
|
|||
}
|
||||
}
|
||||
|
||||
// Accept sender as a new participant (different email in From: and the iTip)
|
||||
// Use ATTENDEE entry from the iTip with replaced email address
|
||||
if (!$event_attendee) {
|
||||
// remove the organizer
|
||||
$itip_attendees = array_filter(
|
||||
$event['attendees'],
|
||||
function($item) { return $item['role'] != 'ORGANIZER'; }
|
||||
);
|
||||
|
||||
// there must be only one attendee
|
||||
if (is_array($itip_attendees) && count($itip_attendees) == 1) {
|
||||
$event_attendee = $itip_attendees[key($itip_attendees)];
|
||||
$event_attendee['email'] = $event['_sender'];
|
||||
$update_attendees[] = $event_attendee;
|
||||
$metadata['fallback'] = $event_attendee['status'];
|
||||
$metadata['attendee'] = $event_attendee['email'];
|
||||
$metadata['rsvp'] = !empty($event_attendee['rsvp']) || $event_attendee['role'] != 'NON-PARTICIPANT';
|
||||
}
|
||||
}
|
||||
|
||||
// found matching attendee entry in both existing and new events
|
||||
if ($existing_attendee_index >= 0 && $event_attendee) {
|
||||
$existing['attendees'][$existing_attendee_index] = $event_attendee;
|
||||
if ($existing_attendee >= 0 && $event_attendee) {
|
||||
$existing['attendees'][$existing_attendee] = $event_attendee;
|
||||
$success = $this->driver->update_attendees($existing, $update_attendees);
|
||||
}
|
||||
// update the entire attendees block
|
||||
|
|
|
@ -459,7 +459,7 @@ function rcube_calendar_ui(settings)
|
|||
for (var j=0; j < num_attendees; j++) {
|
||||
data = event.attendees[j];
|
||||
if (data.email) {
|
||||
if (data.role != 'ORGANIZER' && is_this_me(data.email)) {
|
||||
if (data.role != 'ORGANIZER' && settings.identity.emails.indexOf(';'+data.email) >= 0) {
|
||||
mystatus = (data.status || 'UNKNOWN').toLowerCase();
|
||||
if (data.status == 'NEEDS-ACTION' || data.status == 'TENTATIVE' || data.rsvp)
|
||||
rsvp = mystatus;
|
||||
|
@ -2379,14 +2379,6 @@ function rcube_calendar_ui(settings)
|
|||
add_attendee($.extend({ role:'REQ-PARTICIPANT', status:'NEEDS-ACTION', cutype:'RESOURCE' }, resource));
|
||||
}
|
||||
|
||||
var is_this_me = function(email)
|
||||
{
|
||||
if (settings.identity.emails.indexOf(';'+email) >= 0 || settings.identity.ownedResources.indexOf(';'+email) >= 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// when the user accepts or declines an event invitation
|
||||
var event_rsvp = function(response, delegate, replymode, event)
|
||||
{
|
||||
|
@ -2421,8 +2413,7 @@ function rcube_calendar_ui(settings)
|
|||
attendees = [];
|
||||
for (var data, i=0; i < me.selected_event.attendees.length; i++) {
|
||||
data = me.selected_event.attendees[i];
|
||||
//FIXME this can only work if there is a single resource per invitation
|
||||
if (is_this_me(String(data.email).toLowerCase())) {
|
||||
if (settings.identity.emails.indexOf(';'+String(data.email).toLowerCase()) >= 0) {
|
||||
data.status = response.toUpperCase();
|
||||
data.rsvp = 0; // unset RSVP flag
|
||||
|
||||
|
@ -2962,7 +2953,7 @@ function rcube_calendar_ui(settings)
|
|||
$dialog = $('<iframe>').attr('src', rcmail.url('calendar', params)).on('load', function() {
|
||||
var contents = $(this).contents();
|
||||
contents.find('#calendar-name')
|
||||
.prop('disabled', !calendar.editable && !calendar.editable_name)
|
||||
.prop('disabled', !calendar.editable)
|
||||
.val(calendar.editname || calendar.name)
|
||||
.select();
|
||||
contents.find('#calendar-color')
|
||||
|
@ -3616,7 +3607,7 @@ function rcube_calendar_ui(settings)
|
|||
if (node && node.id && me.calendars[node.id]) {
|
||||
me.select_calendar(node.id, true);
|
||||
rcmail.enable_command('calendar-edit', 'calendar-showurl', 'calendar-showfburl', true);
|
||||
rcmail.enable_command('calendar-delete', me.calendars[node.id].editable || me.calendars[node.id].deletable);
|
||||
rcmail.enable_command('calendar-delete', me.calendars[node.id].editable);
|
||||
rcmail.enable_command('calendar-remove', me.calendars[node.id] && me.calendars[node.id].removable);
|
||||
}
|
||||
});
|
||||
|
@ -4235,7 +4226,7 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
|||
rcmail.register_command('print', function(){ cal.print_calendars(); }, true);
|
||||
|
||||
// configure list operations
|
||||
rcmail.register_command('calendar-sources-new', cal.calendar_new_source, true);
|
||||
rcmail.register_command('calendar-sources-add', cal.calendar_new_source, true);
|
||||
rcmail.register_command('calendar-sources-delete', cal.calendar_delete_sources, true);
|
||||
rcmail.register_command('calendar-create', function(){ cal.calendar_edit_dialog(null); }, true);
|
||||
rcmail.register_command('calendar-edit', function(){ cal.calendar_edit_dialog(cal.calendars[cal.selected_calendar]); }, false);
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"description": "Calendar plugin",
|
||||
"homepage": "https://git.kolab.org/diffusion/RPK/",
|
||||
"license": "AGPLv3",
|
||||
"version": "3.5.11",
|
||||
"version": "3.5.7",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Thomas Bruederli",
|
||||
|
@ -30,7 +30,7 @@
|
|||
"require": {
|
||||
"php": ">=5.5",
|
||||
"roundcube/plugin-installer": ">=0.1.3",
|
||||
"jodlidev/libcalendaring": "dev-master",
|
||||
"jodlidev/libcalendaring": ">=3.4.0",
|
||||
"kolab/libkolab": ">=3.4.0",
|
||||
"sabre/dav": ">=4.1.5"
|
||||
},
|
||||
|
|
|
@ -30,13 +30,13 @@ $config['calendar_driver'] = "caldav";
|
|||
|
||||
// Enable debugging output for iCAL/CalDAV drivers
|
||||
$config['calendar_caldav_debug'] = false;
|
||||
$config['calendar_ical_debug'] = false;
|
||||
|
||||
// default calendar view (agendaDay, agendaWeek, month)
|
||||
$config['calendar_default_view'] = "agendaWeek";
|
||||
|
||||
// show a birthdays calendar from the user's address book(s)
|
||||
$config['calendar_contact_birthdays'] = false;
|
||||
$config['birthday_calendar'] = array('color' => 'fffb00');
|
||||
|
||||
// timeslots per hour (1, 2, 3, 4, 6)
|
||||
$config['calendar_timeslots'] = 2;
|
||||
|
@ -143,20 +143,6 @@ $config['kolab_invitation_calendars'] = false;
|
|||
// %i - Calendar UUID
|
||||
// $config['calendar_caldav_url'] = 'http://%h/iRony/calendars/%u/%i';
|
||||
|
||||
// List of CalDAV sources that should be allready installed.
|
||||
// They will be added when the calendar section is accessed for the first time by a user.
|
||||
// For 'caldav_user' and 'caldav_url' the following replacement variables are supported:
|
||||
// %u - Current webmail user name
|
||||
// For 'caldav_pass' %p is replaced by the current user's password.
|
||||
// $config['calendar_caldav_preinstalled_sources'] = array(
|
||||
// 'name' => array(
|
||||
// 'caldav_user' => '%u',
|
||||
// 'caldav_pass' => '%p',
|
||||
// 'caldav_url' => 'https://example.net/dav',
|
||||
// 'showAlarms' => 1
|
||||
// )
|
||||
// );
|
||||
|
||||
// Driver to provide a resource directory ('ldap' is the only implementation yet).
|
||||
// Leave empty or commented to disable resources support.
|
||||
// $config['calendar_resources_driver'] = 'ldap';
|
||||
|
|
|
@ -37,8 +37,8 @@ CREATE TABLE IF NOT EXISTS `caldav_sources` (
|
|||
CREATE TABLE IF NOT EXISTS `caldav_calendars` (
|
||||
`calendar_id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int(10) UNSIGNED NOT NULL DEFAULT '0',
|
||||
`source_id` int(10) UNSIGNED DEFAULT NULL,
|
||||
`name` varchar(255) CHARACTER SET utf8mb4 NOT NULL,
|
||||
`source_id` int(10) UNSIGNED NOT NULL DEFAULT '0',
|
||||
`name` varchar(255) NOT NULL,
|
||||
`color` varchar(8) NOT NULL,
|
||||
`showalarms` tinyint(1) NOT NULL DEFAULT '1',
|
||||
|
||||
|
@ -55,7 +55,7 @@ CREATE TABLE IF NOT EXISTS `caldav_calendars` (
|
|||
REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `fk_caldav_calendars_sources` FOREIGN KEY (`source_id`)
|
||||
REFERENCES `caldav_sources`(`source_id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci */;
|
||||
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `caldav_events` (
|
||||
`event_id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
|
@ -70,10 +70,10 @@ CREATE TABLE IF NOT EXISTS `caldav_events` (
|
|||
`start` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
|
||||
`end` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
|
||||
`recurrence` varchar(255) DEFAULT NULL,
|
||||
`title` varchar(255) CHARACTER SET utf8mb4 NOT NULL,
|
||||
`description` text CHARACTER SET utf8mb4 NOT NULL,
|
||||
`location` varchar(255) CHARACTER SET utf8mb4 NOT NULL DEFAULT '',
|
||||
`categories` varchar(255) CHARACTER SET utf8mb4 NOT NULL DEFAULT '',
|
||||
`title` varchar(255) NOT NULL,
|
||||
`description` text NOT NULL,
|
||||
`location` varchar(255) NOT NULL DEFAULT '',
|
||||
`categories` varchar(255) NOT NULL DEFAULT '',
|
||||
`url` varchar(255) NOT NULL DEFAULT '',
|
||||
`all_day` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`free_busy` tinyint(1) NOT NULL DEFAULT '0',
|
||||
|
@ -94,7 +94,7 @@ CREATE TABLE IF NOT EXISTS `caldav_events` (
|
|||
INDEX `caldav_calendar_notify_idx` (`calendar_id`,`notifyat`),
|
||||
CONSTRAINT `fk_caldav_events_calendar_id` FOREIGN KEY (`calendar_id`)
|
||||
REFERENCES `caldav_calendars`(`calendar_id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci */;
|
||||
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `caldav_attachments` (
|
||||
`attachment_id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
*/
|
||||
|
||||
-- SQLINES LICENSE FOR EVALUATION USE ONLY
|
||||
CREATE SEQUENCE IF NOT EXISTS caldav_sources_seq;
|
||||
CREATE SEQUENCE caldav_sources_seq;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS caldav_sources (
|
||||
source_id int CHECK (source_id > 0) NOT NULL DEFAULT NEXTVAL ('caldav_sources_seq'),
|
||||
|
@ -36,12 +36,12 @@ CREATE TABLE IF NOT EXISTS caldav_sources (
|
|||
) /* SQLINES DEMO *** DB */ /* SQLINES DEMO *** ET utf8 COLLATE utf8_general_ci */;
|
||||
|
||||
-- SQLINES LICENSE FOR EVALUATION USE ONLY
|
||||
CREATE SEQUENCE IF NOT EXISTS caldav_calendars_seq;
|
||||
CREATE SEQUENCE caldav_calendars_seq;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS caldav_calendars (
|
||||
calendar_id int CHECK (calendar_id > 0) NOT NULL DEFAULT NEXTVAL ('caldav_calendars_seq'),
|
||||
user_id int CHECK (user_id > 0) NOT NULL DEFAULT '0',
|
||||
source_id int CHECK (source_id > 0) DEFAULT NULL,
|
||||
source_id int CHECK (source_id > 0) NOT NULL DEFAULT '0',
|
||||
name varchar(255) NOT NULL,
|
||||
color varchar(8) NOT NULL,
|
||||
showalarms smallint NOT NULL DEFAULT '1',
|
||||
|
@ -61,23 +61,23 @@ CREATE TABLE IF NOT EXISTS caldav_calendars (
|
|||
REFERENCES caldav_sources(source_id) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) /* SQLINES DEMO *** DB */ /* SQLINES DEMO *** ET utf8 COLLATE utf8_general_ci */;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS caldav_user_name_idx ON caldav_calendars (user_id, name);
|
||||
CREATE INDEX caldav_user_name_idx ON caldav_calendars (user_id, name);
|
||||
|
||||
-- SQLINES LICENSE FOR EVALUATION USE ONLY
|
||||
CREATE SEQUENCE IF NOT EXISTS caldav_events_seq;
|
||||
CREATE SEQUENCE caldav_events_seq;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS caldav_events (
|
||||
event_id int CHECK (event_id > 0) NOT NULL DEFAULT NEXTVAL ('caldav_events_seq'),
|
||||
calendar_id int CHECK (calendar_id > 0) NOT NULL DEFAULT '0',
|
||||
recurrence_id int NOT NULL DEFAULT '0',
|
||||
recurrence_id int CHECK (recurrence_id > 0) NOT NULL DEFAULT '0',
|
||||
uid varchar(255) NOT NULL DEFAULT '',
|
||||
instance varchar(16) NOT NULL DEFAULT '',
|
||||
isexception smallint NOT NULL DEFAULT '0',
|
||||
created timestamp(0) NOT NULL DEFAULT '1000-01-01 00:00:00',
|
||||
changed timestamp(0) NOT NULL DEFAULT '1000-01-01 00:00:00',
|
||||
sequence int NOT NULL DEFAULT '0',
|
||||
sequence int CHECK (sequence > 0) NOT NULL DEFAULT '0',
|
||||
start timestamp(0) NOT NULL DEFAULT '1000-01-01 00:00:00',
|
||||
"end" timestamp(0) NOT NULL DEFAULT '1000-01-01 00:00:00',
|
||||
end timestamp(0) NOT NULL DEFAULT '1000-01-01 00:00:00',
|
||||
recurrence varchar(255) DEFAULT NULL,
|
||||
title varchar(255) NOT NULL,
|
||||
description text NOT NULL,
|
||||
|
@ -103,12 +103,12 @@ CREATE TABLE IF NOT EXISTS caldav_events (
|
|||
REFERENCES caldav_calendars(calendar_id) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) /* SQLINES DEMO *** DB */ /* SQLINES DEMO *** ET utf8 COLLATE utf8_general_ci */;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS caldav_uid_idx ON caldav_events (uid);
|
||||
CREATE INDEX IF NOT EXISTS caldav_recurrence_idx ON caldav_events (recurrence_id);
|
||||
CREATE INDEX IF NOT EXISTS caldav_calendar_notify_idx ON caldav_events (calendar_id,notifyat);
|
||||
CREATE INDEX caldav_uid_idx ON caldav_events (uid);
|
||||
CREATE INDEX caldav_recurrence_idx ON caldav_events (recurrence_id);
|
||||
CREATE INDEX caldav_calendar_notify_idx ON caldav_events (calendar_id,notifyat);
|
||||
|
||||
-- SQLINES LICENSE FOR EVALUATION USE ONLY
|
||||
CREATE SEQUENCE IF NOT EXISTS caldav_attachments_seq;
|
||||
CREATE SEQUENCE caldav_attachments_seq;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS caldav_attachments (
|
||||
attachment_id int CHECK (attachment_id > 0) NOT NULL DEFAULT NEXTVAL ('caldav_attachments_seq'),
|
||||
|
@ -122,4 +122,4 @@ CREATE TABLE IF NOT EXISTS caldav_attachments (
|
|||
REFERENCES caldav_events(event_id) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) /* SQLINES DEMO *** DB */ /* SQLINES DEMO *** ET utf8 COLLATE utf8_general_ci */;
|
||||
|
||||
INSERT INTO system (name, value) VALUES ('calendar-caldav-version', '2021082400') ON CONFLICT (name) DO UPDATE SET value = excluded.value;
|
||||
REPLACE INTO `system` (name, value) SELECT ('calendar-caldav-version', '2021082400');
|
|
@ -33,7 +33,7 @@ CREATE TABLE IF NOT EXISTS `caldav_sources` (
|
|||
CREATE TABLE IF NOT EXISTS `caldav_calendars` (
|
||||
`calendar_id` INTEGER NOT NULL PRIMARY KEY,
|
||||
`user_id` INTEGER NOT NULL DEFAULT '0',
|
||||
`source_id` INTEGER DEFAULT NULL,
|
||||
`source_id` INTEGER NOT NULL DEFAULT '0',
|
||||
`name` TEXT NOT NULL,
|
||||
`color` TEXT NOT NULL,
|
||||
`showalarms` tinyINTEGER NOT NULL DEFAULT '1',
|
||||
|
|
|
@ -388,11 +388,7 @@ class caldav_client extends Sabre\DAV\Client
|
|||
</C:mkcalendar>";
|
||||
|
||||
$response = $this->request('MKCALENDAR', $path, $body, $headers);
|
||||
if($response['statusCode'] !== 201) {
|
||||
rcmail::console('Could not create calendar. Response:' .print_r($response, true));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return $response["statusCode"] === 201;
|
||||
}
|
||||
|
||||
public function delete_calendar() {
|
||||
|
|
|
@ -101,41 +101,9 @@ class caldav_driver extends calendar_driver
|
|||
if(self::$debug === null)
|
||||
self::$debug = $this->rc->config->get('calendar_caldav_debug', False);
|
||||
|
||||
$this->_setup_preinstalled_sources();
|
||||
$this->_read_calendars();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup preinstalled sources defined in config file
|
||||
*/
|
||||
protected function _setup_preinstalled_sources()
|
||||
{
|
||||
$preinstalled_sources = $this->rc->config->get('calendar_caldav_preinstalled_sources', FALSE);
|
||||
if ($preinstalled_sources && is_array($preinstalled_sources)) {
|
||||
$username = $this->rc->get_user_name();
|
||||
$password = $this->rc->get_user_password();
|
||||
|
||||
foreach ($preinstalled_sources as $cal){
|
||||
$url = $cal['caldav_url'];
|
||||
$user = $cal['caldav_user'];
|
||||
$pass = $cal['caldav_pass'];
|
||||
|
||||
$url = str_replace('%u', $username, $url);
|
||||
$user = str_replace('%u', $username, $user);
|
||||
$pass = str_replace('%p', $password, $pass);
|
||||
|
||||
$cal['caldav_url'] = $url;
|
||||
$cal['caldav_user'] = $user;
|
||||
$cal['caldav_pass'] = $pass;
|
||||
|
||||
if (!$this->create_source($cal)) {
|
||||
$error_msg = 'Unable to add default calendars' . ($this->last_error ? ': ' . $this->last_error :'');
|
||||
$this->rc->output->show_message($error_msg, 'error');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read available calendars for the current user and store them internally
|
||||
*/
|
||||
|
@ -173,6 +141,7 @@ class caldav_driver extends calendar_driver
|
|||
|
||||
if($arr['is_ical']) {
|
||||
$this->sync_clients[$arr['id']] = new ical_sync($arr);
|
||||
$arr["readonly"] = true;
|
||||
$arr["editable"] = false;
|
||||
$arr["deletable"] = true;
|
||||
$arr["editable_name"] = true;
|
||||
|
@ -217,16 +186,16 @@ class caldav_driver extends calendar_driver
|
|||
|
||||
if (empty($active) || !in_array($id, $hidden)) {
|
||||
$calendars[$id] = array(
|
||||
'id' => $id,
|
||||
'name' => $this->cal->gettext('birthdays'),
|
||||
'listname' => $this->cal->gettext('birthdays'),
|
||||
'color' => $prefs['color'],
|
||||
'showalarms' => (bool)$this->rc->config->get('calendar_birthdays_alarm_type'),
|
||||
'active' => !in_array($id, $hidden),
|
||||
'group' => 'x-birthdays',
|
||||
'editable' => false,
|
||||
'default' => false,
|
||||
'children' => false,
|
||||
'id' => $id,
|
||||
'name' => $this->cal->gettext('birthdays'),
|
||||
'listname' => $this->cal->gettext('birthdays'),
|
||||
'color' => $prefs['color'],
|
||||
'showalarms' => (bool)$this->rc->config->get('calendar_birthdays_alarm_type'),
|
||||
'active' => !in_array($id, $hidden),
|
||||
'group' => 'x-birthdays',
|
||||
'editable' => false,
|
||||
'default' => false,
|
||||
'children' => false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -272,7 +241,7 @@ class caldav_driver extends calendar_driver
|
|||
$source['caldav_pass'] = $this->_decrypt_pass($source['caldav_pass']);
|
||||
|
||||
$server_url = self::_encode_url($source['caldav_url']);
|
||||
$server_path = rtrim(parse_url($server_url, PHP_URL_PATH), '/');
|
||||
$server_path = parse_url($server_url, PHP_URL_PATH);
|
||||
$calId = $this->cal->generate_uid();
|
||||
$path = "/calendars/$source[caldav_user]/$calId";
|
||||
|
||||
|
@ -301,7 +270,7 @@ class caldav_driver extends calendar_driver
|
|||
if($this->rc->db->affected_rows($result)) continue;
|
||||
|
||||
$cal = array(
|
||||
'caldav_url' => $calendar['href'],
|
||||
'caldav_url' => self::_encode_url($calendar['href']),
|
||||
'name' => $calendar['name'],
|
||||
'color' => $calendar['color']
|
||||
);
|
||||
|
@ -333,36 +302,15 @@ class caldav_driver extends calendar_driver
|
|||
public function create_source($source)
|
||||
{
|
||||
$source['caldav_url'] = self::_encode_url($source['caldav_url']);
|
||||
|
||||
// Re-discover all existing calendars systematically
|
||||
try {
|
||||
|
||||
try {
|
||||
$calendars = $this->_autodiscover_calendars($source);
|
||||
}
|
||||
}
|
||||
catch(Exception $e) {
|
||||
self::debug_log($e);
|
||||
$this->rc->output->show_message($this->cal->gettext('source_notadded_error'), 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove local data associated with deprecated calendars
|
||||
$caldav_urls = array_column($calendars, 'href');
|
||||
$query = $this->rc->db->query(
|
||||
"DELETE FROM " . $this->db_calendars . " WHERE user_id=? AND caldav_url NOT IN ('" . implode("','", $caldav_urls) . "')",
|
||||
$this->rc->user->ID);
|
||||
$this->rc->db->affected_rows($query);
|
||||
|
||||
// Skip update if the set of available calendars matches
|
||||
$result = $this->rc->db->query(
|
||||
"SELECT calendar_id FROM " . $this->db_calendars . " WHERE user_id=? AND caldav_url LIKE ?",
|
||||
$this->rc->user->ID, $source['caldav_url'] . '%');
|
||||
$count_cur = $this->rc->db->num_rows($result);
|
||||
$count_avail = count($calendars);
|
||||
if ($count_cur == $count_avail)
|
||||
{
|
||||
self::debug_log("Skip source update.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if(count($calendars)) {
|
||||
$pass = isset($source['caldav_pass']) ? $this->_encrypt_pass($source['caldav_pass']) : null;
|
||||
$db_source_result = $this->rc->db->query(
|
||||
|
@ -412,11 +360,11 @@ class caldav_driver extends calendar_driver
|
|||
$this->rc->user->ID,
|
||||
$source ? $source['source_id'] : null,
|
||||
$prop['name'],
|
||||
isset($prop['color']) ? $prop['color'] : '#cc0000',
|
||||
$prop['color'],
|
||||
$prop['showalarms']?1:0,
|
||||
$prop['caldav_url'],
|
||||
isset($prop["caldav_tag"]) ? $prop["caldav_tag"] : null,
|
||||
$prop['is_ical']?1:0
|
||||
!!$prop['is_ical']
|
||||
);
|
||||
|
||||
if ($result)
|
||||
|
@ -555,11 +503,6 @@ class caldav_driver extends calendar_driver
|
|||
{
|
||||
//$event = $this->_save_preprocess($event);
|
||||
|
||||
//TODO: proper 4 byte character (eg emoticons) handling
|
||||
//utf8 in mysql only supports 3 byte characters, so this throws an error if there are emoticons in the description.
|
||||
//For now we just remove them. But instead of removing, we should prepare the database for them (by using utf8mb4)
|
||||
$desc = preg_replace('/[\xF0-\xF7].../s', '', strval($event['description']));
|
||||
|
||||
$this->rc->db->query(sprintf(
|
||||
"INSERT INTO " . $this->db_events . "
|
||||
(calendar_id, created, changed, uid, recurrence_id, instance, isexception, %s, %s, all_day, recurrence,
|
||||
|
@ -581,7 +524,7 @@ class caldav_driver extends calendar_driver
|
|||
intval($event['all_day']),
|
||||
$event['_recurrence'],
|
||||
strval($event['title']),
|
||||
$desc,
|
||||
strval($event['description']),
|
||||
strval($event['location']),
|
||||
join(',', (array)$event['categories']),
|
||||
strval($event['url']),
|
||||
|
@ -937,20 +880,19 @@ class caldav_driver extends calendar_driver
|
|||
}
|
||||
|
||||
// compose vcalendar-style recurrencue rule from structured data
|
||||
$rrule = !empty($event['recurrence']) ? libcalendaring::to_rrule($event['recurrence']) : '';
|
||||
|
||||
$sensitivity = strtolower($event['sensitivity']);
|
||||
$free_busy = strtolower($event['free_busy']);
|
||||
|
||||
$rrule = $event['recurrence'] ? libcalendaring::to_rrule($event['recurrence']) : '';
|
||||
$event['_recurrence'] = rtrim($rrule, ';');
|
||||
$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;
|
||||
$event['free_busy'] = intval($this->free_busy_map[strtolower($event['free_busy'])]);
|
||||
$event['sensitivity'] = intval($this->sensitivity_map[strtolower($event['sensitivity'])]);
|
||||
|
||||
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);
|
||||
|
||||
|
@ -998,15 +940,8 @@ class caldav_driver extends calendar_driver
|
|||
$sql_set[] = $this->rc->db->quote_identifier($col) . '=' . $this->rc->db->quote($event[$col]->format(self::DB_DATE_FORMAT));
|
||||
else if (is_array($event[$col]))
|
||||
$sql_set[] = $this->rc->db->quote_identifier($col) . '=' . $this->rc->db->quote(join(',', $event[$col]));
|
||||
else if (array_key_exists($col, $event) && is_null($event[$col]))
|
||||
$sql_set[] = $this->rc->db->quote_identifier($col) . '=NULL';
|
||||
else if (array_key_exists($col, $event)) {
|
||||
//TODO: proper 4 byte character (eg emoticons) handling
|
||||
//utf8 in mysql only supports 3 byte characters, so this throws an error if there are emoticons in the description.
|
||||
//For now we just remove them. But instead of removing, we should prepare the database for them (by using utf8mb4)
|
||||
$text = preg_replace('/[\xF0-\xF7].../s', '', $event[$col]);
|
||||
$sql_set[] = $this->rc->db->quote_identifier($col) . '=' . $this->rc->db->quote($text);
|
||||
}
|
||||
else if (array_key_exists($col, $event))
|
||||
$sql_set[] = $this->rc->db->quote_identifier($col) . '=' . $this->rc->db->quote($event[$col]);
|
||||
}
|
||||
|
||||
if ($event['_recurrence'])
|
||||
|
@ -1962,11 +1897,11 @@ class caldav_driver extends calendar_driver
|
|||
'SELECT source_id, caldav_url FROM '.$this->db_sources .' WHERE user_id = ?',
|
||||
$this->rc->user->ID
|
||||
);
|
||||
$sources_exist = $this->rc->db->num_rows($result);
|
||||
if($this->rc->db->num_rows($result)) {
|
||||
$is_ical = new html_checkbox( array(
|
||||
'name' => "is_ical",
|
||||
'value' => 1,
|
||||
'onload' => 'alert(123)',
|
||||
'onclick' => '
|
||||
if(this.checked) {
|
||||
$("#ical_url").removeClass("hidden");
|
||||
|
@ -1978,7 +1913,7 @@ else {
|
|||
}'
|
||||
));
|
||||
$formfields['is_ical'] = array(
|
||||
'label' => $this->cal->gettext('calendar_ical_file'),
|
||||
'label' => $this->cal->gettext('calendar_is_ical'),
|
||||
'value' => $is_ical->show(null),
|
||||
'class' => 'hidden'
|
||||
);
|
||||
|
@ -1989,6 +1924,7 @@ else {
|
|||
'class' => 'hidden'
|
||||
));
|
||||
|
||||
|
||||
$caldav_url = new html_select([
|
||||
'name' => 'source_id',
|
||||
'id' => 'caldav_url'
|
||||
|
@ -2002,19 +1938,8 @@ else {
|
|||
);
|
||||
}
|
||||
else {
|
||||
$ical_url = new html_inputfield( array(
|
||||
'name' => 'ical_url',
|
||||
'size' => 20,
|
||||
));
|
||||
$formfields['url'] = array(
|
||||
'label' => $this->cal->gettext('calendar_ical_file'),
|
||||
'value' => $ical_url->show(null),
|
||||
);
|
||||
$enable_ics = new html_hiddenfield(['name' => 'is_ical', 'value' => 1]);
|
||||
$formfields['hidden'] = array(
|
||||
'label' => ' ',
|
||||
'value' =>$enable_ics->show(null),
|
||||
);
|
||||
$this->rc->output->show_message($this->cal->gettext('nosources_error'), 'error');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2122,13 +2047,14 @@ else {
|
|||
preg_match('/#(.)(.)(.)/', $value, $matches);
|
||||
$color = $matches[1] .$matches[1] .
|
||||
$matches[2] .$matches[2] .
|
||||
$matches[3] .$matches[3];
|
||||
$matches[3] .$matches[3] .
|
||||
'ff';
|
||||
break;
|
||||
case 7:
|
||||
$color = substr($value, 1);
|
||||
$color = substr($value, 1) . 'ff';
|
||||
break;
|
||||
case 9:
|
||||
$color = substr($value, 1, 6);
|
||||
$color = substr($value, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -2230,17 +2156,6 @@ else {
|
|||
|
||||
foreach($updates as $update)
|
||||
{
|
||||
if($update['remote_event']['allday'])
|
||||
{
|
||||
//caldav has exclusive end dates set to midnight of the next day.
|
||||
//But we need it inclusive, so we just reduce it by an hour:
|
||||
|
||||
$old = $update['remote_event']['end'];
|
||||
$new = new DateTime('now', $old->getTimezone());
|
||||
$new->setTimestamp($old->getTimestamp() - 3600);
|
||||
$update['remote_event']['end'] = $new;
|
||||
}
|
||||
|
||||
// local event -> update event
|
||||
if(isset($update["local_event"]))
|
||||
{
|
||||
|
@ -2332,7 +2247,7 @@ else {
|
|||
{
|
||||
switch ($this->rc->db->db_provider) {
|
||||
case 'postgres':
|
||||
return "EXTRACT (EPOCH FROM $field::timestamp without time zone)";
|
||||
return "EXTRACT (EPOCH FROM $field)";
|
||||
default:
|
||||
return "UNIX_TIMESTAMP($field)";
|
||||
}
|
||||
|
|
|
@ -715,8 +715,6 @@ class database_driver extends calendar_driver
|
|||
'title', 'description', 'location', 'categories', 'url', 'free_busy', 'priority',
|
||||
'sensitivity', 'status', 'attendees', 'alarms', 'notifyat'
|
||||
);
|
||||
if(array_key_exists('notifyat', $event) && empty($event['notifyat']))
|
||||
unset($event['notifyat']);
|
||||
|
||||
foreach ($set_cols as $col) {
|
||||
if (!empty($event[$col]) && is_a($event[$col], 'DateTime')) {
|
||||
|
|
|
@ -41,13 +41,12 @@ class resources_driver_ldap extends resources_driver
|
|||
/**
|
||||
* Fetch resource objects to be displayed for booking
|
||||
*
|
||||
* @param string $query Search query (optional)
|
||||
* @param int $num Max size of the result
|
||||
* @param string $searchField Field to search with query
|
||||
* @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, $searchField = '*')
|
||||
public function load_resources($query = null, $num = 5000)
|
||||
{
|
||||
if (!($ldap = $this->connect())) {
|
||||
return [];
|
||||
|
@ -57,7 +56,7 @@ class resources_driver_ldap extends resources_driver
|
|||
$ldap->set_pagesize($num);
|
||||
|
||||
if (isset($query)) {
|
||||
$results = $ldap->search($searchField, $query, 0, true, true);
|
||||
$results = $ldap->search('*', $query, 0, true, true);
|
||||
}
|
||||
else {
|
||||
$results = $ldap->list_records();
|
||||
|
|
|
@ -8,8 +8,9 @@
|
|||
*/
|
||||
$labels['addsources'] = 'CalDAV Quellen hinzufügen';
|
||||
$labels['deletesources'] = 'CalDAV Quellen löschen';
|
||||
$labels['nosources_error'] = 'Keine CalDAV Quellen vorhanden.';
|
||||
$labels['source_notadded_error'] = 'CalDAV Quelle konnte nicht hinzugefügt werden.';
|
||||
$labels['calendar_ical_file'] = 'ics-Datei';
|
||||
$labels['calendar_is_ical'] = 'Ist ics-Datei';
|
||||
|
||||
$labels['default_view'] = 'Standardansicht';
|
||||
$labels['time_format'] = 'Zeitformatierung';
|
||||
|
|
|
@ -8,8 +8,9 @@
|
|||
*/
|
||||
$labels['addsources'] = 'CalDAV Quellen hinzufügen';
|
||||
$labels['deletesources'] = 'CalDAV Quellen löschen';
|
||||
$labels['nosources_error'] = 'Keine CalDAV Quellen vorhanden.';
|
||||
$labels['source_notadded_error'] = 'CalDAV Quelle konnte nicht hinzugefügt werden.';
|
||||
$labels['calendar_ical_file'] = 'ics-Datei';
|
||||
$labels['calendar_is_ical'] = 'Ist ics-Datei';
|
||||
|
||||
$labels['default_view'] = 'Standardansicht';
|
||||
$labels['time_format'] = 'Zeitformatierung';
|
||||
|
|
|
@ -8,8 +8,9 @@
|
|||
*/
|
||||
$labels['addsources'] = 'CalDAV Quellen hinzufügen';
|
||||
$labels['deletesources'] = 'CalDAV Quellen löschen';
|
||||
$labels['nosources_error'] = 'Keine CalDAV Quellen vorhanden.';
|
||||
$labels['source_notadded_error'] = 'CalDAV Quelle konnte nicht hinzugefügt werden.';
|
||||
$labels['calendar_ical_file'] = 'ics-Datei';
|
||||
$labels['calendar_is_ical'] = 'Ist ics-Datei';
|
||||
|
||||
$labels['default_view'] = 'Standardansicht';
|
||||
$labels['time_format'] = 'Zeitformatierung';
|
||||
|
|
|
@ -13,8 +13,9 @@ $labels = array();
|
|||
//caldav driver
|
||||
$labels['addsources'] = 'Add CalDAV sources';
|
||||
$labels['deletesources'] = 'Delete CalDAV sources';
|
||||
$labels['nosources_error'] = 'No CalDAV sources available.';
|
||||
$labels['source_notadded_error'] = 'CalDAV source could not be added.';
|
||||
$labels['calendar_ical_file'] = 'ics file';
|
||||
$labels['calendar_is_ical'] = 'Is ics file';
|
||||
|
||||
// preferences
|
||||
$labels['default_view'] = 'Default view';
|
||||
|
@ -213,10 +214,6 @@ $labels['itipmailbodycancel'] = "\$sender has rejected your participation in the
|
|||
$labels['itipmailbodydelegated'] = "\$sender has delegated the participation in the following event:\n\n*\$title*\n\nWhen: \$date";
|
||||
$labels['itipmailbodydelegatedto'] = "\$sender has delegated the participation in the following event to you:\n\n*\$title*\n\nWhen: \$date";
|
||||
|
||||
$labels['itipmailbodyresourceaccepted'] = "\$sender has accepted the following resource booking:\n\n*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees";
|
||||
$labels['itipmailbodyresourcetentative'] = "\$sender has tentatively accepted the following resource booking:\n\n*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees";
|
||||
$labels['itipmailbodyresourcedeclined'] = "\$sender has declined the the following resource booking:\n\n*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees";
|
||||
|
||||
$labels['itipdeclineevent'] = 'Do you want to decline your invitation to this event?';
|
||||
$labels['declinedeleteconfirm'] = 'Do you also want to delete this declined event from your calendar?';
|
||||
$labels['itipcomment'] = 'Invitation/notification comment';
|
||||
|
|
Loading…
Add table
Reference in a new issue