2 use actix_web::web::Data;
3 use lemmy_api_common::{
6 check_community_deleted_or_removed,
7 check_downvotes_enabled,
9 get_local_user_view_from_jwt,
16 fetcher::post_or_comment::PostOrComment,
17 objects::post::ApubPost,
18 protocol::activities::{
19 create_or_update::post::CreateOrUpdatePost,
22 vote::{Vote, VoteType},
27 use lemmy_db_schema::{
28 source::{moderator::*, post::*},
29 traits::{Crud, Likeable, Saveable},
31 use lemmy_db_views::post_view::PostView;
32 use lemmy_utils::{request::fetch_site_metadata, ApiError, ConnectionId, LemmyError};
33 use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperation};
34 use std::convert::TryInto;
36 #[async_trait::async_trait(?Send)]
37 impl Perform for CreatePostLike {
38 type Response = PostResponse;
42 context: &Data<LemmyContext>,
43 websocket_id: Option<ConnectionId>,
44 ) -> Result<PostResponse, LemmyError> {
45 let data: &CreatePostLike = self;
47 get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
49 // Don't do a downvote if site has downvotes disabled
50 check_downvotes_enabled(data.score, context.pool()).await?;
52 // Check for a community ban
53 let post_id = data.post_id;
54 let post: ApubPost = blocking(context.pool(), move |conn| Post::read(conn, post_id))
58 check_community_ban(local_user_view.person.id, post.community_id, context.pool()).await?;
59 check_community_deleted_or_removed(post.community_id, context.pool()).await?;
61 check_person_block(local_user_view.person.id, post.creator_id, context.pool()).await?;
63 let like_form = PostLikeForm {
64 post_id: data.post_id,
65 person_id: local_user_view.person.id,
69 // Remove any likes first
70 let person_id = local_user_view.person.id;
71 blocking(context.pool(), move |conn| {
72 PostLike::remove(conn, person_id, post_id)
76 let community_id = post.community_id;
77 let object = PostOrComment::Post(Box::new(post));
79 // Only add the like if the score isnt 0
80 let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
82 let like_form2 = like_form.clone();
83 let like = move |conn: &'_ _| PostLike::like(conn, &like_form2);
84 blocking(context.pool(), like)
86 .map_err(|e| ApiError::err("couldnt_like_post", e))?;
90 &local_user_view.person.clone().into(),
92 like_form.score.try_into()?,
97 // API doesn't distinguish between Undo/Like and Undo/Dislike
100 &local_user_view.person.clone().into(),
108 // Mark the post as read
109 mark_post_as_read(person_id, post_id, context.pool()).await?;
111 send_post_ws_message(
113 UserOperation::CreatePostLike,
115 Some(local_user_view.person.id),
122 #[async_trait::async_trait(?Send)]
123 impl Perform for MarkPostAsRead {
124 type Response = PostResponse;
128 context: &Data<LemmyContext>,
129 _websocket_id: Option<ConnectionId>,
130 ) -> Result<Self::Response, LemmyError> {
132 let local_user_view =
133 get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
135 let post_id = data.post_id;
136 let person_id = local_user_view.person.id;
138 // Mark the post as read / unread
140 mark_post_as_read(person_id, post_id, context.pool()).await?;
142 mark_post_as_unread(person_id, post_id, context.pool()).await?;
146 let post_view = blocking(context.pool(), move |conn| {
147 PostView::read(conn, post_id, Some(person_id))
151 let res = Self::Response { post_view };
157 #[async_trait::async_trait(?Send)]
158 impl Perform for LockPost {
159 type Response = PostResponse;
163 context: &Data<LemmyContext>,
164 websocket_id: Option<ConnectionId>,
165 ) -> Result<PostResponse, LemmyError> {
166 let data: &LockPost = self;
167 let local_user_view =
168 get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
170 let post_id = data.post_id;
171 let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
174 local_user_view.person.id,
175 orig_post.community_id,
179 check_community_deleted_or_removed(orig_post.community_id, context.pool()).await?;
181 // Verify that only the mods can lock
184 local_user_view.person.id,
185 orig_post.community_id,
190 let post_id = data.post_id;
191 let locked = data.locked;
192 let updated_post: ApubPost = blocking(context.pool(), move |conn| {
193 Post::update_locked(conn, post_id, locked)
199 let form = ModLockPostForm {
200 mod_person_id: local_user_view.person.id,
201 post_id: data.post_id,
202 locked: Some(locked),
204 blocking(context.pool(), move |conn| ModLockPost::create(conn, &form)).await??;
207 CreateOrUpdatePost::send(
209 &local_user_view.person.clone().into(),
210 CreateOrUpdateType::Update,
215 send_post_ws_message(
217 UserOperation::LockPost,
219 Some(local_user_view.person.id),
226 #[async_trait::async_trait(?Send)]
227 impl Perform for StickyPost {
228 type Response = PostResponse;
232 context: &Data<LemmyContext>,
233 websocket_id: Option<ConnectionId>,
234 ) -> Result<PostResponse, LemmyError> {
235 let data: &StickyPost = self;
236 let local_user_view =
237 get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
239 let post_id = data.post_id;
240 let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
243 local_user_view.person.id,
244 orig_post.community_id,
248 check_community_deleted_or_removed(orig_post.community_id, context.pool()).await?;
250 // Verify that only the mods can sticky
253 local_user_view.person.id,
254 orig_post.community_id,
259 let post_id = data.post_id;
260 let stickied = data.stickied;
261 let updated_post: ApubPost = blocking(context.pool(), move |conn| {
262 Post::update_stickied(conn, post_id, stickied)
268 let form = ModStickyPostForm {
269 mod_person_id: local_user_view.person.id,
270 post_id: data.post_id,
271 stickied: Some(stickied),
273 blocking(context.pool(), move |conn| {
274 ModStickyPost::create(conn, &form)
279 // TODO stickied should pry work like locked for ease of use
280 CreateOrUpdatePost::send(
282 &local_user_view.person.clone().into(),
283 CreateOrUpdateType::Update,
288 send_post_ws_message(
290 UserOperation::StickyPost,
292 Some(local_user_view.person.id),
299 #[async_trait::async_trait(?Send)]
300 impl Perform for SavePost {
301 type Response = PostResponse;
305 context: &Data<LemmyContext>,
306 _websocket_id: Option<ConnectionId>,
307 ) -> Result<PostResponse, LemmyError> {
308 let data: &SavePost = self;
309 let local_user_view =
310 get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
312 let post_saved_form = PostSavedForm {
313 post_id: data.post_id,
314 person_id: local_user_view.person.id,
318 let save = move |conn: &'_ _| PostSaved::save(conn, &post_saved_form);
319 blocking(context.pool(), save)
321 .map_err(|e| ApiError::err("couldnt_save_post", e))?;
323 let unsave = move |conn: &'_ _| PostSaved::unsave(conn, &post_saved_form);
324 blocking(context.pool(), unsave)
326 .map_err(|e| ApiError::err("couldnt_save_post", e))?;
329 let post_id = data.post_id;
330 let person_id = local_user_view.person.id;
331 let post_view = blocking(context.pool(), move |conn| {
332 PostView::read(conn, post_id, Some(person_id))
336 // Mark the post as read
337 mark_post_as_read(person_id, post_id, context.pool()).await?;
339 Ok(PostResponse { post_view })
343 #[async_trait::async_trait(?Send)]
344 impl Perform for GetSiteMetadata {
345 type Response = GetSiteMetadataResponse;
349 context: &Data<LemmyContext>,
350 _websocket_id: Option<ConnectionId>,
351 ) -> Result<GetSiteMetadataResponse, LemmyError> {
352 let data: &Self = self;
354 let metadata = fetch_site_metadata(context.client(), &data.url).await?;
356 Ok(GetSiteMetadataResponse { metadata })