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,
18 traits::{ApubActor, Bannable, Crud, Followable, Joinable},
19 utils::{functions::lower, get_conn, DbPool},
22 use diesel::{dsl::insert_into, result::Error, ExpressionMethods, QueryDsl, TextExpressionMethods};
23 use diesel_async::RunQueryDsl;
39 posting_restricted_to_mods,
45 source::community::Community,
64 posting_restricted_to_mods,
68 impl ToSafe for Community {
69 type SafeColumns = Columns;
70 fn safe_columns_tuple() -> Self::SafeColumns {
86 posting_restricted_to_mods,
94 impl Crud for Community {
95 type InsertForm = CommunityInsertForm;
96 type UpdateForm = CommunityUpdateForm;
97 type IdType = CommunityId;
98 async fn read(pool: &DbPool, community_id: CommunityId) -> Result<Self, Error> {
99 let conn = &mut get_conn(pool).await?;
100 community.find(community_id).first::<Self>(conn).await
103 async fn delete(pool: &DbPool, community_id: CommunityId) -> Result<usize, Error> {
104 let conn = &mut get_conn(pool).await?;
105 diesel::delete(community.find(community_id))
110 async fn create(pool: &DbPool, form: &Self::InsertForm) -> Result<Self, Error> {
111 let conn = &mut get_conn(pool).await?;
112 let community_ = insert_into(community)
114 .on_conflict(actor_id)
117 .get_result::<Self>(conn)
120 let site_languages = SiteLanguage::read_local(pool).await;
121 if let Ok(langs) = site_languages {
122 // if site exists, init user with site languages
123 CommunityLanguage::update(pool, langs, community_.id).await?;
125 // otherwise, init with all languages (this only happens during tests)
126 CommunityLanguage::update(pool, vec![], community_.id).await?;
134 community_id: CommunityId,
135 form: &Self::UpdateForm,
136 ) -> Result<Self, Error> {
137 let conn = &mut get_conn(pool).await?;
138 diesel::update(community.find(community_id))
140 .get_result::<Self>(conn)
146 impl Joinable for CommunityModerator {
147 type Form = CommunityModeratorForm;
150 community_moderator_form: &CommunityModeratorForm,
151 ) -> Result<Self, Error> {
152 use crate::schema::community_moderator::dsl::community_moderator;
153 let conn = &mut get_conn(pool).await?;
154 insert_into(community_moderator)
155 .values(community_moderator_form)
156 .get_result::<Self>(conn)
162 community_moderator_form: &CommunityModeratorForm,
163 ) -> Result<usize, Error> {
164 use crate::schema::community_moderator::dsl::{community_id, community_moderator, person_id};
165 let conn = &mut get_conn(pool).await?;
168 .filter(community_id.eq(community_moderator_form.community_id))
169 .filter(person_id.eq(community_moderator_form.person_id)),
176 pub enum CollectionType {
182 /// Get the community which has a given moderators or featured url, also return the collection type
183 pub async fn get_by_collection_url(
186 ) -> Result<(Community, CollectionType), Error> {
187 use crate::schema::community::dsl::{featured_url, moderators_url};
188 use CollectionType::*;
189 let conn = &mut get_conn(pool).await?;
191 .filter(moderators_url.eq(url))
195 return Ok((c, Moderators));
198 .filter(featured_url.eq(url))
202 return Ok((c, Featured));
204 Err(diesel::NotFound)
208 impl CommunityModerator {
209 pub async fn delete_for_community(
211 for_community_id: CommunityId,
212 ) -> Result<usize, Error> {
213 use crate::schema::community_moderator::dsl::{community_id, community_moderator};
214 let conn = &mut get_conn(pool).await?;
216 diesel::delete(community_moderator.filter(community_id.eq(for_community_id)))
221 pub async fn get_person_moderated_communities(
223 for_person_id: PersonId,
224 ) -> Result<Vec<CommunityId>, Error> {
225 use crate::schema::community_moderator::dsl::{community_id, community_moderator, person_id};
226 let conn = &mut get_conn(pool).await?;
228 .filter(person_id.eq(for_person_id))
229 .select(community_id)
230 .load::<CommunityId>(conn)
236 impl Bannable for CommunityPersonBan {
237 type Form = CommunityPersonBanForm;
240 community_person_ban_form: &CommunityPersonBanForm,
241 ) -> Result<Self, Error> {
242 use crate::schema::community_person_ban::dsl::{community_id, community_person_ban, person_id};
243 let conn = &mut get_conn(pool).await?;
244 insert_into(community_person_ban)
245 .values(community_person_ban_form)
246 .on_conflict((community_id, person_id))
248 .set(community_person_ban_form)
249 .get_result::<Self>(conn)
255 community_person_ban_form: &CommunityPersonBanForm,
256 ) -> Result<usize, Error> {
257 use crate::schema::community_person_ban::dsl::{community_id, community_person_ban, person_id};
258 let conn = &mut get_conn(pool).await?;
261 .filter(community_id.eq(community_person_ban_form.community_id))
262 .filter(person_id.eq(community_person_ban_form.person_id)),
269 impl CommunityFollower {
270 pub fn to_subscribed_type(follower: &Option<Self>) -> SubscribedType {
274 SubscribedType::Pending
276 SubscribedType::Subscribed
279 // If the row doesn't exist, the person isn't a follower.
280 None => SubscribedType::NotSubscribed,
286 impl Followable for CommunityFollower {
287 type Form = CommunityFollowerForm;
288 async fn follow(pool: &DbPool, form: &CommunityFollowerForm) -> Result<Self, Error> {
289 use crate::schema::community_follower::dsl::{community_follower, community_id, person_id};
290 let conn = &mut get_conn(pool).await?;
291 insert_into(community_follower)
293 .on_conflict((community_id, person_id))
296 .get_result::<Self>(conn)
299 async fn follow_accepted(
301 community_id_: CommunityId,
302 person_id_: PersonId,
303 ) -> Result<Self, Error> {
304 use crate::schema::community_follower::dsl::{
310 let conn = &mut get_conn(pool).await?;
313 .filter(community_id.eq(community_id_))
314 .filter(person_id.eq(person_id_)),
316 .set(pending.eq(false))
317 .get_result::<Self>(conn)
320 async fn unfollow(pool: &DbPool, form: &CommunityFollowerForm) -> Result<usize, Error> {
321 use crate::schema::community_follower::dsl::{community_follower, community_id, person_id};
322 let conn = &mut get_conn(pool).await?;
325 .filter(community_id.eq(&form.community_id))
326 .filter(person_id.eq(&form.person_id)),
334 impl ApubActor for Community {
335 async fn read_from_apub_id(pool: &DbPool, object_id: &DbUrl) -> Result<Option<Self>, Error> {
336 let conn = &mut get_conn(pool).await?;
339 .filter(actor_id.eq(object_id))
340 .first::<Community>(conn)
347 async fn read_from_name(
349 community_name: &str,
350 include_deleted: bool,
351 ) -> Result<Community, Error> {
352 let conn = &mut get_conn(pool).await?;
353 let mut q = community
355 .filter(local.eq(true))
356 .filter(lower(name).eq(lower(community_name)));
357 if !include_deleted {
358 q = q.filter(deleted.eq(false)).filter(removed.eq(false));
360 q.first::<Self>(conn).await
363 async fn read_from_name_and_domain(
365 community_name: &str,
366 protocol_domain: &str,
367 ) -> Result<Community, Error> {
368 let conn = &mut get_conn(pool).await?;
370 .filter(lower(name).eq(lower(community_name)))
371 .filter(actor_id.like(format!("{protocol_domain}%")))
384 CommunityFollowerForm,
387 CommunityModeratorForm,
389 CommunityPersonBanForm,
393 person::{Person, PersonInsertForm},
395 traits::{Bannable, Crud, Followable, Joinable},
396 utils::build_db_pool_for_tests,
398 use serial_test::serial;
402 async fn test_crud() {
403 let pool = &build_db_pool_for_tests().await;
405 let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
409 let new_person = PersonInsertForm::builder()
410 .name("bobbee".into())
411 .public_key("pubkey".to_string())
412 .instance_id(inserted_instance.id)
415 let inserted_person = Person::create(pool, &new_person).await.unwrap();
417 let new_community = CommunityInsertForm::builder()
419 .title("nada".to_owned())
420 .public_key("pubkey".to_string())
421 .instance_id(inserted_instance.id)
424 let inserted_community = Community::create(pool, &new_community).await.unwrap();
426 let expected_community = Community {
427 id: inserted_community.id,
429 title: "nada".to_owned(),
434 published: inserted_community.published,
436 actor_id: inserted_community.actor_id.clone(),
439 public_key: "pubkey".to_owned(),
440 last_refreshed_at: inserted_community.published,
443 followers_url: inserted_community.followers_url.clone(),
444 inbox_url: inserted_community.inbox_url.clone(),
445 shared_inbox_url: None,
446 moderators_url: None,
449 posting_restricted_to_mods: false,
450 instance_id: inserted_instance.id,
453 let community_follower_form = CommunityFollowerForm {
454 community_id: inserted_community.id,
455 person_id: inserted_person.id,
459 let inserted_community_follower = CommunityFollower::follow(pool, &community_follower_form)
463 let expected_community_follower = CommunityFollower {
464 id: inserted_community_follower.id,
465 community_id: inserted_community.id,
466 person_id: inserted_person.id,
468 published: inserted_community_follower.published,
471 let community_moderator_form = CommunityModeratorForm {
472 community_id: inserted_community.id,
473 person_id: inserted_person.id,
476 let inserted_community_moderator = CommunityModerator::join(pool, &community_moderator_form)
480 let expected_community_moderator = CommunityModerator {
481 id: inserted_community_moderator.id,
482 community_id: inserted_community.id,
483 person_id: inserted_person.id,
484 published: inserted_community_moderator.published,
487 let community_person_ban_form = CommunityPersonBanForm {
488 community_id: inserted_community.id,
489 person_id: inserted_person.id,
493 let inserted_community_person_ban = CommunityPersonBan::ban(pool, &community_person_ban_form)
497 let expected_community_person_ban = CommunityPersonBan {
498 id: inserted_community_person_ban.id,
499 community_id: inserted_community.id,
500 person_id: inserted_person.id,
501 published: inserted_community_person_ban.published,
505 let read_community = Community::read(pool, inserted_community.id).await.unwrap();
507 let update_community_form = CommunityUpdateForm::builder()
508 .title(Some("nada".to_owned()))
510 let updated_community = Community::update(pool, inserted_community.id, &update_community_form)
514 let ignored_community = CommunityFollower::unfollow(pool, &community_follower_form)
517 let left_community = CommunityModerator::leave(pool, &community_moderator_form)
520 let unban = CommunityPersonBan::unban(pool, &community_person_ban_form)
523 let num_deleted = Community::delete(pool, inserted_community.id)
526 Person::delete(pool, inserted_person.id).await.unwrap();
527 Instance::delete(pool, inserted_instance.id).await.unwrap();
529 assert_eq!(expected_community, read_community);
530 assert_eq!(expected_community, inserted_community);
531 assert_eq!(expected_community, updated_community);
532 assert_eq!(expected_community_follower, inserted_community_follower);
533 assert_eq!(expected_community_moderator, inserted_community_moderator);
534 assert_eq!(expected_community_person_ban, inserted_community_person_ban);
535 assert_eq!(1, ignored_community);
536 assert_eq!(1, left_community);
537 assert_eq!(1, unban);
538 // assert_eq!(2, loaded_count);
539 assert_eq!(1, num_deleted);