]> Untitled Git - lemmy.git/blobdiff - crates/db_schema/src/impls/community.rs
implement language tags for site/community in db and api (#2434)
[lemmy.git] / crates / db_schema / src / impls / community.rs
index 19a3a95486f583d17596321335972aa5b2e6cd61..966761b2d8f3c0611bb98630502f7e76ae3b2f2e 100644 (file)
@@ -1,22 +1,32 @@
 use crate::{
-  functions::lower,
-  naive_now,
   newtypes::{CommunityId, DbUrl, PersonId},
-  source::community::{
-    Community,
-    CommunityFollower,
-    CommunityFollowerForm,
-    CommunityForm,
-    CommunityModerator,
-    CommunityModeratorForm,
-    CommunityPersonBan,
-    CommunityPersonBanForm,
-    CommunitySafe,
+  source::{
+    actor_language::{CommunityLanguage, SiteLanguage},
+    community::{
+      Community,
+      CommunityFollower,
+      CommunityFollowerForm,
+      CommunityForm,
+      CommunityModerator,
+      CommunityModeratorForm,
+      CommunityPersonBan,
+      CommunityPersonBanForm,
+      CommunitySafe,
+    },
   },
-  traits::{Bannable, Crud, DeleteableOrRemoveable, Followable, Joinable},
+  traits::{ApubActor, Bannable, Crud, DeleteableOrRemoveable, Followable, Joinable},
+  utils::{functions::lower, naive_now},
+  SubscribedType,
+};
+use diesel::{
+  dsl::*,
+  result::Error,
+  ExpressionMethods,
+  PgConnection,
+  QueryDsl,
+  RunQueryDsl,
+  TextExpressionMethods,
 };
-use diesel::{dsl::*, result::Error, ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl};
-use url::Url;
 
 mod safe_type {
   use crate::{schema::community::*, source::community::Community, traits::ToSafe};
@@ -35,6 +45,8 @@ mod safe_type {
     local,
     icon,
     banner,
+    hidden,
+    posting_restricted_to_mods,
   );
 
   impl ToSafe for Community {
@@ -54,6 +66,8 @@ mod safe_type {
         local,
         icon,
         banner,
+        hidden,
+        posting_restricted_to_mods,
       )
     }
   }
@@ -62,25 +76,36 @@ mod safe_type {
 impl Crud for Community {
   type Form = CommunityForm;
   type IdType = CommunityId;
-  fn read(conn: &PgConnection, community_id: CommunityId) -> Result<Self, Error> {
+  fn read(conn: &mut PgConnection, community_id: CommunityId) -> Result<Self, Error> {
     use crate::schema::community::dsl::*;
     community.find(community_id).first::<Self>(conn)
   }
 
-  fn delete(conn: &PgConnection, community_id: CommunityId) -> Result<usize, Error> {
+  fn delete(conn: &mut PgConnection, community_id: CommunityId) -> Result<usize, Error> {
     use crate::schema::community::dsl::*;
     diesel::delete(community.find(community_id)).execute(conn)
   }
 
-  fn create(conn: &PgConnection, new_community: &CommunityForm) -> Result<Self, Error> {
+  fn create(conn: &mut PgConnection, new_community: &CommunityForm) -> Result<Self, Error> {
     use crate::schema::community::dsl::*;
-    insert_into(community)
+    let community_ = insert_into(community)
       .values(new_community)
-      .get_result::<Self>(conn)
+      .get_result::<Self>(conn)?;
+
+    let site_languages = SiteLanguage::read_local(conn);
+    if let Ok(langs) = site_languages {
+      // if site exists, init user with site languages
+      CommunityLanguage::update(conn, langs, community_.id)?;
+    } else {
+      // otherwise, init with all languages (this only happens during tests)
+      CommunityLanguage::update(conn, vec![], community_.id)?;
+    }
+
+    Ok(community_)
   }
 
   fn update(
-    conn: &PgConnection,
+    conn: &mut PgConnection,
     community_id: CommunityId,
     new_community: &CommunityForm,
   ) -> Result<Self, Error> {
@@ -92,16 +117,8 @@ impl Crud for Community {
 }
 
 impl Community {
-  pub fn read_from_name(conn: &PgConnection, community_name: &str) -> Result<Community, Error> {
-    use crate::schema::community::dsl::*;
-    community
-      .filter(local.eq(true))
-      .filter(lower(name).eq(lower(community_name)))
-      .first::<Self>(conn)
-  }
-
   pub fn update_deleted(
-    conn: &PgConnection,
+    conn: &mut PgConnection,
     community_id: CommunityId,
     new_deleted: bool,
   ) -> Result<Community, Error> {
@@ -112,7 +129,7 @@ impl Community {
   }
 
   pub fn update_removed(
-    conn: &PgConnection,
+    conn: &mut PgConnection,
     community_id: CommunityId,
     new_removed: bool,
   ) -> Result<Community, Error> {
@@ -122,12 +139,15 @@ impl Community {
       .get_result::<Self>(conn)
   }
 
-  pub fn distinct_federated_communities(conn: &PgConnection) -> Result<Vec<String>, Error> {
+  pub fn distinct_federated_communities(conn: &mut PgConnection) -> Result<Vec<DbUrl>, Error> {
     use crate::schema::community::dsl::*;
-    community.select(actor_id).distinct().load::<String>(conn)
+    community.select(actor_id).distinct().load::<DbUrl>(conn)
   }
 
-  pub fn upsert(conn: &PgConnection, community_form: &CommunityForm) -> Result<Community, Error> {
+  pub fn upsert(
+    conn: &mut PgConnection,
+    community_form: &CommunityForm,
+  ) -> Result<Community, Error> {
     use crate::schema::community::dsl::*;
     insert_into(community)
       .values(community_form)
@@ -136,23 +156,25 @@ impl Community {
       .set(community_form)
       .get_result::<Self>(conn)
   }
-  pub fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, Error> {
+
+  pub fn remove_avatar_and_banner(
+    conn: &mut PgConnection,
+    community_id: CommunityId,
+  ) -> Result<Self, Error> {
     use crate::schema::community::dsl::*;
-    let object_id: DbUrl = object_id.into();
-    Ok(
-      community
-        .filter(actor_id.eq(object_id))
-        .first::<Community>(conn)
-        .ok()
-        .map(Into::into),
-    )
+    diesel::update(community.find(community_id))
+      .set((
+        icon.eq::<Option<String>>(None),
+        banner.eq::<Option<String>>(None),
+      ))
+      .get_result::<Self>(conn)
   }
 }
 
 impl Joinable for CommunityModerator {
   type Form = CommunityModeratorForm;
   fn join(
-    conn: &PgConnection,
+    conn: &mut PgConnection,
     community_moderator_form: &CommunityModeratorForm,
   ) -> Result<Self, Error> {
     use crate::schema::community_moderator::dsl::*;
@@ -162,7 +184,7 @@ impl Joinable for CommunityModerator {
   }
 
   fn leave(
-    conn: &PgConnection,
+    conn: &mut PgConnection,
     community_moderator_form: &CommunityModeratorForm,
   ) -> Result<usize, Error> {
     use crate::schema::community_moderator::dsl::*;
@@ -197,7 +219,7 @@ impl DeleteableOrRemoveable for Community {
 
 impl CommunityModerator {
   pub fn delete_for_community(
-    conn: &PgConnection,
+    conn: &mut PgConnection,
     for_community_id: CommunityId,
   ) -> Result<usize, Error> {
     use crate::schema::community_moderator::dsl::*;
@@ -205,7 +227,7 @@ impl CommunityModerator {
   }
 
   pub fn get_person_moderated_communities(
-    conn: &PgConnection,
+    conn: &mut PgConnection,
     for_person_id: PersonId,
   ) -> Result<Vec<CommunityId>, Error> {
     use crate::schema::community_moderator::dsl::*;
@@ -219,17 +241,20 @@ impl CommunityModerator {
 impl Bannable for CommunityPersonBan {
   type Form = CommunityPersonBanForm;
   fn ban(
-    conn: &PgConnection,
+    conn: &mut PgConnection,
     community_person_ban_form: &CommunityPersonBanForm,
   ) -> Result<Self, Error> {
     use crate::schema::community_person_ban::dsl::*;
     insert_into(community_person_ban)
       .values(community_person_ban_form)
+      .on_conflict((community_id, person_id))
+      .do_update()
+      .set(community_person_ban_form)
       .get_result::<Self>(conn)
   }
 
   fn unban(
-    conn: &PgConnection,
+    conn: &mut PgConnection,
     community_person_ban_form: &CommunityPersonBanForm,
   ) -> Result<usize, Error> {
     use crate::schema::community_person_ban::dsl::*;
@@ -242,10 +267,26 @@ impl Bannable for CommunityPersonBan {
   }
 }
 
+impl CommunityFollower {
+  pub fn to_subscribed_type(follower: &Option<Self>) -> SubscribedType {
+    match follower {
+      Some(f) => {
+        if f.pending.unwrap_or(false) {
+          SubscribedType::Pending
+        } else {
+          SubscribedType::Subscribed
+        }
+      }
+      // If the row doesn't exist, the person isn't a follower.
+      None => SubscribedType::NotSubscribed,
+    }
+  }
+}
+
 impl Followable for CommunityFollower {
   type Form = CommunityFollowerForm;
   fn follow(
-    conn: &PgConnection,
+    conn: &mut PgConnection,
     community_follower_form: &CommunityFollowerForm,
   ) -> Result<Self, Error> {
     use crate::schema::community_follower::dsl::*;
@@ -257,7 +298,7 @@ impl Followable for CommunityFollower {
       .get_result::<Self>(conn)
   }
   fn follow_accepted(
-    conn: &PgConnection,
+    conn: &mut PgConnection,
     community_id_: CommunityId,
     person_id_: PersonId,
   ) -> Result<Self, Error>
@@ -270,11 +311,11 @@ impl Followable for CommunityFollower {
         .filter(community_id.eq(community_id_))
         .filter(person_id.eq(person_id_)),
     )
-    .set(pending.eq(true))
+    .set(pending.eq(false))
     .get_result::<Self>(conn)
   }
   fn unfollow(
-    conn: &PgConnection,
+    conn: &mut PgConnection,
     community_follower_form: &CommunityFollowerForm,
   ) -> Result<usize, Error> {
     use crate::schema::community_follower::dsl::*;
@@ -287,7 +328,10 @@ impl Followable for CommunityFollower {
   }
   // TODO: this function name only makes sense if you call it with a remote community. for a local
   //       community, it will also return true if only remote followers exist
-  fn has_local_followers(conn: &PgConnection, community_id_: CommunityId) -> Result<bool, Error> {
+  fn has_local_followers(
+    conn: &mut PgConnection,
+    community_id_: CommunityId,
+  ) -> Result<bool, Error> {
     use crate::schema::community_follower::dsl::*;
     diesel::select(exists(
       community_follower.filter(community_id.eq(community_id_)),
@@ -296,35 +340,77 @@ impl Followable for CommunityFollower {
   }
 }
 
+impl ApubActor for Community {
+  fn read_from_apub_id(conn: &mut PgConnection, object_id: &DbUrl) -> Result<Option<Self>, Error> {
+    use crate::schema::community::dsl::*;
+    Ok(
+      community
+        .filter(actor_id.eq(object_id))
+        .first::<Community>(conn)
+        .ok()
+        .map(Into::into),
+    )
+  }
+
+  fn read_from_name(
+    conn: &mut PgConnection,
+    community_name: &str,
+    include_deleted: bool,
+  ) -> Result<Community, Error> {
+    use crate::schema::community::dsl::*;
+    let mut q = community
+      .into_boxed()
+      .filter(local.eq(true))
+      .filter(lower(name).eq(lower(community_name)));
+    if !include_deleted {
+      q = q.filter(deleted.eq(false)).filter(removed.eq(false));
+    }
+    q.first::<Self>(conn)
+  }
+
+  fn read_from_name_and_domain(
+    conn: &mut PgConnection,
+    community_name: &str,
+    protocol_domain: &str,
+  ) -> Result<Community, Error> {
+    use crate::schema::community::dsl::*;
+    community
+      .filter(lower(name).eq(lower(community_name)))
+      .filter(actor_id.like(format!("{}%", protocol_domain)))
+      .first::<Self>(conn)
+  }
+}
+
 #[cfg(test)]
 mod tests {
   use crate::{
-    establish_unpooled_connection,
     source::{community::*, person::*},
     traits::{Bannable, Crud, Followable, Joinable},
+    utils::establish_unpooled_connection,
   };
   use serial_test::serial;
 
   #[test]
   #[serial]
   fn test_crud() {
-    let conn = establish_unpooled_connection();
+    let conn = &mut establish_unpooled_connection();
 
     let new_person = PersonForm {
       name: "bobbee".into(),
+      public_key: Some("pubkey".to_string()),
       ..PersonForm::default()
     };
 
-    let inserted_person = Person::create(&conn, &new_person).unwrap();
+    let inserted_person = Person::create(conn, &new_person).unwrap();
 
     let new_community = CommunityForm {
       name: "TIL".into(),
       title: "nada".to_owned(),
-      public_key: "nada".to_owned(),
+      public_key: Some("pubkey".to_string()),
       ..CommunityForm::default()
     };
 
-    let inserted_community = Community::create(&conn, &new_community).unwrap();
+    let inserted_community = Community::create(conn, &new_community).unwrap();
 
     let expected_community = Community {
       id: inserted_community.id,
@@ -339,13 +425,15 @@ mod tests {
       actor_id: inserted_community.actor_id.to_owned(),
       local: true,
       private_key: None,
-      public_key: "nada".to_owned(),
+      public_key: "pubkey".to_owned(),
       last_refreshed_at: inserted_community.published,
       icon: None,
       banner: None,
       followers_url: inserted_community.followers_url.to_owned(),
       inbox_url: inserted_community.inbox_url.to_owned(),
       shared_inbox_url: None,
+      hidden: false,
+      posting_restricted_to_mods: false,
     };
 
     let community_follower_form = CommunityFollowerForm {
@@ -355,7 +443,7 @@ mod tests {
     };
 
     let inserted_community_follower =
-      CommunityFollower::follow(&conn, &community_follower_form).unwrap();
+      CommunityFollower::follow(conn, &community_follower_form).unwrap();
 
     let expected_community_follower = CommunityFollower {
       id: inserted_community_follower.id,
@@ -371,7 +459,7 @@ mod tests {
     };
 
     let inserted_community_moderator =
-      CommunityModerator::join(&conn, &community_moderator_form).unwrap();
+      CommunityModerator::join(conn, &community_moderator_form).unwrap();
 
     let expected_community_moderator = CommunityModerator {
       id: inserted_community_moderator.id,
@@ -383,26 +471,27 @@ mod tests {
     let community_person_ban_form = CommunityPersonBanForm {
       community_id: inserted_community.id,
       person_id: inserted_person.id,
+      expires: None,
     };
 
     let inserted_community_person_ban =
-      CommunityPersonBan::ban(&conn, &community_person_ban_form).unwrap();
+      CommunityPersonBan::ban(conn, &community_person_ban_form).unwrap();
 
     let expected_community_person_ban = CommunityPersonBan {
       id: inserted_community_person_ban.id,
       community_id: inserted_community.id,
       person_id: inserted_person.id,
       published: inserted_community_person_ban.published,
+      expires: None,
     };
 
-    let read_community = Community::read(&conn, inserted_community.id).unwrap();
-    let updated_community =
-      Community::update(&conn, inserted_community.id, &new_community).unwrap();
-    let ignored_community = CommunityFollower::unfollow(&conn, &community_follower_form).unwrap();
-    let left_community = CommunityModerator::leave(&conn, &community_moderator_form).unwrap();
-    let unban = CommunityPersonBan::unban(&conn, &community_person_ban_form).unwrap();
-    let num_deleted = Community::delete(&conn, inserted_community.id).unwrap();
-    Person::delete(&conn, inserted_person.id).unwrap();
+    let read_community = Community::read(conn, inserted_community.id).unwrap();
+    let updated_community = Community::update(conn, inserted_community.id, &new_community).unwrap();
+    let ignored_community = CommunityFollower::unfollow(conn, &community_follower_form).unwrap();
+    let left_community = CommunityModerator::leave(conn, &community_moderator_form).unwrap();
+    let unban = CommunityPersonBan::unban(conn, &community_person_ban_form).unwrap();
+    let num_deleted = Community::delete(conn, inserted_community.id).unwrap();
+    Person::delete(conn, inserted_person.id).unwrap();
 
     assert_eq!(expected_community, read_community);
     assert_eq!(expected_community, inserted_community);