]> Untitled Git - lemmy.git/blob - crates/api/src/post.rs
Rewrite delete activities (#1699)
[lemmy.git] / crates / api / src / post.rs
1 use crate::Perform;
2 use actix_web::web::Data;
3 use lemmy_api_common::{
4   blocking,
5   check_community_ban,
6   check_downvotes_enabled,
7   get_local_user_view_from_jwt,
8   is_mod_or_admin,
9   mark_post_as_read,
10   post::*,
11 };
12 use lemmy_apub::{
13   activities::{
14     post::create_or_update::CreateOrUpdatePost,
15     voting::{
16       undo_vote::UndoVote,
17       vote::{Vote, VoteType},
18     },
19     CreateOrUpdateType,
20   },
21   PostOrComment,
22 };
23 use lemmy_db_queries::{source::post::Post_, Crud, Likeable, Saveable};
24 use lemmy_db_schema::source::{moderator::*, post::*};
25 use lemmy_db_views::post_view::PostView;
26 use lemmy_utils::{ApiError, ConnectionId, LemmyError};
27 use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperation};
28 use std::convert::TryInto;
29
30 #[async_trait::async_trait(?Send)]
31 impl Perform for CreatePostLike {
32   type Response = PostResponse;
33
34   async fn perform(
35     &self,
36     context: &Data<LemmyContext>,
37     websocket_id: Option<ConnectionId>,
38   ) -> Result<PostResponse, LemmyError> {
39     let data: &CreatePostLike = self;
40     let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
41
42     // Don't do a downvote if site has downvotes disabled
43     check_downvotes_enabled(data.score, context.pool()).await?;
44
45     // Check for a community ban
46     let post_id = data.post_id;
47     let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
48
49     check_community_ban(local_user_view.person.id, post.community_id, context.pool()).await?;
50
51     let like_form = PostLikeForm {
52       post_id: data.post_id,
53       person_id: local_user_view.person.id,
54       score: data.score,
55     };
56
57     // Remove any likes first
58     let person_id = local_user_view.person.id;
59     blocking(context.pool(), move |conn| {
60       PostLike::remove(conn, person_id, post_id)
61     })
62     .await??;
63
64     let community_id = post.community_id;
65     let object = PostOrComment::Post(Box::new(post));
66
67     // Only add the like if the score isnt 0
68     let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
69     if do_add {
70       let like_form2 = like_form.clone();
71       let like = move |conn: &'_ _| PostLike::like(conn, &like_form2);
72       if blocking(context.pool(), like).await?.is_err() {
73         return Err(ApiError::err("couldnt_like_post").into());
74       }
75
76       Vote::send(
77         &object,
78         &local_user_view.person,
79         community_id,
80         like_form.score.try_into()?,
81         context,
82       )
83       .await?;
84     } else {
85       // API doesn't distinguish between Undo/Like and Undo/Dislike
86       UndoVote::send(
87         &object,
88         &local_user_view.person,
89         community_id,
90         VoteType::Like,
91         context,
92       )
93       .await?;
94     }
95
96     // Mark the post as read
97     mark_post_as_read(person_id, post_id, context.pool()).await?;
98
99     send_post_ws_message(
100       data.post_id,
101       UserOperation::CreatePostLike,
102       websocket_id,
103       Some(local_user_view.person.id),
104       context,
105     )
106     .await
107   }
108 }
109
110 #[async_trait::async_trait(?Send)]
111 impl Perform for LockPost {
112   type Response = PostResponse;
113
114   async fn perform(
115     &self,
116     context: &Data<LemmyContext>,
117     websocket_id: Option<ConnectionId>,
118   ) -> Result<PostResponse, LemmyError> {
119     let data: &LockPost = self;
120     let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
121
122     let post_id = data.post_id;
123     let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
124
125     check_community_ban(
126       local_user_view.person.id,
127       orig_post.community_id,
128       context.pool(),
129     )
130     .await?;
131
132     // Verify that only the mods can lock
133     is_mod_or_admin(
134       context.pool(),
135       local_user_view.person.id,
136       orig_post.community_id,
137     )
138     .await?;
139
140     // Update the post
141     let post_id = data.post_id;
142     let locked = data.locked;
143     let updated_post = blocking(context.pool(), move |conn| {
144       Post::update_locked(conn, post_id, locked)
145     })
146     .await??;
147
148     // Mod tables
149     let form = ModLockPostForm {
150       mod_person_id: local_user_view.person.id,
151       post_id: data.post_id,
152       locked: Some(locked),
153     };
154     blocking(context.pool(), move |conn| ModLockPost::create(conn, &form)).await??;
155
156     // apub updates
157     CreateOrUpdatePost::send(
158       &updated_post,
159       &local_user_view.person,
160       CreateOrUpdateType::Update,
161       context,
162     )
163     .await?;
164
165     send_post_ws_message(
166       data.post_id,
167       UserOperation::LockPost,
168       websocket_id,
169       Some(local_user_view.person.id),
170       context,
171     )
172     .await
173   }
174 }
175
176 #[async_trait::async_trait(?Send)]
177 impl Perform for StickyPost {
178   type Response = PostResponse;
179
180   async fn perform(
181     &self,
182     context: &Data<LemmyContext>,
183     websocket_id: Option<ConnectionId>,
184   ) -> Result<PostResponse, LemmyError> {
185     let data: &StickyPost = self;
186     let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
187
188     let post_id = data.post_id;
189     let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
190
191     check_community_ban(
192       local_user_view.person.id,
193       orig_post.community_id,
194       context.pool(),
195     )
196     .await?;
197
198     // Verify that only the mods can sticky
199     is_mod_or_admin(
200       context.pool(),
201       local_user_view.person.id,
202       orig_post.community_id,
203     )
204     .await?;
205
206     // Update the post
207     let post_id = data.post_id;
208     let stickied = data.stickied;
209     let updated_post = blocking(context.pool(), move |conn| {
210       Post::update_stickied(conn, post_id, stickied)
211     })
212     .await??;
213
214     // Mod tables
215     let form = ModStickyPostForm {
216       mod_person_id: local_user_view.person.id,
217       post_id: data.post_id,
218       stickied: Some(stickied),
219     };
220     blocking(context.pool(), move |conn| {
221       ModStickyPost::create(conn, &form)
222     })
223     .await??;
224
225     // Apub updates
226     // TODO stickied should pry work like locked for ease of use
227     CreateOrUpdatePost::send(
228       &updated_post,
229       &local_user_view.person,
230       CreateOrUpdateType::Update,
231       context,
232     )
233     .await?;
234
235     send_post_ws_message(
236       data.post_id,
237       UserOperation::StickyPost,
238       websocket_id,
239       Some(local_user_view.person.id),
240       context,
241     )
242     .await
243   }
244 }
245
246 #[async_trait::async_trait(?Send)]
247 impl Perform for SavePost {
248   type Response = PostResponse;
249
250   async fn perform(
251     &self,
252     context: &Data<LemmyContext>,
253     _websocket_id: Option<ConnectionId>,
254   ) -> Result<PostResponse, LemmyError> {
255     let data: &SavePost = self;
256     let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
257
258     let post_saved_form = PostSavedForm {
259       post_id: data.post_id,
260       person_id: local_user_view.person.id,
261     };
262
263     if data.save {
264       let save = move |conn: &'_ _| PostSaved::save(conn, &post_saved_form);
265       if blocking(context.pool(), save).await?.is_err() {
266         return Err(ApiError::err("couldnt_save_post").into());
267       }
268     } else {
269       let unsave = move |conn: &'_ _| PostSaved::unsave(conn, &post_saved_form);
270       if blocking(context.pool(), unsave).await?.is_err() {
271         return Err(ApiError::err("couldnt_save_post").into());
272       }
273     }
274
275     let post_id = data.post_id;
276     let person_id = local_user_view.person.id;
277     let post_view = blocking(context.pool(), move |conn| {
278       PostView::read(conn, post_id, Some(person_id))
279     })
280     .await??;
281
282     // Mark the post as read
283     mark_post_as_read(person_id, post_id, context.pool()).await?;
284
285     Ok(PostResponse { post_view })
286   }
287 }