3 newtypes::{CommunityId, LanguageId, LocalUserId, SiteId},
4 source::{actor_language::*, language::Language},
17 use lemmy_utils::error::LemmyError;
19 impl LocalUserLanguage {
21 conn: &mut PgConnection,
22 for_local_user_id: LocalUserId,
23 ) -> Result<Vec<LanguageId>, Error> {
24 use crate::schema::local_user_language::dsl::*;
27 .filter(local_user_id.eq(for_local_user_id))
32 /// Update the user's languages.
34 /// If no language_id vector is given, it will show all languages
36 conn: &mut PgConnection,
37 language_ids: Vec<LanguageId>,
38 for_local_user_id: LocalUserId,
39 ) -> Result<(), Error> {
40 conn.build_transaction().read_write().run(|conn| {
41 use crate::schema::local_user_language::dsl::*;
42 // Clear the current user languages
43 delete(local_user_language.filter(local_user_id.eq(for_local_user_id))).execute(conn)?;
45 let lang_ids = update_languages(conn, language_ids)?;
47 let form = LocalUserLanguageForm {
48 local_user_id: for_local_user_id,
51 insert_into(local_user_language)
53 .get_result::<Self>(conn)?;
61 pub fn read_local(conn: &mut PgConnection) -> Result<Vec<LanguageId>, Error> {
62 use crate::schema::{site, site_language::dsl::*};
63 // TODO: remove this subquery once site.local column is added
64 let subquery = crate::schema::site::dsl::site
70 .filter(site_id.eq_any(subquery))
75 pub fn read(conn: &mut PgConnection, for_site_id: SiteId) -> Result<Vec<LanguageId>, Error> {
76 use crate::schema::site_language::dsl::*;
78 .filter(site_id.eq(for_site_id))
84 conn: &mut PgConnection,
85 language_ids: Vec<LanguageId>,
87 ) -> Result<(), Error> {
88 conn.build_transaction().read_write().run(|conn| {
89 use crate::schema::site_language::dsl::*;
90 // Clear the current languages
91 delete(site_language.filter(site_id.eq(for_site_id))).execute(conn)?;
93 let lang_ids = update_languages(conn, language_ids)?;
95 let form = SiteLanguageForm {
99 insert_into(site_language)
101 .get_result::<Self>(conn)?;
104 CommunityLanguage::limit_languages(conn)?;
111 impl CommunityLanguage {
112 /// Returns true if the given language is one of configured languages for given community
113 pub fn is_allowed_community_language(
114 conn: &mut PgConnection,
115 for_language_id: Option<LanguageId>,
116 for_community_id: CommunityId,
117 ) -> Result<(), LemmyError> {
118 use crate::schema::community_language::dsl::*;
119 if let Some(for_language_id) = for_language_id {
120 let is_allowed = select(exists(
122 .filter(language_id.eq(for_language_id))
123 .filter(community_id.eq(for_community_id)),
130 Err(LemmyError::from_message("language_not_allowed"))
137 /// When site languages are updated, delete all languages of local communities which are not
138 /// also part of site languages. This is because post/comment language is only checked against
139 /// community language, and it shouldnt be possible to post content in languages which are not
140 /// allowed by local site.
141 fn limit_languages(conn: &mut PgConnection) -> Result<(), Error> {
144 community_language::dsl as cl,
145 site_language::dsl as sl,
147 let community_languages: Vec<LanguageId> = cl::community_language
148 .left_outer_join(sl::site_language.on(cl::language_id.eq(sl::language_id)))
149 .inner_join(c::community)
151 .filter(sl::language_id.is_null())
152 .select(cl::language_id)
155 for c in community_languages {
156 delete(cl::community_language.filter(cl::language_id.eq(c))).execute(conn)?;
162 conn: &mut PgConnection,
163 for_community_id: CommunityId,
164 ) -> Result<Vec<LanguageId>, Error> {
165 use crate::schema::community_language::dsl::*;
167 .filter(community_id.eq(for_community_id))
173 conn: &mut PgConnection,
174 mut language_ids: Vec<LanguageId>,
175 for_community_id: CommunityId,
176 ) -> Result<(), Error> {
177 conn.build_transaction().read_write().run(|conn| {
178 use crate::schema::community_language::dsl::*;
179 // Clear the current languages
180 delete(community_language.filter(community_id.eq(for_community_id))).execute(conn)?;
182 if language_ids.is_empty() {
183 language_ids = SiteLanguage::read_local(conn)?;
185 for l in language_ids {
186 let form = CommunityLanguageForm {
187 community_id: for_community_id,
190 insert_into(community_language)
192 .get_result::<Self>(conn)?;
199 pub fn default_post_language(
200 conn: &mut PgConnection,
201 community_id: CommunityId,
202 local_user_id: LocalUserId,
203 ) -> Result<Option<LanguageId>, Error> {
204 use crate::schema::{community_language::dsl as cl, local_user_language::dsl as ul};
205 let intersection = ul::local_user_language
206 .inner_join(cl::community_language.on(ul::language_id.eq(cl::language_id)))
207 .filter(ul::local_user_id.eq(local_user_id))
208 .filter(cl::community_id.eq(community_id))
209 .select(cl::language_id)
210 .get_results::<LanguageId>(conn)?;
212 if intersection.len() == 1 {
213 Ok(Some(intersection[0]))
219 // If no language is given, set all languages
221 conn: &mut PgConnection,
222 language_ids: Vec<LanguageId>,
223 ) -> Result<Vec<LanguageId>, Error> {
224 if language_ids.is_empty() {
226 Language::read_all(conn)?
239 impls::actor_language::*,
241 community::{Community, CommunityForm},
242 local_user::{LocalUser, LocalUserForm},
243 person::{Person, PersonForm},
244 site::{Site, SiteForm},
247 utils::establish_unpooled_connection,
249 use serial_test::serial;
251 fn test_langs1(conn: &mut PgConnection) -> Vec<LanguageId> {
253 Language::read_id_from_code(conn, "en").unwrap(),
254 Language::read_id_from_code(conn, "fr").unwrap(),
255 Language::read_id_from_code(conn, "ru").unwrap(),
258 fn test_langs2(conn: &mut PgConnection) -> Vec<LanguageId> {
260 Language::read_id_from_code(conn, "fi").unwrap(),
261 Language::read_id_from_code(conn, "se").unwrap(),
265 fn create_test_site(conn: &mut PgConnection) -> Site {
266 let site_form = SiteForm {
267 name: "test site".to_string(),
270 Site::create(conn, &site_form).unwrap()
275 fn test_update_languages() {
276 let conn = &mut establish_unpooled_connection();
278 // call with empty vec, returns all languages
279 let updated1 = update_languages(conn, vec![]).unwrap();
280 assert_eq!(184, updated1.len());
282 // call with nonempty vec, returns same vec
283 let test_langs = test_langs1(conn);
284 let updated2 = update_languages(conn, test_langs.clone()).unwrap();
285 assert_eq!(test_langs, updated2);
290 fn test_site_languages() {
291 let conn = &mut establish_unpooled_connection();
293 let site = create_test_site(conn);
294 let site_languages1 = SiteLanguage::read_local(conn).unwrap();
295 // site is created with all languages
296 assert_eq!(184, site_languages1.len());
298 let test_langs = test_langs1(conn);
299 SiteLanguage::update(conn, test_langs.clone(), site.id).unwrap();
301 let site_languages2 = SiteLanguage::read_local(conn).unwrap();
302 // after update, site only has new languages
303 assert_eq!(test_langs, site_languages2);
305 Site::delete(conn, site.id).unwrap();
310 fn test_user_languages() {
311 let conn = &mut establish_unpooled_connection();
313 let site = create_test_site(conn);
314 let test_langs = test_langs1(conn);
315 SiteLanguage::update(conn, test_langs.clone(), site.id).unwrap();
317 let person_form = PersonForm {
318 name: "my test person".to_string(),
319 public_key: Some("pubkey".to_string()),
322 let person = Person::create(conn, &person_form).unwrap();
323 let local_user_form = LocalUserForm {
324 person_id: Some(person.id),
325 password_encrypted: Some("my_pw".to_string()),
328 let local_user = LocalUser::create(conn, &local_user_form).unwrap();
329 let local_user_langs1 = LocalUserLanguage::read(conn, local_user.id).unwrap();
331 // new user should be initialized with site languages
332 assert_eq!(test_langs, local_user_langs1);
334 // update user languages
335 let test_langs2 = test_langs2(conn);
336 LocalUserLanguage::update(conn, test_langs2, local_user.id).unwrap();
337 let local_user_langs2 = LocalUserLanguage::read(conn, local_user.id).unwrap();
338 assert_eq!(2, local_user_langs2.len());
340 Person::delete(conn, person.id).unwrap();
341 LocalUser::delete(conn, local_user.id).unwrap();
342 Site::delete(conn, site.id).unwrap();
347 fn test_community_languages() {
348 let conn = &mut establish_unpooled_connection();
349 let site = create_test_site(conn);
350 let test_langs = test_langs1(conn);
351 SiteLanguage::update(conn, test_langs.clone(), site.id).unwrap();
353 let community_form = CommunityForm {
354 name: "test community".to_string(),
355 title: "test community".to_string(),
356 public_key: Some("pubkey".to_string()),
359 let community = Community::create(conn, &community_form).unwrap();
360 let community_langs1 = CommunityLanguage::read(conn, community.id).unwrap();
361 // community is initialized with site languages
362 assert_eq!(test_langs, community_langs1);
365 CommunityLanguage::is_allowed_community_language(conn, Some(test_langs[0]), community.id);
366 assert!(allowed_lang1.is_ok());
368 let test_langs2 = test_langs2(conn);
370 CommunityLanguage::is_allowed_community_language(conn, Some(test_langs2[0]), community.id);
371 assert!(allowed_lang2.is_err());
373 // limit site languages to en, fi. after this, community languages should be updated to
374 // intersection of old languages (en, fr, ru) and (en, fi), which is only fi.
375 SiteLanguage::update(conn, vec![test_langs[0], test_langs2[0]], site.id).unwrap();
376 let community_langs2 = CommunityLanguage::read(conn, community.id).unwrap();
377 assert_eq!(vec![test_langs[0]], community_langs2);
379 // update community languages to different ones
380 CommunityLanguage::update(conn, test_langs2.clone(), community.id).unwrap();
381 let community_langs3 = CommunityLanguage::read(conn, community.id).unwrap();
382 assert_eq!(test_langs2, community_langs3);
384 Site::delete(conn, site.id).unwrap();
385 Community::delete(conn, community.id).unwrap();
390 fn test_default_post_language() {
391 let conn = &mut establish_unpooled_connection();
392 let test_langs = test_langs1(conn);
393 let test_langs2 = test_langs2(conn);
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 CommunityLanguage::update(conn, test_langs, community.id).unwrap();
404 let person_form = PersonForm {
405 name: "my test person".to_string(),
406 public_key: Some("pubkey".to_string()),
409 let person = Person::create(conn, &person_form).unwrap();
410 let local_user_form = LocalUserForm {
411 person_id: Some(person.id),
412 password_encrypted: Some("my_pw".to_string()),
415 let local_user = LocalUser::create(conn, &local_user_form).unwrap();
416 LocalUserLanguage::update(conn, test_langs2, local_user.id).unwrap();
418 // no overlap in user/community languages, so no default language for post
419 let def1 = default_post_language(conn, community.id, local_user.id).unwrap();
420 assert_eq!(None, def1);
422 let ru = Language::read_id_from_code(conn, "ru").unwrap();
423 let test_langs3 = vec![
425 Language::read_id_from_code(conn, "fi").unwrap(),
426 Language::read_id_from_code(conn, "se").unwrap(),
428 LocalUserLanguage::update(conn, test_langs3, local_user.id).unwrap();
430 // this time, both have ru as common lang
431 let def2 = default_post_language(conn, community.id, local_user.id).unwrap();
432 assert_eq!(Some(ru), def2);
434 Person::delete(conn, person.id).unwrap();
435 Community::delete(conn, community.id).unwrap();
436 LocalUser::delete(conn, local_user.id).unwrap();