3 newtypes::{CommunityId, InstanceId, LanguageId, LocalUserId, SiteId},
4 schema::{local_site, site, site_language},
5 source::{actor_language::*, language::Language, site::Site},
6 utils::{get_conn, DbPool},
8 use diesel::{delete, dsl::*, insert_into, result::Error, select, ExpressionMethods, QueryDsl};
9 use diesel_async::{AsyncPgConnection, RunQueryDsl};
10 use lemmy_utils::error::LemmyError;
11 use tokio::sync::OnceCell;
13 impl LocalUserLanguage {
16 for_local_user_id: LocalUserId,
17 ) -> Result<Vec<LanguageId>, Error> {
18 use crate::schema::local_user_language::dsl::*;
19 let conn = &mut get_conn(pool).await?;
25 let langs = local_user_language
26 .filter(local_user_id.eq(for_local_user_id))
30 convert_read_languages(conn, langs).await
36 /// Update the user's languages.
38 /// If no language_id vector is given, it will show all languages
41 language_ids: Vec<LanguageId>,
42 for_local_user_id: LocalUserId,
43 ) -> Result<(), Error> {
44 let conn = &mut get_conn(pool).await?;
50 use crate::schema::local_user_language::dsl::*;
51 // Clear the current user languages
52 delete(local_user_language.filter(local_user_id.eq(for_local_user_id)))
56 let lang_ids = convert_update_languages(conn, language_ids).await?;
58 let form = LocalUserLanguageForm {
59 local_user_id: for_local_user_id,
62 insert_into(local_user_language)
64 .get_result::<Self>(conn)
75 pub async fn read_local(pool: &DbPool) -> Result<Vec<LanguageId>, Error> {
76 let conn = &mut get_conn(pool).await?;
78 .inner_join(local_site::table)
79 .inner_join(site_language::table)
80 .select(site_language::language_id)
85 pub async fn read(pool: &DbPool, for_site_id: SiteId) -> Result<Vec<LanguageId>, Error> {
86 let conn = &mut get_conn(pool).await?;
88 let langs = site_language::table
89 .filter(site_language::site_id.eq(for_site_id))
90 .select(site_language::language_id)
93 convert_read_languages(conn, langs).await
98 language_ids: Vec<LanguageId>,
100 ) -> Result<(), Error> {
101 let conn = &mut get_conn(pool).await?;
102 let for_site_id = site.id;
103 let instance_id = site.instance_id;
108 Box::pin(async move {
109 use crate::schema::site_language::dsl::*;
111 // Clear the current languages
112 delete(site_language.filter(site_id.eq(for_site_id)))
116 let lang_ids = convert_update_languages(conn, language_ids).await?;
118 let form = SiteLanguageForm {
119 site_id: for_site_id,
122 insert_into(site_language)
124 .get_result::<Self>(conn)
128 CommunityLanguage::limit_languages(conn, instance_id).await?;
137 impl CommunityLanguage {
138 /// Returns true if the given language is one of configured languages for given community
139 pub async fn is_allowed_community_language(
141 for_language_id: Option<LanguageId>,
142 for_community_id: CommunityId,
143 ) -> Result<(), LemmyError> {
144 use crate::schema::community_language::dsl::*;
145 let conn = &mut get_conn(pool).await?;
147 if let Some(for_language_id) = for_language_id {
148 let is_allowed = select(exists(
150 .filter(language_id.eq(for_language_id))
151 .filter(community_id.eq(for_community_id)),
159 Err(LemmyError::from_message("language_not_allowed"))
166 /// When site languages are updated, delete all languages of local communities which are not
167 /// also part of site languages. This is because post/comment language is only checked against
168 /// community language, and it shouldnt be possible to post content in languages which are not
169 /// allowed by local site.
170 async fn limit_languages(
171 conn: &mut AsyncPgConnection,
172 for_instance_id: InstanceId,
173 ) -> Result<(), Error> {
176 community_language::dsl as cl,
177 site_language::dsl as sl,
179 let community_languages: Vec<LanguageId> = cl::community_language
180 .left_outer_join(sl::site_language.on(cl::language_id.eq(sl::language_id)))
181 .inner_join(c::community)
182 .filter(c::instance_id.eq(for_instance_id))
183 .filter(sl::language_id.is_null())
184 .select(cl::language_id)
188 for c in community_languages {
189 delete(cl::community_language.filter(cl::language_id.eq(c)))
198 for_community_id: CommunityId,
199 ) -> Result<Vec<LanguageId>, Error> {
200 use crate::schema::community_language::dsl::*;
201 let conn = &mut get_conn(pool).await?;
203 let langs = community_language
204 .filter(community_id.eq(for_community_id))
208 convert_read_languages(conn, langs).await
213 mut language_ids: Vec<LanguageId>,
214 for_community_id: CommunityId,
215 ) -> Result<(), Error> {
216 let conn = &mut get_conn(pool).await?;
218 if language_ids.is_empty() {
219 language_ids = SiteLanguage::read_local(pool).await?;
225 Box::pin(async move {
226 use crate::schema::community_language::dsl::*;
227 // Clear the current languages
228 delete(community_language.filter(community_id.eq(for_community_id)))
232 for l in language_ids {
233 let form = CommunityLanguageForm {
234 community_id: for_community_id,
237 insert_into(community_language)
239 .get_result::<Self>(conn)
249 pub async fn default_post_language(
251 community_id: CommunityId,
252 local_user_id: LocalUserId,
253 ) -> Result<Option<LanguageId>, Error> {
254 let conn = &mut get_conn(pool).await?;
255 use crate::schema::{community_language::dsl as cl, local_user_language::dsl as ul};
256 let intersection = ul::local_user_language
257 .inner_join(cl::community_language.on(ul::language_id.eq(cl::language_id)))
258 .filter(ul::local_user_id.eq(local_user_id))
259 .filter(cl::community_id.eq(community_id))
260 .select(cl::language_id)
261 .get_results::<LanguageId>(conn)
264 if intersection.len() == 1 {
265 Ok(Some(intersection[0]))
271 /// If no language is given, set all languages
272 async fn convert_update_languages(
273 conn: &mut AsyncPgConnection,
274 language_ids: Vec<LanguageId>,
275 ) -> Result<Vec<LanguageId>, Error> {
276 if language_ids.is_empty() {
278 Language::read_all_conn(conn)
289 /// If all languages are returned, return empty vec instead
290 async fn convert_read_languages(
291 conn: &mut AsyncPgConnection,
292 language_ids: Vec<LanguageId>,
293 ) -> Result<Vec<LanguageId>, Error> {
294 static ALL_LANGUAGES_COUNT: OnceCell<usize> = OnceCell::const_new();
295 let count = ALL_LANGUAGES_COUNT
296 .get_or_init(|| async {
297 use crate::schema::language::dsl::*;
298 let count: i64 = language
302 .expect("read number of languages");
307 if &language_ids.len() == count {
317 impls::actor_language::*,
319 community::{Community, CommunityInsertForm},
321 local_site::{LocalSite, LocalSiteInsertForm},
322 local_user::{LocalUser, LocalUserInsertForm},
323 person::{Person, PersonInsertForm},
324 site::{Site, SiteInsertForm},
327 utils::build_db_pool_for_tests,
329 use serial_test::serial;
331 async fn test_langs1(pool: &DbPool) -> Vec<LanguageId> {
333 Language::read_id_from_code(pool, "en").await.unwrap(),
334 Language::read_id_from_code(pool, "fr").await.unwrap(),
335 Language::read_id_from_code(pool, "ru").await.unwrap(),
338 async fn test_langs2(pool: &DbPool) -> Vec<LanguageId> {
340 Language::read_id_from_code(pool, "fi").await.unwrap(),
341 Language::read_id_from_code(pool, "se").await.unwrap(),
345 async fn create_test_site(pool: &DbPool) -> (Site, Instance) {
346 let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
348 let site_form = SiteInsertForm::builder()
349 .name("test site".to_string())
350 .instance_id(inserted_instance.id)
352 let site = Site::create(pool, &site_form).await.unwrap();
354 // Create a local site, since this is necessary for local languages
355 let local_site_form = LocalSiteInsertForm::builder().site_id(site.id).build();
356 LocalSite::create(pool, &local_site_form).await.unwrap();
358 (site, inserted_instance)
363 async fn test_convert_update_languages() {
364 let pool = &build_db_pool_for_tests().await;
366 // call with empty vec, returns all languages
367 let conn = &mut get_conn(pool).await.unwrap();
368 let converted1 = convert_update_languages(conn, vec![]).await.unwrap();
369 assert_eq!(184, converted1.len());
371 // call with nonempty vec, returns same vec
372 let test_langs = test_langs1(pool).await;
373 let converted2 = convert_update_languages(conn, test_langs.clone())
376 assert_eq!(test_langs, converted2);
380 async fn test_convert_read_languages() {
381 let pool = &build_db_pool_for_tests().await;
383 // call with all languages, returns empty vec
384 use crate::schema::language::dsl::*;
385 let conn = &mut get_conn(pool).await.unwrap();
386 let all_langs = language.select(id).get_results(conn).await.unwrap();
387 let converted1: Vec<LanguageId> = convert_read_languages(conn, all_langs).await.unwrap();
388 assert_eq!(0, converted1.len());
390 // call with nonempty vec, returns same vec
391 let test_langs = test_langs1(pool).await;
392 let converted2 = convert_read_languages(conn, test_langs.clone())
395 assert_eq!(test_langs, converted2);
400 async fn test_site_languages() {
401 let pool = &build_db_pool_for_tests().await;
403 let (site, instance) = create_test_site(pool).await;
404 let site_languages1 = SiteLanguage::read_local(pool).await.unwrap();
405 // site is created with all languages
406 assert_eq!(184, site_languages1.len());
408 let test_langs = test_langs1(pool).await;
409 SiteLanguage::update(pool, test_langs.clone(), &site)
413 let site_languages2 = SiteLanguage::read_local(pool).await.unwrap();
414 // after update, site only has new languages
415 assert_eq!(test_langs, site_languages2);
417 Site::delete(pool, site.id).await.unwrap();
418 Instance::delete(pool, instance.id).await.unwrap();
419 LocalSite::delete(pool).await.unwrap();
424 async fn test_user_languages() {
425 let pool = &build_db_pool_for_tests().await;
427 let (site, instance) = create_test_site(pool).await;
428 let test_langs = test_langs1(pool).await;
429 SiteLanguage::update(pool, test_langs.clone(), &site)
433 let person_form = PersonInsertForm::builder()
434 .name("my test person".to_string())
435 .public_key("pubkey".to_string())
436 .instance_id(instance.id)
438 let person = Person::create(pool, &person_form).await.unwrap();
439 let local_user_form = LocalUserInsertForm::builder()
440 .person_id(person.id)
441 .password_encrypted("my_pw".to_string())
444 let local_user = LocalUser::create(pool, &local_user_form).await.unwrap();
445 let local_user_langs1 = LocalUserLanguage::read(pool, local_user.id).await.unwrap();
447 // new user should be initialized with site languages
448 assert_eq!(test_langs, local_user_langs1);
450 // update user languages
451 let test_langs2 = test_langs2(pool).await;
452 LocalUserLanguage::update(pool, test_langs2, local_user.id)
455 let local_user_langs2 = LocalUserLanguage::read(pool, local_user.id).await.unwrap();
456 assert_eq!(2, local_user_langs2.len());
458 Person::delete(pool, person.id).await.unwrap();
459 LocalUser::delete(pool, local_user.id).await.unwrap();
460 Site::delete(pool, site.id).await.unwrap();
461 LocalSite::delete(pool).await.unwrap();
462 Instance::delete(pool, instance.id).await.unwrap();
467 async fn test_community_languages() {
468 let pool = &build_db_pool_for_tests().await;
469 let (site, instance) = create_test_site(pool).await;
470 let test_langs = test_langs1(pool).await;
471 SiteLanguage::update(pool, test_langs.clone(), &site)
475 let read_site_langs = SiteLanguage::read(pool, site.id).await.unwrap();
476 assert_eq!(test_langs, read_site_langs);
478 // Test the local ones are the same
479 let read_local_site_langs = SiteLanguage::read_local(pool).await.unwrap();
480 assert_eq!(test_langs, read_local_site_langs);
482 let community_form = CommunityInsertForm::builder()
483 .name("test community".to_string())
484 .title("test community".to_string())
485 .public_key("pubkey".to_string())
486 .instance_id(instance.id)
488 let community = Community::create(pool, &community_form).await.unwrap();
489 let community_langs1 = CommunityLanguage::read(pool, community.id).await.unwrap();
491 // community is initialized with site languages
492 assert_eq!(test_langs, community_langs1);
495 CommunityLanguage::is_allowed_community_language(pool, Some(test_langs[0]), community.id)
497 assert!(allowed_lang1.is_ok());
499 let test_langs2 = test_langs2(pool).await;
501 CommunityLanguage::is_allowed_community_language(pool, Some(test_langs2[0]), community.id)
503 assert!(allowed_lang2.is_err());
505 // limit site languages to en, fi. after this, community languages should be updated to
506 // intersection of old languages (en, fr, ru) and (en, fi), which is only fi.
507 SiteLanguage::update(pool, vec![test_langs[0], test_langs2[0]], &site)
510 let community_langs2 = CommunityLanguage::read(pool, community.id).await.unwrap();
511 assert_eq!(vec![test_langs[0]], community_langs2);
513 // update community languages to different ones
514 CommunityLanguage::update(pool, test_langs2.clone(), community.id)
517 let community_langs3 = CommunityLanguage::read(pool, community.id).await.unwrap();
518 assert_eq!(test_langs2, community_langs3);
520 Community::delete(pool, community.id).await.unwrap();
521 Site::delete(pool, site.id).await.unwrap();
522 LocalSite::delete(pool).await.unwrap();
523 Instance::delete(pool, instance.id).await.unwrap();
528 async fn test_default_post_language() {
529 let pool = &build_db_pool_for_tests().await;
530 let (site, instance) = create_test_site(pool).await;
531 let test_langs = test_langs1(pool).await;
532 let test_langs2 = test_langs2(pool).await;
534 let community_form = CommunityInsertForm::builder()
535 .name("test community".to_string())
536 .title("test community".to_string())
537 .public_key("pubkey".to_string())
538 .instance_id(instance.id)
540 let community = Community::create(pool, &community_form).await.unwrap();
541 CommunityLanguage::update(pool, test_langs, community.id)
545 let person_form = PersonInsertForm::builder()
546 .name("my test person".to_string())
547 .public_key("pubkey".to_string())
548 .instance_id(instance.id)
550 let person = Person::create(pool, &person_form).await.unwrap();
551 let local_user_form = LocalUserInsertForm::builder()
552 .person_id(person.id)
553 .password_encrypted("my_pw".to_string())
555 let local_user = LocalUser::create(pool, &local_user_form).await.unwrap();
556 LocalUserLanguage::update(pool, test_langs2, local_user.id)
560 // no overlap in user/community languages, so no default language for post
561 let def1 = default_post_language(pool, community.id, local_user.id)
564 assert_eq!(None, def1);
566 let ru = Language::read_id_from_code(pool, "ru").await.unwrap();
567 let test_langs3 = vec![
569 Language::read_id_from_code(pool, "fi").await.unwrap(),
570 Language::read_id_from_code(pool, "se").await.unwrap(),
572 LocalUserLanguage::update(pool, test_langs3, local_user.id)
576 // this time, both have ru as common lang
577 let def2 = default_post_language(pool, community.id, local_user.id)
580 assert_eq!(Some(ru), def2);
582 Person::delete(pool, person.id).await.unwrap();
583 Community::delete(pool, community.id).await.unwrap();
584 LocalUser::delete(pool, local_user.id).await.unwrap();
585 Site::delete(pool, site.id).await.unwrap();
586 LocalSite::delete(pool).await.unwrap();
587 Instance::delete(pool, instance.id).await.unwrap();