From 79dd2dd93f14fabd795695183b324b47d4de6ee5 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Thu, 9 Sep 2021 22:43:12 -0400 Subject: [PATCH] First untested pass at reporting. --- crates/api/src/comment_report.rs | 62 +++++++++------------ crates/api/src/local_user.rs | 52 +++++------------ crates/api/src/post_report.rs | 65 +++++++++------------- crates/api_common/src/comment.rs | 25 +++------ crates/api_common/src/lib.rs | 33 +---------- crates/api_common/src/person.rs | 8 +-- crates/api_common/src/post.rs | 25 ++++----- crates/db_views/src/comment_report_view.rs | 64 +++++++++++++++------ crates/db_views/src/post_report_view.rs | 61 ++++++++++++++------ 9 files changed, 184 insertions(+), 211 deletions(-) diff --git a/crates/api/src/comment_report.rs b/crates/api/src/comment_report.rs index 6d2e2280d..2b73500da 100644 --- a/crates/api/src/comment_report.rs +++ b/crates/api/src/comment_report.rs @@ -3,7 +3,6 @@ use actix_web::web::Data; use lemmy_api_common::{ blocking, check_community_ban, - collect_moderated_communities, comment::*, get_local_user_view_from_jwt, is_mod_or_admin, @@ -15,22 +14,18 @@ use lemmy_db_views::{ comment_view::CommentView, }; use lemmy_utils::{ApiError, ConnectionId, LemmyError}; -use lemmy_websocket::{ - messages::{SendModRoomMessage, SendUserRoomMessage}, - LemmyContext, - UserOperation, -}; +use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation}; /// Creates a comment report and notifies the moderators of the community #[async_trait::async_trait(?Send)] impl Perform for CreateCommentReport { - type Response = CreateCommentReportResponse; + type Response = CommentReportResponse; async fn perform( &self, context: &Data, websocket_id: Option, - ) -> Result { + ) -> Result { let data: &CreateCommentReport = self; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; @@ -66,18 +61,18 @@ impl Perform for CreateCommentReport { .await? .map_err(|_| ApiError::err("couldnt_create_report"))?; - let res = CreateCommentReportResponse { success: true }; + let comment_report_view = blocking(context.pool(), move |conn| { + CommentReportView::read(conn, report.id) + }) + .await??; - context.chat_server().do_send(SendUserRoomMessage { - op: UserOperation::CreateCommentReport, - response: res.clone(), - local_recipient_id: local_user_view.local_user.id, - websocket_id, - }); + let res = CommentReportResponse { + comment_report_view, + }; context.chat_server().do_send(SendModRoomMessage { op: UserOperation::CreateCommentReport, - response: report, + response: res.clone(), community_id: comment_view.community.id, websocket_id, }); @@ -89,13 +84,13 @@ impl Perform for CreateCommentReport { /// Resolves or unresolves a comment report and notifies the moderators of the community #[async_trait::async_trait(?Send)] impl Perform for ResolveCommentReport { - type Response = ResolveCommentReportResponse; + type Response = CommentReportResponse; async fn perform( &self, context: &Data, websocket_id: Option, - ) -> Result { + ) -> Result { let data: &ResolveCommentReport = self; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; @@ -123,9 +118,13 @@ impl Perform for ResolveCommentReport { }; let report_id = data.report_id; - let res = ResolveCommentReportResponse { - report_id, - resolved, + let comment_report_view = blocking(context.pool(), move |conn| { + CommentReportView::read(conn, report_id) + }) + .await??; + + let res = CommentReportResponse { + comment_report_view, }; context.chat_server().do_send(SendModRoomMessage { @@ -148,36 +147,27 @@ impl Perform for ListCommentReports { async fn perform( &self, context: &Data, - websocket_id: Option, + _websocket_id: Option, ) -> Result { let data: &ListCommentReports = self; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let person_id = local_user_view.person.id; - let community_id = data.community; - let community_ids = - collect_moderated_communities(person_id, community_id, context.pool()).await?; + let community_id = data.community_id; let page = data.page; let limit = data.limit; - let comments = blocking(context.pool(), move |conn| { - CommentReportQueryBuilder::create(conn) - .community_ids(community_ids) + let comment_reports = blocking(context.pool(), move |conn| { + CommentReportQueryBuilder::create(conn, person_id) + .community_id(community_id) .page(page) .limit(limit) .list() }) .await??; - let res = ListCommentReportsResponse { comments }; - - context.chat_server().do_send(SendUserRoomMessage { - op: UserOperation::ListCommentReports, - response: res.clone(), - local_recipient_id: local_user_view.local_user.id, - websocket_id, - }); + let res = ListCommentReportsResponse { comment_reports }; Ok(res) } diff --git a/crates/api/src/local_user.rs b/crates/api/src/local_user.rs index c69ea48d3..1d4f27b28 100644 --- a/crates/api/src/local_user.rs +++ b/crates/api/src/local_user.rs @@ -6,7 +6,6 @@ use captcha::{gen, Difficulty}; use chrono::Duration; use lemmy_api_common::{ blocking, - collect_moderated_communities, get_local_user_view_from_jwt, is_admin, password_length_check, @@ -67,7 +66,7 @@ use lemmy_utils::{ LemmyError, }; use lemmy_websocket::{ - messages::{CaptchaItem, SendAllMessage, SendUserRoomMessage}, + messages::{CaptchaItem, SendAllMessage}, LemmyContext, UserOperation, }; @@ -816,52 +815,31 @@ impl Perform for GetReportCount { async fn perform( &self, context: &Data, - websocket_id: Option, + _websocket_id: Option, ) -> Result { let data: &GetReportCount = self; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let person_id = local_user_view.person.id; - let community_id = data.community; - let community_ids = - collect_moderated_communities(person_id, community_id, context.pool()).await?; + let community_id = data.community_id; - let res = { - if community_ids.is_empty() { - GetReportCountResponse { - community: None, - comment_reports: 0, - post_reports: 0, - } - } else { - let ids = community_ids.clone(); - let comment_reports = blocking(context.pool(), move |conn| { - CommentReportView::get_report_count(conn, &ids) - }) - .await??; + let comment_reports = blocking(context.pool(), move |conn| { + CommentReportView::get_report_count(conn, person_id, community_id) + }) + .await??; - let ids = community_ids.clone(); - let post_reports = blocking(context.pool(), move |conn| { - PostReportView::get_report_count(conn, &ids) - }) - .await??; + let post_reports = blocking(context.pool(), move |conn| { + PostReportView::get_report_count(conn, person_id, community_id) + }) + .await??; - GetReportCountResponse { - community: data.community, - comment_reports, - post_reports, - } - } + let res = GetReportCountResponse { + community_id, + comment_reports, + post_reports, }; - context.chat_server().do_send(SendUserRoomMessage { - op: UserOperation::GetReportCount, - response: res.clone(), - local_recipient_id: local_user_view.local_user.id, - websocket_id, - }); - Ok(res) } } diff --git a/crates/api/src/post_report.rs b/crates/api/src/post_report.rs index e08a64b3f..b1f46a4bd 100644 --- a/crates/api/src/post_report.rs +++ b/crates/api/src/post_report.rs @@ -3,16 +3,14 @@ use actix_web::web::Data; use lemmy_api_common::{ blocking, check_community_ban, - collect_moderated_communities, get_local_user_view_from_jwt, is_mod_or_admin, post::{ CreatePostReport, - CreatePostReportResponse, ListPostReports, ListPostReportsResponse, + PostReportResponse, ResolvePostReport, - ResolvePostReportResponse, }, }; use lemmy_db_queries::Reportable; @@ -22,22 +20,18 @@ use lemmy_db_views::{ post_view::PostView, }; use lemmy_utils::{ApiError, ConnectionId, LemmyError}; -use lemmy_websocket::{ - messages::{SendModRoomMessage, SendUserRoomMessage}, - LemmyContext, - UserOperation, -}; +use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation}; /// Creates a post report and notifies the moderators of the community #[async_trait::async_trait(?Send)] impl Perform for CreatePostReport { - type Response = CreatePostReportResponse; + type Response = PostReportResponse; async fn perform( &self, context: &Data, websocket_id: Option, - ) -> Result { + ) -> Result { let data: &CreatePostReport = self; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; @@ -75,18 +69,16 @@ impl Perform for CreatePostReport { .await? .map_err(|_| ApiError::err("couldnt_create_report"))?; - let res = CreatePostReportResponse { success: true }; + let post_report_view = blocking(context.pool(), move |conn| { + PostReportView::read(conn, report.id) + }) + .await??; - context.chat_server().do_send(SendUserRoomMessage { - op: UserOperation::CreatePostReport, - response: res.clone(), - local_recipient_id: local_user_view.local_user.id, - websocket_id, - }); + let res = PostReportResponse { post_report_view }; context.chat_server().do_send(SendModRoomMessage { op: UserOperation::CreatePostReport, - response: report, + response: res.clone(), community_id: post_view.community.id, websocket_id, }); @@ -98,13 +90,13 @@ impl Perform for CreatePostReport { /// Resolves or unresolves a post report and notifies the moderators of the community #[async_trait::async_trait(?Send)] impl Perform for ResolvePostReport { - type Response = ResolvePostReportResponse; + type Response = PostReportResponse; async fn perform( &self, context: &Data, websocket_id: Option, - ) -> Result { + ) -> Result { let data: &ResolvePostReport = self; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; @@ -127,15 +119,17 @@ impl Perform for ResolvePostReport { } }; - let res = ResolvePostReportResponse { - report_id, - resolved: true, - }; - if blocking(context.pool(), resolve_fun).await?.is_err() { return Err(ApiError::err("couldnt_resolve_report").into()); }; + let post_report_view = blocking(context.pool(), move |conn| { + PostReportView::read(conn, report_id) + }) + .await??; + + let res = PostReportResponse { post_report_view }; + context.chat_server().do_send(SendModRoomMessage { op: UserOperation::ResolvePostReport, response: res.clone(), @@ -156,36 +150,27 @@ impl Perform for ListPostReports { async fn perform( &self, context: &Data, - websocket_id: Option, + _websocket_id: Option, ) -> Result { let data: &ListPostReports = self; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let person_id = local_user_view.person.id; - let community_id = data.community; - let community_ids = - collect_moderated_communities(person_id, community_id, context.pool()).await?; + let community_id = data.community_id; let page = data.page; let limit = data.limit; - let posts = blocking(context.pool(), move |conn| { - PostReportQueryBuilder::create(conn) - .community_ids(community_ids) + let post_reports = blocking(context.pool(), move |conn| { + PostReportQueryBuilder::create(conn, person_id) + .community_id(community_id) .page(page) .limit(limit) .list() }) .await??; - let res = ListPostReportsResponse { posts }; - - context.chat_server().do_send(SendUserRoomMessage { - op: UserOperation::ListPostReports, - response: res.clone(), - local_recipient_id: local_user_view.local_user.id, - websocket_id, - }); + let res = ListPostReportsResponse { post_reports }; Ok(res) } diff --git a/crates/api_common/src/comment.rs b/crates/api_common/src/comment.rs index ce5182cf3..fa9ec0bb2 100644 --- a/crates/api_common/src/comment.rs +++ b/crates/api_common/src/comment.rs @@ -79,42 +79,35 @@ pub struct GetCommentsResponse { pub comments: Vec, } -#[derive(Serialize, Deserialize)] +#[derive(Deserialize)] pub struct CreateCommentReport { pub comment_id: CommentId, pub reason: String, pub auth: String, } -#[derive(Serialize, Deserialize, Clone)] -pub struct CreateCommentReportResponse { - pub success: bool, +#[derive(Serialize, Clone)] +pub struct CommentReportResponse { + pub comment_report_view: CommentReportView, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Deserialize)] pub struct ResolveCommentReport { pub report_id: i32, pub resolved: bool, pub auth: String, } -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct ResolveCommentReportResponse { - // TODO this should probably return the view - pub report_id: i32, - pub resolved: bool, -} - -#[derive(Serialize, Deserialize, Debug)] +#[derive(Deserialize)] pub struct ListCommentReports { pub page: Option, pub limit: Option, /// if no community is given, it returns reports for all communities moderated by the auth user - pub community: Option, + pub community_id: Option, pub auth: String, } -#[derive(Serialize, Clone, Debug)] +#[derive(Serialize)] pub struct ListCommentReportsResponse { - pub comments: Vec, + pub comment_reports: Vec, } diff --git a/crates/api_common/src/lib.rs b/crates/api_common/src/lib.rs index c38d4e620..19715c3a5 100644 --- a/crates/api_common/src/lib.rs +++ b/crates/api_common/src/lib.rs @@ -8,11 +8,7 @@ pub mod websocket; use crate::site::FederatedInstances; use diesel::PgConnection; use lemmy_db_queries::{ - source::{ - community::{CommunityModerator_, Community_}, - person_block::PersonBlock_, - site::Site_, - }, + source::{community::Community_, person_block::PersonBlock_, site::Site_}, Crud, DbPool, Readable, @@ -20,7 +16,7 @@ use lemmy_db_queries::{ use lemmy_db_schema::{ source::{ comment::Comment, - community::{Community, CommunityModerator}, + community::Community, person::Person, person_block::PersonBlock, person_mention::{PersonMention, PersonMentionForm}, @@ -384,31 +380,6 @@ pub async fn check_downvotes_enabled(score: i16, pool: &DbPool) -> Result<(), Le Ok(()) } -/// Returns a list of communities that the user moderates -/// or if a community_id is supplied validates the user is a moderator -/// of that community and returns the community id in a vec -/// -/// * `person_id` - the person id of the moderator -/// * `community_id` - optional community id to check for moderator privileges -/// * `pool` - the diesel db pool -pub async fn collect_moderated_communities( - person_id: PersonId, - community_id: Option, - pool: &DbPool, -) -> Result, LemmyError> { - if let Some(community_id) = community_id { - // if the user provides a community_id, just check for mod/admin privileges - is_mod_or_admin(pool, person_id, community_id).await?; - Ok(vec![community_id]) - } else { - let ids = blocking(pool, move |conn: &'_ _| { - CommunityModerator::get_person_moderated_communities(conn, person_id) - }) - .await??; - Ok(ids) - } -} - pub async fn build_federated_instances( pool: &DbPool, federation_config: &FederationConfig, diff --git a/crates/api_common/src/person.rs b/crates/api_common/src/person.rs index a63e55d7e..c71cf540b 100644 --- a/crates/api_common/src/person.rs +++ b/crates/api_common/src/person.rs @@ -253,15 +253,15 @@ pub struct PrivateMessageResponse { pub private_message_view: PrivateMessageView, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Deserialize)] pub struct GetReportCount { - pub community: Option, + pub community_id: Option, pub auth: String, } -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Clone)] pub struct GetReportCountResponse { - pub community: Option, + pub community_id: Option, pub comment_reports: i64, pub post_reports: i64, } diff --git a/crates/api_common/src/post.rs b/crates/api_common/src/post.rs index 5a83a0c50..4420b673a 100644 --- a/crates/api_common/src/post.rs +++ b/crates/api_common/src/post.rs @@ -112,42 +112,37 @@ pub struct SavePost { pub auth: String, } -#[derive(Serialize, Deserialize)] +#[derive(Deserialize)] pub struct CreatePostReport { pub post_id: PostId, pub reason: String, pub auth: String, } -#[derive(Serialize, Deserialize, Clone)] -pub struct CreatePostReportResponse { - pub success: bool, +#[derive(Serialize, Clone)] +pub struct PostReportResponse { + pub post_report_view: PostReportView, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Deserialize)] pub struct ResolvePostReport { pub report_id: i32, pub resolved: bool, pub auth: String, } -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct ResolvePostReportResponse { - pub report_id: i32, - pub resolved: bool, -} - -#[derive(Serialize, Deserialize, Debug)] +#[derive(Deserialize)] pub struct ListPostReports { pub page: Option, pub limit: Option, - pub community: Option, + /// if no community is given, it returns reports for all communities moderated by the auth user + pub community_id: Option, pub auth: String, } -#[derive(Serialize, Clone, Debug)] +#[derive(Serialize)] pub struct ListPostReportsResponse { - pub posts: Vec, + pub post_reports: Vec, } #[derive(Deserialize, Debug)] diff --git a/crates/db_views/src/comment_report_view.rs b/crates/db_views/src/comment_report_view.rs index 2aa4bdfcf..4b65cd1ce 100644 --- a/crates/db_views/src/comment_report_view.rs +++ b/crates/db_views/src/comment_report_view.rs @@ -1,7 +1,16 @@ use diesel::{result::Error, *}; use lemmy_db_queries::{limit_and_offset, MaybeOptional, ToSafe, ViewToVec}; use lemmy_db_schema::{ - schema::{comment, comment_report, community, person, person_alias_1, person_alias_2, post}, + schema::{ + comment, + comment_report, + community, + community_moderator, + person, + person_alias_1, + person_alias_2, + post, + }, source::{ comment::Comment, comment_report::CommentReport, @@ -10,6 +19,7 @@ use lemmy_db_schema::{ post::Post, }, CommunityId, + PersonId, }; use serde::Serialize; @@ -76,46 +86,60 @@ impl CommentReportView { /// /// * `community_ids` - a Vec of community_ids to get a count for /// TODO this eq_any is a bad way to do this, would be better to join to communitymoderator + /// TODO FIX THIS NOW /// for a person id pub fn get_report_count( conn: &PgConnection, - community_ids: &[CommunityId], + my_person_id: PersonId, + community_id: Option, ) -> Result { use diesel::dsl::*; - comment_report::table + + let mut query = comment_report::table .inner_join(comment::table) .inner_join(post::table.on(comment::post_id.eq(post::id))) - .filter( - comment_report::resolved - .eq(false) - .and(post::community_id.eq_any(community_ids)), + // Test this join + .inner_join( + community_moderator::table.on( + community_moderator::community_id + .eq(post::community_id) + .and(community_moderator::person_id.eq(my_person_id)), + ), ) - .select(count(comment_report::id)) - .first::(conn) + .filter(comment_report::resolved.eq(false)) + .into_boxed(); + + if let Some(community_id) = community_id { + query = query.filter(post::community_id.eq(community_id)) + } + + query.select(count(comment_report::id)).first::(conn) } } pub struct CommentReportQueryBuilder<'a> { conn: &'a PgConnection, - community_ids: Option>, // TODO bad way to do this + my_person_id: PersonId, + community_id: Option, page: Option, limit: Option, resolved: Option, } impl<'a> CommentReportQueryBuilder<'a> { - pub fn create(conn: &'a PgConnection) -> Self { + pub fn create(conn: &'a PgConnection, my_person_id: PersonId) -> Self { CommentReportQueryBuilder { conn, - community_ids: None, + my_person_id, + community_id: None, page: None, limit: None, resolved: Some(false), } } - pub fn community_ids>>(mut self, community_ids: T) -> Self { - self.community_ids = community_ids.get_optional(); + pub fn community_id>(mut self, community_id: T) -> Self { + self.community_id = community_id.get_optional(); self } @@ -141,6 +165,14 @@ impl<'a> CommentReportQueryBuilder<'a> { .inner_join(community::table.on(post::community_id.eq(community::id))) .inner_join(person::table.on(comment_report::creator_id.eq(person::id))) .inner_join(person_alias_1::table.on(post::creator_id.eq(person_alias_1::id))) + // Test this join + .inner_join( + community_moderator::table.on( + community_moderator::community_id + .eq(post::community_id) + .and(community_moderator::person_id.eq(self.my_person_id)), + ), + ) .left_join( person_alias_2::table.on(comment_report::resolver_id.eq(person_alias_2::id.nullable())), ) @@ -155,8 +187,8 @@ impl<'a> CommentReportQueryBuilder<'a> { )) .into_boxed(); - if let Some(comm_ids) = self.community_ids { - query = query.filter(post::community_id.eq_any(comm_ids)); + if let Some(community_id) = self.community_id { + query = query.filter(post::community_id.eq(community_id)); } if let Some(resolved_flag) = self.resolved { diff --git a/crates/db_views/src/post_report_view.rs b/crates/db_views/src/post_report_view.rs index a3dc49e28..ecc76a484 100644 --- a/crates/db_views/src/post_report_view.rs +++ b/crates/db_views/src/post_report_view.rs @@ -1,7 +1,15 @@ use diesel::{result::Error, *}; use lemmy_db_queries::{limit_and_offset, MaybeOptional, ToSafe, ViewToVec}; use lemmy_db_schema::{ - schema::{community, person, person_alias_1, person_alias_2, post, post_report}, + schema::{ + community, + community_moderator, + person, + person_alias_1, + person_alias_2, + post, + post_report, + }, source::{ community::{Community, CommunitySafe}, person::{Person, PersonAlias1, PersonAlias2, PersonSafe, PersonSafeAlias1, PersonSafeAlias2}, @@ -9,6 +17,7 @@ use lemmy_db_schema::{ post_report::PostReport, }, CommunityId, + PersonId, }; use serde::Serialize; @@ -72,42 +81,54 @@ impl PostReportView { /// for a person id pub fn get_report_count( conn: &PgConnection, - community_ids: &[CommunityId], + my_person_id: PersonId, + community_id: Option, ) -> Result { use diesel::dsl::*; - post_report::table + let mut query = post_report::table .inner_join(post::table) - .filter( - post_report::resolved - .eq(false) - .and(post::community_id.eq_any(community_ids)), + // Test this join + .inner_join( + community_moderator::table.on( + community_moderator::community_id + .eq(post::community_id) + .and(community_moderator::person_id.eq(my_person_id)), + ), ) - .select(count(post_report::id)) - .first::(conn) + .filter(post_report::resolved.eq(false)) + .into_boxed(); + + if let Some(community_id) = community_id { + query = query.filter(post::community_id.eq(community_id)) + } + + query.select(count(post_report::id)).first::(conn) } } pub struct PostReportQueryBuilder<'a> { conn: &'a PgConnection, - community_ids: Option>, // TODO bad way to do this + my_person_id: PersonId, + community_id: Option, page: Option, limit: Option, resolved: Option, } impl<'a> PostReportQueryBuilder<'a> { - pub fn create(conn: &'a PgConnection) -> Self { + pub fn create(conn: &'a PgConnection, my_person_id: PersonId) -> Self { PostReportQueryBuilder { conn, - community_ids: None, + my_person_id, + community_id: None, page: None, limit: None, resolved: Some(false), } } - pub fn community_ids>>(mut self, community_ids: T) -> Self { - self.community_ids = community_ids.get_optional(); + pub fn community_id>(mut self, community_id: T) -> Self { + self.community_id = community_id.get_optional(); self } @@ -132,6 +153,14 @@ impl<'a> PostReportQueryBuilder<'a> { .inner_join(community::table.on(post::community_id.eq(community::id))) .inner_join(person::table.on(post_report::creator_id.eq(person::id))) .inner_join(person_alias_1::table.on(post::creator_id.eq(person_alias_1::id))) + // Test this join + .inner_join( + community_moderator::table.on( + community_moderator::community_id + .eq(post::community_id) + .and(community_moderator::person_id.eq(self.my_person_id)), + ), + ) .left_join( person_alias_2::table.on(post_report::resolver_id.eq(person_alias_2::id.nullable())), ) @@ -145,8 +174,8 @@ impl<'a> PostReportQueryBuilder<'a> { )) .into_boxed(); - if let Some(comm_ids) = self.community_ids { - query = query.filter(post::community_id.eq_any(comm_ids)); + if let Some(community_id) = self.community_id { + query = query.filter(post::community_id.eq(community_id)); } if let Some(resolved_flag) = self.resolved {