3 newtypes::{CommunityId, LanguageId, LocalUserId, SiteId},
4 source::{actor_language::*, language::Language},
17 use lemmy_utils::error::LemmyError;
18 use once_cell::sync::OnceCell;
20 impl LocalUserLanguage {
22 conn: &mut PgConnection,
23 for_local_user_id: LocalUserId,
24 ) -> Result<Vec<LanguageId>, Error> {
25 use crate::schema::local_user_language::dsl::*;
27 let langs = local_user_language
28 .filter(local_user_id.eq(for_local_user_id))
31 convert_read_languages(conn, langs)
34 /// Update the user's languages.
36 /// If no language_id vector is given, it will show all languages
38 conn: &mut PgConnection,
39 language_ids: Vec<LanguageId>,
40 for_local_user_id: LocalUserId,
41 ) -> Result<(), Error> {
42 conn.build_transaction().read_write().run(|conn| {
43 use crate::schema::local_user_language::dsl::*;
44 // Clear the current user languages
45 delete(local_user_language.filter(local_user_id.eq(for_local_user_id))).execute(conn)?;
47 let lang_ids = convert_update_languages(conn, language_ids)?;
49 let form = LocalUserLanguageForm {
50 local_user_id: for_local_user_id,
53 insert_into(local_user_language)
55 .get_result::<Self>(conn)?;
63 pub fn read_local(conn: &mut PgConnection) -> Result<Vec<LanguageId>, Error> {
64 use crate::schema::{site, site_language::dsl::*};
65 // TODO: remove this subquery once site.local column is added
66 let subquery = crate::schema::site::dsl::site
72 .filter(site_id.eq_any(subquery))
77 pub fn read(conn: &mut PgConnection, for_site_id: SiteId) -> Result<Vec<LanguageId>, Error> {
78 use crate::schema::site_language::dsl::*;
79 let langs = site_language
80 .filter(site_id.eq(for_site_id))
83 convert_read_languages(conn, langs)
87 conn: &mut PgConnection,
88 language_ids: Vec<LanguageId>,
90 ) -> Result<(), Error> {
91 conn.build_transaction().read_write().run(|conn| {
92 use crate::schema::site_language::dsl::*;
93 // Clear the current languages
94 delete(site_language.filter(site_id.eq(for_site_id))).execute(conn)?;
96 let lang_ids = convert_update_languages(conn, language_ids)?;
98 let form = SiteLanguageForm {
102 insert_into(site_language)
104 .get_result::<Self>(conn)?;
107 CommunityLanguage::limit_languages(conn)?;
114 impl CommunityLanguage {
115 /// Returns true if the given language is one of configured languages for given community
116 pub fn is_allowed_community_language(
117 conn: &mut PgConnection,
118 for_language_id: Option<LanguageId>,
119 for_community_id: CommunityId,
120 ) -> Result<(), LemmyError> {
121 use crate::schema::community_language::dsl::*;
122 if let Some(for_language_id) = for_language_id {
123 let is_allowed = select(exists(
125 .filter(language_id.eq(for_language_id))
126 .filter(community_id.eq(for_community_id)),
133 Err(LemmyError::from_message("language_not_allowed"))
140 /// When site languages are updated, delete all languages of local communities which are not
141 /// also part of site languages. This is because post/comment language is only checked against
142 /// community language, and it shouldnt be possible to post content in languages which are not
143 /// allowed by local site.
144 fn limit_languages(conn: &mut PgConnection) -> Result<(), Error> {
147 community_language::dsl as cl,
148 site_language::dsl as sl,
150 let community_languages: Vec<LanguageId> = cl::community_language
151 .left_outer_join(sl::site_language.on(cl::language_id.eq(sl::language_id)))
152 .inner_join(c::community)
154 .filter(sl::language_id.is_null())
155 .select(cl::language_id)
158 for c in community_languages {
159 delete(cl::community_language.filter(cl::language_id.eq(c))).execute(conn)?;
165 conn: &mut PgConnection,
166 for_community_id: CommunityId,
167 ) -> Result<Vec<LanguageId>, Error> {
168 use crate::schema::community_language::dsl::*;
169 let langs = community_language
170 .filter(community_id.eq(for_community_id))
173 convert_read_languages(conn, langs)
177 conn: &mut PgConnection,
178 mut language_ids: Vec<LanguageId>,
179 for_community_id: CommunityId,
180 ) -> Result<(), Error> {
181 conn.build_transaction().read_write().run(|conn| {
182 use crate::schema::community_language::dsl::*;
183 // Clear the current languages
184 delete(community_language.filter(community_id.eq(for_community_id))).execute(conn)?;
186 if language_ids.is_empty() {
187 language_ids = SiteLanguage::read_local(conn)?;
189 for l in language_ids {
190 let form = CommunityLanguageForm {
191 community_id: for_community_id,
194 insert_into(community_language)
196 .get_result::<Self>(conn)?;
203 pub fn default_post_language(
204 conn: &mut PgConnection,
205 community_id: CommunityId,
206 local_user_id: LocalUserId,
207 ) -> Result<Option<LanguageId>, Error> {
208 use crate::schema::{community_language::dsl as cl, local_user_language::dsl as ul};
209 let intersection = ul::local_user_language
210 .inner_join(cl::community_language.on(ul::language_id.eq(cl::language_id)))
211 .filter(ul::local_user_id.eq(local_user_id))
212 .filter(cl::community_id.eq(community_id))
213 .select(cl::language_id)
214 .get_results::<LanguageId>(conn)?;
216 if intersection.len() == 1 {
217 Ok(Some(intersection[0]))
223 /// If no language is given, set all languages
224 fn convert_update_languages(
225 conn: &mut PgConnection,
226 language_ids: Vec<LanguageId>,
227 ) -> Result<Vec<LanguageId>, Error> {
228 if language_ids.is_empty() {
230 Language::read_all(conn)?
240 /// If all languages are returned, return empty vec instead
241 fn convert_read_languages(
242 conn: &mut PgConnection,
243 language_ids: Vec<LanguageId>,
244 ) -> Result<Vec<LanguageId>, Error> {
245 static ALL_LANGUAGES_COUNT: OnceCell<usize> = OnceCell::new();
246 let count = ALL_LANGUAGES_COUNT.get_or_init(|| {
247 use crate::schema::language::dsl::*;
248 let count: i64 = language
251 .expect("read number of languages");
255 if &language_ids.len() == count {
265 impls::actor_language::*,
267 community::{Community, CommunityForm},
268 local_user::{LocalUser, LocalUserForm},
269 person::{Person, PersonForm},
270 site::{Site, SiteForm},
273 utils::establish_unpooled_connection,
275 use serial_test::serial;
277 fn test_langs1(conn: &mut PgConnection) -> Vec<LanguageId> {
279 Language::read_id_from_code(conn, "en").unwrap(),
280 Language::read_id_from_code(conn, "fr").unwrap(),
281 Language::read_id_from_code(conn, "ru").unwrap(),
284 fn test_langs2(conn: &mut PgConnection) -> Vec<LanguageId> {
286 Language::read_id_from_code(conn, "fi").unwrap(),
287 Language::read_id_from_code(conn, "se").unwrap(),
291 fn create_test_site(conn: &mut PgConnection) -> Site {
292 let site_form = SiteForm {
293 name: "test site".to_string(),
296 Site::create(conn, &site_form).unwrap()
301 fn test_convert_update_languages() {
302 let conn = &mut establish_unpooled_connection();
304 // call with empty vec, returns all languages
305 let converted1 = convert_update_languages(conn, vec![]).unwrap();
306 assert_eq!(184, converted1.len());
308 // call with nonempty vec, returns same vec
309 let test_langs = test_langs1(conn);
310 let converted2 = convert_update_languages(conn, test_langs.clone()).unwrap();
311 assert_eq!(test_langs, converted2);
315 fn test_convert_read_languages() {
316 let conn = &mut establish_unpooled_connection();
318 // call with all languages, returns empty vec
319 use crate::schema::language::dsl::*;
320 let all_langs = language.select(id).get_results(conn).unwrap();
321 let converted1: Vec<LanguageId> = convert_read_languages(conn, all_langs).unwrap();
322 assert_eq!(0, converted1.len());
324 // call with nonempty vec, returns same vec
325 let test_langs = test_langs1(conn);
326 let converted2 = convert_read_languages(conn, test_langs.clone()).unwrap();
327 assert_eq!(test_langs, converted2);
332 fn test_site_languages() {
333 let conn = &mut establish_unpooled_connection();
335 let site = create_test_site(conn);
336 let site_languages1 = SiteLanguage::read_local(conn).unwrap();
337 // site is created with all languages
338 assert_eq!(184, site_languages1.len());
340 let test_langs = test_langs1(conn);
341 SiteLanguage::update(conn, test_langs.clone(), site.id).unwrap();
343 let site_languages2 = SiteLanguage::read_local(conn).unwrap();
344 // after update, site only has new languages
345 assert_eq!(test_langs, site_languages2);
347 Site::delete(conn, site.id).unwrap();
352 fn test_user_languages() {
353 let conn = &mut establish_unpooled_connection();
355 let site = create_test_site(conn);
356 let test_langs = test_langs1(conn);
357 SiteLanguage::update(conn, test_langs.clone(), site.id).unwrap();
359 let person_form = PersonForm {
360 name: "my test person".to_string(),
361 public_key: Some("pubkey".to_string()),
364 let person = Person::create(conn, &person_form).unwrap();
365 let local_user_form = LocalUserForm {
366 person_id: Some(person.id),
367 password_encrypted: Some("my_pw".to_string()),
370 let local_user = LocalUser::create(conn, &local_user_form).unwrap();
371 let local_user_langs1 = LocalUserLanguage::read(conn, local_user.id).unwrap();
373 // new user should be initialized with site languages
374 assert_eq!(test_langs, local_user_langs1);
376 // update user languages
377 let test_langs2 = test_langs2(conn);
378 LocalUserLanguage::update(conn, test_langs2, local_user.id).unwrap();
379 let local_user_langs2 = LocalUserLanguage::read(conn, local_user.id).unwrap();
380 assert_eq!(2, local_user_langs2.len());
382 Person::delete(conn, person.id).unwrap();
383 LocalUser::delete(conn, local_user.id).unwrap();
384 Site::delete(conn, site.id).unwrap();
389 fn test_community_languages() {
390 let conn = &mut establish_unpooled_connection();
391 let site = create_test_site(conn);
392 let test_langs = test_langs1(conn);
393 SiteLanguage::update(conn, test_langs.clone(), site.id).unwrap();
395 let community_form = CommunityForm {
396 name: "test community".to_string(),
397 title: "test community".to_string(),
398 public_key: Some("pubkey".to_string()),
401 let community = Community::create(conn, &community_form).unwrap();
402 let community_langs1 = CommunityLanguage::read(conn, community.id).unwrap();
403 // community is initialized with site languages
404 assert_eq!(test_langs, community_langs1);
407 CommunityLanguage::is_allowed_community_language(conn, Some(test_langs[0]), community.id);
408 assert!(allowed_lang1.is_ok());
410 let test_langs2 = test_langs2(conn);
412 CommunityLanguage::is_allowed_community_language(conn, Some(test_langs2[0]), community.id);
413 assert!(allowed_lang2.is_err());
415 // limit site languages to en, fi. after this, community languages should be updated to
416 // intersection of old languages (en, fr, ru) and (en, fi), which is only fi.
417 SiteLanguage::update(conn, vec![test_langs[0], test_langs2[0]], site.id).unwrap();
418 let community_langs2 = CommunityLanguage::read(conn, community.id).unwrap();
419 assert_eq!(vec![test_langs[0]], community_langs2);
421 // update community languages to different ones
422 CommunityLanguage::update(conn, test_langs2.clone(), community.id).unwrap();
423 let community_langs3 = CommunityLanguage::read(conn, community.id).unwrap();
424 assert_eq!(test_langs2, community_langs3);
426 Site::delete(conn, site.id).unwrap();
427 Community::delete(conn, community.id).unwrap();
432 fn test_default_post_language() {
433 let conn = &mut establish_unpooled_connection();
434 let test_langs = test_langs1(conn);
435 let test_langs2 = test_langs2(conn);
437 let community_form = CommunityForm {
438 name: "test community".to_string(),
439 title: "test community".to_string(),
440 public_key: Some("pubkey".to_string()),
443 let community = Community::create(conn, &community_form).unwrap();
444 CommunityLanguage::update(conn, test_langs, community.id).unwrap();
446 let person_form = PersonForm {
447 name: "my test person".to_string(),
448 public_key: Some("pubkey".to_string()),
451 let person = Person::create(conn, &person_form).unwrap();
452 let local_user_form = LocalUserForm {
453 person_id: Some(person.id),
454 password_encrypted: Some("my_pw".to_string()),
457 let local_user = LocalUser::create(conn, &local_user_form).unwrap();
458 LocalUserLanguage::update(conn, test_langs2, local_user.id).unwrap();
460 // no overlap in user/community languages, so no default language for post
461 let def1 = default_post_language(conn, community.id, local_user.id).unwrap();
462 assert_eq!(None, def1);
464 let ru = Language::read_id_from_code(conn, "ru").unwrap();
465 let test_langs3 = vec![
467 Language::read_id_from_code(conn, "fi").unwrap(),
468 Language::read_id_from_code(conn, "se").unwrap(),
470 LocalUserLanguage::update(conn, test_langs3, local_user.id).unwrap();
472 // this time, both have ru as common lang
473 let def2 = default_post_language(conn, community.id, local_user.id).unwrap();
474 assert_eq!(Some(ru), def2);
476 Person::delete(conn, person.id).unwrap();
477 Community::delete(conn, community.id).unwrap();
478 LocalUser::delete(conn, local_user.id).unwrap();