2 newtypes::{CommunityId, DbUrl, PersonId},
3 schema::community::dsl::*,
5 actor_language::{CommunityLanguage, SiteLanguage},
12 CommunityModeratorForm,
14 CommunityPersonBanForm,
19 traits::{ApubActor, Bannable, Crud, DeleteableOrRemoveable, Followable, Joinable},
20 utils::{functions::lower, get_conn, DbPool},
23 use diesel::{dsl::*, result::Error, ExpressionMethods, QueryDsl, TextExpressionMethods};
24 use diesel_async::RunQueryDsl;
27 use crate::{schema::community::*, source::community::Community, traits::ToSafe};
44 posting_restricted_to_mods,
48 impl ToSafe for Community {
49 type SafeColumns = Columns;
50 fn safe_columns_tuple() -> Self::SafeColumns {
66 posting_restricted_to_mods,
74 impl Crud for Community {
75 type InsertForm = CommunityInsertForm;
76 type UpdateForm = CommunityUpdateForm;
77 type IdType = CommunityId;
78 async fn read(pool: &DbPool, community_id: CommunityId) -> Result<Self, Error> {
79 let conn = &mut get_conn(pool).await?;
80 community.find(community_id).first::<Self>(conn).await
83 async fn delete(pool: &DbPool, community_id: CommunityId) -> Result<usize, Error> {
84 let conn = &mut get_conn(pool).await?;
85 diesel::delete(community.find(community_id))
90 async fn create(pool: &DbPool, form: &Self::InsertForm) -> Result<Self, Error> {
91 let conn = &mut get_conn(pool).await?;
92 let community_ = insert_into(community)
94 .on_conflict(actor_id)
97 .get_result::<Self>(conn)
100 let site_languages = SiteLanguage::read_local(pool).await;
101 if let Ok(langs) = site_languages {
102 // if site exists, init user with site languages
103 CommunityLanguage::update(pool, langs, community_.id).await?;
105 // otherwise, init with all languages (this only happens during tests)
106 CommunityLanguage::update(pool, vec![], community_.id).await?;
114 community_id: CommunityId,
115 form: &Self::UpdateForm,
116 ) -> Result<Self, Error> {
117 let conn = &mut get_conn(pool).await?;
118 diesel::update(community.find(community_id))
120 .get_result::<Self>(conn)
126 impl Joinable for CommunityModerator {
127 type Form = CommunityModeratorForm;
130 community_moderator_form: &CommunityModeratorForm,
131 ) -> Result<Self, Error> {
132 use crate::schema::community_moderator::dsl::*;
133 let conn = &mut get_conn(pool).await?;
134 insert_into(community_moderator)
135 .values(community_moderator_form)
136 .get_result::<Self>(conn)
142 community_moderator_form: &CommunityModeratorForm,
143 ) -> Result<usize, Error> {
144 use crate::schema::community_moderator::dsl::*;
145 let conn = &mut get_conn(pool).await?;
148 .filter(community_id.eq(community_moderator_form.community_id))
149 .filter(person_id.eq(community_moderator_form.person_id)),
156 impl DeleteableOrRemoveable for CommunitySafe {
157 fn blank_out_deleted_or_removed_info(mut self) -> Self {
158 self.title = "".into();
159 self.description = None;
166 impl DeleteableOrRemoveable for Community {
167 fn blank_out_deleted_or_removed_info(mut self) -> Self {
168 self.title = "".into();
169 self.description = None;
176 impl CommunityModerator {
177 pub async fn delete_for_community(
179 for_community_id: CommunityId,
180 ) -> Result<usize, Error> {
181 use crate::schema::community_moderator::dsl::*;
182 let conn = &mut get_conn(pool).await?;
184 diesel::delete(community_moderator.filter(community_id.eq(for_community_id)))
189 pub async fn get_person_moderated_communities(
191 for_person_id: PersonId,
192 ) -> Result<Vec<CommunityId>, Error> {
193 use crate::schema::community_moderator::dsl::*;
194 let conn = &mut get_conn(pool).await?;
196 .filter(person_id.eq(for_person_id))
197 .select(community_id)
198 .load::<CommunityId>(conn)
204 impl Bannable for CommunityPersonBan {
205 type Form = CommunityPersonBanForm;
208 community_person_ban_form: &CommunityPersonBanForm,
209 ) -> Result<Self, Error> {
210 use crate::schema::community_person_ban::dsl::*;
211 let conn = &mut get_conn(pool).await?;
212 insert_into(community_person_ban)
213 .values(community_person_ban_form)
214 .on_conflict((community_id, person_id))
216 .set(community_person_ban_form)
217 .get_result::<Self>(conn)
223 community_person_ban_form: &CommunityPersonBanForm,
224 ) -> Result<usize, Error> {
225 use crate::schema::community_person_ban::dsl::*;
226 let conn = &mut get_conn(pool).await?;
229 .filter(community_id.eq(community_person_ban_form.community_id))
230 .filter(person_id.eq(community_person_ban_form.person_id)),
237 impl CommunityFollower {
238 pub fn to_subscribed_type(follower: &Option<Self>) -> SubscribedType {
241 if f.pending.unwrap_or(false) {
242 SubscribedType::Pending
244 SubscribedType::Subscribed
247 // If the row doesn't exist, the person isn't a follower.
248 None => SubscribedType::NotSubscribed,
254 impl Followable for CommunityFollower {
255 type Form = CommunityFollowerForm;
258 community_follower_form: &CommunityFollowerForm,
259 ) -> Result<Self, Error> {
260 use crate::schema::community_follower::dsl::*;
261 let conn = &mut get_conn(pool).await?;
262 insert_into(community_follower)
263 .values(community_follower_form)
264 .on_conflict((community_id, person_id))
266 .set(community_follower_form)
267 .get_result::<Self>(conn)
270 async fn follow_accepted(
272 community_id_: CommunityId,
273 person_id_: PersonId,
274 ) -> Result<Self, Error> {
275 use crate::schema::community_follower::dsl::*;
276 let conn = &mut get_conn(pool).await?;
279 .filter(community_id.eq(community_id_))
280 .filter(person_id.eq(person_id_)),
282 .set(pending.eq(false))
283 .get_result::<Self>(conn)
288 community_follower_form: &CommunityFollowerForm,
289 ) -> Result<usize, Error> {
290 use crate::schema::community_follower::dsl::*;
291 let conn = &mut get_conn(pool).await?;
294 .filter(community_id.eq(&community_follower_form.community_id))
295 .filter(person_id.eq(&community_follower_form.person_id)),
300 // TODO: this function name only makes sense if you call it with a remote community. for a local
301 // community, it will also return true if only remote followers exist
302 async fn has_local_followers(pool: &DbPool, community_id_: CommunityId) -> Result<bool, Error> {
303 use crate::schema::community_follower::dsl::*;
304 let conn = &mut get_conn(pool).await?;
305 diesel::select(exists(
306 community_follower.filter(community_id.eq(community_id_)),
314 impl ApubActor for Community {
315 async fn read_from_apub_id(pool: &DbPool, object_id: &DbUrl) -> Result<Option<Self>, Error> {
316 let conn = &mut get_conn(pool).await?;
319 .filter(actor_id.eq(object_id))
320 .first::<Community>(conn)
327 async fn read_from_name(
329 community_name: &str,
330 include_deleted: bool,
331 ) -> Result<Community, Error> {
332 let conn = &mut get_conn(pool).await?;
333 let mut q = community
335 .filter(local.eq(true))
336 .filter(lower(name).eq(lower(community_name)));
337 if !include_deleted {
338 q = q.filter(deleted.eq(false)).filter(removed.eq(false));
340 q.first::<Self>(conn).await
343 async fn read_from_name_and_domain(
345 community_name: &str,
346 protocol_domain: &str,
347 ) -> Result<Community, Error> {
348 let conn = &mut get_conn(pool).await?;
350 .filter(lower(name).eq(lower(community_name)))
351 .filter(actor_id.like(format!("{}%", protocol_domain)))
360 source::{community::*, instance::Instance, person::*},
361 traits::{Bannable, Crud, Followable, Joinable},
362 utils::build_db_pool_for_tests,
364 use serial_test::serial;
368 async fn test_crud() {
369 let pool = &build_db_pool_for_tests().await;
371 let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
373 let new_person = PersonInsertForm::builder()
374 .name("bobbee".into())
375 .public_key("pubkey".to_string())
376 .instance_id(inserted_instance.id)
379 let inserted_person = Person::create(pool, &new_person).await.unwrap();
381 let new_community = CommunityInsertForm::builder()
383 .title("nada".to_owned())
384 .public_key("pubkey".to_string())
385 .instance_id(inserted_instance.id)
388 let inserted_community = Community::create(pool, &new_community).await.unwrap();
390 let expected_community = Community {
391 id: inserted_community.id,
393 title: "nada".to_owned(),
398 published: inserted_community.published,
400 actor_id: inserted_community.actor_id.to_owned(),
403 public_key: "pubkey".to_owned(),
404 last_refreshed_at: inserted_community.published,
407 followers_url: inserted_community.followers_url.to_owned(),
408 inbox_url: inserted_community.inbox_url.to_owned(),
409 shared_inbox_url: None,
411 posting_restricted_to_mods: false,
412 instance_id: inserted_instance.id,
415 let community_follower_form = CommunityFollowerForm {
416 community_id: inserted_community.id,
417 person_id: inserted_person.id,
421 let inserted_community_follower = CommunityFollower::follow(pool, &community_follower_form)
425 let expected_community_follower = CommunityFollower {
426 id: inserted_community_follower.id,
427 community_id: inserted_community.id,
428 person_id: inserted_person.id,
429 pending: Some(false),
430 published: inserted_community_follower.published,
433 let community_moderator_form = CommunityModeratorForm {
434 community_id: inserted_community.id,
435 person_id: inserted_person.id,
438 let inserted_community_moderator = CommunityModerator::join(pool, &community_moderator_form)
442 let expected_community_moderator = CommunityModerator {
443 id: inserted_community_moderator.id,
444 community_id: inserted_community.id,
445 person_id: inserted_person.id,
446 published: inserted_community_moderator.published,
449 let community_person_ban_form = CommunityPersonBanForm {
450 community_id: inserted_community.id,
451 person_id: inserted_person.id,
455 let inserted_community_person_ban = CommunityPersonBan::ban(pool, &community_person_ban_form)
459 let expected_community_person_ban = CommunityPersonBan {
460 id: inserted_community_person_ban.id,
461 community_id: inserted_community.id,
462 person_id: inserted_person.id,
463 published: inserted_community_person_ban.published,
467 let read_community = Community::read(pool, inserted_community.id).await.unwrap();
469 let update_community_form = CommunityUpdateForm::builder()
470 .title(Some("nada".to_owned()))
472 let updated_community = Community::update(pool, inserted_community.id, &update_community_form)
476 let ignored_community = CommunityFollower::unfollow(pool, &community_follower_form)
479 let left_community = CommunityModerator::leave(pool, &community_moderator_form)
482 let unban = CommunityPersonBan::unban(pool, &community_person_ban_form)
485 let num_deleted = Community::delete(pool, inserted_community.id)
488 Person::delete(pool, inserted_person.id).await.unwrap();
489 Instance::delete(pool, inserted_instance.id).await.unwrap();
491 assert_eq!(expected_community, read_community);
492 assert_eq!(expected_community, inserted_community);
493 assert_eq!(expected_community, updated_community);
494 assert_eq!(expected_community_follower, inserted_community_follower);
495 assert_eq!(expected_community_moderator, inserted_community_moderator);
496 assert_eq!(expected_community_person_ban, inserted_community_person_ban);
497 assert_eq!(1, ignored_community);
498 assert_eq!(1, left_community);
499 assert_eq!(1, unban);
500 // assert_eq!(2, loaded_count);
501 assert_eq!(1, num_deleted);