1 use diesel::{dsl::*, result::Error, *};
3 aggregates::comment_aggregates::CommentAggregates,
5 newtypes::{CommentReportId, CommunityId, PersonId},
21 comment_report::CommentReport,
22 community::{Community, CommunityPersonBan, CommunitySafe},
23 person::{Person, PersonAlias1, PersonAlias2, PersonSafe, PersonSafeAlias1, PersonSafeAlias2},
26 traits::{MaybeOptional, ToSafe, ViewToVec},
28 use serde::{Deserialize, Serialize};
30 #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
31 pub struct CommentReportView {
32 pub comment_report: CommentReport,
35 pub community: CommunitySafe,
36 pub creator: PersonSafe,
37 pub comment_creator: PersonSafeAlias1,
38 pub counts: CommentAggregates,
39 pub creator_banned_from_community: bool, // Left Join to CommunityPersonBan
40 pub my_vote: Option<i16>, // Left join to CommentLike
41 pub resolver: Option<PersonSafeAlias2>,
44 type CommentReportViewTuple = (
52 Option<CommunityPersonBan>,
54 Option<PersonSafeAlias2>,
57 impl CommentReportView {
58 /// returns the CommentReportView for the provided report_id
60 /// * `report_id` - the report id to obtain
63 report_id: CommentReportId,
64 my_person_id: PersonId,
65 ) -> Result<Self, Error> {
74 creator_banned_from_community,
77 ) = comment_report::table
79 .inner_join(comment::table)
80 .inner_join(post::table.on(comment::post_id.eq(post::id)))
81 .inner_join(community::table.on(post::community_id.eq(community::id)))
82 .inner_join(person::table.on(comment_report::creator_id.eq(person::id)))
83 .inner_join(person_alias_1::table.on(comment::creator_id.eq(person_alias_1::id)))
85 comment_aggregates::table.on(comment_report::comment_id.eq(comment_aggregates::comment_id)),
88 community_person_ban::table.on(
90 .eq(community_person_ban::community_id)
91 .and(community_person_ban::person_id.eq(comment::creator_id))
93 community_person_ban::expires
95 .or(community_person_ban::expires.gt(now)),
100 comment_like::table.on(
102 .eq(comment_like::comment_id)
103 .and(comment_like::person_id.eq(my_person_id)),
107 person_alias_2::table.on(comment_report::resolver_id.eq(person_alias_2::id.nullable())),
110 comment_report::all_columns,
111 comment::all_columns,
113 Community::safe_columns_tuple(),
114 Person::safe_columns_tuple(),
115 PersonAlias1::safe_columns_tuple(),
116 comment_aggregates::all_columns,
117 community_person_ban::all_columns.nullable(),
118 comment_like::score.nullable(),
119 PersonAlias2::safe_columns_tuple().nullable(),
121 .first::<CommentReportViewTuple>(conn)?;
123 let my_vote = if comment_like.is_none() {
137 creator_banned_from_community: creator_banned_from_community.is_some(),
143 /// Returns the current unresolved post report count for the communities you mod
144 pub fn get_report_count(
146 my_person_id: PersonId,
148 community_id: Option<CommunityId>,
149 ) -> Result<i64, Error> {
152 let mut query = comment_report::table
153 .inner_join(comment::table)
154 .inner_join(post::table.on(comment::post_id.eq(post::id)))
155 .filter(comment_report::resolved.eq(false))
158 if let Some(community_id) = community_id {
159 query = query.filter(post::community_id.eq(community_id))
162 // If its not an admin, get only the ones you mod
166 community_moderator::table.on(
167 community_moderator::community_id
168 .eq(post::community_id)
169 .and(community_moderator::person_id.eq(my_person_id)),
172 .select(count(comment_report::id))
175 query.select(count(comment_report::id)).first::<i64>(conn)
180 pub struct CommentReportQueryBuilder<'a> {
181 conn: &'a PgConnection,
182 my_person_id: PersonId,
184 community_id: Option<CommunityId>,
187 unresolved_only: Option<bool>,
190 impl<'a> CommentReportQueryBuilder<'a> {
191 pub fn create(conn: &'a PgConnection, my_person_id: PersonId, admin: bool) -> Self {
192 CommentReportQueryBuilder {
199 unresolved_only: Some(true),
203 pub fn community_id<T: MaybeOptional<CommunityId>>(mut self, community_id: T) -> Self {
204 self.community_id = community_id.get_optional();
208 pub fn page<T: MaybeOptional<i64>>(mut self, page: T) -> Self {
209 self.page = page.get_optional();
213 pub fn limit<T: MaybeOptional<i64>>(mut self, limit: T) -> Self {
214 self.limit = limit.get_optional();
218 pub fn unresolved_only<T: MaybeOptional<bool>>(mut self, unresolved_only: T) -> Self {
219 self.unresolved_only = unresolved_only.get_optional();
223 pub fn list(self) -> Result<Vec<CommentReportView>, Error> {
224 let mut query = comment_report::table
225 .inner_join(comment::table)
226 .inner_join(post::table.on(comment::post_id.eq(post::id)))
227 .inner_join(community::table.on(post::community_id.eq(community::id)))
228 .inner_join(person::table.on(comment_report::creator_id.eq(person::id)))
229 .inner_join(person_alias_1::table.on(comment::creator_id.eq(person_alias_1::id)))
231 comment_aggregates::table.on(comment_report::comment_id.eq(comment_aggregates::comment_id)),
234 community_person_ban::table.on(
236 .eq(community_person_ban::community_id)
237 .and(community_person_ban::person_id.eq(comment::creator_id))
239 community_person_ban::expires
241 .or(community_person_ban::expires.gt(now)),
246 comment_like::table.on(
248 .eq(comment_like::comment_id)
249 .and(comment_like::person_id.eq(self.my_person_id)),
253 person_alias_2::table.on(comment_report::resolver_id.eq(person_alias_2::id.nullable())),
256 comment_report::all_columns,
257 comment::all_columns,
259 Community::safe_columns_tuple(),
260 Person::safe_columns_tuple(),
261 PersonAlias1::safe_columns_tuple(),
262 comment_aggregates::all_columns,
263 community_person_ban::all_columns.nullable(),
264 comment_like::score.nullable(),
265 PersonAlias2::safe_columns_tuple().nullable(),
269 if let Some(community_id) = self.community_id {
270 query = query.filter(post::community_id.eq(community_id));
273 if self.unresolved_only.unwrap_or(false) {
274 query = query.filter(comment_report::resolved.eq(false));
277 let (limit, offset) = limit_and_offset(self.page, self.limit);
280 .order_by(comment_report::published.desc())
284 // If its not an admin, get only the ones you mod
285 let res = if !self.admin {
288 community_moderator::table.on(
289 community_moderator::community_id
290 .eq(post::community_id)
291 .and(community_moderator::person_id.eq(self.my_person_id)),
294 .load::<CommentReportViewTuple>(self.conn)?
296 query.load::<CommentReportViewTuple>(self.conn)?
299 Ok(CommentReportView::from_tuple_to_vec(res))
303 impl ViewToVec for CommentReportView {
304 type DbTuple = CommentReportViewTuple;
305 fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
309 comment_report: a.0.to_owned(),
310 comment: a.1.to_owned(),
311 post: a.2.to_owned(),
312 community: a.3.to_owned(),
313 creator: a.4.to_owned(),
314 comment_creator: a.5.to_owned(),
315 counts: a.6.to_owned(),
316 creator_banned_from_community: a.7.is_some(),
318 resolver: a.9.to_owned(),
320 .collect::<Vec<Self>>()
326 use crate::comment_report_view::{CommentReportQueryBuilder, CommentReportView};
327 use lemmy_db_schema::{
328 aggregates::comment_aggregates::CommentAggregates,
329 establish_unpooled_connection,
330 source::{comment::*, comment_report::*, community::*, person::*, post::*},
331 traits::{Crud, Joinable, Reportable},
333 use serial_test::serial;
338 let conn = establish_unpooled_connection();
340 let new_person = PersonForm {
341 name: "timmy_crv".into(),
342 ..PersonForm::default()
345 let inserted_timmy = Person::create(&conn, &new_person).unwrap();
347 let new_person_2 = PersonForm {
348 name: "sara_crv".into(),
349 ..PersonForm::default()
352 let inserted_sara = Person::create(&conn, &new_person_2).unwrap();
354 // Add a third person, since new ppl can only report something once.
355 let new_person_3 = PersonForm {
356 name: "jessica_crv".into(),
357 ..PersonForm::default()
360 let inserted_jessica = Person::create(&conn, &new_person_3).unwrap();
362 let new_community = CommunityForm {
363 name: "test community crv".to_string(),
364 title: "nada".to_owned(),
365 ..CommunityForm::default()
368 let inserted_community = Community::create(&conn, &new_community).unwrap();
371 let timmy_moderator_form = CommunityModeratorForm {
372 community_id: inserted_community.id,
373 person_id: inserted_timmy.id,
376 let _inserted_moderator = CommunityModerator::join(&conn, &timmy_moderator_form).unwrap();
378 let new_post = PostForm {
379 name: "A test post crv".into(),
380 creator_id: inserted_timmy.id,
381 community_id: inserted_community.id,
382 ..PostForm::default()
385 let inserted_post = Post::create(&conn, &new_post).unwrap();
387 let comment_form = CommentForm {
388 content: "A test comment 32".into(),
389 creator_id: inserted_timmy.id,
390 post_id: inserted_post.id,
391 ..CommentForm::default()
394 let inserted_comment = Comment::create(&conn, &comment_form).unwrap();
397 let sara_report_form = CommentReportForm {
398 creator_id: inserted_sara.id,
399 comment_id: inserted_comment.id,
400 original_comment_text: "this was it at time of creation".into(),
401 reason: "from sara".into(),
404 let inserted_sara_report = CommentReport::report(&conn, &sara_report_form).unwrap();
407 let jessica_report_form = CommentReportForm {
408 creator_id: inserted_jessica.id,
409 comment_id: inserted_comment.id,
410 original_comment_text: "this was it at time of creation".into(),
411 reason: "from jessica".into(),
414 let inserted_jessica_report = CommentReport::report(&conn, &jessica_report_form).unwrap();
416 let agg = CommentAggregates::read(&conn, inserted_comment.id).unwrap();
418 let read_jessica_report_view =
419 CommentReportView::read(&conn, inserted_jessica_report.id, inserted_timmy.id).unwrap();
420 let expected_jessica_report_view = CommentReportView {
421 comment_report: inserted_jessica_report.to_owned(),
422 comment: inserted_comment.to_owned(),
424 community: CommunitySafe {
425 id: inserted_community.id,
426 name: inserted_community.name,
431 actor_id: inserted_community.actor_id.to_owned(),
433 title: inserted_community.title,
437 published: inserted_community.published,
439 creator: PersonSafe {
440 id: inserted_jessica.id,
441 name: inserted_jessica.name,
443 published: inserted_jessica.published,
445 actor_id: inserted_jessica.actor_id.to_owned(),
454 inbox_url: inserted_jessica.inbox_url.to_owned(),
455 shared_inbox_url: None,
456 matrix_user_id: None,
459 comment_creator: PersonSafeAlias1 {
460 id: inserted_timmy.id,
461 name: inserted_timmy.name.to_owned(),
463 published: inserted_timmy.published,
465 actor_id: inserted_timmy.actor_id.to_owned(),
474 inbox_url: inserted_timmy.inbox_url.to_owned(),
475 shared_inbox_url: None,
476 matrix_user_id: None,
479 creator_banned_from_community: false,
480 counts: CommentAggregates {
482 comment_id: inserted_comment.id,
486 published: agg.published,
492 assert_eq!(read_jessica_report_view, expected_jessica_report_view);
494 let mut expected_sara_report_view = expected_jessica_report_view.clone();
495 expected_sara_report_view.comment_report = inserted_sara_report;
496 expected_sara_report_view.creator = PersonSafe {
497 id: inserted_sara.id,
498 name: inserted_sara.name,
500 published: inserted_sara.published,
502 actor_id: inserted_sara.actor_id.to_owned(),
511 inbox_url: inserted_sara.inbox_url.to_owned(),
512 shared_inbox_url: None,
513 matrix_user_id: None,
517 // Do a batch read of timmys reports
518 let reports = CommentReportQueryBuilder::create(&conn, inserted_timmy.id, false)
525 expected_jessica_report_view.to_owned(),
526 expected_sara_report_view.to_owned()
530 // Make sure the counts are correct
532 CommentReportView::get_report_count(&conn, inserted_timmy.id, false, None).unwrap();
533 assert_eq!(2, report_count);
535 // Try to resolve the report
536 CommentReport::resolve(&conn, inserted_jessica_report.id, inserted_timmy.id).unwrap();
537 let read_jessica_report_view_after_resolve =
538 CommentReportView::read(&conn, inserted_jessica_report.id, inserted_timmy.id).unwrap();
540 let mut expected_jessica_report_view_after_resolve = expected_jessica_report_view;
541 expected_jessica_report_view_after_resolve
544 expected_jessica_report_view_after_resolve
546 .resolver_id = Some(inserted_timmy.id);
547 expected_jessica_report_view_after_resolve
549 .updated = read_jessica_report_view_after_resolve
552 expected_jessica_report_view_after_resolve.resolver = Some(PersonSafeAlias2 {
553 id: inserted_timmy.id,
554 name: inserted_timmy.name.to_owned(),
556 published: inserted_timmy.published,
558 actor_id: inserted_timmy.actor_id.to_owned(),
567 inbox_url: inserted_timmy.inbox_url.to_owned(),
568 shared_inbox_url: None,
569 matrix_user_id: None,
574 read_jessica_report_view_after_resolve,
575 expected_jessica_report_view_after_resolve
578 // Do a batch read of timmys reports
579 // It should only show saras, which is unresolved
580 let reports_after_resolve = CommentReportQueryBuilder::create(&conn, inserted_timmy.id, false)
583 assert_eq!(reports_after_resolve[0], expected_sara_report_view);
585 // Make sure the counts are correct
586 let report_count_after_resolved =
587 CommentReportView::get_report_count(&conn, inserted_timmy.id, false, None).unwrap();
588 assert_eq!(1, report_count_after_resolved);
590 Person::delete(&conn, inserted_timmy.id).unwrap();
591 Person::delete(&conn, inserted_sara.id).unwrap();
592 Person::delete(&conn, inserted_jessica.id).unwrap();
593 Community::delete(&conn, inserted_community.id).unwrap();