Implemented proper property overrides
This commit is contained in:
@@ -59,6 +59,7 @@ collection_by_map = true
|
||||
collection_by_token = true
|
||||
permit_create_token = true
|
||||
permit_create_map = true
|
||||
permit_properties_overlay = true
|
||||
|
||||
"""
|
||||
)
|
||||
|
||||
@@ -112,3 +112,61 @@ def test_create_and_delete_share_by_map(page: Page, radicale_server: str) -> Non
|
||||
expect(
|
||||
page.locator("tr[data-name='sharemaprowtemplate']:not(.hidden)")
|
||||
).to_have_count(0)
|
||||
|
||||
|
||||
def test_share_with_property_overrides(page: Page, radicale_server: str) -> None:
|
||||
login(page, radicale_server)
|
||||
# Create a collection with specific details
|
||||
page.click('a[data-name="new"]')
|
||||
page.locator('#createcollectionscene input[data-name="displayname"]').fill("Test Collection")
|
||||
page.locator('#createcollectionscene input[data-name="description"]').fill("Original Description")
|
||||
page.locator('#createcollectionscene input[data-name="color"]').fill("#ff0000")
|
||||
page.click('#createcollectionscene button[data-name="submit"]')
|
||||
|
||||
page.hover("article:not(.hidden)")
|
||||
page.click('article:not(.hidden) a[data-name="share"]', force=True, strict=True)
|
||||
page.click('button[data-name="sharebytoken"]')
|
||||
|
||||
# Verify defaults
|
||||
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="description_override"]')).to_be_disabled()
|
||||
expect(page.locator('input[data-name="color_override"]')).to_be_disabled()
|
||||
|
||||
# Set overrides
|
||||
page.click('label[for="newshare_attr_description_enabled"]')
|
||||
page.locator('input[data-name="description_override"]').fill("Overridden Description")
|
||||
page.click('label[for="newshare_attr_color_enabled"]')
|
||||
page.locator('input[data-name="color_override"]').fill("#00ff00")
|
||||
|
||||
page.click('#newshare button[data-name="submit"]')
|
||||
|
||||
# Verify the share was created
|
||||
expect(
|
||||
page.locator("tr[data-name='sharetokenrowtemplate']:not(.hidden)")
|
||||
).to_have_count(1)
|
||||
|
||||
|
||||
def test_share_journal_no_overrides(page: Page, radicale_server: str) -> None:
|
||||
login(page, radicale_server)
|
||||
# Create a collection of type JOURNAL
|
||||
page.click('a[data-name="new"]')
|
||||
page.locator('#createcollectionscene select[data-name="type"]').select_option("JOURNAL")
|
||||
page.locator('#createcollectionscene input[data-name="displayname"]').fill("Test Journal")
|
||||
page.locator('#createcollectionscene input[data-name="description"]').fill("Journal Description")
|
||||
page.click('#createcollectionscene button[data-name="submit"]')
|
||||
|
||||
page.hover("article:not(.hidden)")
|
||||
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()
|
||||
|
||||
# Create the share
|
||||
page.click('#newshare button[data-name="submit"]')
|
||||
|
||||
# Verify the share was created
|
||||
expect(
|
||||
page.locator("tr[data-name='sharetokenrowtemplate']:not(.hidden)")
|
||||
).to_have_count(1)
|
||||
|
||||
@@ -227,9 +227,18 @@
|
||||
<input type="radio" data-name="permissions" checked="false" id="newshare_attr_permissions_rw"
|
||||
name="newshare_permissions" /><label for="newshare_attr_permissions_rw">Read/Write</label>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<fieldset data-name="properties_override">
|
||||
<legend>Properties override</legend>
|
||||
<input type="text" data-name="properties" />
|
||||
<div class="property-override">
|
||||
<input type="checkbox" data-name="description_override_enabled" id="newshare_attr_description_enabled">
|
||||
<label for="newshare_attr_description_enabled">Description:</label>
|
||||
<input type="text" data-name="description_override" disabled>
|
||||
</div>
|
||||
<div class="property-override">
|
||||
<input type="checkbox" data-name="color_override_enabled" id="newshare_attr_color_enabled">
|
||||
<label for="newshare_attr_color_enabled">Color:</label>
|
||||
<input type="color" data-name="color_override" disabled>
|
||||
</div>
|
||||
</fieldset>
|
||||
<button type="submit" class="green" data-name="submit">Create</button>
|
||||
<button type="button" class="red" data-name="cancel">Cancel</button>
|
||||
|
||||
@@ -483,6 +483,36 @@ export function reload_sharing_list(user, password, collection, callback) {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Property keys for different collection types.
|
||||
* Map to OVERLAY_PROPERTIES_WHITELIST in radicale/sharing/__init__.py
|
||||
*/
|
||||
export const OVERLAY_PROPERTIES = {
|
||||
CALENDAR: {
|
||||
DESCRIPTION: "C:calendar-description",
|
||||
COLOR: "ICAL:calendar-color",
|
||||
},
|
||||
ADDRESSBOOK: {
|
||||
DESCRIPTION: "CR:addressbook-description",
|
||||
COLOR: "INF:addressbook-color",
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @returns {string | null} Internal property key or null if not supported
|
||||
*/
|
||||
export function get_property_key(type, property) {
|
||||
if (type === CollectionType.ADDRESSBOOK) {
|
||||
return OVERLAY_PROPERTIES.ADDRESSBOOK[property];
|
||||
} else if (CollectionType.is_subset(CollectionType.CALENDAR, type)) {
|
||||
return OVERLAY_PROPERTIES.CALENDAR[property];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* @param {string} user
|
||||
* @param {string} password
|
||||
@@ -490,7 +520,7 @@ export function reload_sharing_list(user, password, collection, callback) {
|
||||
* @param {string} permissions
|
||||
* @param {boolean} enabled
|
||||
* @param {boolean} hidden
|
||||
* @param {string} properties
|
||||
* @param {Object} properties
|
||||
* @param {function(?string):void} callback
|
||||
*/
|
||||
export function add_share_by_token(
|
||||
@@ -536,7 +566,7 @@ export function add_share_by_token(
|
||||
* @param {string} permissions
|
||||
* @param {boolean} enabled
|
||||
* @param {boolean} hidden
|
||||
* @param {string} properties
|
||||
* @param {Object} properties
|
||||
* @param {string} share_user
|
||||
* @param {string} href
|
||||
* @param {function(?string):void} callback
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { add_share_by_map, add_share_by_token } from "../api/api.js";
|
||||
import { add_share_by_map, add_share_by_token, get_property_key } from "../api/api.js";
|
||||
import { CollectionType } from "../models/collection.js";
|
||||
import { ErrorHandler } from "../utils/error.js";
|
||||
import { FormValidator, validate_href, validate_non_empty } from "../utils/form_validator.js";
|
||||
import { onCleanHREFinput, random_uuid } from "../utils/misc.js";
|
||||
@@ -32,11 +33,12 @@ export class NewShareScene {
|
||||
/**
|
||||
* @param {string} user
|
||||
* @param {string} password
|
||||
* @param {string} pathMapped
|
||||
* @param {import("../models/collection.js").Collection} collection
|
||||
* @param {string} shareType
|
||||
* @param {function():void} onclose
|
||||
*/
|
||||
constructor(user, password, pathMapped, shareType, onclose) {
|
||||
constructor(user, password, collection, shareType, onclose) {
|
||||
let pathMapped = collection.href;
|
||||
/** @type {HTMLElement} */ let html_scene = document.getElementById("newshare");
|
||||
/** @type {HTMLFormElement} */ let form = html_scene.querySelector("form");
|
||||
/** @type {HTMLElement} */ let sharemapfields = html_scene.querySelector("[data-name=sharemapfields]");
|
||||
@@ -46,7 +48,13 @@ export class NewShareScene {
|
||||
/** @type {HTMLInputElement} */ let hidden_checkbox = html_scene.querySelector("[data-name=hidden]");
|
||||
let permissions_ro_radio = /** @type {HTMLInputElement} */ (document.getElementById("newshare_attr_permissions_ro"));
|
||||
let permissions_rw_radio = /** @type {HTMLInputElement} */ (document.getElementById("newshare_attr_permissions_rw"));
|
||||
/** @type {HTMLInputElement} */ let properties_input = html_scene.querySelector("[data-name=properties]");
|
||||
|
||||
/** @type {HTMLElement} */ let properties_fieldset = html_scene.querySelector("[data-name=properties_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]");
|
||||
/** @type {HTMLInputElement} */ let color_override_input = html_scene.querySelector("[data-name=color_override]");
|
||||
|
||||
/** @type {HTMLElement} */ let error_form = html_scene.querySelector("[data-name=error]");
|
||||
/** @type {HTMLElement} */ let cancel_btn = html_scene.querySelector("[data-name=cancel]");
|
||||
|
||||
@@ -58,6 +66,13 @@ export class NewShareScene {
|
||||
|
||||
sharehref_input.addEventListener("input", onCleanHREFinput);
|
||||
|
||||
description_override_enabled.onchange = function () {
|
||||
description_override_input.disabled = !description_override_enabled.checked;
|
||||
};
|
||||
color_override_enabled.onchange = function () {
|
||||
color_override_input.disabled = !color_override_enabled.checked;
|
||||
};
|
||||
|
||||
/** @type {?number} */ let scene_index = null;
|
||||
|
||||
function oncancel() {
|
||||
@@ -82,7 +97,16 @@ export class NewShareScene {
|
||||
let enabled = enabled_checkbox.checked;
|
||||
let hidden = hidden_checkbox.checked;
|
||||
let permissions = permissions_rw_radio.checked ? "rw" : "r";
|
||||
let properties = properties_input.value;
|
||||
|
||||
let properties = {};
|
||||
if (description_override_enabled.checked) {
|
||||
let key = get_property_key(collection.type, "DESCRIPTION");
|
||||
if (key) properties[key] = description_override_input.value;
|
||||
}
|
||||
if (color_override_enabled.checked) {
|
||||
let key = get_property_key(collection.type, "COLOR");
|
||||
if (key) properties[key] = color_override_input.value + (color_override_input.value ? "ff" : "");
|
||||
}
|
||||
|
||||
let callback = function (/** @type {string} */ error) {
|
||||
if (scene_index === null) {
|
||||
@@ -122,7 +146,23 @@ export class NewShareScene {
|
||||
hidden_checkbox.checked = false;
|
||||
permissions_ro_radio.checked = true;
|
||||
permissions_rw_radio.checked = false;
|
||||
properties_input.value = "";
|
||||
|
||||
description_override_enabled.checked = false;
|
||||
description_override_input.value = collection.description || "";
|
||||
description_override_input.disabled = true;
|
||||
|
||||
color_override_enabled.checked = false;
|
||||
color_override_input.value = collection.color || "#ffffff";
|
||||
color_override_input.disabled = true;
|
||||
|
||||
let is_calendar = CollectionType.is_subset(CollectionType.CALENDAR, collection.type);
|
||||
let is_addressbook = collection.type === CollectionType.ADDRESSBOOK;
|
||||
if (is_calendar || is_addressbook) {
|
||||
properties_fieldset.classList.remove("hidden");
|
||||
} else {
|
||||
properties_fieldset.classList.add("hidden");
|
||||
}
|
||||
|
||||
if (shareType === "map") {
|
||||
sharehref_input.value = random_uuid();
|
||||
sharemapfields.classList.remove("hidden");
|
||||
|
||||
@@ -73,14 +73,14 @@ export class ShareCollectionScene {
|
||||
}
|
||||
|
||||
function onsharebytoken() {
|
||||
let new_share_scene = new NewShareScene(user, password, collection.href, "token", function () {
|
||||
let new_share_scene = new NewShareScene(user, password, collection, "token", function () {
|
||||
update_share_list(user, password, collection, errorHandler);
|
||||
});
|
||||
push_scene(new_share_scene, false);
|
||||
}
|
||||
|
||||
function onsharebymap() {
|
||||
let new_share_scene = new NewShareScene(user, password, collection.href, "map", function () {
|
||||
let new_share_scene = new NewShareScene(user, password, collection, "map", function () {
|
||||
update_share_list(user, password, collection, errorHandler);
|
||||
});
|
||||
push_scene(new_share_scene, false);
|
||||
|
||||
Reference in New Issue
Block a user