3 newtypes::{CommunityId, InstanceId, LanguageId, LocalUserId, SiteId},
4 schema::{local_site, site, site_language},
10 LocalUserLanguageForm,
17 utils::{get_conn, DbPool},
28 use diesel_async::{AsyncPgConnection, RunQueryDsl};
29 use lemmy_utils::error::LemmyError;
30 use tokio::sync::OnceCell;
32 impl LocalUserLanguage {
35 for_local_user_id: LocalUserId,
36 ) -> Result<Vec<LanguageId>, Error> {
37 use crate::schema::local_user_language::dsl::{
42 let conn = &mut get_conn(pool).await?;
48 let langs = local_user_language
49 .filter(local_user_id.eq(for_local_user_id))
54 convert_read_languages(conn, langs).await
60 /// Update the user's languages.
62 /// If no language_id vector is given, it will show all languages
65 language_ids: Vec<LanguageId>,
66 for_local_user_id: LocalUserId,
67 ) -> Result<(), Error> {
68 let conn = &mut get_conn(pool).await?;
74 use crate::schema::local_user_language::dsl::{local_user_id, local_user_language};
75 // Clear the current user languages
76 delete(local_user_language.filter(local_user_id.eq(for_local_user_id)))
80 let lang_ids = convert_update_languages(conn, language_ids).await?;
82 let form = LocalUserLanguageForm {
83 local_user_id: for_local_user_id,
86 insert_into(local_user_language)
88 .get_result::<Self>(conn)
99 pub async fn read_local(pool: &DbPool) -> Result<Vec<LanguageId>, Error> {
100 let conn = &mut get_conn(pool).await?;
102 .inner_join(local_site::table)
103 .inner_join(site_language::table)
104 .order(site_language::id)
105 .select(site_language::language_id)
110 pub async fn read(pool: &DbPool, for_site_id: SiteId) -> Result<Vec<LanguageId>, Error> {
111 let conn = &mut get_conn(pool).await?;
113 let langs = site_language::table
114 .filter(site_language::site_id.eq(for_site_id))
115 .order(site_language::language_id)
116 .select(site_language::language_id)
119 convert_read_languages(conn, langs).await
124 language_ids: Vec<LanguageId>,
126 ) -> Result<(), Error> {
127 let conn = &mut get_conn(pool).await?;
128 let for_site_id = site.id;
129 let instance_id = site.instance_id;
134 Box::pin(async move {
135 use crate::schema::site_language::dsl::{site_id, site_language};
137 // Clear the current languages
138 delete(site_language.filter(site_id.eq(for_site_id)))
142 let lang_ids = convert_update_languages(conn, language_ids).await?;
144 let form = SiteLanguageForm {
145 site_id: for_site_id,
148 insert_into(site_language)
150 .get_result::<Self>(conn)
154 CommunityLanguage::limit_languages(conn, instance_id).await?;
163 impl CommunityLanguage {
164 /// Returns true if the given language is one of configured languages for given community
165 pub async fn is_allowed_community_language(
167 for_language_id: Option<LanguageId>,
168 for_community_id: CommunityId,
169 ) -> Result<(), LemmyError> {
170 use crate::schema::community_language::dsl::{community_id, community_language, language_id};
171 let conn = &mut get_conn(pool).await?;
173 if let Some(for_language_id) = for_language_id {
174 let is_allowed = select(exists(
176 .filter(language_id.eq(for_language_id))
177 .filter(community_id.eq(for_community_id)),
185 Err(LemmyError::from_message("language_not_allowed"))
192 /// When site languages are updated, delete all languages of local communities which are not
193 /// also part of site languages. This is because post/comment language is only checked against
194 /// community language, and it shouldnt be possible to post content in languages which are not
195 /// allowed by local site.
196 async fn limit_languages(
197 conn: &mut AsyncPgConnection,
198 for_instance_id: InstanceId,
199 ) -> Result<(), Error> {
202 community_language::dsl as cl,
203 site_language::dsl as sl,
205 let community_languages: Vec<LanguageId> = cl::community_language
206 .left_outer_join(sl::site_language.on(cl::language_id.eq(sl::language_id)))
207 .inner_join(c::community)
208 .filter(c::instance_id.eq(for_instance_id))
209 .filter(sl::language_id.is_null())
210 .select(cl::language_id)
214 for c in community_languages {
215 delete(cl::community_language.filter(cl::language_id.eq(c)))
224 for_community_id: CommunityId,
225 ) -> Result<Vec<LanguageId>, Error> {
226 use crate::schema::community_language::dsl::{community_id, community_language, language_id};
227 let conn = &mut get_conn(pool).await?;
229 let langs = community_language
230 .filter(community_id.eq(for_community_id))
235 convert_read_languages(conn, langs).await
240 mut language_ids: Vec<LanguageId>,
241 for_community_id: CommunityId,
242 ) -> Result<(), Error> {
243 let conn = &mut get_conn(pool).await?;
245 if language_ids.is_empty() {
246 language_ids = SiteLanguage::read_local(pool).await?;
252 Box::pin(async move {
253 use crate::schema::community_language::dsl::{community_id, community_language};
254 // Clear the current languages
255 delete(community_language.filter(community_id.eq(for_community_id)))
259 for l in language_ids {
260 let form = CommunityLanguageForm {
261 community_id: for_community_id,
264 insert_into(community_language)
266 .get_result::<Self>(conn)
276 pub async fn default_post_language(
278 community_id: CommunityId,
279 local_user_id: LocalUserId,
280 ) -> Result<Option<LanguageId>, Error> {
281 use crate::schema::{community_language::dsl as cl, local_user_language::dsl as ul};
282 let conn = &mut get_conn(pool).await?;
283 let intersection = ul::local_user_language
284 .inner_join(cl::community_language.on(ul::language_id.eq(cl::language_id)))
285 .filter(ul::local_user_id.eq(local_user_id))
286 .filter(cl::community_id.eq(community_id))
287 .select(cl::language_id)
288 .get_results::<LanguageId>(conn)
291 if intersection.len() == 1 {
292 Ok(Some(intersection[0]))
298 /// If no language is given, set all languages
299 async fn convert_update_languages(
300 conn: &mut AsyncPgConnection,
301 language_ids: Vec<LanguageId>,
302 ) -> Result<Vec<LanguageId>, Error> {
303 if language_ids.is_empty() {
305 Language::read_all_conn(conn)
316 /// If all languages are returned, return empty vec instead
317 async fn convert_read_languages(
318 conn: &mut AsyncPgConnection,
319 language_ids: Vec<LanguageId>,
320 ) -> Result<Vec<LanguageId>, Error> {
321 static ALL_LANGUAGES_COUNT: OnceCell<usize> = OnceCell::const_new();
322 let count = ALL_LANGUAGES_COUNT
323 .get_or_init(|| async {
324 use crate::schema::language::dsl::{id, language};
325 let count: i64 = language
329 .expect("read number of languages");
334 if &language_ids.len() == count {
344 impls::actor_language::{
345 convert_read_languages,
346 convert_update_languages,
347 default_post_language,
359 community::{Community, CommunityInsertForm},
361 local_site::{LocalSite, LocalSiteInsertForm},
362 local_user::{LocalUser, LocalUserInsertForm},
363 person::{Person, PersonInsertForm},
364 site::{Site, SiteInsertForm},
367 utils::build_db_pool_for_tests,
369 use serial_test::serial;
371 async fn test_langs1(pool: &DbPool) -> Vec<LanguageId> {
373 Language::read_id_from_code(pool, "en").await.unwrap(),
374 Language::read_id_from_code(pool, "fr").await.unwrap(),
375 Language::read_id_from_code(pool, "ru").await.unwrap(),
378 async fn test_langs2(pool: &DbPool) -> Vec<LanguageId> {
380 Language::read_id_from_code(pool, "fi").await.unwrap(),
381 Language::read_id_from_code(pool, "se").await.unwrap(),
385 async fn create_test_site(pool: &DbPool) -> (Site, Instance) {
386 let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
388 let site_form = SiteInsertForm::builder()
389 .name("test site".to_string())
390 .instance_id(inserted_instance.id)
392 let site = Site::create(pool, &site_form).await.unwrap();
394 // Create a local site, since this is necessary for local languages
395 let local_site_form = LocalSiteInsertForm::builder().site_id(site.id).build();
396 LocalSite::create(pool, &local_site_form).await.unwrap();
398 (site, inserted_instance)
403 async fn test_convert_update_languages() {
404 let pool = &build_db_pool_for_tests().await;
406 // call with empty vec, returns all languages
407 let conn = &mut get_conn(pool).await.unwrap();
408 let converted1 = convert_update_languages(conn, vec![]).await.unwrap();
409 assert_eq!(184, converted1.len());
411 // call with nonempty vec, returns same vec
412 let test_langs = test_langs1(pool).await;
413 let converted2 = convert_update_languages(conn, test_langs.clone())
416 assert_eq!(test_langs, converted2);
420 async fn test_convert_read_languages() {
421 use crate::schema::language::dsl::{id, language};
422 let pool = &build_db_pool_for_tests().await;
424 // call with all languages, returns empty vec
425 let conn = &mut get_conn(pool).await.unwrap();
426 let all_langs = language.select(id).get_results(conn).await.unwrap();
427 let converted1: Vec<LanguageId> = convert_read_languages(conn, all_langs).await.unwrap();
428 assert_eq!(0, converted1.len());
430 // call with nonempty vec, returns same vec
431 let test_langs = test_langs1(pool).await;
432 let converted2 = convert_read_languages(conn, test_langs.clone())
435 assert_eq!(test_langs, converted2);
440 async fn test_site_languages() {
441 let pool = &build_db_pool_for_tests().await;
443 let (site, instance) = create_test_site(pool).await;
444 let site_languages1 = SiteLanguage::read_local(pool).await.unwrap();
445 // site is created with all languages
446 assert_eq!(184, site_languages1.len());
448 let test_langs = test_langs1(pool).await;
449 SiteLanguage::update(pool, test_langs.clone(), &site)
453 let site_languages2 = SiteLanguage::read_local(pool).await.unwrap();
454 // after update, site only has new languages
455 assert_eq!(test_langs, site_languages2);
457 Site::delete(pool, site.id).await.unwrap();
458 Instance::delete(pool, instance.id).await.unwrap();
459 LocalSite::delete(pool).await.unwrap();
464 async fn test_user_languages() {
465 let pool = &build_db_pool_for_tests().await;
467 let (site, instance) = create_test_site(pool).await;
468 let test_langs = test_langs1(pool).await;
469 SiteLanguage::update(pool, test_langs.clone(), &site)
473 let person_form = PersonInsertForm::builder()
474 .name("my test person".to_string())
475 .public_key("pubkey".to_string())
476 .instance_id(instance.id)
478 let person = Person::create(pool, &person_form).await.unwrap();
479 let local_user_form = LocalUserInsertForm::builder()
480 .person_id(person.id)
481 .password_encrypted("my_pw".to_string())
484 let local_user = LocalUser::create(pool, &local_user_form).await.unwrap();
485 let local_user_langs1 = LocalUserLanguage::read(pool, local_user.id).await.unwrap();
487 // new user should be initialized with site languages
488 assert_eq!(test_langs, local_user_langs1);
490 // update user languages
491 let test_langs2 = test_langs2(pool).await;
492 LocalUserLanguage::update(pool, test_langs2, local_user.id)
495 let local_user_langs2 = LocalUserLanguage::read(pool, local_user.id).await.unwrap();
496 assert_eq!(2, local_user_langs2.len());
498 Person::delete(pool, person.id).await.unwrap();
499 LocalUser::delete(pool, local_user.id).await.unwrap();
500 Site::delete(pool, site.id).await.unwrap();
501 LocalSite::delete(pool).await.unwrap();
502 Instance::delete(pool, instance.id).await.unwrap();
507 async fn test_community_languages() {
508 let pool = &build_db_pool_for_tests().await;
509 let (site, instance) = create_test_site(pool).await;
510 let test_langs = test_langs1(pool).await;
511 SiteLanguage::update(pool, test_langs.clone(), &site)
515 let read_site_langs = SiteLanguage::read(pool, site.id).await.unwrap();
516 assert_eq!(test_langs, read_site_langs);
518 // Test the local ones are the same
519 let read_local_site_langs = SiteLanguage::read_local(pool).await.unwrap();
520 assert_eq!(test_langs, read_local_site_langs);
522 let community_form = CommunityInsertForm::builder()
523 .name("test community".to_string())
524 .title("test community".to_string())
525 .public_key("pubkey".to_string())
526 .instance_id(instance.id)
528 let community = Community::create(pool, &community_form).await.unwrap();
529 let community_langs1 = CommunityLanguage::read(pool, community.id).await.unwrap();
531 // community is initialized with site languages
532 assert_eq!(test_langs, community_langs1);
535 CommunityLanguage::is_allowed_community_language(pool, Some(test_langs[0]), community.id)
537 assert!(allowed_lang1.is_ok());
539 let test_langs2 = test_langs2(pool).await;
541 CommunityLanguage::is_allowed_community_language(pool, Some(test_langs2[0]), community.id)
543 assert!(allowed_lang2.is_err());
545 // limit site languages to en, fi. after this, community languages should be updated to
546 // intersection of old languages (en, fr, ru) and (en, fi), which is only fi.
547 SiteLanguage::update(pool, vec![test_langs[0], test_langs2[0]], &site)
550 let community_langs2 = CommunityLanguage::read(pool, community.id).await.unwrap();
551 assert_eq!(vec![test_langs[0]], community_langs2);
553 // update community languages to different ones
554 CommunityLanguage::update(pool, test_langs2.clone(), community.id)
557 let community_langs3 = CommunityLanguage::read(pool, community.id).await.unwrap();
558 assert_eq!(test_langs2, community_langs3);
560 Community::delete(pool, community.id).await.unwrap();
561 Site::delete(pool, site.id).await.unwrap();
562 LocalSite::delete(pool).await.unwrap();
563 Instance::delete(pool, instance.id).await.unwrap();
568 async fn test_default_post_language() {
569 let pool = &build_db_pool_for_tests().await;
570 let (site, instance) = create_test_site(pool).await;
571 let test_langs = test_langs1(pool).await;
572 let test_langs2 = test_langs2(pool).await;
574 let community_form = CommunityInsertForm::builder()
575 .name("test community".to_string())
576 .title("test community".to_string())
577 .public_key("pubkey".to_string())
578 .instance_id(instance.id)
580 let community = Community::create(pool, &community_form).await.unwrap();
581 CommunityLanguage::update(pool, test_langs, community.id)
585 let person_form = PersonInsertForm::builder()
586 .name("my test person".to_string())
587 .public_key("pubkey".to_string())
588 .instance_id(instance.id)
590 let person = Person::create(pool, &person_form).await.unwrap();
591 let local_user_form = LocalUserInsertForm::builder()
592 .person_id(person.id)
593 .password_encrypted("my_pw".to_string())
595 let local_user = LocalUser::create(pool, &local_user_form).await.unwrap();
596 LocalUserLanguage::update(pool, test_langs2, local_user.id)
600 // no overlap in user/community languages, so no default language for post
601 let def1 = default_post_language(pool, community.id, local_user.id)
604 assert_eq!(None, def1);
606 let ru = Language::read_id_from_code(pool, "ru").await.unwrap();
607 let test_langs3 = vec![
609 Language::read_id_from_code(pool, "fi").await.unwrap(),
610 Language::read_id_from_code(pool, "se").await.unwrap(),
612 LocalUserLanguage::update(pool, test_langs3, local_user.id)
616 // this time, both have ru as common lang
617 let def2 = default_post_language(pool, community.id, local_user.id)
620 assert_eq!(Some(ru), def2);
622 Person::delete(pool, person.id).await.unwrap();
623 Community::delete(pool, community.id).await.unwrap();
624 LocalUser::delete(pool, local_user.id).await.unwrap();
625 Site::delete(pool, site.id).await.unwrap();
626 LocalSite::delete(pool).await.unwrap();
627 Instance::delete(pool, instance.id).await.unwrap();