Adding ban_expires_at to views (#5924)

* Refactoring queries.

* Adding ban_expires_at for views.

* Adding unit tests for expires_at

* Added ban_expires_at for local_user_view

* Fixing millis -> micros

* Renaming creator_community_ban field.
This commit is contained in:
Dessalines 2025-09-01 05:17:27 -04:00 committed by GitHub
parent b7a9eb05da
commit 98995b4ebf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
43 changed files with 981 additions and 573 deletions

8
Cargo.lock generated
View file

@ -3407,6 +3407,7 @@ dependencies = [
name = "lemmy_db_views_comment"
version = "1.0.0-alpha.7"
dependencies = [
"chrono",
"diesel",
"diesel-async",
"diesel_ltree",
@ -3523,6 +3524,7 @@ name = "lemmy_db_views_local_user"
version = "1.0.0-alpha.7"
dependencies = [
"actix-web",
"chrono",
"diesel",
"diesel-async",
"i-love-jesus",
@ -3559,6 +3561,7 @@ dependencies = [
name = "lemmy_db_views_notification"
version = "1.0.0-alpha.7"
dependencies = [
"chrono",
"diesel",
"diesel-async",
"i-love-jesus",
@ -3577,6 +3580,7 @@ dependencies = [
name = "lemmy_db_views_person"
version = "1.0.0-alpha.7"
dependencies = [
"chrono",
"diesel",
"diesel-async",
"i-love-jesus",
@ -3596,6 +3600,7 @@ dependencies = [
name = "lemmy_db_views_person_content_combined"
version = "1.0.0-alpha.7"
dependencies = [
"chrono",
"derive-new",
"diesel",
"diesel-async",
@ -3618,6 +3623,7 @@ dependencies = [
name = "lemmy_db_views_person_liked_combined"
version = "1.0.0-alpha.7"
dependencies = [
"chrono",
"diesel",
"diesel-async",
"i-love-jesus",
@ -3639,6 +3645,7 @@ dependencies = [
name = "lemmy_db_views_person_saved_combined"
version = "1.0.0-alpha.7"
dependencies = [
"chrono",
"diesel",
"diesel-async",
"i-love-jesus",
@ -3749,6 +3756,7 @@ dependencies = [
name = "lemmy_db_views_search_combined"
version = "1.0.0-alpha.7"
dependencies = [
"chrono",
"diesel",
"diesel-async",
"i-love-jesus",

View file

@ -167,6 +167,7 @@ pub async fn register(
person,
local_user,
banned: false,
ban_expires_at: None,
})
}
.scope_boxed()
@ -410,6 +411,7 @@ pub async fn authenticate_with_oauth(
person,
local_user,
banned: false,
ban_expires_at: None,
})
}
.scope_boxed()

View file

@ -510,6 +510,8 @@ pub mod functions {
}
define_sql_function!(#[sql_name = "coalesce"] fn coalesce_2_nullable<T: diesel::sql_types::SqlType + diesel::sql_types::SingleValue>(x: diesel::sql_types::Nullable<T>, y: diesel::sql_types::Nullable<T>) -> diesel::sql_types::Nullable<T>);
define_sql_function!(#[sql_name = "coalesce"] fn coalesce_3_nullable<T: diesel::sql_types::SqlType + diesel::sql_types::SingleValue>(x: diesel::sql_types::Nullable<T>, y: diesel::sql_types::Nullable<T>, z: diesel::sql_types::Nullable<T>) -> diesel::sql_types::Nullable<T>);
}
pub const DELETED_REPLACEMENT_TEXT: &str = "*Permanently Deleted*";

View file

@ -1,460 +0,0 @@
use crate::{
aliases::{
creator_community_actions,
creator_community_instance_actions,
creator_home_instance_actions,
creator_local_instance_actions,
creator_local_user,
my_instance_persons_actions,
person1,
person2,
},
newtypes::{InstanceId, PersonId},
MyInstancePersonsActionsAllColumnsTuple,
Person1AliasAllColumnsTuple,
Person2AliasAllColumnsTuple,
};
use diesel::{
dsl::{case_when, exists, not},
expression::SqlLiteral,
helper_types::{Eq, NotEq, Nullable},
sql_types::Json,
BoolExpressionMethods,
ExpressionMethods,
JoinOnDsl,
NullableExpressionMethods,
PgExpressionMethods,
QueryDsl,
};
use lemmy_db_schema_file::{
enums::{CommunityFollowerState, CommunityVisibility},
schema::{
comment,
comment_actions,
community,
community_actions,
image_details,
instance_actions,
local_site,
local_user,
multi_community,
multi_community_entry,
person,
person_actions,
post,
post_actions,
post_tag,
tag,
},
};
/// Hide all content from blocked communities and persons. Content from blocked instances is also
/// hidden, unless the user followed the community explicitly.
#[diesel::dsl::auto_type]
pub fn filter_blocked() -> _ {
instance_actions::blocked_communities_at
.is_null()
.or(community_actions::followed_at.is_not_null())
.and(community_actions::blocked_at.is_null())
.and(person_actions::blocked_at.is_null())
.and(
my_instance_persons_actions
.field(instance_actions::blocked_persons_at)
.is_null(),
)
}
/// Checks that the creator_local_user is an admin.
#[diesel::dsl::auto_type]
pub fn creator_is_admin() -> _ {
creator_local_user
.field(local_user::admin)
.nullable()
.is_not_distinct_from(true)
}
/// Checks that the local_user is an admin.
#[diesel::dsl::auto_type]
pub fn local_user_is_admin() -> _ {
local_user::admin.nullable().is_not_distinct_from(true)
}
/// Checks to see if the comment creator is an admin.
#[diesel::dsl::auto_type]
pub fn comment_creator_is_admin() -> _ {
exists(
creator_local_user.filter(
comment::creator_id
.eq(creator_local_user.field(local_user::person_id))
.and(creator_local_user.field(local_user::admin).eq(true)),
),
)
}
#[diesel::dsl::auto_type]
pub fn post_creator_is_admin() -> _ {
exists(
creator_local_user.filter(
post::creator_id
.eq(creator_local_user.field(local_user::person_id))
.and(creator_local_user.field(local_user::admin).eq(true)),
),
)
}
#[diesel::dsl::auto_type]
pub fn creator_is_moderator() -> _ {
creator_community_actions
.field(community_actions::became_moderator_at)
.nullable()
.is_not_null()
}
#[diesel::dsl::auto_type]
pub fn creator_banned_from_community() -> _ {
creator_community_actions
.field(community_actions::received_ban_at)
.nullable()
.is_not_null()
}
#[diesel::dsl::auto_type]
pub fn creator_home_banned() -> _ {
creator_home_instance_actions
.field(instance_actions::received_ban_at)
.nullable()
.is_not_null()
}
#[diesel::dsl::auto_type]
/// Checks to see if a user is site banned from any of these places:
/// - Their own instance
/// - The local instance
pub fn creator_banned() -> _ {
let local_ban = creator_local_instance_actions
.field(instance_actions::received_ban_at)
.nullable()
.is_not_null();
local_ban.or(creator_home_banned())
}
/// Similar to creator_banned(), but also checks if creator was banned from instance where the
/// community is hosted.
#[diesel::dsl::auto_type]
pub fn creator_banned_within_community() -> _ {
let community_ban = creator_community_instance_actions
.field(instance_actions::received_ban_at)
.nullable()
.is_not_null();
creator_banned().or(community_ban)
}
#[diesel::dsl::auto_type]
pub fn creator_local_user_admin_join() -> _ {
creator_local_user.on(
person::id
.eq(creator_local_user.field(local_user::person_id))
.and(creator_local_user.field(local_user::admin).eq(true)),
)
}
#[diesel::dsl::auto_type]
fn am_higher_mod() -> _ {
let i_became_moderator = community_actions::became_moderator_at.nullable();
let creator_became_moderator = creator_community_actions
.field(community_actions::became_moderator_at)
.nullable();
i_became_moderator.is_not_null().and(
creator_became_moderator
.ge(i_became_moderator)
.is_distinct_from(false),
)
}
/// Checks to see if you can mod an item.
///
/// Caveat: Since admin status isn't federated or ordered, it can't know whether
/// item creator is a federated admin, or a higher admin.
/// The back-end will reject an action for admin that is higher via
/// LocalUser::is_higher_mod_or_admin_check
#[diesel::dsl::auto_type]
pub fn local_user_can_mod() -> _ {
local_user_is_admin().or(not(creator_is_admin()).and(am_higher_mod()))
}
/// Checks to see if you can mod a post.
#[diesel::dsl::auto_type]
pub fn local_user_can_mod_post() -> _ {
local_user_is_admin().or(not(post_creator_is_admin()).and(am_higher_mod()))
}
/// Checks to see if you can mod a comment.
#[diesel::dsl::auto_type]
pub fn local_user_can_mod_comment() -> _ {
local_user_is_admin().or(not(comment_creator_is_admin()).and(am_higher_mod()))
}
/// A special type of can_mod for communities, which dont have creators.
#[diesel::dsl::auto_type]
pub fn local_user_community_can_mod() -> _ {
let am_admin = local_user::admin.nullable();
let am_moderator = community_actions::became_moderator_at
.nullable()
.is_not_null();
am_admin.or(am_moderator).is_not_distinct_from(true)
}
/// Selects the comment columns, but gives an empty string for content when
/// deleted or removed, and you're not a mod/admin.
#[diesel::dsl::auto_type]
pub fn comment_select_remove_deletes() -> _ {
let deleted_or_removed = comment::deleted.or(comment::removed);
// You can only view the content if it hasn't been removed, or you can mod.
let can_view_content = not(deleted_or_removed).or(local_user_can_mod_comment());
let content = case_when(can_view_content, comment::content).otherwise("");
(
comment::id,
comment::creator_id,
comment::post_id,
content,
comment::removed,
comment::published_at,
comment::updated_at,
comment::deleted,
comment::ap_id,
comment::local,
comment::path,
comment::distinguished,
comment::language_id,
comment::score,
comment::upvotes,
comment::downvotes,
comment::child_count,
comment::hot_rank,
comment::controversy_rank,
comment::report_count,
comment::unresolved_report_count,
comment::federation_pending,
)
}
#[diesel::dsl::auto_type]
// Gets the post tags set on a specific post
pub fn post_tags_fragment() -> _ {
let sel: SqlLiteral<Json> = diesel::dsl::sql::<diesel::sql_types::Json>("json_agg(tag.*)");
post_tag::table
.inner_join(tag::table)
.select(sel)
.filter(post_tag::post_id.eq(post::id))
.filter(tag::deleted.eq(false))
.single_value()
}
#[diesel::dsl::auto_type]
/// Gets the post tags available within a specific community
pub fn community_post_tags_fragment() -> _ {
let sel: SqlLiteral<Json> = diesel::dsl::sql::<diesel::sql_types::Json>("json_agg(tag.*)");
tag::table
.select(sel)
.filter(tag::community_id.eq(community::id))
.filter(tag::deleted.eq(false))
.single_value()
}
/// The select for the person1 alias.
pub fn person1_select() -> Person1AliasAllColumnsTuple {
person1.fields(person::all_columns)
}
/// The select for the person2 alias.
pub fn person2_select() -> Person2AliasAllColumnsTuple {
person2.fields(person::all_columns)
}
type IsSubscribedType =
Eq<lemmy_db_schema_file::schema::community_actions::follow_state, Option<CommunityFollowerState>>;
pub fn filter_is_subscribed() -> IsSubscribedType {
community_actions::follow_state.eq(Some(CommunityFollowerState::Accepted))
}
type IsNotUnlistedType =
NotEq<lemmy_db_schema_file::schema::community::visibility, CommunityVisibility>;
#[diesel::dsl::auto_type]
pub fn filter_not_unlisted_or_is_subscribed() -> _ {
let not_unlisted: IsNotUnlistedType = community::visibility.ne(CommunityVisibility::Unlisted);
let is_subscribed: IsSubscribedType = filter_is_subscribed();
not_unlisted.or(is_subscribed)
}
#[diesel::dsl::auto_type]
pub fn community_join() -> _ {
community::table.on(post::community_id.eq(community::id))
}
#[diesel::dsl::auto_type]
pub fn creator_home_instance_actions_join() -> _ {
creator_home_instance_actions.on(
creator_home_instance_actions
.field(instance_actions::instance_id)
.eq(person::instance_id)
.and(
creator_home_instance_actions
.field(instance_actions::person_id)
.eq(person::id),
),
)
}
#[diesel::dsl::auto_type]
pub fn creator_community_instance_actions_join() -> _ {
creator_community_instance_actions.on(
creator_home_instance_actions
.field(instance_actions::instance_id)
.eq(community::instance_id)
.and(
creator_community_instance_actions
.field(instance_actions::person_id)
.eq(person::id),
),
)
}
/// join with instance actions for local instance
///
/// Requires annotation for return type, see https://docs.diesel.rs/2.2.x/diesel/dsl/attr.auto_type.html#annotating-types
#[diesel::dsl::auto_type]
pub fn creator_local_instance_actions_join(local_instance_id: InstanceId) -> _ {
creator_local_instance_actions.on(
creator_local_instance_actions
.field(instance_actions::instance_id)
.eq(local_instance_id)
.and(
creator_local_instance_actions
.field(instance_actions::person_id)
.eq(person::id),
),
)
}
/// Your instance actions for the community's instance.
#[diesel::dsl::auto_type]
pub fn my_instance_communities_actions_join(my_person_id: Option<PersonId>) -> _ {
instance_actions::table.on(
instance_actions::instance_id
.eq(community::instance_id)
.and(instance_actions::person_id.nullable().eq(my_person_id)),
)
}
/// Your instance actions for the person's instance.
#[diesel::dsl::auto_type]
pub fn my_instance_persons_actions_join(my_person_id: Option<PersonId>) -> _ {
instance_actions::table.on(
instance_actions::instance_id
.eq(person::instance_id)
.and(instance_actions::person_id.nullable().eq(my_person_id)),
)
}
/// The select for the my_instance_persons_actions alias
pub fn my_instance_persons_actions_select() -> Nullable<MyInstancePersonsActionsAllColumnsTuple> {
my_instance_persons_actions
.fields(instance_actions::all_columns)
.nullable()
}
/// Your instance actions for the person's instance.
/// A dupe of the above function, but aliased
#[diesel::dsl::auto_type]
pub fn my_instance_persons_actions_join_1(my_person_id: Option<PersonId>) -> _ {
my_instance_persons_actions.on(
my_instance_persons_actions
.field(instance_actions::instance_id)
.eq(person::instance_id)
.and(
my_instance_persons_actions
.field(instance_actions::person_id)
.nullable()
.eq(my_person_id),
),
)
}
#[diesel::dsl::auto_type]
pub fn image_details_join() -> _ {
image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable()))
}
#[diesel::dsl::auto_type]
pub fn my_community_actions_join(my_person_id: Option<PersonId>) -> _ {
community_actions::table.on(
community_actions::community_id
.eq(community::id)
.and(community_actions::person_id.nullable().eq(my_person_id)),
)
}
#[diesel::dsl::auto_type]
pub fn my_post_actions_join(my_person_id: Option<PersonId>) -> _ {
post_actions::table.on(
post_actions::post_id
.eq(post::id)
.and(post_actions::person_id.nullable().eq(my_person_id)),
)
}
#[diesel::dsl::auto_type]
pub fn my_comment_actions_join(my_person_id: Option<PersonId>) -> _ {
comment_actions::table.on(
comment_actions::comment_id
.eq(comment::id)
.and(comment_actions::person_id.nullable().eq(my_person_id)),
)
}
#[diesel::dsl::auto_type]
pub fn my_person_actions_join(my_person_id: Option<PersonId>) -> _ {
person_actions::table.on(
person_actions::target_id
.eq(person::id)
.and(person_actions::person_id.nullable().eq(my_person_id)),
)
}
#[diesel::dsl::auto_type]
pub fn my_local_user_admin_join(my_person_id: Option<PersonId>) -> _ {
local_user::table.on(
local_user::person_id
.nullable()
.eq(my_person_id)
.and(local_user::admin.eq(true)),
)
}
#[diesel::dsl::auto_type]
pub fn creator_community_actions_join() -> _ {
creator_community_actions.on(
creator_community_actions
.field(community_actions::community_id)
.eq(community::id)
.and(
creator_community_actions
.field(community_actions::person_id)
.eq(person::id),
),
)
}
#[diesel::dsl::auto_type]
pub fn suggested_communities() -> _ {
community::id.eq_any(
local_site::table
.left_join(multi_community::table.inner_join(multi_community_entry::table))
.filter(multi_community_entry::community_id.is_not_null())
.select(multi_community_entry::community_id.assume_not_null()),
)
}

View file

@ -0,0 +1,63 @@
use crate::aliases::my_instance_persons_actions;
use diesel::{
helper_types::{Eq, NotEq},
BoolExpressionMethods,
ExpressionMethods,
NullableExpressionMethods,
QueryDsl,
};
use lemmy_db_schema_file::{
enums::{CommunityFollowerState, CommunityVisibility},
schema::{
community,
community_actions,
instance_actions,
local_site,
multi_community,
multi_community_entry,
person_actions,
},
};
/// Hide all content from blocked communities and persons. Content from blocked instances is also
/// hidden, unless the user followed the community explicitly.
#[diesel::dsl::auto_type]
pub fn filter_blocked() -> _ {
instance_actions::blocked_communities_at
.is_null()
.or(community_actions::followed_at.is_not_null())
.and(community_actions::blocked_at.is_null())
.and(person_actions::blocked_at.is_null())
.and(
my_instance_persons_actions
.field(instance_actions::blocked_persons_at)
.is_null(),
)
}
type IsSubscribedType =
Eq<lemmy_db_schema_file::schema::community_actions::follow_state, Option<CommunityFollowerState>>;
pub fn filter_is_subscribed() -> IsSubscribedType {
community_actions::follow_state.eq(Some(CommunityFollowerState::Accepted))
}
type IsNotUnlistedType =
NotEq<lemmy_db_schema_file::schema::community::visibility, CommunityVisibility>;
#[diesel::dsl::auto_type]
pub fn filter_not_unlisted_or_is_subscribed() -> _ {
let not_unlisted: IsNotUnlistedType = community::visibility.ne(CommunityVisibility::Unlisted);
let is_subscribed: IsSubscribedType = filter_is_subscribed();
not_unlisted.or(is_subscribed)
}
#[diesel::dsl::auto_type]
pub fn filter_suggested_communities() -> _ {
community::id.eq_any(
local_site::table
.left_join(multi_community::table.inner_join(multi_community_entry::table))
.filter(multi_community_entry::community_id.is_not_null())
.select(multi_community_entry::community_id.assume_not_null()),
)
}

View file

@ -0,0 +1,184 @@
use crate::{
aliases::{
creator_community_actions,
creator_community_instance_actions,
creator_home_instance_actions,
creator_local_instance_actions,
creator_local_user,
my_instance_persons_actions,
},
newtypes::{InstanceId, PersonId},
};
use diesel::{BoolExpressionMethods, ExpressionMethods, JoinOnDsl, NullableExpressionMethods};
use lemmy_db_schema_file::schema::{
comment,
comment_actions,
community,
community_actions,
image_details,
instance_actions,
local_user,
person,
person_actions,
post,
post_actions,
};
#[diesel::dsl::auto_type]
pub fn creator_local_user_admin_join() -> _ {
creator_local_user.on(
person::id
.eq(creator_local_user.field(local_user::person_id))
.and(creator_local_user.field(local_user::admin).eq(true)),
)
}
#[diesel::dsl::auto_type]
pub fn community_join() -> _ {
community::table.on(post::community_id.eq(community::id))
}
#[diesel::dsl::auto_type]
pub fn creator_home_instance_actions_join() -> _ {
creator_home_instance_actions.on(
creator_home_instance_actions
.field(instance_actions::instance_id)
.eq(person::instance_id)
.and(
creator_home_instance_actions
.field(instance_actions::person_id)
.eq(person::id),
),
)
}
#[diesel::dsl::auto_type]
pub fn creator_community_instance_actions_join() -> _ {
creator_community_instance_actions.on(
creator_home_instance_actions
.field(instance_actions::instance_id)
.eq(community::instance_id)
.and(
creator_community_instance_actions
.field(instance_actions::person_id)
.eq(person::id),
),
)
}
/// join with instance actions for local instance
///
/// Requires annotation for return type, see https://docs.diesel.rs/2.2.x/diesel/dsl/attr.auto_type.html#annotating-types
#[diesel::dsl::auto_type]
pub fn creator_local_instance_actions_join(local_instance_id: InstanceId) -> _ {
creator_local_instance_actions.on(
creator_local_instance_actions
.field(instance_actions::instance_id)
.eq(local_instance_id)
.and(
creator_local_instance_actions
.field(instance_actions::person_id)
.eq(person::id),
),
)
}
/// Your instance actions for the community's instance.
#[diesel::dsl::auto_type]
pub fn my_instance_communities_actions_join(my_person_id: Option<PersonId>) -> _ {
instance_actions::table.on(
instance_actions::instance_id
.eq(community::instance_id)
.and(instance_actions::person_id.nullable().eq(my_person_id)),
)
}
/// Your instance actions for the person's instance.
#[diesel::dsl::auto_type]
pub fn my_instance_persons_actions_join(my_person_id: Option<PersonId>) -> _ {
instance_actions::table.on(
instance_actions::instance_id
.eq(person::instance_id)
.and(instance_actions::person_id.nullable().eq(my_person_id)),
)
}
/// Your instance actions for the person's instance.
/// A dupe of the above function, but aliased
#[diesel::dsl::auto_type]
pub fn my_instance_persons_actions_join_1(my_person_id: Option<PersonId>) -> _ {
my_instance_persons_actions.on(
my_instance_persons_actions
.field(instance_actions::instance_id)
.eq(person::instance_id)
.and(
my_instance_persons_actions
.field(instance_actions::person_id)
.nullable()
.eq(my_person_id),
),
)
}
#[diesel::dsl::auto_type]
pub fn image_details_join() -> _ {
image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable()))
}
#[diesel::dsl::auto_type]
pub fn my_community_actions_join(my_person_id: Option<PersonId>) -> _ {
community_actions::table.on(
community_actions::community_id
.eq(community::id)
.and(community_actions::person_id.nullable().eq(my_person_id)),
)
}
#[diesel::dsl::auto_type]
pub fn my_post_actions_join(my_person_id: Option<PersonId>) -> _ {
post_actions::table.on(
post_actions::post_id
.eq(post::id)
.and(post_actions::person_id.nullable().eq(my_person_id)),
)
}
#[diesel::dsl::auto_type]
pub fn my_comment_actions_join(my_person_id: Option<PersonId>) -> _ {
comment_actions::table.on(
comment_actions::comment_id
.eq(comment::id)
.and(comment_actions::person_id.nullable().eq(my_person_id)),
)
}
#[diesel::dsl::auto_type]
pub fn my_person_actions_join(my_person_id: Option<PersonId>) -> _ {
person_actions::table.on(
person_actions::target_id
.eq(person::id)
.and(person_actions::person_id.nullable().eq(my_person_id)),
)
}
#[diesel::dsl::auto_type]
pub fn my_local_user_admin_join(my_person_id: Option<PersonId>) -> _ {
local_user::table.on(
local_user::person_id
.nullable()
.eq(my_person_id)
.and(local_user::admin.eq(true)),
)
}
#[diesel::dsl::auto_type]
pub fn creator_community_actions_join() -> _ {
creator_community_actions.on(
creator_community_actions
.field(community_actions::community_id)
.eq(community::id)
.and(
creator_community_actions
.field(community_actions::person_id)
.eq(person::id),
),
)
}

View file

@ -0,0 +1,3 @@
pub mod filters;
pub mod joins;
pub mod selects;

View file

@ -0,0 +1,320 @@
use crate::{
aliases::{
creator_community_actions,
creator_community_instance_actions,
creator_home_instance_actions,
creator_local_instance_actions,
creator_local_user,
my_instance_persons_actions,
person1,
person2,
CreatorCommunityInstanceActions,
CreatorHomeInstanceActions,
CreatorLocalInstanceActions,
},
utils::functions::{coalesce_2_nullable, coalesce_3_nullable},
MyInstancePersonsActionsAllColumnsTuple,
Person1AliasAllColumnsTuple,
Person2AliasAllColumnsTuple,
};
use diesel::{
dsl::{case_when, exists, not},
expression::SqlLiteral,
helper_types::Nullable,
query_source::AliasedField,
sql_types::{Json, Timestamptz},
BoolExpressionMethods,
ExpressionMethods,
NullableExpressionMethods,
PgExpressionMethods,
QueryDsl,
};
use lemmy_db_schema_file::schema::{
comment,
community,
community_actions,
instance_actions,
local_user,
person,
post,
post_tag,
tag,
};
/// Checks that the creator_local_user is an admin.
#[diesel::dsl::auto_type]
pub fn creator_is_admin() -> _ {
creator_local_user
.field(local_user::admin)
.nullable()
.is_not_distinct_from(true)
}
/// Checks that the local_user is an admin.
#[diesel::dsl::auto_type]
pub fn local_user_is_admin() -> _ {
local_user::admin.nullable().is_not_distinct_from(true)
}
/// Checks to see if the comment creator is an admin.
#[diesel::dsl::auto_type]
pub fn comment_creator_is_admin() -> _ {
exists(
creator_local_user.filter(
comment::creator_id
.eq(creator_local_user.field(local_user::person_id))
.and(creator_local_user.field(local_user::admin).eq(true)),
),
)
}
#[diesel::dsl::auto_type]
pub fn post_creator_is_admin() -> _ {
exists(
creator_local_user.filter(
post::creator_id
.eq(creator_local_user.field(local_user::person_id))
.and(creator_local_user.field(local_user::admin).eq(true)),
),
)
}
#[diesel::dsl::auto_type]
pub fn creator_is_moderator() -> _ {
creator_community_actions
.field(community_actions::became_moderator_at)
.nullable()
.is_not_null()
}
#[diesel::dsl::auto_type]
pub fn creator_banned_from_community() -> _ {
creator_community_actions
.field(community_actions::received_ban_at)
.nullable()
.is_not_null()
}
#[diesel::dsl::auto_type]
pub fn creator_ban_expires_from_community() -> _ {
creator_community_actions
.field(community_actions::ban_expires_at)
.nullable()
}
#[diesel::dsl::auto_type]
/// Checks to see if a creator is banned from the local instance.
fn creator_local_banned() -> _ {
creator_local_instance_actions
.field(instance_actions::received_ban_at)
.nullable()
.is_not_null()
}
#[diesel::dsl::auto_type]
fn creator_local_ban_expires() -> _ {
creator_local_instance_actions
.field(instance_actions::ban_expires_at)
.nullable()
}
#[diesel::dsl::auto_type]
/// Checks to see if a creator is banned from their community's instance
fn creator_community_instance_banned() -> _ {
creator_community_instance_actions
.field(instance_actions::received_ban_at)
.nullable()
.is_not_null()
}
#[diesel::dsl::auto_type]
fn creator_community_instance_ban_expires() -> _ {
creator_community_instance_actions
.field(instance_actions::ban_expires_at)
.nullable()
}
#[diesel::dsl::auto_type]
/// Checks to see if a creator is banned from their home instance
pub fn creator_home_banned() -> _ {
creator_home_instance_actions
.field(instance_actions::received_ban_at)
.nullable()
.is_not_null()
}
#[diesel::dsl::auto_type]
/// Checks to see if a creator is banned from their home instance
pub fn creator_home_ban_expires() -> _ {
creator_home_instance_actions
.field(instance_actions::ban_expires_at)
.nullable()
}
#[diesel::dsl::auto_type]
/// Checks to see if a user is site banned from any of these places:
/// - Their own instance
/// - The local instance
pub fn creator_local_home_banned() -> _ {
creator_local_banned().or(creator_home_banned())
}
pub type CreatorLocalHomeBanExpiresType = coalesce_2_nullable<
Timestamptz,
Nullable<AliasedField<CreatorLocalInstanceActions, instance_actions::ban_expires_at>>,
Nullable<AliasedField<CreatorHomeInstanceActions, instance_actions::ban_expires_at>>,
>;
pub fn creator_local_home_ban_expires() -> CreatorLocalHomeBanExpiresType {
coalesce_2_nullable(creator_local_ban_expires(), creator_home_ban_expires())
}
/// Checks to see if a user is site banned from any of these places:
/// - The local instance
/// - Their own instance
/// - The community instance.
#[diesel::dsl::auto_type]
pub fn creator_local_home_community_banned() -> _ {
creator_local_banned()
.or(creator_home_banned())
.or(creator_community_instance_banned())
}
pub type CreatorLocalHomeCommunityBanExpiresType = coalesce_3_nullable<
Timestamptz,
Nullable<AliasedField<CreatorLocalInstanceActions, instance_actions::ban_expires_at>>,
Nullable<AliasedField<CreatorHomeInstanceActions, instance_actions::ban_expires_at>>,
Nullable<AliasedField<CreatorCommunityInstanceActions, instance_actions::ban_expires_at>>,
>;
pub fn creator_local_home_community_ban_expires() -> CreatorLocalHomeCommunityBanExpiresType {
coalesce_3_nullable(
creator_local_ban_expires(),
creator_home_ban_expires(),
creator_community_instance_ban_expires(),
)
}
#[diesel::dsl::auto_type]
fn am_higher_mod() -> _ {
let i_became_moderator = community_actions::became_moderator_at.nullable();
let creator_became_moderator = creator_community_actions
.field(community_actions::became_moderator_at)
.nullable();
i_became_moderator.is_not_null().and(
creator_became_moderator
.ge(i_became_moderator)
.is_distinct_from(false),
)
}
/// Checks to see if you can mod an item.
///
/// Caveat: Since admin status isn't federated or ordered, it can't know whether
/// item creator is a federated admin, or a higher admin.
/// The back-end will reject an action for admin that is higher via
/// LocalUser::is_higher_mod_or_admin_check
#[diesel::dsl::auto_type]
pub fn local_user_can_mod() -> _ {
local_user_is_admin().or(not(creator_is_admin()).and(am_higher_mod()))
}
/// Checks to see if you can mod a post.
#[diesel::dsl::auto_type]
pub fn local_user_can_mod_post() -> _ {
local_user_is_admin().or(not(post_creator_is_admin()).and(am_higher_mod()))
}
/// Checks to see if you can mod a comment.
#[diesel::dsl::auto_type]
pub fn local_user_can_mod_comment() -> _ {
local_user_is_admin().or(not(comment_creator_is_admin()).and(am_higher_mod()))
}
/// A special type of can_mod for communities, which dont have creators.
#[diesel::dsl::auto_type]
pub fn local_user_community_can_mod() -> _ {
let am_admin = local_user::admin.nullable();
let am_moderator = community_actions::became_moderator_at
.nullable()
.is_not_null();
am_admin.or(am_moderator).is_not_distinct_from(true)
}
/// Selects the comment columns, but gives an empty string for content when
/// deleted or removed, and you're not a mod/admin.
#[diesel::dsl::auto_type]
pub fn comment_select_remove_deletes() -> _ {
let deleted_or_removed = comment::deleted.or(comment::removed);
// You can only view the content if it hasn't been removed, or you can mod.
let can_view_content = not(deleted_or_removed).or(local_user_can_mod_comment());
let content = case_when(can_view_content, comment::content).otherwise("");
(
comment::id,
comment::creator_id,
comment::post_id,
content,
comment::removed,
comment::published_at,
comment::updated_at,
comment::deleted,
comment::ap_id,
comment::local,
comment::path,
comment::distinguished,
comment::language_id,
comment::score,
comment::upvotes,
comment::downvotes,
comment::child_count,
comment::hot_rank,
comment::controversy_rank,
comment::report_count,
comment::unresolved_report_count,
comment::federation_pending,
)
}
#[diesel::dsl::auto_type]
// Gets the post tags set on a specific post
pub fn post_tags_fragment() -> _ {
let sel: SqlLiteral<Json> = diesel::dsl::sql::<diesel::sql_types::Json>("json_agg(tag.*)");
post_tag::table
.inner_join(tag::table)
.select(sel)
.filter(post_tag::post_id.eq(post::id))
.filter(tag::deleted.eq(false))
.single_value()
}
#[diesel::dsl::auto_type]
/// Gets the post tags available within a specific community
pub fn community_post_tags_fragment() -> _ {
let sel: SqlLiteral<Json> = diesel::dsl::sql::<diesel::sql_types::Json>("json_agg(tag.*)");
tag::table
.select(sel)
.filter(tag::community_id.eq(community::id))
.filter(tag::deleted.eq(false))
.single_value()
}
/// The select for the person1 alias.
pub fn person1_select() -> Person1AliasAllColumnsTuple {
person1.fields(person::all_columns)
}
/// The select for the person2 alias.
pub fn person2_select() -> Person2AliasAllColumnsTuple {
person2.fields(person::all_columns)
}
/// The select for the my_instance_persons_actions alias
pub fn my_instance_persons_actions_select() -> Nullable<MyInstancePersonsActionsAllColumnsTuple> {
my_instance_persons_actions
.fields(instance_actions::all_columns)
.nullable()
}

View file

@ -45,6 +45,7 @@ serde = { workspace = true }
serde_with = { workspace = true }
ts-rs = { workspace = true, optional = true }
i-love-jesus = { workspace = true, optional = true }
chrono = { workspace = true }
[dev-dependencies]
lemmy_db_views_local_user = { workspace = true }

View file

@ -26,18 +26,19 @@ use lemmy_db_schema::{
now,
paginate,
queries::{
creator_community_actions_join,
creator_community_instance_actions_join,
creator_home_instance_actions_join,
creator_local_instance_actions_join,
filter_blocked,
my_comment_actions_join,
my_community_actions_join,
my_instance_communities_actions_join,
my_instance_persons_actions_join_1,
my_local_user_admin_join,
my_person_actions_join,
suggested_communities,
filters::{filter_blocked, filter_suggested_communities},
joins::{
creator_community_actions_join,
creator_community_instance_actions_join,
creator_home_instance_actions_join,
creator_local_instance_actions_join,
my_comment_actions_join,
my_community_actions_join,
my_instance_communities_actions_join,
my_instance_persons_actions_join_1,
my_local_user_admin_join,
my_person_actions_join,
},
},
seconds_to_pg_interval,
DbPool,
@ -202,7 +203,7 @@ impl CommentQuery<'_> {
ListingType::ModeratorView => {
query.filter(community_actions::became_moderator_at.is_not_null())
}
ListingType::Suggested => query.filter(suggested_communities()),
ListingType::Suggested => query.filter(filter_suggested_communities()),
};
if !o.local_user.show_bot_accounts() {
@ -463,6 +464,7 @@ mod tests {
local_user: inserted_timmy_local_user.clone(),
person: inserted_timmy_person.clone(),
banned: false,
ban_expires_at: None,
};
let site_form = SiteInsertForm::new("test site".to_string(), inserted_instance.id);
let site = Site::create(pool, &site_form).await?;

View file

@ -1,3 +1,4 @@
use chrono::{DateTime, Utc};
use lemmy_db_schema::source::{
comment::{Comment, CommentActions},
community::{Community, CommunityActions},
@ -10,14 +11,17 @@ use serde_with::skip_serializing_none;
#[cfg(feature = "full")]
use {
diesel::{Queryable, Selectable},
lemmy_db_schema::utils::queries::{
lemmy_db_schema::utils::queries::selects::{
comment_creator_is_admin,
comment_select_remove_deletes,
creator_ban_expires_from_community,
creator_banned_from_community,
creator_banned_within_community,
creator_is_moderator,
creator_local_home_community_ban_expires,
creator_local_home_community_banned,
local_user_can_mod_comment,
post_tags_fragment,
CreatorLocalHomeCommunityBanExpiresType,
},
};
@ -71,10 +75,17 @@ pub struct CommentView {
pub can_mod: bool,
#[cfg_attr(feature = "full",
diesel(
select_expression = creator_banned_within_community()
select_expression = creator_local_home_community_banned()
)
)]
pub creator_banned: bool,
#[cfg_attr(feature = "full",
diesel(
select_expression_type = CreatorLocalHomeCommunityBanExpiresType,
select_expression = creator_local_home_community_ban_expires()
)
)]
pub creator_ban_expires_at: Option<DateTime<Utc>>,
#[cfg_attr(feature = "full",
diesel(
select_expression = creator_is_moderator()
@ -87,6 +98,12 @@ pub struct CommentView {
)
)]
pub creator_banned_from_community: bool,
#[cfg_attr(feature = "full",
diesel(
select_expression = creator_ban_expires_from_community()
)
)]
pub creator_community_ban_expires_at: Option<DateTime<Utc>>,
}
#[skip_serializing_none]

View file

@ -17,12 +17,16 @@ use lemmy_db_schema::{
now,
paginate,
queries::{
filter_is_subscribed,
filter_not_unlisted_or_is_subscribed,
my_community_actions_join,
my_instance_communities_actions_join,
my_local_user_admin_join,
suggested_communities,
filters::{
filter_is_subscribed,
filter_not_unlisted_or_is_subscribed,
filter_suggested_communities,
},
joins::{
my_community_actions_join,
my_instance_communities_actions_join,
my_local_user_admin_join,
},
},
seconds_to_pg_interval,
DbPool,
@ -144,7 +148,7 @@ impl CommunityQuery<'_> {
ListingType::ModeratorView => {
query.filter(community_actions::became_moderator_at.is_not_null())
}
ListingType::Suggested => query.filter(suggested_communities()),
ListingType::Suggested => query.filter(filter_suggested_communities()),
};
}

View file

@ -9,7 +9,10 @@ use serde_with::skip_serializing_none;
#[cfg(feature = "full")]
use {
diesel::{Queryable, Selectable},
lemmy_db_schema::utils::queries::{community_post_tags_fragment, local_user_community_can_mod},
lemmy_db_schema::utils::queries::selects::{
community_post_tags_fragment,
local_user_community_can_mod,
},
};
pub mod api;

View file

@ -38,6 +38,7 @@ serde_with = { workspace = true }
ts-rs = { workspace = true, optional = true }
actix-web = { workspace = true, optional = true }
i-love-jesus = { workspace = true, optional = true }
chrono = { workspace = true }
[dev-dependencies]
serial_test = { workspace = true }

View file

@ -23,7 +23,7 @@ use lemmy_db_schema::{
get_conn,
now,
paginate,
queries::creator_home_instance_actions_join,
queries::joins::creator_home_instance_actions_join,
DbPool,
},
};

View file

@ -1,9 +1,10 @@
use chrono::{DateTime, Utc};
use lemmy_db_schema::source::{local_user::LocalUser, person::Person};
use serde::{Deserialize, Serialize};
#[cfg(feature = "full")]
use {
diesel::{Queryable, Selectable},
lemmy_db_schema::utils::queries::creator_home_banned,
lemmy_db_schema::utils::queries::selects::{creator_home_ban_expires, creator_home_banned},
};
pub mod api;
@ -27,4 +28,10 @@ pub struct LocalUserView {
)
)]
pub banned: bool,
#[cfg_attr(feature = "full",
diesel(
select_expression = creator_home_ban_expires()
)
)]
pub ban_expires_at: Option<DateTime<Utc>>,
}

