Add language switching, fix logout
This commit is contained in:
parent
fa924a8e8c
commit
0a23b786b0
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -3450,6 +3450,8 @@ dependencies = [
|
||||
"chrono",
|
||||
"enum-map",
|
||||
"serde",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3728,15 +3730,15 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.20.0"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7318c509b5ba57f18533982607f24070a55d353e90d4cae30c467cdb2ad5ac5c"
|
||||
checksum = "aaf86bbcfd1fa9670b7a129f64fc0c9fcbbfe4f1bc4210e9e98fe71ffc12cde2"
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.20.1"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee8bc6b87a5112aeeab1f4a9f7ab634fe6cbefc4850006df31267f4cfb9e3149"
|
||||
checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2 1.0.27",
|
||||
|
@ -20,8 +20,8 @@ fluent = "0.15"
|
||||
seed = "0.8"
|
||||
serde = "1.0"
|
||||
unic-langid = "0.9"
|
||||
strum_macros = "0.20"
|
||||
strum = "0.20"
|
||||
strum_macros = "0.21"
|
||||
strum = "0.21"
|
||||
enum-map = "1"
|
||||
qrcode = "0.12"
|
||||
image = "0.23"
|
||||
|
@ -2,7 +2,7 @@ use std::sync::Arc;
|
||||
|
||||
use fluent::{FluentArgs, FluentBundle, FluentResource};
|
||||
use seed::log;
|
||||
use strum_macros::{AsRefStr, Display, EnumIter, EnumString};
|
||||
use shared::datatypes::Lang;
|
||||
use unic_langid::LanguageIdentifier;
|
||||
|
||||
// A struct containing the functions and the current language to query the localized strings.
|
||||
@ -22,10 +22,8 @@ impl I18n {
|
||||
/// Create a new translator struct
|
||||
#[must_use]
|
||||
pub fn new(lang: Lang) -> Self {
|
||||
Self {
|
||||
lang,
|
||||
ftl_bundle: Arc::new(lang.create_ftl_bundle()),
|
||||
}
|
||||
let ftl_bundle = Arc::new(Self::create_ftl_bundle(lang));
|
||||
Self { lang, ftl_bundle }
|
||||
}
|
||||
|
||||
/// Get the current language
|
||||
@ -35,10 +33,9 @@ impl I18n {
|
||||
}
|
||||
|
||||
/// Set the current language
|
||||
pub fn set_lang(&mut self, lang: Lang) -> &Self {
|
||||
pub fn set_lang(&mut self, lang: Lang) {
|
||||
self.lang = lang;
|
||||
self.ftl_bundle = Arc::new(lang.create_ftl_bundle());
|
||||
self
|
||||
self.ftl_bundle = Arc::new(Self::create_ftl_bundle(lang));
|
||||
}
|
||||
|
||||
/// Get a localized string. Optionally with parameters provided in `args`.
|
||||
@ -57,55 +54,44 @@ impl I18n {
|
||||
}
|
||||
}
|
||||
|
||||
/// An `enum` containing the available languages.
|
||||
/// To add an additional language add it to this enum aswell as an appropriate file into the locales folder.
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
#[derive(Debug, Copy, Clone, Display, EnumIter, EnumString, AsRefStr, Eq, PartialEq)]
|
||||
pub enum Lang {
|
||||
#[strum(serialize = "en-US")]
|
||||
EnUS,
|
||||
#[strum(serialize = "de-DE")]
|
||||
DeDE,
|
||||
}
|
||||
|
||||
impl Lang {
|
||||
impl I18n {
|
||||
/// Prettyprint the language name
|
||||
#[must_use]
|
||||
pub const fn label(self) -> &'static str {
|
||||
match self {
|
||||
Self::EnUS => "English (US)",
|
||||
Self::DeDE => "Deutsch (Deutschland)",
|
||||
pub const fn label(&self) -> &'static str {
|
||||
match self.lang {
|
||||
Lang::EnUS => "English (US)",
|
||||
Lang::DeDE => "Deutsch (Deutschland)",
|
||||
}
|
||||
}
|
||||
|
||||
/// include the fluent messages into the binary
|
||||
#[must_use]
|
||||
pub const fn ftl_messages(self) -> &'static str {
|
||||
pub const fn ftl_messages(lang: Lang) -> &'static str {
|
||||
macro_rules! include_ftl_messages {
|
||||
( $lang_id:literal ) => {
|
||||
include_str!(concat!("../../locales/", $lang_id, "/main.ftl"))
|
||||
};
|
||||
}
|
||||
match self {
|
||||
Self::EnUS => include_ftl_messages!("en"),
|
||||
Self::DeDE => include_ftl_messages!("de"),
|
||||
match lang {
|
||||
Lang::EnUS => include_ftl_messages!("en"),
|
||||
Lang::DeDE => include_ftl_messages!("de"),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn to_language_identifier(self) -> LanguageIdentifier {
|
||||
self.as_ref()
|
||||
pub fn language_identifier(lang: Lang) -> LanguageIdentifier {
|
||||
lang.as_ref()
|
||||
.parse()
|
||||
.expect("parse Lang to LanguageIdentifier")
|
||||
}
|
||||
|
||||
/// Create and initialize a fluent bundle.
|
||||
#[must_use]
|
||||
pub fn create_ftl_bundle(self) -> FluentBundle<FluentResource> {
|
||||
let ftl_resource =
|
||||
FluentResource::try_new(self.ftl_messages().to_owned()).expect("parse FTL messages");
|
||||
pub fn create_ftl_bundle(lang: Lang) -> FluentBundle<FluentResource> {
|
||||
let ftl_resource = FluentResource::try_new(Self::ftl_messages(lang).to_owned())
|
||||
.expect("parse FTL messages");
|
||||
|
||||
let mut bundle = FluentBundle::new(vec![self.to_language_identifier()]);
|
||||
let mut bundle = FluentBundle::new(vec![Self::language_identifier(lang)]);
|
||||
bundle.add_resource(ftl_resource).expect("add FTL resource");
|
||||
bundle
|
||||
}
|
||||
|
@ -7,9 +7,10 @@ use pages::list_users;
|
||||
use seed::window;
|
||||
use seed::{attrs, button, div, input, label, log, prelude::*, App, Url, C};
|
||||
use shared::apirequests::users::LoginUser;
|
||||
use shared::datatypes::Lang;
|
||||
use shared::datatypes::{Loadable, User};
|
||||
|
||||
use crate::i18n::{I18n, Lang};
|
||||
use crate::i18n::I18n;
|
||||
|
||||
// ------ ------
|
||||
// Init
|
||||
@ -47,6 +48,17 @@ struct Model {
|
||||
login_data: LoginUser,
|
||||
}
|
||||
|
||||
impl Model {
|
||||
fn set_lang(&mut self, l: Lang) {
|
||||
self.i18n.set_lang(l);
|
||||
match &mut self.page {
|
||||
Page::Home(ref mut m) => m.set_lang(l),
|
||||
Page::ListUsers(ref mut m) => m.set_lang(l),
|
||||
Page::NotFound => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct LoginForm {
|
||||
username: ElRef<web_sys::HtmlInputElement>,
|
||||
@ -104,6 +116,18 @@ impl Page {
|
||||
_other => Self::NotFound,
|
||||
};
|
||||
|
||||
orders.perform_cmd(async {
|
||||
// create request
|
||||
let request = Request::new("/admin/json/get_language/");
|
||||
// perform and get response
|
||||
let response = unwrap_or_return!(fetch(request).await, Msg::NoMessage);
|
||||
// validate response status
|
||||
let response = unwrap_or_return!(response.check_status(), Msg::NoMessage);
|
||||
let lang: Lang = unwrap_or_return!(response.json().await, Msg::NoMessage);
|
||||
|
||||
Msg::LanguageChanged(lang)
|
||||
});
|
||||
|
||||
log!("Page initialized");
|
||||
result
|
||||
}
|
||||
@ -125,6 +149,8 @@ pub enum Msg {
|
||||
Login,
|
||||
UsernameChanged(String),
|
||||
PasswordChanged(String),
|
||||
SetLanguage(Lang),
|
||||
LanguageChanged(Lang),
|
||||
}
|
||||
|
||||
fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
@ -163,6 +189,7 @@ fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
});
|
||||
}
|
||||
Msg::UserReceived(user) => {
|
||||
model.set_lang(user.language);
|
||||
model.user = Loadable::Data(Some(user));
|
||||
model.page = Page::init(
|
||||
model.location.current_url.clone(),
|
||||
@ -175,6 +202,7 @@ fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
model.user = Loadable::Data(None);
|
||||
logout(orders)
|
||||
}
|
||||
model.user = Loadable::Data(None);
|
||||
}
|
||||
Msg::Logout => {
|
||||
model.user = Loadable::Data(None);
|
||||
@ -183,9 +211,35 @@ fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
Msg::Login => login_user(model, orders),
|
||||
Msg::UsernameChanged(s) => model.login_data.username = s,
|
||||
Msg::PasswordChanged(s) => model.login_data.password = s,
|
||||
Msg::SetLanguage(l) => {
|
||||
change_language(l, orders);
|
||||
}
|
||||
Msg::LanguageChanged(l) => {
|
||||
log!("Changed Language", &l);
|
||||
model.set_lang(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn change_language(l: Lang, orders: &mut impl Orders<Msg>) {
|
||||
orders.perform_cmd(async move {
|
||||
// create request
|
||||
let request = unwrap_or_return!(
|
||||
Request::new("/admin/json/change_language/")
|
||||
.method(Method::Post)
|
||||
.json(&l),
|
||||
Msg::NoMessage
|
||||
);
|
||||
// perform and get response
|
||||
let response = unwrap_or_return!(fetch(request).await, Msg::NoMessage);
|
||||
// validate response status
|
||||
let response = unwrap_or_return!(response.check_status(), Msg::NoMessage);
|
||||
let l: Lang = unwrap_or_return!(response.json().await, Msg::NoMessage);
|
||||
|
||||
Msg::LanguageChanged(l)
|
||||
});
|
||||
}
|
||||
|
||||
fn logout(orders: &mut impl Orders<Msg>) {
|
||||
orders.perform_cmd(async {
|
||||
let request = Request::new("/admin/logout/");
|
||||
|
@ -1,6 +1,6 @@
|
||||
use fluent::fluent_args;
|
||||
use seed::{a, attrs, div, li, nav, ol, prelude::*, Url};
|
||||
use shared::datatypes::User;
|
||||
use seed::{a, attrs, div, li, nav, ol, prelude::*, Url, C};
|
||||
use shared::datatypes::{Lang, User};
|
||||
|
||||
use crate::{i18n::I18n, Msg};
|
||||
|
||||
@ -50,6 +50,12 @@ pub fn navigation(i18n: &I18n, base_url: &Url, user: &User) -> Node<Msg> {
|
||||
],],
|
||||
],
|
||||
ol![
|
||||
li![div![
|
||||
C!("languageselector"),
|
||||
t("language"),
|
||||
a![ev(Ev::Click, |_| Msg::SetLanguage(Lang::DeDE)), "de"],
|
||||
a![ev(Ev::Click, |_| Msg::SetLanguage(Lang::EnUS)), "en"]
|
||||
]],
|
||||
// The Welcome message
|
||||
li![div![welcome]],
|
||||
// The logout button
|
||||
|
@ -13,7 +13,7 @@ use shared::{
|
||||
general::{EditMode, Message, Operation, Status},
|
||||
links::{LinkDelta, LinkOverviewColumns, LinkRequestForm},
|
||||
},
|
||||
datatypes::{FullLink, Loadable},
|
||||
datatypes::{FullLink, Lang, Loadable},
|
||||
};
|
||||
|
||||
use crate::{get_host, i18n::I18n, unwrap_or_return};
|
||||
@ -54,6 +54,12 @@ pub struct Model {
|
||||
handle_timeout: Option<CmdHandle>, // Rendering qr-codes takes time... it is aborted when this handle is dropped and replaced.
|
||||
}
|
||||
|
||||
impl Model {
|
||||
pub fn set_lang(&mut self, l: Lang) {
|
||||
self.i18n.set_lang(l);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum Dialog {
|
||||
EditLink {
|
||||
|
@ -10,7 +10,7 @@ use shared::{
|
||||
general::{EditMode, Status},
|
||||
users::{UserDelta, UserOverviewColumns, UserRequestForm},
|
||||
},
|
||||
datatypes::User,
|
||||
datatypes::{Lang, User},
|
||||
};
|
||||
/*
|
||||
* init
|
||||
@ -42,6 +42,12 @@ pub struct Model {
|
||||
last_message: Option<Status>,
|
||||
}
|
||||
|
||||
impl Model {
|
||||
pub fn set_lang(&mut self, l: Lang) {
|
||||
self.i18n.set_lang(l);
|
||||
}
|
||||
}
|
||||
|
||||
impl Model {
|
||||
fn clean_dialogs(&mut self) {
|
||||
self.last_message = None;
|
||||
|
@ -8,6 +8,7 @@ logout = Abmelden
|
||||
login = Login
|
||||
yes = Ja
|
||||
no = Nein
|
||||
language = Sprache:
|
||||
|
||||
not-found = Dieser Link existiert nicht, oder wurde gelöscht.
|
||||
|
||||
|
@ -8,6 +8,7 @@ logout = Logout
|
||||
login = Login
|
||||
yes = Ja
|
||||
no = Nein
|
||||
language = Language:
|
||||
|
||||
not-found = This Link has not been found or has been deleted
|
||||
|
||||
|
@ -234,6 +234,7 @@ pub async fn webservice(
|
||||
// admin block
|
||||
.service(
|
||||
web::scope("/admin")
|
||||
.route("/logout/", web::to(views::logout))
|
||||
.service(
|
||||
web::scope("/download")
|
||||
.route("/png/{redirect_id}", web::get().to(views::download_png)),
|
||||
@ -241,6 +242,8 @@ pub async fn webservice(
|
||||
.service(
|
||||
web::scope("/json")
|
||||
.route("/list_links/", web::post().to(views::index_json))
|
||||
.route("/get_language/", web::get().to(views::get_language))
|
||||
.route("/change_language/", web::post().to(views::set_language))
|
||||
.route(
|
||||
"/create_link/",
|
||||
web::post().to(views::process_create_link_json),
|
||||
|
@ -1,3 +1,5 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::{forms::LinkForm, Secret, ServerConfig, ServerError};
|
||||
|
||||
use argonautica::Hasher;
|
||||
@ -7,7 +9,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use shared::{
|
||||
apirequests::links::LinkDelta,
|
||||
datatypes::{Count, Link, User},
|
||||
datatypes::{Count, Lang, Link, User},
|
||||
};
|
||||
use sqlx::Row;
|
||||
use tracing::{error, info, instrument};
|
||||
@ -22,7 +24,7 @@ pub trait UserDbOperations<T> {
|
||||
async fn set_language(
|
||||
self,
|
||||
server_config: &ServerConfig,
|
||||
new_language: &str,
|
||||
new_language: Lang,
|
||||
) -> Result<(), ServerError>;
|
||||
async fn count_admins(server_config: &ServerConfig) -> Result<Count, ServerError>;
|
||||
}
|
||||
@ -40,7 +42,7 @@ impl UserDbOperations<Self> for User {
|
||||
email: row.email,
|
||||
password: Secret::new(row.password),
|
||||
role: row.role,
|
||||
language: row.language,
|
||||
language: Lang::from_str(&row.language).expect("Should parse"),
|
||||
});
|
||||
user.map_err(ServerError::Database)
|
||||
}
|
||||
@ -63,7 +65,7 @@ impl UserDbOperations<Self> for User {
|
||||
email: row.email,
|
||||
password: Secret::new(row.password),
|
||||
role: row.role,
|
||||
language: row.language,
|
||||
language: Lang::from_str(&row.language).expect("Should parse"),
|
||||
});
|
||||
user.map_err(ServerError::Database)
|
||||
}
|
||||
@ -81,7 +83,8 @@ impl UserDbOperations<Self> for User {
|
||||
email: r.get("email"),
|
||||
password: Secret::new(r.get("password")),
|
||||
role: r.get("role"),
|
||||
language: r.get("language"),
|
||||
language: Lang::from_str(r.get("language"))
|
||||
.expect("should parse correctly"),
|
||||
})
|
||||
.collect()
|
||||
});
|
||||
@ -124,11 +127,12 @@ impl UserDbOperations<Self> for User {
|
||||
async fn set_language(
|
||||
self,
|
||||
server_config: &ServerConfig,
|
||||
new_language: &str,
|
||||
new_language: Lang,
|
||||
) -> Result<(), ServerError> {
|
||||
let lang_code = new_language.to_string();
|
||||
sqlx::query!(
|
||||
"UPDATE users SET language = ? where id = ?",
|
||||
new_language,
|
||||
lang_code,
|
||||
self.id
|
||||
)
|
||||
.execute(&server_config.db_pool)
|
||||
|
@ -1,3 +1,5 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use actix_identity::Identity;
|
||||
use actix_web::web;
|
||||
use enum_map::EnumMap;
|
||||
@ -8,7 +10,7 @@ use shared::{
|
||||
links::{LinkDelta, LinkOverviewColumns, LinkRequestForm},
|
||||
users::{UserDelta, UserOverviewColumns, UserRequestForm},
|
||||
},
|
||||
datatypes::{Count, FullLink, Link, Secret, User},
|
||||
datatypes::{Count, FullLink, Lang, Link, Secret, User},
|
||||
};
|
||||
use sqlx::Row;
|
||||
use tracing::{info, instrument, warn};
|
||||
@ -129,7 +131,7 @@ pub async fn list_all_allowed(
|
||||
email: v.get("uemail"),
|
||||
password: Secret::new("invalid".to_string()),
|
||||
role: v.get("urole"),
|
||||
language: v.get("ulang"),
|
||||
language: Lang::from_str(v.get("ulang")).expect("Should parse"),
|
||||
},
|
||||
clicks: Count {
|
||||
number: v.get("counter"), /* count is never None */
|
||||
@ -242,7 +244,7 @@ pub async fn list_users(
|
||||
email: v.get("email"),
|
||||
password: Secret::new("".to_string()),
|
||||
role: v.get("role"),
|
||||
language: v.get("language"),
|
||||
language: Lang::from_str(v.get("language")).expect("Should parse"),
|
||||
})
|
||||
.collect();
|
||||
|
||||
@ -591,24 +593,14 @@ pub async fn toggle_admin(
|
||||
#[instrument(skip(id))]
|
||||
pub async fn set_language(
|
||||
id: &Identity,
|
||||
lang_code: &str,
|
||||
lang_code: Lang,
|
||||
server_config: &ServerConfig,
|
||||
) -> Result<(), ServerError> {
|
||||
match lang_code {
|
||||
"de" | "en" => match authenticate(id, server_config).await? {
|
||||
Role::Admin { user } | Role::Regular { user } => {
|
||||
user.set_language(server_config, lang_code).await
|
||||
}
|
||||
Role::Disabled | Role::NotAuthenticated => {
|
||||
Err(ServerError::User("Not Allowed".to_owned()))
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
warn!("An invalid language was selected!");
|
||||
Err(ServerError::User(
|
||||
"This language is not supported!".to_owned(),
|
||||
))
|
||||
match authenticate(id, server_config).await? {
|
||||
Role::Admin { user } | Role::Regular { user } => {
|
||||
user.set_language(server_config, lang_code).await
|
||||
}
|
||||
Role::Disabled | Role::NotAuthenticated => Err(ServerError::User("Not Allowed".to_owned())),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,10 +14,13 @@ use fluent_templates::LanguageIdentifier;
|
||||
use image::{DynamicImage, ImageOutputFormat, Luma};
|
||||
use qrcode::QrCode;
|
||||
use queries::{authenticate, Role};
|
||||
use shared::apirequests::{
|
||||
general::{Message, Status},
|
||||
links::{LinkDelta, LinkRequestForm},
|
||||
users::{LoginUser, UserDelta, UserRequestForm},
|
||||
use shared::{
|
||||
apirequests::{
|
||||
general::{Message, Status},
|
||||
links::{LinkDelta, LinkRequestForm},
|
||||
users::{LoginUser, UserDelta, UserRequestForm},
|
||||
},
|
||||
datatypes::Lang,
|
||||
};
|
||||
use tracing::{error, info, instrument, warn};
|
||||
|
||||
@ -38,7 +41,7 @@ fn redirect_builder(target: &str) -> HttpResponse {
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
fn detect_language(request: &HttpRequest) -> Result<String, ServerError> {
|
||||
fn detect_language(request: &HttpRequest) -> Result<Lang, ServerError> {
|
||||
let requested = parse_accepted_languages(
|
||||
request
|
||||
.headers()
|
||||
@ -49,7 +52,9 @@ fn detect_language(request: &HttpRequest) -> Result<String, ServerError> {
|
||||
ServerError::User("Failed to convert Accept_language to str".to_owned())
|
||||
})?,
|
||||
);
|
||||
info!("accepted languages: {:?}", requested);
|
||||
let available = convert_vec_str_to_langids_lossy(&["de", "en"]);
|
||||
info!("available languages: {:?}", available);
|
||||
let default: LanguageIdentifier = "en"
|
||||
.parse()
|
||||
.map_err(|_| ServerError::User("Failed to parse a langid.".to_owned()))?;
|
||||
@ -60,10 +65,18 @@ fn detect_language(request: &HttpRequest) -> Result<String, ServerError> {
|
||||
Some(&default),
|
||||
NegotiationStrategy::Filtering,
|
||||
);
|
||||
let languagecode = supported
|
||||
.get(0)
|
||||
.map_or("en".to_string(), std::string::ToString::to_string);
|
||||
Ok(languagecode)
|
||||
info!("supported languages: {:?}", supported);
|
||||
|
||||
if let Some(languagecode) = supported.get(0) {
|
||||
info!("Supported Language: {}", languagecode);
|
||||
Ok(languagecode
|
||||
.to_string()
|
||||
.parse()
|
||||
.expect("Failed to parse 2 language"))
|
||||
} else {
|
||||
info!("Unsupported language using default!");
|
||||
Ok("enEN".parse::<Lang>().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument()]
|
||||
@ -201,14 +214,35 @@ pub async fn toggle_admin(
|
||||
)))
|
||||
}
|
||||
|
||||
#[instrument(skip(id))]
|
||||
pub async fn get_language(
|
||||
id: Option<Identity>,
|
||||
config: web::Data<crate::ServerConfig>,
|
||||
req: HttpRequest,
|
||||
) -> Result<HttpResponse, ServerError> {
|
||||
if let Some(id) = id {
|
||||
let user = authenticate(&id, &config).await?;
|
||||
match user {
|
||||
Role::NotAuthenticated | Role::Disabled => {
|
||||
Ok(HttpResponse::Ok().json2(&detect_language(&req)?))
|
||||
}
|
||||
Role::Regular { user } | Role::Admin { user } => {
|
||||
Ok(HttpResponse::Ok().json2(&user.language))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Ok(HttpResponse::Ok().json2(&detect_language(&req)?))
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(id))]
|
||||
pub async fn set_language(
|
||||
data: web::Path<String>,
|
||||
data: web::Json<Lang>,
|
||||
config: web::Data<crate::ServerConfig>,
|
||||
id: Identity,
|
||||
) -> Result<HttpResponse, ServerError> {
|
||||
queries::set_language(&id, &data.0, &config).await?;
|
||||
Ok(redirect_builder("/admin/index/"))
|
||||
queries::set_language(&id, data.0, &config).await?;
|
||||
Ok(HttpResponse::Ok().json2(&data.0))
|
||||
}
|
||||
|
||||
#[instrument(skip(id))]
|
||||
@ -259,6 +293,13 @@ pub async fn process_login_json(
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(id))]
|
||||
pub async fn logout(id: Identity) -> Result<HttpResponse, ServerError> {
|
||||
info!("Logging out the user");
|
||||
id.forget();
|
||||
Ok(redirect_builder("/app/"))
|
||||
}
|
||||
|
||||
#[instrument()]
|
||||
pub async fn redirect(
|
||||
config: web::Data<crate::ServerConfig>,
|
||||
|
@ -210,7 +210,14 @@ img.trashicon {
|
||||
width: 0.5cm;
|
||||
}
|
||||
|
||||
qrdownload {
|
||||
.qrdownload {
|
||||
display: flex;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.languageselector a {
|
||||
height: 70%;
|
||||
padding: 5px;
|
||||
margin: 3px;
|
||||
border-radius: 50%;
|
||||
}
|
@ -13,4 +13,6 @@ version = "0.3.1"
|
||||
[dependencies]
|
||||
serde = "1.0"
|
||||
chrono = {version = "0.4", features = ["serde"] }
|
||||
enum-map = {version="1", features = ["serde"]}
|
||||
enum-map = {version="1", features = ["serde"]}
|
||||
strum_macros = "0.21"
|
||||
strum = "0.21"
|
@ -1,6 +1,7 @@
|
||||
use std::ops::Deref;
|
||||
|
||||
use serde::{Deserialize, Serialize, Serializer};
|
||||
use strum_macros::{AsRefStr, EnumIter, EnumString, ToString};
|
||||
/// A generic list returntype containing the User and a Vec containing e.g. Links or Users
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
pub struct ListWithOwner<T> {
|
||||
@ -23,7 +24,7 @@ pub struct User {
|
||||
pub email: String,
|
||||
pub password: Secret,
|
||||
pub role: i64,
|
||||
pub language: String,
|
||||
pub language: Lang,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
@ -105,3 +106,26 @@ impl<T> Deref for Loadable<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An `enum` containing the available languages.
|
||||
/// To add an additional language add it to this enum aswell as an appropriate file into the locales folder.
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
#[derive(
|
||||
Debug,
|
||||
Copy,
|
||||
Clone,
|
||||
EnumIter,
|
||||
EnumString,
|
||||
ToString,
|
||||
AsRefStr,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
)]
|
||||
pub enum Lang {
|
||||
#[strum(serialize = "en-US", serialize = "en", serialize = "enUS")]
|
||||
EnUS,
|
||||
#[strum(serialize = "de-DE", serialize = "de", serialize = "deDE")]
|
||||
DeDE,
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user