]> Untitled Git - lemmy.git/blob - server/src/api/post.rs
Adding emoji support
[lemmy.git] / server / src / api / post.rs
1 use super::*;
2 use std::str::FromStr;
3
4 #[derive(Serialize, Deserialize)]
5 pub struct CreatePost {
6   name: String,
7   url: Option<String>,
8   body: Option<String>,
9   community_id: i32,
10   auth: String
11 }
12
13 #[derive(Serialize, Deserialize, Clone)]
14 pub struct PostResponse {
15   op: String,
16   pub post: PostView
17 }
18
19
20 #[derive(Serialize, Deserialize)]
21 pub struct GetPost {
22   pub id: i32,
23   auth: Option<String>
24 }
25
26 #[derive(Serialize, Deserialize)]
27 pub struct GetPostResponse {
28   op: String,
29   post: PostView,
30   comments: Vec<CommentView>,
31   community: CommunityView,
32   moderators: Vec<CommunityModeratorView>,
33   admins: Vec<UserView>,
34 }
35
36 #[derive(Serialize, Deserialize)]
37 pub struct GetPosts {
38   type_: String,
39   sort: String,
40   page: Option<i64>,
41   limit: Option<i64>,
42   community_id: Option<i32>,
43   auth: Option<String>
44 }
45
46 #[derive(Serialize, Deserialize)]
47 pub struct GetPostsResponse {
48   op: String,
49   posts: Vec<PostView>,
50 }
51
52 #[derive(Serialize, Deserialize)]
53 pub struct CreatePostLike {
54   post_id: i32,
55   score: i16,
56   auth: String
57 }
58
59 #[derive(Serialize, Deserialize)]
60 pub struct CreatePostLikeResponse {
61   op: String,
62   post: PostView
63 }
64
65
66 #[derive(Serialize, Deserialize)]
67 pub struct EditPost {
68   pub edit_id: i32,
69   creator_id: i32,
70   community_id: i32,
71   name: String,
72   url: Option<String>,
73   body: Option<String>,
74   removed: Option<bool>,
75   deleted: Option<bool>,
76   locked: Option<bool>,
77   reason: Option<String>,
78   auth: String
79 }
80
81 #[derive(Serialize, Deserialize)]
82 pub struct SavePost {
83   post_id: i32,
84   save: bool,
85   auth: String
86 }
87
88 impl Perform<PostResponse> for Oper<CreatePost> {
89   fn perform(&self) -> Result<PostResponse, Error> {
90     let data: &CreatePost = &self.data;
91     let conn = establish_connection();
92
93
94     let claims = match Claims::decode(&data.auth) {
95       Ok(claims) => claims.claims,
96       Err(_e) => {
97         return Err(APIError::err(&self.op, "Not logged in."))?
98       }
99     };
100
101     if has_slurs(&data.name) || 
102       (data.body.is_some() && has_slurs(&data.body.to_owned().unwrap())) {
103         return Err(APIError::err(&self.op, "No slurs"))?
104       }
105
106     let user_id = claims.id;
107
108     // Check for a community ban
109     if CommunityUserBanView::get(&conn, user_id, data.community_id).is_ok() {
110       return Err(APIError::err(&self.op, "You have been banned from this community"))?
111     }
112
113     // Check for a site ban
114     if UserView::read(&conn, user_id)?.banned {
115       return Err(APIError::err(&self.op, "You have been banned from the site"))?
116     }
117
118     let post_form = PostForm {
119       name: data.name.to_owned(),
120       url: data.url.to_owned(),
121       body: data.body.to_owned(),
122       community_id: data.community_id,
123       creator_id: user_id,
124       removed: None,
125       deleted: None,
126       locked: None,
127       updated: None
128     };
129
130     let inserted_post = match Post::create(&conn, &post_form) {
131       Ok(post) => post,
132       Err(_e) => {
133         return Err(APIError::err(&self.op, "Couldn't create Post"))?
134       }
135     };
136
137     // They like their own post by default
138     let like_form = PostLikeForm {
139       post_id: inserted_post.id,
140       user_id: user_id,
141       score: 1
142     };
143
144     // Only add the like if the score isnt 0
145     let _inserted_like = match PostLike::like(&conn, &like_form) {
146       Ok(like) => like,
147       Err(_e) => {
148         return Err(APIError::err(&self.op, "Couldn't like post."))?
149       }
150     };
151
152     // Refetch the view
153     let post_view = match PostView::read(&conn, inserted_post.id, Some(user_id)) {
154       Ok(post) => post,
155       Err(_e) => {
156         return Err(APIError::err(&self.op, "Couldn't find Post"))?
157       }
158     };
159
160     Ok(
161       PostResponse {
162         op: self.op.to_string(), 
163         post: post_view
164       }
165       )
166   }
167 }
168
169 impl Perform<GetPostResponse> for Oper<GetPost> {
170   fn perform(&self) -> Result<GetPostResponse, Error> {
171     let data: &GetPost = &self.data;
172     let conn = establish_connection();
173
174     let user_id: Option<i32> = match &data.auth {
175       Some(auth) => {
176         match Claims::decode(&auth) {
177           Ok(claims) => {
178             let user_id = claims.claims.id;
179             Some(user_id)
180           }
181           Err(_e) => None
182         }
183       }
184       None => None
185     };
186
187     let post_view = match PostView::read(&conn, data.id, user_id) {
188       Ok(post) => post,
189       Err(_e) => {
190         return Err(APIError::err(&self.op, "Couldn't find Post"))?
191       }
192     };
193
194     let comments = CommentView::list(&conn, &SortType::New, Some(data.id), None, None, user_id, false, None, Some(9999))?;
195
196     let community = CommunityView::read(&conn, post_view.community_id, user_id)?;
197
198     let moderators = CommunityModeratorView::for_community(&conn, post_view.community_id)?;
199
200     let admins = UserView::admins(&conn)?;
201
202     // Return the jwt
203     Ok(
204       GetPostResponse {
205         op: self.op.to_string(),
206         post: post_view,
207         comments: comments,
208         community: community,
209         moderators: moderators,
210         admins: admins,
211       }
212       )
213   }
214 }
215
216
217 impl Perform<GetPostsResponse> for Oper<GetPosts> {
218   fn perform(&self) -> Result<GetPostsResponse, Error> {
219     let data: &GetPosts = &self.data;
220     let conn = establish_connection();
221
222     let user_id: Option<i32> = match &data.auth {
223       Some(auth) => {
224         match Claims::decode(&auth) {
225           Ok(claims) => {
226             let user_id = claims.claims.id;
227             Some(user_id)
228           }
229           Err(_e) => None
230         }
231       }
232       None => None
233     };
234
235     let type_ = PostListingType::from_str(&data.type_)?;
236     let sort = SortType::from_str(&data.sort)?;
237
238     let posts = match PostView::list(&conn, 
239                                      type_, 
240                                      &sort, 
241                                      data.community_id, 
242                                      None,
243                                      None,
244                                      user_id, 
245                                      false, 
246                                      false, 
247                                      data.page, 
248                                      data.limit) {
249       Ok(posts) => posts,
250       Err(_e) => {
251         return Err(APIError::err(&self.op, "Couldn't get posts"))?
252       }
253     };
254
255     // Return the jwt
256     Ok(
257       GetPostsResponse {
258         op: self.op.to_string(),
259         posts: posts
260       }
261       )
262   }
263 }
264
265 impl Perform<CreatePostLikeResponse> for Oper<CreatePostLike> {
266   fn perform(&self) -> Result<CreatePostLikeResponse, Error> {
267     let data: &CreatePostLike = &self.data;
268     let conn = establish_connection();
269
270     let claims = match Claims::decode(&data.auth) {
271       Ok(claims) => claims.claims,
272       Err(_e) => {
273         return Err(APIError::err(&self.op, "Not logged in."))?
274       }
275     };
276
277     let user_id = claims.id;
278
279     // Check for a community ban
280     let post = Post::read(&conn, data.post_id)?;
281     if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() {
282       return Err(APIError::err(&self.op, "You have been banned from this community"))?
283     }
284
285     // Check for a site ban
286     if UserView::read(&conn, user_id)?.banned {
287       return Err(APIError::err(&self.op, "You have been banned from the site"))?
288     }
289
290     let like_form = PostLikeForm {
291       post_id: data.post_id,
292       user_id: user_id,
293       score: data.score
294     };
295
296     // Remove any likes first
297     PostLike::remove(&conn, &like_form)?;
298
299     // Only add the like if the score isnt 0
300     let do_add = &like_form.score != &0 && (&like_form.score == &1 || &like_form.score == &-1);
301     if do_add {
302       let _inserted_like = match PostLike::like(&conn, &like_form) {
303         Ok(like) => like,
304         Err(_e) => {
305           return Err(APIError::err(&self.op, "Couldn't like post."))?
306         }
307       };
308     }
309
310     let post_view = match PostView::read(&conn, data.post_id, Some(user_id)) {
311       Ok(post) => post,
312       Err(_e) => {
313         return Err(APIError::err(&self.op, "Couldn't find Post"))?
314       }
315     };
316
317     // just output the score
318     Ok(
319       CreatePostLikeResponse {
320         op: self.op.to_string(), 
321         post: post_view
322       }
323       )
324   }
325 }
326
327 impl Perform<PostResponse> for Oper<EditPost> {
328   fn perform(&self) -> Result<PostResponse, Error> {
329     let data: &EditPost = &self.data;
330     if has_slurs(&data.name) || 
331       (data.body.is_some() && has_slurs(&data.body.to_owned().unwrap())) {
332         return Err(APIError::err(&self.op, "No slurs"))?
333       }
334
335     let conn = establish_connection();
336
337     let claims = match Claims::decode(&data.auth) {
338       Ok(claims) => claims.claims,
339       Err(_e) => {
340         return Err(APIError::err(&self.op, "Not logged in."))?
341       }
342     };
343
344     let user_id = claims.id;
345
346     // Verify its the creator or a mod or admin
347     let mut editors: Vec<i32> = vec![data.creator_id];
348     editors.append(
349       &mut CommunityModeratorView::for_community(&conn, data.community_id)
350       ?
351       .into_iter()
352       .map(|m| m.user_id)
353       .collect()
354       );
355     editors.append(
356       &mut UserView::admins(&conn)
357       ?
358       .into_iter()
359       .map(|a| a.id)
360       .collect()
361       );
362     if !editors.contains(&user_id) {
363       return Err(APIError::err(&self.op, "Not allowed to edit post."))?
364     }
365
366     // Check for a community ban
367     if CommunityUserBanView::get(&conn, user_id, data.community_id).is_ok() {
368       return Err(APIError::err(&self.op, "You have been banned from this community"))?
369     }
370
371     // Check for a site ban
372     if UserView::read(&conn, user_id)?.banned {
373       return Err(APIError::err(&self.op, "You have been banned from the site"))?
374     }
375
376     let post_form = PostForm {
377       name: data.name.to_owned(),
378       url: data.url.to_owned(),
379       body: data.body.to_owned(),
380       creator_id: data.creator_id.to_owned(),
381       community_id: data.community_id,
382       removed: data.removed.to_owned(),
383       deleted: data.deleted.to_owned(),
384       locked: data.locked.to_owned(),
385       updated: Some(naive_now())
386     };
387
388     let _updated_post = match Post::update(&conn, data.edit_id, &post_form) {
389       Ok(post) => post,
390       Err(_e) => {
391         return Err(APIError::err(&self.op, "Couldn't update Post"))?
392       }
393     };
394
395     // Mod tables
396     if let Some(removed) = data.removed.to_owned() {
397       let form = ModRemovePostForm {
398         mod_user_id: user_id,
399         post_id: data.edit_id,
400         removed: Some(removed),
401         reason: data.reason.to_owned(),
402       };
403       ModRemovePost::create(&conn, &form)?;
404     }
405
406     if let Some(locked) = data.locked.to_owned() {
407       let form = ModLockPostForm {
408         mod_user_id: user_id,
409         post_id: data.edit_id,
410         locked: Some(locked),
411       };
412       ModLockPost::create(&conn, &form)?;
413     }
414
415     let post_view = PostView::read(&conn, data.edit_id, Some(user_id))?;
416
417     Ok(
418       PostResponse {
419         op: self.op.to_string(), 
420         post: post_view
421       }
422       )
423   }
424 }
425
426 impl Perform<PostResponse> for Oper<SavePost> {
427   fn perform(&self) -> Result<PostResponse, Error> {
428     let data: &SavePost = &self.data;
429     let conn = establish_connection();
430
431     let claims = match Claims::decode(&data.auth) {
432       Ok(claims) => claims.claims,
433       Err(_e) => {
434         return Err(APIError::err(&self.op, "Not logged in."))?
435       }
436     };
437
438     let user_id = claims.id;
439
440     let post_saved_form = PostSavedForm {
441       post_id: data.post_id,
442       user_id: user_id,
443     };
444
445     if data.save {
446       match PostSaved::save(&conn, &post_saved_form) {
447         Ok(post) => post,
448         Err(_e) => {
449           return Err(APIError::err(&self.op, "Couldnt do post save"))?
450         }
451       };
452     } else {
453       match PostSaved::unsave(&conn, &post_saved_form) {
454         Ok(post) => post,
455         Err(_e) => {
456           return Err(APIError::err(&self.op, "Couldnt do post save"))?
457         }
458       };
459     }
460
461     let post_view = PostView::read(&conn, data.post_id, Some(user_id))?;
462
463     Ok(
464       PostResponse {
465         op: self.op.to_string(), 
466         post: post_view
467       }
468       )
469   }
470 }