View file

@ -42,7 +42,11 @@ use lemmy_db_schema::{
get_conn,
limit_fetch,
paginate,
queries::{filter_is_subscribed, filter_not_unlisted_or_is_subscribed, suggested_communities},
queries::filters::{
filter_is_subscribed,
filter_not_unlisted_or_is_subscribed,
filter_suggested_communities,
},
DbPool,
},
ModlogActionType,
@ -380,7 +384,7 @@ impl ModlogCombinedQuery<'_> {
ListingType::ModeratorView => {
query.filter(community_actions::became_moderator_at.is_not_null())
}
ListingType::Suggested => query.filter(suggested_communities()),
ListingType::Suggested => query.filter(filter_suggested_communities()),
};
// Sorting by published

View file

@ -33,7 +33,7 @@ use serde_with::skip_serializing_none;
#[cfg(feature = "full")]
use {
diesel::{dsl::Nullable, NullableExpressionMethods, Queryable, Selectable},
lemmy_db_schema::{utils::queries::person1_select, Person1AliasAllColumnsTuple},
lemmy_db_schema::{utils::queries::selects::person1_select, Person1AliasAllColumnsTuple},
};
pub mod api;

View file

@ -39,5 +39,6 @@ serde = { workspace = true }
ts-rs = { workspace = true, optional = true }
i-love-jesus = { workspace = true, optional = true }
serde_with = { workspace = true }
chrono = { workspace = true }
[dev-dependencies]

