]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/deletion/delete.rs
337ba9e8afa271c37388ec1bdd63bacbc96e194c
[lemmy.git] / crates / apub / src / activities / deletion / delete.rs
1 use crate::{
2   activities::{
3     deletion::{receive_delete_action, verify_delete_activity, DeletableObjects},
4     generate_activity_id,
5   },
6   insert_activity,
7   objects::person::ApubPerson,
8   protocol::{activities::deletion::delete::Delete, IdOrNestedObject},
9 };
10 use activitypub_federation::{config::Data, kinds::activity::DeleteType, traits::ActivityHandler};
11 use lemmy_api_common::{
12   context::LemmyContext,
13   websocket::{
14     send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message},
15     UserOperationCrud,
16   },
17 };
18 use lemmy_db_schema::{
19   source::{
20     comment::{Comment, CommentUpdateForm},
21     community::{Community, CommunityUpdateForm},
22     moderator::{
23       ModRemoveComment,
24       ModRemoveCommentForm,
25       ModRemoveCommunity,
26       ModRemoveCommunityForm,
27       ModRemovePost,
28       ModRemovePostForm,
29     },
30     post::{Post, PostUpdateForm},
31   },
32   traits::Crud,
33 };
34 use lemmy_utils::error::LemmyError;
35 use url::Url;
36
37 #[async_trait::async_trait]
38 impl ActivityHandler for Delete {
39   type DataType = LemmyContext;
40   type Error = LemmyError;
41
42   fn id(&self) -> &Url {
43     &self.id
44   }
45
46   fn actor(&self) -> &Url {
47     self.actor.inner()
48   }
49
50   #[tracing::instrument(skip_all)]
51   async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
52     verify_delete_activity(self, self.summary.is_some(), context).await?;
53     Ok(())
54   }
55
56   #[tracing::instrument(skip_all)]
57   async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
58     insert_activity(&self.id, &self, false, false, context).await?;
59     if let Some(reason) = self.summary {
60       // We set reason to empty string if it doesn't exist, to distinguish between delete and
61       // remove. Here we change it back to option, so we don't write it to db.
62       let reason = if reason.is_empty() {
63         None
64       } else {
65         Some(reason)
66       };
67       receive_remove_action(
68         &self.actor.dereference(context).await?,
69         self.object.id(),
70         reason,
71         context,
72       )
73       .await
74     } else {
75       receive_delete_action(self.object.id(), &self.actor, true, context).await
76     }
77   }
78 }
79
80 impl Delete {
81   pub(in crate::activities::deletion) fn new(
82     actor: &ApubPerson,
83     object: DeletableObjects,
84     to: Url,
85     community: Option<&Community>,
86     summary: Option<String>,
87     context: &Data<LemmyContext>,
88   ) -> Result<Delete, LemmyError> {
89     let id = generate_activity_id(
90       DeleteType::Delete,
91       &context.settings().get_protocol_and_hostname(),
92     )?;
93     let cc: Option<Url> = community.map(|c| c.actor_id.clone().into());
94     Ok(Delete {
95       actor: actor.actor_id.clone().into(),
96       to: vec![to],
97       object: IdOrNestedObject::Id(object.id()),
98       cc: cc.into_iter().collect(),
99       kind: DeleteType::Delete,
100       summary,
101       id,
102       audience: community.map(|c| c.actor_id.clone().into()),
103     })
104   }
105 }
106
107 #[tracing::instrument(skip_all)]
108 pub(in crate::activities) async fn receive_remove_action(
109   actor: &ApubPerson,
110   object: &Url,
111   reason: Option<String>,
112   context: &Data<LemmyContext>,
113 ) -> Result<(), LemmyError> {
114   use UserOperationCrud::*;
115   match DeletableObjects::read_from_db(object, context).await? {
116     DeletableObjects::Community(community) => {
117       if community.local {
118         return Err(LemmyError::from_message(
119           "Only local admin can remove community",
120         ));
121       }
122       let form = ModRemoveCommunityForm {
123         mod_person_id: actor.id,
124         community_id: community.id,
125         removed: Some(true),
126         reason,
127         expires: None,
128       };
129       ModRemoveCommunity::create(context.pool(), &form).await?;
130       let deleted_community = Community::update(
131         context.pool(),
132         community.id,
133         &CommunityUpdateForm::builder().removed(Some(true)).build(),
134       )
135       .await?;
136
137       send_community_ws_message(deleted_community.id, RemoveCommunity, None, None, context).await?;
138     }
139     DeletableObjects::Post(post) => {
140       let form = ModRemovePostForm {
141         mod_person_id: actor.id,
142         post_id: post.id,
143         removed: Some(true),
144         reason,
145       };
146       ModRemovePost::create(context.pool(), &form).await?;
147       let removed_post = Post::update(
148         context.pool(),
149         post.id,
150         &PostUpdateForm::builder().removed(Some(true)).build(),
151       )
152       .await?;
153
154       send_post_ws_message(removed_post.id, RemovePost, None, None, context).await?;
155     }
156     DeletableObjects::Comment(comment) => {
157       let form = ModRemoveCommentForm {
158         mod_person_id: actor.id,
159         comment_id: comment.id,
160         removed: Some(true),
161         reason,
162       };
163       ModRemoveComment::create(context.pool(), &form).await?;
164       let removed_comment = Comment::update(
165         context.pool(),
166         comment.id,
167         &CommentUpdateForm::builder().removed(Some(true)).build(),
168       )
169       .await?;
170
171       send_comment_ws_message_simple(removed_comment.id, RemoveComment, context).await?;
172     }
173     DeletableObjects::PrivateMessage(_) => unimplemented!(),
174   }
175   Ok(())
176 }