-use crate::{
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
check_community_ban,
check_downvotes_enabled,
- collect_moderated_communities,
get_local_user_view_from_jwt,
- get_local_user_view_from_jwt_opt,
is_mod_or_admin,
- Perform,
-};
-use actix_web::web::Data;
-use lemmy_api_structs::{blocking, post::*};
-use lemmy_apub::{generate_apub_endpoint, ApubLikeableType, ApubObjectType, EndpointType};
-use lemmy_db_queries::{
- source::post::Post_,
- Crud,
- Likeable,
- ListingType,
- Reportable,
- Saveable,
- SortType,
-};
-use lemmy_db_schema::{
- naive_now,
- source::{
- moderator::*,
- post::*,
- post_report::{PostReport, PostReportForm},
- },
-};
-use lemmy_db_views::{
- comment_view::CommentQueryBuilder,
- post_report_view::{PostReportQueryBuilder, PostReportView},
- post_view::{PostQueryBuilder, PostView},
-};
-use lemmy_db_views_actor::{
- community_moderator_view::CommunityModeratorView,
- community_view::CommunityView,
+ post::*,
};
-use lemmy_utils::{
- request::fetch_iframely_and_pictrs_data,
- utils::{check_slurs, check_slurs_opt, is_valid_post_title},
- ApiError,
- ConnectionId,
- LemmyError,
-};
-use lemmy_websocket::{
- messages::{GetPostUsersOnline, SendModRoomMessage, SendPost, SendUserRoomMessage},
- LemmyContext,
- UserOperation,
-};
-use std::str::FromStr;
-
-#[async_trait::async_trait(?Send)]
-impl Perform for CreatePost {
- type Response = PostResponse;
-
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- websocket_id: Option<ConnectionId>,
- ) -> Result<PostResponse, LemmyError> {
- let data: &CreatePost = &self;
- let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
-
- check_slurs(&data.name)?;
- check_slurs_opt(&data.body)?;
-
- if !is_valid_post_title(&data.name) {
- return Err(ApiError::err("invalid_post_title").into());
- }
-
- check_community_ban(local_user_view.person.id, data.community_id, context.pool()).await?;
-
- // Fetch Iframely and pictrs cached image
- let data_url = data.url.as_ref();
- let (iframely_title, iframely_description, iframely_html, pictrs_thumbnail) =
- fetch_iframely_and_pictrs_data(context.client(), data_url).await;
-
- let post_form = PostForm {
- name: data.name.trim().to_owned(),
- url: data_url.map(|u| u.to_owned().into()),
- body: data.body.to_owned(),
- community_id: data.community_id,
- creator_id: local_user_view.person.id,
- removed: None,
- deleted: None,
- nsfw: data.nsfw,
- locked: None,
- stickied: None,
- updated: None,
- embed_title: iframely_title,
- embed_description: iframely_description,
- embed_html: iframely_html,
- thumbnail_url: pictrs_thumbnail.map(|u| u.into()),
- ap_id: None,
- local: true,
- published: None,
- };
-
- let inserted_post =
- match blocking(context.pool(), move |conn| Post::create(conn, &post_form)).await? {
- Ok(post) => post,
- Err(e) => {
- let err_type = if e.to_string() == "value too long for type character varying(200)" {
- "post_title_too_long"
- } else {
- "couldnt_create_post"
- };
-
- return Err(ApiError::err(err_type).into());
- }
- };
-
- let inserted_post_id = inserted_post.id;
- let updated_post = match blocking(context.pool(), move |conn| -> Result<Post, LemmyError> {
- let apub_id = generate_apub_endpoint(EndpointType::Post, &inserted_post_id.to_string())?;
- Ok(Post::update_ap_id(conn, inserted_post_id, apub_id)?)
- })
- .await?
- {
- Ok(post) => post,
- Err(_e) => return Err(ApiError::err("couldnt_create_post").into()),
- };
-
- updated_post
- .send_create(&local_user_view.person, context)
- .await?;
-
- // They like their own post by default
- let like_form = PostLikeForm {
- post_id: inserted_post.id,
- person_id: local_user_view.person.id,
- score: 1,
- };
-
- let like = move |conn: &'_ _| PostLike::like(conn, &like_form);
- if blocking(context.pool(), like).await?.is_err() {
- return Err(ApiError::err("couldnt_like_post").into());
- }
-
- updated_post
- .send_like(&local_user_view.person, context)
- .await?;
-
- // Refetch the view
- let inserted_post_id = inserted_post.id;
- let post_view = match blocking(context.pool(), move |conn| {
- PostView::read(conn, inserted_post_id, Some(local_user_view.person.id))
- })
- .await?
- {
- Ok(post) => post,
- Err(_e) => return Err(ApiError::err("couldnt_find_post").into()),
- };
-
- let res = PostResponse { post_view };
-
- context.chat_server().do_send(SendPost {
- op: UserOperation::CreatePost,
- post: res.clone(),
- websocket_id,
- });
-
- Ok(res)
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for GetPost {
- type Response = GetPostResponse;
-
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<GetPostResponse, LemmyError> {
- let data: &GetPost = &self;
- let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?;
- let person_id = local_user_view.map(|u| u.person.id);
-
- let id = data.id;
- let post_view = match blocking(context.pool(), move |conn| {
- PostView::read(conn, id, person_id)
- })
- .await?
- {
- Ok(post) => post,
- Err(_e) => return Err(ApiError::err("couldnt_find_post").into()),
- };
-
- let id = data.id;
- let comments = blocking(context.pool(), move |conn| {
- CommentQueryBuilder::create(conn)
- .my_person_id(person_id)
- .post_id(id)
- .limit(9999)
- .list()
- })
- .await??;
-
- let community_id = post_view.community.id;
- let moderators = blocking(context.pool(), move |conn| {
- CommunityModeratorView::for_community(conn, community_id)
- })
- .await??;
-
- // Necessary for the sidebar
- let community_view = match blocking(context.pool(), move |conn| {
- CommunityView::read(conn, community_id, person_id)
- })
- .await?
- {
- Ok(community) => community,
- Err(_e) => return Err(ApiError::err("couldnt_find_community").into()),
- };
-
- let online = context
- .chat_server()
- .send(GetPostUsersOnline { post_id: data.id })
- .await
- .unwrap_or(1);
-
- // Return the jwt
- Ok(GetPostResponse {
- post_view,
- community_view,
- comments,
- moderators,
- online,
- })
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for GetPosts {
- type Response = GetPostsResponse;
-
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<GetPostsResponse, LemmyError> {
- let data: &GetPosts = &self;
- let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?;
-
- let person_id = match &local_user_view {
- Some(uv) => Some(uv.person.id),
- None => None,
- };
-
- let show_nsfw = match &local_user_view {
- Some(uv) => uv.local_user.show_nsfw,
- None => false,
- };
-
- let type_ = ListingType::from_str(&data.type_)?;
- let sort = SortType::from_str(&data.sort)?;
-
- let page = data.page;
- let limit = data.limit;
- let community_id = data.community_id;
- let community_name = data.community_name.to_owned();
- let saved_only = data.saved_only;
-
- let posts = match blocking(context.pool(), move |conn| {
- PostQueryBuilder::create(conn)
- .listing_type(&type_)
- .sort(&sort)
- .show_nsfw(show_nsfw)
- .community_id(community_id)
- .community_name(community_name)
- .saved_only(saved_only)
- .my_person_id(person_id)
- .page(page)
- .limit(limit)
- .list()
- })
- .await?
- {
- Ok(posts) => posts,
- Err(_e) => return Err(ApiError::err("couldnt_get_posts").into()),
- };
-
- Ok(GetPostsResponse { posts })
- }
-}
+use lemmy_apub::{ApubLikeableType, ApubObjectType};
+use lemmy_db_queries::{source::post::Post_, Crud, Likeable, Saveable};
+use lemmy_db_schema::source::{moderator::*, post::*};
+use lemmy_db_views::post_view::PostView;
+use lemmy_utils::{ApiError, ConnectionId, LemmyError};
+use lemmy_websocket::{messages::SendPost, LemmyContext, UserOperation};
#[async_trait::async_trait(?Send)]
impl Perform for CreatePostLike {
}
}
-#[async_trait::async_trait(?Send)]
-impl Perform for EditPost {
- type Response = PostResponse;
-
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- websocket_id: Option<ConnectionId>,
- ) -> Result<PostResponse, LemmyError> {
- let data: &EditPost = &self;
- let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
-
- check_slurs(&data.name)?;
- check_slurs_opt(&data.body)?;
-
- if !is_valid_post_title(&data.name) {
- return Err(ApiError::err("invalid_post_title").into());
- }
-
- let post_id = data.post_id;
- let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
-
- check_community_ban(
- local_user_view.person.id,
- orig_post.community_id,
- context.pool(),
- )
- .await?;
-
- // Verify that only the creator can edit
- if !Post::is_post_creator(local_user_view.person.id, orig_post.creator_id) {
- return Err(ApiError::err("no_post_edit_allowed").into());
- }
-
- // Fetch Iframely and Pictrs cached image
- let data_url = data.url.as_ref();
- let (iframely_title, iframely_description, iframely_html, pictrs_thumbnail) =
- fetch_iframely_and_pictrs_data(context.client(), data_url).await;
-
- let post_form = PostForm {
- name: data.name.trim().to_owned(),
- url: data_url.map(|u| u.to_owned().into()),
- body: data.body.to_owned(),
- nsfw: data.nsfw,
- creator_id: orig_post.creator_id.to_owned(),
- community_id: orig_post.community_id,
- removed: Some(orig_post.removed),
- deleted: Some(orig_post.deleted),
- locked: Some(orig_post.locked),
- stickied: Some(orig_post.stickied),
- updated: Some(naive_now()),
- embed_title: iframely_title,
- embed_description: iframely_description,
- embed_html: iframely_html,
- thumbnail_url: pictrs_thumbnail.map(|u| u.into()),
- ap_id: Some(orig_post.ap_id),
- local: orig_post.local,
- published: None,
- };
-
- let post_id = data.post_id;
- let res = blocking(context.pool(), move |conn| {
- Post::update(conn, post_id, &post_form)
- })
- .await?;
- let updated_post: Post = match res {
- Ok(post) => post,
- Err(e) => {
- let err_type = if e.to_string() == "value too long for type character varying(200)" {
- "post_title_too_long"
- } else {
- "couldnt_update_post"
- };
-
- return Err(ApiError::err(err_type).into());
- }
- };
-
- // Send apub update
- updated_post
- .send_update(&local_user_view.person, context)
- .await?;
-
- let post_id = data.post_id;
- let post_view = blocking(context.pool(), move |conn| {
- PostView::read(conn, post_id, Some(local_user_view.person.id))
- })
- .await??;
-
- let res = PostResponse { post_view };
-
- context.chat_server().do_send(SendPost {
- op: UserOperation::EditPost,
- post: res.clone(),
- websocket_id,
- });
-
- Ok(res)
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for DeletePost {
- type Response = PostResponse;
-
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- websocket_id: Option<ConnectionId>,
- ) -> Result<PostResponse, LemmyError> {
- let data: &DeletePost = &self;
- let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
-
- let post_id = data.post_id;
- let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
-
- check_community_ban(
- local_user_view.person.id,
- orig_post.community_id,
- context.pool(),
- )
- .await?;
-
- // Verify that only the creator can delete
- if !Post::is_post_creator(local_user_view.person.id, orig_post.creator_id) {
- return Err(ApiError::err("no_post_edit_allowed").into());
- }
-
- // Update the post
- let post_id = data.post_id;
- let deleted = data.deleted;
- let updated_post = blocking(context.pool(), move |conn| {
- Post::update_deleted(conn, post_id, deleted)
- })
- .await??;
-
- // apub updates
- if deleted {
- updated_post
- .send_delete(&local_user_view.person, context)
- .await?;
- } else {
- updated_post
- .send_undo_delete(&local_user_view.person, context)
- .await?;
- }
-
- // Refetch the post
- let post_id = data.post_id;
- let post_view = blocking(context.pool(), move |conn| {
- PostView::read(conn, post_id, Some(local_user_view.person.id))
- })
- .await??;
-
- let res = PostResponse { post_view };
-
- context.chat_server().do_send(SendPost {
- op: UserOperation::DeletePost,
- post: res.clone(),
- websocket_id,
- });
-
- Ok(res)
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for RemovePost {
- type Response = PostResponse;
-
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- websocket_id: Option<ConnectionId>,
- ) -> Result<PostResponse, LemmyError> {
- let data: &RemovePost = &self;
- let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
-
- let post_id = data.post_id;
- let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
-
- check_community_ban(
- local_user_view.person.id,
- orig_post.community_id,
- context.pool(),
- )
- .await?;
-
- // Verify that only the mods can remove
- is_mod_or_admin(
- context.pool(),
- local_user_view.person.id,
- orig_post.community_id,
- )
- .await?;
-
- // Update the post
- let post_id = data.post_id;
- let removed = data.removed;
- let updated_post = blocking(context.pool(), move |conn| {
- Post::update_removed(conn, post_id, removed)
- })
- .await??;
-
- // Mod tables
- let form = ModRemovePostForm {
- mod_person_id: local_user_view.person.id,
- post_id: data.post_id,
- removed: Some(removed),
- reason: data.reason.to_owned(),
- };
- blocking(context.pool(), move |conn| {
- ModRemovePost::create(conn, &form)
- })
- .await??;
-
- // apub updates
- if removed {
- updated_post
- .send_remove(&local_user_view.person, context)
- .await?;
- } else {
- updated_post
- .send_undo_remove(&local_user_view.person, context)
- .await?;
- }
-
- // Refetch the post
- let post_id = data.post_id;
- let person_id = local_user_view.person.id;
- let post_view = blocking(context.pool(), move |conn| {
- PostView::read(conn, post_id, Some(person_id))
- })
- .await??;
-
- let res = PostResponse { post_view };
-
- context.chat_server().do_send(SendPost {
- op: UserOperation::RemovePost,
- post: res.clone(),
- websocket_id,
- });
-
- Ok(res)
- }
-}
-
#[async_trait::async_trait(?Send)]
impl Perform for LockPost {
type Response = PostResponse;
Ok(PostResponse { post_view })
}
}
-
-/// Creates a post report and notifies the moderators of the community
-#[async_trait::async_trait(?Send)]
-impl Perform for CreatePostReport {
- type Response = CreatePostReportResponse;
-
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- websocket_id: Option<ConnectionId>,
- ) -> Result<CreatePostReportResponse, LemmyError> {
- let data: &CreatePostReport = &self;
- let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
-
- // check size of report and check for whitespace
- let reason = data.reason.trim();
- if reason.is_empty() {
- return Err(ApiError::err("report_reason_required").into());
- }
- if reason.chars().count() > 1000 {
- return Err(ApiError::err("report_too_long").into());
- }
-
- let person_id = local_user_view.person.id;
- let post_id = data.post_id;
- let post_view = blocking(context.pool(), move |conn| {
- PostView::read(&conn, post_id, None)
- })
- .await??;
-
- check_community_ban(person_id, post_view.community.id, context.pool()).await?;
-
- let report_form = PostReportForm {
- creator_id: person_id,
- post_id,
- original_post_name: post_view.post.name,
- original_post_url: post_view.post.url,
- original_post_body: post_view.post.body,
- reason: data.reason.to_owned(),
- };
-
- let report = match blocking(context.pool(), move |conn| {
- PostReport::report(conn, &report_form)
- })
- .await?
- {
- Ok(report) => report,
- Err(_e) => return Err(ApiError::err("couldnt_create_report").into()),
- };
-
- let res = CreatePostReportResponse { success: true };
-
- context.chat_server().do_send(SendUserRoomMessage {
- op: UserOperation::CreatePostReport,
- response: res.clone(),
- local_recipient_id: local_user_view.local_user.id,
- websocket_id,
- });
-
- context.chat_server().do_send(SendModRoomMessage {
- op: UserOperation::CreatePostReport,
- response: report,
- community_id: post_view.community.id,
- websocket_id,
- });
-
- Ok(res)
- }
-}
-
-/// Resolves or unresolves a post report and notifies the moderators of the community
-#[async_trait::async_trait(?Send)]
-impl Perform for ResolvePostReport {
- type Response = ResolvePostReportResponse;
-
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- websocket_id: Option<ConnectionId>,
- ) -> Result<ResolvePostReportResponse, LemmyError> {
- let data: &ResolvePostReport = &self;
- let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
-
- let report_id = data.report_id;
- let report = blocking(context.pool(), move |conn| {
- PostReportView::read(&conn, report_id)
- })
- .await??;
-
- let person_id = local_user_view.person.id;
- is_mod_or_admin(context.pool(), person_id, report.community.id).await?;
-
- let resolved = data.resolved;
- let resolve_fun = move |conn: &'_ _| {
- if resolved {
- PostReport::resolve(conn, report_id, person_id)
- } else {
- PostReport::unresolve(conn, report_id, person_id)
- }
- };
-
- let res = ResolvePostReportResponse {
- report_id,
- resolved: true,
- };
-
- if blocking(context.pool(), resolve_fun).await?.is_err() {
- return Err(ApiError::err("couldnt_resolve_report").into());
- };
-
- context.chat_server().do_send(SendModRoomMessage {
- op: UserOperation::ResolvePostReport,
- response: res.clone(),
- community_id: report.community.id,
- websocket_id,
- });
-
- Ok(res)
- }
-}
-
-/// Lists post reports for a community if an id is supplied
-/// or returns all post reports for communities a user moderates
-#[async_trait::async_trait(?Send)]
-impl Perform for ListPostReports {
- type Response = ListPostReportsResponse;
-
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- websocket_id: Option<ConnectionId>,
- ) -> Result<ListPostReportsResponse, LemmyError> {
- let data: &ListPostReports = &self;
- let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
-
- let person_id = local_user_view.person.id;
- let community_id = data.community;
- let community_ids =
- collect_moderated_communities(person_id, community_id, context.pool()).await?;
-
- let page = data.page;
- let limit = data.limit;
- let posts = blocking(context.pool(), move |conn| {
- PostReportQueryBuilder::create(conn)
- .community_ids(community_ids)
- .page(page)
- .limit(limit)
- .list()
- })
- .await??;
-
- let res = ListPostReportsResponse { posts };
-
- context.chat_server().do_send(SendUserRoomMessage {
- op: UserOperation::ListPostReports,
- response: res.clone(),
- local_recipient_id: local_user_view.local_user.id,
- websocket_id,
- });
-
- Ok(res)
- }
-}