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