]> Untitled Git - lemmy.git/blob - server/lemmy_db/src/community.rs
918d44f1519aa94e1bbbc6f19bb6c2136bf4a4e6
[lemmy.git] / server / lemmy_db / src / community.rs
1 use crate::{
2   naive_now,
3   schema::{community, community_follower, community_moderator, community_user_ban},
4   Bannable,
5   Crud,
6   Followable,
7   Joinable,
8 };
9 use diesel::{dsl::*, result::Error, *};
10
11 #[derive(Clone, Queryable, Identifiable, PartialEq, Debug)]
12 #[table_name = "community"]
13 pub struct Community {
14   pub id: i32,
15   pub name: String,
16   pub title: String,
17   pub description: Option<String>,
18   pub category_id: i32,
19   pub creator_id: i32,
20   pub removed: bool,
21   pub published: chrono::NaiveDateTime,
22   pub updated: Option<chrono::NaiveDateTime>,
23   pub deleted: bool,
24   pub nsfw: bool,
25   pub actor_id: String,
26   pub local: bool,
27   pub private_key: Option<String>,
28   pub public_key: Option<String>,
29   pub last_refreshed_at: chrono::NaiveDateTime,
30   pub icon: Option<String>,
31   pub banner: Option<String>,
32 }
33
34 #[derive(Insertable, AsChangeset, Debug)]
35 #[table_name = "community"]
36 pub struct CommunityForm {
37   pub name: String,
38   pub title: String,
39   pub description: Option<String>,
40   pub category_id: i32,
41   pub creator_id: i32,
42   pub removed: Option<bool>,
43   pub published: Option<chrono::NaiveDateTime>,
44   pub updated: Option<chrono::NaiveDateTime>,
45   pub deleted: Option<bool>,
46   pub nsfw: bool,
47   pub actor_id: Option<String>,
48   pub local: bool,
49   pub private_key: Option<String>,
50   pub public_key: Option<String>,
51   pub last_refreshed_at: Option<chrono::NaiveDateTime>,
52   pub icon: Option<Option<String>>,
53   pub banner: Option<Option<String>>,
54 }
55
56 impl Crud<CommunityForm> for Community {
57   fn read(conn: &PgConnection, community_id: i32) -> Result<Self, Error> {
58     use crate::schema::community::dsl::*;
59     community.find(community_id).first::<Self>(conn)
60   }
61
62   fn delete(conn: &PgConnection, community_id: i32) -> Result<usize, Error> {
63     use crate::schema::community::dsl::*;
64     diesel::delete(community.find(community_id)).execute(conn)
65   }
66
67   fn create(conn: &PgConnection, new_community: &CommunityForm) -> Result<Self, Error> {
68     use crate::schema::community::dsl::*;
69     insert_into(community)
70       .values(new_community)
71       .get_result::<Self>(conn)
72   }
73
74   fn update(
75     conn: &PgConnection,
76     community_id: i32,
77     new_community: &CommunityForm,
78   ) -> Result<Self, Error> {
79     use crate::schema::community::dsl::*;
80     diesel::update(community.find(community_id))
81       .set(new_community)
82       .get_result::<Self>(conn)
83   }
84 }
85
86 impl Community {
87   pub fn read_from_name(conn: &PgConnection, community_name: &str) -> Result<Self, Error> {
88     use crate::schema::community::dsl::*;
89     community
90       .filter(name.eq(community_name))
91       .first::<Self>(conn)
92   }
93
94   pub fn read_from_actor_id(conn: &PgConnection, for_actor_id: &str) -> Result<Self, Error> {
95     use crate::schema::community::dsl::*;
96     community
97       .filter(actor_id.eq(for_actor_id))
98       .first::<Self>(conn)
99   }
100
101   pub fn update_deleted(
102     conn: &PgConnection,
103     community_id: i32,
104     new_deleted: bool,
105   ) -> Result<Self, Error> {
106     use crate::schema::community::dsl::*;
107     diesel::update(community.find(community_id))
108       .set((deleted.eq(new_deleted), updated.eq(naive_now())))
109       .get_result::<Self>(conn)
110   }
111
112   pub fn update_removed(
113     conn: &PgConnection,
114     community_id: i32,
115     new_removed: bool,
116   ) -> Result<Self, Error> {
117     use crate::schema::community::dsl::*;
118     diesel::update(community.find(community_id))
119       .set((removed.eq(new_removed), updated.eq(naive_now())))
120       .get_result::<Self>(conn)
121   }
122
123   pub fn update_removed_for_creator(
124     conn: &PgConnection,
125     for_creator_id: i32,
126     new_removed: bool,
127   ) -> Result<Vec<Self>, Error> {
128     use crate::schema::community::dsl::*;
129     diesel::update(community.filter(creator_id.eq(for_creator_id)))
130       .set((removed.eq(new_removed), updated.eq(naive_now())))
131       .get_results::<Self>(conn)
132   }
133
134   pub fn update_creator(
135     conn: &PgConnection,
136     community_id: i32,
137     new_creator_id: i32,
138   ) -> Result<Self, Error> {
139     use crate::schema::community::dsl::*;
140     diesel::update(community.find(community_id))
141       .set((creator_id.eq(new_creator_id), updated.eq(naive_now())))
142       .get_result::<Self>(conn)
143   }
144
145   fn community_mods_and_admins(conn: &PgConnection, community_id: i32) -> Result<Vec<i32>, Error> {
146     use crate::{community_view::CommunityModeratorView, user_view::UserView};
147     let mut mods_and_admins: Vec<i32> = Vec::new();
148     mods_and_admins.append(
149       &mut CommunityModeratorView::for_community(conn, community_id)
150         .map(|v| v.into_iter().map(|m| m.user_id).collect())?,
151     );
152     mods_and_admins
153       .append(&mut UserView::admins(conn).map(|v| v.into_iter().map(|a| a.id).collect())?);
154     Ok(mods_and_admins)
155   }
156
157   pub fn is_mod_or_admin(conn: &PgConnection, user_id: i32, community_id: i32) -> bool {
158     Self::community_mods_and_admins(conn, community_id)
159       .unwrap_or_default()
160       .contains(&user_id)
161   }
162
163   pub fn upsert(conn: &PgConnection, community_form: &CommunityForm) -> Result<Community, Error> {
164     use crate::schema::community::dsl::*;
165     insert_into(community)
166       .values(community_form)
167       .on_conflict(actor_id)
168       .do_update()
169       .set(community_form)
170       .get_result::<Self>(conn)
171   }
172 }
173
174 #[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
175 #[belongs_to(Community)]
176 #[table_name = "community_moderator"]
177 pub struct CommunityModerator {
178   pub id: i32,
179   pub community_id: i32,
180   pub user_id: i32,
181   pub published: chrono::NaiveDateTime,
182 }
183
184 #[derive(Insertable, AsChangeset, Clone)]
185 #[table_name = "community_moderator"]
186 pub struct CommunityModeratorForm {
187   pub community_id: i32,
188   pub user_id: i32,
189 }
190
191 impl Joinable<CommunityModeratorForm> for CommunityModerator {
192   fn join(
193     conn: &PgConnection,
194     community_user_form: &CommunityModeratorForm,
195   ) -> Result<Self, Error> {
196     use crate::schema::community_moderator::dsl::*;
197     insert_into(community_moderator)
198       .values(community_user_form)
199       .get_result::<Self>(conn)
200   }
201
202   fn leave(
203     conn: &PgConnection,
204     community_user_form: &CommunityModeratorForm,
205   ) -> Result<usize, Error> {
206     use crate::schema::community_moderator::dsl::*;
207     diesel::delete(
208       community_moderator
209         .filter(community_id.eq(community_user_form.community_id))
210         .filter(user_id.eq(community_user_form.user_id)),
211     )
212     .execute(conn)
213   }
214 }
215
216 impl CommunityModerator {
217   pub fn delete_for_community(conn: &PgConnection, for_community_id: i32) -> Result<usize, Error> {
218     use crate::schema::community_moderator::dsl::*;
219     diesel::delete(community_moderator.filter(community_id.eq(for_community_id))).execute(conn)
220   }
221 }
222
223 #[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
224 #[belongs_to(Community)]
225 #[table_name = "community_user_ban"]
226 pub struct CommunityUserBan {
227   pub id: i32,
228   pub community_id: i32,
229   pub user_id: i32,
230   pub published: chrono::NaiveDateTime,
231 }
232
233 #[derive(Insertable, AsChangeset, Clone)]
234 #[table_name = "community_user_ban"]
235 pub struct CommunityUserBanForm {
236   pub community_id: i32,
237   pub user_id: i32,
238 }
239
240 impl Bannable<CommunityUserBanForm> for CommunityUserBan {
241   fn ban(
242     conn: &PgConnection,
243     community_user_ban_form: &CommunityUserBanForm,
244   ) -> Result<Self, Error> {
245     use crate::schema::community_user_ban::dsl::*;
246     insert_into(community_user_ban)
247       .values(community_user_ban_form)
248       .get_result::<Self>(conn)
249   }
250
251   fn unban(
252     conn: &PgConnection,
253     community_user_ban_form: &CommunityUserBanForm,
254   ) -> Result<usize, Error> {
255     use crate::schema::community_user_ban::dsl::*;
256     diesel::delete(
257       community_user_ban
258         .filter(community_id.eq(community_user_ban_form.community_id))
259         .filter(user_id.eq(community_user_ban_form.user_id)),
260     )
261     .execute(conn)
262   }
263 }
264
265 #[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
266 #[belongs_to(Community)]
267 #[table_name = "community_follower"]
268 pub struct CommunityFollower {
269   pub id: i32,
270   pub community_id: i32,
271   pub user_id: i32,
272   pub published: chrono::NaiveDateTime,
273 }
274
275 #[derive(Insertable, AsChangeset, Clone)]
276 #[table_name = "community_follower"]
277 pub struct CommunityFollowerForm {
278   pub community_id: i32,
279   pub user_id: i32,
280 }
281
282 impl Followable<CommunityFollowerForm> for CommunityFollower {
283   fn follow(
284     conn: &PgConnection,
285     community_follower_form: &CommunityFollowerForm,
286   ) -> Result<Self, Error> {
287     use crate::schema::community_follower::dsl::*;
288     insert_into(community_follower)
289       .values(community_follower_form)
290       .get_result::<Self>(conn)
291   }
292   fn unfollow(
293     conn: &PgConnection,
294     community_follower_form: &CommunityFollowerForm,
295   ) -> Result<usize, Error> {
296     use crate::schema::community_follower::dsl::*;
297     diesel::delete(
298       community_follower
299         .filter(community_id.eq(&community_follower_form.community_id))
300         .filter(user_id.eq(&community_follower_form.user_id)),
301     )
302     .execute(conn)
303   }
304 }
305
306 #[cfg(test)]
307 mod tests {
308   use crate::{community::*, tests::establish_unpooled_connection, user::*, ListingType, SortType};
309
310   #[test]
311   fn test_crud() {
312     let conn = establish_unpooled_connection();
313
314     let new_user = UserForm {
315       name: "bobbee".into(),
316       preferred_username: None,
317       password_encrypted: "nope".into(),
318       email: None,
319       matrix_user_id: None,
320       avatar: None,
321       banner: None,
322       admin: false,
323       banned: false,
324       updated: None,
325       show_nsfw: false,
326       theme: "darkly".into(),
327       default_sort_type: SortType::Hot as i16,
328       default_listing_type: ListingType::Subscribed as i16,
329       lang: "browser".into(),
330       show_avatars: true,
331       send_notifications_to_email: false,
332       actor_id: None,
333       bio: None,
334       local: true,
335       private_key: None,
336       public_key: None,
337       last_refreshed_at: None,
338     };
339
340     let inserted_user = User_::create(&conn, &new_user).unwrap();
341
342     let new_community = CommunityForm {
343       name: "TIL".into(),
344       creator_id: inserted_user.id,
345       title: "nada".to_owned(),
346       description: None,
347       category_id: 1,
348       nsfw: false,
349       removed: None,
350       deleted: None,
351       updated: None,
352       actor_id: None,
353       local: true,
354       private_key: None,
355       public_key: None,
356       last_refreshed_at: None,
357       published: None,
358       icon: None,
359       banner: None,
360     };
361
362     let inserted_community = Community::create(&conn, &new_community).unwrap();
363
364     let expected_community = Community {
365       id: inserted_community.id,
366       creator_id: inserted_user.id,
367       name: "TIL".into(),
368       title: "nada".to_owned(),
369       description: None,
370       category_id: 1,
371       nsfw: false,
372       removed: false,
373       deleted: false,
374       published: inserted_community.published,
375       updated: None,
376       actor_id: inserted_community.actor_id.to_owned(),
377       local: true,
378       private_key: None,
379       public_key: None,
380       last_refreshed_at: inserted_community.published,
381       icon: None,
382       banner: None,
383     };
384
385     let community_follower_form = CommunityFollowerForm {
386       community_id: inserted_community.id,
387       user_id: inserted_user.id,
388     };
389
390     let inserted_community_follower =
391       CommunityFollower::follow(&conn, &community_follower_form).unwrap();
392
393     let expected_community_follower = CommunityFollower {
394       id: inserted_community_follower.id,
395       community_id: inserted_community.id,
396       user_id: inserted_user.id,
397       published: inserted_community_follower.published,
398     };
399
400     let community_user_form = CommunityModeratorForm {
401       community_id: inserted_community.id,
402       user_id: inserted_user.id,
403     };
404
405     let inserted_community_user = CommunityModerator::join(&conn, &community_user_form).unwrap();
406
407     let expected_community_user = CommunityModerator {
408       id: inserted_community_user.id,
409       community_id: inserted_community.id,
410       user_id: inserted_user.id,
411       published: inserted_community_user.published,
412     };
413
414     let community_user_ban_form = CommunityUserBanForm {
415       community_id: inserted_community.id,
416       user_id: inserted_user.id,
417     };
418
419     let inserted_community_user_ban =
420       CommunityUserBan::ban(&conn, &community_user_ban_form).unwrap();
421
422     let expected_community_user_ban = CommunityUserBan {
423       id: inserted_community_user_ban.id,
424       community_id: inserted_community.id,
425       user_id: inserted_user.id,
426       published: inserted_community_user_ban.published,
427     };
428
429     let read_community = Community::read(&conn, inserted_community.id).unwrap();
430     let updated_community =
431       Community::update(&conn, inserted_community.id, &new_community).unwrap();
432     let ignored_community = CommunityFollower::unfollow(&conn, &community_follower_form).unwrap();
433     let left_community = CommunityModerator::leave(&conn, &community_user_form).unwrap();
434     let unban = CommunityUserBan::unban(&conn, &community_user_ban_form).unwrap();
435     let num_deleted = Community::delete(&conn, inserted_community.id).unwrap();
436     User_::delete(&conn, inserted_user.id).unwrap();
437
438     assert_eq!(expected_community, read_community);
439     assert_eq!(expected_community, inserted_community);
440     assert_eq!(expected_community, updated_community);
441     assert_eq!(expected_community_follower, inserted_community_follower);
442     assert_eq!(expected_community_user, inserted_community_user);
443     assert_eq!(expected_community_user_ban, inserted_community_user_ban);
444     assert_eq!(1, ignored_community);
445     assert_eq!(1, left_community);
446     assert_eq!(1, unban);
447     // assert_eq!(2, loaded_count);
448     assert_eq!(1, num_deleted);
449   }
450 }