diff --git a/integ_tests/test_sharing.py b/integ_tests/test_sharing.py
index 267a2580..94bde826 100644
--- a/integ_tests/test_sharing.py
+++ b/integ_tests/test_sharing.py
@@ -364,30 +364,37 @@ def test_create_and_delete_share_by_bday(
page.click('article:not(.hidden) a[data-name="share"]', force=True, strict=True)
expect(
- page.locator("tr[data-name='sharebdayrowtemplate']:not(.hidden)")
+ page.locator("tr[data-name='sharemaprowtemplate']:not(.hidden)")
).to_have_count(0)
- page.click('button[data-name="sharebybday"]')
+ page.click('button[data-name="sharebymap"]')
+ page.click('label[for="newshare_conv_bday"]')
- # verify user is auto-filled with current user (admin)
- expect(page.locator('input[data-name="shareuser"]')).to_have_value("admin")
- page.locator('input[data-name="sharehref"]').fill("bdaymapped")
+ # share to self (admin), which is allowed for conversions
+ page.locator('input[data-name="shareuser"]').fill("admin")
- # verify that the permissions section is hidden entirely
- expect(page.locator("input#newshare_attr_permissions_ro")).to_be_hidden()
- expect(page.locator("input#newshare_attr_permissions_rw")).to_be_hidden()
+ # verify that the permissions section is disabled
+ expect(page.locator("input#newshare_attr_permissions_ro")).to_be_disabled()
+ expect(page.locator("input#newshare_attr_permissions_rw")).to_be_disabled()
page.click('#newshare button[data-name="submit"]')
expect(
- page.locator("tr[data-name='sharebdayrowtemplate']:not(.hidden)")
+ page.locator("tr[data-name='sharemaprowtemplate']:not(.hidden)")
).to_have_count(1)
# verify no permissions pill in the bday row
expect(
page.locator(
- "tr[data-name='sharebdayrowtemplate']:not(.hidden) span[data-name='ro']"
+ "tr[data-name='sharemaprowtemplate']:not(.hidden) span[data-name='ro']"
)
- ).to_have_count(0)
+ ).to_be_hidden()
+
+ # Instead, there should be the conversion icon
+ expect(
+ page.locator(
+ "tr[data-name='sharemaprowtemplate']:not(.hidden) span[data-name='conversion']"
+ )
+ ).to_have_count(1)
# Close the share scene and verify the virtual bday calendar is now in the collections list
page.click('#sharecollectionscene button[data-name="cancel"]')
@@ -401,12 +408,12 @@ def test_create_and_delete_share_by_bday(
page.hover("article:not(.hidden) >> nth=0")
page.click('article:not(.hidden) >> nth=0 >> a[data-name="share"]', force=True)
page.click(
- "tr[data-name='sharebdayrowtemplate']:not(.hidden) button[data-name='delete']",
+ "tr[data-name='sharemaprowtemplate']:not(.hidden) button[data-name='delete']",
strict=True,
)
page.click('#deleteconfirmationscene button[data-name="delete"]')
expect(
- page.locator("tr[data-name='sharebdayrowtemplate']:not(.hidden)")
+ page.locator("tr[data-name='sharemaprowtemplate']:not(.hidden)")
).to_have_count(0)
@@ -429,8 +436,9 @@ def test_bday_section_hidden_for_calendar(
page.click('article:not(.hidden) a[data-name="share"]', force=True, strict=True)
expect(page.locator("#sharecollectionscene")).to_be_visible()
- expect(page.locator("div[data-name='sharebybday']")).to_be_hidden()
- page.click('#sharecollectionscene button[data-name="cancel"]')
+ page.click('button[data-name="sharebymap"]')
+ expect(page.locator("details[data-name='conversions']")).to_be_hidden()
+ page.click('#newshare button[data-name="cancel"]')
def test_bday_section_visible_for_addressbook(
@@ -452,5 +460,6 @@ def test_bday_section_visible_for_addressbook(
page.click('article:not(.hidden) a[data-name="share"]', force=True, strict=True)
expect(page.locator("#sharecollectionscene")).to_be_visible()
- expect(page.locator("div[data-name='sharebybday']")).to_be_visible()
- page.click('#sharecollectionscene button[data-name="cancel"]')
+ page.click('button[data-name="sharebymap"]')
+ expect(page.locator("details[data-name='conversions']")).to_be_visible()
+ page.click('#newshare button[data-name="cancel"]')
diff --git a/radicale/web/internal_data/index.html b/radicale/web/internal_data/index.html
index c1b917e3..278632c9 100644
--- a/radicale/web/internal_data/index.html
+++ b/radicale/web/internal_data/index.html
@@ -158,7 +158,7 @@
-
Properties override
diff --git a/radicale/web/internal_data/js/api/sharing.js b/radicale/web/internal_data/js/api/sharing.js
index 4a9a3e9f..49db2661 100644
--- a/radicale/web/internal_data/js/api/sharing.js
+++ b/radicale/web/internal_data/js/api/sharing.js
@@ -28,8 +28,7 @@ import { create_request, to_error_message } from "./common.js";
* @property {boolean} [PermittedCreateCollectionByMap]
* @property {boolean} [FeatureEnabledCollectionByToken]
* @property {boolean} [PermittedCreateCollectionByToken]
- * @property {boolean} [FeatureEnabledCollectionByBday]
- * @property {boolean} [PermittedCreateCollectionByBday]
+ * @property {Array} [SupportedConversions]
*/
/**
@@ -133,13 +132,12 @@ export function discover_server_features(user, password, callback) {
* @property {string} [Permissions]
* @property {?boolean} [EnabledByOwner]
* @property {?boolean} [EnabledByUser]
- * @property {?boolean} [Enabled]
* @property {?boolean} [HiddenByOwner]
* @property {?boolean} [HiddenByUser]
- * @property {?boolean} [Hidden]
* @property {number} [TimestampCreated]
* @property {number} [TimestampUpdated]
* @property {Object} [Properties]
+ * @property {string} [Conversion]
*/
@@ -154,13 +152,14 @@ export class Share {
/** @type {string} */ this.Owner = data.Owner || "";
/** @type {string} */ this.User = data.User || "";
/** @type {string} */ this.Permissions = data.Permissions || "r";
- /** @type {?boolean} */ this.EnabledByOwner = data.EnabledByOwner ?? data.Enabled ?? false;
- /** @type {?boolean} */ this.EnabledByUser = data.EnabledByUser ?? data.Enabled ?? null;
- /** @type {?boolean} */ this.HiddenByOwner = data.HiddenByOwner ?? data.Hidden ?? false;
- /** @type {?boolean} */ this.HiddenByUser = data.HiddenByUser ?? data.Hidden ?? null;
+ /** @type {?boolean} */ this.EnabledByOwner = data.EnabledByOwner ?? null;
+ /** @type {?boolean} */ this.EnabledByUser = data.EnabledByUser ?? null;
+ /** @type {?boolean} */ this.HiddenByOwner = data.HiddenByOwner ?? null;
+ /** @type {?boolean} */ this.HiddenByUser = data.HiddenByUser ?? null;
/** @type {number} */ this.TimestampCreated = data.TimestampCreated || 0;
/** @type {number} */ this.TimestampUpdated = data.TimestampUpdated || 0;
/** @type {Object} */ this.Properties = data.Properties || {};
+ /** @type {string} */ this.Conversion = data.Conversion || "";
}
}
@@ -247,6 +246,7 @@ export function add_share_by_token(
Enabled: share.EnabledByOwner,
Hidden: share.HiddenByOwner,
Properties: share.Properties,
+ Conversion: share.Conversion,
},
function (response) {
let json_response = JSON.parse(response);
@@ -287,6 +287,7 @@ export function add_share_by_map(
Properties: share.Properties,
User: share.User,
PathOrToken: share.PathOrToken,
+ Conversion: share.Conversion,
},
function (response) {
let json_response = JSON.parse(response);
@@ -388,6 +389,7 @@ export function update_share_by_token(
Enabled: share.EnabledByOwner,
Hidden: share.HiddenByOwner,
Properties: share.Properties,
+ Conversion: share.Conversion,
},
function (response) {
let json_response = JSON.parse(response);
@@ -428,6 +430,7 @@ export function update_share_by_map(
Enabled: share.EnabledByOwner,
Hidden: share.HiddenByOwner,
Properties: share.Properties,
+ Conversion: share.Conversion,
},
function (response) {
let json_response = JSON.parse(response);
@@ -482,114 +485,3 @@ export function update_incoming_share(
);
}
-/**
- * @param {string} user
- * @param {?string} password
- * @param {Share} share
- * @param {function(?string):void} callback
- */
-export function add_share_by_bday(
- user,
- password,
- share,
- callback,
-) {
- call_sharing_api(
- user,
- password,
- "bday/create",
- {
- PathMapped: share.PathMapped,
- Permissions: share.Permissions,
- Enabled: share.EnabledByOwner,
- Hidden: share.HiddenByOwner,
- Properties: share.Properties,
- User: share.User,
- PathOrToken: share.PathOrToken,
- },
- function (response) {
- let json_response = JSON.parse(response);
- if (json_response["Status"] !== "success") {
- callback(json_response["Status"] || "Unknown error");
- } else {
- callback(null);
- }
- },
- null,
- function (error) {
- callback(error);
- }
- );
-}
-
-/**
- * @param {string} user
- * @param {?string} password
- * @param {Share} share
- * @param {function(?string):void} callback
- */
-export function delete_share_by_bday(
- user,
- password,
- share,
- callback,
-) {
- call_sharing_api(
- user,
- password,
- "bday/delete",
- { PathOrToken: share.PathOrToken },
- function (response) {
- let json_response = JSON.parse(response);
- if (json_response["Status"] !== "success") {
- callback(json_response["Status"] || "Unknown error");
- } else {
- callback(null);
- }
- },
- null,
- function (error) {
- callback(error);
- }
- );
-}
-
-/**
- * @param {string} user
- * @param {?string} password
- * @param {Share} share
- * @param {function(?string):void} callback
- */
-export function update_share_by_bday(
- user,
- password,
- share,
- callback,
-) {
- call_sharing_api(
- user,
- password,
- "bday/update",
- {
- PathOrToken: share.PathOrToken,
- PathMapped: share.PathMapped,
- User: share.User,
- Permissions: share.Permissions,
- Enabled: share.EnabledByOwner,
- Hidden: share.HiddenByOwner,
- Properties: share.Properties,
- },
- function (response) {
- let json_response = JSON.parse(response);
- if (json_response["Status"] !== "success") {
- callback(json_response["Status"] || "Unknown error");
- } else {
- callback(null);
- }
- },
- null,
- function (error) {
- callback(error);
- }
- );
-}
diff --git a/radicale/web/internal_data/js/scenes/CollectionsScene.js b/radicale/web/internal_data/js/scenes/CollectionsScene.js
index 8e4ce432..c37a233c 100644
--- a/radicale/web/internal_data/js/scenes/CollectionsScene.js
+++ b/radicale/web/internal_data/js/scenes/CollectionsScene.js
@@ -183,6 +183,7 @@ export class CollectionsScene {
let share_info = get_element(node, "[data-name=shared-by]");
let transformed_from = get_element(node, "[data-name=transformed-from]");
let share = (shares || []).find(
+ // TODO: Eventually remove the check for "bday"
s => (s.ShareType === "map" || s.ShareType === "bday") &&
(s.PathOrToken || "").replace(/\/+$/, "") === (collection.href || "").replace(/\/+$/, ""));
if (share) {
diff --git a/radicale/web/internal_data/js/scenes/CreateEditShareScene.js b/radicale/web/internal_data/js/scenes/CreateEditShareScene.js
index f5c97518..9bc79d14 100644
--- a/radicale/web/internal_data/js/scenes/CreateEditShareScene.js
+++ b/radicale/web/internal_data/js/scenes/CreateEditShareScene.js
@@ -19,7 +19,7 @@
* along with this program. If not, see .
*/
-import { Share, add_share_by_bday, add_share_by_map, add_share_by_token, get_property_key, update_share_by_bday, update_share_by_map, update_share_by_token } from "../api/sharing.js";
+import { Share, add_share_by_map, add_share_by_token, get_property_key, update_share_by_map, update_share_by_token } from "../api/sharing.js";
import { CollectionType } from "../models/collection.js";
import { collectionsCache } from "../utils/collections_cache.js";
import { ErrorHandler } from "../utils/error.js";
@@ -51,6 +51,8 @@ export class CreateEditShareScene {
/** @type {HTMLInputElement} */ let hidden_checkbox = /** @type {HTMLInputElement} */ (get_element(html_scene, "[data-name=hidden]"));
let permissions_ro_radio = /** @type {HTMLInputElement} */ (get_element_by_id("newshare_attr_permissions_ro"));
let permissions_rw_radio = /** @type {HTMLInputElement} */ (get_element_by_id("newshare_attr_permissions_rw"));
+ /** @type {HTMLDetailsElement} */ let conversions_details = /** @type {HTMLDetailsElement} */ (get_element(html_scene, "[data-name=conversions]"));
+ /** @type {HTMLElement} */ let conversions_container = get_element(html_scene, "[data-name=conversions_container]");
/** @type {HTMLDetailsElement} */ let properties_fieldset = /** @type {HTMLDetailsElement} */ (get_element(html_scene, "[data-name=properties_override]"));
/** @type {HTMLInputElement} */ let displayname_override_enabled = /** @type {HTMLInputElement} */ (get_element(html_scene, "[data-name=displayname_override_enabled]"));
@@ -67,12 +69,14 @@ export class CreateEditShareScene {
let errorHandler = new ErrorHandler(error_form);
let map_validator = new FormValidator(errorHandler);
- if (shareType === "bday") {
- // bday can share to self, so just validate non-empty
- map_validator.addValidator(shareuser_input, validate_non_empty(shareuser_input, "Share User"));
- } else {
- map_validator.addValidator(shareuser_input, validate_not_empty_or_equals(shareuser_input, user, "Share User"));
- }
+ map_validator.addValidator(shareuser_input, function () {
+ let conversion = get_selected_conversion();
+ if (conversion === "bday") {
+ return validate_non_empty(shareuser_input, "Share User")();
+ } else {
+ return validate_not_empty_or_equals(shareuser_input, user, "Share User")();
+ }
+ });
map_validator.addValidator(sharehref_input, validate_href(sharehref_input, "Share Href"));
sharehref_input.addEventListener("input", onCleanHREFinput);
@@ -86,7 +90,29 @@ export class CreateEditShareScene {
color_override_enabled.onchange = function () {
color_override_input.disabled = !color_override_enabled.checked;
};
+ function get_selected_conversion() {
+ /** @type {HTMLInputElement | null} */
+ let checked = conversions_container.querySelector("input[name=conversion]:checked");
+ return checked ? checked.value : "none";
+ }
+ function on_conversion_change() {
+ let conversion = get_selected_conversion();
+ if (conversion === "bday") {
+ permissions_ro_radio.checked = true;
+ permissions_rw_radio.checked = false;
+ permissions_ro_radio.disabled = true;
+ permissions_rw_radio.disabled = true;
+ enabled_checkbox.checked = true;
+ hidden_checkbox.checked = false;
+ } else {
+ permissions_ro_radio.disabled = false;
+ permissions_rw_radio.disabled = false;
+ }
+ if (shareType === "map") {
+ map_validator.validate();
+ }
+ }
function oncancel() {
try {
@@ -99,14 +125,17 @@ export class CreateEditShareScene {
function onsubmit() {
try {
- if (shareType === "map" || shareType === "bday") {
+ if (shareType === "map") {
if (!map_validator.validate()) {
return false;
}
}
- let enabled_by_owner = (shareType === "bday" && shareuser_input.value === user) ? true : enabled_checkbox.checked;
- let hidden_by_owner = (shareType === "bday" && shareuser_input.value === user) ? false : hidden_checkbox.checked;
- let permissions = permissions_rw_radio.checked ? "rw" : "r";
+ let conversion = get_selected_conversion();
+ let is_birthday = conversion === "bday";
+ let enabled_by_owner = is_birthday ? true : enabled_checkbox.checked;
+ let hidden_by_owner = is_birthday ? false : hidden_checkbox.checked;
+ let permissions = is_birthday ? "r" : (permissions_rw_radio.checked ? "rw" : "r");
+ /** @type {string} */ let conversion_value = conversion;
/** @type {Object} */ let properties = {};
if (displayname_override_enabled.checked) {
@@ -129,9 +158,9 @@ export class CreateEditShareScene {
if (error) {
errorHandler.setError(error);
} else {
- // For bday shares to the current user, invalidate the
+ // On any share-to-self (currently only bday conversion), invalidate the
// collections cache so the virtual calendar appears immediately.
- if (shareType === "bday" && new_share.User === user) {
+ if (shareuser_input.value === user) {
collectionsCache.invalidate();
}
pop_scene();
@@ -148,22 +177,19 @@ export class CreateEditShareScene {
HiddenByUser: (edit && share) ? share.HiddenByUser : null,
Properties: properties,
User: (edit && share) ? share.User : shareuser_input.value,
- PathOrToken: (edit && share) ? share.PathOrToken : ((shareType === "map" || shareType === "bday") ? "/" + shareuser_input.value + "/" + sharehref_input.value + "/" : ""),
+ PathOrToken: (edit && share) ? share.PathOrToken : (shareType === "map" ? "/" + shareuser_input.value + "/" + sharehref_input.value + "/" : ""),
+ Conversion: conversion_value,
});
if (edit) {
if (shareType === "map") {
update_share_by_map(user, password, new_share, callback);
- } else if (shareType === "bday") {
- update_share_by_bday(user, password, new_share, callback);
} else {
update_share_by_token(user, password, new_share, callback);
}
} else {
if (shareType === "map") {
add_share_by_map(user, password, new_share, callback);
- } else if (shareType === "bday") {
- add_share_by_bday(user, password, new_share, callback);
} else {
add_share_by_token(user, password, new_share, callback);
}
@@ -191,25 +217,52 @@ export class CreateEditShareScene {
title.textContent = edit ? "Edit Share" : "New Share";
submit_btn.textContent = edit ? "Save" : "Create";
- shareuser_input.value = (edit && share) ? share.User : (shareType === "bday" ? user : "");
+ shareuser_input.value = (edit && share) ? share.User : "";
shareuser_input.disabled = edit;
enabled_checkbox.checked = (edit && share && share.EnabledByOwner !== null) ? share.EnabledByOwner : true;
hidden_checkbox.checked = (edit && share && share.HiddenByOwner !== null) ? share.HiddenByOwner : false;
- if (shareType === "bday") {
- // bday is always read-only; hide the permissions section entirely
- permissions_ro_radio.checked = true;
- permissions_rw_radio.checked = false;
- permissions_ro_radio.disabled = true;
- permissions_rw_radio.disabled = true;
- permissions_ro_radio.closest("details")?.classList.add("hidden");
- } else {
- permissions_ro_radio.closest("details")?.classList.remove("hidden");
- permissions_ro_radio.disabled = false;
- permissions_rw_radio.disabled = false;
- permissions_ro_radio.checked = (edit && share) ? share.Permissions.toLowerCase() === "r" : true;
- permissions_rw_radio.checked = (edit && share) ? share.Permissions.toLowerCase() === "rw" : false;
- }
+ let initial_conversion = (edit && share && share.Conversion) ? share.Conversion : "none";
+
+ collectionsCache.getServerFeatures(user, password, errorHandler.setError, (features) => {
+ conversions_container.innerHTML = "";
+ let supported = (features.sharing && features.sharing.SupportedConversions) || [];
+ if (supported.length > 0) {
+ let options = ["none", ...supported.filter(opt => opt !== "none")];
+ options.forEach(opt => {
+ let id = "newshare_conv_" + opt;
+ let radio = document.createElement("input");
+ radio.type = "radio";
+ radio.name = "conversion";
+ radio.value = opt;
+ radio.id = id;
+ radio.checked = (opt === initial_conversion);
+ radio.onchange = on_conversion_change;
+
+ let label = document.createElement("label");
+ label.htmlFor = id;
+ if (opt === "bday") {
+ label.textContent = "Birthday";
+ } else {
+ label.textContent = opt.charAt(0).toUpperCase() + opt.slice(1);
+ }
+
+ conversions_container.appendChild(radio);
+ conversions_container.appendChild(label);
+ });
+
+ let is_addressbook = collection.type === CollectionType.ADDRESSBOOK;
+ if (is_addressbook) {
+ conversions_details.classList.remove("hidden");
+ conversions_details.open = true;
+ } else {
+ conversions_details.classList.add("hidden");
+ }
+ } else {
+ conversions_details.classList.add("hidden");
+ }
+ on_conversion_change();
+ });
let displayname = collection.displayname || "";
let description = collection.description || "";
@@ -251,29 +304,44 @@ export class CreateEditShareScene {
color_override_input.value = color;
color_override_input.disabled = !color_override_enabled_value;
- let is_calendar = CollectionType.is_subset(CollectionType.CALENDAR, collection.type);
- let is_addressbook = collection.type === CollectionType.ADDRESSBOOK;
properties_fieldset.classList.remove("hidden");
if (displayname_override_enabled_value || description_override_enabled_value || color_override_enabled_value) {
properties_fieldset.open = true;
}
- if (is_calendar || is_addressbook) {
- if (description_override_enabled.parentElement) {
- description_override_enabled.parentElement.classList.remove("hidden");
- }
- if (color_override_enabled.parentElement) {
- color_override_enabled.parentElement.classList.remove("hidden");
+
+ let displayname_key = get_property_key(collection.type, "DISPLAYNAME");
+ if (!displayname_key) {
+ if (displayname_override_enabled.parentElement) {
+ displayname_override_enabled.parentElement.classList.add("hidden");
}
} else {
+ if (displayname_override_enabled.parentElement) {
+ displayname_override_enabled.parentElement.classList.remove("hidden");
+ }
+ }
+ let description_key = get_property_key(collection.type, "DESCRIPTION");
+ if (!description_key) {
if (description_override_enabled.parentElement) {
description_override_enabled.parentElement.classList.add("hidden");
}
- if (color_override_enabled.parentElement) {
- color_override_enabled.parentElement.classList.add("hidden");
+ } else {
+ if (description_override_enabled.parentElement) {
+ description_override_enabled.parentElement.classList.remove("hidden");
}
}
- if (shareType === "map" || shareType === "bday") {
+ let color_key = get_property_key(collection.type, "COLOR");
+ if (!color_key) {
+ if (color_override_enabled.parentElement) {
+ color_override_enabled.parentElement.classList.add("hidden");
+ }
+ } else {
+ if (color_override_enabled.parentElement) {
+ color_override_enabled.parentElement.classList.remove("hidden");
+ }
+ }
+
+ if (shareType === "map") {
if (edit && share) {
sharehref_input.value = share.PathOrToken.split("/").filter(Boolean).pop() || "";
} else {
diff --git a/radicale/web/internal_data/js/scenes/IncomingSharingScene.js b/radicale/web/internal_data/js/scenes/IncomingSharingScene.js
index 46bbfe94..557ab0ff 100644
--- a/radicale/web/internal_data/js/scenes/IncomingSharingScene.js
+++ b/radicale/web/internal_data/js/scenes/IncomingSharingScene.js
@@ -23,7 +23,7 @@ import { update_incoming_share } from "../api/sharing.js";
import { collectionsCache } from "../utils/collections_cache.js";
import { ErrorHandler } from "../utils/error.js";
import { get_element, get_element_by_id } from "../utils/misc.js";
-import { displayPermissions } from "../utils/permissions.js";
+import { displayPermissionsOrConversion } from "../utils/permissions.js";
import { Scene, pop_scene } from "./scene_manager.js";
/**
@@ -99,6 +99,7 @@ export class IncomingSharingScene {
let prefix = "/" + user + "/";
let filtered_shares = shares.filter(
+ // TODO: Eventually remove bday
share => (share.ShareType === "map" || share.ShareType === "bday")
&& share.PathOrToken.startsWith(prefix));
@@ -127,7 +128,7 @@ export class IncomingSharingScene {
pathortoken.value = displayPath;
owner_td.textContent = share.Owner;
- displayPermissions(share.Permissions, permissions_td);
+ displayPermissionsOrConversion(share.Conversion, share.Permissions, permissions_td);
let enabled = share.EnabledByUser !== null ? share.EnabledByUser : true;
let shown = share.HiddenByUser !== null ? !share.HiddenByUser : true;
diff --git a/radicale/web/internal_data/js/scenes/ShareCollectionScene.js b/radicale/web/internal_data/js/scenes/ShareCollectionScene.js
index 96dd459d..befe225c 100644
--- a/radicale/web/internal_data/js/scenes/ShareCollectionScene.js
+++ b/radicale/web/internal_data/js/scenes/ShareCollectionScene.js
@@ -20,16 +20,15 @@
*/
import {
- delete_share_by_bday,
delete_share_by_map,
delete_share_by_token,
reload_sharing_list,
} from "../api/sharing.js";
-import { Collection, CollectionType } from "../models/collection.js";
+import { Collection } from "../models/collection.js";
import { collectionsCache } from "../utils/collections_cache.js";
import { ErrorHandler } from "../utils/error.js";
import { get_element, get_element_by_id } from "../utils/misc.js";
-import { displayPermissions } from "../utils/permissions.js";
+import { displayPermissionsOrConversion } from "../utils/permissions.js";
import { CreateEditShareScene } from "./CreateEditShareScene.js";
import { DeleteConfirmationScene } from "./DeleteConfirmationScene.js";
import { Scene, pop_scene, push_scene } from "./scene_manager.js";
@@ -50,10 +49,8 @@ export class ShareCollectionScene {
/** @type {HTMLElement} */ let cancel_btn = get_element(html_scene, "[data-name=cancel]");
/** @type {HTMLElement} */ let share_by_token_btn = get_element(html_scene, "button[data-name=sharebytoken]");
/** @type {HTMLElement} */ let share_by_map_btn = get_element(html_scene, "button[data-name=sharebymap]");
- /** @type {HTMLElement} */ let share_by_bday_btn = get_element(html_scene, "button[data-name=sharebybday]");
/** @type {HTMLElement} */ let share_by_token_div = get_element(html_scene, "div[data-name=sharebytoken]");
/** @type {HTMLElement} */ let share_by_map_div = get_element(html_scene, "div[data-name=sharebymap]");
- /** @type {HTMLElement} */ let share_by_bday_div = get_element(html_scene, "div[data-name=sharebybday]");
/** @type {HTMLElement} */ let error_form = get_element(html_scene, "[data-name=error]");
let errorHandler = new ErrorHandler(error_form);
@@ -79,11 +76,6 @@ export class ShareCollectionScene {
push_scene(create_edit_share_scene);
}
- function onsharebybday() {
- let create_edit_share_scene = new CreateEditShareScene(user, password, collection, "bday");
- push_scene(create_edit_share_scene);
- }
-
this.show = function () {
this.release();
html_scene.classList.remove("hidden");
@@ -123,25 +115,6 @@ export class ShareCollectionScene {
if (share_by_map_div) share_by_map_div.classList.add("hidden");
}
- if (features.sharing && features.sharing.PermittedCreateCollectionByBday) {
- let is_addressbook = collection.type === CollectionType.ADDRESSBOOK;
- if (share_by_bday_btn) {
- if (is_addressbook) {
- share_by_bday_btn.classList.remove("hidden");
- share_by_bday_btn.onclick = onsharebybday;
- } else {
- share_by_bday_btn.classList.add("hidden");
- }
- }
- } else {
- if (share_by_bday_btn) share_by_bday_btn.classList.add("hidden");
- }
-
- if (features.sharing && features.sharing.FeatureEnabledCollectionByBday && collection.type === CollectionType.ADDRESSBOOK) {
- if (share_by_bday_div) share_by_bday_div.classList.remove("hidden");
- } else {
- if (share_by_bday_div) share_by_bday_div.classList.add("hidden");
- }
});
title.textContent = collection.displayname || collection.href;
@@ -164,7 +137,7 @@ export class ShareCollectionScene {
*/
function update_share_list(user, password, collection, errorHandler) {
let share_rows = document.querySelectorAll(
- "[data-name=sharetokenrowtemplate], [data-name=sharemaprowtemplate], [data-name=sharebdayrowtemplate]",
+ "[data-name=sharetokenrowtemplate], [data-name=sharemaprowtemplate]",
);
share_rows.forEach(function (row) {
if (!row.classList.contains("hidden")) {
@@ -206,7 +179,7 @@ function add_share_row_node(user, password, collection, share, template, delete_
}
let permissions = (share["Permissions"] || "").toLowerCase();
- displayPermissions(permissions, node);
+ displayPermissionsOrConversion(share["Conversion"], permissions, node);
/** @type {HTMLElement} */ let edit_btn = get_element(node, "[data-name=edit]");
edit_btn.onclick = function () {
@@ -242,7 +215,6 @@ function add_share_row_node(user, password, collection, share, template, delete_
function add_share_rows(user, password, collection, shares, errorHandler) {
/** @type {HTMLElement} */ let token_template = get_element(document, "[data-name=sharetokenrowtemplate]");
/** @type {HTMLElement} */ let map_template = get_element(document, "[data-name=sharemaprowtemplate]");
- /** @type {HTMLElement} */ let bday_template = get_element(document, "[data-name=sharebdayrowtemplate]");
shares.forEach(function (share) {
let pathortoken = share["PathOrToken"] || "";
let pathmapped = share["PathMapped"] || "";
@@ -252,11 +224,10 @@ function add_share_rows(user, password, collection, shares, errorHandler) {
) {
if (share["ShareType"] === "token") {
add_share_row_node(user, password, collection, share, token_template, "share", delete_share_by_token, errorHandler);
- } else if (share["ShareType"] === "map") {
+ }
+ // TODO: Eventually remove bday
+ else if (share["ShareType"] === "map" || share["ShareType"] === "bday") {
add_share_row_node(user, password, collection, share, map_template, "map", delete_share_by_map, errorHandler);
- } else if (share["ShareType"] === "bday") {
- let onDeleteSuccess = (share["User"] === user) ? function () { collectionsCache.invalidate(); } : undefined;
- add_share_row_node(user, password, collection, share, bday_template, "bday", delete_share_by_bday, errorHandler, onDeleteSuccess);
}
}
});
@@ -269,8 +240,7 @@ export function maybe_enable_sharing_options(features) {
if (!features || !features.sharing) return;
let map_is_enabled = features.sharing.FeatureEnabledCollectionByMap || false;
let token_is_enabled = features.sharing.FeatureEnabledCollectionByToken || false;
- let bday_is_enabled = features.sharing.FeatureEnabledCollectionByBday || false;
- let any_sharing_enabled = map_is_enabled || token_is_enabled || bday_is_enabled;
+ let any_sharing_enabled = map_is_enabled || token_is_enabled;
let share_options = document.querySelectorAll("[data-name=shareoption]");
for (let i = 0; i < share_options.length; i++) {
diff --git a/radicale/web/internal_data/js/utils/permissions.js b/radicale/web/internal_data/js/utils/permissions.js
index d5fff8e3..517b6e28 100644
--- a/radicale/web/internal_data/js/utils/permissions.js
+++ b/radicale/web/internal_data/js/utils/permissions.js
@@ -16,23 +16,34 @@
* along with this program. If not, see .
*/
+import { get_element } from "../utils/misc.js";
+
/**
+ * @param {string} conversion
* @param {string} permissions
* @param {HTMLElement} node
*/
-export function displayPermissions(permissions, node) {
- permissions = (permissions || "").toLowerCase();
- if (permissions === "rw") {
- const roElement = node.querySelector("[data-name=ro]");
- if (roElement && roElement.parentNode) {
- roElement.parentNode.removeChild(roElement);
- }
- } else if (permissions === "r") {
- const rwElement = node.querySelector("[data-name=rw]");
- if (rwElement && rwElement.parentNode) {
- rwElement.parentNode.removeChild(rwElement);
- }
+export function displayPermissionsOrConversion(conversion, permissions, node) {
+ const conversionElement = get_element(node, "[data-name=conversion]");
+ const roElement = get_element(node, "[data-name=ro]");
+ const rwElement = get_element(node, "[data-name=rw]");
+ let fixedConversion = (conversion || "").toLowerCase();
+ if (fixedConversion === "bday") {
+ conversionElement.classList.remove("hidden");
+ roElement.classList.add("hidden");
+ rwElement.classList.add("hidden");
} else {
- console.warn("Unknown permissions", permissions);
+ permissions = (permissions || "").toLowerCase();
+ if (permissions === "rw") {
+ rwElement.classList.remove("hidden");
+ roElement.classList.add("hidden");
+ conversionElement.classList.add("hidden");
+ } else if (permissions === "r") {
+ roElement.classList.remove("hidden");
+ rwElement.classList.add("hidden");
+ conversionElement.classList.add("hidden");
+ } else {
+ console.warn("Unknown permissions", permissions);
+ }
}
}