]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/post/create.rs
Merge pull request #1678 from LemmyNet/rewrite-post
[lemmy.git] / crates / apub / src / activities / post / create.rs
1 use crate::{
2   activities::{
3     community::announce::AnnouncableActivities,
4     extract_community,
5     generate_activity_id,
6     post::send_websocket_message,
7     verify_activity,
8     verify_person_in_community,
9   },
10   activity_queue::send_to_community_new,
11   extensions::context::lemmy_context,
12   fetcher::person::get_or_fetch_and_upsert_person,
13   objects::{post::Page, FromApub, ToApub},
14   ActorType,
15 };
16 use activitystreams::activity::kind::CreateType;
17 use anyhow::anyhow;
18 use lemmy_api_common::blocking;
19 use lemmy_apub_lib::{
20   values::PublicUrl,
21   verify_domains_match,
22   verify_urls_match,
23   ActivityCommonFields,
24   ActivityHandler,
25 };
26 use lemmy_db_queries::Crud;
27 use lemmy_db_schema::source::{community::Community, person::Person, post::Post};
28 use lemmy_utils::LemmyError;
29 use lemmy_websocket::{LemmyContext, UserOperationCrud};
30 use url::Url;
31
32 #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
33 #[serde(rename_all = "camelCase")]
34 pub struct CreatePost {
35   to: PublicUrl,
36   object: Page,
37   cc: [Url; 1],
38   r#type: CreateType,
39   #[serde(flatten)]
40   common: ActivityCommonFields,
41 }
42
43 impl CreatePost {
44   pub async fn send(post: &Post, actor: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
45     let community_id = post.community_id;
46     let community = blocking(context.pool(), move |conn| {
47       Community::read(conn, community_id)
48     })
49     .await??;
50
51     let id = generate_activity_id(CreateType::Create)?;
52     let create = CreatePost {
53       to: PublicUrl::Public,
54       object: post.to_apub(context.pool()).await?,
55       cc: [community.actor_id()],
56       r#type: Default::default(),
57       common: ActivityCommonFields {
58         context: lemmy_context(),
59         id: id.clone(),
60         actor: actor.actor_id(),
61         unparsed: Default::default(),
62       },
63     };
64
65     let activity = AnnouncableActivities::CreatePost(create);
66     send_to_community_new(activity, &id, actor, &community, vec![], context).await
67   }
68 }
69
70 #[async_trait::async_trait(?Send)]
71 impl ActivityHandler for CreatePost {
72   async fn verify(
73     &self,
74     context: &LemmyContext,
75     request_counter: &mut i32,
76   ) -> Result<(), LemmyError> {
77     let community = extract_community(&self.cc, context, request_counter).await?;
78     let community_id = &community.actor_id();
79
80     verify_activity(self.common())?;
81     verify_person_in_community(&self.common.actor, community_id, context, request_counter).await?;
82     verify_domains_match(&self.common.actor, &self.object.id)?;
83     verify_urls_match(&self.common.actor, &self.object.attributed_to)?;
84     // Check that the post isnt locked or stickied, as that isnt possible for newly created posts.
85     // However, when fetching a remote post we generate a new create activity with the current
86     // locked/stickied value, so this check may fail. So only check if its a local community,
87     // because then we will definitely receive all create and update activities separately.
88     let is_stickied_or_locked =
89       self.object.stickied == Some(true) || self.object.comments_enabled == Some(false);
90     if community.local && is_stickied_or_locked {
91       return Err(anyhow!("New post cannot be stickied or locked").into());
92     }
93     self.object.verify(context, request_counter).await?;
94     Ok(())
95   }
96
97   async fn receive(
98     &self,
99     context: &LemmyContext,
100     request_counter: &mut i32,
101   ) -> Result<(), LemmyError> {
102     let actor =
103       get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
104     let post = Post::from_apub(
105       &self.object,
106       context,
107       actor.actor_id(),
108       request_counter,
109       false,
110     )
111     .await?;
112
113     send_websocket_message(post.id, UserOperationCrud::CreatePost, context).await
114   }
115
116   fn common(&self) -> &ActivityCommonFields {
117     &self.common
118   }
119 }