-use crate::Perform;
+use crate::{check_report_reason, Perform};
use activitypub_federation::core::object_id::ObjectId;
use actix_web::web::Data;
use lemmy_api_common::{
let local_user_view =
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
- // check size of report and check for whitespace
- let reason = data.reason.trim();
- if reason.is_empty() {
- return Err(LemmyError::from_message("report_reason_required"));
- }
- if reason.chars().count() > 1000 {
- return Err(LemmyError::from_message("report_too_long"));
- }
+ let reason = self.reason.trim();
+ check_report_reason(reason, context)?;
let person_id = local_user_view.person.id;
let comment_id = data.comment_id;
creator_id: person_id,
comment_id,
original_comment_text: comment_view.comment.content,
- reason: data.reason.to_owned(),
+ reason: reason.to_owned(),
};
let report = blocking(context.pool(), move |conn| {
use actix_web::{web, web::Data};
use captcha::Captcha;
-use lemmy_api_common::{comment::*, community::*, person::*, post::*, site::*, websocket::*};
-use lemmy_utils::{error::LemmyError, ConnectionId};
+use lemmy_api_common::{
+ comment::*,
+ community::*,
+ person::*,
+ post::*,
+ private_message::*,
+ site::*,
+ websocket::*,
+};
+use lemmy_utils::{error::LemmyError, utils::check_slurs, ConnectionId};
use lemmy_websocket::{serialize_websocket_message, LemmyContext, UserOperation};
use serde::Deserialize;
mod post;
mod post_report;
mod private_message;
+mod private_message_report;
mod site;
mod websocket;
UserOperation::MarkPrivateMessageAsRead => {
do_websocket_operation::<MarkPrivateMessageAsRead>(context, id, op, data).await
}
+ UserOperation::CreatePrivateMessageReport => {
+ do_websocket_operation::<CreatePrivateMessageReport>(context, id, op, data).await
+ }
+ UserOperation::ResolvePrivateMessageReport => {
+ do_websocket_operation::<ResolvePrivateMessageReport>(context, id, op, data).await
+ }
+ UserOperation::ListPrivateMessageReports => {
+ do_websocket_operation::<ListPrivateMessageReports>(context, id, op, data).await
+ }
// Site ops
UserOperation::GetModlog => do_websocket_operation::<GetModlog>(context, id, op, data).await,
base64::encode(concat_letters)
}
+/// Check size of report and remove whitespace
+pub(crate) fn check_report_reason(reason: &str, context: &LemmyContext) -> Result<(), LemmyError> {
+ check_slurs(reason, &context.settings().slur_regex())?;
+ if reason.is_empty() {
+ return Err(LemmyError::from_message("report_reason_required"));
+ }
+ if reason.chars().count() > 1000 {
+ return Err(LemmyError::from_message("report_too_long"));
+ }
+ Ok(())
+}
+
#[cfg(test)]
mod tests {
use lemmy_api_common::utils::check_validator_time;
person::{GetReportCount, GetReportCountResponse},
utils::{blocking, get_local_user_view_from_jwt},
};
-use lemmy_db_views::structs::{CommentReportView, PostReportView};
+use lemmy_db_views::structs::{CommentReportView, PostReportView, PrivateMessageReportView};
use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
})
.await??;
+ let private_message_reports = if admin && community_id.is_none() {
+ Some(
+ blocking(context.pool(), move |conn| {
+ PrivateMessageReportView::get_report_count(conn)
+ })
+ .await??,
+ )
+ } else {
+ None
+ };
+
let res = GetReportCountResponse {
community_id,
comment_reports,
post_reports,
+ private_message_reports,
};
Ok(res)
-use crate::Perform;
+use crate::{check_report_reason, Perform};
use activitypub_federation::core::object_id::ObjectId;
use actix_web::web::Data;
use lemmy_api_common::{
let local_user_view =
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
- // check size of report and check for whitespace
- let reason = data.reason.trim();
- if reason.is_empty() {
- return Err(LemmyError::from_message("report_reason_required"));
- }
- if reason.chars().count() > 1000 {
- return Err(LemmyError::from_message("report_too_long"));
- }
+ let reason = self.reason.trim();
+ check_report_reason(reason, context)?;
let person_id = local_user_view.person.id;
let post_id = data.post_id;
original_post_name: post_view.post.name,
original_post_url: post_view.post.url,
original_post_body: post_view.post.body,
- reason: data.reason.to_owned(),
+ reason: reason.to_owned(),
};
let report = blocking(context.pool(), move |conn| {
use crate::Perform;
use actix_web::web::Data;
use lemmy_api_common::{
- person::{MarkPrivateMessageAsRead, PrivateMessageResponse},
+ private_message::{MarkPrivateMessageAsRead, PrivateMessageResponse},
utils::{blocking, get_local_user_view_from_jwt},
};
use lemmy_db_schema::{source::private_message::PrivateMessage, traits::Crud};
--- /dev/null
+use crate::{check_report_reason, Perform};
+use actix_web::web::Data;
+use lemmy_api_common::{
+ private_message::{CreatePrivateMessageReport, PrivateMessageReportResponse},
+ utils::{blocking, get_local_user_view_from_jwt},
+};
+use lemmy_db_schema::{
+ newtypes::CommunityId,
+ source::{
+ private_message::PrivateMessage,
+ private_message_report::{PrivateMessageReport, PrivateMessageReportForm},
+ },
+ traits::{Crud, Reportable},
+};
+use lemmy_db_views::structs::PrivateMessageReportView;
+use lemmy_utils::{error::LemmyError, ConnectionId};
+use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
+
+#[async_trait::async_trait(?Send)]
+impl Perform for CreatePrivateMessageReport {
+ type Response = PrivateMessageReportResponse;
+
+ #[tracing::instrument(skip(context, websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ websocket_id: Option<ConnectionId>,
+ ) -> Result<Self::Response, LemmyError> {
+ let local_user_view =
+ get_local_user_view_from_jwt(&self.auth, context.pool(), context.secret()).await?;
+
+ let reason = self.reason.trim();
+ check_report_reason(reason, context)?;
+
+ let person_id = local_user_view.person.id;
+ let private_message_id = self.private_message_id;
+ let private_message = blocking(context.pool(), move |conn| {
+ PrivateMessage::read(conn, private_message_id)
+ })
+ .await??;
+
+ let report_form = PrivateMessageReportForm {
+ creator_id: person_id,
+ private_message_id,
+ original_pm_text: private_message.content,
+ reason: reason.to_owned(),
+ };
+
+ let report = blocking(context.pool(), move |conn| {
+ PrivateMessageReport::report(conn, &report_form)
+ })
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "couldnt_create_report"))?;
+
+ let private_message_report_view = blocking(context.pool(), move |conn| {
+ PrivateMessageReportView::read(conn, report.id)
+ })
+ .await??;
+
+ let res = PrivateMessageReportResponse {
+ private_message_report_view,
+ };
+
+ context.chat_server().do_send(SendModRoomMessage {
+ op: UserOperation::CreatePrivateMessageReport,
+ response: res.clone(),
+ community_id: CommunityId(0),
+ websocket_id,
+ });
+
+ // TODO: consider federating this
+
+ Ok(res)
+ }
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ private_message::{ListPrivateMessageReports, ListPrivateMessageReportsResponse},
+ utils::{blocking, get_local_user_view_from_jwt, is_admin},
+};
+use lemmy_db_views::private_message_report_view::PrivateMessageReportQuery;
+use lemmy_utils::{error::LemmyError, ConnectionId};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for ListPrivateMessageReports {
+ type Response = ListPrivateMessageReportsResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<Self::Response, LemmyError> {
+ let local_user_view =
+ get_local_user_view_from_jwt(&self.auth, context.pool(), context.secret()).await?;
+
+ is_admin(&local_user_view)?;
+
+ let unresolved_only = self.unresolved_only;
+ let page = self.page;
+ let limit = self.limit;
+ let private_message_reports = blocking(context.pool(), move |conn| {
+ PrivateMessageReportQuery::builder()
+ .conn(conn)
+ .unresolved_only(unresolved_only)
+ .page(page)
+ .limit(limit)
+ .build()
+ .list()
+ })
+ .await??;
+
+ let res = ListPrivateMessageReportsResponse {
+ private_message_reports,
+ };
+
+ Ok(res)
+ }
+}
--- /dev/null
+mod create;
+mod list;
+mod resolve;
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ private_message::{PrivateMessageReportResponse, ResolvePrivateMessageReport},
+ utils::{blocking, get_local_user_view_from_jwt, is_admin},
+};
+use lemmy_db_schema::{
+ newtypes::CommunityId,
+ source::private_message_report::PrivateMessageReport,
+ traits::Reportable,
+};
+use lemmy_db_views::structs::PrivateMessageReportView;
+use lemmy_utils::{error::LemmyError, ConnectionId};
+use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
+
+#[async_trait::async_trait(?Send)]
+impl Perform for ResolvePrivateMessageReport {
+ type Response = PrivateMessageReportResponse;
+
+ #[tracing::instrument(skip(context, websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ websocket_id: Option<ConnectionId>,
+ ) -> Result<Self::Response, LemmyError> {
+ let local_user_view =
+ get_local_user_view_from_jwt(&self.auth, context.pool(), context.secret()).await?;
+
+ is_admin(&local_user_view)?;
+
+ let resolved = self.resolved;
+ let report_id = self.report_id;
+ let person_id = local_user_view.person.id;
+ let resolve_fn = move |conn: &'_ _| {
+ if resolved {
+ PrivateMessageReport::resolve(conn, report_id, person_id)
+ } else {
+ PrivateMessageReport::unresolve(conn, report_id, person_id)
+ }
+ };
+
+ blocking(context.pool(), resolve_fn)
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "couldnt_resolve_report"))?;
+
+ let private_message_report_view = blocking(context.pool(), move |conn| {
+ PrivateMessageReportView::read(conn, report_id)
+ })
+ .await??;
+
+ let res = PrivateMessageReportResponse {
+ private_message_report_view,
+ };
+
+ context.chat_server().do_send(SendModRoomMessage {
+ op: UserOperation::ResolvePrivateMessageReport,
+ response: res.clone(),
+ community_id: CommunityId(0),
+ websocket_id,
+ });
+
+ Ok(res)
+ }
+}
pub mod community;
pub mod person;
pub mod post;
+pub mod private_message;
#[cfg(feature = "full")]
pub mod request;
pub mod sensitive;
use crate::sensitive::Sensitive;
-use lemmy_db_views::structs::{CommentView, PostView, PrivateMessageView};
+use lemmy_db_schema::{
+ newtypes::{CommentReplyId, CommunityId, LanguageId, PersonId, PersonMentionId},
+ CommentSortType,
+ SortType,
+};
+use lemmy_db_views::structs::{CommentView, PostView};
use lemmy_db_views_actor::structs::{
CommentReplyView,
CommunityModeratorView,
pub username_or_email: Sensitive<String>,
pub password: Sensitive<String>,
}
-use lemmy_db_schema::{
- newtypes::{
- CommentReplyId,
- CommunityId,
- LanguageId,
- PersonId,
- PersonMentionId,
- PrivateMessageId,
- },
- CommentSortType,
- SortType,
-};
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct Register {
pub password_verify: Sensitive<String>,
}
-#[derive(Debug, Serialize, Deserialize, Clone, Default)]
-pub struct CreatePrivateMessage {
- pub content: String,
- pub recipient_id: PersonId,
- pub auth: Sensitive<String>,
-}
-
-#[derive(Debug, Serialize, Deserialize, Clone, Default)]
-pub struct EditPrivateMessage {
- pub private_message_id: PrivateMessageId,
- pub content: String,
- pub auth: Sensitive<String>,
-}
-
-#[derive(Debug, Serialize, Deserialize, Clone, Default)]
-pub struct DeletePrivateMessage {
- pub private_message_id: PrivateMessageId,
- pub deleted: bool,
- pub auth: Sensitive<String>,
-}
-
-#[derive(Debug, Serialize, Deserialize, Clone, Default)]
-pub struct MarkPrivateMessageAsRead {
- pub private_message_id: PrivateMessageId,
- pub read: bool,
- pub auth: Sensitive<String>,
-}
-
-#[derive(Debug, Serialize, Deserialize, Clone, Default)]
-pub struct GetPrivateMessages {
- pub unread_only: Option<bool>,
- pub page: Option<i64>,
- pub limit: Option<i64>,
- pub auth: Sensitive<String>,
-}
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct PrivateMessagesResponse {
- pub private_messages: Vec<PrivateMessageView>,
-}
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct PrivateMessageResponse {
- pub private_message_view: PrivateMessageView,
-}
-
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct GetReportCount {
pub community_id: Option<CommunityId>,
pub community_id: Option<CommunityId>,
pub comment_reports: i64,
pub post_reports: i64,
+ pub private_message_reports: Option<i64>,
}
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
--- /dev/null
+use crate::sensitive::Sensitive;
+use lemmy_db_schema::newtypes::{PersonId, PrivateMessageId, PrivateMessageReportId};
+use lemmy_db_views::structs::{PrivateMessageReportView, PrivateMessageView};
+use serde::{Deserialize, Serialize};
+
+#[derive(Debug, Serialize, Deserialize, Clone, Default)]
+pub struct CreatePrivateMessage {
+ pub content: String,
+ pub recipient_id: PersonId,
+ pub auth: Sensitive<String>,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, Default)]
+pub struct EditPrivateMessage {
+ pub private_message_id: PrivateMessageId,
+ pub content: String,
+ pub auth: Sensitive<String>,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, Default)]
+pub struct DeletePrivateMessage {
+ pub private_message_id: PrivateMessageId,
+ pub deleted: bool,
+ pub auth: Sensitive<String>,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, Default)]
+pub struct MarkPrivateMessageAsRead {
+ pub private_message_id: PrivateMessageId,
+ pub read: bool,
+ pub auth: Sensitive<String>,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, Default)]
+pub struct GetPrivateMessages {
+ pub unread_only: Option<bool>,
+ pub page: Option<i64>,
+ pub limit: Option<i64>,
+ pub auth: Sensitive<String>,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct PrivateMessagesResponse {
+ pub private_messages: Vec<PrivateMessageView>,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct PrivateMessageResponse {
+ pub private_message_view: PrivateMessageView,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, Default)]
+pub struct CreatePrivateMessageReport {
+ pub private_message_id: PrivateMessageId,
+ pub reason: String,
+ pub auth: Sensitive<String>,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct PrivateMessageReportResponse {
+ pub private_message_report_view: PrivateMessageReportView,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, Default)]
+pub struct ResolvePrivateMessageReport {
+ pub report_id: PrivateMessageReportId,
+ pub resolved: bool,
+ pub auth: Sensitive<String>,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, Default)]
+pub struct ListPrivateMessageReports {
+ pub page: Option<i64>,
+ pub limit: Option<i64>,
+ /// Only shows the unresolved reports
+ pub unresolved_only: Option<bool>,
+ pub auth: Sensitive<String>,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct ListPrivateMessageReportsResponse {
+ pub private_message_reports: Vec<PrivateMessageReportView>,
+}
use actix_web::{web, web::Data};
-use lemmy_api_common::{comment::*, community::*, person::*, post::*, site::*};
+use lemmy_api_common::{comment::*, community::*, person::*, post::*, private_message::*, site::*};
use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{serialize_websocket_message, LemmyContext, UserOperationCrud};
use serde::Deserialize;
use crate::PerformCrud;
use actix_web::web::Data;
use lemmy_api_common::{
- person::{CreatePrivateMessage, PrivateMessageResponse},
+ private_message::{CreatePrivateMessage, PrivateMessageResponse},
utils::{
blocking,
check_person_block,
use crate::PerformCrud;
use actix_web::web::Data;
use lemmy_api_common::{
- person::{DeletePrivateMessage, PrivateMessageResponse},
+ private_message::{DeletePrivateMessage, PrivateMessageResponse},
utils::{blocking, get_local_user_view_from_jwt},
};
use lemmy_apub::activities::deletion::send_apub_delete_private_message;
use crate::PerformCrud;
use actix_web::web::Data;
use lemmy_api_common::{
- person::{GetPrivateMessages, PrivateMessagesResponse},
+ private_message::{GetPrivateMessages, PrivateMessagesResponse},
utils::{blocking, get_local_user_view_from_jwt},
};
use lemmy_db_schema::traits::DeleteableOrRemoveable;
use crate::PerformCrud;
use actix_web::web::Data;
use lemmy_api_common::{
- person::{EditPrivateMessage, PrivateMessageResponse},
+ private_message::{EditPrivateMessage, PrivateMessageResponse},
utils::{blocking, get_local_user_view_from_jwt},
};
use lemmy_apub::protocol::activities::{
pub mod post;
pub mod post_report;
pub mod private_message;
+pub mod private_message_report;
pub mod registration_application;
pub mod secret;
pub mod site;
--- /dev/null
+use crate::{
+ newtypes::{PersonId, PrivateMessageReportId},
+ source::private_message_report::{PrivateMessageReport, PrivateMessageReportForm},
+ traits::Reportable,
+ utils::naive_now,
+};
+use diesel::{dsl::*, result::Error, *};
+
+impl Reportable for PrivateMessageReport {
+ type Form = PrivateMessageReportForm;
+ type IdType = PrivateMessageReportId;
+ /// creates a comment report and returns it
+ ///
+ /// * `conn` - the postgres connection
+ /// * `comment_report_form` - the filled CommentReportForm to insert
+ fn report(conn: &PgConnection, pm_report_form: &PrivateMessageReportForm) -> Result<Self, Error> {
+ use crate::schema::private_message_report::dsl::*;
+ insert_into(private_message_report)
+ .values(pm_report_form)
+ .get_result::<Self>(conn)
+ }
+
+ /// resolve a pm report
+ ///
+ /// * `conn` - the postgres connection
+ /// * `report_id` - the id of the report to resolve
+ /// * `by_resolver_id` - the id of the user resolving the report
+ fn resolve(
+ conn: &PgConnection,
+ report_id: Self::IdType,
+ by_resolver_id: PersonId,
+ ) -> Result<usize, Error> {
+ use crate::schema::private_message_report::dsl::*;
+ update(private_message_report.find(report_id))
+ .set((
+ resolved.eq(true),
+ resolver_id.eq(by_resolver_id),
+ updated.eq(naive_now()),
+ ))
+ .execute(conn)
+ }
+
+ /// unresolve a comment report
+ ///
+ /// * `conn` - the postgres connection
+ /// * `report_id` - the id of the report to unresolve
+ /// * `by_resolver_id` - the id of the user unresolving the report
+ fn unresolve(
+ conn: &PgConnection,
+ report_id: Self::IdType,
+ by_resolver_id: PersonId,
+ ) -> Result<usize, Error> {
+ use crate::schema::private_message_report::dsl::*;
+ update(private_message_report.find(report_id))
+ .set((
+ resolved.eq(false),
+ resolver_id.eq(by_resolver_id),
+ updated.eq(naive_now()),
+ ))
+ .execute(conn)
+ }
+}
#[cfg_attr(feature = "full", derive(DieselNewType))]
pub struct PostReportId(i32);
+#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
+#[cfg_attr(feature = "full", derive(DieselNewType))]
+pub struct PrivateMessageReportId(i32);
+
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
#[cfg_attr(feature = "full", derive(DieselNewType))]
pub struct LanguageId(pub i32);
}
}
+table! {
+ private_message_report (id) {
+ id -> Int4,
+ creator_id -> Int4,
+ private_message_id -> Int4,
+ original_pm_text -> Text,
+ reason -> Text,
+ resolved -> Bool,
+ resolver_id -> Nullable<Int4>,
+ published -> Timestamp,
+ updated -> Nullable<Timestamp>,
+ }
+}
+
table! {
site (id) {
id -> Int4,
joinable!(comment_reply -> person_alias_1 (recipient_id));
joinable!(post -> person_alias_1 (creator_id));
joinable!(comment -> person_alias_1 (creator_id));
+joinable!(private_message_report -> person_alias_1 (resolver_id));
joinable!(post_report -> person_alias_2 (resolver_id));
joinable!(comment_report -> person_alias_2 (resolver_id));
+joinable!(private_message_report -> person_alias_2 (resolver_id));
joinable!(person_block -> person (person_id));
joinable!(person_block -> person_alias_1 (target_id));
joinable!(comment -> language (language_id));
joinable!(local_user_language -> language (language_id));
joinable!(local_user_language -> local_user (local_user_id));
+joinable!(private_message_report -> private_message (private_message_id));
joinable!(admin_purge_comment -> person (admin_person_id));
joinable!(admin_purge_comment -> post (post_id));
post_report,
post_saved,
private_message,
+ private_message_report,
site,
site_aggregates,
person_alias_1,
pub mod post;
pub mod post_report;
pub mod private_message;
+pub mod private_message_report;
pub mod registration_application;
pub mod secret;
pub mod site;
--- /dev/null
+use crate::newtypes::{PersonId, PrivateMessageId, PrivateMessageReportId};
+use serde::{Deserialize, Serialize};
+
+#[cfg(feature = "full")]
+use crate::schema::private_message_report;
+
+#[derive(PartialEq, Serialize, Deserialize, Debug, Clone)]
+#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable))]
+#[cfg_attr(
+ feature = "full",
+ belongs_to(crate::source::private_message::PrivateMessage)
+)]
+#[cfg_attr(feature = "full", table_name = "private_message_report")]
+pub struct PrivateMessageReport {
+ pub id: PrivateMessageReportId,
+ pub creator_id: PersonId,
+ pub private_message_id: PrivateMessageId,
+ pub original_pm_text: String,
+ pub reason: String,
+ pub resolved: bool,
+ pub resolver_id: Option<PersonId>,
+ pub published: chrono::NaiveDateTime,
+ pub updated: Option<chrono::NaiveDateTime>,
+}
+
+#[derive(Clone)]
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", table_name = "private_message_report")]
+pub struct PrivateMessageReportForm {
+ pub creator_id: PersonId,
+ pub private_message_id: PrivateMessageId,
+ pub original_pm_text: String,
+ pub reason: String,
+}
#[cfg(feature = "full")]
pub mod post_view;
#[cfg(feature = "full")]
+#[cfg(feature = "full")]
+pub mod private_message_report_view;
+#[cfg(feature = "full")]
pub mod private_message_view;
#[cfg(feature = "full")]
pub mod registration_application_view;
--- /dev/null
+use crate::structs::PrivateMessageReportView;
+use diesel::{result::Error, *};
+use lemmy_db_schema::{
+ newtypes::PrivateMessageReportId,
+ schema::{person, person_alias_1, person_alias_2, private_message, private_message_report},
+ source::{
+ person::{Person, PersonAlias1, PersonAlias2, PersonSafe, PersonSafeAlias1, PersonSafeAlias2},
+ private_message::PrivateMessage,
+ private_message_report::PrivateMessageReport,
+ },
+ traits::{ToSafe, ViewToVec},
+ utils::limit_and_offset,
+};
+use typed_builder::TypedBuilder;
+
+type PrivateMessageReportViewTuple = (
+ PrivateMessageReport,
+ PrivateMessage,
+ PersonSafe,
+ PersonSafeAlias1,
+ Option<PersonSafeAlias2>,
+);
+
+impl PrivateMessageReportView {
+ /// returns the PrivateMessageReportView for the provided report_id
+ ///
+ /// * `report_id` - the report id to obtain
+ pub fn read(conn: &PgConnection, report_id: PrivateMessageReportId) -> Result<Self, Error> {
+ let (private_message_report, private_message, private_message_creator, creator, resolver) =
+ private_message_report::table
+ .find(report_id)
+ .inner_join(private_message::table)
+ .inner_join(person::table.on(private_message::creator_id.eq(person::id)))
+ .inner_join(
+ person_alias_1::table.on(private_message_report::creator_id.eq(person_alias_1::id)),
+ )
+ .left_join(
+ person_alias_2::table
+ .on(private_message_report::resolver_id.eq(person_alias_2::id.nullable())),
+ )
+ .select((
+ private_message_report::all_columns,
+ private_message::all_columns,
+ Person::safe_columns_tuple(),
+ PersonAlias1::safe_columns_tuple(),
+ PersonAlias2::safe_columns_tuple().nullable(),
+ ))
+ .first::<PrivateMessageReportViewTuple>(conn)?;
+
+ Ok(Self {
+ private_message_report,
+ private_message,
+ private_message_creator,
+ creator,
+ resolver,
+ })
+ }
+
+ /// Returns the current unresolved post report count for the communities you mod
+ pub fn get_report_count(conn: &PgConnection) -> Result<i64, Error> {
+ use diesel::dsl::*;
+
+ private_message_report::table
+ .inner_join(private_message::table)
+ .filter(private_message_report::resolved.eq(false))
+ .into_boxed()
+ .select(count(private_message_report::id))
+ .first::<i64>(conn)
+ }
+}
+
+#[derive(TypedBuilder)]
+#[builder(field_defaults(default))]
+pub struct PrivateMessageReportQuery<'a> {
+ #[builder(!default)]
+ conn: &'a PgConnection,
+ page: Option<i64>,
+ limit: Option<i64>,
+ unresolved_only: Option<bool>,
+}
+
+impl<'a> PrivateMessageReportQuery<'a> {
+ pub fn list(self) -> Result<Vec<PrivateMessageReportView>, Error> {
+ let mut query = private_message_report::table
+ .inner_join(private_message::table)
+ .inner_join(person::table.on(private_message::creator_id.eq(person::id)))
+ .inner_join(
+ person_alias_1::table.on(private_message_report::creator_id.eq(person_alias_1::id)),
+ )
+ .left_join(
+ person_alias_2::table
+ .on(private_message_report::resolver_id.eq(person_alias_2::id.nullable())),
+ )
+ .select((
+ private_message_report::all_columns,
+ private_message::all_columns,
+ Person::safe_columns_tuple(),
+ PersonAlias1::safe_columns_tuple(),
+ PersonAlias2::safe_columns_tuple().nullable(),
+ ))
+ .into_boxed();
+
+ if self.unresolved_only.unwrap_or(true) {
+ query = query.filter(private_message_report::resolved.eq(false));
+ }
+
+ let (limit, offset) = limit_and_offset(self.page, self.limit)?;
+
+ query = query
+ .order_by(private_message::published.desc())
+ .limit(limit)
+ .offset(offset);
+
+ let res = query.load::<PrivateMessageReportViewTuple>(self.conn)?;
+
+ Ok(PrivateMessageReportView::from_tuple_to_vec(res))
+ }
+}
+
+impl ViewToVec for PrivateMessageReportView {
+ type DbTuple = PrivateMessageReportViewTuple;
+ fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
+ items
+ .into_iter()
+ .map(|a| Self {
+ private_message_report: a.0,
+ private_message: a.1,
+ private_message_creator: a.2,
+ creator: a.3,
+ resolver: a.4,
+ })
+ .collect::<Vec<Self>>()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::private_message_report_view::PrivateMessageReportQuery;
+ use lemmy_db_schema::{
+ source::{
+ person::{Person, PersonForm},
+ private_message::{PrivateMessage, PrivateMessageForm},
+ private_message_report::{PrivateMessageReport, PrivateMessageReportForm},
+ },
+ traits::{Crud, Reportable},
+ utils::establish_unpooled_connection,
+ };
+ use serial_test::serial;
+
+ #[test]
+ #[serial]
+ fn test_crud() {
+ let conn = establish_unpooled_connection();
+
+ let new_person_1 = PersonForm {
+ name: "timmy_mrv".into(),
+ public_key: Some("pubkey".to_string()),
+ ..PersonForm::default()
+ };
+ let inserted_timmy = Person::create(&conn, &new_person_1).unwrap();
+
+ let new_person_2 = PersonForm {
+ name: "jessica_mrv".into(),
+ public_key: Some("pubkey".to_string()),
+ ..PersonForm::default()
+ };
+ let inserted_jessica = Person::create(&conn, &new_person_2).unwrap();
+
+ // timmy sends private message to jessica
+ let pm_form = PrivateMessageForm {
+ creator_id: inserted_timmy.id,
+ recipient_id: inserted_jessica.id,
+ content: "something offensive".to_string(),
+ ..Default::default()
+ };
+ let pm = PrivateMessage::create(&conn, &pm_form).unwrap();
+
+ // jessica reports private message
+ let pm_report_form = PrivateMessageReportForm {
+ creator_id: inserted_jessica.id,
+ original_pm_text: pm.content.clone(),
+ private_message_id: pm.id,
+ reason: "its offensive".to_string(),
+ };
+ let pm_report = PrivateMessageReport::report(&conn, &pm_report_form).unwrap();
+
+ let reports = PrivateMessageReportQuery::builder()
+ .conn(&conn)
+ .build()
+ .list()
+ .unwrap();
+ assert_eq!(1, reports.len());
+ assert!(!reports[0].private_message_report.resolved);
+ assert_eq!(inserted_timmy.name, reports[0].private_message_creator.name);
+ assert_eq!(inserted_jessica.name, reports[0].creator.name);
+ assert_eq!(pm_report.reason, reports[0].private_message_report.reason);
+ assert_eq!(pm.content, reports[0].private_message.content);
+
+ let new_person_3 = PersonForm {
+ name: "admin_mrv".into(),
+ public_key: Some("pubkey".to_string()),
+ ..PersonForm::default()
+ };
+ let inserted_admin = Person::create(&conn, &new_person_3).unwrap();
+
+ // admin resolves the report (after taking appropriate action)
+ PrivateMessageReport::resolve(&conn, pm_report.id, inserted_admin.id).unwrap();
+
+ let reports = PrivateMessageReportQuery::builder()
+ .conn(&conn)
+ .unresolved_only(Some(false))
+ .build()
+ .list()
+ .unwrap();
+ assert_eq!(1, reports.len());
+ assert!(reports[0].private_message_report.resolved);
+ assert!(reports[0].resolver.is_some());
+ assert_eq!(
+ inserted_admin.name,
+ reports[0].resolver.as_ref().unwrap().name
+ );
+ }
+}
post::Post,
post_report::PostReport,
private_message::PrivateMessage,
+ private_message_report::PrivateMessageReport,
registration_application::RegistrationApplication,
site::Site,
},
pub recipient: PersonSafeAlias1,
}
+#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
+pub struct PrivateMessageReportView {
+ pub private_message_report: PrivateMessageReport,
+ pub private_message: PrivateMessage,
+ pub private_message_creator: PersonSafe,
+ pub creator: PersonSafeAlias1,
+ pub resolver: Option<PersonSafeAlias2>,
+}
+
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
pub struct RegistrationApplicationView {
pub registration_application: RegistrationApplication,
PasswordReset,
PasswordChange,
MarkPrivateMessageAsRead,
+ CreatePrivateMessageReport,
+ ResolvePrivateMessageReport,
+ ListPrivateMessageReports,
UserJoin,
PostJoin,
CommunityJoin,
pub websocket_id: Option<ConnectionId>,
}
+/// Send message to all users viewing the given community.
#[derive(Message)]
#[rtype(result = "()")]
pub struct SendCommunityRoomMessage<OP: ToString, Response> {
pub websocket_id: Option<ConnectionId>,
}
+/// Send message to mods of a given community. Set community_id = 0 to send to site admins.
#[derive(Message)]
#[rtype(result = "()")]
pub struct SendModRoomMessage<Response> {
use lemmy_api_common::{
comment::CommentResponse,
community::CommunityResponse,
- person::PrivateMessageResponse,
post::PostResponse,
+ private_message::PrivateMessageResponse,
utils::{blocking, check_person_block, get_interface_language, send_email_to_user},
};
use lemmy_db_schema::{
--- /dev/null
+drop table private_message_report;
--- /dev/null
+create table private_message_report (
+ id serial primary key,
+ creator_id int references person on update cascade on delete cascade not null, -- user reporting comment
+ private_message_id int references private_message on update cascade on delete cascade not null, -- comment being reported
+ original_pm_text text not null,
+ reason text not null,
+ resolved bool not null default false,
+ resolver_id int references person on update cascade on delete cascade, -- user resolving report
+ published timestamp not null default now(),
+ updated timestamp null,
+ unique(private_message_id, creator_id) -- users should only be able to report a pm once
+);
use actix_web::*;
use lemmy_api::Perform;
-use lemmy_api_common::{comment::*, community::*, person::*, post::*, site::*, websocket::*};
+use lemmy_api_common::{
+ comment::*,
+ community::*,
+ person::*,
+ post::*,
+ private_message::*,
+ site::*,
+ websocket::*,
+};
use lemmy_api_crud::PerformCrud;
use lemmy_utils::rate_limit::RateLimit;
use lemmy_websocket::{routes::chat_route, LemmyContext};
.route(
"/mark_as_read",
web::post().to(route_post::<MarkPrivateMessageAsRead>),
+ )
+ .route(
+ "/report",
+ web::post().to(route_post::<CreatePrivateMessageReport>),
+ )
+ .route(
+ "/report/resolve",
+ web::put().to(route_post::<ResolvePrivateMessageReport>),
+ )
+ .route(
+ "/report/list",
+ web::get().to(route_get::<ListPrivateMessageReports>),
),
)
// User