This commit is contained in:
Fenris Wolf 2024-10-10 23:00:29 +02:00
parent 252a4dc697
commit d3b61b620d
14 changed files with 376 additions and 43 deletions

View file

@ -2919,6 +2919,58 @@ declare namespace lib_plankton.session {
clear?: boolean;
}): Promise<void>;
}
declare namespace lib_plankton {
namespace order {
/**
*/
type type_order<type_value> = ((x: type_value, y: type_value) => boolean);
/**
*/
type type_comparator<type_value> = ((x: type_value, y: type_value) => int);
/**
*/
type type_sorter<type_element> = ((list: Array<type_element>) => Array<type_element>);
/**
*/
function from_comparator<type_value>(comparator: type_comparator<type_value>): type_order<type_value>;
/**
*/
function to_comparator<type_value>(order: type_order<type_value>): type_comparator<type_value>;
/**
*/
function order_default<type_value>(): type_order<type_value>;
/**
* @desc provide a total order given by a list
*/
function order_total<type_value>(list: Array<type_value>, options?: {
collation?: ((x: type_value, y: type_value) => boolean);
}): type_order<type_value>;
/**
* @desc lexicographic order
*/
function order_lexicographic_pair<type_value_first, type_value_second>(options?: {
order_first?: type_order<type_value_first>;
order_second?: type_order<type_value_second>;
}): type_order<lib_plankton.pair.type_pair<type_value_first, type_value_second>>;
/**
*/
function order_lexicographic_pair_wrapped<type_container, type_value_first, type_value_second>(extract_first: ((container: type_container) => type_value_first), extract_second: ((container: type_container) => type_value_second), options?: {
order_first?: type_order<type_value_first>;
order_second?: type_order<type_value_second>;
}): type_order<type_container>;
/**
* @desc lexicographic order
*/
function order_lexicographic_list<type_element>(options?: {
order?: type_order<type_element>;
}): type_order<Array<type_element>>;
/**
*/
function sorter_merge<type_element>(options?: {
order?: type_order<type_element>;
}): type_sorter<type_element>;
}
}
declare namespace lib_plankton.pit {
/**
*/

View file

@ -10098,6 +10098,198 @@ var lib_plankton;
})(session = lib_plankton.session || (lib_plankton.session = {}));
})(lib_plankton || (lib_plankton = {}));
/*
This file is part of »bacterio-plankton:order«.
Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR'
<info@greenscale.de>
»bacterio-plankton:order« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»bacterio-plankton:order« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »bacterio-plankton:order«. If not, see <http://www.gnu.org/licenses/>.
*/
var lib_plankton;
(function (lib_plankton) {
var order;
(function (order_1) {
/**
*/
function from_comparator(comparator) {
return (function (x, y) { return (comparator(x, y) <= 0); });
}
order_1.from_comparator = from_comparator;
/**
*/
function to_comparator(order) {
return (function (x, y) { return (order(x, y) ? (order(y, x) ? 0 : -1) : 1); });
}
order_1.to_comparator = to_comparator;
/**
*/
function order_default() {
return (function (value1, value2) { return (value1 <= value2); });
}
order_1.order_default = order_default;
/**
* @desc provide a total order given by a list
*/
function order_total(list, options) {
if (options === void 0) { options = {}; }
options = Object.assign({
"collation": (function (x, y) { return (x === y); })
}, options);
return (function (value1, value2) {
var index1 = list.findIndex(function (value) { return options.collation(value, value1); });
var index2 = list.findIndex(function (value) { return options.collation(value, value2); });
return (index1 <= index2);
});
}
order_1.order_total = order_total;
/**
* @desc lexicographic order
*/
function order_lexicographic_pair(options) {
if (options === void 0) { options = {}; }
options = Object.assign({
"order_first": order_default(),
"order_second": order_default()
}, options);
return (function (pair1, pair2) {
var le_first = options.order_first(pair1.first, pair2.first);
var ge_first = options.order_first(pair2.first, pair1.first);
if (le_first && !ge_first) {
return true;
}
else if (!le_first && ge_first) {
return false;
}
else {
var le_second = options.order_second(pair1.second, pair2.second);
var ge_second = options.order_second(pair2.second, pair1.second);
if (le_second && !ge_second) {
return true;
}
else if (!le_second && ge_second) {
return false;
}
else {
return true;
}
}
});
}
order_1.order_lexicographic_pair = order_lexicographic_pair;
/**
*/
function order_lexicographic_pair_wrapped(extract_first, extract_second, options) {
if (options === void 0) { options = {}; }
return (function (container1, container2) { return order_lexicographic_pair({
"order_first": options.order_first,
"order_second": options.order_second
})({
"first": extract_first(container1),
"second": extract_second(container1)
}, {
"first": extract_first(container2),
"second": extract_second(container2)
}); });
}
order_1.order_lexicographic_pair_wrapped = order_lexicographic_pair_wrapped;
/**
* @desc lexicographic order
*/
function order_lexicographic_list(options) {
if (options === void 0) { options = {}; }
options = Object.assign({
"order": order_default()
}, options);
return (function (list1, list2) {
if (list1.length <= 0) {
if (list2.length <= 0) {
return true;
}
else {
return false;
}
}
else {
if (list2.length <= 0) {
return false;
}
else {
var element1 = list1[0];
var element2 = list2[0];
var le = options.order(element1, element2);
var ge = options.order(element2, element1);
if (le && !ge) {
return true;
}
else if (!le && ge) {
return false;
}
else {
return order_lexicographic_list({ "order": options.order })(list1.slice(1), list2.slice(1));
}
}
}
});
}
order_1.order_lexicographic_list = order_lexicographic_list;
/**
*/
function merger(options) {
if (options === void 0) { options = {}; }
options = Object.assign({
"order": order_default()
}, options);
return (function (list1, list2) {
if (list1.length <= 0) {
return list2;
}
else if (list2.length <= 0) {
return list1;
}
else {
var element1 = list1[0];
var element2 = list2[0];
return (options.order(element1, element2)
? ([element1].concat(merger({ "order": options.order })(list1.slice(1), list2)))
: ([element2].concat(merger({ "order": options.order })(list1, list2.slice(1)))));
}
});
}
/**
*/
function sorter_merge(options) {
if (options === void 0) { options = {}; }
options = Object.assign({
"order": order_default()
}, options);
return (function (list) {
if (list.length <= 0) {
return [];
}
else if (list.length === 1) {
return [list[0]];
}
else {
var n = ((list.length + 1) >> 1);
return merger({ "order": options.order })(sorter_merge({ "order": options.order })(list.slice(0, n)), sorter_merge({ "order": options.order })(list.slice(n)));
}
});
}
order_1.sorter_merge = sorter_merge;
})(order = lib_plankton.order || (lib_plankton.order = {}));
})(lib_plankton || (lib_plankton = {}));
/*
This file is part of »bacterio-plankton:pit«.
Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR'

View file

@ -12,6 +12,7 @@ namespace _zeitbild.api
{
name : string;
access : {
public : boolean;
default_level : string;
attributed : Array<
{
@ -89,6 +90,7 @@ namespace _zeitbild.api
const calendar_object : _zeitbild.type_calendar_object = {
"name": stuff.input.name,
"access": {
"public": stuff.input.access.public,
"default_level": _zeitbild.value_object.access_level.from_string(stuff.input.access.default_level),
"attributed": lib_plankton.map.hashmap.implementation_map(
lib_plankton.map.hashmap.make(

View file

@ -12,6 +12,7 @@ namespace _zeitbild.api
{
name : string;
access : {
public : boolean;
default_level : ("none" | "view" | "edit" | "admin");
attributed : Array<
{
@ -54,6 +55,7 @@ namespace _zeitbild.api
const calendar_object_new : _zeitbild.type_calendar_object = {
"name": stuff.input.name,
"access": {
"public": stuff.input.access.public,
"default_level": _zeitbild.value_object.access_level.from_string(stuff.input.access.default_level),
"attributed": lib_plankton.map.hashmap.implementation_map(
lib_plankton.map.hashmap.make(

View file

@ -45,6 +45,7 @@ namespace _zeitbild.api
const result = {
"name": calendar_object.name,
"access": {
"public": calendar_object.access.public,
"default_level": _zeitbild.api.access_level_encode(calendar_object.access.default_level),
"attributed": lib_plankton.call.convey(
calendar_object.access.attributed,

View file

@ -54,10 +54,18 @@ namespace _zeitbild.api
],
}
}),
"restriction": restriction_logged_in,
"restriction": restriction_none,
"execution": async (stuff) => {
const session : {key : string; value : lib_plankton.session.type_session;} = await session_from_stuff(stuff);
const user_id : _zeitbild.type_user_id = await _zeitbild.service.user.identify(session.value.name);
const user_id : (null | _zeitbild.type_user_id) = await (
session_from_stuff(stuff)
.then(
(session : {key : string; value : lib_plankton.session.type_session;}) => (
_zeitbild.service.user.identify(session.value.name)
.catch(x => Promise.resolve(null))
)
)
.catch(x => Promise.resolve(null))
);
return (
_zeitbild.service.calendar.overview(user_id)

View file

@ -123,10 +123,18 @@ namespace _zeitbild.api
],
}
}),
"restriction": restriction_logged_in,
"restriction": restriction_none,
"execution": async (stuff) => {
const session : {key : string; value : lib_plankton.session.type_session;} = await session_from_stuff(stuff);
const user_id : _zeitbild.type_user_id = await _zeitbild.service.user.identify(session.value.name);
const user_id : (null | _zeitbild.type_user_id) = await (
session_from_stuff(stuff)
.then(
(session : {key : string; value : lib_plankton.session.type_session;}) => (
_zeitbild.service.user.identify(session.value.name)
.catch(x => Promise.resolve(null))
)
)
.catch(x => Promise.resolve(null))
);
const from : lib_plankton.pit.type_pit = parseInt(stuff.query_parameters["from"]);
const to : lib_plankton.pit.type_pit = parseInt(stuff.query_parameters["to"]);

View file

@ -5,7 +5,7 @@ namespace _zeitbild.database
/**
*/
const _compatible_revisions : Array<string> = [
"r1",
"r2",
];