View file

@ -23,20 +23,22 @@ use lemmy_db_schema::{
limit_fetch,
paginate,
queries::{
community_join,
creator_community_actions_join,
creator_home_instance_actions_join,
creator_local_instance_actions_join,
creator_local_user_admin_join,
filter_blocked,
image_details_join,
my_comment_actions_join,
my_community_actions_join,
my_instance_communities_actions_join,
my_instance_persons_actions_join_1,
my_local_user_admin_join,
my_person_actions_join,
my_post_actions_join,
filters::filter_blocked,
joins::{
community_join,
creator_community_actions_join,
creator_home_instance_actions_join,
creator_local_instance_actions_join,
creator_local_user_admin_join,
image_details_join,
my_comment_actions_join,
my_community_actions_join,
my_instance_communities_actions_join,
my_instance_persons_actions_join_1,
my_local_user_admin_join,
my_person_actions_join,
my_post_actions_join,
},
},
DbPool,
},
@ -294,8 +296,10 @@ fn map_to_enum(v: NotificationViewInternal) -> Option<NotificationView> {
post_tags: v.post_tags,
can_mod: v.can_mod,
creator_banned: v.creator_banned,
creator_ban_expires_at: v.creator_ban_expires_at,
creator_is_moderator: v.creator_is_moderator,
creator_banned_from_community: v.creator_banned_from_community,
creator_community_ban_expires_at: v.creator_community_ban_expires_at,
})
} else if let (Some(post), Some(community)) = (v.post, v.community) {
NotificationData::Post(PostView {
@ -310,8 +314,10 @@ fn map_to_enum(v: NotificationViewInternal) -> Option<NotificationView> {
tags: v.post_tags,
can_mod: v.can_mod,
creator_banned: v.creator_banned,
creator_ban_expires_at: v.creator_ban_expires_at,
creator_is_moderator: v.creator_is_moderator,
creator_banned_from_community: v.creator_banned_from_community,
creator_community_ban_expires_at: v.creator_community_ban_expires_at,
})
} else if let Some(private_message) = v.private_message {
NotificationData::PrivateMessage(PrivateMessageView {

View file

@ -1,3 +1,4 @@
use chrono::{DateTime, Utc};
use lemmy_db_schema::{
newtypes::PaginationCursor,
source::{
@ -21,14 +22,17 @@ use serde_with::skip_serializing_none;
use {
diesel::{Queryable, Selectable},
lemmy_db_schema::{
utils::queries::{
creator_banned,
utils::queries::selects::{
creator_ban_expires_from_community,
creator_banned_from_community,
creator_is_admin,
creator_is_moderator,
creator_local_home_ban_expires,
creator_local_home_banned,
local_user_can_mod,
person1_select,
post_tags_fragment,
CreatorLocalHomeBanExpiresType,
},
Person1AliasAllColumnsTuple,
},
@ -91,10 +95,17 @@ struct NotificationViewInternal {
can_mod: bool,
#[cfg_attr(feature = "full",
diesel(
select_expression = creator_banned()
select_expression = creator_local_home_banned()
)
)]
creator_banned: bool,
#[cfg_attr(feature = "full",
diesel(
select_expression_type = CreatorLocalHomeBanExpiresType,
select_expression = creator_local_home_ban_expires()
)
)]
pub creator_ban_expires_at: Option<DateTime<Utc>>,
#[cfg_attr(feature = "full",
diesel(
select_expression = creator_is_moderator()
@ -107,6 +118,12 @@ struct NotificationViewInternal {
)
)]
creator_banned_from_community: bool,
#[cfg_attr(feature = "full",
diesel(
select_expression = creator_ban_expires_from_community()
)
)]
pub creator_community_ban_expires_at: Option<DateTime<Utc>>,
}
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]

