use anyhow::Context;
use lemmy_api_structs::{blocking, community::*};
use lemmy_apub::{
+ activities::send::community::{send_add_mod, send_remove_mod},
generate_apub_endpoint,
generate_followers_url,
generate_inbox_url,
};
use lemmy_db_schema::{
naive_now,
- source::{comment::Comment, community::*, moderator::*, post::Post, site::*},
+ source::{comment::Comment, community::*, moderator::*, post::Post, site::*, user::User_},
};
use lemmy_db_views::comment_view::CommentQueryBuilder;
use lemmy_db_views_actor::{
let data: &AddModToCommunity = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?;
- let community_moderator_form = CommunityModeratorForm {
- community_id: data.community_id,
- user_id: data.user_id,
- };
-
let community_id = data.community_id;
// Verify that only mods or admins can add mod
is_mod_or_admin(context.pool(), user.id, community_id).await?;
+ // Update in local database
+ let community_moderator_form = CommunityModeratorForm {
+ community_id: data.community_id,
+ user_id: data.user_id,
+ };
if data.added {
let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
if blocking(context.pool(), join).await?.is_err() {
})
.await??;
+ // Send to federated instances
+ let updated_mod_id = data.user_id;
+ let updated_mod = blocking(context.pool(), move |conn| {
+ User_::read(conn, updated_mod_id)
+ })
+ .await??;
+ let community = blocking(context.pool(), move |conn| {
+ Community::read(conn, community_id)
+ })
+ .await??;
+ dbg!(data.added);
+ if data.added {
+ send_add_mod(user, updated_mod, community, context).await?;
+ } else {
+ send_remove_mod(user, updated_mod, community, context).await?;
+ }
+
+ // Note: in case a remote mod is added, this returns the old moderators list, it will only get
+ // updated once we receive an activity from the community (like `Announce/Add/Moderator`)
let community_id = data.community_id;
let moderators = blocking(context.pool(), move |conn| {
CommunityModeratorView::for_community(conn, community_id)
.await??;
let res = AddModToCommunityResponse { moderators };
-
context.chat_server().do_send(SendCommunityRoomMessage {
op: UserOperation::AddModToCommunity,
response: res.clone(),
community_id,
websocket_id,
});
-
Ok(res)
}
}
+// TODO: we dont do anything for federation here, it should be updated the next time the community
+// gets fetched. i hope we can get rid of the community creator role soon.
#[async_trait::async_trait(?Send)]
impl Perform for TransferCommunity {
type Response = GetCommunityResponse;
pub(crate) mod receive;
-pub(crate) mod send;
+pub mod send;
use crate::{
activities::send::generate_activity_id,
- activity_queue::{send_activity_single_dest, send_to_community_followers},
+ activity_queue::{send_activity_single_dest, send_to_community, send_to_community_followers},
check_is_apub_id_valid,
extensions::context::lemmy_context,
fetcher::user::get_or_fetch_and_upsert_user,
+ generate_moderators_url,
ActorType,
};
use activitystreams::{
activity::{
- kind::{AcceptType, AnnounceType, DeleteType, LikeType, RemoveType, UndoType},
+ kind::{AcceptType, AddType, AnnounceType, DeleteType, LikeType, RemoveType, UndoType},
Accept,
ActorAndObjectRefExt,
+ Add,
Announce,
Delete,
Follow,
+ OptTargetRefExt,
Remove,
Undo,
},
use itertools::Itertools;
use lemmy_api_structs::blocking;
use lemmy_db_queries::DbPool;
-use lemmy_db_schema::source::community::Community;
+use lemmy_db_schema::source::{community::Community, user::User_};
use lemmy_db_views_actor::community_follower_view::CommunityFollowerView;
use lemmy_utils::{location_info, LemmyError};
use lemmy_websocket::LemmyContext;
Ok(inboxes)
}
}
+
+pub async fn send_add_mod(
+ actor: User_,
+ added_mod: User_,
+ community: Community,
+ context: &LemmyContext,
+) -> Result<(), LemmyError> {
+ let mut add = Add::new(
+ actor.actor_id.clone().into_inner(),
+ added_mod.actor_id.into_inner(),
+ );
+ add
+ .set_many_contexts(lemmy_context()?)
+ .set_id(generate_activity_id(AddType::Add)?)
+ .set_many_tos(vec![community.actor_id.to_owned().into_inner(), public()])
+ .set_target(generate_moderators_url(&community.actor_id)?.into_inner());
+
+ if community.local {
+ community
+ .send_announce(add.into_any_base()?, context)
+ .await?;
+ } else {
+ send_to_community(add, &actor, &community, context).await?;
+ }
+ Ok(())
+}
+
+pub async fn send_remove_mod(
+ actor: User_,
+ removed_mod: User_,
+ community: Community,
+ context: &LemmyContext,
+) -> Result<(), LemmyError> {
+ let mut remove = Remove::new(
+ actor.actor_id.clone().into_inner(),
+ removed_mod.actor_id.into_inner(),
+ );
+ remove
+ .set_many_contexts(lemmy_context()?)
+ .set_id(generate_activity_id(RemoveType::Remove)?)
+ .set_many_tos(vec![community.actor_id.to_owned().into_inner(), public()])
+ .set_target(generate_moderators_url(&community.actor_id)?.into_inner());
+
+ if community.local {
+ community
+ .send_announce(remove.into_any_base()?, context)
+ .await?;
+ } else {
+ send_to_community(remove, &actor, &community, context).await?;
+ }
+ Ok(())
+}
use uuid::Uuid;
pub(crate) mod comment;
-pub(crate) mod community;
+pub mod community;
pub(crate) mod post;
pub(crate) mod private_message;
pub(crate) mod user;
},
find_post_or_comment_by_id,
inbox::is_addressed_to_public,
+ ActorType,
PostOrComment,
};
use activitystreams::{
use anyhow::{anyhow, Context};
use diesel::result::Error::NotFound;
use lemmy_api_structs::blocking;
-use lemmy_db_queries::{ApubObject, Crud, Joinable};
+use lemmy_db_queries::{source::community::CommunityModerator_, ApubObject, Crud, Joinable};
use lemmy_db_schema::{
source::{
community::{Community, CommunityModerator, CommunityModeratorForm},
expected_domain: &Url,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- let remove = Remove::from_any_base(activity)?.context(location_info!())?;
+ let remove = Remove::from_any_base(activity.to_owned())?.context(location_info!())?;
verify_activity_domains_valid(&remove, &expected_domain, false)?;
is_addressed_to_public(&remove)?;
CommunityModerator::leave(conn, &form)
})
.await??;
+ community.send_announce(activity, context).await?;
+ // TODO: send websocket notification about removed mod
Ok(())
}
// Remove a post or comment
expected_domain: &Url,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- let add = Add::from_any_base(activity)?.context(location_info!())?;
+ let add = Add::from_any_base(activity.to_owned())?.context(location_info!())?;
verify_activity_domains_valid(&add, &expected_domain, false)?;
is_addressed_to_public(&add)?;
let community = verify_actor_is_community_mod(&add, context).await?;
.as_single_xsd_any_uri()
.context(location_info!())?;
let new_mod = get_or_fetch_and_upsert_user(&new_mod, context, request_counter).await?;
- let form = CommunityModeratorForm {
- community_id: community.id,
- user_id: new_mod.id,
- };
- blocking(context.pool(), move |conn| {
- CommunityModerator::join(conn, &form)
+
+ // If we had to refetch the community while parsing the activity, then the new mod has already
+ // been added. Skip it here as it would result in a duplicate key error.
+ let new_mod_id = new_mod.id;
+ let moderated_communities = blocking(context.pool(), move |conn| {
+ CommunityModerator::get_user_moderated_communities(conn, new_mod_id)
})
.await??;
+ if moderated_communities.contains(&community.id) {
+ let form = CommunityModeratorForm {
+ community_id: community.id,
+ user_id: new_mod.id,
+ };
+ blocking(context.pool(), move |conn| {
+ CommunityModerator::join(conn, &form)
+ })
+ .await??;
+ }
+ if community.local {
+ community.send_announce(activity, context).await?;
+ }
+ // TODO: send websocket notification about added mod
Ok(())
}
// should be the moderators collection of a local community
let target = activity
.target()
- .map(|t| t.as_single_xsd_string())
+ .map(|t| t.as_single_xsd_any_uri())
.flatten()
.context(location_info!())?;
// TODO: very hacky, we should probably store the moderators url in db
Undo,
Remove,
Announce,
+ Add,
}
// TODO: this isnt entirely correct, cause some of these receive are not ActorAndObject,
is_addressed_to_local_user,
is_addressed_to_public,
receive_for_community::{
+ receive_add_for_community,
receive_create_for_community,
receive_delete_for_community,
receive_dislike_for_community,
Delete,
Remove,
Undo,
+ Add,
}
/// Takes an announce and passes the inner activity to the appropriate handler.
Some(Undo) => {
receive_undo_for_community(context, inner_activity, &inner_id, request_counter).await
}
+ Some(Add) => {
+ receive_add_for_community(context, inner_activity, &inner_id, request_counter).await
+ }
_ => receive_unhandled_activity(inner_activity),
}
}