[add] nav [add] session management [add] pages

This commit is contained in:
Fenris Wolf 2024-09-19 10:17:43 +02:00
parent 03f29fae11
commit 2738299c1b
12 changed files with 5696 additions and 3823 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -22,5 +22,13 @@ document.addEventListener(
</script> </script>
</head> </head>
<body> <body>
<header>
<nav>
<ul>
</ul>
</nav>
</header>
<main>
</main>
</body> </body>
</html> </html>

View file

@ -5,7 +5,48 @@ namespace _zeitbild.frontend_web.backend
/** /**
*/ */
var _session_key : (null | string) = null; var _data_chest : (
null
|
lib_plankton.storage.type_chest<string, string, void, string, string>
) = null;
/**
*/
export async function init(
) : Promise<void>
{
_data_chest = lib_plankton.storage.localstorage.implementation_chest(
{
"corner": "zeitbild",
}
);
return Promise.resolve<void>(undefined);
}
/**
*/
async function get_session_key(
) : Promise<(null | string)>
{
try {
return (await _data_chest.read("session_key"));
}
catch (error) {
return null;
}
}
/**
*/
export async function is_logged_in(
) : Promise<boolean>
{
return ((await get_session_key()) !== null);
}
/** /**
@ -23,6 +64,7 @@ namespace _zeitbild.frontend_web.backend
lib_plankton.http.enum_method.patch, lib_plankton.http.enum_method.patch,
].includes(method) ].includes(method)
); );
const session_key : (null | string) = await get_session_key();
const http_request : lib_plankton.http.type_request = { const http_request : lib_plankton.http.type_request = {
"version": "HTTP/2", "version": "HTTP/2",
"scheme": ( "scheme": (
@ -64,11 +106,11 @@ namespace _zeitbild.frontend_web.backend
{"Content-Type": "application/json"} {"Content-Type": "application/json"}
), ),
( (
(_session_key === null) (session_key === null)
? ?
{} {}
: :
{"X-Session-Key": _session_key} {"X-Session-Key": session_key}
) )
), ),
"body": ( "body": (
@ -80,9 +122,20 @@ namespace _zeitbild.frontend_web.backend
), ),
}; };
const http_response : lib_plankton.http.type_response = await lib_plankton.http.call(http_request); const http_response : lib_plankton.http.type_response = await lib_plankton.http.call(http_request);
if (
! (
(http_response.status_code >= 200)
&&
(http_response.status_code < 300)
)
) {
return Promise.reject<any>(http_response.body.toString());
}
else {
const output : any = lib_plankton.json.decode(http_response.body.toString()); const output : any = lib_plankton.json.decode(http_response.body.toString());
return Promise.resolve<any>(output); return Promise.resolve<any>(output);
} }
}
/** /**
@ -92,7 +145,7 @@ namespace _zeitbild.frontend_web.backend
password : string password : string
) : Promise<void> ) : Promise<void>
{ {
_session_key = await call( const session_key : string = await call(
lib_plankton.http.enum_method.post, lib_plankton.http.enum_method.post,
"/session/begin", "/session/begin",
{ {
@ -100,6 +153,7 @@ namespace _zeitbild.frontend_web.backend
"password": password, "password": password,
} }
); );
await _data_chest.write("session_key", session_key);
return Promise.resolve<void>(undefined); return Promise.resolve<void>(undefined);
} }
@ -109,11 +163,13 @@ namespace _zeitbild.frontend_web.backend
export async function session_end( export async function session_end(
) : Promise<void> ) : Promise<void>
{ {
return call( await call(
lib_plankton.http.enum_method.delete, lib_plankton.http.enum_method.delete,
"/session/end", "/session/end",
null null
); );
await _data_chest.delete("session_key");
return Promise.resolve<void>(undefined);
} }
@ -123,7 +179,7 @@ namespace _zeitbild.frontend_web.backend
) : Promise< ) : Promise<
Array< Array<
{ {
key : type_calendar_id; key : _zeitbild.frontend_web.type.calendar_id;
preview : { preview : {
name : string; name : string;
} }
@ -146,14 +202,14 @@ namespace _zeitbild.frontend_web.backend
from_pit : _zeitbild.frontend_web.helpers.type_pit, from_pit : _zeitbild.frontend_web.helpers.type_pit,
to_pit : _zeitbild.frontend_web.helpers.type_pit, to_pit : _zeitbild.frontend_web.helpers.type_pit,
options : { options : {
calendar_ids ?: (null | Array<type_calendar_id>); calendar_ids ?: (null | Array<_zeitbild.frontend_web.type.calendar_id>);
} = {} } = {}
) : Promise< ) : Promise<
Array< Array<
{ {
calendar_id : type_calendar_id; calendar_id : _zeitbild.frontend_web.type.calendar_id;
calendar_name : string; calendar_name : string;
event : type_event; event : _zeitbild.frontend_web.type.event_object;
} }
> >
> >

View file

@ -1,218 +0,0 @@
/**
*/
async function main(
args_raw : Array<string>
) : Promise<void>
{
const arg_handler : lib_plankton.args.class_handler = new lib_plankton.args.class_handler({
"data_path": lib_plankton.args.class_argument.volatile({
"indicators_long": ["data-path"],
"indicators_short": ["d"],
"type": lib_plankton.args.enum_type.string,
"mode": lib_plankton.args.enum_mode.replace,
"default": "data.json",
// "info": null,
"name": "data-path",
}),
"timezone_shift": lib_plankton.args.class_argument.volatile({
"indicators_long": ["timezone-shift"],
"indicators_short": ["t"],
"type": lib_plankton.args.enum_type.integer,
"mode": lib_plankton.args.enum_mode.replace,
"default": 0,
// "info": null,
"name": "timezone-shift",
}),
"calendar_id": lib_plankton.args.class_argument.volatile({
"indicators_long": ["calendar"],
"indicators_short": ["c"],
"type": lib_plankton.args.enum_type.integer,
"mode": lib_plankton.args.enum_mode.replace,
"default": 1,
// "info": null,
"name": "calendar_id",
}),
"view_mode": lib_plankton.args.class_argument.volatile({
"indicators_long": ["view-moode"],
"indicators_short": ["m"],
"type": lib_plankton.args.enum_type.string,
"mode": lib_plankton.args.enum_mode.replace,
"default": "table",
// "info": null,
"name": "view_mode",
}),
"help": lib_plankton.args.class_argument.volatile({
"indicators_long": ["help"],
"indicators_short": ["h"],
"type": lib_plankton.args.enum_type.boolean,
"mode": lib_plankton.args.enum_mode.replace,
"default": false,
// "info": null,
"name": "help",
}),
});
const args : Record<string, any> = arg_handler.read(lib_plankton.args.enum_environment.cli, args_raw.join(" "));
// init
lib_plankton.log.conf_push(
[
// lib_plankton.log.channel_make({"kind": "file", "data": {"threshold": "debug", "path": "/tmp/kalender.log"}}),
lib_plankton.log.channel_make({"kind": "file", "data": {"threshold": "info", "path": "/dev/stderr"}}),
// lib_plankton.log.channel_make({"kind": "stdout", "data": {"threshold": "info"}}),
]
);
// exec
if (args["help"]) {
process.stdout.write(
arg_handler.generate_help(
{
"programname": "kalender",
"description": "Kalender",
"executable": "kalender",
}
)
+
"\n"
);
}
else {
const data : type_datamodel = lib_plankton.call.convey(
await lib_plankton.file.read(args["data_path"]),
[
lib_plankton.json.decode,
(data_raw : any) => (
({
"users": data_raw["users"],
"calendars": (
data_raw["calendars"]
.map(
(calendar_entry_raw : any) => ({
"id": calendar_entry_raw["id"],
"object": (
((calendar_object_raw) => {
switch (calendar_object_raw["kind"]) {
default: {
return calendar_object_raw;
break;
}
case "concrete": {
return {
"kind": "concrete",
"data": {
"name": calendar_object_raw["data"]["name"],
"users": calendar_object_raw["data"]["users"],
"events": (
calendar_object_raw["data"]["events"]
.map(
(event_raw : any) => ({
"name": event_raw["name"],
"begin": event_raw["begin"],
"end": (
(event_raw["end"] === null)
?
null
:
event_raw["end"]
),
"description": event_raw["description"],
})
)
),
},
};
break;
}
}
}) (calendar_entry_raw["object"])
),
})
)
),
}) as type_datamodel
),
]
);
let content : string;
switch (args["view_mode"]) {
default: {
content = "";
throw (new Error("invalid view mode"));
break;
}
case "table": {
content = await calendar_view_table_html(
data,
args.calendar_id,
{
"timezone_shift": args["timezone_shift"],
}
);
break;
}
case "list": {
content = await calendar_view_list_html(
data,
args.calendar_id,
{
"timezone_shift": args["timezone_shift"],
}
);
break;
}
}
const output : string = template_coin(
"main",
{
"content": content,
}
);
process.stdout.write(output);
}
return Promise.resolve<void>(undefined);
}
/*
process.stderr.write(
JSON.stringify(
{
"x1": datetime_from_date_object(
new Date(Date.now()),
{
"timezone_shift": 2,
}
),
"x2": datetime_from_date_object(
new Date(Date.now()),
{
"timezone_shift": 0,
}
),
"x3": pit_from_year_and_week(
2024,
37,
{
"timezone_shift": 0,
}
),
},
undefined,
"\t"
)
+
"\n"
);
*/
(
main(process.argv.slice(2))
.then(
() => {}
)
/*
.catch(
(error) => {process.stderr.write(String(error) + "\n");}
)
*/
);

View file

@ -6,27 +6,103 @@ namespace _zeitbild.frontend_web
/** /**
*/ */
type type_conf = { export async function main(
view_mode : string;
calendar_ids : Array<int>;
timezone_shift : int;
};
/**
*/
async function render(
) : Promise<void> ) : Promise<void>
{ {
const target : HTMLElement = (document.querySelector("body") as HTMLBodyElement); // conf
const view_type : string = "table"; await _zeitbild.frontend_web.conf.init("conf.json");
switch (view_type) {
default: { // init
throw (new Error("invalid view mode")); await _zeitbild.frontend_web.backend.init();
break; lib_plankton.zoo_page.init(
document.querySelector("main"),
{
"pool": {
"login": async (parameters, target_element) => {
target_element.innerHTML = await _zeitbild.frontend_web.helpers.template_coin(
"login",
{
"form": "",
} }
case "table": { );
const content : string = await _zeitbild.frontend_web.view.calendar_view_table_html( const form : lib_plankton.zoo_form.class_form<
{name : string; password : string;},
{name : string; password : string;}
> = 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 : string; password : string;}
>(
[
{
"name": "name",
"input": new lib_plankton.zoo_input.class_input_text(),
"label": "Name", // TODO: translate
},
{
"name": "password",
"input": new lib_plankton.zoo_input.class_input_password(),
"label": "Kennwort", // TODO: translate
},
]
),
[
{
"label": "Anmelden", // TODO: translate
"target": "submit",
"procedure": async (get_value, get_representation) => {
const value : any = await get_value();
try {
await _zeitbild.frontend_web.backend.session_begin(
value.name,
value.password
);
lib_plankton.zoo_page.set(
{
"name": "events",
"parameters": {}
}
);
}
catch (error) {
lib_plankton.zoo_page.set(
{
"name": "login",
"parameters": {
"name": value.name,
}
}
);
}
}
},
]
);
await form.setup(document.querySelector("#login"));
await form.input_write(
{
"name": (parameters.name ?? ""),
"password": "",
}
);
},
"logout": async (parameters, target_element) => {
await _zeitbild.frontend_web.backend.session_end(
);
lib_plankton.zoo_page.set(
{
"name": "login",
"parameters": {
}
}
);
},
"events": async (parameters, target_element) => {
const content = await _zeitbild.frontend_web.view.calendar_view_table_html(
{ {
"calendar_ids": null, "calendar_ids": null,
"from": { "from": {
@ -40,94 +116,21 @@ namespace _zeitbild.frontend_web
"timezone_shift": /*conf.timezone_shift*/0, "timezone_shift": /*conf.timezone_shift*/0,
} }
); );
target.innerHTML = content; target_element.innerHTML = content;
/* },
document.querySelectorAll(".tableview-sources-entry").forEach( },
(element) => { "fallback": {
element.addEventListener( "name": "login",
"click", "parameters": {}
(event) => {
const element_ : HTMLElement = (event.target as HTMLElement);
const calendar_id : type_calendar_id = parseInt(element_.getAttribute("rel") as string);
const active : boolean = element_.classList.toggle("tableview-sources-entry-active");
render(
conf,
lib_plankton.call.convey(
calendar_ids,
[
(x : Array<int>) => (
active
?
calendar_ids.concat([calendar_id])
:
calendar_ids.filter(y => (y !== calendar_id))
),
]
)
);
}
);
}
);
*/
break;
}
case "list": {
const content : string = await _zeitbild.frontend_web.view.calendar_view_list_html(
null,
{
"timezone_shift": /*conf.timezone_shift*/0,
}
);
target.innerHTML = content;
break;
} }
} }
return Promise.resolve<void>(undefined);
}
/**
*/
export async function main(
) : Promise<void>
{
// init
lib_plankton.log.conf_push(
[
lib_plankton.log.channel_make({"kind": "console", "data": {"threshold": "info"}}),
]
); );
lib_plankton.zoo_page.add_nav_entry({"name": "login", "parameters": {}});
// conf lib_plankton.zoo_page.add_nav_entry({"name": "events", "parameters": {}});
await _zeitbild.frontend_web.conf.init(("conf.json")); lib_plankton.zoo_page.add_nav_entry({"name": "logout", "parameters": {}});
// setup
await _zeitbild.frontend_web.backend.session_begin(
"alice",
"alice"
);
// args
/*
const url : URL = new URL(window.location.toString());
const calendar_ids : Array<type_calendar_id> = (
(url.searchParams.get("ids") !== null)
?
lib_plankton.call.convey(
url.searchParams.get("ids"),
[
(x : string) => lib_plankton.string.split(x, ","),
(x : Array<string>) => x.map(y => parseInt(y)),
]
)
:
(await _zeitbild.frontend_web.backend.calendar_list()).map(x => x.key)
);
*/
// exec // exec
await render(); lib_plankton.zoo_page.start();
return Promise.resolve<void>(undefined); return Promise.resolve<void>(undefined);
} }

View file

@ -1,12 +1,14 @@
/** /**
*/ */
namespace _zeitbild.frontend_web namespace _zeitbild.frontend_web.type
{ {
/** /**
*/ */
type type_role = ( export type role = (
"admin"
|
"editor" "editor"
| |
"viewer" "viewer"
@ -15,19 +17,24 @@ namespace _zeitbild.frontend_web
/** /**
*/ */
type type_user_id = int; export type user_id = int;
/** /**
*/ */
type type_user_object = { export type user_object = {
name : string; name : string;
email_address : (
null
|
string
);
}; };
/** /**
*/ */
export type type_event = { export type event_object = {
name : string; name : string;
begin : _zeitbild.frontend_web.helpers.type_datetime; begin : _zeitbild.frontend_web.helpers.type_datetime;
end : ( end : (
@ -50,54 +57,48 @@ namespace _zeitbild.frontend_web
/** /**
*/ */
export type type_calendar_id = int; export type resource_id = int;
/** /**
*/ */
export type type_calendar_object = ( export type resource_object = (
{ {
kind : "concrete"; kind : "local";
data : { data : {
name : string; events : Array<
private : boolean; event_object
users : Array<
{
id : type_user_id;
role : type_role;
}
>; >;
events : Array<type_event>;
}; };
} }
| |
{ {
kind : "caldav"; kind : "caldav";
data : { data : {
name : string;
private : boolean;
read_only : boolean; read_only : boolean;
source_url : string; url : string;
} };
} }
); );
/** /**
*/ */
export type type_datamodel = { export type calendar_id = int;
users : Array<
/**
*/
export type calendar_object = {
name : string;
public : boolean;
members : Array<
{ {
id : type_user_id; user_id : user_id;
object : type_user_object; role : role;
}
>;
calendars : Array<
{
id : type_calendar_id;
object : type_calendar_object;
} }
>; >;
resource_id : resource_id;
}; };
} }

View file

@ -8,7 +8,7 @@ namespace _zeitbild.frontend_web.view
*/ */
function event_generate_tooltip( function event_generate_tooltip(
calendar_name : string, calendar_name : string,
event : type_event event : _zeitbild.frontend_web.type.event_object
) : string ) : string
{ {
return ( return (
@ -137,7 +137,7 @@ namespace _zeitbild.frontend_web.view
calendar_ids : ( calendar_ids : (
null null
| |
Array<type_calendar_id> Array<_zeitbild.frontend_web.type.calendar_id>
), ),
from : { from : {
year : int; year : int;
@ -151,7 +151,7 @@ namespace _zeitbild.frontend_web.view
) : Promise< ) : Promise<
{ {
sources : lib_plankton.map.type_map< sources : lib_plankton.map.type_map<
type_calendar_id, _zeitbild.frontend_web.type.calendar_id,
{ {
name : string; name : string;
} }
@ -164,8 +164,8 @@ namespace _zeitbild.frontend_web.view
pit : _zeitbild.frontend_web.helpers.type_pit; pit : _zeitbild.frontend_web.helpers.type_pit;
entries : Array< entries : Array<
{ {
calendar_id : type_calendar_id; calendar_id : _zeitbild.frontend_web.type.calendar_id;
event : type_event; event : _zeitbild.frontend_web.type.event_object;
} }
>; >;
today : boolean; today : boolean;
@ -177,12 +177,6 @@ namespace _zeitbild.frontend_web.view
> >
{ {
const now_pit : _zeitbild.frontend_web.helpers.type_pit = _zeitbild.frontend_web.helpers.pit_now(); const now_pit : _zeitbild.frontend_web.helpers.type_pit = _zeitbild.frontend_web.helpers.pit_now();
/*
const calendar_object : type_calendar_object = calendar_read(
data,
calendar_id
);
*/
const from_pit : _zeitbild.frontend_web.helpers.type_pit = _zeitbild.frontend_web.helpers.pit_from_year_and_week( const from_pit : _zeitbild.frontend_web.helpers.type_pit = _zeitbild.frontend_web.helpers.pit_from_year_and_week(
(from as {year : int; week : int}).year, (from as {year : int; week : int}).year,
(from as {year : int; week : int}).week, (from as {year : int; week : int}).week,
@ -201,9 +195,9 @@ namespace _zeitbild.frontend_web.view
// prepare // prepare
const entries : Array< const entries : Array<
{ {
calendar_id : type_calendar_id; calendar_id : _zeitbild.frontend_web.type.calendar_id;
calendar_name : string; calendar_name : string;
event : type_event; event : _zeitbild.frontend_web.type.event_object;
} }
> = await _zeitbild.frontend_web.backend.events( > = await _zeitbild.frontend_web.backend.events(
from_pit, from_pit,
@ -214,7 +208,7 @@ namespace _zeitbild.frontend_web.view
); );
let result : { let result : {
sources : lib_plankton.map.type_map< sources : lib_plankton.map.type_map<
type_calendar_id, _zeitbild.frontend_web.type.calendar_id,
{ {
name : string; name : string;
} }
@ -227,8 +221,8 @@ namespace _zeitbild.frontend_web.view
pit : _zeitbild.frontend_web.helpers.type_pit; pit : _zeitbild.frontend_web.helpers.type_pit;
entries : Array< entries : Array<
{ {
calendar_id : type_calendar_id; calendar_id : _zeitbild.frontend_web.type.calendar_id;
event : type_event; event : _zeitbild.frontend_web.type.event_object;
} }
>; >;
today : boolean; today : boolean;
@ -264,8 +258,8 @@ namespace _zeitbild.frontend_web.view
pit : _zeitbild.frontend_web.helpers.type_pit; pit : _zeitbild.frontend_web.helpers.type_pit;
entries : Array< entries : Array<
{ {
calendar_id : type_calendar_id; calendar_id : _zeitbild.frontend_web.type.calendar_id;
event : type_event; event : _zeitbild.frontend_web.type.event_object;
} }
>; >;
today : boolean; today : boolean;
@ -373,7 +367,7 @@ namespace _zeitbild.frontend_web.view
calendar_ids ?: ( calendar_ids ?: (
null null
| |
Array<type_calendar_id> Array<_zeitbild.frontend_web.type.calendar_id>
); );
from ?: { from ?: {
year : int; year : int;
@ -419,7 +413,7 @@ namespace _zeitbild.frontend_web.view
); );
const stuff : { const stuff : {
sources : lib_plankton.map.type_map< sources : lib_plankton.map.type_map<
type_calendar_id, _zeitbild.frontend_web.type.calendar_id,
{ {
name : string; name : string;
} }
@ -432,8 +426,8 @@ namespace _zeitbild.frontend_web.view
pit : _zeitbild.frontend_web.helpers.type_pit; pit : _zeitbild.frontend_web.helpers.type_pit;
entries : Array< entries : Array<
{ {
calendar_id : type_calendar_id; calendar_id : _zeitbild.frontend_web.type.calendar_id;
event : type_event; event : _zeitbild.frontend_web.type.event_object;
} }
>; >;
today : boolean; today : boolean;
@ -448,7 +442,7 @@ namespace _zeitbild.frontend_web.view
options.timezone_shift options.timezone_shift
); );
const sources : lib_plankton.map.type_map< const sources : lib_plankton.map.type_map<
type_calendar_id, _zeitbild.frontend_web.type.calendar_id,
{ {
name : string; name : string;
color : lib_plankton.color.type_color; color : lib_plankton.color.type_color;
@ -588,7 +582,7 @@ namespace _zeitbild.frontend_web.view
/** /**
*/ */
async function calendar_view_list_data( async function calendar_view_list_data(
calendar_ids : Array<type_calendar_id>, calendar_ids : Array<_zeitbild.frontend_web.type.calendar_id>,
options : { options : {
from ?: _zeitbild.frontend_web.helpers.type_pit; from ?: _zeitbild.frontend_web.helpers.type_pit;
to ?: _zeitbild.frontend_web.helpers.type_pit; to ?: _zeitbild.frontend_web.helpers.type_pit;
@ -597,8 +591,8 @@ namespace _zeitbild.frontend_web.view
) : Promise< ) : Promise<
Array< Array<
{ {
calendar_id : type_calendar_id; calendar_id : _zeitbild.frontend_web.type.calendar_id;
event : type_event; event : _zeitbild.frontend_web.type.event_object;
} }
> >
> >
@ -625,8 +619,8 @@ namespace _zeitbild.frontend_web.view
const entries : Array< const entries : Array<
{ {
calendar_id : type_calendar_id; calendar_id : _zeitbild.frontend_web.type.calendar_id;
event : type_event; event : _zeitbild.frontend_web.type.event_object;
} }
> = await _zeitbild.frontend_web.backend.events( > = await _zeitbild.frontend_web.backend.events(
(options.from as _zeitbild.frontend_web.helpers.type_pit), (options.from as _zeitbild.frontend_web.helpers.type_pit),
@ -651,7 +645,7 @@ namespace _zeitbild.frontend_web.view
/** /**
*/ */
export async function calendar_view_list_html( export async function calendar_view_list_html(
calendar_ids : Array<type_calendar_id>, calendar_ids : Array<_zeitbild.frontend_web.type.calendar_id>,
options : { options : {
from ?: _zeitbild.frontend_web.helpers.type_pit; from ?: _zeitbild.frontend_web.helpers.type_pit;
to ?: _zeitbild.frontend_web.helpers.type_pit; to ?: _zeitbild.frontend_web.helpers.type_pit;
@ -661,8 +655,8 @@ namespace _zeitbild.frontend_web.view
{ {
const stuff : Array< const stuff : Array<
{ {
calendar_id : type_calendar_id; calendar_id : _zeitbild.frontend_web.type.calendar_id;
event : type_event; event : _zeitbild.frontend_web.type.event_object;
} }
> = await calendar_view_list_data( > = await calendar_view_list_data(
calendar_ids, calendar_ids,

View file

@ -4,6 +4,29 @@ html {
font-family: sans-serif; font-family: sans-serif;
} }
header {
border-bottom: 2px solid #888;
padding-bottom: 16px;
margin-bottom: 16px;
}
nav > ul {
list-style-type: none;
margin: 0;
padding: 0;
}
nav > ul > li {
display: inline-block;
margin: 16px;
padding: 8px;
}
a {
text-decoration: none;
color: hsl(0, 0%, 100%);
}
.calendar { .calendar {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -82,3 +105,13 @@ html {
font-weight: bold; font-weight: bold;
cursor: pointer; cursor: pointer;
} }
.plankton_input_group_field {
margin-bottom: 8px;
}
.plankton_input_group_field_label {
display: block;
font-weight: bold;
font-size: 0.8em;
}

View file

@ -0,0 +1,2 @@
<div id="login">
</div>

View file

@ -6,20 +6,22 @@ dir=lib/plankton
modules="" modules=""
modules="${modules} base" modules="${modules} base"
modules="${modules} conf"
modules="${modules} call" modules="${modules} call"
modules="${modules} storage"
modules="${modules} file" modules="${modules} file"
modules="${modules} json" modules="${modules} json"
modules="${modules} args"
modules="${modules} string" modules="${modules} string"
modules="${modules} color" modules="${modules} color"
modules="${modules} xml" modules="${modules} xml"
modules="${modules} map" modules="${modules} map"
# modules="${modules} ical"
modules="${modules} http" modules="${modules} http"
modules="${modules} log" modules="${modules} log"
modules="${modules} url" modules="${modules} url"
modules="${modules} conf"
modules="${modules} www_form" modules="${modules} www_form"
modules="${modules} zoo-page"
modules="${modules} zoo-form"
modules="${modules} zoo-input"
## exec ## exec