View file

@ -42,6 +42,7 @@ serde = { workspace = true }
serde_with = { workspace = true }
ts-rs = { workspace = true, optional = true }
i-love-jesus = { workspace = true, optional = true }
chrono = { workspace = true }
[dev-dependencies]
serial_test = { workspace = true }

View file

@ -10,7 +10,7 @@ use lemmy_db_schema::{
get_conn,
limit_fetch,
paginate,
queries::{
queries::joins::{
creator_home_instance_actions_join,
creator_local_instance_actions_join,
my_person_actions_join,

View file

@ -1,9 +1,17 @@
use chrono::{DateTime, Utc};
use lemmy_db_schema::source::person::{Person, PersonActions};
use serde::{Deserialize, Serialize};
#[cfg(feature = "full")]
use {
diesel::{helper_types::Nullable, NullableExpressionMethods, Queryable, Selectable},
lemmy_db_schema::utils::{functions::coalesce, queries::creator_banned},
lemmy_db_schema::utils::{
functions::coalesce,
queries::selects::{
creator_local_home_ban_expires,
creator_local_home_banned,
CreatorLocalHomeBanExpiresType,
},
},
lemmy_db_schema_file::schema::local_user,
};
@ -31,8 +39,15 @@ pub struct PersonView {
pub person_actions: Option<PersonActions>,
#[cfg_attr(feature = "full",
diesel(
select_expression = creator_banned()
select_expression = creator_local_home_banned()
)
)]
pub creator_banned: bool,
#[cfg_attr(feature = "full",
diesel(
select_expression_type = CreatorLocalHomeBanExpiresType,
select_expression = creator_local_home_ban_expires()
)
)]
pub creator_ban_expires_at: Option<DateTime<Utc>>,
}

