3 community::announce::AnnouncableActivities,
7 verify_person_in_community,
10 activity_queue::send_to_community_new,
11 extensions::context::lemmy_context,
12 fetcher::object_id::ObjectId,
13 objects::{post::Page, FromApub, ToApub},
16 use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed};
18 use lemmy_api_common::blocking;
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::{send::send_post_ws_message, LemmyContext, UserOperationCrud};
30 use serde::{Deserialize, Serialize};
33 #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
34 #[serde(rename_all = "camelCase")]
35 pub struct CreateOrUpdatePost {
36 actor: ObjectId<Person>,
39 cc: [ObjectId<Community>; 1],
40 #[serde(rename = "type")]
41 kind: CreateOrUpdateType,
43 #[serde(rename = "@context")]
44 context: OneOrMany<AnyBase>,
49 impl CreateOrUpdatePost {
53 kind: CreateOrUpdateType,
54 context: &LemmyContext,
55 ) -> Result<(), LemmyError> {
56 let community_id = post.community_id;
57 let community = blocking(context.pool(), move |conn| {
58 Community::read(conn, community_id)
62 let id = generate_activity_id(kind.clone())?;
63 let create_or_update = CreateOrUpdatePost {
64 actor: ObjectId::new(actor.actor_id()),
65 to: [PublicUrl::Public],
66 object: post.to_apub(context.pool()).await?,
67 cc: [ObjectId::new(community.actor_id())],
70 context: lemmy_context(),
71 unparsed: Default::default(),
74 let activity = AnnouncableActivities::CreateOrUpdatePost(Box::new(create_or_update));
75 send_to_community_new(activity, &id, actor, &community, vec![], context).await
79 #[async_trait::async_trait(?Send)]
80 impl ActivityHandler for CreateOrUpdatePost {
83 context: &LemmyContext,
84 request_counter: &mut i32,
85 ) -> Result<(), LemmyError> {
86 verify_activity(self)?;
87 let community = self.cc[0].dereference(context, request_counter).await?;
88 verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
90 CreateOrUpdateType::Create => {
91 verify_domains_match(self.actor.inner(), self.object.id_unchecked())?;
92 verify_urls_match(self.actor(), self.object.attributed_to.inner())?;
93 // Check that the post isnt locked or stickied, as that isnt possible for newly created posts.
94 // However, when fetching a remote post we generate a new create activity with the current
95 // locked/stickied value, so this check may fail. So only check if its a local community,
96 // because then we will definitely receive all create and update activities separately.
97 let is_stickied_or_locked =
98 self.object.stickied == Some(true) || self.object.comments_enabled == Some(false);
99 if community.local && is_stickied_or_locked {
100 return Err(anyhow!("New post cannot be stickied or locked").into());
103 CreateOrUpdateType::Update => {
104 let is_mod_action = self.object.is_mod_action(context.pool()).await?;
106 verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
108 verify_domains_match(self.actor.inner(), self.object.id_unchecked())?;
109 verify_urls_match(self.actor(), self.object.attributed_to.inner())?;
113 self.object.verify(context, request_counter).await?;
119 context: &LemmyContext,
120 request_counter: &mut i32,
121 ) -> Result<(), LemmyError> {
122 let actor = self.actor.dereference(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_post_ws_message(post.id, notif_type, None, None, context).await?;