Use collection for moderators, instead of attributedTo (ref #1061)

This commit is contained in:
Felix Ableitner 2021-03-05 14:45:30 +01:00
parent 8f6b8895f4
commit beb8b9fe69
6 changed files with 95 additions and 68 deletions

View file

@ -11,7 +11,8 @@ pub(crate) fn lemmy_context() -> Result<Vec<AnyBase>, LemmyError> {
"comments_enabled": { "comments_enabled": {
"kind": "sc:Boolean", "kind": "sc:Boolean",
"id": "pt:commentsEnabled" "id": "pt:commentsEnabled"
} },
"moderators": "as:moderators"
}))?; }))?;
Ok(vec![AnyBase::from(context()), context_ext]) Ok(vec![AnyBase::from(context()), context_ext])
} }

View file

@ -1,7 +1,11 @@
use activitystreams::unparsed::UnparsedMutExt; use activitystreams::{
collection::{CollectionExt, OrderedCollection},
unparsed::UnparsedMutExt,
};
use activitystreams_ext::UnparsedExtension; use activitystreams_ext::UnparsedExtension;
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use url::Url;
/// Activitystreams extension to allow (de)serializing additional Community field /// Activitystreams extension to allow (de)serializing additional Community field
/// `sensitive` (called 'nsfw' in Lemmy). /// `sensitive` (called 'nsfw' in Lemmy).
@ -9,12 +13,17 @@ use serde::{Deserialize, Serialize};
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct GroupExtension { pub struct GroupExtension {
pub sensitive: Option<bool>, pub sensitive: Option<bool>,
pub moderators: Option<OrderedCollection>,
} }
impl GroupExtension { impl GroupExtension {
pub fn new(sensitive: bool) -> Result<GroupExtension, LemmyError> { pub fn new(sensitive: bool, moderators: Vec<Url>) -> Result<GroupExtension, LemmyError> {
let mut mods = OrderedCollection::new();
mods.set_total_items(moderators.len() as u64);
mods.set_many_items(moderators);
Ok(GroupExtension { Ok(GroupExtension {
sensitive: Some(sensitive), sensitive: Some(sensitive),
moderators: Some(mods),
}) })
} }
} }
@ -28,11 +37,13 @@ where
fn try_from_unparsed(unparsed_mut: &mut U) -> Result<Self, Self::Error> { fn try_from_unparsed(unparsed_mut: &mut U) -> Result<Self, Self::Error> {
Ok(GroupExtension { Ok(GroupExtension {
sensitive: unparsed_mut.remove("sensitive")?, sensitive: unparsed_mut.remove("sensitive")?,
moderators: unparsed_mut.remove("moderators")?,
}) })
} }
fn try_into_unparsed(self, unparsed_mut: &mut U) -> Result<(), Self::Error> { fn try_into_unparsed(self, unparsed_mut: &mut U) -> Result<(), Self::Error> {
unparsed_mut.insert("sensitive", self.sensitive)?; unparsed_mut.insert("sensitive", self.sensitive)?;
unparsed_mut.insert("moderators", self.moderators)?;
Ok(()) Ok(())
} }
} }

View file