View file

@ -46,6 +46,7 @@ ts-rs = { workspace = true, optional = true }
i-love-jesus = { workspace = true, optional = true }
derive-new = { workspace = true }
serde_with = { workspace = true }
chrono = { workspace = true }
[dev-dependencies]
pretty_assertions = { workspace = true }

View file

@ -24,7 +24,7 @@ use lemmy_db_schema::{
get_conn,
limit_fetch,
paginate,
queries::{
queries::joins::{
community_join,
creator_community_actions_join,
creator_home_instance_actions_join,
@ -240,8 +240,10 @@ impl InternalToCombinedView for PersonContentCombinedViewInternal {
post_tags: v.post_tags,
can_mod: v.can_mod,
creator_banned: v.creator_banned,
creator_ban_expires_at: v.creator_ban_expires_at,
creator_is_moderator: v.creator_is_moderator,
creator_banned_from_community: v.creator_banned_from_community,
creator_community_ban_expires_at: v.creator_community_ban_expires_at,
}))
} else {
Some(PersonContentCombinedView::Post(PostView {
@ -256,8 +258,10 @@ impl InternalToCombinedView for PersonContentCombinedViewInternal {
tags: v.post_tags,
can_mod: v.can_mod,
creator_banned: v.creator_banned,
creator_ban_expires_at: v.creator_ban_expires_at,
creator_is_moderator: v.creator_is_moderator,
creator_banned_from_community: v.creator_banned_from_community,
creator_community_ban_expires_at: v.creator_community_ban_expires_at,
}))
}
}

