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