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