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: [ObjectId<ApubCommunity>; 1],
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 verify_delete_activity(
102 self.summary.is_some(),
112 context: &Data<LemmyContext>,
113 request_counter: &mut i32,
114 ) -> Result<(), LemmyError> {
115 if let Some(reason) = self.summary {
116 // We set reason to empty string if it doesn't exist, to distinguish between delete and
117 // remove. Here we change it back to option, so we don't write it to db.
118 let reason = if reason.is_empty() {
123 receive_remove_action(&self.actor, &self.object, reason, context, request_counter).await
125 receive_delete_action(
129 community: UserOperationCrud::DeleteCommunity,
130 post: UserOperationCrud::DeletePost,
131 comment: UserOperationCrud::DeleteComment,
143 pub(in crate::activities::deletion) fn new(
145 community: &ApubCommunity,
147 summary: Option<String>,
148 context: &LemmyContext,
149 ) -> Result<Delete, LemmyError> {
151 actor: ObjectId::new(actor.actor_id()),
154 cc: [ObjectId::new(community.actor_id())],
155 kind: DeleteType::Delete,
157 id: generate_activity_id(
159 &context.settings().get_protocol_and_hostname(),
161 context: lemmy_context(),
162 unparsed: Default::default(),
165 pub(in crate::activities::deletion) async fn send(
167 community: &ApubCommunity,
169 summary: Option<String>,
170 context: &LemmyContext,
171 ) -> Result<(), LemmyError> {
172 let delete = Delete::new(actor, community, object_id, summary, context)?;
173 let delete_id = delete.id.clone();
175 let activity = AnnouncableActivities::Delete(delete);
176 send_to_community(activity, &delete_id, actor, community, vec![], context).await
180 pub(in crate::activities) async fn receive_remove_action(
181 actor: &ObjectId<ApubPerson>,
183 reason: Option<String>,
184 context: &LemmyContext,
185 request_counter: &mut i32,
186 ) -> Result<(), LemmyError> {
187 let actor = actor.dereference(context, request_counter).await?;
188 use UserOperationCrud::*;
189 match DeletableObjects::read_from_db(object, context).await? {
190 DeletableObjects::Community(community) => {
192 return Err(anyhow!("Only local admin can remove community").into());
194 let form = ModRemoveCommunityForm {
195 mod_person_id: actor.id,
196 community_id: community.id,
201 blocking(context.pool(), move |conn| {
202 ModRemoveCommunity::create(conn, &form)
205 let deleted_community = blocking(context.pool(), move |conn| {
206 Community::update_removed(conn, community.id, true)
210 send_community_ws_message(deleted_community.id, RemoveCommunity, None, None, context).await?;
212 DeletableObjects::Post(post) => {
213 let form = ModRemovePostForm {
214 mod_person_id: actor.id,
219 blocking(context.pool(), move |conn| {
220 ModRemovePost::create(conn, &form)
223 let removed_post = blocking(context.pool(), move |conn| {
224 Post::update_removed(conn, post.id, true)
228 send_post_ws_message(removed_post.id, RemovePost, None, None, context).await?;
230 DeletableObjects::Comment(comment) => {
231 let form = ModRemoveCommentForm {
232 mod_person_id: actor.id,
233 comment_id: comment.id,
237 blocking(context.pool(), move |conn| {
238 ModRemoveComment::create(conn, &form)
241 let removed_comment = blocking(context.pool(), move |conn| {
242 Comment::update_removed(conn, comment.id, true)
246 send_comment_ws_message_simple(removed_comment.id, RemoveComment, context).await?;
252 #[async_trait::async_trait(?Send)]
253 impl GetCommunity for Delete {
254 async fn get_community(
256 context: &LemmyContext,
257 _request_counter: &mut i32,
258 ) -> Result<ApubCommunity, LemmyError> {
259 let community_id = match DeletableObjects::read_from_db(&self.object, context).await? {
260 DeletableObjects::Community(c) => c.id,
261 DeletableObjects::Comment(c) => {
262 let post = blocking(context.pool(), move |conn| Post::read(conn, c.post_id)).await??;
265 DeletableObjects::Post(p) => p.community_id,
267 let community = blocking(context.pool(), move |conn| {
268 Community::read(conn, community_id)