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