2 newtypes::{CommunityId, DbUrl, PersonId},
4 actor_language::{CommunityLanguage, SiteLanguage},
11 CommunityModeratorForm,
13 CommunityPersonBanForm,
18 traits::{ApubActor, Bannable, Crud, DeleteableOrRemoveable, Followable, Joinable},
19 utils::functions::lower,
29 TextExpressionMethods,
33 use crate::{schema::community::*, source::community::Community, traits::ToSafe};
50 posting_restricted_to_mods,
54 impl ToSafe for Community {
55 type SafeColumns = Columns;
56 fn safe_columns_tuple() -> Self::SafeColumns {
72 posting_restricted_to_mods,
79 impl Crud for Community {
80 type InsertForm = CommunityInsertForm;
81 type UpdateForm = CommunityUpdateForm;
82 type IdType = CommunityId;
83 fn read(conn: &mut PgConnection, community_id: CommunityId) -> Result<Self, Error> {
84 use crate::schema::community::dsl::*;
85 community.find(community_id).first::<Self>(conn)
88 fn delete(conn: &mut PgConnection, community_id: CommunityId) -> Result<usize, Error> {
89 use crate::schema::community::dsl::*;
90 diesel::delete(community.find(community_id)).execute(conn)
93 fn create(conn: &mut PgConnection, form: &Self::InsertForm) -> Result<Self, Error> {
94 use crate::schema::community::dsl::*;
95 let community_ = insert_into(community)
97 .on_conflict(actor_id)
100 .get_result::<Self>(conn)?;
102 let site_languages = SiteLanguage::read_local(conn);
103 if let Ok(langs) = site_languages {
104 // if site exists, init user with site languages
105 CommunityLanguage::update(conn, langs, community_.id)?;
107 // otherwise, init with all languages (this only happens during tests)
108 CommunityLanguage::update(conn, vec![], community_.id)?;
115 conn: &mut PgConnection,
116 community_id: CommunityId,
117 form: &Self::UpdateForm,
118 ) -> Result<Self, Error> {
119 use crate::schema::community::dsl::*;
120 diesel::update(community.find(community_id))
122 .get_result::<Self>(conn)
126 impl Joinable for CommunityModerator {
127 type Form = CommunityModeratorForm;
129 conn: &mut PgConnection,
130 community_moderator_form: &CommunityModeratorForm,
131 ) -> Result<Self, Error> {
132 use crate::schema::community_moderator::dsl::*;
133 insert_into(community_moderator)
134 .values(community_moderator_form)
135 .get_result::<Self>(conn)
139 conn: &mut PgConnection,
140 community_moderator_form: &CommunityModeratorForm,
141 ) -> Result<usize, Error> {
142 use crate::schema::community_moderator::dsl::*;
145 .filter(community_id.eq(community_moderator_form.community_id))
146 .filter(person_id.eq(community_moderator_form.person_id)),
152 impl DeleteableOrRemoveable for CommunitySafe {
153 fn blank_out_deleted_or_removed_info(mut self) -> Self {
154 self.title = "".into();
155 self.description = None;
162 impl DeleteableOrRemoveable for Community {
163 fn blank_out_deleted_or_removed_info(mut self) -> Self {
164 self.title = "".into();
165 self.description = None;
172 impl CommunityModerator {
173 pub fn delete_for_community(
174 conn: &mut PgConnection,
175 for_community_id: CommunityId,
176 ) -> Result<usize, Error> {
177 use crate::schema::community_moderator::dsl::*;
178 diesel::delete(community_moderator.filter(community_id.eq(for_community_id))).execute(conn)
181 pub fn get_person_moderated_communities(
182 conn: &mut PgConnection,
183 for_person_id: PersonId,
184 ) -> Result<Vec<CommunityId>, Error> {
185 use crate::schema::community_moderator::dsl::*;
187 .filter(person_id.eq(for_person_id))
188 .select(community_id)
189 .load::<CommunityId>(conn)
193 impl Bannable for CommunityPersonBan {
194 type Form = CommunityPersonBanForm;
196 conn: &mut PgConnection,
197 community_person_ban_form: &CommunityPersonBanForm,
198 ) -> Result<Self, Error> {
199 use crate::schema::community_person_ban::dsl::*;
200 insert_into(community_person_ban)
201 .values(community_person_ban_form)
202 .on_conflict((community_id, person_id))
204 .set(community_person_ban_form)
205 .get_result::<Self>(conn)
209 conn: &mut PgConnection,
210 community_person_ban_form: &CommunityPersonBanForm,
211 ) -> Result<usize, Error> {
212 use crate::schema::community_person_ban::dsl::*;
215 .filter(community_id.eq(community_person_ban_form.community_id))
216 .filter(person_id.eq(community_person_ban_form.person_id)),
222 impl CommunityFollower {
223 pub fn to_subscribed_type(follower: &Option<Self>) -> SubscribedType {
226 if f.pending.unwrap_or(false) {
227 SubscribedType::Pending
229 SubscribedType::Subscribed
232 // If the row doesn't exist, the person isn't a follower.
233 None => SubscribedType::NotSubscribed,
238 impl Followable for CommunityFollower {
239 type Form = CommunityFollowerForm;
241 conn: &mut PgConnection,
242 community_follower_form: &CommunityFollowerForm,
243 ) -> Result<Self, Error> {
244 use crate::schema::community_follower::dsl::*;
245 insert_into(community_follower)
246 .values(community_follower_form)
247 .on_conflict((community_id, person_id))
249 .set(community_follower_form)
250 .get_result::<Self>(conn)
253 conn: &mut PgConnection,
254 community_id_: CommunityId,
255 person_id_: PersonId,
256 ) -> Result<Self, Error>
260 use crate::schema::community_follower::dsl::*;
263 .filter(community_id.eq(community_id_))
264 .filter(person_id.eq(person_id_)),
266 .set(pending.eq(false))
267 .get_result::<Self>(conn)
270 conn: &mut PgConnection,
271 community_follower_form: &CommunityFollowerForm,
272 ) -> Result<usize, Error> {
273 use crate::schema::community_follower::dsl::*;
276 .filter(community_id.eq(&community_follower_form.community_id))
277 .filter(person_id.eq(&community_follower_form.person_id)),
281 // TODO: this function name only makes sense if you call it with a remote community. for a local
282 // community, it will also return true if only remote followers exist
283 fn has_local_followers(
284 conn: &mut PgConnection,
285 community_id_: CommunityId,
286 ) -> Result<bool, Error> {
287 use crate::schema::community_follower::dsl::*;
288 diesel::select(exists(
289 community_follower.filter(community_id.eq(community_id_)),
295 impl ApubActor for Community {
296 fn read_from_apub_id(conn: &mut PgConnection, object_id: &DbUrl) -> Result<Option<Self>, Error> {
297 use crate::schema::community::dsl::*;
300 .filter(actor_id.eq(object_id))
301 .first::<Community>(conn)
308 conn: &mut PgConnection,
309 community_name: &str,
310 include_deleted: bool,
311 ) -> Result<Community, Error> {
312 use crate::schema::community::dsl::*;
313 let mut q = community
315 .filter(local.eq(true))
316 .filter(lower(name).eq(lower(community_name)));
317 if !include_deleted {
318 q = q.filter(deleted.eq(false)).filter(removed.eq(false));
320 q.first::<Self>(conn)
323 fn read_from_name_and_domain(
324 conn: &mut PgConnection,
325 community_name: &str,
326 protocol_domain: &str,
327 ) -> Result<Community, Error> {
328 use crate::schema::community::dsl::*;
330 .filter(lower(name).eq(lower(community_name)))
331 .filter(actor_id.like(format!("{}%", protocol_domain)))
339 source::{community::*, instance::Instance, person::*},
340 traits::{Bannable, Crud, Followable, Joinable},
341 utils::establish_unpooled_connection,
343 use serial_test::serial;
348 let conn = &mut establish_unpooled_connection();
350 let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap();
352 let new_person = PersonInsertForm::builder()
353 .name("bobbee".into())
354 .public_key("pubkey".to_string())
355 .instance_id(inserted_instance.id)
358 let inserted_person = Person::create(conn, &new_person).unwrap();
360 let new_community = CommunityInsertForm::builder()
362 .title("nada".to_owned())
363 .public_key("pubkey".to_string())
364 .instance_id(inserted_instance.id)
367 let inserted_community = Community::create(conn, &new_community).unwrap();
369 let expected_community = Community {
370 id: inserted_community.id,
372 title: "nada".to_owned(),
377 published: inserted_community.published,
379 actor_id: inserted_community.actor_id.to_owned(),
382 public_key: "pubkey".to_owned(),
383 last_refreshed_at: inserted_community.published,
386 followers_url: inserted_community.followers_url.to_owned(),
387 inbox_url: inserted_community.inbox_url.to_owned(),
388 shared_inbox_url: None,
390 posting_restricted_to_mods: false,
391 instance_id: inserted_instance.id,
394 let community_follower_form = CommunityFollowerForm {
395 community_id: inserted_community.id,
396 person_id: inserted_person.id,
400 let inserted_community_follower =
401 CommunityFollower::follow(conn, &community_follower_form).unwrap();
403 let expected_community_follower = CommunityFollower {
404 id: inserted_community_follower.id,
405 community_id: inserted_community.id,
406 person_id: inserted_person.id,
407 pending: Some(false),
408 published: inserted_community_follower.published,
411 let community_moderator_form = CommunityModeratorForm {
412 community_id: inserted_community.id,
413 person_id: inserted_person.id,
416 let inserted_community_moderator =
417 CommunityModerator::join(conn, &community_moderator_form).unwrap();
419 let expected_community_moderator = CommunityModerator {
420 id: inserted_community_moderator.id,
421 community_id: inserted_community.id,
422 person_id: inserted_person.id,
423 published: inserted_community_moderator.published,
426 let community_person_ban_form = CommunityPersonBanForm {
427 community_id: inserted_community.id,
428 person_id: inserted_person.id,
432 let inserted_community_person_ban =
433 CommunityPersonBan::ban(conn, &community_person_ban_form).unwrap();
435 let expected_community_person_ban = CommunityPersonBan {
436 id: inserted_community_person_ban.id,
437 community_id: inserted_community.id,
438 person_id: inserted_person.id,
439 published: inserted_community_person_ban.published,
443 let read_community = Community::read(conn, inserted_community.id).unwrap();
445 let update_community_form = CommunityUpdateForm::builder()
446 .title(Some("nada".to_owned()))
448 let updated_community =
449 Community::update(conn, inserted_community.id, &update_community_form).unwrap();
451 let ignored_community = CommunityFollower::unfollow(conn, &community_follower_form).unwrap();
452 let left_community = CommunityModerator::leave(conn, &community_moderator_form).unwrap();
453 let unban = CommunityPersonBan::unban(conn, &community_person_ban_form).unwrap();
454 let num_deleted = Community::delete(conn, inserted_community.id).unwrap();
455 Person::delete(conn, inserted_person.id).unwrap();
456 Instance::delete(conn, inserted_instance.id).unwrap();
458 assert_eq!(expected_community, read_community);
459 assert_eq!(expected_community, inserted_community);
460 assert_eq!(expected_community, updated_community);
461 assert_eq!(expected_community_follower, inserted_community_follower);
462 assert_eq!(expected_community_moderator, inserted_community_moderator);
463 assert_eq!(expected_community_person_ban, inserted_community_person_ban);
464 assert_eq!(1, ignored_community);
465 assert_eq!(1, left_community);
466 assert_eq!(1, unban);
467 // assert_eq!(2, loaded_count);
468 assert_eq!(1, num_deleted);