2 newtypes::{CommunityId, DbUrl, PersonId},
9 CommunityModeratorForm,
11 CommunityPersonBanForm,
14 traits::{ApubActor, Bannable, Crud, DeleteableOrRemoveable, Followable, Joinable},
15 utils::{functions::lower, naive_now},
25 TextExpressionMethods,
29 use crate::{schema::community::*, source::community::Community, traits::ToSafe};
46 posting_restricted_to_mods,
49 impl ToSafe for Community {
50 type SafeColumns = Columns;
51 fn safe_columns_tuple() -> Self::SafeColumns {
67 posting_restricted_to_mods,
73 impl Crud for Community {
74 type Form = CommunityForm;
75 type IdType = CommunityId;
76 fn read(conn: &mut PgConnection, community_id: CommunityId) -> Result<Self, Error> {
77 use crate::schema::community::dsl::*;
78 community.find(community_id).first::<Self>(conn)
81 fn delete(conn: &mut PgConnection, community_id: CommunityId) -> Result<usize, Error> {
82 use crate::schema::community::dsl::*;
83 diesel::delete(community.find(community_id)).execute(conn)
86 fn create(conn: &mut PgConnection, new_community: &CommunityForm) -> Result<Self, Error> {
87 use crate::schema::community::dsl::*;
88 insert_into(community)
89 .values(new_community)
90 .get_result::<Self>(conn)
94 conn: &mut PgConnection,
95 community_id: CommunityId,
96 new_community: &CommunityForm,
97 ) -> Result<Self, Error> {
98 use crate::schema::community::dsl::*;
99 diesel::update(community.find(community_id))
101 .get_result::<Self>(conn)
106 pub fn update_deleted(
107 conn: &mut PgConnection,
108 community_id: CommunityId,
110 ) -> Result<Community, Error> {
111 use crate::schema::community::dsl::*;
112 diesel::update(community.find(community_id))
113 .set((deleted.eq(new_deleted), updated.eq(naive_now())))
114 .get_result::<Self>(conn)
117 pub fn update_removed(
118 conn: &mut PgConnection,
119 community_id: CommunityId,
121 ) -> Result<Community, Error> {
122 use crate::schema::community::dsl::*;
123 diesel::update(community.find(community_id))
124 .set((removed.eq(new_removed), updated.eq(naive_now())))
125 .get_result::<Self>(conn)
128 pub fn distinct_federated_communities(conn: &mut PgConnection) -> Result<Vec<DbUrl>, Error> {
129 use crate::schema::community::dsl::*;
130 community.select(actor_id).distinct().load::<DbUrl>(conn)
134 conn: &mut PgConnection,
135 community_form: &CommunityForm,
136 ) -> Result<Community, Error> {
137 use crate::schema::community::dsl::*;
138 insert_into(community)
139 .values(community_form)
140 .on_conflict(actor_id)
143 .get_result::<Self>(conn)
146 pub fn remove_avatar_and_banner(
147 conn: &mut PgConnection,
148 community_id: CommunityId,
149 ) -> Result<Self, Error> {
150 use crate::schema::community::dsl::*;
151 diesel::update(community.find(community_id))
153 icon.eq::<Option<String>>(None),
154 banner.eq::<Option<String>>(None),
156 .get_result::<Self>(conn)
160 impl Joinable for CommunityModerator {
161 type Form = CommunityModeratorForm;
163 conn: &mut PgConnection,
164 community_moderator_form: &CommunityModeratorForm,
165 ) -> Result<Self, Error> {
166 use crate::schema::community_moderator::dsl::*;
167 insert_into(community_moderator)
168 .values(community_moderator_form)
169 .get_result::<Self>(conn)
173 conn: &mut PgConnection,
174 community_moderator_form: &CommunityModeratorForm,
175 ) -> Result<usize, Error> {
176 use crate::schema::community_moderator::dsl::*;
179 .filter(community_id.eq(community_moderator_form.community_id))
180 .filter(person_id.eq(community_moderator_form.person_id)),
186 impl DeleteableOrRemoveable for CommunitySafe {
187 fn blank_out_deleted_or_removed_info(mut self) -> Self {
188 self.title = "".into();
189 self.description = None;
196 impl DeleteableOrRemoveable for Community {
197 fn blank_out_deleted_or_removed_info(mut self) -> Self {
198 self.title = "".into();
199 self.description = None;
206 impl CommunityModerator {
207 pub fn delete_for_community(
208 conn: &mut PgConnection,
209 for_community_id: CommunityId,
210 ) -> Result<usize, Error> {
211 use crate::schema::community_moderator::dsl::*;
212 diesel::delete(community_moderator.filter(community_id.eq(for_community_id))).execute(conn)
215 pub fn get_person_moderated_communities(
216 conn: &mut PgConnection,
217 for_person_id: PersonId,
218 ) -> Result<Vec<CommunityId>, Error> {
219 use crate::schema::community_moderator::dsl::*;
221 .filter(person_id.eq(for_person_id))
222 .select(community_id)
223 .load::<CommunityId>(conn)
227 impl Bannable for CommunityPersonBan {
228 type Form = CommunityPersonBanForm;
230 conn: &mut PgConnection,
231 community_person_ban_form: &CommunityPersonBanForm,
232 ) -> Result<Self, Error> {
233 use crate::schema::community_person_ban::dsl::*;
234 insert_into(community_person_ban)
235 .values(community_person_ban_form)
236 .on_conflict((community_id, person_id))
238 .set(community_person_ban_form)
239 .get_result::<Self>(conn)
243 conn: &mut PgConnection,
244 community_person_ban_form: &CommunityPersonBanForm,
245 ) -> Result<usize, Error> {
246 use crate::schema::community_person_ban::dsl::*;
249 .filter(community_id.eq(community_person_ban_form.community_id))
250 .filter(person_id.eq(community_person_ban_form.person_id)),
256 impl CommunityFollower {
257 pub fn to_subscribed_type(follower: &Option<Self>) -> SubscribedType {
260 if f.pending.unwrap_or(false) {
261 SubscribedType::Pending
263 SubscribedType::Subscribed
266 // If the row doesn't exist, the person isn't a follower.
267 None => SubscribedType::NotSubscribed,
272 impl Followable for CommunityFollower {
273 type Form = CommunityFollowerForm;
275 conn: &mut PgConnection,
276 community_follower_form: &CommunityFollowerForm,
277 ) -> Result<Self, Error> {
278 use crate::schema::community_follower::dsl::*;
279 insert_into(community_follower)
280 .values(community_follower_form)
281 .on_conflict((community_id, person_id))
283 .set(community_follower_form)
284 .get_result::<Self>(conn)
287 conn: &mut PgConnection,
288 community_id_: CommunityId,
289 person_id_: PersonId,
290 ) -> Result<Self, Error>
294 use crate::schema::community_follower::dsl::*;
297 .filter(community_id.eq(community_id_))
298 .filter(person_id.eq(person_id_)),
300 .set(pending.eq(false))
301 .get_result::<Self>(conn)
304 conn: &mut PgConnection,
305 community_follower_form: &CommunityFollowerForm,
306 ) -> Result<usize, Error> {
307 use crate::schema::community_follower::dsl::*;
310 .filter(community_id.eq(&community_follower_form.community_id))
311 .filter(person_id.eq(&community_follower_form.person_id)),
315 // TODO: this function name only makes sense if you call it with a remote community. for a local
316 // community, it will also return true if only remote followers exist
317 fn has_local_followers(
318 conn: &mut PgConnection,
319 community_id_: CommunityId,
320 ) -> Result<bool, Error> {
321 use crate::schema::community_follower::dsl::*;
322 diesel::select(exists(
323 community_follower.filter(community_id.eq(community_id_)),
329 impl ApubActor for Community {
330 fn read_from_apub_id(conn: &mut PgConnection, object_id: &DbUrl) -> Result<Option<Self>, Error> {
331 use crate::schema::community::dsl::*;
334 .filter(actor_id.eq(object_id))
335 .first::<Community>(conn)
342 conn: &mut PgConnection,
343 community_name: &str,
344 include_deleted: bool,
345 ) -> Result<Community, Error> {
346 use crate::schema::community::dsl::*;
347 let mut q = community
349 .filter(local.eq(true))
350 .filter(lower(name).eq(lower(community_name)));
351 if !include_deleted {
352 q = q.filter(deleted.eq(false)).filter(removed.eq(false));
354 q.first::<Self>(conn)
357 fn read_from_name_and_domain(
358 conn: &mut PgConnection,
359 community_name: &str,
360 protocol_domain: &str,
361 ) -> Result<Community, Error> {
362 use crate::schema::community::dsl::*;
364 .filter(lower(name).eq(lower(community_name)))
365 .filter(actor_id.like(format!("{}%", protocol_domain)))
373 source::{community::*, person::*},
374 traits::{Bannable, Crud, Followable, Joinable},
375 utils::establish_unpooled_connection,
377 use serial_test::serial;
382 let conn = &mut establish_unpooled_connection();
384 let new_person = PersonForm {
385 name: "bobbee".into(),
386 public_key: Some("pubkey".to_string()),
387 ..PersonForm::default()
390 let inserted_person = Person::create(conn, &new_person).unwrap();
392 let new_community = CommunityForm {
394 title: "nada".to_owned(),
395 public_key: Some("pubkey".to_string()),
396 ..CommunityForm::default()
399 let inserted_community = Community::create(conn, &new_community).unwrap();
401 let expected_community = Community {
402 id: inserted_community.id,
404 title: "nada".to_owned(),
409 published: inserted_community.published,
411 actor_id: inserted_community.actor_id.to_owned(),
414 public_key: "pubkey".to_owned(),
415 last_refreshed_at: inserted_community.published,
418 followers_url: inserted_community.followers_url.to_owned(),
419 inbox_url: inserted_community.inbox_url.to_owned(),
420 shared_inbox_url: None,
422 posting_restricted_to_mods: false,
425 let community_follower_form = CommunityFollowerForm {
426 community_id: inserted_community.id,
427 person_id: inserted_person.id,
431 let inserted_community_follower =
432 CommunityFollower::follow(conn, &community_follower_form).unwrap();
434 let expected_community_follower = CommunityFollower {
435 id: inserted_community_follower.id,
436 community_id: inserted_community.id,
437 person_id: inserted_person.id,
438 pending: Some(false),
439 published: inserted_community_follower.published,
442 let community_moderator_form = CommunityModeratorForm {
443 community_id: inserted_community.id,
444 person_id: inserted_person.id,
447 let inserted_community_moderator =
448 CommunityModerator::join(conn, &community_moderator_form).unwrap();
450 let expected_community_moderator = CommunityModerator {
451 id: inserted_community_moderator.id,
452 community_id: inserted_community.id,
453 person_id: inserted_person.id,
454 published: inserted_community_moderator.published,
457 let community_person_ban_form = CommunityPersonBanForm {
458 community_id: inserted_community.id,
459 person_id: inserted_person.id,
463 let inserted_community_person_ban =
464 CommunityPersonBan::ban(conn, &community_person_ban_form).unwrap();
466 let expected_community_person_ban = CommunityPersonBan {
467 id: inserted_community_person_ban.id,
468 community_id: inserted_community.id,
469 person_id: inserted_person.id,
470 published: inserted_community_person_ban.published,
474 let read_community = Community::read(conn, inserted_community.id).unwrap();
475 let updated_community = Community::update(conn, inserted_community.id, &new_community).unwrap();
476 let ignored_community = CommunityFollower::unfollow(conn, &community_follower_form).unwrap();
477 let left_community = CommunityModerator::leave(conn, &community_moderator_form).unwrap();
478 let unban = CommunityPersonBan::unban(conn, &community_person_ban_form).unwrap();
479 let num_deleted = Community::delete(conn, inserted_community.id).unwrap();
480 Person::delete(conn, inserted_person.id).unwrap();
482 assert_eq!(expected_community, read_community);
483 assert_eq!(expected_community, inserted_community);
484 assert_eq!(expected_community, updated_community);
485 assert_eq!(expected_community_follower, inserted_community_follower);
486 assert_eq!(expected_community_moderator, inserted_community_moderator);
487 assert_eq!(expected_community_person_ban, inserted_community_person_ban);
488 assert_eq!(1, ignored_community);
489 assert_eq!(1, left_community);
490 assert_eq!(1, unban);
491 // assert_eq!(2, loaded_count);
492 assert_eq!(1, num_deleted);