]> Untitled Git - lemmy.git/blob - server/src/api/comment.rs
Adding emoji support.
[lemmy.git] / server / src / api / comment.rs
1 use super::*;
2
3 #[derive(Serialize, Deserialize)]
4 pub struct CreateComment {
5   content: String,
6   parent_id: Option<i32>,
7   edit_id: Option<i32>,
8   pub post_id: i32,
9   auth: String
10 }
11
12 #[derive(Serialize, Deserialize)]
13 pub struct EditComment {
14   content: String,
15   parent_id: Option<i32>,
16   edit_id: i32,
17   creator_id: i32,
18   pub post_id: i32,
19   removed: Option<bool>,
20   deleted: Option<bool>,
21   reason: Option<String>,
22   read: Option<bool>,
23   auth: String
24 }
25
26 #[derive(Serialize, Deserialize)]
27 pub struct SaveComment {
28   comment_id: i32,
29   save: bool,
30   auth: String
31 }
32
33 #[derive(Serialize, Deserialize, Clone)]
34 pub struct CommentResponse {
35   op: String,
36   pub comment: CommentView
37 }
38
39 #[derive(Serialize, Deserialize)]
40 pub struct CreateCommentLike {
41   comment_id: i32,
42   pub post_id: i32,
43   score: i16,
44   auth: String
45 }
46
47
48 impl Perform<CommentResponse> for Oper<CreateComment> {
49   fn perform(&self) -> Result<CommentResponse, Error> {
50     let data: &CreateComment = &self.data;
51     let conn = establish_connection();
52
53     let claims = match Claims::decode(&data.auth) {
54       Ok(claims) => claims.claims,
55       Err(_e) => {
56         return Err(APIError::err(&self.op, "not_logged_in"))?
57       }
58     };
59
60     let user_id = claims.id;
61
62     // Check for a community ban
63     let post = Post::read(&conn, data.post_id)?;
64     if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() {
65       return Err(APIError::err(&self.op, "community_ban"))?
66     }
67
68     // Check for a site ban
69     if UserView::read(&conn, user_id)?.banned {
70       return Err(APIError::err(&self.op, "site_ban"))?
71     }
72
73     let content_slurs_removed = remove_slurs(&data.content.to_owned());
74
75     let comment_form = CommentForm {
76       content: content_slurs_removed,
77       parent_id: data.parent_id.to_owned(),
78       post_id: data.post_id,
79       creator_id: user_id,
80       removed: None,
81       deleted: None,
82       read: None,
83       updated: None
84     };
85
86     let inserted_comment = match Comment::create(&conn, &comment_form) {
87       Ok(comment) => comment,
88       Err(_e) => {
89         return Err(APIError::err(&self.op, "couldnt_create_comment"))?
90       }
91     };
92
93     // You like your own comment by default
94     let like_form = CommentLikeForm {
95       comment_id: inserted_comment.id,
96       post_id: data.post_id,
97       user_id: user_id,
98       score: 1
99     };
100
101     let _inserted_like = match CommentLike::like(&conn, &like_form) {
102       Ok(like) => like,
103       Err(_e) => {
104         return Err(APIError::err(&self.op, "couldnt_like_comment"))?
105       }
106     };
107
108     let comment_view = CommentView::read(&conn, inserted_comment.id, Some(user_id))?;
109
110     Ok(
111       CommentResponse {
112         op: self.op.to_string(), 
113         comment: comment_view
114       }
115       )
116   }
117 }
118
119 impl Perform<CommentResponse> for Oper<EditComment> {
120   fn perform(&self) -> Result<CommentResponse, Error> {
121     let data: &EditComment = &self.data;
122     let conn = establish_connection();
123
124     let claims = match Claims::decode(&data.auth) {
125       Ok(claims) => claims.claims,
126       Err(_e) => {
127         return Err(APIError::err(&self.op, "not_logged_in"))?
128       }
129     };
130
131     let user_id = claims.id;
132
133     let orig_comment = CommentView::read(&conn, data.edit_id, None)?;
134
135     // You are allowed to mark the comment as read even if you're banned.
136     if data.read.is_none() {
137
138       // Verify its the creator or a mod, or an admin
139       let mut editors: Vec<i32> = vec![data.creator_id];
140       editors.append(
141         &mut CommunityModeratorView::for_community(&conn, orig_comment.community_id)
142         ?
143         .into_iter()
144         .map(|m| m.user_id)
145         .collect()
146         );
147       editors.append(
148         &mut UserView::admins(&conn)
149         ?
150         .into_iter()
151         .map(|a| a.id)
152         .collect()
153         );
154
155       if !editors.contains(&user_id) {
156         return Err(APIError::err(&self.op, "no_comment_edit_allowed"))?
157       }
158
159       // Check for a community ban
160       if CommunityUserBanView::get(&conn, user_id, orig_comment.community_id).is_ok() {
161         return Err(APIError::err(&self.op, "community_ban"))?
162       }
163
164       // Check for a site ban
165       if UserView::read(&conn, user_id)?.banned {
166         return Err(APIError::err(&self.op, "site_ban"))?
167       }
168
169     }
170
171     let content_slurs_removed = remove_slurs(&data.content.to_owned());
172
173     let comment_form = CommentForm {
174       content: content_slurs_removed,
175       parent_id: data.parent_id,
176       post_id: data.post_id,
177       creator_id: data.creator_id,
178       removed: data.removed.to_owned(),
179       deleted: data.deleted.to_owned(),
180       read: data.read.to_owned(),
181       updated: if data.read.is_some() { orig_comment.updated } else {Some(naive_now())}
182     };
183
184     let _updated_comment = match Comment::update(&conn, data.edit_id, &comment_form) {
185       Ok(comment) => comment,
186       Err(_e) => {
187         return Err(APIError::err(&self.op, "couldnt_update_comment"))?
188       }
189     };
190
191     // Mod tables
192     if let Some(removed) = data.removed.to_owned() {
193       let form = ModRemoveCommentForm {
194         mod_user_id: user_id,
195         comment_id: data.edit_id,
196         removed: Some(removed),
197         reason: data.reason.to_owned(),
198       };
199       ModRemoveComment::create(&conn, &form)?;
200     }
201
202
203     let comment_view = CommentView::read(&conn, data.edit_id, Some(user_id))?;
204
205     Ok(
206       CommentResponse {
207         op: self.op.to_string(), 
208         comment: comment_view
209       }
210       )
211
212   }
213 }
214
215 impl Perform<CommentResponse> for Oper<SaveComment> {
216   fn perform(&self) -> Result<CommentResponse, Error> {
217     let data: &SaveComment = &self.data;
218     let conn = establish_connection();
219
220     let claims = match Claims::decode(&data.auth) {
221       Ok(claims) => claims.claims,
222       Err(_e) => {
223         return Err(APIError::err(&self.op, "not_logged_in"))?
224       }
225     };
226
227     let user_id = claims.id;
228
229     let comment_saved_form = CommentSavedForm {
230       comment_id: data.comment_id,
231       user_id: user_id,
232     };
233
234     if data.save {
235       match CommentSaved::save(&conn, &comment_saved_form) {
236         Ok(comment) => comment,
237         Err(_e) => {
238           return Err(APIError::err(&self.op, "couldnt_save_comment"))?
239         }
240       };
241     } else {
242       match CommentSaved::unsave(&conn, &comment_saved_form) {
243         Ok(comment) => comment,
244         Err(_e) => {
245           return Err(APIError::err(&self.op, "couldnt_save_comment"))?
246         }
247       };
248     }
249
250     let comment_view = CommentView::read(&conn, data.comment_id, Some(user_id))?;
251
252     Ok(
253       CommentResponse {
254         op: self.op.to_string(), 
255         comment: comment_view
256       }
257       )
258   }
259 }
260
261 impl Perform<CommentResponse> for Oper<CreateCommentLike> {
262   fn perform(&self) -> Result<CommentResponse, Error> {
263     let data: &CreateCommentLike = &self.data;
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 community ban
276     let post = Post::read(&conn, data.post_id)?;
277     if CommunityUserBanView::get(&conn, user_id, post.community_id).is_ok() {
278       return Err(APIError::err(&self.op, "community_ban"))?
279     }
280
281     // Check for a site ban
282     if UserView::read(&conn, user_id)?.banned {
283       return Err(APIError::err(&self.op, "site_ban"))?
284     }
285
286     let like_form = CommentLikeForm {
287       comment_id: data.comment_id,
288       post_id: data.post_id,
289       user_id: user_id,
290       score: data.score
291     };
292
293     // Remove any likes first
294     CommentLike::remove(&conn, &like_form)?;
295
296     // Only add the like if the score isnt 0
297     let do_add = &like_form.score != &0 && (&like_form.score == &1 || &like_form.score == &-1);
298     if do_add {
299       let _inserted_like = match CommentLike::like(&conn, &like_form) {
300         Ok(like) => like,
301         Err(_e) => {
302           return Err(APIError::err(&self.op, "couldnt_like_comment"))?
303         }
304       };
305     }
306
307     // Have to refetch the comment to get the current state
308     let liked_comment = CommentView::read(&conn, data.comment_id, Some(user_id))?;
309
310     Ok(
311       CommentResponse {
312         op: self.op.to_string(), 
313         comment: liked_comment
314       }
315       )
316   }
317 }