On registration, automatically set content languages from accept-language
header (#4550)
* On registration, automatically set content languages from accept header * no need to set site language or default language for new user anymore * fix test * fix langs * avoid duplicate writing of new user languages
This commit is contained in:
parent
d06ef2c47e
commit
846848c4f6
18 changed files with 130 additions and 94 deletions
|
@ -139,3 +139,23 @@ test("Create user with Arabic name", async () => {
|
|||
let alphaPerson = (await resolvePerson(alpha, apShortname)).person;
|
||||
expect(alphaPerson).toBeDefined();
|
||||
});
|
||||
|
||||
test("Create user with accept-language", async () => {
|
||||
let lemmy_http = new LemmyHttp(alphaUrl, {
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language#syntax
|
||||
headers: { "Accept-Language": "fr-CH, en;q=0.8, de;q=0.7, *;q=0.5" },
|
||||
});
|
||||
let user = await registerUser(lemmy_http, alphaUrl);
|
||||
|
||||
let site = await getSite(user);
|
||||
expect(site.my_user).toBeDefined();
|
||||
expect(site.my_user?.local_user_view.local_user.interface_language).toBe(
|
||||
"fr",
|
||||
);
|
||||
let langs = site.all_languages
|
||||
.filter(a => site.my_user?.discussion_languages.includes(a.id))
|
||||
.map(l => l.code);
|
||||
// should have languages from accept header, as well as "undetermined"
|
||||
// which is automatically enabled by backend
|
||||
expect(langs).toStrictEqual(["und", "de", "en", "fr"]);
|
||||
});
|
||||
|
|
|
@ -6,10 +6,7 @@ use lemmy_api_common::{
|
|||
person::GenerateTotpSecretResponse,
|
||||
sensitive::Sensitive,
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
source::local_user::{LocalUser, LocalUserUpdateForm},
|
||||
traits::Crud,
|
||||
};
|
||||
use lemmy_db_schema::source::local_user::{LocalUser, LocalUserUpdateForm};
|
||||
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
||||
use lemmy_utils::error::{LemmyError, LemmyErrorType};
|
||||
|
||||
|
|
|
@ -4,10 +4,7 @@ use lemmy_api_common::{
|
|||
context::LemmyContext,
|
||||
person::{UpdateTotp, UpdateTotpResponse},
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
source::local_user::{LocalUser, LocalUserUpdateForm},
|
||||
traits::Crud,
|
||||
};
|
||||
use lemmy_db_schema::source::local_user::{LocalUser, LocalUserUpdateForm};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
|
||||
|
|
|
@ -124,7 +124,9 @@ mod tests {
|
|||
.password_encrypted("123456".to_string())
|
||||
.build();
|
||||
|
||||
let inserted_local_user = LocalUser::create(pool, &local_user_form).await.unwrap();
|
||||
let inserted_local_user = LocalUser::create(pool, &local_user_form, vec![])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let req = TestRequest::default().to_http_request();
|
||||
let jwt = Claims::generate(inserted_local_user.id, req, &context)
|
||||
|
|
|
@ -20,6 +20,7 @@ use lemmy_db_schema::{
|
|||
aggregates::structs::PersonAggregates,
|
||||
source::{
|
||||
captcha_answer::{CaptchaAnswer, CheckCaptchaAnswer},
|
||||
language::Language,
|
||||
local_user::{LocalUser, LocalUserInsertForm},
|
||||
local_user_vote_display_mode::LocalUserVoteDisplayMode,
|
||||
person::{Person, PersonInsertForm},
|
||||
|
@ -36,6 +37,7 @@ use lemmy_utils::{
|
|||
validation::is_valid_actor_name,
|
||||
},
|
||||
};
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn register(
|
||||
|
@ -128,12 +130,15 @@ pub async fn register(
|
|||
let accepted_application = Some(!require_registration_application);
|
||||
|
||||
// Get the user's preferred language using the Accept-Language header
|
||||
let language_tag = req.headers().get("Accept-Language").and_then(|hdr| {
|
||||
accept_language::parse(hdr.to_str().unwrap_or_default())
|
||||
.first()
|
||||
let language_tags: Vec<String> = req
|
||||
.headers()
|
||||
.get("Accept-Language")
|
||||
.map(|hdr| accept_language::parse(hdr.to_str().unwrap_or_default()))
|
||||
.iter()
|
||||
.flatten()
|
||||
// Remove the optional region code
|
||||
.map(|lang_str| lang_str.split('-').next().unwrap_or_default().to_string())
|
||||
});
|
||||
.collect();
|
||||
|
||||
// Create the local user
|
||||
let local_user_form = LocalUserInsertForm::builder()
|
||||
|
@ -144,12 +149,23 @@ pub async fn register(
|
|||
.accepted_application(accepted_application)
|
||||
.default_listing_type(Some(local_site.default_post_listing_type))
|
||||
.post_listing_mode(Some(local_site.default_post_listing_mode))
|
||||
.interface_language(language_tag)
|
||||
.interface_language(language_tags.first().cloned())
|
||||
// If its the initial site setup, they are an admin
|
||||
.admin(Some(!local_site.site_setup))
|
||||
.build();
|
||||
|
||||
let inserted_local_user = LocalUser::create(&mut context.pool(), &local_user_form).await?;
|
||||
let all_languages = Language::read_all(&mut context.pool()).await?;
|
||||
// use hashset to avoid duplicates
|
||||
let mut language_ids = HashSet::new();
|
||||
for l in language_tags {
|
||||
if let Some(found) = all_languages.iter().find(|all| all.code == l) {
|
||||
language_ids.insert(found.id);
|
||||
}
|
||||
}
|
||||
let language_ids = language_ids.into_iter().collect();
|
||||
|
||||
let inserted_local_user =
|
||||
LocalUser::create(&mut context.pool(), &local_user_form, language_ids).await?;
|
||||
|
||||
if local_site.site_setup && require_registration_application {
|
||||
// Create the registration application
|
||||
|
|
|
@ -361,7 +361,7 @@ mod tests {
|
|||
.person_id(person.id)
|
||||
.password_encrypted("pass".to_string())
|
||||
.build();
|
||||
let local_user = LocalUser::create(&mut context.pool(), &user_form).await?;
|
||||
let local_user = LocalUser::create(&mut context.pool(), &user_form, vec![]).await?;
|
||||
|
||||
Ok(LocalUserView::read(&mut context.pool(), local_user.id).await?)
|
||||
}
|
||||
|
|
|
@ -523,10 +523,6 @@ mod tests {
|
|||
let pool = &mut pool.into();
|
||||
|
||||
let (site, instance) = create_test_site(pool).await;
|
||||
let mut test_langs = test_langs1(pool).await;
|
||||
SiteLanguage::update(pool, test_langs.clone(), &site)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let person_form = PersonInsertForm::builder()
|
||||
.name("my test person".to_string())
|
||||
|
@ -539,14 +535,13 @@ mod tests {
|
|||
.password_encrypted("my_pw".to_string())
|
||||
.build();
|
||||
|
||||
let local_user = LocalUser::create(pool, &local_user_form).await.unwrap();
|
||||
let local_user = LocalUser::create(pool, &local_user_form, vec![])
|
||||
.await
|
||||
.unwrap();
|
||||
let local_user_langs1 = LocalUserLanguage::read(pool, local_user.id).await.unwrap();
|
||||
|
||||
// new user should be initialized with site languages and undetermined
|
||||
//test_langs.push(UNDETERMINED_ID);
|
||||
//test_langs.sort();
|
||||
test_langs.insert(0, UNDETERMINED_ID);
|
||||
assert_eq!(test_langs, local_user_langs1);
|
||||
// new user should be initialized with all languages
|
||||
assert_eq!(0, local_user_langs1.len());
|
||||
|
||||
// update user languages
|
||||
let test_langs2 = test_langs2(pool).await;
|
||||
|
@ -655,7 +650,9 @@ mod tests {
|
|||
.person_id(person.id)
|
||||
.password_encrypted("my_pw".to_string())
|
||||
.build();
|
||||
let local_user = LocalUser::create(pool, &local_user_form).await.unwrap();
|
||||
let local_user = LocalUser::create(pool, &local_user_form, vec![])
|
||||
.await
|
||||
.unwrap();
|
||||
LocalUserLanguage::update(pool, test_langs2, local_user.id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
use crate::{
|
||||
newtypes::{DbUrl, LocalUserId, PersonId},
|
||||
newtypes::{DbUrl, LanguageId, LocalUserId, PersonId},
|
||||
schema::{local_user, person, registration_application},
|
||||
source::{
|
||||
actor_language::{LocalUserLanguage, SiteLanguage},
|
||||
actor_language::LocalUserLanguage,
|
||||
local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm},
|
||||
local_user_vote_display_mode::{LocalUserVoteDisplayMode, LocalUserVoteDisplayModeInsertForm},
|
||||
},
|
||||
traits::Crud,
|
||||
utils::{
|
||||
functions::{coalesce, lower},
|
||||
get_conn,
|
||||
|
@ -25,6 +24,52 @@ use diesel::{
|
|||
use diesel_async::RunQueryDsl;
|
||||
|
||||
impl LocalUser {
|
||||
pub async fn create(
|
||||
pool: &mut DbPool<'_>,
|
||||
form: &LocalUserInsertForm,
|
||||
languages: Vec<LanguageId>,
|
||||
) -> Result<LocalUser, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let mut form_with_encrypted_password = form.clone();
|
||||
let password_hash =
|
||||
hash(&form.password_encrypted, DEFAULT_COST).expect("Couldn't hash password");
|
||||
form_with_encrypted_password.password_encrypted = password_hash;
|
||||
|
||||
let local_user_ = insert_into(local_user::table)
|
||||
.values(form_with_encrypted_password)
|
||||
.get_result::<Self>(conn)
|
||||
.await?;
|
||||
|
||||
LocalUserLanguage::update(pool, languages, local_user_.id).await?;
|
||||
|
||||
// Create their vote_display_modes
|
||||
let vote_display_mode_form = LocalUserVoteDisplayModeInsertForm::builder()
|
||||
.local_user_id(local_user_.id)
|
||||
.build();
|
||||
LocalUserVoteDisplayMode::create(pool, &vote_display_mode_form).await?;
|
||||
|
||||
Ok(local_user_)
|
||||
}
|
||||
|
||||
pub async fn update(
|
||||
pool: &mut DbPool<'_>,
|
||||
local_user_id: LocalUserId,
|
||||
form: &LocalUserUpdateForm,
|
||||
) -> Result<LocalUser, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
diesel::update(local_user::table.find(local_user_id))
|
||||
.set(form)
|
||||
.get_result::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn delete(pool: &mut DbPool<'_>, id: LocalUserId) -> Result<usize, Error> {
|
||||
let conn = &mut *get_conn(pool).await?;
|
||||
diesel::delete(local_user::table.find(id))
|
||||
.execute(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn update_password(
|
||||
pool: &mut DbPool<'_>,
|
||||
local_user_id: LocalUserId,
|
||||
|
@ -183,52 +228,3 @@ pub struct UserBackupLists {
|
|||
pub blocked_users: Vec<DbUrl>,
|
||||
pub blocked_instances: Vec<String>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Crud for LocalUser {
|
||||
type InsertForm = LocalUserInsertForm;
|
||||
type UpdateForm = LocalUserUpdateForm;
|
||||
type IdType = LocalUserId;
|
||||
|
||||
async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let mut form_with_encrypted_password = form.clone();
|
||||
let password_hash =
|
||||
hash(&form.password_encrypted, DEFAULT_COST).expect("Couldn't hash password");
|
||||
form_with_encrypted_password.password_encrypted = password_hash;
|
||||
|
||||
let local_user_ = insert_into(local_user::table)
|
||||
.values(form_with_encrypted_password)
|
||||
.get_result::<Self>(conn)
|
||||
.await?;
|
||||
|
||||
let site_languages = SiteLanguage::read_local_raw(pool).await;
|
||||
if let Ok(langs) = site_languages {
|
||||
// if site exists, init user with site languages
|
||||
LocalUserLanguage::update(pool, langs, local_user_.id).await?;
|
||||
} else {
|
||||
// otherwise, init with all languages (this only happens during tests and
|
||||
// for first admin user, which is created before site)
|
||||
LocalUserLanguage::update(pool, vec![], local_user_.id).await?;
|
||||
}
|
||||
|
||||
// Create their vote_display_modes
|
||||
let vote_display_mode_form = LocalUserVoteDisplayModeInsertForm::builder()
|
||||
.local_user_id(local_user_.id)
|
||||
.build();
|
||||
LocalUserVoteDisplayMode::create(pool, &vote_display_mode_form).await?;
|
||||
|
||||
Ok(local_user_)
|
||||
}
|
||||
async fn update(
|
||||
pool: &mut DbPool<'_>,
|
||||
local_user_id: LocalUserId,
|
||||
form: &Self::UpdateForm,
|
||||
) -> Result<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
diesel::update(local_user::table.find(local_user_id))
|
||||
.set(form)
|
||||
.get_result::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -121,7 +121,9 @@ mod tests {
|
|||
.password_encrypted("pass".to_string())
|
||||
.build();
|
||||
|
||||
let inserted_local_user = LocalUser::create(pool, &new_local_user).await.unwrap();
|
||||
let inserted_local_user = LocalUser::create(pool, &new_local_user, vec![])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let token = "nope";
|
||||
|
||||
|
|
|
@ -308,7 +308,9 @@ mod tests {
|
|||
.person_id(inserted_timmy.id)
|
||||
.password_encrypted("123".to_string())
|
||||
.build();
|
||||
let timmy_local_user = LocalUser::create(pool, &new_local_user).await.unwrap();
|
||||
let timmy_local_user = LocalUser::create(pool, &new_local_user, vec![])
|
||||
.await
|
||||
.unwrap();
|
||||
let timmy_view = LocalUserView {
|
||||
local_user: timmy_local_user,
|
||||
local_user_vote_display_mode: LocalUserVoteDisplayMode::default(),
|
||||
|
|
|
@ -503,7 +503,7 @@ mod tests {
|
|||
.admin(Some(true))
|
||||
.password_encrypted(String::new())
|
||||
.build();
|
||||
let inserted_timmy_local_user = LocalUser::create(pool, &timmy_local_user_form)
|
||||
let inserted_timmy_local_user = LocalUser::create(pool, &timmy_local_user_form, vec![])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
|
|
@ -330,7 +330,9 @@ mod tests {
|
|||
.person_id(inserted_timmy.id)
|
||||
.password_encrypted("123".to_string())
|
||||
.build();
|
||||
let timmy_local_user = LocalUser::create(pool, &new_local_user).await.unwrap();
|
||||
let timmy_local_user = LocalUser::create(pool, &new_local_user, vec![])
|
||||
.await
|
||||
.unwrap();
|
||||
let timmy_view = LocalUserView {
|
||||
local_user: timmy_local_user,
|
||||
local_user_vote_display_mode: LocalUserVoteDisplayMode::default(),
|
||||
|
|
|
@ -829,7 +829,7 @@ mod tests {
|
|||
admin: Some(true),
|
||||
..LocalUserInsertForm::test_form(inserted_person.id)
|
||||
};
|
||||
let inserted_local_user = LocalUser::create(pool, &local_user_form).await?;
|
||||
let inserted_local_user = LocalUser::create(pool, &local_user_form, vec![]).await?;
|
||||
|
||||
let new_bot = PersonInsertForm {
|
||||
bot_account: Some(true),
|
||||
|
@ -855,6 +855,7 @@ mod tests {
|
|||
let inserted_blocked_local_user = LocalUser::create(
|
||||
pool,
|
||||
&LocalUserInsertForm::test_form(inserted_blocked_person.id),
|
||||
vec![],
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
|
|
@ -176,7 +176,7 @@ mod tests {
|
|||
.admin(Some(true))
|
||||
.build();
|
||||
|
||||
let _inserted_timmy_local_user = LocalUser::create(pool, &timmy_local_user_form)
|
||||
let _inserted_timmy_local_user = LocalUser::create(pool, &timmy_local_user_form, vec![])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -193,7 +193,7 @@ mod tests {
|
|||
.password_encrypted("nada".to_string())
|
||||
.build();
|
||||
|
||||
let inserted_sara_local_user = LocalUser::create(pool, &sara_local_user_form)
|
||||
let inserted_sara_local_user = LocalUser::create(pool, &sara_local_user_form, vec![])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -224,7 +224,7 @@ mod tests {
|
|||
.password_encrypted("nada".to_string())
|
||||
.build();
|
||||
|
||||
let inserted_jess_local_user = LocalUser::create(pool, &jess_local_user_form)
|
||||
let inserted_jess_local_user = LocalUser::create(pool, &jess_local_user_form, vec![])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
|
|
@ -296,7 +296,9 @@ mod tests {
|
|||
.person_id(inserted_person.id)
|
||||
.password_encrypted(String::new())
|
||||
.build();
|
||||
let local_user = LocalUser::create(pool, &local_user_form).await.unwrap();
|
||||
let local_user = LocalUser::create(pool, &local_user_form, vec![])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let new_community = CommunityInsertForm::builder()
|
||||
.name("test_community_3".to_string())
|
||||
|
|
|
@ -204,7 +204,7 @@ mod tests {
|
|||
.person_id(alice.id)
|
||||
.password_encrypted(String::new())
|
||||
.build();
|
||||
let alice_local_user = LocalUser::create(pool, &alice_local_user_form).await?;
|
||||
let alice_local_user = LocalUser::create(pool, &alice_local_user_form, vec![]).await?;
|
||||
|
||||
let bob_form = PersonInsertForm::builder()
|
||||
.name("bob".to_string())
|
||||
|
@ -218,7 +218,7 @@ mod tests {
|
|||
.person_id(bob.id)
|
||||
.password_encrypted(String::new())
|
||||
.build();
|
||||
let bob_local_user = LocalUser::create(pool, &bob_local_user_form).await?;
|
||||
let bob_local_user = LocalUser::create(pool, &bob_local_user_form, vec![]).await?;
|
||||
|
||||
Ok(Data {
|
||||
alice,
|
||||
|
|
|
@ -475,7 +475,7 @@ async fn initialize_local_site_2022_10_10(
|
|||
.email(setup.admin_email.clone())
|
||||
.admin(Some(true))
|
||||
.build();
|
||||
LocalUser::create(pool, &local_user_form).await?;
|
||||
LocalUser::create(pool, &local_user_form, vec![]).await?;
|
||||
};
|
||||
|
||||
// Add an entry for the site table
|
||||
|
|
|
@ -155,7 +155,9 @@ mod tests {
|
|||
.password_encrypted("123456".to_string())
|
||||
.build();
|
||||
|
||||
let inserted_local_user = LocalUser::create(pool, &local_user_form).await.unwrap();
|
||||
let inserted_local_user = LocalUser::create(pool, &local_user_form, vec![])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let req = TestRequest::default().to_http_request();
|
||||
let jwt = Claims::generate(inserted_local_user.id, req, &context)
|
||||
|
|
Loading…
Reference in a new issue