WIP: make the interface aware of the admin role

This commit is contained in:
Dietrich 2021-06-14 20:59:01 +02:00 committed by Franz Dietrich
parent 61afbecda9
commit eee0a8dba2
7 changed files with 120 additions and 56 deletions

View File

@ -1,6 +1,9 @@
use fluent::fluent_args; use fluent::fluent_args;
use seed::{a, attrs, div, li, nav, ol, prelude::*, Url, C}; use seed::{a, attrs, div, li, nav, nodes, ol, prelude::*, Url, C};
use shared::datatypes::{Lang, User}; use shared::{
apirequests::users::Role,
datatypes::{Lang, User},
};
use crate::{i18n::I18n, Msg}; use crate::{i18n::I18n, Msg};
@ -33,21 +36,27 @@ pub fn navigation(i18n: &I18n, base_url: &Url, user: &User) -> Node<Msg> {
)), )),
t("add-link"), t("add-link"),
],], ],],
// A button to create a new user if user.role == Role::Admin {
li![a![ nodes![
attrs! {At::Href => crate::Urls::new(base_url).create_user()}, // A button to create a new user
ev(Ev::Click, |_| Msg::ListUsers( li![a![
super::pages::list_users::Msg::Edit( attrs! {At::Href => crate::Urls::new(base_url).create_user()},
super::pages::list_users::UserEditMsg::CreateNewUser 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 t("invite-user"),
li![a![ ],],
attrs! {At::Href => crate::Urls::new(base_url).list_users()}, // A button to list all users
t("list-users"), li![a![
],], attrs! {At::Href => crate::Urls::new(base_url).list_users()},
t("list-users"),
],],
]
} else {
nodes!()
},
], ],
ol![ ol![
li![div![ li![div![

View File

@ -1,5 +1,3 @@
use std::cell::RefCell;
use enum_map::EnumMap; use enum_map::EnumMap;
use seed::{ use seed::{
a, attrs, button, div, h1, input, log, p, prelude::*, section, table, td, th, tr, Url, C, IF, 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::{Operation, Ordering},
apirequests::{ apirequests::{
general::{EditMode, Status}, general::{EditMode, Status},
users::{UserDelta, UserOverviewColumns, UserRequestForm}, users::{Role, UserDelta, UserOverviewColumns, UserRequestForm},
}, },
datatypes::{Lang, User}, datatypes::{Lang, User},
}; };
@ -20,7 +18,7 @@ use crate::{i18n::I18n, unwrap_or_return};
pub fn init(mut url: Url, orders: &mut impl Orders<Msg>, i18n: I18n) -> Model { pub fn init(mut url: Url, orders: &mut impl Orders<Msg>, i18n: I18n) -> Model {
orders.send_msg(Msg::Query(UserQueryMsg::Fetch)); orders.send_msg(Msg::Query(UserQueryMsg::Fetch));
let user_edit = match url.next_path_part() { 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, None | Some(_) => None,
}; };
Model { Model {
@ -38,7 +36,7 @@ pub struct Model {
i18n: I18n, i18n: I18n,
formconfig: UserRequestForm, formconfig: UserRequestForm,
inputs: EnumMap<UserOverviewColumns, FilterInput>, inputs: EnumMap<UserOverviewColumns, FilterInput>,
user_edit: Option<RefCell<UserDelta>>, user_edit: Option<UserDelta>,
last_message: Option<Status>, last_message: Option<Status>,
} }
@ -87,6 +85,8 @@ pub enum UserEditMsg {
EditUsernameChanged(String), EditUsernameChanged(String),
EditEmailChanged(String), EditEmailChanged(String),
EditPasswordChanged(String), EditPasswordChanged(String),
MakeAdmin(UserDelta),
MakeRegular(UserDelta),
SaveUser, SaveUser,
FailedToCreateUser, FailedToCreateUser,
} }
@ -203,38 +203,32 @@ pub fn process_user_edit_messages(
match msg { match msg {
UserEditMsg::EditUserSelected(user) => { UserEditMsg::EditUserSelected(user) => {
model.clean_dialogs(); model.clean_dialogs();
model.user_edit = Some(RefCell::new(user)) model.user_edit = Some(user)
} }
UserEditMsg::CreateNewUser => { UserEditMsg::CreateNewUser => {
model.clean_dialogs(); model.clean_dialogs();
model.user_edit = Some(RefCell::new(UserDelta::default())) model.user_edit = Some(UserDelta::default())
} }
UserEditMsg::EditUsernameChanged(s) => { UserEditMsg::EditUsernameChanged(s) => {
if let Some(ref ue) = model.user_edit { if let Some(ref mut ue) = model.user_edit {
ue.try_borrow_mut() ue.username = s;
.expect("Failed to borrow mutably")
.username = s;
}; };
} }
UserEditMsg::EditEmailChanged(s) => { UserEditMsg::EditEmailChanged(s) => {
if let Some(ref ue) = model.user_edit { if let Some(ref mut ue) = model.user_edit {
ue.try_borrow_mut().expect("Failed to borrow mutably").email = s; ue.email = s;
}; };
} }
UserEditMsg::EditPasswordChanged(s) => { UserEditMsg::EditPasswordChanged(s) => {
if let Some(ref ue) = model.user_edit { if let Some(ref mut ue) = model.user_edit {
ue.try_borrow_mut() ue.password = Some(s);
.expect("Failed to borrow mutably")
.password = Some(s);
}; };
} }
UserEditMsg::SaveUser => { UserEditMsg::SaveUser => {
let data = model let data = model
.user_edit .user_edit
.as_ref() .take()
.expect("Somehow a user should exist!") .expect("A user should allways be there on save"); // complicated way to move into the closure
.borrow()
.clone(); // complicated way to move into the closure
log!("Saving User: ", &data.username); log!("Saving User: ", &data.username);
save_user(data, orders); save_user(data, orders);
} }
@ -247,6 +241,8 @@ pub fn process_user_edit_messages(
model.user_edit = None; model.user_edit = None;
orders.send_msg(Msg::Query(UserQueryMsg::Fetch)); 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<Msg> {
// Add filter fields right below the headlines // Add filter fields right below the headlines
view_user_table_filter_input(model, &t), view_user_table_filter_input(model, &t),
// Add all the users one line for each // 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. // A refresh button. This will be removed in future versions.
button![ button![
@ -324,7 +320,7 @@ pub fn view(model: &Model) -> Node<Msg> {
], ],
// Display the user edit dialog if available // Display the user edit dialog if available
if let Some(l) = &model.user_edit { if let Some(l) = &model.user_edit {
edit_or_create_user(l, t) edit_or_create_user(l.clone(), t)
} else { } else {
section!() section!()
}, },
@ -351,6 +347,7 @@ fn view_user_table_head<F: Fn(&str) -> String>(t: F) -> Node<Msg> {
))), ))),
t("username") t("username")
], ],
th![t("role")],
] ]
} }
@ -392,30 +389,64 @@ fn view_user_table_filter_input<F: Fn(&str) -> String>(model: &Model, t: F) -> N
}), }),
el_ref(&model.inputs[UserOverviewColumns::Username].filter_input), el_ref(&model.inputs[UserOverviewColumns::Username].filter_input),
]], ]],
td![],
] ]
} }
fn view_user(l: &User) -> Node<Msg> { fn view_user<F: Fn(&str) -> String>(l: &User, t: F) -> Node<Msg> {
let user = UserDelta::from(l.clone()); let user = UserDelta::from(l.clone());
tr![ 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.id],
td![&l.email], td![&l.email],
td![&l.username], 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<F: Fn(&str) -> String>(l: &RefCell<UserDelta>, t: F) -> Node<Msg> { fn edit_or_create_user<F: Fn(&str) -> String>(l: UserDelta, t: F) -> Node<Msg> {
let user = l.borrow(); let user = l;
let headline: Node<Msg> = 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![ div![
C!["editdialog", "center"], C!["editdialog", "center"],
close_button(), close_button(),
h1![match &user.edit { headline,
EditMode::Edit => t("edit-user"),
EditMode::Create => t("new-user"),
}],
table![ table![
tr![ tr![
th![ th![

View File

@ -36,10 +36,13 @@ password = Passwort
leave-password-empty-hint = Leer lassen um das Passwort nicht zu ändern leave-password-empty-hint = Leer lassen um das Passwort nicht zu ändern
save-user = Benutzer speichern save-user = Benutzer speichern
edit-user = Benutzer editieren edit-user = Benutzer editieren
edit-admin = Administrator editieren
create-user = Benutzer Erstellen create-user = Benutzer Erstellen
new-user = Neuer Benutzer new-user = Neuer Benutzer
new-admin = Neuer Administrator
make-user-admin = Zum Administrator befördern make-user-admin = Zum Administrator befördern
make-user-regular = Zurückstufen zum normalen Nutzer make-user-regular = Zurückstufen zum normalen Nutzer
role = Rolle
userid = Benutzernummer userid = Benutzernummer
statistics = Statistik statistics = Statistik

View File

@ -37,10 +37,13 @@ password = Password
leave-password-empty-hint = Leave this empty to keep the current password leave-password-empty-hint = Leave this empty to keep the current password
save-user = Save this user save-user = Save this user
edit-user = Edit this user edit-user = Edit this user
edit-admin = Edit this administrator
new-user = Neuer Benutzer new-user = Neuer Benutzer
create-user = Create user create-user = Create user
create-admin = Create administrator
make-user-admin = Promote to admin make-user-admin = Promote to admin
make-user-regular = Demote to regular make-user-regular = Demote to regular
role = Role
userid = User ID userid = User ID
statistics = Statistics statistics = Statistics

View File

@ -32,12 +32,12 @@ pub enum RoleGuard {
} }
impl RoleGuard { impl RoleGuard {
fn create(user: User) -> Self { fn create(user: &User) -> Self {
match user.role { match user.role {
shared::apirequests::users::Role::NotAuthenticated => Self::NotAuthenticated, shared::apirequests::users::Role::NotAuthenticated => Self::NotAuthenticated,
shared::apirequests::users::Role::Disabled => Self::Disabled, shared::apirequests::users::Role::Disabled => Self::Disabled,
shared::apirequests::users::Role::Regular => Self::Regular { user }, shared::apirequests::users::Role::Regular => Self::Regular { user: user.clone() },
shared::apirequests::users::Role::Admin => Self::Admin { user }, 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. /// 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?; let user = User::get_user_by_name(&username, server_config).await?;
info!("Found user {:?}", user); info!("Found user {:?}", user);
return Ok(RoleGuard::create(user)); return Ok(RoleGuard::create(&user));
} }
Ok(RoleGuard::NotAuthenticated) Ok(RoleGuard::NotAuthenticated)
} }

View File

@ -76,6 +76,14 @@ table tr:nth-child(odd) {
background-color: #fff; 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 { table tr.filters input {
background-image: url("/static/search.svg"); background-image: url("/static/search.svg");
background-repeat: no-repeat; background-repeat: no-repeat;

View File

@ -39,6 +39,7 @@ pub struct UserDelta {
pub username: String, pub username: String,
pub email: String, pub email: String,
pub password: Option<String>, pub password: Option<String>,
pub role: Role,
} }
impl From<User> for UserDelta { impl From<User> for UserDelta {
@ -50,6 +51,7 @@ impl From<User> for UserDelta {
username: u.username, username: u.username,
email: u.email, email: u.email,
password: None, password: None,
role: u.role,
} }
} }
} }
@ -73,7 +75,8 @@ pub enum Role {
} }
impl Role { impl Role {
pub fn convert(i: i64) -> Self { #[must_use]
pub const fn convert(i: i64) -> Self {
match i { match i {
0 => Self::Disabled, 0 => Self::Disabled,
1 => Self::Regular, 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 { match self {
Role::NotAuthenticated => 3, Role::NotAuthenticated => 3,
Role::Disabled => 0, Role::Disabled => 0,
@ -91,3 +95,9 @@ impl Role {
} }
} }
} }
impl Default for Role {
fn default() -> Self {
Self::Regular
}
}