2 extensions::{context::lemmy_context, group_extensions::GroupExtension},
3 fetcher::user::get_or_fetch_and_upsert_user,
8 get_source_markdown_value,
9 set_content_and_source,
17 use activitystreams::{
18 actor::{kind::GroupType, ApActor, Endpoints, Group},
20 object::{ApObject, Image, Tombstone},
23 use activitystreams_ext::Ext2;
25 use lemmy_db_queries::DbPool;
26 use lemmy_db_schema::{
28 source::community::{Community, CommunityForm},
30 use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView;
31 use lemmy_structs::blocking;
34 utils::{check_slurs, check_slurs_opt, convert_datetime},
37 use lemmy_websocket::LemmyContext;
40 #[async_trait::async_trait(?Send)]
41 impl ToApub for Community {
42 type ApubType = GroupExt;
44 async fn to_apub(&self, pool: &DbPool) -> Result<GroupExt, LemmyError> {
45 // The attributed to, is an ordered vector with the creator actor_ids first,
46 // then the rest of the moderators
47 // TODO Technically the instance admins can mod the community, but lets
48 // ignore that for now
50 let moderators = blocking(pool, move |conn| {
51 CommunityModeratorView::for_community(&conn, id)
54 let moderators: Vec<Url> = moderators
56 .map(|m| m.moderator.actor_id.into_inner())
59 let mut group = ApObject::new(Group::new());
61 .set_many_contexts(lemmy_context()?)
62 .set_id(self.actor_id.to_owned().into())
63 .set_name(self.title.to_owned())
64 .set_published(convert_datetime(self.published))
65 .set_many_attributed_tos(moderators);
67 if let Some(u) = self.updated.to_owned() {
68 group.set_updated(convert_datetime(u));
70 if let Some(d) = self.description.to_owned() {
71 set_content_and_source(&mut group, &d)?;
74 if let Some(icon_url) = &self.icon {
75 let mut image = Image::new();
76 image.set_url(Url::parse(icon_url)?);
77 group.set_icon(image.into_any_base()?);
80 if let Some(banner_url) = &self.banner {
81 let mut image = Image::new();
82 image.set_url(Url::parse(banner_url)?);
83 group.set_image(image.into_any_base()?);
86 let mut ap_actor = ApActor::new(self.inbox_url.clone().into(), group);
88 .set_preferred_username(self.name.to_owned())
89 .set_outbox(self.get_outbox_url()?)
90 .set_followers(self.followers_url.clone().into())
91 .set_endpoints(Endpoints {
92 shared_inbox: Some(self.get_shared_inbox_or_inbox_url()),
97 let category_id = self.category_id;
98 let group_extension = blocking(pool, move |conn| {
99 GroupExtension::new(conn, category_id, nsfw)
106 self.get_public_key_ext()?,
110 fn to_tombstone(&self) -> Result<Tombstone, LemmyError> {
113 self.actor_id.to_owned().into(),
120 #[async_trait::async_trait(?Send)]
121 impl FromApub for Community {
122 type ApubType = GroupExt;
124 /// Converts a `Group` to `Community`.
127 context: &LemmyContext,
128 expected_domain: Url,
129 request_counter: &mut i32,
130 ) -> Result<Community, LemmyError> {
131 get_object_from_apub(group, context, expected_domain, request_counter).await
135 #[async_trait::async_trait(?Send)]
136 impl FromApubToForm<GroupExt> for CommunityForm {
139 context: &LemmyContext,
140 expected_domain: Url,
141 request_counter: &mut i32,
142 ) -> Result<Self, LemmyError> {
143 let creator_and_moderator_uris = group.inner.attributed_to().context(location_info!())?;
144 let creator_uri = creator_and_moderator_uris
146 .context(location_info!())?
149 .context(location_info!())?
151 .context(location_info!())?;
153 let creator = get_or_fetch_and_upsert_user(creator_uri, context, request_counter).await?;
156 .preferred_username()
157 .context(location_info!())?
162 .context(location_info!())?
164 .context(location_info!())?
166 .context(location_info!())?
169 let description = get_source_markdown_value(group)?;
172 check_slurs(&title)?;
173 check_slurs_opt(&description)?;
175 let icon = match group.icon() {
176 Some(any_image) => Some(
177 Image::from_any_base(any_image.as_one().context(location_info!())?.clone())
178 .context(location_info!())?
179 .context(location_info!())?
181 .context(location_info!())?
182 .as_single_xsd_any_uri()
183 .map(|u| u.to_string()),
187 let banner = match group.image() {
188 Some(any_image) => Some(
189 Image::from_any_base(any_image.as_one().context(location_info!())?.clone())
190 .context(location_info!())?
191 .context(location_info!())?
193 .context(location_info!())?
194 .as_single_xsd_any_uri()
195 .map(|u| u.to_string()),
199 let shared_inbox = group
202 .map(|e| e.shared_inbox)
204 .map(|s| s.to_owned().into());
210 category_id: group.ext_one.category.identifier.parse::<i32>()?,
211 creator_id: creator.id,
213 published: group.inner.published().map(|u| u.to_owned().naive_local()),
214 updated: group.inner.updated().map(|u| u.to_owned().naive_local()),
216 nsfw: group.ext_one.sensitive,
217 actor_id: Some(check_object_domain(group, expected_domain)?),
220 public_key: Some(group.ext_two.to_owned().public_key.public_key_pem),
221 last_refreshed_at: Some(naive_now()),
228 .context(location_info!())?
232 inbox_url: Some(group.inner.inbox()?.to_owned().into()),
233 shared_inbox_url: Some(shared_inbox),