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,
9 get_source_markdown_value,
10 set_content_and_source,
18 use activitystreams::{
19 actor::{kind::GroupType, ApActor, Endpoints, Group},
21 object::{ApObject, Image, Tombstone},
24 use activitystreams_ext::Ext2;
26 use lemmy_api_structs::blocking;
27 use lemmy_db_queries::DbPool;
28 use lemmy_db_schema::{
30 source::community::{Community, CommunityForm},
32 use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView;
35 utils::{check_slurs, check_slurs_opt, convert_datetime},
38 use lemmy_websocket::LemmyContext;
41 #[async_trait::async_trait(?Send)]
42 impl ToApub for Community {
43 type ApubType = GroupExt;
45 async fn to_apub(&self, pool: &DbPool) -> Result<GroupExt, LemmyError> {
47 let moderators = blocking(pool, move |conn| {
48 CommunityModeratorView::for_community(&conn, id)
51 let moderators: Vec<Url> = moderators
53 .map(|m| m.moderator.actor_id.into_inner())
56 let mut group = ApObject::new(Group::new());
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);
65 if let Some(u) = self.updated.to_owned() {
66 group.set_updated(convert_datetime(u));
68 if let Some(d) = self.description.to_owned() {
69 set_content_and_source(&mut group, &d)?;
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()?);
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()?);
84 let mut ap_actor = ApActor::new(self.inbox_url.clone().into(), group);
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()),
96 GroupExtension::new(self.nsfw, generate_moderators_url(&self.actor_id)?.into())?,
97 self.get_public_key_ext()?,
101 fn to_tombstone(&self) -> Result<Tombstone, LemmyError> {
104 self.actor_id.to_owned().into(),
111 #[async_trait::async_trait(?Send)]
112 impl FromApub for Community {
113 type ApubType = GroupExt;
115 /// Converts a `Group` to `Community`, inserts it into the database and updates moderators.
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(
134 #[async_trait::async_trait(?Send)]
135 impl FromApubToForm<GroupExt> for CommunityForm {
138 context: &LemmyContext,
139 expected_domain: Url,
140 request_counter: &mut i32,
141 _mod_action_allowed: bool,
142 ) -> Result<Self, LemmyError> {
143 let moderator_uris = fetch_community_mods(context, group, request_counter).await?;
144 let creator = if let Some(creator_uri) = moderator_uris.first() {
145 get_or_fetch_and_upsert_person(creator_uri, context, request_counter)
147 // NOTE: code for compatibility with lemmy v0.9.9
148 let creator_uri = group
151 .map(|a| a.as_many())
155 .map(|a| a.as_xsd_any_uri())
157 .context(location_info!())?;
158 get_or_fetch_and_upsert_person(creator_uri, context, request_counter)
164 .preferred_username()
165 .context(location_info!())?
170 .context(location_info!())?
172 .context(location_info!())?
174 .context(location_info!())?
177 let description = get_source_markdown_value(group)?;
180 check_slurs(&title)?;
181 check_slurs_opt(&description)?;
183 let icon = match group.icon() {
184 Some(any_image) => Some(
185 Image::from_any_base(any_image.as_one().context(location_info!())?.clone())
186 .context(location_info!())?
187 .context(location_info!())?
189 .context(location_info!())?
190 .as_single_xsd_any_uri()
191 .map(|u| u.to_owned().into()),
195 let banner = match group.image() {
196 Some(any_image) => Some(
197 Image::from_any_base(any_image.as_one().context(location_info!())?.clone())
198 .context(location_info!())?
199 .context(location_info!())?
201 .context(location_info!())?
202 .as_single_xsd_any_uri()
203 .map(|u| u.to_owned().into()),
207 let shared_inbox = group
210 .map(|e| e.shared_inbox)
212 .map(|s| s.to_owned().into());
218 creator_id: creator.id,
220 published: group.inner.published().map(|u| u.to_owned().naive_local()),
221 updated: group.inner.updated().map(|u| u.to_owned().naive_local()),
223 nsfw: Some(group.ext_one.sensitive.unwrap_or(false)),
224 actor_id: Some(check_object_domain(group, expected_domain)?),
227 public_key: Some(group.ext_two.to_owned().public_key.public_key_pem),
228 last_refreshed_at: Some(naive_now()),
235 .context(location_info!())?
239 inbox_url: Some(group.inner.inbox()?.to_owned().into()),
240 shared_inbox_url: Some(shared_inbox),