mirror of
https://github.com/LemmyNet/lemmy
synced 2024-11-23 04:53:06 +00:00
Merge pull request #1678 from LemmyNet/rewrite-post
Rewrite apub post (de)serialization using structs (ref #1657)
This commit is contained in:
commit
bb7750d8ee
49 changed files with 689 additions and 484 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1760,6 +1760,7 @@ dependencies = [
|
||||||
"lemmy_utils",
|
"lemmy_utils",
|
||||||
"lemmy_websocket",
|
"lemmy_websocket",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ use lemmy_api_common::{
|
||||||
mark_post_as_read,
|
mark_post_as_read,
|
||||||
post::*,
|
post::*,
|
||||||
};
|
};
|
||||||
use lemmy_apub::{ApubLikeableType, ApubObjectType};
|
use lemmy_apub::{activities::post::update::UpdatePost, ApubLikeableType};
|
||||||
use lemmy_db_queries::{source::post::Post_, Crud, Likeable, Saveable};
|
use lemmy_db_queries::{source::post::Post_, Crud, Likeable, Saveable};
|
||||||
use lemmy_db_schema::source::{moderator::*, post::*};
|
use lemmy_db_schema::source::{moderator::*, post::*};
|
||||||
use lemmy_db_views::post_view::PostView;
|
use lemmy_db_views::post_view::PostView;
|
||||||
|
@ -140,9 +140,7 @@ impl Perform for LockPost {
|
||||||
blocking(context.pool(), move |conn| ModLockPost::create(conn, &form)).await??;
|
blocking(context.pool(), move |conn| ModLockPost::create(conn, &form)).await??;
|
||||||
|
|
||||||
// apub updates
|
// apub updates
|
||||||
updated_post
|
UpdatePost::send(&updated_post, &local_user_view.person, context).await?;
|
||||||
.send_update(&local_user_view.person, context)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Refetch the post
|
// Refetch the post
|
||||||
let post_id = data.post_id;
|
let post_id = data.post_id;
|
||||||
|
@ -214,9 +212,7 @@ impl Perform for StickyPost {
|
||||||
|
|
||||||
// Apub updates
|
// Apub updates
|
||||||
// TODO stickied should pry work like locked for ease of use
|
// TODO stickied should pry work like locked for ease of use
|
||||||
updated_post
|
UpdatePost::send(&updated_post, &local_user_view.person, context).await?;
|
||||||
.send_update(&local_user_view.person, context)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Refetch the post
|
// Refetch the post
|
||||||
let post_id = data.post_id;
|
let post_id = data.post_id;
|
||||||
|
|
|
@ -7,7 +7,12 @@ use lemmy_api_common::{
|
||||||
mark_post_as_read,
|
mark_post_as_read,
|
||||||
post::*,
|
post::*,
|
||||||
};
|
};
|
||||||
use lemmy_apub::{generate_apub_endpoint, ApubLikeableType, ApubObjectType, EndpointType};
|
use lemmy_apub::{
|
||||||
|
activities::post::create::CreatePost as CreateApubPost,
|
||||||
|
generate_apub_endpoint,
|
||||||
|
ApubLikeableType,
|
||||||
|
EndpointType,
|
||||||
|
};
|
||||||
use lemmy_db_queries::{source::post::Post_, Crud, Likeable};
|
use lemmy_db_queries::{source::post::Post_, Crud, Likeable};
|
||||||
use lemmy_db_schema::source::post::*;
|
use lemmy_db_schema::source::post::*;
|
||||||
use lemmy_db_views::post_view::PostView;
|
use lemmy_db_views::post_view::PostView;
|
||||||
|
@ -82,9 +87,7 @@ impl PerformCrud for CreatePost {
|
||||||
.await?
|
.await?
|
||||||
.map_err(|_| ApiError::err("couldnt_create_post"))?;
|
.map_err(|_| ApiError::err("couldnt_create_post"))?;
|
||||||
|
|
||||||
updated_post
|
CreateApubPost::send(&updated_post, &local_user_view.person, context).await?;
|
||||||
.send_create(&local_user_view.person, context)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// They like their own post by default
|
// They like their own post by default
|
||||||
let person_id = local_user_view.person.id;
|
let person_id = local_user_view.person.id;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::PerformCrud;
|
use crate::PerformCrud;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
use lemmy_api_common::{blocking, check_community_ban, get_local_user_view_from_jwt, post::*};
|
use lemmy_api_common::{blocking, check_community_ban, get_local_user_view_from_jwt, post::*};
|
||||||
use lemmy_apub::ApubObjectType;
|
use lemmy_apub::activities::post::update::UpdatePost;
|
||||||
use lemmy_db_queries::{source::post::Post_, Crud, DeleteableOrRemoveable};
|
use lemmy_db_queries::{source::post::Post_, Crud, DeleteableOrRemoveable};
|
||||||
use lemmy_db_schema::{naive_now, source::post::*};
|
use lemmy_db_schema::{naive_now, source::post::*};
|
||||||
use lemmy_db_views::post_view::PostView;
|
use lemmy_db_views::post_view::PostView;
|
||||||
|
@ -89,9 +89,7 @@ impl PerformCrud for EditPost {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Send apub update
|
// Send apub update
|
||||||
updated_post
|
UpdatePost::send(&updated_post, &local_user_view.person, context).await?;
|
||||||
.send_update(&local_user_view.person, context)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let post_id = data.post_id;
|
let post_id = data.post_id;
|
||||||
let mut post_view = blocking(context.pool(), move |conn| {
|
let mut post_view = blocking(context.pool(), move |conn| {
|
||||||
|
|
|
@ -1,14 +1,21 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{
|
activities::{
|
||||||
comment::{get_notif_recipients, send_websocket_message},
|
comment::{get_notif_recipients, send_websocket_message},
|
||||||
|
extract_community,
|
||||||
verify_activity,
|
verify_activity,
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
},
|
},
|
||||||
objects::FromApub,
|
objects::FromApub,
|
||||||
|
ActorType,
|
||||||
NoteExt,
|
NoteExt,
|
||||||
};
|
};
|
||||||
use activitystreams::{activity::kind::CreateType, base::BaseExt};
|
use activitystreams::{activity::kind::CreateType, base::BaseExt};
|
||||||
use lemmy_apub_lib::{verify_domains_match_opt, ActivityCommonFields, ActivityHandler, PublicUrl};
|
use lemmy_apub_lib::{
|
||||||
|
values::PublicUrl,
|
||||||
|
verify_domains_match_opt,
|
||||||
|
ActivityCommonFields,
|
||||||
|
ActivityHandler,
|
||||||
|
};
|
||||||
use lemmy_db_schema::source::comment::Comment;
|
use lemmy_db_schema::source::comment::Comment;
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||||
|
@ -33,8 +40,16 @@ impl ActivityHandler for CreateComment {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
|
let community = extract_community(&self.cc, context, request_counter).await?;
|
||||||
|
|
||||||
verify_activity(self.common())?;
|
verify_activity(self.common())?;
|
||||||
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
|
verify_person_in_community(
|
||||||
|
&self.common.actor,
|
||||||
|
&community.actor_id(),
|
||||||
|
context,
|
||||||
|
request_counter,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
verify_domains_match_opt(&self.common.actor, self.object.id_unchecked())?;
|
verify_domains_match_opt(&self.common.actor, self.object.id_unchecked())?;
|
||||||
// TODO: should add a check that the correct community is in cc (probably needs changes to
|
// TODO: should add a check that the correct community is in cc (probably needs changes to
|
||||||
// comment deserialization)
|
// comment deserialization)
|
||||||
|
|
|
@ -1,8 +1,16 @@
|
||||||
use crate::activities::{comment::send_websocket_message, verify_mod_action};
|
use crate::{
|
||||||
|
activities::{comment::send_websocket_message, verify_mod_action},
|
||||||
|
check_is_apub_id_valid,
|
||||||
|
fetcher::objects::get_or_fetch_and_insert_comment,
|
||||||
|
};
|
||||||
use activitystreams::activity::kind::RemoveType;
|
use activitystreams::activity::kind::RemoveType;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use crate::{check_is_apub_id_valid, fetcher::objects::get_or_fetch_and_insert_comment};
|
use lemmy_apub_lib::{
|
||||||
use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl};
|
values::PublicUrl,
|
||||||
|
verify_domains_match,
|
||||||
|
ActivityCommonFields,
|
||||||
|
ActivityHandlerNew,
|
||||||
|
};
|
||||||
use lemmy_db_queries::source::comment::Comment_;
|
use lemmy_db_queries::source::comment::Comment_;
|
||||||
use lemmy_db_schema::source::comment::Comment;
|
use lemmy_db_schema::source::comment::Comment;
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
|
|
|
@ -1,11 +1,19 @@
|
||||||
use crate::activities::{
|
use crate::{
|
||||||
comment::{remove::RemoveComment, send_websocket_message},
|
activities::{
|
||||||
verify_mod_action,
|
comment::{remove::RemoveComment, send_websocket_message},
|
||||||
|
verify_mod_action,
|
||||||
|
},
|
||||||
|
check_is_apub_id_valid,
|
||||||
|
fetcher::objects::get_or_fetch_and_insert_comment,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::UndoType;
|
use activitystreams::activity::kind::UndoType;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use crate::{check_is_apub_id_valid, fetcher::objects::get_or_fetch_and_insert_comment};
|
use lemmy_apub_lib::{
|
||||||
use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl};
|
values::PublicUrl,
|
||||||
|
verify_domains_match,
|
||||||
|
ActivityCommonFields,
|
||||||
|
ActivityHandlerNew,
|
||||||
|
};
|
||||||
use lemmy_db_queries::source::comment::Comment_;
|
use lemmy_db_queries::source::comment::Comment_;
|
||||||
use lemmy_db_schema::source::comment::Comment;
|
use lemmy_db_schema::source::comment::Comment;
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
|
|
|
@ -1,14 +1,21 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{
|
activities::{
|
||||||
comment::{get_notif_recipients, send_websocket_message},
|
comment::{get_notif_recipients, send_websocket_message},
|
||||||
|
extract_community,
|
||||||
verify_activity,
|
verify_activity,
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
},
|
},
|
||||||
objects::FromApub,
|
objects::FromApub,
|
||||||
|
ActorType,
|
||||||
NoteExt,
|
NoteExt,
|
||||||
};
|
};
|
||||||
use activitystreams::{activity::kind::UpdateType, base::BaseExt};
|
use activitystreams::{activity::kind::UpdateType, base::BaseExt};
|
||||||
use lemmy_apub_lib::{verify_domains_match_opt, ActivityCommonFields, ActivityHandler, PublicUrl};
|
use lemmy_apub_lib::{
|
||||||
|
values::PublicUrl,
|
||||||
|
verify_domains_match_opt,
|
||||||
|
ActivityCommonFields,
|
||||||
|
ActivityHandler,
|
||||||
|
};
|
||||||
use lemmy_db_schema::source::comment::Comment;
|
use lemmy_db_schema::source::comment::Comment;
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||||
|
@ -33,8 +40,16 @@ impl ActivityHandler for UpdateComment {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
|
let community = extract_community(&self.cc, context, request_counter).await?;
|
||||||
|
|
||||||
verify_activity(self.common())?;
|
verify_activity(self.common())?;
|
||||||
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
|
verify_person_in_community(
|
||||||
|
&self.common.actor,
|
||||||
|
&community.actor_id(),
|
||||||
|
context,
|
||||||
|
request_counter,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
verify_domains_match_opt(&self.common.actor, self.object.id_unchecked())?;
|
verify_domains_match_opt(&self.common.actor, self.object.id_unchecked())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use activitystreams::{activity::kind::AddType, base::AnyBase};
|
use activitystreams::{activity::kind::AddType, base::AnyBase};
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{ActivityCommonFields, ActivityHandler, PublicUrl};
|
use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
|
||||||
use lemmy_db_queries::{source::community::CommunityModerator_, Joinable};
|
use lemmy_db_queries::{source::community::CommunityModerator_, Joinable};
|
||||||
use lemmy_db_schema::source::community::{CommunityModerator, CommunityModeratorForm};
|
use lemmy_db_schema::source::community::{CommunityModerator, CommunityModeratorForm};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
|
@ -38,7 +38,7 @@ impl ActivityHandler for AddMod {
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self.common())?;
|
verify_activity(self.common())?;
|
||||||
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
|
verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
|
||||||
verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
|
verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
|
||||||
verify_add_remove_moderator_target(&self.target, self.cc[0].clone())?;
|
verify_add_remove_moderator_target(&self.target, self.cc[0].clone())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -4,12 +4,14 @@ use crate::{
|
||||||
community::{
|
community::{
|
||||||
add_mod::AddMod,
|
add_mod::AddMod,
|
||||||
block_user::BlockUserFromCommunity,
|
block_user::BlockUserFromCommunity,
|
||||||
|
list_community_follower_inboxes,
|
||||||
undo_block_user::UndoBlockUserFromCommunity,
|
undo_block_user::UndoBlockUserFromCommunity,
|
||||||
},
|
},
|
||||||
deletion::{
|
deletion::{
|
||||||
delete::DeletePostCommentOrCommunity,
|
delete::DeletePostCommentOrCommunity,
|
||||||
undo_delete::UndoDeletePostCommentOrCommunity,
|
undo_delete::UndoDeletePostCommentOrCommunity,
|
||||||
},
|
},
|
||||||
|
generate_activity_id,
|
||||||
post::{create::CreatePost, update::UpdatePost},
|
post::{create::CreatePost, update::UpdatePost},
|
||||||
removal::{
|
removal::{
|
||||||
remove::RemovePostCommentCommunityOrMod,
|
remove::RemovePostCommentCommunityOrMod,
|
||||||
|
@ -24,11 +26,16 @@ use crate::{
|
||||||
undo_like::UndoLikePostOrComment,
|
undo_like::UndoLikePostOrComment,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
activity_queue::send_activity_new,
|
||||||
|
extensions::context::lemmy_context,
|
||||||
http::is_activity_already_known,
|
http::is_activity_already_known,
|
||||||
insert_activity,
|
insert_activity,
|
||||||
|
ActorType,
|
||||||
|
CommunityType,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::AnnounceType;
|
use activitystreams::activity::kind::AnnounceType;
|
||||||
use lemmy_apub_lib::{ActivityCommonFields, ActivityHandler, PublicUrl};
|
use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
|
||||||
|
use lemmy_db_schema::source::community::Community;
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -66,6 +73,38 @@ pub struct AnnounceActivity {
|
||||||
common: ActivityCommonFields,
|
common: ActivityCommonFields,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AnnounceActivity {
|
||||||
|
pub async fn send(
|
||||||
|
object: AnnouncableActivities,
|
||||||
|
community: &Community,
|
||||||
|
additional_inboxes: Vec<Url>,
|
||||||
|
context: &LemmyContext,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
let announce = AnnounceActivity {
|
||||||
|
to: PublicUrl::Public,
|
||||||
|
object,
|
||||||
|
cc: vec![community.followers_url()],
|
||||||
|
kind: AnnounceType::Announce,
|
||||||
|
common: ActivityCommonFields {
|
||||||
|
context: lemmy_context(),
|
||||||
|
id: generate_activity_id(AnnounceType::Announce)?,
|
||||||
|
actor: community.actor_id(),
|
||||||
|
unparsed: Default::default(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let inboxes = list_community_follower_inboxes(community, additional_inboxes, context).await?;
|
||||||
|
send_activity_new(
|
||||||
|
context,
|
||||||
|
&announce,
|
||||||
|
&announce.common.id,
|
||||||
|
community,
|
||||||
|
inboxes,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl ActivityHandler for AnnounceActivity {
|
impl ActivityHandler for AnnounceActivity {
|
||||||
async fn verify(
|
async fn verify(
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::BlockType;
|
use activitystreams::activity::kind::BlockType;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{ActivityCommonFields, ActivityHandler, PublicUrl};
|
use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
|
||||||
use lemmy_db_queries::{Bannable, Followable};
|
use lemmy_db_queries::{Bannable, Followable};
|
||||||
use lemmy_db_schema::source::community::{
|
use lemmy_db_schema::source::community::{
|
||||||
CommunityFollower,
|
CommunityFollower,
|
||||||
|
@ -36,7 +36,7 @@ impl ActivityHandler for BlockUserFromCommunity {
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self.common())?;
|
verify_activity(self.common())?;
|
||||||
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
|
verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
|
||||||
verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
|
verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
|
use crate::{check_is_apub_id_valid, CommunityType};
|
||||||
|
use itertools::Itertools;
|
||||||
use lemmy_api_common::{blocking, community::CommunityResponse};
|
use lemmy_api_common::{blocking, community::CommunityResponse};
|
||||||
use lemmy_db_schema::CommunityId;
|
use lemmy_db_schema::{source::community::Community, CommunityId};
|
||||||
use lemmy_db_views_actor::community_view::CommunityView;
|
use lemmy_db_views_actor::community_view::CommunityView;
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::{settings::structs::Settings, LemmyError};
|
||||||
use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext};
|
use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
pub mod add_mod;
|
pub mod add_mod;
|
||||||
pub mod announce;
|
pub mod announce;
|
||||||
|
@ -33,3 +36,23 @@ pub(crate) async fn send_websocket_message<
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn list_community_follower_inboxes(
|
||||||
|
community: &Community,
|
||||||
|
additional_inboxes: Vec<Url>,
|
||||||
|
context: &LemmyContext,
|
||||||
|
) -> Result<Vec<Url>, LemmyError> {
|
||||||
|
Ok(
|
||||||
|
vec![
|
||||||
|
community.get_follower_inboxes(context.pool()).await?,
|
||||||
|
additional_inboxes,
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.flatten()
|
||||||
|
.unique()
|
||||||
|
.filter(|inbox| inbox.host_str() != Some(&Settings::get().hostname()))
|
||||||
|
.filter(|inbox| check_is_apub_id_valid(inbox, false).is_ok())
|
||||||
|
.map(|inbox| inbox.to_owned())
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::UndoType;
|
use activitystreams::activity::kind::UndoType;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{ActivityCommonFields, ActivityHandler, PublicUrl};
|
use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
|
||||||
use lemmy_db_queries::Bannable;
|
use lemmy_db_queries::Bannable;
|
||||||
use lemmy_db_schema::source::community::{CommunityPersonBan, CommunityPersonBanForm};
|
use lemmy_db_schema::source::community::{CommunityPersonBan, CommunityPersonBanForm};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
|
@ -36,7 +36,7 @@ impl ActivityHandler for UndoBlockUserFromCommunity {
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self.common())?;
|
verify_activity(self.common())?;
|
||||||
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
|
verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
|
||||||
verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
|
verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -10,7 +10,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::UpdateType;
|
use activitystreams::activity::kind::UpdateType;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{ActivityCommonFields, ActivityHandler, PublicUrl};
|
use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
|
||||||
use lemmy_db_queries::{ApubObject, Crud};
|
use lemmy_db_queries::{ApubObject, Crud};
|
||||||
use lemmy_db_schema::source::community::{Community, CommunityForm};
|
use lemmy_db_schema::source::community::{Community, CommunityForm};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
|
@ -39,7 +39,7 @@ impl ActivityHandler for UpdateCommunity {
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self.common())?;
|
verify_activity(self.common())?;
|
||||||
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
|
verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
|
||||||
verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
|
verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::DeleteType;
|
use activitystreams::activity::kind::DeleteType;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{verify_urls_match, ActivityCommonFields, ActivityHandler, PublicUrl};
|
use lemmy_apub_lib::{values::PublicUrl, verify_urls_match, ActivityCommonFields, ActivityHandler};
|
||||||
use lemmy_db_queries::{
|
use lemmy_db_queries::{
|
||||||
source::{comment::Comment_, community::Community_, post::Post_},
|
source::{comment::Comment_, community::Community_, post::Post_},
|
||||||
Crud,
|
Crud,
|
||||||
|
@ -64,7 +64,8 @@ impl ActivityHandler for DeletePostCommentOrCommunity {
|
||||||
}
|
}
|
||||||
// deleting a post or comment
|
// deleting a post or comment
|
||||||
else {
|
else {
|
||||||
verify_person_in_community(&self.common().actor, &self.cc, context, request_counter).await?;
|
verify_person_in_community(&self.common().actor, &self.cc[0], context, request_counter)
|
||||||
|
.await?;
|
||||||
let object_creator =
|
let object_creator =
|
||||||
get_post_or_comment_actor_id(&self.object, context, request_counter).await?;
|
get_post_or_comment_actor_id(&self.object, context, request_counter).await?;
|
||||||
verify_urls_match(&self.common.actor, &object_creator)?;
|
verify_urls_match(&self.common.actor, &object_creator)?;
|
||||||
|
@ -83,7 +84,7 @@ impl ActivityHandler for DeletePostCommentOrCommunity {
|
||||||
if let Ok(community) = object_community {
|
if let Ok(community) = object_community {
|
||||||
if community.local {
|
if community.local {
|
||||||
// repeat these checks just to be sure
|
// repeat these checks just to be sure
|
||||||
verify_person_in_community(&self.common().actor, &self.cc, context, request_counter)
|
verify_person_in_community(&self.common().actor, &self.cc[0], context, request_counter)
|
||||||
.await?;
|
.await?;
|
||||||
verify_mod_action(&self.common.actor, self.object.clone(), context).await?;
|
verify_mod_action(&self.common.actor, self.object.clone(), context).await?;
|
||||||
let mod_ =
|
let mod_ =
|
||||||
|
|
|
@ -18,7 +18,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::UndoType;
|
use activitystreams::activity::kind::UndoType;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{verify_urls_match, ActivityCommonFields, ActivityHandler, PublicUrl};
|
use lemmy_apub_lib::{values::PublicUrl, verify_urls_match, ActivityCommonFields, ActivityHandler};
|
||||||
use lemmy_db_queries::source::{comment::Comment_, community::Community_, post::Post_};
|
use lemmy_db_queries::source::{comment::Comment_, community::Community_, post::Post_};
|
||||||
use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post};
|
use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
|
@ -54,7 +54,8 @@ impl ActivityHandler for UndoDeletePostCommentOrCommunity {
|
||||||
}
|
}
|
||||||
// restoring a post or comment
|
// restoring a post or comment
|
||||||
else {
|
else {
|
||||||
verify_person_in_community(&self.common().actor, &self.cc, context, request_counter).await?;
|
verify_person_in_community(&self.common().actor, &self.cc[0], context, request_counter)
|
||||||
|
.await?;
|
||||||
verify_urls_match(&self.common.actor, &self.object.common().actor)?;
|
verify_urls_match(&self.common.actor, &self.object.common().actor)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -71,7 +72,7 @@ impl ActivityHandler for UndoDeletePostCommentOrCommunity {
|
||||||
if let Ok(community) = object_community {
|
if let Ok(community) = object_community {
|
||||||
if community.local {
|
if community.local {
|
||||||
// repeat these checks just to be sure
|
// repeat these checks just to be sure
|
||||||
verify_person_in_community(&self.common().actor, &self.cc, context, request_counter)
|
verify_person_in_community(&self.common().actor, &self.cc[0], context, request_counter)
|
||||||
.await?;
|
.await?;
|
||||||
verify_mod_action(&self.common.actor, self.object.object.clone(), context).await?;
|
verify_mod_action(&self.common.actor, self.object.object.clone(), context).await?;
|
||||||
let mod_ =
|
let mod_ =
|
||||||
|
|
|
@ -13,9 +13,10 @@ use lemmy_db_schema::{
|
||||||
DbUrl,
|
DbUrl,
|
||||||
};
|
};
|
||||||
use lemmy_db_views_actor::community_view::CommunityView;
|
use lemmy_db_views_actor::community_view::CommunityView;
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::{settings::structs::Settings, LemmyError};
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use url::Url;
|
use url::{ParseError, Url};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
pub mod comment;
|
pub mod comment;
|
||||||
pub mod community;
|
pub mod community;
|
||||||
|
@ -41,27 +42,34 @@ async fn verify_person(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetches the person and community to verify their type, then checks if person is banned from site
|
pub(crate) async fn extract_community(
|
||||||
/// or community.
|
|
||||||
async fn verify_person_in_community(
|
|
||||||
person_id: &Url,
|
|
||||||
cc: &[Url],
|
cc: &[Url],
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<Community, LemmyError> {
|
) -> Result<Community, LemmyError> {
|
||||||
let person = get_or_fetch_and_upsert_person(person_id, context, request_counter).await?;
|
|
||||||
let mut cc_iter = cc.iter();
|
let mut cc_iter = cc.iter();
|
||||||
let community: Community = loop {
|
loop {
|
||||||
if let Some(cid) = cc_iter.next() {
|
if let Some(cid) = cc_iter.next() {
|
||||||
if let Ok(c) = get_or_fetch_and_upsert_community(cid, context, request_counter).await {
|
if let Ok(c) = get_or_fetch_and_upsert_community(cid, context, request_counter).await {
|
||||||
break c;
|
break Ok(c);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(anyhow!("No community found in cc").into());
|
return Err(anyhow!("No community found in cc").into());
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
check_community_or_site_ban(&person, community.id, context.pool()).await?;
|
}
|
||||||
Ok(community)
|
|
||||||
|
/// Fetches the person and community to verify their type, then checks if person is banned from site
|
||||||
|
/// or community.
|
||||||
|
async fn verify_person_in_community(
|
||||||
|
person_id: &Url,
|
||||||
|
community_id: &Url,
|
||||||
|
context: &LemmyContext,
|
||||||
|
request_counter: &mut i32,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
let community = get_or_fetch_and_upsert_community(community_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
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Simply check that the url actually refers to a valid group.
|
/// Simply check that the url actually refers to a valid group.
|
||||||
|
@ -80,13 +88,16 @@ fn verify_activity(common: &ActivityCommonFields) -> Result<(), LemmyError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn verify_mod_action(
|
/// Verify that the actor is a community mod. This check is only run if the community is local,
|
||||||
|
/// because in case of remote communities, admins can also perform mod actions. As admin status
|
||||||
|
/// is not federated, we cant verify their actions remotely.
|
||||||
|
pub(crate) async fn verify_mod_action(
|
||||||
actor_id: &Url,
|
actor_id: &Url,
|
||||||
activity_cc: Url,
|
community: Url,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let community = blocking(context.pool(), move |conn| {
|
let community = blocking(context.pool(), move |conn| {
|
||||||
Community::read_from_apub_id(conn, &activity_cc.into())
|
Community::read_from_apub_id(conn, &community.into())
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
|
|
||||||
|
@ -120,3 +131,18 @@ fn verify_add_remove_moderator_target(target: &Url, community: Url) -> Result<()
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generate a unique ID for an activity, in the format:
|
||||||
|
/// `http(s)://example.com/receive/create/202daf0a-1489-45df-8d2e-c8a3173fed36`
|
||||||
|
fn generate_activity_id<T>(kind: T) -> Result<Url, ParseError>
|
||||||
|
where
|
||||||
|
T: ToString,
|
||||||
|
{
|
||||||
|
let id = format!(
|
||||||
|
"{}/activities/{}/{}",
|
||||||
|
Settings::get().get_protocol_and_hostname(),
|
||||||
|
kind.to_string().to_lowercase(),
|
||||||
|
Uuid::new_v4()
|
||||||
|
);
|
||||||
|
Url::parse(&id)
|
||||||
|
}
|
||||||
|
|
|
@ -1,13 +1,30 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{post::send_websocket_message, verify_activity, verify_person_in_community},
|
activities::{
|
||||||
|
community::announce::AnnouncableActivities,
|
||||||
|
extract_community,
|
||||||
|
generate_activity_id,
|
||||||
|
post::send_websocket_message,
|
||||||
|
verify_activity,
|
||||||
|
verify_person_in_community,
|
||||||
|
},
|
||||||
|
activity_queue::send_to_community_new,
|
||||||
|
extensions::context::lemmy_context,
|
||||||
fetcher::person::get_or_fetch_and_upsert_person,
|
fetcher::person::get_or_fetch_and_upsert_person,
|
||||||
objects::FromApub,
|
objects::{post::Page, FromApub, ToApub},
|
||||||
ActorType,
|
ActorType,
|
||||||
PageExt,
|
|
||||||
};
|
};
|
||||||
use activitystreams::{activity::kind::CreateType, base::BaseExt};
|
use activitystreams::activity::kind::CreateType;
|
||||||
use lemmy_apub_lib::{verify_domains_match_opt, ActivityCommonFields, ActivityHandler, PublicUrl};
|
use anyhow::anyhow;
|
||||||
use lemmy_db_schema::source::post::Post;
|
use lemmy_api_common::blocking;
|
||||||
|
use lemmy_apub_lib::{
|
||||||
|
values::PublicUrl,
|
||||||
|
verify_domains_match,
|
||||||
|
verify_urls_match,
|
||||||
|
ActivityCommonFields,
|
||||||
|
ActivityHandler,
|
||||||
|
};
|
||||||
|
use lemmy_db_queries::Crud;
|
||||||
|
use lemmy_db_schema::source::{community::Community, person::Person, post::Post};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
@ -16,14 +33,40 @@ use url::Url;
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct CreatePost {
|
pub struct CreatePost {
|
||||||
to: PublicUrl,
|
to: PublicUrl,
|
||||||
object: PageExt,
|
object: Page,
|
||||||
cc: Vec<Url>,
|
cc: [Url; 1],
|
||||||
#[serde(rename = "type")]
|
r#type: CreateType,
|
||||||
kind: CreateType,
|
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
common: ActivityCommonFields,
|
common: ActivityCommonFields,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CreatePost {
|
||||||
|
pub async fn send(post: &Post, actor: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
|
||||||
|
let community_id = post.community_id;
|
||||||
|
let community = blocking(context.pool(), move |conn| {
|
||||||
|
Community::read(conn, community_id)
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
|
||||||
|
let id = generate_activity_id(CreateType::Create)?;
|
||||||
|
let create = CreatePost {
|
||||||
|
to: PublicUrl::Public,
|
||||||
|
object: post.to_apub(context.pool()).await?,
|
||||||
|
cc: [community.actor_id()],
|
||||||
|
r#type: Default::default(),
|
||||||
|
common: ActivityCommonFields {
|
||||||
|
context: lemmy_context(),
|
||||||
|
id: id.clone(),
|
||||||
|
actor: actor.actor_id(),
|
||||||
|
unparsed: Default::default(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let activity = AnnouncableActivities::CreatePost(create);
|
||||||
|
send_to_community_new(activity, &id, actor, &community, vec![], context).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl ActivityHandler for CreatePost {
|
impl ActivityHandler for CreatePost {
|
||||||
async fn verify(
|
async fn verify(
|
||||||
|
@ -31,9 +74,23 @@ impl ActivityHandler for CreatePost {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
|
let community = extract_community(&self.cc, context, request_counter).await?;
|
||||||
|
let community_id = &community.actor_id();
|
||||||
|
|
||||||
verify_activity(self.common())?;
|
verify_activity(self.common())?;
|
||||||
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
|
verify_person_in_community(&self.common.actor, community_id, context, request_counter).await?;
|
||||||
verify_domains_match_opt(&self.common.actor, self.object.id_unchecked())?;
|
verify_domains_match(&self.common.actor, &self.object.id)?;
|
||||||
|
verify_urls_match(&self.common.actor, &self.object.attributed_to)?;
|
||||||
|
// Check that the post isnt locked or stickied, as that isnt possible for newly created posts.
|
||||||
|
// However, when fetching a remote post we generate a new create activity with the current
|
||||||
|
// locked/stickied value, so this check may fail. So only check if its a local community,
|
||||||
|
// because then we will definitely receive all create and update activities separately.
|
||||||
|
let is_stickied_or_locked =
|
||||||
|
self.object.stickied == Some(true) || self.object.comments_enabled == Some(false);
|
||||||
|
if community.local && is_stickied_or_locked {
|
||||||
|
return Err(anyhow!("New post cannot be stickied or locked").into());
|
||||||
|
}
|
||||||
|
self.object.verify(context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{
|
activities::{
|
||||||
|
community::announce::AnnouncableActivities,
|
||||||
|
generate_activity_id,
|
||||||
post::send_websocket_message,
|
post::send_websocket_message,
|
||||||
verify_activity,
|
verify_activity,
|
||||||
verify_mod_action,
|
verify_mod_action,
|
||||||
verify_person_in_community,
|
verify_person_in_community,
|
||||||
},
|
},
|
||||||
objects::{FromApub, FromApubToForm},
|
activity_queue::send_to_community_new,
|
||||||
|
extensions::context::lemmy_context,
|
||||||
|
fetcher::community::get_or_fetch_and_upsert_community,
|
||||||
|
objects::{post::Page, FromApub, ToApub},
|
||||||
ActorType,
|
ActorType,
|
||||||
PageExt,
|
|
||||||
};
|
};
|
||||||
use activitystreams::{activity::kind::UpdateType, base::BaseExt};
|
use activitystreams::activity::kind::UpdateType;
|
||||||
use anyhow::Context;
|
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{verify_domains_match_opt, ActivityCommonFields, ActivityHandler, PublicUrl};
|
use lemmy_apub_lib::{values::PublicUrl, verify_urls_match, ActivityCommonFields, ActivityHandler};
|
||||||
use lemmy_db_queries::ApubObject;
|
use lemmy_db_queries::Crud;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::source::{community::Community, person::Person, post::Post};
|
||||||
source::post::{Post, PostForm},
|
use lemmy_utils::LemmyError;
|
||||||
DbUrl,
|
|
||||||
};
|
|
||||||
use lemmy_utils::{location_info, LemmyError};
|
|
||||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
@ -26,14 +26,39 @@ use url::Url;
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UpdatePost {
|
pub struct UpdatePost {
|
||||||
to: PublicUrl,
|
to: PublicUrl,
|
||||||
object: PageExt,
|
object: Page,
|
||||||
cc: Vec<Url>,
|
cc: [Url; 1],
|
||||||
#[serde(rename = "type")]
|
r#type: UpdateType,
|
||||||
kind: UpdateType,
|
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
common: ActivityCommonFields,
|
common: ActivityCommonFields,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl UpdatePost {
|
||||||
|
pub async fn send(post: &Post, actor: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
|
||||||
|
let community_id = post.community_id;
|
||||||
|
let community = blocking(context.pool(), move |conn| {
|
||||||
|
Community::read(conn, community_id)
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
|
||||||
|
let id = generate_activity_id(UpdateType::Update)?;
|
||||||
|
let update = UpdatePost {
|
||||||
|
to: PublicUrl::Public,
|
||||||
|
object: post.to_apub(context.pool()).await?,
|
||||||
|
cc: [community.actor_id()],
|
||||||
|
r#type: Default::default(),
|
||||||
|
common: ActivityCommonFields {
|
||||||
|
context: lemmy_context(),
|
||||||
|
id: id.clone(),
|
||||||
|
actor: actor.actor_id(),
|
||||||
|
unparsed: Default::default(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let activity = AnnouncableActivities::UpdatePost(update);
|
||||||
|
send_to_community_new(activity, &id, actor, &community, vec![], context).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl ActivityHandler for UpdatePost {
|
impl ActivityHandler for UpdatePost {
|
||||||
async fn verify(
|
async fn verify(
|
||||||
|
@ -41,34 +66,19 @@ impl ActivityHandler for UpdatePost {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
|
let community_id = get_or_fetch_and_upsert_community(&self.cc[0], context, request_counter)
|
||||||
|
.await?
|
||||||
|
.actor_id();
|
||||||
|
let is_mod_action = self.object.is_mod_action(context.pool()).await?;
|
||||||
|
|
||||||
verify_activity(self.common())?;
|
verify_activity(self.common())?;
|
||||||
let community =
|
verify_person_in_community(&self.common.actor, &community_id, context, request_counter).await?;
|
||||||
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
|
if is_mod_action {
|
||||||
|
verify_mod_action(&self.common.actor, community_id, context).await?;
|
||||||
let temp_post = PostForm::from_apub(
|
} else {
|
||||||
&self.object,
|
verify_urls_match(&self.common.actor, &self.object.attributed_to)?;
|
||||||
context,
|
|
||||||
self.common.actor.clone(),
|
|
||||||
request_counter,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
let post_id: DbUrl = temp_post.ap_id.context(location_info!())?;
|
|
||||||
let old_post = blocking(context.pool(), move |conn| {
|
|
||||||
Post::read_from_apub_id(conn, &post_id)
|
|
||||||
})
|
|
||||||
.await??;
|
|
||||||
let stickied = temp_post.stickied.context(location_info!())?;
|
|
||||||
let locked = temp_post.locked.context(location_info!())?;
|
|
||||||
// community mod changed locked/sticky status
|
|
||||||
if (stickied != old_post.stickied) || (locked != old_post.locked) {
|
|
||||||
verify_mod_action(&self.common.actor, community.actor_id(), context).await?;
|
|
||||||
}
|
}
|
||||||
// user edited their own post
|
self.object.verify(context, request_counter).await?;
|
||||||
else {
|
|
||||||
verify_domains_match_opt(&self.common.actor, self.object.id_unchecked())?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ use crate::{
|
||||||
use activitystreams::{activity::kind::RemoveType, base::AnyBase};
|
use activitystreams::{activity::kind::RemoveType, base::AnyBase};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{ActivityCommonFields, ActivityHandler, PublicUrl};
|
use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
|
||||||
use lemmy_db_queries::{
|
use lemmy_db_queries::{
|
||||||
source::{comment::Comment_, community::Community_, post::Post_},
|
source::{comment::Comment_, community::Community_, post::Post_},
|
||||||
Joinable,
|
Joinable,
|
||||||
|
@ -64,13 +64,13 @@ impl ActivityHandler for RemovePostCommentCommunityOrMod {
|
||||||
}
|
}
|
||||||
// removing community mod
|
// removing community mod
|
||||||
else if let Some(target) = &self.target {
|
else if let Some(target) = &self.target {
|
||||||
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
|
verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
|
||||||
verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
|
verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
|
||||||
verify_add_remove_moderator_target(target, self.cc[0].clone())?;
|
verify_add_remove_moderator_target(target, self.cc[0].clone())?;
|
||||||
}
|
}
|
||||||
// removing a post or comment
|
// removing a post or comment
|
||||||
else {
|
else {
|
||||||
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
|
verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
|
||||||
verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
|
verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -17,7 +17,7 @@ use crate::{
|
||||||
use activitystreams::activity::kind::UndoType;
|
use activitystreams::activity::kind::UndoType;
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_apub_lib::{ActivityCommonFields, ActivityHandler, PublicUrl};
|
use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
|
||||||
use lemmy_db_queries::source::{comment::Comment_, community::Community_, post::Post_};
|
use lemmy_db_queries::source::{comment::Comment_, community::Community_, post::Post_};
|
||||||
use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post};
|
use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
|
@ -52,7 +52,7 @@ impl ActivityHandler for UndoRemovePostCommentOrCommunity {
|
||||||
}
|
}
|
||||||
// removing a post or comment
|
// removing a post or comment
|
||||||
else {
|
else {
|
||||||
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
|
verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
|
||||||
verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
|
verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
|
||||||
}
|
}
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::send::generate_activity_id,
|
activities::generate_activity_id,
|
||||||
activity_queue::{send_comment_mentions, send_to_community},
|
activity_queue::{send_comment_mentions, send_to_community},
|
||||||
extensions::context::lemmy_context,
|
extensions::context::lemmy_context,
|
||||||
fetcher::person::get_or_fetch_and_upsert_person,
|
fetcher::person::get_or_fetch_and_upsert_person,
|
||||||
|
@ -64,7 +64,7 @@ impl ApubObjectType for Comment {
|
||||||
note.into_any_base()?,
|
note.into_any_base()?,
|
||||||
);
|
);
|
||||||
create
|
create
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(CreateType::Create)?)
|
.set_id(generate_activity_id(CreateType::Create)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(maa.ccs.to_owned())
|
.set_many_ccs(maa.ccs.to_owned())
|
||||||
|
@ -97,7 +97,7 @@ impl ApubObjectType for Comment {
|
||||||
note.into_any_base()?,
|
note.into_any_base()?,
|
||||||
);
|
);
|
||||||
update
|
update
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(UpdateType::Update)?)
|
.set_id(generate_activity_id(UpdateType::Update)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(maa.ccs.to_owned())
|
.set_many_ccs(maa.ccs.to_owned())
|
||||||
|
@ -124,7 +124,7 @@ impl ApubObjectType for Comment {
|
||||||
self.ap_id.to_owned().into_inner(),
|
self.ap_id.to_owned().into_inner(),
|
||||||
);
|
);
|
||||||
delete
|
delete
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(DeleteType::Delete)?)
|
.set_id(generate_activity_id(DeleteType::Delete)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(vec![community.actor_id()]);
|
.set_many_ccs(vec![community.actor_id()]);
|
||||||
|
@ -153,7 +153,7 @@ impl ApubObjectType for Comment {
|
||||||
self.ap_id.to_owned().into_inner(),
|
self.ap_id.to_owned().into_inner(),
|
||||||
);
|
);
|
||||||
delete
|
delete
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(DeleteType::Delete)?)
|
.set_id(generate_activity_id(DeleteType::Delete)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(vec![community.actor_id()]);
|
.set_many_ccs(vec![community.actor_id()]);
|
||||||
|
@ -164,7 +164,7 @@ impl ApubObjectType for Comment {
|
||||||
delete.into_any_base()?,
|
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![community.actor_id()]);
|
.set_many_ccs(vec![community.actor_id()]);
|
||||||
|
@ -188,7 +188,7 @@ impl ApubObjectType for Comment {
|
||||||
self.ap_id.to_owned().into_inner(),
|
self.ap_id.to_owned().into_inner(),
|
||||||
);
|
);
|
||||||
remove
|
remove
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(RemoveType::Remove)?)
|
.set_id(generate_activity_id(RemoveType::Remove)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(vec![community.actor_id()]);
|
.set_many_ccs(vec![community.actor_id()]);
|
||||||
|
@ -217,7 +217,7 @@ impl ApubObjectType for Comment {
|
||||||
self.ap_id.to_owned().into_inner(),
|
self.ap_id.to_owned().into_inner(),
|
||||||
);
|
);
|
||||||
remove
|
remove
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(RemoveType::Remove)?)
|
.set_id(generate_activity_id(RemoveType::Remove)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(vec![community.actor_id()]);
|
.set_many_ccs(vec![community.actor_id()]);
|
||||||
|
@ -228,7 +228,7 @@ impl ApubObjectType for Comment {
|
||||||
remove.into_any_base()?,
|
remove.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![community.actor_id()]);
|
.set_many_ccs(vec![community.actor_id()]);
|
||||||
|
@ -255,7 +255,7 @@ impl ApubLikeableType for Comment {
|
||||||
self.ap_id.to_owned().into_inner(),
|
self.ap_id.to_owned().into_inner(),
|
||||||
);
|
);
|
||||||
like
|
like
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(LikeType::Like)?)
|
.set_id(generate_activity_id(LikeType::Like)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(vec![community.actor_id()]);
|
.set_many_ccs(vec![community.actor_id()]);
|
||||||
|
@ -279,7 +279,7 @@ impl ApubLikeableType for Comment {
|
||||||
self.ap_id.to_owned().into_inner(),
|
self.ap_id.to_owned().into_inner(),
|
||||||
);
|
);
|
||||||
dislike
|
dislike
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(DislikeType::Dislike)?)
|
.set_id(generate_activity_id(DislikeType::Dislike)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(vec![community.actor_id()]);
|
.set_many_ccs(vec![community.actor_id()]);
|
||||||
|
@ -307,7 +307,7 @@ impl ApubLikeableType for Comment {
|
||||||
self.ap_id.to_owned().into_inner(),
|
self.ap_id.to_owned().into_inner(),
|
||||||
);
|
);
|
||||||
like
|
like
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(DislikeType::Dislike)?)
|
.set_id(generate_activity_id(DislikeType::Dislike)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(vec![community.actor_id()]);
|
.set_many_ccs(vec![community.actor_id()]);
|
||||||
|
@ -318,7 +318,7 @@ impl ApubLikeableType for Comment {
|
||||||
like.into_any_base()?,
|
like.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![community.actor_id()]);
|
.set_many_ccs(vec![community.actor_id()]);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::send::generate_activity_id,
|
activities::generate_activity_id,
|
||||||
activity_queue::{send_activity_single_dest, send_to_community, send_to_community_followers},
|
activity_queue::{send_activity_single_dest, send_to_community, send_to_community_followers},
|
||||||
check_is_apub_id_valid,
|
check_is_apub_id_valid,
|
||||||
extensions::context::lemmy_context,
|
extensions::context::lemmy_context,
|
||||||
|
@ -98,7 +98,7 @@ impl CommunityType for Community {
|
||||||
follow.into_any_base()?,
|
follow.into_any_base()?,
|
||||||
);
|
);
|
||||||
accept
|
accept
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(AcceptType::Accept)?)
|
.set_id(generate_activity_id(AcceptType::Accept)?)
|
||||||
.set_to(person.actor_id());
|
.set_to(person.actor_id());
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ impl CommunityType for Community {
|
||||||
self.to_apub(context.pool()).await?.into_any_base()?,
|
self.to_apub(context.pool()).await?.into_any_base()?,
|
||||||
);
|
);
|
||||||
update
|
update
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(UpdateType::Update)?)
|
.set_id(generate_activity_id(UpdateType::Update)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(vec![self.actor_id()]);
|
.set_many_ccs(vec![self.actor_id()]);
|
||||||
|
@ -134,7 +134,7 @@ impl CommunityType for Community {
|
||||||
if self.local {
|
if self.local {
|
||||||
let mut delete = Delete::new(self.actor_id(), self.actor_id());
|
let mut delete = Delete::new(self.actor_id(), self.actor_id());
|
||||||
delete
|
delete
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(DeleteType::Delete)?)
|
.set_id(generate_activity_id(DeleteType::Delete)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(vec![self.followers_url()]);
|
.set_many_ccs(vec![self.followers_url()]);
|
||||||
|
@ -145,7 +145,7 @@ impl CommunityType for Community {
|
||||||
else {
|
else {
|
||||||
let mut delete = Delete::new(mod_.actor_id(), self.actor_id());
|
let mut delete = Delete::new(mod_.actor_id(), self.actor_id());
|
||||||
delete
|
delete
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(DeleteType::Delete)?)
|
.set_id(generate_activity_id(DeleteType::Delete)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(vec![self.actor_id()]);
|
.set_many_ccs(vec![self.actor_id()]);
|
||||||
|
@ -163,14 +163,14 @@ impl CommunityType for Community {
|
||||||
if self.local {
|
if self.local {
|
||||||
let mut delete = Delete::new(self.actor_id(), self.actor_id());
|
let mut delete = Delete::new(self.actor_id(), self.actor_id());
|
||||||
delete
|
delete
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(DeleteType::Delete)?)
|
.set_id(generate_activity_id(DeleteType::Delete)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(vec![self.followers_url()]);
|
.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()]);
|
||||||
|
@ -181,14 +181,14 @@ impl CommunityType for Community {
|
||||||
else {
|
else {
|
||||||
let mut delete = Delete::new(mod_.actor_id(), self.actor_id());
|
let mut delete = Delete::new(mod_.actor_id(), self.actor_id());
|
||||||
delete
|
delete
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(DeleteType::Delete)?)
|
.set_id(generate_activity_id(DeleteType::Delete)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(vec![self.actor_id()]);
|
.set_many_ccs(vec![self.actor_id()]);
|
||||||
|
|
||||||
let mut undo = Undo::new(mod_.actor_id(), delete.into_any_base()?);
|
let mut undo = Undo::new(mod_.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.actor_id()]);
|
.set_many_ccs(vec![self.actor_id()]);
|
||||||
|
@ -202,7 +202,7 @@ impl CommunityType for Community {
|
||||||
async fn send_remove(&self, context: &LemmyContext) -> Result<(), LemmyError> {
|
async fn send_remove(&self, context: &LemmyContext) -> Result<(), LemmyError> {
|
||||||
let mut remove = Remove::new(self.actor_id(), self.actor_id());
|
let mut remove = Remove::new(self.actor_id(), self.actor_id());
|
||||||
remove
|
remove
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(RemoveType::Remove)?)
|
.set_id(generate_activity_id(RemoveType::Remove)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(vec![self.followers_url()]);
|
.set_many_ccs(vec![self.followers_url()]);
|
||||||
|
@ -215,7 +215,7 @@ impl CommunityType for Community {
|
||||||
async fn send_undo_remove(&self, context: &LemmyContext) -> Result<(), LemmyError> {
|
async fn send_undo_remove(&self, context: &LemmyContext) -> Result<(), LemmyError> {
|
||||||
let mut remove = Remove::new(self.actor_id(), self.actor_id());
|
let mut remove = Remove::new(self.actor_id(), self.actor_id());
|
||||||
remove
|
remove
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(RemoveType::Remove)?)
|
.set_id(generate_activity_id(RemoveType::Remove)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(vec![self.followers_url()]);
|
.set_many_ccs(vec![self.followers_url()]);
|
||||||
|
@ -223,7 +223,7 @@ impl CommunityType for Community {
|
||||||
// Undo that fake activity
|
// Undo that fake activity
|
||||||
let mut undo = Undo::new(self.actor_id(), remove.into_any_base()?);
|
let mut undo = Undo::new(self.actor_id(), remove.into_any_base()?);
|
||||||
undo
|
undo
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(LikeType::Like)?)
|
.set_id(generate_activity_id(LikeType::Like)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(vec![self.followers_url()]);
|
.set_many_ccs(vec![self.followers_url()]);
|
||||||
|
@ -267,7 +267,7 @@ impl CommunityType for Community {
|
||||||
}
|
}
|
||||||
let mut announce = Announce::new(self.actor_id(), activity);
|
let mut announce = Announce::new(self.actor_id(), activity);
|
||||||
announce
|
announce
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(AnnounceType::Announce)?)
|
.set_id(generate_activity_id(AnnounceType::Announce)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(ccs);
|
.set_many_ccs(ccs);
|
||||||
|
@ -306,7 +306,7 @@ impl CommunityType for Community {
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let mut add = Add::new(actor.actor_id(), added_mod.actor_id());
|
let mut add = Add::new(actor.actor_id(), added_mod.actor_id());
|
||||||
add
|
add
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(AddType::Add)?)
|
.set_id(generate_activity_id(AddType::Add)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(vec![self.actor_id()])
|
.set_many_ccs(vec![self.actor_id()])
|
||||||
|
@ -324,7 +324,7 @@ impl CommunityType for Community {
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let mut remove = Remove::new(actor.actor_id(), removed_mod.actor_id());
|
let mut remove = Remove::new(actor.actor_id(), removed_mod.actor_id());
|
||||||
remove
|
remove
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(RemoveType::Remove)?)
|
.set_id(generate_activity_id(RemoveType::Remove)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(vec![self.actor_id()])
|
.set_many_ccs(vec![self.actor_id()])
|
||||||
|
@ -342,7 +342,7 @@ impl CommunityType for Community {
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let mut block = Block::new(actor.actor_id(), blocked_user.actor_id());
|
let mut block = Block::new(actor.actor_id(), blocked_user.actor_id());
|
||||||
block
|
block
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(BlockType::Block)?)
|
.set_id(generate_activity_id(BlockType::Block)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(vec![self.actor_id()]);
|
.set_many_ccs(vec![self.actor_id()]);
|
||||||
|
@ -359,7 +359,7 @@ impl CommunityType for Community {
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let mut block = Block::new(actor.actor_id(), unblocked_user.actor_id());
|
let mut block = Block::new(actor.actor_id(), unblocked_user.actor_id());
|
||||||
block
|
block
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(BlockType::Block)?)
|
.set_id(generate_activity_id(BlockType::Block)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(vec![self.actor_id()]);
|
.set_many_ccs(vec![self.actor_id()]);
|
||||||
|
@ -367,7 +367,7 @@ impl CommunityType for Community {
|
||||||
// Undo that fake activity
|
// Undo that fake activity
|
||||||
let mut undo = Undo::new(actor.actor_id(), block.into_any_base()?);
|
let mut undo = Undo::new(actor.actor_id(), block.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.actor_id()]);
|
.set_many_ccs(vec![self.actor_id()]);
|
||||||
|
|
|
@ -1,24 +1,5 @@
|
||||||
use lemmy_utils::settings::structs::Settings;
|
|
||||||
use url::{ParseError, Url};
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
pub(crate) mod comment;
|
pub(crate) mod comment;
|
||||||
pub(crate) mod community;
|
pub(crate) mod community;
|
||||||
pub(crate) mod person;
|
pub(crate) mod person;
|
||||||
pub(crate) mod post;
|
pub(crate) mod post;
|
||||||
pub(crate) mod private_message;
|
pub(crate) mod private_message;
|
||||||
|
|
||||||
/// Generate a unique ID for an activity, in the format:
|
|
||||||
/// `http(s)://example.com/receive/create/202daf0a-1489-45df-8d2e-c8a3173fed36`
|
|
||||||
fn generate_activity_id<T>(kind: T) -> Result<Url, ParseError>
|
|
||||||
where
|
|
||||||
T: ToString,
|
|
||||||
{
|
|
||||||
let id = format!(
|
|
||||||
"{}/activities/{}/{}",
|
|
||||||
Settings::get().get_protocol_and_hostname(),
|
|
||||||
kind.to_string().to_lowercase(),
|
|
||||||
Uuid::new_v4()
|
|
||||||
);
|
|
||||||
Url::parse(&id)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::send::generate_activity_id,
|
activities::generate_activity_id,
|
||||||
activity_queue::send_activity_single_dest,
|
activity_queue::send_activity_single_dest,
|
||||||
extensions::context::lemmy_context,
|
extensions::context::lemmy_context,
|
||||||
ActorType,
|
ActorType,
|
||||||
|
@ -78,7 +78,7 @@ impl UserType for Person {
|
||||||
|
|
||||||
let mut follow = Follow::new(self.actor_id(), community.actor_id());
|
let mut follow = Follow::new(self.actor_id(), community.actor_id());
|
||||||
follow
|
follow
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(FollowType::Follow)?)
|
.set_id(generate_activity_id(FollowType::Follow)?)
|
||||||
.set_to(community.actor_id());
|
.set_to(community.actor_id());
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ impl UserType for Person {
|
||||||
|
|
||||||
let mut follow = Follow::new(self.actor_id(), community.actor_id());
|
let mut follow = Follow::new(self.actor_id(), community.actor_id());
|
||||||
follow
|
follow
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(FollowType::Follow)?)
|
.set_id(generate_activity_id(FollowType::Follow)?)
|
||||||
.set_to(community.actor_id());
|
.set_to(community.actor_id());
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ impl UserType for Person {
|
||||||
follow.into_any_base()?,
|
follow.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(community.actor_id());
|
.set_to(community.actor_id());
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,19 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::send::generate_activity_id,
|
activities::generate_activity_id,
|
||||||
activity_queue::send_to_community,
|
activity_queue::send_to_community,
|
||||||
extensions::context::lemmy_context,
|
extensions::context::lemmy_context,
|
||||||
objects::ToApub,
|
|
||||||
ActorType,
|
ActorType,
|
||||||
ApubLikeableType,
|
ApubLikeableType,
|
||||||
ApubObjectType,
|
ApubObjectType,
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
activity::{
|
activity::{
|
||||||
kind::{CreateType, DeleteType, DislikeType, LikeType, RemoveType, UndoType, UpdateType},
|
kind::{DeleteType, DislikeType, LikeType, RemoveType, UndoType},
|
||||||
Create,
|
|
||||||
Delete,
|
Delete,
|
||||||
Dislike,
|
Dislike,
|
||||||
Like,
|
Like,
|
||||||
Remove,
|
Remove,
|
||||||
Undo,
|
Undo,
|
||||||
Update,
|
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
public,
|
public,
|
||||||
|
@ -29,52 +26,20 @@ use lemmy_websocket::LemmyContext;
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl ApubObjectType for Post {
|
impl ApubObjectType for Post {
|
||||||
/// Send out information about a newly created post, to the followers of the community.
|
async fn send_create(
|
||||||
async fn send_create(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
|
&self,
|
||||||
let page = self.to_apub(context.pool()).await?;
|
_creator: &Person,
|
||||||
|
_context: &LemmyContext,
|
||||||
let community_id = self.community_id;
|
) -> Result<(), LemmyError> {
|
||||||
let community = blocking(context.pool(), move |conn| {
|
unimplemented!()
|
||||||
Community::read(conn, community_id)
|
|
||||||
})
|
|
||||||
.await??;
|
|
||||||
|
|
||||||
let mut create = Create::new(
|
|
||||||
creator.actor_id.to_owned().into_inner(),
|
|
||||||
page.into_any_base()?,
|
|
||||||
);
|
|
||||||
create
|
|
||||||
.set_many_contexts(lemmy_context()?)
|
|
||||||
.set_id(generate_activity_id(CreateType::Create)?)
|
|
||||||
.set_to(public())
|
|
||||||
.set_many_ccs(vec![community.actor_id()]);
|
|
||||||
|
|
||||||
send_to_community(create, creator, &community, None, context).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send out information about an edited post, to the followers of the community.
|
async fn send_update(
|
||||||
async fn send_update(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
|
&self,
|
||||||
let page = self.to_apub(context.pool()).await?;
|
_creator: &Person,
|
||||||
|
_context: &LemmyContext,
|
||||||
let community_id = self.community_id;
|
) -> Result<(), LemmyError> {
|
||||||
let community = blocking(context.pool(), move |conn| {
|
unimplemented!()
|
||||||
Community::read(conn, community_id)
|
|
||||||
})
|
|
||||||
.await??;
|
|
||||||
|
|
||||||
let mut update = Update::new(
|
|
||||||
creator.actor_id.to_owned().into_inner(),
|
|
||||||
page.into_any_base()?,
|
|
||||||
);
|
|
||||||
update
|
|
||||||
.set_many_contexts(lemmy_context()?)
|
|
||||||
.set_id(generate_activity_id(UpdateType::Update)?)
|
|
||||||
.set_to(public())
|
|
||||||
.set_many_ccs(vec![community.actor_id()]);
|
|
||||||
|
|
||||||
send_to_community(update, creator, &community, None, context).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_delete(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
|
async fn send_delete(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
|
||||||
|
@ -89,7 +54,7 @@ impl ApubObjectType for Post {
|
||||||
self.ap_id.to_owned().into_inner(),
|
self.ap_id.to_owned().into_inner(),
|
||||||
);
|
);
|
||||||
delete
|
delete
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(DeleteType::Delete)?)
|
.set_id(generate_activity_id(DeleteType::Delete)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(vec![community.actor_id()]);
|
.set_many_ccs(vec![community.actor_id()]);
|
||||||
|
@ -114,7 +79,7 @@ impl ApubObjectType for Post {
|
||||||
self.ap_id.to_owned().into_inner(),
|
self.ap_id.to_owned().into_inner(),
|
||||||
);
|
);
|
||||||
delete
|
delete
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(DeleteType::Delete)?)
|
.set_id(generate_activity_id(DeleteType::Delete)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(vec![community.actor_id()]);
|
.set_many_ccs(vec![community.actor_id()]);
|
||||||
|
@ -125,7 +90,7 @@ impl ApubObjectType for Post {
|
||||||
delete.into_any_base()?,
|
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![community.actor_id()]);
|
.set_many_ccs(vec![community.actor_id()]);
|
||||||
|
@ -146,7 +111,7 @@ impl ApubObjectType for Post {
|
||||||
self.ap_id.to_owned().into_inner(),
|
self.ap_id.to_owned().into_inner(),
|
||||||
);
|
);
|
||||||
remove
|
remove
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(RemoveType::Remove)?)
|
.set_id(generate_activity_id(RemoveType::Remove)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(vec![community.actor_id()]);
|
.set_many_ccs(vec![community.actor_id()]);
|
||||||
|
@ -171,7 +136,7 @@ impl ApubObjectType for Post {
|
||||||
self.ap_id.to_owned().into_inner(),
|
self.ap_id.to_owned().into_inner(),
|
||||||
);
|
);
|
||||||
remove
|
remove
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(RemoveType::Remove)?)
|
.set_id(generate_activity_id(RemoveType::Remove)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(vec![community.actor_id()]);
|
.set_many_ccs(vec![community.actor_id()]);
|
||||||
|
@ -182,7 +147,7 @@ impl ApubObjectType for Post {
|
||||||
remove.into_any_base()?,
|
remove.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![community.actor_id()]);
|
.set_many_ccs(vec![community.actor_id()]);
|
||||||
|
@ -206,7 +171,7 @@ impl ApubLikeableType for Post {
|
||||||
self.ap_id.to_owned().into_inner(),
|
self.ap_id.to_owned().into_inner(),
|
||||||
);
|
);
|
||||||
like
|
like
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(LikeType::Like)?)
|
.set_id(generate_activity_id(LikeType::Like)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(vec![community.actor_id()]);
|
.set_many_ccs(vec![community.actor_id()]);
|
||||||
|
@ -227,7 +192,7 @@ impl ApubLikeableType for Post {
|
||||||
self.ap_id.to_owned().into_inner(),
|
self.ap_id.to_owned().into_inner(),
|
||||||
);
|
);
|
||||||
dislike
|
dislike
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(DislikeType::Dislike)?)
|
.set_id(generate_activity_id(DislikeType::Dislike)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(vec![community.actor_id()]);
|
.set_many_ccs(vec![community.actor_id()]);
|
||||||
|
@ -252,7 +217,7 @@ impl ApubLikeableType for Post {
|
||||||
self.ap_id.to_owned().into_inner(),
|
self.ap_id.to_owned().into_inner(),
|
||||||
);
|
);
|
||||||
like
|
like
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(LikeType::Like)?)
|
.set_id(generate_activity_id(LikeType::Like)?)
|
||||||
.set_to(public())
|
.set_to(public())
|
||||||
.set_many_ccs(vec![community.actor_id()]);
|
.set_many_ccs(vec![community.actor_id()]);
|
||||||
|
@ -263,7 +228,7 @@ impl ApubLikeableType for Post {
|
||||||
like.into_any_base()?,
|
like.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![community.actor_id()]);
|
.set_many_ccs(vec![community.actor_id()]);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::send::generate_activity_id,
|
activities::generate_activity_id,
|
||||||
activity_queue::send_activity_single_dest,
|
activity_queue::send_activity_single_dest,
|
||||||
extensions::context::lemmy_context,
|
extensions::context::lemmy_context,
|
||||||
objects::ToApub,
|
objects::ToApub,
|
||||||
|
@ -38,7 +38,7 @@ impl ApubObjectType for PrivateMessage {
|
||||||
);
|
);
|
||||||
|
|
||||||
create
|
create
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(CreateType::Create)?)
|
.set_id(generate_activity_id(CreateType::Create)?)
|
||||||
.set_to(recipient.actor_id());
|
.set_to(recipient.actor_id());
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ impl ApubObjectType for PrivateMessage {
|
||||||
note.into_any_base()?,
|
note.into_any_base()?,
|
||||||
);
|
);
|
||||||
update
|
update
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(UpdateType::Update)?)
|
.set_id(generate_activity_id(UpdateType::Update)?)
|
||||||
.set_to(recipient.actor_id());
|
.set_to(recipient.actor_id());
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ impl ApubObjectType for PrivateMessage {
|
||||||
self.ap_id.to_owned().into_inner(),
|
self.ap_id.to_owned().into_inner(),
|
||||||
);
|
);
|
||||||
delete
|
delete
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(DeleteType::Delete)?)
|
.set_id(generate_activity_id(DeleteType::Delete)?)
|
||||||
.set_to(recipient.actor_id());
|
.set_to(recipient.actor_id());
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ impl ApubObjectType for PrivateMessage {
|
||||||
self.ap_id.to_owned().into_inner(),
|
self.ap_id.to_owned().into_inner(),
|
||||||
);
|
);
|
||||||
delete
|
delete
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(generate_activity_id(DeleteType::Delete)?)
|
.set_id(generate_activity_id(DeleteType::Delete)?)
|
||||||
.set_to(recipient.actor_id());
|
.set_to(recipient.actor_id());
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ impl ApubObjectType for PrivateMessage {
|
||||||
delete.into_any_base()?,
|
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(recipient.actor_id());
|
.set_to(recipient.actor_id());
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::activities::{
|
||||||
voting::receive_like_or_dislike,
|
voting::receive_like_or_dislike,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::DislikeType;
|
use activitystreams::activity::kind::DislikeType;
|
||||||
use lemmy_apub_lib::{ActivityCommonFields, ActivityHandler, PublicUrl};
|
use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
@ -29,7 +29,7 @@ impl ActivityHandler for DislikePostOrComment {
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self.common())?;
|
verify_activity(self.common())?;
|
||||||
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
|
verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::activities::{
|
||||||
voting::receive_like_or_dislike,
|
voting::receive_like_or_dislike,
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::LikeType;
|
use activitystreams::activity::kind::LikeType;
|
||||||
use lemmy_apub_lib::{ActivityCommonFields, ActivityHandler, PublicUrl};
|
use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
@ -29,7 +29,7 @@ impl ActivityHandler for LikePostOrComment {
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self.common())?;
|
verify_activity(self.common())?;
|
||||||
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
|
verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::activities::{
|
||||||
voting::{dislike::DislikePostOrComment, receive_undo_like_or_dislike},
|
voting::{dislike::DislikePostOrComment, receive_undo_like_or_dislike},
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::UndoType;
|
use activitystreams::activity::kind::UndoType;
|
||||||
use lemmy_apub_lib::{verify_urls_match, ActivityCommonFields, ActivityHandler, PublicUrl};
|
use lemmy_apub_lib::{values::PublicUrl, verify_urls_match, ActivityCommonFields, ActivityHandler};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
@ -29,7 +29,7 @@ impl ActivityHandler for UndoDislikePostOrComment {
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self.common())?;
|
verify_activity(self.common())?;
|
||||||
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
|
verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
|
||||||
verify_urls_match(&self.common.actor, &self.object.common().actor)?;
|
verify_urls_match(&self.common.actor, &self.object.common().actor)?;
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::activities::{
|
||||||
voting::{like::LikePostOrComment, receive_undo_like_or_dislike},
|
voting::{like::LikePostOrComment, receive_undo_like_or_dislike},
|
||||||
};
|
};
|
||||||
use activitystreams::activity::kind::UndoType;
|
use activitystreams::activity::kind::UndoType;
|
||||||
use lemmy_apub_lib::{verify_urls_match, ActivityCommonFields, ActivityHandler, PublicUrl};
|
use lemmy_apub_lib::{values::PublicUrl, verify_urls_match, ActivityCommonFields, ActivityHandler};
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
@ -29,7 +29,7 @@ impl ActivityHandler for UndoLikePostOrComment {
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
verify_activity(self.common())?;
|
verify_activity(self.common())?;
|
||||||
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
|
verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
|
||||||
verify_urls_match(&self.common.actor, &self.object.common().actor)?;
|
verify_urls_match(&self.common.actor, &self.object.common().actor)?;
|
||||||
self.object.verify(context, request_counter).await?;
|
self.object.verify(context, request_counter).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
|
activities::community::announce::{AnnouncableActivities, AnnounceActivity},
|
||||||
check_is_apub_id_valid,
|
check_is_apub_id_valid,
|
||||||
extensions::signatures::sign_and_send,
|
extensions::signatures::sign_and_send,
|
||||||
insert_activity,
|
insert_activity,
|
||||||
|
@ -24,7 +25,7 @@ use itertools::Itertools;
|
||||||
use lemmy_db_schema::source::{community::Community, person::Person};
|
use lemmy_db_schema::source::{community::Community, person::Person};
|
||||||
use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
|
use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use log::{debug, warn};
|
use log::{debug, info, warn};
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{collections::BTreeMap, env, fmt::Debug, future::Future, pin::Pin};
|
use std::{collections::BTreeMap, env, fmt::Debug, future::Future, pin::Pin};
|
||||||
|
@ -171,6 +172,80 @@ where
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn send_to_community_new(
|
||||||
|
activity: AnnouncableActivities,
|
||||||
|
activity_id: &Url,
|
||||||
|
actor: &dyn ActorType,
|
||||||
|
community: &Community,
|
||||||
|
additional_inboxes: Vec<Url>,
|
||||||
|
context: &LemmyContext,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
// if this is a local community, we need to do an announce from the community instead
|
||||||
|
if community.local {
|
||||||
|
insert_activity(activity_id, activity.clone(), true, false, context.pool()).await?;
|
||||||
|
AnnounceActivity::send(activity, community, additional_inboxes, context).await?;
|
||||||
|
} else {
|
||||||
|
let mut inboxes = additional_inboxes;
|
||||||
|
inboxes.push(community.get_shared_inbox_or_inbox_url());
|
||||||
|
send_activity_new(context, &activity, activity_id, actor, inboxes, false).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn send_activity_new<T>(
|
||||||
|
context: &LemmyContext,
|
||||||
|
activity: &T,
|
||||||
|
activity_id: &Url,
|
||||||
|
actor: &dyn ActorType,
|
||||||
|
inboxes: Vec<Url>,
|
||||||
|
sensitive: bool,
|
||||||
|
) -> Result<(), LemmyError>
|
||||||
|
where
|
||||||
|
T: Serialize,
|
||||||
|
{
|
||||||
|
if !Settings::get().federation().enabled || inboxes.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Sending activity {}", activity_id.to_string());
|
||||||
|
|
||||||
|
// Don't send anything to ourselves
|
||||||
|
// TODO: this should be a debug assert
|
||||||
|
let hostname = Settings::get().get_hostname_without_port()?;
|
||||||
|
let inboxes: Vec<&Url> = inboxes
|
||||||
|
.iter()
|
||||||
|
.filter(|i| i.domain().expect("valid inbox url") != hostname)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let serialised_activity = serde_json::to_string(&activity)?;
|
||||||
|
|
||||||
|
insert_activity(
|
||||||
|
activity_id,
|
||||||
|
serialised_activity.clone(),
|
||||||
|
true,
|
||||||
|
sensitive,
|
||||||
|
context.pool(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
for i in inboxes {
|
||||||
|
let message = SendActivityTask {
|
||||||
|
activity: serialised_activity.to_owned(),
|
||||||
|
inbox: i.to_owned(),
|
||||||
|
actor_id: actor.actor_id(),
|
||||||
|
private_key: actor.private_key().context(location_info!())?,
|
||||||
|
};
|
||||||
|
if env::var("LEMMY_TEST_SEND_SYNC").is_ok() {
|
||||||
|
do_send(message, &Client::default()).await?;
|
||||||
|
} else {
|
||||||
|
context.activity_queue.queue::<SendActivityTask>(message)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Create new `SendActivityTasks`, which will deliver the given activity to inboxes, as well as
|
/// Create new `SendActivityTasks`, which will deliver the given activity to inboxes, as well as
|
||||||
/// handling signing and retrying failed deliveres.
|
/// handling signing and retrying failed deliveres.
|
||||||
///
|
///
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use activitystreams::{base::AnyBase, context};
|
use activitystreams::{base::AnyBase, context, primitives::OneOrMany};
|
||||||
use lemmy_utils::LemmyError;
|
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
pub fn lemmy_context() -> Result<Vec<AnyBase>, LemmyError> {
|
pub fn lemmy_context() -> OneOrMany<AnyBase> {
|
||||||
let context_ext = AnyBase::from_arbitrary_json(json!(
|
let context_ext = AnyBase::from_arbitrary_json(json!(
|
||||||
{
|
{
|
||||||
"sc": "http://schema.org#",
|
"sc": "http://schema.org#",
|
||||||
|
@ -19,10 +18,11 @@ pub fn lemmy_context() -> Result<Vec<AnyBase>, LemmyError> {
|
||||||
"type": "sc:Text",
|
"type": "sc:Text",
|
||||||
"id": "as:alsoKnownAs"
|
"id": "as:alsoKnownAs"
|
||||||
},
|
},
|
||||||
}))?;
|
}))
|
||||||
Ok(vec![
|
.expect("parse context");
|
||||||
|
OneOrMany::from(vec![
|
||||||
AnyBase::from(context()),
|
AnyBase::from(context()),
|
||||||
context_ext,
|
context_ext,
|
||||||
AnyBase::from(Url::parse("https://w3id.org/security/v1")?),
|
AnyBase::from(Url::parse("https://w3id.org/security/v1").expect("parse context")),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
pub mod context;
|
pub mod context;
|
||||||
pub(crate) mod group_extension;
|
pub(crate) mod group_extension;
|
||||||
pub(crate) mod page_extension;
|
|
||||||
pub(crate) mod person_extension;
|
pub(crate) mod person_extension;
|
||||||
pub mod signatures;
|
pub mod signatures;
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
use activitystreams::unparsed::UnparsedMutExt;
|
|
||||||
use activitystreams_ext::UnparsedExtension;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
/// Activitystreams extension to allow (de)serializing additional Post fields
|
|
||||||
/// `comemnts_enabled` (called 'locked' in Lemmy),
|
|
||||||
/// `sensitive` (called 'nsfw') and `stickied`.
|
|
||||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct PageExtension {
|
|
||||||
pub comments_enabled: Option<bool>,
|
|
||||||
pub sensitive: Option<bool>,
|
|
||||||
pub stickied: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<U> UnparsedExtension<U> for PageExtension
|
|
||||||
where
|
|
||||||
U: UnparsedMutExt,
|
|
||||||
{
|
|
||||||
type Error = serde_json::Error;
|
|
||||||
|
|
||||||
fn try_from_unparsed(unparsed_mut: &mut U) -> Result<Self, Self::Error> {
|
|
||||||
Ok(PageExtension {
|
|
||||||
comments_enabled: unparsed_mut.remove("commentsEnabled")?,
|
|
||||||
sensitive: unparsed_mut.remove("sensitive")?,
|
|
||||||
stickied: unparsed_mut.remove("stickied")?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_into_unparsed(self, unparsed_mut: &mut U) -> Result<(), Self::Error> {
|
|
||||||
unparsed_mut.insert("commentsEnabled", self.comments_enabled)?;
|
|
||||||
unparsed_mut.insert("sensitive", self.sensitive)?;
|
|
||||||
unparsed_mut.insert("stickied", self.stickied)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
fetcher::fetch::fetch_remote_object,
|
fetcher::fetch::fetch_remote_object,
|
||||||
objects::FromApub,
|
objects::{post::Page, FromApub},
|
||||||
NoteExt,
|
NoteExt,
|
||||||
PageExt,
|
|
||||||
PostOrComment,
|
PostOrComment,
|
||||||
};
|
};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
|
@ -35,7 +34,7 @@ pub async fn get_or_fetch_and_insert_post(
|
||||||
Err(NotFound {}) => {
|
Err(NotFound {}) => {
|
||||||
debug!("Fetching and creating remote post: {}", post_ap_id);
|
debug!("Fetching and creating remote post: {}", post_ap_id);
|
||||||
let page =
|
let page =
|
||||||
fetch_remote_object::<PageExt>(context.client(), post_ap_id, recursion_counter).await?;
|
fetch_remote_object::<Page>(context.client(), post_ap_id, recursion_counter).await?;
|
||||||
let post = Post::from_apub(
|
let post = Post::from_apub(
|
||||||
&page,
|
&page,
|
||||||
context,
|
context,
|
||||||
|
|
|
@ -6,11 +6,10 @@ use crate::{
|
||||||
is_deleted,
|
is_deleted,
|
||||||
},
|
},
|
||||||
find_object_by_id,
|
find_object_by_id,
|
||||||
objects::FromApub,
|
objects::{post::Page, FromApub},
|
||||||
GroupExt,
|
GroupExt,
|
||||||
NoteExt,
|
NoteExt,
|
||||||
Object,
|
Object,
|
||||||
PageExt,
|
|
||||||
PersonExt,
|
PersonExt,
|
||||||
};
|
};
|
||||||
use activitystreams::base::BaseExt;
|
use activitystreams::base::BaseExt;
|
||||||
|
@ -46,7 +45,7 @@ use url::Url;
|
||||||
enum SearchAcceptedObjects {
|
enum SearchAcceptedObjects {
|
||||||
Person(Box<PersonExt>),
|
Person(Box<PersonExt>),
|
||||||
Group(Box<GroupExt>),
|
Group(Box<GroupExt>),
|
||||||
Page(Box<PageExt>),
|
Page(Box<Page>),
|
||||||
Comment(Box<NoteExt>),
|
Comment(Box<NoteExt>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -81,7 +81,7 @@ pub(crate) async fn get_apub_community_followers(
|
||||||
|
|
||||||
let mut collection = UnorderedCollection::new();
|
let mut collection = UnorderedCollection::new();
|
||||||
collection
|
collection
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(community.followers_url.into())
|
.set_id(community.followers_url.into())
|
||||||
.set_total_items(community_followers.len() as u64);
|
.set_total_items(community_followers.len() as u64);
|
||||||
Ok(create_apub_response(&collection))
|
Ok(create_apub_response(&collection))
|
||||||
|
@ -112,7 +112,7 @@ pub(crate) async fn get_apub_community_outbox(
|
||||||
let mut collection = OrderedCollection::new();
|
let mut collection = OrderedCollection::new();
|
||||||
collection
|
collection
|
||||||
.set_many_items(activities)
|
.set_many_items(activities)
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(community.get_outbox_url()?)
|
.set_id(community.get_outbox_url()?)
|
||||||
.set_total_items(len as u64);
|
.set_total_items(len as u64);
|
||||||
Ok(create_apub_response(&collection))
|
Ok(create_apub_response(&collection))
|
||||||
|
@ -130,7 +130,7 @@ pub(crate) async fn get_apub_community_inbox(
|
||||||
let mut collection = OrderedCollection::new();
|
let mut collection = OrderedCollection::new();
|
||||||
collection
|
collection
|
||||||
.set_id(community.inbox_url.into())
|
.set_id(community.inbox_url.into())
|
||||||
.set_many_contexts(lemmy_context()?);
|
.set_many_contexts(lemmy_context());
|
||||||
Ok(create_apub_response(&collection))
|
Ok(create_apub_response(&collection))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,6 +162,6 @@ pub(crate) async fn get_apub_community_moderators(
|
||||||
.set_id(generate_moderators_url(&community.actor_id)?.into())
|
.set_id(generate_moderators_url(&community.actor_id)?.into())
|
||||||
.set_total_items(moderators.len() as u64)
|
.set_total_items(moderators.len() as u64)
|
||||||
.set_many_items(moderators)
|
.set_many_items(moderators)
|
||||||
.set_many_contexts(lemmy_context()?);
|
.set_many_contexts(lemmy_context());
|
||||||
Ok(create_apub_response(&collection))
|
Ok(create_apub_response(&collection))
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,7 +71,7 @@ pub(crate) async fn get_apub_person_outbox(
|
||||||
let mut collection = OrderedCollection::new();
|
let mut collection = OrderedCollection::new();
|
||||||
collection
|
collection
|
||||||
.set_many_items(Vec::<Url>::new())
|
.set_many_items(Vec::<Url>::new())
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(person.get_outbox_url()?)
|
.set_id(person.get_outbox_url()?)
|
||||||
.set_total_items(0_u64);
|
.set_total_items(0_u64);
|
||||||
Ok(create_apub_response(&collection))
|
Ok(create_apub_response(&collection))
|
||||||
|
@ -89,6 +89,6 @@ pub(crate) async fn get_apub_person_inbox(
|
||||||
let mut collection = OrderedCollection::new();
|
let mut collection = OrderedCollection::new();
|
||||||
collection
|
collection
|
||||||
.set_id(person.inbox_url.into())
|
.set_id(person.inbox_url.into())
|
||||||
.set_many_contexts(lemmy_context()?);
|
.set_many_contexts(lemmy_context());
|
||||||
Ok(create_apub_response(&collection))
|
Ok(create_apub_response(&collection))
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ pub mod objects;
|
||||||
use crate::{
|
use crate::{
|
||||||
extensions::{
|
extensions::{
|
||||||
group_extension::GroupExtension,
|
group_extension::GroupExtension,
|
||||||
page_extension::PageExtension,
|
|
||||||
person_extension::PersonExtension,
|
person_extension::PersonExtension,
|
||||||
signatures::{PublicKey, PublicKeyExtension},
|
signatures::{PublicKey, PublicKeyExtension},
|
||||||
},
|
},
|
||||||
|
@ -21,9 +20,9 @@ use activitystreams::{
|
||||||
activity::Follow,
|
activity::Follow,
|
||||||
actor,
|
actor,
|
||||||
base::AnyBase,
|
base::AnyBase,
|
||||||
object::{ApObject, AsObject, Note, ObjectExt, Page},
|
object::{ApObject, AsObject, Note, ObjectExt},
|
||||||
};
|
};
|
||||||
use activitystreams_ext::{Ext1, Ext2};
|
use activitystreams_ext::Ext2;
|
||||||
use anyhow::{anyhow, Context};
|
use anyhow::{anyhow, Context};
|
||||||
use diesel::NotFound;
|
use diesel::NotFound;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
|
@ -54,8 +53,6 @@ pub type GroupExt =
|
||||||
type PersonExt =
|
type PersonExt =
|
||||||
Ext2<actor::ApActor<ApObject<actor::Actor<UserTypes>>>, PersonExtension, PublicKeyExtension>;
|
Ext2<actor::ApActor<ApObject<actor::Actor<UserTypes>>>, PersonExtension, PublicKeyExtension>;
|
||||||
pub type SiteExt = actor::ApActor<ApObject<actor::Service>>;
|
pub type SiteExt = actor::ApActor<ApObject<actor::Service>>;
|
||||||
/// Activitystreams type for post
|
|
||||||
pub type PageExt = Ext1<ApObject<Page>, PageExtension>;
|
|
||||||
pub type NoteExt = ApObject<Note>;
|
pub type NoteExt = ApObject<Note>;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, serde::Deserialize, serde::Serialize, PartialEq)]
|
#[derive(Clone, Copy, Debug, serde::Deserialize, serde::Serialize, PartialEq)]
|
||||||
|
|
|
@ -69,7 +69,7 @@ impl ToApub for Comment {
|
||||||
|
|
||||||
comment
|
comment
|
||||||
// Not needed when the Post is embedded in a collection (like for community outbox)
|
// Not needed when the Post is embedded in a collection (like for community outbox)
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(self.ap_id.to_owned().into_inner())
|
.set_id(self.ap_id.to_owned().into_inner())
|
||||||
.set_published(convert_datetime(self.published))
|
.set_published(convert_datetime(self.published))
|
||||||
// NOTE: included community id for compatibility with lemmy v0.9.9
|
// NOTE: included community id for compatibility with lemmy v0.9.9
|
||||||
|
|
|
@ -55,7 +55,7 @@ impl ToApub for Community {
|
||||||
|
|
||||||
let mut group = ApObject::new(Group::new());
|
let mut group = ApObject::new(Group::new());
|
||||||
group
|
group
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(self.actor_id.to_owned().into())
|
.set_id(self.actor_id.to_owned().into())
|
||||||
.set_name(self.title.to_owned())
|
.set_name(self.title.to_owned())
|
||||||
.set_published(convert_datetime(self.published))
|
.set_published(convert_datetime(self.published))
|
||||||
|
|
|
@ -12,6 +12,7 @@ use activitystreams::{
|
||||||
use anyhow::{anyhow, Context};
|
use anyhow::{anyhow, Context};
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
|
use lemmy_apub_lib::values::MediaTypeMarkdown;
|
||||||
use lemmy_db_queries::{ApubObject, Crud, DbPool};
|
use lemmy_db_queries::{ApubObject, Crud, DbPool};
|
||||||
use lemmy_db_schema::{CommunityId, DbUrl};
|
use lemmy_db_schema::{CommunityId, DbUrl};
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
|
@ -70,6 +71,13 @@ pub trait FromApubToForm<ApubType> {
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Source {
|
||||||
|
content: String,
|
||||||
|
media_type: MediaTypeMarkdown,
|
||||||
|
}
|
||||||
|
|
||||||
/// Updated is actually the deletion time
|
/// Updated is actually the deletion time
|
||||||
fn create_tombstone<T>(
|
fn create_tombstone<T>(
|
||||||
deleted: bool,
|
deleted: bool,
|
||||||
|
|
|
@ -50,7 +50,7 @@ impl ToApub for DbPerson {
|
||||||
let mut person = ApObject::new(actor);
|
let mut person = ApObject::new(actor);
|
||||||
|
|
||||||
person
|
person
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(self.actor_id.to_owned().into_inner())
|
.set_id(self.actor_id.to_owned().into_inner())
|
||||||
.set_published(convert_datetime(self.published));
|
.set_published(convert_datetime(self.published));
|
||||||
|
|
||||||
|
|
|
@ -1,30 +1,26 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
check_is_apub_id_valid,
|
activities::extract_community,
|
||||||
extensions::{context::lemmy_context, page_extension::PageExtension},
|
extensions::context::lemmy_context,
|
||||||
fetcher::person::get_or_fetch_and_upsert_person,
|
fetcher::person::get_or_fetch_and_upsert_person,
|
||||||
get_community_from_to_or_cc,
|
objects::{create_tombstone, FromApub, Source, ToApub},
|
||||||
objects::{
|
|
||||||
check_object_domain,
|
|
||||||
check_object_for_community_or_site_ban,
|
|
||||||
create_tombstone,
|
|
||||||
get_object_from_apub,
|
|
||||||
get_source_markdown_value,
|
|
||||||
set_content_and_source,
|
|
||||||
FromApub,
|
|
||||||
FromApubToForm,
|
|
||||||
ToApub,
|
|
||||||
},
|
|
||||||
PageExt,
|
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
object::{kind::PageType, ApObject, Image, Page, Tombstone},
|
base::AnyBase,
|
||||||
prelude::*,
|
object::{
|
||||||
|
kind::{ImageType, PageType},
|
||||||
|
Tombstone,
|
||||||
|
},
|
||||||
|
primitives::OneOrMany,
|
||||||
public,
|
public,
|
||||||
|
unparsed::Unparsed,
|
||||||
};
|
};
|
||||||
use activitystreams_ext::Ext1;
|
use chrono::{DateTime, FixedOffset};
|
||||||
use anyhow::Context;
|
|
||||||
use lemmy_api_common::blocking;
|
use lemmy_api_common::blocking;
|
||||||
use lemmy_db_queries::{Crud, DbPool};
|
use lemmy_apub_lib::{
|
||||||
|
values::{MediaTypeHtml, MediaTypeMarkdown},
|
||||||
|
verify_domains_match,
|
||||||
|
};
|
||||||
|
use lemmy_db_queries::{ApubObject, Crud, DbPool};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
self,
|
self,
|
||||||
source::{
|
source::{
|
||||||
|
@ -34,66 +30,117 @@ use lemmy_db_schema::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
location_info,
|
|
||||||
request::fetch_iframely_and_pictrs_data,
|
request::fetch_iframely_and_pictrs_data,
|
||||||
utils::{check_slurs, convert_datetime, remove_slurs},
|
utils::{check_slurs, convert_datetime, markdown_to_html, remove_slurs},
|
||||||
LemmyError,
|
LemmyError,
|
||||||
};
|
};
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Page {
|
||||||
|
#[serde(rename = "@context")]
|
||||||
|
context: OneOrMany<AnyBase>,
|
||||||
|
r#type: PageType,
|
||||||
|
pub(crate) id: Url,
|
||||||
|
pub(crate) attributed_to: Url,
|
||||||
|
to: [Url; 2],
|
||||||
|
name: String,
|
||||||
|
content: Option<String>,
|
||||||
|
media_type: MediaTypeHtml,
|
||||||
|
source: Option<Source>,
|
||||||
|
url: Option<Url>,
|
||||||
|
image: Option<ImageObject>,
|
||||||
|
pub(crate) comments_enabled: Option<bool>,
|
||||||
|
sensitive: Option<bool>,
|
||||||
|
pub(crate) stickied: Option<bool>,
|
||||||
|
published: DateTime<FixedOffset>,
|
||||||
|
updated: Option<DateTime<FixedOffset>>,
|
||||||
|
|
||||||
|
// unparsed fields
|
||||||
|
#[serde(flatten)]
|
||||||
|
unparsed: Unparsed,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct ImageObject {
|
||||||
|
content: ImageType,
|
||||||
|
url: Url,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Page {
|
||||||
|
/// Only mods can change the post's stickied/locked status. So if either of these is changed from
|
||||||
|
/// the current value, it is a mod action and needs to be verified as such.
|
||||||
|
///
|
||||||
|
/// Both stickied and locked need to be false on a newly created post (verified in [[CreatePost]].
|
||||||
|
pub(crate) async fn is_mod_action(&self, pool: &DbPool) -> Result<bool, LemmyError> {
|
||||||
|
let post_id = self.id.clone();
|
||||||
|
let old_post = blocking(pool, move |conn| {
|
||||||
|
Post::read_from_apub_id(conn, &post_id.into())
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let is_mod_action = if let Ok(old_post) = old_post {
|
||||||
|
self.stickied != Some(old_post.stickied) || self.comments_enabled != Some(!old_post.locked)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
Ok(is_mod_action)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn verify(
|
||||||
|
&self,
|
||||||
|
_context: &LemmyContext,
|
||||||
|
_request_counter: &mut i32,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
check_slurs(&self.name)?;
|
||||||
|
verify_domains_match(&self.attributed_to, &self.id)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl ToApub for Post {
|
impl ToApub for Post {
|
||||||
type ApubType = PageExt;
|
type ApubType = Page;
|
||||||
|
|
||||||
// Turn a Lemmy post into an ActivityPub page that can be sent out over the network.
|
// Turn a Lemmy post into an ActivityPub page that can be sent out over the network.
|
||||||
async fn to_apub(&self, pool: &DbPool) -> Result<PageExt, LemmyError> {
|
async fn to_apub(&self, pool: &DbPool) -> Result<Page, LemmyError> {
|
||||||
let mut page = ApObject::new(Page::new());
|
|
||||||
|
|
||||||
let creator_id = self.creator_id;
|
let creator_id = self.creator_id;
|
||||||
let creator = blocking(pool, move |conn| Person::read(conn, creator_id)).await??;
|
let creator = blocking(pool, move |conn| Person::read(conn, creator_id)).await??;
|
||||||
|
|
||||||
let community_id = self.community_id;
|
let community_id = self.community_id;
|
||||||
let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
|
let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
|
||||||
|
|
||||||
page
|
let source = self.body.clone().map(|body| Source {
|
||||||
// Not needed when the Post is embedded in a collection (like for community outbox)
|
content: body,
|
||||||
// TODO: need to set proper context defining sensitive/commentsEnabled fields
|
media_type: MediaTypeMarkdown::Markdown,
|
||||||
// https://git.asonix.dog/Aardwolf/activitystreams/issues/5
|
});
|
||||||
.set_many_contexts(lemmy_context()?)
|
let image = self.thumbnail_url.clone().map(|thumb| ImageObject {
|
||||||
.set_id(self.ap_id.to_owned().into_inner())
|
content: ImageType::Image,
|
||||||
.set_name(self.name.to_owned())
|
url: thumb.into(),
|
||||||
// `summary` field for compatibility with lemmy v0.9.9 and older,
|
});
|
||||||
// TODO: remove this after some time
|
|
||||||
.set_summary(self.name.to_owned())
|
|
||||||
.set_published(convert_datetime(self.published))
|
|
||||||
.set_many_tos(vec![community.actor_id.into_inner(), public()])
|
|
||||||
.set_attributed_to(creator.actor_id.into_inner());
|
|
||||||
|
|
||||||
if let Some(body) = &self.body {
|
let page = Page {
|
||||||
set_content_and_source(&mut page, body)?;
|
context: lemmy_context(),
|
||||||
}
|
r#type: PageType::Page,
|
||||||
|
id: self.ap_id.clone().into(),
|
||||||
if let Some(url) = &self.url {
|
attributed_to: creator.actor_id.into(),
|
||||||
page.set_url::<Url>(url.to_owned().into());
|
to: [community.actor_id.into(), public()],
|
||||||
}
|
name: self.name.clone(),
|
||||||
|
content: self.body.as_ref().map(|b| markdown_to_html(b)),
|
||||||
if let Some(thumbnail_url) = &self.thumbnail_url {
|
media_type: MediaTypeHtml::Html,
|
||||||
let mut image = Image::new();
|
source,
|
||||||
image.set_url::<Url>(thumbnail_url.to_owned().into());
|
url: self.url.clone().map(|u| u.into()),
|
||||||
page.set_image(image.into_any_base()?);
|
image,
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(u) = self.updated {
|
|
||||||
page.set_updated(convert_datetime(u));
|
|
||||||
}
|
|
||||||
|
|
||||||
let ext = PageExtension {
|
|
||||||
comments_enabled: Some(!self.locked),
|
comments_enabled: Some(!self.locked),
|
||||||
sensitive: Some(self.nsfw),
|
sensitive: Some(self.nsfw),
|
||||||
stickied: Some(self.stickied),
|
stickied: Some(self.stickied),
|
||||||
|
published: convert_datetime(self.published),
|
||||||
|
updated: self.updated.map(convert_datetime),
|
||||||
|
unparsed: Default::default(),
|
||||||
};
|
};
|
||||||
Ok(Ext1::new(page, ext))
|
Ok(page)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_tombstone(&self) -> Result<Tombstone, LemmyError> {
|
fn to_tombstone(&self) -> Result<Tombstone, LemmyError> {
|
||||||
|
@ -108,136 +155,48 @@ impl ToApub for Post {
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl FromApub for Post {
|
impl FromApub for Post {
|
||||||
type ApubType = PageExt;
|
type ApubType = Page;
|
||||||
|
|
||||||
/// Converts a `PageExt` to `PostForm`.
|
|
||||||
///
|
|
||||||
/// If the post's community or creator are not known locally, these are also fetched.
|
|
||||||
async fn from_apub(
|
async fn from_apub(
|
||||||
page: &PageExt,
|
page: &Page,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
expected_domain: Url,
|
_expected_domain: Url,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
mod_action_allowed: bool,
|
_mod_action_allowed: bool,
|
||||||
) -> Result<Post, LemmyError> {
|
) -> Result<Post, LemmyError> {
|
||||||
let post: Post = get_object_from_apub(
|
|
||||||
page,
|
|
||||||
context,
|
|
||||||
expected_domain,
|
|
||||||
request_counter,
|
|
||||||
mod_action_allowed,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
check_object_for_community_or_site_ban(page, post.community_id, context, request_counter)
|
|
||||||
.await?;
|
|
||||||
Ok(post)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
|
||||||
impl FromApubToForm<PageExt> for PostForm {
|
|
||||||
async fn from_apub(
|
|
||||||
page: &PageExt,
|
|
||||||
context: &LemmyContext,
|
|
||||||
expected_domain: Url,
|
|
||||||
request_counter: &mut i32,
|
|
||||||
mod_action_allowed: bool,
|
|
||||||
) -> Result<PostForm, LemmyError> {
|
|
||||||
let community = get_community_from_to_or_cc(page, context, request_counter).await?;
|
|
||||||
let ap_id = if mod_action_allowed {
|
|
||||||
let id = page.id_unchecked().context(location_info!())?;
|
|
||||||
check_is_apub_id_valid(id, community.local)?;
|
|
||||||
id.to_owned().into()
|
|
||||||
} else {
|
|
||||||
check_object_domain(page, expected_domain, community.local)?
|
|
||||||
};
|
|
||||||
let ext = &page.ext_one;
|
|
||||||
let creator_actor_id = page
|
|
||||||
.inner
|
|
||||||
.attributed_to()
|
|
||||||
.as_ref()
|
|
||||||
.context(location_info!())?
|
|
||||||
.as_single_xsd_any_uri()
|
|
||||||
.context(location_info!())?;
|
|
||||||
|
|
||||||
let creator =
|
let creator =
|
||||||
get_or_fetch_and_upsert_person(creator_actor_id, context, request_counter).await?;
|
get_or_fetch_and_upsert_person(&page.attributed_to, context, request_counter).await?;
|
||||||
|
let community = extract_community(&page.to, context, request_counter).await?;
|
||||||
let thumbnail_url: Option<Url> = match &page.inner.image() {
|
|
||||||
Some(any_image) => Image::from_any_base(
|
|
||||||
any_image
|
|
||||||
.to_owned()
|
|
||||||
.as_one()
|
|
||||||
.context(location_info!())?
|
|
||||||
.to_owned(),
|
|
||||||
)?
|
|
||||||
.context(location_info!())?
|
|
||||||
.url()
|
|
||||||
.context(location_info!())?
|
|
||||||
.as_single_xsd_any_uri()
|
|
||||||
.map(|url| url.to_owned()),
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
let url = page
|
|
||||||
.inner
|
|
||||||
.url()
|
|
||||||
.map(|u| u.as_single_xsd_any_uri())
|
|
||||||
.flatten()
|
|
||||||
.map(|u| u.to_owned());
|
|
||||||
|
|
||||||
|
let thumbnail_url: Option<Url> = page.image.clone().map(|i| i.url);
|
||||||
let (iframely_title, iframely_description, iframely_html, pictrs_thumbnail) =
|
let (iframely_title, iframely_description, iframely_html, pictrs_thumbnail) =
|
||||||
if let Some(url) = &url {
|
if let Some(url) = &page.url {
|
||||||
fetch_iframely_and_pictrs_data(context.client(), Some(url)).await
|
fetch_iframely_and_pictrs_data(context.client(), Some(url)).await
|
||||||
} else {
|
} else {
|
||||||
(None, None, None, thumbnail_url)
|
(None, None, None, thumbnail_url)
|
||||||
};
|
};
|
||||||
|
|
||||||
let name = page
|
let body_slurs_removed = page.source.as_ref().map(|s| remove_slurs(&s.content));
|
||||||
.inner
|
let form = PostForm {
|
||||||
.name()
|
name: page.name.clone(),
|
||||||
// The following is for compatibility with lemmy v0.9.9 and older
|
url: page.url.clone().map(|u| u.into()),
|
||||||
// TODO: remove it after some time (along with the map above)
|
|
||||||
.or_else(|| page.inner.summary())
|
|
||||||
.context(location_info!())?
|
|
||||||
.as_single_xsd_string()
|
|
||||||
.context(location_info!())?
|
|
||||||
.to_string();
|
|
||||||
let body = get_source_markdown_value(page)?;
|
|
||||||
|
|
||||||
// TODO: expected_domain is wrong in this case, because it simply takes the domain of the actor
|
|
||||||
// maybe we need to take id_unchecked() if the activity is from community to user?
|
|
||||||
// why did this work before? -> i dont think it did?
|
|
||||||
// -> try to make expected_domain optional and set it null if it is a mod action
|
|
||||||
|
|
||||||
check_slurs(&name)?;
|
|
||||||
let body_slurs_removed = body.map(|b| remove_slurs(&b));
|
|
||||||
Ok(PostForm {
|
|
||||||
name,
|
|
||||||
url: url.map(|u| u.into()),
|
|
||||||
body: body_slurs_removed,
|
body: body_slurs_removed,
|
||||||
creator_id: creator.id,
|
creator_id: creator.id,
|
||||||
community_id: community.id,
|
community_id: community.id,
|
||||||
removed: None,
|
removed: None,
|
||||||
locked: ext.comments_enabled.map(|e| !e),
|
locked: page.comments_enabled.map(|e| !e),
|
||||||
published: page
|
published: Some(page.published.naive_local()),
|
||||||
.inner
|
updated: page.updated.map(|u| u.naive_local()),
|
||||||
.published()
|
|
||||||
.as_ref()
|
|
||||||
.map(|u| u.to_owned().naive_local()),
|
|
||||||
updated: page
|
|
||||||
.inner
|
|
||||||
.updated()
|
|
||||||
.as_ref()
|
|
||||||
.map(|u| u.to_owned().naive_local()),
|
|
||||||
deleted: None,
|
deleted: None,
|
||||||
nsfw: ext.sensitive,
|
nsfw: page.sensitive,
|
||||||
stickied: ext.stickied,
|
stickied: page.stickied,
|
||||||
embed_title: iframely_title,
|
embed_title: iframely_title,
|
||||||
embed_description: iframely_description,
|
embed_description: iframely_description,
|
||||||
embed_html: iframely_html,
|
embed_html: iframely_html,
|
||||||
thumbnail_url: pictrs_thumbnail.map(|u| u.into()),
|
thumbnail_url: pictrs_thumbnail.map(|u| u.into()),
|
||||||
ap_id: Some(ap_id),
|
ap_id: Some(page.id.clone().into()),
|
||||||
local: Some(false),
|
local: Some(false),
|
||||||
})
|
};
|
||||||
|
Ok(blocking(context.pool(), move |conn| Post::upsert(conn, &form)).await??)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ impl ToApub for PrivateMessage {
|
||||||
let recipient = blocking(pool, move |conn| Person::read(conn, recipient_id)).await??;
|
let recipient = blocking(pool, move |conn| Person::read(conn, recipient_id)).await??;
|
||||||
|
|
||||||
private_message
|
private_message
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context())
|
||||||
.set_id(self.ap_id.to_owned().into_inner())
|
.set_id(self.ap_id.to_owned().into_inner())
|
||||||
.set_published(convert_datetime(self.published))
|
.set_published(convert_datetime(self.published))
|
||||||
.set_to(recipient.actor_id.into_inner())
|
.set_to(recipient.actor_id.into_inner())
|
||||||
|
|
|
@ -12,3 +12,4 @@ activitystreams-ext = "0.1.0-alpha.2"
|
||||||
serde = { version = "1.0.123", features = ["derive"] }
|
serde = { version = "1.0.123", features = ["derive"] }
|
||||||
async-trait = "0.1.42"
|
async-trait = "0.1.42"
|
||||||
url = { version = "2.2.1", features = ["serde"] }
|
url = { version = "2.2.1", features = ["serde"] }
|
||||||
|
serde_json = { version = "1.0.64", features = ["preserve_order"] }
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
pub mod values;
|
||||||
|
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
base::AnyBase,
|
base::AnyBase,
|
||||||
error::DomainError,
|
error::DomainError,
|
||||||
|
@ -9,18 +11,12 @@ use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub enum PublicUrl {
|
|
||||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#Public")]
|
|
||||||
Public,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct ActivityCommonFields {
|
pub struct ActivityCommonFields {
|
||||||
#[serde(rename = "@context")]
|
#[serde(rename = "@context")]
|
||||||
pub context: OneOrMany<AnyBase>,
|
pub context: OneOrMany<AnyBase>,
|
||||||
id: Url,
|
pub id: Url,
|
||||||
pub actor: Url,
|
pub actor: Url,
|
||||||
|
|
||||||
// unparsed fields
|
// unparsed fields
|
||||||
|
|
61
crates/apub_lib/src/values/mod.rs
Normal file
61
crates/apub_lib/src/values/mod.rs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
//! The enums here serve to limit a json string value to a single, hardcoded value which can be
|
||||||
|
//! verified at compilation time. When using it as the type of a struct field, the struct can only
|
||||||
|
//! be constructed or deserialized if the field has the exact same value.
|
||||||
|
//!
|
||||||
|
//! If we used String as the field type, any value would be accepted, and we would have to check
|
||||||
|
//! manually at runtime that it contains the expected value.
|
||||||
|
//!
|
||||||
|
//! The enums in [`activitystreams::activity::kind`] work in the same way, and can be used to
|
||||||
|
//! distinguish different activity types.
|
||||||
|
//!
|
||||||
|
//! In the example below, `MyObject` can only be constructed or
|
||||||
|
//! deserialized if `media_type` is `text/markdown`, but not if it is `text/html`.
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! use lemmy_apub_lib::values::MediaTypeMarkdown;
|
||||||
|
//! use serde_json::from_str;
|
||||||
|
//! use serde::{Deserialize, Serialize};
|
||||||
|
//!
|
||||||
|
//! #[derive(Deserialize, Serialize)]
|
||||||
|
//! struct MyObject {
|
||||||
|
//! content: String,
|
||||||
|
//! media_type: MediaTypeMarkdown,
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! let markdown_json = r#"{"content": "**test**", "media_type": "text/markdown"}"#;
|
||||||
|
//! let from_markdown = from_str::<MyObject>(markdown_json);
|
||||||
|
//! assert!(from_markdown.is_ok());
|
||||||
|
//!
|
||||||
|
//! let markdown_html = r#"{"content": "<b>test</b>", "media_type": "text/html"}"#;
|
||||||
|
//! let from_html = from_str::<MyObject>(markdown_html);
|
||||||
|
//! assert!(from_html.is_err());
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// The identifier used to address activities to the public.
|
||||||
|
///
|
||||||
|
/// <https://www.w3.org/TR/activitypub/#public-addressing>
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
|
pub enum PublicUrl {
|
||||||
|
#[serde(rename = "https://www.w3.org/ns/activitystreams#Public")]
|
||||||
|
Public,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Media type for markdown text.
|
||||||
|
///
|
||||||
|
/// <https://www.iana.org/assignments/media-types/media-types.xhtml>
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
pub enum MediaTypeMarkdown {
|
||||||
|
#[serde(rename = "text/markdown")]
|
||||||
|
Markdown,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Media type for HTML text/
|
||||||
|
///
|
||||||
|
/// <https://www.iana.org/assignments/media-types/media-types.xhtml>
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
pub enum MediaTypeHtml {
|
||||||
|
#[serde(rename = "text/html")]
|
||||||
|
Html,
|
||||||
|
}
|
Loading…
Reference in a new issue