]> Untitled Git - lemmy.git/blob - crates/apub/src/objects/community.rs
Address review comments
[lemmy.git] / crates / apub / src / objects / community.rs
1 use crate::{
2   extensions::{context::lemmy_context, group_extensions::GroupExtension},
3   fetcher::{community::fetch_community_mods, person::get_or_fetch_and_upsert_person},
4   generate_moderators_url,
5   objects::{
6     check_object_domain,
7     create_tombstone,
8     get_object_from_apub,
9     get_source_markdown_value,
10     set_content_and_source,
11     FromApub,
12     FromApubToForm,
13     ToApub,
14   },
15   ActorType,
16   GroupExt,
17 };
18 use activitystreams::{
19   actor::{kind::GroupType, ApActor, Endpoints, Group},
20   base::BaseExt,
21   object::{ApObject, Image, Tombstone},
22   prelude::*,
23 };
24 use activitystreams_ext::Ext2;
25 use anyhow::Context;
26 use lemmy_db_queries::DbPool;
27 use lemmy_db_schema::{
28   naive_now,
29   source::community::{Community, CommunityForm},
30 };
31 use lemmy_utils::{
32   location_info,
33   utils::{check_slurs, check_slurs_opt, convert_datetime},
34   LemmyError,
35 };
36 use lemmy_websocket::LemmyContext;
37 use url::Url;
38
39 #[async_trait::async_trait(?Send)]
40 impl ToApub for Community {
41   type ApubType = GroupExt;
42
43   async fn to_apub(&self, _pool: &DbPool) -> Result<GroupExt, LemmyError> {
44     let mut group = ApObject::new(Group::new());
45     group
46       .set_many_contexts(lemmy_context()?)
47       .set_id(self.actor_id.to_owned().into())
48       .set_name(self.title.to_owned())
49       .set_published(convert_datetime(self.published));
50
51     if let Some(u) = self.updated.to_owned() {
52       group.set_updated(convert_datetime(u));
53     }
54     if let Some(d) = self.description.to_owned() {
55       set_content_and_source(&mut group, &d)?;
56     }
57
58     if let Some(icon_url) = &self.icon {
59       let mut image = Image::new();
60       image.set_url::<Url>(icon_url.to_owned().into());
61       group.set_icon(image.into_any_base()?);
62     }
63
64     if let Some(banner_url) = &self.banner {
65       let mut image = Image::new();
66       image.set_url::<Url>(banner_url.to_owned().into());
67       group.set_image(image.into_any_base()?);
68     }
69
70     let mut ap_actor = ApActor::new(self.inbox_url.clone().into(), group);
71     ap_actor
72       .set_preferred_username(self.name.to_owned())
73       .set_outbox(self.get_outbox_url()?)
74       .set_followers(self.followers_url.clone().into())
75       .set_endpoints(Endpoints {
76         shared_inbox: Some(self.get_shared_inbox_or_inbox_url()),
77         ..Default::default()
78       });
79
80     Ok(Ext2::new(
81       ap_actor,
82       GroupExtension::new(self.nsfw, generate_moderators_url(&self.actor_id)?.into())?,
83       self.get_public_key_ext()?,
84     ))
85   }
86
87   fn to_tombstone(&self) -> Result<Tombstone, LemmyError> {
88     create_tombstone(
89       self.deleted,
90       self.actor_id.to_owned().into(),
91       self.updated,
92       GroupType::Group,
93     )
94   }
95 }
96
97 #[async_trait::async_trait(?Send)]
98 impl FromApub for Community {
99   type ApubType = GroupExt;
100
101   /// Converts a `Group` to `Community`, inserts it into the database and updates moderators.
102   async fn from_apub(
103     group: &GroupExt,
104     context: &LemmyContext,
105     expected_domain: Url,
106     request_counter: &mut i32,
107     mod_action_allowed: bool,
108   ) -> Result<Community, LemmyError> {
109     get_object_from_apub(
110       group,
111       context,
112       expected_domain,
113       request_counter,
114       mod_action_allowed,
115     )
116     .await
117   }
118 }
119
120 #[async_trait::async_trait(?Send)]
121 impl FromApubToForm<GroupExt> for CommunityForm {
122   async fn from_apub(
123     group: &GroupExt,
124     context: &LemmyContext,
125     expected_domain: Url,
126     request_counter: &mut i32,
127     _mod_action_allowed: bool,
128   ) -> Result<Self, LemmyError> {
129     let moderator_uris = fetch_community_mods(context, group, request_counter).await?;
130     let creator_uri = moderator_uris.first().context(location_info!())?;
131
132     let creator = get_or_fetch_and_upsert_person(creator_uri, context, request_counter).await?;
133     let name = group
134       .inner
135       .preferred_username()
136       .context(location_info!())?
137       .to_string();
138     let title = group
139       .inner
140       .name()
141       .context(location_info!())?
142       .as_one()
143       .context(location_info!())?
144       .as_xsd_string()
145       .context(location_info!())?
146       .to_string();
147
148     let description = get_source_markdown_value(group)?;
149
150     check_slurs(&name)?;
151     check_slurs(&title)?;
152     check_slurs_opt(&description)?;
153
154     let icon = match group.icon() {
155       Some(any_image) => Some(
156         Image::from_any_base(any_image.as_one().context(location_info!())?.clone())
157           .context(location_info!())?
158           .context(location_info!())?
159           .url()
160           .context(location_info!())?
161           .as_single_xsd_any_uri()
162           .map(|u| u.to_owned().into()),
163       ),
164       None => None,
165     };
166     let banner = match group.image() {
167       Some(any_image) => Some(
168         Image::from_any_base(any_image.as_one().context(location_info!())?.clone())
169           .context(location_info!())?
170           .context(location_info!())?
171           .url()
172           .context(location_info!())?
173           .as_single_xsd_any_uri()
174           .map(|u| u.to_owned().into()),
175       ),
176       None => None,
177     };
178     let shared_inbox = group
179       .inner
180       .endpoints()?
181       .map(|e| e.shared_inbox)
182       .flatten()
183       .map(|s| s.to_owned().into());
184
185     Ok(CommunityForm {
186       name,
187       title,
188       description,
189       creator_id: creator.id,
190       removed: None,
191       published: group.inner.published().map(|u| u.to_owned().naive_local()),
192       updated: group.inner.updated().map(|u| u.to_owned().naive_local()),
193       deleted: None,
194       nsfw: group.ext_one.sensitive.unwrap_or(false),
195       actor_id: Some(check_object_domain(group, expected_domain)?),
196       local: false,
197       private_key: None,
198       public_key: Some(group.ext_two.to_owned().public_key.public_key_pem),
199       last_refreshed_at: Some(naive_now()),
200       icon,
201       banner,
202       followers_url: Some(
203         group
204           .inner
205           .followers()?
206           .context(location_info!())?
207           .to_owned()
208           .into(),
209       ),
210       inbox_url: Some(group.inner.inbox()?.to_owned().into()),
211       shared_inbox_url: Some(shared_inbox),
212     })
213   }
214 }