[ini]
This commit is contained in:
commit
e73424e2f2
20 changed files with 9930 additions and 0 deletions
27
.editorconfig
Normal file
27
.editorconfig
Normal file
|
@ -0,0 +1,27 @@
|
|||
# see https://EditorConfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = tab
|
||||
indent_style = tab
|
||||
tab_width = 4
|
||||
insert_final_newline = true
|
||||
max_line_length = 80
|
||||
trim_trailing_whitespace = true
|
||||
curly_bracket_next_line = false
|
||||
indent_brace_style = K&R
|
||||
spaces_around_operators = true
|
||||
spaces_around_brackets = false
|
||||
quote_type = double
|
||||
|
||||
[*.y{,a}ml{,lint}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
/.geany
|
||||
/temp/
|
||||
/build/
|
2298
lib/plankton/plankton.d.ts
vendored
Normal file
2298
lib/plankton/plankton.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load diff
6303
lib/plankton/plankton.js
Normal file
6303
lib/plankton/plankton.js
Normal file
File diff suppressed because it is too large
Load diff
0
readme.md
Normal file
0
readme.md
Normal file
323
source/logic/backend.ts
Normal file
323
source/logic/backend.ts
Normal file
|
@ -0,0 +1,323 @@
|
|||
namespace _aum.backend
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
var _session_key_chest : lib_plankton.storage.type_chest<string, string, void, string, string>;
|
||||
|
||||
|
||||
/**
|
||||
* @todo use backend call
|
||||
*/
|
||||
export async function logged_in(
|
||||
) : Promise<boolean>
|
||||
{
|
||||
let session_key : (null | string);
|
||||
try {
|
||||
session_key = (await _session_key_chest.read("session_key"));
|
||||
}
|
||||
catch (error) {
|
||||
session_key = null;
|
||||
}
|
||||
return Promise.resolve<boolean>(session_key !== null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function init(
|
||||
) : Promise<void>
|
||||
{
|
||||
_session_key_chest = lib_plankton.storage.localstorage.implementation_chest(
|
||||
{
|
||||
"corner": "aum",
|
||||
}
|
||||
);
|
||||
return Promise.resolve<void>(undefined);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
async function abstract_call<type_input, type_output>(
|
||||
http_method : string,
|
||||
path : string,
|
||||
options : {
|
||||
data ?: type_input;
|
||||
} = {}
|
||||
) : Promise<type_output>
|
||||
{
|
||||
options = Object.assign(
|
||||
{
|
||||
"data": null,
|
||||
},
|
||||
options
|
||||
);
|
||||
|
||||
let session_key : (null | string);
|
||||
try {
|
||||
session_key = (await _session_key_chest.read("session_key"));
|
||||
}
|
||||
catch (error) {
|
||||
session_key = null;
|
||||
}
|
||||
|
||||
const without_content : boolean = ["HEAD","OPTIONS","GET"].includes(http_method);
|
||||
return (
|
||||
fetch(
|
||||
lib_plankton.string.coin(
|
||||
("{{scheme}}://{{host}}:{{port}}{{path_base}}{{path_action}}"),
|
||||
{
|
||||
"scheme": _aum.conf.get().backend.scheme,
|
||||
"host": _aum.conf.get().backend.host,
|
||||
"port": _aum.conf.get().backend.port.toFixed(0),
|
||||
"path_base": _aum.conf.get().backend.path_base,
|
||||
"path_action": path,
|
||||
}
|
||||
),
|
||||
{
|
||||
"method": http_method,
|
||||
"headers": Object.assign(
|
||||
{},
|
||||
((! without_content) ? {"Content-Type": "application/json"} : {}),
|
||||
((session_key !== null) ? {"X-Session-Key": session_key} : {})
|
||||
),
|
||||
"body": (
|
||||
without_content
|
||||
? null
|
||||
: JSON.stringify(options.data)
|
||||
),
|
||||
}
|
||||
)
|
||||
.then(
|
||||
x => {
|
||||
if ((x.status >= 200) && (x.status < 300)) {
|
||||
return x.json();
|
||||
}
|
||||
else {
|
||||
return Promise.reject<type_output>(new Error("unexpected response status code"));
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function login(
|
||||
name : string,
|
||||
password : string
|
||||
) : Promise<void>
|
||||
{
|
||||
const session_key : string = await abstract_call<{name : string; password : string;}, string>(
|
||||
"POST",
|
||||
"/session/begin",
|
||||
{
|
||||
"data": {
|
||||
"name": name,
|
||||
"password": password,
|
||||
}
|
||||
}
|
||||
);
|
||||
await _session_key_chest.write("session_key", session_key);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function logout(
|
||||
) : Promise<void>
|
||||
{
|
||||
try {
|
||||
await abstract_call<void, null>(
|
||||
"DELETE",
|
||||
"/session/end"
|
||||
);
|
||||
}
|
||||
catch (error) {
|
||||
// do nothing
|
||||
}
|
||||
await _session_key_chest.delete("session_key");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function email(
|
||||
receivers : Array<string>,
|
||||
subject : string,
|
||||
content : string
|
||||
) : Promise<void>
|
||||
{
|
||||
return (
|
||||
abstract_call(
|
||||
"POST",
|
||||
"/email",
|
||||
{
|
||||
"data": {
|
||||
"receivers": receivers,
|
||||
"subject": subject,
|
||||
"content": content,
|
||||
},
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function verification_get(
|
||||
data : any
|
||||
) : Promise<string>
|
||||
{
|
||||
return (
|
||||
abstract_call(
|
||||
"POST",
|
||||
"/verification/get",
|
||||
{
|
||||
"data": {
|
||||
"data": data,
|
||||
},
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function verification_check(
|
||||
data : any,
|
||||
verification : string
|
||||
) : Promise<boolean>
|
||||
{
|
||||
return (
|
||||
abstract_call(
|
||||
"POST",
|
||||
"/verification/check",
|
||||
{
|
||||
"data": {
|
||||
"data": data,
|
||||
"verification": verification,
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function member_list(
|
||||
) : Promise<Array<any>>
|
||||
{
|
||||
return (
|
||||
abstract_call(
|
||||
"GET",
|
||||
"/member"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function member_get(
|
||||
id : int
|
||||
) : Promise<any>
|
||||
{
|
||||
return abstract_call(
|
||||
"GET",
|
||||
"/member/" + id.toFixed(0)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function member_add(
|
||||
data : Record<string, any>
|
||||
) : Promise<int>
|
||||
{
|
||||
return abstract_call(
|
||||
"POST",
|
||||
"/member",
|
||||
{
|
||||
"data": {
|
||||
"membership_number": data["membership_number"],
|
||||
"enabled": data["enabled"],
|
||||
"name_real_value": data["name_real_value"],
|
||||
"name_real_extension": data["name_real_extension"],
|
||||
"name_display": data["name_display"],
|
||||
"name_login": data["name_login"],
|
||||
"password_image": null,
|
||||
"email_address_private_value": data["email_address_private_value"],
|
||||
"email_address_numberbased_use": data["email_address_numberbased_use"],
|
||||
"email_address_namebased_use": data["email_address_namebased_use"],
|
||||
"email_redirect_to_private": data["email_redirect_to_private"],
|
||||
"salutation": data["salutation"],
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function member_modify(
|
||||
id : int,
|
||||
data : Record<string, any>
|
||||
) : Promise<void>
|
||||
{
|
||||
return abstract_call(
|
||||
"PATCH",
|
||||
"/member/" + id.toFixed(0),
|
||||
{
|
||||
"data": {
|
||||
"membership_number": data["membership_number"],
|
||||
"enabled": data["enabled"],
|
||||
"name_real_value": data["name_real_value"],
|
||||
"name_real_extension": data["name_real_extension"],
|
||||
"name_display": data["name_display"],
|
||||
"name_login": data["name_login"],
|
||||
"password_image": null,
|
||||
"email_address_private_value": data["email_address_private_value"],
|
||||
"email_address_numberbased_use": data["email_address_numberbased_use"],
|
||||
"email_address_namebased_use": data["email_address_namebased_use"],
|
||||
"email_redirect_to_private": data["email_redirect_to_private"],
|
||||
"salutation": data["salutation"],
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function member_register(
|
||||
id : int,
|
||||
verification : string,
|
||||
data : {
|
||||
name_login : string;
|
||||
name_display : string;
|
||||
salutation : string;
|
||||
email_mode : ("none" | "number" | "number_and_name");
|
||||
email_redirect : boolean;
|
||||
password : string;
|
||||
}
|
||||
) : Promise<void>
|
||||
{
|
||||
return (
|
||||
abstract_call(
|
||||
"POST",
|
||||
"/member/register/" + id.toFixed(0) + "?verification=" + verification,
|
||||
{
|
||||
"data": data,
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
43
source/logic/conf.ts
Normal file
43
source/logic/conf.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
namespace _aum.conf
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
export type type_data = {
|
||||
backend : {
|
||||
scheme : string;
|
||||
host : string;
|
||||
port : int;
|
||||
path_base : string;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
var _data : (null | type_data) = null;
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function load(
|
||||
) : Promise<void>
|
||||
{
|
||||
_data = lib_plankton.json.decode(await lib_plankton.file.read("conf.json"));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function get(
|
||||
) : type_data
|
||||
{
|
||||
if (_data === null) {
|
||||
throw (new Error("conf not loaded yet"));
|
||||
}
|
||||
else {
|
||||
return _data;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
105
source/logic/main.ts
Normal file
105
source/logic/main.ts
Normal file
|
@ -0,0 +1,105 @@
|
|||
|
||||
/**
|
||||
*/
|
||||
function template_request(
|
||||
id : string
|
||||
) : DocumentFragment
|
||||
{
|
||||
let dom_template = document.querySelector("template#" + id);
|
||||
// return template["content"].cloneNode(true);
|
||||
return (document.importNode(dom_template["content"], true) as DocumentFragment)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
async function update_nav(
|
||||
options : {
|
||||
mode ?: string;
|
||||
} = {}
|
||||
) : Promise<void>
|
||||
{
|
||||
options = Object.assign(
|
||||
{
|
||||
"mode": ((await _aum.backend.logged_in()) ? "logged_in" : "logged_out"),
|
||||
},
|
||||
options
|
||||
);
|
||||
let dom_body = document.querySelector("body");
|
||||
if (options.mode === null) {
|
||||
dom_body.removeAttribute("rel");
|
||||
}
|
||||
else {
|
||||
dom_body.setAttribute("rel", options.mode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function setup_nav(
|
||||
) : void
|
||||
{
|
||||
const entries : Array<
|
||||
{
|
||||
location : lib_plankton.zoo_page.type_location;
|
||||
label : string;
|
||||
classes : Array<string>;
|
||||
}
|
||||
> = [
|
||||
{
|
||||
"location": {"name": "login", "parameters": {}},
|
||||
"label": "Anmelden",
|
||||
"classes": ["logged_out"],
|
||||
},
|
||||
{
|
||||
"location": {"name": "logout", "parameters": {}},
|
||||
"label": "Abmelden",
|
||||
"classes": ["logged_in"],
|
||||
},
|
||||
{
|
||||
"location": {"name": "create", "parameters": {}},
|
||||
"label": "Mitglied anlegen",
|
||||
"classes": ["logged_in"],
|
||||
},
|
||||
{
|
||||
"location": {"name": "members", "parameters": {}},
|
||||
"label": "Mitglieder-Übersicht",
|
||||
"classes": ["logged_in"],
|
||||
},
|
||||
];
|
||||
|
||||
let dom_ul : HTMLElement = document.querySelector("nav > ul");
|
||||
entries.forEach(
|
||||
entry => {
|
||||
let dom_li : HTMLElement = document.createElement("li");
|
||||
{
|
||||
let dom_a : HTMLElement = document.createElement("a");
|
||||
dom_a.textContent = entry.label;
|
||||
dom_a.setAttribute("href", lib_plankton.zoo_page.encode(entry.location));
|
||||
entry.classes.forEach(class_ => {dom_a.classList.add(class_);});
|
||||
dom_li.appendChild(dom_a);
|
||||
}
|
||||
dom_ul.appendChild(dom_li);
|
||||
}
|
||||
);
|
||||
update_nav();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
async function main(
|
||||
) : Promise<void>
|
||||
{
|
||||
await _aum.conf.load();
|
||||
await _aum.backend.init();
|
||||
await lib_plankton.zoo_page.init(
|
||||
document.querySelector("main"),
|
||||
{
|
||||
"fallback": {"name": "index", "parameters": {}},
|
||||
}
|
||||
);
|
||||
setup_nav();
|
||||
lib_plankton.zoo_page.start();
|
||||
}
|
72
source/logic/pages/create.ts
Normal file
72
source/logic/pages/create.ts
Normal file
|
@ -0,0 +1,72 @@
|
|||
lib_plankton.zoo_page.register(
|
||||
"create",
|
||||
(parameters, target_element) => {
|
||||
const form = new lib_plankton.zoo_form.class_form<
|
||||
{
|
||||
membership_number : string;
|
||||
name_real_value : string;
|
||||
email_address_private_value : (null | string);
|
||||
},
|
||||
{
|
||||
membership_number : string;
|
||||
name_real_value : string;
|
||||
email_address_private_value : string;
|
||||
}
|
||||
>(
|
||||
value => ({
|
||||
"membership_number": value.membership_number,
|
||||
"name_real_value": value.name_real_value,
|
||||
"email_address_private_value": (value.email_address_private_value ?? ""),
|
||||
}),
|
||||
representation => ({
|
||||
"membership_number": representation.membership_number,
|
||||
"name_real_value": representation.name_real_value,
|
||||
"email_address_private_value": representation.email_address_private_value,
|
||||
}),
|
||||
new lib_plankton.zoo_input.class_input_group(
|
||||
[
|
||||
{
|
||||
"name": "membership_number",
|
||||
"input": new lib_plankton.zoo_input.class_input_text(),
|
||||
"label": "Mitgliedsnummer",
|
||||
},
|
||||
{
|
||||
"name": "name_real_value",
|
||||
"input": new lib_plankton.zoo_input.class_input_text(),
|
||||
"label": "Echter Name",
|
||||
},
|
||||
{
|
||||
"name": "email_address_private_value",
|
||||
"input": new lib_plankton.zoo_input.class_input_text(),
|
||||
"label": "Private E-Mail-Adresse",
|
||||
},
|
||||
]
|
||||
),
|
||||
[
|
||||
{
|
||||
"label": "Senden",
|
||||
"procedure": async (get_value, get_representation) => {
|
||||
const value = await get_value();
|
||||
const id = await _aum.backend.member_add(
|
||||
{
|
||||
"membership_number": value.membership_number,
|
||||
"name_real_value": value.name_real_value,
|
||||
"email_address_private_value": value.email_address_private_value,
|
||||
"name_real_extension": null,
|
||||
"name_display": null,
|
||||
"name_login": null,
|
||||
"salutation": null,
|
||||
"password_image": null,
|
||||
"email_address_numberbased_use": false,
|
||||
"email_address_namebased_use": false,
|
||||
"email_redirect_to_private": false,
|
||||
}
|
||||
);
|
||||
lib_plankton.zoo_page.set({"name": "view", "parameters": {"id": id}});
|
||||
},
|
||||
}
|
||||
]
|
||||
);
|
||||
form.setup(target_element as HTMLElement);
|
||||
}
|
||||
);
|
5
source/logic/pages/index.ts
Normal file
5
source/logic/pages/index.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
lib_plankton.zoo_page.register(
|
||||
"index",
|
||||
(parameters, target_element) => {
|
||||
}
|
||||
);
|
62
source/logic/pages/login.ts
Normal file
62
source/logic/pages/login.ts
Normal file
|
@ -0,0 +1,62 @@
|
|||
lib_plankton.zoo_page.register(
|
||||
"login",
|
||||
(parameters, target_element) => {
|
||||
target_element.appendChild(template_request("login"));
|
||||
|
||||
const form = new lib_plankton.zoo_form.class_form<
|
||||
{
|
||||
name : string;
|
||||
password : string;
|
||||
},
|
||||
{
|
||||
name : string;
|
||||
password : string;
|
||||
}
|
||||
>(
|
||||
x => x,
|
||||
x => x,
|
||||
new lib_plankton.zoo_input.class_input_group(
|
||||
[
|
||||
{
|
||||
"name": "name",
|
||||
"input": new lib_plankton.zoo_input.class_input_text(),
|
||||
"label": "Name",
|
||||
},
|
||||
{
|
||||
"name": "password",
|
||||
"input": new lib_plankton.zoo_input.class_input_password(),
|
||||
"label": "Passwort",
|
||||
},
|
||||
]
|
||||
),
|
||||
[
|
||||
{
|
||||
"label": "Anmelden",
|
||||
"procedure": async (get_value, get_representation) => {
|
||||
const value = await get_value();
|
||||
(
|
||||
_aum.backend.login(value.name, value.password)
|
||||
.then(
|
||||
() => {
|
||||
lib_plankton.zoo_page.set({"name": "index", "parameters": {}});
|
||||
update_nav({"mode": "logged_in"});
|
||||
}
|
||||
)
|
||||
.catch(
|
||||
(error) => {
|
||||
form.input_write(
|
||||
{
|
||||
"name": value.name,
|
||||
"password": ""
|
||||
}
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
},
|
||||
}
|
||||
]
|
||||
);
|
||||
form.setup(target_element.querySelector(".login-form") as HTMLElement);
|
||||
}
|
||||
);
|
8
source/logic/pages/logout.ts
Normal file
8
source/logic/pages/logout.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
lib_plankton.zoo_page.register(
|
||||
"logout",
|
||||
async (parameters, target_element) => {
|
||||
await _aum.backend.logout();
|
||||
lib_plankton.zoo_page.set({"name": "index", "parameters": {}});
|
||||
update_nav({"mode": "logged_in"});
|
||||
}
|
||||
);
|
219
source/logic/pages/members.ts
Normal file
219
source/logic/pages/members.ts
Normal file
|
@ -0,0 +1,219 @@
|
|||
lib_plankton.zoo_page.register(
|
||||
"members",
|
||||
(parameters, target_element) => {
|
||||
const state : (null | any) = (
|
||||
("state" in parameters)
|
||||
? lib_plankton.json.decode(lib_plankton.base64.decode(parameters["state"]))
|
||||
: null
|
||||
);
|
||||
|
||||
const set_state = function (state) {
|
||||
target_element.querySelector(".members").setAttribute("rel", state);
|
||||
};
|
||||
|
||||
let _id : (null | int) = null;
|
||||
|
||||
const editor : lib_plankton.zoo_editor.type_editor<int, any> = lib_plankton.zoo_editor.make<int, any>(
|
||||
{
|
||||
"setup": (parameters) => Promise.resolve<void>(undefined),
|
||||
"search": async (term) => lib_plankton.call.convey(
|
||||
await _aum.backend.member_list(),
|
||||
[
|
||||
// (raw as Array<{id : int; preview : any;}>)
|
||||
x => x.filter(
|
||||
(
|
||||
(term === null)
|
||||
||
|
||||
(term === "")
|
||||
)
|
||||
? (entry => true)
|
||||
: (
|
||||
(term.length < 2)
|
||||
? (entry => false)
|
||||
: (
|
||||
entry => (
|
||||
entry.preview["membership_number"].includes(term)
|
||||
||
|
||||
entry.preview["name_real_value"].toLowerCase().includes(term)
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
x => x.map(
|
||||
entry => ({
|
||||
"key": entry.id,
|
||||
"preview": entry.preview,
|
||||
})
|
||||
),
|
||||
]
|
||||
),
|
||||
"read": (key) => _aum.backend.member_get(key),
|
||||
"create": (value) => /*_aum.backend.member_add(value)*/Promise.reject(new Error("missing")),
|
||||
"update": (key, value) => _aum.backend.member_modify(key, value),
|
||||
"delete": (key) => /*_aum.backend.member_remove(key)*/Promise.reject(new Error("missing")),
|
||||
},
|
||||
lib_plankton.zoo_form.make<any>(
|
||||
"GET",
|
||||
[
|
||||
{
|
||||
"name": "membership_number",
|
||||
"type": "text",
|
||||
"label": "Mitgliedsnummer",
|
||||
},
|
||||
{
|
||||
"name": "enabled",
|
||||
"type": "checkbox",
|
||||
"label": "Für Online-Dienste freischalten",
|
||||
},
|
||||
{
|
||||
"name": "name_real_value",
|
||||
"type": "text",
|
||||
"label": "Echter Name",
|
||||
},
|
||||
{
|
||||
"name": "name_real_extension",
|
||||
"type": "text",
|
||||
"label": "Zusatz für echten Name (für mögliche Dopplung)",
|
||||
},
|
||||
{
|
||||
"name": "name_display",
|
||||
"type": "text",
|
||||
"label": "Anzeigename",
|
||||
},
|
||||
{
|
||||
"name": "name_login",
|
||||
"type": "text",
|
||||
"label": "Anmeldename",
|
||||
},
|
||||
{
|
||||
"name": "email_address_private_value",
|
||||
"type": "text",
|
||||
"label": "Private E-Mail-Adresse",
|
||||
},
|
||||
{
|
||||
"name": "email_address_numberbased_use",
|
||||
"type": "checkbox",
|
||||
"label": "Nummernbasierte E-Mail-Adresse verwenden",
|
||||
},
|
||||
{
|
||||
"name": "email_address_namebased_use",
|
||||
"type": "checkbox",
|
||||
"label": "Namensbasierte E-Mail-Adresse verwenden",
|
||||
},
|
||||
{
|
||||
"name": "email_redirect_to_private",
|
||||
"type": "checkbox",
|
||||
"label": "E-Mails an private Adresse umleiten",
|
||||
},
|
||||
{
|
||||
"name": "salutation",
|
||||
"type": "text",
|
||||
"label": "Anrede/Pronomen",
|
||||
},
|
||||
],
|
||||
value => ({
|
||||
"enabled": (value.enabled ? "on" : ""),
|
||||
"membership_number": value.membership_number,
|
||||
"name_real_value": value.name_real_value,
|
||||
"name_real_extension": value.name_real_extension,
|
||||
"name_display": value.name_display,
|
||||
"name_login": value.name_login,
|
||||
"email_address_private_value": value.email_address_private_value,
|
||||
"email_address_numberbased_use": (value.email_address_numberbased_use ? "on" : ""),
|
||||
"email_address_namebased_use": (value.email_address_namebased_use ? "on" : ""),
|
||||
"email_redirect_to_private": (value.email_redirect_to_private ? "on" : ""),
|
||||
"salutation": value.salutation,
|
||||
}),
|
||||
raw => ({
|
||||
"enabled": (raw["enabled"] === "on"),
|
||||
"membership_number": raw["membership_number"],
|
||||
"name_real_value": raw["name_real_value"],
|
||||
"name_real_extension": raw["name_real_extension"],
|
||||
"name_display": raw["name_display"],
|
||||
"name_login": raw["name_login"],
|
||||
"email_address_private_value": raw["email_address_private_value"],
|
||||
"email_address_numberbased_use": (raw["email_address_numberbased_use"] === "on"),
|
||||
"email_address_namebased_use": (raw["email_address_namebased_use"] === "on"),
|
||||
"email_redirect_to_private": (raw["email_redirect_to_private"] === "on"),
|
||||
"salutation": raw["salutation"],
|
||||
}),
|
||||
{
|
||||
}
|
||||
),
|
||||
{
|
||||
"encode_hit": hit => lib_plankton.string.coin(
|
||||
"{{number}} | {{name}}",
|
||||
{
|
||||
"number": hit.preview["membership_number"],
|
||||
"name": hit.preview["name_real_value"],
|
||||
}
|
||||
),
|
||||
"hook_switch": (state) => {
|
||||
_id = state.key;
|
||||
set_state((_id === null) ? "poor" : "rich");
|
||||
const state_encoded : string = lib_plankton.base64.encode(lib_plankton.json.encode(state));
|
||||
lib_plankton.zoo_page.set({"name": "members", "parameters": {"state": state_encoded}});
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
target_element.appendChild(template_request("members"));
|
||||
|
||||
lib_plankton.zoo_editor.render<int, any>(
|
||||
editor,
|
||||
target_element.querySelector(".members-editor"),
|
||||
(
|
||||
Object.assign(
|
||||
{
|
||||
},
|
||||
((state !== null) ? {"state": state} : {})
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
target_element.querySelector(".members-urge_for_registration").addEventListener(
|
||||
"click",
|
||||
async () => {
|
||||
const verification : string = await _aum.backend.verification_get(_id);
|
||||
const location_encoded : string = lib_plankton.zoo_page.encode(
|
||||
{
|
||||
"name": "register",
|
||||
"parameters": {
|
||||
"id": _id,
|
||||
"verification": verification,
|
||||
}
|
||||
}
|
||||
);
|
||||
const url_base : string = window.location.href.split("#")[0];
|
||||
const url : string = (url_base + location_encoded);
|
||||
|
||||
const data : Record<string, any> = await lib_plankton.zoo_form.read(editor.form);
|
||||
|
||||
const text_paragraphs : Array<string> = [
|
||||
lib_plankton.string.coin(
|
||||
"Hi, {{name}}!",
|
||||
{
|
||||
"name": "data.name_real_value",
|
||||
}
|
||||
),
|
||||
"Willkommen bei der Linken!",
|
||||
"Wir als Landesverband Sachsen stellen für unsere Mitglieder verschiedene Online-Dienste zur Verfügung. Dazu gehört eine E-Mail-Adresse bei der Partei, ein Instant Messenger, eine Cloud, ein Wiki und noch einiges mehr.",
|
||||
"Wenn du die Dienste nutzen möchtest, rufe bitte folgende Adresse auf:",
|
||||
url,
|
||||
"Solidarische Grüße, dein Landesverband Sachsen",
|
||||
];
|
||||
|
||||
_aum.backend.email(
|
||||
[
|
||||
data.email_address_private_value,
|
||||
],
|
||||
"DIE LINKE. | Landesverband Sachsen | Registierung für Online-Dienste",
|
||||
text_paragraphs.join("\n\n"),
|
||||
);
|
||||
// TODO: statt dessen eine E-Mail an die private Adresse des Neumitglieds senden
|
||||
// target_element.querySelector(".members-result").setAttribute("href", url);
|
||||
alert(url);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
132
source/logic/pages/register.ts
Normal file
132
source/logic/pages/register.ts
Normal file
|
@ -0,0 +1,132 @@
|
|||
lib_plankton.zoo_page.register(
|
||||
"register",
|
||||
async (parameters, target_element) => {
|
||||
const set_state = function (state) {
|
||||
target_element.querySelector(".register").setAttribute("rel", state);
|
||||
};
|
||||
const set_message = function (message) {
|
||||
target_element.querySelector(".register-message").textContent = message;
|
||||
};
|
||||
|
||||
const id : int = parseInt(parameters["id"]);
|
||||
const verification : string = parameters["verification"];
|
||||
|
||||
update_nav({"mode": null});
|
||||
|
||||
const form = new lib_plankton.zoo_form.class_form<any, any>(
|
||||
x => x,
|
||||
x => x,
|
||||
new lib_plankton.zoo_input.class_input_group(
|
||||
[
|
||||
{
|
||||
"name": "id",
|
||||
"input": new lib_plankton.zoo_input.class_input_hidden(),
|
||||
},
|
||||
{
|
||||
"name": "verification",
|
||||
"input": new lib_plankton.zoo_input.class_input_hidden(),
|
||||
},
|
||||
{
|
||||
"name": "email_mode",
|
||||
"input": new lib_plankton.zoo_input.class_input_enumeration(
|
||||
[
|
||||
{"value": "none", "label": "keine"},
|
||||
{"value": "number", "label": "nur numerische"},
|
||||
{"value": "number_and_name", "label": "numerische und namensbasierte"},
|
||||
]
|
||||
),
|
||||
"label": "Partei-E-Mail-Adresse einrichten",
|
||||
"help": "Die nummernbasierte Partei-E-Mail-Adresse hat folgenden schematischen Aufbau: \"mitglied-<mitglieds-nummer>@dielinke-sachsen.de\", z.B. \"mitglied-11223344@dielinke-sachsen.de\".\n\nDie namensbasierte Partei-E-Mail-Adresse hat folgenden schematischen Aufbau: \"<name-in-kleinbuchstaben-mit-punkten-getrennt>@dielinke-sachsen.de\". Beispiel: \"Karl Liebknecht\" würde die Adresse \"karl.liebknecht@dielinke-sachsen.de\" bekommen.\n\nDie Partei-E-Mail-Adressen können zum Empfangen von E-Mails verwendet werden. Falls es nötig werden sollte, dass du auch E-Mails mit über die Partei-Adresse verschicken kannst, wende dich bitte an den/die Mitgliederbeauftragte:n!"
|
||||
},
|
||||
{
|
||||
"name": "email_redirect",
|
||||
"input": new lib_plankton.zoo_input.class_input_checkbox(),
|
||||
"label": "eingehende E-Mails zu privater Adresse umleiten",
|
||||
"help": "Um die bei der Partei-Adresse eingegangenen E-Mails zu lesen, gibt es zwei Wege: Entweder du hinterlegst das zugehörige Konto im E-Mail-Client-Programm deiner Wahl und kümmerst dich selbst darum die E-Mails regelmäßig abzurufen oder die E-Mails werden an deine private Adresse weitergeleitet, sodass sie bei deinen gewöhnlichen E-Mails mit auftauchen.\n\nWenn du dir unsicher bist, empfehlen wir dir die Umleitung anzuschalten.",
|
||||
},
|
||||
{
|
||||
"name": "name_display",
|
||||
"input": new lib_plankton.zoo_input.class_input_text(),
|
||||
"label": "Anzeigename",
|
||||
"help": "So wirst du bei Online-Diensten anderen angezeigt.",
|
||||
},
|
||||
{
|
||||
"name": "salutation",
|
||||
"input": new lib_plankton.zoo_input.class_input_text(),
|
||||
"label": "Anrede/Pronomen (z.B. 'er/ihn')",
|
||||
},
|
||||
{
|
||||
"name": "name_login",
|
||||
"input": new lib_plankton.zoo_input.class_input_text(
|
||||
{
|
||||
"pattern": "^[0-9a-zA-Z_]+$",
|
||||
}
|
||||
),
|
||||
"label": "Anmeldename",
|
||||
"help": "Dieser Wert ist der Nutzername für die Anmeldung bei den Online-Diensten. Hierfür solltest du etwas kurzes und prägnantes wählen. Diesen Namen bekommt für gewöhnlich niemand zu sehen. Bitte beachte, dass der Name noch verfügbar sein muss!",
|
||||
},
|
||||
{
|
||||
"name": "password",
|
||||
"input": new lib_plankton.zoo_input.class_input_password(),
|
||||
"label": "Passwort",
|
||||
"help": "Das Passwort für die Anmeldung bei den Online-Diensten",
|
||||
},
|
||||
{
|
||||
"name": "password_confirmation",
|
||||
"input": new lib_plankton.zoo_input.class_input_password(),
|
||||
"label": "Passwort wiederholen",
|
||||
},
|
||||
]
|
||||
),
|
||||
[
|
||||
{
|
||||
"label": "Senden",
|
||||
"procedure": async (get_value, get_representation) => {
|
||||
set_message("wird verarbeitet …");
|
||||
set_state("wait");
|
||||
|
||||
const value : any = await get_value();
|
||||
|
||||
if (value.password !== value.password_confirmation) {
|
||||
set_message("Die Passwörter stimmen nicht überein.");
|
||||
set_state("fill");
|
||||
}
|
||||
else {
|
||||
try {
|
||||
await _aum.backend.member_register(
|
||||
id,
|
||||
verification,
|
||||
value
|
||||
);
|
||||
set_message("Danke!");
|
||||
set_state("done");
|
||||
}
|
||||
catch (error) {
|
||||
set_message("Da ist etwas schief gelaufen :/");
|
||||
set_state("fill");
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
target_element.appendChild(template_request("register"));
|
||||
|
||||
await form.setup(target_element.querySelector(".register-form") as HTMLElement);
|
||||
await form.input_write(
|
||||
{
|
||||
"id": id,
|
||||
"verification": verification,
|
||||
"name_login": "",
|
||||
"name_display": "",
|
||||
"salutation": "",
|
||||
"email_mode": "number_and_name",
|
||||
"email_redirect": true,
|
||||
"password": "",
|
||||
"password_confirmation": "",
|
||||
}
|
||||
);
|
||||
set_state("fill");
|
||||
},
|
||||
);
|
62
source/structure/index.html.tpl
Normal file
62
source/structure/index.html.tpl
Normal file
|
@ -0,0 +1,62 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<link rel="stylesheet" type="text/css" href="style.css"/>
|
||||
<script type="text/javascript" src="logic.js"></script>
|
||||
<script type="text/javascript">document.addEventListener("DOMContentLoaded", () => {main();});</script>
|
||||
<template id="login">
|
||||
<section class="login">
|
||||
<h2>Login</h2>
|
||||
<div class="login-form">
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
<template id="view">
|
||||
<section class="view">
|
||||
<h2>Mitglied</h2>
|
||||
<pre class="view-data">
|
||||
</pre>
|
||||
<hr/>
|
||||
<div>
|
||||
<button class="view-urge_for_registration">Zur Registrierung auffordern</button>
|
||||
</div>
|
||||
<a class="view-result">Registrierung</a>
|
||||
</section>
|
||||
</template>
|
||||
<template id="members">
|
||||
<section class="members">
|
||||
<h2>Mitglied</h2>
|
||||
<div class="members-editor">
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="members-extras">
|
||||
<button class="members-urge_for_registration">Zur Registrierung auffordern</button>
|
||||
<a class="members-result">Registrierung</a>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
<template id="register">
|
||||
<section class="register">
|
||||
<h2>Registrieren</h2>
|
||||
<div class="register-message">
|
||||
</div>
|
||||
<div class="register-form">
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>DIE LINKE. Landesverband Sachsen</h1>
|
||||
</header>
|
||||
<hr/>
|
||||
<nav>
|
||||
<ul>
|
||||
</ul>
|
||||
</nav>
|
||||
<main>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
183
source/style/style.css
Normal file
183
source/style/style.css
Normal file
|
@ -0,0 +1,183 @@
|
|||
:root
|
||||
{
|
||||
--hue: 0;
|
||||
}
|
||||
|
||||
html
|
||||
{
|
||||
font-family: monospace;
|
||||
font-size: 1.5em;
|
||||
|
||||
background-color: hsl(var(--hue), 0%, 0%);
|
||||
color: hsl(var(--hue), 0%, 100%);
|
||||
}
|
||||
|
||||
|
||||
body
|
||||
{
|
||||
max-width: 960px;
|
||||
margin: auto;
|
||||
padding: 24px;
|
||||
|
||||
background-color: hsl(var(--hue), 0%, 12.5%);
|
||||
color: hsl(var(--hue), 0%, 87.5%);
|
||||
}
|
||||
|
||||
body:not([rel="logged_out"]) nav .logged_out {display: none;}
|
||||
body:not([rel="logged_in"]) nav .logged_in {display: none;}
|
||||
|
||||
a
|
||||
{
|
||||
background-color: hsl(var(--hue), 0%, 12.5%);
|
||||
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:not(:hover)
|
||||
{
|
||||
/*
|
||||
color: hsl(var(--hue), 75%, 50%);
|
||||
*/
|
||||
color: hsl(var(--hue), 72.3%, 54.7%);
|
||||
}
|
||||
|
||||
a:hover
|
||||
{
|
||||
/*
|
||||
color: hsl(var(--hue), 75%, 75%);
|
||||
*/
|
||||
color: hsl(var(--hue), 72.8%, 79.8%)
|
||||
}
|
||||
|
||||
input[type="text"]
|
||||
,
|
||||
input[type="number"]
|
||||
,
|
||||
input[type="password"]
|
||||
{
|
||||
background-color: hsl(var(--hue), 0%, 87.5%);
|
||||
color: hsl(var(--hue), 0%, 12.5%);
|
||||
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
nav
|
||||
{
|
||||
border-bottom: 1px solid hsl(var(--hue), 0%, 100%);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
nav > ul
|
||||
{
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
nav > ul > li
|
||||
{
|
||||
display: inline-block;
|
||||
margin-right: 16px;
|
||||
padding: 8px;
|
||||
|
||||
/*
|
||||
text-transform: uppercase;
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
nav > ul > li:hover::before
|
||||
{
|
||||
content: "[";
|
||||
}
|
||||
|
||||
nav > ul > li:hover::after
|
||||
{
|
||||
content: "]";
|
||||
}
|
||||
*/
|
||||
|
||||
.plankton_form_field
|
||||
{
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.plankton_form_label
|
||||
{
|
||||
display: block;
|
||||
|
||||
font-size: 0.75em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.plankton_input_group_field
|
||||
{
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.plankton_input_group_field_label
|
||||
{
|
||||
font-size: 0.75em;
|
||||
font-weight: bold;
|
||||
/*
|
||||
text-transform: capitalize;
|
||||
*/
|
||||
}
|
||||
|
||||
.plankton_input_group_field_label + :not(.plankton_input_group_field_help)
|
||||
{
|
||||
display: block;
|
||||
}
|
||||
|
||||
.plankton_input_group_field_help
|
||||
{
|
||||
margin-left: 8px;
|
||||
|
||||
font-size: 0.75em;
|
||||
font-weight: bold;
|
||||
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.plankton_input_group_field_help + *
|
||||
{
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
.plankton_input_enumeration > *
|
||||
{
|
||||
display: block;
|
||||
}
|
||||
|
||||
.plankton_editor_actions
|
||||
{
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.plankton_editor_action
|
||||
{
|
||||
text-transform: uppercase;
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
.members-result:not([href])
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.register:not([rel]) .register-message {display: none;}
|
||||
.register:not([rel]) .register-form {display: none;}
|
||||
|
||||
.register[rel="fill"] .register-message {}
|
||||
.register[rel="fill"] .register-form {}
|
||||
|
||||
.register[rel="wait"] .register-message {}
|
||||
.register[rel="wait"] .register-form {display: none;}
|
||||
|
||||
.register[rel="done"] .register-message {}
|
||||
.register[rel="done"] .register-form {display: none;}
|
||||
|
||||
.members:not([rel]) .members-extras {display: none;}
|
||||
.members[rel="poor"] .members-extras {display: none;}
|
||||
.members[rel="rich"] .members-extras {}
|
5
tools/build
Executable file
5
tools/build
Executable file
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
## exec
|
||||
|
||||
make --file=tools/makefile
|
49
tools/makefile
Normal file
49
tools/makefile
Normal file
|
@ -0,0 +1,49 @@
|
|||
## consts
|
||||
|
||||
dir_lib := lib
|
||||
dir_source := source
|
||||
dir_temp := temp
|
||||
dir_build := build
|
||||
|
||||
cmd_log := echo "--"
|
||||
cmd_cat := cat
|
||||
cmd_chmod := chmod
|
||||
cmd_mkdir := mkdir -p
|
||||
cmd_cp := cp
|
||||
|
||||
|
||||
## rules
|
||||
|
||||
.PHONY: default
|
||||
default: ${dir_build}/index.html ${dir_build}/logic.js ${dir_build}/style.css
|
||||
|
||||
${dir_build}/index.html: ${dir_source}/structure/index.html.tpl
|
||||
@ ${cmd_log} "structure …"
|
||||
@ ${cmd_mkdir} $(dir $@)
|
||||
@ ${cmd_cat} $^ > $@
|
||||
|
||||
${dir_temp}/logic-unlinked.js: \
|
||||
${dir_lib}/plankton/plankton.d.ts \
|
||||
${dir_source}/logic/backend.ts \
|
||||
${dir_source}/logic/conf.ts \
|
||||
${dir_source}/logic/pages/index.ts \
|
||||
${dir_source}/logic/pages/login.ts \
|
||||
${dir_source}/logic/pages/logout.ts \
|
||||
${dir_source}/logic/pages/members.ts \
|
||||
${dir_source}/logic/pages/create.ts \
|
||||
${dir_source}/logic/pages/register.ts \
|
||||
${dir_source}/logic/main.ts
|
||||
@ ${cmd_log} "logic | compile …"
|
||||
@ ${cmd_mkdir} $(dir $@)
|
||||
@ tsc --lib es2020,dom $^ --outFile $@
|
||||
|
||||
${dir_build}/logic.js: ${dir_lib}/plankton/plankton.js ${dir_temp}/logic-unlinked.js
|
||||
@ ${cmd_log} "logic | link …"
|
||||
@ ${cmd_mkdir} $(dir $@)
|
||||
@ ${cmd_cat} $^ > $@
|
||||
@ ${cmd_chmod} +x $@
|
||||
|
||||
${dir_build}/style.css: ${dir_source}/style/style.css
|
||||
@ ${cmd_log} "style …"
|
||||
@ ${cmd_mkdir} $(dir $@)
|
||||
@ ${cmd_cat} $^ > $@
|
5
tools/run
Executable file
5
tools/run
Executable file
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
cd build
|
||||
web-server . 8888
|
||||
cd -
|
26
tools/update-plankton
Executable file
26
tools/update-plankton
Executable file
|
@ -0,0 +1,26 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
## consts
|
||||
|
||||
dir=lib/plankton
|
||||
|
||||
modules=""
|
||||
modules="${modules} base"
|
||||
modules="${modules} file"
|
||||
modules="${modules} json"
|
||||
modules="${modules} base64"
|
||||
modules="${modules} string"
|
||||
modules="${modules} storage"
|
||||
modules="${modules} zoo-input"
|
||||
modules="${modules} zoo-form"
|
||||
modules="${modules} zoo-search"
|
||||
modules="${modules} zoo-editor"
|
||||
modules="${modules} zoo-page"
|
||||
|
||||
|
||||
## exec
|
||||
|
||||
mkdir -p ${dir}
|
||||
cd ${dir}
|
||||
ptk bundle web ${modules}
|
||||
cd - > /dev/null
|
Loading…
Add table
Reference in a new issue