]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/removal/remove.rs
83aaabc6f1a35598219f4400391e2a442c4e9c5a
[lemmy.git] / crates / apub / src / activities / removal / remove.rs
1 use crate::{
2   activities::{
3     comment::send_websocket_message as send_comment_message,
4     community::send_websocket_message as send_community_message,
5     post::send_websocket_message as send_post_message,
6     verify_activity,
7     verify_add_remove_moderator_target,
8     verify_mod_action,
9     verify_person_in_community,
10   },
11   fetcher::{
12     community::get_or_fetch_and_upsert_community,
13     objects::get_or_fetch_and_insert_post_or_comment,
14     person::get_or_fetch_and_upsert_person,
15   },
16   CommunityType,
17   PostOrComment,
18 };
19 use activitystreams::{activity::kind::RemoveType, base::AnyBase};
20 use anyhow::anyhow;
21 use lemmy_api_common::blocking;
22 use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
23 use lemmy_db_queries::{
24   source::{comment::Comment_, community::Community_, post::Post_},
25   Joinable,
26 };
27 use lemmy_db_schema::source::{
28   comment::Comment,
29   community::{Community, CommunityModerator, CommunityModeratorForm},
30   post::Post,
31 };
32 use lemmy_utils::LemmyError;
33 use lemmy_websocket::{LemmyContext, UserOperationCrud};
34 use url::Url;
35
36 // TODO: we can probably deduplicate a bunch of code between this and DeletePostCommentOrCommunity
37 #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
38 #[serde(rename_all = "camelCase")]
39 pub struct RemovePostCommentCommunityOrMod {
40   to: PublicUrl,
41   pub(in crate::activities::removal) object: Url,
42   cc: [Url; 1],
43   #[serde(rename = "type")]
44   kind: RemoveType,
45   // if target is set, this is means remove mod from community
46   target: Option<Url>,
47   #[serde(flatten)]
48   common: ActivityCommonFields,
49 }
50
51 #[async_trait::async_trait(?Send)]
52 impl ActivityHandler for RemovePostCommentCommunityOrMod {
53   async fn verify(
54     &self,
55     context: &LemmyContext,
56     request_counter: &mut i32,
57   ) -> Result<(), LemmyError> {
58     verify_activity(self.common())?;
59     let object_community =
60       get_or_fetch_and_upsert_community(&self.object, context, request_counter).await;
61     // removing a community
62     if object_community.is_ok() {
63       verify_mod_action(&self.common.actor, self.object.clone(), context).await?;
64     }
65     // removing community mod
66     else if let Some(target) = &self.target {
67       verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
68       verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
69       verify_add_remove_moderator_target(target, self.cc[0].clone())?;
70     }
71     // removing a post or comment
72     else {
73       verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
74       verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
75     }
76     Ok(())
77   }
78
79   async fn receive(
80     self,
81     context: &LemmyContext,
82     request_counter: &mut i32,
83   ) -> Result<(), LemmyError> {
84     let object_community =
85       get_or_fetch_and_upsert_community(&self.object, context, request_counter).await;
86     // removing a community
87     if let Ok(community) = object_community {
88       if community.local {
89         return Err(anyhow!("Only local admin can remove community").into());
90       }
91       let deleted_community = blocking(context.pool(), move |conn| {
92         Community::update_removed(conn, community.id, true)
93       })
94       .await??;
95
96       send_community_message(
97         deleted_community.id,
98         UserOperationCrud::RemoveCommunity,
99         context,
100       )
101       .await
102     }
103     // removing community mod
104     else if self.target.is_some() {
105       let community =
106         get_or_fetch_and_upsert_community(&self.cc[0], context, request_counter).await?;
107       let remove_mod =
108         get_or_fetch_and_upsert_person(&self.object, context, request_counter).await?;
109
110       let form = CommunityModeratorForm {
111         community_id: community.id,
112         person_id: remove_mod.id,
113       };
114       blocking(context.pool(), move |conn| {
115         CommunityModerator::leave(conn, &form)
116       })
117       .await??;
118       let anybase = AnyBase::from_arbitrary_json(serde_json::to_string(&self)?)?;
119       community
120         .send_announce(anybase, Some(self.object.clone()), context)
121         .await?;
122       // TODO: send websocket notification about removed mod
123       Ok(())
124     }
125     // removing a post or comment
126     else {
127       match get_or_fetch_and_insert_post_or_comment(&self.object, context, request_counter).await? {
128         PostOrComment::Post(post) => {
129           let removed_post = blocking(context.pool(), move |conn| {
130             Post::update_removed(conn, post.id, true)
131           })
132           .await??;
133           send_post_message(removed_post.id, UserOperationCrud::EditPost, context).await
134         }
135         PostOrComment::Comment(comment) => {
136           let removed_comment = blocking(context.pool(), move |conn| {
137             Comment::update_removed(conn, comment.id, true)
138           })
139           .await??;
140           send_comment_message(
141             removed_comment.id,
142             vec![],
143             UserOperationCrud::EditComment,
144             context,
145           )
146           .await
147         }
148       }
149     }
150   }
151
152   fn common(&self) -> &ActivityCommonFields {
153     &self.common
154   }
155 }