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},
23 use diesel::{dsl::insert_into, result::Error, ExpressionMethods, QueryDsl, TextExpressionMethods};
24 use diesel_async::RunQueryDsl;
40 posting_restricted_to_mods,
46 source::community::Community,
65 posting_restricted_to_mods,
69 impl ToSafe for Community {
70 type SafeColumns = Columns;
71 fn safe_columns_tuple() -> Self::SafeColumns {
87 posting_restricted_to_mods,
95 impl Crud for Community {
96 type InsertForm = CommunityInsertForm;
97 type UpdateForm = CommunityUpdateForm;
98 type IdType = CommunityId;
99 async fn read(pool: &DbPool, community_id: CommunityId) -> Result<Self, Error> {
100 let conn = &mut get_conn(pool).await?;
101 community.find(community_id).first::<Self>(conn).await
104 async fn delete(pool: &DbPool, community_id: CommunityId) -> Result<usize, Error> {
105 let conn = &mut get_conn(pool).await?;
106 diesel::delete(community.find(community_id))
111 async fn create(pool: &DbPool, form: &Self::InsertForm) -> Result<Self, Error> {
112 let conn = &mut get_conn(pool).await?;
113 let community_ = insert_into(community)
115 .on_conflict(actor_id)
118 .get_result::<Self>(conn)
121 let site_languages = SiteLanguage::read_local(pool).await;
122 if let Ok(langs) = site_languages {
123 // if site exists, init user with site languages
124 CommunityLanguage::update(pool, langs, community_.id).await?;
126 // otherwise, init with all languages (this only happens during tests)
127 CommunityLanguage::update(pool, vec![], community_.id).await?;
135 community_id: CommunityId,
136 form: &Self::UpdateForm,
137 ) -> Result<Self, Error> {
138 let conn = &mut get_conn(pool).await?;
139 diesel::update(community.find(community_id))
141 .get_result::<Self>(conn)
147 impl Joinable for CommunityModerator {
148 type Form = CommunityModeratorForm;
151 community_moderator_form: &CommunityModeratorForm,
152 ) -> Result<Self, Error> {
153 use crate::schema::community_moderator::dsl::community_moderator;
154 let conn = &mut get_conn(pool).await?;
155 insert_into(community_moderator)
156 .values(community_moderator_form)
157 .get_result::<Self>(conn)
163 community_moderator_form: &CommunityModeratorForm,
164 ) -> Result<usize, Error> {
165 use crate::schema::community_moderator::dsl::{community_id, community_moderator, person_id};
166 let conn = &mut get_conn(pool).await?;
169 .filter(community_id.eq(community_moderator_form.community_id))
170 .filter(person_id.eq(community_moderator_form.person_id)),
177 impl DeleteableOrRemoveable for CommunitySafe {
178 fn blank_out_deleted_or_removed_info(mut self) -> Self {
179 self.title = String::new();
180 self.description = None;
187 impl DeleteableOrRemoveable for Community {
188 fn blank_out_deleted_or_removed_info(mut self) -> Self {
189 self.title = String::new();
190 self.description = None;
197 pub enum CollectionType {
203 /// Get the community which has a given moderators or featured url, also return the collection type
204 pub async fn get_by_collection_url(
207 ) -> Result<(Community, CollectionType), Error> {
208 use crate::schema::community::dsl::{featured_url, moderators_url};
209 use CollectionType::*;
210 let conn = &mut get_conn(pool).await?;
212 .filter(moderators_url.eq(url))
216 return Ok((c, Moderators));
219 .filter(featured_url.eq(url))
223 return Ok((c, Featured));
225 Err(diesel::NotFound)
229 impl CommunityModerator {
230 pub async fn delete_for_community(
232 for_community_id: CommunityId,
233 ) -> Result<usize, Error> {
234 use crate::schema::community_moderator::dsl::{community_id, community_moderator};
235 let conn = &mut get_conn(pool).await?;
237 diesel::delete(community_moderator.filter(community_id.eq(for_community_id)))
242 pub async fn get_person_moderated_communities(
244 for_person_id: PersonId,
245 ) -> Result<Vec<CommunityId>, Error> {
246 use crate::schema::community_moderator::dsl::{community_id, community_moderator, person_id};
247 let conn = &mut get_conn(pool).await?;
249 .filter(person_id.eq(for_person_id))
250 .select(community_id)
251 .load::<CommunityId>(conn)
257 impl Bannable for CommunityPersonBan {
258 type Form = CommunityPersonBanForm;
261 community_person_ban_form: &CommunityPersonBanForm,
262 ) -> Result<Self, Error> {
263 use crate::schema::community_person_ban::dsl::{community_id, community_person_ban, person_id};
264 let conn = &mut get_conn(pool).await?;
265 insert_into(community_person_ban)
266 .values(community_person_ban_form)
267 .on_conflict((community_id, person_id))
269 .set(community_person_ban_form)
270 .get_result::<Self>(conn)
276 community_person_ban_form: &CommunityPersonBanForm,
277 ) -> Result<usize, Error> {
278 use crate::schema::community_person_ban::dsl::{community_id, community_person_ban, person_id};
279 let conn = &mut get_conn(pool).await?;
282 .filter(community_id.eq(community_person_ban_form.community_id))
283 .filter(person_id.eq(community_person_ban_form.person_id)),
290 impl CommunityFollower {
291 pub fn to_subscribed_type(follower: &Option<Self>) -> SubscribedType {
295 SubscribedType::Pending
297 SubscribedType::Subscribed
300 // If the row doesn't exist, the person isn't a follower.
301 None => SubscribedType::NotSubscribed,
307 impl Followable for CommunityFollower {
308 type Form = CommunityFollowerForm;
309 async fn follow(pool: &DbPool, form: &CommunityFollowerForm) -> Result<Self, Error> {
310 use crate::schema::community_follower::dsl::{community_follower, community_id, person_id};
311 let conn = &mut get_conn(pool).await?;
312 insert_into(community_follower)
314 .on_conflict((community_id, person_id))
317 .get_result::<Self>(conn)
320 async fn follow_accepted(
322 community_id_: CommunityId,
323 person_id_: PersonId,
324 ) -> Result<Self, Error> {
325 use crate::schema::community_follower::dsl::{
331 let conn = &mut get_conn(pool).await?;
334 .filter(community_id.eq(community_id_))
335 .filter(person_id.eq(person_id_)),
337 .set(pending.eq(false))
338 .get_result::<Self>(conn)
341 async fn unfollow(pool: &DbPool, form: &CommunityFollowerForm) -> Result<usize, Error> {
342 use crate::schema::community_follower::dsl::{community_follower, community_id, person_id};
343 let conn = &mut get_conn(pool).await?;
346 .filter(community_id.eq(&form.community_id))
347 .filter(person_id.eq(&form.person_id)),
355 impl ApubActor for Community {
356 async fn read_from_apub_id(pool: &DbPool, object_id: &DbUrl) -> Result<Option<Self>, Error> {
357 let conn = &mut get_conn(pool).await?;
360 .filter(actor_id.eq(object_id))
361 .first::<Community>(conn)
368 async fn read_from_name(
370 community_name: &str,
371 include_deleted: bool,
372 ) -> Result<Community, Error> {
373 let conn = &mut get_conn(pool).await?;
374 let mut q = community
376 .filter(local.eq(true))
377 .filter(lower(name).eq(lower(community_name)));
378 if !include_deleted {
379 q = q.filter(deleted.eq(false)).filter(removed.eq(false));
381 q.first::<Self>(conn).await
384 async fn read_from_name_and_domain(
386 community_name: &str,
387 protocol_domain: &str,
388 ) -> Result<Community, Error> {
389 let conn = &mut get_conn(pool).await?;
391 .filter(lower(name).eq(lower(community_name)))
392 .filter(actor_id.like(format!("{protocol_domain}%")))
405 CommunityFollowerForm,
408 CommunityModeratorForm,
410 CommunityPersonBanForm,
414 person::{Person, PersonInsertForm},
416 traits::{Bannable, Crud, Followable, Joinable},
417 utils::build_db_pool_for_tests,
419 use serial_test::serial;
423 async fn test_crud() {
424 let pool = &build_db_pool_for_tests().await;
426 let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
430 let new_person = PersonInsertForm::builder()
431 .name("bobbee".into())
432 .public_key("pubkey".to_string())
433 .instance_id(inserted_instance.id)
436 let inserted_person = Person::create(pool, &new_person).await.unwrap();
438 let new_community = CommunityInsertForm::builder()
440 .title("nada".to_owned())
441 .public_key("pubkey".to_string())
442 .instance_id(inserted_instance.id)
445 let inserted_community = Community::create(pool, &new_community).await.unwrap();
447 let expected_community = Community {
448 id: inserted_community.id,
450 title: "nada".to_owned(),
455 published: inserted_community.published,
457 actor_id: inserted_community.actor_id.clone(),
460 public_key: "pubkey".to_owned(),
461 last_refreshed_at: inserted_community.published,
464 followers_url: inserted_community.followers_url.clone(),
465 inbox_url: inserted_community.inbox_url.clone(),
466 shared_inbox_url: None,
467 moderators_url: None,
470 posting_restricted_to_mods: false,
471 instance_id: inserted_instance.id,
474 let community_follower_form = CommunityFollowerForm {
475 community_id: inserted_community.id,
476 person_id: inserted_person.id,
480 let inserted_community_follower = CommunityFollower::follow(pool, &community_follower_form)
484 let expected_community_follower = CommunityFollower {
485 id: inserted_community_follower.id,
486 community_id: inserted_community.id,
487 person_id: inserted_person.id,
489 published: inserted_community_follower.published,
492 let community_moderator_form = CommunityModeratorForm {
493 community_id: inserted_community.id,
494 person_id: inserted_person.id,
497 let inserted_community_moderator = CommunityModerator::join(pool, &community_moderator_form)
501 let expected_community_moderator = CommunityModerator {
502 id: inserted_community_moderator.id,
503 community_id: inserted_community.id,
504 person_id: inserted_person.id,
505 published: inserted_community_moderator.published,
508 let community_person_ban_form = CommunityPersonBanForm {
509 community_id: inserted_community.id,
510 person_id: inserted_person.id,
514 let inserted_community_person_ban = CommunityPersonBan::ban(pool, &community_person_ban_form)
518 let expected_community_person_ban = CommunityPersonBan {
519 id: inserted_community_person_ban.id,
520 community_id: inserted_community.id,
521 person_id: inserted_person.id,
522 published: inserted_community_person_ban.published,
526 let read_community = Community::read(pool, inserted_community.id).await.unwrap();
528 let update_community_form = CommunityUpdateForm::builder()
529 .title(Some("nada".to_owned()))
531 let updated_community = Community::update(pool, inserted_community.id, &update_community_form)
535 let ignored_community = CommunityFollower::unfollow(pool, &community_follower_form)
538 let left_community = CommunityModerator::leave(pool, &community_moderator_form)
541 let unban = CommunityPersonBan::unban(pool, &community_person_ban_form)
544 let num_deleted = Community::delete(pool, inserted_community.id)
547 Person::delete(pool, inserted_person.id).await.unwrap();
548 Instance::delete(pool, inserted_instance.id).await.unwrap();
550 assert_eq!(expected_community, read_community);
551 assert_eq!(expected_community, inserted_community);
552 assert_eq!(expected_community, updated_community);
553 assert_eq!(expected_community_follower, inserted_community_follower);
554 assert_eq!(expected_community_moderator, inserted_community_moderator);
555 assert_eq!(expected_community_person_ban, inserted_community_person_ban);
556 assert_eq!(1, ignored_community);
557 assert_eq!(1, left_community);
558 assert_eq!(1, unban);
559 // assert_eq!(2, loaded_count);
560 assert_eq!(1, num_deleted);