]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/deletion/delete.rs
275683a379c71ba88a04dfb3184ccfb79e8e2a88
[lemmy.git] / crates / apub / src / activities / deletion / delete.rs
1 use crate::{
2   activities::{
3     community::announce::GetCommunity,
4     deletion::{receive_delete_action, verify_delete_activity, DeletableObjects},
5     generate_activity_id,
6     verify_activity,
7   },
8   objects::{community::ApubCommunity, person::ApubPerson},
9   protocol::activities::deletion::delete::{Delete, IdOrNestedObject, NestedObject},
10 };
11 use activitystreams_kinds::activity::DeleteType;
12 use anyhow::anyhow;
13 use lemmy_api_common::blocking;
14 use lemmy_apub_lib::{data::Data, object_id::ObjectId, traits::ActivityHandler};
15 use lemmy_db_schema::{
16   source::{
17     comment::Comment,
18     community::Community,
19     moderator::{
20       ModRemoveComment,
21       ModRemoveCommentForm,
22       ModRemoveCommunity,
23       ModRemoveCommunityForm,
24       ModRemovePost,
25       ModRemovePostForm,
26     },
27     person::Person,
28     post::Post,
29   },
30   traits::Crud,
31 };
32 use lemmy_utils::LemmyError;
33 use lemmy_websocket::{
34   send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message},
35   LemmyContext,
36   UserOperationCrud,
37 };
38 use url::Url;
39
40 #[async_trait::async_trait(?Send)]
41 impl ActivityHandler for Delete {
42   type DataType = LemmyContext;
43
44   #[tracing::instrument(skip_all)]
45   async fn verify(
46     &self,
47     context: &Data<LemmyContext>,
48     request_counter: &mut i32,
49   ) -> Result<(), LemmyError> {
50     verify_activity(&self.id, self.actor.inner(), &context.settings())?;
51     verify_delete_activity(self, self.summary.is_some(), context, request_counter).await?;
52     Ok(())
53   }
54
55   #[tracing::instrument(skip_all)]
56   async fn receive(
57     self,
58     context: &Data<LemmyContext>,
59     request_counter: &mut i32,
60   ) -> Result<(), LemmyError> {
61     if let Some(reason) = self.summary {
62       // We set reason to empty string if it doesn't exist, to distinguish between delete and
63       // remove. Here we change it back to option, so we don't write it to db.
64       let reason = if reason.is_empty() {
65         None
66       } else {
67         Some(reason)
68       };
69       receive_remove_action(
70         &self
71           .actor
72           .dereference(context, context.client(), request_counter)
73           .await?,
74         self.object.id(),
75         reason,
76         context,
77       )
78       .await
79     } else {
80       receive_delete_action(
81         self.object.id(),
82         &self.actor,
83         true,
84         context,
85         request_counter,
86       )
87       .await
88     }
89   }
90 }
91
92 impl Delete {
93   pub(in crate::activities::deletion) fn new(
94     actor: &Person,
95     object: DeletableObjects,
96     to: Url,
97     community: Option<&Community>,
98     summary: Option<String>,
99     context: &LemmyContext,
100   ) -> Result<Delete, LemmyError> {
101     let id = generate_activity_id(
102       DeleteType::Delete,
103       &context.settings().get_protocol_and_hostname(),
104     )?;
105     let cc: Option<Url> = community.map(|c| c.actor_id.clone().into());
106     Ok(Delete {
107       actor: ObjectId::new(actor.actor_id.clone()),
108       to: vec![to],
109       object: IdOrNestedObject::NestedObject(NestedObject {
110         id: object.id(),
111         kind: Default::default(),
112       }),
113       cc: cc.into_iter().collect(),
114       kind: DeleteType::Delete,
115       summary,
116       id,
117       unparsed: Default::default(),
118     })
119   }
120 }
121
122 #[tracing::instrument(skip_all)]
123 pub(in crate::activities) async fn receive_remove_action(
124   actor: &ApubPerson,
125   object: &Url,
126   reason: Option<String>,
127   context: &LemmyContext,
128 ) -> Result<(), LemmyError> {
129   use UserOperationCrud::*;
130   match DeletableObjects::read_from_db(object, context).await? {
131     DeletableObjects::Community(community) => {
132       if community.local {
133         return Err(LemmyError::from_message(
134           "Only local admin can remove community",
135         ));
136       }
137       let form = ModRemoveCommunityForm {
138         mod_person_id: actor.id,
139         community_id: community.id,
140         removed: Some(true),
141         reason,
142         expires: None,
143       };
144       blocking(context.pool(), move |conn| {
145         ModRemoveCommunity::create(conn, &form)
146       })
147       .await??;
148       let deleted_community = blocking(context.pool(), move |conn| {
149         Community::update_removed(conn, community.id, true)
150       })
151       .await??;
152
153       send_community_ws_message(deleted_community.id, RemoveCommunity, None, None, context).await?;
154     }
155     DeletableObjects::Post(post) => {
156       let form = ModRemovePostForm {
157         mod_person_id: actor.id,
158         post_id: post.id,
159         removed: Some(true),
160         reason,
161       };
162       blocking(context.pool(), move |conn| {
163         ModRemovePost::create(conn, &form)
164       })
165       .await??;
166       let removed_post = blocking(context.pool(), move |conn| {
167         Post::update_removed(conn, post.id, true)
168       })
169       .await??;
170
171       send_post_ws_message(removed_post.id, RemovePost, None, None, context).await?;
172     }
173     DeletableObjects::Comment(comment) => {
174       let form = ModRemoveCommentForm {
175         mod_person_id: actor.id,
176         comment_id: comment.id,
177         removed: Some(true),
178         reason,
179       };
180       blocking(context.pool(), move |conn| {
181         ModRemoveComment::create(conn, &form)
182       })
183       .await??;
184       let removed_comment = blocking(context.pool(), move |conn| {
185         Comment::update_removed(conn, comment.id, true)
186       })
187       .await??;
188
189       send_comment_ws_message_simple(removed_comment.id, RemoveComment, context).await?;
190     }
191     DeletableObjects::PrivateMessage(_) => unimplemented!(),
192   }
193   Ok(())
194 }
195
196 #[async_trait::async_trait(?Send)]
197 impl GetCommunity for Delete {
198   #[tracing::instrument(skip_all)]
199   async fn get_community(
200     &self,
201     context: &LemmyContext,
202     _request_counter: &mut i32,
203   ) -> Result<ApubCommunity, LemmyError> {
204     let community_id = match DeletableObjects::read_from_db(self.object.id(), context).await? {
205       DeletableObjects::Community(c) => c.id,
206       DeletableObjects::Comment(c) => {
207         let post = blocking(context.pool(), move |conn| Post::read(conn, c.post_id)).await??;
208         post.community_id
209       }
210       DeletableObjects::Post(p) => p.community_id,
211       DeletableObjects::PrivateMessage(_) => {
212         return Err(anyhow!("Private message is not part of community").into())
213       }
214     };
215     let community = blocking(context.pool(), move |conn| {
216       Community::read(conn, community_id)
217     })
218     .await??;
219     Ok(community.into())
220   }
221 }