2 newtypes::{CommunityId, DbUrl, PersonId},
3 schema::community::dsl::{actor_id, community, deleted, local, name, removed},
5 actor_language::{CommunityLanguage, SiteLanguage},
12 CommunityModeratorForm,
14 CommunityPersonBanForm,
19 traits::{ApubActor, Bannable, Crud, DeleteableOrRemoveable, Followable, Joinable},
20 utils::{functions::lower, get_conn, DbPool},
24 dsl::{exists, insert_into},
28 TextExpressionMethods,
30 use diesel_async::RunQueryDsl;
46 posting_restricted_to_mods,
52 source::community::Community,
71 posting_restricted_to_mods,
75 impl ToSafe for Community {
76 type SafeColumns = Columns;
77 fn safe_columns_tuple() -> Self::SafeColumns {
93 posting_restricted_to_mods,
101 impl Crud for Community {
102 type InsertForm = CommunityInsertForm;
103 type UpdateForm = CommunityUpdateForm;
104 type IdType = CommunityId;
105 async fn read(pool: &DbPool, community_id: CommunityId) -> Result<Self, Error> {
106 let conn = &mut get_conn(pool).await?;
107 community.find(community_id).first::<Self>(conn).await
110 async fn delete(pool: &DbPool, community_id: CommunityId) -> Result<usize, Error> {
111 let conn = &mut get_conn(pool).await?;
112 diesel::delete(community.find(community_id))
117 async fn create(pool: &DbPool, form: &Self::InsertForm) -> Result<Self, Error> {
118 let conn = &mut get_conn(pool).await?;
119 let community_ = insert_into(community)
121 .on_conflict(actor_id)
124 .get_result::<Self>(conn)
127 let site_languages = SiteLanguage::read_local(pool).await;
128 if let Ok(langs) = site_languages {
129 // if site exists, init user with site languages
130 CommunityLanguage::update(pool, langs, community_.id).await?;
132 // otherwise, init with all languages (this only happens during tests)
133 CommunityLanguage::update(pool, vec![], community_.id).await?;
141 community_id: CommunityId,
142 form: &Self::UpdateForm,
143 ) -> Result<Self, Error> {
144 let conn = &mut get_conn(pool).await?;
145 diesel::update(community.find(community_id))
147 .get_result::<Self>(conn)
153 impl Joinable for CommunityModerator {
154 type Form = CommunityModeratorForm;
157 community_moderator_form: &CommunityModeratorForm,
158 ) -> Result<Self, Error> {
159 use crate::schema::community_moderator::dsl::community_moderator;
160 let conn = &mut get_conn(pool).await?;
161 insert_into(community_moderator)
162 .values(community_moderator_form)
163 .get_result::<Self>(conn)
169 community_moderator_form: &CommunityModeratorForm,
170 ) -> Result<usize, Error> {
171 use crate::schema::community_moderator::dsl::{community_id, community_moderator, person_id};
172 let conn = &mut get_conn(pool).await?;
175 .filter(community_id.eq(community_moderator_form.community_id))
176 .filter(person_id.eq(community_moderator_form.person_id)),
183 impl DeleteableOrRemoveable for CommunitySafe {
184 fn blank_out_deleted_or_removed_info(mut self) -> Self {
185 self.title = String::new();
186 self.description = None;
193 impl DeleteableOrRemoveable for Community {
194 fn blank_out_deleted_or_removed_info(mut self) -> Self {
195 self.title = String::new();
196 self.description = None;
203 impl CommunityModerator {
204 pub async fn delete_for_community(
206 for_community_id: CommunityId,
207 ) -> Result<usize, Error> {
208 use crate::schema::community_moderator::dsl::{community_id, community_moderator};
209 let conn = &mut get_conn(pool).await?;
211 diesel::delete(community_moderator.filter(community_id.eq(for_community_id)))
216 pub async fn get_person_moderated_communities(
218 for_person_id: PersonId,
219 ) -> Result<Vec<CommunityId>, Error> {
220 use crate::schema::community_moderator::dsl::{community_id, community_moderator, person_id};
221 let conn = &mut get_conn(pool).await?;
223 .filter(person_id.eq(for_person_id))
224 .select(community_id)
225 .load::<CommunityId>(conn)
231 impl Bannable for CommunityPersonBan {
232 type Form = CommunityPersonBanForm;
235 community_person_ban_form: &CommunityPersonBanForm,
236 ) -> Result<Self, Error> {
237 use crate::schema::community_person_ban::dsl::{community_id, community_person_ban, person_id};
238 let conn = &mut get_conn(pool).await?;
239 insert_into(community_person_ban)
240 .values(community_person_ban_form)
241 .on_conflict((community_id, person_id))
243 .set(community_person_ban_form)
244 .get_result::<Self>(conn)
250 community_person_ban_form: &CommunityPersonBanForm,
251 ) -> Result<usize, Error> {
252 use crate::schema::community_person_ban::dsl::{community_id, community_person_ban, person_id};
253 let conn = &mut get_conn(pool).await?;
256 .filter(community_id.eq(community_person_ban_form.community_id))
257 .filter(person_id.eq(community_person_ban_form.person_id)),
264 impl CommunityFollower {
265 pub fn to_subscribed_type(follower: &Option<Self>) -> SubscribedType {
268 if f.pending.unwrap_or(false) {
269 SubscribedType::Pending
271 SubscribedType::Subscribed
274 // If the row doesn't exist, the person isn't a follower.
275 None => SubscribedType::NotSubscribed,
281 impl Followable for CommunityFollower {
282 type Form = CommunityFollowerForm;
285 community_follower_form: &CommunityFollowerForm,
286 ) -> Result<Self, Error> {
287 use crate::schema::community_follower::dsl::{community_follower, community_id, person_id};
288 let conn = &mut get_conn(pool).await?;
289 insert_into(community_follower)
290 .values(community_follower_form)
291 .on_conflict((community_id, person_id))
293 .set(community_follower_form)
294 .get_result::<Self>(conn)
297 async fn follow_accepted(
299 community_id_: CommunityId,
300 person_id_: PersonId,
301 ) -> Result<Self, Error> {
302 use crate::schema::community_follower::dsl::{
308 let conn = &mut get_conn(pool).await?;
311 .filter(community_id.eq(community_id_))
312 .filter(person_id.eq(person_id_)),
314 .set(pending.eq(false))
315 .get_result::<Self>(conn)
320 community_follower_form: &CommunityFollowerForm,
321 ) -> Result<usize, Error> {
322 use crate::schema::community_follower::dsl::{community_follower, community_id, person_id};
323 let conn = &mut get_conn(pool).await?;
326 .filter(community_id.eq(&community_follower_form.community_id))
327 .filter(person_id.eq(&community_follower_form.person_id)),
332 // TODO: this function name only makes sense if you call it with a remote community. for a local
333 // community, it will also return true if only remote followers exist
334 async fn has_local_followers(pool: &DbPool, community_id_: CommunityId) -> Result<bool, Error> {
335 use crate::schema::community_follower::dsl::{community_follower, community_id};
336 let conn = &mut get_conn(pool).await?;
337 diesel::select(exists(
338 community_follower.filter(community_id.eq(community_id_)),
346 impl ApubActor for Community {
347 async fn read_from_apub_id(pool: &DbPool, object_id: &DbUrl) -> Result<Option<Self>, Error> {
348 let conn = &mut get_conn(pool).await?;
351 .filter(actor_id.eq(object_id))
352 .first::<Community>(conn)
359 async fn read_from_name(
361 community_name: &str,
362 include_deleted: bool,
363 ) -> Result<Community, Error> {
364 let conn = &mut get_conn(pool).await?;
365 let mut q = community
367 .filter(local.eq(true))
368 .filter(lower(name).eq(lower(community_name)));
369 if !include_deleted {
370 q = q.filter(deleted.eq(false)).filter(removed.eq(false));
372 q.first::<Self>(conn).await
375 async fn read_from_name_and_domain(
377 community_name: &str,
378 protocol_domain: &str,
379 ) -> Result<Community, Error> {
380 let conn = &mut get_conn(pool).await?;
382 .filter(lower(name).eq(lower(community_name)))
383 .filter(actor_id.like(format!("{}%", protocol_domain)))
396 CommunityFollowerForm,
399 CommunityModeratorForm,
401 CommunityPersonBanForm,
405 person::{Person, PersonInsertForm},
407 traits::{Bannable, Crud, Followable, Joinable},
408 utils::build_db_pool_for_tests,
410 use serial_test::serial;
414 async fn test_crud() {
415 let pool = &build_db_pool_for_tests().await;
417 let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
419 let new_person = PersonInsertForm::builder()
420 .name("bobbee".into())
421 .public_key("pubkey".to_string())
422 .instance_id(inserted_instance.id)
425 let inserted_person = Person::create(pool, &new_person).await.unwrap();
427 let new_community = CommunityInsertForm::builder()
429 .title("nada".to_owned())
430 .public_key("pubkey".to_string())
431 .instance_id(inserted_instance.id)
434 let inserted_community = Community::create(pool, &new_community).await.unwrap();
436 let expected_community = Community {
437 id: inserted_community.id,
439 title: "nada".to_owned(),
444 published: inserted_community.published,
446 actor_id: inserted_community.actor_id.clone(),
449 public_key: "pubkey".to_owned(),
450 last_refreshed_at: inserted_community.published,
453 followers_url: inserted_community.followers_url.clone(),
454 inbox_url: inserted_community.inbox_url.clone(),
455 shared_inbox_url: None,
457 posting_restricted_to_mods: false,
458 instance_id: inserted_instance.id,
461 let community_follower_form = CommunityFollowerForm {
462 community_id: inserted_community.id,
463 person_id: inserted_person.id,
467 let inserted_community_follower = CommunityFollower::follow(pool, &community_follower_form)
471 let expected_community_follower = CommunityFollower {
472 id: inserted_community_follower.id,
473 community_id: inserted_community.id,
474 person_id: inserted_person.id,
475 pending: Some(false),
476 published: inserted_community_follower.published,
479 let community_moderator_form = CommunityModeratorForm {
480 community_id: inserted_community.id,
481 person_id: inserted_person.id,
484 let inserted_community_moderator = CommunityModerator::join(pool, &community_moderator_form)
488 let expected_community_moderator = CommunityModerator {
489 id: inserted_community_moderator.id,
490 community_id: inserted_community.id,
491 person_id: inserted_person.id,
492 published: inserted_community_moderator.published,
495 let community_person_ban_form = CommunityPersonBanForm {
496 community_id: inserted_community.id,
497 person_id: inserted_person.id,
501 let inserted_community_person_ban = CommunityPersonBan::ban(pool, &community_person_ban_form)
505 let expected_community_person_ban = CommunityPersonBan {
506 id: inserted_community_person_ban.id,
507 community_id: inserted_community.id,
508 person_id: inserted_person.id,
509 published: inserted_community_person_ban.published,
513 let read_community = Community::read(pool, inserted_community.id).await.unwrap();
515 let update_community_form = CommunityUpdateForm::builder()
516 .title(Some("nada".to_owned()))
518 let updated_community = Community::update(pool, inserted_community.id, &update_community_form)
522 let ignored_community = CommunityFollower::unfollow(pool, &community_follower_form)
525 let left_community = CommunityModerator::leave(pool, &community_moderator_form)
528 let unban = CommunityPersonBan::unban(pool, &community_person_ban_form)
531 let num_deleted = Community::delete(pool, inserted_community.id)
534 Person::delete(pool, inserted_person.id).await.unwrap();
535 Instance::delete(pool, inserted_instance.id).await.unwrap();
537 assert_eq!(expected_community, read_community);
538 assert_eq!(expected_community, inserted_community);
539 assert_eq!(expected_community, updated_community);
540 assert_eq!(expected_community_follower, inserted_community_follower);
541 assert_eq!(expected_community_moderator, inserted_community_moderator);
542 assert_eq!(expected_community_person_ban, inserted_community_person_ban);
543 assert_eq!(1, ignored_community);
544 assert_eq!(1, left_community);
545 assert_eq!(1, unban);
546 // assert_eq!(2, loaded_count);
547 assert_eq!(1, num_deleted);