285 lines
7.7 KiB
JavaScript
285 lines
7.7 KiB
JavaScript
/**
|
|
* @copyright Copyright (c) 2020 Georg Ehrke
|
|
*
|
|
* @author Georg Ehrke <oc.list@georgehrke.com>
|
|
*
|
|
* @license AGPL-3.0-or-later
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
import DavClient from '@nextcloud/cdav-library'
|
|
import { generateRemoteUrl } from '@nextcloud/router'
|
|
import { getRequestToken } from '@nextcloud/auth'
|
|
import { CALDAV_BIRTHDAY_CALENDAR } from '../models/consts.js'
|
|
|
|
const clients = {}
|
|
|
|
const getClientKey = (headers) => JSON.stringify(headers)
|
|
|
|
const getClient = (headers = {}) => {
|
|
const clientKey = getClientKey(headers)
|
|
if (clients[clientKey]) {
|
|
return clients[clientKey]
|
|
}
|
|
|
|
clients[clientKey] = new DavClient({
|
|
rootUrl: generateRemoteUrl('dav'),
|
|
}, () => {
|
|
const mergedHeaders = {
|
|
'X-Requested-With': 'XMLHttpRequest',
|
|
requesttoken: getRequestToken(),
|
|
'X-NC-CalDAV-Webcal-Caching': 'On',
|
|
...headers,
|
|
}
|
|
const xhr = new XMLHttpRequest()
|
|
const oldOpen = xhr.open
|
|
|
|
// override open() method to add headers
|
|
xhr.open = function() {
|
|
const result = oldOpen.apply(this, arguments)
|
|
for (const name in mergedHeaders) {
|
|
xhr.setRequestHeader(name, mergedHeaders[name])
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
OC.registerXHRForErrorProcessing(xhr) // eslint-disable-line no-undef
|
|
return xhr
|
|
})
|
|
|
|
return clients[clientKey]
|
|
}
|
|
|
|
/**
|
|
* Initializes the client for use in the user-view
|
|
*/
|
|
const initializeClientForUserView = async () => {
|
|
await getClient().connect({ enableCalDAV: true })
|
|
}
|
|
|
|
/**
|
|
* Initializes the client for use in the public/embed-view
|
|
*/
|
|
const initializeClientForPublicView = async () => {
|
|
await getClient()._createPublicCalendarHome()
|
|
}
|
|
|
|
/**
|
|
* Fetch all calendars from the server
|
|
*
|
|
* @param {object} headers
|
|
* @return {Promise<CalendarHome>}
|
|
*/
|
|
const getCalendarHome = (headers) => getClient(headers).calendarHomes[0]
|
|
|
|
/**
|
|
* Fetch all collections in the calendar home from the server
|
|
*
|
|
* @return {Promise<Collection[]>}
|
|
*/
|
|
const findAll = () => {
|
|
return getCalendarHome().findAllCalDAVCollectionsGrouped()
|
|
}
|
|
|
|
/**
|
|
* Fetch all subscriptions in the calendar home from the server
|
|
*/
|
|
export const findAllSubscriptions = async () => {
|
|
const headers = {
|
|
'X-NC-CalDAV-Webcal-Caching': 'Off',
|
|
}
|
|
|
|
// Ensure the client is initialized once
|
|
await getClient(headers).connect({ enableCalDAV: true })
|
|
|
|
return getCalendarHome(headers).findAllSubscriptions()
|
|
}
|
|
|
|
/**
|
|
* Fetch all deleted calendars from the server
|
|
*
|
|
* @return {Promise<Calendar[]>}
|
|
*/
|
|
const findAllDeletedCalendars = () => {
|
|
return getCalendarHome().findAllDeletedCalendars()
|
|
}
|
|
|
|
/**
|
|
* Fetch public calendars by their token
|
|
*
|
|
* @param {string[]} tokens List of tokens
|
|
* @return {Promise<Calendar[]>}
|
|
*/
|
|
const findPublicCalendarsByTokens = async (tokens) => {
|
|
const findPromises = []
|
|
|
|
for (const token of tokens) {
|
|
const promise = getClient().publicCalendarHome
|
|
.find(token)
|
|
.catch(() => null) // Catch outdated tokens
|
|
|
|
findPromises.push(promise)
|
|
}
|
|
|
|
const calendars = await Promise.all(findPromises)
|
|
return calendars.filter((calendar) => calendar !== null)
|
|
}
|
|
|
|
/**
|
|
* Fetches all scheduling inboxes
|
|
*
|
|
* Nitpick detail: Technically, we shouldn't be querying all scheduling inboxes
|
|
* in the calendar-home and just take the first one, but rather query the
|
|
* "CALDAV:schedule-inbox-URL" property on the principal URL and take that one.
|
|
* However, it doesn't make any difference for the Nextcloud CalDAV server
|
|
* and saves us extraneous requests here.
|
|
*
|
|
* https://tools.ietf.org/html/rfc6638#section-2.2.1
|
|
*
|
|
* @return {Promise<ScheduleInbox[]>}
|
|
*/
|
|
const findSchedulingInbox = async () => {
|
|
const inboxes = await getCalendarHome().findAllScheduleInboxes()
|
|
return inboxes[0]
|
|
}
|
|
|
|
/**
|
|
* Fetches all scheduling outboxes
|
|
*
|
|
* Nitpick detail: Technically, we shouldn't be querying all scheduling outboxes
|
|
* in the calendar-home and just take the first one, but rather query the
|
|
* "CALDAV:schedule-outbox-URL" property on the principal URL and take that one.
|
|
* However, it doesn't make any difference for the Nextcloud CalDAV server
|
|
* and saves us extraneous requests here.
|
|
*
|
|
* https://tools.ietf.org/html/rfc6638#section-2.1.1
|
|
*
|
|
* @return {Promise<ScheduleOutbox>}
|
|
*/
|
|
const findSchedulingOutbox = async () => {
|
|
const outboxes = await getCalendarHome().findAllScheduleOutboxes()
|
|
return outboxes[0]
|
|
}
|
|
|
|
/**
|
|
* Creates a calendar
|
|
*
|
|
* @param {string} displayName Visible name
|
|
* @param {string} color Color
|
|
* @param {string[]} components Supported component set
|
|
* @param {number} order Order of calendar in list
|
|
* @param {string} timezoneIcs ICS representation of timezone
|
|
* @return {Promise<Calendar>}
|
|
*/
|
|
const createCalendar = async (displayName, color, components, order, timezoneIcs) => {
|
|
return getCalendarHome().createCalendarCollection(displayName, color, components, order, timezoneIcs)
|
|
}
|
|
|
|
/**
|
|
* Creates a subscription
|
|
*
|
|
* This function does not return a subscription, but a cached calendar
|
|
*
|
|
* @param {string} displayName Visible name
|
|
* @param {string} color Color
|
|
* @param {string} source Link to WebCAL Source
|
|
* @param {number} order Order of calendar in list
|
|
* @return {Promise<Calendar>}
|
|
*/
|
|
const createSubscription = async (displayName, color, source, order) => {
|
|
return getCalendarHome().createSubscribedCollection(displayName, color, source, order)
|
|
}
|
|
|
|
/**
|
|
* Enables the birthday calendar
|
|
*
|
|
* @return {Promise<Calendar>}
|
|
*/
|
|
const enableBirthdayCalendar = async () => {
|
|
await getCalendarHome().enableBirthdayCalendar()
|
|
return getBirthdayCalendar()
|
|
}
|
|
|
|
/**
|
|
* Gets the birthday calendar
|
|
*
|
|
* @return {Promise<Calendar>}
|
|
*/
|
|
const getBirthdayCalendar = async () => {
|
|
return getCalendarHome().find(CALDAV_BIRTHDAY_CALENDAR)
|
|
}
|
|
|
|
/**
|
|
* Returns the Current User Principal
|
|
*
|
|
* @return {Principal}
|
|
*/
|
|
const getCurrentUserPrincipal = () => {
|
|
return getClient().currentUserPrincipal
|
|
}
|
|
|
|
/**
|
|
* Finds calendar principals by displayname
|
|
*
|
|
* @param {string} term The search-term
|
|
* @return {Promise<void>}
|
|
*/
|
|
const principalPropertySearchByDisplaynameOrEmail = async (term) => {
|
|
return getClient().principalPropertySearchByDisplaynameOrEmail(term)
|
|
}
|
|
|
|
/**
|
|
* Performs a principal property search based on multiple advanced filters
|
|
*
|
|
* @param {object} query The destructuring query object
|
|
* @param {string=} query.displayName The display name to search for
|
|
* @param {number=} query.capacity The minimum required seating capacity
|
|
* @param {string[]=} query.features The features to filter by
|
|
* @param {string=} query.roomType The room type to filter by
|
|
* @return {Promise<Principal[]>}
|
|
*/
|
|
const advancedPrincipalPropertySearch = async (query) => {
|
|
return getClient().advancedPrincipalPropertySearch(query)
|
|
}
|
|
|
|
/**
|
|
* Finds one principal by it's URL
|
|
*
|
|
* @param {string} url The principal-url
|
|
* @return {Promise<Principal>}
|
|
*/
|
|
const findPrincipalByUrl = async (url) => {
|
|
return getClient().findPrincipal(url)
|
|
}
|
|
|
|
export {
|
|
initializeClientForUserView,
|
|
initializeClientForPublicView,
|
|
findAll,
|
|
findAllDeletedCalendars,
|
|
findPublicCalendarsByTokens,
|
|
findSchedulingInbox,
|
|
findSchedulingOutbox,
|
|
createCalendar,
|
|
createSubscription,
|
|
enableBirthdayCalendar,
|
|
getBirthdayCalendar,
|
|
getCurrentUserPrincipal,
|
|
principalPropertySearchByDisplaynameOrEmail,
|
|
advancedPrincipalPropertySearch,
|
|
findPrincipalByUrl,
|
|
}
|