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