@ -1,10 +1,5 @@
use crate::{ use crate::{
fetcher::{ fetcher::{fetch::fetch_remote_object, is_deleted, should_refetch_actor},
fetch::fetch_remote_object,
get_or_fetch_and_upsert_user,
is_deleted,
should_refetch_actor,
},
inbox::user_inbox::receive_announce, inbox::user_inbox::receive_announce,
objects::FromApub, objects::FromApub,
GroupExt, GroupExt,
@ -12,13 +7,12 @@ use crate::{
use activitystreams::{ use activitystreams::{
actor::ApActorExt, actor::ApActorExt,
collection::{CollectionExt, OrderedCollection}, collection::{CollectionExt, OrderedCollection},
object::ObjectExt,
}; };
use anyhow::Context; use anyhow::Context;
use diesel::result::Error::NotFound; use diesel::result::Error::NotFound;
use lemmy_api_structs::blocking; use lemmy_api_structs::blocking;
use lemmy_db_queries::{source::community::Community_, ApubObject, Joinable}; use lemmy_db_queries::{source::community::Community_, ApubObject};
use lemmy_db_schema::source::community::{Community, CommunityModerator, CommunityModeratorForm}; use lemmy_db_schema::source::community::Community;
use lemmy_utils::{location_info, LemmyError}; use lemmy_utils::{location_info, LemmyError};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use log::debug; use log::debug;
@ -80,40 +74,6 @@ async fn fetch_remote_community(
let community = let community =
Community::from_apub(&group, context, apub_id.to_owned(), recursion_counter).await?; Community::from_apub(&group, context, apub_id.to_owned(), recursion_counter).await?;
// Also add the community moderators too
let attributed_to = group.inner.attributed_to().context(location_info!())?;
let creator_and_moderator_uris: Vec<&Url> = attributed_to
.as_many()
.context(location_info!())?
.iter()
.map(|a| a.as_xsd_any_uri().context(""))
.collect::<Result<Vec<&Url>, anyhow::Error>>()?;
let mut creator_and_moderators = Vec::new();
for uri in creator_and_moderator_uris {
let c_or_m = get_or_fetch_and_upsert_user(uri, context, recursion_counter).await?;
creator_and_moderators.push(c_or_m);
}
// TODO: need to make this work to update mods of existing communities
if old_community.is_none() {
let community_id = community.id;
blocking(context.pool(), move |conn| {
for mod_ in creator_and_moderators {
let community_moderator_form = CommunityModeratorForm {
community_id,
user_id: mod_.id,
};
CommunityModerator::join(conn, &community_moderator_form)?;
}
Ok(()) as Result<(), LemmyError>
})
.await??;
}
// only fetch outbox for new communities, otherwise this can create an infinite loop // only fetch outbox for new communities, otherwise this can create an infinite loop
if old_community.is_none() { if old_community.is_none() {
let outbox = group.inner.outbox()?.context(location_info!())?; let outbox = group.inner.outbox()?.context(location_info!())?;

View file

@ -23,10 +23,11 @@ use activitystreams::{
use activitystreams_ext::Ext2; use activitystreams_ext::Ext2;
use anyhow::Context; use anyhow::Context;
use lemmy_api_structs::blocking; use lemmy_api_structs::blocking;
use lemmy_db_queries::DbPool; use lemmy_db_queries::{DbPool, Joinable};
use lemmy_db_schema::{ use lemmy_db_schema::{
naive_now, naive_now,
source::community::{Community, CommunityForm}, source::community::{Community, CommunityForm, CommunityModerator, CommunityModeratorForm},
DbUrl,
}; };
use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView; use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView;
use lemmy_utils::{ use lemmy_utils::{
@ -51,18 +52,13 @@ impl ToApub for Community {
CommunityModeratorView::for_community(&conn, id) CommunityModeratorView::for_community(&conn, id)
}) })
.await??; .await??;
let moderators: Vec<Url> = moderators
.into_iter()
.map(|m| m.moderator.actor_id.into_inner())
.collect();
let mut group = ApObject::new(Group::new()); let mut group = ApObject::new(Group::new());
group group
.set_many_contexts(lemmy_context()?) .set_many_contexts(lemmy_context()?)
.set_id(self.actor_id.to_owned().into()) .set_id(self.actor_id.to_owned().into())
.set_name(self.title.to_owned()) .set_name(self.title.to_owned())
.set_published(convert_datetime(self.published)) .set_published(convert_datetime(self.published));
.set_many_attributed_tos(moderators);
if let Some(u) = self.updated.to_owned() { if let Some(u) = self.updated.to_owned() {
group.set_updated(convert_datetime(u)); group.set_updated(convert_datetime(u));
@ -93,9 +89,14 @@ impl ToApub for Community {
..Default::default() ..Default::default()
}); });
let moderators: Vec<Url> = moderators
.into_iter()
.map(|m| m.moderator.actor_id.into_inner())
.collect();
Ok(Ext2::new( Ok(Ext2::new(
ap_actor, ap_actor,
GroupExtension::new(self.nsfw)?, GroupExtension::new(self.nsfw, moderators)?,
self.get_public_key_ext()?, self.get_public_key_ext()?,
)) ))
} }
@ -114,14 +115,57 @@ impl ToApub for Community {
impl FromApub for Community { impl FromApub for Community {
type ApubType = GroupExt; type ApubType = GroupExt;
/// Converts a `Group` to `Community`. /// Converts a `Group` to `Community`, inserts it into the database and updates moderators.
async fn from_apub( async fn from_apub(
group: &GroupExt, group: &GroupExt,
context: &LemmyContext, context: &LemmyContext,
expected_domain: Url, expected_domain: Url,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<Community, LemmyError> { ) -> Result<Community, LemmyError> {
get_object_from_apub(group, context, expected_domain, request_counter).await let community: Community =
get_object_from_apub(group, context, expected_domain, request_counter).await?;
let new_moderators = get_community_moderators(group)?;
let community_id = community.id;
let current_moderators = blocking(context.pool(), move |conn| {
CommunityModeratorView::for_community(&conn, community_id)
})
.await??;
// Remove old mods from database which arent in the moderators collection anymore
for mod_user in &current_moderators {
if !new_moderators.contains(&&mod_user.moderator.actor_id.clone().into()) {
let community_moderator_form = CommunityModeratorForm {
community_id: mod_user.community.id,
user_id: mod_user.moderator.id,
};
blocking(context.pool(), move |conn| {
CommunityModerator::leave(conn, &community_moderator_form)
})
.await??;
}
}
// Add new mods to database which have been added to moderators collection
for mod_uri in new_moderators {
let mod_user = get_or_fetch_and_upsert_user(&mod_uri, context, request_counter).await?;
let current_mod_uris: Vec<DbUrl> = current_moderators
.clone()
.iter()
.map(|c| c.moderator.actor_id.clone())
.collect();
if !current_mod_uris.contains(&mod_user.actor_id) {
let community_moderator_form = CommunityModeratorForm {
community_id: community.id,
user_id: mod_user.id,
};
blocking(context.pool(), move |conn| {
CommunityModerator::join(conn, &community_moderator_form)
})
.await??;
}
}
Ok(community)
} }
} }
@ -133,15 +177,8 @@ impl FromApubToForm<GroupExt> for CommunityForm {
expected_domain: Url, expected_domain: Url,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<Self, LemmyError> { ) -> Result<Self, LemmyError> {
let creator_and_moderator_uris = group.inner.attributed_to().context(location_info!())?; let moderator_uris = get_community_moderators(group)?;
let creator_uri = creator_and_moderator_uris let creator_uri = moderator_uris.first().context(location_info!())?;
.as_many()
.context(location_info!())?
.iter()
.next()
.context(location_info!())?
.as_xsd_any_uri()
.context(location_info!())?;
let creator = get_or_fetch_and_upsert_user(creator_uri, context, request_counter).await?; let creator = get_or_fetch_and_upsert_user(creator_uri, context, request_counter).await?;
let name = group let name = group
@ -226,3 +263,20 @@ impl FromApubToForm<GroupExt> for CommunityForm {
}) })
} }
} }
fn get_community_moderators(group: &GroupExt) -> Result<Vec<&Url>, LemmyError> {
if let Some(moderators) = &group.ext_one.moderators {
Ok(
moderators
.items()
.map(|i| i.as_many())
.flatten()
.context(location_info!())?
.iter()
.filter_map(|i| i.as_xsd_any_uri())
.collect(),
)
} else {
Ok(vec![])
}
}

