use crate::{
check_community_ban,
+ collect_moderated_communities,
get_post,
get_user_from_jwt,
get_user_from_jwt_opt,
is_mod_or_admin,
Perform,
- collect_moderated_communities,
};
use actix_web::web::Data;
use lemmy_apub::{ApubLikeableType, ApubObjectType};
Crud,
Likeable,
ListingType,
+ Reportable,
Saveable,
SortType,
- Reportable,
};
use lemmy_structs::{blocking, comment::*, send_local_notifs};
use lemmy_utils::{
ConnectionId,
LemmyError,
};
-use lemmy_websocket::{messages::{SendComment, SendUserRoomMessage}, LemmyContext, UserOperation};
+use lemmy_websocket::{
+ messages::{SendComment, SendModRoomMessage, SendUserRoomMessage},
+ LemmyContext,
+ UserOperation,
+};
use std::str::FromStr;
-use lemmy_websocket::messages::SendModRoomMessage;
#[async_trait::async_trait(?Send)]
impl Perform for CreateComment {
}
}
+/// Creates a comment report and notifies the moderators of the community
#[async_trait::async_trait(?Send)]
impl Perform for CreateCommentReport {
type Response = CreateCommentReportResponse;
if reason.len() > 1000 {
return Err(APIError::err("report_too_long").into());
}
-
+
let user_id = user.id;
let comment_id = data.comment_id;
let comment = blocking(context.pool(), move |conn| {
CommentView::read(&conn, comment_id, None)
- }).await??;
+ })
+ .await??;
check_community_ban(user_id, comment.community_id, context.pool()).await?;
let report_form = CommentReportForm {
creator_id: user_id,
comment_id,
- comment_text: comment.content,
+ original_comment_text: comment.content,
reason: data.reason.to_owned(),
};
let report = match blocking(context.pool(), move |conn| {
CommentReport::report(conn, &report_form)
- }).await? {
+ })
+ .await?
+ {
Ok(report) => report,
- Err(_e) => return Err(APIError::err("couldnt_create_report").into())
+ Err(_e) => return Err(APIError::err("couldnt_create_report").into()),
};
let res = CreateCommentReportResponse { success: true };
}
}
+/// 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;
let report_id = data.report_id;
let report = blocking(context.pool(), move |conn| {
CommentReportView::read(&conn, report_id)
- }).await??;
+ })
+ .await??;
let user_id = user.id;
is_mod_or_admin(context.pool(), user_id, report.community_id).await?;
}
};
- if blocking(context.pool(),resolve_fun).await?.is_err() {
- return Err(APIError::err("couldnt_resolve_report").into())
+ if blocking(context.pool(), resolve_fun).await?.is_err() {
+ return Err(APIError::err("couldnt_resolve_report").into());
};
let report_id = data.report_id;
}
}
+/// Lists comment reports for a community if an id is supplied
+/// or returns all comment reports for communities a user moderates
#[async_trait::async_trait(?Send)]
impl Perform for ListCommentReports {
type Response = ListCommentReportsResponse;
let user_id = user.id;
let community_id = data.community;
- let community_ids = collect_moderated_communities(user_id, community_id, context.pool()).await?;
+ let community_ids =
+ collect_moderated_communities(user_id, community_id, context.pool()).await?;
let page = data.page;
let limit = data.limit;
let comments = blocking(context.pool(), move |conn| {
CommentReportQueryBuilder::create(conn)
- .community_ids(community_ids)
- .page(page)
- .limit(limit)
- .list()
+ .community_ids(community_ids)
+ .page(page)
+ .limit(limit)
+ .list()
})
- .await??;
+ .await??;
let res = ListCommentReportsResponse { comments };
Ok(res)
}
-}
\ No newline at end of file
+}
LemmyError,
};
use lemmy_websocket::{
- messages::{GetCommunityUsersOnline, JoinCommunityRoom, SendCommunityRoomMessage},
+ messages::{GetCommunityUsersOnline, JoinCommunityRoom, JoinModRoom, SendCommunityRoomMessage},
LemmyContext,
UserOperation,
};
use std::str::FromStr;
-use lemmy_websocket::messages::JoinModRoom;
#[async_trait::async_trait(?Send)]
impl Perform for GetCommunity {
}
}
+/// 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
+///
+/// * `user_id` - the user id of the moderator
+/// * `community_id` - optional community id to check for moderator privileges
+/// * `pool` - the diesel db pool
pub(in crate) async fn collect_moderated_communities(
user_id: i32,
community_id: Option<i32>,
} else {
let ids = blocking(pool, move |conn: &'_ _| {
CommunityModerator::get_user_moderated_communities(conn, user_id)
- }).await??;
+ })
+ .await??;
Ok(ids)
}
}
UserOperation::CommunityJoin => {
do_websocket_operation::<CommunityJoin>(context, id, op, data).await
}
- UserOperation::ModJoin => {
- do_websocket_operation::<ModJoin>(context, id, op, data).await
- }
+ UserOperation::ModJoin => do_websocket_operation::<ModJoin>(context, id, op, data).await,
UserOperation::SaveUserSettings => {
do_websocket_operation::<SaveUserSettings>(context, id, op, data).await
}
use crate::{
check_community_ban,
check_optional_url,
+ collect_moderated_communities,
get_user_from_jwt,
get_user_from_jwt_opt,
is_mod_or_admin,
Perform,
- collect_moderated_communities,
};
use actix_web::web::Data;
use lemmy_apub::{ApubLikeableType, ApubObjectType};
Crud,
Likeable,
ListingType,
+ Reportable,
Saveable,
SortType,
- Reportable,
};
use lemmy_structs::{blocking, post::*};
use lemmy_utils::{
LemmyError,
};
use lemmy_websocket::{
- messages::{
- GetPostUsersOnline,
- JoinPostRoom,
- SendModRoomMessage,
- SendPost,
- SendUserRoomMessage
- },
+ messages::{GetPostUsersOnline, JoinPostRoom, SendModRoomMessage, SendPost, SendUserRoomMessage},
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;
if reason.len() > 1000 {
return Err(APIError::err("report_too_long").into());
}
-
+
let user_id = user.id;
let post_id = data.post_id;
let post = blocking(context.pool(), move |conn| {
PostView::read(&conn, post_id, None)
- }).await??;
+ })
+ .await??;
check_community_ban(user_id, post.community_id, context.pool()).await?;
let report_form = PostReportForm {
creator_id: user_id,
post_id,
- post_name: post.name,
- post_url: post.url,
- post_body: post.body,
+ original_post_name: post.name,
+ original_post_url: post.url,
+ original_post_body: post.body,
reason: data.reason.to_owned(),
};
let report = match blocking(context.pool(), move |conn| {
PostReport::report(conn, &report_form)
- }).await? {
+ })
+ .await?
+ {
Ok(report) => report,
- Err(_e) => return Err(APIError::err("couldnt_create_report").into())
+ Err(_e) => return Err(APIError::err("couldnt_create_report").into()),
};
let res = CreatePostReportResponse { success: true };
}
}
+/// 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;
let report_id = data.report_id;
let report = blocking(context.pool(), move |conn| {
PostReportView::read(&conn, report_id)
- }).await??;
+ })
+ .await??;
let user_id = user.id;
is_mod_or_admin(context.pool(), user_id, report.community_id).await?;
resolved: true,
};
- if blocking(context.pool(),resolve_fun).await?.is_err() {
- return Err(APIError::err("couldnt_resolve_report").into())
+ if blocking(context.pool(), resolve_fun).await?.is_err() {
+ return Err(APIError::err("couldnt_resolve_report").into());
};
context.chat_server().do_send(SendModRoomMessage {
}
}
+/// Lists post reports for a community if an id is supplied
+/// or returns all post reports for communities a user moderates
#[async_trait::async_trait(?Send)]
impl Perform for ListPostReports {
type Response = ListPostReportsResponse;
let user_id = user.id;
let community_id = data.community;
- let community_ids = collect_moderated_communities(user_id, community_id, context.pool()).await?;
+ let community_ids =
+ collect_moderated_communities(user_id, community_id, context.pool()).await?;
let page = data.page;
let limit = data.limit;
let posts = blocking(context.pool(), move |conn| {
PostReportQueryBuilder::create(conn)
- .community_ids(community_ids)
- .page(page)
- .limit(limit)
- .list()
+ .community_ids(community_ids)
+ .page(page)
+ .limit(limit)
+ .list()
})
- .await??;
+ .await??;
let res = ListPostReportsResponse { posts };
captcha_espeak_wav_base64,
check_optional_url,
claims::Claims,
+ collect_moderated_communities,
get_user_from_jwt,
get_user_from_jwt_opt,
is_admin,
Perform,
- collect_moderated_communities
};
use actix_web::web::Data;
use anyhow::Context;
let user_id = user.id;
let community_id = data.community;
- let community_ids = collect_moderated_communities(user_id, community_id, context.pool()).await?;
+ let community_ids =
+ collect_moderated_communities(user_id, community_id, context.pool()).await?;
let res = {
if community_ids.is_empty() {
}
} 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, &ids)
+ })
+ .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, &ids)
+ })
+ .await??;
GetReportCountResponse {
community: data.community,
use diesel::{dsl::*, pg::Pg, result::Error, *};
use serde::{Deserialize, Serialize};
-use crate::{limit_and_offset, MaybeOptional, schema::comment_report, comment::Comment, Reportable, naive_now};
+use crate::{
+ comment::Comment,
+ limit_and_offset,
+ naive_now,
+ schema::comment_report,
+ MaybeOptional,
+ Reportable,
+};
table! {
comment_report_view (id) {
id -> Int4,
creator_id -> Int4,
comment_id -> Int4,
- comment_text -> Text,
+ original_comment_text -> Text,
reason -> Text,
resolved -> Bool,
resolver_id -> Nullable<Int4>,
pub id: i32,
pub creator_id: i32,
pub comment_id: i32,
- pub comment_text: String,
+ pub original_comment_text: String,
pub reason: String,
pub resolved: bool,
pub resolver_id: Option<i32>,
pub struct CommentReportForm {
pub creator_id: i32,
pub comment_id: i32,
- pub comment_text: String,
+ pub original_comment_text: String,
pub reason: String,
}
impl Reportable<CommentReportForm> for CommentReport {
+ /// creates a comment report and returns it
+ ///
+ /// * `conn` - the postgres connection
+ /// * `comment_report_form` - the filled CommentReportForm to insert
fn report(conn: &PgConnection, comment_report_form: &CommentReportForm) -> Result<Self, Error> {
use crate::schema::comment_report::dsl::*;
insert_into(comment_report)
.get_result::<Self>(conn)
}
+ /// resolve a comment 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: i32, by_resolver_id: i32) -> Result<usize, Error> {
use crate::schema::comment_report::dsl::*;
update(comment_report.find(report_id))
.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: i32, by_resolver_id: i32) -> Result<usize, Error> {
use crate::schema::comment_report::dsl::*;
update(comment_report.find(report_id))
}
}
-#[derive(
-Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize, Clone,
-)]
+#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize, Clone)]
#[table_name = "comment_report_view"]
pub struct CommentReportView {
pub id: i32,
pub creator_id: i32,
pub comment_id: i32,
- pub comment_text: String,
+ pub original_comment_text: String,
pub reason: String,
pub resolved: bool,
pub resolver_id: Option<i32>,
}
impl CommentReportView {
+ /// returns the CommentReportView for the provided report_id
+ ///
+ /// * `report_id` - the report id to obtain
pub fn read(conn: &PgConnection, report_id: i32) -> Result<Self, Error> {
use super::comment_report::comment_report_view::dsl::*;
- comment_report_view
- .find(report_id)
- .first::<Self>(conn)
+ comment_report_view.find(report_id).first::<Self>(conn)
}
- pub fn get_report_count(conn: &PgConnection, community_ids: &Vec<i32>) -> Result<i32, Error> {
+ /// returns the current unresolved comment report count for the supplied community ids
+ ///
+ /// * `community_ids` - a Vec<i32> of community_ids to get a count for
+ pub fn get_report_count(conn: &PgConnection, community_ids: &Vec<i32>) -> Result<i64, Error> {
use super::comment_report::comment_report_view::dsl::*;
comment_report_view
.filter(resolved.eq(false).and(community_id.eq_any(community_ids)))
- .select(sql::<sql_types::Integer>("COUNT(*)"))
- .first::<i32>(conn)
+ .select(count(id))
+ .first::<i64>(conn)
}
}
diesel::delete(community_moderator.filter(community_id.eq(for_community_id))).execute(conn)
}
- pub fn get_user_moderated_communities(conn: &PgConnection, for_user_id: i32) -> Result<Vec<i32>, Error> {
+ pub fn get_user_moderated_communities(
+ conn: &PgConnection,
+ for_user_id: i32,
+ ) -> Result<Vec<i32>, Error> {
use crate::schema::community_moderator::dsl::*;
community_moderator
- .filter(user_id.eq(for_user_id))
- .select(community_id)
- .load::<i32>(conn)
+ .filter(user_id.eq(for_user_id))
+ .select(community_id)
+ .load::<i32>(conn)
}
}
pub trait Reportable<T> {
fn report(conn: &PgConnection, form: &T) -> Result<Self, Error>
- where
- Self: Sized;
+ where
+ Self: Sized;
fn resolve(conn: &PgConnection, report_id: i32, resolver_id: i32) -> Result<usize, Error>
- where
- Self: Sized;
+ where
+ Self: Sized;
fn unresolve(conn: &PgConnection, report_id: i32, resolver_id: i32) -> Result<usize, Error>
- where
- Self: Sized;
+ where
+ Self: Sized;
}
pub trait MaybeOptional<T> {
use diesel::{dsl::*, pg::Pg, result::Error, *};
use serde::{Deserialize, Serialize};
-use crate::{limit_and_offset, MaybeOptional, schema::post_report, post::Post, Reportable, naive_now};
+use crate::{
+ limit_and_offset,
+ naive_now,
+ post::Post,
+ schema::post_report,
+ MaybeOptional,
+ Reportable,
+};
table! {
post_report_view (id) {
id -> Int4,
creator_id -> Int4,
post_id -> Int4,
- post_name -> Varchar,
- post_url -> Nullable<Text>,
- post_body -> Nullable<Text>,
+ original_post_name -> Varchar,
+ original_post_url -> Nullable<Text>,
+ original_post_body -> Nullable<Text>,
reason -> Text,
resolved -> Bool,
resolver_id -> Nullable<Int4>,
pub id: i32,
pub creator_id: i32,
pub post_id: i32,
- pub post_name: String,
- pub post_url: Option<String>,
- pub post_body: Option<String>,
+ pub original_post_name: String,
+ pub original_post_url: Option<String>,
+ pub original_post_body: Option<String>,
pub reason: String,
pub resolved: bool,
pub resolver_id: Option<i32>,
pub struct PostReportForm {
pub creator_id: i32,
pub post_id: i32,
- pub post_name: String,
- pub post_url: Option<String>,
- pub post_body: Option<String>,
+ pub original_post_name: String,
+ pub original_post_url: Option<String>,
+ pub original_post_body: Option<String>,
pub reason: String,
}
impl Reportable<PostReportForm> for PostReport {
+ /// creates a post report and returns it
+ ///
+ /// * `conn` - the postgres connection
+ /// * `post_report_form` - the filled CommentReportForm to insert
fn report(conn: &PgConnection, post_report_form: &PostReportForm) -> Result<Self, Error> {
use crate::schema::post_report::dsl::*;
insert_into(post_report)
.get_result::<Self>(conn)
}
+ /// resolve a post 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: i32, by_resolver_id: i32) -> Result<usize, Error> {
use crate::schema::post_report::dsl::*;
update(post_report.find(report_id))
.execute(conn)
}
+ /// resolve a post 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: i32, by_resolver_id: i32) -> Result<usize, Error> {
use crate::schema::post_report::dsl::*;
update(post_report.find(report_id))
}
}
-#[derive(
-Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize, Clone,
-)]
+#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize, Clone)]
#[table_name = "post_report_view"]
pub struct PostReportView {
pub id: i32,
pub creator_id: i32,
pub post_id: i32,
- pub post_name: String,
- pub post_url: Option<String>,
- pub post_body: Option<String>,
+ pub original_post_name: String,
+ pub original_post_url: Option<String>,
+ pub original_post_body: Option<String>,
pub reason: String,
pub resolved: bool,
pub resolver_id: Option<i32>,
}
impl PostReportView {
+ /// returns the PostReportView for the provided report_id
+ ///
+ /// * `report_id` - the report id to obtain
pub fn read(conn: &PgConnection, report_id: i32) -> Result<Self, Error> {
use super::post_report::post_report_view::dsl::*;
- post_report_view
- .find(report_id)
- .first::<Self>(conn)
+ post_report_view.find(report_id).first::<Self>(conn)
}
- pub fn get_report_count(conn: &PgConnection, community_ids: &Vec<i32>) -> Result<i32, Error> {
+ /// returns the current unresolved post report count for the supplied community ids
+ ///
+ /// * `community_ids` - a Vec<i32> of community_ids to get a count for
+ pub fn get_report_count(conn: &PgConnection, community_ids: &Vec<i32>) -> Result<i64, Error> {
use super::post_report::post_report_view::dsl::*;
post_report_view
.filter(resolved.eq(false).and(community_id.eq_any(community_ids)))
- .select(sql::<sql_types::Integer>("COUNT(*)"))
- .first::<i32>(conn)
+ .select(count(id))
+ .first::<i64>(conn)
}
}
id -> Int4,
creator_id -> Int4,
comment_id -> Int4,
- comment_text -> Text,
+ original_comment_text -> Text,
reason -> Text,
resolved -> Bool,
resolver_id -> Nullable<Int4>,
id -> Int4,
creator_id -> Int4,
post_id -> Int4,
- post_name -> Varchar,
- post_url -> Nullable<Text>,
- post_body -> Nullable<Text>,
+ original_post_name -> Varchar,
+ original_post_url -> Nullable<Text>,
+ original_post_body -> Nullable<Text>,
reason -> Text,
resolved -> Bool,
resolver_id -> Nullable<Int4>,
-use lemmy_db::{
- comment_view::CommentView,
- comment_report::CommentReportView,
-};
+use lemmy_db::{comment_report::CommentReportView, comment_view::CommentView};
use serde::{Deserialize, Serialize};
#[derive(Deserialize)]
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct GetReportCountResponse {
pub community: Option<i32>,
- pub comment_reports: i32,
- pub post_reports: i32,
+ pub comment_reports: i64,
+ pub post_reports: i64,
}
community_id: CommunityId,
websocket_id: Option<ConnectionId>,
) -> Result<(), LemmyError>
- where
- Response: Serialize,
+ where
+ Response: Serialize,
{
let res_str = &serialize_websocket_message(op, response)?;
if let Some(sessions) = self.mod_rooms.get(&community_id) {
}
impl<Response> Handler<SendModRoomMessage<Response>> for ChatServer
- where
- Response: Serialize,
+where
+ Response: Serialize,
{
type Result = ();
id serial primary key,
creator_id int references user_ on update cascade on delete cascade not null, -- user reporting comment
comment_id int references comment on update cascade on delete cascade not null, -- comment being reported
- comment_text text not null,
+ original_comment_text text not null,
reason text not null,
resolved bool not null default false,
resolver_id int references user_ on update cascade on delete cascade, -- user resolving report
id serial primary key,
creator_id int references user_ on update cascade on delete cascade not null, -- user reporting post
post_id int references post on update cascade on delete cascade not null, -- post being reported
- post_name varchar(100) not null,
- post_url text,
- post_body text,
+ original_post_name varchar(100) not null,
+ original_post_url text,
+ original_post_body text,
reason text not null,
resolved bool not null default false,
resolver_id int references user_ on update cascade on delete cascade, -- user resolving report
left join comment c on c.id = cr.comment_id
left join post p on p.id = c.post_id
left join user_ u on u.id = c.creator_id
-left join user_ f on f.id = cr.creator_id;
+left join user_ f on f.id = cr.creator_id
left join user_ r on r.id = cr.resolver_id;
create or replace view post_report_view as
p.url as current_post_url,
p.body as current_post_body,
p.community_id,
-f.name as creator_name,
-- report creator details
f.actor_id as creator_actor_id,
f.name as creator_name,
from post_report pr
left join post p on p.id = pr.post_id
left join user_ u on u.id = p.creator_id
-left join user_ f on f.id = pr.creator_id;
+left join user_ f on f.id = pr.creator_id
left join user_ r on r.id = pr.resolver_id;
.route("/save", web::put().to(route_post::<SavePost>))
.route("/join", web::post().to(route_post::<PostJoin>))
.route("/report", web::post().to(route_post::<CreatePostReport>))
- .route("/report/resolve", web::put().to(route_post::<ResolvePostReport>))
- .route("/report/list", web::get().to(route_get::<ListPostReports>))
+ .route(
+ "/report/resolve",
+ web::put().to(route_post::<ResolvePostReport>),
+ )
+ .route("/report/list", web::get().to(route_get::<ListPostReports>)),
)
// Comment
.service(
.route("/save", web::put().to(route_post::<SaveComment>))
.route("/list", web::get().to(route_get::<GetComments>))
.route("/report", web::post().to(route_post::<CreateCommentReport>))
- .route("/report/resolve", web::put().to(route_post::<ResolveCommentReport>))
- .route("/report/list", web::get().to(route_get::<ListCommentReports>))
+ .route(
+ "/report/resolve",
+ web::put().to(route_post::<ResolveCommentReport>),
+ )
+ .route(
+ "/report/list",
+ web::get().to(route_get::<ListCommentReports>),
+ ),
)
// Private Message
.service(
"/save_user_settings",
web::put().to(route_post::<SaveUserSettings>),
)
- .route(
- "/report_count",
- web::get().to(route_get::<GetReportCount>)
- ),
+ .route("/report_count", web::get().to(route_get::<GetReportCount>)),
)
// Admin Actions
.service(