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