From 797c95877ea468a6e345db580f4910f4a08b695f Mon Sep 17 00:00:00 2001
From: the-djmaze <>
Date: Tue, 25 Oct 2022 16:08:28 +0200
Subject: [PATCH] Added Nextcloud save to Calendar #569
---
plugins/nextcloud/index.php | 1 +
plugins/nextcloud/js/composer.js | 2 +-
plugins/nextcloud/js/message.js | 31 ++++++-
plugins/nextcloud/js/webdav.js | 137 +++++++++++++++++++++++++++----
plugins/nextcloud/langs/en.ini | 2 +
5 files changed, 154 insertions(+), 19 deletions(-)
diff --git a/plugins/nextcloud/index.php b/plugins/nextcloud/index.php
index ca0bf73507..d629bbffbf 100644
--- a/plugins/nextcloud/index.php
+++ b/plugins/nextcloud/index.php
@@ -30,6 +30,7 @@ public function Init() : void
$this->addJs('js/messagelist.js');
$this->addTemplate('templates/PopupsNextcloudFiles.html');
+ $this->addTemplate('templates/PopupsNextcloudCalendars.html');
}
}
diff --git a/plugins/nextcloud/js/composer.js b/plugins/nextcloud/js/composer.js
index 26073b63fc..02d65f0210 100644
--- a/plugins/nextcloud/js/composer.js
+++ b/plugins/nextcloud/js/composer.js
@@ -5,7 +5,7 @@
if ('PopupsCompose' === e.detail.viewModelTemplateID) {
let view = e.detail;
view.nextcloudAttach = () => {
- rl.ncFiles.selectFiles().then(files => {
+ rl.nextcloud.selectFiles().then(files => {
files && files.forEach(file => {
let attachment = view.addAttachmentHelper(
Jua?.randomId(),
diff --git a/plugins/nextcloud/js/message.js b/plugins/nextcloud/js/message.js
index bc7353897e..8bde31d695 100644
--- a/plugins/nextcloud/js/message.js
+++ b/plugins/nextcloud/js/message.js
@@ -13,7 +13,7 @@
.filter(v => v);
if (hashes.length) {
view.saveNextcloudLoading(true);
- rl.ncFiles.selectFolder().then(folder => {
+ rl.nextcloud.selectFolder().then(folder => {
if (folder) {
rl.fetchJSON('./?/Json/&q[]=/0/', {}, {
Action: 'AttachmentsActions',
@@ -41,7 +41,7 @@
};
view.nextcloudSaveMsg = () => {
- rl.ncFiles.selectFolder().then(folder => {
+ rl.nextcloud.selectFolder().then(folder => {
let msg = view.message();
folder && rl.pluginRemoteRequest(
(iError, data) => {
@@ -59,6 +59,28 @@
);
});
};
+
+ view.nextcloudICS = ko.computed(() => {
+ let msg = view.message();
+ return msg
+ ? msg.attachments.find(attachment => 'text/calendar' == attachment.mimeType)
+ : null;
+ }, {'pure':true});
+
+ view.nextcloudSaveICS = () => {
+ let attachment = view.nextcloudICS();
+ attachment && rl.nextcloud.selectCalendar().then(href => {
+ console.dir({href: href});
+ fetch(attachment.linkDownload(), {
+ mode: 'same-origin',
+ cache: 'no-cache',
+ redirect: 'error',
+ credentials: 'same-origin'
+ })
+ .then(response => (response.status < 400) ? response.text() : Promise.reject(new Error({ response })))
+ .then(text => rl.nextcloud.calendarPut(href, text));
+ });
+ }
}
});
@@ -71,6 +93,11 @@
+ '💾'
+ ''
+ ''));
+
+ // https://github.com/nextcloud/calendar/issues/4684
+// attachmentsControls.append(Element.fromHTML(''
+// + ''
+// + ''));
}
const msgMenu = template.content.querySelector('#more-view-dropdown-id + menu');
diff --git a/plugins/nextcloud/js/webdav.js b/plugins/nextcloud/js/webdav.js
index 722a7ce554..12a8aa5449 100644
--- a/plugins/nextcloud/js/webdav.js
+++ b/plugins/nextcloud/js/webdav.js
@@ -2,24 +2,34 @@
const
namespace = 'DAV:',
+ nsCalDAV = 'urn:ietf:params:xml:ns:caldav',
- propfindBody = `
-
+ propfindFiles = `
+
-
-
-
+
+
+
+
+`,
+
+ propfindCal = `
+
+
+
+
+
`,
xmlParser = new DOMParser(),
- pathRegex = /.*\/remote.php\/dav\/files\/[^/]+/g,
+ pathRegex = /.*\/remote.php\/dav\/[^/]+\/[^/]+/g,
getDavElementsByTagName = (parent, localName) => parent.getElementsByTagNameNS(namespace, localName),
getDavElementByTagName = (parent, localName) => getDavElementsByTagName(parent, localName)?.item(0),
getElementByTagName = (parent, localName) => +parent.getElementsByTagName(localName)?.item(0),
- davFetch = (path, options) => {
+ davFetch = (mode, path, options) => {
if (!parent.OC.requestToken) {
return Promise.reject(new Error('OC.requestToken missing'));
}
@@ -32,21 +42,23 @@ const
headers: {}
}, options);
options.headers.requesttoken = parent.OC.requestToken;
- return fetch(cfg.WebDAV + '/files/' + cfg.UID + path, options);
+ return fetch(cfg.WebDAV + '/' + mode + '/' + cfg.UID + path, options);
},
- createDirectory = path => davFetch(path, { method: 'MKCOL' }),
+ davFetchFiles = (path, options) => davFetch('files', path, options),
+
+ createDirectory = path => davFetchFiles(path, { method: 'MKCOL' }),
fetchFiles = path => {
if (!parent.OC.requestToken) {
return Promise.reject(new Error('OC.requestToken missing'));
}
- return davFetch(path, {
+ return davFetchFiles(path, {
method: 'PROPFIND',
headers: {
'Content-Type': 'application/xml; charset=utf-8'
},
- body: propfindBody
+ body: propfindFiles
})
.then(response => (response.status < 400) ? response.text() : Promise.reject(new Error({ response })))
.then(text => {
@@ -61,10 +73,10 @@ const
const
e = responseList.item(i),
elem = {
- name: decodeURIComponent(getDavElementByTagName(e, 'href').innerHTML)
+ name: getDavElementByTagName(e, 'href').textContent
.replace(pathRegex, '').replace(/\/$/, ''),
isFile: false
- }
+ };
if (getDavElementsByTagName(getDavElementByTagName(e, 'resourcetype'), 'collection').length) {
// skip current directory
if (elem.name === path) {
@@ -72,8 +84,8 @@ const
}
} else {
elem.isFile = true;
- elem.size = getDavElementByTagName(e, 'getcontentlength')?.innerHTML
- || getElementByTagName(e, 'oc:size')?.innerHTML;
+ elem.size = getDavElementByTagName(e, 'getcontentlength')?.textContent
+ || getElementByTagName(e, 'oc:size')?.textContent;
}
elemList.push(elem);
}
@@ -202,7 +214,100 @@ close() {}
*/
}
-rl.ncFiles = {
+class NextcloudCalendarsPopupView extends rl.pluginPopupView {
+ constructor() {
+ super('NextcloudCalendars');
+ }
+
+ onBuild(dom) {
+ this.tree = dom.querySelector('#sm-nc-calendars');
+ this.tree.addEventListener('click', event => {
+ let el = event.target;
+ if (el.matches('button')) {
+ this.select = el.href;
+ this.close();
+ }
+ });
+ }
+
+ // Happens after showModal()
+ beforeShow(fResolve) {
+ this.select = '';
+ this.fResolve = fResolve;
+ this.tree.innerHTML = '';
+ davFetch('calendars', '/', {
+ method: 'PROPFIND',
+ headers: {
+ 'Content-Type': 'application/xml; charset=utf-8'
+ },
+ body: propfindCal
+ })
+ .then(response => (response.status < 400) ? response.text() : Promise.reject(new Error({ response })))
+ .then(text => {
+ const
+ responseList = getDavElementsByTagName(
+ xmlParser.parseFromString(text, 'application/xml').documentElement,
+ 'response'
+ );
+ for (let i = 0; i < responseList.length; ++i) {
+ const e = responseList.item(i);
+ if (getDavElementByTagName(e, 'resourcetype').getElementsByTagNameNS(nsCalDAV, 'calendar').length) {
+// && getDavElementsByTagName(getDavElementByTagName(e, 'current-user-privilege-set'), 'write').length) {
+ const li = document.createElement('li'),
+ btn = document.createElement('button');
+ li.textContent = getDavElementByTagName(e, 'displayname').textContent;
+ btn.href = getDavElementByTagName(e, 'href').textContent
+ .replace(pathRegex, '').replace(/\/$/, '');
+ btn.textContent = 'select';
+ btn.className = 'button-vue';
+ btn.style.marginLeft = '1em';
+ li.append(btn);
+ this.tree.append(li);
+ }
+ }
+ })
+ .catch(err => console.error(err));
+ }
+
+ onHide() {
+ this.fResolve(this.select);
+ }
+/*
+beforeShow() {} // Happens before showModal()
+onShow() {} // Happens after showModal()
+afterShow() {} // Happens after showModal() animation transitionend
+onHide() {} // Happens before animation transitionend
+afterHide() {} // Happens after animation transitionend
+close() {}
+*/
+}
+
+rl.nextcloud = {
+ selectCalendar: () =>
+ new Promise(resolve => {
+ NextcloudCalendarsPopupView.showModal([
+ href => resolve(href),
+ ]);
+ }),
+
+ calendarPut: (path, event) => {
+ // Validation error in iCalendar: A calendar object on a CalDAV server MUST NOT have a METHOD property.
+ event = event.replace(/METHOD:.+\r?\n/i, '');
+
+ let m = event.match(/UID:(.+)/);
+ davFetch('calendars', path + '/' + m[1] + '.ics', {
+ method: 'PUT',
+ headers: {
+ 'Content-Type': 'text/calendar'
+ },
+ body: event
+ })
+ .then(response => (response.status < 400) ? response.text() : Promise.reject(new Error({ response })))
+ .then(text => {
+ console.dir({event_response:text});
+ });
+ },
+
selectFolder: () =>
new Promise(resolve => {
NextcloudFilesPopupView.showModal([
diff --git a/plugins/nextcloud/langs/en.ini b/plugins/nextcloud/langs/en.ini
index 4a5c783fda..830b4ecabe 100644
--- a/plugins/nextcloud/langs/en.ini
+++ b/plugins/nextcloud/langs/en.ini
@@ -1,6 +1,8 @@
[NEXTCLOUD]
SAVE_ATTACHMENTS = "Save in Nextcloud"
SAVE_EML = "Save as .eml in Nextcloud"
+SAVE_ICS = "Put in Calendar"
SELECT_FOLDER = "Select folder"
SELECT_FILES = "Select file(s)"
ATTACH_FILES = "Attach Nextcloud files"
+SELECT_CALENDAR = "Select calendar"