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