From eee0a8dba2fb9d68839beb1897e7e297b8d2e70a Mon Sep 17 00:00:00 2001 From: Dietrich Date: Mon, 14 Jun 2021 20:59:01 +0200 Subject: [PATCH] WIP: make the interface aware of the admin role --- app/src/navigation.rs | 43 +++++++++------ app/src/pages/list_users.rs | 97 ++++++++++++++++++++++----------- locales/de/main.ftl | 3 + locales/en/main.ftl | 3 + pslink/src/queries.rs | 8 +-- pslink/static/admin.css | 8 +++ shared/src/apirequests/users.rs | 14 ++++- 7 files changed, 120 insertions(+), 56 deletions(-) diff --git a/app/src/navigation.rs b/app/src/navigation.rs index 9663656..8b1478c 100644 --- a/app/src/navigation.rs +++ b/app/src/navigation.rs @@ -1,6 +1,9 @@ use fluent::fluent_args; -use seed::{a, attrs, div, li, nav, ol, prelude::*, Url, C}; -use shared::datatypes::{Lang, User}; +use seed::{a, attrs, div, li, nav, nodes, ol, prelude::*, Url, C}; +use shared::{ + apirequests::users::Role, + datatypes::{Lang, User}, +}; use crate::{i18n::I18n, Msg}; @@ -33,21 +36,27 @@ pub fn navigation(i18n: &I18n, base_url: &Url, user: &User) -> Node { )), t("add-link"), ],], - // A button to create a new user - li![a![ - attrs! {At::Href => crate::Urls::new(base_url).create_user()}, - ev(Ev::Click, |_| Msg::ListUsers( - super::pages::list_users::Msg::Edit( - super::pages::list_users::UserEditMsg::CreateNewUser - ) - )), - t("invite-user"), - ],], - // A button to list all users - li![a![ - attrs! {At::Href => crate::Urls::new(base_url).list_users()}, - t("list-users"), - ],], + if user.role == Role::Admin { + nodes![ + // A button to create a new user + li![a![ + attrs! {At::Href => crate::Urls::new(base_url).create_user()}, + ev(Ev::Click, |_| Msg::ListUsers( + super::pages::list_users::Msg::Edit( + super::pages::list_users::UserEditMsg::CreateNewUser + ) + )), + t("invite-user"), + ],], + // A button to list all users + li![a![ + attrs! {At::Href => crate::Urls::new(base_url).list_users()}, + t("list-users"), + ],], + ] + } else { + nodes!() + }, ], ol![ li![div![ diff --git a/app/src/pages/list_users.rs b/app/src/pages/list_users.rs index 4af2958..c90de5b 100644 --- a/app/src/pages/list_users.rs +++ b/app/src/pages/list_users.rs @@ -1,5 +1,3 @@ -use std::cell::RefCell; - use enum_map::EnumMap; use seed::{ a, attrs, button, div, h1, input, log, p, prelude::*, section, table, td, th, tr, Url, C, IF, @@ -8,7 +6,7 @@ use shared::{ apirequests::general::{Operation, Ordering}, apirequests::{ general::{EditMode, Status}, - users::{UserDelta, UserOverviewColumns, UserRequestForm}, + users::{Role, UserDelta, UserOverviewColumns, UserRequestForm}, }, datatypes::{Lang, User}, }; @@ -20,7 +18,7 @@ use crate::{i18n::I18n, unwrap_or_return}; pub fn init(mut url: Url, orders: &mut impl Orders, i18n: I18n) -> Model { orders.send_msg(Msg::Query(UserQueryMsg::Fetch)); let user_edit = match url.next_path_part() { - Some("create_user") => Some(RefCell::new(UserDelta::default())), + Some("create_user") => Some(UserDelta::default()), None | Some(_) => None, }; Model { @@ -38,7 +36,7 @@ pub struct Model { i18n: I18n, formconfig: UserRequestForm, inputs: EnumMap, - user_edit: Option>, + user_edit: Option, last_message: Option, } @@ -87,6 +85,8 @@ pub enum UserEditMsg { EditUsernameChanged(String), EditEmailChanged(String), EditPasswordChanged(String), + MakeAdmin(UserDelta), + MakeRegular(UserDelta), SaveUser, FailedToCreateUser, } @@ -203,38 +203,32 @@ pub fn process_user_edit_messages( match msg { UserEditMsg::EditUserSelected(user) => { model.clean_dialogs(); - model.user_edit = Some(RefCell::new(user)) + model.user_edit = Some(user) } UserEditMsg::CreateNewUser => { model.clean_dialogs(); - model.user_edit = Some(RefCell::new(UserDelta::default())) + model.user_edit = Some(UserDelta::default()) } UserEditMsg::EditUsernameChanged(s) => { - if let Some(ref ue) = model.user_edit { - ue.try_borrow_mut() - .expect("Failed to borrow mutably") - .username = s; + if let Some(ref mut ue) = model.user_edit { + ue.username = s; }; } UserEditMsg::EditEmailChanged(s) => { - if let Some(ref ue) = model.user_edit { - ue.try_borrow_mut().expect("Failed to borrow mutably").email = s; + if let Some(ref mut ue) = model.user_edit { + ue.email = s; }; } UserEditMsg::EditPasswordChanged(s) => { - if let Some(ref ue) = model.user_edit { - ue.try_borrow_mut() - .expect("Failed to borrow mutably") - .password = Some(s); + if let Some(ref mut ue) = model.user_edit { + ue.password = Some(s); }; } UserEditMsg::SaveUser => { let data = model .user_edit - .as_ref() - .expect("Somehow a user should exist!") - .borrow() - .clone(); // complicated way to move into the closure + .take() + .expect("A user should allways be there on save"); // complicated way to move into the closure log!("Saving User: ", &data.username); save_user(data, orders); } @@ -247,6 +241,8 @@ pub fn process_user_edit_messages( model.user_edit = None; orders.send_msg(Msg::Query(UserQueryMsg::Fetch)); } + UserEditMsg::MakeAdmin(user) => todo!(), + UserEditMsg::MakeRegular(user) => todo!(), } } @@ -315,7 +311,7 @@ pub fn view(model: &Model) -> Node { // Add filter fields right below the headlines view_user_table_filter_input(model, &t), // Add all the users one line for each - model.users.iter().map(view_user) + model.users.iter().map(|u| { view_user(u, &t) }) ], // A refresh button. This will be removed in future versions. button![ @@ -324,7 +320,7 @@ pub fn view(model: &Model) -> Node { ], // Display the user edit dialog if available if let Some(l) = &model.user_edit { - edit_or_create_user(l, t) + edit_or_create_user(l.clone(), t) } else { section!() }, @@ -351,6 +347,7 @@ fn view_user_table_head String>(t: F) -> Node { ))), t("username") ], + th![t("role")], ] } @@ -392,30 +389,64 @@ fn view_user_table_filter_input String>(model: &Model, t: F) -> N }), el_ref(&model.inputs[UserOverviewColumns::Username].filter_input), ]], + td![], ] } -fn view_user(l: &User) -> Node { +fn view_user String>(l: &User, t: F) -> Node { let user = UserDelta::from(l.clone()); tr![ - ev(Ev::Click, |_| Msg::Edit(UserEditMsg::EditUserSelected( - user - ))), + { + let user = user.clone(); + ev(Ev::Click, |_| { + Msg::Edit(UserEditMsg::EditUserSelected(user)) + }) + }, + match l.role { + Role::NotAuthenticated | Role::Disabled => C!("inactive"), + Role::Regular => C!("regular"), + Role::Admin => C!("admin"), + }, td![&l.id], td![&l.email], td![&l.username], + match l.role { + Role::NotAuthenticated | Role::Disabled | Role::Regular => td![ + ev(Ev::Click, |_| Msg::Edit(UserEditMsg::EditUserSelected( + user + ))), + t("make-user-admin") + ], + Role::Admin => td![ + ev(Ev::Click, |_| Msg::Edit(UserEditMsg::EditUserSelected( + user + ))), + t("make-user-regular"), + ], + } ] } -fn edit_or_create_user String>(l: &RefCell, t: F) -> Node { - let user = l.borrow(); +fn edit_or_create_user String>(l: UserDelta, t: F) -> Node { + let user = l; + let headline: Node = match &user.role { + Role::NotAuthenticated | Role::Disabled | Role::Regular => { + h1![match &user.edit { + EditMode::Edit => t("edit-user"), + EditMode::Create => t("new-user"), + }] + } + Role::Admin => { + h1![match &user.edit { + EditMode::Edit => t("edit-admin"), + EditMode::Create => t("new-admin"), + }] + } + }; div![ C!["editdialog", "center"], close_button(), - h1![match &user.edit { - EditMode::Edit => t("edit-user"), - EditMode::Create => t("new-user"), - }], + headline, table![ tr![ th![ diff --git a/locales/de/main.ftl b/locales/de/main.ftl index 9e61b30..4da9906 100644 --- a/locales/de/main.ftl +++ b/locales/de/main.ftl @@ -36,10 +36,13 @@ password = Passwort leave-password-empty-hint = Leer lassen um das Passwort nicht zu ändern save-user = Benutzer speichern edit-user = Benutzer editieren +edit-admin = Administrator editieren create-user = Benutzer Erstellen new-user = Neuer Benutzer +new-admin = Neuer Administrator make-user-admin = Zum Administrator befördern make-user-regular = Zurückstufen zum normalen Nutzer +role = Rolle userid = Benutzernummer statistics = Statistik \ No newline at end of file diff --git a/locales/en/main.ftl b/locales/en/main.ftl index 9f4054c..872e5d9 100644 --- a/locales/en/main.ftl +++ b/locales/en/main.ftl @@ -37,10 +37,13 @@ password = Password leave-password-empty-hint = Leave this empty to keep the current password save-user = Save this user edit-user = Edit this user +edit-admin = Edit this administrator new-user = Neuer Benutzer create-user = Create user +create-admin = Create administrator make-user-admin = Promote to admin make-user-regular = Demote to regular +role = Role userid = User ID statistics = Statistics \ No newline at end of file diff --git a/pslink/src/queries.rs b/pslink/src/queries.rs index 691ea47..cf76b1e 100644 --- a/pslink/src/queries.rs +++ b/pslink/src/queries.rs @@ -32,12 +32,12 @@ pub enum RoleGuard { } impl RoleGuard { - fn create(user: User) -> Self { + fn create(user: &User) -> Self { match user.role { shared::apirequests::users::Role::NotAuthenticated => Self::NotAuthenticated, shared::apirequests::users::Role::Disabled => Self::Disabled, - shared::apirequests::users::Role::Regular => Self::Regular { user }, - shared::apirequests::users::Role::Admin => Self::Admin { user }, + shared::apirequests::users::Role::Regular => Self::Regular { user: user.clone() }, + shared::apirequests::users::Role::Admin => Self::Admin { user: user.clone() }, } } /// Determin if the user is admin or the given user id is his own. This is used for things where users can edit or view their own entries, whereas admins can do so for all entries. @@ -64,7 +64,7 @@ pub async fn authenticate( let user = User::get_user_by_name(&username, server_config).await?; info!("Found user {:?}", user); - return Ok(RoleGuard::create(user)); + return Ok(RoleGuard::create(&user)); } Ok(RoleGuard::NotAuthenticated) } diff --git a/pslink/static/admin.css b/pslink/static/admin.css index d017efc..2b6ce93 100644 --- a/pslink/static/admin.css +++ b/pslink/static/admin.css @@ -76,6 +76,14 @@ table tr:nth-child(odd) { background-color: #fff; } +table tr:nth-child(even).admin { + background-color: rgb(240, 142, 142); +} + +table tr:nth-child(odd).admin { + background-color: rgb(255, 204, 169); +} + table tr.filters input { background-image: url("/static/search.svg"); background-repeat: no-repeat; diff --git a/shared/src/apirequests/users.rs b/shared/src/apirequests/users.rs index 9b35cb8..5bef4ce 100644 --- a/shared/src/apirequests/users.rs +++ b/shared/src/apirequests/users.rs @@ -39,6 +39,7 @@ pub struct UserDelta { pub username: String, pub email: String, pub password: Option, + pub role: Role, } impl From for UserDelta { @@ -50,6 +51,7 @@ impl From for UserDelta { username: u.username, email: u.email, password: None, + role: u.role, } } } @@ -73,7 +75,8 @@ pub enum Role { } impl Role { - pub fn convert(i: i64) -> Self { + #[must_use] + pub const fn convert(i: i64) -> Self { match i { 0 => Self::Disabled, 1 => Self::Regular, @@ -82,7 +85,8 @@ impl Role { } } - pub fn to_i64(&self) -> i64 { + #[must_use] + pub const fn to_i64(self) -> i64 { match self { Role::NotAuthenticated => 3, Role::Disabled => 0, @@ -91,3 +95,9 @@ impl Role { } } } + +impl Default for Role { + fn default() -> Self { + Self::Regular + } +}