From d8a4fd6125b541d71df182be0cdb339779c0066c Mon Sep 17 00:00:00 2001 From: Dessalines Date: Fri, 7 Feb 2025 12:53:15 -0500 Subject: [PATCH] Adding listing_type filter for modlog. (#5399) * Adding listing_type filter for modlog. - Fixes #4219 * Running fmt. * Change the listing_type.all to not filter by community. * Adding GetModlog API docs. * Addressing PR comments 2 --- crates/api/src/site/mod_log.rs | 8 +- crates/api_common/src/site.rs | 10 + .../src/combined/modlog_combined_view.rs | 253 ++++++++++-------- 3 files changed, 160 insertions(+), 111 deletions(-) diff --git a/crates/api/src/site/mod_log.rs b/crates/api/src/site/mod_log.rs index 2e6c9b42d..90d74727f 100644 --- a/crates/api/src/site/mod_log.rs +++ b/crates/api/src/site/mod_log.rs @@ -18,10 +18,11 @@ pub async fn get_mod_log( check_private_instance(&local_user_view, &local_site)?; let type_ = data.type_; + let listing_type = data.listing_type; let community_id = data.community_id; - let is_mod_or_admin = if let Some(local_user_view) = local_user_view { - check_community_mod_of_any_or_admin_action(&local_user_view, &mut context.pool()) + let is_mod_or_admin = if let Some(local_user_view) = &local_user_view { + check_community_mod_of_any_or_admin_action(local_user_view, &mut context.pool()) .await .is_ok() } else { @@ -37,6 +38,7 @@ pub async fn get_mod_log( let other_person_id = data.other_person_id; let post_id = data.post_id; let comment_id = data.comment_id; + let local_user = local_user_view.as_ref().map(|u| &u.local_user); // parse pagination token let page_after = if let Some(pa) = &data.page_cursor { @@ -48,9 +50,11 @@ pub async fn get_mod_log( let modlog = ModlogCombinedQuery { type_, + listing_type, community_id, mod_person_id, other_person_id, + local_user, post_id, comment_id, hide_modlog_names: Some(hide_modlog_names), diff --git a/crates/api_common/src/site.rs b/crates/api_common/src/site.rs index 25dcf90b3..474d586e8 100644 --- a/crates/api_common/src/site.rs +++ b/crates/api_common/src/site.rs @@ -124,16 +124,26 @@ pub struct ResolveObjectResponse { #[cfg_attr(feature = "full", ts(export))] /// Fetches the modlog. pub struct GetModlog { + /// Filter by the moderator. #[cfg_attr(feature = "full", ts(optional))] pub mod_person_id: Option, + /// Filter by the community. #[cfg_attr(feature = "full", ts(optional))] pub community_id: Option, + /// Filter by the modlog action type. #[cfg_attr(feature = "full", ts(optional))] pub type_: Option, + /// Filter by listing type. When not using All, it will remove the non-community modlog entries, + /// such as site bans, instance blocks, adding an admin, etc. + #[cfg_attr(feature = "full", ts(optional))] + pub listing_type: Option, + /// Filter by the other / modded person. #[cfg_attr(feature = "full", ts(optional))] pub other_person_id: Option, + /// Filter by post. Will include comments of that post. #[cfg_attr(feature = "full", ts(optional))] pub post_id: Option, + /// Filter by comment. #[cfg_attr(feature = "full", ts(optional))] pub comment_id: Option, #[cfg_attr(feature = "full", ts(optional))] diff --git a/crates/db_views/src/combined/modlog_combined_view.rs b/crates/db_views/src/combined/modlog_combined_view.rs index af2b655cc..9e5279be0 100644 --- a/crates/db_views/src/combined/modlog_combined_view.rs +++ b/crates/db_views/src/combined/modlog_combined_view.rs @@ -34,6 +34,7 @@ use diesel_async::RunQueryDsl; use i_love_jesus::PaginatedQueryBuilder; use lemmy_db_schema::{ aliases, + impls::local_user::LocalUserOptionHelper, newtypes::{CommentId, CommunityId, PersonId, PostId}, schema::{ admin_allow_instance, @@ -44,6 +45,7 @@ use lemmy_db_schema::{ admin_purge_post, comment, community, + community_actions, instance, mod_add, mod_add_community, @@ -60,15 +62,23 @@ use lemmy_db_schema::{ person, post, }, - source::combined::modlog::{modlog_combined_keys as key, ModlogCombined}, + source::{ + combined::modlog::{modlog_combined_keys as key, ModlogCombined}, + local_user::LocalUser, + }, traits::InternalToCombinedView, utils::{get_conn, DbPool}, + ListingType, ModlogActionType, }; use lemmy_utils::error::LemmyResult; impl ModlogCombinedViewInternal { #[diesel::dsl::auto_type(no_type_alias)] - fn joins(mod_person_id: Option, hide_modlog_names: Option) -> _ { + fn joins( + mod_person_id: Option, + hide_modlog_names: Option, + my_person_id: Option, + ) -> _ { // The modded / other person let other_person = aliases::person1.field(person::id); @@ -78,101 +88,117 @@ impl ModlogCombinedViewInternal { // The query for the admin / mod person // It needs an OR condition to every mod table // After this you can use person::id to refer to the moderator - let moderator_names_join = show_mod_names_expr - .or(person::id.nullable().eq(mod_person_id)) - .and( - admin_allow_instance::admin_person_id - .eq(person::id) - .or(admin_block_instance::admin_person_id.eq(person::id)) - .or(admin_purge_comment::admin_person_id.eq(person::id)) - .or(admin_purge_community::admin_person_id.eq(person::id)) - .or(admin_purge_person::admin_person_id.eq(person::id)) - .or(admin_purge_post::admin_person_id.eq(person::id)) - .or(mod_add::mod_person_id.eq(person::id)) - .or(mod_add_community::mod_person_id.eq(person::id)) - .or(mod_ban::mod_person_id.eq(person::id)) - .or(mod_ban_from_community::mod_person_id.eq(person::id)) - .or(mod_feature_post::mod_person_id.eq(person::id)) - .or(mod_hide_community::mod_person_id.eq(person::id)) - .or(mod_lock_post::mod_person_id.eq(person::id)) - .or(mod_remove_comment::mod_person_id.eq(person::id)) - .or(mod_remove_community::mod_person_id.eq(person::id)) - .or(mod_remove_post::mod_person_id.eq(person::id)) - .or(mod_transfer_community::mod_person_id.eq(person::id)), - ); + let moderator_names_join = person::table.on( + show_mod_names_expr + .or(person::id.nullable().eq(mod_person_id)) + .and( + admin_allow_instance::admin_person_id + .eq(person::id) + .or(admin_block_instance::admin_person_id.eq(person::id)) + .or(admin_purge_comment::admin_person_id.eq(person::id)) + .or(admin_purge_community::admin_person_id.eq(person::id)) + .or(admin_purge_person::admin_person_id.eq(person::id)) + .or(admin_purge_post::admin_person_id.eq(person::id)) + .or(mod_add::mod_person_id.eq(person::id)) + .or(mod_add_community::mod_person_id.eq(person::id)) + .or(mod_ban::mod_person_id.eq(person::id)) + .or(mod_ban_from_community::mod_person_id.eq(person::id)) + .or(mod_feature_post::mod_person_id.eq(person::id)) + .or(mod_hide_community::mod_person_id.eq(person::id)) + .or(mod_lock_post::mod_person_id.eq(person::id)) + .or(mod_remove_comment::mod_person_id.eq(person::id)) + .or(mod_remove_community::mod_person_id.eq(person::id)) + .or(mod_remove_post::mod_person_id.eq(person::id)) + .or(mod_transfer_community::mod_person_id.eq(person::id)), + ), + ); - let other_person_join = mod_add::other_person_id - .eq(other_person) - .or(mod_add_community::other_person_id.eq(other_person)) - .or(mod_ban::other_person_id.eq(other_person)) - .or(mod_ban_from_community::other_person_id.eq(other_person)) - // Some tables don't have the other_person_id directly, so you need to join - .or( - mod_feature_post::id - .is_not_null() - .and(post::creator_id.eq(other_person)), - ) - .or( - mod_lock_post::id - .is_not_null() - .and(post::creator_id.eq(other_person)), - ) - .or( - mod_remove_comment::id - .is_not_null() - .and(comment::creator_id.eq(other_person)), - ) - .or( - mod_remove_post::id - .is_not_null() - .and(post::creator_id.eq(other_person)), - ) - .or(mod_transfer_community::other_person_id.eq(other_person)); + let other_person_join = aliases::person1.on( + mod_add::other_person_id + .eq(other_person) + .or(mod_add_community::other_person_id.eq(other_person)) + .or(mod_ban::other_person_id.eq(other_person)) + .or(mod_ban_from_community::other_person_id.eq(other_person)) + // Some tables don't have the other_person_id directly, so you need to join + .or( + mod_feature_post::id + .is_not_null() + .and(post::creator_id.eq(other_person)), + ) + .or( + mod_lock_post::id + .is_not_null() + .and(post::creator_id.eq(other_person)), + ) + .or( + mod_remove_comment::id + .is_not_null() + .and(comment::creator_id.eq(other_person)), + ) + .or( + mod_remove_post::id + .is_not_null() + .and(post::creator_id.eq(other_person)), + ) + .or(mod_transfer_community::other_person_id.eq(other_person)), + ); - let comment_join = mod_remove_comment::comment_id.eq(comment::id); + let comment_join = comment::table.on(mod_remove_comment::comment_id.eq(comment::id)); - let post_join = admin_purge_comment::post_id - .eq(post::id) - .or(mod_feature_post::post_id.eq(post::id)) - .or(mod_lock_post::post_id.eq(post::id)) - .or( - mod_remove_comment::id - .is_not_null() - .and(comment::post_id.eq(post::id)), - ) - .or(mod_remove_post::post_id.eq(post::id)); + let post_join = post::table.on( + admin_purge_comment::post_id + .eq(post::id) + .or(mod_feature_post::post_id.eq(post::id)) + .or(mod_lock_post::post_id.eq(post::id)) + .or( + mod_remove_comment::id + .is_not_null() + .and(comment::post_id.eq(post::id)), + ) + .or(mod_remove_post::post_id.eq(post::id)), + ); - let community_join = admin_purge_post::community_id - .eq(community::id) - .or(mod_add_community::community_id.eq(community::id)) - .or(mod_ban_from_community::community_id.eq(community::id)) - .or( - mod_feature_post::id - .is_not_null() - .and(post::community_id.eq(community::id)), - ) - .or(mod_hide_community::community_id.eq(community::id)) - .or( - mod_lock_post::id - .is_not_null() - .and(post::community_id.eq(community::id)), - ) - .or( - mod_remove_comment::id - .is_not_null() - .and(post::community_id.eq(community::id)), - ) - .or(mod_remove_community::community_id.eq(community::id)) - .or( - mod_remove_post::id - .is_not_null() - .and(post::community_id.eq(community::id)), - ) - .or(mod_transfer_community::community_id.eq(community::id)); + let community_join = community::table.on( + admin_purge_post::community_id + .eq(community::id) + .or(mod_add_community::community_id.eq(community::id)) + .or(mod_ban_from_community::community_id.eq(community::id)) + .or( + mod_feature_post::id + .is_not_null() + .and(post::community_id.eq(community::id)), + ) + .or(mod_hide_community::community_id.eq(community::id)) + .or( + mod_lock_post::id + .is_not_null() + .and(post::community_id.eq(community::id)), + ) + .or( + mod_remove_comment::id + .is_not_null() + .and(post::community_id.eq(community::id)), + ) + .or(mod_remove_community::community_id.eq(community::id)) + .or( + mod_remove_post::id + .is_not_null() + .and(post::community_id.eq(community::id)), + ) + .or(mod_transfer_community::community_id.eq(community::id)), + ); - let instance_join = admin_allow_instance::instance_id - .eq(instance::id) - .or(admin_block_instance::instance_id.eq(instance::id)); + let instance_join = instance::table.on( + admin_allow_instance::instance_id + .eq(instance::id) + .or(admin_block_instance::instance_id.eq(instance::id)), + ); + + let community_actions_join = community_actions::table.on( + community_actions::community_id + .eq(community::id) + .and(community_actions::person_id.nullable().eq(my_person_id)), + ); modlog_combined::table .left_join(admin_allow_instance::table) @@ -192,18 +218,13 @@ impl ModlogCombinedViewInternal { .left_join(mod_remove_community::table) .left_join(mod_remove_post::table) .left_join(mod_transfer_community::table) - // The moderator - .left_join(person::table.on(moderator_names_join)) - // The comment - .left_join(comment::table.on(comment_join)) - // The post - .left_join(post::table.on(post_join)) - // The community - .left_join(community::table.on(community_join)) - // The instance - .left_join(instance::table.on(instance_join)) - // The other / modded person - .left_join(aliases::person1.on(other_person_join)) + .left_join(moderator_names_join) + .left_join(comment_join) + .left_join(post_join) + .left_join(community_join) + .left_join(instance_join) + .left_join(other_person_join) + .left_join(community_actions_join) } } @@ -284,26 +305,30 @@ pub struct PaginationCursorData(ModlogCombined); #[derive(Default)] /// Querying / filtering the modlog. -pub struct ModlogCombinedQuery { +pub struct ModlogCombinedQuery<'a> { pub type_: Option, + pub listing_type: Option, pub comment_id: Option, pub post_id: Option, pub community_id: Option, pub hide_modlog_names: Option, + pub local_user: Option<&'a LocalUser>, pub mod_person_id: Option, pub other_person_id: Option, pub page_after: Option, pub page_back: Option, } -impl ModlogCombinedQuery { +impl ModlogCombinedQuery<'_> { pub async fn list(self, pool: &mut DbPool<'_>) -> LemmyResult> { let conn = &mut get_conn(pool).await?; let other_person = aliases::person1.field(person::id); + let my_person_id = self.local_user.person_id(); - let mut query = ModlogCombinedViewInternal::joins(self.mod_person_id, self.hide_modlog_names) - .select(ModlogCombinedViewInternal::as_select()) - .into_boxed(); + let mut query = + ModlogCombinedViewInternal::joins(self.mod_person_id, self.hide_modlog_names, my_person_id) + .select(ModlogCombinedViewInternal::as_select()) + .into_boxed(); if let Some(mod_person_id) = self.mod_person_id { query = query.filter(person::id.eq(mod_person_id)); @@ -355,6 +380,16 @@ impl ModlogCombinedQuery { } } + let is_subscribed = community_actions::followed.is_not_null(); + query = match self.listing_type.unwrap_or(ListingType::All) { + ListingType::All => query, + ListingType::Subscribed => query.filter(is_subscribed), + ListingType::Local => query + .filter(community::local.eq(true)) + .filter(community::hidden.eq(false).or(is_subscribed)), + ListingType::ModeratorView => query.filter(community_actions::became_moderator.is_not_null()), + }; + let mut query = PaginatedQueryBuilder::new(query); let page_after = self.page_after.map(|c| c.0);