4 announce::{AnnouncableActivities, GetCommunity},
9 verify_delete_activity,
17 context::lemmy_context,
18 fetcher::object_id::ObjectId,
19 objects::{community::ApubCommunity, person::ApubPerson},
21 use activitystreams::{
22 activity::kind::DeleteType,
24 primitives::OneOrMany,
29 use lemmy_api_common::blocking;
32 traits::{ActivityFields, ActivityHandler, ActorType},
34 use lemmy_db_schema::{
42 ModRemoveCommunityForm,
50 use lemmy_utils::LemmyError;
51 use lemmy_websocket::{
52 send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message},
56 use serde::{Deserialize, Serialize};
57 use serde_with::skip_serializing_none;
60 /// This is very confusing, because there are four distinct cases to handle:
61 /// - user deletes their post
62 /// - user deletes their comment
63 /// - remote community mod deletes local community
64 /// - remote community deletes itself (triggered by a mod)
66 /// TODO: we should probably change how community deletions work to simplify this. Probably by
67 /// wrapping it in an announce just like other activities, instead of having the community send it.
68 #[skip_serializing_none]
69 #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
70 #[serde(rename_all = "camelCase")]
72 actor: ObjectId<ApubPerson>,
74 pub(in crate::activities::deletion) object: Url,
75 pub(in crate::activities::deletion) cc: Vec<Url>,
76 #[serde(rename = "type")]
78 /// If summary is present, this is a mod action (Remove in Lemmy terms). Otherwise, its a user
79 /// deleting their own content.
80 pub(in crate::activities::deletion) summary: Option<String>,
82 #[serde(rename = "@context")]
83 context: OneOrMany<AnyBase>,
88 #[async_trait::async_trait(?Send)]
89 impl ActivityHandler for Delete {
90 type DataType = LemmyContext;
93 context: &Data<LemmyContext>,
94 request_counter: &mut i32,
95 ) -> Result<(), LemmyError> {
96 verify_is_public(&self.to)?;
97 verify_activity(self, &context.settings())?;
98 let community = self.get_community(context, request_counter).await?;
99 verify_delete_activity(
103 self.summary.is_some(),
113 context: &Data<LemmyContext>,
114 request_counter: &mut i32,
115 ) -> Result<(), LemmyError> {
116 if let Some(reason) = self.summary {
117 // We set reason to empty string if it doesn't exist, to distinguish between delete and
118 // remove. Here we change it back to option, so we don't write it to db.
119 let reason = if reason.is_empty() {
124 receive_remove_action(&self.actor, &self.object, reason, context, request_counter).await
126 receive_delete_action(
130 community: UserOperationCrud::DeleteCommunity,
131 post: UserOperationCrud::DeletePost,
132 comment: UserOperationCrud::DeleteComment,
144 pub(in crate::activities::deletion) fn new(
146 community: &ApubCommunity,
148 summary: Option<String>,
149 context: &LemmyContext,
150 ) -> Result<Delete, LemmyError> {
152 actor: ObjectId::new(actor.actor_id()),
155 cc: vec![community.actor_id()],
156 kind: DeleteType::Delete,
158 id: generate_activity_id(
160 &context.settings().get_protocol_and_hostname(),
162 context: lemmy_context(),
163 unparsed: Default::default(),
166 pub(in crate::activities::deletion) async fn send(
168 community: &ApubCommunity,
170 summary: Option<String>,
171 context: &LemmyContext,
172 ) -> Result<(), LemmyError> {
173 let delete = Delete::new(actor, community, object_id, summary, context)?;
174 let delete_id = delete.id.clone();
176 let activity = AnnouncableActivities::Delete(delete);
177 send_to_community(activity, &delete_id, actor, community, vec![], context).await
181 pub(in crate::activities) async fn receive_remove_action(
182 actor: &ObjectId<ApubPerson>,
184 reason: Option<String>,
185 context: &LemmyContext,
186 request_counter: &mut i32,
187 ) -> Result<(), LemmyError> {
188 let actor = actor.dereference(context, request_counter).await?;
189 use UserOperationCrud::*;
190 match DeletableObjects::read_from_db(object, context).await? {
191 DeletableObjects::Community(community) => {
193 return Err(anyhow!("Only local admin can remove community").into());
195 let form = ModRemoveCommunityForm {
196 mod_person_id: actor.id,
197 community_id: community.id,
202 blocking(context.pool(), move |conn| {
203 ModRemoveCommunity::create(conn, &form)
206 let deleted_community = blocking(context.pool(), move |conn| {
207 Community::update_removed(conn, community.id, true)
211 send_community_ws_message(deleted_community.id, RemoveCommunity, None, None, context).await?;
213 DeletableObjects::Post(post) => {
214 let form = ModRemovePostForm {
215 mod_person_id: actor.id,
220 blocking(context.pool(), move |conn| {
221 ModRemovePost::create(conn, &form)
224 let removed_post = blocking(context.pool(), move |conn| {
225 Post::update_removed(conn, post.id, true)
229 send_post_ws_message(removed_post.id, RemovePost, None, None, context).await?;
231 DeletableObjects::Comment(comment) => {
232 let form = ModRemoveCommentForm {
233 mod_person_id: actor.id,
234 comment_id: comment.id,
238 blocking(context.pool(), move |conn| {
239 ModRemoveComment::create(conn, &form)
242 let removed_comment = blocking(context.pool(), move |conn| {
243 Comment::update_removed(conn, comment.id, true)
247 send_comment_ws_message_simple(removed_comment.id, RemoveComment, context).await?;
253 #[async_trait::async_trait(?Send)]
254 impl GetCommunity for Delete {
255 async fn get_community(
257 context: &LemmyContext,
258 _request_counter: &mut i32,
259 ) -> Result<ApubCommunity, LemmyError> {
260 let community_id = match DeletableObjects::read_from_db(&self.object, context).await? {
261 DeletableObjects::Community(c) => c.id,
262 DeletableObjects::Comment(c) => {
263 let post = blocking(context.pool(), move |conn| Post::read(conn, c.post_id)).await??;
266 DeletableObjects::Post(p) => p.community_id,
268 let community = blocking(context.pool(), move |conn| {
269 Community::read(conn, community_id)