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