]> Untitled Git - lemmy.git/blob - server/src/api/community.rs
37bc20dbf6b836fadf588823ae0958eae45c2163
[lemmy.git] / server / src / api / community.rs
1 use super::*;
2 use std::str::FromStr;
3
4 #[derive(Serialize, Deserialize)]
5 pub struct GetCommunity {
6   id: Option<i32>,
7   name: Option<String>,
8   auth: Option<String>
9 }
10
11 #[derive(Serialize, Deserialize)]
12 pub struct GetCommunityResponse {
13   op: String,
14   community: CommunityView,
15   moderators: Vec<CommunityModeratorView>,
16   admins: Vec<UserView>,
17 }
18
19
20 #[derive(Serialize, Deserialize)]
21 pub struct CreateCommunity {
22   name: String,
23   title: String,
24   description: Option<String>,
25   category_id: i32,
26   nsfw: bool,
27   auth: String
28 }
29
30 #[derive(Serialize, Deserialize, Clone)]
31 pub struct CommunityResponse {
32   op: String,
33   pub community: CommunityView
34 }
35
36 #[derive(Serialize, Deserialize)]
37 pub struct ListCommunities {
38   sort: String,
39   page: Option<i64>,
40   limit: Option<i64>,
41   auth: Option<String>
42 }
43
44 #[derive(Serialize, Deserialize)]
45 pub struct ListCommunitiesResponse {
46   op: String,
47   communities: Vec<CommunityView>
48 }
49
50 #[derive(Serialize, Deserialize, Clone)]
51 pub struct BanFromCommunity {
52   pub community_id: i32,
53   user_id: i32,
54   ban: bool,
55   reason: Option<String>,
56   expires: Option<i64>,
57   auth: String
58 }
59
60 #[derive(Serialize, Deserialize)]
61 pub struct BanFromCommunityResponse {
62   op: String,
63   user: UserView,
64   banned: bool,
65 }
66
67 #[derive(Serialize, Deserialize)]
68 pub struct AddModToCommunity {
69   pub community_id: i32,
70   user_id: i32,
71   added: bool,
72   auth: String
73 }
74
75 #[derive(Serialize, Deserialize)]
76 pub struct AddModToCommunityResponse {
77   op: String,
78   moderators: Vec<CommunityModeratorView>,
79 }
80
81 #[derive(Serialize, Deserialize)]
82 pub struct EditCommunity {
83   pub edit_id: i32,
84   name: String,
85   title: String,
86   description: Option<String>,
87   category_id: i32,
88   removed: Option<bool>,
89   deleted: Option<bool>,
90   nsfw: bool,
91   reason: Option<String>,
92   expires: Option<i64>,
93   auth: String
94 }
95
96 #[derive(Serialize, Deserialize)]
97 pub struct FollowCommunity {
98   community_id: i32,
99   follow: bool,
100   auth: String
101 }
102
103 #[derive(Serialize, Deserialize)]
104 pub struct GetFollowedCommunities {
105   auth: String
106 }
107
108 #[derive(Serialize, Deserialize)]
109 pub struct GetFollowedCommunitiesResponse {
110   op: String,
111   communities: Vec<CommunityFollowerView>
112 }
113
114 #[derive(Serialize, Deserialize)]
115 pub struct TransferCommunity {
116   community_id: i32,
117   user_id: i32,
118   auth: String
119 }
120
121 impl Perform<GetCommunityResponse> for Oper<GetCommunity> {
122   fn perform(&self) -> Result<GetCommunityResponse, Error> {
123     let data: &GetCommunity = &self.data;
124     let conn = establish_connection();
125
126     let user_id: Option<i32> = match &data.auth {
127       Some(auth) => {
128         match Claims::decode(&auth) {
129           Ok(claims) => {
130             let user_id = claims.claims.id;
131             Some(user_id)
132           }
133           Err(_e) => None
134         }
135       }
136       None => None
137     };
138
139     let community_id = match data.id {
140       Some(id) => id,
141       None => Community::read_from_name(&conn, data.name.to_owned().unwrap_or("main".to_string()))?.id
142     };
143
144     let community_view = match CommunityView::read(&conn, community_id, user_id) {
145       Ok(community) => community,
146       Err(_e) => {
147         return Err(APIError::err(&self.op, "couldnt_find_community"))?
148       }
149     };
150
151     let moderators = match CommunityModeratorView::for_community(&conn, community_id) {
152       Ok(moderators) => moderators,
153       Err(_e) => {
154         return Err(APIError::err(&self.op, "couldnt_find_community"))?
155       }
156     };
157
158     let site_creator_id = Site::read(&conn, 1)?.creator_id;
159     let mut admins = UserView::admins(&conn)?;
160     let creator_index = admins.iter().position(|r| r.id == site_creator_id).unwrap();
161     let creator_user = admins.remove(creator_index);
162     admins.insert(0, creator_user);
163
164     // Return the jwt
165     Ok(
166       GetCommunityResponse {
167         op: self.op.to_string(),
168         community: community_view,
169         moderators: moderators,
170         admins: admins,
171       }
172       )
173   }
174 }
175
176 impl Perform<CommunityResponse> for Oper<CreateCommunity> {
177   fn perform(&self) -> Result<CommunityResponse, Error> {
178     let data: &CreateCommunity = &self.data;
179     let conn = establish_connection();
180
181     let claims = match Claims::decode(&data.auth) {
182       Ok(claims) => claims.claims,
183       Err(_e) => {
184         return Err(APIError::err(&self.op, "not_logged_in"))?
185       }
186     };
187
188     if has_slurs(&data.name) || 
189       has_slurs(&data.title) || 
190         (data.description.is_some() && has_slurs(&data.description.to_owned().unwrap())) {
191           return Err(APIError::err(&self.op, "no_slurs"))?
192         }
193
194     let user_id = claims.id;
195
196     // Check for a site ban
197     if UserView::read(&conn, user_id)?.banned {
198       return Err(APIError::err(&self.op, "site_ban"))?
199     }
200
201     // When you create a community, make sure the user becomes a moderator and a follower
202     let community_form = CommunityForm {
203       name: data.name.to_owned(),
204       title: data.title.to_owned(),
205       description: data.description.to_owned(),
206       category_id: data.category_id,
207       creator_id: user_id,
208       removed: None,
209       deleted: None,
210       nsfw: data.nsfw,
211       updated: None,
212     };
213
214     let inserted_community = match Community::create(&conn, &community_form) {
215       Ok(community) => community,
216       Err(_e) => {
217         return Err(APIError::err(&self.op, "community_already_exists"))?
218       }
219     };
220
221     let community_moderator_form = CommunityModeratorForm {
222       community_id: inserted_community.id,
223       user_id: user_id
224     };
225
226     let _inserted_community_moderator = match CommunityModerator::join(&conn, &community_moderator_form) {
227       Ok(user) => user,
228       Err(_e) => {
229         return Err(APIError::err(&self.op, "community_moderator_already_exists"))?
230       }
231     };
232
233     let community_follower_form = CommunityFollowerForm {
234       community_id: inserted_community.id,
235       user_id: user_id
236     };
237
238     let _inserted_community_follower = match CommunityFollower::follow(&conn, &community_follower_form) {
239       Ok(user) => user,
240       Err(_e) => {
241         return Err(APIError::err(&self.op, "community_follower_already_exists"))?
242       }
243     };
244
245     let community_view = CommunityView::read(&conn, inserted_community.id, Some(user_id))?;
246
247     Ok(
248       CommunityResponse {
249         op: self.op.to_string(),
250         community: community_view
251       }
252       )
253   }
254 }
255
256 impl Perform<CommunityResponse> for Oper<EditCommunity> {
257   fn perform(&self) -> Result<CommunityResponse, Error> {
258     let data: &EditCommunity = &self.data;
259
260     if has_slurs(&data.name) || has_slurs(&data.title) {
261       return Err(APIError::err(&self.op, "no_slurs"))?
262     }
263
264     let conn = establish_connection();
265
266     let claims = match Claims::decode(&data.auth) {
267       Ok(claims) => claims.claims,
268       Err(_e) => {
269         return Err(APIError::err(&self.op, "not_logged_in"))?
270       }
271     };
272
273     let user_id = claims.id;
274
275     // Check for a site ban
276     if UserView::read(&conn, user_id)?.banned {
277       return Err(APIError::err(&self.op, "site_ban"))?
278     }
279
280     // Verify its a mod
281     let mut editors: Vec<i32> = Vec::new();
282     editors.append(
283       &mut CommunityModeratorView::for_community(&conn, data.edit_id)
284       ?
285       .into_iter()
286       .map(|m| m.user_id)
287       .collect()
288       );
289     editors.append(
290       &mut UserView::admins(&conn)
291       ?
292       .into_iter()
293       .map(|a| a.id)
294       .collect()
295       );
296     if !editors.contains(&user_id) {
297       return Err(APIError::err(&self.op, "no_community_edit_allowed"))?
298     }
299
300     let community_form = CommunityForm {
301       name: data.name.to_owned(),
302       title: data.title.to_owned(),
303       description: data.description.to_owned(),
304       category_id: data.category_id.to_owned(),
305       creator_id: user_id,
306       removed: data.removed.to_owned(),
307       deleted: data.deleted.to_owned(),
308       nsfw: data.nsfw,
309       updated: Some(naive_now())
310     };
311
312     let _updated_community = match Community::update(&conn, data.edit_id, &community_form) {
313       Ok(community) => community,
314       Err(_e) => {
315         return Err(APIError::err(&self.op, "couldnt_update_community"))?
316       }
317     };
318
319     // Mod tables
320     if let Some(removed) = data.removed.to_owned() {
321       let expires = match data.expires {
322         Some(time) => Some(naive_from_unix(time)),
323         None => None
324       };
325       let form = ModRemoveCommunityForm {
326         mod_user_id: user_id,
327         community_id: data.edit_id,
328         removed: Some(removed),
329         reason: data.reason.to_owned(),
330         expires: expires
331       };
332       ModRemoveCommunity::create(&conn, &form)?;
333     }
334
335     let community_view = CommunityView::read(&conn, data.edit_id, Some(user_id))?;
336
337     Ok(
338       CommunityResponse {
339         op: self.op.to_string(), 
340         community: community_view
341       }
342       )
343   }
344 }
345
346 impl Perform<ListCommunitiesResponse> for Oper<ListCommunities> {
347   fn perform(&self) -> Result<ListCommunitiesResponse, Error> {
348     let data: &ListCommunities = &self.data;
349     let conn = establish_connection();
350
351     let user_claims: Option<Claims> = match &data.auth {
352       Some(auth) => {
353         match Claims::decode(&auth) {
354           Ok(claims) => {
355             Some(claims.claims)
356           }
357           Err(_e) => None
358         }
359       }
360       None => None
361     };
362     
363     let user_id = match &user_claims {
364       Some(claims) => Some(claims.id),
365       None => None
366     };
367
368     let show_nsfw = match &user_claims {
369       Some(claims) => claims.show_nsfw,
370       None => false
371     };
372
373     let sort = SortType::from_str(&data.sort)?;
374
375     let communities: Vec<CommunityView> = CommunityView::list(
376       &conn, 
377       &sort, 
378       user_id, 
379       show_nsfw, 
380       None, 
381       data.page, 
382       data.limit)?;
383
384     // Return the jwt
385     Ok(
386       ListCommunitiesResponse {
387         op: self.op.to_string(),
388         communities: communities
389       }
390       )
391   }
392 }
393
394
395 impl Perform<CommunityResponse> for Oper<FollowCommunity> {
396   fn perform(&self) -> Result<CommunityResponse, Error> {
397     let data: &FollowCommunity = &self.data;
398     let conn = establish_connection();
399
400     let claims = match Claims::decode(&data.auth) {
401       Ok(claims) => claims.claims,
402       Err(_e) => {
403         return Err(APIError::err(&self.op, "not_logged_in"))?
404       }
405     };
406
407     let user_id = claims.id;
408
409     let community_follower_form = CommunityFollowerForm {
410       community_id: data.community_id,
411       user_id: user_id
412     };
413
414     if data.follow {
415       match CommunityFollower::follow(&conn, &community_follower_form) {
416         Ok(user) => user,
417         Err(_e) => {
418           return Err(APIError::err(&self.op, "community_follower_already_exists"))?
419         }
420       };
421     } else {
422       match CommunityFollower::ignore(&conn, &community_follower_form) {
423         Ok(user) => user,
424         Err(_e) => {
425           return Err(APIError::err(&self.op, "community_follower_already_exists"))?
426         }
427       };
428     }
429
430     let community_view = CommunityView::read(&conn, data.community_id, Some(user_id))?;
431
432     Ok(
433       CommunityResponse {
434         op: self.op.to_string(), 
435         community: community_view
436       }
437       )
438   }
439 }
440
441
442 impl Perform<GetFollowedCommunitiesResponse> for Oper<GetFollowedCommunities> {
443   fn perform(&self) -> Result<GetFollowedCommunitiesResponse, Error> {
444     let data: &GetFollowedCommunities = &self.data;
445     let conn = establish_connection();
446
447     let claims = match Claims::decode(&data.auth) {
448       Ok(claims) => claims.claims,
449       Err(_e) => {
450         return Err(APIError::err(&self.op, "not_logged_in"))?
451       }
452     };
453
454     let user_id = claims.id;
455
456     let communities: Vec<CommunityFollowerView> = match CommunityFollowerView::for_user(&conn, user_id) {
457       Ok(communities) => communities,
458       Err(_e) => {
459         return Err(APIError::err(&self.op, "system_err_login"))?
460       }
461     };
462
463     // Return the jwt
464     Ok(
465       GetFollowedCommunitiesResponse {
466         op: self.op.to_string(),
467         communities: communities
468       }
469       )
470   }
471 }
472
473
474 impl Perform<BanFromCommunityResponse> for Oper<BanFromCommunity> {
475   fn perform(&self) -> Result<BanFromCommunityResponse, Error> {
476     let data: &BanFromCommunity = &self.data;
477     let conn = establish_connection();
478
479     let claims = match Claims::decode(&data.auth) {
480       Ok(claims) => claims.claims,
481       Err(_e) => {
482         return Err(APIError::err(&self.op, "not_logged_in"))?
483       }
484     };
485
486     let user_id = claims.id;
487
488     let community_user_ban_form = CommunityUserBanForm {
489       community_id: data.community_id,
490       user_id: data.user_id,
491     };
492
493     if data.ban {
494       match CommunityUserBan::ban(&conn, &community_user_ban_form) {
495         Ok(user) => user,
496         Err(_e) => {
497           return Err(APIError::err(&self.op, "community_user_already_banned"))?
498         }
499       };
500     } else {
501       match CommunityUserBan::unban(&conn, &community_user_ban_form) {
502         Ok(user) => user,
503         Err(_e) => {
504           return Err(APIError::err(&self.op, "community_user_already_banned"))?
505         }
506       };
507     }
508
509     // Mod tables
510     let expires = match data.expires {
511       Some(time) => Some(naive_from_unix(time)),
512       None => None
513     };
514
515     let form = ModBanFromCommunityForm {
516       mod_user_id: user_id,
517       other_user_id: data.user_id,
518       community_id: data.community_id,
519       reason: data.reason.to_owned(),
520       banned: Some(data.ban),
521       expires: expires,
522     };
523     ModBanFromCommunity::create(&conn, &form)?;
524
525     let user_view = UserView::read(&conn, data.user_id)?;
526
527     Ok(
528       BanFromCommunityResponse {
529         op: self.op.to_string(), 
530         user: user_view,
531         banned: data.ban
532       }
533       )
534   }
535 }
536
537 impl Perform<AddModToCommunityResponse> for Oper<AddModToCommunity> {
538   fn perform(&self) -> Result<AddModToCommunityResponse, Error> {
539     let data: &AddModToCommunity = &self.data;
540     let conn = establish_connection();
541
542     let claims = match Claims::decode(&data.auth) {
543       Ok(claims) => claims.claims,
544       Err(_e) => {
545         return Err(APIError::err(&self.op, "not_logged_in"))?
546       }
547     };
548
549     let user_id = claims.id;
550
551     let community_moderator_form = CommunityModeratorForm {
552       community_id: data.community_id,
553       user_id: data.user_id
554     };
555
556     if data.added {
557       match CommunityModerator::join(&conn, &community_moderator_form) {
558         Ok(user) => user,
559         Err(_e) => {
560           return Err(APIError::err(&self.op, "community_moderator_already_exists"))?
561         }
562       };
563     } else {
564       match CommunityModerator::leave(&conn, &community_moderator_form) {
565         Ok(user) => user,
566         Err(_e) => {
567           return Err(APIError::err(&self.op, "community_moderator_already_exists"))?
568         }
569       };
570     }
571
572     // Mod tables
573     let form = ModAddCommunityForm {
574       mod_user_id: user_id,
575       other_user_id: data.user_id,
576       community_id: data.community_id,
577       removed: Some(!data.added),
578     };
579     ModAddCommunity::create(&conn, &form)?;
580
581     let moderators = CommunityModeratorView::for_community(&conn, data.community_id)?;
582
583     Ok(
584       AddModToCommunityResponse {
585         op: self.op.to_string(), 
586         moderators: moderators,
587       }
588       )
589   }
590 }
591   
592 impl Perform<GetCommunityResponse> for Oper<TransferCommunity> {
593   fn perform(&self) -> Result<GetCommunityResponse, Error> {
594     let data: &TransferCommunity = &self.data;
595     let conn = establish_connection();
596
597     let claims = match Claims::decode(&data.auth) {
598       Ok(claims) => claims.claims,
599       Err(_e) => {
600         return Err(APIError::err(&self.op, "not_logged_in"))?
601       }
602     };
603
604     let user_id = claims.id;
605
606     let read_community = Community::read(&conn, data.community_id)?;
607
608     // Make sure user is the creator
609     if read_community.creator_id != user_id {
610       return Err(APIError::err(&self.op, "not_an_admin"))?
611     }
612     
613     let community_form = CommunityForm {
614       name: read_community.name,
615       title: read_community.title,
616       description: read_community.description,
617       category_id: read_community.category_id,
618       creator_id: data.user_id,
619       removed: None,
620       deleted: None,
621       nsfw: read_community.nsfw,
622       updated: Some(naive_now())
623     };
624
625     let _updated_community = match Community::update(&conn, data.community_id, &community_form) {
626       Ok(community) => community,
627       Err(_e) => {
628         return Err(APIError::err(&self.op, "couldnt_update_community"))?
629       }
630     };
631
632     // You also have to re-do the community_moderator table, reordering it.
633     let mut community_mods = CommunityModeratorView::for_community(&conn, data.community_id)?;
634     let creator_index = community_mods.iter().position(|r| r.user_id == data.user_id).unwrap();
635     let creator_user = community_mods.remove(creator_index);
636     community_mods.insert(0, creator_user);
637
638     CommunityModerator::delete_for_community(&conn, data.community_id)?;
639
640     for cmod in &community_mods {
641
642       let community_moderator_form = CommunityModeratorForm {
643         community_id: cmod.community_id,
644         user_id: cmod.user_id
645       };
646
647       let _inserted_community_moderator = match CommunityModerator::join(&conn, &community_moderator_form) {
648         Ok(user) => user,
649         Err(_e) => {
650           return Err(APIError::err(&self.op, "community_moderator_already_exists"))?
651         }
652       };
653     }
654
655     // Mod tables
656     let form = ModAddCommunityForm {
657       mod_user_id: user_id,
658       other_user_id: data.user_id,
659       community_id: data.community_id,
660       removed: Some(false),
661     };
662     ModAddCommunity::create(&conn, &form)?;
663
664     let community_view = match CommunityView::read(&conn, data.community_id, Some(user_id)) {
665       Ok(community) => community,
666       Err(_e) => {
667         return Err(APIError::err(&self.op, "couldnt_find_community"))?
668       }
669     };
670
671     let moderators = match CommunityModeratorView::for_community(&conn, data.community_id) {
672       Ok(moderators) => moderators,
673       Err(_e) => {
674         return Err(APIError::err(&self.op, "couldnt_find_community"))?
675       }
676     };
677
678     let site_creator_id = Site::read(&conn, 1)?.creator_id;
679     let mut admins = UserView::admins(&conn)?;
680     let creator_index = admins.iter().position(|r| r.id == site_creator_id).unwrap();
681     let creator_user = admins.remove(creator_index);
682     admins.insert(0, creator_user);
683
684     // Return the jwt
685     Ok(
686       GetCommunityResponse {
687         op: self.op.to_string(),
688         community: community_view,
689         moderators: moderators,
690         admins: admins,
691       }
692       )
693   }
694 }