View file

@ -1,3 +1,4 @@
use chrono::{DateTime, Utc};
use lemmy_db_schema::{
newtypes::{PaginationCursor, PersonId},
source::{
@ -18,13 +19,16 @@ use serde_with::skip_serializing_none;
#[cfg(feature = "full")]
use {
diesel::{Queryable, Selectable},
lemmy_db_schema::utils::queries::{
creator_banned,
lemmy_db_schema::utils::queries::selects::{
creator_ban_expires_from_community,
creator_banned_from_community,
creator_is_admin,
creator_is_moderator,
creator_local_home_ban_expires,
creator_local_home_banned,
local_user_can_mod,
post_tags_fragment,
CreatorLocalHomeBanExpiresType,
},
lemmy_db_views_local_user::LocalUserView,
@ -78,10 +82,17 @@ pub(crate) struct PersonContentCombinedViewInternal {
pub can_mod: bool,
#[cfg_attr(feature = "full",
diesel(
select_expression = creator_banned()
select_expression = creator_local_home_banned()
)
)]
pub creator_banned: bool,
#[cfg_attr(feature = "full",
diesel(
select_expression_type = CreatorLocalHomeBanExpiresType,
select_expression = creator_local_home_ban_expires()
)
)]
pub creator_ban_expires_at: Option<DateTime<Utc>>,
#[cfg_attr(feature = "full",
diesel(
select_expression = creator_is_moderator()
@ -94,6 +105,12 @@ pub(crate) struct PersonContentCombinedViewInternal {
)
)]
pub creator_banned_from_community: bool,
#[cfg_attr(feature = "full",
diesel(
select_expression = creator_ban_expires_from_community()
)
)]
pub creator_community_ban_expires_at: Option<DateTime<Utc>>,
}
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]

View file

@ -46,6 +46,7 @@ serde = { workspace = true }
serde_with = { workspace = true }
ts-rs = { workspace = true, optional = true }
i-love-jesus = { workspace = true, optional = true }
chrono = { workspace = true }
[dev-dependencies]
pretty_assertions = { workspace = true }

View file

@ -23,7 +23,7 @@ use lemmy_db_schema::{
get_conn,
limit_fetch,
paginate,
queries::{
queries::joins::{
community_join,
creator_community_actions_join,
creator_community_instance_actions_join,
@ -230,8 +230,10 @@ impl InternalToCombinedView for PersonLikedCombinedViewInternal {
post_tags: v.post_tags,
can_mod: v.can_mod,
creator_banned: v.creator_banned,
creator_ban_expires_at: v.creator_ban_expires_at,
creator_is_moderator: v.creator_is_moderator,
creator_banned_from_community: v.creator_banned_from_community,
creator_community_ban_expires_at: v.creator_community_ban_expires_at,
}))
} else {
Some(PersonLikedCombinedView::Post(PostView {
@ -246,8 +248,10 @@ impl InternalToCombinedView for PersonLikedCombinedViewInternal {
tags: v.post_tags,
can_mod: v.can_mod,
creator_banned: v.creator_banned,
creator_ban_expires_at: v.creator_ban_expires_at,
creator_is_moderator: v.creator_is_moderator,
creator_banned_from_community: v.creator_banned_from_community,
creator_community_ban_expires_at: v.creator_community_ban_expires_at,
}))
}
}
@ -296,6 +300,7 @@ mod tests {
local_user: timmy_local_user,
person: timmy.clone(),
banned: false,
ban_expires_at: None,
};
let sara_form = PersonInsertForm::test_form(instance.id, "sara_pcv");

View file

@ -1,3 +1,4 @@
use chrono::{DateTime, Utc};
use lemmy_db_schema::{
newtypes::PaginationCursor,
source::{
@ -19,13 +20,16 @@ use serde_with::skip_serializing_none;
#[cfg(feature = "full")]
use {
diesel::{Queryable, Selectable},
lemmy_db_schema::utils::queries::{
lemmy_db_schema::utils::queries::selects::{
creator_ban_expires_from_community,
creator_banned_from_community,
creator_banned_within_community,
creator_is_admin,
creator_is_moderator,
creator_local_home_community_ban_expires,
creator_local_home_community_banned,
local_user_can_mod,
post_tags_fragment,
CreatorLocalHomeCommunityBanExpiresType,
},
lemmy_db_views_local_user::LocalUserView,
};
@ -78,10 +82,17 @@ pub(crate) struct PersonLikedCombinedViewInternal {
pub can_mod: bool,
#[cfg_attr(feature = "full",
diesel(
select_expression = creator_banned_within_community()
select_expression = creator_local_home_community_banned()
)
)]
pub creator_banned: bool,
#[cfg_attr(feature = "full",
diesel(
select_expression_type = CreatorLocalHomeCommunityBanExpiresType,
select_expression = creator_local_home_community_ban_expires()
)
)]
pub creator_ban_expires_at: Option<DateTime<Utc>>,
#[cfg_attr(feature = "full",
diesel(
select_expression = creator_is_moderator()
@ -94,6 +105,12 @@ pub(crate) struct PersonLikedCombinedViewInternal {
)
)]
pub creator_banned_from_community: bool,
#[cfg_attr(feature = "full",
diesel(
select_expression = creator_ban_expires_from_community()
)
)]
pub creator_community_ban_expires_at: Option<DateTime<Utc>>,
}
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]

View file

@ -46,6 +46,7 @@ serde = { workspace = true }
ts-rs = { workspace = true, optional = true }
i-love-jesus = { workspace = true, optional = true }
serde_with = { workspace = true }
chrono = { workspace = true }
[dev-dependencies]
pretty_assertions = { workspace = true }

View file

@ -23,7 +23,7 @@ use lemmy_db_schema::{
get_conn,
limit_fetch,
paginate,
queries::{
queries::joins::{
community_join,
creator_community_actions_join,
creator_community_instance_actions_join,
@ -219,8 +219,10 @@ impl InternalToCombinedView for PersonSavedCombinedViewInternal {
post_tags: v.post_tags,
can_mod: v.can_mod,
creator_banned: v.creator_banned,
creator_ban_expires_at: v.creator_ban_expires_at,
creator_is_moderator: v.creator_is_moderator,
creator_banned_from_community: v.creator_banned_from_community,
creator_community_ban_expires_at: v.creator_community_ban_expires_at,
}))
} else {
Some(PersonSavedCombinedView::Post(PostView {
@ -235,8 +237,10 @@ impl InternalToCombinedView for PersonSavedCombinedViewInternal {
tags: v.post_tags,
can_mod: v.can_mod,
creator_banned: v.creator_banned,
creator_ban_expires_at: v.creator_ban_expires_at,
creator_is_moderator: v.creator_is_moderator,
creator_banned_from_community: v.creator_banned_from_community,
creator_community_ban_expires_at: v.creator_community_ban_expires_at,
}))
}
}
@ -284,6 +288,7 @@ mod tests {
local_user: timmy_local_user,
person: timmy.clone(),
banned: false,
ban_expires_at: None,
};
let sara_form = PersonInsertForm::test_form(instance.id, "sara_pcv");

