2 extensions::{context::lemmy_context, group_extensions::GroupExtension},
3 fetcher::{community::fetch_community_mods, user::get_or_fetch_and_upsert_user},
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, Joinable};
28 use lemmy_db_schema::{
30 source::community::{Community, CommunityForm, CommunityModerator, CommunityModeratorForm},
33 use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView;
36 utils::{check_slurs, check_slurs_opt, convert_datetime},
39 use lemmy_websocket::LemmyContext;
42 #[async_trait::async_trait(?Send)]
43 impl ToApub for Community {
44 type ApubType = GroupExt;
46 async fn to_apub(&self, _pool: &DbPool) -> Result<GroupExt, LemmyError> {
47 let mut group = ApObject::new(Group::new());
49 .set_many_contexts(lemmy_context()?)
50 .set_id(self.actor_id.to_owned().into())
51 .set_name(self.title.to_owned())
52 .set_published(convert_datetime(self.published));
54 if let Some(u) = self.updated.to_owned() {
55 group.set_updated(convert_datetime(u));
57 if let Some(d) = self.description.to_owned() {
58 set_content_and_source(&mut group, &d)?;
61 if let Some(icon_url) = &self.icon {
62 let mut image = Image::new();
63 image.set_url::<Url>(icon_url.to_owned().into());
64 group.set_icon(image.into_any_base()?);
67 if let Some(banner_url) = &self.banner {
68 let mut image = Image::new();
69 image.set_url::<Url>(banner_url.to_owned().into());
70 group.set_image(image.into_any_base()?);
73 let mut ap_actor = ApActor::new(self.inbox_url.clone().into(), group);
75 .set_preferred_username(self.name.to_owned())
76 .set_outbox(self.get_outbox_url()?)
77 .set_followers(self.followers_url.clone().into())
78 .set_endpoints(Endpoints {
79 shared_inbox: Some(self.get_shared_inbox_or_inbox_url()),
85 GroupExtension::new(self.nsfw, generate_moderators_url(&self.actor_id)?.into())?,
86 self.get_public_key_ext()?,
90 fn to_tombstone(&self) -> Result<Tombstone, LemmyError> {
93 self.actor_id.to_owned().into(),
100 #[async_trait::async_trait(?Send)]
101 impl FromApub for Community {
102 type ApubType = GroupExt;
104 /// Converts a `Group` to `Community`, inserts it into the database and updates moderators.
107 context: &LemmyContext,
108 expected_domain: Url,
109 request_counter: &mut i32,
111 ) -> Result<Community, LemmyError> {
112 let community: Community = get_object_from_apub(
121 let new_moderators = fetch_community_mods(context, group, request_counter).await?;
122 let community_id = community.id;
123 let current_moderators = blocking(context.pool(), move |conn| {
124 CommunityModeratorView::for_community(&conn, community_id)
127 // Remove old mods from database which arent in the moderators collection anymore
128 for mod_user in ¤t_moderators {
129 if !new_moderators.contains(&&mod_user.moderator.actor_id.clone().into()) {
130 let community_moderator_form = CommunityModeratorForm {
131 community_id: mod_user.community.id,
132 user_id: mod_user.moderator.id,
134 blocking(context.pool(), move |conn| {
135 CommunityModerator::leave(conn, &community_moderator_form)
141 // Add new mods to database which have been added to moderators collection
142 for mod_uri in new_moderators {
143 let mod_user = get_or_fetch_and_upsert_user(&mod_uri, context, request_counter).await?;
144 let current_mod_uris: Vec<DbUrl> = current_moderators
147 .map(|c| c.moderator.actor_id.clone())
149 if !current_mod_uris.contains(&mod_user.actor_id) {
150 let community_moderator_form = CommunityModeratorForm {
151 community_id: community.id,
152 user_id: mod_user.id,
154 blocking(context.pool(), move |conn| {
155 CommunityModerator::join(conn, &community_moderator_form)
165 #[async_trait::async_trait(?Send)]
166 impl FromApubToForm<GroupExt> for CommunityForm {
169 context: &LemmyContext,
170 expected_domain: Url,
171 request_counter: &mut i32,
172 _is_mod_action: bool,
173 ) -> Result<Self, LemmyError> {
174 let moderator_uris = fetch_community_mods(context, group, request_counter).await?;
175 let creator_uri = moderator_uris.first().context(location_info!())?;
177 let creator = get_or_fetch_and_upsert_user(creator_uri, context, request_counter).await?;
180 .preferred_username()
181 .context(location_info!())?
186 .context(location_info!())?
188 .context(location_info!())?
190 .context(location_info!())?
193 let description = get_source_markdown_value(group)?;
196 check_slurs(&title)?;
197 check_slurs_opt(&description)?;
199 let icon = match group.icon() {
200 Some(any_image) => Some(
201 Image::from_any_base(any_image.as_one().context(location_info!())?.clone())
202 .context(location_info!())?
203 .context(location_info!())?
205 .context(location_info!())?
206 .as_single_xsd_any_uri()
207 .map(|u| u.to_owned().into()),
211 let banner = match group.image() {
212 Some(any_image) => Some(
213 Image::from_any_base(any_image.as_one().context(location_info!())?.clone())
214 .context(location_info!())?
215 .context(location_info!())?
217 .context(location_info!())?
218 .as_single_xsd_any_uri()
219 .map(|u| u.to_owned().into()),
223 let shared_inbox = group
226 .map(|e| e.shared_inbox)
228 .map(|s| s.to_owned().into());
234 creator_id: creator.id,
236 published: group.inner.published().map(|u| u.to_owned().naive_local()),
237 updated: group.inner.updated().map(|u| u.to_owned().naive_local()),
239 nsfw: group.ext_one.sensitive.unwrap_or(false),
240 actor_id: Some(check_object_domain(group, expected_domain)?),
243 public_key: Some(group.ext_two.to_owned().public_key.public_key_pem),
244 last_refreshed_at: Some(naive_now()),
251 .context(location_info!())?
255 inbox_url: Some(group.inner.inbox()?.to_owned().into()),
256 shared_inbox_url: Some(shared_inbox),