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
This commit is contained in:
Dessalines 2025-02-07 12:53:15 -05:00 committed by GitHub
parent 496ae58cb6
commit d8a4fd6125
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 160 additions and 111 deletions

View file

@ -18,10 +18,11 @@ pub async fn get_mod_log(
check_private_instance(&local_user_view, &local_site)?; check_private_instance(&local_user_view, &local_site)?;
let type_ = data.type_; let type_ = data.type_;
let listing_type = data.listing_type;
let community_id = data.community_id; let community_id = data.community_id;
let is_mod_or_admin = if let Some(local_user_view) = local_user_view { 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()) check_community_mod_of_any_or_admin_action(local_user_view, &mut context.pool())
.await .await
.is_ok() .is_ok()
} else { } else {
@ -37,6 +38,7 @@ pub async fn get_mod_log(
let other_person_id = data.other_person_id; let other_person_id = data.other_person_id;
let post_id = data.post_id; let post_id = data.post_id;
let comment_id = data.comment_id; let comment_id = data.comment_id;
let local_user = local_user_view.as_ref().map(|u| &u.local_user);
// parse pagination token // parse pagination token
let page_after = if let Some(pa) = &data.page_cursor { let page_after = if let Some(pa) = &data.page_cursor {
@ -48,9 +50,11 @@ pub async fn get_mod_log(
let modlog = ModlogCombinedQuery { let modlog = ModlogCombinedQuery {
type_, type_,
listing_type,
community_id, community_id,
mod_person_id, mod_person_id,
other_person_id, other_person_id,
local_user,
post_id, post_id,
comment_id, comment_id,
hide_modlog_names: Some(hide_modlog_names), hide_modlog_names: Some(hide_modlog_names),

View file

@ -124,16 +124,26 @@ pub struct ResolveObjectResponse {
#[cfg_attr(feature = "full", ts(export))] #[cfg_attr(feature = "full", ts(export))]
/// Fetches the modlog. /// Fetches the modlog.
pub struct GetModlog { pub struct GetModlog {
/// Filter by the moderator.
#[cfg_attr(feature = "full", ts(optional))] #[cfg_attr(feature = "full", ts(optional))]
pub mod_person_id: Option<PersonId>, pub mod_person_id: Option<PersonId>,
/// Filter by the community.
#[cfg_attr(feature = "full", ts(optional))] #[cfg_attr(feature = "full", ts(optional))]
pub community_id: Option<CommunityId>, pub community_id: Option<CommunityId>,
/// Filter by the modlog action type.
#[cfg_attr(feature = "full", ts(optional))] #[cfg_attr(feature = "full", ts(optional))]
pub type_: Option<ModlogActionType>, pub type_: Option<ModlogActionType>,
/// 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<ListingType>,
/// Filter by the other / modded person.
#[cfg_attr(feature = "full", ts(optional))] #[cfg_attr(feature = "full", ts(optional))]
pub other_person_id: Option<PersonId>, pub other_person_id: Option<PersonId>,
/// Filter by post. Will include comments of that post.
#[cfg_attr(feature = "full", ts(optional))] #[cfg_attr(feature = "full", ts(optional))]
pub post_id: Option<PostId>, pub post_id: Option<PostId>,
/// Filter by comment.
#[cfg_attr(feature = "full", ts(optional))] #[cfg_attr(feature = "full", ts(optional))]
pub comment_id: Option<CommentId>, pub comment_id: Option<CommentId>,
#[cfg_attr(feature = "full", ts(optional))] #[cfg_attr(feature = "full", ts(optional))]

View file

@ -34,6 +34,7 @@ use diesel_async::RunQueryDsl;
use i_love_jesus::PaginatedQueryBuilder; use i_love_jesus::PaginatedQueryBuilder;
use lemmy_db_schema::{ use lemmy_db_schema::{
aliases, aliases,
impls::local_user::LocalUserOptionHelper,
newtypes::{CommentId, CommunityId, PersonId, PostId}, newtypes::{CommentId, CommunityId, PersonId, PostId},
schema::{ schema::{
admin_allow_instance, admin_allow_instance,
@ -44,6 +45,7 @@ use lemmy_db_schema::{
admin_purge_post, admin_purge_post,
comment, comment,
community, community,
community_actions,
instance, instance,
mod_add, mod_add,
mod_add_community, mod_add_community,
@ -60,15 +62,23 @@ use lemmy_db_schema::{
person, person,
post, post,
}, },
source::combined::modlog::{modlog_combined_keys as key, ModlogCombined}, source::{
combined::modlog::{modlog_combined_keys as key, ModlogCombined},
local_user::LocalUser,
},
traits::InternalToCombinedView, traits::InternalToCombinedView,
utils::{get_conn, DbPool}, utils::{get_conn, DbPool},
ListingType,
ModlogActionType, ModlogActionType,
}; };
use lemmy_utils::error::LemmyResult; use lemmy_utils::error::LemmyResult;
impl ModlogCombinedViewInternal { impl ModlogCombinedViewInternal {
#[diesel::dsl::auto_type(no_type_alias)] #[diesel::dsl::auto_type(no_type_alias)]
fn joins(mod_person_id: Option<PersonId>, hide_modlog_names: Option<bool>) -> _ { fn joins(
mod_person_id: Option<PersonId>,
hide_modlog_names: Option<bool>,
my_person_id: Option<PersonId>,
) -> _ {
// The modded / other person // The modded / other person
let other_person = aliases::person1.field(person::id); let other_person = aliases::person1.field(person::id);
@ -78,101 +88,117 @@ impl ModlogCombinedViewInternal {
// The query for the admin / mod person // The query for the admin / mod person
// It needs an OR condition to every mod table // It needs an OR condition to every mod table
// After this you can use person::id to refer to the moderator // After this you can use person::id to refer to the moderator
let moderator_names_join = show_mod_names_expr let moderator_names_join = person::table.on(
.or(person::id.nullable().eq(mod_person_id)) show_mod_names_expr
.and( .or(person::id.nullable().eq(mod_person_id))
admin_allow_instance::admin_person_id .and(
.eq(person::id) admin_allow_instance::admin_person_id
.or(admin_block_instance::admin_person_id.eq(person::id)) .eq(person::id)
.or(admin_purge_comment::admin_person_id.eq(person::id)) .or(admin_block_instance::admin_person_id.eq(person::id))
.or(admin_purge_community::admin_person_id.eq(person::id)) .or(admin_purge_comment::admin_person_id.eq(person::id))
.or(admin_purge_person::admin_person_id.eq(person::id)) .or(admin_purge_community::admin_person_id.eq(person::id))
.or(admin_purge_post::admin_person_id.eq(person::id)) .or(admin_purge_person::admin_person_id.eq(person::id))
.or(mod_add::mod_person_id.eq(person::id)) .or(admin_purge_post::admin_person_id.eq(person::id))
.or(mod_add_community::mod_person_id.eq(person::id)) .or(mod_add::mod_person_id.eq(person::id))
.or(mod_ban::mod_person_id.eq(person::id)) .or(mod_add_community::mod_person_id.eq(person::id))
.or(mod_ban_from_community::mod_person_id.eq(person::id)) .or(mod_ban::mod_person_id.eq(person::id))
.or(mod_feature_post::mod_person_id.eq(person::id)) .or(mod_ban_from_community::mod_person_id.eq(person::id))
.or(mod_hide_community::mod_person_id.eq(person::id)) .or(mod_feature_post::mod_person_id.eq(person::id))
.or(mod_lock_post::mod_person_id.eq(person::id)) .or(mod_hide_community::mod_person_id.eq(person::id))
.or(mod_remove_comment::mod_person_id.eq(person::id)) .or(mod_lock_post::mod_person_id.eq(person::id))
.or(mod_remove_community::mod_person_id.eq(person::id)) .or(mod_remove_comment::mod_person_id.eq(person::id))
.or(mod_remove_post::mod_person_id.eq(person::id)) .or(mod_remove_community::mod_person_id.eq(person::id))
.or(mod_transfer_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 let other_person_join = aliases::person1.on(
.eq(other_person) mod_add::other_person_id
.or(mod_add_community::other_person_id.eq(other_person)) .eq(other_person)
.or(mod_ban::other_person_id.eq(other_person)) .or(mod_add_community::other_person_id.eq(other_person))
.or(mod_ban_from_community::other_person_id.eq(other_person)) .or(mod_ban::other_person_id.eq(other_person))
// Some tables don't have the other_person_id directly, so you need to join .or(mod_ban_from_community::other_person_id.eq(other_person))
.or( // Some tables don't have the other_person_id directly, so you need to join
mod_feature_post::id .or(
.is_not_null() mod_feature_post::id
.and(post::creator_id.eq(other_person)), .is_not_null()
) .and(post::creator_id.eq(other_person)),
.or( )
mod_lock_post::id .or(
.is_not_null() mod_lock_post::id
.and(post::creator_id.eq(other_person)), .is_not_null()
) .and(post::creator_id.eq(other_person)),
.or( )
mod_remove_comment::id .or(
.is_not_null() mod_remove_comment::id
.and(comment::creator_id.eq(other_person)), .is_not_null()
) .and(comment::creator_id.eq(other_person)),
.or( )
mod_remove_post::id .or(
.is_not_null() mod_remove_post::id
.and(post::creator_id.eq(other_person)), .is_not_null()
) .and(post::creator_id.eq(other_person)),
.or(mod_transfer_community::other_person_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 let post_join = post::table.on(
.eq(post::id) admin_purge_comment::post_id
.or(mod_feature_post::post_id.eq(post::id)) .eq(post::id)
.or(mod_lock_post::post_id.eq(post::id)) .or(mod_feature_post::post_id.eq(post::id))
.or( .or(mod_lock_post::post_id.eq(post::id))
mod_remove_comment::id .or(
.is_not_null() mod_remove_comment::id
.and(comment::post_id.eq(post::id)), .is_not_null()
) .and(comment::post_id.eq(post::id)),
.or(mod_remove_post::post_id.eq(post::id)); )
.or(mod_remove_post::post_id.eq(post::id)),
);
let community_join = admin_purge_post::community_id let community_join = community::table.on(
.eq(community::id) admin_purge_post::community_id
.or(mod_add_community::community_id.eq(community::id)) .eq(community::id)
.or(mod_ban_from_community::community_id.eq(community::id)) .or(mod_add_community::community_id.eq(community::id))
.or( .or(mod_ban_from_community::community_id.eq(community::id))
mod_feature_post::id .or(
.is_not_null() mod_feature_post::id
.and(post::community_id.eq(community::id)), .is_not_null()
) .and(post::community_id.eq(community::id)),
.or(mod_hide_community::community_id.eq(community::id)) )
.or( .or(mod_hide_community::community_id.eq(community::id))
mod_lock_post::id .or(
.is_not_null() mod_lock_post::id
.and(post::community_id.eq(community::id)), .is_not_null()
) .and(post::community_id.eq(community::id)),
.or( )
mod_remove_comment::id .or(
.is_not_null() mod_remove_comment::id
.and(post::community_id.eq(community::id)), .is_not_null()
) .and(post::community_id.eq(community::id)),
.or(mod_remove_community::community_id.eq(community::id)) )
.or( .or(mod_remove_community::community_id.eq(community::id))
mod_remove_post::id .or(
.is_not_null() mod_remove_post::id
.and(post::community_id.eq(community::id)), .is_not_null()
) .and(post::community_id.eq(community::id)),
.or(mod_transfer_community::community_id.eq(community::id)); )
.or(mod_transfer_community::community_id.eq(community::id)),
);
let instance_join = admin_allow_instance::instance_id let instance_join = instance::table.on(
.eq(instance::id) admin_allow_instance::instance_id
.or(admin_block_instance::instance_id.eq(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 modlog_combined::table
.left_join(admin_allow_instance::table) .left_join(admin_allow_instance::table)
@ -192,18 +218,13 @@ impl ModlogCombinedViewInternal {
.left_join(mod_remove_community::table) .left_join(mod_remove_community::table)
.left_join(mod_remove_post::table) .left_join(mod_remove_post::table)
.left_join(mod_transfer_community::table) .left_join(mod_transfer_community::table)
// The moderator .left_join(moderator_names_join)
.left_join(person::table.on(moderator_names_join)) .left_join(comment_join)
// The comment .left_join(post_join)
.left_join(comment::table.on(comment_join)) .left_join(community_join)
// The post .left_join(instance_join)
.left_join(post::table.on(post_join)) .left_join(other_person_join)
// The community .left_join(community_actions_join)
.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))
} }
} }
@ -284,26 +305,30 @@ pub struct PaginationCursorData(ModlogCombined);
#[derive(Default)] #[derive(Default)]
/// Querying / filtering the modlog. /// Querying / filtering the modlog.
pub struct ModlogCombinedQuery { pub struct ModlogCombinedQuery<'a> {
pub type_: Option<ModlogActionType>, pub type_: Option<ModlogActionType>,
pub listing_type: Option<ListingType>,
pub comment_id: Option<CommentId>, pub comment_id: Option<CommentId>,
pub post_id: Option<PostId>, pub post_id: Option<PostId>,
pub community_id: Option<CommunityId>, pub community_id: Option<CommunityId>,
pub hide_modlog_names: Option<bool>, pub hide_modlog_names: Option<bool>,
pub local_user: Option<&'a LocalUser>,
pub mod_person_id: Option<PersonId>, pub mod_person_id: Option<PersonId>,
pub other_person_id: Option<PersonId>, pub other_person_id: Option<PersonId>,
pub page_after: Option<PaginationCursorData>, pub page_after: Option<PaginationCursorData>,
pub page_back: Option<bool>, pub page_back: Option<bool>,
} }
impl ModlogCombinedQuery { impl ModlogCombinedQuery<'_> {
pub async fn list(self, pool: &mut DbPool<'_>) -> LemmyResult<Vec<ModlogCombinedView>> { pub async fn list(self, pool: &mut DbPool<'_>) -> LemmyResult<Vec<ModlogCombinedView>> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
let other_person = aliases::person1.field(person::id); 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) let mut query =
.select(ModlogCombinedViewInternal::as_select()) ModlogCombinedViewInternal::joins(self.mod_person_id, self.hide_modlog_names, my_person_id)
.into_boxed(); .select(ModlogCombinedViewInternal::as_select())
.into_boxed();
if let Some(mod_person_id) = self.mod_person_id { if let Some(mod_person_id) = self.mod_person_id {
query = query.filter(person::id.eq(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 mut query = PaginatedQueryBuilder::new(query);
let page_after = self.page_after.map(|c| c.0); let page_after = self.page_after.map(|c| c.0);