Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f432dd2

Browse files
Add scheduling availability settings
Signed-off-by: Christoph Wurst <[email protected]>
1 parent 643e85c commit f432dd2

21 files changed

+2714
-3
lines changed

apps/dav/appinfo/info.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060

6161
<settings>
6262
<admin>OCA\DAV\Settings\CalDAVSettings</admin>
63+
<personal>OCA\DAV\Settings\AvailabilitySettings</personal>
6364
</settings>
6465

6566
<activity>

apps/dav/composer/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@
277277
'OCA\\DAV\\Search\\EventsSearchProvider' => $baseDir . '/../lib/Search/EventsSearchProvider.php',
278278
'OCA\\DAV\\Search\\TasksSearchProvider' => $baseDir . '/../lib/Search/TasksSearchProvider.php',
279279
'OCA\\DAV\\Server' => $baseDir . '/../lib/Server.php',
280+
'OCA\\DAV\\Settings\\AvailabilitySettings' => $baseDir . '/../lib/Settings/AvailabilitySettings.php',
280281
'OCA\\DAV\\Settings\\CalDAVSettings' => $baseDir . '/../lib/Settings/CalDAVSettings.php',
281282
'OCA\\DAV\\Storage\\PublicOwnerWrapper' => $baseDir . '/../lib/Storage/PublicOwnerWrapper.php',
282283
'OCA\\DAV\\SystemTag\\SystemTagMappingNode' => $baseDir . '/../lib/SystemTag/SystemTagMappingNode.php',

apps/dav/composer/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ class ComposerStaticInitDAV
292292
'OCA\\DAV\\Search\\EventsSearchProvider' => __DIR__ . '/..' . '/../lib/Search/EventsSearchProvider.php',
293293
'OCA\\DAV\\Search\\TasksSearchProvider' => __DIR__ . '/..' . '/../lib/Search/TasksSearchProvider.php',
294294
'OCA\\DAV\\Server' => __DIR__ . '/..' . '/../lib/Server.php',
295+
'OCA\\DAV\\Settings\\AvailabilitySettings' => __DIR__ . '/..' . '/../lib/Settings/AvailabilitySettings.php',
295296
'OCA\\DAV\\Settings\\CalDAVSettings' => __DIR__ . '/..' . '/../lib/Settings/CalDAVSettings.php',
296297
'OCA\\DAV\\Storage\\PublicOwnerWrapper' => __DIR__ . '/..' . '/../lib/Storage/PublicOwnerWrapper.php',
297298
'OCA\\DAV\\SystemTag\\SystemTagMappingNode' => __DIR__ . '/..' . '/../lib/SystemTag/SystemTagMappingNode.php',

apps/dav/js/settings-admin-caldav.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/dav/js/settings-admin-caldav.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/dav/js/settings-personal-availability.js

Lines changed: 2122 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/dav/js/settings-personal-availability.js.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* @copyright 2021 Christoph Wurst <[email protected]>
7+
*
8+
* @author 2021 Christoph Wurst <[email protected]>
9+
*
10+
* @license GNU AGPL version 3 or any later version
11+
*
12+
* This program is free software: you can redistribute it and/or modify
13+
* it under the terms of the GNU Affero General Public License as
14+
* published by the Free Software Foundation, either version 3 of the
15+
* License, or (at your option) any later version.
16+
*
17+
* This program is distributed in the hope that it will be useful,
18+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
19+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+
* GNU Affero General Public License for more details.
21+
*
22+
* You should have received a copy of the GNU Affero General Public License
23+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
24+
*/
25+
26+
namespace OCA\DAV\Settings;
27+
28+
use OCA\DAV\AppInfo\Application;
29+
use OCP\AppFramework\Http\TemplateResponse;
30+
use OCP\Settings\ISettings;
31+
32+
class AvailabilitySettings implements ISettings {
33+
public function getForm(): TemplateResponse {
34+
return new TemplateResponse(Application::APP_ID, 'settings-personal-availability');
35+
}
36+
37+
public function getSection(): string {
38+
return 'groupware';
39+
}
40+
41+
public function getPriority(): int {
42+
return 10;
43+
}
44+
}

