From bab5c9306274d7590940d4d30d5f268534778b52 Mon Sep 17 00:00:00 2001 From: Nutomic Date: Tue, 24 Sep 2024 10:33:53 +0200 Subject: [PATCH] Conditionally hide comments on nsfw posts (fixes #4237) (#5028) * Conditionally hide comments on nsfw posts (fixes #4237) * fix test --- crates/api_common/src/utils.rs | 3 +- crates/apub/src/api/list_comments.rs | 17 +++--- crates/apub/src/api/read_person.rs | 2 +- crates/apub/src/api/search.rs | 8 ++- crates/db_views/src/comment_view.rs | 90 +++++++++++++++++++++------- 5 files changed, 86 insertions(+), 34 deletions(-) diff --git a/crates/api_common/src/utils.rs b/crates/api_common/src/utils.rs index c34261511..06c37ad16 100644 --- a/crates/api_common/src/utils.rs +++ b/crates/api_common/src/utils.rs @@ -776,12 +776,13 @@ pub async fn remove_or_restore_user_data_in_community( // Comments // TODO Diesel doesn't allow updates with joins, so this has to be a loop + let site = Site::read_local(pool).await?; let comments = CommentQuery { creator_id: Some(banned_person_id), community_id: Some(community_id), ..Default::default() } - .list(pool) + .list(&site, pool) .await?; for comment_view in &comments { diff --git a/crates/apub/src/api/list_comments.rs b/crates/apub/src/api/list_comments.rs index c97e051d0..3e7a2f4eb 100644 --- a/crates/apub/src/api/list_comments.rs +++ b/crates/apub/src/api/list_comments.rs @@ -12,10 +12,13 @@ use lemmy_api_common::{ utils::check_private_instance, }; use lemmy_db_schema::{ - source::{comment::Comment, community::Community, local_site::LocalSite}, + source::{comment::Comment, community::Community}, traits::Crud, }; -use lemmy_db_views::{comment_view::CommentQuery, structs::LocalUserView}; +use lemmy_db_views::{ + comment_view::CommentQuery, + structs::{LocalUserView, SiteView}, +}; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult}; #[tracing::instrument(skip(context))] @@ -24,8 +27,8 @@ pub async fn list_comments( context: Data, local_user_view: Option, ) -> LemmyResult> { - let local_site = LocalSite::read(&mut context.pool()).await?; - check_private_instance(&local_user_view, &local_site)?; + let site_view = SiteView::read_local(&mut context.pool()).await?; + check_private_instance(&local_user_view, &site_view.local_site)?; let community_id = if let Some(name) = &data.community_name { Some( @@ -40,7 +43,7 @@ pub async fn list_comments( let sort = Some(comment_sort_type_with_default( data.sort, local_user_ref, - &local_site, + &site_view.local_site, )); let max_depth = data.max_depth; let saved_only = data.saved_only; @@ -58,7 +61,7 @@ pub async fn list_comments( let listing_type = Some(listing_type_with_default( data.type_, local_user_view.as_ref().map(|u| &u.local_user), - &local_site, + &site_view.local_site, community_id, )); @@ -88,7 +91,7 @@ pub async fn list_comments( limit, ..Default::default() } - .list(&mut context.pool()) + .list(&site_view.site, &mut context.pool()) .await .with_lemmy_type(LemmyErrorType::CouldntGetComments)?; diff --git a/crates/apub/src/api/read_person.rs b/crates/apub/src/api/read_person.rs index 58e68f4b9..fac68cd63 100644 --- a/crates/apub/src/api/read_person.rs +++ b/crates/apub/src/api/read_person.rs @@ -85,7 +85,7 @@ pub async fn read_person( creator_id, ..Default::default() } - .list(&mut context.pool()) + .list(&local_site.site, &mut context.pool()) .await?; let moderates = CommunityModeratorView::for_person( diff --git a/crates/apub/src/api/search.rs b/crates/apub/src/api/search.rs index 492d2b087..642c32efa 100644 --- a/crates/apub/src/api/search.rs +++ b/crates/apub/src/api/search.rs @@ -127,7 +127,9 @@ pub async fn search( .await?; } SearchType::Comments => { - comments = comment_query.list(&mut context.pool()).await?; + comments = comment_query + .list(&local_site.site, &mut context.pool()) + .await?; } SearchType::Communities => { communities = community_query @@ -146,7 +148,9 @@ pub async fn search( .list(&local_site.site, &mut context.pool()) .await?; - comments = comment_query.list(&mut context.pool()).await?; + comments = comment_query + .list(&local_site.site, &mut context.pool()) + .await?; communities = if community_or_creator_included { vec![] diff --git a/crates/db_views/src/comment_view.rs b/crates/db_views/src/comment_view.rs index 4f6135479..494536c87 100644 --- a/crates/db_views/src/comment_view.rs +++ b/crates/db_views/src/comment_view.rs @@ -35,7 +35,7 @@ use lemmy_db_schema::{ person_block, post, }, - source::local_user::LocalUser, + source::{local_user::LocalUser, site::Site}, utils::{fuzzy_search, limit_and_offset, DbConn, DbPool, ListFn, Queries, ReadFn}, CommentSortType, ListingType, @@ -43,7 +43,7 @@ use lemmy_db_schema::{ fn queries<'a>() -> Queries< impl ReadFn<'a, CommentView, (CommentId, Option<&'a LocalUser>)>, - impl ListFn<'a, CommentView, CommentQuery<'a>>, + impl ListFn<'a, CommentView, (CommentQuery<'a>, &'a Site)>, > { let is_creator_banned_from_community = exists( community_person_ban::table.filter( @@ -182,7 +182,7 @@ fn queries<'a>() -> Queries< query.first(&mut conn).await }; - let list = move |mut conn: DbConn<'a>, options: CommentQuery<'a>| async move { + let list = move |mut conn: DbConn<'a>, (options, site): (CommentQuery<'a>, &'a Site)| async move { // The left join below will return None in this case let person_id_join = options.local_user.person_id().unwrap_or(PersonId(-1)); let local_user_id_join = options @@ -295,6 +295,12 @@ fn queries<'a>() -> Queries< query = query.filter(not(is_creator_blocked(person_id_join))); }; + if !options.local_user.show_nsfw(site) { + query = query + .filter(post::nsfw.eq(false)) + .filter(community::nsfw.eq(false)); + }; + query = options.local_user.visible_communities_only(query); // A Max depth given means its a tree fetch @@ -398,10 +404,10 @@ pub struct CommentQuery<'a> { } impl<'a> CommentQuery<'a> { - pub async fn list(self, pool: &mut DbPool<'_>) -> Result, Error> { + pub async fn list(self, site: &Site, pool: &mut DbPool<'_>) -> Result, Error> { Ok( queries() - .list(pool, self) + .list(pool, (self, site)) .await? .into_iter() .map(|mut c| { @@ -455,7 +461,8 @@ mod tests { local_user_vote_display_mode::LocalUserVoteDisplayMode, person::{Person, PersonInsertForm}, person_block::{PersonBlock, PersonBlockForm}, - post::{Post, PostInsertForm}, + post::{Post, PostInsertForm, PostUpdateForm}, + site::{Site, SiteInsertForm}, }, traits::{Bannable, Blockable, Crud, Joinable, Likeable, Saveable}, utils::{build_db_pool_for_tests, RANK_DEFAULT}, @@ -475,6 +482,7 @@ mod tests { timmy_local_user_view: LocalUserView, inserted_sara_person: Person, inserted_community: Community, + site: Site, } async fn init_data(pool: &mut DbPool<'_>) -> LemmyResult { @@ -611,6 +619,11 @@ mod tests { person: inserted_timmy_person.clone(), counts: Default::default(), }; + let site_form = SiteInsertForm::builder() + .name("test site".to_string()) + .instance_id(inserted_instance.id) + .build(); + let site = Site::create(pool, &site_form).await?; Ok(Data { inserted_instance, inserted_comment_0, @@ -620,6 +633,7 @@ mod tests { timmy_local_user_view, inserted_sara_person, inserted_community, + site, }) } @@ -640,7 +654,7 @@ mod tests { post_id: (Some(data.inserted_post.id)), ..Default::default() } - .list(pool) + .list(&data.site, pool) .await?; assert_eq!( @@ -654,7 +668,7 @@ mod tests { local_user: (Some(&data.timmy_local_user_view.local_user)), ..Default::default() } - .list(pool) + .list(&data.site, pool) .await?; assert_eq!( @@ -706,7 +720,7 @@ mod tests { liked_only: Some(true), ..Default::default() } - .list(pool) + .list(&data.site, pool) .await? .into_iter() .map(|c| c.comment.content) @@ -722,7 +736,7 @@ mod tests { disliked_only: Some(true), ..Default::default() } - .list(pool) + .list(&data.site, pool) .await?; assert!(read_disliked_comment_views.is_empty()); @@ -743,7 +757,7 @@ mod tests { parent_path: (Some(top_path)), ..Default::default() } - .list(pool) + .list(&data.site, pool) .await?; let child_path = data.inserted_comment_1.path.clone(); @@ -752,7 +766,7 @@ mod tests { parent_path: (Some(child_path)), ..Default::default() } - .list(pool) + .list(&data.site, pool) .await?; // Make sure the comment parent-limited fetch is correct @@ -772,7 +786,7 @@ mod tests { max_depth: (Some(1)), ..Default::default() } - .list(pool) + .list(&data.site, pool) .await?; // Make sure a depth limited one only has the top comment @@ -790,7 +804,7 @@ mod tests { sort: (Some(CommentSortType::New)), ..Default::default() } - .list(pool) + .list(&data.site, pool) .await?; // Make sure a depth limited one, and given child comment 1, has 3 @@ -816,7 +830,7 @@ mod tests { local_user: (Some(&data.timmy_local_user_view.local_user)), ..Default::default() } - .list(pool) + .list(&data.site, pool) .await?; assert_length!(5, all_languages); @@ -834,7 +848,7 @@ mod tests { local_user: (Some(&data.timmy_local_user_view.local_user)), ..Default::default() } - .list(pool) + .list(&data.site, pool) .await?; assert_length!(2, finnish_comments); let finnish_comment = finnish_comments @@ -857,7 +871,7 @@ mod tests { local_user: (Some(&data.timmy_local_user_view.local_user)), ..Default::default() } - .list(pool) + .list(&data.site, pool) .await?; assert_length!(1, undetermined_comment); @@ -881,7 +895,7 @@ mod tests { post_id: Some(data.inserted_comment_2.post_id), ..Default::default() } - .list(pool) + .list(&data.site, pool) .await?; assert_eq!(comments[0].comment.id, data.inserted_comment_2.id); assert!(comments[0].comment.distinguished); @@ -910,7 +924,7 @@ mod tests { sort: (Some(CommentSortType::Old)), ..Default::default() } - .list(pool) + .list(&data.site, pool) .await?; assert_eq!(comments[1].creator.name, "sara"); @@ -931,7 +945,7 @@ mod tests { sort: (Some(CommentSortType::Old)), ..Default::default() } - .list(pool) + .list(&data.site, pool) .await?; // Timmy is an admin, and make sure that field is true @@ -971,7 +985,7 @@ mod tests { saved_only: Some(true), ..Default::default() } - .list(pool) + .list(&data.site, pool) .await?; // There should only be two comments @@ -1001,6 +1015,7 @@ mod tests { LocalUser::delete(pool, data.timmy_local_user_view.local_user.id).await?; Person::delete(pool, data.inserted_sara_person.id).await?; Instance::delete(pool, data.inserted_instance.id).await?; + Site::delete(pool, data.site.id).await?; Ok(()) } @@ -1139,7 +1154,7 @@ mod tests { let unauthenticated_query = CommentQuery { ..Default::default() } - .list(pool) + .list(&data.site, pool) .await?; assert_eq!(0, unauthenticated_query.len()); @@ -1147,7 +1162,7 @@ mod tests { local_user: Some(&data.timmy_local_user_view.local_user), ..Default::default() } - .list(pool) + .list(&data.site, pool) .await?; assert_eq!(5, authenticated_query.len()); @@ -1225,4 +1240,33 @@ mod tests { cleanup(data, pool).await } + + #[tokio::test] + #[serial] + async fn comment_listings_hide_nsfw() -> LemmyResult<()> { + let pool = &build_db_pool_for_tests().await; + let pool = &mut pool.into(); + let data = init_data(pool).await?; + + // Mark a post as nsfw + let update_form = PostUpdateForm { + nsfw: Some(true), + ..Default::default() + }; + Post::update(pool, data.inserted_post.id, &update_form).await?; + + // Make sure comments of this post are not returned + let comments = CommentQuery::default().list(&data.site, pool).await?; + assert_eq!(0, comments.len()); + + // Mark site as nsfw + let mut site = data.site.clone(); + site.content_warning = Some("nsfw".to_string()); + + // Now comments of nsfw post are returned + let comments = CommentQuery::default().list(&site, pool).await?; + assert_eq!(6, comments.len()); + + cleanup(data, pool).await + } }