View file

@ -16,6 +16,7 @@ type type_data = {
id : int;
name : string;
access : {
public ?: boolean;
default_level : ("none" | "view" | "edit" | "admin");
attributed : Array<
{
@ -119,6 +120,7 @@ async function data_init(
const calendar_object : _zeitbild.type_calendar_object = {
"name": calendar_raw.name,
"access": {
"public": (calendar_raw.access.public ?? false),
"default_level": _zeitbild.value_object.access_level.from_string(calendar_raw.access.default_level),
"attributed": lib_plankton.map.hashmap.implementation_map(
lib_plankton.map.hashmap.make(

View file

@ -145,6 +145,7 @@ namespace _zeitbild.repository.calendar
return {
"core_row": {
"name": object.name,
"access_public": object.access.public,
"access_level_default": encode_access_level(object.access.default_level),
"resource_id": object.resource_id,
},
@ -171,6 +172,7 @@ namespace _zeitbild.repository.calendar
return {
"name": dispersal.core_row["name"],
"access": {
"public": dispersal.core_row["access_public"],
"default_level": decode_access_level(dispersal.core_row["access_level_default"]),
"attributed": lib_plankton.map.hashmap.implementation_map(
lib_plankton.map.hashmap.make<_zeitbild.type_user_id, _zeitbild.enum_access_level>(
@ -351,15 +353,21 @@ namespace _zeitbild.repository.calendar
/**
*/
type type_overview_entry = {
id : _zeitbild.type_calendar_id;
name : string;
access_level : _zeitbild.enum_access_level;
}
/**
* @todo caching
*/
export async function overview(
user_id : _zeitbild.type_user_id
user_id : (null | _zeitbild.type_user_id)
) : Promise<
Array<
{
id : _zeitbild.type_calendar_id;
name : string;
access_level : _zeitbild.enum_access_level;
}
type_overview_entry
>
>
{
@ -377,13 +385,52 @@ namespace _zeitbild.repository.calendar
)
.then(
(rows) => Promise.resolve(
rows
.map(
(row) => ({
"id": row["id"],
"name": row["name"],
"access_level": decode_access_level(row["access_level"]),
})
lib_plankton.call.convey(
rows,
[
(x : Array<Record<string, any>>) => x.map(
(row : Record<string, any>) => ({
"id": row["id"],
"name": row["name"],
/**
* @todo unite with _zeitbild.service.calendar.get_access_level
*/
"access_level": decode_access_level(
Math.max(
(row["access_public"] ? 1 : 0),
(
(user_id === null)
?
0
:
(row["access_level_attributed"] ?? row["access_level_default"])
)
)
),
})
),
(x : Array<type_overview_entry>) => x.filter(
(row) => (
! _zeitbild.value_object.access_level.order(
row.access_level,
_zeitbild.enum_access_level.none
)
)
),
(x : Array<type_overview_entry>) => lib_plankton.list.sorted<type_overview_entry>(
x,
{
"compare_element": lib_plankton.order.order_lexicographic_pair_wrapped<type_overview_entry, _zeitbild.enum_access_level, int>(
row => row.access_level,
row => row.id,
{
"order_first": _zeitbild.value_object.access_level.order,
"order_second": (a, b) => (a <= b)
}
),
}
),
]
)
)
)

View file

@ -1,20 +1,12 @@
SELECT
x.id AS id,
x.name AS name,
(
CASE
WHEN MAX(y.level) IS NULL THEN x.access_level_default
ELSE MAX(y.level)
END
) AS access_level
x.access_public AS access_public,
x.access_level_default AS access_level_default,
y.level AS access_level_attributed
FROM
calendars AS x
LEFT OUTER JOIN calendar_access_attributed AS y ON ((x.id = y.calendar_id) AND (y.user_id = $user_id))
GROUP BY
x.id
HAVING
(access_level > 0)
ORDER BY
access_level DESC,
id
;

View file

@ -3,18 +3,42 @@ namespace _zeitbild.service.calendar
{
/**
* checks if a user has a sufficient access level
*/
function get_access_level(
calendar_object : _zeitbild.type_calendar_object,
user_id : _zeitbild.type_user_id
user_id : (null | _zeitbild.type_user_id)
) : _zeitbild.enum_access_level
{
return calendar_object.access.attributed.get(
user_id,
lib_plankton.pod.make_filled<_zeitbild.enum_access_level>(
calendar_object.access.default_level
)
return (
lib_plankton.list.max<_zeitbild.enum_access_level, _zeitbild.enum_access_level>(
[
(
calendar_object.access.public
?
_zeitbild.enum_access_level.view
:
_zeitbild.enum_access_level.none
),
(
(user_id === null)
?
_zeitbild.enum_access_level.none
:
calendar_object.access.attributed.get(
user_id,
lib_plankton.pod.make_filled<_zeitbild.enum_access_level>(
calendar_object.access.default_level
)
)
),
],
x => x,
{
"compare_value": _zeitbild.value_object.access_level.order,
}
)?.value
??
_zeitbild.enum_access_level.none
);
}
@ -24,7 +48,7 @@ namespace _zeitbild.service.calendar
*/
function wrap_check_access_level<type_result>(
calendar_object : _zeitbild.type_calendar_object,
user_id : _zeitbild.type_user_id,
user_id : (null | _zeitbild.type_user_id),
threshold : _zeitbild.enum_access_level,
success_handler : (
(access_level : _zeitbild.enum_access_level)
@ -59,7 +83,7 @@ namespace _zeitbild.service.calendar
/**
*/
export function overview(
user_id : _zeitbild.type_user_id
user_id : (null | _zeitbild.type_user_id)
) : Promise<
Array<
{
@ -243,12 +267,13 @@ namespace _zeitbild.service.calendar
/**
* @todo optimize by reducing the number of database queries
*/
async function get_events(
calendar_id : _zeitbild.type_calendar_id,
from_pit : lib_plankton.pit.type_pit,
to_pit : lib_plankton.pit.type_pit,
user_id : _zeitbild.type_user_id
user_id : (null | _zeitbild.type_user_id)
) : Promise<
Array<
{
@ -401,13 +426,12 @@ namespace _zeitbild.service.calendar
/**
* @todo check access level
*/
export async function gather_events(
calendar_ids_wanted : (null | Array<_zeitbild.type_calendar_id>),
from_pit : lib_plankton.pit.type_pit,
to_pit : lib_plankton.pit.type_pit,
user_id : _zeitbild.type_user_id
user_id : (null | _zeitbild.type_user_id)
) : Promise<
Array<
{

View file

@ -96,6 +96,7 @@ namespace _zeitbild
export type type_calendar_object = {
name : string;
access : {
public : boolean;
default_level : enum_access_level;
attributed : lib_plankton.map.type_map<
type_user_id,

View file

@ -15,6 +15,8 @@ modules="${modules} session"
modules="${modules} file"
modules="${modules} string"
modules="${modules} json"
modules="${modules} list"
modules="${modules} order"
modules="${modules} ical"
modules="${modules} url"
modules="${modules} http"