mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-11-15 08:54:01 +00:00
Remote mods can update/delete/undelete communities
This commit is contained in:
parent
dee02e6642
commit
c572dc0cc6
8 changed files with 327 additions and 94 deletions
|
@ -52,9 +52,13 @@ impl PerformCrud for DeleteCommunity {
|
||||||
|
|
||||||
// Send apub messages
|
// Send apub messages
|
||||||
if deleted {
|
if deleted {
|
||||||
updated_community.send_delete(context).await?;
|
updated_community
|
||||||
|
.send_delete(local_user_view.person.to_owned(), context)
|
||||||
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
updated_community.send_undo_delete(context).await?;
|
updated_community
|
||||||
|
.send_undo_delete(local_user_view.person.to_owned(), context)
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let community_id = data.community_id;
|
let community_id = data.community_id;
|
||||||
|
|
|
@ -5,6 +5,7 @@ use lemmy_api_common::{
|
||||||
community::{CommunityResponse, EditCommunity},
|
community::{CommunityResponse, EditCommunity},
|
||||||
get_local_user_view_from_jwt,
|
get_local_user_view_from_jwt,
|
||||||
};
|
};
|
||||||
|
use lemmy_apub::CommunityType;
|
||||||
use lemmy_db_queries::{diesel_option_overwrite_to_url, Crud};
|
use lemmy_db_queries::{diesel_option_overwrite_to_url, Crud};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
naive_now,
|
naive_now,
|
||||||
|
@ -70,17 +71,15 @@ impl PerformCrud for EditCommunity {
|
||||||
};
|
};
|
||||||
|
|
||||||
let community_id = data.community_id;
|
let community_id = data.community_id;
|
||||||
match blocking(context.pool(), move |conn| {
|
let updated_community = blocking(context.pool(), move |conn| {
|
||||||
Community::update(conn, community_id, &community_form)
|
Community::update(conn, community_id, &community_form)
|
||||||
})
|
})
|
||||||
.await?
|
.await?
|
||||||
{
|
.map_err(|_| ApiError::err("couldnt_update_community"))?;
|
||||||
Ok(community) => community,
|
|
||||||
Err(_e) => return Err(ApiError::err("couldnt_update_community").into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO there needs to be some kind of an apub update
|
updated_community
|
||||||
// process for communities and users
|
.send_update(local_user_view.person.to_owned(), context)
|
||||||
|
.await?;
|
||||||
|
|
||||||
let community_id = data.community_id;
|
let community_id = data.community_id;
|
||||||
let person_id = local_user_view.person.id;
|
let person_id = local_user_view.person.id;
|
||||||
|
|
|
@ -6,6 +6,7 @@ use crate::{
|
||||||
fetcher::{get_or_fetch_and_upsert_actor, person::get_or_fetch_and_upsert_person},
|
fetcher::{get_or_fetch_and_upsert_actor, person::get_or_fetch_and_upsert_person},
|
||||||
generate_moderators_url,
|
generate_moderators_url,
|
||||||
insert_activity,
|
insert_activity,
|
||||||
|
objects::ToApub,
|
||||||
ActorType,
|
ActorType,
|
||||||
CommunityType,
|
CommunityType,
|
||||||
};
|
};
|
||||||
|
@ -20,6 +21,7 @@ use activitystreams::{
|
||||||
LikeType,
|
LikeType,
|
||||||
RemoveType,
|
RemoveType,
|
||||||
UndoType,
|
UndoType,
|
||||||
|
UpdateType,
|
||||||
},
|
},
|
||||||
Accept,
|
Accept,
|
||||||
ActorAndObjectRefExt,
|
ActorAndObjectRefExt,
|
||||||
|
@ -31,6 +33,7 @@ use activitystreams::{
|
||||||
OptTargetRefExt,
|
OptTargetRefExt,
|
||||||
Remove,
|
Remove,
|
||||||
Undo,
|
Undo,
|
||||||
|
Update,
|
||||||
},
|
},
|
||||||
base::{AnyBase, BaseExt, ExtendsExt},
|
base::{AnyBase, BaseExt, ExtendsExt},
|
||||||
object::ObjectExt,
|
object::ObjectExt,
|
||||||
|
@ -101,36 +104,95 @@ impl CommunityType for Community {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If the creator of a community deletes the community, send this to all followers.
|
/// If a remote community is updated by a local mod, send the updated info to the community's
|
||||||
async fn send_delete(&self, context: &LemmyContext) -> Result<(), LemmyError> {
|
/// instance.
|
||||||
let mut delete = Delete::new(self.actor_id(), self.actor_id());
|
async fn send_update(&self, mod_: Person, context: &LemmyContext) -> Result<(), LemmyError> {
|
||||||
delete
|
if self.local {
|
||||||
.set_many_contexts(lemmy_context()?)
|
// Do nothing, other instances will automatically refetch the community
|
||||||
.set_id(generate_activity_id(DeleteType::Delete)?)
|
} else {
|
||||||
.set_to(public())
|
let mut update = Update::new(
|
||||||
.set_many_ccs(vec![self.followers_url()]);
|
mod_.actor_id(),
|
||||||
|
self.to_apub(context.pool()).await?.into_any_base()?,
|
||||||
|
);
|
||||||
|
update
|
||||||
|
.set_many_contexts(lemmy_context()?)
|
||||||
|
.set_id(generate_activity_id(UpdateType::Update)?)
|
||||||
|
.set_to(public())
|
||||||
|
.set_many_ccs(vec![self.actor_id()]);
|
||||||
|
send_to_community(update, &mod_, self, None, context).await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
send_to_community_followers(delete, self, None, context).await?;
|
/// If the creator of a community deletes the community, send this to all followers.
|
||||||
|
///
|
||||||
|
/// We need to handle deletion by a remote mod separately.
|
||||||
|
async fn send_delete(&self, mod_: Person, context: &LemmyContext) -> Result<(), LemmyError> {
|
||||||
|
// Local mod, send directly from community to followers
|
||||||
|
if self.local {
|
||||||
|
let mut delete = Delete::new(self.actor_id(), self.actor_id());
|
||||||
|
delete
|
||||||
|
.set_many_contexts(lemmy_context()?)
|
||||||
|
.set_id(generate_activity_id(DeleteType::Delete)?)
|
||||||
|
.set_to(public())
|
||||||
|
.set_many_ccs(vec![self.followers_url()]);
|
||||||
|
|
||||||
|
send_to_community_followers(delete, self, None, context).await?;
|
||||||
|
}
|
||||||
|
// Remote mod, send from mod to community
|
||||||
|
else {
|
||||||
|
let mut delete = Delete::new(mod_.actor_id(), self.actor_id());
|
||||||
|
delete
|
||||||
|
.set_many_contexts(lemmy_context()?)
|
||||||
|
.set_id(generate_activity_id(DeleteType::Delete)?)
|
||||||
|
.set_to(public())
|
||||||
|
.set_many_ccs(vec![self.actor_id()]);
|
||||||
|
|
||||||
|
send_to_community(delete, &mod_, self, None, context).await?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If the creator of a community reverts the deletion of a community, send this to all followers.
|
/// If the creator of a community reverts the deletion of a community, send this to all followers.
|
||||||
async fn send_undo_delete(&self, context: &LemmyContext) -> Result<(), LemmyError> {
|
///
|
||||||
let mut delete = Delete::new(self.actor_id(), self.actor_id());
|
/// We need to handle undelete by a remote mod separately.
|
||||||
delete
|
async fn send_undo_delete(&self, mod_: Person, context: &LemmyContext) -> Result<(), LemmyError> {
|
||||||
.set_many_contexts(lemmy_context()?)
|
// Local mod, send directly from community to followers
|
||||||
.set_id(generate_activity_id(DeleteType::Delete)?)
|
if self.local {
|
||||||
.set_to(public())
|
let mut delete = Delete::new(self.actor_id(), self.actor_id());
|
||||||
.set_many_ccs(vec![self.followers_url()]);
|
delete
|
||||||
|
.set_many_contexts(lemmy_context()?)
|
||||||
|
.set_id(generate_activity_id(DeleteType::Delete)?)
|
||||||
|
.set_to(public())
|
||||||
|
.set_many_ccs(vec![self.followers_url()]);
|
||||||
|
|
||||||
let mut undo = Undo::new(self.actor_id(), delete.into_any_base()?);
|
let mut undo = Undo::new(self.actor_id(), delete.into_any_base()?);
|
||||||
undo
|
undo
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context()?)
|
||||||
.set_id(generate_activity_id(UndoType::Undo)?)
|
.set_id(generate_activity_id(UndoType::Undo)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(vec![self.followers_url()]);
|
.set_many_ccs(vec![self.followers_url()]);
|
||||||
|
|
||||||
send_to_community_followers(undo, self, None, context).await?;
|
send_to_community_followers(undo, self, None, context).await?;
|
||||||
|
}
|
||||||
|
// Remote mod, send from mod to community
|
||||||
|
else {
|
||||||
|
let mut delete = Delete::new(mod_.actor_id(), self.actor_id());
|
||||||
|
delete
|
||||||
|
.set_many_contexts(lemmy_context()?)
|
||||||
|
.set_id(generate_activity_id(DeleteType::Delete)?)
|
||||||
|
.set_to(public())
|
||||||
|
.set_many_ccs(vec![self.actor_id()]);
|
||||||
|
|
||||||
|
let mut undo = Undo::new(mod_.actor_id(), delete.into_any_base()?);
|
||||||
|
undo
|
||||||
|
.set_many_contexts(lemmy_context()?)
|
||||||
|
.set_id(generate_activity_id(UndoType::Undo)?)
|
||||||
|
.set_to(public())
|
||||||
|
.set_many_ccs(vec![self.actor_id()]);
|
||||||
|
|
||||||
|
send_to_community(undo, &mod_, self, None, context).await?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,14 @@ pub mod extensions;
|
||||||
pub mod fetcher;
|
pub mod fetcher;
|
||||||
pub mod objects;
|
pub mod objects;
|
||||||
|
|
||||||
use crate::extensions::{
|
use crate::{
|
||||||
group_extension::GroupExtension,
|
extensions::{
|
||||||
page_extension::PageExtension,
|
group_extension::GroupExtension,
|
||||||
person_extension::PersonExtension,
|
page_extension::PageExtension,
|
||||||
signatures::{PublicKey, PublicKeyExtension},
|
person_extension::PersonExtension,
|
||||||
|
signatures::{PublicKey, PublicKeyExtension},
|
||||||
|
},
|
||||||
|
fetcher::community::get_or_fetch_and_upsert_community,
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
activity::Follow,
|
activity::Follow,
|
||||||
|
@ -44,7 +47,8 @@ use std::net::IpAddr;
|
||||||
use url::{ParseError, Url};
|
use url::{ParseError, Url};
|
||||||
|
|
||||||
/// Activitystreams type for community
|
/// Activitystreams type for community
|
||||||
type GroupExt = Ext2<actor::ApActor<ApObject<actor::Group>>, GroupExtension, PublicKeyExtension>;
|
pub type GroupExt =
|
||||||
|
Ext2<actor::ApActor<ApObject<actor::Group>>, GroupExtension, PublicKeyExtension>;
|
||||||
/// Activitystreams type for person
|
/// Activitystreams type for person
|
||||||
type PersonExt = Ext2<actor::ApActor<ApObject<actor::Person>>, PersonExtension, PublicKeyExtension>;
|
type PersonExt = Ext2<actor::ApActor<ApObject<actor::Person>>, PersonExtension, PublicKeyExtension>;
|
||||||
/// Activitystreams type for post
|
/// Activitystreams type for post
|
||||||
|
@ -171,9 +175,11 @@ pub trait ActorType {
|
||||||
/// Outbox URL is not generally used by Lemmy, so it can be generated on the fly (but only for
|
/// Outbox URL is not generally used by Lemmy, so it can be generated on the fly (but only for
|
||||||
/// local actors).
|
/// local actors).
|
||||||
fn get_outbox_url(&self) -> Result<Url, LemmyError> {
|
fn get_outbox_url(&self) -> Result<Url, LemmyError> {
|
||||||
|
/* TODO
|
||||||
if !self.is_local() {
|
if !self.is_local() {
|
||||||
return Err(anyhow!("get_outbox_url() called for remote actor").into());
|
return Err(anyhow!("get_outbox_url() called for remote actor").into());
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
Ok(Url::parse(&format!("{}/outbox", &self.actor_id()))?)
|
Ok(Url::parse(&format!("{}/outbox", &self.actor_id()))?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,8 +205,9 @@ pub trait CommunityType {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
) -> Result<(), LemmyError>;
|
) -> Result<(), LemmyError>;
|
||||||
|
|
||||||
async fn send_delete(&self, context: &LemmyContext) -> Result<(), LemmyError>;
|
async fn send_update(&self, mod_: Person, context: &LemmyContext) -> Result<(), LemmyError>;
|
||||||
async fn send_undo_delete(&self, context: &LemmyContext) -> Result<(), LemmyError>;
|
async fn send_delete(&self, mod_: Person, context: &LemmyContext) -> Result<(), LemmyError>;
|
||||||
|
async fn send_undo_delete(&self, mod_: Person, context: &LemmyContext) -> Result<(), LemmyError>;
|
||||||
|
|
||||||
async fn send_remove(&self, context: &LemmyContext) -> Result<(), LemmyError>;
|
async fn send_remove(&self, context: &LemmyContext) -> Result<(), LemmyError>;
|
||||||
async fn send_undo_remove(&self, context: &LemmyContext) -> Result<(), LemmyError>;
|
async fn send_undo_remove(&self, context: &LemmyContext) -> Result<(), LemmyError>;
|
||||||
|
@ -366,7 +373,7 @@ pub async fn find_post_or_comment_by_id(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum Object {
|
pub enum Object {
|
||||||
Comment(Box<Comment>),
|
Comment(Box<Comment>),
|
||||||
Post(Box<Post>),
|
Post(Box<Post>),
|
||||||
Community(Box<Community>),
|
Community(Box<Community>),
|
||||||
|
@ -374,10 +381,7 @@ pub(crate) enum Object {
|
||||||
PrivateMessage(Box<PrivateMessage>),
|
PrivateMessage(Box<PrivateMessage>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn find_object_by_id(
|
pub async fn find_object_by_id(context: &LemmyContext, apub_id: Url) -> Result<Object, LemmyError> {
|
||||||
context: &LemmyContext,
|
|
||||||
apub_id: Url,
|
|
||||||
) -> Result<Object, LemmyError> {
|
|
||||||
let ap_id = apub_id.clone();
|
let ap_id = apub_id.clone();
|
||||||
if let Ok(pc) = find_post_or_comment_by_id(context, ap_id.to_owned()).await {
|
if let Ok(pc) = find_post_or_comment_by_id(context, ap_id.to_owned()).await {
|
||||||
return Ok(match pc {
|
return Ok(match pc {
|
||||||
|
@ -460,3 +464,20 @@ where
|
||||||
}
|
}
|
||||||
to_and_cc
|
to_and_cc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_community_from_to_or_cc<T, Kind>(
|
||||||
|
activity: &T,
|
||||||
|
context: &LemmyContext,
|
||||||
|
request_counter: &mut i32,
|
||||||
|
) -> Result<Community, LemmyError>
|
||||||
|
where
|
||||||
|
T: AsObject<Kind>,
|
||||||
|
{
|
||||||
|
for cid in get_activity_to_and_cc(activity) {
|
||||||
|
let community = get_or_fetch_and_upsert_community(&cid, context, request_counter).await;
|
||||||
|
if community.is_ok() {
|
||||||
|
return community;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(NotFound.into())
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
check_community_or_site_ban,
|
check_community_or_site_ban,
|
||||||
check_is_apub_id_valid,
|
check_is_apub_id_valid,
|
||||||
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
|
fetcher::person::get_or_fetch_and_upsert_person,
|
||||||
get_activity_to_and_cc,
|
|
||||||
PageExt,
|
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
base::{AsBase, BaseExt, ExtendsExt},
|
base::{AsBase, BaseExt, ExtendsExt},
|
||||||
|
@ -13,10 +11,9 @@ use activitystreams::{
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Context};
|
use anyhow::{anyhow, Context};
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use diesel::result::Error::NotFound;
|
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_db_queries::{ApubObject, Crud, DbPool};
|
use lemmy_db_queries::{ApubObject, Crud, DbPool};
|
||||||
use lemmy_db_schema::{source::community::Community, CommunityId, DbUrl};
|
use lemmy_db_schema::{CommunityId, DbUrl};
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
location_info,
|
location_info,
|
||||||
settings::structs::Settings,
|
settings::structs::Settings,
|
||||||
|
@ -61,7 +58,7 @@ pub trait FromApub {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
pub(in crate::objects) trait FromApubToForm<ApubType> {
|
pub trait FromApubToForm<ApubType> {
|
||||||
async fn from_apub(
|
async fn from_apub(
|
||||||
apub: &ApubType,
|
apub: &ApubType,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
|
@ -175,7 +172,7 @@ pub(in crate::objects) fn check_is_markdown(mime: Option<&Mime>) -> Result<(), L
|
||||||
/// Converts an ActivityPub object (eg `Note`) to a database object (eg `Comment`). If an object
|
/// Converts an ActivityPub object (eg `Note`) to a database object (eg `Comment`). If an object
|
||||||
/// with the same ActivityPub ID already exists in the database, it is returned directly. Otherwise
|
/// with the same ActivityPub ID already exists in the database, it is returned directly. Otherwise
|
||||||
/// the apub object is parsed, inserted and returned.
|
/// the apub object is parsed, inserted and returned.
|
||||||
pub(in crate::objects) async fn get_object_from_apub<From, Kind, To, ToForm, IdType>(
|
pub async fn get_object_from_apub<From, Kind, To, ToForm, IdType>(
|
||||||
from: &From,
|
from: &From,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
expected_domain: Url,
|
expected_domain: Url,
|
||||||
|
@ -231,17 +228,3 @@ where
|
||||||
let person = get_or_fetch_and_upsert_person(person_id, context, request_counter).await?;
|
let person = get_or_fetch_and_upsert_person(person_id, context, request_counter).await?;
|
||||||
check_community_or_site_ban(&person, community_id, context.pool()).await
|
check_community_or_site_ban(&person, community_id, context.pool()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(in crate::objects) async fn get_community_from_to_or_cc(
|
|
||||||
page: &PageExt,
|
|
||||||
context: &LemmyContext,
|
|
||||||
request_counter: &mut i32,
|
|
||||||
) -> Result<Community, LemmyError> {
|
|
||||||
for cid in get_activity_to_and_cc(page) {
|
|
||||||
let community = get_or_fetch_and_upsert_community(&cid, context, request_counter).await;
|
|
||||||
if community.is_ok() {
|
|
||||||
return community;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(NotFound.into())
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,11 +2,11 @@ use crate::{
|
||||||
check_is_apub_id_valid,
|
check_is_apub_id_valid,
|
||||||
extensions::{context::lemmy_context, page_extension::PageExtension},
|
extensions::{context::lemmy_context, page_extension::PageExtension},
|
||||||
fetcher::person::get_or_fetch_and_upsert_person,
|
fetcher::person::get_or_fetch_and_upsert_person,
|
||||||
|
get_community_from_to_or_cc,
|
||||||
objects::{
|
objects::{
|
||||||
check_object_domain,
|
check_object_domain,
|
||||||
check_object_for_community_or_site_ban,
|
check_object_for_community_or_site_ban,
|
||||||
create_tombstone,
|
create_tombstone,
|
||||||
get_community_from_to_or_cc,
|
|
||||||
get_object_from_apub,
|
get_object_from_apub,
|
||||||
get_source_markdown_value,
|
get_source_markdown_value,
|
||||||
set_content_and_source,
|
set_content_and_source,
|
||||||
|
|
|
@ -1,10 +1,86 @@
|
||||||
|
use crate::{
|
||||||
|
activities::receive::get_actor_as_person,
|
||||||
|
inbox::receive_for_community::verify_actor_is_community_mod,
|
||||||
|
};
|
||||||
|
use activitystreams::{
|
||||||
|
activity::{ActorAndObjectRefExt, Delete, Undo, Update},
|
||||||
|
base::ExtendsExt,
|
||||||
|
};
|
||||||
|
use anyhow::{anyhow, Context};
|
||||||
use lemmy_api_common::{blocking, community::CommunityResponse};
|
use lemmy_api_common::{blocking, community::CommunityResponse};
|
||||||
use lemmy_db_queries::source::community::Community_;
|
use lemmy_apub::{
|
||||||
use lemmy_db_schema::source::community::Community;
|
get_community_from_to_or_cc,
|
||||||
use lemmy_db_views_actor::community_view::CommunityView;
|
objects::FromApubToForm,
|
||||||
use lemmy_utils::LemmyError;
|
ActorType,
|
||||||
|
CommunityType,
|
||||||
|
GroupExt,
|
||||||
|
};
|
||||||
|
use lemmy_db_queries::{source::community::Community_, Crud};
|
||||||
|
use lemmy_db_schema::source::{
|
||||||
|
community::{Community, CommunityForm},
|
||||||
|
person::Person,
|
||||||
|
};
|
||||||
|
use lemmy_db_views_actor::{
|
||||||
|
community_moderator_view::CommunityModeratorView,
|
||||||
|
community_view::CommunityView,
|
||||||
|
};
|
||||||
|
use lemmy_utils::{location_info, LemmyError};
|
||||||
use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext, UserOperationCrud};
|
||||||
|
|
||||||
|
/// This activity is received from a remote community mod, and updates the description or other
|
||||||
|
/// fields of a local community.
|
||||||
|
pub(crate) async fn receive_remote_mod_update_community(
|
||||||
|
update: Update,
|
||||||
|
context: &LemmyContext,
|
||||||
|
request_counter: &mut i32,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
let community = get_community_from_to_or_cc(&update, context, request_counter).await?;
|
||||||
|
verify_actor_is_community_mod(&update, &community, context).await?;
|
||||||
|
let group = GroupExt::from_any_base(update.object().to_owned().one().context(location_info!())?)?
|
||||||
|
.context(location_info!())?;
|
||||||
|
let updated_community = CommunityForm::from_apub(
|
||||||
|
&group,
|
||||||
|
context,
|
||||||
|
community.actor_id(),
|
||||||
|
request_counter,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let cf = CommunityForm {
|
||||||
|
name: updated_community.name,
|
||||||
|
title: updated_community.title,
|
||||||
|
description: updated_community.description,
|
||||||
|
nsfw: updated_community.nsfw,
|
||||||
|
// TODO: icon and banner would be hosted on the other instance, ideally we would copy it to ours
|
||||||
|
icon: updated_community.icon,
|
||||||
|
banner: updated_community.banner,
|
||||||
|
..CommunityForm::default()
|
||||||
|
};
|
||||||
|
blocking(context.pool(), move |conn| {
|
||||||
|
Community::update(conn, community.id, &cf)
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn receive_remote_mod_delete_community(
|
||||||
|
delete: Delete,
|
||||||
|
community: Community,
|
||||||
|
context: &LemmyContext,
|
||||||
|
request_counter: &mut i32,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
verify_actor_is_community_mod(&delete, &community, context).await?;
|
||||||
|
let actor = get_actor_as_person(&delete, context, request_counter).await?;
|
||||||
|
verify_is_remote_community_creator(&actor, &community, context).await?;
|
||||||
|
let community_id = community.id;
|
||||||
|
blocking(context.pool(), move |conn| {
|
||||||
|
Community::update_deleted(conn, community_id, true)
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
community.send_delete(actor, context).await
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) async fn receive_delete_community(
|
pub(crate) async fn receive_delete_community(
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
community: Community,
|
community: Community,
|
||||||
|
@ -61,6 +137,23 @@ pub(crate) async fn receive_remove_community(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn receive_remote_mod_undo_delete_community(
|
||||||
|
undo: Undo,
|
||||||
|
community: Community,
|
||||||
|
context: &LemmyContext,
|
||||||
|
request_counter: &mut i32,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
verify_actor_is_community_mod(&undo, &community, context).await?;
|
||||||
|
let actor = get_actor_as_person(&undo, context, request_counter).await?;
|
||||||
|
verify_is_remote_community_creator(&actor, &community, context).await?;
|
||||||
|
let community_id = community.id;
|
||||||
|
blocking(context.pool(), move |conn| {
|
||||||
|
Community::update_deleted(conn, community_id, false)
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
community.send_undo_delete(actor, context).await
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) async fn receive_undo_delete_community(
|
pub(crate) async fn receive_undo_delete_community(
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
community: Community,
|
community: Community,
|
||||||
|
@ -117,3 +210,23 @@ pub(crate) async fn receive_undo_remove_community(
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if the remote user is creator of the local community. This can only happen if a community
|
||||||
|
/// is created by a local user, and then transferred to a remote user.
|
||||||
|
async fn verify_is_remote_community_creator(
|
||||||
|
user: &Person,
|
||||||
|
community: &Community,
|
||||||
|
context: &LemmyContext,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
let community_id = community.id;
|
||||||
|
let community_mods = blocking(context.pool(), move |conn| {
|
||||||
|
CommunityModeratorView::for_community(conn, community_id)
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
|
||||||
|
if user.id != community_mods[0].moderator.id {
|
||||||
|
Err(anyhow!("Actor is not community creator").into())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -14,6 +14,11 @@ use crate::{
|
||||||
receive_undo_like_comment,
|
receive_undo_like_comment,
|
||||||
receive_undo_remove_comment,
|
receive_undo_remove_comment,
|
||||||
},
|
},
|
||||||
|
community::{
|
||||||
|
receive_remote_mod_delete_community,
|
||||||
|
receive_remote_mod_undo_delete_community,
|
||||||
|
receive_remote_mod_update_community,
|
||||||
|
},
|
||||||
post::{
|
post::{
|
||||||
receive_create_post,
|
receive_create_post,
|
||||||
receive_delete_post,
|
receive_delete_post,
|
||||||
|
@ -60,9 +65,12 @@ use lemmy_apub::{
|
||||||
objects::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
|
objects::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
|
||||||
person::get_or_fetch_and_upsert_person,
|
person::get_or_fetch_and_upsert_person,
|
||||||
},
|
},
|
||||||
|
find_object_by_id,
|
||||||
find_post_or_comment_by_id,
|
find_post_or_comment_by_id,
|
||||||
generate_moderators_url,
|
generate_moderators_url,
|
||||||
|
ActorType,
|
||||||
CommunityType,
|
CommunityType,
|
||||||
|
Object,
|
||||||
PostOrComment,
|
PostOrComment,
|
||||||
};
|
};
|
||||||
use lemmy_db_queries::{
|
use lemmy_db_queries::{
|
||||||
|
@ -101,6 +109,14 @@ enum PageOrNote {
|
||||||
Note,
|
Note,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(EnumString)]
|
||||||
|
enum ObjectTypes {
|
||||||
|
Page,
|
||||||
|
Note,
|
||||||
|
Group,
|
||||||
|
Person,
|
||||||
|
}
|
||||||
|
|
||||||
/// This file is for post/comment activities received by the community, and for post/comment
|
/// This file is for post/comment activities received by the community, and for post/comment
|
||||||
/// activities announced by the community and received by the person.
|
/// activities announced by the community and received by the person.
|
||||||
|
|
||||||
|
@ -120,8 +136,8 @@ pub(in crate::inbox) async fn receive_create_for_community(
|
||||||
.as_single_kind_str()
|
.as_single_kind_str()
|
||||||
.and_then(|s| s.parse().ok());
|
.and_then(|s| s.parse().ok());
|
||||||
match kind {
|
match kind {
|
||||||
Some(PageOrNote::Page) => receive_create_post(create, context, request_counter).await,
|
Some(ObjectTypes::Page) => receive_create_post(create, context, request_counter).await,
|
||||||
Some(PageOrNote::Note) => receive_create_comment(create, context, request_counter).await,
|
Some(ObjectTypes::Note) => receive_create_comment(create, context, request_counter).await,
|
||||||
_ => receive_unhandled_activity(create),
|
_ => receive_unhandled_activity(create),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,7 +150,7 @@ pub(in crate::inbox) async fn receive_update_for_community(
|
||||||
expected_domain: &Url,
|
expected_domain: &Url,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let update = Update::from_any_base(activity)?.context(location_info!())?;
|
let update = Update::from_any_base(activity.to_owned())?.context(location_info!())?;
|
||||||
verify_activity_domains_valid(&update, &expected_domain, false)?;
|
verify_activity_domains_valid(&update, &expected_domain, false)?;
|
||||||
verify_is_addressed_to_public(&update)?;
|
verify_is_addressed_to_public(&update)?;
|
||||||
verify_modification_actor_instance(&update, &announce, context, request_counter).await?;
|
verify_modification_actor_instance(&update, &announce, context, request_counter).await?;
|
||||||
|
@ -144,8 +160,13 @@ pub(in crate::inbox) async fn receive_update_for_community(
|
||||||
.as_single_kind_str()
|
.as_single_kind_str()
|
||||||
.and_then(|s| s.parse().ok());
|
.and_then(|s| s.parse().ok());
|
||||||
match kind {
|
match kind {
|
||||||
Some(PageOrNote::Page) => receive_update_post(update, announce, context, request_counter).await,
|
Some(ObjectTypes::Page) => {
|
||||||
Some(PageOrNote::Note) => receive_update_comment(update, context, request_counter).await,
|
receive_update_post(update, announce, context, request_counter).await
|
||||||
|
}
|
||||||
|
Some(ObjectTypes::Note) => receive_update_comment(update, context, request_counter).await,
|
||||||
|
Some(ObjectTypes::Group) => {
|
||||||
|
receive_remote_mod_update_community(update, context, request_counter).await
|
||||||
|
}
|
||||||
_ => receive_unhandled_activity(update),
|
_ => receive_unhandled_activity(update),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -215,7 +236,7 @@ pub(in crate::inbox) async fn receive_delete_for_community(
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let delete = Delete::from_any_base(activity)?.context(location_info!())?;
|
let delete = Delete::from_any_base(activity)?.context(location_info!())?;
|
||||||
verify_activity_domains_valid(&delete, &expected_domain, true)?;
|
// TODO: skip this check if action is done by remote mod
|
||||||
verify_is_addressed_to_public(&delete)?;
|
verify_is_addressed_to_public(&delete)?;
|
||||||
verify_modification_actor_instance(&delete, &announce, context, request_counter).await?;
|
verify_modification_actor_instance(&delete, &announce, context, request_counter).await?;
|
||||||
|
|
||||||
|
@ -225,11 +246,20 @@ pub(in crate::inbox) async fn receive_delete_for_community(
|
||||||
.single_xsd_any_uri()
|
.single_xsd_any_uri()
|
||||||
.context(location_info!())?;
|
.context(location_info!())?;
|
||||||
|
|
||||||
match find_post_or_comment_by_id(context, object).await {
|
match find_object_by_id(context, object).await {
|
||||||
Ok(PostOrComment::Post(p)) => receive_delete_post(context, *p).await,
|
Ok(Object::Post(p)) => {
|
||||||
Ok(PostOrComment::Comment(c)) => receive_delete_comment(context, *c).await,
|
verify_activity_domains_valid(&delete, &expected_domain, true)?;
|
||||||
// if we dont have the object, no need to do anything
|
receive_delete_post(context, *p).await
|
||||||
Err(_) => Ok(()),
|
}
|
||||||
|
Ok(Object::Comment(c)) => {
|
||||||
|
verify_activity_domains_valid(&delete, &expected_domain, true)?;
|
||||||
|
receive_delete_comment(context, *c).await
|
||||||
|
}
|
||||||
|
Ok(Object::Community(c)) => {
|
||||||
|
receive_remote_mod_delete_community(delete, *c, context, request_counter).await
|
||||||
|
}
|
||||||
|
// if we dont have the object or dont support its deletion, no need to do anything
|
||||||
|
_ => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,7 +344,9 @@ pub(in crate::inbox) async fn receive_undo_for_community(
|
||||||
.as_single_kind_str()
|
.as_single_kind_str()
|
||||||
.and_then(|s| s.parse().ok())
|
.and_then(|s| s.parse().ok())
|
||||||
{
|
{
|
||||||
Some(Delete) => receive_undo_delete_for_community(context, undo, expected_domain).await,
|
Some(Delete) => {
|
||||||
|
receive_undo_delete_for_community(context, undo, expected_domain, request_counter).await
|
||||||
|
}
|
||||||
Some(Remove) => {
|
Some(Remove) => {
|
||||||
receive_undo_remove_for_community(context, undo, announce, expected_domain).await
|
receive_undo_remove_for_community(context, undo, announce, expected_domain).await
|
||||||
}
|
}
|
||||||
|
@ -338,15 +370,15 @@ pub(in crate::inbox) async fn receive_undo_for_community(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A post or comment deletion being reverted
|
/// A post, comment or community deletion being reverted
|
||||||
pub(in crate::inbox) async fn receive_undo_delete_for_community(
|
pub(in crate::inbox) async fn receive_undo_delete_for_community(
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
undo: Undo,
|
undo: Undo,
|
||||||
expected_domain: &Url,
|
expected_domain: &Url,
|
||||||
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let delete = Delete::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
|
let delete = Delete::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
|
||||||
.context(location_info!())?;
|
.context(location_info!())?;
|
||||||
verify_activity_domains_valid(&delete, &expected_domain, true)?;
|
|
||||||
verify_is_addressed_to_public(&delete)?;
|
verify_is_addressed_to_public(&delete)?;
|
||||||
|
|
||||||
let object = delete
|
let object = delete
|
||||||
|
@ -354,11 +386,21 @@ pub(in crate::inbox) async fn receive_undo_delete_for_community(
|
||||||
.to_owned()
|
.to_owned()
|
||||||
.single_xsd_any_uri()
|
.single_xsd_any_uri()
|
||||||
.context(location_info!())?;
|
.context(location_info!())?;
|
||||||
match find_post_or_comment_by_id(context, object).await {
|
match find_object_by_id(context, object).await {
|
||||||
Ok(PostOrComment::Post(p)) => receive_undo_delete_post(context, *p).await,
|
Ok(Object::Post(p)) => {
|
||||||
Ok(PostOrComment::Comment(c)) => receive_undo_delete_comment(context, *c).await,
|
verify_activity_domains_valid(&delete, &expected_domain, true)?;
|
||||||
// if we dont have the object, no need to do anything
|
receive_undo_delete_post(context, *p).await
|
||||||
Err(_) => Ok(()),
|
}
|
||||||
|
Ok(Object::Comment(c)) => {
|
||||||
|
verify_activity_domains_valid(&delete, &expected_domain, true)?;
|
||||||
|
receive_undo_delete_comment(context, *c).await
|
||||||
|
}
|
||||||
|
Ok(Object::Community(c)) => {
|
||||||
|
verify_actor_is_community_mod(&undo, &c, context).await?;
|
||||||
|
receive_remote_mod_undo_delete_community(undo, *c, context, request_counter).await
|
||||||
|
}
|
||||||
|
// if we dont have the object or dont support its deletion, no need to do anything
|
||||||
|
_ => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -617,7 +659,7 @@ where
|
||||||
///
|
///
|
||||||
/// This method should only be used for activities received by the community, not for activities
|
/// This method should only be used for activities received by the community, not for activities
|
||||||
/// used by community followers.
|
/// used by community followers.
|
||||||
async fn verify_actor_is_community_mod<T, Kind>(
|
pub(crate) async fn verify_actor_is_community_mod<T, Kind>(
|
||||||
activity: &T,
|
activity: &T,
|
||||||
community: &Community,
|
community: &Community,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
|
@ -722,9 +764,18 @@ where
|
||||||
.map(|o| o.id())
|
.map(|o| o.id())
|
||||||
.flatten()
|
.flatten()
|
||||||
.context(location_info!())?;
|
.context(location_info!())?;
|
||||||
let original_id = match fetch_post_or_comment_by_id(object_id, context, request_counter).await? {
|
let original_id = match fetch_post_or_comment_by_id(object_id, context, request_counter).await {
|
||||||
PostOrComment::Post(p) => p.ap_id.into_inner(),
|
Ok(PostOrComment::Post(p)) => p.ap_id.into_inner(),
|
||||||
PostOrComment::Comment(c) => c.ap_id.into_inner(),
|
Ok(PostOrComment::Comment(c)) => c.ap_id.into_inner(),
|
||||||
|
Err(_) => {
|
||||||
|
// We can also receive Update activity from remote mod for local activity
|
||||||
|
let object_id = object_id.to_owned().into();
|
||||||
|
blocking(context.pool(), move |conn| {
|
||||||
|
Community::read_from_apub_id(conn, &object_id)
|
||||||
|
})
|
||||||
|
.await??
|
||||||
|
.actor_id()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
if actor_id.domain() != original_id.domain() {
|
if actor_id.domain() != original_id.domain() {
|
||||||
let community = extract_community_from_cc(activity, context).await?;
|
let community = extract_community_from_cc(activity, context).await?;
|
||||||
|
|
Loading…
Reference in a new issue