mirror of https://github.com/LemmyNet/lemmy
Merge branch 'main' into simplify-config
commit
65acc29ae4
@ -1 +1 @@
|
||||
0.11.2
|
||||
0.11.3
|
||||
|
@ -1,71 +0,0 @@
|
||||
use crate::{
|
||||
activities::{
|
||||
comment::{get_notif_recipients, send_websocket_message},
|
||||
verify_activity,
|
||||
verify_person_in_community,
|
||||
},
|
||||
objects::FromApub,
|
||||
NoteExt,
|
||||
};
|
||||
use activitystreams::{activity::kind::CreateType, base::BaseExt};
|
||||
use lemmy_apub_lib::{verify_domains_match_opt, ActivityCommonFields, ActivityHandler, PublicUrl};
|
||||
use lemmy_db_schema::source::comment::Comment;
|
||||
use lemmy_utils::LemmyError;
|
||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateComment {
|
||||
to: PublicUrl,
|
||||
object: NoteExt,
|
||||
cc: Vec<Url>,
|
||||
#[serde(rename = "type")]
|
||||
kind: CreateType,
|
||||
#[serde(flatten)]
|
||||
common: ActivityCommonFields,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ActivityHandler for CreateComment {
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
verify_activity(self.common())?;
|
||||
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
|
||||
verify_domains_match_opt(&self.common.actor, self.object.id_unchecked())?;
|
||||
// TODO: should add a check that the correct community is in cc (probably needs changes to
|
||||
// comment deserialization)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn receive(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let comment = Comment::from_apub(
|
||||
&self.object,
|
||||
context,
|
||||
self.common.actor.clone(),
|
||||
request_counter,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
let recipients =
|
||||
get_notif_recipients(&self.common.actor, &comment, context, request_counter).await?;
|
||||
send_websocket_message(
|
||||
comment.id,
|
||||
recipients,
|
||||
UserOperationCrud::CreateComment,
|
||||
context,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
fn common(&self) -> &ActivityCommonFields {
|
||||
&self.common
|
||||
}
|
||||
}
|
@ -0,0 +1,131 @@
|
||||
use crate::{
|
||||
activities::{
|
||||
comment::{collect_non_local_mentions, get_notif_recipients, send_websocket_message},
|
||||
community::announce::AnnouncableActivities,
|
||||
extract_community,
|
||||
generate_activity_id,
|
||||
verify_activity,
|
||||
verify_person_in_community,
|
||||
CreateOrUpdateType,
|
||||
},
|
||||
activity_queue::send_to_community_new,
|
||||
extensions::context::lemmy_context,
|
||||
objects::{comment::Note, FromApub, ToApub},
|
||||
ActorType,
|
||||
};
|
||||
use activitystreams::link::Mention;
|
||||
use lemmy_api_common::blocking;
|
||||
use lemmy_apub_lib::{
|
||||
values::PublicUrl,
|
||||
verify_domains_match,
|
||||
ActivityCommonFields,
|
||||
ActivityHandler,
|
||||
};
|
||||
use lemmy_db_queries::Crud;
|
||||
use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post};
|
||||
use lemmy_utils::LemmyError;
|
||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateOrUpdateComment {
|
||||
to: PublicUrl,
|
||||
object: Note,
|
||||
cc: Vec<Url>,
|
||||
tag: Vec<Mention>,
|
||||
#[serde(rename = "type")]
|
||||
kind: CreateOrUpdateType,
|
||||
#[serde(flatten)]
|
||||
common: ActivityCommonFields,
|
||||
}
|
||||
|
||||
impl CreateOrUpdateComment {
|
||||
pub async fn send(
|
||||
comment: &Comment,
|
||||
actor: &Person,
|
||||
kind: CreateOrUpdateType,
|
||||
context: &LemmyContext,
|
||||
) -> Result<(), LemmyError> {
|
||||
// TODO: might be helpful to add a comment method to retrieve community directly
|
||||
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| {
|
||||
Community::read(conn, community_id)
|
||||
})
|
||||
.await??;
|
||||
|
||||
let id = generate_activity_id(kind.clone())?;
|
||||
let maa = collect_non_local_mentions(comment, &community, context).await?;
|
||||
|
||||
let create_or_update = CreateOrUpdateComment {
|
||||
to: PublicUrl::Public,
|
||||
object: comment.to_apub(context.pool()).await?,
|
||||
cc: maa.ccs,
|
||||
tag: maa.tags,
|
||||
kind,
|
||||
common: ActivityCommonFields {
|
||||
context: lemmy_context(),
|
||||
id: id.clone(),
|
||||
actor: actor.actor_id(),
|
||||
unparsed: Default::default(),
|
||||
},
|
||||
};
|
||||
|
||||
let activity = AnnouncableActivities::CreateOrUpdateComment(create_or_update);
|
||||
send_to_community_new(activity, &id, actor, &community, maa.inboxes, context).await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ActivityHandler for CreateOrUpdateComment {
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let community = extract_community(&self.cc, context, request_counter).await?;
|
||||
|
||||
verify_activity(self.common())?;
|
||||
verify_person_in_community(
|
||||
&self.common.actor,
|
||||
&community.actor_id(),
|
||||
context,
|
||||
request_counter,
|
||||
)
|
||||
.await?;
|
||||
verify_domains_match(&self.common.actor, &self.object.id)?;
|
||||
// TODO: should add a check that the correct community is in cc (probably needs changes to
|
||||
// comment deserialization)
|
||||
self.object.verify(context, request_counter).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn receive(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let comment = Comment::from_apub(
|
||||
&self.object,
|
||||
context,
|
||||
self.common.actor.clone(),
|
||||
request_counter,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
let recipients =
|
||||
get_notif_recipients(&self.common.actor, &comment, context, request_counter).await?;
|
||||
let notif_type = match self.kind {
|
||||
CreateOrUpdateType::Create => UserOperationCrud::CreateComment,
|
||||
CreateOrUpdateType::Update => UserOperationCrud::EditComment,
|
||||
};
|
||||
send_websocket_message(comment.id, recipients, notif_type, context).await
|
||||
}
|
||||
|
||||
fn common(&self) -> &ActivityCommonFields {
|
||||
&self.common
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
use crate::activities::{comment::send_websocket_message, verify_mod_action};
|
||||
use activitystreams::activity::kind::RemoveType;
|
||||
use lemmy_api_common::blocking;
|
||||
use crate::{check_is_apub_id_valid, fetcher::objects::get_or_fetch_and_insert_comment};
|
||||
use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl};
|
||||
use lemmy_db_queries::source::comment::Comment_;
|
||||
use lemmy_db_schema::source::comment::Comment;
|
||||
use lemmy_utils::LemmyError;
|
||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RemoveComment {
|
||||
to: PublicUrl,
|
||||
pub(in crate::activities::comment) object: Url,
|
||||
cc: [Url; 1],
|
||||
#[serde(rename = "type")]
|
||||
kind: RemoveType,
|
||||
#[serde(flatten)]
|
||||
common: ActivityCommonFields,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ActivityHandlerNew for RemoveComment {
|
||||
async fn verify(&self, context: &LemmyContext, _: &mut i32) -> Result<(), LemmyError> {
|
||||
verify_domains_match(&self.common.actor, self.common.id_unchecked())?;
|
||||
check_is_apub_id_valid(&self.common.actor, false)?;
|
||||
verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await
|
||||
}
|
||||
|
||||
async fn receive(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let comment = get_or_fetch_and_insert_comment(&self.object, context, request_counter).await?;
|
||||
|
||||
let removed_comment = blocking(context.pool(), move |conn| {
|
||||
Comment::update_removed(conn, comment.id, true)
|
||||
})
|
||||
.await??;
|
||||
|
||||
send_websocket_message(
|
||||
removed_comment.id,
|
||||
vec![],
|
||||
UserOperationCrud::EditComment,
|
||||
context,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
fn common(&self) -> &ActivityCommonFields {
|
||||
&self.common
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
use crate::activities::{
|
||||
comment::{remove::RemoveComment, send_websocket_message},
|
||||
verify_mod_action,
|
||||
};
|
||||
use activitystreams::activity::kind::UndoType;
|
||||
use lemmy_api_common::blocking;
|
||||
use crate::{check_is_apub_id_valid, fetcher::objects::get_or_fetch_and_insert_comment};
|
||||
use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandlerNew, PublicUrl};
|
||||
use lemmy_db_queries::source::comment::Comment_;
|
||||
use lemmy_db_schema::source::comment::Comment;
|
||||
use lemmy_utils::LemmyError;
|
||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UndoRemoveComment {
|
||||
to: PublicUrl,
|
||||
object: RemoveComment,
|
||||
cc: [Url; 1],
|
||||
#[serde(rename = "type")]
|
||||
kind: UndoType,
|
||||
#[serde(flatten)]
|
||||
common: ActivityCommonFields,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ActivityHandlerNew for UndoRemoveComment {
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
verify_domains_match(&self.common.actor, self.common.id_unchecked())?;
|
||||
check_is_apub_id_valid(&self.common.actor, false)?;
|
||||
verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
|
||||
self.object.verify(context, request_counter).await
|
||||
}
|
||||
|
||||
async fn receive(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let comment =
|
||||
get_or_fetch_and_insert_comment(&self.object.object, context, request_counter).await?;
|
||||
|
||||
let removed_comment = blocking(context.pool(), move |conn| {
|
||||
Comment::update_removed(conn, comment.id, false)
|
||||
})
|
||||
.await??;
|
||||
|
||||
send_websocket_message(
|
||||
removed_comment.id,
|
||||
vec![],
|
||||
UserOperationCrud::EditComment,
|
||||
context,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
fn common(&self) -> &ActivityCommonFields {
|
||||
&self.common
|
||||
}
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
use crate::{
|
||||
activities::{
|
||||
comment::{get_notif_recipients, send_websocket_message},
|
||||
verify_activity,
|
||||
verify_person_in_community,
|
||||
},
|
||||
objects::FromApub,
|
||||
NoteExt,
|
||||
};
|
||||
use activitystreams::{activity::kind::UpdateType, base::BaseExt};
|
||||
use lemmy_apub_lib::{verify_domains_match_opt, ActivityCommonFields, ActivityHandler, PublicUrl};
|
||||
use lemmy_db_schema::source::comment::Comment;
|
||||
use lemmy_utils::LemmyError;
|
||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UpdateComment {
|
||||
to: PublicUrl,
|
||||
object: NoteExt,
|
||||
cc: Vec<Url>,
|
||||
#[serde(rename = "type")]
|
||||
kind: UpdateType,
|
||||
#[serde(flatten)]
|
||||
common: ActivityCommonFields,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ActivityHandler for UpdateComment {
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
verify_activity(self.common())?;
|
||||
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
|
||||
verify_domains_match_opt(&self.common.actor, self.object.id_unchecked())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn receive(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let comment = Comment::from_apub(
|
||||
&self.object,
|
||||
context,
|
||||
self.common.actor.clone(),
|
||||
request_counter,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let recipients =
|
||||
get_notif_recipients(&self.common.actor, &comment, context, request_counter).await?;
|
||||
send_websocket_message(
|
||||
comment.id,
|
||||
recipients,
|
||||
UserOperationCrud::EditComment,
|
||||
context,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
fn common(&self) -> &ActivityCommonFields {
|
||||
&self.common
|
||||
}
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
use crate::{
|
||||
activities::{post::send_websocket_message, verify_activity, verify_person_in_community},
|
||||
fetcher::person::get_or_fetch_and_upsert_person,
|
||||
objects::FromApub,
|
||||
ActorType,
|
||||
PageExt,
|
||||
};
|
||||
use activitystreams::{activity::kind::CreateType, base::BaseExt};
|
||||
use lemmy_apub_lib::{verify_domains_match_opt, ActivityCommonFields, ActivityHandler, PublicUrl};
|
||||
use lemmy_db_schema::source::post::Post;
|
||||
use lemmy_utils::LemmyError;
|
||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreatePost {
|
||||
to: PublicUrl,
|
||||
object: PageExt,
|
||||
cc: Vec<Url>,
|
||||
#[serde(rename = "type")]
|
||||
kind: CreateType,
|
||||
#[serde(flatten)]
|
||||
common: ActivityCommonFields,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ActivityHandler for CreatePost {
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
verify_activity(self.common())?;
|
||||
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
|
||||
verify_domains_match_opt(&self.common.actor, self.object.id_unchecked())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn receive(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let actor =
|
||||
get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
|
||||
let post = Post::from_apub(
|
||||
&self.object,
|
||||
context,
|
||||
actor.actor_id(),
|
||||
request_counter,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
send_websocket_message(post.id, UserOperationCrud::CreatePost, context).await
|
||||
}
|
||||
|
||||
fn common(&self) -> &ActivityCommonFields {
|
||||
&self.common
|
||||
}
|
||||
}
|
@ -0,0 +1,142 @@
|
||||
use crate::{
|
||||
activities::{
|
||||
community::announce::AnnouncableActivities,
|
||||
extract_community,
|
||||
generate_activity_id,
|
||||
post::send_websocket_message,
|
||||
verify_activity,
|
||||
verify_mod_action,
|
||||
verify_person_in_community,
|
||||
CreateOrUpdateType,
|
||||
},
|
||||
activity_queue::send_to_community_new,
|
||||
extensions::context::lemmy_context,
|
||||
fetcher::person::get_or_fetch_and_upsert_person,
|
||||
objects::{post::Page, FromApub, ToApub},
|
||||
ActorType,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use lemmy_api_common::blocking;
|
||||
use lemmy_apub_lib::{
|
||||
values::PublicUrl,
|
||||
verify_domains_match,
|
||||
verify_urls_match,
|
||||
ActivityCommonFields,
|
||||
ActivityHandler,
|
||||
};
|
||||
use lemmy_db_queries::Crud;
|
||||
use lemmy_db_schema::source::{community::Community, person::Person, post::Post};
|
||||
use lemmy_utils::LemmyError;
|
||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateOrUpdatePost {
|
||||
to: PublicUrl,
|
||||
object: Page,
|
||||
cc: [Url; 1],
|
||||
#[serde(rename = "type")]
|
||||
kind: CreateOrUpdateType,
|
||||
#[serde(flatten)]
|
||||
common: ActivityCommonFields,
|
||||
}
|
||||
|
||||
impl CreateOrUpdatePost {
|
||||
pub async fn send(
|
||||
post: &Post,
|
||||
actor: &Person,
|
||||
kind: CreateOrUpdateType,
|
||||
context: &LemmyContext,
|
||||
) -> Result<(), LemmyError> {
|
||||
let community_id = post.community_id;
|
||||
let community = blocking(context.pool(), move |conn| {
|
||||
Community::read(conn, community_id)
|
||||
})
|
||||
.await??;
|
||||
|
||||
let id = generate_activity_id(kind.clone())?;
|
||||
let create_or_update = CreateOrUpdatePost {
|
||||
to: PublicUrl::Public,
|
||||
object: post.to_apub(context.pool()).await?,
|
||||
cc: [community.actor_id()],
|
||||
kind,
|
||||
common: ActivityCommonFields {
|
||||
context: lemmy_context(),
|
||||
id: id.clone(),
|
||||
actor: actor.actor_id(),
|
||||
unparsed: Default::default(),
|
||||
},
|
||||
};
|
||||
|
||||
let activity = AnnouncableActivities::CreateOrUpdatePost(Box::new(create_or_update));
|
||||
send_to_community_new(activity, &id, actor, &community, vec![], context).await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ActivityHandler for CreateOrUpdatePost {
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
verify_activity(self.common())?;
|
||||
let community = extract_community(&self.cc, context, request_counter).await?;
|
||||
let community_id = community.actor_id();
|
||||
verify_person_in_community(&self.common.actor, &community_id, context, request_counter).await?;
|
||||
match self.kind {
|
||||
CreateOrUpdateType::Create => {
|
||||
verify_domains_match(&self.common.actor, &self.object.id)?;
|
||||
verify_urls_match(&self.common.actor, &self.object.attributed_to)?;
|
||||
// Check that the post isnt locked or stickied, as that isnt possible for newly created posts.
|
||||
// However, when fetching a remote post we generate a new create activity with the current
|
||||
// locked/stickied value, so this check may fail. So only check if its a local community,
|
||||
// because then we will definitely receive all create and update activities separately.
|
||||
let is_stickied_or_locked =
|
||||
self.object.stickied == Some(true) || self.object.comments_enabled == Some(false);
|
||||
if community.local && is_stickied_or_locked {
|
||||
return Err(anyhow!("New post cannot be stickied or locked").into());
|
||||
}
|
||||
}
|
||||
CreateOrUpdateType::Update => {
|
||||
let is_mod_action = self.object.is_mod_action(context.pool()).await?;
|
||||
if is_mod_action {
|
||||
verify_mod_action(&self.common.actor, community_id, context).await?;
|
||||
} else {
|
||||
verify_domains_match(&self.common.actor, &self.object.id)?;
|
||||
verify_urls_match(&self.common.actor, &self.object.attributed_to)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.object.verify(context, request_counter).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn receive(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let actor =
|
||||
get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
|
||||
let post = Post::from_apub(
|
||||
&self.object,
|
||||
context,
|
||||
actor.actor_id(),
|
||||
request_counter,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let notif_type = match self.kind {
|
||||
CreateOrUpdateType::Create => UserOperationCrud::CreatePost,
|
||||
CreateOrUpdateType::Update => UserOperationCrud::EditPost,
|
||||
};
|
||||
send_websocket_message(post.id, notif_type, context).await
|
||||
}
|
||||
|
||||
fn common(&self) -> &ActivityCommonFields {
|
||||
&self.common
|
||||
}
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
use crate::{
|
||||
activities::{
|
||||
post::send_websocket_message,
|
||||
verify_activity,
|
||||
verify_mod_action,
|
||||
verify_person_in_community,
|
||||
},
|
||||
objects::{FromApub, FromApubToForm},
|
||||
ActorType,
|
||||
PageExt,
|
||||
};
|
||||
use activitystreams::{activity::kind::UpdateType, base::BaseExt};
|
||||
use anyhow::Context;
|
||||
use lemmy_api_common::blocking;
|
||||
use lemmy_apub_lib::{verify_domains_match_opt, ActivityCommonFields, ActivityHandler, PublicUrl};
|
||||
use lemmy_db_queries::ApubObject;
|
||||
use lemmy_db_schema::{
|
||||
source::post::{Post, PostForm},
|
||||
DbUrl,
|
||||
};
|
||||
use lemmy_utils::{location_info, LemmyError};
|
||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UpdatePost {
|
||||
to: PublicUrl,
|
||||
object: PageExt,
|
||||
cc: Vec<Url>,
|
||||
#[serde(rename = "type")]
|
||||
kind: UpdateType,
|
||||
#[serde(flatten)]
|
||||
common: ActivityCommonFields,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ActivityHandler for UpdatePost {
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
verify_activity(self.common())?;
|
||||
let community =
|
||||
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
|
||||
|
||||
let temp_post = PostForm::from_apub(
|
||||
&self.object,
|
||||
context,
|
||||
self.common.actor.clone(),
|
||||
request_counter,
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
let post_id: DbUrl = temp_post.ap_id.context(location_info!())?;
|
||||
let old_post = blocking(context.pool(), move |conn| {
|
||||
Post::read_from_apub_id(conn, &post_id)
|
||||
})
|
||||
.await??;
|
||||
let stickied = temp_post.stickied.context(location_info!())?;
|
||||
let locked = temp_post.locked.context(location_info!())?;
|
||||
// community mod changed locked/sticky status
|
||||
if (stickied != old_post.stickied) || (locked != old_post.locked) {
|
||||
verify_mod_action(&self.common.actor, community.actor_id(), context).await?;
|
||||
}
|
||||
// user edited their own post
|
||||
else {
|
||||
verify_domains_match_opt(&self.common.actor, self.object.id_unchecked())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn receive(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let post = Post::from_apub(
|
||||
&self.object,
|
||||
context,
|
||||
self.common.actor.clone(),
|
||||
request_counter,
|
||||
// TODO: we already check here if the mod action is valid, can remove that check param
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
|
||||
send_websocket_message(post.id, UserOperationCrud::EditPost, context).await
|
||||
}
|
||||
|
||||
fn common(&self) -> &ActivityCommonFields {
|
||||
&self.common
|
||||
}
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
use crate::{
|
||||
activities::{private_message::send_websocket_message, verify_activity, verify_person},
|
||||
objects::FromApub,
|
||||
NoteExt,
|
||||
};
|
||||
use activitystreams::{activity::kind::CreateType, base::BaseExt};
|
||||
use lemmy_apub_lib::{verify_domains_match_opt, ActivityCommonFields, ActivityHandler};
|
||||
use lemmy_db_schema::source::private_message::PrivateMessage;
|
||||
use lemmy_utils::LemmyError;
|
||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreatePrivateMessage {
|
||||
to: Url,
|
||||
object: NoteExt,
|
||||
#[serde(rename = "type")]
|
||||
kind: CreateType,
|
||||
#[serde(flatten)]
|
||||
common: ActivityCommonFields,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ActivityHandler for CreatePrivateMessage {
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
verify_activity(self.common())?;
|
||||
verify_person(&self.common.actor, context, request_counter).await?;
|
||||
verify_domains_match_opt(&self.common.actor, self.object.id_unchecked())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn receive(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let private_message = PrivateMessage::from_apub(
|
||||
&self.object,
|
||||
context,
|
||||
self.common.actor.clone(),
|
||||
request_counter,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
send_websocket_message(
|
||||
private_message.id,
|
||||
UserOperationCrud::CreatePrivateMessage,
|
||||
context,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn common(&self) -> &ActivityCommonFields {
|
||||
&self.common
|
||||
}
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
use crate::{
|
||||
activities::{
|
||||
generate_activity_id,
|
||||
private_message::send_websocket_message,
|
||||
verify_activity,
|
||||
verify_person,
|
||||
CreateOrUpdateType,
|
||||
},
|
||||
activity_queue::send_activity_new,
|
||||
extensions::context::lemmy_context,
|
||||
objects::{private_message::Note, FromApub, ToApub},
|
||||
ActorType,
|
||||
};
|
||||
use lemmy_api_common::blocking;
|
||||
use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandler};
|
||||
use lemmy_db_queries::Crud;
|
||||
use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage};
|
||||
use lemmy_utils::LemmyError;
|
||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateOrUpdatePrivateMessage {
|
||||
to: Url,
|
||||
object: Note,
|
||||
#[serde(rename = "type")]
|
||||
kind: CreateOrUpdateType,
|
||||
#[serde(flatten)]
|
||||
common: ActivityCommonFields,
|
||||
}
|
||||
|
||||
impl CreateOrUpdatePrivateMessage {
|
||||
pub async fn send(
|
||||
private_message: &PrivateMessage,
|
||||
actor: &Person,
|
||||
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 id = generate_activity_id(kind.clone())?;
|
||||
let create_or_update = CreateOrUpdatePrivateMessage {
|
||||
to: recipient.actor_id(),
|
||||
object: private_message.to_apub(context.pool()).await?,
|
||||
kind,
|
||||
common: ActivityCommonFields {
|
||||
context: lemmy_context(),
|
||||
id: id.clone(),
|
||||
actor: actor.actor_id(),
|
||||
unparsed: Default::default(),
|
||||
},
|
||||
};
|
||||
send_activity_new(
|
||||
context,
|
||||
&create_or_update,
|
||||
&id,
|
||||
actor,
|
||||
vec![recipient.get_shared_inbox_or_inbox_url()],
|
||||
true,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ActivityHandler for CreateOrUpdatePrivateMessage {
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
verify_activity(self.common())?;
|
||||
verify_person(&self.common.actor, context, request_counter).await?;
|
||||
verify_domains_match(&self.common.actor, &self.object.id)?;
|
||||
self.object.verify(context, request_counter).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn receive(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let private_message = PrivateMessage::from_apub(
|
||||
&self.object,
|
||||
context,
|
||||
self.common.actor.clone(),
|
||||
request_counter,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let notif_type = match self.kind {
|
||||
CreateOrUpdateType::Create => UserOperationCrud::CreatePrivateMessage,
|
||||
CreateOrUpdateType::Update => UserOperationCrud::EditPrivateMessage,
|
||||
};
|
||||
send_websocket_message(private_message.id, notif_type, context).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn common(&self) -> &ActivityCommonFields {
|
||||
&self.common
|
||||
}
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
use crate::{
|
||||
activities::{private_message::send_websocket_message, verify_activity, verify_person},
|
||||
objects::FromApub,
|
||||
NoteExt,
|
||||
};
|
||||
use activitystreams::{activity::kind::UpdateType, base::BaseExt};
|
||||
use lemmy_apub_lib::{verify_domains_match_opt, ActivityCommonFields, ActivityHandler};
|
||||
use lemmy_db_schema::source::private_message::PrivateMessage;
|
||||
use lemmy_utils::LemmyError;
|
||||
use lemmy_websocket::{LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UpdatePrivateMessage {
|
||||
to: Url,
|
||||
object: NoteExt,
|
||||
#[serde(rename = "type")]
|
||||
kind: UpdateType,
|
||||
#[serde(flatten)]
|
||||
common: ActivityCommonFields,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ActivityHandler for UpdatePrivateMessage {
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
verify_activity(self.common())?;
|
||||
verify_person(&self.common.actor, context, request_counter).await?;
|
||||
verify_domains_match_opt(&self.common.actor, self.object.id_unchecked())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn receive(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let private_message = PrivateMessage::from_apub(
|
||||
&self.object,
|
||||
context,
|
||||
self.common.actor.clone(),
|
||||
request_counter,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
send_websocket_message(
|
||||
private_message.id,
|
||||
UserOperationCrud::EditPrivateMessage,
|
||||
context,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn common(&self) -> &ActivityCommonFields {
|
||||
&self.common
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
use crate::activities::{
|
||||
verify_activity,
|
||||
verify_person_in_community,
|
||||
voting::receive_like_or_dislike,
|
||||
};
|
||||
use activitystreams::activity::kind::DislikeType;
|
||||
use lemmy_apub_lib::{ActivityCommonFields, ActivityHandler, PublicUrl};
|
||||
use lemmy_utils::LemmyError;
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DislikePostOrComment {
|
||||
to: PublicUrl,
|
||||
pub(in crate::activities) object: Url,
|
||||
cc: [Url; 1],
|
||||
#[serde(rename = "type")]
|
||||
kind: DislikeType,
|
||||
#[serde(flatten)]
|
||||
common: ActivityCommonFields,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ActivityHandler for DislikePostOrComment {
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
verify_activity(self.common())?;
|
||||
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn receive(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
receive_like_or_dislike(
|
||||
-1,
|
||||
&self.common.actor,
|
||||
&self.object,
|
||||
context,
|
||||
request_counter,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
fn common(&self) -> &ActivityCommonFields {
|
||||
&self.common
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
use crate::activities::{
|
||||
verify_activity,
|
||||
verify_person_in_community,
|
||||
voting::receive_like_or_dislike,
|
||||
};
|
||||
use activitystreams::activity::kind::LikeType;
|
||||
use lemmy_apub_lib::{ActivityCommonFields, ActivityHandler, PublicUrl};
|
||||
use lemmy_utils::LemmyError;
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct LikePostOrComment {
|
||||
to: PublicUrl,
|
||||
pub(in crate::activities::voting) object: Url,
|
||||
cc: [Url; 1],
|
||||
#[serde(rename = "type")]
|
||||
kind: LikeType,
|
||||
#[serde(flatten)]
|
||||
common: ActivityCommonFields,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ActivityHandler for LikePostOrComment {
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
verify_activity(self.common())?;
|
||||
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn receive(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
receive_like_or_dislike(
|
||||
1,
|
||||
&self.common.actor,
|
||||
&self.object,
|
||||
context,
|
||||
request_counter,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
fn common(&self) -> &ActivityCommonFields {
|
||||
&self.common
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
use crate::activities::{
|
||||
verify_activity,
|
||||
verify_person_in_community,
|
||||
voting::{dislike::DislikePostOrComment, receive_undo_like_or_dislike},
|
||||
};
|
||||
use activitystreams::activity::kind::UndoType;
|
||||
use lemmy_apub_lib::{verify_urls_match, ActivityCommonFields, ActivityHandler, PublicUrl};
|
||||
use lemmy_utils::LemmyError;
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UndoDislikePostOrComment {
|
||||
to: PublicUrl,
|
||||
object: DislikePostOrComment,
|
||||
cc: [Url; 1],
|
||||
#[serde(rename = "type")]
|
||||
kind: UndoType,
|
||||
#[serde(flatten)]
|
||||
common: ActivityCommonFields,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ActivityHandler for UndoDislikePostOrComment {
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
verify_activity(self.common())?;
|
||||
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
|
||||
verify_urls_match(&self.common.actor, &self.object.common().actor)?;
|
||||
self.object.verify(context, request_counter).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn receive(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
receive_undo_like_or_dislike(
|
||||
&self.common.actor,
|
||||
&self.object.object,
|
||||
context,
|
||||
request_counter,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
fn common(&self) -> &ActivityCommonFields {
|
||||
&self.common
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
use crate::activities::{
|
||||
verify_activity,
|
||||
verify_person_in_community,
|
||||
voting::{like::LikePostOrComment, receive_undo_like_or_dislike},
|
||||
};
|
||||
use activitystreams::activity::kind::UndoType;
|
||||
use lemmy_apub_lib::{verify_urls_match, ActivityCommonFields, ActivityHandler, PublicUrl};
|
||||
use lemmy_utils::LemmyError;
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UndoLikePostOrComment {
|
||||
to: PublicUrl,
|
||||
object: LikePostOrComment,
|
||||
cc: [Url; 1],
|
||||
#[serde(rename = "type")]
|
||||
kind: UndoType,
|
||||
#[serde(flatten)]
|
||||
common: ActivityCommonFields,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ActivityHandler for UndoLikePostOrComment {
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
verify_activity(self.common())?;
|
||||
verify_person_in_community(&self.common.actor, &self.cc, context, request_counter).await?;
|
||||
verify_urls_match(&self.common.actor, &self.object.common().actor)?;
|
||||
self.object.verify(context, request_counter).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn receive(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
receive_undo_like_or_dislike(
|
||||
&self.common.actor,
|
||||
&self.object.object,
|
||||
context,
|
||||
request_counter,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
fn common(&self) -> &ActivityCommonFields {
|
||||
&self.common
|
||||
}
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
use crate::{
|
||||
activities::{
|
||||
community::announce::AnnouncableActivities,
|
||||
generate_activity_id,
|
||||
verify_activity,
|
||||
verify_person_in_community,
|
||||
voting::{
|
||||
undo_vote_comment,
|
||||
undo_vote_post,
|
||||
vote::{Vote, VoteType},
|
||||
},
|
||||
},
|
||||
activity_queue::send_to_community_new,
|
||||
extensions::context::lemmy_context,
|
||||
fetcher::{
|
||||
objects::get_or_fetch_and_insert_post_or_comment,
|
||||
person::get_or_fetch_and_upsert_person,
|
||||
},
|
||||
ActorType,
|
||||
PostOrComment,
|
||||
};
|
||||
use activitystreams::activity::kind::UndoType;
|
||||
use lemmy_api_common::blocking;
|
||||
use lemmy_apub_lib::{values::PublicUrl, verify_urls_match, ActivityCommonFields, ActivityHandler};
|
||||
use lemmy_db_queries::Crud;
|
||||
use lemmy_db_schema::{
|
||||
source::{community::Community, person::Person},
|
||||
CommunityId,
|
||||
};
|
||||
use lemmy_utils::LemmyError;
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use std::ops::Deref;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UndoVote {
|
||||
to: PublicUrl,
|
||||
object: Vote,
|
||||
cc: [Url; 1],
|
||||
#[serde(rename = "type")]
|
||||
kind: UndoType,
|
||||
#[serde(flatten)]
|
||||
common: ActivityCommonFields,
|
||||
}
|
||||
|
||||
impl UndoVote {
|
||||
pub async fn send(
|
||||
object: &PostOrComment,
|
||||
actor: &Person,
|
||||
community_id: CommunityId,
|
||||
kind: VoteType,
|
||||
context: &LemmyContext,
|
||||
) -> Result<(), LemmyError> {
|
||||
let community = blocking(context.pool(), move |conn| {
|
||||
Community::read(conn, community_id)
|
||||
})
|
||||
.await??;
|
||||
let id = generate_activity_id(UndoType::Undo)?;
|
||||
|
||||
let undo_vote = UndoVote {
|
||||
to: PublicUrl::Public,
|
||||
object: Vote {
|
||||
to: PublicUrl::Public,
|
||||
object: object.ap_id(),
|
||||
cc: [community.actor_id()],
|
||||
kind: kind.clone(),
|
||||
common: ActivityCommonFields {
|
||||
context: lemmy_context(),
|
||||
id: generate_activity_id(kind)?,
|
||||
actor: actor.actor_id(),
|
||||
unparsed: Default::default(),
|
||||
},
|
||||
},
|
||||
cc: [community.actor_id()],
|
||||
kind: UndoType::Undo,
|
||||
common: ActivityCommonFields {
|
||||
context: lemmy_context(),
|
||||
id: id.clone(),
|
||||
actor: actor.actor_id(),
|
||||
unparsed: Default::default(),
|
||||
},
|
||||
};
|
||||
let activity = AnnouncableActivities::UndoVote(undo_vote);
|
||||
send_to_community_new(activity, &id, actor, &community, vec![], context).await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ActivityHandler for UndoVote {
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
verify_activity(self.common())?;
|
||||
verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
|
||||
verify_urls_match(&self.common.actor, &self.object.common().actor)?;
|
||||
self.object.verify(context, request_counter).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn receive(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let actor =
|
||||
get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
|
||||
let object =
|
||||
get_or_fetch_and_insert_post_or_comment(&self.object.object, context, request_counter)
|
||||
.await?;
|
||||
match object {
|
||||
PostOrComment::Post(p) => undo_vote_post(actor, p.deref(), context).await,
|
||||
PostOrComment::Comment(c) => undo_vote_comment(actor, c.deref(), context).await,
|
||||
}
|
||||
}
|
||||
|
||||
fn common(&self) -> &ActivityCommonFields {
|
||||
&self.common
|
||||
}
|
||||
}
|
@ -0,0 +1,133 @@
|
||||
use crate::{
|
||||
activities::{
|
||||
community::announce::AnnouncableActivities,
|
||||
generate_activity_id,
|
||||
verify_activity,
|
||||
verify_person_in_community,
|
||||
voting::{vote_comment, vote_post},
|
||||
},
|
||||
activity_queue::send_to_community_new,
|
||||
extensions::context::lemmy_context,
|
||||
fetcher::{
|
||||
objects::get_or_fetch_and_insert_post_or_comment,
|
||||
person::get_or_fetch_and_upsert_person,
|
||||
},
|
||||
ActorType,
|
||||
PostOrComment,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use lemmy_api_common::blocking;
|
||||
use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
|
||||
use lemmy_db_queries::Crud;
|
||||
use lemmy_db_schema::{
|
||||
source::{community::Community, person::Person},
|
||||
CommunityId,
|
||||
};
|
||||
use lemmy_utils::LemmyError;
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{convert::TryFrom, ops::Deref};
|
||||
use strum_macros::ToString;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, ToString, Deserialize, Serialize)]
|
||||
pub enum VoteType {
|
||||
Like,
|
||||
Dislike,
|
||||
}
|
||||
|
||||
impl TryFrom<i16> for VoteType {
|
||||
type Error = LemmyError;
|
||||
|
||||
fn try_from(value: i16) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
1 => Ok(VoteType::Like),
|
||||
-1 => Ok(VoteType::Dislike),
|
||||
_ => Err(anyhow!("invalid vote value").into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&VoteType> for i16 {
|
||||
fn from(value: &VoteType) -> i16 {
|
||||
match value {
|
||||
VoteType::Like => 1,
|
||||
VoteType::Dislike => -1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Vote {
|
||||
pub(in crate::activities::voting) to: PublicUrl,
|
||||
pub(in crate::activities::voting) object: Url,
|
||||
pub(in crate::activities::voting) cc: [Url; 1],
|
||||
#[serde(rename = "type")]
|
||||
pub(in crate::activities::voting) kind: VoteType,
|
||||
#[serde(flatten)]
|
||||
pub(in crate::activities::voting) common: ActivityCommonFields,
|
||||
}
|
||||
|
||||
impl Vote {
|
||||
pub async fn send(
|
||||
object: &PostOrComment,
|
||||
actor: &Person,
|
||||
community_id: CommunityId,
|
||||
kind: VoteType,
|
||||
context: &LemmyContext,
|
||||
) -> Result<(), LemmyError> {
|
||||
let community = blocking(context.pool(), move |conn| {
|
||||
Community::read(conn, community_id)
|
||||
})
|
||||
.await??;
|
||||
let id = generate_activity_id(kind.clone())?;
|
||||
|
||||
let vote = Vote {
|
||||
to: PublicUrl::Public,
|
||||
object: object.ap_id(),
|
||||
cc: [community.actor_id()],
|
||||
kind,
|
||||
common: ActivityCommonFields {
|
||||
context: lemmy_context(),
|
||||
id: id.clone(),
|
||||
actor: actor.actor_id(),
|
||||
unparsed: Default::default(),
|
||||
},
|
||||
};
|
||||
let activity = AnnouncableActivities::Vote(vote);
|
||||
send_to_community_new(activity, &id, actor, &community, vec![], context).await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ActivityHandler for Vote {
|
||||
async fn verify(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
verify_activity(self.common())?;
|
||||
verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn receive(
|
||||
&self,
|
||||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let actor =
|
||||
get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
|
||||
let object =
|
||||
get_or_fetch_and_insert_post_or_comment(&self.object, context, request_counter).await?;
|
||||
match object {
|
||||
PostOrComment::Post(p) => vote_post(&self.kind, actor, p.deref(), context).await,
|
||||
PostOrComment::Comment(c) => vote_comment(&self.kind, actor, c.deref(), context).await,
|
||||
}
|
||||
}
|
||||
|
||||
fn common(&self) -> &ActivityCommonFields {
|
||||
&self.common
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
pub mod context;
|
||||
pub(crate) mod group_extension;
|
||||
pub(crate) mod page_extension;
|
||||
pub(crate) mod person_extension;
|
||||
pub mod signatures;
|
||||
|
@ -1,36 +0,0 @@
|
||||
use activitystreams::unparsed::UnparsedMutExt;
|
||||
use activitystreams_ext::UnparsedExtension;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Activitystreams extension to allow (de)serializing additional Post fields
|
||||
/// `comemnts_enabled` (called 'locked' in Lemmy),
|
||||
/// `sensitive` (called 'nsfw') and `stickied`.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PageExtension {
|
||||
pub comments_enabled: Option<bool>,
|
||||
pub sensitive: Option<bool>,
|
||||
pub stickied: Option<bool>,
|
||||
}
|
||||
|
||||
impl<U> UnparsedExtension<U> for PageExtension
|
||||
where
|
||||
U: UnparsedMutExt,
|
||||
{
|
||||
type Error = serde_json::Error;
|
||||
|
||||
fn try_from_unparsed(unparsed_mut: &mut U) -> Result<Self, Self::Error> {
|
||||
Ok(PageExtension {
|
||||
comments_enabled: unparsed_mut.remove("commentsEnabled")?,
|
||||
sensitive: unparsed_mut.remove("sensitive")?,
|
||||
stickied: unparsed_mut.remove("stickied")?,
|
||||
})
|
||||
}
|
||||
|
||||
fn try_into_unparsed(self, unparsed_mut: &mut U) -> Result<(), Self::Error> {
|
||||
unparsed_mut.insert("commentsEnabled", self.comments_enabled)?;
|
||||
unparsed_mut.insert("sensitive", self.sensitive)?;
|
||||
unparsed_mut.insert("stickied", self.stickied)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
//! The enums here serve to limit a json string value to a single, hardcoded value which can be
|
||||
//! verified at compilation time. When using it as the type of a struct field, the struct can only
|
||||
//! be constructed or deserialized if the field has the exact same value.
|
||||
//!
|
||||
//! If we used String as the field type, any value would be accepted, and we would have to check
|
||||
//! manually at runtime that it contains the expected value.
|
||||
//!
|
||||
//! The enums in [`activitystreams::activity::kind`] work in the same way, and can be used to
|
||||
//! distinguish different activity types.
|
||||
//!
|
||||
//! In the example below, `MyObject` can only be constructed or
|
||||
//! deserialized if `media_type` is `text/markdown`, but not if it is `text/html`.
|
||||
//!
|
||||
//! ```
|
||||
//! use lemmy_apub_lib::values::MediaTypeMarkdown;
|
||||
//! use serde_json::from_str;
|
||||
//! use serde::{Deserialize, Serialize};
|
||||
//!
|
||||
//! #[derive(Deserialize, Serialize)]
|
||||
//! struct MyObject {
|
||||
//! content: String,
|
||||
//! media_type: MediaTypeMarkdown,
|
||||
//! }
|
||||
//!
|
||||
//! let markdown_json = r#"{"content": "**test**", "media_type": "text/markdown"}"#;
|
||||
//! let from_markdown = from_str::<MyObject>(markdown_json);
|
||||
//! assert!(from_markdown.is_ok());
|
||||
//!
|
||||
//! let markdown_html = r#"{"content": "<b>test</b>", "media_type": "text/html"}"#;
|
||||
//! let from_html = from_str::<MyObject>(markdown_html);
|
||||
//! assert!(from_html.is_err());
|
||||
//! ```
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// The identifier used to address activities to the public.
|
||||
///
|
||||
/// <https://www.w3.org/TR/activitypub/#public-addressing>
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub enum PublicUrl {
|
||||
#[serde(rename = "https://www.w3.org/ns/activitystreams#Public")]
|
||||
Public,
|
||||
}
|
||||
|
||||
/// Media type for markdown text.
|
||||
///
|
||||
/// <https://www.iana.org/assignments/media-types/media-types.xhtml>
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub enum MediaTypeMarkdown {
|
||||
#[serde(rename = "text/markdown")]
|
||||
Markdown,
|
||||
}
|
||||
|
||||
/// Media type for HTML text/
|
||||
///
|
||||
/// <https://www.iana.org/assignments/media-types/media-types.xhtml>
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub enum MediaTypeHtml {
|
||||
#[serde(rename = "text/html")]
|
||||
Html,
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
drop trigger post_aggregates_comment_set_deleted on comment;
|
||||
drop function post_aggregates_comment_deleted;
|
||||
|
||||
create or replace function post_aggregates_comment_count()
|
||||
returns trigger language plpgsql
|
||||
as $$
|
||||
begin
|
||||
IF (TG_OP = 'INSERT') THEN
|
||||
update post_aggregates pa
|
||||
set comments = comments + 1,
|
||||
newest_comment_time = NEW.published
|
||||
where pa.post_id = NEW.post_id;
|
||||
|
||||
-- A 2 day necro-bump limit
|
||||
update post_aggregates pa
|
||||
set newest_comment_time_necro = NEW.published
|
||||
where pa.post_id = NEW.post_id
|
||||
and published > ('now'::timestamp - '2 days'::interval);
|
||||
ELSIF (TG_OP = 'DELETE') THEN
|
||||
-- Join to post because that post may not exist anymore
|
||||
update post_aggregates pa
|
||||
set comments = comments - 1
|
||||
from post p
|
||||
where pa.post_id = p.id
|
||||
and pa.post_id = OLD.post_id;
|
||||
END IF;
|
||||
return null;
|
||||
end $$;
|
@ -0,0 +1,61 @@
|
||||
-- Creating a new trigger for when comment.deleted is updated
|
||||
|
||||
create or replace function post_aggregates_comment_deleted()
|
||||
returns trigger language plpgsql
|
||||
as $$
|
||||
begin
|
||||
IF NEW.deleted = TRUE THEN
|
||||
update post_aggregates pa
|
||||
set comments = comments - 1
|
||||
where pa.post_id = NEW.post_id;
|
||||
ELSE
|
||||
update post_aggregates pa
|
||||
set comments = comments + 1
|
||||
where pa.post_id = NEW.post_id;
|
||||
END IF;
|
||||
return null;
|
||||
end $$;
|
||||
|
||||
create trigger post_aggregates_comment_set_deleted
|
||||
after update of deleted on comment
|
||||
for each row
|
||||
execute procedure post_aggregates_comment_deleted();
|
||||
|
||||
-- Fix issue with being able to necro-bump your own post
|
||||
create or replace function post_aggregates_comment_count()
|
||||
returns trigger language plpgsql
|
||||
as $$
|
||||
begin
|
||||
IF (TG_OP = 'INSERT') THEN
|
||||
update post_aggregates pa
|
||||
set comments = comments + 1,
|
||||
newest_comment_time = NEW.published
|
||||
where pa.post_id = NEW.post_id;
|
||||
|
||||
-- A 2 day necro-bump limit
|
||||
update post_aggregates pa
|
||||
set newest_comment_time_necro = NEW.published
|
||||
from post p
|
||||
where pa.post_id = p.id
|
||||
and pa.post_id = NEW.post_id
|
||||
-- Fix issue with being able to necro-bump your own post
|
||||
and NEW.creator_id != p.creator_id
|
||||
and pa.published > ('now'::timestamp - '2 days'::interval);
|
||||
|
||||
ELSIF (TG_OP = 'DELETE') THEN
|
||||
-- Join to post because that post may not exist anymore
|
||||
update post_aggregates pa
|
||||
set comments = comments - 1
|
||||
from post p
|
||||
where pa.post_id = p.id
|
||||
and pa.post_id = OLD.post_id;
|
||||
ELSIF (TG_OP = 'UPDATE') THEN
|
||||
-- Join to post because that post may not exist anymore
|
||||
update post_aggregates pa
|
||||
set comments = comments - 1
|
||||
from post p
|
||||
where pa.post_id = p.id
|
||||
and pa.post_id = OLD.post_id;
|
||||
END IF;
|
||||
return null;
|
||||
end $$;
|
Loading…
Reference in New Issue