3 community::announce::AnnouncableActivities,
6 post::send_websocket_message,
9 verify_person_in_community,
12 activity_queue::send_to_community_new,
13 extensions::context::lemmy_context,
14 fetcher::person::get_or_fetch_and_upsert_person,
15 objects::{post::Page, FromApub, ToApub},
19 use lemmy_api_common::blocking;
27 use lemmy_db_queries::Crud;
28 use lemmy_db_schema::source::{community::Community, person::Person, post::Post};
29 use lemmy_utils::LemmyError;
30 use lemmy_websocket::{LemmyContext, UserOperationCrud};
33 #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
34 #[serde(rename_all = "camelCase")]
35 pub struct CreateOrUpdatePost {
39 #[serde(rename = "type")]
40 kind: CreateOrUpdateType,
42 common: ActivityCommonFields,
45 impl CreateOrUpdatePost {
49 kind: CreateOrUpdateType,
50 context: &LemmyContext,
51 ) -> Result<(), LemmyError> {
52 let community_id = post.community_id;
53 let community = blocking(context.pool(), move |conn| {
54 Community::read(conn, community_id)
58 let id = generate_activity_id(kind.clone())?;
59 let create_or_update = CreateOrUpdatePost {
60 to: PublicUrl::Public,
61 object: post.to_apub(context.pool()).await?,
62 cc: [community.actor_id()],
64 common: ActivityCommonFields {
65 context: lemmy_context(),
67 actor: actor.actor_id(),
68 unparsed: Default::default(),
72 let activity = AnnouncableActivities::CreateOrUpdatePost(Box::new(create_or_update));
73 send_to_community_new(activity, &id, actor, &community, vec![], context).await
77 #[async_trait::async_trait(?Send)]
78 impl ActivityHandler for CreateOrUpdatePost {
81 context: &LemmyContext,
82 request_counter: &mut i32,
83 ) -> Result<(), LemmyError> {
84 verify_activity(self.common())?;
85 let community = extract_community(&self.cc, context, request_counter).await?;
86 let community_id = community.actor_id();
87 verify_person_in_community(&self.common.actor, &community_id, context, request_counter).await?;
89 CreateOrUpdateType::Create => {
90 verify_domains_match(&self.common.actor, self.object.id_unchecked())?;
91 verify_urls_match(&self.common.actor, &self.object.attributed_to)?;
92 // Check that the post isnt locked or stickied, as that isnt possible for newly created posts.
93 // However, when fetching a remote post we generate a new create activity with the current
94 // locked/stickied value, so this check may fail. So only check if its a local community,
95 // because then we will definitely receive all create and update activities separately.
96 let is_stickied_or_locked =
97 self.object.stickied == Some(true) || self.object.comments_enabled == Some(false);
98 if community.local && is_stickied_or_locked {
99 return Err(anyhow!("New post cannot be stickied or locked").into());
102 CreateOrUpdateType::Update => {
103 let is_mod_action = self.object.is_mod_action(context.pool()).await?;
105 verify_mod_action(&self.common.actor, community_id, context).await?;
107 verify_domains_match(&self.common.actor, self.object.id_unchecked())?;
108 verify_urls_match(&self.common.actor, &self.object.attributed_to)?;
112 self.object.verify(context, request_counter).await?;
118 context: &LemmyContext,
119 request_counter: &mut i32,
120 ) -> Result<(), LemmyError> {
122 get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
123 let post = Post::from_apub(&self.object, context, &actor.actor_id(), request_counter).await?;
125 let notif_type = match self.kind {
126 CreateOrUpdateType::Create => UserOperationCrud::CreatePost,
127 CreateOrUpdateType::Update => UserOperationCrud::EditPost,
129 send_websocket_message(post.id, notif_type, context).await
132 fn common(&self) -> &ActivityCommonFields {