diff --git a/integ_tests/test_sharing.py b/integ_tests/test_sharing.py index 6138514f..d9515ec6 100644 --- a/integ_tests/test_sharing.py +++ b/integ_tests/test_sharing.py @@ -140,14 +140,22 @@ def test_share_with_property_overrides(page: Page, radicale_server: str) -> None page.click('button[data-name="sharebytoken"]') # Verify defaults + expect(page.locator('input[data-name="displayname_override"]')).to_have_value( + "Test Collection" + ) expect(page.locator('input[data-name="description_override"]')).to_have_value( "Original Description" ) expect(page.locator('input[data-name="color_override"]')).to_have_value("#ff0000") + expect(page.locator('input[data-name="displayname_override"]')).to_be_disabled() expect(page.locator('input[data-name="description_override"]')).to_be_disabled() expect(page.locator('input[data-name="color_override"]')).to_be_disabled() # Set overrides + page.click('label[for="newshare_attr_displayname_enabled"]') + page.locator('input[data-name="displayname_override"]').fill( + "Overridden Displayname" + ) page.click('label[for="newshare_attr_description_enabled"]') page.locator('input[data-name="description_override"]').fill( "Overridden Description" @@ -182,8 +190,11 @@ def test_share_journal_no_overrides(page: Page, radicale_server: str) -> None: page.click('article:not(.hidden) a[data-name="share"]', force=True, strict=True) page.click('button[data-name="sharebytoken"]') - # Verify property override fieldset is hidden - expect(page.locator('fieldset[data-name="properties_override"]')).to_be_hidden() + # Verify property override visibility + expect(page.locator('fieldset[data-name="properties_override"]')).to_be_visible() + expect(page.locator('input[data-name="displayname_override_enabled"]')).to_be_visible() + expect(page.locator('input[data-name="description_override_enabled"]')).to_be_hidden() + expect(page.locator('input[data-name="color_override_enabled"]')).to_be_hidden() # Create the share page.click('#newshare button[data-name="submit"]') diff --git a/radicale/web/internal_data/index.html b/radicale/web/internal_data/index.html index a6571212..dadc1807 100644 --- a/radicale/web/internal_data/index.html +++ b/radicale/web/internal_data/index.html @@ -262,6 +262,11 @@
Properties override +
+ + + +
diff --git a/radicale/web/internal_data/js/api/sharing.js b/radicale/web/internal_data/js/api/sharing.js index d145c3e1..a727362d 100644 --- a/radicale/web/internal_data/js/api/sharing.js +++ b/radicale/web/internal_data/js/api/sharing.js @@ -182,10 +182,13 @@ export const OVERLAY_PROPERTIES = { /** * Returns the correct internal property key for a given collection type and property name. * @param {string} type Collection type (ADDRESSBOOK, CALENDAR, etc.) - * @param {"DESCRIPTION" | "COLOR"} property Property name + * @param {"DISPLAYNAME" | "DESCRIPTION" | "COLOR"} property Property name * @returns {string | null} Internal property key or null if not supported */ export function get_property_key(type, property) { + if (property === "DISPLAYNAME") { + return "D:displayname"; + } if (type === CollectionType.ADDRESSBOOK) { return OVERLAY_PROPERTIES.ADDRESSBOOK[property]; } else if (CollectionType.is_subset(CollectionType.CALENDAR, type)) { diff --git a/radicale/web/internal_data/js/scenes/CreateEditShareScene.js b/radicale/web/internal_data/js/scenes/CreateEditShareScene.js index 9ff49b0e..9bb7392d 100644 --- a/radicale/web/internal_data/js/scenes/CreateEditShareScene.js +++ b/radicale/web/internal_data/js/scenes/CreateEditShareScene.js @@ -52,6 +52,8 @@ export class CreateEditShareScene { let permissions_rw_radio = /** @type {HTMLInputElement} */ (document.getElementById("newshare_attr_permissions_rw")); /** @type {HTMLElement} */ let properties_fieldset = html_scene.querySelector("[data-name=properties_override]"); + /** @type {HTMLInputElement} */ let displayname_override_enabled = html_scene.querySelector("[data-name=displayname_override_enabled]"); + /** @type {HTMLInputElement} */ let displayname_override_input = html_scene.querySelector("[data-name=displayname_override]"); /** @type {HTMLInputElement} */ let description_override_enabled = html_scene.querySelector("[data-name=description_override_enabled]"); /** @type {HTMLInputElement} */ let description_override_input = html_scene.querySelector("[data-name=description_override]"); /** @type {HTMLInputElement} */ let color_override_enabled = html_scene.querySelector("[data-name=color_override_enabled]"); @@ -69,6 +71,9 @@ export class CreateEditShareScene { sharehref_input.addEventListener("input", onCleanHREFinput); + displayname_override_enabled.onchange = function () { + displayname_override_input.disabled = !displayname_override_enabled.checked; + }; description_override_enabled.onchange = function () { description_override_input.disabled = !description_override_enabled.checked; }; @@ -98,6 +103,10 @@ export class CreateEditShareScene { let permissions = permissions_rw_radio.checked ? "rw" : "r"; let properties = {}; + if (displayname_override_enabled.checked) { + let key = get_property_key(collection.type, "DISPLAYNAME"); + if (key) properties[key] = displayname_override_input.value; + } if (description_override_enabled.checked) { let key = get_property_key(collection.type, "DESCRIPTION"); if (key) properties[key] = description_override_input.value; @@ -166,19 +175,26 @@ export class CreateEditShareScene { permissions_ro_radio.checked = edit ? share.Permissions.toLowerCase() === "r" : true; permissions_rw_radio.checked = edit ? share.Permissions.toLowerCase() === "rw" : false; + let displayname = collection.displayname || ""; let description = collection.description || ""; let color = collection.color || "#ffffff"; + let displayname_override_enabled_value = false; let description_override_enabled_value = false; let color_override_enabled_value = false; if (edit && share.Properties) { + let displayname_key = get_property_key(collection.type, "DISPLAYNAME"); + if (displayname_key && share.Properties[displayname_key] !== undefined) { + displayname = share.Properties[displayname_key]; + displayname_override_enabled_value = true; + } let description_key = get_property_key(collection.type, "DESCRIPTION"); - if (description_key && share.Properties[description_key]) { + if (description_key && share.Properties[description_key] !== undefined) { description = share.Properties[description_key]; description_override_enabled_value = true; } let color_key = get_property_key(collection.type, "COLOR"); - if (color_key && share.Properties[color_key]) { + if (color_key && share.Properties[color_key] !== undefined) { color = share.Properties[color_key]; if (color.length === 9 && color.endsWith("ff")) { color = color.substring(0, 7); @@ -187,6 +203,10 @@ export class CreateEditShareScene { } } + displayname_override_enabled.checked = displayname_override_enabled_value; + displayname_override_input.value = displayname; + displayname_override_input.disabled = !displayname_override_enabled_value; + description_override_enabled.checked = description_override_enabled_value; description_override_input.value = description; description_override_input.disabled = !description_override_enabled_value; @@ -197,10 +217,13 @@ export class CreateEditShareScene { let is_calendar = CollectionType.is_subset(CollectionType.CALENDAR, collection.type); let is_addressbook = collection.type === CollectionType.ADDRESSBOOK; + properties_fieldset.classList.remove("hidden"); if (is_calendar || is_addressbook) { - properties_fieldset.classList.remove("hidden"); + description_override_enabled.parentElement.classList.remove("hidden"); + color_override_enabled.parentElement.classList.remove("hidden"); } else { - properties_fieldset.classList.add("hidden"); + description_override_enabled.parentElement.classList.add("hidden"); + color_override_enabled.parentElement.classList.add("hidden"); } if (shareType === "map") {