-use crate::Perform;
-use actix_web::web::Data;
+use activitypub_federation::config::Data;
+use actix_web::web::Json;
use lemmy_api_common::{
build_response::build_comment_response,
comment::{CommentResponse, CreateCommentLike},
context::LemmyContext,
+ send_activity::{ActivityChannel, SendActivityData},
utils::{check_community_ban, check_downvotes_enabled, local_user_view_from_jwt},
};
use lemmy_db_schema::{
};
use lemmy_db_views::structs::{CommentView, LocalUserView};
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
+use std::ops::Deref;
-#[async_trait::async_trait(?Send)]
-impl Perform for CreateCommentLike {
- type Response = CommentResponse;
+#[tracing::instrument(skip(context))]
+pub async fn like_comment(
+ data: Json<CreateCommentLike>,
+ context: Data<LemmyContext>,
+) -> Result<Json<CommentResponse>, LemmyError> {
+ let local_site = LocalSite::read(&mut context.pool()).await?;
+ let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
- #[tracing::instrument(skip(context))]
- async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommentResponse, LemmyError> {
- let data: &CreateCommentLike = self;
- let local_site = LocalSite::read(&mut context.pool()).await?;
- let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
+ let mut recipient_ids = Vec::<LocalUserId>::new();
- let mut recipient_ids = Vec::<LocalUserId>::new();
+ // Don't do a downvote if site has downvotes disabled
+ check_downvotes_enabled(data.score, &local_site)?;
- // Don't do a downvote if site has downvotes disabled
- check_downvotes_enabled(data.score, &local_site)?;
+ let comment_id = data.comment_id;
+ let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
- let comment_id = data.comment_id;
- let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
+ check_community_ban(
+ local_user_view.person.id,
+ orig_comment.community.id,
+ &mut context.pool(),
+ )
+ .await?;
- check_community_ban(
- local_user_view.person.id,
- orig_comment.community.id,
- &mut context.pool(),
- )
- .await?;
-
- // Add parent poster or commenter to recipients
- let comment_reply = CommentReply::read_by_comment(&mut context.pool(), comment_id).await;
- if let Ok(reply) = comment_reply {
- let recipient_id = reply.recipient_id;
- if let Ok(local_recipient) =
- LocalUserView::read_person(&mut context.pool(), recipient_id).await
- {
- recipient_ids.push(local_recipient.local_user.id);
- }
+ // Add parent poster or commenter to recipients
+ let comment_reply = CommentReply::read_by_comment(&mut context.pool(), comment_id).await;
+ if let Ok(reply) = comment_reply {
+ let recipient_id = reply.recipient_id;
+ if let Ok(local_recipient) = LocalUserView::read_person(&mut context.pool(), recipient_id).await
+ {
+ recipient_ids.push(local_recipient.local_user.id);
}
+ }
- let like_form = CommentLikeForm {
- comment_id: data.comment_id,
- post_id: orig_comment.post.id,
- person_id: local_user_view.person.id,
- score: data.score,
- };
+ let like_form = CommentLikeForm {
+ comment_id: data.comment_id,
+ post_id: orig_comment.post.id,
+ person_id: local_user_view.person.id,
+ score: data.score,
+ };
- // Remove any likes first
- let person_id = local_user_view.person.id;
+ // Remove any likes first
+ let person_id = local_user_view.person.id;
- CommentLike::remove(&mut context.pool(), person_id, comment_id).await?;
+ CommentLike::remove(&mut context.pool(), person_id, comment_id).await?;
- // Only add the like if the score isnt 0
- let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
- if do_add {
- CommentLike::like(&mut context.pool(), &like_form)
- .await
- .with_lemmy_type(LemmyErrorType::CouldntLikeComment)?;
- }
+ // Only add the like if the score isnt 0
+ let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
+ if do_add {
+ CommentLike::like(&mut context.pool(), &like_form)
+ .await
+ .with_lemmy_type(LemmyErrorType::CouldntLikeComment)?;
+ }
+
+ ActivityChannel::submit_activity(
+ SendActivityData::LikePostOrComment(
+ orig_comment.comment.ap_id,
+ local_user_view.person.clone(),
+ orig_comment.community,
+ data.score,
+ ),
+ &context,
+ )
+ .await?;
+ Ok(Json(
build_comment_response(
- context,
+ context.deref(),
comment_id,
Some(local_user_view),
None,
recipient_ids,
)
- .await
- }
+ .await?,
+ ))
}
-use crate::Perform;
-use actix_web::web::Data;
+use activitypub_federation::config::Data;
+use actix_web::web::Json;
use lemmy_api_common::{
build_response::build_post_response,
context::LemmyContext,
post::{CreatePostLike, PostResponse},
+ send_activity::{ActivityChannel, SendActivityData},
utils::{
check_community_ban,
check_community_deleted_or_removed,
};
use lemmy_db_schema::{
source::{
+ community::Community,
local_site::LocalSite,
post::{Post, PostLike, PostLikeForm},
},
traits::{Crud, Likeable},
};
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
+use std::ops::Deref;
-#[async_trait::async_trait(?Send)]
-impl Perform for CreatePostLike {
- type Response = PostResponse;
+#[tracing::instrument(skip(context))]
+pub async fn like_post(
+ data: Json<CreatePostLike>,
+ context: Data<LemmyContext>,
+) -> Result<Json<PostResponse>, LemmyError> {
+ let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
+ let local_site = LocalSite::read(&mut context.pool()).await?;
- #[tracing::instrument(skip(context))]
- async fn perform(&self, context: &Data<LemmyContext>) -> Result<PostResponse, LemmyError> {
- let data: &CreatePostLike = self;
- let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
- let local_site = LocalSite::read(&mut context.pool()).await?;
+ // Don't do a downvote if site has downvotes disabled
+ check_downvotes_enabled(data.score, &local_site)?;
- // Don't do a downvote if site has downvotes disabled
- check_downvotes_enabled(data.score, &local_site)?;
+ // Check for a community ban
+ let post_id = data.post_id;
+ let post = Post::read(&mut context.pool(), post_id).await?;
- // Check for a community ban
- let post_id = data.post_id;
- let post = Post::read(&mut context.pool(), post_id).await?;
+ check_community_ban(
+ local_user_view.person.id,
+ post.community_id,
+ &mut context.pool(),
+ )
+ .await?;
+ check_community_deleted_or_removed(post.community_id, &mut context.pool()).await?;
- check_community_ban(
- local_user_view.person.id,
- post.community_id,
- &mut context.pool(),
- )
- .await?;
- check_community_deleted_or_removed(post.community_id, &mut context.pool()).await?;
+ let like_form = PostLikeForm {
+ post_id: data.post_id,
+ person_id: local_user_view.person.id,
+ score: data.score,
+ };
- let like_form = PostLikeForm {
- post_id: data.post_id,
- person_id: local_user_view.person.id,
- score: data.score,
- };
+ // Remove any likes first
+ let person_id = local_user_view.person.id;
- // Remove any likes first
- let person_id = local_user_view.person.id;
+ PostLike::remove(&mut context.pool(), person_id, post_id).await?;
- PostLike::remove(&mut context.pool(), person_id, post_id).await?;
+ // Only add the like if the score isnt 0
+ let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
+ if do_add {
+ PostLike::like(&mut context.pool(), &like_form)
+ .await
+ .with_lemmy_type(LemmyErrorType::CouldntLikePost)?;
+ }
- // Only add the like if the score isnt 0
- let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
- if do_add {
- PostLike::like(&mut context.pool(), &like_form)
- .await
- .with_lemmy_type(LemmyErrorType::CouldntLikePost)?;
- }
+ // Mark the post as read
+ mark_post_as_read(person_id, post_id, &mut context.pool()).await?;
- // Mark the post as read
- mark_post_as_read(person_id, post_id, &mut context.pool()).await?;
+ ActivityChannel::submit_activity(
+ SendActivityData::LikePostOrComment(
+ post.ap_id,
+ local_user_view.person.clone(),
+ Community::read(&mut context.pool(), post.community_id).await?,
+ data.score,
+ ),
+ &context,
+ )
+ .await?;
+ Ok(Json(
build_post_response(
- context,
+ context.deref(),
post.community_id,
local_user_view.person.id,
post_id,
)
- .await
- }
+ .await?,
+ ))
}
-mod feature;
-mod get_link_metadata;
-mod like;
-mod lock;
-mod mark_read;
-mod save;
+pub mod feature;
+pub mod get_link_metadata;
+pub mod like;
+pub mod lock;
+pub mod mark_read;
+pub mod save;
use crate::context::LemmyContext;
use activitypub_federation::config::Data;
use futures::future::BoxFuture;
-use lemmy_db_schema::source::{comment::Comment, post::Post};
+use lemmy_db_schema::{
+ newtypes::DbUrl,
+ source::{comment::Comment, community::Community, person::Person, post::Post},
+};
use lemmy_utils::{error::LemmyResult, SYNCHRONOUS_FEDERATION};
use once_cell::sync::{Lazy, OnceCell};
use tokio::{
#[derive(Debug)]
pub enum SendActivityData {
CreatePost(Post),
+ UpdatePost(Post),
CreateComment(Comment),
+ DeleteComment(Comment, Person, Community),
+ RemoveComment(Comment, Person, Community, Option<String>),
+ UpdateComment(Comment),
+ LikePostOrComment(DbUrl, Person, Community, i16),
}
// TODO: instead of static, move this into LemmyContext. make sure that stopping the process with
validation::is_valid_body_field,
},
};
-use std::ops::Deref;
const MAX_COMMENT_DEPTH_LIMIT: usize = 100;
Ok(Json(
build_comment_response(
- context.deref(),
+ &context,
inserted_comment.id,
Some(local_user_view),
data.form_id.clone(),
-use crate::PerformCrud;
-use actix_web::web::Data;
+use activitypub_federation::config::Data;
+use actix_web::web::Json;
use lemmy_api_common::{
build_response::{build_comment_response, send_local_notifs},
comment::{CommentResponse, DeleteComment},
context::LemmyContext,
+ send_activity::{ActivityChannel, SendActivityData},
utils::{check_community_ban, local_user_view_from_jwt},
};
use lemmy_db_schema::{
};
use lemmy_db_views::structs::CommentView;
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
-use std::ops::Deref;
-#[async_trait::async_trait(?Send)]
-impl PerformCrud for DeleteComment {
- type Response = CommentResponse;
+#[tracing::instrument(skip(context))]
+pub async fn delete_comment(
+ data: Json<DeleteComment>,
+ context: Data<LemmyContext>,
+) -> Result<Json<CommentResponse>, LemmyError> {
+ let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
- #[tracing::instrument(skip(context))]
- async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommentResponse, LemmyError> {
- let data: &DeleteComment = self;
- let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
+ let comment_id = data.comment_id;
+ let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
- let comment_id = data.comment_id;
- let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
+ // Dont delete it if its already been deleted.
+ if orig_comment.comment.deleted == data.deleted {
+ return Err(LemmyErrorType::CouldntUpdateComment)?;
+ }
- // Dont delete it if its already been deleted.
- if orig_comment.comment.deleted == data.deleted {
- return Err(LemmyErrorType::CouldntUpdateComment)?;
- }
+ check_community_ban(
+ local_user_view.person.id,
+ orig_comment.community.id,
+ &mut context.pool(),
+ )
+ .await?;
- check_community_ban(
- local_user_view.person.id,
- orig_comment.community.id,
- &mut context.pool(),
- )
- .await?;
+ // Verify that only the creator can delete
+ if local_user_view.person.id != orig_comment.creator.id {
+ return Err(LemmyErrorType::NoCommentEditAllowed)?;
+ }
- // Verify that only the creator can delete
- if local_user_view.person.id != orig_comment.creator.id {
- return Err(LemmyErrorType::NoCommentEditAllowed)?;
- }
+ // Do the delete
+ let deleted = data.deleted;
+ let updated_comment = Comment::update(
+ &mut context.pool(),
+ comment_id,
+ &CommentUpdateForm::builder().deleted(Some(deleted)).build(),
+ )
+ .await
+ .with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
- // Do the delete
- let deleted = data.deleted;
- let updated_comment = Comment::update(
- &mut context.pool(),
- comment_id,
- &CommentUpdateForm::builder().deleted(Some(deleted)).build(),
- )
- .await
- .with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
+ let post_id = updated_comment.post_id;
+ let post = Post::read(&mut context.pool(), post_id).await?;
+ let recipient_ids = send_local_notifs(
+ vec![],
+ &updated_comment,
+ &local_user_view.person,
+ &post,
+ false,
+ &context,
+ )
+ .await?;
+ let updated_comment_id = updated_comment.id;
- let post_id = updated_comment.post_id;
- let post = Post::read(&mut context.pool(), post_id).await?;
- let recipient_ids = send_local_notifs(
- vec![],
- &updated_comment,
- &local_user_view.person,
- &post,
- false,
- context,
- )
- .await?;
+ ActivityChannel::submit_activity(
+ SendActivityData::DeleteComment(
+ updated_comment,
+ local_user_view.person.clone(),
+ orig_comment.community,
+ ),
+ &context,
+ )
+ .await?;
+ Ok(Json(
build_comment_response(
- context.deref(),
- updated_comment.id,
+ &context,
+ updated_comment_id,
Some(local_user_view),
None,
recipient_ids,
)
- .await
- }
+ .await?,
+ ))
}
};
use lemmy_db_schema::source::local_site::LocalSite;
use lemmy_utils::error::LemmyError;
-use std::ops::Deref;
#[tracing::instrument(skip(context))]
pub async fn get_comment(
check_private_instance(&local_user_view, &local_site)?;
Ok(Json(
- build_comment_response(context.deref(), data.id, local_user_view, None, vec![]).await?,
+ build_comment_response(&context, data.id, local_user_view, None, vec![]).await?,
))
}
-use crate::PerformCrud;
-use actix_web::web::Data;
+use activitypub_federation::config::Data;
+use actix_web::web::Json;
use lemmy_api_common::{
build_response::{build_comment_response, send_local_notifs},
comment::{CommentResponse, RemoveComment},
context::LemmyContext,
+ send_activity::{ActivityChannel, SendActivityData},
utils::{check_community_ban, is_mod_or_admin, local_user_view_from_jwt},
};
use lemmy_db_schema::{
};
use lemmy_db_views::structs::CommentView;
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
-use std::ops::Deref;
-#[async_trait::async_trait(?Send)]
-impl PerformCrud for RemoveComment {
- type Response = CommentResponse;
+#[tracing::instrument(skip(context))]
+pub async fn remove_comment(
+ data: Json<RemoveComment>,
+ context: Data<LemmyContext>,
+) -> Result<Json<CommentResponse>, LemmyError> {
+ let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
- #[tracing::instrument(skip(context))]
- async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommentResponse, LemmyError> {
- let data: &RemoveComment = self;
- let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
+ let comment_id = data.comment_id;
+ let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
- let comment_id = data.comment_id;
- let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
+ check_community_ban(
+ local_user_view.person.id,
+ orig_comment.community.id,
+ &mut context.pool(),
+ )
+ .await?;
- check_community_ban(
- local_user_view.person.id,
- orig_comment.community.id,
- &mut context.pool(),
- )
- .await?;
+ // Verify that only a mod or admin can remove
+ is_mod_or_admin(
+ &mut context.pool(),
+ local_user_view.person.id,
+ orig_comment.community.id,
+ )
+ .await?;
- // Verify that only a mod or admin can remove
- is_mod_or_admin(
- &mut context.pool(),
- local_user_view.person.id,
- orig_comment.community.id,
- )
- .await?;
+ // Do the remove
+ let removed = data.removed;
+ let updated_comment = Comment::update(
+ &mut context.pool(),
+ comment_id,
+ &CommentUpdateForm::builder().removed(Some(removed)).build(),
+ )
+ .await
+ .with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
- // Do the remove
- let removed = data.removed;
- let updated_comment = Comment::update(
- &mut context.pool(),
- comment_id,
- &CommentUpdateForm::builder().removed(Some(removed)).build(),
- )
- .await
- .with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
+ // Mod tables
+ let form = ModRemoveCommentForm {
+ mod_person_id: local_user_view.person.id,
+ comment_id: data.comment_id,
+ removed: Some(removed),
+ reason: data.reason.clone(),
+ };
+ ModRemoveComment::create(&mut context.pool(), &form).await?;
- // Mod tables
- let form = ModRemoveCommentForm {
- mod_person_id: local_user_view.person.id,
- comment_id: data.comment_id,
- removed: Some(removed),
- reason: data.reason.clone(),
- };
- ModRemoveComment::create(&mut context.pool(), &form).await?;
+ let post_id = updated_comment.post_id;
+ let post = Post::read(&mut context.pool(), post_id).await?;
+ let recipient_ids = send_local_notifs(
+ vec![],
+ &updated_comment,
+ &local_user_view.person.clone(),
+ &post,
+ false,
+ &context,
+ )
+ .await?;
+ let updated_comment_id = updated_comment.id;
- let post_id = updated_comment.post_id;
- let post = Post::read(&mut context.pool(), post_id).await?;
- let recipient_ids = send_local_notifs(
- vec![],
- &updated_comment,
- &local_user_view.person.clone(),
- &post,
- false,
- context,
- )
- .await?;
+ ActivityChannel::submit_activity(
+ SendActivityData::RemoveComment(
+ updated_comment,
+ local_user_view.person.clone(),
+ orig_comment.community,
+ data.reason.clone(),
+ ),
+ &context,
+ )
+ .await?;
+ Ok(Json(
build_comment_response(
- context.deref(),
- updated_comment.id,
+ &context,
+ updated_comment_id,
Some(local_user_view),
None,
recipient_ids,
)
- .await
- }
+ .await?,
+ ))
}
-use crate::PerformCrud;
-use actix_web::web::Data;
+use activitypub_federation::config::Data;
+use actix_web::web::Json;
use lemmy_api_common::{
build_response::{build_comment_response, send_local_notifs},
comment::{CommentResponse, EditComment},
context::LemmyContext,
+ send_activity::{ActivityChannel, SendActivityData},
utils::{
check_community_ban,
local_site_to_slur_regex,
validation::is_valid_body_field,
},
};
-use std::ops::Deref;
-#[async_trait::async_trait(?Send)]
-impl PerformCrud for EditComment {
- type Response = CommentResponse;
+#[tracing::instrument(skip(context))]
+pub async fn update_comment(
+ data: Json<EditComment>,
+ context: Data<LemmyContext>,
+) -> Result<Json<CommentResponse>, LemmyError> {
+ let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
+ let local_site = LocalSite::read(&mut context.pool()).await?;
- #[tracing::instrument(skip(context))]
- async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommentResponse, LemmyError> {
- let data: &EditComment = self;
- let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
- let local_site = LocalSite::read(&mut context.pool()).await?;
+ let comment_id = data.comment_id;
+ let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
- let comment_id = data.comment_id;
- let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
+ check_community_ban(
+ local_user_view.person.id,
+ orig_comment.community.id,
+ &mut context.pool(),
+ )
+ .await?;
- check_community_ban(
- local_user_view.person.id,
- orig_comment.community.id,
- &mut context.pool(),
- )
- .await?;
+ // Verify that only the creator can edit
+ if local_user_view.person.id != orig_comment.creator.id {
+ return Err(LemmyErrorType::NoCommentEditAllowed)?;
+ }
- // Verify that only the creator can edit
- if local_user_view.person.id != orig_comment.creator.id {
- return Err(LemmyErrorType::NoCommentEditAllowed)?;
- }
+ let language_id = data.language_id;
+ CommunityLanguage::is_allowed_community_language(
+ &mut context.pool(),
+ language_id,
+ orig_comment.community.id,
+ )
+ .await?;
- let language_id = self.language_id;
- CommunityLanguage::is_allowed_community_language(
- &mut context.pool(),
- language_id,
- orig_comment.community.id,
- )
- .await?;
+ // Update the Content
+ let content = data
+ .content
+ .as_ref()
+ .map(|c| remove_slurs(c, &local_site_to_slur_regex(&local_site)));
+ is_valid_body_field(&content, false)?;
+ let content = sanitize_html_opt(&content);
- // Update the Content
- let content = data
- .content
- .as_ref()
- .map(|c| remove_slurs(c, &local_site_to_slur_regex(&local_site)));
- is_valid_body_field(&content, false)?;
- let content = sanitize_html_opt(&content);
+ let comment_id = data.comment_id;
+ let form = CommentUpdateForm::builder()
+ .content(content)
+ .language_id(data.language_id)
+ .updated(Some(Some(naive_now())))
+ .build();
+ let updated_comment = Comment::update(&mut context.pool(), comment_id, &form)
+ .await
+ .with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
- let comment_id = data.comment_id;
- let form = CommentUpdateForm::builder()
- .content(content)
- .language_id(data.language_id)
- .updated(Some(Some(naive_now())))
- .build();
- let updated_comment = Comment::update(&mut context.pool(), comment_id, &form)
- .await
- .with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
+ // Do the mentions / recipients
+ let updated_comment_content = updated_comment.content.clone();
+ let mentions = scrape_text_for_mentions(&updated_comment_content);
+ let recipient_ids = send_local_notifs(
+ mentions,
+ &updated_comment,
+ &local_user_view.person,
+ &orig_comment.post,
+ false,
+ &context,
+ )
+ .await?;
- // Do the mentions / recipients
- let updated_comment_content = updated_comment.content.clone();
- let mentions = scrape_text_for_mentions(&updated_comment_content);
- let recipient_ids = send_local_notifs(
- mentions,
- &updated_comment,
- &local_user_view.person,
- &orig_comment.post,
- false,
- context,
- )
- .await?;
+ ActivityChannel::submit_activity(
+ SendActivityData::UpdateComment(updated_comment.clone()),
+ &context,
+ )
+ .await?;
+ Ok(Json(
build_comment_response(
- context.deref(),
+ &context,
updated_comment.id,
Some(local_user_view),
- self.form_id.clone(),
+ data.form_id.clone(),
recipient_ids,
)
- .await
- }
+ .await?,
+ ))
}
-use crate::PerformCrud;
-use actix_web::web::Data;
+use activitypub_federation::config::Data;
+use actix_web::web::Json;
use lemmy_api_common::{
build_response::build_post_response,
context::LemmyContext,
post::{EditPost, PostResponse},
request::fetch_site_data,
+ send_activity::{ActivityChannel, SendActivityData},
utils::{
check_community_ban,
local_site_to_slur_regex,
validation::{check_url_scheme, clean_url_params, is_valid_body_field, is_valid_post_title},
},
};
+use std::ops::Deref;
-#[async_trait::async_trait(?Send)]
-impl PerformCrud for EditPost {
- type Response = PostResponse;
+#[tracing::instrument(skip(context))]
+pub async fn update_post(
+ data: Json<EditPost>,
+ context: Data<LemmyContext>,
+) -> Result<Json<PostResponse>, LemmyError> {
+ let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
+ let local_site = LocalSite::read(&mut context.pool()).await?;
- #[tracing::instrument(skip(context))]
- async fn perform(&self, context: &Data<LemmyContext>) -> Result<PostResponse, LemmyError> {
- let data: &EditPost = self;
- let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
- let local_site = LocalSite::read(&mut context.pool()).await?;
+ let data_url = data.url.as_ref();
- let data_url = data.url.as_ref();
+ // TODO No good way to handle a clear.
+ // Issue link: https://github.com/LemmyNet/lemmy/issues/2287
+ let url = Some(data_url.map(clean_url_params).map(Into::into));
- // TODO No good way to handle a clear.
- // Issue link: https://github.com/LemmyNet/lemmy/issues/2287
- let url = Some(data_url.map(clean_url_params).map(Into::into));
+ let slur_regex = local_site_to_slur_regex(&local_site);
+ check_slurs_opt(&data.name, &slur_regex)?;
+ check_slurs_opt(&data.body, &slur_regex)?;
- let slur_regex = local_site_to_slur_regex(&local_site);
- check_slurs_opt(&data.name, &slur_regex)?;
- check_slurs_opt(&data.body, &slur_regex)?;
+ if let Some(name) = &data.name {
+ is_valid_post_title(name)?;
+ }
- if let Some(name) = &data.name {
- is_valid_post_title(name)?;
- }
+ is_valid_body_field(&data.body, true)?;
+ check_url_scheme(&data.url)?;
- is_valid_body_field(&data.body, true)?;
- check_url_scheme(&data.url)?;
+ let post_id = data.post_id;
+ let orig_post = Post::read(&mut context.pool(), post_id).await?;
- let post_id = data.post_id;
- let orig_post = Post::read(&mut context.pool(), post_id).await?;
+ check_community_ban(
+ local_user_view.person.id,
+ orig_post.community_id,
+ &mut context.pool(),
+ )
+ .await?;
- check_community_ban(
- local_user_view.person.id,
- orig_post.community_id,
- &mut 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(LemmyErrorType::NoPostEditAllowed)?;
- }
-
- // Fetch post links and Pictrs cached image
- let data_url = data.url.as_ref();
- let (metadata_res, thumbnail_url) =
- fetch_site_data(context.client(), context.settings(), data_url, true).await;
- let (embed_title, embed_description, embed_video_url) = metadata_res
- .map(|u| (Some(u.title), Some(u.description), Some(u.embed_video_url)))
- .unwrap_or_default();
-
- let name = sanitize_html_opt(&data.name);
- let body = sanitize_html_opt(&data.body);
- let body = diesel_option_overwrite(body);
- let embed_title = embed_title.map(|e| sanitize_html_opt(&e));
- let embed_description = embed_description.map(|e| sanitize_html_opt(&e));
-
- let language_id = self.language_id;
- CommunityLanguage::is_allowed_community_language(
- &mut context.pool(),
- language_id,
- orig_post.community_id,
- )
- .await?;
-
- let post_form = PostUpdateForm::builder()
- .name(name)
- .url(url)
- .body(body)
- .nsfw(data.nsfw)
- .embed_title(embed_title)
- .embed_description(embed_description)
- .embed_video_url(embed_video_url)
- .language_id(data.language_id)
- .thumbnail_url(Some(thumbnail_url))
- .updated(Some(Some(naive_now())))
- .build();
-
- let post_id = data.post_id;
- Post::update(&mut context.pool(), post_id, &post_form)
- .await
- .with_lemmy_type(LemmyErrorType::CouldntUpdatePost)?;
+ // Verify that only the creator can edit
+ if !Post::is_post_creator(local_user_view.person.id, orig_post.creator_id) {
+ return Err(LemmyErrorType::NoPostEditAllowed)?;
+ }
+
+ // Fetch post links and Pictrs cached image
+ let data_url = data.url.as_ref();
+ let (metadata_res, thumbnail_url) =
+ fetch_site_data(context.client(), context.settings(), data_url, true).await;
+ let (embed_title, embed_description, embed_video_url) = metadata_res
+ .map(|u| (Some(u.title), Some(u.description), Some(u.embed_video_url)))
+ .unwrap_or_default();
+
+ let name = sanitize_html_opt(&data.name);
+ let body = sanitize_html_opt(&data.body);
+ let body = diesel_option_overwrite(body);
+ let embed_title = embed_title.map(|e| sanitize_html_opt(&e));
+ let embed_description = embed_description.map(|e| sanitize_html_opt(&e));
+
+ let language_id = data.language_id;
+ CommunityLanguage::is_allowed_community_language(
+ &mut context.pool(),
+ language_id,
+ orig_post.community_id,
+ )
+ .await?;
+
+ let post_form = PostUpdateForm::builder()
+ .name(name)
+ .url(url)
+ .body(body)
+ .nsfw(data.nsfw)
+ .embed_title(embed_title)
+ .embed_description(embed_description)
+ .embed_video_url(embed_video_url)
+ .language_id(data.language_id)
+ .thumbnail_url(Some(thumbnail_url))
+ .updated(Some(Some(naive_now())))
+ .build();
+
+ let post_id = data.post_id;
+ let updated_post = Post::update(&mut context.pool(), post_id, &post_form)
+ .await
+ .with_lemmy_type(LemmyErrorType::CouldntUpdatePost)?;
+ ActivityChannel::submit_activity(SendActivityData::UpdatePost(updated_post), &context).await?;
+
+ Ok(Json(
build_post_response(
- context,
+ context.deref(),
orig_post.community_id,
local_user_view.person.id,
post_id,
)
- .await
- }
+ .await?,
+ ))
}
activities::{create_or_update::note::CreateOrUpdateNote, CreateOrUpdateType},
InCommunity,
},
- SendActivity,
};
use activitypub_federation::{
config::Data,
};
use lemmy_api_common::{
build_response::send_local_notifs,
- comment::{CommentResponse, EditComment},
context::LemmyContext,
utils::{check_post_deleted_or_removed, is_mod_or_admin},
};
use lemmy_utils::{error::LemmyError, utils::mention::scrape_text_for_mentions};
use url::Url;
-#[async_trait::async_trait]
-impl SendActivity for EditComment {
- type Response = CommentResponse;
-
- async fn send_activity(
- _request: &Self,
- response: &Self::Response,
- context: &Data<LemmyContext>,
- ) -> Result<(), LemmyError> {
- CreateOrUpdateNote::send(
- response.comment_view.comment.clone(),
- response.comment_view.creator.id,
- CreateOrUpdateType::Update,
- context.reset_request_count(),
- )
- .await
- }
-}
-
impl CreateOrUpdateNote {
#[tracing::instrument(skip(comment, person_id, kind, context))]
pub(crate) async fn send(
activities::{create_or_update::page::CreateOrUpdatePage, CreateOrUpdateType},
InCommunity,
},
- SendActivity,
};
use activitypub_federation::{
config::Data,
protocol::verification::{verify_domains_match, verify_urls_match},
traits::{ActivityHandler, Actor, Object},
};
-use lemmy_api_common::{
- context::LemmyContext,
- post::{EditPost, PostResponse},
-};
+use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::{
aggregates::structs::PostAggregates,
newtypes::PersonId,
use lemmy_utils::error::{LemmyError, LemmyErrorType};
use url::Url;
-#[async_trait::async_trait]
-impl SendActivity for EditPost {
- type Response = PostResponse;
-
- async fn send_activity(
- _request: &Self,
- response: &Self::Response,
- context: &Data<LemmyContext>,
- ) -> Result<(), LemmyError> {
- CreateOrUpdatePage::send(
- response.post_view.post.clone(),
- response.post_view.creator.id,
- CreateOrUpdateType::Update,
- context.reset_request_count(),
- )
- .await
- }
-}
-
impl CreateOrUpdatePage {
pub(crate) async fn new(
post: ApubPost,
traits::{Actor, Object},
};
use lemmy_api_common::{
- comment::{CommentResponse, DeleteComment, RemoveComment},
community::{CommunityResponse, DeleteCommunity, RemoveCommunity},
context::LemmyContext,
post::{DeletePost, PostResponse, RemovePost},
}
}
-#[async_trait::async_trait]
-impl SendActivity for DeleteComment {
- type Response = CommentResponse;
-
- async fn send_activity(
- request: &Self,
- response: &Self::Response,
- context: &Data<LemmyContext>,
- ) -> Result<(), LemmyError> {
- let community_id = response.comment_view.community.id;
- let community = Community::read(&mut context.pool(), community_id).await?;
- let person = Person::read(&mut context.pool(), response.comment_view.creator.id).await?;
- let deletable = DeletableObjects::Comment(response.comment_view.comment.clone().into());
- send_apub_delete_in_community(person, community, deletable, None, request.deleted, context)
- .await
- }
-}
-
-#[async_trait::async_trait]
-impl SendActivity for RemoveComment {
- type Response = CommentResponse;
-
- async fn send_activity(
- request: &Self,
- response: &Self::Response,
- context: &Data<LemmyContext>,
- ) -> Result<(), LemmyError> {
- let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
- let comment = Comment::read(&mut context.pool(), request.comment_id).await?;
- let community =
- Community::read(&mut context.pool(), response.comment_view.community.id).await?;
- let deletable = DeletableObjects::Comment(comment.into());
- send_apub_delete_in_community(
- local_user_view.person,
- community,
- deletable,
- request.reason.clone().or_else(|| Some(String::new())),
- request.removed,
- context,
- )
- .await
- }
-}
-
#[async_trait::async_trait]
impl SendActivity for DeletePrivateMessage {
type Response = PrivateMessageResponse;
/// Parameter `reason` being set indicates that this is a removal by a mod. If its unset, this
/// action was done by a normal user.
#[tracing::instrument(skip_all)]
-async fn send_apub_delete_in_community(
+pub(crate) async fn send_apub_delete_in_community(
actor: Person,
community: Community,
object: DeletableObjects,
use crate::{
+ activities::{
+ deletion::{send_apub_delete_in_community, DeletableObjects},
+ voting::send_like_activity,
+ },
objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::{
create_or_update::{note::CreateOrUpdateNote, page::CreateOrUpdatePage},
) -> LemmyResult<()> {
let context = context.reset_request_count();
let fed_task = async {
+ use SendActivityData::*;
match data {
- SendActivityData::CreatePost(post) => {
+ CreatePost(post) | UpdatePost(post) => {
let creator_id = post.creator_id;
CreateOrUpdatePage::send(post, creator_id, CreateOrUpdateType::Create, context).await
}
- SendActivityData::CreateComment(comment) => {
+ CreateComment(comment) | UpdateComment(comment) => {
let creator_id = comment.creator_id;
CreateOrUpdateNote::send(comment, creator_id, CreateOrUpdateType::Create, context).await
}
+ DeleteComment(comment, actor, community) => {
+ let is_deleted = comment.deleted;
+ let deletable = DeletableObjects::Comment(comment.into());
+ send_apub_delete_in_community(actor, community, deletable, None, is_deleted, &context).await
+ }
+ RemoveComment(comment, actor, community, reason) => {
+ let is_removed = comment.removed;
+ let deletable = DeletableObjects::Comment(comment.into());
+ send_apub_delete_in_community(actor, community, deletable, reason, is_removed, &context)
+ .await
+ }
+ LikePostOrComment(object_id, person, community, score) => {
+ send_like_activity(object_id, person, community, score, context).await
+ }
}
};
if *SYNCHRONOUS_FEDERATION {
activities::community::send_activity_in_community,
activity_lists::AnnouncableActivities,
fetcher::post_or_comment::PostOrComment,
- objects::{comment::ApubComment, person::ApubPerson, post::ApubPost},
+ objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost},
protocol::activities::voting::{
undo_vote::UndoVote,
vote::{Vote, VoteType},
},
- SendActivity,
};
use activitypub_federation::{config::Data, fetch::object_id::ObjectId};
-use lemmy_api_common::{
- comment::{CommentResponse, CreateCommentLike},
- context::LemmyContext,
- post::{CreatePostLike, PostResponse},
- sensitive::Sensitive,
- utils::local_user_view_from_jwt,
-};
+use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::{
- newtypes::CommunityId,
+ newtypes::DbUrl,
source::{
comment::{CommentLike, CommentLikeForm},
community::Community,
person::Person,
post::{PostLike, PostLikeForm},
},
- traits::{Crud, Likeable},
+ traits::Likeable,
};
use lemmy_utils::error::LemmyError;
pub mod undo_vote;
pub mod vote;
-#[async_trait::async_trait]
-impl SendActivity for CreatePostLike {
- type Response = PostResponse;
-
- async fn send_activity(
- request: &Self,
- response: &Self::Response,
- context: &Data<LemmyContext>,
- ) -> Result<(), LemmyError> {
- let object_id = ObjectId::from(response.post_view.post.ap_id.clone());
- let community_id = response.post_view.community.id;
- send_activity(
- object_id,
- community_id,
- request.score,
- &request.auth,
- context,
- )
- .await
- }
-}
-
-#[async_trait::async_trait]
-impl SendActivity for CreateCommentLike {
- type Response = CommentResponse;
-
- async fn send_activity(
- request: &Self,
- response: &Self::Response,
- context: &Data<LemmyContext>,
- ) -> Result<(), LemmyError> {
- let object_id = ObjectId::from(response.comment_view.comment.ap_id.clone());
- let community_id = response.comment_view.community.id;
- send_activity(
- object_id,
- community_id,
- request.score,
- &request.auth,
- context,
- )
- .await
- }
-}
-
-async fn send_activity(
- object_id: ObjectId<PostOrComment>,
- community_id: CommunityId,
+pub(crate) async fn send_like_activity(
+ object_id: DbUrl,
+ actor: Person,
+ community: Community,
score: i16,
- jwt: &Sensitive<String>,
- context: &Data<LemmyContext>,
+ context: Data<LemmyContext>,
) -> Result<(), LemmyError> {
- let community = Community::read(&mut context.pool(), community_id)
- .await?
- .into();
- let local_user_view = local_user_view_from_jwt(jwt, context).await?;
- let actor = Person::read(&mut context.pool(), local_user_view.person.id)
- .await?
- .into();
+ let object_id: ObjectId<PostOrComment> = object_id.try_into()?;
+ let actor: ApubPerson = actor.into();
+ let community: ApubCommunity = community.into();
// score of 1 means upvote, -1 downvote, 0 undo a previous vote
if score != 0 {
- let vote = Vote::new(object_id, &actor, &community, score.try_into()?, context)?;
+ let vote = Vote::new(object_id, &actor, &community, score.try_into()?, &context)?;
let activity = AnnouncableActivities::Vote(vote);
- send_activity_in_community(activity, &actor, &community, vec![], false, context).await
+ send_activity_in_community(activity, &actor, &community, vec![], false, &context).await
} else {
// Lemmy API doesnt distinguish between Undo/Like and Undo/Dislike, so we hardcode it here.
- let vote = Vote::new(object_id, &actor, &community, VoteType::Like, context)?;
- let undo_vote = UndoVote::new(vote, &actor, &community, context)?;
+ let vote = Vote::new(object_id, &actor, &community, VoteType::Like, &context)?;
+ let undo_vote = UndoVote::new(vote, &actor, &community, &context)?;
let activity = AnnouncableActivities::UndoVote(undo_vote);
- send_activity_in_community(activity, &actor, &community, vec![], false, context).await
+ send_activity_in_community(activity, &actor, &community, vec![], false, &context).await
}
}
use actix_web::{guard, web, Error, HttpResponse, Result};
use lemmy_api::{
- comment::{distinguish::distinguish_comment, save::save_comment},
+ comment::{distinguish::distinguish_comment, like::like_comment, save::save_comment},
comment_report::{list::list_comment_reports, resolve::resolve_comment_report},
local_user::notifications::mark_reply_read::mark_reply_as_read,
+ post::like::like_post,
Perform,
};
use lemmy_api_common::{
- comment::{CreateCommentLike, CreateCommentReport, DeleteComment, EditComment, RemoveComment},
+ comment::CreateCommentReport,
community::{
AddModToCommunity,
BanFromCommunity,
VerifyEmail,
},
post::{
- CreatePostLike,
CreatePostReport,
DeletePost,
- EditPost,
FeaturePost,
GetSiteMetadata,
ListPostReports,
},
};
use lemmy_api_crud::{
- comment::{create::create_comment, read::get_comment},
+ comment::{
+ create::create_comment,
+ delete::delete_comment,
+ read::get_comment,
+ remove::remove_comment,
+ update::update_comment,
+ },
community::list::list_communities,
- post::{create::create_post, read::get_post},
+ post::{create::create_post, read::get_post, update::update_post},
private_message::read::get_private_message,
site::{create::create_site, read::get_site, update::update_site},
PerformCrud,
web::scope("/post")
.wrap(rate_limit.message())
.route("", web::get().to(get_post))
- .route("", web::put().to(route_post_crud::<EditPost>))
+ .route("", web::put().to(update_post))
.route("/delete", web::post().to(route_post_crud::<DeletePost>))
.route("/remove", web::post().to(route_post_crud::<RemovePost>))
.route(
.route("/lock", web::post().to(route_post::<LockPost>))
.route("/feature", web::post().to(route_post::<FeaturePost>))
.route("/list", web::get().to(list_posts))
- .route("/like", web::post().to(route_post::<CreatePostLike>))
+ .route("/like", web::post().to(like_post))
.route("/save", web::put().to(route_post::<SavePost>))
.route("/report", web::post().to(route_post::<CreatePostReport>))
.route(
web::scope("/comment")
.wrap(rate_limit.message())
.route("", web::get().to(get_comment))
- .route("", web::put().to(route_post_crud::<EditComment>))
- .route("/delete", web::post().to(route_post_crud::<DeleteComment>))
- .route("/remove", web::post().to(route_post_crud::<RemoveComment>))
+ .route("", web::put().to(update_comment))
+ .route("/delete", web::post().to(delete_comment))
+ .route("/remove", web::post().to(remove_comment))
.route("/mark_as_read", web::post().to(mark_reply_as_read))
.route("/distinguish", web::post().to(distinguish_comment))
- .route("/like", web::post().to(route_post::<CreateCommentLike>))
+ .route("/like", web::post().to(like_comment))
.route("/save", web::put().to(save_comment))
.route("/list", web::get().to(list_comments))
.route("/report", web::post().to(route_post::<CreateCommentReport>))