apps/dav/src/dav/client.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* @copyright 2021 Christoph Wurst <[email protected]>
3+
*
4+
* @author 2021 Christoph Wurst <[email protected]>
5+
*
6+
* @license GNU AGPL version 3 or any later version
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Affero General Public License as
10+
* published by the Free Software Foundation, either version 3 of the
11+
* License, or (at your option) any later version.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU Affero General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU Affero General Public License
19+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
20+
*/
21+
22+
import * as webdav from 'webdav'
23+
import axios from '@nextcloud/axios'
24+
import memoize from 'lodash/fp/memoize'
25+
import { generateRemoteUrl } from '@nextcloud/router'
26+
import { getCurrentUser } from '@nextcloud/auth'
27+
28+
export const getClient = memoize((service) => {
29+
// Add this so the server knows it is an request from the browser
30+
axios.defaults.headers['X-Requested-With'] = 'XMLHttpRequest'
31+
32+
// force our axios
33+
const patcher = webdav.getPatcher()
34+
patcher.patch('request', axios)
35+
36+
return webdav.createClient(
37+
generateRemoteUrl(`dav/${service}/${getCurrentUser().uid}`)
38+
)
39+
})
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/**
2+
* @copyright 2021 Christoph Wurst <[email protected]>
3+
*
4+
* @author 2021 Christoph Wurst <[email protected]>
5+
*
6+
* @license GNU AGPL version 3 or any later version
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Affero General Public License as
10+
* published by the Free Software Foundation, either version 3 of the
11+
* License, or (at your option) any later version.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU Affero General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU Affero General Public License
19+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
20+
*/
21+
import { getClient } from '../dav/client'
22+
import ICAL from 'ical.js'
23+
import logger from './logger'
24+
import { parseXML } from 'webdav/dist/node/tools/dav'
25+
import { v4 as uuidv4 } from 'uuid'
26+
27+
export function getEmptySlots() {
28+
return {
29+
MO: [],
30+
TU: [],
31+
WE: [],
32+
TH: [],
33+
FR: [],
34+
SA: [],
35+
SU: [],
36+
}
37+
}
38+
39+
export async function findScheduleInboxAvailability() {
40+
const client = getClient('calendars')
41+
42+
const response = await client.customRequest('inbox', {
43+
method: 'PROPFIND',
44+
data: `<?xml version="1.0"?>
45+
<x0:propfind xmlns:x0="DAV:">
46+
<x0:prop>
47+
<x1:calendar-availability xmlns:x1="urn:ietf:params:xml:ns:caldav"/>
48+
</x0:prop>
49+
</x0:propfind>`
50+
})
51+
52+
const xml = await parseXML(response.data)
53+
54+
if (!xml) {
55+
return undefined
56+
}
57+
58+
const availability = xml?.multistatus?.response[0]?.propstat?.prop['calendar-availability']
59+
if (!availability) {
60+
return undefined
61+
}
62+
63+
const parsedIcal = ICAL.parse(availability)
64+
65+
const vcalendarComp = new ICAL.Component(parsedIcal)
66+
const vavailabilityComp = vcalendarComp.getFirstSubcomponent('vavailability')
67+
const availableComps = vavailabilityComp.getAllSubcomponents('available')
68+
69+
// Combine all AVAILABLE blocks into a week of slots
70+
const slots = getEmptySlots()
71+
availableComps.forEach((availableComp) => {
72+
const start = availableComp.getFirstProperty('dtstart').getFirstValue().toJSDate()
73+
const end = availableComp.getFirstProperty('dtend').getFirstValue().toJSDate()
74+
const rrule = availableComp.getFirstProperty('rrule')
75+
76+
if (rrule.getFirstValue().freq !== 'WEEKLY') {
77+
logger.warn('rrule not supported', {
78+
rrule: rrule.toICALString(),
79+
})
80+
return
81+
}
82+
83+
rrule.getFirstValue().getComponent('BYDAY').forEach(day => {
84+
slots[day].push({
85+
start,
86+
end,
87+
})
88+
})
89+
})
90+
91+
return {
92+
slots,
93+
}
94+
}
95+
96+
export async function saveScheduleInboxAvailability(slots, timezoneId) {
97+
const all = [...Object.keys(slots).flatMap(dayId => slots[dayId].map(slot => ({
98+
...slot,
99+
day: dayId,
100+
})))]
101+
102+
const vavailabilityComp = new ICAL.Component('vavailability')
103+
// TODO: deduplicate slots that occur on more than one day
104+
all.map(slot => {
105+
const availableComp = new ICAL.Component('available')
106+
107+
// Define DTSTART and DTEND
108+
// TODO: tz? moment.tz(dateTime, timezone).toDate()
109+
const startTimeProp = availableComp.addPropertyWithValue('dtstart', ICAL.Time.fromJSDate(slot.start, false))
110+
startTimeProp.setParameter('tzid', timezoneId)
111+
const endTimeProp = availableComp.addPropertyWithValue('dtend', ICAL.Time.fromJSDate(slot.end, false))
112+
endTimeProp.setParameter('tzid', timezoneId)
113+
114+
// Add mandatory UID
115+
availableComp.addPropertyWithValue('uid', uuidv4())
116+
117+
// TODO: add optional summary
118+
119+
// Define RRULE
120+
availableComp.addPropertyWithValue('rrule', {
121+
freq: 'WEEKLY',
122+
byday: slot.day,
123+
})
124+
125+
return availableComp
126+
}).map(vavailabilityComp.addSubcomponent.bind(vavailabilityComp))
127+
128+
const vcalendarComp = new ICAL.Component('vcalendar')
129+
vcalendarComp.addSubcomponent(vavailabilityComp)
130+
logger.debug('New availability ical created', {
131+
asObject: vcalendarComp,
132+
asString: vcalendarComp.toString(),
133+
})
134+
135+
const client = getClient('calendars')
136+
await client.customRequest('inbox', {
137+
method: 'PROPPATCH',
138+
data: `<?xml version="1.0"?>
139+
<x0:propertyupdate xmlns:x0="DAV:">
140+
<x0:set>
141+
<x0:prop>
142+
<x1:calendar-availability xmlns:x1="urn:ietf:params:xml:ns:caldav">${vcalendarComp.toString()}</x1:calendar-availability>
143+
</x0:prop>
144+
</x0:set>
145+
</x0:propertyupdate>`
146+
})
147+
}

0 commit comments

Comments
 (0)