]> Untitled Git - lemmy.git/blob - crates/apub/src/objects/community.rs
Simplify lemmy_context() function (dont return errors)
[lemmy.git] / crates / apub / src / objects / community.rs
1 use crate::{
2   extensions::{context::lemmy_context, group_extension::GroupExtension},
3   fetcher::community::fetch_community_mods,
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_api_common::blocking;
27 use lemmy_db_queries::DbPool;
28 use lemmy_db_schema::{
29   naive_now,
30   source::community::{Community, CommunityForm},
31 };
32 use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView;
33 use lemmy_utils::{
34   location_info,
35   utils::{check_slurs, check_slurs_opt, convert_datetime},
36   LemmyError,
37 };
38 use lemmy_websocket::LemmyContext;
39 use url::Url;
40
41 #[async_trait::async_trait(?Send)]
42 impl ToApub for Community {
43   type ApubType = GroupExt;
44
45   async fn to_apub(&self, pool: &DbPool) -> Result<GroupExt, LemmyError> {
46     let id = self.id;
47     let moderators = blocking(pool, move |conn| {
48       CommunityModeratorView::for_community(conn, id)
49     })
50     .await??;
51     let moderators: Vec<Url> = moderators
52       .into_iter()
53       .map(|m| m.moderator.actor_id.into_inner())
54       .collect();
55
56     let mut group = ApObject::new(Group::new());
57     group
58       .set_many_contexts(lemmy_context())
59       .set_id(self.actor_id.to_owned().into())
60       .set_name(self.title.to_owned())
61       .set_published(convert_datetime(self.published))
62       // NOTE: included attritubed_to field for compatibility with lemmy v0.9.9
63       .set_many_attributed_tos(moderators);
64
65     if let Some(u) = self.updated.to_owned() {
66       group.set_updated(convert_datetime(u));
67     }
68     if let Some(d) = self.description.to_owned() {
69       set_content_and_source(&mut group, &d)?;
70     }
71
72     if let Some(icon_url) = &self.icon {
73       let mut image = Image::new();
74       image.set_url::<Url>(icon_url.to_owned().into());
75       group.set_icon(image.into_any_base()?);
76     }
77
78     if let Some(banner_url) = &self.banner {
79       let mut image = Image::new();
80       image.set_url::<Url>(banner_url.to_owned().into());
81       group.set_image(image.into_any_base()?);
82     }
83
84     let mut ap_actor = ApActor::new(self.inbox_url.clone().into(), group);
85     ap_actor
86       .set_preferred_username(self.name.to_owned())
87       .set_outbox(self.get_outbox_url()?)
88       .set_followers(self.followers_url.clone().into())
89       .set_endpoints(Endpoints {
90         shared_inbox: Some(self.get_shared_inbox_or_inbox_url()),
91         ..Default::default()
92       });
93
94     Ok(Ext2::new(
95       ap_actor,
96       GroupExtension::new(self.nsfw, generate_moderators_url(&self.actor_id)?.into())?,
97       self.get_public_key_ext()?,
98     ))
99   }
100
101   fn to_tombstone(&self) -> Result<Tombstone, LemmyError> {
102     create_tombstone(
103       self.deleted,
104       self.actor_id.to_owned().into(),
105       self.updated,
106       GroupType::Group,
107     )
108   }
109 }
110
111 #[async_trait::async_trait(?Send)]
112 impl FromApub for Community {
113   type ApubType = GroupExt;
114
115   /// Converts a `Group` to `Community`, inserts it into the database and updates moderators.
116   async fn from_apub(
117     group: &GroupExt,
118     context: &LemmyContext,
119     expected_domain: Url,
120     request_counter: &mut i32,
121     mod_action_allowed: bool,
122   ) -> Result<Community, LemmyError> {
123     get_object_from_apub(
124       group,
125       context,
126       expected_domain,
127       request_counter,
128       mod_action_allowed,
129     )
130     .await
131   }
132 }
133
134 #[async_trait::async_trait(?Send)]
135 impl FromApubToForm<GroupExt> for CommunityForm {
136   async fn from_apub(
137     group: &GroupExt,
138     context: &LemmyContext,
139     expected_domain: Url,
140     request_counter: &mut i32,
141     _mod_action_allowed: bool,
142   ) -> Result<Self, LemmyError> {
143     fetch_community_mods(context, group, request_counter).await?;
144
145     let name = group
146       .inner
147       .preferred_username()
148       .context(location_info!())?
149       .to_string();
150     let title = group
151       .inner
152       .name()
153       .context(location_info!())?
154       .as_one()
155       .context(location_info!())?
156       .as_xsd_string()
157       .context(location_info!())?
158       .to_string();
159
160     let description = get_source_markdown_value(group)?;
161
162     check_slurs(&name)?;
163     check_slurs(&title)?;
164     check_slurs_opt(&description)?;
165
166     let icon = match group.icon() {
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 banner = match group.image() {
179       Some(any_image) => Some(
180         Image::from_any_base(any_image.as_one().context(location_info!())?.clone())
181           .context(location_info!())?
182           .context(location_info!())?
183           .url()
184           .context(location_info!())?
185           .as_single_xsd_any_uri()
186           .map(|u| u.to_owned().into()),
187       ),
188       None => None,
189     };
190     let shared_inbox = group
191       .inner
192       .endpoints()?
193       .map(|e| e.shared_inbox)
194       .flatten()
195       .map(|s| s.to_owned().into());
196
197     Ok(CommunityForm {
198       name,
199       title,
200       description,
201       removed: None,
202       published: group.inner.published().map(|u| u.to_owned().naive_local()),
203       updated: group.inner.updated().map(|u| u.to_owned().naive_local()),
204       deleted: None,
205       nsfw: Some(group.ext_one.sensitive.unwrap_or(false)),
206       actor_id: Some(check_object_domain(group, expected_domain, true)?),
207       local: Some(false),
208       private_key: None,
209       public_key: Some(group.ext_two.to_owned().public_key.public_key_pem),
210       last_refreshed_at: Some(naive_now()),
211       icon,
212       banner,
213       followers_url: Some(
214         group
215           .inner
216           .followers()?
217           .context(location_info!())?
218           .to_owned()
219           .into(),
220       ),
221       inbox_url: Some(group.inner.inbox()?.to_owned().into()),
222       shared_inbox_url: Some(shared_inbox),
223     })
224   }
225 }