WIP: make the interface aware of the admin role
This commit is contained in:
parent
61afbecda9
commit
eee0a8dba2
@ -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,6 +36,8 @@ pub fn navigation(i18n: &I18n, base_url: &Url, user: &User) -> Node<Msg> {
|
|||||||
)),
|
)),
|
||||||
t("add-link"),
|
t("add-link"),
|
||||||
],],
|
],],
|
||||||
|
if user.role == Role::Admin {
|
||||||
|
nodes![
|
||||||
// A button to create a new user
|
// A button to create a new user
|
||||||
li![a![
|
li![a![
|
||||||
attrs! {At::Href => crate::Urls::new(base_url).create_user()},
|
attrs! {At::Href => crate::Urls::new(base_url).create_user()},
|
||||||
@ -48,6 +53,10 @@ pub fn navigation(i18n: &I18n, base_url: &Url, user: &User) -> Node<Msg> {
|
|||||||
attrs! {At::Href => crate::Urls::new(base_url).list_users()},
|
attrs! {At::Href => crate::Urls::new(base_url).list_users()},
|
||||||
t("list-users"),
|
t("list-users"),
|
||||||
],],
|
],],
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
nodes!()
|
||||||
|
},
|
||||||
],
|
],
|
||||||
ol![
|
ol![
|
||||||
li![div![
|
li![div![
|
||||||
|
@ -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;
|
||||||
div![
|
let headline: Node<Msg> = match &user.role {
|
||||||
C!["editdialog", "center"],
|
Role::NotAuthenticated | Role::Disabled | Role::Regular => {
|
||||||
close_button(),
|
|
||||||
h1![match &user.edit {
|
h1![match &user.edit {
|
||||||
EditMode::Edit => t("edit-user"),
|
EditMode::Edit => t("edit-user"),
|
||||||
EditMode::Create => t("new-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(),
|
||||||
|
headline,
|
||||||
table![
|
table![
|
||||||
tr![
|
tr![
|
||||||
th![
|
th![
|
||||||
|
@ -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
|
@ -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
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user