mirror of
https://github.com/alextselegidis/easyappointments.git
synced 2024-11-08 17:12:25 +03:00
Remove the automated calendar detection from the CalDAV sync as it will not work with all CalDAV providers (some use different URL structures than Baikal)
This commit is contained in:
parent
dd34836c88
commit
f398d18bbc
6 changed files with 121 additions and 269 deletions
|
@ -11,6 +11,8 @@
|
|||
* @since v1.5.0
|
||||
* ---------------------------------------------------------------------------- */
|
||||
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
|
||||
/**
|
||||
* Caldav controller.
|
||||
*
|
||||
|
@ -54,19 +56,7 @@ class Caldav extends EA_Controller
|
|||
$caldav_username = request('caldav_username');
|
||||
$caldav_password = request('caldav_password');
|
||||
|
||||
$default_caldav_calendar = $this->caldav_sync->get_default_calendar(
|
||||
$caldav_url,
|
||||
$caldav_username,
|
||||
$caldav_password,
|
||||
);
|
||||
|
||||
if (!$default_caldav_calendar) {
|
||||
json_response([
|
||||
'success' => false,
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
$this->caldav_sync->test_connection($caldav_url, $caldav_username, $caldav_password);
|
||||
|
||||
$provider = $this->providers_model->find($provider_id);
|
||||
|
||||
|
@ -74,13 +64,17 @@ class Caldav extends EA_Controller
|
|||
$provider['settings']['caldav_url'] = $caldav_url;
|
||||
$provider['settings']['caldav_username'] = $caldav_username;
|
||||
$provider['settings']['caldav_password'] = $caldav_password;
|
||||
$provider['settings']['caldav_calendar'] = $default_caldav_calendar;
|
||||
|
||||
$this->providers_model->save($provider);
|
||||
|
||||
json_response([
|
||||
'success' => true,
|
||||
]);
|
||||
} catch (GuzzleException | InvalidArgumentException $e) {
|
||||
json_response([
|
||||
'success' => false,
|
||||
'message' => $e->getMessage(),
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
json_exception($e);
|
||||
}
|
||||
|
@ -277,62 +271,6 @@ class Caldav extends EA_Controller
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get CalDAV Calendars
|
||||
*
|
||||
* This method will return a list of the available CalDAV Calendars.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function get_caldav_calendars(): void
|
||||
{
|
||||
try {
|
||||
$provider_id = (int) request('provider_id');
|
||||
|
||||
$user_id = session('user_id');
|
||||
|
||||
if (cannot('edit', PRIV_USERS) && (int) $user_id !== (int) $provider_id) {
|
||||
throw new RuntimeException('You do not have the required permissions for this task.');
|
||||
}
|
||||
|
||||
$calendars = $this->caldav_sync->get_caldav_calendars($provider_id);
|
||||
|
||||
json_response($calendars);
|
||||
} catch (Throwable $e) {
|
||||
json_exception($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a specific caldav calendar for a provider.
|
||||
*
|
||||
* All the appointments will be synced with this particular calendar.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function select_caldav_calendar(): void
|
||||
{
|
||||
try {
|
||||
$provider_id = (int) request('provider_id');
|
||||
|
||||
$user_id = session('user_id');
|
||||
|
||||
if (cannot('edit', PRIV_USERS) && (int) $user_id !== (int) $provider_id) {
|
||||
throw new RuntimeException('You do not have the required permissions for this task.');
|
||||
}
|
||||
|
||||
$calendar_id = request('calendar_id');
|
||||
|
||||
$this->providers_model->set_setting($provider_id, 'caldav_calendar', $calendar_id);
|
||||
|
||||
json_response([
|
||||
'success' => true,
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
json_exception($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable a providers sync setting.
|
||||
*
|
||||
|
@ -361,7 +299,6 @@ class Caldav extends EA_Controller
|
|||
$provider['settings']['caldav_url'] = null;
|
||||
$provider['settings']['caldav_username'] = null;
|
||||
$provider['settings']['caldav_password'] = null;
|
||||
$provider['settings']['caldav_calendar'] = null;
|
||||
|
||||
$this->providers_model->save($provider);
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Sabre\VObject\Component\VEvent;
|
||||
use Sabre\VObject\Reader;
|
||||
|
||||
|
@ -68,12 +69,10 @@ class Caldav_sync
|
|||
|
||||
$client = $this->get_http_client_by_provider_id($provider['id']);
|
||||
|
||||
$caldav_calendar = $provider['settings']['caldav_calendar'];
|
||||
|
||||
$caldav_event_id =
|
||||
$appointment['id_caldav_calendar'] ?: $this->CI->ics_file->generate_uid($appointment['id']);
|
||||
|
||||
$uri = $this->get_caldav_event_uri($caldav_calendar, $caldav_event_id);
|
||||
$uri = $this->get_caldav_event_uri($caldav_event_id);
|
||||
|
||||
$client->request('PUT', $uri, [
|
||||
'headers' => [
|
||||
|
@ -106,12 +105,10 @@ class Caldav_sync
|
|||
|
||||
$client = $this->get_http_client_by_provider_id($provider['id']);
|
||||
|
||||
$caldav_calendar = $provider['settings']['caldav_calendar'];
|
||||
|
||||
$caldav_event_id =
|
||||
$unavailability['id_caldav_calendar'] ?: $this->CI->ics_file->generate_uid($unavailability['id']);
|
||||
|
||||
$uri = $this->get_caldav_event_uri($caldav_calendar, $caldav_event_id);
|
||||
$uri = $this->get_caldav_event_uri($caldav_event_id);
|
||||
|
||||
$client->request('PUT', $uri, [
|
||||
'headers' => [
|
||||
|
@ -138,9 +135,7 @@ class Caldav_sync
|
|||
try {
|
||||
$client = $this->get_http_client_by_provider_id($provider['id']);
|
||||
|
||||
$caldav_calendar = $provider['settings']['caldav_calendar'];
|
||||
|
||||
$uri = $this->get_caldav_event_uri($caldav_calendar, $caldav_event_id);
|
||||
$uri = $this->get_caldav_event_uri($caldav_event_id);
|
||||
|
||||
$client->request('DELETE', $uri);
|
||||
} catch (GuzzleException $e) {
|
||||
|
@ -162,9 +157,7 @@ class Caldav_sync
|
|||
try {
|
||||
$client = $this->get_http_client_by_provider_id($provider['id']);
|
||||
|
||||
$caldav_calendar = $provider['settings']['caldav_calendar'];
|
||||
|
||||
$uri = $this->get_caldav_event_uri($caldav_calendar, $caldav_event_id);
|
||||
$uri = $this->get_caldav_event_uri($caldav_event_id);
|
||||
|
||||
$response = $client->request('GET', $uri);
|
||||
|
||||
|
@ -194,41 +187,7 @@ class Caldav_sync
|
|||
try {
|
||||
$client = $this->get_http_client_by_provider_id($provider['id']);
|
||||
|
||||
$caldav_calendar = $provider['settings']['caldav_calendar'];
|
||||
|
||||
$uri = $this->get_caldav_event_uri($caldav_calendar);
|
||||
|
||||
$start_date_time_object = new DateTime($start_date_time);
|
||||
$formatted_start_date_time = $start_date_time_object->format('Ymd\THis\Z');
|
||||
$end_date_time_object = new DateTime($end_date_time);
|
||||
$formatted_end_date_time = $end_date_time_object->format('Ymd\THis\Z');
|
||||
|
||||
$response = $client->request('REPORT', $uri, [
|
||||
'headers' => [
|
||||
'Content-Type' => 'application/xml',
|
||||
'Depth' => '1',
|
||||
],
|
||||
'body' =>
|
||||
'
|
||||
<c:calendar-query xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav">
|
||||
<d:prop>
|
||||
<d:getetag />
|
||||
<c:calendar-data />
|
||||
</d:prop>
|
||||
<c:filter>
|
||||
<c:comp-filter name="VCALENDAR">
|
||||
<c:comp-filter name="VEVENT">
|
||||
<c:time-range start="' .
|
||||
$formatted_start_date_time .
|
||||
'" end="' .
|
||||
$formatted_end_date_time .
|
||||
'"/>
|
||||
</c:comp-filter>
|
||||
</c:comp-filter>
|
||||
</c:filter>
|
||||
</c:calendar-query>
|
||||
',
|
||||
]);
|
||||
$response = $this->fetch_events($client, $start_date_time, $end_date_time);
|
||||
|
||||
$xml = new SimpleXMLElement($response->getBody(), 0, false, 'd', true);
|
||||
|
||||
|
@ -249,94 +208,6 @@ class Caldav_sync
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return available Caldav Calendars for specific user.
|
||||
*
|
||||
* The given user's token must already exist in db in order to get access to his
|
||||
* Caldav Calendar account.
|
||||
*
|
||||
* @return array Returns an array with the available calendars.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function get_caldav_calendars(int $provider_id): array
|
||||
{
|
||||
try {
|
||||
$provider = $this->CI->providers_model->find($provider_id);
|
||||
|
||||
if (!$provider['settings']['caldav_sync']) {
|
||||
throw new RuntimeException(
|
||||
'The selected provider does not have the CalDAV sync enabled: ' . $provider_id,
|
||||
);
|
||||
}
|
||||
|
||||
$caldav_url = $provider['settings']['caldav_url'];
|
||||
$caldav_username = $provider['settings']['caldav_username'];
|
||||
$caldav_password = $provider['settings']['caldav_password'];
|
||||
|
||||
$client = $this->get_http_client($caldav_url, $caldav_username, $caldav_password);
|
||||
|
||||
$caldav_calendars = [];
|
||||
|
||||
$response = $client->request('PROPFIND', 'calendars/' . $caldav_username);
|
||||
|
||||
$xml = new SimpleXMLElement($response->getBody(), 0, false, 'd', true);
|
||||
|
||||
foreach ($xml->children('d', true) as $response) {
|
||||
$resource_type = $response->propstat->prop->resourcetype->children('cal', true)->getName();
|
||||
|
||||
if ($resource_type !== 'calendar') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$caldav_calendars[] = [
|
||||
'id' => $this->sanitize_href($caldav_url, $response->href),
|
||||
'summary' => (string) $response->propstat->prop->displayname,
|
||||
];
|
||||
}
|
||||
|
||||
return $caldav_calendars;
|
||||
} catch (GuzzleException $e) {
|
||||
$this->handle_guzzle_exception($e, 'Failed to get the CalDAV calendars');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the remote CalDAV server and get the default calendar URL.
|
||||
*
|
||||
* @param string $caldav_url
|
||||
* @param string $caldav_username
|
||||
* @param string $caldav_password
|
||||
*
|
||||
* @return string|null
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function get_default_calendar(string $caldav_url, string $caldav_username, string $caldav_password): ?string
|
||||
{
|
||||
try {
|
||||
$client = $this->get_http_client($caldav_url, $caldav_username, $caldav_password);
|
||||
|
||||
$response = $client->request('PROPFIND', 'calendars/' . $caldav_username);
|
||||
|
||||
$xml = new SimpleXMLElement($response->getBody(), 0, false, 'd', true);
|
||||
|
||||
foreach ($xml->children('d', true) as $response) {
|
||||
$resource_type = $response->propstat->prop->resourcetype->children('cal', true)->getName();
|
||||
|
||||
if ($resource_type === 'calendar') {
|
||||
return $this->sanitize_href($caldav_url, $response->href); // Use this response as the default calendar
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (GuzzleException $e) {
|
||||
$this->handle_guzzle_exception($e, 'Failed to check CalDAV credentials');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Common error handling for the CalDAV requests.
|
||||
*
|
||||
|
@ -390,11 +261,24 @@ class Caldav_sync
|
|||
]);
|
||||
}
|
||||
|
||||
private function sanitize_href(string $caldav_url, string $href): string
|
||||
/**
|
||||
* @throws Exception
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function test_connection(string $caldav_url, string $caldav_username, string $caldav_password): void
|
||||
{
|
||||
$parts = parse_url($caldav_url);
|
||||
try {
|
||||
// Fetch some events to see if the connection is valid
|
||||
$client = $this->get_http_client($caldav_url, $caldav_username, $caldav_password);
|
||||
|
||||
return str_replace($parts['path'], '', $href);
|
||||
$start_date_time = date('Y-m-d 00:00:00');
|
||||
$end_date_time = date('Y-m-d 23:59:59');
|
||||
|
||||
$this->fetch_events($client, $start_date_time, $end_date_time);
|
||||
} catch (GuzzleException $e) {
|
||||
$this->handle_guzzle_exception($e, 'Failed to save CalDAV event');
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
private function get_http_client_by_provider_id(int $provider_id): Client
|
||||
|
@ -415,14 +299,13 @@ class Caldav_sync
|
|||
/**
|
||||
* Generate the event URI, used in various requests.
|
||||
*
|
||||
* @param string $caldav_calendar
|
||||
* @param string|null $caldav_event_id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_caldav_event_uri(string $caldav_calendar, ?string $caldav_event_id = null): string
|
||||
private function get_caldav_event_uri(?string $caldav_event_id = null): string
|
||||
{
|
||||
return trim($caldav_calendar, '/') . ($caldav_event_id ? '/' . $caldav_event_id . '.ics' : '');
|
||||
return $caldav_event_id ? '/' . $caldav_event_id . '.ics' : '';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -475,4 +358,43 @@ class Caldav_sync
|
|||
'location' => (string) $vevent->LOCATION,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
*/
|
||||
private function fetch_events(Client $client, string $start_date_time, string $end_date_time): ResponseInterface
|
||||
{
|
||||
$start_date_time_object = new DateTime($start_date_time);
|
||||
$formatted_start_date_time = $start_date_time_object->format('Ymd\THis\Z');
|
||||
$end_date_time_object = new DateTime($end_date_time);
|
||||
$formatted_end_date_time = $end_date_time_object->format('Ymd\THis\Z');
|
||||
|
||||
return $client->request('REPORT', '', [
|
||||
'headers' => [
|
||||
'Content-Type' => 'application/xml',
|
||||
'Depth' => '1',
|
||||
],
|
||||
'body' =>
|
||||
'
|
||||
<c:calendar-query xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav">
|
||||
<d:prop>
|
||||
<d:getetag />
|
||||
<c:calendar-data />
|
||||
</d:prop>
|
||||
<c:filter>
|
||||
<c:comp-filter name="VCALENDAR">
|
||||
<c:comp-filter name="VEVENT">
|
||||
<c:time-range start="' .
|
||||
$formatted_start_date_time .
|
||||
'" end="' .
|
||||
$formatted_end_date_time .
|
||||
'"/>
|
||||
</c:comp-filter>
|
||||
</c:comp-filter>
|
||||
</c:filter>
|
||||
</c:calendar-query>
|
||||
',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<?php defined('BASEPATH') or exit('No direct script access allowed');
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Easy!Appointments - Online Appointment Scheduler
|
||||
*
|
||||
* @package EasyAppointments
|
||||
* @author A.Tselegidis <alextselegidis@gmail.com>
|
||||
* @copyright Copyright (c) Alex Tselegidis
|
||||
* @license https://opensource.org/licenses/GPL-3.0 - GPLv3
|
||||
* @link https://easyappointments.org
|
||||
* @since v1.4.0
|
||||
* ---------------------------------------------------------------------------- */
|
||||
|
||||
class Migration_Drop_caldav_calendar_column_from_user_settings_table extends EA_Migration
|
||||
{
|
||||
/**
|
||||
* Upgrade method.
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if ($this->db->field_exists('caldav_calendar', 'user_settings')) {
|
||||
$this->dbforge->drop_column('user_settings', 'caldav_calendar');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Downgrade method.
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
if (!$this->db->field_exists('caldav_calendar', 'user_settings')) {
|
||||
$fields = [
|
||||
'caldav_calendar' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => '256',
|
||||
'null' => true,
|
||||
'after' => 'caldav_password',
|
||||
],
|
||||
];
|
||||
|
||||
$this->dbforge->add_column('user_settings', $fields);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -799,9 +799,6 @@ class Providers_model extends EA_Model
|
|||
'caldavPassword' => array_key_exists('caldav_password', $provider['settings'])
|
||||
? $provider['settings']['caldav_password']
|
||||
: null,
|
||||
'caldavCalendar' => array_key_exists('caldav_calendar', $provider['settings'])
|
||||
? $provider['settings']['caldav_calendar']
|
||||
: null,
|
||||
'syncFutureDays' => array_key_exists('sync_future_days', $provider['settings'])
|
||||
? (int) $provider['settings']['sync_future_days']
|
||||
: null,
|
||||
|
@ -949,10 +946,6 @@ class Providers_model extends EA_Model
|
|||
$decoded_resource['settings']['caldav_password'] = $provider['settings']['caldavPassword'];
|
||||
}
|
||||
|
||||
if (array_key_exists('caldavCalendar', $provider['settings'])) {
|
||||
$decoded_resource['settings']['caldav_calendar'] = $provider['settings']['caldavCalendar'];
|
||||
}
|
||||
|
||||
if (array_key_exists('syncFutureDays', $provider['settings'])) {
|
||||
$decoded_resource['settings']['sync_future_days'] = $provider['settings']['syncFutureDays'];
|
||||
}
|
||||
|
|
|
@ -171,26 +171,26 @@ App.Utils.CalendarSync = (function () {
|
|||
});
|
||||
}
|
||||
|
||||
function enableCaldavSync() {
|
||||
function enableCaldavSync(defaultCaldavUrl = '', defaultCaldavUsername = '', defaultCaldavPassword = '') {
|
||||
const $container = $(`
|
||||
<div>
|
||||
<div class="mb-3">
|
||||
<label for="caldav-url" class="form-label">
|
||||
${lang('calendar_url')}
|
||||
</label>
|
||||
<input type="text" class="form-control" id="caldav-url"/>
|
||||
<input type="text" class="form-control" id="caldav-url" value="${defaultCaldavUrl}"/>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="caldav-username" class="form-label">
|
||||
${lang('username')}
|
||||
</label>
|
||||
<input type="text" class="form-control" id="caldav-username"/>
|
||||
<input type="text" class="form-control" id="caldav-username" value="${defaultCaldavUsername}"/>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="caldav-password" class="form-label">
|
||||
${lang('password')}
|
||||
</label>
|
||||
<input type="password" class="form-control" id="caldav-password"/>
|
||||
<input type="password" class="form-control" id="caldav-password" value="${defaultCaldavPassword}"/>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-danger" hidden>
|
||||
|
@ -247,7 +247,7 @@ App.Utils.CalendarSync = (function () {
|
|||
$caldavUsername.addClass('is-invalid');
|
||||
$caldavPassword.addClass('is-invalid');
|
||||
|
||||
$alert.text(lang('login_failed')).prop('hidden', false);
|
||||
$alert.text(lang('login_failed') + ' ' + response.message).prop('hidden', false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -258,7 +258,7 @@ App.Utils.CalendarSync = (function () {
|
|||
|
||||
updateSyncButtons();
|
||||
|
||||
selectCaldavCalendar();
|
||||
App.Layouts.Backend.displayNotification(lang('sync_calendar_selected'));
|
||||
|
||||
messageModal.hide();
|
||||
},
|
||||
|
@ -296,7 +296,6 @@ App.Utils.CalendarSync = (function () {
|
|||
provider.settings.caldav_url = null;
|
||||
provider.settings.caldav_username = null;
|
||||
provider.settings.caldav_password = null;
|
||||
provider.settings.caldav_calendar = null;
|
||||
|
||||
App.Http.Caldav.disableProviderSync(provider.id);
|
||||
|
||||
|
@ -312,43 +311,6 @@ App.Utils.CalendarSync = (function () {
|
|||
]);
|
||||
}
|
||||
|
||||
function selectCaldavCalendar() {
|
||||
const providerId = $selectFilterItem.val();
|
||||
|
||||
App.Http.Caldav.getCaldavCalendars(providerId).done((caldavCalendars) => {
|
||||
const $selectCaldavCalendar = $(`
|
||||
<select class="form-control">
|
||||
<!-- JS -->
|
||||
</select>
|
||||
`);
|
||||
|
||||
caldavCalendars.forEach((caldavCalendar) => {
|
||||
$selectCaldavCalendar.append(new Option(caldavCalendar.summary, caldavCalendar.id));
|
||||
});
|
||||
|
||||
const $messageModal = App.Utils.Message.show(
|
||||
lang('select_sync_calendar'),
|
||||
lang('select_sync_calendar_prompt'),
|
||||
[
|
||||
{
|
||||
text: lang('select'),
|
||||
click: (event, messageModal) => {
|
||||
const caldavCalendarId = $selectCaldavCalendar.val();
|
||||
|
||||
App.Http.Caldav.selectCaldavCalendar(providerId, caldavCalendarId).done(() => {
|
||||
App.Layouts.Backend.displayNotification(lang('sync_calendar_selected'));
|
||||
});
|
||||
|
||||
messageModal.hide();
|
||||
},
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
$selectCaldavCalendar.appendTo($messageModal.find('.modal-body'));
|
||||
});
|
||||
}
|
||||
|
||||
function triggerCaldavSync() {
|
||||
const providerId = $selectFilterItem.val();
|
||||
|
||||
|
|
|
@ -2280,8 +2280,6 @@ components:
|
|||
type: string
|
||||
caldavPassword:
|
||||
type: string
|
||||
caldavCalendar:
|
||||
type: string
|
||||
syncFutureDays:
|
||||
type: string
|
||||
syncPastDays:
|
||||
|
@ -2316,7 +2314,6 @@ components:
|
|||
caldavUrl: null
|
||||
caldavUsername: null
|
||||
caldavPassword: null
|
||||
caldavCalendar: null
|
||||
syncFutureDays: 90
|
||||
syncPastDays: 30
|
||||
workingPlan:
|
||||
|
@ -2401,8 +2398,6 @@ components:
|
|||
type: string
|
||||
caldavPassword:
|
||||
type: string
|
||||
caldavCalendar:
|
||||
type: string
|
||||
syncFutureDays:
|
||||
type: string
|
||||
syncPastDays:
|
||||
|
@ -2436,7 +2431,6 @@ components:
|
|||
caldavUrl: null
|
||||
caldavUsername: null
|
||||
caldavPassword: null
|
||||
caldavCalendar: null
|
||||
syncFutureDays: 90
|
||||
syncPastDays: 30
|
||||
workingPlan:
|
||||
|
|
Loading…
Reference in a new issue