diff --git a/crates/api/src/local_user/save_settings.rs b/crates/api/src/local_user/save_settings.rs index b2bcb82c4..8abe01328 100644 --- a/crates/api/src/local_user/save_settings.rs +++ b/crates/api/src/local_user/save_settings.rs @@ -5,8 +5,9 @@ use lemmy_api_common::{ utils::{blocking, get_local_user_view_from_jwt, send_verification_email}, }; use lemmy_db_schema::{ - newtypes::LanguageIdentifier, + newtypes::LanguageId, source::{ + language::Language, local_user::{LocalUser, LocalUserForm}, person::{Person, PersonForm}, site::Site, @@ -118,16 +119,25 @@ impl Perform for SaveUserSettings { }) .await? .map_err(|e| LemmyError::from_error_message(e, "user_already_exists"))?; - let mut discussion_languages: Vec = data - .discussion_languages - .clone() - .into_iter() - .flatten() - .map(|l| LanguageIdentifier::new(&l)) - .collect(); - if discussion_languages.is_empty() { - discussion_languages = LanguageIdentifier::all_languages() - } + let discussion_languages: Option> = + if let Some(discussion_languages) = data.discussion_languages.clone() { + if discussion_languages.len() > 5 { + return Err(LemmyError::from_message("max_languages_is_five")); + } + + let mut language_ids = vec![]; + for l in discussion_languages { + language_ids.push( + blocking(context.pool(), move |conn| { + Language::read_id_from_code(conn, l) + }) + .await??, + ); + } + Some(language_ids) + } else { + None + }; let local_user_form = LocalUserForm { person_id: Some(person_id), @@ -146,7 +156,7 @@ impl Perform for SaveUserSettings { send_notifications_to_email: data.send_notifications_to_email, email_verified: None, accepted_application: None, - discussion_languages: Some(discussion_languages), + discussion_languages, }; let local_user_res = blocking(context.pool(), move |conn| { diff --git a/crates/api/src/site/leave_admin.rs b/crates/api/src/site/leave_admin.rs index 55b49fc7d..1756359a0 100644 --- a/crates/api/src/site/leave_admin.rs +++ b/crates/api/src/site/leave_admin.rs @@ -6,6 +6,7 @@ use lemmy_api_common::{ }; use lemmy_db_schema::{ source::{ + language::Language, moderator::{ModAdd, ModAddForm}, person::Person, }, @@ -60,6 +61,8 @@ impl Perform for LeaveAdmin { let federated_instances = build_federated_instances(context.pool(), &context.settings()).await?; + let all_languages = blocking(context.pool(), Language::read_all).await??; + Ok(GetSiteResponse { site_view: Some(site_view), admins, @@ -67,6 +70,7 @@ impl Perform for LeaveAdmin { version: version::VERSION.to_string(), my_user: None, federated_instances, + all_languages, }) } } diff --git a/crates/api_common/src/site.rs b/crates/api_common/src/site.rs index f61d34055..924d836a7 100644 --- a/crates/api_common/src/site.rs +++ b/crates/api_common/src/site.rs @@ -1,6 +1,7 @@ use crate::sensitive::Sensitive; use lemmy_db_schema::{ newtypes::{CommentId, CommunityId, PersonId, PostId}, + source::language::Language, ListingType, SearchType, SortType, @@ -163,6 +164,7 @@ pub struct GetSiteResponse { pub version: String, pub my_user: Option, pub federated_instances: Option, // Federation may be disabled + pub all_languages: Vec, } #[derive(Debug, Serialize, Deserialize, Clone)] diff --git a/crates/api_crud/src/post/create.rs b/crates/api_crud/src/post/create.rs index c067f649c..7a0599e1b 100644 --- a/crates/api_crud/src/post/create.rs +++ b/crates/api_crud/src/post/create.rs @@ -19,9 +19,9 @@ use lemmy_apub::{ EndpointType, }; use lemmy_db_schema::{ - newtypes::LanguageIdentifier, source::{ community::Community, + language::Language, post::{Post, PostForm, PostLike, PostLikeForm}, }, traits::{Crud, Likeable}, @@ -92,6 +92,25 @@ impl PerformCrud for CreatePost { let (embed_title, embed_description, embed_video_url) = metadata_res .map(|u| (u.title, u.description, u.embed_video_url)) .unwrap_or_default(); + let language = data.language.clone(); + let mut language = blocking(context.pool(), move |conn| { + Language::read_id_from_code_opt(conn, language) + }) + .await??; + // if user only speaks one language, use that as post language. otherwise, set it as "undetermined" + if language.is_none() { + let user_langs = local_user_view.local_user.discussion_languages; + language = if user_langs.len() == 1 { + Some(user_langs[0]) + } else { + Some( + blocking(context.pool(), move |conn| { + Language::read_undetermined(conn) + }) + .await??, + ) + }; + }; let post_form = PostForm { name: data.name.trim().to_owned(), @@ -104,7 +123,7 @@ impl PerformCrud for CreatePost { embed_description, embed_video_url, thumbnail_url, - language: data.language.as_ref().map(|l| LanguageIdentifier::new(l)), + language, ..PostForm::default() }; diff --git a/crates/api_crud/src/post/update.rs b/crates/api_crud/src/post/update.rs index 551b0144f..f69210c10 100644 --- a/crates/api_crud/src/post/update.rs +++ b/crates/api_crud/src/post/update.rs @@ -14,8 +14,10 @@ use lemmy_apub::protocol::activities::{ CreateOrUpdateType, }; use lemmy_db_schema::{ - newtypes::LanguageIdentifier, - source::post::{Post, PostForm}, + source::{ + language::Language, + post::{Post, PostForm}, + }, traits::Crud, utils::naive_now, }; @@ -75,6 +77,11 @@ impl PerformCrud for EditPost { let (embed_title, embed_description, embed_video_url) = metadata_res .map(|u| (u.title, u.description, u.embed_video_url)) .unwrap_or_default(); + let language = data.language.clone(); + let language = blocking(context.pool(), move |conn| { + Language::read_id_from_code_opt(conn, language) + }) + .await??; let post_form = PostForm { creator_id: orig_post.creator_id.to_owned(), @@ -88,7 +95,7 @@ impl PerformCrud for EditPost { embed_description, embed_video_url, thumbnail_url, - language: data.language.as_ref().map(|l| LanguageIdentifier::new(l)), + language, ..PostForm::default() }; diff --git a/crates/api_crud/src/site/read.rs b/crates/api_crud/src/site/read.rs index 1ef84a91f..607d64458 100644 --- a/crates/api_crud/src/site/read.rs +++ b/crates/api_crud/src/site/read.rs @@ -5,6 +5,7 @@ use lemmy_api_common::{ site::{CreateSite, GetSite, GetSiteResponse, MyUserInfo}, utils::{blocking, build_federated_instances, get_local_user_settings_view_from_jwt_opt}, }; +use lemmy_db_schema::source::language::Language; use lemmy_db_views::structs::SiteView; use lemmy_db_views_actor::structs::{ CommunityBlockView, @@ -123,6 +124,8 @@ impl PerformCrud for GetSite { let federated_instances = build_federated_instances(context.pool(), &context.settings()).await?; + let all_languages = blocking(context.pool(), Language::read_all).await??; + Ok(GetSiteResponse { site_view, admins, @@ -130,6 +133,7 @@ impl PerformCrud for GetSite { version: version::VERSION.to_string(), my_user, federated_instances, + all_languages, }) } } diff --git a/crates/api_crud/src/user/create.rs b/crates/api_crud/src/user/create.rs index cd0905992..44e5b8820 100644 --- a/crates/api_crud/src/user/create.rs +++ b/crates/api_crud/src/user/create.rs @@ -14,7 +14,7 @@ use lemmy_apub::{ }; use lemmy_db_schema::{ aggregates::structs::PersonAggregates, - newtypes::{CommunityId, LanguageIdentifier}, + newtypes::CommunityId, source::{ community::{ Community, @@ -149,7 +149,6 @@ impl PerformCrud for Register { password_encrypted: Some(data.password.to_string()), show_nsfw: Some(data.show_nsfw), email_verified: Some(false), - discussion_languages: Some(LanguageIdentifier::all_languages()), ..LocalUserForm::default() }; diff --git a/crates/apub/src/objects/post.rs b/crates/apub/src/objects/post.rs index df0dce586..8f8a1d043 100644 --- a/crates/apub/src/objects/post.rs +++ b/crates/apub/src/objects/post.rs @@ -4,7 +4,7 @@ use crate::{ local_instance, objects::{read_from_string_or_source_opt, verify_is_remote_object}, protocol::{ - objects::page::{Attachment, AttributedTo, Language, Page, PageType}, + objects::page::{Attachment, AttributedTo, LanguageTag, Page, PageType}, ImageObject, Source, }, @@ -20,9 +20,9 @@ use chrono::NaiveDateTime; use lemmy_api_common::{request::fetch_site_data, utils::blocking}; use lemmy_db_schema::{ self, - newtypes::LanguageIdentifier, source::{ community::Community, + language::Language, moderator::{ModLockPost, ModLockPostForm, ModStickyPost, ModStickyPostForm}, person::Person, post::{Post, PostForm}, @@ -99,6 +99,11 @@ impl ApubObject for ApubPost { Community::read(conn, community_id) }) .await??; + let language = self.language; + let language = blocking(context.pool(), move |conn| { + Language::read_from_id(conn, language) + }) + .await??; let page = Page { kind: PageType::Page, @@ -116,7 +121,7 @@ impl ApubObject for ApubPost { comments_enabled: Some(!self.locked), sensitive: Some(self.nsfw), stickied: Some(self.stickied), - language: Language::new(self.language.clone()), + language: LanguageTag::new(language), published: Some(convert_datetime(self.published)), updated: self.updated.map(convert_datetime), }; @@ -180,9 +185,11 @@ impl ApubObject for ApubPost { let body_slurs_removed = read_from_string_or_source_opt(&page.content, &page.media_type, &page.source) .map(|s| remove_slurs(&s, &context.settings().slur_regex())); - let language = page - .language - .map(|l| LanguageIdentifier::new(&l.identifier)); + let language = page.language.map(|l| l.identifier); + let language = blocking(context.pool(), move |conn| { + Language::read_id_from_code_opt(conn, language) + }) + .await??; PostForm { name: page.name.clone(), diff --git a/crates/apub/src/protocol/objects/page.rs b/crates/apub/src/protocol/objects/page.rs index bbc006947..cfa4a0817 100644 --- a/crates/apub/src/protocol/objects/page.rs +++ b/crates/apub/src/protocol/objects/page.rs @@ -16,7 +16,7 @@ use activitypub_federation::{ use activitystreams_kinds::link::LinkType; use chrono::{DateTime, FixedOffset}; use itertools::Itertools; -use lemmy_db_schema::newtypes::{DbUrl, LanguageIdentifier}; +use lemmy_db_schema::{newtypes::DbUrl, source::language::Language}; use lemmy_utils::error::LemmyError; use lemmy_websocket::LemmyContext; use serde::{Deserialize, Serialize}; @@ -62,25 +62,25 @@ pub struct Page { pub(crate) stickied: Option, pub(crate) published: Option>, pub(crate) updated: Option>, - pub(crate) language: Option, + pub(crate) language: Option, } #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] -pub(crate) struct Language { +pub(crate) struct LanguageTag { pub(crate) identifier: String, pub(crate) name: String, } -impl Language { - pub(crate) fn new(lang: LanguageIdentifier) -> Option { - if lang.is_undetermined() { +impl LanguageTag { + pub(crate) fn new(lang: Language) -> Option { + // undetermined + if lang.code == "und" { None } else { - // TODO: need to get the language name - Some(Language { - identifier: lang.into_inner(), - name: "todo".to_string(), + Some(LanguageTag { + identifier: lang.code, + name: lang.name, }) } } diff --git a/crates/db_schema/src/impls/language.rs b/crates/db_schema/src/impls/language.rs index fef2fcf11..b13ed311f 100644 --- a/crates/db_schema/src/impls/language.rs +++ b/crates/db_schema/src/impls/language.rs @@ -1,5 +1,5 @@ -use crate::source::language::Language; -use diesel::{result::Error, PgConnection, RunQueryDsl}; +use crate::{newtypes::LanguageId, source::language::Language}; +use diesel::{result::Error, PgConnection, RunQueryDsl, *}; impl Language { pub fn read_all(conn: &PgConnection) -> Result, Error> { @@ -7,9 +7,30 @@ impl Language { language.load::(conn) } - pub fn read_from_code(code_: &str, conn: &PgConnection) -> Result { + pub fn read_from_id(conn: &PgConnection, id_: LanguageId) -> Result { use crate::schema::language::dsl::*; - language.find(code.eq(code_)).load::(conn) + language.filter(id.eq(id_)).first::(conn) + } + + pub fn read_id_from_code(conn: &PgConnection, code_: String) -> Result { + use crate::schema::language::dsl::*; + Ok(language.filter(code.eq(code_)).first::(conn)?.id) + } + + pub fn read_id_from_code_opt( + conn: &PgConnection, + code_: Option, + ) -> Result, Error> { + if let Some(code_) = code_ { + Ok(Some(Language::read_id_from_code(conn, code_)?)) + } else { + Ok(None) + } + } + + pub fn read_undetermined(conn: &PgConnection) -> Result { + use crate::schema::language::dsl::*; + Ok(language.filter(code.eq("und")).first::(conn)?.id) } } @@ -25,7 +46,9 @@ mod tests { let all = Language::read_all(&conn).unwrap(); - assert_eq!(123, all.len()); - assert_eq!("xy", all[5].code); + assert_eq!(184, all.len()); + assert_eq!("ak", all[5].code); + assert_eq!("lv", all[99].code); + assert_eq!("yi", all[179].code); } } diff --git a/crates/db_schema/src/schema.rs b/crates/db_schema/src/schema.rs index 57023a3c4..001c2db64 100644 --- a/crates/db_schema/src/schema.rs +++ b/crates/db_schema/src/schema.rs @@ -162,7 +162,7 @@ table! { show_new_post_notifs -> Bool, email_verified -> Bool, accepted_application -> Bool, - discussion_languages -> Array, + discussion_languages -> Array, } } @@ -362,7 +362,7 @@ table! { thumbnail_url -> Nullable, ap_id -> Varchar, local -> Bool, - language -> Varchar, + language -> Int4, } } diff --git a/crates/db_schema/src/source/language.rs b/crates/db_schema/src/source/language.rs index 97377c9c2..042eba3bf 100644 --- a/crates/db_schema/src/source/language.rs +++ b/crates/db_schema/src/source/language.rs @@ -1,6 +1,9 @@ use crate::newtypes::LanguageId; use serde::{Deserialize, Serialize}; +#[cfg(feature = "full")] +use crate::schema::language; + #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] #[cfg_attr(feature = "full", derive(Queryable, Identifiable))] #[cfg_attr(feature = "full", table_name = "language")] diff --git a/crates/db_views/src/post_view.rs b/crates/db_views/src/post_view.rs index d7940df68..b85c978f4 100644 --- a/crates/db_views/src/post_view.rs +++ b/crates/db_views/src/post_view.rs @@ -2,7 +2,7 @@ use crate::structs::{LocalUserView, PostView}; use diesel::{dsl::*, pg::Pg, result::Error, *}; use lemmy_db_schema::{ aggregates::structs::PostAggregates, - newtypes::{CommunityId, DbUrl, LanguageIdentifier, PersonId, PostId}, + newtypes::{CommunityId, DbUrl, LanguageId, PersonId, PostId}, schema::{ community, community_block, @@ -165,7 +165,7 @@ pub struct PostQueryBuilder<'a> { show_bot_accounts: Option, show_read_posts: Option, saved_only: Option, - languages: Option>, + languages: Option>, page: Option, limit: Option, } @@ -497,7 +497,7 @@ mod tests { use diesel::PgConnection; use lemmy_db_schema::{ aggregates::structs::PostAggregates, - newtypes::LanguageIdentifier, + newtypes::LanguageId, source::{ community::*, community_block::{CommunityBlock, CommunityBlockForm}, @@ -562,7 +562,7 @@ mod tests { name: "blocked_person_post".to_string(), creator_id: inserted_blocked_person.id, community_id: inserted_community.id, - language: Some(LanguageIdentifier::new("en")), + language: Some(LanguageId(1)), ..PostForm::default() }; @@ -581,7 +581,7 @@ mod tests { name: post_name, creator_id: inserted_person.id, community_id: inserted_community.id, - language: Some(LanguageIdentifier::new("fr")), + language: Some(LanguageId(2)), ..PostForm::default() }; @@ -643,7 +643,7 @@ mod tests { thumbnail_url: None, ap_id: inserted_post.ap_id.to_owned(), local: true, - language: LanguageIdentifier::new("fr"), + language: LanguageId(2), }, my_vote: None, creator: PersonSafe { @@ -838,7 +838,7 @@ mod tests { .listing_type(ListingType::Community) .sort(SortType::New) .community_id(data.inserted_community.id); - let fr = LanguageIdentifier::new("fr"); + let fr = LanguageId(2); read_post_listing_french.languages = Some(vec![fr]); let read_post_listing_french = read_post_listing_french.list().unwrap(); diff --git a/src/code_migrations.rs b/src/code_migrations.rs index 66a6314a8..de6816315 100644 --- a/src/code_migrations.rs +++ b/src/code_migrations.rs @@ -13,11 +13,9 @@ use lemmy_apub::{ EndpointType, }; use lemmy_db_schema::{ - newtypes::LanguageIdentifier, source::{ comment::Comment, community::{Community, CommunityForm}, - local_user::LocalUser, person::{Person, PersonForm}, post::Post, private_message::PrivateMessage, @@ -42,7 +40,6 @@ pub fn run_advanced_migrations( post_thumbnail_url_updates_2020_07_27(conn, protocol_and_hostname)?; apub_columns_2021_02_02(conn)?; instance_actor_2022_01_28(conn, protocol_and_hostname)?; - language_tags_2022_05_11(conn)?; Ok(()) } @@ -312,16 +309,3 @@ fn instance_actor_2022_01_28( } Ok(()) } - -fn language_tags_2022_05_11(conn: &PgConnection) -> Result<(), LemmyError> { - use lemmy_db_schema::schema::local_user::dsl::*; - let users = local_user.load::(conn)?; - - for u in &users { - let all_languages = LanguageIdentifier::all_languages(); - diesel::update(local_user.find(u.id)) - .set((discussion_languages.eq(all_languages),)) - .get_result::(conn)?; - } - Ok(()) -}