]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/community/update.rs
b71dcc71b6654c191f5f8f08e70da06e4eb2b30b
[lemmy.git] / crates / apub / src / activities / community / update.rs
1 use crate::{
2   activities::{
3     community::{announce::AnnouncableActivities, send_to_community},
4     generate_activity_id,
5     verify_activity,
6     verify_mod_action,
7     verify_person_in_community,
8   },
9   context::lemmy_context,
10   fetcher::object_id::ObjectId,
11   objects::{community::Group, ToApub},
12 };
13 use activitystreams::{
14   activity::kind::UpdateType,
15   base::AnyBase,
16   primitives::OneOrMany,
17   unparsed::Unparsed,
18 };
19 use lemmy_api_common::blocking;
20 use lemmy_apub_lib::{
21   data::Data,
22   traits::{ActivityFields, ActivityHandler, ActorType},
23   values::PublicUrl,
24 };
25 use lemmy_db_queries::Crud;
26 use lemmy_db_schema::source::{
27   community::{Community, CommunityForm},
28   person::Person,
29 };
30 use lemmy_utils::LemmyError;
31 use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud};
32 use serde::{Deserialize, Serialize};
33 use url::Url;
34
35 /// This activity is received from a remote community mod, and updates the description or other
36 /// fields of a local community.
37 #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
38 #[serde(rename_all = "camelCase")]
39 pub struct UpdateCommunity {
40   actor: ObjectId<Person>,
41   to: [PublicUrl; 1],
42   // TODO: would be nice to use a separate struct here, which only contains the fields updated here
43   object: Group,
44   cc: [ObjectId<Community>; 1],
45   #[serde(rename = "type")]
46   kind: UpdateType,
47   id: Url,
48   #[serde(rename = "@context")]
49   context: OneOrMany<AnyBase>,
50   #[serde(flatten)]
51   unparsed: Unparsed,
52 }
53
54 impl UpdateCommunity {
55   pub async fn send(
56     community: &Community,
57     actor: &Person,
58     context: &LemmyContext,
59   ) -> Result<(), LemmyError> {
60     let id = generate_activity_id(
61       UpdateType::Update,
62       &context.settings().get_protocol_and_hostname(),
63     )?;
64     let update = UpdateCommunity {
65       actor: ObjectId::new(actor.actor_id()),
66       to: [PublicUrl::Public],
67       object: community.to_apub(context.pool()).await?,
68       cc: [ObjectId::new(community.actor_id())],
69       kind: UpdateType::Update,
70       id: id.clone(),
71       context: lemmy_context(),
72       unparsed: Default::default(),
73     };
74
75     let activity = AnnouncableActivities::UpdateCommunity(Box::new(update));
76     send_to_community(activity, &id, actor, community, vec![], context).await
77   }
78 }
79
80 #[async_trait::async_trait(?Send)]
81 impl ActivityHandler for UpdateCommunity {
82   type DataType = LemmyContext;
83   async fn verify(
84     &self,
85     context: &Data<LemmyContext>,
86     request_counter: &mut i32,
87   ) -> Result<(), LemmyError> {
88     verify_activity(self, &context.settings())?;
89     verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
90     verify_mod_action(&self.actor, self.cc[0].clone(), context, request_counter).await?;
91     Ok(())
92   }
93
94   async fn receive(
95     self,
96     context: &Data<LemmyContext>,
97     request_counter: &mut i32,
98   ) -> Result<(), LemmyError> {
99     let cc = self.cc[0].clone();
100     let community = cc.dereference(context, request_counter).await?;
101
102     let updated_community = Group::from_apub_to_form(
103       &self.object,
104       &community.actor_id.clone().into(),
105       &context.settings(),
106     )
107     .await?;
108     let cf = CommunityForm {
109       name: updated_community.name,
110       title: updated_community.title,
111       description: updated_community.description,
112       nsfw: updated_community.nsfw,
113       // TODO: icon and banner would be hosted on the other instance, ideally we would copy it to ours
114       icon: updated_community.icon,
115       banner: updated_community.banner,
116       ..CommunityForm::default()
117     };
118     let updated_community = blocking(context.pool(), move |conn| {
119       Community::update(conn, community.id, &cf)
120     })
121     .await??;
122
123     send_community_ws_message(
124       updated_community.id,
125       UserOperationCrud::EditCommunity,
126       None,
127       None,
128       context,
129     )
130     .await?;
131     Ok(())
132   }
133 }