2 newtypes::{CommunityId, DbUrl, PersonId},
4 actor_language::{CommunityLanguage, SiteLanguage},
11 CommunityModeratorForm,
13 CommunityPersonBanForm,
17 traits::{ApubActor, Bannable, Crud, DeleteableOrRemoveable, Followable, Joinable},
18 utils::{functions::lower, naive_now},
28 TextExpressionMethods,
32 use crate::{schema::community::*, source::community::Community, traits::ToSafe};
49 posting_restricted_to_mods,
52 impl ToSafe for Community {
53 type SafeColumns = Columns;
54 fn safe_columns_tuple() -> Self::SafeColumns {
70 posting_restricted_to_mods,
76 impl Crud for Community {
77 type Form = CommunityForm;
78 type IdType = CommunityId;
79 fn read(conn: &mut PgConnection, community_id: CommunityId) -> Result<Self, Error> {
80 use crate::schema::community::dsl::*;
81 community.find(community_id).first::<Self>(conn)
84 fn delete(conn: &mut PgConnection, community_id: CommunityId) -> Result<usize, Error> {
85 use crate::schema::community::dsl::*;
86 diesel::delete(community.find(community_id)).execute(conn)
89 fn create(conn: &mut PgConnection, new_community: &CommunityForm) -> Result<Self, Error> {
90 use crate::schema::community::dsl::*;
91 let community_ = insert_into(community)
92 .values(new_community)
93 .get_result::<Self>(conn)?;
95 let site_languages = SiteLanguage::read_local(conn);
96 if let Ok(langs) = site_languages {
97 // if site exists, init user with site languages
98 CommunityLanguage::update(conn, langs, community_.id)?;
100 // otherwise, init with all languages (this only happens during tests)
101 CommunityLanguage::update(conn, vec![], community_.id)?;
108 conn: &mut PgConnection,
109 community_id: CommunityId,
110 new_community: &CommunityForm,
111 ) -> Result<Self, Error> {
112 use crate::schema::community::dsl::*;
113 diesel::update(community.find(community_id))
115 .get_result::<Self>(conn)
120 pub fn update_deleted(
121 conn: &mut PgConnection,
122 community_id: CommunityId,
124 ) -> Result<Community, Error> {
125 use crate::schema::community::dsl::*;
126 diesel::update(community.find(community_id))
127 .set((deleted.eq(new_deleted), updated.eq(naive_now())))
128 .get_result::<Self>(conn)
131 pub fn update_removed(
132 conn: &mut PgConnection,
133 community_id: CommunityId,
135 ) -> Result<Community, Error> {
136 use crate::schema::community::dsl::*;
137 diesel::update(community.find(community_id))
138 .set((removed.eq(new_removed), updated.eq(naive_now())))
139 .get_result::<Self>(conn)
142 pub fn distinct_federated_communities(conn: &mut PgConnection) -> Result<Vec<DbUrl>, Error> {
143 use crate::schema::community::dsl::*;
144 community.select(actor_id).distinct().load::<DbUrl>(conn)
148 conn: &mut PgConnection,
149 community_form: &CommunityForm,
150 ) -> Result<Community, Error> {
151 use crate::schema::community::dsl::*;
152 insert_into(community)
153 .values(community_form)
154 .on_conflict(actor_id)
157 .get_result::<Self>(conn)
160 pub fn remove_avatar_and_banner(
161 conn: &mut PgConnection,
162 community_id: CommunityId,
163 ) -> Result<Self, Error> {
164 use crate::schema::community::dsl::*;
165 diesel::update(community.find(community_id))
167 icon.eq::<Option<String>>(None),
168 banner.eq::<Option<String>>(None),
170 .get_result::<Self>(conn)
174 impl Joinable for CommunityModerator {
175 type Form = CommunityModeratorForm;
177 conn: &mut PgConnection,
178 community_moderator_form: &CommunityModeratorForm,
179 ) -> Result<Self, Error> {
180 use crate::schema::community_moderator::dsl::*;
181 insert_into(community_moderator)
182 .values(community_moderator_form)
183 .get_result::<Self>(conn)
187 conn: &mut PgConnection,
188 community_moderator_form: &CommunityModeratorForm,
189 ) -> Result<usize, Error> {
190 use crate::schema::community_moderator::dsl::*;
193 .filter(community_id.eq(community_moderator_form.community_id))
194 .filter(person_id.eq(community_moderator_form.person_id)),
200 impl DeleteableOrRemoveable for CommunitySafe {
201 fn blank_out_deleted_or_removed_info(mut self) -> Self {
202 self.title = "".into();
203 self.description = None;
210 impl DeleteableOrRemoveable for Community {
211 fn blank_out_deleted_or_removed_info(mut self) -> Self {
212 self.title = "".into();
213 self.description = None;
220 impl CommunityModerator {
221 pub fn delete_for_community(
222 conn: &mut PgConnection,
223 for_community_id: CommunityId,
224 ) -> Result<usize, Error> {
225 use crate::schema::community_moderator::dsl::*;
226 diesel::delete(community_moderator.filter(community_id.eq(for_community_id))).execute(conn)
229 pub fn get_person_moderated_communities(
230 conn: &mut PgConnection,
231 for_person_id: PersonId,
232 ) -> Result<Vec<CommunityId>, Error> {
233 use crate::schema::community_moderator::dsl::*;
235 .filter(person_id.eq(for_person_id))
236 .select(community_id)
237 .load::<CommunityId>(conn)
241 impl Bannable for CommunityPersonBan {
242 type Form = CommunityPersonBanForm;
244 conn: &mut PgConnection,
245 community_person_ban_form: &CommunityPersonBanForm,
246 ) -> Result<Self, Error> {
247 use crate::schema::community_person_ban::dsl::*;
248 insert_into(community_person_ban)
249 .values(community_person_ban_form)
250 .on_conflict((community_id, person_id))
252 .set(community_person_ban_form)
253 .get_result::<Self>(conn)
257 conn: &mut PgConnection,
258 community_person_ban_form: &CommunityPersonBanForm,
259 ) -> Result<usize, Error> {
260 use crate::schema::community_person_ban::dsl::*;
263 .filter(community_id.eq(community_person_ban_form.community_id))
264 .filter(person_id.eq(community_person_ban_form.person_id)),
270 impl CommunityFollower {
271 pub fn to_subscribed_type(follower: &Option<Self>) -> SubscribedType {
274 if f.pending.unwrap_or(false) {
275 SubscribedType::Pending
277 SubscribedType::Subscribed
280 // If the row doesn't exist, the person isn't a follower.
281 None => SubscribedType::NotSubscribed,
286 impl Followable for CommunityFollower {
287 type Form = CommunityFollowerForm;
289 conn: &mut PgConnection,
290 community_follower_form: &CommunityFollowerForm,
291 ) -> Result<Self, Error> {
292 use crate::schema::community_follower::dsl::*;
293 insert_into(community_follower)
294 .values(community_follower_form)
295 .on_conflict((community_id, person_id))
297 .set(community_follower_form)
298 .get_result::<Self>(conn)
301 conn: &mut PgConnection,
302 community_id_: CommunityId,
303 person_id_: PersonId,
304 ) -> Result<Self, Error>
308 use crate::schema::community_follower::dsl::*;
311 .filter(community_id.eq(community_id_))
312 .filter(person_id.eq(person_id_)),
314 .set(pending.eq(false))
315 .get_result::<Self>(conn)
318 conn: &mut PgConnection,
319 community_follower_form: &CommunityFollowerForm,
320 ) -> Result<usize, Error> {
321 use crate::schema::community_follower::dsl::*;
324 .filter(community_id.eq(&community_follower_form.community_id))
325 .filter(person_id.eq(&community_follower_form.person_id)),
329 // TODO: this function name only makes sense if you call it with a remote community. for a local
330 // community, it will also return true if only remote followers exist
331 fn has_local_followers(
332 conn: &mut PgConnection,
333 community_id_: CommunityId,
334 ) -> Result<bool, Error> {
335 use crate::schema::community_follower::dsl::*;
336 diesel::select(exists(
337 community_follower.filter(community_id.eq(community_id_)),
343 impl ApubActor for Community {
344 fn read_from_apub_id(conn: &mut PgConnection, object_id: &DbUrl) -> Result<Option<Self>, Error> {
345 use crate::schema::community::dsl::*;
348 .filter(actor_id.eq(object_id))
349 .first::<Community>(conn)
356 conn: &mut PgConnection,
357 community_name: &str,
358 include_deleted: bool,
359 ) -> Result<Community, Error> {
360 use crate::schema::community::dsl::*;
361 let mut q = community
363 .filter(local.eq(true))
364 .filter(lower(name).eq(lower(community_name)));
365 if !include_deleted {
366 q = q.filter(deleted.eq(false)).filter(removed.eq(false));
368 q.first::<Self>(conn)
371 fn read_from_name_and_domain(
372 conn: &mut PgConnection,
373 community_name: &str,
374 protocol_domain: &str,
375 ) -> Result<Community, Error> {
376 use crate::schema::community::dsl::*;
378 .filter(lower(name).eq(lower(community_name)))
379 .filter(actor_id.like(format!("{}%", protocol_domain)))
387 source::{community::*, person::*},
388 traits::{Bannable, Crud, Followable, Joinable},
389 utils::establish_unpooled_connection,
391 use serial_test::serial;
396 let conn = &mut establish_unpooled_connection();
398 let new_person = PersonForm {
399 name: "bobbee".into(),
400 public_key: Some("pubkey".to_string()),
401 ..PersonForm::default()
404 let inserted_person = Person::create(conn, &new_person).unwrap();
406 let new_community = CommunityForm {
408 title: "nada".to_owned(),
409 public_key: Some("pubkey".to_string()),
410 ..CommunityForm::default()
413 let inserted_community = Community::create(conn, &new_community).unwrap();
415 let expected_community = Community {
416 id: inserted_community.id,
418 title: "nada".to_owned(),
423 published: inserted_community.published,
425 actor_id: inserted_community.actor_id.to_owned(),
428 public_key: "pubkey".to_owned(),
429 last_refreshed_at: inserted_community.published,
432 followers_url: inserted_community.followers_url.to_owned(),
433 inbox_url: inserted_community.inbox_url.to_owned(),
434 shared_inbox_url: None,
436 posting_restricted_to_mods: false,
439 let community_follower_form = CommunityFollowerForm {
440 community_id: inserted_community.id,
441 person_id: inserted_person.id,
445 let inserted_community_follower =
446 CommunityFollower::follow(conn, &community_follower_form).unwrap();
448 let expected_community_follower = CommunityFollower {
449 id: inserted_community_follower.id,
450 community_id: inserted_community.id,
451 person_id: inserted_person.id,
452 pending: Some(false),
453 published: inserted_community_follower.published,
456 let community_moderator_form = CommunityModeratorForm {
457 community_id: inserted_community.id,
458 person_id: inserted_person.id,
461 let inserted_community_moderator =
462 CommunityModerator::join(conn, &community_moderator_form).unwrap();
464 let expected_community_moderator = CommunityModerator {
465 id: inserted_community_moderator.id,
466 community_id: inserted_community.id,
467 person_id: inserted_person.id,
468 published: inserted_community_moderator.published,
471 let community_person_ban_form = CommunityPersonBanForm {
472 community_id: inserted_community.id,
473 person_id: inserted_person.id,
477 let inserted_community_person_ban =
478 CommunityPersonBan::ban(conn, &community_person_ban_form).unwrap();
480 let expected_community_person_ban = CommunityPersonBan {
481 id: inserted_community_person_ban.id,
482 community_id: inserted_community.id,
483 person_id: inserted_person.id,
484 published: inserted_community_person_ban.published,
488 let read_community = Community::read(conn, inserted_community.id).unwrap();
489 let updated_community = Community::update(conn, inserted_community.id, &new_community).unwrap();
490 let ignored_community = CommunityFollower::unfollow(conn, &community_follower_form).unwrap();
491 let left_community = CommunityModerator::leave(conn, &community_moderator_form).unwrap();
492 let unban = CommunityPersonBan::unban(conn, &community_person_ban_form).unwrap();
493 let num_deleted = Community::delete(conn, inserted_community.id).unwrap();
494 Person::delete(conn, inserted_person.id).unwrap();
496 assert_eq!(expected_community, read_community);
497 assert_eq!(expected_community, inserted_community);
498 assert_eq!(expected_community, updated_community);
499 assert_eq!(expected_community_follower, inserted_community_follower);
500 assert_eq!(expected_community_moderator, inserted_community_moderator);
501 assert_eq!(expected_community_person_ban, inserted_community_person_ban);
502 assert_eq!(1, ignored_community);
503 assert_eq!(1, left_community);
504 assert_eq!(1, unban);
505 // assert_eq!(2, loaded_count);
506 assert_eq!(1, num_deleted);