]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/deletion/delete.rs
e141fcadd04087f9af2824211ba0b6d5377501d1
[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},
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.actor,
71         self.object.id(),
72         reason,
73         context,
74         request_counter,
75       )
76       .await
77     } else {
78       receive_delete_action(
79         self.object.id(),
80         &self.actor,
81         true,
82         context,
83         request_counter,
84       )
85       .await
86     }
87   }
88 }
89
90 impl Delete {
91   pub(in crate::activities::deletion) fn new(
92     actor: &Person,
93     object: DeletableObjects,
94     to: Url,
95     community: Option<&Community>,
96     summary: Option<String>,
97     context: &LemmyContext,
98   ) -> Result<Delete, LemmyError> {
99     let id = generate_activity_id(
100       DeleteType::Delete,
101       &context.settings().get_protocol_and_hostname(),
102     )?;
103     let cc: Option<Url> = community.map(|c| c.actor_id.clone().into());
104     Ok(Delete {
105       actor: ObjectId::new(actor.actor_id.clone()),
106       to: vec![to],
107       object: IdOrNestedObject::Id(object.id()),
108       cc: cc.into_iter().collect(),
109       kind: DeleteType::Delete,
110       summary,
111       id,
112       unparsed: Default::default(),
113     })
114   }
115 }
116
117 #[tracing::instrument(skip_all)]
118 pub(in crate::activities) async fn receive_remove_action(
119   actor: &ObjectId<ApubPerson>,
120   object: &Url,
121   reason: Option<String>,
122   context: &LemmyContext,
123   request_counter: &mut i32,
124 ) -> Result<(), LemmyError> {
125   let actor = actor
126     .dereference(context, context.client(), request_counter)
127     .await?;
128   use UserOperationCrud::*;
129   match DeletableObjects::read_from_db(object, context).await? {
130     DeletableObjects::Community(community) => {
131       if community.local {
132         return Err(LemmyError::from_message(
133           "Only local admin can remove community",
134         ));
135       }
136       let form = ModRemoveCommunityForm {
137         mod_person_id: actor.id,
138         community_id: community.id,
139         removed: Some(true),
140         reason,
141         expires: None,
142       };
143       blocking(context.pool(), move |conn| {
144         ModRemoveCommunity::create(conn, &form)
145       })
146       .await??;
147       let deleted_community = blocking(context.pool(), move |conn| {
148         Community::update_removed(conn, community.id, true)
149       })
150       .await??;
151
152       send_community_ws_message(deleted_community.id, RemoveCommunity, None, None, context).await?;
153     }
154     DeletableObjects::Post(post) => {
155       let form = ModRemovePostForm {
156         mod_person_id: actor.id,
157         post_id: post.id,
158         removed: Some(true),
159         reason,
160       };
161       blocking(context.pool(), move |conn| {
162         ModRemovePost::create(conn, &form)
163       })
164       .await??;
165       let removed_post = blocking(context.pool(), move |conn| {
166         Post::update_removed(conn, post.id, true)
167       })
168       .await??;
169
170       send_post_ws_message(removed_post.id, RemovePost, None, None, context).await?;
171     }
172     DeletableObjects::Comment(comment) => {
173       let form = ModRemoveCommentForm {
174         mod_person_id: actor.id,
175         comment_id: comment.id,
176         removed: Some(true),
177         reason,
178       };
179       blocking(context.pool(), move |conn| {
180         ModRemoveComment::create(conn, &form)
181       })
182       .await??;
183       let removed_comment = blocking(context.pool(), move |conn| {
184         Comment::update_removed(conn, comment.id, true)
185       })
186       .await??;
187
188       send_comment_ws_message_simple(removed_comment.id, RemoveComment, context).await?;
189     }
190     DeletableObjects::PrivateMessage(_) => unimplemented!(),
191   }
192   Ok(())
193 }
194
195 #[async_trait::async_trait(?Send)]
196 impl GetCommunity for Delete {
197   #[tracing::instrument(skip_all)]
198   async fn get_community(
199     &self,
200     context: &LemmyContext,
201     _request_counter: &mut i32,
202   ) -> Result<ApubCommunity, LemmyError> {
203     let community_id = match DeletableObjects::read_from_db(self.object.id(), context).await? {
204       DeletableObjects::Community(c) => c.id,
205       DeletableObjects::Comment(c) => {
206         let post = blocking(context.pool(), move |conn| Post::read(conn, c.post_id)).await??;
207         post.community_id
208       }
209       DeletableObjects::Post(p) => p.community_id,
210       DeletableObjects::PrivateMessage(_) => {
211         return Err(anyhow!("Private message is not part of community").into())
212       }
213     };
214     let community = blocking(context.pool(), move |conn| {
215       Community::read(conn, community_id)
216     })
217     .await??;
218     Ok(community.into())
219   }
220 }