View file

@ -1,3 +1,4 @@
use chrono::{DateTime, Utc};
use lemmy_db_schema::{
newtypes::PaginationCursor,
source::{
@ -18,13 +19,16 @@ use serde_with::skip_serializing_none;
#[cfg(feature = "full")]
use {
diesel::{Queryable, Selectable},
lemmy_db_schema::utils::queries::{
lemmy_db_schema::utils::queries::selects::{
creator_ban_expires_from_community,
creator_banned_from_community,
creator_banned_within_community,
creator_is_admin,
creator_is_moderator,
creator_local_home_community_ban_expires,
creator_local_home_community_banned,
local_user_can_mod,
post_tags_fragment,
CreatorLocalHomeCommunityBanExpiresType,
},
lemmy_db_views_local_user::LocalUserView,
@ -78,10 +82,17 @@ pub(crate) struct PersonSavedCombinedViewInternal {
pub can_mod: bool,
#[cfg_attr(feature = "full",
diesel(
select_expression = creator_banned_within_community()
select_expression = creator_local_home_community_banned()
)
)]
pub creator_banned: bool,
#[cfg_attr(feature = "full",
diesel(
select_expression_type = CreatorLocalHomeCommunityBanExpiresType,
select_expression = creator_local_home_community_ban_expires()
)
)]
pub creator_ban_expires_at: Option<DateTime<Utc>>,
#[cfg_attr(feature = "full",
diesel(
select_expression = creator_is_moderator()
@ -94,6 +105,12 @@ pub(crate) struct PersonSavedCombinedViewInternal {
)
)]
pub creator_banned_from_community: bool,
#[cfg_attr(feature = "full",
diesel(
select_expression = creator_ban_expires_from_community()
)
)]
pub creator_community_ban_expires_at: Option<DateTime<Utc>>,
}
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]

View file

