mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-11-30 00:01:25 +00:00
Adding federated mod remove actions.
This commit is contained in:
parent
5366797a4b
commit
a09c818746
11 changed files with 1079 additions and 25 deletions
|
@ -343,6 +343,12 @@ impl Perform for Oper<EditComment> {
|
||||||
} else {
|
} else {
|
||||||
updated_comment.send_undo_delete(&user, &conn)?;
|
updated_comment.send_undo_delete(&user, &conn)?;
|
||||||
}
|
}
|
||||||
|
} else if let Some(removed) = data.removed.to_owned() {
|
||||||
|
if removed {
|
||||||
|
updated_comment.send_remove(&user, &conn)?;
|
||||||
|
} else {
|
||||||
|
updated_comment.send_undo_remove(&user, &conn)?;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
updated_comment.send_update(&user, &conn)?;
|
updated_comment.send_update(&user, &conn)?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -386,6 +386,12 @@ impl Perform for Oper<EditCommunity> {
|
||||||
} else {
|
} else {
|
||||||
updated_community.send_undo_delete(&user, &conn)?;
|
updated_community.send_undo_delete(&user, &conn)?;
|
||||||
}
|
}
|
||||||
|
} else if let Some(removed) = data.removed.to_owned() {
|
||||||
|
if removed {
|
||||||
|
updated_community.send_remove(&user, &conn)?;
|
||||||
|
} else {
|
||||||
|
updated_community.send_undo_remove(&user, &conn)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let community_view = CommunityView::read(&conn, data.edit_id, Some(user_id))?;
|
let community_view = CommunityView::read(&conn, data.edit_id, Some(user_id))?;
|
||||||
|
|
|
@ -547,6 +547,12 @@ impl Perform for Oper<EditPost> {
|
||||||
} else {
|
} else {
|
||||||
updated_post.send_undo_delete(&user, &conn)?;
|
updated_post.send_undo_delete(&user, &conn)?;
|
||||||
}
|
}
|
||||||
|
} else if let Some(removed) = data.removed.to_owned() {
|
||||||
|
if removed {
|
||||||
|
updated_post.send_remove(&user, &conn)?;
|
||||||
|
} else {
|
||||||
|
updated_post.send_undo_remove(&user, &conn)?;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
updated_post.send_update(&user, &conn)?;
|
updated_post.send_update(&user, &conn)?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -187,7 +187,7 @@ impl ApubObjectType for Comment {
|
||||||
|
|
||||||
// Insert the sent activity into the activity table
|
// Insert the sent activity into the activity table
|
||||||
let activity_form = activity::ActivityForm {
|
let activity_form = activity::ActivityForm {
|
||||||
user_id: self.creator_id,
|
user_id: creator.id,
|
||||||
data: serde_json::to_value(&delete)?,
|
data: serde_json::to_value(&delete)?,
|
||||||
local: true,
|
local: true,
|
||||||
updated: None,
|
updated: None,
|
||||||
|
@ -240,7 +240,7 @@ impl ApubObjectType for Comment {
|
||||||
|
|
||||||
// Insert the sent activity into the activity table
|
// Insert the sent activity into the activity table
|
||||||
let activity_form = activity::ActivityForm {
|
let activity_form = activity::ActivityForm {
|
||||||
user_id: self.creator_id,
|
user_id: creator.id,
|
||||||
data: serde_json::to_value(&undo)?,
|
data: serde_json::to_value(&undo)?,
|
||||||
local: true,
|
local: true,
|
||||||
updated: None,
|
updated: None,
|
||||||
|
@ -255,6 +255,95 @@ impl ApubObjectType for Comment {
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn send_remove(&self, mod_: &User_, conn: &PgConnection) -> Result<(), Error> {
|
||||||
|
let note = self.to_apub(&conn)?;
|
||||||
|
let post = Post::read(&conn, self.post_id)?;
|
||||||
|
let community = Community::read(&conn, post.community_id)?;
|
||||||
|
let id = format!("{}/remove/{}", self.ap_id, uuid::Uuid::new_v4());
|
||||||
|
let mut remove = Remove::default();
|
||||||
|
|
||||||
|
populate_object_props(
|
||||||
|
&mut remove.object_props,
|
||||||
|
&community.get_followers_url(),
|
||||||
|
&id,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
remove
|
||||||
|
.remove_props
|
||||||
|
.set_actor_xsd_any_uri(mod_.actor_id.to_owned())?
|
||||||
|
.set_object_base_box(note)?;
|
||||||
|
|
||||||
|
// Insert the sent activity into the activity table
|
||||||
|
let activity_form = activity::ActivityForm {
|
||||||
|
user_id: mod_.id,
|
||||||
|
data: serde_json::to_value(&remove)?,
|
||||||
|
local: true,
|
||||||
|
updated: None,
|
||||||
|
};
|
||||||
|
activity::Activity::create(&conn, &activity_form)?;
|
||||||
|
|
||||||
|
send_activity(
|
||||||
|
&remove,
|
||||||
|
&mod_.private_key.as_ref().unwrap(),
|
||||||
|
&mod_.actor_id,
|
||||||
|
community.get_follower_inboxes(&conn)?,
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_undo_remove(&self, mod_: &User_, conn: &PgConnection) -> Result<(), Error> {
|
||||||
|
let note = self.to_apub(&conn)?;
|
||||||
|
let post = Post::read(&conn, self.post_id)?;
|
||||||
|
let community = Community::read(&conn, post.community_id)?;
|
||||||
|
|
||||||
|
// Generate a fake delete activity, with the correct object
|
||||||
|
let id = format!("{}/remove/{}", self.ap_id, uuid::Uuid::new_v4());
|
||||||
|
let mut remove = Remove::default();
|
||||||
|
|
||||||
|
populate_object_props(
|
||||||
|
&mut remove.object_props,
|
||||||
|
&community.get_followers_url(),
|
||||||
|
&id,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
remove
|
||||||
|
.remove_props
|
||||||
|
.set_actor_xsd_any_uri(mod_.actor_id.to_owned())?
|
||||||
|
.set_object_base_box(note)?;
|
||||||
|
|
||||||
|
// Undo that fake activity
|
||||||
|
let undo_id = format!("{}/undo/remove/{}", self.ap_id, uuid::Uuid::new_v4());
|
||||||
|
let mut undo = Undo::default();
|
||||||
|
|
||||||
|
populate_object_props(
|
||||||
|
&mut undo.object_props,
|
||||||
|
&community.get_followers_url(),
|
||||||
|
&undo_id,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
undo
|
||||||
|
.undo_props
|
||||||
|
.set_actor_xsd_any_uri(mod_.actor_id.to_owned())?
|
||||||
|
.set_object_base_box(remove)?;
|
||||||
|
|
||||||
|
// Insert the sent activity into the activity table
|
||||||
|
let activity_form = activity::ActivityForm {
|
||||||
|
user_id: mod_.id,
|
||||||
|
data: serde_json::to_value(&undo)?,
|
||||||
|
local: true,
|
||||||
|
updated: None,
|
||||||
|
};
|
||||||
|
activity::Activity::create(&conn, &activity_form)?;
|
||||||
|
|
||||||
|
send_activity(
|
||||||
|
&undo,
|
||||||
|
&mod_.private_key.as_ref().unwrap(),
|
||||||
|
&mod_.actor_id,
|
||||||
|
community.get_follower_inboxes(&conn)?,
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApubLikeableType for Comment {
|
impl ApubLikeableType for Comment {
|
||||||
|
|
|
@ -14,13 +14,21 @@ impl ToApub for Community {
|
||||||
let mut group = Group::default();
|
let mut group = Group::default();
|
||||||
let oprops: &mut ObjectProperties = group.as_mut();
|
let oprops: &mut ObjectProperties = group.as_mut();
|
||||||
|
|
||||||
let creator = User_::read(conn, self.creator_id)?;
|
// The attributed to, is an ordered vector with the creator actor_ids first,
|
||||||
|
// then the rest of the moderators
|
||||||
|
// TODO Technically the instance admins can mod the community, but lets
|
||||||
|
// ignore that for now
|
||||||
|
let moderators = CommunityModeratorView::for_community(&conn, self.id)?
|
||||||
|
.into_iter()
|
||||||
|
.map(|m| m.user_actor_id)
|
||||||
|
.collect();
|
||||||
|
|
||||||
oprops
|
oprops
|
||||||
.set_context_xsd_any_uri(context())?
|
.set_context_xsd_any_uri(context())?
|
||||||
.set_id(self.actor_id.to_owned())?
|
.set_id(self.actor_id.to_owned())?
|
||||||
.set_name_xsd_string(self.name.to_owned())?
|
.set_name_xsd_string(self.name.to_owned())?
|
||||||
.set_published(convert_datetime(self.published))?
|
.set_published(convert_datetime(self.published))?
|
||||||
.set_attributed_to_xsd_any_uri(creator.actor_id)?;
|
.set_many_attributed_to_xsd_any_uris(moderators)?;
|
||||||
|
|
||||||
if let Some(u) = self.updated.to_owned() {
|
if let Some(u) = self.updated.to_owned() {
|
||||||
oprops.set_updated(convert_datetime(u))?;
|
oprops.set_updated(convert_datetime(u))?;
|
||||||
|
@ -181,6 +189,83 @@ impl ActorType for Community {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn send_remove(&self, mod_: &User_, conn: &PgConnection) -> Result<(), Error> {
|
||||||
|
let group = self.to_apub(conn)?;
|
||||||
|
let id = format!("{}/remove/{}", self.actor_id, uuid::Uuid::new_v4());
|
||||||
|
|
||||||
|
let mut remove = Remove::default();
|
||||||
|
populate_object_props(&mut remove.object_props, &self.get_followers_url(), &id)?;
|
||||||
|
|
||||||
|
remove
|
||||||
|
.remove_props
|
||||||
|
.set_actor_xsd_any_uri(mod_.actor_id.to_owned())?
|
||||||
|
.set_object_base_box(group)?;
|
||||||
|
|
||||||
|
// Insert the sent activity into the activity table
|
||||||
|
let activity_form = activity::ActivityForm {
|
||||||
|
user_id: mod_.id,
|
||||||
|
data: serde_json::to_value(&remove)?,
|
||||||
|
local: true,
|
||||||
|
updated: None,
|
||||||
|
};
|
||||||
|
activity::Activity::create(&conn, &activity_form)?;
|
||||||
|
|
||||||
|
// Note: For an accept, since it was automatic, no one pushed a button,
|
||||||
|
// the community was the actor.
|
||||||
|
// But for delete, the creator is the actor, and does the signing
|
||||||
|
send_activity(
|
||||||
|
&remove,
|
||||||
|
&mod_.private_key.as_ref().unwrap(),
|
||||||
|
&mod_.actor_id,
|
||||||
|
self.get_follower_inboxes(&conn)?,
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_undo_remove(&self, mod_: &User_, conn: &PgConnection) -> Result<(), Error> {
|
||||||
|
let group = self.to_apub(conn)?;
|
||||||
|
let id = format!("{}/remove/{}", self.actor_id, uuid::Uuid::new_v4());
|
||||||
|
|
||||||
|
let mut remove = Remove::default();
|
||||||
|
populate_object_props(&mut remove.object_props, &self.get_followers_url(), &id)?;
|
||||||
|
|
||||||
|
remove
|
||||||
|
.remove_props
|
||||||
|
.set_actor_xsd_any_uri(mod_.actor_id.to_owned())?
|
||||||
|
.set_object_base_box(group)?;
|
||||||
|
|
||||||
|
// Undo that fake activity
|
||||||
|
let undo_id = format!("{}/undo/remove/{}", self.actor_id, uuid::Uuid::new_v4());
|
||||||
|
let mut undo = Undo::default();
|
||||||
|
|
||||||
|
populate_object_props(&mut undo.object_props, &self.get_followers_url(), &undo_id)?;
|
||||||
|
|
||||||
|
undo
|
||||||
|
.undo_props
|
||||||
|
.set_actor_xsd_any_uri(mod_.actor_id.to_owned())?
|
||||||
|
.set_object_base_box(remove)?;
|
||||||
|
|
||||||
|
// Insert the sent activity into the activity table
|
||||||
|
let activity_form = activity::ActivityForm {
|
||||||
|
user_id: mod_.id,
|
||||||
|
data: serde_json::to_value(&undo)?,
|
||||||
|
local: true,
|
||||||
|
updated: None,
|
||||||
|
};
|
||||||
|
activity::Activity::create(&conn, &activity_form)?;
|
||||||
|
|
||||||
|
// Note: For an accept, since it was automatic, no one pushed a button,
|
||||||
|
// the community was the actor.
|
||||||
|
// But for remove , the creator is the actor, and does the signing
|
||||||
|
send_activity(
|
||||||
|
&undo,
|
||||||
|
&mod_.private_key.as_ref().unwrap(),
|
||||||
|
&mod_.actor_id,
|
||||||
|
self.get_follower_inboxes(&conn)?,
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// For a given community, returns the inboxes of all followers.
|
/// For a given community, returns the inboxes of all followers.
|
||||||
fn get_follower_inboxes(&self, conn: &PgConnection) -> Result<Vec<String>, Error> {
|
fn get_follower_inboxes(&self, conn: &PgConnection) -> Result<Vec<String>, Error> {
|
||||||
Ok(
|
Ok(
|
||||||
|
@ -220,8 +305,11 @@ impl FromApub for CommunityForm {
|
||||||
// TODO don't do extra fetching here
|
// TODO don't do extra fetching here
|
||||||
// let _outbox = fetch_remote_object::<OrderedCollection>(&outbox_uri)?;
|
// let _outbox = fetch_remote_object::<OrderedCollection>(&outbox_uri)?;
|
||||||
// let _followers = fetch_remote_object::<UnorderedCollection>(&followers_uri)?;
|
// let _followers = fetch_remote_object::<UnorderedCollection>(&followers_uri)?;
|
||||||
let apub_id = &oprops.get_attributed_to_xsd_any_uri().unwrap().to_string();
|
let mut creator_and_moderator_uris = oprops.get_many_attributed_to_xsd_any_uris().unwrap();
|
||||||
let creator = get_or_fetch_and_upsert_remote_user(&apub_id, conn)?;
|
let creator = creator_and_moderator_uris
|
||||||
|
.next()
|
||||||
|
.map(|c| get_or_fetch_and_upsert_remote_user(&c.to_string(), &conn).unwrap())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
Ok(CommunityForm {
|
Ok(CommunityForm {
|
||||||
name: oprops.get_name_xsd_string().unwrap().to_string(),
|
name: oprops.get_name_xsd_string().unwrap().to_string(),
|
||||||
|
|
|
@ -86,8 +86,8 @@ pub fn get_or_fetch_and_upsert_remote_user(
|
||||||
match User_::read_from_actor_id(&conn, &apub_id) {
|
match User_::read_from_actor_id(&conn, &apub_id) {
|
||||||
Ok(u) => {
|
Ok(u) => {
|
||||||
// If its older than a day, re-fetch it
|
// If its older than a day, re-fetch it
|
||||||
// TODO the less than needs to be tested
|
if !u.local
|
||||||
if u
|
&& u
|
||||||
.last_refreshed_at
|
.last_refreshed_at
|
||||||
.lt(&(naive_now() - chrono::Duration::days(1)))
|
.lt(&(naive_now() - chrono::Duration::days(1)))
|
||||||
{
|
{
|
||||||
|
@ -118,8 +118,8 @@ pub fn get_or_fetch_and_upsert_remote_community(
|
||||||
match Community::read_from_actor_id(&conn, &apub_id) {
|
match Community::read_from_actor_id(&conn, &apub_id) {
|
||||||
Ok(c) => {
|
Ok(c) => {
|
||||||
// If its older than a day, re-fetch it
|
// If its older than a day, re-fetch it
|
||||||
// TODO the less than needs to be tested
|
if !c.local
|
||||||
if c
|
&& c
|
||||||
.last_refreshed_at
|
.last_refreshed_at
|
||||||
.lt(&(naive_now() - chrono::Duration::days(1)))
|
.lt(&(naive_now() - chrono::Duration::days(1)))
|
||||||
{
|
{
|
||||||
|
@ -136,7 +136,28 @@ pub fn get_or_fetch_and_upsert_remote_community(
|
||||||
debug!("Fetching and creating remote community: {}", apub_id);
|
debug!("Fetching and creating remote community: {}", apub_id);
|
||||||
let group = fetch_remote_object::<GroupExt>(&Url::parse(apub_id)?)?;
|
let group = fetch_remote_object::<GroupExt>(&Url::parse(apub_id)?)?;
|
||||||
let cf = CommunityForm::from_apub(&group, conn)?;
|
let cf = CommunityForm::from_apub(&group, conn)?;
|
||||||
Ok(Community::create(conn, &cf)?)
|
let community = Community::create(conn, &cf)?;
|
||||||
|
|
||||||
|
// Also add the community moderators too
|
||||||
|
let creator_and_moderator_uris = group
|
||||||
|
.base
|
||||||
|
.base
|
||||||
|
.object_props
|
||||||
|
.get_many_attributed_to_xsd_any_uris()
|
||||||
|
.unwrap();
|
||||||
|
let creator_and_moderators = creator_and_moderator_uris
|
||||||
|
.map(|c| get_or_fetch_and_upsert_remote_user(&c.to_string(), &conn).unwrap())
|
||||||
|
.collect::<Vec<User_>>();
|
||||||
|
|
||||||
|
for mod_ in creator_and_moderators {
|
||||||
|
let community_moderator_form = CommunityModeratorForm {
|
||||||
|
community_id: community.id,
|
||||||
|
user_id: mod_.id,
|
||||||
|
};
|
||||||
|
CommunityModerator::join(&conn, &community_moderator_form)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(community)
|
||||||
}
|
}
|
||||||
Err(e) => Err(Error::from(e)),
|
Err(e) => Err(Error::from(e)),
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ use crate::api::community::CommunityResponse;
|
||||||
use crate::websocket::server::SendCommunityRoomMessage;
|
use crate::websocket::server::SendCommunityRoomMessage;
|
||||||
use activitystreams::object::kind::{NoteType, PageType};
|
use activitystreams::object::kind::{NoteType, PageType};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
activity::{Accept, Create, Delete, Dislike, Follow, Like, Undo, Update},
|
activity::{Accept, Create, Delete, Dislike, Follow, Like, Remove, Undo, Update},
|
||||||
actor::{properties::ApActorProperties, Actor, Group, Person},
|
actor::{properties::ApActorProperties, Actor, Group, Person},
|
||||||
collection::UnorderedCollection,
|
collection::UnorderedCollection,
|
||||||
context,
|
context,
|
||||||
|
@ -47,13 +47,16 @@ use crate::api::post::PostResponse;
|
||||||
use crate::api::site::SearchResponse;
|
use crate::api::site::SearchResponse;
|
||||||
use crate::db::comment::{Comment, CommentForm, CommentLike, CommentLikeForm};
|
use crate::db::comment::{Comment, CommentForm, CommentLike, CommentLikeForm};
|
||||||
use crate::db::comment_view::CommentView;
|
use crate::db::comment_view::CommentView;
|
||||||
use crate::db::community::{Community, CommunityFollower, CommunityFollowerForm, CommunityForm};
|
use crate::db::community::{
|
||||||
use crate::db::community_view::{CommunityFollowerView, CommunityView};
|
Community, CommunityFollower, CommunityFollowerForm, CommunityForm, CommunityModerator,
|
||||||
|
CommunityModeratorForm,
|
||||||
|
};
|
||||||
|
use crate::db::community_view::{CommunityFollowerView, CommunityModeratorView, CommunityView};
|
||||||
use crate::db::post::{Post, PostForm, PostLike, PostLikeForm};
|
use crate::db::post::{Post, PostForm, PostLike, PostLikeForm};
|
||||||
use crate::db::post_view::PostView;
|
use crate::db::post_view::PostView;
|
||||||
use crate::db::user::{UserForm, User_};
|
use crate::db::user::{UserForm, User_};
|
||||||
use crate::db::user_view::UserView;
|
use crate::db::user_view::UserView;
|
||||||
use crate::db::{activity, Crud, Followable, Likeable, SearchType};
|
use crate::db::{activity, Crud, Followable, Joinable, Likeable, SearchType};
|
||||||
use crate::routes::nodeinfo::{NodeInfo, NodeInfoWellKnown};
|
use crate::routes::nodeinfo::{NodeInfo, NodeInfoWellKnown};
|
||||||
use crate::routes::{ChatServerParam, DbPoolParam};
|
use crate::routes::{ChatServerParam, DbPoolParam};
|
||||||
use crate::websocket::{
|
use crate::websocket::{
|
||||||
|
@ -197,6 +200,8 @@ pub trait ApubObjectType {
|
||||||
fn send_update(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
|
fn send_update(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
|
||||||
fn send_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
|
fn send_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
|
||||||
fn send_undo_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
|
fn send_undo_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
|
||||||
|
fn send_remove(&self, mod_: &User_, conn: &PgConnection) -> Result<(), Error>;
|
||||||
|
fn send_undo_remove(&self, mod_: &User_, conn: &PgConnection) -> Result<(), Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ApubLikeableType {
|
pub trait ApubLikeableType {
|
||||||
|
@ -239,6 +244,9 @@ pub trait ActorType {
|
||||||
fn send_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
|
fn send_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
|
||||||
fn send_undo_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
|
fn send_undo_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
|
||||||
|
|
||||||
|
fn send_remove(&self, mod_: &User_, conn: &PgConnection) -> Result<(), Error>;
|
||||||
|
fn send_undo_remove(&self, mod_: &User_, conn: &PgConnection) -> Result<(), Error>;
|
||||||
|
|
||||||
// TODO default because there is no user following yet.
|
// TODO default because there is no user following yet.
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
/// For a given community, returns the inboxes of all followers.
|
/// For a given community, returns the inboxes of all followers.
|
||||||
|
|
|
@ -263,6 +263,92 @@ impl ApubObjectType for Post {
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn send_remove(&self, mod_: &User_, conn: &PgConnection) -> Result<(), Error> {
|
||||||
|
let page = self.to_apub(conn)?;
|
||||||
|
let community = Community::read(conn, self.community_id)?;
|
||||||
|
let id = format!("{}/remove/{}", self.ap_id, uuid::Uuid::new_v4());
|
||||||
|
let mut remove = Remove::default();
|
||||||
|
|
||||||
|
populate_object_props(
|
||||||
|
&mut remove.object_props,
|
||||||
|
&community.get_followers_url(),
|
||||||
|
&id,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
remove
|
||||||
|
.remove_props
|
||||||
|
.set_actor_xsd_any_uri(mod_.actor_id.to_owned())?
|
||||||
|
.set_object_base_box(page)?;
|
||||||
|
|
||||||
|
// Insert the sent activity into the activity table
|
||||||
|
let activity_form = activity::ActivityForm {
|
||||||
|
user_id: mod_.id,
|
||||||
|
data: serde_json::to_value(&remove)?,
|
||||||
|
local: true,
|
||||||
|
updated: None,
|
||||||
|
};
|
||||||
|
activity::Activity::create(&conn, &activity_form)?;
|
||||||
|
|
||||||
|
let community = Community::read(conn, self.community_id)?;
|
||||||
|
send_activity(
|
||||||
|
&remove,
|
||||||
|
&mod_.private_key.as_ref().unwrap(),
|
||||||
|
&mod_.actor_id,
|
||||||
|
community.get_follower_inboxes(&conn)?,
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn send_undo_remove(&self, mod_: &User_, conn: &PgConnection) -> Result<(), Error> {
|
||||||
|
let page = self.to_apub(conn)?;
|
||||||
|
let community = Community::read(conn, self.community_id)?;
|
||||||
|
let id = format!("{}/remove/{}", self.ap_id, uuid::Uuid::new_v4());
|
||||||
|
let mut remove = Remove::default();
|
||||||
|
|
||||||
|
populate_object_props(
|
||||||
|
&mut remove.object_props,
|
||||||
|
&community.get_followers_url(),
|
||||||
|
&id,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
remove
|
||||||
|
.remove_props
|
||||||
|
.set_actor_xsd_any_uri(mod_.actor_id.to_owned())?
|
||||||
|
.set_object_base_box(page)?;
|
||||||
|
|
||||||
|
// Undo that fake activity
|
||||||
|
let undo_id = format!("{}/undo/remove/{}", self.ap_id, uuid::Uuid::new_v4());
|
||||||
|
let mut undo = Undo::default();
|
||||||
|
|
||||||
|
populate_object_props(
|
||||||
|
&mut undo.object_props,
|
||||||
|
&community.get_followers_url(),
|
||||||
|
&undo_id,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
undo
|
||||||
|
.undo_props
|
||||||
|
.set_actor_xsd_any_uri(mod_.actor_id.to_owned())?
|
||||||
|
.set_object_base_box(remove)?;
|
||||||
|
|
||||||
|
// Insert the sent activity into the activity table
|
||||||
|
let activity_form = activity::ActivityForm {
|
||||||
|
user_id: mod_.id,
|
||||||
|
data: serde_json::to_value(&undo)?,
|
||||||
|
local: true,
|
||||||
|
updated: None,
|
||||||
|
};
|
||||||
|
activity::Activity::create(&conn, &activity_form)?;
|
||||||
|
|
||||||
|
let community = Community::read(conn, self.community_id)?;
|
||||||
|
send_activity(
|
||||||
|
&undo,
|
||||||
|
&mod_.private_key.as_ref().unwrap(),
|
||||||
|
&mod_.actor_id,
|
||||||
|
community.get_follower_inboxes(&conn)?,
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApubLikeableType for Post {
|
impl ApubLikeableType for Post {
|
||||||
|
|
|
@ -3,12 +3,13 @@ use super::*;
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub enum SharedAcceptedObjects {
|
pub enum SharedAcceptedObjects {
|
||||||
Create(Create),
|
Create(Box<Create>),
|
||||||
Update(Update),
|
Update(Box<Update>),
|
||||||
Like(Like),
|
Like(Box<Like>),
|
||||||
Dislike(Dislike),
|
Dislike(Box<Dislike>),
|
||||||
Delete(Delete),
|
Delete(Box<Delete>),
|
||||||
Undo(Undo),
|
Undo(Box<Undo>),
|
||||||
|
Remove(Box<Remove>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SharedAcceptedObjects {
|
impl SharedAcceptedObjects {
|
||||||
|
@ -20,6 +21,7 @@ impl SharedAcceptedObjects {
|
||||||
SharedAcceptedObjects::Dislike(d) => d.dislike_props.get_object_base_box(),
|
SharedAcceptedObjects::Dislike(d) => d.dislike_props.get_object_base_box(),
|
||||||
SharedAcceptedObjects::Delete(d) => d.delete_props.get_object_base_box(),
|
SharedAcceptedObjects::Delete(d) => d.delete_props.get_object_base_box(),
|
||||||
SharedAcceptedObjects::Undo(d) => d.undo_props.get_object_base_box(),
|
SharedAcceptedObjects::Undo(d) => d.undo_props.get_object_base_box(),
|
||||||
|
SharedAcceptedObjects::Remove(r) => r.remove_props.get_object_base_box(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,6 +58,9 @@ pub async fn shared_inbox(
|
||||||
(SharedAcceptedObjects::Delete(d), Some("Page")) => {
|
(SharedAcceptedObjects::Delete(d), Some("Page")) => {
|
||||||
receive_delete_post(&d, &request, &conn, chat_server)
|
receive_delete_post(&d, &request, &conn, chat_server)
|
||||||
}
|
}
|
||||||
|
(SharedAcceptedObjects::Remove(r), Some("Page")) => {
|
||||||
|
receive_remove_post(&r, &request, &conn, chat_server)
|
||||||
|
}
|
||||||
(SharedAcceptedObjects::Create(c), Some("Note")) => {
|
(SharedAcceptedObjects::Create(c), Some("Note")) => {
|
||||||
receive_create_comment(&c, &request, &conn, chat_server)
|
receive_create_comment(&c, &request, &conn, chat_server)
|
||||||
}
|
}
|
||||||
|
@ -71,12 +76,21 @@ pub async fn shared_inbox(
|
||||||
(SharedAcceptedObjects::Delete(d), Some("Note")) => {
|
(SharedAcceptedObjects::Delete(d), Some("Note")) => {
|
||||||
receive_delete_comment(&d, &request, &conn, chat_server)
|
receive_delete_comment(&d, &request, &conn, chat_server)
|
||||||
}
|
}
|
||||||
|
(SharedAcceptedObjects::Remove(r), Some("Note")) => {
|
||||||
|
receive_remove_comment(&r, &request, &conn, chat_server)
|
||||||
|
}
|
||||||
(SharedAcceptedObjects::Delete(d), Some("Group")) => {
|
(SharedAcceptedObjects::Delete(d), Some("Group")) => {
|
||||||
receive_delete_community(&d, &request, &conn, chat_server)
|
receive_delete_community(&d, &request, &conn, chat_server)
|
||||||
}
|
}
|
||||||
|
(SharedAcceptedObjects::Remove(r), Some("Group")) => {
|
||||||
|
receive_remove_community(&r, &request, &conn, chat_server)
|
||||||
|
}
|
||||||
(SharedAcceptedObjects::Undo(u), Some("Delete")) => {
|
(SharedAcceptedObjects::Undo(u), Some("Delete")) => {
|
||||||
receive_undo_delete(&u, &request, &conn, chat_server)
|
receive_undo_delete(&u, &request, &conn, chat_server)
|
||||||
}
|
}
|
||||||
|
(SharedAcceptedObjects::Undo(u), Some("Remove")) => {
|
||||||
|
receive_undo_remove(&u, &request, &conn, chat_server)
|
||||||
|
}
|
||||||
_ => Err(format_err!("Unknown incoming activity type.")),
|
_ => Err(format_err!("Unknown incoming activity type.")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -588,6 +602,75 @@ fn receive_delete_community(
|
||||||
Ok(HttpResponse::Ok().finish())
|
Ok(HttpResponse::Ok().finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn receive_remove_community(
|
||||||
|
remove: &Remove,
|
||||||
|
request: &HttpRequest,
|
||||||
|
conn: &PgConnection,
|
||||||
|
chat_server: ChatServerParam,
|
||||||
|
) -> Result<HttpResponse, Error> {
|
||||||
|
let mod_uri = remove
|
||||||
|
.remove_props
|
||||||
|
.get_actor_xsd_any_uri()
|
||||||
|
.unwrap()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let group = remove
|
||||||
|
.remove_props
|
||||||
|
.get_object_base_box()
|
||||||
|
.to_owned()
|
||||||
|
.unwrap()
|
||||||
|
.to_owned()
|
||||||
|
.into_concrete::<GroupExt>()?;
|
||||||
|
|
||||||
|
let mod_ = get_or_fetch_and_upsert_remote_user(&mod_uri, &conn)?;
|
||||||
|
verify(request, &mod_.public_key.unwrap())?;
|
||||||
|
|
||||||
|
// Insert the received activity into the activity table
|
||||||
|
let activity_form = activity::ActivityForm {
|
||||||
|
user_id: mod_.id,
|
||||||
|
data: serde_json::to_value(&remove)?,
|
||||||
|
local: false,
|
||||||
|
updated: None,
|
||||||
|
};
|
||||||
|
activity::Activity::create(&conn, &activity_form)?;
|
||||||
|
|
||||||
|
let community_actor_id = CommunityForm::from_apub(&group, &conn)?.actor_id;
|
||||||
|
let community = Community::read_from_actor_id(conn, &community_actor_id)?;
|
||||||
|
|
||||||
|
let community_form = CommunityForm {
|
||||||
|
name: community.name.to_owned(),
|
||||||
|
title: community.title.to_owned(),
|
||||||
|
description: community.description.to_owned(),
|
||||||
|
category_id: community.category_id, // Note: need to keep this due to foreign key constraint
|
||||||
|
creator_id: community.creator_id, // Note: need to keep this due to foreign key constraint
|
||||||
|
removed: Some(true),
|
||||||
|
published: None,
|
||||||
|
updated: Some(naive_now()),
|
||||||
|
deleted: None,
|
||||||
|
nsfw: community.nsfw,
|
||||||
|
actor_id: community.actor_id,
|
||||||
|
local: community.local,
|
||||||
|
private_key: community.private_key,
|
||||||
|
public_key: community.public_key,
|
||||||
|
last_refreshed_at: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Community::update(&conn, community.id, &community_form)?;
|
||||||
|
|
||||||
|
let res = CommunityResponse {
|
||||||
|
community: CommunityView::read(&conn, community.id, None)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
chat_server.do_send(SendCommunityRoomMessage {
|
||||||
|
op: UserOperation::EditCommunity,
|
||||||
|
response: res,
|
||||||
|
community_id: community.id,
|
||||||
|
my_id: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().finish())
|
||||||
|
}
|
||||||
|
|
||||||
fn receive_delete_post(
|
fn receive_delete_post(
|
||||||
delete: &Delete,
|
delete: &Delete,
|
||||||
request: &HttpRequest,
|
request: &HttpRequest,
|
||||||
|
@ -659,6 +742,77 @@ fn receive_delete_post(
|
||||||
Ok(HttpResponse::Ok().finish())
|
Ok(HttpResponse::Ok().finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn receive_remove_post(
|
||||||
|
remove: &Remove,
|
||||||
|
request: &HttpRequest,
|
||||||
|
conn: &PgConnection,
|
||||||
|
chat_server: ChatServerParam,
|
||||||
|
) -> Result<HttpResponse, Error> {
|
||||||
|
let mod_uri = remove
|
||||||
|
.remove_props
|
||||||
|
.get_actor_xsd_any_uri()
|
||||||
|
.unwrap()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let page = remove
|
||||||
|
.remove_props
|
||||||
|
.get_object_base_box()
|
||||||
|
.to_owned()
|
||||||
|
.unwrap()
|
||||||
|
.to_owned()
|
||||||
|
.into_concrete::<Page>()?;
|
||||||
|
|
||||||
|
let mod_ = get_or_fetch_and_upsert_remote_user(&mod_uri, &conn)?;
|
||||||
|
verify(request, &mod_.public_key.unwrap())?;
|
||||||
|
|
||||||
|
// Insert the received activity into the activity table
|
||||||
|
let activity_form = activity::ActivityForm {
|
||||||
|
user_id: mod_.id,
|
||||||
|
data: serde_json::to_value(&remove)?,
|
||||||
|
local: false,
|
||||||
|
updated: None,
|
||||||
|
};
|
||||||
|
activity::Activity::create(&conn, &activity_form)?;
|
||||||
|
|
||||||
|
let post_ap_id = PostForm::from_apub(&page, conn)?.ap_id;
|
||||||
|
let post = Post::read_from_apub_id(conn, &post_ap_id)?;
|
||||||
|
|
||||||
|
let post_form = PostForm {
|
||||||
|
name: post.name.to_owned(),
|
||||||
|
url: post.url.to_owned(),
|
||||||
|
body: post.body.to_owned(),
|
||||||
|
creator_id: post.creator_id.to_owned(),
|
||||||
|
community_id: post.community_id,
|
||||||
|
removed: Some(true),
|
||||||
|
deleted: None,
|
||||||
|
nsfw: post.nsfw,
|
||||||
|
locked: None,
|
||||||
|
stickied: None,
|
||||||
|
updated: Some(naive_now()),
|
||||||
|
embed_title: post.embed_title,
|
||||||
|
embed_description: post.embed_description,
|
||||||
|
embed_html: post.embed_html,
|
||||||
|
thumbnail_url: post.thumbnail_url,
|
||||||
|
ap_id: post.ap_id,
|
||||||
|
local: post.local,
|
||||||
|
published: None,
|
||||||
|
};
|
||||||
|
Post::update(&conn, post.id, &post_form)?;
|
||||||
|
|
||||||
|
// Refetch the view
|
||||||
|
let post_view = PostView::read(&conn, post.id, None)?;
|
||||||
|
|
||||||
|
let res = PostResponse { post: post_view };
|
||||||
|
|
||||||
|
chat_server.do_send(SendPost {
|
||||||
|
op: UserOperation::EditPost,
|
||||||
|
post: res,
|
||||||
|
my_id: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().finish())
|
||||||
|
}
|
||||||
|
|
||||||
fn receive_delete_comment(
|
fn receive_delete_comment(
|
||||||
delete: &Delete,
|
delete: &Delete,
|
||||||
request: &HttpRequest,
|
request: &HttpRequest,
|
||||||
|
@ -727,6 +881,74 @@ fn receive_delete_comment(
|
||||||
Ok(HttpResponse::Ok().finish())
|
Ok(HttpResponse::Ok().finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn receive_remove_comment(
|
||||||
|
remove: &Remove,
|
||||||
|
request: &HttpRequest,
|
||||||
|
conn: &PgConnection,
|
||||||
|
chat_server: ChatServerParam,
|
||||||
|
) -> Result<HttpResponse, Error> {
|
||||||
|
let mod_uri = remove
|
||||||
|
.remove_props
|
||||||
|
.get_actor_xsd_any_uri()
|
||||||
|
.unwrap()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let note = remove
|
||||||
|
.remove_props
|
||||||
|
.get_object_base_box()
|
||||||
|
.to_owned()
|
||||||
|
.unwrap()
|
||||||
|
.to_owned()
|
||||||
|
.into_concrete::<Note>()?;
|
||||||
|
|
||||||
|
let mod_ = get_or_fetch_and_upsert_remote_user(&mod_uri, &conn)?;
|
||||||
|
verify(request, &mod_.public_key.unwrap())?;
|
||||||
|
|
||||||
|
// Insert the received activity into the activity table
|
||||||
|
let activity_form = activity::ActivityForm {
|
||||||
|
user_id: mod_.id,
|
||||||
|
data: serde_json::to_value(&remove)?,
|
||||||
|
local: false,
|
||||||
|
updated: None,
|
||||||
|
};
|
||||||
|
activity::Activity::create(&conn, &activity_form)?;
|
||||||
|
|
||||||
|
let comment_ap_id = CommentForm::from_apub(¬e, &conn)?.ap_id;
|
||||||
|
let comment = Comment::read_from_apub_id(conn, &comment_ap_id)?;
|
||||||
|
let comment_form = CommentForm {
|
||||||
|
content: comment.content.to_owned(),
|
||||||
|
parent_id: comment.parent_id,
|
||||||
|
post_id: comment.post_id,
|
||||||
|
creator_id: comment.creator_id,
|
||||||
|
removed: Some(true),
|
||||||
|
deleted: None,
|
||||||
|
read: None,
|
||||||
|
published: None,
|
||||||
|
updated: Some(naive_now()),
|
||||||
|
ap_id: comment.ap_id,
|
||||||
|
local: comment.local,
|
||||||
|
};
|
||||||
|
Comment::update(&conn, comment.id, &comment_form)?;
|
||||||
|
|
||||||
|
// Refetch the view
|
||||||
|
let comment_view = CommentView::read(&conn, comment.id, None)?;
|
||||||
|
|
||||||
|
// TODO get those recipient actor ids from somewhere
|
||||||
|
let recipient_ids = vec![];
|
||||||
|
let res = CommentResponse {
|
||||||
|
comment: comment_view,
|
||||||
|
recipient_ids,
|
||||||
|
};
|
||||||
|
|
||||||
|
chat_server.do_send(SendComment {
|
||||||
|
op: UserOperation::EditComment,
|
||||||
|
comment: res,
|
||||||
|
my_id: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().finish())
|
||||||
|
}
|
||||||
|
|
||||||
fn receive_undo_delete(
|
fn receive_undo_delete(
|
||||||
undo: &Undo,
|
undo: &Undo,
|
||||||
request: &HttpRequest,
|
request: &HttpRequest,
|
||||||
|
@ -757,6 +979,36 @@ fn receive_undo_delete(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn receive_undo_remove(
|
||||||
|
undo: &Undo,
|
||||||
|
request: &HttpRequest,
|
||||||
|
conn: &PgConnection,
|
||||||
|
chat_server: ChatServerParam,
|
||||||
|
) -> Result<HttpResponse, Error> {
|
||||||
|
let remove = undo
|
||||||
|
.undo_props
|
||||||
|
.get_object_base_box()
|
||||||
|
.to_owned()
|
||||||
|
.unwrap()
|
||||||
|
.to_owned()
|
||||||
|
.into_concrete::<Remove>()?;
|
||||||
|
|
||||||
|
let type_ = remove
|
||||||
|
.remove_props
|
||||||
|
.get_object_base_box()
|
||||||
|
.to_owned()
|
||||||
|
.unwrap()
|
||||||
|
.kind()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match type_ {
|
||||||
|
"Note" => receive_undo_remove_comment(&remove, &request, &conn, chat_server),
|
||||||
|
"Page" => receive_undo_remove_post(&remove, &request, &conn, chat_server),
|
||||||
|
"Group" => receive_undo_remove_community(&remove, &request, &conn, chat_server),
|
||||||
|
d => Err(format_err!("Undo Delete type {} not supported", d)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn receive_undo_delete_comment(
|
fn receive_undo_delete_comment(
|
||||||
delete: &Delete,
|
delete: &Delete,
|
||||||
request: &HttpRequest,
|
request: &HttpRequest,
|
||||||
|
@ -825,6 +1077,74 @@ fn receive_undo_delete_comment(
|
||||||
Ok(HttpResponse::Ok().finish())
|
Ok(HttpResponse::Ok().finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn receive_undo_remove_comment(
|
||||||
|
remove: &Remove,
|
||||||
|
request: &HttpRequest,
|
||||||
|
conn: &PgConnection,
|
||||||
|
chat_server: ChatServerParam,
|
||||||
|
) -> Result<HttpResponse, Error> {
|
||||||
|
let mod_uri = remove
|
||||||
|
.remove_props
|
||||||
|
.get_actor_xsd_any_uri()
|
||||||
|
.unwrap()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let note = remove
|
||||||
|
.remove_props
|
||||||
|
.get_object_base_box()
|
||||||
|
.to_owned()
|
||||||
|
.unwrap()
|
||||||
|
.to_owned()
|
||||||
|
.into_concrete::<Note>()?;
|
||||||
|
|
||||||
|
let mod_ = get_or_fetch_and_upsert_remote_user(&mod_uri, &conn)?;
|
||||||
|
verify(request, &mod_.public_key.unwrap())?;
|
||||||
|
|
||||||
|
// Insert the received activity into the activity table
|
||||||
|
let activity_form = activity::ActivityForm {
|
||||||
|
user_id: mod_.id,
|
||||||
|
data: serde_json::to_value(&remove)?,
|
||||||
|
local: false,
|
||||||
|
updated: None,
|
||||||
|
};
|
||||||
|
activity::Activity::create(&conn, &activity_form)?;
|
||||||
|
|
||||||
|
let comment_ap_id = CommentForm::from_apub(¬e, &conn)?.ap_id;
|
||||||
|
let comment = Comment::read_from_apub_id(conn, &comment_ap_id)?;
|
||||||
|
let comment_form = CommentForm {
|
||||||
|
content: comment.content.to_owned(),
|
||||||
|
parent_id: comment.parent_id,
|
||||||
|
post_id: comment.post_id,
|
||||||
|
creator_id: comment.creator_id,
|
||||||
|
removed: Some(false),
|
||||||
|
deleted: None,
|
||||||
|
read: None,
|
||||||
|
published: None,
|
||||||
|
updated: Some(naive_now()),
|
||||||
|
ap_id: comment.ap_id,
|
||||||
|
local: comment.local,
|
||||||
|
};
|
||||||
|
Comment::update(&conn, comment.id, &comment_form)?;
|
||||||
|
|
||||||
|
// Refetch the view
|
||||||
|
let comment_view = CommentView::read(&conn, comment.id, None)?;
|
||||||
|
|
||||||
|
// TODO get those recipient actor ids from somewhere
|
||||||
|
let recipient_ids = vec![];
|
||||||
|
let res = CommentResponse {
|
||||||
|
comment: comment_view,
|
||||||
|
recipient_ids,
|
||||||
|
};
|
||||||
|
|
||||||
|
chat_server.do_send(SendComment {
|
||||||
|
op: UserOperation::EditComment,
|
||||||
|
comment: res,
|
||||||
|
my_id: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().finish())
|
||||||
|
}
|
||||||
|
|
||||||
fn receive_undo_delete_post(
|
fn receive_undo_delete_post(
|
||||||
delete: &Delete,
|
delete: &Delete,
|
||||||
request: &HttpRequest,
|
request: &HttpRequest,
|
||||||
|
@ -896,6 +1216,77 @@ fn receive_undo_delete_post(
|
||||||
Ok(HttpResponse::Ok().finish())
|
Ok(HttpResponse::Ok().finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn receive_undo_remove_post(
|
||||||
|
remove: &Remove,
|
||||||
|
request: &HttpRequest,
|
||||||
|
conn: &PgConnection,
|
||||||
|
chat_server: ChatServerParam,
|
||||||
|
) -> Result<HttpResponse, Error> {
|
||||||
|
let mod_uri = remove
|
||||||
|
.remove_props
|
||||||
|
.get_actor_xsd_any_uri()
|
||||||
|
.unwrap()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let page = remove
|
||||||
|
.remove_props
|
||||||
|
.get_object_base_box()
|
||||||
|
.to_owned()
|
||||||
|
.unwrap()
|
||||||
|
.to_owned()
|
||||||
|
.into_concrete::<Page>()?;
|
||||||
|
|
||||||
|
let mod_ = get_or_fetch_and_upsert_remote_user(&mod_uri, &conn)?;
|
||||||
|
verify(request, &mod_.public_key.unwrap())?;
|
||||||
|
|
||||||
|
// Insert the received activity into the activity table
|
||||||
|
let activity_form = activity::ActivityForm {
|
||||||
|
user_id: mod_.id,
|
||||||
|
data: serde_json::to_value(&remove)?,
|
||||||
|
local: false,
|
||||||
|
updated: None,
|
||||||
|
};
|
||||||
|
activity::Activity::create(&conn, &activity_form)?;
|
||||||
|
|
||||||
|
let post_ap_id = PostForm::from_apub(&page, conn)?.ap_id;
|
||||||
|
let post = Post::read_from_apub_id(conn, &post_ap_id)?;
|
||||||
|
|
||||||
|
let post_form = PostForm {
|
||||||
|
name: post.name.to_owned(),
|
||||||
|
url: post.url.to_owned(),
|
||||||
|
body: post.body.to_owned(),
|
||||||
|
creator_id: post.creator_id.to_owned(),
|
||||||
|
community_id: post.community_id,
|
||||||
|
removed: Some(false),
|
||||||
|
deleted: None,
|
||||||
|
nsfw: post.nsfw,
|
||||||
|
locked: None,
|
||||||
|
stickied: None,
|
||||||
|
updated: Some(naive_now()),
|
||||||
|
embed_title: post.embed_title,
|
||||||
|
embed_description: post.embed_description,
|
||||||
|
embed_html: post.embed_html,
|
||||||
|
thumbnail_url: post.thumbnail_url,
|
||||||
|
ap_id: post.ap_id,
|
||||||
|
local: post.local,
|
||||||
|
published: None,
|
||||||
|
};
|
||||||
|
Post::update(&conn, post.id, &post_form)?;
|
||||||
|
|
||||||
|
// Refetch the view
|
||||||
|
let post_view = PostView::read(&conn, post.id, None)?;
|
||||||
|
|
||||||
|
let res = PostResponse { post: post_view };
|
||||||
|
|
||||||
|
chat_server.do_send(SendPost {
|
||||||
|
op: UserOperation::EditPost,
|
||||||
|
post: res,
|
||||||
|
my_id: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().finish())
|
||||||
|
}
|
||||||
|
|
||||||
fn receive_undo_delete_community(
|
fn receive_undo_delete_community(
|
||||||
delete: &Delete,
|
delete: &Delete,
|
||||||
request: &HttpRequest,
|
request: &HttpRequest,
|
||||||
|
@ -964,3 +1355,72 @@ fn receive_undo_delete_community(
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().finish())
|
Ok(HttpResponse::Ok().finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn receive_undo_remove_community(
|
||||||
|
remove: &Remove,
|
||||||
|
request: &HttpRequest,
|
||||||
|
conn: &PgConnection,
|
||||||
|
chat_server: ChatServerParam,
|
||||||
|
) -> Result<HttpResponse, Error> {
|
||||||
|
let mod_uri = remove
|
||||||
|
.remove_props
|
||||||
|
.get_actor_xsd_any_uri()
|
||||||
|
.unwrap()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let group = remove
|
||||||
|
.remove_props
|
||||||
|
.get_object_base_box()
|
||||||
|
.to_owned()
|
||||||
|
.unwrap()
|
||||||
|
.to_owned()
|
||||||
|
.into_concrete::<GroupExt>()?;
|
||||||
|
|
||||||
|
let mod_ = get_or_fetch_and_upsert_remote_user(&mod_uri, &conn)?;
|
||||||
|
verify(request, &mod_.public_key.unwrap())?;
|
||||||
|
|
||||||
|
// Insert the received activity into the activity table
|
||||||
|
let activity_form = activity::ActivityForm {
|
||||||
|
user_id: mod_.id,
|
||||||
|
data: serde_json::to_value(&remove)?,
|
||||||
|
local: false,
|
||||||
|
updated: None,
|
||||||
|
};
|
||||||
|
activity::Activity::create(&conn, &activity_form)?;
|
||||||
|
|
||||||
|
let community_actor_id = CommunityForm::from_apub(&group, &conn)?.actor_id;
|
||||||
|
let community = Community::read_from_actor_id(conn, &community_actor_id)?;
|
||||||
|
|
||||||
|
let community_form = CommunityForm {
|
||||||
|
name: community.name.to_owned(),
|
||||||
|
title: community.title.to_owned(),
|
||||||
|
description: community.description.to_owned(),
|
||||||
|
category_id: community.category_id, // Note: need to keep this due to foreign key constraint
|
||||||
|
creator_id: community.creator_id, // Note: need to keep this due to foreign key constraint
|
||||||
|
removed: Some(false),
|
||||||
|
published: None,
|
||||||
|
updated: Some(naive_now()),
|
||||||
|
deleted: None,
|
||||||
|
nsfw: community.nsfw,
|
||||||
|
actor_id: community.actor_id,
|
||||||
|
local: community.local,
|
||||||
|
private_key: community.private_key,
|
||||||
|
public_key: community.public_key,
|
||||||
|
last_refreshed_at: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Community::update(&conn, community.id, &community_form)?;
|
||||||
|
|
||||||
|
let res = CommunityResponse {
|
||||||
|
community: CommunityView::read(&conn, community.id, None)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
chat_server.do_send(SendCommunityRoomMessage {
|
||||||
|
op: UserOperation::EditCommunity,
|
||||||
|
response: res,
|
||||||
|
community_id: community.id,
|
||||||
|
my_id: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().finish())
|
||||||
|
}
|
||||||
|
|
|
@ -98,6 +98,14 @@ impl ActorType for User_ {
|
||||||
fn send_undo_delete(&self, _creator: &User_, _conn: &PgConnection) -> Result<(), Error> {
|
fn send_undo_delete(&self, _creator: &User_, _conn: &PgConnection) -> Result<(), Error> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn send_remove(&self, _creator: &User_, _conn: &PgConnection) -> Result<(), Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_undo_remove(&self, _creator: &User_, _conn: &PgConnection) -> Result<(), Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromApub for UserForm {
|
impl FromApub for UserForm {
|
||||||
|
|
276
ui/src/api_tests/api.spec.ts
vendored
276
ui/src/api_tests/api.spec.ts
vendored
|
@ -603,6 +603,282 @@ describe('main', () => {
|
||||||
expect(getCommunityResAgain.community.deleted).toBe(false);
|
expect(getCommunityResAgain.community.deleted).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('remove things', () => {
|
||||||
|
test('/u/lemmy_beta removes and unremoves a federated comment, post, and community, lemmy_alpha sees its removed.', async () => {
|
||||||
|
// Create a test community
|
||||||
|
let communityName = 'test_community_rem';
|
||||||
|
let communityForm: CommunityForm = {
|
||||||
|
name: communityName,
|
||||||
|
title: communityName,
|
||||||
|
category_id: 1,
|
||||||
|
nsfw: false,
|
||||||
|
auth: lemmyBetaAuth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let createCommunityRes: CommunityResponse = await fetch(
|
||||||
|
`${lemmyBetaApiUrl}/community`,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(communityForm),
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
|
||||||
|
expect(createCommunityRes.community.name).toBe(communityName);
|
||||||
|
|
||||||
|
// Cache it on lemmy_alpha
|
||||||
|
let searchUrl = `${lemmyAlphaApiUrl}/search?q=http://lemmy_beta:8550/c/${communityName}&type_=All&sort=TopAll`;
|
||||||
|
let searchResponse: SearchResponse = await fetch(searchUrl, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(d => d.json());
|
||||||
|
|
||||||
|
let communityOnAlphaId = searchResponse.communities[0].id;
|
||||||
|
|
||||||
|
// Follow it
|
||||||
|
let followForm: FollowCommunityForm = {
|
||||||
|
community_id: communityOnAlphaId,
|
||||||
|
follow: true,
|
||||||
|
auth: lemmyAlphaAuth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let followRes: CommunityResponse = await fetch(
|
||||||
|
`${lemmyAlphaApiUrl}/community/follow`,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(followForm),
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
|
||||||
|
// Make sure the follow response went through
|
||||||
|
expect(followRes.community.local).toBe(false);
|
||||||
|
expect(followRes.community.name).toBe(communityName);
|
||||||
|
|
||||||
|
// Lemmy beta creates a test post
|
||||||
|
let postName = 'A jest test post with remove';
|
||||||
|
let createPostForm: PostForm = {
|
||||||
|
name: postName,
|
||||||
|
auth: lemmyBetaAuth,
|
||||||
|
community_id: createCommunityRes.community.id,
|
||||||
|
creator_id: 2,
|
||||||
|
nsfw: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let createPostRes: PostResponse = await fetch(`${lemmyBetaApiUrl}/post`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(createPostForm),
|
||||||
|
}).then(d => d.json());
|
||||||
|
expect(createPostRes.post.name).toBe(postName);
|
||||||
|
|
||||||
|
// Lemmy beta creates a test comment
|
||||||
|
let commentContent = 'A jest test federated comment with remove';
|
||||||
|
let createCommentForm: CommentForm = {
|
||||||
|
content: commentContent,
|
||||||
|
post_id: createPostRes.post.id,
|
||||||
|
auth: lemmyBetaAuth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let createCommentRes: CommentResponse = await fetch(
|
||||||
|
`${lemmyBetaApiUrl}/comment`,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(createCommentForm),
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
|
||||||
|
expect(createCommentRes.comment.content).toBe(commentContent);
|
||||||
|
|
||||||
|
// lemmy_beta removes the comment
|
||||||
|
let removeCommentForm: CommentForm = {
|
||||||
|
content: commentContent,
|
||||||
|
edit_id: createCommentRes.comment.id,
|
||||||
|
post_id: createPostRes.post.id,
|
||||||
|
removed: true,
|
||||||
|
auth: lemmyBetaAuth,
|
||||||
|
creator_id: createCommentRes.comment.creator_id,
|
||||||
|
};
|
||||||
|
|
||||||
|
let removeCommentRes: CommentResponse = await fetch(
|
||||||
|
`${lemmyBetaApiUrl}/comment`,
|
||||||
|
{
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(removeCommentForm),
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
expect(removeCommentRes.comment.removed).toBe(true);
|
||||||
|
|
||||||
|
// lemmy_alpha sees that the comment is removed
|
||||||
|
let getPostUrl = `${lemmyAlphaApiUrl}/post?id=4`;
|
||||||
|
let getPostRes: GetPostResponse = await fetch(getPostUrl, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(d => d.json());
|
||||||
|
expect(getPostRes.comments[0].removed).toBe(true);
|
||||||
|
|
||||||
|
// lemmy_beta undeletes the comment
|
||||||
|
let unremoveCommentForm: CommentForm = {
|
||||||
|
content: commentContent,
|
||||||
|
edit_id: createCommentRes.comment.id,
|
||||||
|
post_id: createPostRes.post.id,
|
||||||
|
removed: false,
|
||||||
|
auth: lemmyBetaAuth,
|
||||||
|
creator_id: createCommentRes.comment.creator_id,
|
||||||
|
};
|
||||||
|
|
||||||
|
let unremoveCommentRes: CommentResponse = await fetch(
|
||||||
|
`${lemmyBetaApiUrl}/comment`,
|
||||||
|
{
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(unremoveCommentForm),
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
expect(unremoveCommentRes.comment.removed).toBe(false);
|
||||||
|
|
||||||
|
// lemmy_alpha sees that the comment is undeleted
|
||||||
|
let getPostUnremoveRes: GetPostResponse = await fetch(getPostUrl, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(d => d.json());
|
||||||
|
expect(getPostUnremoveRes.comments[0].removed).toBe(false);
|
||||||
|
|
||||||
|
// lemmy_beta deletes the post
|
||||||
|
let removePostForm: PostForm = {
|
||||||
|
name: postName,
|
||||||
|
edit_id: createPostRes.post.id,
|
||||||
|
auth: lemmyBetaAuth,
|
||||||
|
community_id: createPostRes.post.community_id,
|
||||||
|
creator_id: createPostRes.post.creator_id,
|
||||||
|
nsfw: false,
|
||||||
|
removed: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
let removePostRes: PostResponse = await fetch(`${lemmyBetaApiUrl}/post`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(removePostForm),
|
||||||
|
}).then(d => d.json());
|
||||||
|
expect(removePostRes.post.removed).toBe(true);
|
||||||
|
|
||||||
|
// Make sure lemmy_alpha sees the post is deleted
|
||||||
|
let getPostResAgain: GetPostResponse = await fetch(getPostUrl, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(d => d.json());
|
||||||
|
expect(getPostResAgain.post.removed).toBe(true);
|
||||||
|
|
||||||
|
// lemmy_beta unremoves the post
|
||||||
|
let unremovePostForm: PostForm = {
|
||||||
|
name: postName,
|
||||||
|
edit_id: createPostRes.post.id,
|
||||||
|
auth: lemmyBetaAuth,
|
||||||
|
community_id: createPostRes.post.community_id,
|
||||||
|
creator_id: createPostRes.post.creator_id,
|
||||||
|
nsfw: false,
|
||||||
|
removed: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let unremovePostRes: PostResponse = await fetch(
|
||||||
|
`${lemmyBetaApiUrl}/post`,
|
||||||
|
{
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(unremovePostForm),
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
expect(unremovePostRes.post.removed).toBe(false);
|
||||||
|
|
||||||
|
// Make sure lemmy_alpha sees the post is unremoved
|
||||||
|
let getPostResAgainTwo: GetPostResponse = await fetch(getPostUrl, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(d => d.json());
|
||||||
|
expect(getPostResAgainTwo.post.removed).toBe(false);
|
||||||
|
|
||||||
|
// lemmy_beta deletes the community
|
||||||
|
let removeCommunityForm: CommunityForm = {
|
||||||
|
name: communityName,
|
||||||
|
title: communityName,
|
||||||
|
category_id: 1,
|
||||||
|
edit_id: createCommunityRes.community.id,
|
||||||
|
nsfw: false,
|
||||||
|
removed: true,
|
||||||
|
auth: lemmyBetaAuth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let removeCommunityRes: CommunityResponse = await fetch(
|
||||||
|
`${lemmyBetaApiUrl}/community`,
|
||||||
|
{
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(removeCommunityForm),
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
|
||||||
|
// Make sure the delete went through
|
||||||
|
expect(removeCommunityRes.community.removed).toBe(true);
|
||||||
|
|
||||||
|
// Re-get it from alpha, make sure its removed there too
|
||||||
|
let getCommunityUrl = `${lemmyAlphaApiUrl}/community?id=${communityOnAlphaId}&auth=${lemmyAlphaAuth}`;
|
||||||
|
let getCommunityRes: GetCommunityResponse = await fetch(getCommunityUrl, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(d => d.json());
|
||||||
|
|
||||||
|
expect(getCommunityRes.community.removed).toBe(true);
|
||||||
|
|
||||||
|
// lemmy_beta unremoves the community
|
||||||
|
let unremoveCommunityForm: CommunityForm = {
|
||||||
|
name: communityName,
|
||||||
|
title: communityName,
|
||||||
|
category_id: 1,
|
||||||
|
edit_id: createCommunityRes.community.id,
|
||||||
|
nsfw: false,
|
||||||
|
removed: false,
|
||||||
|
auth: lemmyBetaAuth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let unremoveCommunityRes: CommunityResponse = await fetch(
|
||||||
|
`${lemmyBetaApiUrl}/community`,
|
||||||
|
{
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(unremoveCommunityForm),
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
|
||||||
|
// Make sure the delete went through
|
||||||
|
expect(unremoveCommunityRes.community.removed).toBe(false);
|
||||||
|
|
||||||
|
// Re-get it from alpha, make sure its deleted there too
|
||||||
|
let getCommunityResAgain: GetCommunityResponse = await fetch(
|
||||||
|
getCommunityUrl,
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
expect(getCommunityResAgain.community.removed).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function wrapper(form: any): string {
|
function wrapper(form: any): string {
|
||||||
|
|
Loading…
Reference in a new issue