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