]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/deletion/delete.rs
Merge pull request #1683 from LemmyNet/fix/comment_count_trigger_issues
[lemmy.git] / crates / apub / src / activities / deletion / delete.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_mod_action,
8     verify_person_in_community,
9   },
10   fetcher::{
11     community::get_or_fetch_and_upsert_community,
12     objects::get_or_fetch_and_insert_post_or_comment,
13     person::get_or_fetch_and_upsert_person,
14   },
15   ActorType,
16   CommunityType,
17   PostOrComment,
18 };
19 use activitystreams::activity::kind::DeleteType;
20 use lemmy_api_common::blocking;
21 use lemmy_apub_lib::{values::PublicUrl, verify_urls_match, ActivityCommonFields, ActivityHandler};
22 use lemmy_db_queries::{
23   source::{comment::Comment_, community::Community_, post::Post_},
24   Crud,
25 };
26 use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post};
27 use lemmy_utils::LemmyError;
28 use lemmy_websocket::{LemmyContext, UserOperationCrud};
29 use url::Url;
30
31 /// This is very confusing, because there are four distinct cases to handle:
32 /// - user deletes their post
33 /// - user deletes their comment
34 /// - remote community mod deletes local community
35 /// - remote community deletes itself (triggered by a mod)
36 ///
37 /// TODO: we should probably change how community deletions work to simplify this. Probably by
38 /// wrapping it in an announce just like other activities, instead of having the community send it.
39 #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
40 #[serde(rename_all = "camelCase")]
41 pub struct DeletePostCommentOrCommunity {
42   to: PublicUrl,
43   pub(in crate::activities::deletion) object: Url,
44   cc: [Url; 1],
45   #[serde(rename = "type")]
46   kind: DeleteType,
47   #[serde(flatten)]
48   common: ActivityCommonFields,
49 }
50
51 #[async_trait::async_trait(?Send)]
52 impl ActivityHandler for DeletePostCommentOrCommunity {
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     // deleting a community (set counter 0 to only fetch from local db)
62     if object_community.is_ok() {
63       verify_mod_action(&self.common.actor, self.object.clone(), context).await?;
64     }
65     // deleting a post or comment
66     else {
67       verify_person_in_community(&self.common().actor, &self.cc[0], context, request_counter)
68         .await?;
69       let object_creator =
70         get_post_or_comment_actor_id(&self.object, context, request_counter).await?;
71       verify_urls_match(&self.common.actor, &object_creator)?;
72     }
73     Ok(())
74   }
75
76   async fn receive(
77     &self,
78     context: &LemmyContext,
79     request_counter: &mut i32,
80   ) -> Result<(), LemmyError> {
81     let object_community =
82       get_or_fetch_and_upsert_community(&self.object, context, request_counter).await;
83     // deleting a community
84     if let Ok(community) = object_community {
85       if community.local {
86         // repeat these checks just to be sure
87         verify_person_in_community(&self.common().actor, &self.cc[0], context, request_counter)
88           .await?;
89         verify_mod_action(&self.common.actor, self.object.clone(), context).await?;
90         let mod_ =
91           get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
92         community.send_delete(mod_, context).await?;
93       }
94       let deleted_community = blocking(context.pool(), move |conn| {
95         Community::update_deleted(conn, community.id, true)
96       })
97       .await??;
98
99       send_community_message(
100         deleted_community.id,
101         UserOperationCrud::DeleteCommunity,
102         context,
103       )
104       .await
105     }
106     // deleting a post or comment
107     else {
108       match get_or_fetch_and_insert_post_or_comment(&self.object, context, request_counter).await? {
109         PostOrComment::Post(post) => {
110           let deleted_post = blocking(context.pool(), move |conn| {
111             Post::update_deleted(conn, post.id, true)
112           })
113           .await??;
114           send_post_message(deleted_post.id, UserOperationCrud::EditPost, context).await
115         }
116         PostOrComment::Comment(comment) => {
117           let deleted_comment = blocking(context.pool(), move |conn| {
118             Comment::update_deleted(conn, comment.id, true)
119           })
120           .await??;
121           send_comment_message(
122             deleted_comment.id,
123             vec![],
124             UserOperationCrud::EditComment,
125             context,
126           )
127           .await
128         }
129       }
130     }
131   }
132
133   fn common(&self) -> &ActivityCommonFields {
134     &self.common
135   }
136 }
137
138 async fn get_post_or_comment_actor_id(
139   object: &Url,
140   context: &LemmyContext,
141   request_counter: &mut i32,
142 ) -> Result<Url, LemmyError> {
143   let actor_id =
144     match get_or_fetch_and_insert_post_or_comment(object, context, request_counter).await? {
145       PostOrComment::Post(post) => {
146         let creator_id = post.creator_id;
147         blocking(context.pool(), move |conn| Person::read(conn, creator_id))
148           .await??
149           .actor_id()
150       }
151       PostOrComment::Comment(comment) => {
152         let creator_id = comment.creator_id;
153         blocking(context.pool(), move |conn| Person::read(conn, creator_id))
154           .await??
155           .actor_id()
156       }
157     };
158   Ok(actor_id)
159 }