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,
15 fetcher::post_or_comment::PostOrComment,
16 objects::post::ApubPost,
17 protocol::activities::{
18 create_or_update::post::CreateOrUpdatePost,
21 vote::{Vote, VoteType},
26 use lemmy_db_schema::{
27 source::{moderator::*, post::*},
28 traits::{Crud, Likeable, Saveable},
30 use lemmy_db_views::post_view::PostView;
31 use lemmy_utils::{request::fetch_site_metadata, ApiError, ConnectionId, LemmyError};
32 use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperation};
33 use std::convert::TryInto;
35 #[async_trait::async_trait(?Send)]
36 impl Perform for CreatePostLike {
37 type Response = PostResponse;
41 context: &Data<LemmyContext>,
42 websocket_id: Option<ConnectionId>,
43 ) -> Result<PostResponse, LemmyError> {
44 let data: &CreatePostLike = self;
46 get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
48 // Don't do a downvote if site has downvotes disabled
49 check_downvotes_enabled(data.score, context.pool()).await?;
51 // Check for a community ban
52 let post_id = data.post_id;
53 let post: ApubPost = blocking(context.pool(), move |conn| Post::read(conn, post_id))
57 check_community_ban(local_user_view.person.id, post.community_id, context.pool()).await?;
58 check_community_deleted_or_removed(post.community_id, context.pool()).await?;
60 check_person_block(local_user_view.person.id, post.creator_id, context.pool()).await?;
62 let like_form = PostLikeForm {
63 post_id: data.post_id,
64 person_id: local_user_view.person.id,
68 // Remove any likes first
69 let person_id = local_user_view.person.id;
70 blocking(context.pool(), move |conn| {
71 PostLike::remove(conn, person_id, post_id)
75 let community_id = post.community_id;
76 let object = PostOrComment::Post(Box::new(post));
78 // Only add the like if the score isnt 0
79 let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
81 let like_form2 = like_form.clone();
82 let like = move |conn: &'_ _| PostLike::like(conn, &like_form2);
83 blocking(context.pool(), like)
85 .map_err(|e| ApiError::err("couldnt_like_post", e))?;
89 &local_user_view.person.clone().into(),
91 like_form.score.try_into()?,
96 // API doesn't distinguish between Undo/Like and Undo/Dislike
99 &local_user_view.person.clone().into(),
107 // Mark the post as read
108 mark_post_as_read(person_id, post_id, context.pool()).await?;
110 send_post_ws_message(
112 UserOperation::CreatePostLike,
114 Some(local_user_view.person.id),
121 #[async_trait::async_trait(?Send)]
122 impl Perform for LockPost {
123 type Response = PostResponse;
127 context: &Data<LemmyContext>,
128 websocket_id: Option<ConnectionId>,
129 ) -> Result<PostResponse, LemmyError> {
130 let data: &LockPost = self;
131 let local_user_view =
132 get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
134 let post_id = data.post_id;
135 let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
138 local_user_view.person.id,
139 orig_post.community_id,
143 check_community_deleted_or_removed(orig_post.community_id, context.pool()).await?;
145 // Verify that only the mods can lock
148 local_user_view.person.id,
149 orig_post.community_id,
154 let post_id = data.post_id;
155 let locked = data.locked;
156 let updated_post: ApubPost = blocking(context.pool(), move |conn| {
157 Post::update_locked(conn, post_id, locked)
163 let form = ModLockPostForm {
164 mod_person_id: local_user_view.person.id,
165 post_id: data.post_id,
166 locked: Some(locked),
168 blocking(context.pool(), move |conn| ModLockPost::create(conn, &form)).await??;
171 CreateOrUpdatePost::send(
173 &local_user_view.person.clone().into(),
174 CreateOrUpdateType::Update,
179 send_post_ws_message(
181 UserOperation::LockPost,
183 Some(local_user_view.person.id),
190 #[async_trait::async_trait(?Send)]
191 impl Perform for StickyPost {
192 type Response = PostResponse;
196 context: &Data<LemmyContext>,
197 websocket_id: Option<ConnectionId>,
198 ) -> Result<PostResponse, LemmyError> {
199 let data: &StickyPost = self;
200 let local_user_view =
201 get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
203 let post_id = data.post_id;
204 let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
207 local_user_view.person.id,
208 orig_post.community_id,
212 check_community_deleted_or_removed(orig_post.community_id, context.pool()).await?;
214 // Verify that only the mods can sticky
217 local_user_view.person.id,
218 orig_post.community_id,
223 let post_id = data.post_id;
224 let stickied = data.stickied;
225 let updated_post: ApubPost = blocking(context.pool(), move |conn| {
226 Post::update_stickied(conn, post_id, stickied)
232 let form = ModStickyPostForm {
233 mod_person_id: local_user_view.person.id,
234 post_id: data.post_id,
235 stickied: Some(stickied),
237 blocking(context.pool(), move |conn| {
238 ModStickyPost::create(conn, &form)
243 // TODO stickied should pry work like locked for ease of use
244 CreateOrUpdatePost::send(
246 &local_user_view.person.clone().into(),
247 CreateOrUpdateType::Update,
252 send_post_ws_message(
254 UserOperation::StickyPost,
256 Some(local_user_view.person.id),
263 #[async_trait::async_trait(?Send)]
264 impl Perform for SavePost {
265 type Response = PostResponse;
269 context: &Data<LemmyContext>,
270 _websocket_id: Option<ConnectionId>,
271 ) -> Result<PostResponse, LemmyError> {
272 let data: &SavePost = self;
273 let local_user_view =
274 get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
276 let post_saved_form = PostSavedForm {
277 post_id: data.post_id,
278 person_id: local_user_view.person.id,
282 let save = move |conn: &'_ _| PostSaved::save(conn, &post_saved_form);
283 blocking(context.pool(), save)
285 .map_err(|e| ApiError::err("couldnt_save_post", e))?;
287 let unsave = move |conn: &'_ _| PostSaved::unsave(conn, &post_saved_form);
288 blocking(context.pool(), unsave)
290 .map_err(|e| ApiError::err("couldnt_save_post", e))?;
293 let post_id = data.post_id;
294 let person_id = local_user_view.person.id;
295 let post_view = blocking(context.pool(), move |conn| {
296 PostView::read(conn, post_id, Some(person_id))
300 // Mark the post as read
301 mark_post_as_read(person_id, post_id, context.pool()).await?;
303 Ok(PostResponse { post_view })
307 #[async_trait::async_trait(?Send)]
308 impl Perform for GetSiteMetadata {
309 type Response = GetSiteMetadataResponse;
313 context: &Data<LemmyContext>,
314 _websocket_id: Option<ConnectionId>,
315 ) -> Result<GetSiteMetadataResponse, LemmyError> {
316 let data: &Self = self;
318 let metadata = fetch_site_metadata(context.client(), &data.url).await?;
320 Ok(GetSiteMetadataResponse { metadata })