View file

@ -29,7 +29,7 @@ services:
- ./volumes/pictrs_alpha:/mnt - ./volumes/pictrs_alpha:/mnt
lemmy-alpha-ui: lemmy-alpha-ui:
image: dessalines/lemmy-ui:0.9.9 image: lemmy-ui:test
environment: environment:
- LEMMY_INTERNAL_HOST=lemmy-alpha:8541 - LEMMY_INTERNAL_HOST=lemmy-alpha:8541
- LEMMY_EXTERNAL_HOST=localhost:8541 - LEMMY_EXTERNAL_HOST=localhost:8541
@ -58,7 +58,7 @@ services:
- ./volumes/postgres_alpha:/var/lib/postgresql/data - ./volumes/postgres_alpha:/var/lib/postgresql/data
lemmy-beta-ui: lemmy-beta-ui:
image: dessalines/lemmy-ui:0.9.9 image: lemmy-ui:test
environment: environment:
- LEMMY_INTERNAL_HOST=lemmy-beta:8551 - LEMMY_INTERNAL_HOST=lemmy-beta:8551
- LEMMY_EXTERNAL_HOST=localhost:8551 - LEMMY_EXTERNAL_HOST=localhost:8551

View file

@ -1,4 +1,5 @@
{ {
hostname: lemmy-alpha:8541
port: 8541 port: 8541
tls_enabled: false tls_enabled: false
jwt_secret: changeme jwt_secret: changeme