Skip to content

Commit

Permalink
Added Webcal support in web UI
Browse files Browse the repository at this point in the history
Added support to view, edit, and add Webcals in web UI to support functionality added in PR Kozea#1229.
  • Loading branch information
MatthewHana committed Mar 5, 2024
1 parent 6474f8f commit 80d91a8
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 14 deletions.
1 change: 1 addition & 0 deletions radicale/web/internal_data/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ button{
float: right;
margin-left: 10px;
background: black;
cursor: pointer;
}

input, select{
Expand Down
63 changes: 55 additions & 8 deletions radicale/web/internal_data/fn.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* This file is part of Radicale Server - Calendar Server
* Copyright © 2017-2018 Unrud <[email protected]>
* Copyright © 2017-2024 Unrud <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -63,6 +63,7 @@ const CollectionType = {
CALENDAR: "CALENDAR",
JOURNAL: "JOURNAL",
TASKS: "TASKS",
WEBCAL: "WEBCAL",
is_subset: function(a, b) {
let components = a.split("_");
for (let i = 0; i < components.length; i++) {
Expand All @@ -89,6 +90,9 @@ const CollectionType = {
if (a.search(this.TASKS) !== -1 || b.search(this.TASKS) !== -1) {
union.push(this.TASKS);
}
if (a.search(this.WEBCAL) !== -1 || b.search(this.WEBCAL) !== -1) {
union.push(this.WEBCAL);
}
return union.join("_");
}
};
Expand All @@ -102,12 +106,13 @@ const CollectionType = {
* @param {string} description
* @param {string} color
*/
function Collection(href, type, displayname, description, color) {
function Collection(href, type, displayname, description, color, source) {
this.href = href;
this.type = type;
this.displayname = displayname;
this.color = color;
this.description = description;
this.source = source;
}

/**
Expand Down Expand Up @@ -183,18 +188,25 @@ function get_collections(user, password, collection, callback) {
let addressbookcolor_element = response.querySelector(response_query + " > *|propstat > *|prop > *|addressbook-color");
let calendardesc_element = response.querySelector(response_query + " > *|propstat > *|prop > *|calendar-description");
let addressbookdesc_element = response.querySelector(response_query + " > *|propstat > *|prop > *|addressbook-description");
let webcalsource_element = response.querySelector(response_query + " > *|propstat > *|prop > *|source");
let components_query = response_query + " > *|propstat > *|prop > *|supported-calendar-component-set";
let components_element = response.querySelector(components_query);
let href = href_element ? href_element.textContent : "";
let displayname = displayname_element ? displayname_element.textContent : "";
let type = "";
let color = "";
let description = "";
let source = "";
if (resourcetype_element) {
if (resourcetype_element.querySelector(resourcetype_query + " > *|addressbook")) {
type = CollectionType.ADDRESSBOOK;
color = addressbookcolor_element ? addressbookcolor_element.textContent : "";
description = addressbookdesc_element ? addressbookdesc_element.textContent : "";
} else if (resourcetype_element.querySelector(resourcetype_query + " > *|subscribed")) {
type = CollectionType.union(type, CollectionType.WEBCAL);
source = webcalsource_element ? webcalsource_element.textContent : "";
color = calendarcolor_element ? calendarcolor_element.textContent : "";
description = calendardesc_element ? calendardesc_element.textContent : "";
} else if (resourcetype_element.querySelector(resourcetype_query + " > *|calendar")) {
if (components_element) {
if (components_element.querySelector(components_query + " > *|comp[name=VEVENT]")) {
Expand All @@ -221,7 +233,7 @@ function get_collections(user, password, collection, callback) {
}
}
if (href.substr(-1) === "/" && href !== collection.href && type) {
collections.push(new Collection(href, type, displayname, description, sane_color));
collections.push(new Collection(href, type, displayname, description, sane_color, source));
}
}
collections.sort(function(a, b) {
Expand All @@ -235,11 +247,15 @@ function get_collections(user, password, collection, callback) {
}
};
request.send('<?xml version="1.0" encoding="utf-8" ?>' +
'<propfind xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav" ' +
'<propfind ' +
'xmlns="DAV:" ' +
'xmlns:C="urn:ietf:params:xml:ns:caldav" ' +
'xmlns:CR="urn:ietf:params:xml:ns:carddav" ' +
'xmlns:CS="http://calendarserver.org/ns/" ' +
'xmlns:I="http://apple.com/ns/ical/" ' +
'xmlns:INF="http://inf-it.com/ns/ab/" ' +
'xmlns:RADICALE="http://radicale.org/ns/">' +
'xmlns:RADICALE="http://radicale.org/ns/"' +
'>' +
'<prop>' +
'<resourcetype />' +
'<RADICALE:displayname />' +
Expand All @@ -248,6 +264,7 @@ function get_collections(user, password, collection, callback) {
'<C:calendar-description />' +
'<C:supported-calendar-component-set />' +
'<CR:addressbook-description />' +
'<CS:source />' +
'</prop>' +
'</propfind>');
return request;
Expand Down Expand Up @@ -329,12 +346,18 @@ function create_edit_collection(user, password, collection, create, callback) {
let addressbook_color = "";
let calendar_description = "";
let addressbook_description = "";
let calendar_source = "";
let resourcetype;
let components = "";
if (collection.type === CollectionType.ADDRESSBOOK) {
addressbook_color = escape_xml(collection.color + (collection.color ? "ff" : ""));
addressbook_description = escape_xml(collection.description);
resourcetype = '<CR:addressbook />';
} else if (collection.type === CollectionType.WEBCAL) {
calendar_color = escape_xml(collection.color + (collection.color ? "ff" : ""));
calendar_description = escape_xml(collection.description);
resourcetype = '<CS:subscribed />';
calendar_source = collection.source;
} else {
calendar_color = escape_xml(collection.color + (collection.color ? "ff" : ""));
calendar_description = escape_xml(collection.description);
Expand All @@ -351,7 +374,7 @@ function create_edit_collection(user, password, collection, create, callback) {
}
let xml_request = create ? "mkcol" : "propertyupdate";
request.send('<?xml version="1.0" encoding="UTF-8" ?>' +
'<' + xml_request + ' xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:CR="urn:ietf:params:xml:ns:carddav" xmlns:I="http://apple.com/ns/ical/" xmlns:INF="http://inf-it.com/ns/ab/">' +
'<' + xml_request + ' xmlns="DAV:" xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:CR="urn:ietf:params:xml:ns:carddav" xmlns:CS="http://calendarserver.org/ns/" xmlns:I="http://apple.com/ns/ical/" xmlns:INF="http://inf-it.com/ns/ab/">' +
'<set>' +
'<prop>' +
(create ? '<resourcetype><collection />' + resourcetype + '</resourcetype>' : '') +
Expand All @@ -361,6 +384,7 @@ function create_edit_collection(user, password, collection, create, callback) {
(addressbook_color ? '<INF:addressbook-color>' + addressbook_color + '</INF:addressbook-color>' : '') +
(addressbook_description ? '<CR:addressbook-description>' + addressbook_description + '</CR:addressbook-description>' : '') +
(calendar_description ? '<C:calendar-description>' + calendar_description + '</C:calendar-description>' : '') +
(calendar_source ? '<CS:source>' + calendar_source + '</CS:source>' : '') +
'</prop>' +
'</set>' +
(!create ? ('<remove>' +
Expand Down Expand Up @@ -692,7 +716,7 @@ function CollectionsScene(user, password, collection, onerror) {
if (collection.color) {
color_form.style.background = collection.color;
}
let possible_types = [CollectionType.ADDRESSBOOK];
let possible_types = [CollectionType.ADDRESSBOOK, CollectionType.WEBCAL];
[CollectionType.CALENDAR, ""].forEach(function(e) {
[CollectionType.union(e, CollectionType.JOURNAL), e].forEach(function(e) {
[CollectionType.union(e, CollectionType.TASKS), e].forEach(function(e) {
Expand Down Expand Up @@ -1005,12 +1029,19 @@ function CreateEditCollectionScene(user, password, collection) {
let title_form = edit ? html_scene.querySelector("[data-name=title]") : null;
let error_form = html_scene.querySelector("[data-name=error]");
let displayname_form = html_scene.querySelector("[data-name=displayname]");
let displayname_label = html_scene.querySelector("label[for=displayname]");
let description_form = html_scene.querySelector("[data-name=description]");
let description_label = html_scene.querySelector("label[for=description]");
let source_form = html_scene.querySelector("[data-name=source]");
let source_label = html_scene.querySelector("label[for=source]");
let type_form = html_scene.querySelector("[data-name=type]");
let type_label = html_scene.querySelector("label[for=type]");
let color_form = html_scene.querySelector("[data-name=color]");
let color_label = html_scene.querySelector("label[for=color]");
let submit_btn = html_scene.querySelector("[data-name=submit]");
let cancel_btn = html_scene.querySelector("[data-name=cancel]");


/** @type {?number} */ let scene_index = null;
/** @type {?XMLHttpRequest} */ let create_edit_req = null;
let error = "";
Expand All @@ -1019,6 +1050,7 @@ function CreateEditCollectionScene(user, password, collection) {
let href = edit ? collection.href : collection.href + random_uuid() + "/";
let displayname = edit ? collection.displayname : "";
let description = edit ? collection.description : "";
let source = edit ? collection.source : "";
let type = edit ? collection.type : CollectionType.CALENDAR_JOURNAL_TASKS;
let color = edit && collection.color ? collection.color : "#" + random_hex(6);

Expand All @@ -1038,20 +1070,25 @@ function CreateEditCollectionScene(user, password, collection) {
function read_form() {
displayname = displayname_form.value;
description = description_form.value;
source = source_form.value;
type = type_form.value;
color = color_form.value;
}

function fill_form() {
displayname_form.value = displayname;
description_form.value = description;
source_form.value = source;
type_form.value = type;
color_form.value = color;
if(error){
error_form.textContent = "Error: " + error;
error_form.classList.remove("hidden");
}
error_form.classList.add("hidden");

onTypeChange();
type_form.addEventListener("change", onTypeChange);
}

function onsubmit() {
Expand All @@ -1069,7 +1106,7 @@ function CreateEditCollectionScene(user, password, collection) {
}
let loading_scene = new LoadingScene();
push_scene(loading_scene);
let collection = new Collection(href, type, displayname, description, sane_color);
let collection = new Collection(href, type, displayname, description, sane_color, source);
let callback = function(error1) {
if (scene_index === null) {
return;
Expand Down Expand Up @@ -1102,6 +1139,16 @@ function CreateEditCollectionScene(user, password, collection) {
return false;
}

function onTypeChange(e){
if(type_form.value == CollectionType.WEBCAL){
source_label.classList.remove("hidden");
source_form.classList.remove("hidden");
}else{
source_label.classList.add("hidden");
source_form.classList.add("hidden");
}
}

this.show = function() {
this.release();
scene_index = scene_stack.length - 1;
Expand Down
19 changes: 13 additions & 6 deletions radicale/web/internal_data/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ <h3 class="title" data-name="title">Title</h3>
<span data-name="CALENDAR">Calendar</span>
<span data-name="JOURNAL">Journal</span>
<span data-name="TASKS">Tasks</span>
<span data-name="WEBCAL">Webcal</span>
</small>
<input type="text" data-name="url" value="" readonly="" onfocus="this.setSelectionRange(0, 99999);">
<p data-name="description" style="word-wrap:break-word;">Description</p>
Expand Down Expand Up @@ -97,12 +98,15 @@ <h1>Edit Collection</h1>
<option value="CALENDAR">calendar</option>
<option value="JOURNAL">journal</option>
<option value="TASKS">tasks</option>
<option value="WEBCAL">webcal</option>
</select>
<br> Title: <br>
<label for="displayname">Title:</label>
<input data-name="displayname" type="text">
<br> Description: <br>
<label for="description">Description:</label>
<input data-name="description" type="text">
<br> Color: <br>
<label for="source">Source:</label>
<input data-name="source" type="url">
<label for="color">Color:</label>
<input data-name="color" type="color">
<br>
<span class="error hidden" data-name="error"></span>
Expand All @@ -125,12 +129,15 @@ <h1>Create a new Collection</h1>
<option value="CALENDAR">Calendar</option>
<option value="JOURNAL">Journal</option>
<option value="TASKS">Tasks</option>
<option value="WEBCAL">Webcal</option>
</select>
<br> Title: <br>
<label for="displayname">Title:</label>
<input data-name="displayname" type="text">
<br> Description: <br>
<label for="description">Description:</label>
<input data-name="description" type="text">
<br> Color: <br>
<label for="source">Source:</label>
<input data-name="source" type="url">
<label for="color">Color:</label>
<input data-name="color" type="color">
<br>
<span class="error" data-name="error"></span>
Expand Down
3 changes: 3 additions & 0 deletions radicale/xmlutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,9 @@ def props_from_request(xml_request: Optional[ET.Element]
if resource_type.tag == make_clark("C:calendar"):
value = "VCALENDAR"
break
if resource_type.tag == make_clark("CS:subscribed"):
value = "VSUBSCRIBED"
break
if resource_type.tag == make_clark("CR:addressbook"):
value = "VADDRESSBOOK"
break
Expand Down

0 comments on commit 80d91a8

Please sign in to comment.