]> Untitled Git - lemmy.git/blob - server/src/api/community.rs
Adding emoji support
[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   auth: String
27 }
28
29 #[derive(Serialize, Deserialize, Clone)]
30 pub struct CommunityResponse {
31   op: String,
32   pub community: CommunityView
33 }
34
35 #[derive(Serialize, Deserialize)]
36 pub struct ListCommunities {
37   sort: String,
38   page: Option<i64>,
39   limit: Option<i64>,
40   auth: Option<String>
41 }
42
43 #[derive(Serialize, Deserialize)]
44 pub struct ListCommunitiesResponse {
45   op: String,
46   communities: Vec<CommunityView>
47 }
48
49 #[derive(Serialize, Deserialize, Clone)]
50 pub struct BanFromCommunity {
51   pub community_id: i32,
52   user_id: i32,
53   ban: bool,
54   reason: Option<String>,
55   expires: Option<i64>,
56   auth: String
57 }
58
59 #[derive(Serialize, Deserialize)]
60 pub struct BanFromCommunityResponse {
61   op: String,
62   user: UserView,
63   banned: bool,
64 }
65
66 #[derive(Serialize, Deserialize)]
67 pub struct AddModToCommunity {
68   pub community_id: i32,
69   user_id: i32,
70   added: bool,
71   auth: String
72 }
73
74 #[derive(Serialize, Deserialize)]
75 pub struct AddModToCommunityResponse {
76   op: String,
77   moderators: Vec<CommunityModeratorView>,
78 }
79
80 #[derive(Serialize, Deserialize)]
81 pub struct EditCommunity {
82   pub edit_id: i32,
83   name: String,
84   title: String,
85   description: Option<String>,
86   category_id: i32,
87   removed: Option<bool>,
88   deleted: Option<bool>,
89   reason: Option<String>,
90   expires: Option<i64>,
91   auth: String
92 }
93
94 #[derive(Serialize, Deserialize)]
95 pub struct FollowCommunity {
96   community_id: i32,
97   follow: bool,
98   auth: String
99 }
100
101 #[derive(Serialize, Deserialize)]
102 pub struct GetFollowedCommunities {
103   auth: String
104 }
105
106 #[derive(Serialize, Deserialize)]
107 pub struct GetFollowedCommunitiesResponse {
108   op: String,
109   communities: Vec<CommunityFollowerView>
110 }
111
112 impl Perform<GetCommunityResponse> for Oper<GetCommunity> {
113   fn perform(&self) -> Result<GetCommunityResponse, Error> {
114     let data: &GetCommunity = &self.data;
115     let conn = establish_connection();
116
117     let user_id: Option<i32> = match &data.auth {
118       Some(auth) => {
119         match Claims::decode(&auth) {
120           Ok(claims) => {
121             let user_id = claims.claims.id;
122             Some(user_id)
123           }
124           Err(_e) => None
125         }
126       }
127       None => None
128     };
129
130     let community_id = match data.id {
131       Some(id) => id,
132       None => Community::read_from_name(&conn, data.name.to_owned().unwrap_or("main".to_string()))?.id
133     };
134
135     let community_view = match CommunityView::read(&conn, community_id, user_id) {
136       Ok(community) => community,
137       Err(_e) => {
138         return Err(APIError::err(&self.op, "Couldn't find Community"))?
139       }
140     };
141
142     let moderators = match CommunityModeratorView::for_community(&conn, community_id) {
143       Ok(moderators) => moderators,
144       Err(_e) => {
145         return Err(APIError::err(&self.op, "Couldn't find Community"))?
146       }
147     };
148
149     let admins = UserView::admins(&conn)?;
150
151     // Return the jwt
152     Ok(
153       GetCommunityResponse {
154         op: self.op.to_string(),
155         community: community_view,
156         moderators: moderators,
157         admins: admins,
158       }
159       )
160   }
161 }
162
163 impl Perform<CommunityResponse> for Oper<CreateCommunity> {
164   fn perform(&self) -> Result<CommunityResponse, Error> {
165     let data: &CreateCommunity = &self.data;
166     let conn = establish_connection();
167
168     let claims = match Claims::decode(&data.auth) {
169       Ok(claims) => claims.claims,
170       Err(_e) => {
171         return Err(APIError::err(&self.op, "Not logged in."))?
172       }
173     };
174
175     if has_slurs(&data.name) || 
176       has_slurs(&data.title) || 
177         (data.description.is_some() && has_slurs(&data.description.to_owned().unwrap())) {
178           return Err(APIError::err(&self.op, "No slurs"))?
179         }
180
181     let user_id = claims.id;
182
183     // Check for a site ban
184     if UserView::read(&conn, user_id)?.banned {
185       return Err(APIError::err(&self.op, "You have been banned from the site"))?
186     }
187
188     // When you create a community, make sure the user becomes a moderator and a follower
189     let community_form = CommunityForm {
190       name: data.name.to_owned(),
191       title: data.title.to_owned(),
192       description: data.description.to_owned(),
193       category_id: data.category_id,
194       creator_id: user_id,
195       removed: None,
196       deleted: None,
197       updated: None,
198     };
199
200     let inserted_community = match Community::create(&conn, &community_form) {
201       Ok(community) => community,
202       Err(_e) => {
203         return Err(APIError::err(&self.op, "Community already exists."))?
204       }
205     };
206
207     let community_moderator_form = CommunityModeratorForm {
208       community_id: inserted_community.id,
209       user_id: user_id
210     };
211
212     let _inserted_community_moderator = match CommunityModerator::join(&conn, &community_moderator_form) {
213       Ok(user) => user,
214       Err(_e) => {
215         return Err(APIError::err(&self.op, "Community moderator already exists."))?
216       }
217     };
218
219     let community_follower_form = CommunityFollowerForm {
220       community_id: inserted_community.id,
221       user_id: user_id
222     };
223
224     let _inserted_community_follower = match CommunityFollower::follow(&conn, &community_follower_form) {
225       Ok(user) => user,
226       Err(_e) => {
227         return Err(APIError::err(&self.op, "Community follower already exists."))?
228       }
229     };
230
231     let community_view = CommunityView::read(&conn, inserted_community.id, Some(user_id))?;
232
233     Ok(
234       CommunityResponse {
235         op: self.op.to_string(),
236         community: community_view
237       }
238       )
239   }
240 }
241
242 impl Perform<CommunityResponse> for Oper<EditCommunity> {
243   fn perform(&self) -> Result<CommunityResponse, Error> {
244     let data: &EditCommunity = &self.data;
245
246     if has_slurs(&data.name) || has_slurs(&data.title) {
247       return Err(APIError::err(&self.op, "No slurs"))?
248     }
249
250     let conn = establish_connection();
251
252     let claims = match Claims::decode(&data.auth) {
253       Ok(claims) => claims.claims,
254       Err(_e) => {
255         return Err(APIError::err(&self.op, "Not logged in."))?
256       }
257     };
258
259     let user_id = claims.id;
260
261     // Check for a site ban
262     if UserView::read(&conn, user_id)?.banned {
263       return Err(APIError::err(&self.op, "You have been banned from the site"))?
264     }
265
266     // Verify its a mod
267     let mut editors: Vec<i32> = Vec::new();
268     editors.append(
269       &mut CommunityModeratorView::for_community(&conn, data.edit_id)
270       ?
271       .into_iter()
272       .map(|m| m.user_id)
273       .collect()
274       );
275     editors.append(
276       &mut UserView::admins(&conn)
277       ?
278       .into_iter()
279       .map(|a| a.id)
280       .collect()
281       );
282     if !editors.contains(&user_id) {
283       return Err(APIError::err(&self.op, "Not allowed to edit community"))?
284     }
285
286     let community_form = CommunityForm {
287       name: data.name.to_owned(),
288       title: data.title.to_owned(),
289       description: data.description.to_owned(),
290       category_id: data.category_id.to_owned(),
291       creator_id: user_id,
292       removed: data.removed.to_owned(),
293       deleted: data.deleted.to_owned(),
294       updated: Some(naive_now())
295     };
296
297     let _updated_community = match Community::update(&conn, data.edit_id, &community_form) {
298       Ok(community) => community,
299       Err(_e) => {
300         return Err(APIError::err(&self.op, "Couldn't update Community"))?
301       }
302     };
303
304     // Mod tables
305     if let Some(removed) = data.removed.to_owned() {
306       let expires = match data.expires {
307         Some(time) => Some(naive_from_unix(time)),
308         None => None
309       };
310       let form = ModRemoveCommunityForm {
311         mod_user_id: user_id,
312         community_id: data.edit_id,
313         removed: Some(removed),
314         reason: data.reason.to_owned(),
315         expires: expires
316       };
317       ModRemoveCommunity::create(&conn, &form)?;
318     }
319
320     let community_view = CommunityView::read(&conn, data.edit_id, Some(user_id))?;
321
322     Ok(
323       CommunityResponse {
324         op: self.op.to_string(), 
325         community: community_view
326       }
327       )
328   }
329 }
330
331 impl Perform<ListCommunitiesResponse> for Oper<ListCommunities> {
332   fn perform(&self) -> Result<ListCommunitiesResponse, Error> {
333     let data: &ListCommunities = &self.data;
334     let conn = establish_connection();
335
336     let user_id: Option<i32> = match &data.auth {
337       Some(auth) => {
338         match Claims::decode(&auth) {
339           Ok(claims) => {
340             let user_id = claims.claims.id;
341             Some(user_id)
342           }
343           Err(_e) => None
344         }
345       }
346       None => None
347     };
348
349     let sort = SortType::from_str(&data.sort)?;
350
351     let communities: Vec<CommunityView> = CommunityView::list(&conn, user_id, sort, data.page, data.limit)?;
352
353     // Return the jwt
354     Ok(
355       ListCommunitiesResponse {
356         op: self.op.to_string(),
357         communities: communities
358       }
359       )
360   }
361 }
362
363
364 impl Perform<CommunityResponse> for Oper<FollowCommunity> {
365   fn perform(&self) -> Result<CommunityResponse, Error> {
366     let data: &FollowCommunity = &self.data;
367     let conn = establish_connection();
368
369     let claims = match Claims::decode(&data.auth) {
370       Ok(claims) => claims.claims,
371       Err(_e) => {
372         return Err(APIError::err(&self.op, "Not logged in."))?
373       }
374     };
375
376     let user_id = claims.id;
377
378     let community_follower_form = CommunityFollowerForm {
379       community_id: data.community_id,
380       user_id: user_id
381     };
382
383     if data.follow {
384       match CommunityFollower::follow(&conn, &community_follower_form) {
385         Ok(user) => user,
386         Err(_e) => {
387           return Err(APIError::err(&self.op, "Community follower already exists."))?
388         }
389       };
390     } else {
391       match CommunityFollower::ignore(&conn, &community_follower_form) {
392         Ok(user) => user,
393         Err(_e) => {
394           return Err(APIError::err(&self.op, "Community follower already exists."))?
395         }
396       };
397     }
398
399     let community_view = CommunityView::read(&conn, data.community_id, Some(user_id))?;
400
401     Ok(
402       CommunityResponse {
403         op: self.op.to_string(), 
404         community: community_view
405       }
406       )
407   }
408 }
409
410
411 impl Perform<GetFollowedCommunitiesResponse> for Oper<GetFollowedCommunities> {
412   fn perform(&self) -> Result<GetFollowedCommunitiesResponse, Error> {
413     let data: &GetFollowedCommunities = &self.data;
414     let conn = establish_connection();
415
416     let claims = match Claims::decode(&data.auth) {
417       Ok(claims) => claims.claims,
418       Err(_e) => {
419         return Err(APIError::err(&self.op, "Not logged in."))?
420       }
421     };
422
423     let user_id = claims.id;
424
425     let communities: Vec<CommunityFollowerView> = match CommunityFollowerView::for_user(&conn, user_id) {
426       Ok(communities) => communities,
427       Err(_e) => {
428         return Err(APIError::err(&self.op, "System error, try logging out and back in."))?
429       }
430     };
431
432     // Return the jwt
433     Ok(
434       GetFollowedCommunitiesResponse {
435         op: self.op.to_string(),
436         communities: communities
437       }
438       )
439   }
440 }
441
442
443 impl Perform<BanFromCommunityResponse> for Oper<BanFromCommunity> {
444   fn perform(&self) -> Result<BanFromCommunityResponse, Error> {
445     let data: &BanFromCommunity = &self.data;
446     let conn = establish_connection();
447
448     let claims = match Claims::decode(&data.auth) {
449       Ok(claims) => claims.claims,
450       Err(_e) => {
451         return Err(APIError::err(&self.op, "Not logged in."))?
452       }
453     };
454
455     let user_id = claims.id;
456
457     let community_user_ban_form = CommunityUserBanForm {
458       community_id: data.community_id,
459       user_id: data.user_id,
460     };
461
462     if data.ban {
463       match CommunityUserBan::ban(&conn, &community_user_ban_form) {
464         Ok(user) => user,
465         Err(_e) => {
466           return Err(APIError::err(&self.op, "Community user ban already exists"))?
467         }
468       };
469     } else {
470       match CommunityUserBan::unban(&conn, &community_user_ban_form) {
471         Ok(user) => user,
472         Err(_e) => {
473           return Err(APIError::err(&self.op, "Community user ban already exists"))?
474         }
475       };
476     }
477
478     // Mod tables
479     let expires = match data.expires {
480       Some(time) => Some(naive_from_unix(time)),
481       None => None
482     };
483
484     let form = ModBanFromCommunityForm {
485       mod_user_id: user_id,
486       other_user_id: data.user_id,
487       community_id: data.community_id,
488       reason: data.reason.to_owned(),
489       banned: Some(data.ban),
490       expires: expires,
491     };
492     ModBanFromCommunity::create(&conn, &form)?;
493
494     let user_view = UserView::read(&conn, data.user_id)?;
495
496     Ok(
497       BanFromCommunityResponse {
498         op: self.op.to_string(), 
499         user: user_view,
500         banned: data.ban
501       }
502       )
503   }
504 }
505
506 impl Perform<AddModToCommunityResponse> for Oper<AddModToCommunity> {
507   fn perform(&self) -> Result<AddModToCommunityResponse, Error> {
508     let data: &AddModToCommunity = &self.data;
509     let conn = establish_connection();
510
511     let claims = match Claims::decode(&data.auth) {
512       Ok(claims) => claims.claims,
513       Err(_e) => {
514         return Err(APIError::err(&self.op, "Not logged in."))?
515       }
516     };
517
518     let user_id = claims.id;
519
520     let community_moderator_form = CommunityModeratorForm {
521       community_id: data.community_id,
522       user_id: data.user_id
523     };
524
525     if data.added {
526       match CommunityModerator::join(&conn, &community_moderator_form) {
527         Ok(user) => user,
528         Err(_e) => {
529           return Err(APIError::err(&self.op, "Community moderator already exists."))?
530         }
531       };
532     } else {
533       match CommunityModerator::leave(&conn, &community_moderator_form) {
534         Ok(user) => user,
535         Err(_e) => {
536           return Err(APIError::err(&self.op, "Community moderator already exists."))?
537         }
538       };
539     }
540
541     // Mod tables
542     let form = ModAddCommunityForm {
543       mod_user_id: user_id,
544       other_user_id: data.user_id,
545       community_id: data.community_id,
546       removed: Some(!data.added),
547     };
548     ModAddCommunity::create(&conn, &form)?;
549
550     let moderators = CommunityModeratorView::for_community(&conn, data.community_id)?;
551
552     Ok(
553       AddModToCommunityResponse {
554         op: self.op.to_string(), 
555         moderators: moderators,
556       }
557       )
558   }
559 }