Add support for Bday sharing
This commit is contained in:
@@ -60,6 +60,8 @@ collection_by_token = true
|
|||||||
permit_create_token = true
|
permit_create_token = true
|
||||||
permit_create_map = true
|
permit_create_map = true
|
||||||
permit_properties_overlay = true
|
permit_properties_overlay = true
|
||||||
|
collection_by_bday = true
|
||||||
|
permit_create_bday = true
|
||||||
|
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -449,3 +449,107 @@ def test_no_incoming_shares_message(page: Page, radicale_server: str) -> None:
|
|||||||
|
|
||||||
page.click('#incomingsharingscene button[data-name="cancel"]')
|
page.click('#incomingsharingscene button[data-name="cancel"]')
|
||||||
expect(page.locator("#incomingsharingscene")).to_be_hidden()
|
expect(page.locator("#incomingsharingscene")).to_be_hidden()
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_and_delete_share_by_bday(page: Page, radicale_server: str) -> None:
|
||||||
|
login(page, radicale_server)
|
||||||
|
# create collection of type ADDRESSBOOK for bday (bday only works with ADDRESSBOOK)
|
||||||
|
page.click('a[data-name="new"]')
|
||||||
|
page.locator('#createcollectionscene select[data-name="type"]').select_option(
|
||||||
|
"ADDRESSBOOK"
|
||||||
|
)
|
||||||
|
page.locator('#createcollectionscene input[data-name="displayname"]').fill(
|
||||||
|
"Addressbook For Bday"
|
||||||
|
)
|
||||||
|
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)
|
||||||
|
|
||||||
|
expect(
|
||||||
|
page.locator("tr[data-name='sharebdayrowtemplate']:not(.hidden)")
|
||||||
|
).to_have_count(0)
|
||||||
|
|
||||||
|
page.click('button[data-name="sharebybday"]')
|
||||||
|
|
||||||
|
# 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")
|
||||||
|
|
||||||
|
# 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()
|
||||||
|
|
||||||
|
page.click('#newshare button[data-name="submit"]')
|
||||||
|
expect(
|
||||||
|
page.locator("tr[data-name='sharebdayrowtemplate']: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']"
|
||||||
|
)
|
||||||
|
).to_have_count(0)
|
||||||
|
|
||||||
|
# Close the share scene and verify the virtual bday calendar is now in the collections list
|
||||||
|
page.click('#sharecollectionscene button[data-name="cancel"]')
|
||||||
|
expect(page.locator("#sharecollectionscene")).to_be_hidden()
|
||||||
|
|
||||||
|
# The virtual calendar (bdaymapped) should appear as its own article
|
||||||
|
# after the cache was invalidated following the self-share
|
||||||
|
expect(page.locator("article:not(.hidden)")).to_have_count(2)
|
||||||
|
|
||||||
|
# Delete the bday share by re-opening the share scene
|
||||||
|
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']",
|
||||||
|
strict=True,
|
||||||
|
)
|
||||||
|
page.click('#deleteconfirmationscene button[data-name="delete"]')
|
||||||
|
expect(
|
||||||
|
page.locator("tr[data-name='sharebdayrowtemplate']:not(.hidden)")
|
||||||
|
).to_have_count(0)
|
||||||
|
|
||||||
|
|
||||||
|
def test_bday_section_hidden_for_calendar(page: Page, radicale_server: str) -> None:
|
||||||
|
"""Verify the bday calendar section is hidden for CALENDAR collections."""
|
||||||
|
login(page, radicale_server)
|
||||||
|
|
||||||
|
page.click('a[data-name="new"]')
|
||||||
|
page.locator('#createcollectionscene select[data-name="type"]').select_option(
|
||||||
|
"CALENDAR"
|
||||||
|
)
|
||||||
|
page.locator('#createcollectionscene input[data-name="displayname"]').fill(
|
||||||
|
"My Calendar"
|
||||||
|
)
|
||||||
|
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)
|
||||||
|
|
||||||
|
expect(page.locator("#sharecollectionscene")).to_be_visible()
|
||||||
|
expect(page.locator("div[data-name='sharebybday']")).to_be_hidden()
|
||||||
|
page.click('#sharecollectionscene button[data-name="cancel"]')
|
||||||
|
|
||||||
|
|
||||||
|
def test_bday_section_visible_for_addressbook(page: Page, radicale_server: str) -> None:
|
||||||
|
"""Verify the bday calendar section is visible for ADDRESSBOOK collections."""
|
||||||
|
login(page, radicale_server)
|
||||||
|
|
||||||
|
page.click('a[data-name="new"]')
|
||||||
|
page.locator('#createcollectionscene select[data-name="type"]').select_option(
|
||||||
|
"ADDRESSBOOK"
|
||||||
|
)
|
||||||
|
page.locator('#createcollectionscene input[data-name="displayname"]').fill(
|
||||||
|
"My Addressbook"
|
||||||
|
)
|
||||||
|
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)
|
||||||
|
|
||||||
|
expect(page.locator("#sharecollectionscene")).to_be_visible()
|
||||||
|
expect(page.locator("div[data-name='sharebybday']")).to_be_visible()
|
||||||
|
page.click('#sharecollectionscene button[data-name="cancel"]')
|
||||||
|
|||||||
@@ -70,6 +70,7 @@
|
|||||||
<h3 class="title" data-name="title">Title</h3>
|
<h3 class="title" data-name="title">Title</h3>
|
||||||
<small>
|
<small>
|
||||||
<span data-name="shared-by" class="hidden">👥 shared by <span data-name="shared-by-owner"></span> ></span>
|
<span data-name="shared-by" class="hidden">👥 shared by <span data-name="shared-by-owner"></span> ></span>
|
||||||
|
<span data-name="transformed-from" class="hidden">Address book ⟿</span>
|
||||||
<span data-name="ADDRESSBOOK">Address book</span>
|
<span data-name="ADDRESSBOOK">Address book</span>
|
||||||
<span data-name="CALENDAR_JOURNAL_TASKS">Calendar, journal and tasks</span>
|
<span data-name="CALENDAR_JOURNAL_TASKS">Calendar, journal and tasks</span>
|
||||||
<span data-name="CALENDAR_JOURNAL">Calendar and journal</span>
|
<span data-name="CALENDAR_JOURNAL">Calendar and journal</span>
|
||||||
@@ -200,6 +201,33 @@
|
|||||||
</table>
|
</table>
|
||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
|
<div data-name="sharebybday">
|
||||||
|
<details open>
|
||||||
|
<summary>shared as bday calendar</summary>
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr data-name="sharebdayrowtemplate" class="hidden">
|
||||||
|
<td>
|
||||||
|
<button type="button" class="blue inline" data-name="edit"><img src="css/icons/edit.svg"
|
||||||
|
class="small_icon" alt="Edit"></button>
|
||||||
|
</td>
|
||||||
|
<td><input type="text" data-name="pathortoken" value="" readonly="" class="inline selectall"></td>
|
||||||
|
<td>
|
||||||
|
<button type="button" class="red inline" data-name="delete"><img src="css/icons/delete.svg"
|
||||||
|
class="small_icon" alt="Delete"></button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<button type="button" class="blue inline" data-name="sharebybday">
|
||||||
|
<img src="css/icons/new.svg" class="small_icon" alt="New Share as Bday Calendar">
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
<form>
|
<form>
|
||||||
<button type="button" class="green" data-name="cancel">Close</button>
|
<button type="button" class="green" data-name="cancel">Close</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ import { CollectionType } from "../models/collection.js";
|
|||||||
* @property {boolean} [PermittedCreateCollectionByMap]
|
* @property {boolean} [PermittedCreateCollectionByMap]
|
||||||
* @property {boolean} [FeatureEnabledCollectionByToken]
|
* @property {boolean} [FeatureEnabledCollectionByToken]
|
||||||
* @property {boolean} [PermittedCreateCollectionByToken]
|
* @property {boolean} [PermittedCreateCollectionByToken]
|
||||||
|
* @property {boolean} [FeatureEnabledCollectionByBday]
|
||||||
|
* @property {boolean} [PermittedCreateCollectionByBday]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -433,7 +435,7 @@ export function update_incoming_share(
|
|||||||
call_sharing_api(
|
call_sharing_api(
|
||||||
user,
|
user,
|
||||||
password,
|
password,
|
||||||
"map/update",
|
share.ShareType + "/update",
|
||||||
{
|
{
|
||||||
PathOrToken: share.PathOrToken,
|
PathOrToken: share.PathOrToken,
|
||||||
Enabled: share.EnabledByUser,
|
Enabled: share.EnabledByUser,
|
||||||
@@ -453,3 +455,115 @@ 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);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -170,10 +170,17 @@ export class CollectionsScene {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
let share_info = node.querySelector("[data-name=shared-by]");
|
let share_info = node.querySelector("[data-name=shared-by]");
|
||||||
let share = (shares || []).find(s => s.ShareType === "map" && (s.PathOrToken || "").replace(/\/+$/, "") === (collection.href || "").replace(/\/+$/, ""));
|
let transformed_from = node.querySelector("[data-name=transformed-from]");
|
||||||
if (share && share.Owner !== user) {
|
let share = (shares || []).find(
|
||||||
share_info.classList.remove("hidden");
|
s => (s.ShareType === "map" || s.ShareType === "bday") &&
|
||||||
node.querySelector("[data-name=shared-by-owner]").textContent = share.Owner;
|
(s.PathOrToken || "").replace(/\/+$/, "") === (collection.href || "").replace(/\/+$/, ""));
|
||||||
|
if (share) {
|
||||||
|
if (share.Owner !== user) {
|
||||||
|
share_info.classList.remove("hidden");
|
||||||
|
node.querySelector("[data-name=shared-by-owner]").textContent = share.Owner;
|
||||||
|
} else {
|
||||||
|
transformed_from.classList.remove("hidden");
|
||||||
|
}
|
||||||
let share_option = node.querySelector("[data-name=shareoption]");
|
let share_option = node.querySelector("[data-name=shareoption]");
|
||||||
if (share_option) {
|
if (share_option) {
|
||||||
share_option.classList.add("hidden");
|
share_option.classList.add("hidden");
|
||||||
|
|||||||
@@ -19,10 +19,11 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
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 { 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 { CollectionType } from "../models/collection.js";
|
import { CollectionType } from "../models/collection.js";
|
||||||
|
import { collectionsCache } from "../utils/collections_cache.js";
|
||||||
import { ErrorHandler } from "../utils/error.js";
|
import { ErrorHandler } from "../utils/error.js";
|
||||||
import { FormValidator, validate_href, validate_not_empty_or_equals } from "../utils/form_validator.js";
|
import { FormValidator, validate_href, validate_non_empty, validate_not_empty_or_equals } from "../utils/form_validator.js";
|
||||||
import { onCleanHREFinput, random_uuid } from "../utils/misc.js";
|
import { onCleanHREFinput, random_uuid } from "../utils/misc.js";
|
||||||
import { Scene, is_current_scene, pop_scene } from "./scene_manager.js";
|
import { Scene, is_current_scene, pop_scene } from "./scene_manager.js";
|
||||||
|
|
||||||
@@ -66,7 +67,12 @@ export class CreateEditShareScene {
|
|||||||
let errorHandler = new ErrorHandler(error_form);
|
let errorHandler = new ErrorHandler(error_form);
|
||||||
let map_validator = new FormValidator(errorHandler);
|
let map_validator = new FormValidator(errorHandler);
|
||||||
|
|
||||||
map_validator.addValidator(shareuser_input, validate_not_empty_or_equals(shareuser_input, user, "Share User"));
|
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(sharehref_input, validate_href(sharehref_input, "Share Href"));
|
map_validator.addValidator(sharehref_input, validate_href(sharehref_input, "Share Href"));
|
||||||
|
|
||||||
sharehref_input.addEventListener("input", onCleanHREFinput);
|
sharehref_input.addEventListener("input", onCleanHREFinput);
|
||||||
@@ -93,13 +99,13 @@ export class CreateEditShareScene {
|
|||||||
|
|
||||||
function onsubmit() {
|
function onsubmit() {
|
||||||
try {
|
try {
|
||||||
if (shareType === "map") {
|
if (shareType === "map" || shareType === "bday") {
|
||||||
if (!map_validator.validate()) {
|
if (!map_validator.validate()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let enabled_by_owner = enabled_checkbox.checked;
|
let enabled_by_owner = (shareType === "bday" && shareuser_input.value === user) ? true : enabled_checkbox.checked;
|
||||||
let hidden_by_owner = hidden_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 permissions = permissions_rw_radio.checked ? "rw" : "r";
|
||||||
|
|
||||||
let properties = {};
|
let properties = {};
|
||||||
@@ -123,6 +129,11 @@ export class CreateEditShareScene {
|
|||||||
if (error) {
|
if (error) {
|
||||||
errorHandler.setError(error);
|
errorHandler.setError(error);
|
||||||
} else {
|
} else {
|
||||||
|
// For bday shares to the current user, invalidate the
|
||||||
|
// collections cache so the virtual calendar appears immediately.
|
||||||
|
if (shareType === "bday" && new_share.User === user) {
|
||||||
|
collectionsCache.invalidate();
|
||||||
|
}
|
||||||
pop_scene();
|
pop_scene();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -137,18 +148,22 @@ export class CreateEditShareScene {
|
|||||||
HiddenByUser: edit ? share.HiddenByUser : null,
|
HiddenByUser: edit ? share.HiddenByUser : null,
|
||||||
Properties: properties,
|
Properties: properties,
|
||||||
User: edit ? share.User : shareuser_input.value,
|
User: edit ? share.User : shareuser_input.value,
|
||||||
PathOrToken: edit ? share.PathOrToken : (shareType === "map" ? "/" + shareuser_input.value + "/" + sharehref_input.value + "/" : ""),
|
PathOrToken: edit ? share.PathOrToken : ((shareType === "map" || shareType === "bday") ? "/" + shareuser_input.value + "/" + sharehref_input.value + "/" : ""),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (edit) {
|
if (edit) {
|
||||||
if (shareType === "map") {
|
if (shareType === "map") {
|
||||||
update_share_by_map(user, password, new_share, callback);
|
update_share_by_map(user, password, new_share, callback);
|
||||||
|
} else if (shareType === "bday") {
|
||||||
|
update_share_by_bday(user, password, new_share, callback);
|
||||||
} else {
|
} else {
|
||||||
update_share_by_token(user, password, new_share, callback);
|
update_share_by_token(user, password, new_share, callback);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (shareType === "map") {
|
if (shareType === "map") {
|
||||||
add_share_by_map(user, password, new_share, callback);
|
add_share_by_map(user, password, new_share, callback);
|
||||||
|
} else if (shareType === "bday") {
|
||||||
|
add_share_by_bday(user, password, new_share, callback);
|
||||||
} else {
|
} else {
|
||||||
add_share_by_token(user, password, new_share, callback);
|
add_share_by_token(user, password, new_share, callback);
|
||||||
}
|
}
|
||||||
@@ -175,12 +190,25 @@ export class CreateEditShareScene {
|
|||||||
html_scene.querySelector("h1").textContent = edit ? "Edit Share" : "New Share";
|
html_scene.querySelector("h1").textContent = edit ? "Edit Share" : "New Share";
|
||||||
submit_btn.textContent = edit ? "Save" : "Create";
|
submit_btn.textContent = edit ? "Save" : "Create";
|
||||||
|
|
||||||
shareuser_input.value = edit ? share.User : "";
|
shareuser_input.value = edit ? share.User : (shareType === "bday" ? user : "");
|
||||||
shareuser_input.disabled = edit;
|
shareuser_input.disabled = edit;
|
||||||
enabled_checkbox.checked = edit ? share.EnabledByOwner : true;
|
enabled_checkbox.checked = edit ? share.EnabledByOwner : true;
|
||||||
hidden_checkbox.checked = edit ? share.HiddenByOwner : false;
|
hidden_checkbox.checked = edit ? share.HiddenByOwner : false;
|
||||||
permissions_ro_radio.checked = edit ? share.Permissions.toLowerCase() === "r" : true;
|
|
||||||
permissions_rw_radio.checked = edit ? share.Permissions.toLowerCase() === "rw" : 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.Permissions.toLowerCase() === "r" : true;
|
||||||
|
permissions_rw_radio.checked = edit ? share.Permissions.toLowerCase() === "rw" : false;
|
||||||
|
}
|
||||||
|
|
||||||
let displayname = collection.displayname || "";
|
let displayname = collection.displayname || "";
|
||||||
let description = collection.description || "";
|
let description = collection.description || "";
|
||||||
@@ -236,7 +264,7 @@ export class CreateEditShareScene {
|
|||||||
color_override_enabled.parentElement.classList.add("hidden");
|
color_override_enabled.parentElement.classList.add("hidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shareType === "map") {
|
if (shareType === "map" || shareType === "bday") {
|
||||||
if (edit) {
|
if (edit) {
|
||||||
sharehref_input.value = share.PathOrToken.split("/").filter(Boolean).pop() || "";
|
sharehref_input.value = share.PathOrToken.split("/").filter(Boolean).pop() || "";
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -95,7 +95,9 @@ export class IncomingSharingScene {
|
|||||||
nodes = [];
|
nodes = [];
|
||||||
|
|
||||||
let prefix = "/" + user + "/";
|
let prefix = "/" + user + "/";
|
||||||
let filtered_shares = shares.filter(share => share.ShareType === "map" && share.PathOrToken.startsWith(prefix));
|
let filtered_shares = shares.filter(
|
||||||
|
share => (share.ShareType === "map" || share.ShareType === "bday")
|
||||||
|
&& share.PathOrToken.startsWith(prefix));
|
||||||
|
|
||||||
if (filtered_shares.length === 0) {
|
if (filtered_shares.length === 0) {
|
||||||
table.classList.add("hidden");
|
table.classList.add("hidden");
|
||||||
|
|||||||
@@ -20,11 +20,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
delete_share_by_bday,
|
||||||
delete_share_by_map,
|
delete_share_by_map,
|
||||||
delete_share_by_token,
|
delete_share_by_token,
|
||||||
reload_sharing_list,
|
reload_sharing_list,
|
||||||
} from "../api/sharing.js";
|
} from "../api/sharing.js";
|
||||||
import { Collection } from "../models/collection.js";
|
import { Collection, CollectionType } from "../models/collection.js";
|
||||||
import { collectionsCache } from "../utils/collections_cache.js";
|
import { collectionsCache } from "../utils/collections_cache.js";
|
||||||
import { ErrorHandler } from "../utils/error.js";
|
import { ErrorHandler } from "../utils/error.js";
|
||||||
import { displayPermissions } from "../utils/permissions.js";
|
import { displayPermissions } from "../utils/permissions.js";
|
||||||
@@ -51,6 +52,9 @@ export class ShareCollectionScene {
|
|||||||
);
|
);
|
||||||
/** @type {HTMLElement} */ let share_by_map_btn = html_scene.querySelector(
|
/** @type {HTMLElement} */ let share_by_map_btn = html_scene.querySelector(
|
||||||
"button[data-name=sharebymap]"
|
"button[data-name=sharebymap]"
|
||||||
|
);
|
||||||
|
/** @type {HTMLElement} */ let share_by_bday_btn = html_scene.querySelector(
|
||||||
|
"button[data-name=sharebybday]"
|
||||||
);
|
);
|
||||||
/** @type {HTMLElement} */ let share_by_token_div = html_scene.querySelector(
|
/** @type {HTMLElement} */ let share_by_token_div = html_scene.querySelector(
|
||||||
"div[data-name=sharebytoken]"
|
"div[data-name=sharebytoken]"
|
||||||
@@ -58,6 +62,9 @@ export class ShareCollectionScene {
|
|||||||
/** @type {HTMLElement} */ let share_by_map_div = html_scene.querySelector(
|
/** @type {HTMLElement} */ let share_by_map_div = html_scene.querySelector(
|
||||||
"div[data-name=sharebymap]"
|
"div[data-name=sharebymap]"
|
||||||
);
|
);
|
||||||
|
/** @type {HTMLElement} */ let share_by_bday_div = html_scene.querySelector(
|
||||||
|
"div[data-name=sharebybday]"
|
||||||
|
);
|
||||||
/** @type {HTMLElement} */ let error_form = html_scene.querySelector("[data-name=error]");
|
/** @type {HTMLElement} */ let error_form = html_scene.querySelector("[data-name=error]");
|
||||||
|
|
||||||
let errorHandler = new ErrorHandler(error_form);
|
let errorHandler = new ErrorHandler(error_form);
|
||||||
@@ -83,11 +90,16 @@ export class ShareCollectionScene {
|
|||||||
push_scene(create_edit_share_scene);
|
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.show = function () {
|
||||||
this.release();
|
this.release();
|
||||||
html_scene.classList.remove("hidden");
|
html_scene.classList.remove("hidden");
|
||||||
html_scene.querySelectorAll("details").forEach(function (details) {
|
html_scene.querySelectorAll("details").forEach(function (details) {
|
||||||
details.open = true;
|
details.open = true;
|
||||||
});
|
});
|
||||||
cancel_btn.onclick = oncancel;
|
cancel_btn.onclick = oncancel;
|
||||||
|
|
||||||
@@ -121,6 +133,26 @@ export class ShareCollectionScene {
|
|||||||
} else {
|
} else {
|
||||||
if (share_by_map_div) share_by_map_div.classList.add("hidden");
|
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;
|
title.textContent = collection.displayname || collection.href;
|
||||||
@@ -143,7 +175,7 @@ export class ShareCollectionScene {
|
|||||||
*/
|
*/
|
||||||
function update_share_list(user, password, collection, errorHandler) {
|
function update_share_list(user, password, collection, errorHandler) {
|
||||||
let share_rows = document.querySelectorAll(
|
let share_rows = document.querySelectorAll(
|
||||||
"[data-name=sharetokenrowtemplate], [data-name=sharemaprowtemplate]",
|
"[data-name=sharetokenrowtemplate], [data-name=sharemaprowtemplate], [data-name=sharebdayrowtemplate]",
|
||||||
);
|
);
|
||||||
share_rows.forEach(function (row) {
|
share_rows.forEach(function (row) {
|
||||||
if (!row.classList.contains("hidden")) {
|
if (!row.classList.contains("hidden")) {
|
||||||
@@ -170,8 +202,9 @@ function update_share_list(user, password, collection, errorHandler) {
|
|||||||
* @param {string} delete_label
|
* @param {string} delete_label
|
||||||
* @param {function(string, string, import('../api/sharing.js').Share, function(?string):void):void} delete_action
|
* @param {function(string, string, import('../api/sharing.js').Share, function(?string):void):void} delete_action
|
||||||
* @param {ErrorHandler} errorHandler
|
* @param {ErrorHandler} errorHandler
|
||||||
|
* @param {function():void} [onDeleteSuccess] Optional extra callback after a successful delete.
|
||||||
*/
|
*/
|
||||||
function add_share_row_node(user, password, collection, share, template, delete_label, delete_action, errorHandler) {
|
function add_share_row_node(user, password, collection, share, template, delete_label, delete_action, errorHandler, onDeleteSuccess) {
|
||||||
let pathortoken = share["PathOrToken"] || "";
|
let pathortoken = share["PathOrToken"] || "";
|
||||||
let node = /** @type {HTMLElement} */ (template.cloneNode(true));
|
let node = /** @type {HTMLElement} */ (template.cloneNode(true));
|
||||||
node.classList.remove("hidden");
|
node.classList.remove("hidden");
|
||||||
@@ -195,6 +228,7 @@ function add_share_row_node(user, password, collection, share, template, delete_
|
|||||||
let delete_collection_scene = new DeleteConfirmationScene(
|
let delete_collection_scene = new DeleteConfirmationScene(
|
||||||
user, password, "Delete Share", share, delete_label + " " + pathortoken, delete_action, false,
|
user, password, "Delete Share", share, delete_label + " " + pathortoken, delete_action, false,
|
||||||
function () {
|
function () {
|
||||||
|
if (onDeleteSuccess) onDeleteSuccess();
|
||||||
pop_scene();
|
pop_scene();
|
||||||
update_share_list(user, password, collection, errorHandler);
|
update_share_list(user, password, collection, errorHandler);
|
||||||
}
|
}
|
||||||
@@ -215,6 +249,7 @@ function add_share_row_node(user, password, collection, share, template, delete_
|
|||||||
function add_share_rows(user, password, collection, shares, errorHandler) {
|
function add_share_rows(user, password, collection, shares, errorHandler) {
|
||||||
/** @type {HTMLElement} */ let token_template = document.querySelector("[data-name=sharetokenrowtemplate]");
|
/** @type {HTMLElement} */ let token_template = document.querySelector("[data-name=sharetokenrowtemplate]");
|
||||||
/** @type {HTMLElement} */ let map_template = document.querySelector("[data-name=sharemaprowtemplate]");
|
/** @type {HTMLElement} */ let map_template = document.querySelector("[data-name=sharemaprowtemplate]");
|
||||||
|
/** @type {HTMLElement} */ let bday_template = document.querySelector("[data-name=sharebdayrowtemplate]");
|
||||||
shares.forEach(function (share) {
|
shares.forEach(function (share) {
|
||||||
let pathortoken = share["PathOrToken"] || "";
|
let pathortoken = share["PathOrToken"] || "";
|
||||||
let pathmapped = share["PathMapped"] || "";
|
let pathmapped = share["PathMapped"] || "";
|
||||||
@@ -226,6 +261,9 @@ function add_share_rows(user, password, collection, shares, errorHandler) {
|
|||||||
add_share_row_node(user, password, collection, share, token_template, "share", delete_share_by_token, errorHandler);
|
add_share_row_node(user, password, collection, share, token_template, "share", delete_share_by_token, errorHandler);
|
||||||
} else if (share["ShareType"] === "map") {
|
} else if (share["ShareType"] === "map") {
|
||||||
add_share_row_node(user, password, collection, share, map_template, "map", delete_share_by_map, errorHandler);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -238,7 +276,8 @@ export function maybe_enable_sharing_options(features) {
|
|||||||
if (!features || !features.sharing) return;
|
if (!features || !features.sharing) return;
|
||||||
let map_is_enabled = features.sharing.FeatureEnabledCollectionByMap || false;
|
let map_is_enabled = features.sharing.FeatureEnabledCollectionByMap || false;
|
||||||
let token_is_enabled = features.sharing.FeatureEnabledCollectionByToken || false;
|
let token_is_enabled = features.sharing.FeatureEnabledCollectionByToken || false;
|
||||||
if (map_is_enabled || token_is_enabled) {
|
let bday_is_enabled = features.sharing.FeatureEnabledCollectionByBday || false;
|
||||||
|
if (map_is_enabled || token_is_enabled || bday_is_enabled) {
|
||||||
let share_options = document.querySelectorAll("[data-name=shareoption]");
|
let share_options = document.querySelectorAll("[data-name=shareoption]");
|
||||||
for (let i = 0; i < share_options.length; i++) {
|
for (let i = 0; i < share_options.length; i++) {
|
||||||
let share_option = share_options[i];
|
let share_option = share_options[i];
|
||||||
|
|||||||
Reference in New Issue
Block a user