@ -32,21 +32,25 @@ use lemmy_db_schema::{
now,
paginate,
queries::{
creator_community_actions_join,
creator_community_instance_actions_join,
creator_home_instance_actions_join,
creator_local_instance_actions_join,
filter_blocked,
filter_is_subscribed,
filter_not_unlisted_or_is_subscribed,
image_details_join,
my_community_actions_join,
my_instance_communities_actions_join,
my_instance_persons_actions_join_1,
my_local_user_admin_join,
my_person_actions_join,
my_post_actions_join,
suggested_communities,
filters::{
filter_blocked,
filter_is_subscribed,
filter_not_unlisted_or_is_subscribed,
filter_suggested_communities,
},
joins::{
creator_community_actions_join,
creator_community_instance_actions_join,
creator_home_instance_actions_join,
creator_local_instance_actions_join,
image_details_join,
my_community_actions_join,
my_instance_communities_actions_join,
my_instance_persons_actions_join_1,
my_local_user_admin_join,
my_person_actions_join,
my_post_actions_join,
},
},
seconds_to_pg_interval,
Commented,
@ -400,7 +404,7 @@ impl PostQuery<'_> {
ListingType::ModeratorView => {
query = query.filter(community_actions::became_moderator_at.is_not_null());
}
ListingType::Suggested => query = query.filter(suggested_communities()),
ListingType::Suggested => query = query.filter(filter_suggested_communities()),
}
if !o.show_nsfw.unwrap_or(o.local_user.show_nsfw(site)) {
@ -568,7 +572,7 @@ mod tests {
impls::{PostQuery, PostSortType},
PostView,
};
use chrono::Utc;
use chrono::{DateTime, Days, Utc};
use diesel_async::SimpleAsyncConnection;
use diesel_uplete::UpleteCount;
use lemmy_db_schema::{
@ -791,17 +795,20 @@ mod tests {
local_user: inserted_tegan_local_user,
person: inserted_tegan_person,
banned: false,
ban_expires_at: None,
};
let john = LocalUserView {
local_user: inserted_john_local_user,
person: inserted_john_person,
banned: false,
ban_expires_at: None,
};
let bot = LocalUserView {
local_user: inserted_bot_local_user,
person: inserted_bot_person,
banned: false,
ban_expires_at: None,
};
Ok(Data {
@ -2076,10 +2083,17 @@ mod tests {
Ok(())
}
/// Use microseconds for date checks
///
/// Necessary because postgres uses micros, but rust uses nanos
fn micros(dt: DateTime<Utc>) -> i64 {
dt.timestamp_micros()
}
#[test_context(Data)]
#[tokio::test]
#[serial]
async fn post_listing_local_user_banned(data: &mut Data) -> LemmyResult<()> {
async fn post_listing_creator_banned(data: &mut Data) -> LemmyResult<()> {
let pool = &data.pool();
let pool = &mut pool.into();
@ -2097,9 +2111,11 @@ mod tests {
};
let banned_post = Post::create(pool, &post_form).await?;
let expires_at = Utc::now().checked_add_days(Days::new(1));
InstanceActions::ban(
pool,
&InstanceBanForm::new(banned_person.id, data.instance.id, None),
&InstanceBanForm::new(banned_person.id, data.instance.id, expires_at),
)
.await?;
@ -2115,7 +2131,66 @@ mod tests {
assert!(post_view.creator_banned);
assert!(post_view.creator_banned);
// Make sure the expires at is correct
assert_eq!(
expires_at.map(micros),
post_view.creator_ban_expires_at.map(micros)
);
Person::delete(pool, banned_person.id).await?;
Ok(())
}
#[test_context(Data)]
#[tokio::test]
#[serial]
async fn post_listing_creator_community_banned(data: &mut Data) -> LemmyResult<()> {
let pool = &data.pool();
let pool = &mut pool.into();
let banned_person_form = PersonInsertForm::test_form(data.instance.id, "jarvis");
let banned_person = Person::create(pool, &banned_person_form).await?;
let post_form = PostInsertForm {
language_id: Some(LanguageId(1)),
..PostInsertForm::new(
"banned jarvis post".to_string(),
banned_person.id,
data.community.id,
)
};
let banned_post = Post::create(pool, &post_form).await?;
let expires_at = Utc::now().checked_add_days(Days::new(1));
CommunityActions::ban(
pool,
&CommunityPersonBanForm {
ban_expires_at: Some(expires_at),
..CommunityPersonBanForm::new(data.community.id, banned_person.id)
},
)
.await?;
// Let john read their post
let post_view = PostView::read(
pool,
banned_post.id,
Some(&data.john.local_user),
data.instance.id,
false,
)
.await?;
assert!(post_view.creator_banned_from_community);
assert!(!post_view.creator_banned);
// Make sure the expires at is correct
assert_eq!(
expires_at.map(micros),
post_view.creator_community_ban_expires_at.map(micros)
);
Person::delete(pool, banned_person.id).await?;
Ok(())

View file

@ -1,3 +1,4 @@
use chrono::{DateTime, Utc};
use lemmy_db_schema::source::{
community::{Community, CommunityActions},
images::ImageDetails,
@ -12,13 +13,16 @@ use serde_with::skip_serializing_none;
#[cfg(feature = "full")]
use {
diesel::{Queryable, Selectable},
lemmy_db_schema::utils::queries::{
lemmy_db_schema::utils::queries::selects::{
creator_ban_expires_from_community,
creator_banned_from_community,
creator_banned_within_community,
creator_is_moderator,
creator_local_home_ban_expires,
creator_local_home_community_banned,
local_user_can_mod_post,
post_creator_is_admin,
post_tags_fragment,
CreatorLocalHomeBanExpiresType,
},
};
@ -67,10 +71,17 @@ pub struct PostView {
pub can_mod: bool,
#[cfg_attr(feature = "full",
diesel(
select_expression = creator_banned_within_community()
select_expression = creator_local_home_community_banned()
)
)]
pub creator_banned: bool,
#[cfg_attr(feature = "full",
diesel(
select_expression_type = CreatorLocalHomeBanExpiresType,
select_expression = creator_local_home_ban_expires()
)
)]
pub creator_ban_expires_at: Option<DateTime<Utc>>,
#[cfg_attr(feature = "full",
diesel(
select_expression = creator_is_moderator()
@ -83,4 +94,10 @@ pub struct PostView {
)
)]
pub creator_banned_from_community: bool,
#[cfg_attr(feature = "full",
diesel(
select_expression = creator_ban_expires_from_community()
)
)]
pub creator_community_ban_expires_at: Option<DateTime<Utc>>,
}

View file

@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
#[cfg(feature = "full")]
use {
diesel::{Queryable, Selectable},
lemmy_db_schema::utils::queries::person1_select,
lemmy_db_schema::utils::queries::selects::person1_select,
lemmy_db_schema::Person1AliasAllColumnsTuple,
};

View file

@ -8,7 +8,7 @@ use serde_with::skip_serializing_none;
#[cfg(feature = "full")]
use {
diesel::{helper_types::Nullable, NullableExpressionMethods, Queryable, Selectable},
lemmy_db_schema::{utils::queries::person1_select, Person1AliasAllColumnsTuple},
lemmy_db_schema::{utils::queries::selects::person1_select, Person1AliasAllColumnsTuple},
};
pub mod api;

View file

@ -41,7 +41,7 @@ use lemmy_db_schema::{
get_conn,
limit_fetch,
paginate,
queries::{
queries::joins::{
creator_community_instance_actions_join,
creator_home_instance_actions_join,
creator_local_instance_actions_join,
@ -622,6 +622,7 @@ mod tests {
local_user: timmy_local_user,
person: inserted_timmy.clone(),
banned: false,
ban_expires_at: None,
};
// Make an admin, to be able to see private message reports.
@ -633,6 +634,7 @@ mod tests {
local_user: admin_local_user,
person: inserted_admin.clone(),
banned: false,
ban_expires_at: None,
};
let sara_form = PersonInsertForm::test_form(inserted_instance.id, "sara_rcv");

View file

@ -1,3 +1,4 @@
use chrono::{DateTime, Utc};
use lemmy_db_schema::source::{
combined::report::ReportCombined,
comment::{Comment, CommentActions},
@ -15,13 +16,16 @@ use serde_with::skip_serializing_none;
#[cfg(feature = "full")]
use {
diesel::{dsl::Nullable, NullableExpressionMethods, Queryable, Selectable},
lemmy_db_schema::utils::queries::{
lemmy_db_schema::utils::queries::selects::{
creator_ban_expires_from_community,
creator_banned_from_community,
creator_banned_within_community,
creator_is_moderator,
creator_local_home_community_ban_expires,
creator_local_home_community_banned,
local_user_is_admin,
person1_select,
person2_select,
CreatorLocalHomeCommunityBanExpiresType,
},
lemmy_db_schema::{Person1AliasAllColumnsTuple, Person2AliasAllColumnsTuple},
lemmy_db_views_local_user::LocalUserView,
@ -82,16 +86,29 @@ pub struct ReportCombinedViewInternal {
pub creator_is_moderator: bool,
#[cfg_attr(feature = "full",
diesel(
select_expression = creator_banned_within_community()
select_expression = creator_local_home_community_banned()
)
)]
pub creator_banned: bool,
#[cfg_attr(feature = "full",
diesel(
select_expression_type = CreatorLocalHomeCommunityBanExpiresType,
select_expression = creator_local_home_community_ban_expires()
)
)]
pub creator_ban_expires_at: Option<DateTime<Utc>>,
#[cfg_attr(feature = "full",
diesel(
select_expression = creator_banned_from_community()
)
)]
pub creator_banned_from_community: bool,
#[cfg_attr(feature = "full",
diesel(
select_expression = creator_ban_expires_from_community()
)
)]
pub creator_community_ban_expires_at: Option<DateTime<Utc>>,
#[cfg_attr(feature = "full", diesel(embed))]
pub community: Option<Community>,
#[cfg_attr(feature = "full", diesel(embed))]

View file

@ -53,6 +53,7 @@ serde = { workspace = true }
ts-rs = { workspace = true, optional = true }
i-love-jesus = { workspace = true, optional = true }
serde_with = { workspace = true }
chrono = { workspace = true }
[dev-dependencies]
pretty_assertions = { workspace = true }

View file

@ -34,19 +34,23 @@ use lemmy_db_schema::{
now,
paginate,
queries::{
creator_community_actions_join,
creator_home_instance_actions_join,
creator_local_instance_actions_join,
creator_local_user_admin_join,
filter_is_subscribed,
filter_not_unlisted_or_is_subscribed,
image_details_join,
my_comment_actions_join,
my_community_actions_join,
my_local_user_admin_join,
my_person_actions_join,
my_post_actions_join,
suggested_communities,
filters::{
filter_is_subscribed,
filter_not_unlisted_or_is_subscribed,
filter_suggested_communities,
},
joins::{
creator_community_actions_join,
creator_home_instance_actions_join,
creator_local_instance_actions_join,
creator_local_user_admin_join,
image_details_join,
my_comment_actions_join,
my_community_actions_join,
my_local_user_admin_join,
my_person_actions_join,
my_post_actions_join,
},
},
seconds_to_pg_interval,
DbPool,
@ -338,7 +342,7 @@ impl SearchCombinedQuery {
ListingType::ModeratorView => {
query.filter(community_actions::became_moderator_at.is_not_null())
}
ListingType::Suggested => query.filter(suggested_communities()),
ListingType::Suggested => query.filter(filter_suggested_communities()),
};
// Filter by the time range
if let Some(time_range_seconds) = self.time_range_seconds {
@ -430,8 +434,10 @@ impl InternalToCombinedView for SearchCombinedViewInternal {
post_tags: v.post_tags,
can_mod: v.can_mod,
creator_banned: v.creator_banned,
creator_ban_expires_at: v.creator_ban_expires_at,
creator_is_moderator: v.creator_is_moderator,
creator_banned_from_community: v.creator_banned_from_community,
creator_community_ban_expires_at: v.creator_community_ban_expires_at,
}))
} else if let (Some(post), Some(creator), Some(community)) =
(v.post, v.item_creator.clone(), v.community.clone())
@ -448,8 +454,10 @@ impl InternalToCombinedView for SearchCombinedViewInternal {
tags: v.post_tags,
can_mod: v.can_mod,
creator_banned: v.creator_banned,
creator_ban_expires_at: v.creator_ban_expires_at,
creator_is_moderator: v.creator_is_moderator,
creator_banned_from_community: v.creator_banned_from_community,
creator_community_ban_expires_at: v.creator_community_ban_expires_at,
}))
} else if let Some(community) = v.community {
Some(SearchCombinedView::Community(CommunityView {
@ -469,6 +477,7 @@ impl InternalToCombinedView for SearchCombinedViewInternal {
is_admin: v.item_creator_is_admin,
person_actions: v.person_actions,
creator_banned: v.creator_banned,
creator_ban_expires_at: v.creator_ban_expires_at,
}))
} else {
None
@ -536,6 +545,7 @@ mod tests {
local_user: timmy_local_user,
person: timmy.clone(),
banned: false,
ban_expires_at: None,
};
let community_form = CommunityInsertForm {

View file

@ -1,3 +1,4 @@
use chrono::{DateTime, Utc};
use lemmy_db_schema::{
newtypes::{CommunityId, PaginationCursor, PersonId},
source::{
@ -23,14 +24,18 @@ use serde_with::skip_serializing_none;
#[cfg(feature = "full")]
use {
diesel::{Queryable, Selectable},
lemmy_db_schema::utils::queries::{
lemmy_db_schema::utils::queries::selects::{
community_post_tags_fragment,
creator_banned,
creator_ban_expires_from_community,
creator_banned_from_community,
creator_is_admin,
creator_is_moderator,
creator_local_home_ban_expires,
creator_local_home_banned,
local_user_can_mod,
post_tags_fragment,
CreatorLocalHomeBanExpiresType,
},
lemmy_db_schema::utils::queries::{creator_banned_from_community, creator_is_moderator},
lemmy_db_views_local_user::LocalUserView,
};
@ -92,10 +97,17 @@ pub(crate) struct SearchCombinedViewInternal {
pub can_mod: bool,
#[cfg_attr(feature = "full",
diesel(
select_expression = creator_banned()
select_expression = creator_local_home_banned()
)
)]
pub creator_banned: bool,
#[cfg_attr(feature = "full",
diesel(
select_expression_type = CreatorLocalHomeBanExpiresType,
select_expression = creator_local_home_ban_expires()
)
)]
pub creator_ban_expires_at: Option<DateTime<Utc>>,
#[cfg_attr(feature = "full",
diesel(
select_expression = creator_is_moderator()
@ -108,6 +120,12 @@ pub(crate) struct SearchCombinedViewInternal {
)
)]
pub creator_banned_from_community: bool,
#[cfg_attr(feature = "full",
diesel(
select_expression = creator_ban_expires_from_community()
)
)]
pub creator_community_ban_expires_at: Option<DateTime<Utc>>,
}
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]

View file

@ -17,9 +17,8 @@ use lemmy_db_schema::{
limit_fetch,
paginate,
queries::{
creator_banned,
creator_home_instance_actions_join,
creator_local_instance_actions_join,
joins::{creator_home_instance_actions_join, creator_local_instance_actions_join},
selects::creator_local_home_banned,
},
DbPool,
},
@ -89,7 +88,7 @@ impl VoteView {
.filter(post_actions::like_score.is_not_null())
.select((
person::all_columns,
creator_banned(),
creator_local_home_banned(),
creator_community_actions
.field(community_actions::received_ban_at)
.nullable()
@ -163,7 +162,7 @@ impl VoteView {
.filter(comment_actions::like_score.is_not_null())
.select((
person::all_columns,
creator_banned(),
creator_local_home_banned(),
creator_community_actions
.field(community_actions::received_ban_at)
.nullable()