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(
64 &context.settings().get_protocol_and_hostname(),
66 let create_or_update = CreateOrUpdatePost {
67 actor: ObjectId::new(actor.actor_id()),
68 to: [PublicUrl::Public],
69 object: post.to_apub(context.pool()).await?,
70 cc: [ObjectId::new(community.actor_id())],
73 context: lemmy_context(),
74 unparsed: Default::default(),
77 let activity = AnnouncableActivities::CreateOrUpdatePost(Box::new(create_or_update));
78 send_to_community_new(activity, &id, actor, &community, vec![], context).await
82 #[async_trait::async_trait(?Send)]
83 impl ActivityHandler for CreateOrUpdatePost {
86 context: &LemmyContext,
87 request_counter: &mut i32,
88 ) -> Result<(), LemmyError> {
89 verify_activity(self, &context.settings())?;
90 let community = self.cc[0].dereference(context, request_counter).await?;
91 verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
93 CreateOrUpdateType::Create => {
94 verify_domains_match(self.actor.inner(), self.object.id_unchecked())?;
95 verify_urls_match(self.actor(), self.object.attributed_to.inner())?;
96 // Check that the post isnt locked or stickied, as that isnt possible for newly created posts.
97 // However, when fetching a remote post we generate a new create activity with the current
98 // locked/stickied value, so this check may fail. So only check if its a local community,
99 // because then we will definitely receive all create and update activities separately.
100 let is_stickied_or_locked =
101 self.object.stickied == Some(true) || self.object.comments_enabled == Some(false);
102 if community.local && is_stickied_or_locked {
103 return Err(anyhow!("New post cannot be stickied or locked").into());
106 CreateOrUpdateType::Update => {
107 let is_mod_action = self.object.is_mod_action(context.pool()).await?;
109 verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
111 verify_domains_match(self.actor.inner(), self.object.id_unchecked())?;
112 verify_urls_match(self.actor(), self.object.attributed_to.inner())?;
116 self.object.verify(context, request_counter).await?;
122 context: &LemmyContext,
123 request_counter: &mut i32,
124 ) -> Result<(), LemmyError> {
125 let actor = self.actor.dereference(context, request_counter).await?;
126 let post = Post::from_apub(&self.object, context, &actor.actor_id(), request_counter).await?;
128 let notif_type = match self.kind {
129 CreateOrUpdateType::Create => UserOperationCrud::CreatePost,
130 CreateOrUpdateType::Update => UserOperationCrud::EditPost,
132 send_post_ws_message(post.id, notif_type, None, None, context).await?;