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 @@ - rwro + rwro - rwro + rwro @@ -253,7 +226,7 @@ rwro + data-name="ro">ro @@ -285,11 +258,17 @@
Permissions - -
+
Properties override
diff --git a/radicale/web/internal_data/js/api/sharing.js b/radicale/web/internal_data/js/api/sharing.js index e02b1f6d..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] */ /** @@ -138,6 +137,7 @@ export function discover_server_features(user, password, callback) { * @property {number} [TimestampCreated] * @property {number} [TimestampUpdated] * @property {Object} [Properties] + * @property {string} [Conversion] */ @@ -159,6 +159,7 @@ export class Share { /** @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 || ""; } } @@ -245,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); @@ -285,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); @@ -386,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); @@ -426,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); @@ -480,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); + } } }