diff --git a/crates/app/src/public/html/littleweb/browser.lisp b/crates/app/src/public/html/littleweb/browser.lisp index 6da038f..e3e201d 100644 --- a/crates/app/src/public/html/littleweb/browser.lisp +++ b/crates/app/src/public/html/littleweb/browser.lisp @@ -32,7 +32,7 @@ (input ("type" "uri") ("class" "w-full") - ("true_value" "{{ path }}") + ("true_value" "") ("name" "uri") ("id" "uri")) @@ -121,10 +121,6 @@ uri = `${uri}/index.html`; } - if (!uri.startsWith(\"atto://\")) { - uri = `atto://${uri}`; - } - // ... console.log(\"navigate\", uri); document.getElementById(\"browser_iframe\").src = `{{ config.lw_host|safe }}/api/v1/net/${uri}`; @@ -152,7 +148,8 @@ if (data.event === \"change_url\") { const uri = new URL(data.target).pathname.slice(\"/api/v1/net/\".length); window.history.pushState(null, null, `/net/${uri.replace(\"atto://\", \"\")}`); - document.getElementById(\"uri\").setAttribute(\"true_value\", uri); + document.getElementById(\"uri\").setAttribute(\"true_value\", `atto://${uri}`); + document.getElementById(\"uri\").value = uri.split(\"/\")[0]; } }); @@ -207,6 +204,7 @@ is_focused = false; }); - document.getElementById(\"uri\").value = document.getElementById(\"uri\").getAttribute(\"true_value\").replace(\"atto://\", \"\").split(\"/\")[0]")) + // navigate + littleweb_navigate(\"{{ path|safe }}\");")) (text "{% endblock %}") diff --git a/crates/app/src/public/html/littleweb/domains.lisp b/crates/app/src/public/html/littleweb/domains.lisp index 1a9b649..e3a6c10 100644 --- a/crates/app/src/public/html/littleweb/domains.lisp +++ b/crates/app/src/public/html/littleweb/domains.lisp @@ -5,6 +5,17 @@ (text "{% endblock %} {% block body %} {{ macros::nav() }}") (main ("class" "flex flex-col gap-2") + + ; viewing other user's domains warning + (text "{% if profile.id != user.id -%}") + (div + ("class" "card w-full red flex gap-2 items-center") + (text "{{ icon \"skull\" }}") + (b + (text "Viewing other user's domains! Please be careful."))) + (text "{%- endif %}") + + ; ... (text "{% if user -%}") (div ("class" "pillmenu") diff --git a/crates/app/src/public/html/littleweb/services.lisp b/crates/app/src/public/html/littleweb/services.lisp index cca5af7..83a6179 100644 --- a/crates/app/src/public/html/littleweb/services.lisp +++ b/crates/app/src/public/html/littleweb/services.lisp @@ -5,6 +5,17 @@ (text "{% endblock %} {% block body %} {{ macros::nav() }}") (main ("class" "flex flex-col gap-2") + + ; viewing other user's services warning + (text "{% if profile.id != user.id -%}") + (div + ("class" "card w-full red flex gap-2 items-center") + (text "{{ icon \"skull\" }}") + (b + (text "Viewing other user's sites! Please be careful."))) + (text "{%- endif %}") + + ; ... (text "{% if user -%}") (div ("class" "pillmenu") diff --git a/crates/app/src/public/html/mod/profile.lisp b/crates/app/src/public/html/mod/profile.lisp index 9fb5ebf..529228a 100644 --- a/crates/app/src/public/html/mod/profile.lisp +++ b/crates/app/src/public/html/mod/profile.lisp @@ -50,6 +50,18 @@ (span ("class" "notification") (text "{{ profile.request_count }}"))) + (a + ("href" "/services?id={{ profile.id }}") + ("class" "button lowered") + (icon (text "globe")) + (span + (text "Sites"))) + (a + ("href" "/domains?id={{ profile.id }}") + ("class" "button lowered") + (icon (text "globe")) + (span + (text "Domains"))) (button ("class" "red lowered") ("onclick" "delete_account(event)") @@ -155,6 +167,33 @@ }); }; + globalThis.update_user_secondary_role = async (new_role) => { + if ( + !(await trigger(\"atto::confirm\", [ + \"Are you sure you would like to do this?\", + ])) + ) { + return; + } + + fetch(`/api/v1/auth/user/{{ profile.id }}/role/2`, { + method: \"POST\", + headers: { + \"Content-Type\": \"application/json\", + }, + body: JSON.stringify({ + role: Number.parseInt(new_role), + }), + }) + .then((res) => res.json()) + .then((res) => { + trigger(\"atto::toast\", [ + res.ok ? \"success\" : \"error\", + res.message, + ]); + }); + }; + ui.refresh_container(element, [\"actions\"]); setTimeout(() => { @@ -178,6 +217,11 @@ \"{{ profile.permissions }}\", \"input\", ], + [ + [\"secondary_role\", \"Secondary permission level\"], + \"{{ profile.secondary_permissions }}\", + \"input\", + ], ], null, { @@ -194,6 +238,9 @@ role: (new_role) => { return update_user_role(new_role); }, + secondary_role: (new_role) => { + return update_user_secondary_role(new_role); + }, }, ); }, 100); @@ -244,6 +291,24 @@ (div ("class" "card lowered flex flex-col gap-2") ("id" "permission_builder"))) + (div + ("class" "card-nest w-full") + (div + ("class" "card small flex items-center justify-between gap-2") + (div + ("class" "flex items-center gap-2") + (text "{{ icon \"blocks\" }}") + (span + (text "{{ text \"mod_panel:label.permissions_level_builder\" }}"))) + (button + ("class" "small lowered") + ("onclick" "update_user_secondary_role(Number.parseInt(document.getElementById('role').value))") + (text "{{ icon \"check\" }}") + (span + (text "{{ text \"general:action.save\" }}")))) + (div + ("class" "card lowered flex flex-col gap-2") + ("id" "secondary_permission_builder"))) (script (text "setTimeout(async () => { const get_permissions_html = await trigger( @@ -291,6 +356,30 @@ Number.parseInt(\"{{ profile.permissions }}\"), \"permission_builder\", ); + }, 250); + + setTimeout(async () => { + const get_permissions_html = await trigger( + \"ui::generate_permissions_ui\", + [ + { + // https://trisuaso.github.io/tetratto/tetratto/model/permissions/struct.SecondaryPermission.html + DEFAULT: 1 << 0, + ADMINISTRATOR: 1 << 1, + MANAGE_DOMAINS: 1 << 2, + MANAGE_SECONDARY: 1 << 3, + }, + \"secondary_role\", + \"add_permission_to_secondary_role\", + \"remove_permission_to_secondary_role\", + ], + ); + + document.getElementById(\"secondary_permission_builder\").innerHTML = + get_permissions_html( + Number.parseInt(\"{{ profile.secondary_permissions }}\"), + \"secondary_permission_builder\", + ); }, 250);"))) (text "{% endblock %}") diff --git a/crates/app/src/public/js/atto.js b/crates/app/src/public/js/atto.js index 1b2a4db..f67cd2c 100644 --- a/crates/app/src/public/js/atto.js +++ b/crates/app/src/public/js/atto.js @@ -1069,7 +1069,13 @@ ${option.input_element_type === "textarea" ? `${option.value}` : ""} // permissions ui self.define( "generate_permissions_ui", - (_, permissions, field_id = "role") => { + ( + _, + permissions, + field_id = "role", + add_name = "add_permission_to_role", + remove_name = "remove_permission_from_role", + ) => { function all_matching_permissions(role) { const matching = []; const not_matching = []; @@ -1099,7 +1105,7 @@ ${option.input_element_type === "textarea" ? `${option.value}` : ""} function get_permissions_html(role, id) { const [matching, not_matching] = all_matching_permissions(role); - globalThis.remove_permission_from_role = (permission) => { + globalThis[remove_name] = (permission) => { matching.splice(matching.indexOf(permission), 1); not_matching.push(permission); @@ -1107,7 +1113,7 @@ ${option.input_element_type === "textarea" ? `${option.value}` : ""} get_permissions_html(rebuild_role(matching), id); }; - globalThis.add_permission_to_role = (permission) => { + globalThis[add_name] = (permission) => { not_matching.splice(not_matching.indexOf(permission), 1); matching.push(permission); @@ -1120,14 +1126,14 @@ ${option.input_element_type === "textarea" ? `${option.value}` : ""} for (const match of matching) { permissions_html += `
${permissions[match]}
-
+
${permissions[match]}
-
+