"diesel-derive-newtype",
"diesel_migrations",
"lazy_static",
- "lemmy_apub_lib",
"lemmy_utils",
"log",
"regex",
// Only add the like if the score isnt 0
let comment = orig_comment.comment;
- let object = PostOrComment::Comment(comment);
+ let object = PostOrComment::Comment(comment.into());
let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
if do_add {
let like_form2 = like_form.clone();
Vote::send(
&object,
- &local_user_view.person,
+ &local_user_view.person.clone().into(),
orig_comment.community.id,
like_form.score.try_into()?,
context,
// API doesn't distinguish between Undo/Like and Undo/Dislike
UndoVote::send(
&object,
- &local_user_view.person,
+ &local_user_view.person.clone().into(),
orig_comment.community.id,
VoteType::Like,
context,
Report::send(
ObjectId::new(comment_view.comment.ap_id),
- &local_user_view.person,
- comment_view.community.id,
+ &local_user_view.person.into(),
+ ObjectId::new(comment_view.community.actor_id),
reason.to_string(),
context,
)
get_local_user_view_from_jwt,
is_mod_or_admin,
};
-use lemmy_apub::activities::{
- community::{
- add_mod::AddMod,
- block_user::BlockUserFromCommunity,
- remove_mod::RemoveMod,
- undo_block_user::UndoBlockUserFromCommunity,
+use lemmy_apub::{
+ activities::{
+ community::{
+ add_mod::AddMod,
+ block_user::BlockUserFromCommunity,
+ remove_mod::RemoveMod,
+ undo_block_user::UndoBlockUserFromCommunity,
+ },
+ following::{follow::FollowCommunity as FollowCommunityApub, undo::UndoFollowCommunity},
},
- following::{follow::FollowCommunity as FollowCommunityApub, undo::UndoFollowCommunity},
+ objects::{community::ApubCommunity, person::ApubPerson},
};
use lemmy_db_schema::{
source::{
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
let community_id = data.community_id;
- let community = blocking(context.pool(), move |conn| {
+ let community: ApubCommunity = blocking(context.pool(), move |conn| {
Community::read(conn, community_id)
})
- .await??;
+ .await??
+ .into();
let community_follower_form = CommunityFollowerForm {
community_id: data.community_id,
person_id: local_user_view.person.id,
} else if data.follow {
// Dont actually add to the community followers here, because you need
// to wait for the accept
- FollowCommunityApub::send(&local_user_view.person, &community, context).await?;
+ FollowCommunityApub::send(&local_user_view.person.clone().into(), &community, context)
+ .await?;
} else {
- UndoFollowCommunity::send(&local_user_view.person, &community, context).await?;
+ UndoFollowCommunity::send(&local_user_view.person.clone().into(), &community, context)
+ .await?;
let unfollow = move |conn: &'_ _| CommunityFollower::unfollow(conn, &community_follower_form);
blocking(context.pool(), unfollow)
.await?
Community::read(conn, community_id)
})
.await??;
- UndoFollowCommunity::send(&local_user_view.person, &community, context).await?;
+ UndoFollowCommunity::send(&local_user_view.person.into(), &community.into(), context).await?;
} else {
let unblock = move |conn: &'_ _| CommunityBlock::unblock(conn, &community_block_form);
blocking(context.pool(), unblock)
person_id: data.person_id,
};
- let community = blocking(context.pool(), move |conn: &'_ _| {
+ let community: ApubCommunity = blocking(context.pool(), move |conn: &'_ _| {
Community::read(conn, community_id)
})
- .await??;
- let banned_person = blocking(context.pool(), move |conn: &'_ _| {
+ .await??
+ .into();
+ let banned_person: ApubPerson = blocking(context.pool(), move |conn: &'_ _| {
Person::read(conn, banned_person_id)
})
- .await??;
+ .await??
+ .into();
if data.ban {
let ban = move |conn: &'_ _| CommunityPersonBan::ban(conn, &community_user_ban_form);
.await?
.ok();
- BlockUserFromCommunity::send(&community, &banned_person, &local_user_view.person, context)
- .await?;
+ BlockUserFromCommunity::send(
+ &community,
+ &banned_person,
+ &local_user_view.person.clone().into(),
+ context,
+ )
+ .await?;
} else {
let unban = move |conn: &'_ _| CommunityPersonBan::unban(conn, &community_user_ban_form);
blocking(context.pool(), unban)
UndoBlockUserFromCommunity::send(
&community,
&banned_person,
- &local_user_view.person,
+ &local_user_view.person.clone().into(),
context,
)
.await?;
// Send to federated instances
let updated_mod_id = data.person_id;
- let updated_mod = blocking(context.pool(), move |conn| {
+ let updated_mod: ApubPerson = blocking(context.pool(), move |conn| {
Person::read(conn, updated_mod_id)
})
- .await??;
- let community = blocking(context.pool(), move |conn| {
+ .await??
+ .into();
+ let community: ApubCommunity = blocking(context.pool(), move |conn| {
Community::read(conn, community_id)
})
- .await??;
+ .await??
+ .into();
if data.added {
- AddMod::send(&community, &updated_mod, &local_user_view.person, context).await?;
+ AddMod::send(
+ &community,
+ &updated_mod,
+ &local_user_view.person.into(),
+ context,
+ )
+ .await?;
} else {
- RemoveMod::send(&community, &updated_mod, &local_user_view.person, context).await?;
+ RemoveMod::send(
+ &community,
+ &updated_mod,
+ &local_user_view.person.into(),
+ context,
+ )
+ .await?;
}
// Note: in case a remote mod is added, this returns the old moderators list, it will only get
CreateOrUpdateType,
},
fetcher::post_or_comment::PostOrComment,
+ objects::post::ApubPost,
};
use lemmy_db_schema::{
source::{moderator::*, post::*},
// Check for a community ban
let post_id = data.post_id;
- let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
+ let post: ApubPost = blocking(context.pool(), move |conn| Post::read(conn, post_id))
+ .await??
+ .into();
check_community_ban(local_user_view.person.id, post.community_id, context.pool()).await?;
check_community_deleted_or_removed(post.community_id, context.pool()).await?;
Vote::send(
&object,
- &local_user_view.person,
+ &local_user_view.person.clone().into(),
community_id,
like_form.score.try_into()?,
context,
// API doesn't distinguish between Undo/Like and Undo/Dislike
UndoVote::send(
&object,
- &local_user_view.person,
+ &local_user_view.person.clone().into(),
community_id,
VoteType::Like,
context,
// Update the post
let post_id = data.post_id;
let locked = data.locked;
- let updated_post = blocking(context.pool(), move |conn| {
+ let updated_post: ApubPost = blocking(context.pool(), move |conn| {
Post::update_locked(conn, post_id, locked)
})
- .await??;
+ .await??
+ .into();
// Mod tables
let form = ModLockPostForm {
// apub updates
CreateOrUpdatePost::send(
&updated_post,
- &local_user_view.person,
+ &local_user_view.person.clone().into(),
CreateOrUpdateType::Update,
context,
)
// Update the post
let post_id = data.post_id;
let stickied = data.stickied;
- let updated_post = blocking(context.pool(), move |conn| {
+ let updated_post: ApubPost = blocking(context.pool(), move |conn| {
Post::update_stickied(conn, post_id, stickied)
})
- .await??;
+ .await??
+ .into();
// Mod tables
let form = ModStickyPostForm {
// TODO stickied should pry work like locked for ease of use
CreateOrUpdatePost::send(
&updated_post,
- &local_user_view.person,
+ &local_user_view.person.clone().into(),
CreateOrUpdateType::Update,
context,
)
Report::send(
ObjectId::new(post_view.post.ap_id),
- &local_user_view.person,
- post_view.community.id,
+ &local_user_view.person.into(),
+ ObjectId::new(post_view.community.actor_id),
reason.to_string(),
context,
)
pub mod websocket;
use crate::site::FederatedInstances;
-use diesel::PgConnection;
use lemmy_db_schema::{
newtypes::{CommunityId, LocalUserId, PersonId, PostId},
source::{
- comment::Comment,
community::Community,
- person::Person,
person_block::PersonBlock,
- person_mention::{PersonMention, PersonMentionForm},
post::{Post, PostRead, PostReadForm},
secret::Secret,
site::Site,
community_person_ban_view::CommunityPersonBanView,
community_view::CommunityView,
};
-use lemmy_utils::{
- claims::Claims,
- email::send_email,
- settings::structs::{FederationConfig, Settings},
- utils::MentionData,
- ApiError,
- LemmyError,
-};
-use log::error;
+use lemmy_utils::{claims::Claims, settings::structs::FederationConfig, ApiError, LemmyError};
use url::Url;
pub async fn blocking<F, T>(pool: &DbPool, f: F) -> Result<T, LemmyError>
res
}
-pub async fn send_local_notifs(
- mentions: Vec<MentionData>,
- comment: Comment,
- person: Person,
- post: Post,
- pool: &DbPool,
- do_send_email: bool,
- settings: &Settings,
-) -> Result<Vec<LocalUserId>, LemmyError> {
- let settings = settings.to_owned();
- let ids = blocking(pool, move |conn| {
- do_send_local_notifs(
- conn,
- &mentions,
- &comment,
- &person,
- &post,
- do_send_email,
- &settings,
- )
- })
- .await?;
-
- Ok(ids)
-}
-
-fn do_send_local_notifs(
- conn: &PgConnection,
- mentions: &[MentionData],
- comment: &Comment,
- person: &Person,
- post: &Post,
- do_send_email: bool,
- settings: &Settings,
-) -> Vec<LocalUserId> {
- let mut recipient_ids = Vec::new();
-
- // Send the local mentions
- for mention in mentions
- .iter()
- .filter(|m| m.is_local(&settings.hostname) && m.name.ne(&person.name))
- .collect::<Vec<&MentionData>>()
- {
- if let Ok(mention_user_view) = LocalUserView::read_from_name(conn, &mention.name) {
- // TODO
- // At some point, make it so you can't tag the parent creator either
- // This can cause two notifications, one for reply and the other for mention
- recipient_ids.push(mention_user_view.local_user.id);
-
- let user_mention_form = PersonMentionForm {
- recipient_id: mention_user_view.person.id,
- comment_id: comment.id,
- read: None,
- };
-
- // Allow this to fail softly, since comment edits might re-update or replace it
- // Let the uniqueness handle this fail
- PersonMention::create(conn, &user_mention_form).ok();
-
- // Send an email to those local users that have notifications on
- if do_send_email {
- send_email_to_user(
- &mention_user_view,
- "Mentioned by",
- "Person Mention",
- &comment.content,
- settings,
- )
- }
- }
- }
-
- // Send notifs to the parent commenter / poster
- match comment.parent_id {
- Some(parent_id) => {
- if let Ok(parent_comment) = Comment::read(conn, parent_id) {
- // Don't send a notif to yourself
- if parent_comment.creator_id != person.id {
- // Get the parent commenter local_user
- if let Ok(parent_user_view) = LocalUserView::read_person(conn, parent_comment.creator_id)
- {
- recipient_ids.push(parent_user_view.local_user.id);
-
- if do_send_email {
- send_email_to_user(
- &parent_user_view,
- "Reply from",
- "Comment Reply",
- &comment.content,
- settings,
- )
- }
- }
- }
- }
- }
- // Its a post
- None => {
- if post.creator_id != person.id {
- if let Ok(parent_user_view) = LocalUserView::read_person(conn, post.creator_id) {
- recipient_ids.push(parent_user_view.local_user.id);
-
- if do_send_email {
- send_email_to_user(
- &parent_user_view,
- "Reply from",
- "Post Reply",
- &comment.content,
- settings,
- )
- }
- }
- }
- }
- };
- recipient_ids
-}
-
-pub fn send_email_to_user(
- local_user_view: &LocalUserView,
- subject_text: &str,
- body_text: &str,
- comment_content: &str,
- settings: &Settings,
-) {
- if local_user_view.person.banned || !local_user_view.local_user.send_notifications_to_email {
- return;
- }
-
- if let Some(user_email) = &local_user_view.local_user.email {
- let subject = &format!(
- "{} - {} {}",
- subject_text, settings.hostname, local_user_view.person.name,
- );
- let html = &format!(
- "<h1>{}</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
- body_text,
- local_user_view.person.name,
- comment_content,
- settings.get_protocol_and_hostname()
- );
- match send_email(
- subject,
- user_email,
- &local_user_view.person.name,
- html,
- settings,
- ) {
- Ok(_o) => _o,
- Err(e) => error!("{}", e),
- };
- }
-}
-
pub async fn is_mod_or_admin(
pool: &DbPool,
person_id: PersonId,
comment::*,
get_local_user_view_from_jwt,
get_post,
- send_local_notifs,
};
use lemmy_apub::{
activities::{
ConnectionId,
LemmyError,
};
-use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperationCrud};
+use lemmy_websocket::{
+ send::{send_comment_ws_message, send_local_notifs},
+ LemmyContext,
+ UserOperationCrud,
+};
#[async_trait::async_trait(?Send)]
impl PerformCrud for CreateComment {
.map_err(|e| ApiError::err("couldnt_create_comment", e))?;
CreateOrUpdateComment::send(
- &updated_comment,
- &local_user_view.person,
+ &updated_comment.clone().into(),
+ &local_user_view.person.clone().into(),
CreateOrUpdateType::Create,
context,
)
let mentions = scrape_text_for_mentions(&comment_form.content);
let recipient_ids = send_local_notifs(
mentions,
- updated_comment.clone(),
- local_user_view.person.clone(),
- post,
- context.pool(),
+ &updated_comment,
+ &local_user_view.person,
+ &post,
true,
- &context.settings(),
+ context,
)
.await?;
.await?
.map_err(|e| ApiError::err("couldnt_like_comment", e))?;
- let object = PostOrComment::Comment(updated_comment);
+ let object = PostOrComment::Comment(updated_comment.into());
Vote::send(
&object,
- &local_user_view.person,
+ &local_user_view.person.clone().into(),
community_id,
VoteType::Like,
context,
comment::*,
get_local_user_view_from_jwt,
is_mod_or_admin,
- send_local_notifs,
};
use lemmy_apub::activities::deletion::{send_apub_delete, send_apub_remove};
use lemmy_db_schema::{
};
use lemmy_db_views::comment_view::CommentView;
use lemmy_utils::{ApiError, ConnectionId, LemmyError};
-use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperationCrud};
+use lemmy_websocket::{
+ send::{send_comment_ws_message, send_local_notifs},
+ LemmyContext,
+ UserOperationCrud,
+};
#[async_trait::async_trait(?Send)]
impl PerformCrud for DeleteComment {
})
.await??;
send_apub_delete(
- &local_user_view.person,
- &community,
+ &local_user_view.person.clone().into(),
+ &community.clone().into(),
updated_comment.ap_id.clone().into(),
deleted,
context,
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
let recipient_ids = send_local_notifs(
vec![],
- updated_comment,
- local_user_view.person.clone(),
- post,
- context.pool(),
+ &updated_comment,
+ &local_user_view.person,
+ &post,
false,
- &context.settings(),
+ context,
)
.await?;
})
.await??;
send_apub_remove(
- &local_user_view.person,
- &community,
+ &local_user_view.person.clone().into(),
+ &community.into(),
updated_comment.ap_id.clone().into(),
data.reason.clone().unwrap_or_else(|| "".to_string()),
removed,
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
let recipient_ids = send_local_notifs(
vec![],
- updated_comment,
- local_user_view.person.clone(),
- post,
- context.pool(),
+ &updated_comment,
+ &local_user_view.person.clone(),
+ &post,
false,
- &context.settings(),
+ context,
)
.await?;
check_post_deleted_or_removed,
comment::*,
get_local_user_view_from_jwt,
- send_local_notifs,
};
use lemmy_apub::activities::{
comment::create_or_update::CreateOrUpdateComment,
ConnectionId,
LemmyError,
};
-use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperationCrud};
+use lemmy_websocket::{
+ send::{send_comment_ws_message, send_local_notifs},
+ LemmyContext,
+ UserOperationCrud,
+};
#[async_trait::async_trait(?Send)]
impl PerformCrud for EditComment {
// Send the apub update
CreateOrUpdateComment::send(
- &updated_comment,
- &local_user_view.person,
+ &updated_comment.clone().into(),
+ &local_user_view.person.clone().into(),
CreateOrUpdateType::Update,
context,
)
let mentions = scrape_text_for_mentions(&updated_comment_content);
let recipient_ids = send_local_notifs(
mentions,
- updated_comment,
- local_user_view.person.clone(),
- orig_comment.post,
- context.pool(),
+ &updated_comment,
+ &local_user_view.person,
+ &orig_comment.post,
false,
- &context.settings(),
+ context,
)
.await?;
generate_followers_url,
generate_inbox_url,
generate_shared_inbox_url,
+ objects::community::ApubCommunity,
EndpointType,
};
use lemmy_db_schema::{
&data.name,
&context.settings().get_protocol_and_hostname(),
)?;
- let community_actor_id_wrapped = ObjectId::<Community>::new(community_actor_id.clone());
+ let community_actor_id_wrapped = ObjectId::<ApubCommunity>::new(community_actor_id.clone());
let community_dupe = community_actor_id_wrapped.dereference_local(context).await;
if community_dupe.is_ok() {
return Err(ApiError::err_plain("community_already_exists").into());
// Send apub messages
send_apub_delete(
- &local_user_view.person,
- &updated_community,
+ &local_user_view.person.clone().into(),
+ &updated_community.clone().into(),
updated_community.actor_id.clone().into(),
deleted,
context,
// Apub messages
send_apub_remove(
- &local_user_view.person,
- &updated_community,
+ &local_user_view.person.clone().into(),
+ &updated_community.clone().into(),
updated_community.actor_id.clone().into(),
data.reason.clone().unwrap_or_else(|| "".to_string()),
removed,
use crate::PerformCrud;
use actix_web::web::Data;
use lemmy_api_common::{blocking, community::*, get_local_user_view_from_jwt_opt};
-use lemmy_apub::{build_actor_id_from_shortname, fetcher::object_id::ObjectId, EndpointType};
+use lemmy_apub::{
+ build_actor_id_from_shortname,
+ fetcher::object_id::ObjectId,
+ objects::community::ApubCommunity,
+ EndpointType,
+};
use lemmy_db_schema::{
from_opt_str_to_opt_enum,
- source::community::Community,
traits::DeleteableOrRemoveable,
ListingType,
SortType,
let community_actor_id =
build_actor_id_from_shortname(EndpointType::Community, &name, &context.settings())?;
- ObjectId::<Community>::new(community_actor_id)
+ ObjectId::<ApubCommunity>::new(community_actor_id)
.dereference(context, &mut 0)
.await
.map_err(|e| ApiError::err("couldnt_find_community", e))?
.await?
.map_err(|e| ApiError::err("couldnt_update_community", e))?;
- UpdateCommunity::send(&updated_community, &local_user_view.person, context).await?;
+ UpdateCommunity::send(
+ &updated_community.into(),
+ &local_user_view.person.into(),
+ context,
+ )
+ .await?;
let op = UserOperationCrud::EditCommunity;
send_community_ws_message(data.community_id, op, websocket_id, None, context).await
.map_err(|e| ApiError::err("couldnt_create_post", e))?;
CreateOrUpdatePost::send(
- &updated_post,
- &local_user_view.person,
+ &updated_post.clone().into(),
+ &local_user_view.person.clone().into(),
CreateOrUpdateType::Create,
context,
)
}
}
- let object = PostOrComment::Post(Box::new(updated_post));
+ let object = PostOrComment::Post(Box::new(updated_post.into()));
Vote::send(
&object,
- &local_user_view.person,
+ &local_user_view.person.clone().into(),
inserted_post.community_id,
VoteType::Like,
context,
})
.await??;
send_apub_delete(
- &local_user_view.person,
- &community,
+ &local_user_view.person.clone().into(),
+ &community.into(),
updated_post.ap_id.into(),
deleted,
context,
})
.await??;
send_apub_remove(
- &local_user_view.person,
- &community,
+ &local_user_view.person.clone().into(),
+ &community.into(),
updated_post.ap_id.into(),
data.reason.clone().unwrap_or_else(|| "".to_string()),
removed,
// Send apub update
CreateOrUpdatePost::send(
- &updated_post,
- &local_user_view.person,
+ &updated_post.into(),
+ &local_user_view.person.clone().into(),
CreateOrUpdateType::Update,
context,
)
check_person_block,
get_local_user_view_from_jwt,
person::{CreatePrivateMessage, PrivateMessageResponse},
- send_email_to_user,
};
use lemmy_apub::{
activities::{
};
use lemmy_db_views::local_user_view::LocalUserView;
use lemmy_utils::{utils::remove_slurs, ApiError, ConnectionId, LemmyError};
-use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
+use lemmy_websocket::{
+ send::{send_email_to_user, send_pm_ws_message},
+ LemmyContext,
+ UserOperationCrud,
+};
#[async_trait::async_trait(?Send)]
impl PerformCrud for CreatePrivateMessage {
.map_err(|e| ApiError::err("couldnt_create_private_message", e))?;
CreateOrUpdatePrivateMessage::send(
- &updated_private_message,
- &local_user_view.person,
+ &updated_private_message.into(),
+ &local_user_view.person.into(),
CreateOrUpdateType::Create,
context,
)
// Send the apub update
if data.deleted {
DeletePrivateMessageApub::send(
- &local_user_view.person,
- &updated_private_message.blank_out_deleted_or_removed_info(),
+ &local_user_view.person.into(),
+ &updated_private_message
+ .blank_out_deleted_or_removed_info()
+ .into(),
context,
)
.await?;
} else {
- UndoDeletePrivateMessage::send(&local_user_view.person, &updated_private_message, context)
- .await?;
+ UndoDeletePrivateMessage::send(
+ &local_user_view.person.into(),
+ &updated_private_message.into(),
+ context,
+ )
+ .await?;
}
let op = UserOperationCrud::DeletePrivateMessage;
// Send the apub update
CreateOrUpdatePrivateMessage::send(
- &updated_private_message,
- &local_user_view.person,
+ &updated_private_message.into(),
+ &local_user_view.person.into(),
CreateOrUpdateType::Update,
context,
)
use crate::PerformCrud;
use actix_web::web::Data;
use lemmy_api_common::{blocking, get_local_user_view_from_jwt_opt, person::*};
-use lemmy_apub::{build_actor_id_from_shortname, fetcher::object_id::ObjectId, EndpointType};
-use lemmy_db_schema::{from_opt_str_to_opt_enum, source::person::Person, SortType};
+use lemmy_apub::{
+ build_actor_id_from_shortname,
+ fetcher::object_id::ObjectId,
+ objects::person::ApubPerson,
+ EndpointType,
+};
+use lemmy_db_schema::{from_opt_str_to_opt_enum, SortType};
use lemmy_db_views::{comment_view::CommentQueryBuilder, post_view::PostQueryBuilder};
use lemmy_db_views_actor::{
community_moderator_view::CommunityModeratorView,
let actor_id =
build_actor_id_from_shortname(EndpointType::Person, &name, &context.settings())?;
- let person = ObjectId::<Person>::new(actor_id)
+ let person = ObjectId::<ApubPerson>::new(actor_id)
.dereference(context, &mut 0)
.await;
person
},
context::lemmy_context,
fetcher::object_id::ObjectId,
- objects::{comment::Note, FromApub, ToApub},
+ objects::{
+ comment::{ApubComment, Note},
+ community::ApubCommunity,
+ person::ApubPerson,
+ },
};
use activitystreams::{base::AnyBase, link::Mention, primitives::OneOrMany, unparsed::Unparsed};
use lemmy_api_common::{blocking, check_post_deleted_or_removed};
use lemmy_apub_lib::{
data::Data,
- traits::{ActivityFields, ActivityHandler, ActorType},
+ traits::{ActivityFields, ActivityHandler, ActorType, FromApub, ToApub},
values::PublicUrl,
verify::verify_domains_match,
};
use lemmy_db_schema::{
- source::{comment::Comment, community::Community, person::Person, post::Post},
+ source::{community::Community, post::Post},
traits::Crud,
};
use lemmy_utils::LemmyError;
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct CreateOrUpdateComment {
- actor: ObjectId<Person>,
+ actor: ObjectId<ApubPerson>,
to: [PublicUrl; 1],
object: Note,
cc: Vec<Url>,
impl CreateOrUpdateComment {
pub async fn send(
- comment: &Comment,
- actor: &Person,
+ comment: &ApubComment,
+ actor: &ApubPerson,
kind: CreateOrUpdateType,
context: &LemmyContext,
) -> Result<(), LemmyError> {
let post_id = comment.post_id;
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
let community_id = post.community_id;
- let community = blocking(context.pool(), move |conn| {
+ let community: ApubCommunity = blocking(context.pool(), move |conn| {
Community::read(conn, community_id)
})
- .await??;
+ .await??
+ .into();
let id = generate_activity_id(
kind.clone(),
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let comment =
- Comment::from_apub(&self.object, context, self.actor.inner(), request_counter).await?;
+ ApubComment::from_apub(&self.object, context, self.actor.inner(), request_counter).await?;
let recipients = get_notif_recipients(&self.actor, &comment, context, request_counter).await?;
let notif_type = match self.kind {
CreateOrUpdateType::Create => UserOperationCrud::CreateComment,
-use crate::fetcher::object_id::ObjectId;
+use crate::{
+ fetcher::object_id::ObjectId,
+ objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson},
+};
use activitystreams::{
base::BaseExt,
link::{LinkExt, Mention},
};
use anyhow::anyhow;
use itertools::Itertools;
-use lemmy_api_common::{blocking, send_local_notifs};
+use lemmy_api_common::blocking;
use lemmy_apub_lib::{traits::ActorType, webfinger::WebfingerResponse};
use lemmy_db_schema::{
newtypes::LocalUserId,
- source::{comment::Comment, community::Community, person::Person, post::Post},
+ source::{comment::Comment, person::Person, post::Post},
traits::Crud,
DbPool,
};
utils::{scrape_text_for_mentions, MentionData},
LemmyError,
};
-use lemmy_websocket::LemmyContext;
+use lemmy_websocket::{send::send_local_notifs, LemmyContext};
use log::debug;
use url::Url;
pub mod create_or_update;
async fn get_notif_recipients(
- actor: &ObjectId<Person>,
+ actor: &ObjectId<ApubPerson>,
comment: &Comment,
context: &LemmyContext,
request_counter: &mut i32,
// anyway.
// TODO: for compatibility with other projects, it would be much better to read this from cc or tags
let mentions = scrape_text_for_mentions(&comment.content);
- send_local_notifs(
- mentions,
- comment.clone(),
- actor,
- post,
- context.pool(),
- true,
- &context.settings(),
- )
- .await
+ send_local_notifs(mentions, comment, &*actor, &post, true, context).await
}
pub struct MentionsAndAddresses {
/// and mention tags, so they know where to be sent to.
/// Addresses are the persons / addresses that go in the cc field.
pub async fn collect_non_local_mentions(
- comment: &Comment,
- community: &Community,
+ comment: &ApubComment,
+ community: &ApubCommunity,
context: &LemmyContext,
) -> Result<MentionsAndAddresses, LemmyError> {
let parent_creator = get_comment_parent_creator(context.pool(), comment).await?;
- let mut addressed_ccs = vec![community.actor_id(), parent_creator.actor_id()];
+ let mut addressed_ccs: Vec<Url> = vec![community.actor_id(), parent_creator.actor_id()];
// Note: dont include community inbox here, as we send to it separately with `send_to_community()`
let mut inboxes = vec![parent_creator.shared_inbox_or_inbox_url()];
for mention in &mentions {
// TODO should it be fetching it every time?
if let Ok(actor_id) = fetch_webfinger_url(mention, context).await {
- let actor_id: ObjectId<Person> = ObjectId::new(actor_id);
+ let actor_id: ObjectId<ApubPerson> = ObjectId::new(actor_id);
debug!("mention actor_id: {}", actor_id);
- addressed_ccs.push(actor_id.to_owned().to_string().parse()?);
+ addressed_ccs.push(actor_id.to_string().parse()?);
let mention_person = actor_id.dereference(context, &mut 0).await?;
inboxes.push(mention_person.shared_inbox_or_inbox_url());
async fn get_comment_parent_creator(
pool: &DbPool,
comment: &Comment,
-) -> Result<Person, LemmyError> {
+) -> Result<ApubPerson, LemmyError> {
let parent_creator_id = if let Some(parent_comment_id) = comment.parent_id {
let parent_comment =
blocking(pool, move |conn| Comment::read(conn, parent_comment_id)).await??;
let parent_post = blocking(pool, move |conn| Post::read(conn, parent_post_id)).await??;
parent_post.creator_id
};
- Ok(blocking(pool, move |conn| Person::read(conn, parent_creator_id)).await??)
+ Ok(
+ blocking(pool, move |conn| Person::read(conn, parent_creator_id))
+ .await??
+ .into(),
+ )
}
/// Turns a person id like `@name@example.com` into an apub ID, like `https://example.com/user/name`,
context::lemmy_context,
fetcher::object_id::ObjectId,
generate_moderators_url,
+ objects::{community::ApubCommunity, person::ApubPerson},
};
use activitystreams::{
activity::kind::AddType,
values::PublicUrl,
};
use lemmy_db_schema::{
- source::{
- community::{Community, CommunityModerator, CommunityModeratorForm},
- person::Person,
- },
+ source::community::{CommunityModerator, CommunityModeratorForm},
traits::Joinable,
};
use lemmy_utils::LemmyError;
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct AddMod {
- actor: ObjectId<Person>,
+ actor: ObjectId<ApubPerson>,
to: [PublicUrl; 1],
- object: ObjectId<Person>,
+ object: ObjectId<ApubPerson>,
target: Url,
- cc: [ObjectId<Community>; 1],
+ cc: [ObjectId<ApubCommunity>; 1],
#[serde(rename = "type")]
kind: AddType,
id: Url,
impl AddMod {
pub async fn send(
- community: &Community,
- added_mod: &Person,
- actor: &Person,
+ community: &ApubCommunity,
+ added_mod: &ApubPerson,
+ actor: &ApubPerson,
context: &LemmyContext,
) -> Result<(), LemmyError> {
let id = generate_activity_id(
) -> Result<(), LemmyError> {
verify_activity(self, &context.settings())?;
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
- verify_mod_action(&self.actor, self.cc[0].clone(), context, request_counter).await?;
+ verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?;
verify_add_remove_moderator_target(&self.target, &self.cc[0])?;
Ok(())
}
fetcher::object_id::ObjectId,
http::is_activity_already_known,
insert_activity,
+ objects::community::ApubCommunity,
send_lemmy_activity,
CommunityType,
};
traits::{ActivityFields, ActivityHandler, ActorType},
values::PublicUrl,
};
-use lemmy_db_schema::source::community::Community;
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct AnnounceActivity {
- actor: ObjectId<Community>,
+ actor: ObjectId<ApubCommunity>,
to: [PublicUrl; 1],
object: AnnouncableActivities,
cc: Vec<Url>,
impl AnnounceActivity {
pub async fn send(
object: AnnouncableActivities,
- community: &Community,
+ community: &ApubCommunity,
additional_inboxes: Vec<Url>,
context: &LemmyContext,
) -> Result<(), LemmyError> {
},
context::lemmy_context,
fetcher::object_id::ObjectId,
+ objects::{community::ApubCommunity, person::ApubPerson},
};
use activitystreams::{
activity::kind::BlockType,
values::PublicUrl,
};
use lemmy_db_schema::{
- source::{
- community::{
- Community,
- CommunityFollower,
- CommunityFollowerForm,
- CommunityPersonBan,
- CommunityPersonBanForm,
- },
- person::Person,
+ source::community::{
+ CommunityFollower,
+ CommunityFollowerForm,
+ CommunityPersonBan,
+ CommunityPersonBanForm,
},
traits::{Bannable, Followable},
};
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct BlockUserFromCommunity {
- actor: ObjectId<Person>,
+ actor: ObjectId<ApubPerson>,
to: [PublicUrl; 1],
- pub(in crate::activities::community) object: ObjectId<Person>,
- cc: [ObjectId<Community>; 1],
+ pub(in crate::activities::community) object: ObjectId<ApubPerson>,
+ cc: [ObjectId<ApubCommunity>; 1],
#[serde(rename = "type")]
kind: BlockType,
id: Url,
impl BlockUserFromCommunity {
pub(in crate::activities::community) fn new(
- community: &Community,
- target: &Person,
- actor: &Person,
+ community: &ApubCommunity,
+ target: &ApubPerson,
+ actor: &ApubPerson,
context: &LemmyContext,
) -> Result<BlockUserFromCommunity, LemmyError> {
Ok(BlockUserFromCommunity {
}
pub async fn send(
- community: &Community,
- target: &Person,
- actor: &Person,
+ community: &ApubCommunity,
+ target: &ApubPerson,
+ actor: &ApubPerson,
context: &LemmyContext,
) -> Result<(), LemmyError> {
let block = BlockUserFromCommunity::new(community, target, actor, context)?;
) -> Result<(), LemmyError> {
verify_activity(self, &context.settings())?;
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
- verify_mod_action(&self.actor, self.cc[0].clone(), context, request_counter).await?;
+ verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?;
Ok(())
}
activities::community::announce::{AnnouncableActivities, AnnounceActivity},
check_is_apub_id_valid,
insert_activity,
+ objects::community::ApubCommunity,
send_lemmy_activity,
CommunityType,
};
use itertools::Itertools;
use lemmy_apub_lib::traits::ActorType;
-use lemmy_db_schema::source::community::Community;
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
use url::Url;
pub mod update;
async fn list_community_follower_inboxes(
- community: &Community,
+ community: &ApubCommunity,
additional_inboxes: Vec<Url>,
context: &LemmyContext,
) -> Result<Vec<Url>, LemmyError> {
activity: AnnouncableActivities,
activity_id: &Url,
actor: &T,
- community: &Community,
+ community: &ApubCommunity,
additional_inboxes: Vec<Url>,
context: &LemmyContext,
) -> Result<(), LemmyError> {
context::lemmy_context,
fetcher::object_id::ObjectId,
generate_moderators_url,
+ objects::{community::ApubCommunity, person::ApubPerson},
};
use activitystreams::{
activity::kind::RemoveType,
values::PublicUrl,
};
use lemmy_db_schema::{
- source::{
- community::{Community, CommunityModerator, CommunityModeratorForm},
- person::Person,
- },
+ source::community::{CommunityModerator, CommunityModeratorForm},
traits::Joinable,
};
use lemmy_utils::LemmyError;
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct RemoveMod {
- actor: ObjectId<Person>,
+ actor: ObjectId<ApubPerson>,
to: [PublicUrl; 1],
- pub(in crate::activities) object: ObjectId<Person>,
- cc: [ObjectId<Community>; 1],
+ pub(in crate::activities) object: ObjectId<ApubPerson>,
+ cc: [ObjectId<ApubCommunity>; 1],
#[serde(rename = "type")]
kind: RemoveType,
// if target is set, this is means remove mod from community
impl RemoveMod {
pub async fn send(
- community: &Community,
- removed_mod: &Person,
- actor: &Person,
+ community: &ApubCommunity,
+ removed_mod: &ApubPerson,
+ actor: &ApubPerson,
context: &LemmyContext,
) -> Result<(), LemmyError> {
let id = generate_activity_id(
verify_activity(self, &context.settings())?;
if let Some(target) = &self.target {
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
- verify_mod_action(&self.actor, self.cc[0].clone(), context, request_counter).await?;
+ verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?;
verify_add_remove_moderator_target(target, &self.cc[0])?;
} else {
verify_delete_activity(
},
context::lemmy_context,
fetcher::object_id::ObjectId,
+ objects::{community::ApubCommunity, person::ApubPerson},
};
use activitystreams::{
activity::kind::UndoType,
values::PublicUrl,
};
use lemmy_db_schema::{
- source::{
- community::{Community, CommunityPersonBan, CommunityPersonBanForm},
- person::Person,
- },
+ source::community::{CommunityPersonBan, CommunityPersonBanForm},
traits::Bannable,
};
use lemmy_utils::LemmyError;
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct UndoBlockUserFromCommunity {
- actor: ObjectId<Person>,
+ actor: ObjectId<ApubPerson>,
to: [PublicUrl; 1],
object: BlockUserFromCommunity,
- cc: [ObjectId<Community>; 1],
+ cc: [ObjectId<ApubCommunity>; 1],
#[serde(rename = "type")]
kind: UndoType,
id: Url,
impl UndoBlockUserFromCommunity {
pub async fn send(
- community: &Community,
- target: &Person,
- actor: &Person,
+ community: &ApubCommunity,
+ target: &ApubPerson,
+ actor: &ApubPerson,
context: &LemmyContext,
) -> Result<(), LemmyError> {
let block = BlockUserFromCommunity::new(community, target, actor, context)?;
) -> Result<(), LemmyError> {
verify_activity(self, &context.settings())?;
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
- verify_mod_action(&self.actor, self.cc[0].clone(), context, request_counter).await?;
+ verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?;
self.object.verify(context, request_counter).await?;
Ok(())
}
},
context::lemmy_context,
fetcher::object_id::ObjectId,
- objects::{community::Group, ToApub},
+ objects::{
+ community::{ApubCommunity, Group},
+ person::ApubPerson,
+ },
};
use activitystreams::{
activity::kind::UpdateType,
use lemmy_api_common::blocking;
use lemmy_apub_lib::{
data::Data,
- traits::{ActivityFields, ActivityHandler, ActorType},
+ traits::{ActivityFields, ActivityHandler, ActorType, ToApub},
values::PublicUrl,
};
use lemmy_db_schema::{
- source::{
- community::{Community, CommunityForm},
- person::Person,
- },
+ source::community::{Community, CommunityForm},
traits::Crud,
};
use lemmy_utils::LemmyError;
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct UpdateCommunity {
- actor: ObjectId<Person>,
+ actor: ObjectId<ApubPerson>,
to: [PublicUrl; 1],
// TODO: would be nice to use a separate struct here, which only contains the fields updated here
object: Group,
- cc: [ObjectId<Community>; 1],
+ cc: [ObjectId<ApubCommunity>; 1],
#[serde(rename = "type")]
kind: UpdateType,
id: Url,
impl UpdateCommunity {
pub async fn send(
- community: &Community,
- actor: &Person,
+ community: &ApubCommunity,
+ actor: &ApubPerson,
context: &LemmyContext,
) -> Result<(), LemmyError> {
let id = generate_activity_id(
) -> Result<(), LemmyError> {
verify_activity(self, &context.settings())?;
verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
- verify_mod_action(&self.actor, self.cc[0].clone(), context, request_counter).await?;
+ verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?;
Ok(())
}
},
context::lemmy_context,
fetcher::object_id::ObjectId,
+ objects::{community::ApubCommunity, person::ApubPerson},
};
use activitystreams::{
activity::kind::DeleteType,
ModRemovePost,
ModRemovePostForm,
},
- person::Person,
post::Post,
},
traits::Crud,
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct Delete {
- actor: ObjectId<Person>,
+ actor: ObjectId<ApubPerson>,
to: [PublicUrl; 1],
pub(in crate::activities::deletion) object: Url,
- pub(in crate::activities::deletion) cc: [ObjectId<Community>; 1],
+ pub(in crate::activities::deletion) cc: [ObjectId<ApubCommunity>; 1],
#[serde(rename = "type")]
kind: DeleteType,
/// If summary is present, this is a mod action (Remove in Lemmy terms). Otherwise, its a user
impl Delete {
pub(in crate::activities::deletion) fn new(
- actor: &Person,
- community: &Community,
+ actor: &ApubPerson,
+ community: &ApubCommunity,
object_id: Url,
summary: Option<String>,
context: &LemmyContext,
})
}
pub(in crate::activities::deletion) async fn send(
- actor: &Person,
- community: &Community,
+ actor: &ApubPerson,
+ community: &ApubCommunity,
object_id: Url,
summary: Option<String>,
context: &LemmyContext,
}
pub(in crate::activities) async fn receive_remove_action(
- actor: &ObjectId<Person>,
+ actor: &ObjectId<ApubPerson>,
object: &Url,
reason: Option<String>,
context: &LemmyContext,
verify_person_in_community,
},
fetcher::object_id::ObjectId,
+ objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost},
};
-use diesel::PgConnection;
use lemmy_api_common::blocking;
use lemmy_apub_lib::{
traits::{ActivityFields, ActorType, ApubObject},
verify::verify_domains_match,
};
-use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post};
+use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post};
use lemmy_utils::LemmyError;
use lemmy_websocket::{
send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message},
pub mod undo_delete;
pub async fn send_apub_delete(
- actor: &Person,
- community: &Community,
+ actor: &ApubPerson,
+ community: &ApubCommunity,
object_id: Url,
deleted: bool,
context: &LemmyContext,
// TODO: remove reason is actually optional in lemmy. we set an empty string in that case, but its
// ugly
pub async fn send_apub_remove(
- actor: &Person,
- community: &Community,
+ actor: &ApubPerson,
+ community: &ApubCommunity,
object_id: Url,
reason: String,
removed: bool,
}
pub enum DeletableObjects {
- Community(Box<Community>),
- Comment(Box<Comment>),
- Post(Box<Post>),
+ Community(Box<ApubCommunity>),
+ Comment(Box<ApubComment>),
+ Post(Box<ApubPost>),
}
impl DeletableObjects {
ap_id: &Url,
context: &LemmyContext,
) -> Result<DeletableObjects, LemmyError> {
- if let Some(c) =
- DeletableObjects::read_type_from_db::<Community>(ap_id.clone(), context).await?
- {
+ if let Some(c) = ApubCommunity::read_from_apub_id(ap_id.clone(), context).await? {
return Ok(DeletableObjects::Community(Box::new(c)));
}
- if let Some(p) = DeletableObjects::read_type_from_db::<Post>(ap_id.clone(), context).await? {
+ if let Some(p) = ApubPost::read_from_apub_id(ap_id.clone(), context).await? {
return Ok(DeletableObjects::Post(Box::new(p)));
}
- if let Some(c) = DeletableObjects::read_type_from_db::<Comment>(ap_id.clone(), context).await? {
+ if let Some(c) = ApubComment::read_from_apub_id(ap_id.clone(), context).await? {
return Ok(DeletableObjects::Comment(Box::new(c)));
}
Err(diesel::NotFound.into())
}
-
- // TODO: a method like this should be provided by fetcher module
- async fn read_type_from_db<Type>(
- ap_id: Url,
- context: &LemmyContext,
- ) -> Result<Option<Type>, LemmyError>
- where
- Type: ApubObject<DataType = PgConnection> + Send + 'static,
- {
- blocking(context.pool(), move |conn| {
- Type::read_from_apub_id(conn, ap_id)
- })
- .await?
- }
}
pub(in crate::activities) async fn verify_delete_activity(
object: &Url,
activity: &dyn ActivityFields,
- community_id: &ObjectId<Community>,
+ community_id: &ObjectId<ApubCommunity>,
is_mod_action: bool,
context: &LemmyContext,
request_counter: &mut i32,
// community deletion is always a mod (or admin) action
verify_mod_action(
&actor,
- ObjectId::new(c.actor_id()),
+ &ObjectId::new(c.actor_id()),
context,
request_counter,
)
DeletableObjects::Post(p) => {
verify_delete_activity_post_or_comment(
activity,
- &p.ap_id.into(),
+ &p.ap_id.clone().into(),
community_id,
is_mod_action,
context,
DeletableObjects::Comment(c) => {
verify_delete_activity_post_or_comment(
activity,
- &c.ap_id.into(),
+ &c.ap_id.clone().into(),
community_id,
is_mod_action,
context,
async fn verify_delete_activity_post_or_comment(
activity: &dyn ActivityFields,
object_id: &Url,
- community_id: &ObjectId<Community>,
+ community_id: &ObjectId<ApubCommunity>,
is_mod_action: bool,
context: &LemmyContext,
request_counter: &mut i32,
let actor = ObjectId::new(activity.actor().clone());
verify_person_in_community(&actor, community_id, context, request_counter).await?;
if is_mod_action {
- verify_mod_action(&actor, community_id.clone(), context, request_counter).await?;
+ verify_mod_action(&actor, community_id, context, request_counter).await?;
} else {
// domain of post ap_id and post.creator ap_id are identical, so we just check the former
verify_domains_match(activity.actor(), object_id)?;
/// because of the mod log
async fn receive_delete_action(
object: &Url,
- actor: &ObjectId<Person>,
+ actor: &ObjectId<ApubPerson>,
ws_messages: WebsocketMessages,
deleted: bool,
context: &LemmyContext,
},
context::lemmy_context,
fetcher::object_id::ObjectId,
+ objects::{community::ApubCommunity, person::ApubPerson},
};
use activitystreams::{
activity::kind::UndoType,
traits::{ActivityFields, ActivityHandler, ActorType},
values::PublicUrl,
};
-use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post};
+use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post};
use lemmy_utils::LemmyError;
use lemmy_websocket::{
send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message},
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct UndoDelete {
- actor: ObjectId<Person>,
+ actor: ObjectId<ApubPerson>,
to: [PublicUrl; 1],
object: Delete,
- cc: [ObjectId<Community>; 1],
+ cc: [ObjectId<ApubCommunity>; 1],
#[serde(rename = "type")]
kind: UndoType,
id: Url,
impl UndoDelete {
pub(in crate::activities::deletion) async fn send(
- actor: &Person,
- community: &Community,
+ actor: &ApubPerson,
+ community: &ApubCommunity,
object_id: Url,
summary: Option<String>,
context: &LemmyContext,
},
context::lemmy_context,
fetcher::object_id::ObjectId,
+ objects::{community::ApubCommunity, person::ApubPerson},
send_lemmy_activity,
};
use activitystreams::{
traits::{ActivityFields, ActivityHandler, ActorType},
verify::verify_urls_match,
};
-use lemmy_db_schema::{
- source::{
- community::{Community, CommunityFollower},
- person::Person,
- },
- traits::Followable,
-};
+use lemmy_db_schema::{source::community::CommunityFollower, traits::Followable};
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct AcceptFollowCommunity {
- actor: ObjectId<Community>,
- to: ObjectId<Person>,
+ actor: ObjectId<ApubCommunity>,
+ to: ObjectId<ApubPerson>,
object: FollowCommunity,
#[serde(rename = "type")]
kind: AcceptType,
context: lemmy_context(),
unparsed: Default::default(),
};
- let inbox = vec![person.inbox_url.into()];
+ let inbox = vec![person.inbox_url()];
send_lemmy_activity(context, &accept, &accept.id, &community, inbox, true).await
}
}
},
context::lemmy_context,
fetcher::object_id::ObjectId,
+ objects::{community::ApubCommunity, person::ApubPerson},
send_lemmy_activity,
};
use activitystreams::{
verify::verify_urls_match,
};
use lemmy_db_schema::{
- source::{
- community::{Community, CommunityFollower, CommunityFollowerForm},
- person::Person,
- },
+ source::community::{CommunityFollower, CommunityFollowerForm},
traits::Followable,
};
use lemmy_utils::LemmyError;
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct FollowCommunity {
- pub(in crate::activities::following) actor: ObjectId<Person>,
+ pub(in crate::activities::following) actor: ObjectId<ApubPerson>,
// TODO: is there any reason to put the same community id twice, in to and object?
- pub(in crate::activities::following) to: ObjectId<Community>,
- pub(in crate::activities::following) object: ObjectId<Community>,
+ pub(in crate::activities::following) to: ObjectId<ApubCommunity>,
+ pub(in crate::activities::following) object: ObjectId<ApubCommunity>,
#[serde(rename = "type")]
kind: FollowType,
id: Url,
impl FollowCommunity {
pub(in crate::activities::following) fn new(
- actor: &Person,
- community: &Community,
+ actor: &ApubPerson,
+ community: &ApubCommunity,
context: &LemmyContext,
) -> Result<FollowCommunity, LemmyError> {
Ok(FollowCommunity {
})
}
pub async fn send(
- actor: &Person,
- community: &Community,
+ actor: &ApubPerson,
+ community: &ApubCommunity,
context: &LemmyContext,
) -> Result<(), LemmyError> {
let community_follower_form = CommunityFollowerForm {
},
context::lemmy_context,
fetcher::object_id::ObjectId,
+ objects::{community::ApubCommunity, person::ApubPerson},
send_lemmy_activity,
};
use activitystreams::{
verify::verify_urls_match,
};
use lemmy_db_schema::{
- source::{
- community::{Community, CommunityFollower, CommunityFollowerForm},
- person::Person,
- },
+ source::community::{CommunityFollower, CommunityFollowerForm},
traits::Followable,
};
use lemmy_utils::LemmyError;
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct UndoFollowCommunity {
- actor: ObjectId<Person>,
- to: ObjectId<Community>,
+ actor: ObjectId<ApubPerson>,
+ to: ObjectId<ApubCommunity>,
object: FollowCommunity,
#[serde(rename = "type")]
kind: UndoType,
impl UndoFollowCommunity {
pub async fn send(
- actor: &Person,
- community: &Community,
+ actor: &ApubPerson,
+ community: &ApubCommunity,
context: &LemmyContext,
) -> Result<(), LemmyError> {
let object = FollowCommunity::new(actor, community, context)?;
check_is_apub_id_valid,
fetcher::object_id::ObjectId,
generate_moderators_url,
+ objects::{community::ApubCommunity, person::ApubPerson},
};
use anyhow::anyhow;
use lemmy_api_common::blocking;
use lemmy_apub_lib::{traits::ActivityFields, verify::verify_domains_match};
-use lemmy_db_schema::source::{community::Community, person::Person};
+use lemmy_db_schema::source::community::Community;
use lemmy_db_views_actor::community_view::CommunityView;
use lemmy_utils::{settings::structs::Settings, LemmyError};
use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
+use std::ops::Deref;
use strum_macros::ToString;
use url::{ParseError, Url};
use uuid::Uuid;
/// Checks that the specified Url actually identifies a Person (by fetching it), and that the person
/// doesn't have a site ban.
async fn verify_person(
- person_id: &ObjectId<Person>,
+ person_id: &ObjectId<ApubPerson>,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
cc: &[Url],
context: &LemmyContext,
request_counter: &mut i32,
-) -> Result<Community, LemmyError> {
+) -> Result<ApubCommunity, LemmyError> {
let mut cc_iter = cc.iter();
loop {
if let Some(cid) = cc_iter.next() {
/// Fetches the person and community to verify their type, then checks if person is banned from site
/// or community.
pub(crate) async fn verify_person_in_community(
- person_id: &ObjectId<Person>,
- community_id: &ObjectId<Community>,
+ person_id: &ObjectId<ApubPerson>,
+ community_id: &ObjectId<ApubCommunity>,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let community = community_id.dereference(context, request_counter).await?;
let person = person_id.dereference(context, request_counter).await?;
- check_community_or_site_ban(&person, community.id, context.pool()).await
+ check_community_or_site_ban(person.deref(), community.id, context.pool()).await
}
/// Simply check that the url actually refers to a valid group.
async fn verify_community(
- community_id: &ObjectId<Community>,
+ community_id: &ObjectId<ApubCommunity>,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
/// 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: &ObjectId<Person>,
- community_id: ObjectId<Community>,
+ actor_id: &ObjectId<ApubPerson>,
+ community_id: &ObjectId<ApubCommunity>,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
/// /c/community/moderators. Any different values are unsupported.
fn verify_add_remove_moderator_target(
target: &Url,
- community: &ObjectId<Community>,
+ community: &ObjectId<ApubCommunity>,
) -> Result<(), LemmyError> {
if target != &generate_moderators_url(&community.clone().into())?.into_inner() {
return Err(anyhow!("Unkown target url").into());
},
context::lemmy_context,
fetcher::object_id::ObjectId,
- objects::{post::Page, FromApub, ToApub},
+ objects::{
+ community::ApubCommunity,
+ person::ApubPerson,
+ post::{ApubPost, Page},
+ },
};
use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed};
use anyhow::anyhow;
use lemmy_api_common::blocking;
use lemmy_apub_lib::{
data::Data,
- traits::{ActivityFields, ActivityHandler, ActorType},
+ traits::{ActivityFields, ActivityHandler, ActorType, FromApub, ToApub},
values::PublicUrl,
verify::{verify_domains_match, verify_urls_match},
};
-use lemmy_db_schema::{
- source::{community::Community, person::Person, post::Post},
- traits::Crud,
-};
+use lemmy_db_schema::{source::community::Community, traits::Crud};
use lemmy_utils::LemmyError;
use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct CreateOrUpdatePost {
- actor: ObjectId<Person>,
+ actor: ObjectId<ApubPerson>,
to: [PublicUrl; 1],
object: Page,
- cc: [ObjectId<Community>; 1],
+ cc: [ObjectId<ApubCommunity>; 1],
#[serde(rename = "type")]
kind: CreateOrUpdateType,
id: Url,
impl CreateOrUpdatePost {
pub async fn send(
- post: &Post,
- actor: &Person,
+ post: &ApubPost,
+ actor: &ApubPerson,
kind: CreateOrUpdateType,
context: &LemmyContext,
) -> Result<(), LemmyError> {
let community_id = post.community_id;
- let community = blocking(context.pool(), move |conn| {
+ let community: ApubCommunity = blocking(context.pool(), move |conn| {
Community::read(conn, community_id)
})
- .await??;
+ .await??
+ .into();
let id = generate_activity_id(
kind.clone(),
CreateOrUpdateType::Update => {
let is_mod_action = self.object.is_mod_action(context).await?;
if is_mod_action {
- verify_mod_action(&self.actor, self.cc[0].clone(), context, request_counter).await?;
+ verify_mod_action(&self.actor, &self.cc[0], context, request_counter).await?;
} else {
verify_domains_match(self.actor.inner(), self.object.id_unchecked())?;
verify_urls_match(self.actor(), self.object.attributed_to.inner())?;
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let actor = self.actor.dereference(context, request_counter).await?;
- let post = Post::from_apub(&self.object, context, &actor.actor_id(), request_counter).await?;
+ let post =
+ ApubPost::from_apub(&self.object, context, &actor.actor_id(), request_counter).await?;
let notif_type = match self.kind {
CreateOrUpdateType::Create => UserOperationCrud::CreatePost,
activities::{generate_activity_id, verify_activity, verify_person, CreateOrUpdateType},
context::lemmy_context,
fetcher::object_id::ObjectId,
- objects::{private_message::Note, FromApub, ToApub},
+ objects::{
+ person::ApubPerson,
+ private_message::{ApubPrivateMessage, Note},
+ },
send_lemmy_activity,
};
use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed};
use lemmy_api_common::blocking;
use lemmy_apub_lib::{
data::Data,
- traits::{ActivityFields, ActivityHandler, ActorType},
+ traits::{ActivityFields, ActivityHandler, ActorType, FromApub, ToApub},
verify::verify_domains_match,
};
-use lemmy_db_schema::{
- source::{person::Person, private_message::PrivateMessage},
- traits::Crud,
-};
+use lemmy_db_schema::{source::person::Person, traits::Crud};
use lemmy_utils::LemmyError;
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
use serde::{Deserialize, Serialize};
#[serde(rename = "@context")]
pub context: OneOrMany<AnyBase>,
id: Url,
- actor: ObjectId<Person>,
- to: ObjectId<Person>,
+ actor: ObjectId<ApubPerson>,
+ to: ObjectId<ApubPerson>,
object: Note,
#[serde(rename = "type")]
kind: CreateOrUpdateType,
impl CreateOrUpdatePrivateMessage {
pub async fn send(
- private_message: &PrivateMessage,
- actor: &Person,
+ private_message: &ApubPrivateMessage,
+ actor: &ApubPerson,
kind: CreateOrUpdateType,
context: &LemmyContext,
) -> Result<(), LemmyError> {
let recipient_id = private_message.recipient_id;
- let recipient =
- blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??;
+ let recipient: ApubPerson =
+ blocking(context.pool(), move |conn| Person::read(conn, recipient_id))
+ .await??
+ .into();
let id = generate_activity_id(
kind.clone(),
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let private_message =
- PrivateMessage::from_apub(&self.object, context, self.actor.inner(), request_counter).await?;
+ ApubPrivateMessage::from_apub(&self.object, context, self.actor.inner(), request_counter)
+ .await?;
let notif_type = match self.kind {
CreateOrUpdateType::Create => UserOperationCrud::CreatePrivateMessage,
activities::{generate_activity_id, verify_activity, verify_person},
context::lemmy_context,
fetcher::object_id::ObjectId,
+ objects::{person::ApubPerson, private_message::ApubPrivateMessage},
send_lemmy_activity,
};
use activitystreams::{
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct DeletePrivateMessage {
- actor: ObjectId<Person>,
- to: ObjectId<Person>,
- pub(in crate::activities::private_message) object: ObjectId<PrivateMessage>,
+ actor: ObjectId<ApubPerson>,
+ to: ObjectId<ApubPerson>,
+ pub(in crate::activities::private_message) object: ObjectId<ApubPrivateMessage>,
#[serde(rename = "type")]
kind: DeleteType,
id: Url,
impl DeletePrivateMessage {
pub(in crate::activities::private_message) fn new(
- actor: &Person,
+ actor: &ApubPerson,
pm: &PrivateMessage,
context: &LemmyContext,
) -> Result<DeletePrivateMessage, LemmyError> {
})
}
pub async fn send(
- actor: &Person,
- pm: &PrivateMessage,
+ actor: &ApubPerson,
+ pm: &ApubPrivateMessage,
context: &LemmyContext,
) -> Result<(), LemmyError> {
let delete = DeletePrivateMessage::new(actor, pm, context)?;
let delete_id = delete.id.clone();
let recipient_id = pm.recipient_id;
- let recipient =
- blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??;
+ let recipient: ApubPerson =
+ blocking(context.pool(), move |conn| Person::read(conn, recipient_id))
+ .await??
+ .into();
let inbox = vec![recipient.shared_inbox_or_inbox_url()];
send_lemmy_activity(context, &delete, &delete_id, actor, inbox, true).await
}
},
context::lemmy_context,
fetcher::object_id::ObjectId,
+ objects::{person::ApubPerson, private_message::ApubPrivateMessage},
send_lemmy_activity,
};
use activitystreams::{
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct UndoDeletePrivateMessage {
- actor: ObjectId<Person>,
- to: ObjectId<Person>,
+ actor: ObjectId<ApubPerson>,
+ to: ObjectId<ApubPerson>,
object: DeletePrivateMessage,
#[serde(rename = "type")]
kind: UndoType,
impl UndoDeletePrivateMessage {
pub async fn send(
- actor: &Person,
- pm: &PrivateMessage,
+ actor: &ApubPerson,
+ pm: &ApubPrivateMessage,
context: &LemmyContext,
) -> Result<(), LemmyError> {
let recipient_id = pm.recipient_id;
- let recipient =
- blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??;
+ let recipient: ApubPerson =
+ blocking(context.pool(), move |conn| Person::read(conn, recipient_id))
+ .await??
+ .into();
let object = DeletePrivateMessage::new(actor, pm, context)?;
let id = generate_activity_id(
activities::{generate_activity_id, verify_activity, verify_person_in_community},
context::lemmy_context,
fetcher::object_id::ObjectId,
+ objects::{community::ApubCommunity, person::ApubPerson},
send_lemmy_activity,
PostOrComment,
};
traits::{ActivityFields, ActivityHandler, ActorType},
};
use lemmy_db_schema::{
- newtypes::CommunityId,
source::{
comment_report::{CommentReport, CommentReportForm},
- community::Community,
- person::Person,
post_report::{PostReport, PostReportForm},
},
- traits::{Crud, Reportable},
+ traits::Reportable,
};
use lemmy_db_views::{comment_report_view::CommentReportView, post_report_view::PostReportView};
use lemmy_utils::LemmyError;
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct Report {
- actor: ObjectId<Person>,
- to: [ObjectId<Community>; 1],
+ actor: ObjectId<ApubPerson>,
+ to: [ObjectId<ApubCommunity>; 1],
object: ObjectId<PostOrComment>,
summary: String,
#[serde(rename = "type")]
impl Report {
pub async fn send(
object_id: ObjectId<PostOrComment>,
- actor: &Person,
- community_id: CommunityId,
+ actor: &ApubPerson,
+ community_id: ObjectId<ApubCommunity>,
reason: String,
context: &LemmyContext,
) -> Result<(), LemmyError> {
- let community = blocking(context.pool(), move |conn| {
- Community::read(conn, community_id)
- })
- .await??;
+ let community = community_id.dereference_local(context).await?;
let kind = FlagType::Flag;
let id = generate_activity_id(
kind.clone(),
let report_form = PostReportForm {
creator_id: actor.id,
post_id: post.id,
- original_post_name: post.name,
- original_post_url: post.url,
+ original_post_name: post.name.clone(),
+ original_post_url: post.url.clone(),
reason: self.summary,
- original_post_body: post.body,
+ original_post_body: post.body.clone(),
};
let report = blocking(context.pool(), move |conn| {
let report_form = CommentReportForm {
creator_id: actor.id,
comment_id: comment.id,
- original_comment_text: comment.content,
+ original_comment_text: comment.content.clone(),
reason: self.summary,
};
verify_activity,
},
fetcher::object_id::ObjectId,
+ objects::{community::ApubCommunity, person::ApubPerson},
};
use activitystreams::{
activity::kind::UndoType,
traits::{ActivityFields, ActivityHandler},
values::PublicUrl,
};
-use lemmy_db_schema::source::{community::Community, person::Person};
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct UndoRemovePostCommentOrCommunity {
- actor: ObjectId<Person>,
+ actor: ObjectId<ApubPerson>,
to: [PublicUrl; 1],
// Note, there is no such thing as Undo/Remove/Mod, so we ignore that
object: RemoveMod,
- cc: [ObjectId<Community>; 1],
+ cc: [ObjectId<ApubCommunity>; 1],
#[serde(rename = "type")]
kind: UndoType,
id: Url,
-use crate::activities::voting::vote::VoteType;
+use crate::{
+ activities::voting::vote::VoteType,
+ objects::{comment::ApubComment, person::ApubPerson, post::ApubPost},
+};
use lemmy_api_common::blocking;
use lemmy_db_schema::{
source::{
- comment::{Comment, CommentLike, CommentLikeForm},
- person::Person,
- post::{Post, PostLike, PostLikeForm},
+ comment::{CommentLike, CommentLikeForm},
+ post::{PostLike, PostLikeForm},
},
traits::Likeable,
};
async fn vote_comment(
vote_type: &VoteType,
- actor: Person,
- comment: &Comment,
+ actor: ApubPerson,
+ comment: &ApubComment,
context: &LemmyContext,
) -> Result<(), LemmyError> {
let comment_id = comment.id;
async fn vote_post(
vote_type: &VoteType,
- actor: Person,
- post: &Post,
+ actor: ApubPerson,
+ post: &ApubPost,
context: &LemmyContext,
) -> Result<(), LemmyError> {
let post_id = post.id;
}
async fn undo_vote_comment(
- actor: Person,
- comment: &Comment,
+ actor: ApubPerson,
+ comment: &ApubComment,
context: &LemmyContext,
) -> Result<(), LemmyError> {
let comment_id = comment.id;
}
async fn undo_vote_post(
- actor: Person,
- post: &Post,
+ actor: ApubPerson,
+ post: &ApubPost,
context: &LemmyContext,
) -> Result<(), LemmyError> {
let post_id = post.id;
},
context::lemmy_context,
fetcher::object_id::ObjectId,
+ objects::{community::ApubCommunity, person::ApubPerson},
PostOrComment,
};
use activitystreams::{
values::PublicUrl,
verify::verify_urls_match,
};
-use lemmy_db_schema::{
- newtypes::CommunityId,
- source::{community::Community, person::Person},
- traits::Crud,
-};
+use lemmy_db_schema::{newtypes::CommunityId, source::community::Community, traits::Crud};
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct UndoVote {
- actor: ObjectId<Person>,
+ actor: ObjectId<ApubPerson>,
to: [PublicUrl; 1],
object: Vote,
- cc: [ObjectId<Community>; 1],
+ cc: [ObjectId<ApubCommunity>; 1],
#[serde(rename = "type")]
kind: UndoType,
id: Url,
impl UndoVote {
pub async fn send(
object: &PostOrComment,
- actor: &Person,
+ actor: &ApubPerson,
community_id: CommunityId,
kind: VoteType,
context: &LemmyContext,
) -> Result<(), LemmyError> {
- let community = blocking(context.pool(), move |conn| {
+ let community: ApubCommunity = blocking(context.pool(), move |conn| {
Community::read(conn, community_id)
})
- .await??;
+ .await??
+ .into();
let object = Vote::new(object, actor, &community, kind.clone(), context)?;
let id = generate_activity_id(
},
context::lemmy_context,
fetcher::object_id::ObjectId,
+ objects::{community::ApubCommunity, person::ApubPerson},
PostOrComment,
};
use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed};
traits::{ActivityFields, ActivityHandler, ActorType},
values::PublicUrl,
};
-use lemmy_db_schema::{
- newtypes::CommunityId,
- source::{community::Community, person::Person},
- traits::Crud,
-};
+use lemmy_db_schema::{newtypes::CommunityId, source::community::Community, traits::Crud};
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")]
pub struct Vote {
- actor: ObjectId<Person>,
+ actor: ObjectId<ApubPerson>,
to: [PublicUrl; 1],
pub(in crate::activities::voting) object: ObjectId<PostOrComment>,
- cc: [ObjectId<Community>; 1],
+ cc: [ObjectId<ApubCommunity>; 1],
#[serde(rename = "type")]
pub(in crate::activities::voting) kind: VoteType,
id: Url,
impl Vote {
pub(in crate::activities::voting) fn new(
object: &PostOrComment,
- actor: &Person,
- community: &Community,
+ actor: &ApubPerson,
+ community: &ApubCommunity,
kind: VoteType,
context: &LemmyContext,
) -> Result<Vote, LemmyError> {
pub async fn send(
object: &PostOrComment,
- actor: &Person,
+ actor: &ApubPerson,
community_id: CommunityId,
kind: VoteType,
context: &LemmyContext,
let community = blocking(context.pool(), move |conn| {
Community::read(conn, community_id)
})
- .await??;
+ .await??
+ .into();
let vote = Vote::new(object, actor, &community, kind, context)?;
let vote_id = vote.id.clone();
use crate::{
activities::community::announce::AnnounceActivity,
fetcher::{fetch::fetch_remote_object, object_id::ObjectId},
- objects::community::Group,
+ objects::{community::Group, person::ApubPerson},
};
use activitystreams::collection::{CollectionExt, OrderedCollection};
use anyhow::Context;
use lemmy_api_common::blocking;
use lemmy_apub_lib::{data::Data, traits::ActivityHandler};
use lemmy_db_schema::{
- source::{
- community::{Community, CommunityModerator, CommunityModeratorForm},
- person::Person,
- },
+ source::community::{Community, CommunityModerator, CommunityModeratorForm},
traits::Joinable,
};
use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView;
// Add new mods to database which have been added to moderators collection
for mod_id in new_moderators {
let mod_id = ObjectId::new(mod_id);
- let mod_user: Person = mod_id.dereference(context, request_counter).await?;
+ let mod_user: ApubPerson = mod_id.dereference(context, request_counter).await?;
if !current_moderators
.clone()
pub mod post_or_comment;
pub mod search;
-use crate::fetcher::object_id::ObjectId;
+use crate::{
+ fetcher::object_id::ObjectId,
+ objects::{community::ApubCommunity, person::ApubPerson},
+};
use chrono::NaiveDateTime;
use lemmy_apub_lib::traits::ActorType;
-use lemmy_db_schema::{
- naive_now,
- source::{community::Community, person::Person},
-};
+use lemmy_db_schema::naive_now;
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
use url::Url;
context: &LemmyContext,
recursion_counter: &mut i32,
) -> Result<Box<dyn ActorType>, LemmyError> {
- let community_id = ObjectId::<Community>::new(apub_id.clone());
+ let community_id = ObjectId::<ApubCommunity>::new(apub_id.clone());
let community = community_id.dereference(context, recursion_counter).await;
let actor: Box<dyn ActorType> = match community {
Ok(c) => Box::new(c),
Err(_) => {
let person_id = ObjectId::new(apub_id);
- let person: Person = person_id.dereference(context, recursion_counter).await?;
+ let person: ApubPerson = person_id.dereference(context, recursion_counter).await?;
Box::new(person)
}
};
-use crate::{fetcher::should_refetch_actor, objects::FromApub};
+use crate::fetcher::should_refetch_actor;
use anyhow::anyhow;
-use diesel::{NotFound, PgConnection};
-use lemmy_api_common::blocking;
-use lemmy_apub_lib::{traits::ApubObject, APUB_JSON_CONTENT_TYPE};
-use lemmy_db_schema::{newtypes::DbUrl, DbPool};
+use diesel::NotFound;
+use lemmy_apub_lib::{
+ traits::{ApubObject, FromApub},
+ APUB_JSON_CONTENT_TYPE,
+};
+use lemmy_db_schema::newtypes::DbUrl;
use lemmy_utils::{request::retry, settings::structs::Settings, LemmyError};
use lemmy_websocket::LemmyContext;
use reqwest::StatusCode;
#[derive(Clone, PartialEq, Serialize, Deserialize, Debug)]
pub struct ObjectId<Kind>(Url, #[serde(skip)] PhantomData<Kind>)
where
- Kind: FromApub + ApubObject<DataType = PgConnection> + Send + 'static,
+ Kind: FromApub<DataType = LemmyContext> + ApubObject<DataType = LemmyContext> + Send + 'static,
for<'de2> <Kind as FromApub>::ApubType: serde::Deserialize<'de2>;
impl<Kind> ObjectId<Kind>
where
- Kind: FromApub + ApubObject<DataType = PgConnection> + Send + 'static,
+ Kind: FromApub<DataType = LemmyContext> + ApubObject<DataType = LemmyContext> + Send + 'static,
for<'de> <Kind as FromApub>::ApubType: serde::Deserialize<'de>,
{
pub fn new<T>(url: T) -> Self
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<Kind, LemmyError> {
- let db_object = self.dereference_from_db(context.pool()).await?;
+ let db_object = self.dereference_from_db(context).await?;
// if its a local object, only fetch it from the database and not over http
if self.0.domain() == Some(&Settings::get().get_hostname_without_port()?) {
/// Fetch an object from the local db. Instead of falling back to http, this throws an error if
/// the object is not found in the database.
pub async fn dereference_local(&self, context: &LemmyContext) -> Result<Kind, LemmyError> {
- let object = self.dereference_from_db(context.pool()).await?;
+ let object = self.dereference_from_db(context).await?;
object.ok_or_else(|| anyhow!("object not found in database {}", self).into())
}
/// returning none means the object was not found in local db
- async fn dereference_from_db(&self, pool: &DbPool) -> Result<Option<Kind>, LemmyError> {
+ async fn dereference_from_db(&self, context: &LemmyContext) -> Result<Option<Kind>, LemmyError> {
let id = self.0.clone();
- blocking(pool, move |conn| ApubObject::read_from_apub_id(conn, id)).await?
+ ApubObject::read_from_apub_id(id, context).await
}
async fn dereference_from_http(
if res.status() == StatusCode::GONE {
if let Some(db_object) = db_object {
- blocking(context.pool(), move |conn| db_object.delete(conn)).await??;
+ db_object.delete(context).await?;
}
return Err(anyhow!("Fetched remote object {} which was deleted", self).into());
}
impl<Kind> Display for ObjectId<Kind>
where
- Kind: FromApub + ApubObject<DataType = PgConnection> + Send + 'static,
+ Kind: FromApub<DataType = LemmyContext> + ApubObject<DataType = LemmyContext> + Send + 'static,
for<'de> <Kind as FromApub>::ApubType: serde::Deserialize<'de>,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
impl<Kind> From<ObjectId<Kind>> for Url
where
- Kind: FromApub + ApubObject<DataType = PgConnection> + Send + 'static,
+ Kind: FromApub<DataType = LemmyContext> + ApubObject<DataType = LemmyContext> + Send + 'static,
for<'de> <Kind as FromApub>::ApubType: serde::Deserialize<'de>,
{
fn from(id: ObjectId<Kind>) -> Self {
impl<Kind> From<ObjectId<Kind>> for DbUrl
where
- Kind: FromApub + ApubObject<DataType = PgConnection> + Send + 'static,
+ Kind: FromApub<DataType = LemmyContext> + ApubObject<DataType = LemmyContext> + Send + 'static,
for<'de> <Kind as FromApub>::ApubType: serde::Deserialize<'de>,
{
fn from(id: ObjectId<Kind>) -> Self {
-use crate::objects::{comment::Note, post::Page, FromApub};
-use activitystreams::chrono::NaiveDateTime;
-use diesel::PgConnection;
-use lemmy_apub_lib::traits::ApubObject;
-use lemmy_db_schema::source::{
- comment::{Comment, CommentForm},
- post::{Post, PostForm},
+use crate::objects::{
+ comment::{ApubComment, Note},
+ post::{ApubPost, Page},
};
+use activitystreams::chrono::NaiveDateTime;
+use lemmy_apub_lib::traits::{ApubObject, FromApub};
+use lemmy_db_schema::source::{comment::CommentForm, post::PostForm};
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
use serde::Deserialize;
#[derive(Clone, Debug)]
pub enum PostOrComment {
- Post(Box<Post>),
- Comment(Comment),
+ Post(Box<ApubPost>),
+ Comment(ApubComment),
}
pub enum PostOrCommentForm {
#[async_trait::async_trait(?Send)]
impl ApubObject for PostOrComment {
- type DataType = PgConnection;
+ type DataType = LemmyContext;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
None
}
// TODO: this can probably be implemented using a single sql query
- fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, LemmyError>
+ async fn read_from_apub_id(
+ object_id: Url,
+ data: &Self::DataType,
+ ) -> Result<Option<Self>, LemmyError>
where
Self: Sized,
{
- let post = Post::read_from_apub_id(conn, object_id.clone())?;
+ let post = ApubPost::read_from_apub_id(object_id.clone(), data).await?;
Ok(match post {
Some(o) => Some(PostOrComment::Post(Box::new(o))),
- None => Comment::read_from_apub_id(conn, object_id)?.map(PostOrComment::Comment),
+ None => ApubComment::read_from_apub_id(object_id, data)
+ .await?
+ .map(PostOrComment::Comment),
})
}
- fn delete(self, data: &Self::DataType) -> Result<(), LemmyError> {
+ async fn delete(self, data: &Self::DataType) -> Result<(), LemmyError> {
match self {
- PostOrComment::Post(p) => p.delete(data),
- PostOrComment::Comment(c) => c.delete(data),
+ PostOrComment::Post(p) => p.delete(data).await,
+ PostOrComment::Comment(c) => c.delete(data).await,
}
}
}
#[async_trait::async_trait(?Send)]
impl FromApub for PostOrComment {
type ApubType = PageOrNote;
+ type DataType = LemmyContext;
async fn from_apub(
apub: &PageOrNote,
{
Ok(match apub {
PageOrNote::Page(p) => PostOrComment::Post(Box::new(
- Post::from_apub(p, context, expected_domain, request_counter).await?,
+ ApubPost::from_apub(p, context, expected_domain, request_counter).await?,
)),
PageOrNote::Note(n) => PostOrComment::Comment(
- Comment::from_apub(n, context, expected_domain, request_counter).await?,
+ ApubComment::from_apub(n, context, expected_domain, request_counter).await?,
),
})
}
use crate::{
fetcher::object_id::ObjectId,
- objects::{comment::Note, community::Group, person::Person as ApubPerson, post::Page, FromApub},
+ objects::{
+ comment::{ApubComment, Note},
+ community::{ApubCommunity, Group},
+ person::{ApubPerson, Person},
+ post::{ApubPost, Page},
+ },
};
use activitystreams::chrono::NaiveDateTime;
use anyhow::anyhow;
-use diesel::PgConnection;
use itertools::Itertools;
use lemmy_api_common::blocking;
use lemmy_apub_lib::{
- traits::ApubObject,
+ traits::{ApubObject, FromApub},
webfinger::{webfinger_resolve_actor, WebfingerType},
};
use lemmy_db_schema::{
- source::{comment::Comment, community::Community, person::Person, post::Post},
+ source::{community::Community, person::Person as DbPerson},
DbPool,
};
use lemmy_utils::LemmyError;
let name: String = name.into();
Ok(match kind {
WebfingerType::Group => SearchableObjects::Community(
- blocking(pool, move |conn| Community::read_from_name(conn, &name)).await??,
+ blocking(pool, move |conn| Community::read_from_name(conn, &name))
+ .await??
+ .into(),
),
WebfingerType::Person => SearchableObjects::Person(
- blocking(pool, move |conn| Person::find_by_name(conn, &name)).await??,
+ blocking(pool, move |conn| DbPerson::find_by_name(conn, &name))
+ .await??
+ .into(),
),
})
}
/// The types of ActivityPub objects that can be fetched directly by searching for their ID.
#[derive(Debug)]
pub enum SearchableObjects {
- Person(Person),
- Community(Community),
- Post(Post),
- Comment(Comment),
+ Person(ApubPerson),
+ Community(ApubCommunity),
+ Post(ApubPost),
+ Comment(ApubComment),
}
#[derive(Deserialize)]
#[serde(untagged)]
pub enum SearchableApubTypes {
Group(Group),
- Person(ApubPerson),
+ Person(Person),
Page(Page),
Note(Note),
}
+#[async_trait::async_trait(?Send)]
impl ApubObject for SearchableObjects {
- type DataType = PgConnection;
+ type DataType = LemmyContext;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
match self {
// a single query.
// we could skip this and always return an error, but then it would always fetch objects
// over http, and not be able to mark objects as deleted that were deleted by remote server.
- fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, LemmyError> {
- let c = Community::read_from_apub_id(conn, object_id.clone())?;
+ async fn read_from_apub_id(
+ object_id: Url,
+ context: &LemmyContext,
+ ) -> Result<Option<Self>, LemmyError> {
+ let c = ApubCommunity::read_from_apub_id(object_id.clone(), context).await?;
if let Some(c) = c {
return Ok(Some(SearchableObjects::Community(c)));
}
- let p = Person::read_from_apub_id(conn, object_id.clone())?;
+ let p = ApubPerson::read_from_apub_id(object_id.clone(), context).await?;
if let Some(p) = p {
return Ok(Some(SearchableObjects::Person(p)));
}
- let p = Post::read_from_apub_id(conn, object_id.clone())?;
+ let p = ApubPost::read_from_apub_id(object_id.clone(), context).await?;
if let Some(p) = p {
return Ok(Some(SearchableObjects::Post(p)));
}
- let c = Comment::read_from_apub_id(conn, object_id)?;
+ let c = ApubComment::read_from_apub_id(object_id, context).await?;
if let Some(c) = c {
return Ok(Some(SearchableObjects::Comment(c)));
}
Ok(None)
}
- fn delete(self, conn: &Self::DataType) -> Result<(), LemmyError> {
+ async fn delete(self, data: &Self::DataType) -> Result<(), LemmyError> {
match self {
- SearchableObjects::Person(p) => p.delete(conn),
- SearchableObjects::Community(c) => c.delete(conn),
- SearchableObjects::Post(p) => p.delete(conn),
- SearchableObjects::Comment(c) => c.delete(conn),
+ SearchableObjects::Person(p) => p.delete(data).await,
+ SearchableObjects::Community(c) => c.delete(data).await,
+ SearchableObjects::Post(p) => p.delete(data).await,
+ SearchableObjects::Comment(c) => c.delete(data).await,
}
}
}
#[async_trait::async_trait(?Send)]
impl FromApub for SearchableObjects {
type ApubType = SearchableApubTypes;
+ type DataType = LemmyContext;
async fn from_apub(
apub: &Self::ApubType,
use SearchableApubTypes as SAT;
use SearchableObjects as SO;
Ok(match apub {
- SAT::Group(g) => SO::Community(Community::from_apub(g, context, ed, rc).await?),
- SAT::Person(p) => SO::Person(Person::from_apub(p, context, ed, rc).await?),
- SAT::Page(p) => SO::Post(Post::from_apub(p, context, ed, rc).await?),
- SAT::Note(n) => SO::Comment(Comment::from_apub(n, context, ed, rc).await?),
+ SAT::Group(g) => SO::Community(ApubCommunity::from_apub(g, context, ed, rc).await?),
+ SAT::Person(p) => SO::Person(ApubPerson::from_apub(p, context, ed, rc).await?),
+ SAT::Page(p) => SO::Post(ApubPost::from_apub(p, context, ed, rc).await?),
+ SAT::Note(n) => SO::Comment(ApubComment::from_apub(n, context, ed, rc).await?),
})
}
}
use crate::{
http::{create_apub_response, create_apub_tombstone_response},
- objects::ToApub,
+ objects::comment::ApubComment,
};
use actix_web::{body::Body, web, web::Path, HttpResponse};
use diesel::result::Error::NotFound;
use lemmy_api_common::blocking;
+use lemmy_apub_lib::traits::ToApub;
use lemmy_db_schema::{newtypes::CommentId, source::comment::Comment, traits::Crud};
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
context: web::Data<LemmyContext>,
) -> Result<HttpResponse<Body>, LemmyError> {
let id = CommentId(info.comment_id.parse::<i32>()?);
- let comment = blocking(context.pool(), move |conn| Comment::read(conn, id)).await??;
+ let comment: ApubComment = blocking(context.pool(), move |conn| Comment::read(conn, id))
+ .await??
+ .into();
if !comment.local {
return Err(NotFound.into());
}
payload_to_string,
receive_activity,
},
- objects::ToApub,
+ objects::community::ApubCommunity,
};
use activitystreams::{
base::{AnyBase, BaseExt},
};
use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse};
use lemmy_api_common::blocking;
-use lemmy_apub_lib::traits::{ActivityFields, ActivityHandler};
+use lemmy_apub_lib::traits::{ActivityFields, ActivityHandler, ToApub};
use lemmy_db_schema::source::{activity::Activity, community::Community};
use lemmy_db_views_actor::{
community_follower_view::CommunityFollowerView,
info: web::Path<CommunityQuery>,
context: web::Data<LemmyContext>,
) -> Result<HttpResponse<Body>, LemmyError> {
- let community = blocking(context.pool(), move |conn| {
+ let community: ApubCommunity = blocking(context.pool(), move |conn| {
Community::read_from_name(conn, &info.community_name)
})
- .await??;
+ .await??
+ .into();
if !community.deleted {
let apub = community.to_apub(context.pool()).await?;
info: web::Path<CommunityQuery>,
context: web::Data<LemmyContext>,
) -> Result<HttpResponse<Body>, LemmyError> {
- let community = blocking(context.pool(), move |conn| {
+ let community: ApubCommunity = blocking(context.pool(), move |conn| {
Community::read_from_name(conn, &info.community_name)
})
- .await??;
+ .await??
+ .into();
// The attributed to, is an ordered vector with the creator actor_ids first,
// then the rest of the moderators
payload_to_string,
receive_activity,
},
- objects::ToApub,
+ objects::person::ApubPerson,
};
use activitystreams::{
base::BaseExt,
};
use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse};
use lemmy_api_common::blocking;
-use lemmy_apub_lib::traits::{ActivityFields, ActivityHandler};
+use lemmy_apub_lib::traits::{ActivityFields, ActivityHandler, ToApub};
use lemmy_db_schema::source::person::Person;
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
) -> Result<HttpResponse<Body>, LemmyError> {
let user_name = info.into_inner().user_name;
// TODO: this needs to be able to read deleted persons, so that it can send tombstones
- let person = blocking(context.pool(), move |conn| {
+ let person: ApubPerson = blocking(context.pool(), move |conn| {
Person::find_by_name(conn, &user_name)
})
- .await??;
+ .await??
+ .into();
if !person.deleted {
let apub = person.to_apub(context.pool()).await?;
use crate::{
http::{create_apub_response, create_apub_tombstone_response},
- objects::ToApub,
+ objects::post::ApubPost,
};
use actix_web::{body::Body, web, HttpResponse};
use diesel::result::Error::NotFound;
use lemmy_api_common::blocking;
+use lemmy_apub_lib::traits::ToApub;
use lemmy_db_schema::{newtypes::PostId, source::post::Post, traits::Crud};
use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext;
context: web::Data<LemmyContext>,
) -> Result<HttpResponse<Body>, LemmyError> {
let id = PostId(info.post_id.parse::<i32>()?);
- let post = blocking(context.pool(), move |conn| Post::read(conn, id)).await??;
+ let post: ApubPost = blocking(context.pool(), move |conn| Post::read(conn, id))
+ .await??
+ .into();
if !post.local {
return Err(NotFound.into());
}
context::lemmy_context,
fetcher::object_id::ObjectId,
migrations::CommentInReplyToMigration,
- objects::{create_tombstone, FromApub, Source, ToApub},
+ objects::{create_tombstone, person::ApubPerson, post::ApubPost, Source},
PostOrComment,
};
use activitystreams::{
base::AnyBase,
+ chrono::NaiveDateTime,
object::{kind::NoteType, Tombstone},
primitives::OneOrMany,
unparsed::Unparsed,
use chrono::{DateTime, FixedOffset};
use lemmy_api_common::blocking;
use lemmy_apub_lib::{
- traits::ActorType,
+ traits::{ApubObject, FromApub, ToApub},
values::{MediaTypeHtml, MediaTypeMarkdown, PublicUrl},
verify::verify_domains_match,
};
context: OneOrMany<AnyBase>,
r#type: NoteType,
id: Url,
- pub(crate) attributed_to: ObjectId<Person>,
+ pub(crate) attributed_to: ObjectId<ApubPerson>,
/// Indicates that the object is publicly readable. Unlike [`Post.to`], this one doesn't contain
/// the community ID, as it would be incompatible with Pleroma (and we can get the community from
/// the post in [`in_reply_to`]).
&self,
context: &LemmyContext,
request_counter: &mut i32,
- ) -> Result<(Post, Option<CommentId>), LemmyError> {
+ ) -> Result<(ApubPost, Option<CommentId>), LemmyError> {
match &self.in_reply_to {
CommentInReplyToMigration::Old(in_reply_to) => {
// This post, or the parent comment might not yet exist on this server yet, fetch them.
// Nested comments will automatically get fetched recursively
let parent_id: Option<CommentId> = match in_reply_to.get(1) {
Some(comment_id) => {
- let comment_id = ObjectId::<Comment>::new(comment_id.clone());
+ let comment_id = ObjectId::<ApubComment>::new(comment_id.clone());
let parent_comment = Box::pin(comment_id.dereference(context, request_counter)).await?;
Some(parent_comment.id)
let parent = Box::pin(in_reply_to.dereference(context, request_counter).await?);
match parent.deref() {
PostOrComment::Post(p) => {
- // Workaround because I cant figure ut how to get the post out of the box (and we dont
+ // Workaround because I cant figure out how to get the post out of the box (and we dont
// want to stackoverflow in a deep comment hierarchy).
let post_id = p.id;
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
- Ok((post, None))
+ Ok((post.into(), None))
}
PostOrComment::Comment(c) => {
let post_id = c.post_id;
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
- Ok((post, Some(c.id)))
+ Ok((post.into(), Some(c.id)))
}
}
}
verify_domains_match(self.attributed_to.inner(), &self.id)?;
verify_person_in_community(
&self.attributed_to,
- &ObjectId::new(community.actor_id()),
+ &ObjectId::new(community.actor_id),
context,
request_counter,
)
}
}
+#[derive(Clone, Debug)]
+pub struct ApubComment(Comment);
+
+impl Deref for ApubComment {
+ type Target = Comment;
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl From<Comment> for ApubComment {
+ fn from(c: Comment) -> Self {
+ ApubComment { 0: c }
+ }
+}
+
+#[async_trait::async_trait(?Send)]
+impl ApubObject for ApubComment {
+ type DataType = LemmyContext;
+
+ fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
+ None
+ }
+
+ async fn read_from_apub_id(
+ object_id: Url,
+ context: &LemmyContext,
+ ) -> Result<Option<Self>, LemmyError> {
+ Ok(
+ blocking(context.pool(), move |conn| {
+ Comment::read_from_apub_id(conn, object_id)
+ })
+ .await??
+ .map(Into::into),
+ )
+ }
+
+ async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
+ blocking(context.pool(), move |conn| {
+ Comment::update_deleted(conn, self.id, true)
+ })
+ .await??;
+ Ok(())
+ }
+}
+
#[async_trait::async_trait(?Send)]
-impl ToApub for Comment {
+impl ToApub for ApubComment {
type ApubType = Note;
+ type TombstoneType = Tombstone;
+ type DataType = DbPool;
async fn to_apub(&self, pool: &DbPool) -> Result<Note, LemmyError> {
let creator_id = self.creator_id;
}
#[async_trait::async_trait(?Send)]
-impl FromApub for Comment {
+impl FromApub for ApubComment {
type ApubType = Note;
+ type DataType = LemmyContext;
/// Converts a `Note` to `Comment`.
///
context: &LemmyContext,
expected_domain: &Url,
request_counter: &mut i32,
- ) -> Result<Comment, LemmyError> {
+ ) -> Result<ApubComment, LemmyError> {
let ap_id = Some(note.id(expected_domain)?.clone().into());
let creator = note
.attributed_to
ap_id,
local: Some(false),
};
- Ok(blocking(context.pool(), move |conn| Comment::upsert(conn, &form)).await??)
+ let comment = blocking(context.pool(), move |conn| Comment::upsert(conn, &form)).await??;
+ Ok(comment.into())
}
}
fetcher::community::{fetch_community_outbox, update_community_mods},
generate_moderators_url,
generate_outbox_url,
- objects::{create_tombstone, FromApub, ImageObject, Source, ToApub},
+ objects::{create_tombstone, ImageObject, Source},
CommunityType,
};
use activitystreams::{
actor::{kind::GroupType, Endpoints},
base::AnyBase,
+ chrono::NaiveDateTime,
object::{kind::ImageType, Tombstone},
primitives::OneOrMany,
unparsed::Unparsed,
use lemmy_api_common::blocking;
use lemmy_apub_lib::{
signatures::PublicKey,
- traits::ActorType,
+ traits::{ActorType, ApubObject, FromApub, ToApub},
values::{MediaTypeHtml, MediaTypeMarkdown},
verify::verify_domains_match,
};
use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
+use std::ops::Deref;
use url::Url;
#[skip_serializing_none]
}
}
+#[derive(Clone, Debug)]
+pub struct ApubCommunity(Community);
+
+impl Deref for ApubCommunity {
+ type Target = Community;
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl From<Community> for ApubCommunity {
+ fn from(c: Community) -> Self {
+ ApubCommunity { 0: c }
+ }
+}
+
+#[async_trait::async_trait(?Send)]
+impl ApubObject for ApubCommunity {
+ type DataType = LemmyContext;
+
+ fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
+ Some(self.last_refreshed_at)
+ }
+
+ async fn read_from_apub_id(
+ object_id: Url,
+ context: &LemmyContext,
+ ) -> Result<Option<Self>, LemmyError> {
+ Ok(
+ blocking(context.pool(), move |conn| {
+ Community::read_from_apub_id(conn, object_id)
+ })
+ .await??
+ .map(Into::into),
+ )
+ }
+
+ async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
+ blocking(context.pool(), move |conn| {
+ Community::update_deleted(conn, self.id, true)
+ })
+ .await??;
+ Ok(())
+ }
+}
+
+impl ActorType for ApubCommunity {
+ fn is_local(&self) -> bool {
+ self.local
+ }
+ fn actor_id(&self) -> Url {
+ self.actor_id.to_owned().into()
+ }
+ fn name(&self) -> String {
+ self.name.clone()
+ }
+ fn public_key(&self) -> Option<String> {
+ self.public_key.to_owned()
+ }
+ fn private_key(&self) -> Option<String> {
+ self.private_key.to_owned()
+ }
+
+ fn inbox_url(&self) -> Url {
+ self.inbox_url.clone().into()
+ }
+
+ fn shared_inbox_url(&self) -> Option<Url> {
+ self.shared_inbox_url.clone().map(|s| s.into_inner())
+ }
+}
+
#[async_trait::async_trait(?Send)]
-impl ToApub for Community {
+impl ToApub for ApubCommunity {
type ApubType = Group;
+ type TombstoneType = Tombstone;
+ type DataType = DbPool;
async fn to_apub(&self, _pool: &DbPool) -> Result<Group, LemmyError> {
let source = self.description.clone().map(|bio| Source {
}
#[async_trait::async_trait(?Send)]
-impl FromApub for Community {
+impl FromApub for ApubCommunity {
type ApubType = Group;
+ type DataType = LemmyContext;
/// Converts a `Group` to `Community`, inserts it into the database and updates moderators.
async fn from_apub(
context: &LemmyContext,
expected_domain: &Url,
request_counter: &mut i32,
- ) -> Result<Community, LemmyError> {
+ ) -> Result<ApubCommunity, LemmyError> {
let form = Group::from_apub_to_form(group, expected_domain, &context.settings()).await?;
let community = blocking(context.pool(), move |conn| Community::upsert(conn, &form)).await??;
// TODO: doing this unconditionally might cause infinite loop for some reason
fetch_community_outbox(context, &group.outbox, request_counter).await?;
- Ok(community)
+ Ok(community.into())
}
}
use anyhow::anyhow;
use chrono::NaiveDateTime;
use lemmy_apub_lib::values::MediaTypeMarkdown;
-use lemmy_db_schema::DbPool;
use lemmy_utils::{utils::convert_datetime, LemmyError};
-use lemmy_websocket::LemmyContext;
use url::Url;
-pub(crate) mod comment;
-pub(crate) mod community;
-pub(crate) mod person;
-pub(crate) mod post;
-pub(crate) mod private_message;
-
-/// Trait for converting an object or actor into the respective ActivityPub type.
-#[async_trait::async_trait(?Send)]
-pub(crate) trait ToApub {
- type ApubType;
- async fn to_apub(&self, pool: &DbPool) -> Result<Self::ApubType, LemmyError>;
- fn to_tombstone(&self) -> Result<Tombstone, LemmyError>;
-}
-
-#[async_trait::async_trait(?Send)]
-pub trait FromApub {
- type ApubType;
- /// Converts an object from ActivityPub type to Lemmy internal type.
- ///
- /// * `apub` The object to read from
- /// * `context` LemmyContext which holds DB pool, HTTP client etc
- /// * `expected_domain` Domain where the object was received from. None in case of mod action.
- /// * `mod_action_allowed` True if the object can be a mod activity, ignore `expected_domain` in this case
- async fn from_apub(
- apub: &Self::ApubType,
- context: &LemmyContext,
- expected_domain: &Url,
- request_counter: &mut i32,
- ) -> Result<Self, LemmyError>
- where
- Self: Sized;
-}
+pub mod comment;
+pub mod community;
+pub mod person;
+pub mod post;
+pub mod private_message;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
check_is_apub_id_valid,
context::lemmy_context,
generate_outbox_url,
- objects::{FromApub, ImageObject, Source, ToApub},
+ objects::{ImageObject, Source},
};
use activitystreams::{
actor::Endpoints,
base::AnyBase,
- chrono::{DateTime, FixedOffset},
+ chrono::NaiveDateTime,
object::{kind::ImageType, Tombstone},
primitives::OneOrMany,
unparsed::Unparsed,
};
+use chrono::{DateTime, FixedOffset};
use lemmy_api_common::blocking;
use lemmy_apub_lib::{
signatures::PublicKey,
- traits::ActorType,
+ traits::{ActorType, ApubObject, FromApub, ToApub},
values::{MediaTypeHtml, MediaTypeMarkdown},
verify::verify_domains_match,
};
use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
+use std::ops::Deref;
use url::Url;
#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq)]
}
}
+#[derive(Clone, Debug)]
+pub struct ApubPerson(DbPerson);
+
+impl Deref for ApubPerson {
+ type Target = DbPerson;
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl From<DbPerson> for ApubPerson {
+ fn from(p: DbPerson) -> Self {
+ ApubPerson { 0: p }
+ }
+}
+
#[async_trait::async_trait(?Send)]
-impl ToApub for DbPerson {
+impl ApubObject for ApubPerson {
+ type DataType = LemmyContext;
+
+ fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
+ Some(self.last_refreshed_at)
+ }
+
+ async fn read_from_apub_id(
+ object_id: Url,
+ context: &LemmyContext,
+ ) -> Result<Option<Self>, LemmyError> {
+ Ok(
+ blocking(context.pool(), move |conn| {
+ DbPerson::read_from_apub_id(conn, object_id)
+ })
+ .await??
+ .map(Into::into),
+ )
+ }
+
+ async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
+ blocking(context.pool(), move |conn| {
+ DbPerson::update_deleted(conn, self.id, true)
+ })
+ .await??;
+ Ok(())
+ }
+}
+
+impl ActorType for ApubPerson {
+ fn is_local(&self) -> bool {
+ self.local
+ }
+ fn actor_id(&self) -> Url {
+ self.actor_id.to_owned().into_inner()
+ }
+ fn name(&self) -> String {
+ self.name.clone()
+ }
+
+ fn public_key(&self) -> Option<String> {
+ self.public_key.to_owned()
+ }
+
+ fn private_key(&self) -> Option<String> {
+ self.private_key.to_owned()
+ }
+
+ fn inbox_url(&self) -> Url {
+ self.inbox_url.clone().into()
+ }
+
+ fn shared_inbox_url(&self) -> Option<Url> {
+ self.shared_inbox_url.clone().map(|s| s.into_inner())
+ }
+}
+
+#[async_trait::async_trait(?Send)]
+impl ToApub for ApubPerson {
type ApubType = Person;
+ type TombstoneType = Tombstone;
+ type DataType = DbPool;
async fn to_apub(&self, _pool: &DbPool) -> Result<Person, LemmyError> {
let kind = if self.bot_account {
}
#[async_trait::async_trait(?Send)]
-impl FromApub for DbPerson {
+impl FromApub for ApubPerson {
type ApubType = Person;
+ type DataType = LemmyContext;
async fn from_apub(
person: &Person,
context: &LemmyContext,
expected_domain: &Url,
_request_counter: &mut i32,
- ) -> Result<DbPerson, LemmyError> {
+ ) -> Result<ApubPerson, LemmyError> {
let actor_id = Some(person.id(expected_domain)?.clone().into());
let name = person.preferred_username.clone();
let display_name: Option<String> = person.name.clone();
DbPerson::upsert(conn, &person_form)
})
.await??;
- Ok(person)
+ Ok(person.into())
}
}
activities::{extract_community, verify_person_in_community},
context::lemmy_context,
fetcher::object_id::ObjectId,
- objects::{create_tombstone, FromApub, ImageObject, Source, ToApub},
+ objects::{create_tombstone, person::ApubPerson, ImageObject, Source},
};
use activitystreams::{
base::AnyBase,
public,
unparsed::Unparsed,
};
-use chrono::{DateTime, FixedOffset};
+use chrono::{DateTime, FixedOffset, NaiveDateTime};
use lemmy_api_common::blocking;
use lemmy_apub_lib::{
- traits::ActorType,
+ traits::{ActorType, ApubObject, FromApub, ToApub},
values::{MediaTypeHtml, MediaTypeMarkdown},
verify::verify_domains_match,
};
use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
+use std::ops::Deref;
use url::Url;
#[skip_serializing_none]
context: OneOrMany<AnyBase>,
r#type: PageType,
id: Url,
- pub(crate) attributed_to: ObjectId<Person>,
+ pub(crate) attributed_to: ObjectId<ApubPerson>,
to: [Url; 2],
name: String,
content: Option<String>,
///
/// Both stickied and locked need to be false on a newly created post (verified in [[CreatePost]].
pub(crate) async fn is_mod_action(&self, context: &LemmyContext) -> Result<bool, LemmyError> {
- let old_post = ObjectId::<Post>::new(self.id.clone())
+ let old_post = ObjectId::<ApubPost>::new(self.id.clone())
.dereference_local(context)
.await;
}
}
+#[derive(Clone, Debug)]
+pub struct ApubPost(Post);
+
+impl Deref for ApubPost {
+ type Target = Post;
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl From<Post> for ApubPost {
+ fn from(p: Post) -> Self {
+ ApubPost { 0: p }
+ }
+}
+
+#[async_trait::async_trait(?Send)]
+impl ApubObject for ApubPost {
+ type DataType = LemmyContext;
+
+ fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
+ None
+ }
+
+ async fn read_from_apub_id(
+ object_id: Url,
+ context: &LemmyContext,
+ ) -> Result<Option<Self>, LemmyError> {
+ Ok(
+ blocking(context.pool(), move |conn| {
+ Post::read_from_apub_id(conn, object_id)
+ })
+ .await??
+ .map(Into::into),
+ )
+ }
+
+ async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
+ blocking(context.pool(), move |conn| {
+ Post::update_deleted(conn, self.id, true)
+ })
+ .await??;
+ Ok(())
+ }
+}
+
#[async_trait::async_trait(?Send)]
-impl ToApub for Post {
+impl ToApub for ApubPost {
type ApubType = Page;
+ type TombstoneType = Tombstone;
+ type DataType = DbPool;
// Turn a Lemmy post into an ActivityPub page that can be sent out over the network.
async fn to_apub(&self, pool: &DbPool) -> Result<Page, LemmyError> {
}
#[async_trait::async_trait(?Send)]
-impl FromApub for Post {
+impl FromApub for ApubPost {
type ApubType = Page;
+ type DataType = LemmyContext;
async fn from_apub(
page: &Page,
context: &LemmyContext,
expected_domain: &Url,
request_counter: &mut i32,
- ) -> Result<Post, LemmyError> {
+ ) -> Result<ApubPost, LemmyError> {
// We can't verify the domain in case of mod action, because the mod may be on a different
// instance from the post author.
let ap_id = if page.is_mod_action(context).await? {
ap_id,
local: Some(false),
};
- Ok(blocking(context.pool(), move |conn| Post::upsert(conn, &form)).await??)
+ let post = blocking(context.pool(), move |conn| Post::upsert(conn, &form)).await??;
+ Ok(post.into())
}
}
use crate::{
context::lemmy_context,
fetcher::object_id::ObjectId,
- objects::{create_tombstone, FromApub, Source, ToApub},
+ objects::{create_tombstone, person::ApubPerson, Source},
};
use activitystreams::{
base::AnyBase,
+ chrono::NaiveDateTime,
object::{kind::NoteType, Tombstone},
primitives::OneOrMany,
unparsed::Unparsed,
use chrono::{DateTime, FixedOffset};
use lemmy_api_common::blocking;
use lemmy_apub_lib::{
+ traits::{ApubObject, FromApub, ToApub},
values::{MediaTypeHtml, MediaTypeMarkdown},
verify::verify_domains_match,
};
use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
+use std::ops::Deref;
use url::Url;
#[skip_serializing_none]
context: OneOrMany<AnyBase>,
r#type: NoteType,
id: Url,
- pub(crate) attributed_to: ObjectId<Person>,
- to: ObjectId<Person>,
+ pub(crate) attributed_to: ObjectId<ApubPerson>,
+ to: ObjectId<ApubPerson>,
content: String,
media_type: MediaTypeHtml,
source: Source,
}
}
+#[derive(Clone, Debug)]
+pub struct ApubPrivateMessage(PrivateMessage);
+
+impl Deref for ApubPrivateMessage {
+ type Target = PrivateMessage;
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl From<PrivateMessage> for ApubPrivateMessage {
+ fn from(pm: PrivateMessage) -> Self {
+ ApubPrivateMessage { 0: pm }
+ }
+}
+
#[async_trait::async_trait(?Send)]
-impl ToApub for PrivateMessage {
+impl ApubObject for ApubPrivateMessage {
+ type DataType = LemmyContext;
+
+ fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
+ None
+ }
+
+ async fn read_from_apub_id(
+ object_id: Url,
+ context: &LemmyContext,
+ ) -> Result<Option<Self>, LemmyError> {
+ Ok(
+ blocking(context.pool(), move |conn| {
+ PrivateMessage::read_from_apub_id(conn, object_id)
+ })
+ .await??
+ .map(Into::into),
+ )
+ }
+
+ async fn delete(self, _context: &LemmyContext) -> Result<(), LemmyError> {
+ // do nothing, because pm can't be fetched over http
+ unimplemented!()
+ }
+}
+
+#[async_trait::async_trait(?Send)]
+impl ToApub for ApubPrivateMessage {
type ApubType = Note;
+ type TombstoneType = Tombstone;
+ type DataType = DbPool;
async fn to_apub(&self, pool: &DbPool) -> Result<Note, LemmyError> {
let creator_id = self.creator_id;
}
#[async_trait::async_trait(?Send)]
-impl FromApub for PrivateMessage {
+impl FromApub for ApubPrivateMessage {
type ApubType = Note;
+ type DataType = LemmyContext;
async fn from_apub(
note: &Note,
context: &LemmyContext,
expected_domain: &Url,
request_counter: &mut i32,
- ) -> Result<PrivateMessage, LemmyError> {
+ ) -> Result<ApubPrivateMessage, LemmyError> {
let ap_id = Some(note.id(expected_domain)?.clone().into());
let creator = note
.attributed_to
ap_id,
local: Some(false),
};
- Ok(
- blocking(context.pool(), move |conn| {
- PrivateMessage::upsert(conn, &form)
- })
- .await??,
- )
+ let pm = blocking(context.pool(), move |conn| {
+ PrivateMessage::upsert(conn, &form)
+ })
+ .await??;
+ Ok(pm.into())
}
}
) -> Result<(), LemmyError>;
}
+#[async_trait::async_trait(?Send)]
pub trait ApubObject {
type DataType;
/// If this object should be refetched after a certain interval, it should return the last refresh
/// time here. This is mainly used to update remote actors.
fn last_refreshed_at(&self) -> Option<NaiveDateTime>;
/// Try to read the object with given ID from local database. Returns Ok(None) if it doesn't exist.
- fn read_from_apub_id(data: &Self::DataType, object_id: Url) -> Result<Option<Self>, LemmyError>
+ async fn read_from_apub_id(
+ object_id: Url,
+ data: &Self::DataType,
+ ) -> Result<Option<Self>, LemmyError>
where
Self: Sized;
/// Marks the object as deleted in local db. Called when a tombstone is received.
- fn delete(self, data: &Self::DataType) -> Result<(), LemmyError>;
+ async fn delete(self, data: &Self::DataType) -> Result<(), LemmyError>;
}
/// Common methods provided by ActivityPub actors (community and person). Not all methods are
})
}
}
+
+/// Trait for converting an object or actor into the respective ActivityPub type.
+#[async_trait::async_trait(?Send)]
+pub trait ToApub {
+ type ApubType;
+ type TombstoneType;
+ type DataType;
+
+ async fn to_apub(&self, data: &Self::DataType) -> Result<Self::ApubType, LemmyError>;
+ fn to_tombstone(&self) -> Result<Self::TombstoneType, LemmyError>;
+}
+
+#[async_trait::async_trait(?Send)]
+pub trait FromApub {
+ type ApubType;
+ type DataType;
+
+ /// Converts an object from ActivityPub type to Lemmy internal type.
+ ///
+ /// * `apub` The object to read from
+ /// * `context` LemmyContext which holds DB pool, HTTP client etc
+ /// * `expected_domain` Domain where the object was received from. None in case of mod action.
+ /// * `mod_action_allowed` True if the object can be a mod activity, ignore `expected_domain` in this case
+ async fn from_apub(
+ apub: &Self::ApubType,
+ data: &Self::DataType,
+ expected_domain: &Url,
+ request_counter: &mut i32,
+ ) -> Result<Self, LemmyError>
+ where
+ Self: Sized;
+}
[dependencies]
lemmy_utils = { version = "=0.13.0", path = "../utils" }
-lemmy_apub_lib = { version = "=0.13.0", path = "../apub_lib" }
diesel = { version = "1.4.8", features = ["postgres","chrono","r2d2","serde_json"] }
diesel_migrations = "1.4.0"
chrono = { version = "0.4.19", features = ["serde"] }
},
traits::{Crud, DeleteableOrRemoveable, Likeable, Saveable},
};
-use chrono::NaiveDateTime;
use diesel::{dsl::*, result::Error, *};
-use lemmy_apub_lib::traits::ApubObject;
-use lemmy_utils::LemmyError;
use url::Url;
impl Comment {
.set(comment_form)
.get_result::<Self>(conn)
}
+ pub fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, Error> {
+ use crate::schema::comment::dsl::*;
+ let object_id: DbUrl = object_id.into();
+ Ok(
+ comment
+ .filter(ap_id.eq(object_id))
+ .first::<Comment>(conn)
+ .ok()
+ .map(Into::into),
+ )
+ }
}
impl Crud for Comment {
}
}
-impl ApubObject for Comment {
- type DataType = PgConnection;
-
- fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
- None
- }
-
- fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, LemmyError> {
- use crate::schema::comment::dsl::*;
- let object_id: DbUrl = object_id.into();
- Ok(comment.filter(ap_id.eq(object_id)).first::<Self>(conn).ok())
- }
-
- fn delete(self, conn: &PgConnection) -> Result<(), LemmyError> {
- Comment::update_deleted(conn, self.id, true)?;
- Ok(())
- }
-}
-
#[cfg(test)]
mod tests {
use crate::{
},
traits::{Bannable, Crud, DeleteableOrRemoveable, Followable, Joinable},
};
-use chrono::NaiveDateTime;
use diesel::{dsl::*, result::Error, ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl};
-use lemmy_apub_lib::traits::{ActorType, ApubObject};
-use lemmy_utils::LemmyError;
use url::Url;
mod safe_type {
.set(community_form)
.get_result::<Self>(conn)
}
+ pub fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, Error> {
+ use crate::schema::community::dsl::*;
+ let object_id: DbUrl = object_id.into();
+ Ok(
+ community
+ .filter(actor_id.eq(object_id))
+ .first::<Community>(conn)
+ .ok()
+ .map(Into::into),
+ )
+ }
}
impl Joinable for CommunityModerator {
}
}
-impl ApubObject for Community {
- type DataType = PgConnection;
-
- fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
- Some(self.last_refreshed_at)
- }
-
- fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, LemmyError> {
- use crate::schema::community::dsl::*;
- let object_id: DbUrl = object_id.into();
- Ok(
- community
- .filter(actor_id.eq(object_id))
- .first::<Self>(conn)
- .ok(),
- )
- }
-
- fn delete(self, conn: &PgConnection) -> Result<(), LemmyError> {
- use crate::schema::community::dsl::*;
- diesel::update(community.find(self.id))
- .set((deleted.eq(true), updated.eq(naive_now())))
- .get_result::<Self>(conn)?;
- Ok(())
- }
-}
-
-impl ActorType for Community {
- fn is_local(&self) -> bool {
- self.local
- }
- fn actor_id(&self) -> Url {
- self.actor_id.to_owned().into()
- }
- fn name(&self) -> String {
- self.name.clone()
- }
- fn public_key(&self) -> Option<String> {
- self.public_key.to_owned()
- }
- fn private_key(&self) -> Option<String> {
- self.private_key.to_owned()
- }
-
- fn inbox_url(&self) -> Url {
- self.inbox_url.clone().into()
- }
-
- fn shared_inbox_url(&self) -> Option<Url> {
- self.shared_inbox_url.clone().map(|s| s.into_inner())
- }
-}
-
#[cfg(test)]
mod tests {
use crate::{
source::person::{Person, PersonForm},
traits::Crud,
};
-use chrono::NaiveDateTime;
use diesel::{dsl::*, result::Error, ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl, *};
-use lemmy_apub_lib::traits::{ActorType, ApubObject};
-use lemmy_utils::LemmyError;
use url::Url;
mod safe_type {
.set(person_form)
.get_result::<Self>(conn)
}
-}
-
-impl ApubObject for Person {
- type DataType = PgConnection;
- fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
- Some(self.last_refreshed_at)
- }
-
- fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, LemmyError> {
+ pub fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, Error> {
use crate::schema::person::dsl::*;
let object_id: DbUrl = object_id.into();
Ok(
person
.filter(deleted.eq(false))
.filter(actor_id.eq(object_id))
- .first::<Self>(conn)
- .ok(),
+ .first::<Person>(conn)
+ .ok()
+ .map(Into::into),
)
}
- fn delete(self, conn: &PgConnection) -> Result<(), LemmyError> {
+ pub fn update_deleted(
+ conn: &PgConnection,
+ person_id: PersonId,
+ new_deleted: bool,
+ ) -> Result<Person, Error> {
use crate::schema::person::dsl::*;
- diesel::update(person.find(self.id))
- .set((deleted.eq(true), updated.eq(naive_now())))
- .get_result::<Self>(conn)?;
- Ok(())
- }
-}
-
-impl ActorType for Person {
- fn is_local(&self) -> bool {
- self.local
- }
- fn actor_id(&self) -> Url {
- self.actor_id.to_owned().into_inner()
- }
- fn name(&self) -> String {
- self.name.clone()
- }
-
- fn public_key(&self) -> Option<String> {
- self.public_key.to_owned()
- }
-
- fn private_key(&self) -> Option<String> {
- self.private_key.to_owned()
- }
-
- fn inbox_url(&self) -> Url {
- self.inbox_url.clone().into()
- }
-
- fn shared_inbox_url(&self) -> Option<Url> {
- self.shared_inbox_url.clone().map(|s| s.into_inner())
+ diesel::update(person.find(person_id))
+ .set(deleted.eq(new_deleted))
+ .get_result::<Self>(conn)
}
}
},
traits::{Crud, DeleteableOrRemoveable, Likeable, Readable, Saveable},
};
-use chrono::NaiveDateTime;
use diesel::{dsl::*, result::Error, ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl};
-use lemmy_apub_lib::traits::ApubObject;
-use lemmy_utils::LemmyError;
use url::Url;
impl Crud for Post {
.set(post_form)
.get_result::<Self>(conn)
}
+ pub fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, Error> {
+ use crate::schema::post::dsl::*;
+ let object_id: DbUrl = object_id.into();
+ Ok(
+ post
+ .filter(ap_id.eq(object_id))
+ .first::<Post>(conn)
+ .ok()
+ .map(Into::into),
+ )
+ }
}
impl Likeable for PostLike {
}
}
-impl ApubObject for Post {
- type DataType = PgConnection;
-
- fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
- None
- }
-
- fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, LemmyError> {
- use crate::schema::post::dsl::*;
- let object_id: DbUrl = object_id.into();
- Ok(post.filter(ap_id.eq(object_id)).first::<Self>(conn).ok())
- }
-
- fn delete(self, conn: &PgConnection) -> Result<(), LemmyError> {
- use crate::schema::post::dsl::*;
- diesel::update(post.find(self.id))
- .set((deleted.eq(true), updated.eq(naive_now())))
- .get_result::<Self>(conn)?;
- Ok(())
- }
-}
-
#[cfg(test)]
mod tests {
use crate::{
source::private_message::*,
traits::{Crud, DeleteableOrRemoveable},
};
-use chrono::NaiveDateTime;
use diesel::{dsl::*, result::Error, *};
-use lemmy_apub_lib::traits::ApubObject;
use lemmy_utils::LemmyError;
use url::Url;
.set(private_message_form)
.get_result::<Self>(conn)
}
-}
-
-impl DeleteableOrRemoveable for PrivateMessage {
- fn blank_out_deleted_or_removed_info(mut self) -> Self {
- self.content = "".into();
- self
- }
-}
-
-impl ApubObject for PrivateMessage {
- type DataType = PgConnection;
- fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
- None
- }
-
- fn read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, LemmyError> {
+ pub fn read_from_apub_id(
+ conn: &PgConnection,
+ object_id: Url,
+ ) -> Result<Option<Self>, LemmyError> {
use crate::schema::private_message::dsl::*;
let object_id: DbUrl = object_id.into();
Ok(
private_message
.filter(ap_id.eq(object_id))
- .first::<Self>(conn)
- .ok(),
+ .first::<PrivateMessage>(conn)
+ .ok()
+ .map(Into::into),
)
}
+}
- fn delete(self, _conn: &PgConnection) -> Result<(), LemmyError> {
- // do nothing, because pm can't be fetched over http
- unimplemented!()
+impl DeleteableOrRemoveable for PrivateMessage {
+ fn blank_out_deleted_or_removed_info(mut self) -> Self {
+ self.content = "".into();
+ self
}
}
};
use lemmy_db_schema::{
newtypes::{CommentId, CommunityId, LocalUserId, PersonId, PostId, PrivateMessageId},
- traits::DeleteableOrRemoveable,
+ source::{
+ comment::Comment,
+ person::Person,
+ person_mention::{PersonMention, PersonMentionForm},
+ post::Post,
+ },
+ traits::{Crud, DeleteableOrRemoveable},
};
use lemmy_db_views::{
comment_view::CommentView,
private_message_view::PrivateMessageView,
};
use lemmy_db_views_actor::community_view::CommunityView;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{
+ email::send_email,
+ settings::structs::Settings,
+ utils::MentionData,
+ ConnectionId,
+ LemmyError,
+};
+use log::error;
pub async fn send_post_ws_message<OP: ToString + Send + OperationType + 'static>(
post_id: PostId,
Ok(res)
}
+
+pub async fn send_local_notifs(
+ mentions: Vec<MentionData>,
+ comment: &Comment,
+ person: &Person,
+ post: &Post,
+ do_send_email: bool,
+ context: &LemmyContext,
+) -> Result<Vec<LocalUserId>, LemmyError> {
+ let mut recipient_ids = Vec::new();
+
+ // Send the local mentions
+ for mention in mentions
+ .iter()
+ .filter(|m| m.is_local(&context.settings().hostname) && m.name.ne(&person.name))
+ .collect::<Vec<&MentionData>>()
+ {
+ let mention_name = mention.name.clone();
+ let user_view = blocking(context.pool(), move |conn| {
+ LocalUserView::read_from_name(conn, &mention_name)
+ })
+ .await?;
+ if let Ok(mention_user_view) = user_view {
+ // TODO
+ // At some point, make it so you can't tag the parent creator either
+ // This can cause two notifications, one for reply and the other for mention
+ recipient_ids.push(mention_user_view.local_user.id);
+
+ let user_mention_form = PersonMentionForm {
+ recipient_id: mention_user_view.person.id,
+ comment_id: comment.id,
+ read: None,
+ };
+
+ // Allow this to fail softly, since comment edits might re-update or replace it
+ // Let the uniqueness handle this fail
+ blocking(context.pool(), move |conn| {
+ PersonMention::create(conn, &user_mention_form)
+ })
+ .await?
+ .ok();
+
+ // Send an email to those local users that have notifications on
+ if do_send_email {
+ send_email_to_user(
+ &mention_user_view,
+ "Mentioned by",
+ "Person Mention",
+ &comment.content,
+ &context.settings(),
+ )
+ }
+ }
+ }
+
+ // Send notifs to the parent commenter / poster
+ match comment.parent_id {
+ Some(parent_id) => {
+ let parent_comment =
+ blocking(context.pool(), move |conn| Comment::read(conn, parent_id)).await?;
+ if let Ok(parent_comment) = parent_comment {
+ // Don't send a notif to yourself
+ if parent_comment.creator_id != person.id {
+ // Get the parent commenter local_user
+ let user_view = blocking(context.pool(), move |conn| {
+ LocalUserView::read_person(conn, parent_comment.creator_id)
+ })
+ .await?;
+ if let Ok(parent_user_view) = user_view {
+ recipient_ids.push(parent_user_view.local_user.id);
+
+ if do_send_email {
+ send_email_to_user(
+ &parent_user_view,
+ "Reply from",
+ "Comment Reply",
+ &comment.content,
+ &context.settings(),
+ )
+ }
+ }
+ }
+ }
+ }
+ // Its a post
+ None => {
+ if post.creator_id != person.id {
+ let creator_id = post.creator_id;
+ let parent_user = blocking(context.pool(), move |conn| {
+ LocalUserView::read_person(conn, creator_id)
+ })
+ .await?;
+ if let Ok(parent_user_view) = parent_user {
+ recipient_ids.push(parent_user_view.local_user.id);
+
+ if do_send_email {
+ send_email_to_user(
+ &parent_user_view,
+ "Reply from",
+ "Post Reply",
+ &comment.content,
+ &context.settings(),
+ )
+ }
+ }
+ }
+ }
+ };
+ Ok(recipient_ids)
+}
+
+pub fn send_email_to_user(
+ local_user_view: &LocalUserView,
+ subject_text: &str,
+ body_text: &str,
+ comment_content: &str,
+ settings: &Settings,
+) {
+ if local_user_view.person.banned || !local_user_view.local_user.send_notifications_to_email {
+ return;
+ }
+
+ if let Some(user_email) = &local_user_view.local_user.email {
+ let subject = &format!(
+ "{} - {} {}",
+ subject_text, settings.hostname, local_user_view.person.name,
+ );
+ let html = &format!(
+ "<h1>{}</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
+ body_text,
+ local_user_view.person.name,
+ comment_content,
+ settings.get_protocol_and_hostname()
+ );
+ match send_email(
+ subject,
+ user_email,
+ &local_user_view.person.name,
+ html,
+ settings,
+ ) {
+ Ok(_o) => _o,
+ Err(e) => error!("{}", e),
+ };
+ }
+}