mirror of
https://github.com/LemmyNet/lemmy.git
synced 2025-02-04 16:14:48 +00:00
Converting joins, filters, and selects to common functions for DB views. (#5359)
* Renaming person_mention to person_comment_mention. * Finishing up post body mentions. * Combined tables try 2 * Finishing up combined report table. * Fix ts optionals. * Adding tests, triggers, and history updates for report_combined. * Adding profile. * Add cursor pagination to report_combined view (#5244) * add pagination cursor * store timestamp instead of id in cursor (partial) * Revert "store timestamp instead of id in cursor (partial)" This reverts commit89359dde4b
. * use paginated query builder * Fixing migration and paged API. * Using dullbananas trigger procedure * Removing pointless list routes, reorganizing tests. * Fixing column XOR check. * Forgot to remove list report actions. * Cleanup. * Use internal tagging. * Fixing api tests. * Adding a few indexes. * Fixing migration name. * Fixing unique constraints. * Addressing PR comments. * Start working on profile combined * Adding views and replaceable schema. * A few changes to profile view. - Separating the profile fetch from its combined content fetch. - Starting to separate saved_only into its own combined view. * Finishing up combined person_saved and person_content. * Fixing api tests. * Moving to api-v4 routes. * Fixing imports. * Update crates/db_views/src/report_combined_view.rs Co-authored-by: dullbananas <dull.bananas0@gmail.com> * Update crates/db_views/src/report_combined_view.rs Co-authored-by: dullbananas <dull.bananas0@gmail.com> * Update crates/db_views/src/report_combined_view.rs Co-authored-by: dullbananas <dull.bananas0@gmail.com> * Update migrations/2024-12-02-181601_add_report_combined_table/up.sql Co-authored-by: dullbananas <dull.bananas0@gmail.com> * Update migrations/2024-12-02-181601_add_report_combined_table/up.sql Co-authored-by: dullbananas <dull.bananas0@gmail.com> * Fixing import and fmt. * Fixing null types in postgres. * Comment out err. * Fixing TS issues. * Adding types, fixing allow and blocklist crud. * Starting to work on combined views. * Using dullbananas trigger procedure * Adding the full combined view queries. * Adding tests. * taplo fmt. * Upgrading package.json deps. * Updating pnpm * Most of the bulk work done, need to add tests yet. * Finishing up inbox. * Using assert_length * Fixing sql_format. * Running fmt. * Fixing cargo shear. * Fixing clippy. * Addressing PR comments. * Starting to work on search combined. * Fix * Removing serialization * Removing serialization * Moving db_views_actor and _moderator into db_views. - This is necessary because the combined views use both, and that separation was arbitrary to begin with. db_schema has no such crate separation. * Adding search combined view, need to write tests yet. * Filters done, working on tests. * Adding tests for person, post, and community. * Finishing up tests. * Fixing duped trigger. * Remove saved_only test. * Remove pointless post_tags types. * Remove pointless index. * Changing published to saved for person_saved_combined. * Removing comment. * Renaming modlog when_ columns to published. - Fixes #5312 * Adding strum and simplifying imports. * Avoiding clone in map_to_enum * Changing modded_person to other_person. * Update crates/db_views_moderator/src/modlog_combined_view.rs Co-authored-by: dullbananas <dull.bananas0@gmail.com> * Update crates/db_views_moderator/src/modlog_combined_view.rs Co-authored-by: dullbananas <dull.bananas0@gmail.com> * Update crates/db_views_moderator/src/modlog_combined_view.rs Co-authored-by: dullbananas <dull.bananas0@gmail.com> * Addressing PR comments. * Fixing split. * Revert "Adding strum and simplifying imports." This reverts commit15f1671107
. * Running fmt. * Using assert + matches instead of filter_map. * Adding listPersonContent check. * Updating lemmy-js-client * Fixing mark all as read route, changing mark read to SuccessResponse. * Adding post body mention api test, fixing api tests. * Fixing route locations, and api tests. * Formatting sql. * Formatting sql 2. * Fixing search result, running clippy. * Fixing ts_option. * Adding search_combined.score column, and DB triggers. * Fixing API tests. * Adding an index for score. * Using next_back instead of last. * Converting local_user_view to use diesel auto_type. * Use AllColumns as suggested by weiznich to simplify Type. * Registration application view. * Person view simplifying. * Community view. * Community moderator view. * Community view. * Community follower view. * Community moderator view. * Person view. * Custom emoji view. * Local image view. * Removing strum from db_views. * Update crates/db_schema/src/newtypes.rs Co-authored-by: dullbananas <dull.bananas0@gmail.com> * Adding an action_utils. * Avoiding inner joins for up.sql * Adding person_aggregates.published column. * Remove unused private message view. * Implementing Selectable for DB views, moving joins inside impl. * Adding a few more. * Fixing imports. * fmt. * Post view. * Comment view. * Inbox combined view. * Modlog combined view. * The rest of the combined tables. * Finishing up. * Fixing shear. * Fixing cargo.toml. * Use 1.81 image. * Fix api common. * Fixing merge issues. --------- Co-authored-by: dullbananas <dull.bananas0@gmail.com>
This commit is contained in:
parent
1d0a47460d
commit
e654462405
40 changed files with 2093 additions and 2140 deletions
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -1280,15 +1280,6 @@ dependencies = [
|
|||
"tokio-postgres",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diesel-bind-if-some"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ed8ce9db476124d2eaf4c9db45dc6581b8e8c4c4d47d5e0f39de1fb55dfb2a7"
|
||||
dependencies = [
|
||||
"diesel",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diesel-derive-enum"
|
||||
version = "2.1.0"
|
||||
|
@ -2701,7 +2692,6 @@ dependencies = [
|
|||
"derive-new",
|
||||
"diesel",
|
||||
"diesel-async",
|
||||
"diesel-bind-if-some",
|
||||
"diesel-derive-enum",
|
||||
"diesel-derive-newtype",
|
||||
"diesel_ltree",
|
||||
|
@ -2746,7 +2736,6 @@ dependencies = [
|
|||
"serde_json",
|
||||
"serde_with",
|
||||
"serial_test",
|
||||
"strum",
|
||||
"test-context",
|
||||
"tokio",
|
||||
"tracing",
|
||||
|
|
|
@ -157,7 +157,6 @@ i-love-jesus = { version = "0.1.0" }
|
|||
clap = { version = "4.5.27", features = ["derive", "env"] }
|
||||
pretty_assertions = "1.4.1"
|
||||
derive-new = "0.7.0"
|
||||
diesel-bind-if-some = "0.1.0"
|
||||
tuplex = "0.1.2"
|
||||
|
||||
[dependencies]
|
||||
|
|
|
@ -38,7 +38,6 @@ full = [
|
|||
"rustls",
|
||||
"i-love-jesus",
|
||||
"tuplex",
|
||||
"diesel-bind-if-some",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
|
@ -78,7 +77,6 @@ rustls = { workspace = true, optional = true }
|
|||
uuid.workspace = true
|
||||
i-love-jesus = { workspace = true, optional = true }
|
||||
anyhow = { workspace = true }
|
||||
diesel-bind-if-some = { workspace = true, optional = true }
|
||||
derive-new.workspace = true
|
||||
tuplex = { workspace = true, optional = true }
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::{
|
|||
diesel::OptionalExtension,
|
||||
newtypes::{PersonId, PostId},
|
||||
schema::post_actions,
|
||||
utils::{find_action, get_conn, now, DbPool},
|
||||
utils::{get_conn, now, DbPool},
|
||||
};
|
||||
use diesel::{
|
||||
expression::SelectableHelper,
|
||||
|
@ -37,7 +37,9 @@ impl PersonPostAggregates {
|
|||
post_id_: PostId,
|
||||
) -> Result<Option<Self>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
find_action(post_actions::read_comments, (person_id_, post_id_))
|
||||
post_actions::table
|
||||
.find((person_id_, post_id_))
|
||||
.filter(post_actions::read_comments.is_not_null())
|
||||
.select(Self::as_select())
|
||||
.first(conn)
|
||||
.await
|
||||
|
|
|
@ -20,8 +20,6 @@ use crate::{
|
|||
},
|
||||
traits::{ApubActor, Bannable, Crud, Followable, Joinable},
|
||||
utils::{
|
||||
action_query,
|
||||
find_action,
|
||||
functions::{coalesce, coalesce_2_nullable, lower, random_smallint},
|
||||
get_conn,
|
||||
now,
|
||||
|
@ -268,6 +266,13 @@ impl Community {
|
|||
.get_result::<CommunityId>(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
#[diesel::dsl::auto_type(no_type_alias)]
|
||||
pub fn hide_removed_and_deleted() -> _ {
|
||||
community::removed
|
||||
.eq(false)
|
||||
.and(community::deleted.eq(false))
|
||||
}
|
||||
}
|
||||
|
||||
impl CommunityModerator {
|
||||
|
@ -301,7 +306,8 @@ impl CommunityModerator {
|
|||
for_person_id: PersonId,
|
||||
) -> Result<Vec<CommunityId>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
action_query(community_actions::became_moderator)
|
||||
community_actions::table
|
||||
.filter(community_actions::became_moderator.is_not_null())
|
||||
.filter(community_actions::person_id.eq(for_person_id))
|
||||
.select(community_actions::community_id)
|
||||
.load::<CommunityId>(conn)
|
||||
|
@ -322,7 +328,8 @@ impl CommunityModerator {
|
|||
persons.push(mod_person_id);
|
||||
persons.dedup();
|
||||
|
||||
let res = action_query(community_actions::became_moderator)
|
||||
let res = community_actions::table
|
||||
.filter(community_actions::became_moderator.is_not_null())
|
||||
.filter(community_actions::community_id.eq(for_community_id))
|
||||
.filter(community_actions::person_id.eq_any(persons))
|
||||
.order_by(community_actions::became_moderator)
|
||||
|
@ -393,14 +400,14 @@ impl CommunityFollower {
|
|||
remote_community_id: CommunityId,
|
||||
) -> LemmyResult<()> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
select(exists(
|
||||
action_query(community_actions::followed)
|
||||
.filter(community_actions::community_id.eq(remote_community_id)),
|
||||
))
|
||||
.get_result::<bool>(conn)
|
||||
.await?
|
||||
.then_some(())
|
||||
.ok_or(LemmyErrorType::CommunityHasNoFollowers.into())
|
||||
let find_action = community_actions::table
|
||||
.filter(community_actions::followed.is_not_null())
|
||||
.filter(community_actions::community_id.eq(remote_community_id));
|
||||
select(exists(find_action))
|
||||
.get_result::<bool>(conn)
|
||||
.await?
|
||||
.then_some(())
|
||||
.ok_or(LemmyErrorType::CommunityHasNoFollowers.into())
|
||||
}
|
||||
|
||||
pub async fn approve(
|
||||
|
@ -410,16 +417,16 @@ impl CommunityFollower {
|
|||
approver_id: PersonId,
|
||||
) -> LemmyResult<()> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
diesel::update(find_action(
|
||||
community_actions::followed,
|
||||
(follower_id, community_id),
|
||||
))
|
||||
.set((
|
||||
community_actions::follow_state.eq(CommunityFollowerState::Accepted),
|
||||
community_actions::follow_approver_id.eq(approver_id),
|
||||
))
|
||||
.execute(conn)
|
||||
.await?;
|
||||
let find_action = community_actions::table
|
||||
.find((follower_id, community_id))
|
||||
.filter(community_actions::followed.is_not_null());
|
||||
diesel::update(find_action)
|
||||
.set((
|
||||
community_actions::follow_state.eq(CommunityFollowerState::Accepted),
|
||||
community_actions::follow_approver_id.eq(approver_id),
|
||||
))
|
||||
.execute(conn)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -462,14 +469,14 @@ impl Followable for CommunityFollower {
|
|||
person_id: PersonId,
|
||||
) -> Result<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
diesel::update(find_action(
|
||||
community_actions::follow_state,
|
||||
(person_id, community_id),
|
||||
))
|
||||
.set(community_actions::follow_state.eq(Some(CommunityFollowerState::Accepted)))
|
||||
.returning(Self::as_select())
|
||||
.get_result::<Self>(conn)
|
||||
.await
|
||||
let find_action = community_actions::table
|
||||
.find((person_id, community_id))
|
||||
.filter(community_actions::follow_state.is_not_null());
|
||||
diesel::update(find_action)
|
||||
.set(community_actions::follow_state.eq(Some(CommunityFollowerState::Accepted)))
|
||||
.returning(Self::as_select())
|
||||
.get_result::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
async fn unfollow(
|
||||
pool: &mut DbPool<'_>,
|
||||
|
@ -510,9 +517,7 @@ impl ApubActor for Community {
|
|||
.filter(community::local.eq(true))
|
||||
.filter(lower(community::name).eq(community_name.to_lowercase()));
|
||||
if !include_deleted {
|
||||
q = q
|
||||
.filter(community::deleted.eq(false))
|
||||
.filter(community::removed.eq(false));
|
||||
q = q.filter(Self::hide_removed_and_deleted())
|
||||
}
|
||||
q.first(conn).await.optional()
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
|||
community_block::{CommunityBlock, CommunityBlockForm},
|
||||
},
|
||||
traits::Blockable,
|
||||
utils::{action_query, find_action, get_conn, now, uplete, DbPool},
|
||||
utils::{get_conn, now, uplete, DbPool},
|
||||
};
|
||||
use diesel::{
|
||||
dsl::{exists, insert_into, not},
|
||||
|
@ -27,14 +27,14 @@ impl CommunityBlock {
|
|||
for_community_id: CommunityId,
|
||||
) -> LemmyResult<()> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
select(not(exists(find_action(
|
||||
community_actions::blocked,
|
||||
(for_person_id, for_community_id),
|
||||
))))
|
||||
.get_result::<bool>(conn)
|
||||
.await?
|
||||
.then_some(())
|
||||
.ok_or(LemmyErrorType::CommunityIsBlocked.into())
|
||||
let find_action = community_actions::table
|
||||
.find((for_person_id, for_community_id))
|
||||
.filter(community_actions::blocked.is_not_null());
|
||||
select(not(exists(find_action)))
|
||||
.get_result::<bool>(conn)
|
||||
.await?
|
||||
.then_some(())
|
||||
.ok_or(LemmyErrorType::CommunityIsBlocked.into())
|
||||
}
|
||||
|
||||
pub async fn for_person(
|
||||
|
@ -42,7 +42,8 @@ impl CommunityBlock {
|
|||
person_id: PersonId,
|
||||
) -> Result<Vec<Community>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
action_query(community_actions::blocked)
|
||||
community_actions::table
|
||||
.filter(community_actions::blocked.is_not_null())
|
||||
.inner_join(community::table)
|
||||
.select(community::all_columns)
|
||||
.filter(community_actions::person_id.eq(person_id))
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
|||
instance_block::{InstanceBlock, InstanceBlockForm},
|
||||
},
|
||||
traits::Blockable,
|
||||
utils::{action_query, find_action, get_conn, now, uplete, DbPool},
|
||||
utils::{get_conn, now, uplete, DbPool},
|
||||
};
|
||||
use diesel::{
|
||||
dsl::{exists, insert_into, not},
|
||||
|
@ -27,14 +27,14 @@ impl InstanceBlock {
|
|||
for_instance_id: InstanceId,
|
||||
) -> LemmyResult<()> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
select(not(exists(find_action(
|
||||
instance_actions::blocked,
|
||||
(for_person_id, for_instance_id),
|
||||
))))
|
||||
.get_result::<bool>(conn)
|
||||
.await?
|
||||
.then_some(())
|
||||
.ok_or(LemmyErrorType::InstanceIsBlocked.into())
|
||||
let find_action = instance_actions::table
|
||||
.find((for_person_id, for_instance_id))
|
||||
.filter(instance_actions::blocked.is_not_null());
|
||||
select(not(exists(find_action)))
|
||||
.get_result::<bool>(conn)
|
||||
.await?
|
||||
.then_some(())
|
||||
.ok_or(LemmyErrorType::InstanceIsBlocked.into())
|
||||
}
|
||||
|
||||
pub async fn for_person(
|
||||
|
@ -42,7 +42,8 @@ impl InstanceBlock {
|
|||
person_id: PersonId,
|
||||
) -> Result<Vec<Instance>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
action_query(instance_actions::blocked)
|
||||
instance_actions::table
|
||||
.filter(instance_actions::blocked.is_not_null())
|
||||
.inner_join(instance::table)
|
||||
.select(instance::all_columns)
|
||||
.filter(instance_actions::person_id.eq(person_id))
|
||||
|
|
|
@ -8,7 +8,6 @@ use crate::{
|
|||
site::Site,
|
||||
},
|
||||
utils::{
|
||||
action_query,
|
||||
functions::{coalesce, lower},
|
||||
get_conn,
|
||||
now,
|
||||
|
@ -168,42 +167,48 @@ impl LocalUser {
|
|||
};
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
|
||||
let followed_communities = action_query(community_actions::followed)
|
||||
let followed_communities = community_actions::table
|
||||
.filter(community_actions::followed.is_not_null())
|
||||
.filter(community_actions::person_id.eq(person_id_))
|
||||
.inner_join(community::table)
|
||||
.select(community::actor_id)
|
||||
.get_results(conn)
|
||||
.await?;
|
||||
|
||||
let saved_posts = action_query(post_actions::saved)
|
||||
let saved_posts = post_actions::table
|
||||
.filter(post_actions::saved.is_not_null())
|
||||
.filter(post_actions::person_id.eq(person_id_))
|
||||
.inner_join(post::table)
|
||||
.select(post::ap_id)
|
||||
.get_results(conn)
|
||||
.await?;
|
||||
|
||||
let saved_comments = action_query(comment_actions::saved)
|
||||
let saved_comments = comment_actions::table
|
||||
.filter(comment_actions::saved.is_not_null())
|
||||
.filter(comment_actions::person_id.eq(person_id_))
|
||||
.inner_join(comment::table)
|
||||
.select(comment::ap_id)
|
||||
.get_results(conn)
|
||||
.await?;
|
||||
|
||||
let blocked_communities = action_query(community_actions::blocked)
|
||||
let blocked_communities = community_actions::table
|
||||
.filter(community_actions::blocked.is_not_null())
|
||||
.filter(community_actions::person_id.eq(person_id_))
|
||||
.inner_join(community::table)
|
||||
.select(community::actor_id)
|
||||
.get_results(conn)
|
||||
.await?;
|
||||
|
||||
let blocked_users = action_query(person_actions::blocked)
|
||||
let blocked_users = person_actions::table
|
||||
.filter(person_actions::blocked.is_not_null())
|
||||
.filter(person_actions::person_id.eq(person_id_))
|
||||
.inner_join(person::table.on(person_actions::target_id.eq(person::id)))
|
||||
.select(person::actor_id)
|
||||
.get_results(conn)
|
||||
.await?;
|
||||
|
||||
let blocked_instances = action_query(instance_actions::blocked)
|
||||
let blocked_instances = instance_actions::table
|
||||
.filter(instance_actions::blocked.is_not_null())
|
||||
.filter(instance_actions::person_id.eq(person_id_))
|
||||
.inner_join(instance::table)
|
||||
.select(instance::domain)
|
||||
|
@ -271,7 +276,8 @@ impl LocalUser {
|
|||
.order_by(local_user::id)
|
||||
.select(local_user::person_id);
|
||||
|
||||
let mods = action_query(community_actions::became_moderator)
|
||||
let mods = community_actions::table
|
||||
.filter(community_actions::became_moderator.is_not_null())
|
||||
.filter(community_actions::community_id.eq(for_community_id))
|
||||
.filter(community_actions::person_id.eq_any(&persons))
|
||||
.order_by(community_actions::became_moderator)
|
||||
|
|
|
@ -10,7 +10,7 @@ use crate::{
|
|||
PersonUpdateForm,
|
||||
},
|
||||
traits::{ApubActor, Crud, Followable},
|
||||
utils::{action_query, functions::lower, get_conn, now, uplete, DbPool},
|
||||
utils::{functions::lower, get_conn, now, uplete, DbPool},
|
||||
};
|
||||
use chrono::Utc;
|
||||
use diesel::{
|
||||
|
@ -235,7 +235,8 @@ impl PersonFollower {
|
|||
for_person_id: PersonId,
|
||||
) -> Result<Vec<Person>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
action_query(person_actions::followed)
|
||||
person_actions::table
|
||||
.filter(person_actions::followed.is_not_null())
|
||||
.inner_join(person::table.on(person_actions::person_id.eq(person::id)))
|
||||
.filter(person_actions::target_id.eq(for_person_id))
|
||||
.select(person::all_columns)
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
|||
person_block::{PersonBlock, PersonBlockForm},
|
||||
},
|
||||
traits::Blockable,
|
||||
utils::{action_query, find_action, get_conn, now, uplete, DbPool},
|
||||
utils::{get_conn, now, uplete, DbPool},
|
||||
};
|
||||
use diesel::{
|
||||
dsl::{exists, insert_into, not},
|
||||
|
@ -28,14 +28,14 @@ impl PersonBlock {
|
|||
for_recipient_id: PersonId,
|
||||
) -> LemmyResult<()> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
select(not(exists(find_action(
|
||||
person_actions::blocked,
|
||||
(for_person_id, for_recipient_id),
|
||||
))))
|
||||
.get_result::<bool>(conn)
|
||||
.await?
|
||||
.then_some(())
|
||||
.ok_or(LemmyErrorType::PersonIsBlocked.into())
|
||||
let find_action = person_actions::table
|
||||
.find((for_person_id, for_recipient_id))
|
||||
.filter(person_actions::blocked.is_not_null());
|
||||
select(not(exists(find_action)))
|
||||
.get_result::<bool>(conn)
|
||||
.await?
|
||||
.then_some(())
|
||||
.ok_or(LemmyErrorType::PersonIsBlocked.into())
|
||||
}
|
||||
|
||||
pub async fn for_person(
|
||||
|
@ -45,7 +45,8 @@ impl PersonBlock {
|
|||
let conn = &mut get_conn(pool).await?;
|
||||
let target_person_alias = diesel::alias!(person as person1);
|
||||
|
||||
action_query(person_actions::blocked)
|
||||
person_actions::table
|
||||
.filter(person_actions::blocked.is_not_null())
|
||||
.inner_join(person::table.on(person_actions::person_id.eq(person::id)))
|
||||
.inner_join(
|
||||
target_person_alias.on(person_actions::target_id.eq(target_person_alias.field(person::id))),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
newtypes::{LocalUserId, RegistrationApplicationId},
|
||||
schema::registration_application::dsl::{local_user_id, registration_application},
|
||||
schema::registration_application,
|
||||
source::registration_application::{
|
||||
RegistrationApplication,
|
||||
RegistrationApplicationInsertForm,
|
||||
|
@ -20,7 +20,7 @@ impl Crud for RegistrationApplication {
|
|||
|
||||
async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
insert_into(registration_application)
|
||||
insert_into(registration_application::table)
|
||||
.values(form)
|
||||
.get_result::<Self>(conn)
|
||||
.await
|
||||
|
@ -32,7 +32,7 @@ impl Crud for RegistrationApplication {
|
|||
form: &Self::UpdateForm,
|
||||
) -> Result<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
diesel::update(registration_application.find(id_))
|
||||
diesel::update(registration_application::table.find(id_))
|
||||
.set(form)
|
||||
.get_result::<Self>(conn)
|
||||
.await
|
||||
|
@ -45,9 +45,15 @@ impl RegistrationApplication {
|
|||
local_user_id_: LocalUserId,
|
||||
) -> Result<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
registration_application
|
||||
.filter(local_user_id.eq(local_user_id_))
|
||||
registration_application::table
|
||||
.filter(registration_application::local_user_id.eq(local_user_id_))
|
||||
.first(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
/// A missing admin id, means the application is unread
|
||||
#[diesel::dsl::auto_type(no_type_alias)]
|
||||
pub fn is_unread() -> _ {
|
||||
registration_application::admin_id.is_null()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#![recursion_limit = "256"]
|
||||
|
||||
#[cfg(feature = "full")]
|
||||
#[macro_use]
|
||||
extern crate diesel;
|
||||
|
@ -41,6 +39,10 @@ pub mod utils;
|
|||
#[cfg(feature = "full")]
|
||||
pub mod schema_setup;
|
||||
|
||||
#[cfg(feature = "full")]
|
||||
use diesel::query_source::AliasedField;
|
||||
#[cfg(feature = "full")]
|
||||
use schema::person;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum::{Display, EnumString};
|
||||
#[cfg(feature = "full")]
|
||||
|
@ -328,3 +330,28 @@ macro_rules! assert_length {
|
|||
assert_eq!($len, $vec.len(), "Vec has wrong length: {:?}", $vec)
|
||||
}};
|
||||
}
|
||||
|
||||
#[cfg(feature = "full")]
|
||||
/// A helper tuple for person alias columns
|
||||
pub type Person1AliasAllColumnsTuple = (
|
||||
AliasedField<aliases::Person1, person::id>,
|
||||
AliasedField<aliases::Person1, person::name>,
|
||||
AliasedField<aliases::Person1, person::display_name>,
|
||||
AliasedField<aliases::Person1, person::avatar>,
|
||||
AliasedField<aliases::Person1, person::banned>,
|
||||
AliasedField<aliases::Person1, person::published>,
|
||||
AliasedField<aliases::Person1, person::updated>,
|
||||
AliasedField<aliases::Person1, person::actor_id>,
|
||||
AliasedField<aliases::Person1, person::bio>,
|
||||
AliasedField<aliases::Person1, person::local>,
|
||||
AliasedField<aliases::Person1, person::private_key>,
|
||||
AliasedField<aliases::Person1, person::public_key>,
|
||||
AliasedField<aliases::Person1, person::last_refreshed_at>,
|
||||
AliasedField<aliases::Person1, person::banner>,
|
||||
AliasedField<aliases::Person1, person::deleted>,
|
||||
AliasedField<aliases::Person1, person::inbox_url>,
|
||||
AliasedField<aliases::Person1, person::matrix_user_id>,
|
||||
AliasedField<aliases::Person1, person::bot_account>,
|
||||
AliasedField<aliases::Person1, person::ban_expires>,
|
||||
AliasedField<aliases::Person1, person::instance_id>,
|
||||
);
|
||||
|
|
|
@ -17,7 +17,7 @@ use ts_rs::TS;
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize, Default)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = local_site))]
|
||||
#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::site::Site)))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
|
|
|
@ -9,22 +9,15 @@ use diesel::{
|
|||
helper_types::AsExprOf,
|
||||
pg::Pg,
|
||||
query_builder::{Query, QueryFragment},
|
||||
query_dsl::methods::{FilterDsl, FindDsl, LimitDsl},
|
||||
query_source::{Alias, AliasSource, AliasedField},
|
||||
query_dsl::methods::LimitDsl,
|
||||
result::{
|
||||
ConnectionError,
|
||||
ConnectionResult,
|
||||
Error::{self as DieselError, QueryBuilderError},
|
||||
},
|
||||
sql_types::{self, SingleValue, Timestamptz},
|
||||
Column,
|
||||
sql_types::{self, Timestamptz},
|
||||
Expression,
|
||||
ExpressionMethods,
|
||||
IntoSql,
|
||||
JoinOnDsl,
|
||||
NullableExpressionMethods,
|
||||
QuerySource,
|
||||
Table,
|
||||
};
|
||||
use diesel_async::{
|
||||
pg::AsyncPgConnection,
|
||||
|
@ -35,8 +28,7 @@ use diesel_async::{
|
|||
},
|
||||
AsyncConnection,
|
||||
};
|
||||
use diesel_bind_if_some::BindIfSome;
|
||||
use futures_util::{future::BoxFuture, Future, FutureExt};
|
||||
use futures_util::{future::BoxFuture, FutureExt};
|
||||
use i_love_jesus::{CursorKey, PaginatedQueryBuilder};
|
||||
use lemmy_utils::{
|
||||
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
|
||||
|
@ -580,102 +572,6 @@ impl<T: Expression + AsExpression<sql_types::Record<T::SqlType>>> AsRecord for T
|
|||
/// Output of `IntoSql::into_sql` for a type that implements `AsRecord`
|
||||
pub type AsRecordOutput<T> = dsl::AsExprOf<T, sql_types::Record<<T as Expression>::SqlType>>;
|
||||
|
||||
/// Output of `t.on((l0, l1).into_sql().eq((r0, r1)))`
|
||||
type OnTupleEq<T, L0, L1, R0, R1> = dsl::On<T, dsl::Eq<AsRecordOutput<(L0, L1)>, (R0, R1)>>;
|
||||
|
||||
/// Creates an `ON` clause for a table where a person ID and another column are used as the
|
||||
/// primary key. Use with the `QueryDsl::left_join` method.
|
||||
///
|
||||
/// This example modifies a query to make columns in `community_actions` available:
|
||||
///
|
||||
/// ```
|
||||
/// community::table
|
||||
/// .left_join(actions(
|
||||
/// community_actions::table,
|
||||
/// my_person_id,
|
||||
/// community::id,
|
||||
/// ))
|
||||
/// ```
|
||||
pub fn actions<T, P, C, K0, K1>(
|
||||
actions_table: T,
|
||||
person_id: Option<P>,
|
||||
target_id: C,
|
||||
) -> OnTupleEq<T, dsl::Nullable<K0>, K1, BindIfSome<dsl::AsExprOf<P, sql_types::Integer>>, C>
|
||||
where
|
||||
T: Table<PrimaryKey = (K0, K1)> + Copy,
|
||||
K0: Expression,
|
||||
P: AsExpression<sql_types::Integer>,
|
||||
(dsl::Nullable<K0>, K1): AsRecord,
|
||||
(BindIfSome<dsl::AsExprOf<P, sql_types::Integer>>, C):
|
||||
AsExpression<<AsRecordOutput<(dsl::Nullable<K0>, K1)> as Expression>::SqlType>,
|
||||
{
|
||||
let (k0, k1) = actions_table.primary_key();
|
||||
actions_table.on((k0.nullable(), k1).into_sql().eq((
|
||||
BindIfSome(person_id.map(diesel::IntoSql::into_sql)),
|
||||
target_id,
|
||||
)))
|
||||
}
|
||||
|
||||
/// Like `actions` but `actions_table` is an alias and person id is not nullable
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn actions_alias<T, P, C, K0, K1>(
|
||||
actions_table: Alias<T>,
|
||||
person_id: P,
|
||||
target_id: C,
|
||||
) -> OnTupleEq<Alias<T>, AliasedField<T, K0>, AliasedField<T, K1>, P, C>
|
||||
where
|
||||
Alias<T>: QuerySource + Copy,
|
||||
T: AliasSource<Target: Table<PrimaryKey = (K0, K1)>> + Default,
|
||||
K0: Column<Table = T::Target>,
|
||||
K1: Column<Table = T::Target>,
|
||||
(AliasedField<T, K0>, AliasedField<T, K1>): AsRecord,
|
||||
(P, C): AsExpression<
|
||||
<AsRecordOutput<(AliasedField<T, K0>, AliasedField<T, K1>)> as Expression>::SqlType,
|
||||
>,
|
||||
{
|
||||
let (k0, k1) = T::default().target().primary_key();
|
||||
actions_table.on(
|
||||
(actions_table.field(k0), actions_table.field(k1))
|
||||
.into_sql()
|
||||
.eq((person_id, target_id)),
|
||||
)
|
||||
}
|
||||
|
||||
/// `action_query(table_name::action_name)` is the same as
|
||||
/// `table_name::table.filter(table_name::action_name.is_not_null())`.
|
||||
pub fn action_query<C>(column: C) -> dsl::Filter<C::Table, dsl::IsNotNull<C>>
|
||||
where
|
||||
C: Column<Table: Default + FilterDsl<dsl::IsNotNull<C>>, SqlType: SingleValue>,
|
||||
{
|
||||
action_query_with_fn(column, |t| t)
|
||||
}
|
||||
|
||||
/// `find_action(table_name::action_name, key)` is the same as
|
||||
/// `table_name::table.find(key).filter(table_name::action_name.is_not_null())`.
|
||||
pub fn find_action<C, K>(
|
||||
column: C,
|
||||
key: K,
|
||||
) -> dsl::Filter<dsl::Find<C::Table, K>, dsl::IsNotNull<C>>
|
||||
where
|
||||
C:
|
||||
Column<Table: Default + FindDsl<K, Output: FilterDsl<dsl::IsNotNull<C>>>, SqlType: SingleValue>,
|
||||
{
|
||||
action_query_with_fn(column, |t| t.find(key))
|
||||
}
|
||||
|
||||
/// `action_query_with_fn(table_name::action_name, f)` is the same as
|
||||
/// `f(table_name::table).filter(table_name::action_name.is_not_null())`.
|
||||
fn action_query_with_fn<C, Q>(
|
||||
column: C,
|
||||
f: impl FnOnce(C::Table) -> Q,
|
||||
) -> dsl::Filter<Q, dsl::IsNotNull<C>>
|
||||
where
|
||||
C: Column<Table: Default, SqlType: SingleValue>,
|
||||
Q: FilterDsl<dsl::IsNotNull<C>>,
|
||||
{
|
||||
f(C::Table::default()).filter(column.is_not_null())
|
||||
}
|
||||
|
||||
pub type ResultFuture<'a, T> = BoxFuture<'a, Result<T, DieselError>>;
|
||||
|
||||
pub trait ReadFn<'a, T, Args>: Fn(DbConn<'a>, Args) -> ResultFuture<'a, T> {}
|
||||
|
@ -686,58 +582,6 @@ pub trait ListFn<'a, T, Args>: Fn(DbConn<'a>, Args) -> ResultFuture<'a, Vec<T>>
|
|||
|
||||
impl<'a, T, Args, F: Fn(DbConn<'a>, Args) -> ResultFuture<'a, Vec<T>>> ListFn<'a, T, Args> for F {}
|
||||
|
||||
/// Allows read and list functions to capture a shared closure that has an inferred return type,
|
||||
/// which is useful for join logic
|
||||
pub struct Queries<RF, LF> {
|
||||
pub read_fn: RF,
|
||||
pub list_fn: LF,
|
||||
}
|
||||
|
||||
// `()` is used to prevent type inference error
|
||||
impl Queries<(), ()> {
|
||||
pub fn new<'a, RFut, LFut, RT, LT, RA, LA, RF2, LF2>(
|
||||
read_fn: RF2,
|
||||
list_fn: LF2,
|
||||
) -> Queries<impl ReadFn<'a, RT, RA>, impl ListFn<'a, LT, LA>>
|
||||
where
|
||||
RFut: Future<Output = Result<RT, DieselError>> + Sized + Send + 'a,
|
||||
LFut: Future<Output = Result<Vec<LT>, DieselError>> + Sized + Send + 'a,
|
||||
RF2: Fn(DbConn<'a>, RA) -> RFut,
|
||||
LF2: Fn(DbConn<'a>, LA) -> LFut,
|
||||
{
|
||||
Queries {
|
||||
read_fn: move |conn, args| read_fn(conn, args).boxed(),
|
||||
list_fn: move |conn, args| list_fn(conn, args).boxed(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<RF, LF> Queries<RF, LF> {
|
||||
pub async fn read<'a, T, Args>(
|
||||
self,
|
||||
pool: &'a mut DbPool<'_>,
|
||||
args: Args,
|
||||
) -> Result<T, DieselError>
|
||||
where
|
||||
RF: ReadFn<'a, T, Args>,
|
||||
{
|
||||
let conn = get_conn(pool).await?;
|
||||
(self.read_fn)(conn, args).await
|
||||
}
|
||||
|
||||
pub async fn list<'a, T, Args>(
|
||||
self,
|
||||
pool: &'a mut DbPool<'_>,
|
||||
args: Args,
|
||||
) -> Result<Vec<T>, DieselError>
|
||||
where
|
||||
LF: ListFn<'a, T, Args>,
|
||||
{
|
||||
let conn = get_conn(pool).await?;
|
||||
(self.list_fn)(conn, args).await
|
||||
}
|
||||
}
|
||||
|
||||
pub fn paginate<Q, C>(
|
||||
query: Q,
|
||||
page_after: Option<C>,
|
||||
|
|
|
@ -41,8 +41,7 @@ ts-rs = { workspace = true, optional = true }
|
|||
actix-web = { workspace = true, optional = true }
|
||||
i-love-jesus = { workspace = true, optional = true }
|
||||
chrono = { workspace = true }
|
||||
derive-new.workspace = true
|
||||
strum = { workspace = true }
|
||||
derive-new = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
serial_test = { workspace = true }
|
||||
|
|
|
@ -49,35 +49,17 @@ use lemmy_db_schema::{
|
|||
community::CommunityFollower,
|
||||
},
|
||||
traits::InternalToCombinedView,
|
||||
utils::{actions, actions_alias, functions::coalesce, get_conn, DbPool},
|
||||
utils::{functions::coalesce, get_conn, DbPool},
|
||||
InboxDataType,
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
impl InboxCombinedViewInternal {
|
||||
/// Gets the number of unread mentions
|
||||
pub async fn get_unread_count(
|
||||
pool: &mut DbPool<'_>,
|
||||
my_person_id: PersonId,
|
||||
show_bot_accounts: bool,
|
||||
) -> Result<i64, Error> {
|
||||
use diesel::dsl::count;
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
|
||||
#[diesel::dsl::auto_type(no_type_alias)]
|
||||
fn joins(my_person_id: PersonId) -> _ {
|
||||
let item_creator = person::id;
|
||||
let recipient_person = aliases::person1.field(person::id);
|
||||
|
||||
let unread_filter = comment_reply::read
|
||||
.eq(false)
|
||||
.or(person_comment_mention::read.eq(false))
|
||||
.or(person_post_mention::read.eq(false))
|
||||
// If its unread, I only want the messages to me
|
||||
.or(
|
||||
private_message::read
|
||||
.eq(false)
|
||||
.and(private_message::recipient_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
let item_creator_join = comment::creator_id
|
||||
.eq(item_creator)
|
||||
.or(
|
||||
|
@ -87,11 +69,13 @@ impl InboxCombinedViewInternal {
|
|||
)
|
||||
.or(private_message::creator_id.eq(item_creator));
|
||||
|
||||
let recipient_join = comment_reply::recipient_id
|
||||
.eq(recipient_person)
|
||||
.or(person_comment_mention::recipient_id.eq(recipient_person))
|
||||
.or(person_post_mention::recipient_id.eq(recipient_person))
|
||||
.or(private_message::recipient_id.eq(recipient_person));
|
||||
let recipient_join = aliases::person1.on(
|
||||
comment_reply::recipient_id
|
||||
.eq(recipient_person)
|
||||
.or(person_comment_mention::recipient_id.eq(recipient_person))
|
||||
.or(person_post_mention::recipient_id.eq(recipient_person))
|
||||
.or(private_message::recipient_id.eq(recipient_person)),
|
||||
);
|
||||
|
||||
let comment_join = comment_reply::comment_id
|
||||
.eq(comment::id)
|
||||
|
@ -112,27 +96,107 @@ impl InboxCombinedViewInternal {
|
|||
.eq(private_message::id.nullable())
|
||||
.and(not(private_message::deleted));
|
||||
|
||||
let mut query = inbox_combined::table
|
||||
let community_join = post::community_id.eq(community::id);
|
||||
|
||||
let local_user_join = local_user::table.on(
|
||||
item_creator
|
||||
.eq(local_user::person_id)
|
||||
.and(local_user::admin.eq(true)),
|
||||
);
|
||||
|
||||
let post_aggregates_join = post_aggregates::table.on(post::id.eq(post_aggregates::post_id));
|
||||
let comment_aggregates_join =
|
||||
comment_aggregates::table.on(comment::id.eq(comment_aggregates::comment_id));
|
||||
|
||||
let image_details_join =
|
||||
image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable()));
|
||||
|
||||
let creator_community_actions_join = creator_community_actions.on(
|
||||
creator_community_actions
|
||||
.field(community_actions::community_id)
|
||||
.eq(post::community_id)
|
||||
.and(
|
||||
creator_community_actions
|
||||
.field(community_actions::person_id)
|
||||
.eq(item_creator),
|
||||
),
|
||||
);
|
||||
|
||||
let community_actions_join = community_actions::table.on(
|
||||
community_actions::community_id
|
||||
.eq(post::community_id)
|
||||
.and(community_actions::person_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
let instance_actions_join = instance_actions::table.on(
|
||||
instance_actions::instance_id
|
||||
.eq(person::instance_id)
|
||||
.and(instance_actions::person_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
let post_actions_join = post_actions::table.on(
|
||||
post_actions::post_id
|
||||
.eq(post::id)
|
||||
.and(post_actions::person_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
let person_actions_join = person_actions::table.on(
|
||||
person_actions::target_id
|
||||
.eq(item_creator)
|
||||
.and(person_actions::person_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
let comment_actions_join = comment_actions::table.on(
|
||||
comment_actions::comment_id
|
||||
.eq(comment::id)
|
||||
.and(comment_actions::person_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
inbox_combined::table
|
||||
.left_join(comment_reply::table)
|
||||
.left_join(person_comment_mention::table)
|
||||
.left_join(person_post_mention::table)
|
||||
.left_join(private_message::table.on(private_message_join))
|
||||
.left_join(comment::table.on(comment_join))
|
||||
.left_join(post::table.on(post_join))
|
||||
// The item creator
|
||||
.left_join(community::table.on(community_join))
|
||||
.inner_join(person::table.on(item_creator_join))
|
||||
// The recipient
|
||||
.inner_join(aliases::person1.on(recipient_join))
|
||||
.left_join(actions(
|
||||
instance_actions::table,
|
||||
Some(my_person_id),
|
||||
person::instance_id,
|
||||
))
|
||||
.left_join(actions(
|
||||
person_actions::table,
|
||||
Some(my_person_id),
|
||||
item_creator,
|
||||
))
|
||||
.inner_join(recipient_join)
|
||||
.left_join(image_details_join)
|
||||
.left_join(post_aggregates_join)
|
||||
.left_join(comment_aggregates_join)
|
||||
.left_join(creator_community_actions_join)
|
||||
.left_join(local_user_join)
|
||||
.left_join(community_actions_join)
|
||||
.left_join(instance_actions_join)
|
||||
.left_join(post_actions_join)
|
||||
.left_join(person_actions_join)
|
||||
.left_join(comment_actions_join)
|
||||
}
|
||||
|
||||
/// Gets the number of unread mentions
|
||||
pub async fn get_unread_count(
|
||||
pool: &mut DbPool<'_>,
|
||||
my_person_id: PersonId,
|
||||
show_bot_accounts: bool,
|
||||
) -> Result<i64, Error> {
|
||||
use diesel::dsl::count;
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
|
||||
let recipient_person = aliases::person1.field(person::id);
|
||||
|
||||
let unread_filter = comment_reply::read
|
||||
.eq(false)
|
||||
.or(person_comment_mention::read.eq(false))
|
||||
.or(person_post_mention::read.eq(false))
|
||||
// If its unread, I only want the messages to me
|
||||
.or(
|
||||
private_message::read
|
||||
.eq(false)
|
||||
.and(private_message::recipient_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
let mut query = Self::joins(my_person_id)
|
||||
// Filter for your user
|
||||
.filter(recipient_person.eq(my_person_id))
|
||||
// Filter unreads
|
||||
|
@ -140,6 +204,7 @@ impl InboxCombinedViewInternal {
|
|||
// Don't count replies from blocked users
|
||||
.filter(person_actions::blocked.is_null())
|
||||
.filter(instance_actions::blocked.is_null())
|
||||
.select(count(inbox_combined::id))
|
||||
.into_boxed();
|
||||
|
||||
// These filters need to be kept in sync with the filters in queries().list()
|
||||
|
@ -147,10 +212,7 @@ impl InboxCombinedViewInternal {
|
|||
query = query.filter(not(person::bot_account));
|
||||
}
|
||||
|
||||
query
|
||||
.select(count(inbox_combined::id))
|
||||
.first::<i64>(conn)
|
||||
.await
|
||||
query.first::<i64>(conn).await
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,42 +272,6 @@ impl InboxCombinedQuery {
|
|||
let item_creator = person::id;
|
||||
let recipient_person = aliases::person1.field(person::id);
|
||||
|
||||
let item_creator_join = comment::creator_id
|
||||
.eq(item_creator)
|
||||
.or(
|
||||
inbox_combined::person_post_mention_id
|
||||
.is_not_null()
|
||||
.and(post::creator_id.eq(item_creator)),
|
||||
)
|
||||
.or(private_message::creator_id.eq(item_creator));
|
||||
|
||||
let recipient_join = comment_reply::recipient_id
|
||||
.eq(recipient_person)
|
||||
.or(person_comment_mention::recipient_id.eq(recipient_person))
|
||||
.or(person_post_mention::recipient_id.eq(recipient_person))
|
||||
.or(private_message::recipient_id.eq(recipient_person));
|
||||
|
||||
let comment_join = comment_reply::comment_id
|
||||
.eq(comment::id)
|
||||
.or(person_comment_mention::comment_id.eq(comment::id))
|
||||
// Filter out the deleted / removed
|
||||
.and(not(comment::deleted))
|
||||
.and(not(comment::removed));
|
||||
|
||||
let post_join = person_post_mention::post_id
|
||||
.eq(post::id)
|
||||
.or(comment::post_id.eq(post::id))
|
||||
// Filter out the deleted / removed
|
||||
.and(not(post::deleted))
|
||||
.and(not(post::removed));
|
||||
|
||||
// This could be a simple join, but you need to check for deleted here
|
||||
let private_message_join = inbox_combined::private_message_id
|
||||
.eq(private_message::id.nullable())
|
||||
.and(not(private_message::deleted));
|
||||
|
||||
let community_join = post::community_id.eq(community::id);
|
||||
|
||||
let post_tags = post_tag::table
|
||||
.inner_join(tag::table)
|
||||
.select(diesel::dsl::sql::<diesel::sql_types::Json>(
|
||||
|
@ -255,54 +281,7 @@ impl InboxCombinedQuery {
|
|||
.filter(tag::deleted.eq(false))
|
||||
.single_value();
|
||||
|
||||
let mut query = inbox_combined::table
|
||||
.left_join(comment_reply::table)
|
||||
.left_join(person_comment_mention::table)
|
||||
.left_join(person_post_mention::table)
|
||||
.left_join(private_message::table.on(private_message_join))
|
||||
.left_join(comment::table.on(comment_join))
|
||||
.left_join(post::table.on(post_join))
|
||||
.left_join(community::table.on(community_join))
|
||||
// The item creator
|
||||
.inner_join(person::table.on(item_creator_join))
|
||||
// The recipient
|
||||
.inner_join(aliases::person1.on(recipient_join))
|
||||
.left_join(actions_alias(
|
||||
creator_community_actions,
|
||||
item_creator,
|
||||
post::community_id,
|
||||
))
|
||||
.left_join(
|
||||
local_user::table.on(
|
||||
item_creator
|
||||
.eq(local_user::person_id)
|
||||
.and(local_user::admin.eq(true)),
|
||||
),
|
||||
)
|
||||
.left_join(actions(
|
||||
community_actions::table,
|
||||
Some(my_person_id),
|
||||
post::community_id,
|
||||
))
|
||||
.left_join(actions(
|
||||
instance_actions::table,
|
||||
Some(my_person_id),
|
||||
person::instance_id,
|
||||
))
|
||||
.left_join(actions(post_actions::table, Some(my_person_id), post::id))
|
||||
.left_join(actions(
|
||||
person_actions::table,
|
||||
Some(my_person_id),
|
||||
item_creator,
|
||||
))
|
||||
.left_join(post_aggregates::table.on(post::id.eq(post_aggregates::post_id)))
|
||||
.left_join(comment_aggregates::table.on(comment::id.eq(comment_aggregates::comment_id)))
|
||||
.left_join(actions(
|
||||
comment_actions::table,
|
||||
Some(my_person_id),
|
||||
comment::id,
|
||||
))
|
||||
.left_join(image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable())))
|
||||
let mut query = InboxCombinedViewInternal::joins(my_person_id)
|
||||
.select((
|
||||
// Specific
|
||||
comment_reply::all_columns.nullable(),
|
||||
|
|
|
@ -66,6 +66,146 @@ use lemmy_db_schema::{
|
|||
ModlogActionType,
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
impl ModlogCombinedViewInternal {
|
||||
#[diesel::dsl::auto_type(no_type_alias)]
|
||||
fn joins(mod_person_id: Option<PersonId>, hide_modlog_names: Option<bool>) -> _ {
|
||||
// The modded / other person
|
||||
let other_person = aliases::person1.field(person::id);
|
||||
|
||||
let show_mod_names: bool = !(hide_modlog_names.unwrap_or_default());
|
||||
let show_mod_names_expr = show_mod_names.into_sql::<diesel::sql_types::Bool>();
|
||||
|
||||
// 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 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 comment_join = 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 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 instance_join = admin_allow_instance::instance_id
|
||||
.eq(instance::id)
|
||||
.or(admin_block_instance::instance_id.eq(instance::id));
|
||||
|
||||
modlog_combined::table
|
||||
.left_join(admin_allow_instance::table)
|
||||
.left_join(admin_block_instance::table)
|
||||
.left_join(admin_purge_comment::table)
|
||||
.left_join(admin_purge_community::table)
|
||||
.left_join(admin_purge_person::table)
|
||||
.left_join(admin_purge_post::table)
|
||||
.left_join(mod_add::table)
|
||||
.left_join(mod_add_community::table)
|
||||
.left_join(mod_ban::table)
|
||||
.left_join(mod_ban_from_community::table)
|
||||
.left_join(mod_feature_post::table)
|
||||
.left_join(mod_hide_community::table)
|
||||
.left_join(mod_lock_post::table)
|
||||
.left_join(mod_remove_comment::table)
|
||||
.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))
|
||||
}
|
||||
}
|
||||
|
||||
impl ModlogCombinedPaginationCursor {
|
||||
// get cursor for page that starts immediately after the given post
|
||||
|
@ -159,167 +299,10 @@ pub struct ModlogCombinedQuery {
|
|||
impl ModlogCombinedQuery {
|
||||
pub async fn list(self, pool: &mut DbPool<'_>) -> LemmyResult<Vec<ModlogCombinedView>> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
|
||||
let mod_person = self.mod_person_id.unwrap_or(PersonId(-1));
|
||||
let show_mod_names = !(self.hide_modlog_names.unwrap_or_default());
|
||||
let show_mod_names_expr = show_mod_names.as_sql::<diesel::sql_types::Bool>();
|
||||
|
||||
// The modded / other person
|
||||
let other_person = aliases::person1.field(person::id);
|
||||
|
||||
// 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.eq(mod_person)).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 comment_join = 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 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 instance_join = admin_allow_instance::instance_id
|
||||
.eq(instance::id)
|
||||
.or(admin_block_instance::instance_id.eq(instance::id));
|
||||
|
||||
let mut query = modlog_combined::table
|
||||
.left_join(admin_allow_instance::table)
|
||||
.left_join(admin_block_instance::table)
|
||||
.left_join(admin_purge_comment::table)
|
||||
.left_join(admin_purge_community::table)
|
||||
.left_join(admin_purge_person::table)
|
||||
.left_join(admin_purge_post::table)
|
||||
.left_join(mod_add::table)
|
||||
.left_join(mod_add_community::table)
|
||||
.left_join(mod_ban::table)
|
||||
.left_join(mod_ban_from_community::table)
|
||||
.left_join(mod_feature_post::table)
|
||||
.left_join(mod_hide_community::table)
|
||||
.left_join(mod_lock_post::table)
|
||||
.left_join(mod_remove_comment::table)
|
||||
.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))
|
||||
.select((
|
||||
admin_allow_instance::all_columns.nullable(),
|
||||
admin_block_instance::all_columns.nullable(),
|
||||
admin_purge_comment::all_columns.nullable(),
|
||||
admin_purge_community::all_columns.nullable(),
|
||||
admin_purge_person::all_columns.nullable(),
|
||||
admin_purge_post::all_columns.nullable(),
|
||||
mod_add::all_columns.nullable(),
|
||||
mod_add_community::all_columns.nullable(),
|
||||
mod_ban::all_columns.nullable(),
|
||||
mod_ban_from_community::all_columns.nullable(),
|
||||
mod_feature_post::all_columns.nullable(),
|
||||
mod_hide_community::all_columns.nullable(),
|
||||
mod_lock_post::all_columns.nullable(),
|
||||
mod_remove_comment::all_columns.nullable(),
|
||||
mod_remove_community::all_columns.nullable(),
|
||||
mod_remove_post::all_columns.nullable(),
|
||||
mod_transfer_community::all_columns.nullable(),
|
||||
// Shared
|
||||
person::all_columns.nullable(),
|
||||
aliases::person1.fields(person::all_columns).nullable(),
|
||||
instance::all_columns.nullable(),
|
||||
community::all_columns.nullable(),
|
||||
post::all_columns.nullable(),
|
||||
comment::all_columns.nullable(),
|
||||
))
|
||||
let mut query = ModlogCombinedViewInternal::joins(self.mod_person_id, self.hide_modlog_names)
|
||||
.select(ModlogCombinedViewInternal::as_select())
|
||||
.into_boxed();
|
||||
|
||||
if let Some(mod_person_id) = self.mod_person_id {
|
||||
|
|
|
@ -31,6 +31,7 @@ use lemmy_db_schema::{
|
|||
person,
|
||||
person_actions,
|
||||
person_content_combined,
|
||||
person_saved_combined,
|
||||
post,
|
||||
post_actions,
|
||||
post_aggregates,
|
||||
|
@ -42,11 +43,197 @@ use lemmy_db_schema::{
|
|||
community::CommunityFollower,
|
||||
},
|
||||
traits::InternalToCombinedView,
|
||||
utils::{actions, actions_alias, functions::coalesce, get_conn, DbPool},
|
||||
utils::{functions::coalesce, get_conn, DbPool},
|
||||
PersonContentType,
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
impl PersonContentCombinedViewInternal {
|
||||
#[diesel::dsl::auto_type(no_type_alias)]
|
||||
fn joins(my_person_id: Option<PersonId>) -> _ {
|
||||
let item_creator = person::id;
|
||||
|
||||
let comment_join =
|
||||
comment::table.on(person_content_combined::comment_id.eq(comment::id.nullable()));
|
||||
|
||||
let post_join = post::table.on(
|
||||
person_content_combined::post_id
|
||||
.eq(post::id.nullable())
|
||||
.or(comment::post_id.eq(post::id)),
|
||||
);
|
||||
|
||||
let item_creator_join = person::table.on(
|
||||
comment::creator_id
|
||||
.eq(item_creator)
|
||||
// Need to filter out the post rows where the post_id given is null
|
||||
// Otherwise you'll get duped post rows
|
||||
.or(
|
||||
post::creator_id
|
||||
.eq(item_creator)
|
||||
.and(person_content_combined::post_id.is_not_null()),
|
||||
),
|
||||
);
|
||||
|
||||
let community_join = community::table.on(post::community_id.eq(community::id));
|
||||
|
||||
let creator_community_actions_join = creator_community_actions.on(
|
||||
creator_community_actions
|
||||
.field(community_actions::community_id)
|
||||
.eq(post::community_id)
|
||||
.and(
|
||||
creator_community_actions
|
||||
.field(community_actions::person_id)
|
||||
.eq(item_creator),
|
||||
),
|
||||
);
|
||||
|
||||
let local_user_join = local_user::table.on(
|
||||
item_creator
|
||||
.eq(local_user::person_id)
|
||||
.and(local_user::admin.eq(true)),
|
||||
);
|
||||
|
||||
let community_actions_join = community_actions::table.on(
|
||||
community_actions::community_id
|
||||
.eq(post::community_id)
|
||||
.and(community_actions::person_id.nullable().eq(my_person_id)),
|
||||
);
|
||||
|
||||
let post_actions_join = post_actions::table.on(
|
||||
post_actions::post_id
|
||||
.eq(post::id)
|
||||
.and(post_actions::person_id.nullable().eq(my_person_id)),
|
||||
);
|
||||
|
||||
let person_actions_join = person_actions::table.on(
|
||||
person_actions::target_id
|
||||
.eq(item_creator)
|
||||
.and(person_actions::person_id.nullable().eq(my_person_id)),
|
||||
);
|
||||
|
||||
let comment_actions_join = comment_actions::table.on(
|
||||
comment_actions::comment_id
|
||||
.eq(comment::id)
|
||||
.and(comment_actions::person_id.nullable().eq(my_person_id)),
|
||||
);
|
||||
|
||||
let post_aggregates_join = post_aggregates::table.on(post::id.eq(post_aggregates::post_id));
|
||||
|
||||
let comment_aggregates_join = comment_aggregates::table
|
||||
.on(person_content_combined::comment_id.eq(comment_aggregates::comment_id.nullable()));
|
||||
|
||||
let image_details_join =
|
||||
image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable()));
|
||||
|
||||
person_content_combined::table
|
||||
.left_join(comment_join)
|
||||
.inner_join(post_join)
|
||||
.inner_join(item_creator_join)
|
||||
.inner_join(community_join)
|
||||
.left_join(creator_community_actions_join)
|
||||
.left_join(local_user_join)
|
||||
.left_join(community_actions_join)
|
||||
.left_join(post_actions_join)
|
||||
.left_join(person_actions_join)
|
||||
.inner_join(post_aggregates_join)
|
||||
.left_join(comment_aggregates_join)
|
||||
.left_join(comment_actions_join)
|
||||
.left_join(image_details_join)
|
||||
}
|
||||
|
||||
#[diesel::dsl::auto_type(no_type_alias)]
|
||||
pub(crate) fn joins_saved(my_person_id: PersonId) -> _ {
|
||||
let item_creator = person::id;
|
||||
|
||||
let comment_join =
|
||||
comment::table.on(person_saved_combined::comment_id.eq(comment::id.nullable()));
|
||||
|
||||
let post_join = post::table.on(
|
||||
person_saved_combined::post_id
|
||||
.eq(post::id.nullable())
|
||||
.or(comment::post_id.eq(post::id)),
|
||||
);
|
||||
|
||||
let item_creator_join = person::table.on(
|
||||
comment::creator_id
|
||||
.eq(item_creator)
|
||||
// Need to filter out the post rows where the post_id given is null
|
||||
// Otherwise you'll get duped post rows
|
||||
.or(
|
||||
post::creator_id
|
||||
.eq(item_creator)
|
||||
.and(person_saved_combined::post_id.is_not_null()),
|
||||
),
|
||||
);
|
||||
|
||||
let community_join = community::table.on(post::community_id.eq(community::id));
|
||||
|
||||
let creator_community_actions_join = creator_community_actions.on(
|
||||
creator_community_actions
|
||||
.field(community_actions::community_id)
|
||||
.eq(post::community_id)
|
||||
.and(
|
||||
creator_community_actions
|
||||
.field(community_actions::person_id)
|
||||
.eq(item_creator),
|
||||
),
|
||||
);
|
||||
|
||||
let local_user_join = local_user::table.on(
|
||||
item_creator
|
||||
.eq(local_user::person_id)
|
||||
.and(local_user::admin.eq(true)),
|
||||
);
|
||||
|
||||
let community_actions_join = community_actions::table.on(
|
||||
community_actions::community_id
|
||||
.eq(post::community_id)
|
||||
.and(community_actions::person_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
let post_actions_join = post_actions::table.on(
|
||||
post_actions::post_id
|
||||
.eq(post::id)
|
||||
.and(post_actions::person_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
let person_actions_join = person_actions::table.on(
|
||||
person_actions::target_id
|
||||
.eq(item_creator)
|
||||
.and(person_actions::person_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
let comment_actions_join = comment_actions::table.on(
|
||||
comment_actions::comment_id
|
||||
.eq(comment::id)
|
||||
.and(comment_actions::person_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
let post_aggregates_join = post_aggregates::table.on(post::id.eq(post_aggregates::post_id));
|
||||
|
||||
let comment_aggregates_join = comment_aggregates::table
|
||||
.on(person_saved_combined::comment_id.eq(comment_aggregates::comment_id.nullable()));
|
||||
|
||||
let image_details_join =
|
||||
image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable()));
|
||||
|
||||
person_saved_combined::table
|
||||
.left_join(comment_join)
|
||||
.inner_join(post_join)
|
||||
.inner_join(item_creator_join)
|
||||
.inner_join(community_join)
|
||||
.left_join(creator_community_actions_join)
|
||||
.left_join(local_user_join)
|
||||
.left_join(community_actions_join)
|
||||
.left_join(post_actions_join)
|
||||
.left_join(person_actions_join)
|
||||
.inner_join(post_aggregates_join)
|
||||
.left_join(comment_aggregates_join)
|
||||
.left_join(comment_actions_join)
|
||||
.left_join(image_details_join)
|
||||
}
|
||||
}
|
||||
|
||||
impl PersonContentCombinedPaginationCursor {
|
||||
// get cursor for page that starts immediately after the given post
|
||||
pub fn after_post(view: &PersonContentCombinedView) -> PersonContentCombinedPaginationCursor {
|
||||
|
@ -115,61 +302,7 @@ impl PersonContentCombinedQuery {
|
|||
// For example, the creator must be the person table joined to either:
|
||||
// - post.creator_id
|
||||
// - comment.creator_id
|
||||
let query = person_content_combined::table
|
||||
// The comment
|
||||
.left_join(comment::table.on(person_content_combined::comment_id.eq(comment::id.nullable())))
|
||||
// The post
|
||||
// It gets a bit complicated here, because since both comments and post combined have a post
|
||||
// attached, you can do an inner join.
|
||||
.inner_join(
|
||||
post::table.on(
|
||||
person_content_combined::post_id
|
||||
.eq(post::id.nullable())
|
||||
.or(comment::post_id.eq(post::id)),
|
||||
),
|
||||
)
|
||||
// The item creator
|
||||
.inner_join(
|
||||
person::table.on(
|
||||
comment::creator_id
|
||||
.eq(item_creator)
|
||||
// Need to filter out the post rows where the post_id given is null
|
||||
// Otherwise you'll get duped post rows
|
||||
.or(
|
||||
post::creator_id
|
||||
.eq(item_creator)
|
||||
.and(person_content_combined::post_id.is_not_null()),
|
||||
),
|
||||
),
|
||||
)
|
||||
// The community
|
||||
.inner_join(community::table.on(post::community_id.eq(community::id)))
|
||||
.left_join(actions_alias(
|
||||
creator_community_actions,
|
||||
item_creator,
|
||||
post::community_id,
|
||||
))
|
||||
.left_join(
|
||||
local_user::table.on(
|
||||
item_creator
|
||||
.eq(local_user::person_id)
|
||||
.and(local_user::admin.eq(true)),
|
||||
),
|
||||
)
|
||||
.left_join(actions(
|
||||
community_actions::table,
|
||||
my_person_id,
|
||||
post::community_id,
|
||||
))
|
||||
.left_join(actions(post_actions::table, my_person_id, post::id))
|
||||
.left_join(actions(person_actions::table, my_person_id, item_creator))
|
||||
.inner_join(post_aggregates::table.on(post::id.eq(post_aggregates::post_id)))
|
||||
.left_join(
|
||||
comment_aggregates::table
|
||||
.on(person_content_combined::comment_id.eq(comment_aggregates::comment_id.nullable())),
|
||||
)
|
||||
.left_join(actions(comment_actions::table, my_person_id, comment::id))
|
||||
.left_join(image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable())))
|
||||
let mut query = PersonContentCombinedViewInternal::joins(my_person_id)
|
||||
// The creator id filter
|
||||
.filter(item_creator.eq(self.creator_id))
|
||||
.select((
|
||||
|
@ -209,8 +342,6 @@ impl PersonContentCombinedQuery {
|
|||
))
|
||||
.into_boxed();
|
||||
|
||||
let mut query = PaginatedQueryBuilder::new(query);
|
||||
|
||||
if let Some(type_) = self.type_ {
|
||||
query = match type_ {
|
||||
PersonContentType::All => query,
|
||||
|
@ -221,6 +352,8 @@ impl PersonContentCombinedQuery {
|
|||
}
|
||||
}
|
||||
|
||||
let mut query = PaginatedQueryBuilder::new(query);
|
||||
|
||||
let page_after = self.page_after.map(|c| c.0);
|
||||
|
||||
if self.page_back.unwrap_or_default() {
|
||||
|
|
|
@ -6,9 +6,7 @@ use crate::structs::{
|
|||
};
|
||||
use diesel::{
|
||||
result::Error,
|
||||
BoolExpressionMethods,
|
||||
ExpressionMethods,
|
||||
JoinOnDsl,
|
||||
NullableExpressionMethods,
|
||||
QueryDsl,
|
||||
SelectableHelper,
|
||||
|
@ -39,7 +37,7 @@ use lemmy_db_schema::{
|
|||
community::CommunityFollower,
|
||||
},
|
||||
traits::InternalToCombinedView,
|
||||
utils::{actions, actions_alias, functions::coalesce, get_conn, DbPool},
|
||||
utils::{functions::coalesce, get_conn, DbPool},
|
||||
PersonContentType,
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
@ -90,7 +88,6 @@ impl PersonSavedCombinedQuery {
|
|||
user: &LocalUserView,
|
||||
) -> LemmyResult<Vec<PersonContentCombinedView>> {
|
||||
let my_person_id = user.local_user.person_id;
|
||||
let item_creator = person::id;
|
||||
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
|
||||
|
@ -103,75 +100,7 @@ impl PersonSavedCombinedQuery {
|
|||
.filter(tag::deleted.eq(false))
|
||||
.single_value();
|
||||
|
||||
// Notes: since the post_id and comment_id are optional columns,
|
||||
// many joins must use an OR condition.
|
||||
// For example, the creator must be the person table joined to either:
|
||||
// - post.creator_id
|
||||
// - comment.creator_id
|
||||
let query = person_saved_combined::table
|
||||
// The comment
|
||||
.left_join(comment::table.on(person_saved_combined::comment_id.eq(comment::id.nullable())))
|
||||
// The post
|
||||
// It gets a bit complicated here, because since both comments and post combined have a post
|
||||
// attached, you can do an inner join.
|
||||
.inner_join(
|
||||
post::table.on(
|
||||
person_saved_combined::post_id
|
||||
.eq(post::id.nullable())
|
||||
.or(comment::post_id.eq(post::id)),
|
||||
),
|
||||
)
|
||||
// The item creator
|
||||
.inner_join(
|
||||
person::table.on(
|
||||
comment::creator_id
|
||||
.eq(item_creator)
|
||||
// Need to filter out the post rows where the post_id given is null
|
||||
// Otherwise you'll get duped post rows
|
||||
.or(
|
||||
post::creator_id
|
||||
.eq(item_creator)
|
||||
.and(person_saved_combined::post_id.is_not_null()),
|
||||
),
|
||||
),
|
||||
)
|
||||
// The community
|
||||
.inner_join(community::table.on(post::community_id.eq(community::id)))
|
||||
.left_join(actions_alias(
|
||||
creator_community_actions,
|
||||
item_creator,
|
||||
post::community_id,
|
||||
))
|
||||
.left_join(
|
||||
local_user::table.on(
|
||||
item_creator
|
||||
.eq(local_user::person_id)
|
||||
.and(local_user::admin.eq(true)),
|
||||
),
|
||||
)
|
||||
.left_join(actions(
|
||||
community_actions::table,
|
||||
Some(my_person_id),
|
||||
post::community_id,
|
||||
))
|
||||
.left_join(actions(post_actions::table, Some(my_person_id), post::id))
|
||||
.left_join(actions(
|
||||
person_actions::table,
|
||||
Some(my_person_id),
|
||||
item_creator,
|
||||
))
|
||||
.inner_join(post_aggregates::table.on(post::id.eq(post_aggregates::post_id)))
|
||||
.left_join(
|
||||
comment_aggregates::table
|
||||
.on(person_saved_combined::comment_id.eq(comment_aggregates::comment_id.nullable())),
|
||||
)
|
||||
.left_join(actions(
|
||||
comment_actions::table,
|
||||
Some(my_person_id),
|
||||
comment::id,
|
||||
))
|
||||
.left_join(image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable())))
|
||||
// The person id filter
|
||||
let mut query = PersonContentCombinedViewInternal::joins_saved(my_person_id)
|
||||
.filter(person_saved_combined::person_id.eq(my_person_id))
|
||||
.select((
|
||||
// Post-specific
|
||||
|
@ -210,8 +139,6 @@ impl PersonSavedCombinedQuery {
|
|||
))
|
||||
.into_boxed();
|
||||
|
||||
let mut query = PaginatedQueryBuilder::new(query);
|
||||
|
||||
if let Some(type_) = self.type_ {
|
||||
query = match type_ {
|
||||
PersonContentType::All => query,
|
||||
|
@ -222,6 +149,8 @@ impl PersonSavedCombinedQuery {
|
|||
}
|
||||
}
|
||||
|
||||
let mut query = PaginatedQueryBuilder::new(query);
|
||||
|
||||
let page_after = self.page_after.map(|c| c.0);
|
||||
|
||||
if self.page_back.unwrap_or_default() {
|
||||
|
|
|
@ -22,7 +22,7 @@ use diesel_async::RunQueryDsl;
|
|||
use i_love_jesus::PaginatedQueryBuilder;
|
||||
use lemmy_db_schema::{
|
||||
aliases::{self, creator_community_actions},
|
||||
newtypes::{CommunityId, PostId},
|
||||
newtypes::{CommunityId, PersonId, PostId},
|
||||
schema::{
|
||||
comment,
|
||||
comment_actions,
|
||||
|
@ -48,12 +48,130 @@ use lemmy_db_schema::{
|
|||
community::CommunityFollower,
|
||||
},
|
||||
traits::InternalToCombinedView,
|
||||
utils::{actions, actions_alias, functions::coalesce, get_conn, DbPool, ReverseTimestampKey},
|
||||
utils::{functions::coalesce, get_conn, DbPool, ReverseTimestampKey},
|
||||
ReportType,
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
impl ReportCombinedViewInternal {
|
||||
#[diesel::dsl::auto_type(no_type_alias)]
|
||||
fn joins(my_person_id: PersonId) -> _ {
|
||||
let report_creator = person::id;
|
||||
let item_creator = aliases::person1.field(person::id);
|
||||
let resolver = aliases::person2.field(person::id).nullable();
|
||||
|
||||
let comment_join = comment::table.on(comment_report::comment_id.eq(comment::id));
|
||||
let private_message_join =
|
||||
private_message::table.on(private_message_report::private_message_id.eq(private_message::id));
|
||||
|
||||
let post_join = post::table.on(
|
||||
post_report::post_id
|
||||
.eq(post::id)
|
||||
.or(comment::post_id.eq(post::id)),
|
||||
);
|
||||
|
||||
let community_actions_join = community_actions::table.on(
|
||||
community_actions::community_id
|
||||
.eq(community::id)
|
||||
.and(community_actions::person_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
let report_creator_join = person::table.on(
|
||||
post_report::creator_id
|
||||
.eq(report_creator)
|
||||
.or(comment_report::creator_id.eq(report_creator))
|
||||
.or(private_message_report::creator_id.eq(report_creator))
|
||||
.or(community_report::creator_id.eq(report_creator)),
|
||||
);
|
||||
|
||||
let item_creator_join = aliases::person1.on(
|
||||
post::creator_id
|
||||
.eq(item_creator)
|
||||
.or(comment::creator_id.eq(item_creator))
|
||||
.or(private_message::creator_id.eq(item_creator)),
|
||||
);
|
||||
|
||||
let resolver_join = aliases::person2.on(
|
||||
private_message_report::resolver_id
|
||||
.eq(resolver)
|
||||
.or(post_report::resolver_id.eq(resolver))
|
||||
.or(comment_report::resolver_id.eq(resolver))
|
||||
.or(community_report::resolver_id.eq(resolver)),
|
||||
);
|
||||
|
||||
let community_join = community::table.on(
|
||||
community_report::community_id
|
||||
.eq(community::id)
|
||||
.or(post::community_id.eq(community::id)),
|
||||
);
|
||||
|
||||
let local_user_join = local_user::table.on(
|
||||
item_creator
|
||||
.eq(local_user::person_id)
|
||||
.and(local_user::admin.eq(true)),
|
||||
);
|
||||
|
||||
let creator_community_actions_join = creator_community_actions.on(
|
||||
creator_community_actions
|
||||
.field(community_actions::community_id)
|
||||
.eq(post::community_id)
|
||||
.and(
|
||||
creator_community_actions
|
||||
.field(community_actions::person_id)
|
||||
.eq(item_creator),
|
||||
),
|
||||
);
|
||||
|
||||
let post_actions_join = post_actions::table.on(
|
||||
post_actions::post_id
|
||||
.eq(post::id)
|
||||
.and(post_actions::person_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
let person_actions_join = person_actions::table.on(
|
||||
person_actions::target_id
|
||||
.eq(item_creator)
|
||||
.and(person_actions::person_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
let comment_actions_join = comment_actions::table.on(
|
||||
comment_actions::comment_id
|
||||
.eq(comment::id)
|
||||
.and(comment_actions::person_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
let post_aggregates_join =
|
||||
post_aggregates::table.on(post_report::post_id.eq(post_aggregates::post_id));
|
||||
|
||||
let comment_aggregates_join =
|
||||
comment_aggregates::table.on(comment_report::comment_id.eq(comment_aggregates::comment_id));
|
||||
|
||||
let community_aggregates_join = community_aggregates::table
|
||||
.on(community_report::community_id.eq(community_aggregates::community_id));
|
||||
|
||||
report_combined::table
|
||||
.left_join(post_report::table)
|
||||
.left_join(comment_report::table)
|
||||
.left_join(private_message_report::table)
|
||||
.left_join(community_report::table)
|
||||
.inner_join(report_creator_join)
|
||||
.left_join(comment_join)
|
||||
.left_join(private_message_join)
|
||||
.left_join(post_join)
|
||||
.left_join(item_creator_join)
|
||||
.left_join(resolver_join)
|
||||
.left_join(community_join)
|
||||
.left_join(creator_community_actions_join)
|
||||
.left_join(local_user_join)
|
||||
.left_join(community_actions_join)
|
||||
.left_join(post_actions_join)
|
||||
.left_join(person_actions_join)
|
||||
.left_join(post_aggregates_join)
|
||||
.left_join(comment_aggregates_join)
|
||||
.left_join(community_aggregates_join)
|
||||
.left_join(comment_actions_join)
|
||||
}
|
||||
|
||||
/// returns the current unresolved report count for the communities you mod
|
||||
pub async fn get_report_count(
|
||||
pool: &mut DbPool<'_>,
|
||||
|
@ -65,27 +183,7 @@ impl ReportCombinedViewInternal {
|
|||
let conn = &mut get_conn(pool).await?;
|
||||
let my_person_id = user.local_user.person_id;
|
||||
|
||||
let mut query = report_combined::table
|
||||
.left_join(post_report::table)
|
||||
.left_join(comment_report::table)
|
||||
.left_join(private_message_report::table)
|
||||
.left_join(community_report::table)
|
||||
// Need to join to comment and post to get the community
|
||||
.left_join(comment::table.on(comment_report::comment_id.eq(comment::id)))
|
||||
// The post
|
||||
.left_join(
|
||||
post::table.on(
|
||||
post_report::post_id
|
||||
.eq(post::id)
|
||||
.or(comment::post_id.eq(post::id)),
|
||||
),
|
||||
)
|
||||
.left_join(community::table.on(post::community_id.eq(community::id)))
|
||||
.left_join(actions(
|
||||
community_actions::table,
|
||||
Some(my_person_id),
|
||||
post::community_id,
|
||||
))
|
||||
let mut query = Self::joins(my_person_id)
|
||||
.filter(
|
||||
post_report::resolved
|
||||
.or(comment_report::resolved)
|
||||
|
@ -93,21 +191,27 @@ impl ReportCombinedViewInternal {
|
|||
.or(community_report::resolved)
|
||||
.is_distinct_from(true),
|
||||
)
|
||||
.select(count(report_combined::id))
|
||||
.into_boxed();
|
||||
|
||||
if let Some(community_id) = community_id {
|
||||
query = query.filter(post::community_id.eq(community_id))
|
||||
query = query.filter(
|
||||
community::id
|
||||
.eq(community_id)
|
||||
.and(report_combined::community_report_id.is_null()),
|
||||
);
|
||||
}
|
||||
|
||||
// If its not an admin, get only the ones you mod
|
||||
if !user.local_user.admin {
|
||||
query = query.filter(community_actions::became_moderator.is_not_null());
|
||||
query = query.filter(
|
||||
community_actions::became_moderator
|
||||
.is_not_null()
|
||||
.and(report_combined::community_report_id.is_null()),
|
||||
);
|
||||
}
|
||||
|
||||
query
|
||||
.select(count(report_combined::id))
|
||||
.first::<i64>(conn)
|
||||
.await
|
||||
query.first::<i64>(conn).await
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -164,102 +268,9 @@ impl ReportCombinedQuery {
|
|||
user: &LocalUserView,
|
||||
) -> LemmyResult<Vec<ReportCombinedView>> {
|
||||
let my_person_id = user.local_user.person_id;
|
||||
let report_creator = person::id;
|
||||
let item_creator = aliases::person1.field(person::id);
|
||||
let resolver = aliases::person2.field(person::id).nullable();
|
||||
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
|
||||
let report_creator_join = post_report::creator_id
|
||||
.eq(report_creator)
|
||||
.or(comment_report::creator_id.eq(report_creator))
|
||||
.or(private_message_report::creator_id.eq(report_creator))
|
||||
.or(community_report::creator_id.eq(report_creator));
|
||||
|
||||
let item_creator_join = post::creator_id
|
||||
.eq(item_creator)
|
||||
.or(comment::creator_id.eq(item_creator))
|
||||
.or(private_message::creator_id.eq(item_creator));
|
||||
|
||||
let resolver_join = private_message_report::resolver_id
|
||||
.eq(resolver)
|
||||
.or(post_report::resolver_id.eq(resolver))
|
||||
.or(comment_report::resolver_id.eq(resolver))
|
||||
.or(community_report::resolver_id.eq(resolver));
|
||||
|
||||
let post_join = post_report::post_id
|
||||
.eq(post::id)
|
||||
.or(comment::post_id.eq(post::id));
|
||||
|
||||
let community_join = community::table.on(
|
||||
community_report::community_id
|
||||
.eq(community::id)
|
||||
.or(post::community_id.eq(community::id)),
|
||||
);
|
||||
|
||||
// Notes: since the post_report_id and comment_report_id are optional columns,
|
||||
// many joins must use an OR condition.
|
||||
// For example, the report creator must be the person table joined to either:
|
||||
// - post_report.creator_id
|
||||
// - comment_report.creator_id
|
||||
let mut query = report_combined::table
|
||||
.left_join(post_report::table)
|
||||
.left_join(comment_report::table)
|
||||
.left_join(private_message_report::table)
|
||||
.left_join(community_report::table)
|
||||
// The report creator
|
||||
.inner_join(person::table.on(report_creator_join))
|
||||
// The comment
|
||||
.left_join(comment::table.on(comment_report::comment_id.eq(comment::id)))
|
||||
// The private message
|
||||
.left_join(
|
||||
private_message::table
|
||||
.on(private_message_report::private_message_id.eq(private_message::id)),
|
||||
)
|
||||
// The post
|
||||
.left_join(post::table.on(post_join))
|
||||
// The item creator (`item_creator` is the id of this person)
|
||||
.left_join(aliases::person1.on(item_creator_join))
|
||||
// The resolver
|
||||
.left_join(aliases::person2.on(resolver_join))
|
||||
// The community
|
||||
.left_join(community_join)
|
||||
.left_join(actions_alias(
|
||||
creator_community_actions,
|
||||
item_creator,
|
||||
post::community_id,
|
||||
))
|
||||
.left_join(
|
||||
local_user::table.on(
|
||||
item_creator
|
||||
.eq(local_user::person_id)
|
||||
.and(local_user::admin.eq(true)),
|
||||
),
|
||||
)
|
||||
.left_join(actions(
|
||||
community_actions::table,
|
||||
Some(my_person_id),
|
||||
community::id,
|
||||
))
|
||||
.left_join(actions(post_actions::table, Some(my_person_id), post::id))
|
||||
.left_join(actions(
|
||||
person_actions::table,
|
||||
Some(my_person_id),
|
||||
item_creator,
|
||||
))
|
||||
.left_join(post_aggregates::table.on(post_report::post_id.eq(post_aggregates::post_id)))
|
||||
.left_join(
|
||||
comment_aggregates::table.on(comment_report::comment_id.eq(comment_aggregates::comment_id)),
|
||||
)
|
||||
.left_join(
|
||||
community_aggregates::table
|
||||
.on(community_report::community_id.eq(community_aggregates::community_id)),
|
||||
)
|
||||
.left_join(actions(
|
||||
comment_actions::table,
|
||||
Some(my_person_id),
|
||||
comment_report::comment_id,
|
||||
))
|
||||
let mut query = ReportCombinedViewInternal::joins(my_person_id)
|
||||
.select((
|
||||
// Post-specific
|
||||
post_report::all_columns.nullable(),
|
||||
|
|
|
@ -48,15 +48,7 @@ use lemmy_db_schema::{
|
|||
community::CommunityFollower,
|
||||
},
|
||||
traits::InternalToCombinedView,
|
||||
utils::{
|
||||
actions,
|
||||
actions_alias,
|
||||
functions::coalesce,
|
||||
fuzzy_search,
|
||||
get_conn,
|
||||
DbPool,
|
||||
ReverseTimestampKey,
|
||||
},
|
||||
utils::{functions::coalesce, fuzzy_search, get_conn, DbPool, ReverseTimestampKey},
|
||||
ListingType,
|
||||
SearchSortType,
|
||||
SearchType,
|
||||
|
@ -64,6 +56,123 @@ use lemmy_db_schema::{
|
|||
use lemmy_utils::error::LemmyResult;
|
||||
use SearchSortType::*;
|
||||
|
||||
impl SearchCombinedViewInternal {
|
||||
#[diesel::dsl::auto_type(no_type_alias)]
|
||||
fn joins(my_person_id: Option<PersonId>) -> _ {
|
||||
let item_creator = person::id;
|
||||
|
||||
let item_creator_join = person::table.on(
|
||||
search_combined::person_id
|
||||
.eq(item_creator.nullable())
|
||||
.or(
|
||||
search_combined::comment_id
|
||||
.is_not_null()
|
||||
.and(comment::creator_id.eq(item_creator)),
|
||||
)
|
||||
.or(
|
||||
search_combined::post_id
|
||||
.is_not_null()
|
||||
.and(post::creator_id.eq(item_creator)),
|
||||
)
|
||||
.and(not(person::deleted)),
|
||||
);
|
||||
|
||||
let comment_join = comment::table.on(
|
||||
search_combined::comment_id
|
||||
.eq(comment::id.nullable())
|
||||
.and(not(comment::removed))
|
||||
.and(not(comment::deleted)),
|
||||
);
|
||||
|
||||
let post_join = post::table.on(
|
||||
search_combined::post_id
|
||||
.eq(post::id.nullable())
|
||||
.or(comment::post_id.eq(post::id))
|
||||
.and(not(post::removed))
|
||||
.and(not(post::deleted)),
|
||||
);
|
||||
|
||||
let community_join = community::table.on(
|
||||
search_combined::community_id
|
||||
.eq(community::id.nullable())
|
||||
.or(post::community_id.eq(community::id))
|
||||
.and(not(community::removed))
|
||||
.and(not(community::deleted)),
|
||||
);
|
||||
|
||||
let 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(item_creator),
|
||||
),
|
||||
);
|
||||
let local_user_join = local_user::table.on(
|
||||
item_creator
|
||||
.eq(local_user::person_id)
|
||||
.and(local_user::admin.eq(true)),
|
||||
);
|
||||
|
||||
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)),
|
||||
);
|
||||
|
||||
let post_actions_join = post_actions::table.on(
|
||||
post_actions::post_id
|
||||
.eq(post::id)
|
||||
.and(post_actions::person_id.nullable().eq(my_person_id)),
|
||||
);
|
||||
|
||||
let person_actions_join = person_actions::table.on(
|
||||
person_actions::target_id
|
||||
.eq(item_creator)
|
||||
.and(person_actions::person_id.nullable().eq(my_person_id)),
|
||||
);
|
||||
|
||||
let comment_actions_join = comment_actions::table.on(
|
||||
comment_actions::comment_id
|
||||
.eq(comment::id)
|
||||
.and(comment_actions::person_id.nullable().eq(my_person_id)),
|
||||
);
|
||||
|
||||
let post_aggregates_join = post_aggregates::table.on(post::id.eq(post_aggregates::post_id));
|
||||
|
||||
let comment_aggregates_join = comment_aggregates::table
|
||||
.on(search_combined::comment_id.eq(comment_aggregates::comment_id.nullable()));
|
||||
|
||||
let community_aggregates_join = community_aggregates::table
|
||||
.on(search_combined::community_id.eq(community_aggregates::community_id.nullable()));
|
||||
|
||||
let image_details_join =
|
||||
image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable()));
|
||||
|
||||
let person_aggregates_join = person_aggregates::table
|
||||
.on(search_combined::person_id.eq(person_aggregates::person_id.nullable()));
|
||||
|
||||
search_combined::table
|
||||
.left_join(comment_join)
|
||||
.left_join(post_join)
|
||||
.left_join(item_creator_join)
|
||||
.left_join(community_join)
|
||||
.left_join(creator_community_actions_join)
|
||||
.left_join(local_user_join)
|
||||
.left_join(community_actions_join)
|
||||
.left_join(post_actions_join)
|
||||
.left_join(person_actions_join)
|
||||
.left_join(person_aggregates_join)
|
||||
.left_join(post_aggregates_join)
|
||||
.left_join(comment_aggregates_join)
|
||||
.left_join(community_aggregates_join)
|
||||
.left_join(comment_actions_join)
|
||||
.left_join(image_details_join)
|
||||
}
|
||||
}
|
||||
|
||||
impl SearchCombinedPaginationCursor {
|
||||
// get cursor for page that starts immediately after the given post
|
||||
pub fn after_post(view: &SearchCombinedView) -> SearchCombinedPaginationCursor {
|
||||
|
@ -136,85 +245,7 @@ impl SearchCombinedQuery {
|
|||
.filter(tag::deleted.eq(false))
|
||||
.single_value();
|
||||
|
||||
let item_creator_join = search_combined::person_id
|
||||
.eq(item_creator.nullable())
|
||||
.or(
|
||||
search_combined::comment_id
|
||||
.is_not_null()
|
||||
.and(comment::creator_id.eq(item_creator)),
|
||||
)
|
||||
.or(
|
||||
search_combined::post_id
|
||||
.is_not_null()
|
||||
.and(post::creator_id.eq(item_creator)),
|
||||
)
|
||||
.and(not(person::deleted));
|
||||
|
||||
let comment_join = search_combined::comment_id
|
||||
.eq(comment::id.nullable())
|
||||
.and(not(comment::removed))
|
||||
.and(not(comment::deleted));
|
||||
|
||||
let post_join = search_combined::post_id
|
||||
.eq(post::id.nullable())
|
||||
.or(comment::post_id.eq(post::id))
|
||||
.and(not(post::removed))
|
||||
.and(not(post::deleted));
|
||||
|
||||
let community_join = search_combined::community_id
|
||||
.eq(community::id.nullable())
|
||||
.or(post::community_id.eq(community::id))
|
||||
.and(not(community::removed))
|
||||
.and(not(community::deleted));
|
||||
|
||||
// Notes: since the post_id and comment_id are optional columns,
|
||||
// many joins must use an OR condition.
|
||||
// For example, the creator must be the person table joined to either:
|
||||
// - post.creator_id
|
||||
// - comment.creator_id
|
||||
let mut query = search_combined::table
|
||||
// The comment
|
||||
.left_join(comment::table.on(comment_join))
|
||||
// The post
|
||||
.left_join(post::table.on(post_join))
|
||||
// The item creator
|
||||
.left_join(person::table.on(item_creator_join))
|
||||
// The community
|
||||
.left_join(community::table.on(community_join))
|
||||
.left_join(actions_alias(
|
||||
creator_community_actions,
|
||||
item_creator,
|
||||
community::id,
|
||||
))
|
||||
.left_join(
|
||||
local_user::table.on(
|
||||
item_creator
|
||||
.eq(local_user::person_id)
|
||||
.and(local_user::admin.eq(true)),
|
||||
),
|
||||
)
|
||||
.left_join(actions(
|
||||
community_actions::table,
|
||||
my_person_id,
|
||||
community::id,
|
||||
))
|
||||
.left_join(actions(post_actions::table, my_person_id, post::id))
|
||||
.left_join(actions(person_actions::table, my_person_id, item_creator))
|
||||
.left_join(
|
||||
person_aggregates::table
|
||||
.on(search_combined::person_id.eq(person_aggregates::person_id.nullable())),
|
||||
)
|
||||
.left_join(post_aggregates::table.on(post::id.eq(post_aggregates::post_id)))
|
||||
.left_join(
|
||||
comment_aggregates::table
|
||||
.on(search_combined::comment_id.eq(comment_aggregates::comment_id.nullable())),
|
||||
)
|
||||
.left_join(
|
||||
community_aggregates::table
|
||||
.on(search_combined::community_id.eq(community_aggregates::community_id.nullable())),
|
||||
)
|
||||
.left_join(actions(comment_actions::table, my_person_id, comment::id))
|
||||
.left_join(image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable())))
|
||||
let mut query = SearchCombinedViewInternal::joins(my_person_id)
|
||||
.select((
|
||||
// Post-specific
|
||||
post::all_columns.nullable(),
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use crate::structs::{CommentSlimView, CommentView};
|
||||
use diesel::{
|
||||
dsl::{exists, not},
|
||||
pg::Pg,
|
||||
result::Error,
|
||||
BoolExpressionMethods,
|
||||
ExpressionMethods,
|
||||
|
@ -9,13 +8,14 @@ use diesel::{
|
|||
NullableExpressionMethods,
|
||||
PgTextExpressionMethods,
|
||||
QueryDsl,
|
||||
SelectableHelper,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use diesel_ltree::{nlevel, subpath, Ltree, LtreeExtensions};
|
||||
use lemmy_db_schema::{
|
||||
aliases::creator_community_actions,
|
||||
impls::local_user::LocalUserOptionHelper,
|
||||
newtypes::{CommentId, CommunityId, LocalUserId, PersonId, PostId},
|
||||
newtypes::{CommentId, CommunityId, PersonId, PostId},
|
||||
schema::{
|
||||
comment,
|
||||
comment_actions,
|
||||
|
@ -23,108 +23,82 @@ use lemmy_db_schema::{
|
|||
community,
|
||||
community_actions,
|
||||
instance_actions,
|
||||
local_user,
|
||||
local_user_language,
|
||||
person,
|
||||
person_actions,
|
||||
post,
|
||||
},
|
||||
source::{
|
||||
community::{CommunityFollower, CommunityFollowerState},
|
||||
local_user::LocalUser,
|
||||
site::Site,
|
||||
},
|
||||
utils::{
|
||||
actions,
|
||||
actions_alias,
|
||||
fuzzy_search,
|
||||
limit_and_offset,
|
||||
DbConn,
|
||||
DbPool,
|
||||
ListFn,
|
||||
Queries,
|
||||
ReadFn,
|
||||
},
|
||||
source::{community::CommunityFollowerState, local_user::LocalUser, site::Site},
|
||||
utils::{fuzzy_search, get_conn, limit_and_offset, DbPool},
|
||||
CommentSortType,
|
||||
CommunityVisibility,
|
||||
ListingType,
|
||||
};
|
||||
|
||||
type QueriesReadTypes<'a> = (CommentId, Option<&'a LocalUser>);
|
||||
type QueriesListTypes<'a> = (CommentQuery<'a>, &'a Site);
|
||||
impl CommentView {
|
||||
#[diesel::dsl::auto_type(no_type_alias)]
|
||||
fn joins(my_person_id: Option<PersonId>) -> _ {
|
||||
let community_join = community::table.on(post::community_id.eq(community::id));
|
||||
|
||||
fn queries<'a>() -> Queries<
|
||||
impl ReadFn<'a, CommentView, QueriesReadTypes<'a>>,
|
||||
impl ListFn<'a, CommentView, QueriesListTypes<'a>>,
|
||||
> {
|
||||
let creator_is_admin = exists(
|
||||
local_user::table.filter(
|
||||
comment::creator_id
|
||||
.eq(local_user::person_id)
|
||||
.and(local_user::admin.eq(true)),
|
||||
),
|
||||
);
|
||||
let community_actions_join = community_actions::table.on(
|
||||
community_actions::community_id
|
||||
.eq(post::community_id)
|
||||
.and(community_actions::person_id.nullable().eq(my_person_id)),
|
||||
);
|
||||
|
||||
let all_joins = move |query: comment::BoxedQuery<'a, Pg>, my_person_id: Option<PersonId>| {
|
||||
query
|
||||
let comment_actions_join = comment_actions::table.on(
|
||||
comment_actions::comment_id
|
||||
.eq(comment_aggregates::comment_id)
|
||||
.and(comment_actions::person_id.nullable().eq(my_person_id)),
|
||||
);
|
||||
|
||||
let person_actions_join = person_actions::table.on(
|
||||
person_actions::target_id
|
||||
.eq(comment::creator_id)
|
||||
.and(person_actions::person_id.nullable().eq(my_person_id)),
|
||||
);
|
||||
|
||||
let instance_actions_join = instance_actions::table.on(
|
||||
instance_actions::instance_id
|
||||
.eq(community::instance_id)
|
||||
.and(instance_actions::person_id.nullable().eq(my_person_id)),
|
||||
);
|
||||
|
||||
let comment_creator_community_actions_join = creator_community_actions.on(
|
||||
creator_community_actions
|
||||
.field(community_actions::community_id)
|
||||
.eq(post::community_id)
|
||||
.and(
|
||||
creator_community_actions
|
||||
.field(community_actions::person_id)
|
||||
.eq(comment::creator_id),
|
||||
),
|
||||
);
|
||||
|
||||
comment::table
|
||||
.inner_join(person::table)
|
||||
.inner_join(post::table)
|
||||
.inner_join(community::table.on(post::community_id.eq(community::id)))
|
||||
.inner_join(community_join)
|
||||
.inner_join(comment_aggregates::table)
|
||||
.left_join(actions(
|
||||
community_actions::table,
|
||||
my_person_id,
|
||||
post::community_id,
|
||||
))
|
||||
.left_join(actions(
|
||||
comment_actions::table,
|
||||
my_person_id,
|
||||
comment_aggregates::comment_id,
|
||||
))
|
||||
.left_join(actions(
|
||||
person_actions::table,
|
||||
my_person_id,
|
||||
comment::creator_id,
|
||||
))
|
||||
.left_join(actions(
|
||||
instance_actions::table,
|
||||
my_person_id,
|
||||
community::instance_id,
|
||||
))
|
||||
.left_join(actions_alias(
|
||||
creator_community_actions,
|
||||
comment::creator_id,
|
||||
post::community_id,
|
||||
))
|
||||
.select((
|
||||
comment::all_columns,
|
||||
person::all_columns,
|
||||
post::all_columns,
|
||||
community::all_columns,
|
||||
comment_aggregates::all_columns,
|
||||
creator_community_actions
|
||||
.field(community_actions::received_ban)
|
||||
.nullable()
|
||||
.is_not_null(),
|
||||
community_actions::received_ban.nullable().is_not_null(),
|
||||
creator_community_actions
|
||||
.field(community_actions::became_moderator)
|
||||
.nullable()
|
||||
.is_not_null(),
|
||||
creator_is_admin,
|
||||
CommunityFollower::select_subscribed_type(),
|
||||
comment_actions::saved.nullable().is_not_null(),
|
||||
person_actions::blocked.nullable().is_not_null(),
|
||||
comment_actions::like_score.nullable(),
|
||||
))
|
||||
};
|
||||
.left_join(community_actions_join)
|
||||
.left_join(comment_actions_join)
|
||||
.left_join(person_actions_join)
|
||||
.left_join(instance_actions_join)
|
||||
.left_join(comment_creator_community_actions_join)
|
||||
}
|
||||
|
||||
pub async fn read(
|
||||
pool: &mut DbPool<'_>,
|
||||
comment_id: CommentId,
|
||||
my_local_user: Option<&'_ LocalUser>,
|
||||
) -> Result<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
|
||||
let mut query = Self::joins(my_local_user.person_id())
|
||||
.filter(comment::id.eq(comment_id))
|
||||
.select(Self::as_select())
|
||||
.into_boxed();
|
||||
|
||||
let read = move |mut conn: DbConn<'a>,
|
||||
(comment_id, my_local_user): (CommentId, Option<&'a LocalUser>)| async move {
|
||||
let mut query = all_joins(
|
||||
comment::table.find(comment_id).into_boxed(),
|
||||
my_local_user.person_id(),
|
||||
);
|
||||
query = my_local_user.visible_communities_only(query);
|
||||
|
||||
// Check permissions to view private community content.
|
||||
|
@ -138,14 +112,65 @@ fn queries<'a>() -> Queries<
|
|||
.or(community_actions::follow_state.eq(CommunityFollowerState::Accepted)),
|
||||
);
|
||||
}
|
||||
query.first(&mut conn).await
|
||||
};
|
||||
|
||||
let list = move |mut conn: DbConn<'a>, (o, site): (CommentQuery<'a>, &'a Site)| async move {
|
||||
let mut res = query.first::<Self>(conn).await?;
|
||||
|
||||
// If a person is given, then my_vote (res.9), if None, should be 0, not null
|
||||
// Necessary to differentiate between other person's votes
|
||||
if my_local_user.is_some() && res.my_vote.is_none() {
|
||||
res.my_vote = Some(0);
|
||||
}
|
||||
|
||||
let is_admin = my_local_user.map(|u| u.admin).unwrap_or(false);
|
||||
Ok(handle_deleted(res, is_admin))
|
||||
}
|
||||
|
||||
pub fn map_to_slim(self) -> CommentSlimView {
|
||||
CommentSlimView {
|
||||
comment: self.comment,
|
||||
creator: self.creator,
|
||||
counts: self.counts,
|
||||
creator_banned_from_community: self.creator_banned_from_community,
|
||||
banned_from_community: self.banned_from_community,
|
||||
creator_is_moderator: self.creator_is_moderator,
|
||||
creator_is_admin: self.creator_is_admin,
|
||||
subscribed: self.subscribed,
|
||||
saved: self.saved,
|
||||
creator_blocked: self.creator_blocked,
|
||||
my_vote: self.my_vote,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct CommentQuery<'a> {
|
||||
pub listing_type: Option<ListingType>,
|
||||
pub sort: Option<CommentSortType>,
|
||||
pub community_id: Option<CommunityId>,
|
||||
pub post_id: Option<PostId>,
|
||||
pub parent_path: Option<Ltree>,
|
||||
pub creator_id: Option<PersonId>,
|
||||
pub local_user: Option<&'a LocalUser>,
|
||||
pub search_term: Option<String>,
|
||||
pub liked_only: Option<bool>,
|
||||
pub disliked_only: Option<bool>,
|
||||
pub page: Option<i64>,
|
||||
pub limit: Option<i64>,
|
||||
pub max_depth: Option<i32>,
|
||||
}
|
||||
|
||||
impl CommentQuery<'_> {
|
||||
pub async fn list(self, site: &Site, pool: &mut DbPool<'_>) -> Result<Vec<CommentView>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let o = self;
|
||||
|
||||
// The left join below will return None in this case
|
||||
let local_user_id_join = o.local_user.local_user_id().unwrap_or(LocalUserId(-1));
|
||||
let my_person_id = o.local_user.person_id();
|
||||
let local_user_id = o.local_user.local_user_id();
|
||||
|
||||
let mut query = all_joins(comment::table.into_boxed(), o.local_user.person_id());
|
||||
let mut query = CommentView::joins(my_person_id)
|
||||
.select(CommentView::as_select())
|
||||
.into_boxed();
|
||||
|
||||
if let Some(creator_id) = o.creator_id {
|
||||
query = query.filter(comment::creator_id.eq(creator_id));
|
||||
|
@ -186,7 +211,7 @@ fn queries<'a>() -> Queries<
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(my_id) = o.local_user.person_id() {
|
||||
if let Some(my_id) = my_person_id {
|
||||
let not_creator_filter = comment::creator_id.ne(my_id);
|
||||
if o.liked_only.unwrap_or_default() {
|
||||
query = query
|
||||
|
@ -209,7 +234,11 @@ fn queries<'a>() -> Queries<
|
|||
local_user_language::table.filter(
|
||||
comment::language_id
|
||||
.eq(local_user_language::language_id)
|
||||
.and(local_user_language::local_user_id.eq(local_user_id_join)),
|
||||
.and(
|
||||
local_user_language::local_user_id
|
||||
.nullable()
|
||||
.eq(local_user_id),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
|
@ -287,74 +316,17 @@ fn queries<'a>() -> Queries<
|
|||
CommentSortType::Top => query.then_order_by(comment_aggregates::score.desc()),
|
||||
};
|
||||
|
||||
// Note: deleted and removed comments are done on the front side
|
||||
query
|
||||
let res = query
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
.load::<CommentView>(&mut conn)
|
||||
.await
|
||||
};
|
||||
.load::<CommentView>(conn)
|
||||
.await?;
|
||||
|
||||
Queries::new(read, list)
|
||||
}
|
||||
let is_admin = o.local_user.map(|u| u.admin).unwrap_or(false);
|
||||
|
||||
impl CommentView {
|
||||
pub async fn read(
|
||||
pool: &mut DbPool<'_>,
|
||||
comment_id: CommentId,
|
||||
my_local_user: Option<&'_ LocalUser>,
|
||||
) -> Result<Self, Error> {
|
||||
let is_admin = my_local_user.map(|u| u.admin).unwrap_or(false);
|
||||
// If a person is given, then my_vote (res.9), if None, should be 0, not null
|
||||
// Necessary to differentiate between other person's votes
|
||||
let mut res = queries().read(pool, (comment_id, my_local_user)).await?;
|
||||
if my_local_user.is_some() && res.my_vote.is_none() {
|
||||
res.my_vote = Some(0);
|
||||
}
|
||||
Ok(handle_deleted(res, is_admin))
|
||||
}
|
||||
|
||||
pub fn map_to_slim(self) -> CommentSlimView {
|
||||
CommentSlimView {
|
||||
comment: self.comment,
|
||||
creator: self.creator,
|
||||
counts: self.counts,
|
||||
creator_banned_from_community: self.creator_banned_from_community,
|
||||
banned_from_community: self.banned_from_community,
|
||||
creator_is_moderator: self.creator_is_moderator,
|
||||
creator_is_admin: self.creator_is_admin,
|
||||
subscribed: self.subscribed,
|
||||
saved: self.saved,
|
||||
creator_blocked: self.creator_blocked,
|
||||
my_vote: self.my_vote,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct CommentQuery<'a> {
|
||||
pub listing_type: Option<ListingType>,
|
||||
pub sort: Option<CommentSortType>,
|
||||
pub community_id: Option<CommunityId>,
|
||||
pub post_id: Option<PostId>,
|
||||
pub parent_path: Option<Ltree>,
|
||||
pub creator_id: Option<PersonId>,
|
||||
pub local_user: Option<&'a LocalUser>,
|
||||
pub search_term: Option<String>,
|
||||
pub liked_only: Option<bool>,
|
||||
pub disliked_only: Option<bool>,
|
||||
pub page: Option<i64>,
|
||||
pub limit: Option<i64>,
|
||||
pub max_depth: Option<i32>,
|
||||
}
|
||||
|
||||
impl CommentQuery<'_> {
|
||||
pub async fn list(self, site: &Site, pool: &mut DbPool<'_>) -> Result<Vec<CommentView>, Error> {
|
||||
let is_admin = self.local_user.map(|u| u.admin).unwrap_or(false);
|
||||
// Note: deleted and removed comments are done on the front side
|
||||
Ok(
|
||||
queries()
|
||||
.list(pool, (self, site))
|
||||
.await?
|
||||
res
|
||||
.into_iter()
|
||||
.map(|c| handle_deleted(c, is_admin))
|
||||
.collect(),
|
||||
|
@ -362,6 +334,7 @@ impl CommentQuery<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Only show deleted / removed content for admins.
|
||||
fn handle_deleted(mut c: CommentView, is_admin: bool) -> CommentView {
|
||||
if !is_admin && (c.comment.deleted || c.comment.removed) {
|
||||
c.comment.content = String::new();
|
||||
|
|
|
@ -8,6 +8,7 @@ use diesel::{
|
|||
ExpressionMethods,
|
||||
JoinOnDsl,
|
||||
QueryDsl,
|
||||
SelectableHelper,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_db_schema::{
|
||||
|
@ -17,13 +18,20 @@ use lemmy_db_schema::{
|
|||
community::{Community, CommunityFollower, CommunityFollowerState},
|
||||
person::Person,
|
||||
},
|
||||
utils::{action_query, get_conn, limit_and_offset, DbPool},
|
||||
utils::{get_conn, limit_and_offset, DbPool},
|
||||
CommunityVisibility,
|
||||
SubscribedType,
|
||||
};
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
impl CommunityFollowerView {
|
||||
#[diesel::dsl::auto_type(no_type_alias)]
|
||||
fn joins() -> _ {
|
||||
community_actions::table
|
||||
.filter(community_actions::followed.is_not_null())
|
||||
.inner_join(community::table)
|
||||
.inner_join(person::table.on(community_actions::person_id.eq(person::id)))
|
||||
}
|
||||
/// return a list of local community ids and remote inboxes that at least one user of the given
|
||||
/// instance has followed
|
||||
pub async fn get_instance_followed_community_inboxes(
|
||||
|
@ -39,9 +47,7 @@ impl CommunityFollowerView {
|
|||
// that would work for all instances that support fully shared inboxes.
|
||||
// It would be a bit more complicated though to keep it in sync.
|
||||
|
||||
community_actions::table
|
||||
.inner_join(community::table)
|
||||
.inner_join(person::table.on(community_actions::person_id.eq(person::id)))
|
||||
Self::joins()
|
||||
.filter(person::instance_id.eq(instance_id))
|
||||
.filter(community::local) // this should be a no-op since community_followers table only has
|
||||
// local-person+remote-community or remote-person+local-community
|
||||
|
@ -53,12 +59,29 @@ impl CommunityFollowerView {
|
|||
.await
|
||||
.with_lemmy_type(LemmyErrorType::NotFound)
|
||||
}
|
||||
|
||||
pub async fn get_community_follower_inboxes(
|
||||
pool: &mut DbPool<'_>,
|
||||
community_id: CommunityId,
|
||||
) -> Result<Vec<DbUrl>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let res = Self::joins()
|
||||
.filter(community_actions::community_id.eq(community_id))
|
||||
.filter(not(person::local))
|
||||
.select(person::inbox_url)
|
||||
.distinct()
|
||||
.load::<DbUrl>(conn)
|
||||
.await?;
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub async fn count_community_followers(
|
||||
pool: &mut DbPool<'_>,
|
||||
community_id: CommunityId,
|
||||
) -> Result<i64, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let res = action_query(community_actions::followed)
|
||||
let res = Self::joins()
|
||||
.filter(community_actions::community_id.eq(community_id))
|
||||
.select(count_star())
|
||||
.first::<i64>(conn)
|
||||
|
@ -69,13 +92,11 @@ impl CommunityFollowerView {
|
|||
|
||||
pub async fn for_person(pool: &mut DbPool<'_>, person_id: PersonId) -> Result<Vec<Self>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
action_query(community_actions::followed)
|
||||
.inner_join(community::table)
|
||||
.inner_join(person::table.on(community_actions::person_id.eq(person::id)))
|
||||
.select((community::all_columns, person::all_columns))
|
||||
Self::joins()
|
||||
.filter(community_actions::person_id.eq(person_id))
|
||||
.filter(community::deleted.eq(false))
|
||||
.filter(community::removed.eq(false))
|
||||
.select(Self::as_select())
|
||||
.order_by(community::title)
|
||||
.load::<CommunityFollowerView>(conn)
|
||||
.await
|
||||
|
@ -124,9 +145,13 @@ impl CommunityFollowerView {
|
|||
),
|
||||
));
|
||||
|
||||
let mut query = action_query(community_actions::followed)
|
||||
.inner_join(person::table.on(community_actions::person_id.eq(person::id)))
|
||||
.inner_join(community::table)
|
||||
let mut query = Self::joins()
|
||||
.select((
|
||||
person::all_columns,
|
||||
community::all_columns,
|
||||
is_new_instance,
|
||||
CommunityFollower::select_subscribed_type(),
|
||||
))
|
||||
.into_boxed();
|
||||
if all_communities {
|
||||
// if param is false, only return items for communities where user is a mod
|
||||
|
@ -142,12 +167,6 @@ impl CommunityFollowerView {
|
|||
.order_by(community_actions::followed.asc())
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
.select((
|
||||
person::all_columns,
|
||||
community::all_columns,
|
||||
is_new_instance,
|
||||
CommunityFollower::select_subscribed_type(),
|
||||
))
|
||||
.load::<(Person, Community, bool, SubscribedType)>(conn)
|
||||
.await?;
|
||||
Ok(
|
||||
|
@ -170,8 +189,7 @@ impl CommunityFollowerView {
|
|||
community_id: CommunityId,
|
||||
) -> Result<i64, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
action_query(community_actions::followed)
|
||||
.inner_join(person::table.on(community_actions::person_id.eq(person::id)))
|
||||
Self::joins()
|
||||
.filter(community_actions::community_id.eq(community_id))
|
||||
.filter(community_actions::follow_state.eq(CommunityFollowerState::ApprovalRequired))
|
||||
.select(count(community_actions::community_id))
|
||||
|
@ -188,7 +206,7 @@ impl CommunityFollowerView {
|
|||
}
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
select(exists(
|
||||
action_query(community_actions::followed)
|
||||
Self::joins()
|
||||
.filter(community_actions::community_id.eq(community.id))
|
||||
.filter(community_actions::person_id.eq(from_person_id))
|
||||
.filter(community_actions::follow_state.eq(CommunityFollowerState::Accepted)),
|
||||
|
@ -205,8 +223,7 @@ impl CommunityFollowerView {
|
|||
) -> Result<(), Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
select(exists(
|
||||
action_query(community_actions::followed)
|
||||
.inner_join(person::table.on(community_actions::person_id.eq(person::id)))
|
||||
Self::joins()
|
||||
.filter(community_actions::community_id.eq(community_id))
|
||||
.filter(person::instance_id.eq(instance_id))
|
||||
.filter(community_actions::follow_state.eq(CommunityFollowerState::Accepted)),
|
||||
|
@ -224,8 +241,7 @@ impl CommunityFollowerView {
|
|||
) -> Result<(), Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
select(exists(
|
||||
action_query(community_actions::followed)
|
||||
.inner_join(person::table.on(community_actions::person_id.eq(person::id)))
|
||||
Self::joins()
|
||||
.filter(community_actions::community_id.eq(community_id))
|
||||
.filter(person::instance_id.eq(instance_id))
|
||||
.filter(community_actions::follow_state.eq(CommunityFollowerState::Accepted)),
|
||||
|
|
|
@ -1,26 +1,43 @@
|
|||
use crate::structs::CommunityModeratorView;
|
||||
use diesel::{dsl::exists, result::Error, select, ExpressionMethods, JoinOnDsl, QueryDsl};
|
||||
use diesel::{
|
||||
dsl::exists,
|
||||
result::Error,
|
||||
select,
|
||||
ExpressionMethods,
|
||||
JoinOnDsl,
|
||||
QueryDsl,
|
||||
SelectableHelper,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_db_schema::{
|
||||
impls::local_user::LocalUserOptionHelper,
|
||||
newtypes::{CommunityId, PersonId},
|
||||
schema::{community, community_actions, person},
|
||||
source::local_user::LocalUser,
|
||||
utils::{action_query, find_action, get_conn, DbPool},
|
||||
utils::{get_conn, DbPool},
|
||||
};
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||
|
||||
impl CommunityModeratorView {
|
||||
#[diesel::dsl::auto_type(no_type_alias)]
|
||||
fn joins() -> _ {
|
||||
community_actions::table
|
||||
.filter(community_actions::became_moderator.is_not_null())
|
||||
.inner_join(community::table)
|
||||
.inner_join(person::table.on(person::id.eq(community_actions::person_id)))
|
||||
}
|
||||
|
||||
pub async fn check_is_community_moderator(
|
||||
pool: &mut DbPool<'_>,
|
||||
find_community_id: CommunityId,
|
||||
find_person_id: PersonId,
|
||||
community_id: CommunityId,
|
||||
person_id: PersonId,
|
||||
) -> LemmyResult<()> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
select(exists(find_action(
|
||||
community_actions::became_moderator,
|
||||
(find_person_id, find_community_id),
|
||||
)))
|
||||
select(exists(
|
||||
Self::joins()
|
||||
.filter(community_actions::person_id.eq(person_id))
|
||||
.filter(community_actions::community_id.eq(community_id)),
|
||||
))
|
||||
.get_result::<bool>(conn)
|
||||
.await?
|
||||
.then_some(())
|
||||
|
@ -29,12 +46,11 @@ impl CommunityModeratorView {
|
|||
|
||||
pub(crate) async fn is_community_moderator_of_any(
|
||||
pool: &mut DbPool<'_>,
|
||||
find_person_id: PersonId,
|
||||
person_id: PersonId,
|
||||
) -> LemmyResult<()> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
select(exists(
|
||||
action_query(community_actions::became_moderator)
|
||||
.filter(community_actions::person_id.eq(find_person_id)),
|
||||
Self::joins().filter(community_actions::person_id.eq(person_id)),
|
||||
))
|
||||
.get_result::<bool>(conn)
|
||||
.await?
|
||||
|
@ -47,13 +63,11 @@ impl CommunityModeratorView {
|
|||
community_id: CommunityId,
|
||||
) -> Result<Vec<Self>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
action_query(community_actions::became_moderator)
|
||||
.inner_join(community::table)
|
||||
.inner_join(person::table.on(person::id.eq(community_actions::person_id)))
|
||||
Self::joins()
|
||||
.filter(community_actions::community_id.eq(community_id))
|
||||
.select((community::all_columns, person::all_columns))
|
||||
.select(Self::as_select())
|
||||
.order_by(community_actions::became_moderator)
|
||||
.load::<CommunityModeratorView>(conn)
|
||||
.load::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
|
@ -63,11 +77,9 @@ impl CommunityModeratorView {
|
|||
local_user: Option<&LocalUser>,
|
||||
) -> Result<Vec<Self>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let mut query = action_query(community_actions::became_moderator)
|
||||
.inner_join(community::table)
|
||||
.inner_join(person::table.on(person::id.eq(community_actions::person_id)))
|
||||
let mut query = Self::joins()
|
||||
.filter(community_actions::person_id.eq(person_id))
|
||||
.select((community::all_columns, person::all_columns))
|
||||
.select(Self::as_select())
|
||||
.into_boxed();
|
||||
|
||||
query = local_user.visible_communities_only(query);
|
||||
|
@ -82,17 +94,15 @@ impl CommunityModeratorView {
|
|||
query = query.filter(community::removed.eq(false))
|
||||
}
|
||||
|
||||
query.load::<CommunityModeratorView>(conn).await
|
||||
query.load::<Self>(conn).await
|
||||
}
|
||||
|
||||
/// Finds all communities first mods / creators
|
||||
/// Ideally this should be a group by, but diesel doesn't support it yet
|
||||
pub async fn get_community_first_mods(pool: &mut DbPool<'_>) -> Result<Vec<Self>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
action_query(community_actions::became_moderator)
|
||||
.inner_join(community::table)
|
||||
.inner_join(person::table.on(person::id.eq(community_actions::person_id)))
|
||||
.select((community::all_columns, person::all_columns))
|
||||
Self::joins()
|
||||
.select(Self::as_select())
|
||||
// A hacky workaround instead of group_bys
|
||||
// https://stackoverflow.com/questions/24042359/how-to-join-only-one-row-in-joined-table-with-postgres
|
||||
.distinct_on(community_actions::community_id)
|
||||
|
@ -100,7 +110,7 @@ impl CommunityModeratorView {
|
|||
community_actions::community_id,
|
||||
community_actions::became_moderator,
|
||||
))
|
||||
.load::<CommunityModeratorView>(conn)
|
||||
.load::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,12 +2,14 @@ use crate::structs::CommunityPersonBanView;
|
|||
use diesel::{
|
||||
dsl::{exists, not},
|
||||
select,
|
||||
ExpressionMethods,
|
||||
QueryDsl,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_db_schema::{
|
||||
newtypes::{CommunityId, PersonId},
|
||||
schema::community_actions,
|
||||
utils::{find_action, get_conn, DbPool},
|
||||
utils::{get_conn, DbPool},
|
||||
};
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||
|
||||
|
@ -18,13 +20,13 @@ impl CommunityPersonBanView {
|
|||
from_community_id: CommunityId,
|
||||
) -> LemmyResult<()> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
select(not(exists(find_action(
|
||||
community_actions::received_ban,
|
||||
(from_person_id, from_community_id),
|
||||
))))
|
||||
.get_result::<bool>(conn)
|
||||
.await?
|
||||
.then_some(())
|
||||
.ok_or(LemmyErrorType::PersonIsBannedFromCommunity.into())
|
||||
let find_action = community_actions::table
|
||||
.find((from_person_id, from_community_id))
|
||||
.filter(community_actions::received_ban.is_not_null());
|
||||
select(not(exists(find_action)))
|
||||
.get_result::<bool>(conn)
|
||||
.await?
|
||||
.then_some(())
|
||||
.ok_or(LemmyErrorType::PersonIsBannedFromCommunity.into())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use crate::structs::{CommunityModeratorView, CommunitySortType, CommunityView, PersonView};
|
||||
use diesel::{
|
||||
pg::Pg,
|
||||
result::Error,
|
||||
BoolExpressionMethods,
|
||||
ExpressionMethods,
|
||||
JoinOnDsl,
|
||||
NullableExpressionMethods,
|
||||
PgTextExpressionMethods,
|
||||
QueryDsl,
|
||||
SelectableHelper,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_db_schema::{
|
||||
|
@ -14,175 +14,56 @@ use lemmy_db_schema::{
|
|||
newtypes::{CommunityId, PersonId},
|
||||
schema::{community, community_actions, community_aggregates, instance_actions},
|
||||
source::{
|
||||
community::{CommunityFollower, CommunityFollowerState},
|
||||
community::{Community, CommunityFollowerState},
|
||||
local_user::LocalUser,
|
||||
site::Site,
|
||||
},
|
||||
utils::{
|
||||
actions,
|
||||
functions::lower,
|
||||
fuzzy_search,
|
||||
limit_and_offset,
|
||||
DbConn,
|
||||
DbPool,
|
||||
ListFn,
|
||||
Queries,
|
||||
ReadFn,
|
||||
},
|
||||
utils::{functions::lower, get_conn, limit_and_offset, DbPool},
|
||||
ListingType,
|
||||
PostSortType,
|
||||
};
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||
|
||||
type QueriesReadTypes<'a> = (CommunityId, Option<&'a LocalUser>, bool);
|
||||
type QueriesListTypes<'a> = (CommunityQuery<'a>, &'a Site);
|
||||
|
||||
fn queries<'a>() -> Queries<
|
||||
impl ReadFn<'a, CommunityView, QueriesReadTypes<'a>>,
|
||||
impl ListFn<'a, CommunityView, QueriesListTypes<'a>>,
|
||||
> {
|
||||
let all_joins = |query: community::BoxedQuery<'a, Pg>, my_local_user: Option<&'a LocalUser>| {
|
||||
query
|
||||
.inner_join(community_aggregates::table)
|
||||
.left_join(actions(
|
||||
community_actions::table,
|
||||
my_local_user.person_id(),
|
||||
community::id,
|
||||
))
|
||||
.left_join(actions(
|
||||
instance_actions::table,
|
||||
my_local_user.person_id(),
|
||||
community::instance_id,
|
||||
))
|
||||
};
|
||||
|
||||
let selection = (
|
||||
community::all_columns,
|
||||
CommunityFollower::select_subscribed_type(),
|
||||
community_actions::blocked.nullable().is_not_null(),
|
||||
community_aggregates::all_columns,
|
||||
community_actions::received_ban.nullable().is_not_null(),
|
||||
);
|
||||
|
||||
let not_removed_or_deleted = community::removed
|
||||
.eq(false)
|
||||
.and(community::deleted.eq(false));
|
||||
|
||||
let read = move |mut conn: DbConn<'a>,
|
||||
(community_id, my_local_user, is_mod_or_admin): (
|
||||
CommunityId,
|
||||
Option<&'a LocalUser>,
|
||||
bool,
|
||||
)| async move {
|
||||
let mut query = all_joins(
|
||||
community::table.find(community_id).into_boxed(),
|
||||
my_local_user,
|
||||
)
|
||||
.select(selection);
|
||||
|
||||
// Hide deleted and removed for non-admins or mods
|
||||
if !is_mod_or_admin {
|
||||
query = query.filter(not_removed_or_deleted);
|
||||
}
|
||||
|
||||
query = my_local_user.visible_communities_only(query);
|
||||
|
||||
query.first(&mut conn).await
|
||||
};
|
||||
|
||||
let list = move |mut conn: DbConn<'a>, (o, site): (CommunityQuery<'a>, &'a Site)| async move {
|
||||
use CommunitySortType::*;
|
||||
|
||||
let mut query = all_joins(community::table.into_boxed(), o.local_user).select(selection);
|
||||
|
||||
if let Some(search_term) = o.search_term {
|
||||
let searcher = fuzzy_search(&search_term);
|
||||
let name_filter = community::name.ilike(searcher.clone());
|
||||
let title_filter = community::title.ilike(searcher.clone());
|
||||
let description_filter = community::description.ilike(searcher.clone());
|
||||
query = if o.title_only.unwrap_or_default() {
|
||||
query.filter(name_filter.or(title_filter))
|
||||
} else {
|
||||
query.filter(name_filter.or(title_filter.or(description_filter)))
|
||||
}
|
||||
}
|
||||
|
||||
// Hide deleted and removed for non-admins or mods
|
||||
if !o.is_mod_or_admin {
|
||||
query = query.filter(not_removed_or_deleted).filter(
|
||||
community::hidden
|
||||
.eq(false)
|
||||
.or(community_actions::follow_state.is_not_null()),
|
||||
);
|
||||
}
|
||||
|
||||
match o.sort.unwrap_or(Hot) {
|
||||
Hot | Active | Scaled => query = query.order_by(community_aggregates::hot_rank.desc()),
|
||||
NewComments | TopDay | TopTwelveHour | TopSixHour | TopHour => {
|
||||
query = query.order_by(community_aggregates::users_active_day.desc())
|
||||
}
|
||||
New => query = query.order_by(community::published.desc()),
|
||||
Old => query = query.order_by(community::published.asc()),
|
||||
// Controversial is temporary until a CommentSortType is created
|
||||
MostComments | Controversial => query = query.order_by(community_aggregates::comments.desc()),
|
||||
TopAll | TopYear | TopNineMonths => {
|
||||
query = query.order_by(community_aggregates::subscribers.desc())
|
||||
}
|
||||
TopSixMonths | TopThreeMonths => {
|
||||
query = query.order_by(community_aggregates::users_active_half_year.desc())
|
||||
}
|
||||
TopMonth => query = query.order_by(community_aggregates::users_active_month.desc()),
|
||||
TopWeek => query = query.order_by(community_aggregates::users_active_week.desc()),
|
||||
NameAsc => query = query.order_by(lower(community::name).asc()),
|
||||
NameDesc => query = query.order_by(lower(community::name).desc()),
|
||||
};
|
||||
|
||||
let is_subscribed = community_actions::follow_state.eq(Some(CommunityFollowerState::Accepted));
|
||||
|
||||
if let Some(listing_type) = o.listing_type {
|
||||
query = match listing_type {
|
||||
ListingType::All => query.filter(community::hidden.eq(false).or(is_subscribed)),
|
||||
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())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Don't show blocked communities and communities on blocked instances. nsfw communities are
|
||||
// also hidden (based on profile setting)
|
||||
query = query.filter(instance_actions::blocked.is_null());
|
||||
query = query.filter(community_actions::blocked.is_null());
|
||||
if !(o.local_user.show_nsfw(site) || o.show_nsfw) {
|
||||
query = query.filter(community::nsfw.eq(false));
|
||||
}
|
||||
|
||||
query = o.local_user.visible_communities_only(query);
|
||||
|
||||
let (limit, offset) = limit_and_offset(o.page, o.limit)?;
|
||||
query
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
.load::<CommunityView>(&mut conn)
|
||||
.await
|
||||
};
|
||||
|
||||
Queries::new(read, list)
|
||||
}
|
||||
|
||||
impl CommunityView {
|
||||
#[diesel::dsl::auto_type(no_type_alias)]
|
||||
fn joins(person_id: Option<PersonId>) -> _ {
|
||||
let community_actions_join = community_actions::table.on(
|
||||
community_actions::community_id
|
||||
.eq(community::id)
|
||||
.and(community_actions::person_id.nullable().eq(person_id)),
|
||||
);
|
||||
|
||||
let instance_actions_join = instance_actions::table.on(
|
||||
instance_actions::instance_id
|
||||
.eq(community::instance_id)
|
||||
.and(instance_actions::person_id.nullable().eq(person_id)),
|
||||
);
|
||||
|
||||
community::table
|
||||
.inner_join(community_aggregates::table)
|
||||
.left_join(community_actions_join)
|
||||
.left_join(instance_actions_join)
|
||||
}
|
||||
|
||||
pub async fn read(
|
||||
pool: &mut DbPool<'_>,
|
||||
community_id: CommunityId,
|
||||
my_local_user: Option<&'_ LocalUser>,
|
||||
is_mod_or_admin: bool,
|
||||
) -> Result<Self, Error> {
|
||||
queries()
|
||||
.read(pool, (community_id, my_local_user, is_mod_or_admin))
|
||||
.await
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let mut query = Self::joins(my_local_user.person_id())
|
||||
.filter(community::id.eq(community_id))
|
||||
.select(Self::as_select())
|
||||
.into_boxed();
|
||||
|
||||
// Hide deleted and removed for non-admins or mods
|
||||
if !is_mod_or_admin {
|
||||
query = query.filter(Community::hide_removed_and_deleted());
|
||||
}
|
||||
|
||||
query = my_local_user.visible_communities_only(query);
|
||||
|
||||
query.first(conn).await
|
||||
}
|
||||
|
||||
pub async fn check_is_mod_or_admin(
|
||||
|
@ -222,38 +103,11 @@ impl CommunityView {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<PostSortType> for CommunitySortType {
|
||||
fn from(value: PostSortType) -> Self {
|
||||
match value {
|
||||
PostSortType::Active => Self::Active,
|
||||
PostSortType::Hot => Self::Hot,
|
||||
PostSortType::New => Self::New,
|
||||
PostSortType::Old => Self::Old,
|
||||
PostSortType::TopDay => Self::TopDay,
|
||||
PostSortType::TopWeek => Self::TopWeek,
|
||||
PostSortType::TopMonth => Self::TopMonth,
|
||||
PostSortType::TopYear => Self::TopYear,
|
||||
PostSortType::TopAll => Self::TopAll,
|
||||
PostSortType::MostComments => Self::MostComments,
|
||||
PostSortType::NewComments => Self::NewComments,
|
||||
PostSortType::TopHour => Self::TopHour,
|
||||
PostSortType::TopSixHour => Self::TopSixHour,
|
||||
PostSortType::TopTwelveHour => Self::TopTwelveHour,
|
||||
PostSortType::TopThreeMonths => Self::TopThreeMonths,
|
||||
PostSortType::TopSixMonths => Self::TopSixMonths,
|
||||
PostSortType::TopNineMonths => Self::TopNineMonths,
|
||||
PostSortType::Controversial => Self::Controversial,
|
||||
PostSortType::Scaled => Self::Scaled,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct CommunityQuery<'a> {
|
||||
pub listing_type: Option<ListingType>,
|
||||
pub sort: Option<CommunitySortType>,
|
||||
pub local_user: Option<&'a LocalUser>,
|
||||
pub search_term: Option<String>,
|
||||
pub title_only: Option<bool>,
|
||||
pub is_mod_or_admin: bool,
|
||||
pub show_nsfw: bool,
|
||||
|
@ -263,7 +117,73 @@ pub struct CommunityQuery<'a> {
|
|||
|
||||
impl CommunityQuery<'_> {
|
||||
pub async fn list(self, site: &Site, pool: &mut DbPool<'_>) -> Result<Vec<CommunityView>, Error> {
|
||||
queries().list(pool, (self, site)).await
|
||||
use CommunitySortType::*;
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let o = self;
|
||||
|
||||
let mut query = CommunityView::joins(o.local_user.person_id())
|
||||
.select(CommunityView::as_select())
|
||||
.into_boxed();
|
||||
|
||||
// Hide deleted and removed for non-admins or mods
|
||||
if !o.is_mod_or_admin {
|
||||
query = query.filter(Community::hide_removed_and_deleted()).filter(
|
||||
community::hidden
|
||||
.eq(false)
|
||||
.or(community_actions::follow_state.is_not_null()),
|
||||
);
|
||||
}
|
||||
|
||||
let is_subscribed = community_actions::follow_state.eq(Some(CommunityFollowerState::Accepted));
|
||||
|
||||
if let Some(listing_type) = o.listing_type {
|
||||
query = match listing_type {
|
||||
ListingType::All => query.filter(community::hidden.eq(false).or(is_subscribed)),
|
||||
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())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Don't show blocked communities and communities on blocked instances. nsfw communities are
|
||||
// also hidden (based on profile setting)
|
||||
query = query.filter(instance_actions::blocked.is_null());
|
||||
query = query.filter(community_actions::blocked.is_null());
|
||||
if !(o.local_user.show_nsfw(site) || o.show_nsfw) {
|
||||
query = query.filter(community::nsfw.eq(false));
|
||||
}
|
||||
|
||||
query = o.local_user.visible_communities_only(query);
|
||||
|
||||
match o.sort.unwrap_or_default() {
|
||||
Hot => query = query.order_by(community_aggregates::hot_rank.desc()),
|
||||
Comments => query = query.order_by(community_aggregates::comments.desc()),
|
||||
Posts => query = query.order_by(community_aggregates::posts.desc()),
|
||||
New => query = query.order_by(community::published.desc()),
|
||||
Old => query = query.order_by(community::published.asc()),
|
||||
Subscribers => query = query.order_by(community_aggregates::subscribers.desc()),
|
||||
SubscribersLocal => query = query.order_by(community_aggregates::subscribers_local.desc()),
|
||||
ActiveSixMonths => {
|
||||
query = query.order_by(community_aggregates::users_active_half_year.desc())
|
||||
}
|
||||
ActiveMonthly => query = query.order_by(community_aggregates::users_active_month.desc()),
|
||||
ActiveWeekly => query = query.order_by(community_aggregates::users_active_week.desc()),
|
||||
ActiveDaily => query = query.order_by(community_aggregates::users_active_day.desc()),
|
||||
NameAsc => query = query.order_by(lower(community::name).asc()),
|
||||
NameDesc => query = query.order_by(lower(community::name).desc()),
|
||||
};
|
||||
|
||||
let (limit, offset) = limit_and_offset(o.page, o.limit)?;
|
||||
|
||||
query
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
.load::<CommunityView>(conn)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
use crate::structs::LocalUserView;
|
||||
use actix_web::{dev::Payload, FromRequest, HttpMessage, HttpRequest};
|
||||
use diesel::{result::Error, BoolExpressionMethods, ExpressionMethods, JoinOnDsl, QueryDsl};
|
||||
use diesel::{
|
||||
result::Error,
|
||||
BoolExpressionMethods,
|
||||
ExpressionMethods,
|
||||
JoinOnDsl,
|
||||
QueryDsl,
|
||||
SelectableHelper,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_db_schema::{
|
||||
newtypes::{LocalUserId, OAuthProviderId, PersonId},
|
||||
|
@ -13,118 +20,72 @@ use lemmy_db_schema::{
|
|||
traits::Crud,
|
||||
utils::{
|
||||
functions::{coalesce, lower},
|
||||
DbConn,
|
||||
get_conn,
|
||||
DbPool,
|
||||
ListFn,
|
||||
Queries,
|
||||
ReadFn,
|
||||
},
|
||||
};
|
||||
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
use std::future::{ready, Ready};
|
||||
|
||||
enum ReadBy<'a> {
|
||||
Id(LocalUserId),
|
||||
Person(PersonId),
|
||||
Name(&'a str),
|
||||
NameOrEmail(&'a str),
|
||||
Email(&'a str),
|
||||
OAuthID(OAuthProviderId, &'a str),
|
||||
}
|
||||
|
||||
enum ListMode {
|
||||
AdminsWithEmails,
|
||||
}
|
||||
|
||||
fn queries<'a>(
|
||||
) -> Queries<impl ReadFn<'a, LocalUserView, ReadBy<'a>>, impl ListFn<'a, LocalUserView, ListMode>> {
|
||||
let selection = (
|
||||
local_user::all_columns,
|
||||
local_user_vote_display_mode::all_columns,
|
||||
person::all_columns,
|
||||
person_aggregates::all_columns,
|
||||
);
|
||||
|
||||
let read = move |mut conn: DbConn<'a>, search: ReadBy<'a>| async move {
|
||||
let mut query = local_user::table.into_boxed();
|
||||
query = match search {
|
||||
ReadBy::Id(local_user_id) => query.filter(local_user::id.eq(local_user_id)),
|
||||
ReadBy::Email(from_email) => {
|
||||
query.filter(lower(coalesce(local_user::email, "")).eq(from_email.to_lowercase()))
|
||||
}
|
||||
_ => query,
|
||||
};
|
||||
let mut query = query.inner_join(person::table);
|
||||
query = match search {
|
||||
ReadBy::Person(person_id) => query.filter(person::id.eq(person_id)),
|
||||
ReadBy::Name(name) => query.filter(lower(person::name).eq(name.to_lowercase())),
|
||||
ReadBy::NameOrEmail(name_or_email) => query.filter(
|
||||
lower(person::name)
|
||||
.eq(lower(name_or_email.to_lowercase()))
|
||||
.or(lower(coalesce(local_user::email, "")).eq(name_or_email.to_lowercase())),
|
||||
),
|
||||
_ => query,
|
||||
};
|
||||
let query = query
|
||||
.inner_join(local_user_vote_display_mode::table)
|
||||
.inner_join(person_aggregates::table.on(person::id.eq(person_aggregates::person_id)));
|
||||
|
||||
if let ReadBy::OAuthID(oauth_provider_id, oauth_user_id) = search {
|
||||
query
|
||||
.inner_join(oauth_account::table)
|
||||
.filter(oauth_account::oauth_provider_id.eq(oauth_provider_id))
|
||||
.filter(oauth_account::oauth_user_id.eq(oauth_user_id))
|
||||
.select(selection)
|
||||
.first(&mut conn)
|
||||
.await
|
||||
} else {
|
||||
query.select(selection).first(&mut conn).await
|
||||
}
|
||||
};
|
||||
|
||||
let list = move |mut conn: DbConn<'a>, mode: ListMode| async move {
|
||||
match mode {
|
||||
ListMode::AdminsWithEmails => {
|
||||
local_user::table
|
||||
.inner_join(local_user_vote_display_mode::table)
|
||||
.inner_join(person::table)
|
||||
.inner_join(person_aggregates::table.on(person::id.eq(person_aggregates::person_id)))
|
||||
.filter(local_user::email.is_not_null())
|
||||
.filter(local_user::admin.eq(true))
|
||||
.select(selection)
|
||||
.load::<LocalUserView>(&mut conn)
|
||||
.await
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Queries::new(read, list)
|
||||
}
|
||||
|
||||
impl LocalUserView {
|
||||
#[diesel::dsl::auto_type(no_type_alias)]
|
||||
fn joins() -> _ {
|
||||
local_user::table
|
||||
.inner_join(local_user_vote_display_mode::table)
|
||||
.inner_join(person::table)
|
||||
.inner_join(person_aggregates::table.on(person::id.eq(person_aggregates::person_id)))
|
||||
}
|
||||
|
||||
pub async fn read(pool: &mut DbPool<'_>, local_user_id: LocalUserId) -> Result<Self, Error> {
|
||||
queries().read(pool, ReadBy::Id(local_user_id)).await
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
Self::joins()
|
||||
.filter(local_user::id.eq(local_user_id))
|
||||
.select(Self::as_select())
|
||||
.first(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn read_person(pool: &mut DbPool<'_>, person_id: PersonId) -> Result<Self, Error> {
|
||||
queries().read(pool, ReadBy::Person(person_id)).await
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
Self::joins()
|
||||
.filter(person::id.eq(person_id))
|
||||
.select(Self::as_select())
|
||||
.first(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn read_from_name(pool: &mut DbPool<'_>, name: &str) -> Result<Self, Error> {
|
||||
queries().read(pool, ReadBy::Name(name)).await
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
Self::joins()
|
||||
.filter(lower(person::name).eq(name.to_lowercase()))
|
||||
.select(Self::as_select())
|
||||
.first(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn find_by_email_or_name(
|
||||
pool: &mut DbPool<'_>,
|
||||
name_or_email: &str,
|
||||
) -> Result<Self, Error> {
|
||||
queries()
|
||||
.read(pool, ReadBy::NameOrEmail(name_or_email))
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
Self::joins()
|
||||
.filter(
|
||||
lower(person::name)
|
||||
.eq(lower(name_or_email.to_lowercase()))
|
||||
.or(lower(coalesce(local_user::email, "")).eq(name_or_email.to_lowercase())),
|
||||
)
|
||||
.select(Self::as_select())
|
||||
.first(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn find_by_email(pool: &mut DbPool<'_>, from_email: &str) -> Result<Self, Error> {
|
||||
queries().read(pool, ReadBy::Email(from_email)).await
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
Self::joins()
|
||||
.filter(lower(coalesce(local_user::email, "")).eq(from_email.to_lowercase()))
|
||||
.select(Self::as_select())
|
||||
.first(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn find_by_oauth_id(
|
||||
|
@ -132,13 +93,24 @@ impl LocalUserView {
|
|||
oauth_provider_id: OAuthProviderId,
|
||||
oauth_user_id: &str,
|
||||
) -> Result<Self, Error> {
|
||||
queries()
|
||||
.read(pool, ReadBy::OAuthID(oauth_provider_id, oauth_user_id))
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
Self::joins()
|
||||
.inner_join(oauth_account::table)
|
||||
.filter(oauth_account::oauth_provider_id.eq(oauth_provider_id))
|
||||
.filter(oauth_account::oauth_user_id.eq(oauth_user_id))
|
||||
.select(Self::as_select())
|
||||
.first(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn list_admins_with_emails(pool: &mut DbPool<'_>) -> Result<Vec<Self>, Error> {
|
||||
queries().list(pool, ListMode::AdminsWithEmails).await
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
Self::joins()
|
||||
.filter(local_user::email.is_not_null())
|
||||
.filter(local_user::admin.eq(true))
|
||||
.select(Self::as_select())
|
||||
.load::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn create_test_user(
|
||||
|
|
|
@ -1,173 +1,71 @@
|
|||
use crate::structs::PersonView;
|
||||
use diesel::{
|
||||
pg::Pg,
|
||||
result::Error,
|
||||
BoolExpressionMethods,
|
||||
ExpressionMethods,
|
||||
NullableExpressionMethods,
|
||||
PgTextExpressionMethods,
|
||||
QueryDsl,
|
||||
SelectableHelper,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_db_schema::{
|
||||
newtypes::PersonId,
|
||||
schema::{local_user, person, person_aggregates},
|
||||
utils::{
|
||||
functions::coalesce,
|
||||
fuzzy_search,
|
||||
limit_and_offset,
|
||||
now,
|
||||
DbConn,
|
||||
DbPool,
|
||||
ListFn,
|
||||
Queries,
|
||||
ReadFn,
|
||||
},
|
||||
ListingType,
|
||||
PostSortType,
|
||||
utils::{get_conn, now, DbPool},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum::{Display, EnumString};
|
||||
|
||||
enum ListMode {
|
||||
Admins,
|
||||
Banned,
|
||||
Query(PersonQuery),
|
||||
}
|
||||
|
||||
#[derive(EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy)]
|
||||
/// The person sort types. Converted automatically from `SortType`
|
||||
enum PersonSortType {
|
||||
New,
|
||||
Old,
|
||||
MostComments,
|
||||
CommentScore,
|
||||
PostScore,
|
||||
PostCount,
|
||||
}
|
||||
|
||||
fn post_to_person_sort_type(sort: PostSortType) -> PersonSortType {
|
||||
use PostSortType::*;
|
||||
match sort {
|
||||
Active | Hot | Controversial => PersonSortType::CommentScore,
|
||||
New | NewComments => PersonSortType::New,
|
||||
MostComments => PersonSortType::MostComments,
|
||||
Old => PersonSortType::Old,
|
||||
_ => PersonSortType::CommentScore,
|
||||
}
|
||||
}
|
||||
|
||||
fn queries<'a>(
|
||||
) -> Queries<impl ReadFn<'a, PersonView, (PersonId, bool)>, impl ListFn<'a, PersonView, ListMode>> {
|
||||
let all_joins = move |query: person::BoxedQuery<'a, Pg>| {
|
||||
query
|
||||
.inner_join(person_aggregates::table)
|
||||
.left_join(local_user::table)
|
||||
.select((
|
||||
person::all_columns,
|
||||
person_aggregates::all_columns,
|
||||
coalesce(local_user::admin.nullable(), false),
|
||||
))
|
||||
};
|
||||
|
||||
let read = move |mut conn: DbConn<'a>, params: (PersonId, bool)| async move {
|
||||
let (person_id, is_admin) = params;
|
||||
let mut query = all_joins(person::table.find(person_id).into_boxed());
|
||||
if !is_admin {
|
||||
query = query.filter(person::deleted.eq(false));
|
||||
}
|
||||
query.first(&mut conn).await
|
||||
};
|
||||
|
||||
let list = move |mut conn: DbConn<'a>, mode: ListMode| async move {
|
||||
let mut query = all_joins(person::table.into_boxed()).filter(person::deleted.eq(false));
|
||||
match mode {
|
||||
ListMode::Admins => {
|
||||
query = query
|
||||
.filter(local_user::admin.eq(true))
|
||||
.filter(person::deleted.eq(false))
|
||||
.order_by(person::published);
|
||||
}
|
||||
ListMode::Banned => {
|
||||
query = query
|
||||
.filter(
|
||||
person::local.eq(true).and(
|
||||
person::banned.eq(true).and(
|
||||
person::ban_expires
|
||||
.is_null()
|
||||
.or(person::ban_expires.gt(now().nullable())),
|
||||
),
|
||||
),
|
||||
)
|
||||
.filter(person::deleted.eq(false));
|
||||
}
|
||||
ListMode::Query(o) => {
|
||||
if let Some(search_term) = o.search_term {
|
||||
let searcher = fuzzy_search(&search_term);
|
||||
query = query
|
||||
.filter(person::name.ilike(searcher.clone()))
|
||||
.or_filter(person::display_name.ilike(searcher));
|
||||
}
|
||||
|
||||
let sort = o.sort.map(post_to_person_sort_type);
|
||||
query = match sort.unwrap_or(PersonSortType::CommentScore) {
|
||||
PersonSortType::New => query.order_by(person::published.desc()),
|
||||
PersonSortType::Old => query.order_by(person::published.asc()),
|
||||
PersonSortType::MostComments => query.order_by(person_aggregates::comment_count.desc()),
|
||||
PersonSortType::CommentScore => query.order_by(person_aggregates::comment_score.desc()),
|
||||
PersonSortType::PostScore => query.order_by(person_aggregates::post_score.desc()),
|
||||
PersonSortType::PostCount => query.order_by(person_aggregates::post_count.desc()),
|
||||
};
|
||||
|
||||
let (limit, offset) = limit_and_offset(o.page, o.limit)?;
|
||||
query = query.limit(limit).offset(offset);
|
||||
|
||||
if let Some(listing_type) = o.listing_type {
|
||||
query = match listing_type {
|
||||
// return nothing as its not possible to follow users
|
||||
ListingType::Subscribed => query.limit(0),
|
||||
ListingType::Local => query.filter(person::local.eq(true)),
|
||||
_ => query,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
query.load::<PersonView>(&mut conn).await
|
||||
};
|
||||
|
||||
Queries::new(read, list)
|
||||
}
|
||||
|
||||
impl PersonView {
|
||||
#[diesel::dsl::auto_type(no_type_alias)]
|
||||
fn joins() -> _ {
|
||||
person::table
|
||||
.inner_join(person_aggregates::table)
|
||||
.left_join(local_user::table)
|
||||
}
|
||||
|
||||
pub async fn read(
|
||||
pool: &mut DbPool<'_>,
|
||||
person_id: PersonId,
|
||||
is_admin: bool,
|
||||
) -> Result<Self, Error> {
|
||||
queries().read(pool, (person_id, is_admin)).await
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let mut query = Self::joins()
|
||||
.filter(person::id.eq(person_id))
|
||||
.select(Self::as_select())
|
||||
.into_boxed();
|
||||
|
||||
if !is_admin {
|
||||
query = query.filter(person::deleted.eq(false))
|
||||
}
|
||||
|
||||
query.first(conn).await
|
||||
}
|
||||
|
||||
pub async fn admins(pool: &mut DbPool<'_>) -> Result<Vec<Self>, Error> {
|
||||
queries().list(pool, ListMode::Admins).await
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
Self::joins()
|
||||
.filter(person::deleted.eq(false))
|
||||
.filter(local_user::admin.eq(true))
|
||||
.order_by(person::published)
|
||||
.select(Self::as_select())
|
||||
.load::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn banned(pool: &mut DbPool<'_>) -> Result<Vec<Self>, Error> {
|
||||
queries().list(pool, ListMode::Banned).await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PersonQuery {
|
||||
pub sort: Option<PostSortType>,
|
||||
pub search_term: Option<String>,
|
||||
pub listing_type: Option<ListingType>,
|
||||
pub page: Option<i64>,
|
||||
pub limit: Option<i64>,
|
||||
}
|
||||
|
||||
impl PersonQuery {
|
||||
pub async fn list(self, pool: &mut DbPool<'_>) -> Result<Vec<PersonView>, Error> {
|
||||
queries().list(pool, ListMode::Query(self)).await
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
Self::joins()
|
||||
.filter(person::deleted.eq(false))
|
||||
.filter(
|
||||
person::banned.eq(true).and(
|
||||
person::ban_expires
|
||||
.is_null()
|
||||
.or(person::ban_expires.gt(now().nullable())),
|
||||
),
|
||||
)
|
||||
.order_by(person::published)
|
||||
.select(Self::as_select())
|
||||
.load::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -258,15 +156,6 @@ mod tests {
|
|||
let read = PersonView::read(pool, data.alice.id, true).await;
|
||||
assert!(read.is_ok());
|
||||
|
||||
let list = PersonQuery {
|
||||
sort: Some(PostSortType::New),
|
||||
..Default::default()
|
||||
}
|
||||
.list(pool)
|
||||
.await?;
|
||||
assert_length!(1, list);
|
||||
assert_eq!(list[0].person.id, data.bob.id);
|
||||
|
||||
cleanup(data, pool).await
|
||||
}
|
||||
|
||||
|
@ -323,31 +212,4 @@ mod tests {
|
|||
|
||||
cleanup(data, pool).await
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn listing_type() -> LemmyResult<()> {
|
||||
let pool = &build_db_pool_for_tests();
|
||||
let pool = &mut pool.into();
|
||||
let data = init_data(pool).await?;
|
||||
|
||||
let list = PersonQuery {
|
||||
listing_type: Some(ListingType::Local),
|
||||
..Default::default()
|
||||
}
|
||||
.list(pool)
|
||||
.await?;
|
||||
assert_length!(1, list);
|
||||
assert_eq!(list[0].person.id, data.alice.id);
|
||||
|
||||
let list = PersonQuery {
|
||||
listing_type: Some(ListingType::All),
|
||||
..Default::default()
|
||||
}
|
||||
.list(pool)
|
||||
.await?;
|
||||
assert_length!(2, list);
|
||||
|
||||
cleanup(data, pool).await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ use lemmy_db_schema::{
|
|||
aggregates::structs::{post_aggregates_keys as key, PostAggregates},
|
||||
aliases::creator_community_actions,
|
||||
impls::local_user::LocalUserOptionHelper,
|
||||
newtypes::{CommunityId, LocalUserId, PersonId, PostId},
|
||||
newtypes::{CommunityId, PersonId, PostId},
|
||||
schema::{
|
||||
community,
|
||||
community_actions,
|
||||
|
@ -42,9 +42,6 @@ use lemmy_db_schema::{
|
|||
site::Site,
|
||||
},
|
||||
utils::{
|
||||
action_query,
|
||||
actions,
|
||||
actions_alias,
|
||||
functions::coalesce,
|
||||
fuzzy_search,
|
||||
get_conn,
|
||||
|
@ -52,11 +49,7 @@ use lemmy_db_schema::{
|
|||
now,
|
||||
paginate,
|
||||
Commented,
|
||||
DbConn,
|
||||
DbPool,
|
||||
ListFn,
|
||||
Queries,
|
||||
ReadFn,
|
||||
ReverseTimestampKey,
|
||||
},
|
||||
CommunityVisibility,
|
||||
|
@ -66,37 +59,83 @@ use lemmy_db_schema::{
|
|||
use tracing::debug;
|
||||
use PostSortType::*;
|
||||
|
||||
type QueriesReadTypes<'a> = (PostId, Option<&'a LocalUser>, bool);
|
||||
type QueriesListTypes<'a> = (PostQuery<'a>, &'a Site);
|
||||
impl PostView {
|
||||
// TODO while we can abstract the joins into a function, the selects are currently impossible to
|
||||
// do, because they rely on a few types that aren't yet publicly exported in diesel:
|
||||
// https://github.com/diesel-rs/diesel/issues/4462
|
||||
|
||||
fn queries<'a>() -> Queries<
|
||||
impl ReadFn<'a, PostView, QueriesReadTypes<'a>>,
|
||||
impl ListFn<'a, PostView, QueriesListTypes<'a>>,
|
||||
> {
|
||||
let creator_is_admin = exists(
|
||||
local_user::table.filter(
|
||||
post_aggregates::creator_id
|
||||
.eq(local_user::person_id)
|
||||
.and(local_user::admin.eq(true)),
|
||||
),
|
||||
);
|
||||
#[diesel::dsl::auto_type(no_type_alias)]
|
||||
fn joins(my_person_id: Option<PersonId>) -> _ {
|
||||
let community_actions_join = community_actions::table.on(
|
||||
community_actions::community_id
|
||||
.eq(post_aggregates::community_id)
|
||||
.and(community_actions::person_id.nullable().eq(my_person_id)),
|
||||
);
|
||||
|
||||
let person_actions_join = person_actions::table.on(
|
||||
person_actions::target_id
|
||||
.eq(post_aggregates::creator_id)
|
||||
.and(person_actions::person_id.nullable().eq(my_person_id)),
|
||||
);
|
||||
|
||||
let post_actions_join = post_actions::table.on(
|
||||
post_actions::post_id
|
||||
.eq(post_aggregates::post_id)
|
||||
.and(post_actions::person_id.nullable().eq(my_person_id)),
|
||||
);
|
||||
|
||||
let instance_actions_join = instance_actions::table.on(
|
||||
instance_actions::instance_id
|
||||
.eq(post_aggregates::instance_id)
|
||||
.and(instance_actions::person_id.nullable().eq(my_person_id)),
|
||||
);
|
||||
|
||||
let post_creator_community_actions_join = creator_community_actions.on(
|
||||
creator_community_actions
|
||||
.field(community_actions::community_id)
|
||||
.eq(post_aggregates::community_id)
|
||||
.and(
|
||||
creator_community_actions
|
||||
.field(community_actions::person_id)
|
||||
.eq(post_aggregates::creator_id),
|
||||
),
|
||||
);
|
||||
|
||||
let image_details_join =
|
||||
image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable()));
|
||||
|
||||
post_aggregates::table
|
||||
.inner_join(person::table)
|
||||
.inner_join(community::table)
|
||||
.inner_join(post::table)
|
||||
.left_join(image_details_join)
|
||||
.left_join(community_actions_join)
|
||||
.left_join(person_actions_join)
|
||||
.left_join(post_actions_join)
|
||||
.left_join(instance_actions_join)
|
||||
.left_join(post_creator_community_actions_join)
|
||||
}
|
||||
|
||||
#[diesel::dsl::auto_type(no_type_alias)]
|
||||
fn creator_is_admin() -> _ {
|
||||
exists(
|
||||
local_user::table.filter(
|
||||
post_aggregates::creator_id
|
||||
.eq(local_user::person_id)
|
||||
.and(local_user::admin.eq(true)),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn read(
|
||||
pool: &mut DbPool<'_>,
|
||||
post_id: PostId,
|
||||
my_local_user: Option<&'_ LocalUser>,
|
||||
is_mod_or_admin: bool,
|
||||
) -> Result<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let my_person_id = my_local_user.person_id();
|
||||
|
||||
// TODO maybe this should go to localuser also
|
||||
let all_joins = move |query: post_aggregates::BoxedQuery<'a, Pg>,
|
||||
my_person_id: Option<PersonId>| {
|
||||
// We fetch post tags by letting postgresql aggregate them internally in a subquery into JSON.
|
||||
// This is a simple way to join m rows into n rows without duplicating the data and getting
|
||||
// complex diesel types. In pure SQL you would usually do this either using a LEFT JOIN + then
|
||||
// aggregating the results in the application code. But this results in a lot of duplicate
|
||||
// data transferred (since each post will be returned once per tag that it has) and more
|
||||
// complicated application code. The diesel docs suggest doing three separate sequential queries
|
||||
// in this case (see https://diesel.rs/guides/relations.html#many-to-many-or-mn ): First fetch
|
||||
// the posts, then fetch all relevant post-tag-association tuples from the db, and then fetch
|
||||
// all the relevant tag objects.
|
||||
//
|
||||
// If we want to filter by post tag we will have to add
|
||||
// separate logic below since this subquery can't affect filtering, but it is simple (`WHERE
|
||||
// exists (select 1 from post_community_post_tags where community_post_tag_id in (1,2,3,4)`).
|
||||
let post_tags = post_tag::table
|
||||
.inner_join(tag::table)
|
||||
.select(diesel::dsl::sql::<diesel::sql_types::Json>(
|
||||
|
@ -106,36 +145,8 @@ fn queries<'a>() -> Queries<
|
|||
.filter(tag::deleted.eq(false))
|
||||
.single_value();
|
||||
|
||||
query
|
||||
.inner_join(person::table)
|
||||
.inner_join(community::table)
|
||||
.inner_join(post::table)
|
||||
.left_join(image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable())))
|
||||
.left_join(actions(
|
||||
community_actions::table,
|
||||
my_person_id,
|
||||
post_aggregates::community_id,
|
||||
))
|
||||
.left_join(actions(
|
||||
person_actions::table,
|
||||
my_person_id,
|
||||
post_aggregates::creator_id,
|
||||
))
|
||||
.left_join(actions(
|
||||
post_actions::table,
|
||||
my_person_id,
|
||||
post_aggregates::post_id,
|
||||
))
|
||||
.left_join(actions(
|
||||
instance_actions::table,
|
||||
my_person_id,
|
||||
post_aggregates::instance_id,
|
||||
))
|
||||
.left_join(actions_alias(
|
||||
creator_community_actions,
|
||||
post_aggregates::creator_id,
|
||||
post_aggregates::community_id,
|
||||
))
|
||||
let mut query = Self::joins(my_person_id)
|
||||
.filter(post_aggregates::post_id.eq(post_id))
|
||||
.select((
|
||||
post::all_columns,
|
||||
person::all_columns,
|
||||
|
@ -150,7 +161,7 @@ fn queries<'a>() -> Queries<
|
|||
.field(community_actions::became_moderator)
|
||||
.nullable()
|
||||
.is_not_null(),
|
||||
creator_is_admin,
|
||||
Self::creator_is_admin(),
|
||||
post_aggregates::all_columns,
|
||||
CommunityFollower::select_subscribed_type(),
|
||||
post_actions::saved.nullable().is_not_null(),
|
||||
|
@ -164,24 +175,7 @@ fn queries<'a>() -> Queries<
|
|||
),
|
||||
post_tags,
|
||||
))
|
||||
};
|
||||
|
||||
let read = move |mut conn: DbConn<'a>,
|
||||
(post_id, my_local_user, is_mod_or_admin): (
|
||||
PostId,
|
||||
Option<&'a LocalUser>,
|
||||
bool,
|
||||
)| async move {
|
||||
// The left join below will return None in this case
|
||||
let my_person_id = my_local_user.person_id();
|
||||
let person_id_join = my_person_id.unwrap_or(PersonId(-1));
|
||||
|
||||
let mut query = all_joins(
|
||||
post_aggregates::table
|
||||
.filter(post_aggregates::post_id.eq(post_id))
|
||||
.into_boxed(),
|
||||
my_person_id,
|
||||
);
|
||||
.into_boxed();
|
||||
|
||||
// Hide deleted and removed for non-admins or mods
|
||||
if !is_mod_or_admin {
|
||||
|
@ -189,23 +183,23 @@ fn queries<'a>() -> Queries<
|
|||
.filter(
|
||||
community::removed
|
||||
.eq(false)
|
||||
.or(post::creator_id.eq(person_id_join)),
|
||||
.or(post::creator_id.nullable().eq(my_person_id)),
|
||||
)
|
||||
.filter(
|
||||
post::removed
|
||||
.eq(false)
|
||||
.or(post::creator_id.eq(person_id_join)),
|
||||
.or(post::creator_id.nullable().eq(my_person_id)),
|
||||
)
|
||||
// users can see their own deleted posts
|
||||
.filter(
|
||||
community::deleted
|
||||
.eq(false)
|
||||
.or(post::creator_id.eq(person_id_join)),
|
||||
.or(post::creator_id.nullable().eq(my_person_id)),
|
||||
)
|
||||
.filter(
|
||||
post::deleted
|
||||
.eq(false)
|
||||
.or(post::creator_id.eq(person_id_join)),
|
||||
.or(post::creator_id.nullable().eq(my_person_id)),
|
||||
)
|
||||
// private communities can only by browsed by accepted followers
|
||||
.filter(
|
||||
|
@ -219,18 +213,216 @@ fn queries<'a>() -> Queries<
|
|||
|
||||
Commented::new(query)
|
||||
.text("PostView::read")
|
||||
.first(&mut conn)
|
||||
.first(conn)
|
||||
.await
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let list = move |mut conn: DbConn<'a>, (o, site): (PostQuery<'a>, &'a Site)| async move {
|
||||
// The left join below will return None in this case
|
||||
let local_user_id_join = o.local_user.local_user_id().unwrap_or(LocalUserId(-1));
|
||||
|
||||
let mut query = all_joins(
|
||||
post_aggregates::table.into_boxed(),
|
||||
o.local_user.person_id(),
|
||||
impl PaginationCursor {
|
||||
// get cursor for page that starts immediately after the given post
|
||||
pub fn after_post(view: &PostView) -> PaginationCursor {
|
||||
// hex encoding to prevent ossification
|
||||
PaginationCursor(format!("P{:x}", view.counts.post_id.0))
|
||||
}
|
||||
pub async fn read(
|
||||
&self,
|
||||
pool: &mut DbPool<'_>,
|
||||
local_user: Option<&LocalUser>,
|
||||
) -> Result<PaginationCursorData, Error> {
|
||||
let err_msg = || Error::QueryBuilderError("Could not parse pagination token".into());
|
||||
let post_id = PostId(
|
||||
self
|
||||
.0
|
||||
.get(1..)
|
||||
.and_then(|e| i32::from_str_radix(e, 16).ok())
|
||||
.ok_or_else(err_msg)?,
|
||||
);
|
||||
let post_aggregates = PostAggregates::read(pool, post_id).await?;
|
||||
let post_actions = PostActionsCursor::read(pool, post_id, local_user.person_id()).await?;
|
||||
|
||||
Ok(PaginationCursorData {
|
||||
post_aggregates,
|
||||
post_actions,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// currently we use aggregates or actions as the pagination token.
|
||||
// we only use some of the properties, depending on which sort type we page by
|
||||
#[derive(Clone)]
|
||||
pub struct PaginationCursorData {
|
||||
post_aggregates: PostAggregates,
|
||||
post_actions: PostActionsCursor,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PostQuery<'a> {
|
||||
pub listing_type: Option<ListingType>,
|
||||
pub sort: Option<PostSortType>,
|
||||
pub creator_id: Option<PersonId>,
|
||||
pub community_id: Option<CommunityId>,
|
||||
// if true, the query should be handled as if community_id was not given except adding the
|
||||
// literal filter
|
||||
pub community_id_just_for_prefetch: bool,
|
||||
pub local_user: Option<&'a LocalUser>,
|
||||
pub search_term: Option<String>,
|
||||
pub url_only: Option<bool>,
|
||||
pub read_only: Option<bool>,
|
||||
pub liked_only: Option<bool>,
|
||||
pub disliked_only: Option<bool>,
|
||||
pub title_only: Option<bool>,
|
||||
pub page: Option<i64>,
|
||||
pub limit: Option<i64>,
|
||||
pub page_after: Option<PaginationCursorData>,
|
||||
pub page_before_or_equal: Option<PostAggregates>,
|
||||
pub page_back: Option<bool>,
|
||||
pub show_hidden: Option<bool>,
|
||||
pub show_read: Option<bool>,
|
||||
pub show_nsfw: Option<bool>,
|
||||
pub hide_media: Option<bool>,
|
||||
pub no_comments_only: Option<bool>,
|
||||
}
|
||||
|
||||
impl<'a> PostQuery<'a> {
|
||||
#[allow(clippy::expect_used)]
|
||||
async fn prefetch_upper_bound_for_page_before(
|
||||
&self,
|
||||
site: &Site,
|
||||
pool: &mut DbPool<'_>,
|
||||
) -> Result<Option<PostQuery<'a>>, Error> {
|
||||
// first get one page for the most popular community to get an upper bound for the page end for
|
||||
// the real query. the reason this is needed is that when fetching posts for a single
|
||||
// community PostgreSQL can optimize the query to use an index on e.g. (=, >=, >=, >=) and
|
||||
// fetch only LIMIT rows but for the followed-communities query it has to query the index on
|
||||
// (IN, >=, >=, >=) which it currently can't do at all (as of PG 16). see the discussion
|
||||
// here: https://github.com/LemmyNet/lemmy/issues/2877#issuecomment-1673597190
|
||||
//
|
||||
// the results are correct no matter which community we fetch these for, since it basically
|
||||
// covers the "worst case" of the whole page consisting of posts from one community
|
||||
// but using the largest community decreases the pagination-frame so make the real query more
|
||||
// efficient.
|
||||
use lemmy_db_schema::schema::community_aggregates::dsl::{
|
||||
community_aggregates,
|
||||
community_id,
|
||||
users_active_month,
|
||||
};
|
||||
let (limit, offset) = limit_and_offset(self.page, self.limit)?;
|
||||
if offset != 0 && self.page_after.is_some() {
|
||||
return Err(Error::QueryBuilderError(
|
||||
"legacy pagination cannot be combined with v2 pagination".into(),
|
||||
));
|
||||
}
|
||||
let self_person_id = self.local_user.expect("part of the above if").person_id;
|
||||
let largest_subscribed = {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
community_actions::table
|
||||
.filter(community_actions::followed.is_not_null())
|
||||
.filter(community_actions::person_id.eq(self_person_id))
|
||||
.inner_join(community_aggregates.on(community_id.eq(community_actions::community_id)))
|
||||
.order_by(users_active_month.desc())
|
||||
.select(community_id)
|
||||
.limit(1)
|
||||
.get_result::<CommunityId>(conn)
|
||||
.await
|
||||
.optional()?
|
||||
};
|
||||
let Some(largest_subscribed) = largest_subscribed else {
|
||||
// nothing subscribed to? no posts
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let mut v = Box::pin(
|
||||
PostQuery {
|
||||
community_id: Some(largest_subscribed),
|
||||
community_id_just_for_prefetch: true,
|
||||
..self.clone()
|
||||
}
|
||||
.list(site, pool),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// take last element of array. if this query returned less than LIMIT elements,
|
||||
// the heuristic is invalid since we can't guarantee the full query will return >= LIMIT results
|
||||
// (return original query)
|
||||
if (v.len() as i64) < limit {
|
||||
Ok(Some(self.clone()))
|
||||
} else {
|
||||
let item = if self.page_back.unwrap_or_default() {
|
||||
// for backward pagination, get first element instead
|
||||
v.into_iter().next()
|
||||
} else {
|
||||
v.pop()
|
||||
};
|
||||
let limit_cursor = Some(item.expect("else case").counts);
|
||||
Ok(Some(PostQuery {
|
||||
page_before_or_equal: limit_cursor,
|
||||
..self.clone()
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn list(self, site: &Site, pool: &mut DbPool<'_>) -> Result<Vec<PostView>, Error> {
|
||||
let o = if self.listing_type == Some(ListingType::Subscribed)
|
||||
&& self.community_id.is_none()
|
||||
&& self.local_user.is_some()
|
||||
&& self.page_before_or_equal.is_none()
|
||||
{
|
||||
if let Some(query) = self
|
||||
.prefetch_upper_bound_for_page_before(site, pool)
|
||||
.await?
|
||||
{
|
||||
query
|
||||
} else {
|
||||
self
|
||||
}
|
||||
} else {
|
||||
self
|
||||
};
|
||||
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
|
||||
let my_person_id = o.local_user.person_id();
|
||||
let my_local_user_id = o.local_user.local_user_id();
|
||||
|
||||
let post_tags = post_tag::table
|
||||
.inner_join(tag::table)
|
||||
.select(diesel::dsl::sql::<diesel::sql_types::Json>(
|
||||
"json_agg(tag.*)",
|
||||
))
|
||||
.filter(post_tag::post_id.eq(post_aggregates::post_id))
|
||||
.filter(tag::deleted.eq(false))
|
||||
.single_value();
|
||||
|
||||
let mut query = PostView::joins(my_person_id)
|
||||
.select((
|
||||
post::all_columns,
|
||||
person::all_columns,
|
||||
community::all_columns,
|
||||
image_details::all_columns.nullable(),
|
||||
creator_community_actions
|
||||
.field(community_actions::received_ban)
|
||||
.nullable()
|
||||
.is_not_null(),
|
||||
community_actions::received_ban.nullable().is_not_null(),
|
||||
creator_community_actions
|
||||
.field(community_actions::became_moderator)
|
||||
.nullable()
|
||||
.is_not_null(),
|
||||
PostView::creator_is_admin(),
|
||||
post_aggregates::all_columns,
|
||||
CommunityFollower::select_subscribed_type(),
|
||||
post_actions::saved.nullable().is_not_null(),
|
||||
post_actions::read.nullable().is_not_null(),
|
||||
post_actions::hidden.nullable().is_not_null(),
|
||||
person_actions::blocked.nullable().is_not_null(),
|
||||
post_actions::like_score.nullable(),
|
||||
coalesce(
|
||||
post_aggregates::comments.nullable() - post_actions::read_comments_amount.nullable(),
|
||||
post_aggregates::comments,
|
||||
),
|
||||
post_tags,
|
||||
))
|
||||
.into_boxed();
|
||||
|
||||
// hide posts from deleted communities
|
||||
query = query.filter(community::deleted.eq(false));
|
||||
|
@ -360,9 +552,11 @@ fn queries<'a>() -> Queries<
|
|||
if o.local_user.is_some() {
|
||||
query = query.filter(exists(
|
||||
local_user_language::table.filter(
|
||||
post::language_id
|
||||
.eq(local_user_language::language_id)
|
||||
.and(local_user_language::local_user_id.eq(local_user_id_join)),
|
||||
post::language_id.eq(local_user_language::language_id).and(
|
||||
local_user_language::local_user_id
|
||||
.nullable()
|
||||
.eq(my_local_user_id),
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
@ -448,188 +642,8 @@ fn queries<'a>() -> Queries<
|
|||
"getting upper bound for next query",
|
||||
o.community_id_just_for_prefetch,
|
||||
)
|
||||
.load::<PostView>(&mut conn)
|
||||
.load::<PostView>(conn)
|
||||
.await
|
||||
};
|
||||
|
||||
Queries::new(read, list)
|
||||
}
|
||||
|
||||
impl PostView {
|
||||
pub async fn read(
|
||||
pool: &mut DbPool<'_>,
|
||||
post_id: PostId,
|
||||
my_local_user: Option<&'_ LocalUser>,
|
||||
is_mod_or_admin: bool,
|
||||
) -> Result<Self, Error> {
|
||||
queries()
|
||||
.read(pool, (post_id, my_local_user, is_mod_or_admin))
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl PaginationCursor {
|
||||
// get cursor for page that starts immediately after the given post
|
||||
pub fn after_post(view: &PostView) -> PaginationCursor {
|
||||
// hex encoding to prevent ossification
|
||||
PaginationCursor(format!("P{:x}", view.counts.post_id.0))
|
||||
}
|
||||
pub async fn read(
|
||||
&self,
|
||||
pool: &mut DbPool<'_>,
|
||||
local_user: Option<&LocalUser>,
|
||||
) -> Result<PaginationCursorData, Error> {
|
||||
let err_msg = || Error::QueryBuilderError("Could not parse pagination token".into());
|
||||
let post_id = PostId(
|
||||
self
|
||||
.0
|
||||
.get(1..)
|
||||
.and_then(|e| i32::from_str_radix(e, 16).ok())
|
||||
.ok_or_else(err_msg)?,
|
||||
);
|
||||
let post_aggregates = PostAggregates::read(pool, post_id).await?;
|
||||
let post_actions = PostActionsCursor::read(pool, post_id, local_user.person_id()).await?;
|
||||
|
||||
Ok(PaginationCursorData {
|
||||
post_aggregates,
|
||||
post_actions,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// currently we use aggregates or actions as the pagination token.
|
||||
// we only use some of the properties, depending on which sort type we page by
|
||||
#[derive(Clone)]
|
||||
pub struct PaginationCursorData {
|
||||
post_aggregates: PostAggregates,
|
||||
post_actions: PostActionsCursor,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PostQuery<'a> {
|
||||
pub listing_type: Option<ListingType>,
|
||||
pub sort: Option<PostSortType>,
|
||||
pub creator_id: Option<PersonId>,
|
||||
pub community_id: Option<CommunityId>,
|
||||
// if true, the query should be handled as if community_id was not given except adding the
|
||||
// literal filter
|
||||
pub community_id_just_for_prefetch: bool,
|
||||
pub local_user: Option<&'a LocalUser>,
|
||||
pub search_term: Option<String>,
|
||||
pub url_only: Option<bool>,
|
||||
pub read_only: Option<bool>,
|
||||
pub liked_only: Option<bool>,
|
||||
pub disliked_only: Option<bool>,
|
||||
pub title_only: Option<bool>,
|
||||
pub page: Option<i64>,
|
||||
pub limit: Option<i64>,
|
||||
pub page_after: Option<PaginationCursorData>,
|
||||
pub page_before_or_equal: Option<PostAggregates>,
|
||||
pub page_back: Option<bool>,
|
||||
pub show_hidden: Option<bool>,
|
||||
pub show_read: Option<bool>,
|
||||
pub show_nsfw: Option<bool>,
|
||||
pub hide_media: Option<bool>,
|
||||
pub no_comments_only: Option<bool>,
|
||||
}
|
||||
|
||||
impl<'a> PostQuery<'a> {
|
||||
#[allow(clippy::expect_used)]
|
||||
async fn prefetch_upper_bound_for_page_before(
|
||||
&self,
|
||||
site: &Site,
|
||||
pool: &mut DbPool<'_>,
|
||||
) -> Result<Option<PostQuery<'a>>, Error> {
|
||||
// first get one page for the most popular community to get an upper bound for the page end for
|
||||
// the real query. the reason this is needed is that when fetching posts for a single
|
||||
// community PostgreSQL can optimize the query to use an index on e.g. (=, >=, >=, >=) and
|
||||
// fetch only LIMIT rows but for the followed-communities query it has to query the index on
|
||||
// (IN, >=, >=, >=) which it currently can't do at all (as of PG 16). see the discussion
|
||||
// here: https://github.com/LemmyNet/lemmy/issues/2877#issuecomment-1673597190
|
||||
//
|
||||
// the results are correct no matter which community we fetch these for, since it basically
|
||||
// covers the "worst case" of the whole page consisting of posts from one community
|
||||
// but using the largest community decreases the pagination-frame so make the real query more
|
||||
// efficient.
|
||||
use lemmy_db_schema::schema::community_aggregates::dsl::{
|
||||
community_aggregates,
|
||||
community_id,
|
||||
users_active_month,
|
||||
};
|
||||
let (limit, offset) = limit_and_offset(self.page, self.limit)?;
|
||||
if offset != 0 && self.page_after.is_some() {
|
||||
return Err(Error::QueryBuilderError(
|
||||
"legacy pagination cannot be combined with v2 pagination".into(),
|
||||
));
|
||||
}
|
||||
let self_person_id = self.local_user.expect("part of the above if").person_id;
|
||||
let largest_subscribed = {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
action_query(community_actions::followed)
|
||||
.filter(community_actions::person_id.eq(self_person_id))
|
||||
.inner_join(community_aggregates.on(community_id.eq(community_actions::community_id)))
|
||||
.order_by(users_active_month.desc())
|
||||
.select(community_id)
|
||||
.limit(1)
|
||||
.get_result::<CommunityId>(conn)
|
||||
.await
|
||||
.optional()?
|
||||
};
|
||||
let Some(largest_subscribed) = largest_subscribed else {
|
||||
// nothing subscribed to? no posts
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let mut v = queries()
|
||||
.list(
|
||||
pool,
|
||||
(
|
||||
PostQuery {
|
||||
community_id: Some(largest_subscribed),
|
||||
community_id_just_for_prefetch: true,
|
||||
..self.clone()
|
||||
},
|
||||
site,
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
// take last element of array. if this query returned less than LIMIT elements,
|
||||
// the heuristic is invalid since we can't guarantee the full query will return >= LIMIT results
|
||||
// (return original query)
|
||||
if (v.len() as i64) < limit {
|
||||
Ok(Some(self.clone()))
|
||||
} else {
|
||||
let item = if self.page_back.unwrap_or_default() {
|
||||
// for backward pagination, get first element instead
|
||||
v.into_iter().next()
|
||||
} else {
|
||||
v.pop()
|
||||
};
|
||||
let limit_cursor = Some(item.expect("else case").counts);
|
||||
Ok(Some(PostQuery {
|
||||
page_before_or_equal: limit_cursor,
|
||||
..self.clone()
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn list(self, site: &Site, pool: &mut DbPool<'_>) -> Result<Vec<PostView>, Error> {
|
||||
if self.listing_type == Some(ListingType::Subscribed)
|
||||
&& self.community_id.is_none()
|
||||
&& self.local_user.is_some()
|
||||
&& self.page_before_or_equal.is_none()
|
||||
{
|
||||
if let Some(query) = self
|
||||
.prefetch_upper_bound_for_page_before(site, pool)
|
||||
.await?
|
||||
{
|
||||
queries().list(pool, (query, site)).await
|
||||
} else {
|
||||
Ok(vec![])
|
||||
}
|
||||
} else {
|
||||
queries().list(pool, (self, site)).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,41 +1,56 @@
|
|||
use crate::structs::PrivateMessageView;
|
||||
use diesel::{result::Error, ExpressionMethods, JoinOnDsl, QueryDsl};
|
||||
use diesel::{
|
||||
result::Error,
|
||||
BoolExpressionMethods,
|
||||
ExpressionMethods,
|
||||
JoinOnDsl,
|
||||
QueryDsl,
|
||||
SelectableHelper,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_db_schema::{
|
||||
aliases,
|
||||
newtypes::PrivateMessageId,
|
||||
schema::{instance_actions, person, person_actions, private_message},
|
||||
utils::{actions, get_conn, DbPool},
|
||||
utils::{get_conn, DbPool},
|
||||
};
|
||||
|
||||
impl PrivateMessageView {
|
||||
#[diesel::dsl::auto_type(no_type_alias)]
|
||||
fn joins() -> _ {
|
||||
let recipient_id = aliases::person1.field(person::id);
|
||||
|
||||
let person_actions_join = person_actions::table.on(
|
||||
person_actions::target_id
|
||||
.eq(private_message::creator_id)
|
||||
.and(person_actions::person_id.eq(recipient_id)),
|
||||
);
|
||||
|
||||
let instance_actions_join = instance_actions::table.on(
|
||||
instance_actions::instance_id
|
||||
.eq(person::instance_id)
|
||||
.and(instance_actions::person_id.eq(recipient_id)),
|
||||
);
|
||||
|
||||
let creator_join = person::table.on(private_message::creator_id.eq(person::id));
|
||||
|
||||
let recipient_join = aliases::person1.on(private_message::recipient_id.eq(recipient_id));
|
||||
|
||||
private_message::table
|
||||
.inner_join(creator_join)
|
||||
.inner_join(recipient_join)
|
||||
.left_join(person_actions_join)
|
||||
.left_join(instance_actions_join)
|
||||
}
|
||||
|
||||
pub async fn read(
|
||||
pool: &mut DbPool<'_>,
|
||||
private_message_id: PrivateMessageId,
|
||||
) -> Result<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
|
||||
private_message::table
|
||||
.find(private_message_id)
|
||||
.inner_join(person::table.on(private_message::creator_id.eq(person::id)))
|
||||
.inner_join(
|
||||
aliases::person1.on(private_message::recipient_id.eq(aliases::person1.field(person::id))),
|
||||
)
|
||||
.left_join(actions(
|
||||
person_actions::table,
|
||||
Some(aliases::person1.field(person::id)),
|
||||
private_message::creator_id,
|
||||
))
|
||||
.left_join(actions(
|
||||
instance_actions::table,
|
||||
Some(aliases::person1.field(person::id)),
|
||||
person::instance_id,
|
||||
))
|
||||
.select((
|
||||
private_message::all_columns,
|
||||
person::all_columns,
|
||||
aliases::person1.fields(person::all_columns),
|
||||
))
|
||||
Self::joins()
|
||||
.filter(private_message::id.eq(private_message_id))
|
||||
.select(Self::as_select())
|
||||
.first(conn)
|
||||
.await
|
||||
}
|
||||
|
|
|
@ -1,119 +1,69 @@
|
|||
use crate::structs::RegistrationApplicationView;
|
||||
use diesel::{
|
||||
dsl::count,
|
||||
pg::Pg,
|
||||
result::Error,
|
||||
ExpressionMethods,
|
||||
JoinOnDsl,
|
||||
NullableExpressionMethods,
|
||||
QueryDsl,
|
||||
SelectableHelper,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_db_schema::{
|
||||
aliases,
|
||||
newtypes::{PersonId, RegistrationApplicationId},
|
||||
schema::{local_user, person, registration_application},
|
||||
utils::{get_conn, limit_and_offset, DbConn, DbPool, ListFn, Queries, ReadFn},
|
||||
source::registration_application::RegistrationApplication,
|
||||
utils::{get_conn, limit_and_offset, DbPool},
|
||||
};
|
||||
|
||||
enum ReadBy {
|
||||
Id(RegistrationApplicationId),
|
||||
Person(PersonId),
|
||||
}
|
||||
|
||||
fn queries<'a>() -> Queries<
|
||||
impl ReadFn<'a, RegistrationApplicationView, ReadBy>,
|
||||
impl ListFn<'a, RegistrationApplicationView, RegistrationApplicationQuery>,
|
||||
> {
|
||||
let all_joins = |query: registration_application::BoxedQuery<'a, Pg>| {
|
||||
query
|
||||
impl RegistrationApplicationView {
|
||||
#[diesel::dsl::auto_type(no_type_alias)]
|
||||
fn joins() -> _ {
|
||||
registration_application::table
|
||||
.inner_join(local_user::table.on(registration_application::local_user_id.eq(local_user::id)))
|
||||
.inner_join(person::table.on(local_user::person_id.eq(person::id)))
|
||||
.left_join(
|
||||
aliases::person1
|
||||
.on(registration_application::admin_id.eq(aliases::person1.field(person::id).nullable())),
|
||||
)
|
||||
.order_by(registration_application::published.desc())
|
||||
.select((
|
||||
registration_application::all_columns,
|
||||
local_user::all_columns,
|
||||
person::all_columns,
|
||||
aliases::person1.fields(person::all_columns).nullable(),
|
||||
))
|
||||
};
|
||||
}
|
||||
|
||||
let read = move |mut conn: DbConn<'a>, search: ReadBy| async move {
|
||||
let mut query = all_joins(registration_application::table.into_boxed());
|
||||
|
||||
query = match search {
|
||||
ReadBy::Id(id) => query.filter(registration_application::id.eq(id)),
|
||||
ReadBy::Person(person_id) => query.filter(person::id.eq(person_id)),
|
||||
};
|
||||
|
||||
query.first(&mut conn).await
|
||||
};
|
||||
|
||||
let list = move |mut conn: DbConn<'a>, o: RegistrationApplicationQuery| async move {
|
||||
let mut query = all_joins(registration_application::table.into_boxed());
|
||||
|
||||
// If viewing all applications, order by newest, but if viewing unresolved only, show the oldest
|
||||
// first (FIFO)
|
||||
if o.unread_only {
|
||||
query = query
|
||||
.filter(registration_application::admin_id.is_null())
|
||||
.order_by(registration_application::published.asc());
|
||||
} else {
|
||||
query = query.order_by(registration_application::published.desc());
|
||||
}
|
||||
|
||||
if o.verified_email_only {
|
||||
query = query.filter(local_user::email_verified.eq(true))
|
||||
}
|
||||
|
||||
let (limit, offset) = limit_and_offset(o.page, o.limit)?;
|
||||
|
||||
query = query.limit(limit).offset(offset);
|
||||
|
||||
query.load::<RegistrationApplicationView>(&mut conn).await
|
||||
};
|
||||
|
||||
Queries::new(read, list)
|
||||
}
|
||||
|
||||
impl RegistrationApplicationView {
|
||||
pub async fn read(pool: &mut DbPool<'_>, id: RegistrationApplicationId) -> Result<Self, Error> {
|
||||
queries().read(pool, ReadBy::Id(id)).await
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
Self::joins()
|
||||
.filter(registration_application::id.eq(id))
|
||||
.select(Self::as_select())
|
||||
.first(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn read_by_person(pool: &mut DbPool<'_>, person_id: PersonId) -> Result<Self, Error> {
|
||||
queries().read(pool, ReadBy::Person(person_id)).await
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
Self::joins()
|
||||
.filter(person::id.eq(person_id))
|
||||
.select(Self::as_select())
|
||||
.first(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Returns the current unread registration_application count
|
||||
pub async fn get_unread_count(
|
||||
pool: &mut DbPool<'_>,
|
||||
verified_email_only: bool,
|
||||
) -> Result<i64, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let person_alias_1 = diesel::alias!(person as person1);
|
||||
|
||||
let mut query = registration_application::table
|
||||
.inner_join(local_user::table.on(registration_application::local_user_id.eq(local_user::id)))
|
||||
.inner_join(person::table.on(local_user::person_id.eq(person::id)))
|
||||
.left_join(
|
||||
person_alias_1
|
||||
.on(registration_application::admin_id.eq(person_alias_1.field(person::id).nullable())),
|
||||
)
|
||||
.filter(registration_application::admin_id.is_null())
|
||||
let mut query = Self::joins()
|
||||
.filter(RegistrationApplication::is_unread())
|
||||
.select(count(registration_application::id))
|
||||
.into_boxed();
|
||||
|
||||
if verified_email_only {
|
||||
query = query.filter(local_user::email_verified.eq(true))
|
||||
}
|
||||
|
||||
query
|
||||
.select(count(registration_application::id))
|
||||
.first::<i64>(conn)
|
||||
.await
|
||||
query.first::<i64>(conn).await
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,7 +80,32 @@ impl RegistrationApplicationQuery {
|
|||
self,
|
||||
pool: &mut DbPool<'_>,
|
||||
) -> Result<Vec<RegistrationApplicationView>, Error> {
|
||||
queries().list(pool, self).await
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let o = self;
|
||||
|
||||
let mut query = RegistrationApplicationView::joins()
|
||||
.select(RegistrationApplicationView::as_select())
|
||||
.into_boxed();
|
||||
|
||||
if o.unread_only {
|
||||
query = query
|
||||
.filter(RegistrationApplication::is_unread())
|
||||
.order_by(registration_application::published.asc());
|
||||
} else {
|
||||
query = query.order_by(registration_application::published.desc());
|
||||
}
|
||||
|
||||
if o.verified_email_only {
|
||||
query = query.filter(local_user::email_verified.eq(true))
|
||||
}
|
||||
|
||||
let (limit, offset) = limit_and_offset(o.page, o.limit)?;
|
||||
|
||||
query
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
.load::<RegistrationApplicationView>(conn)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,10 +25,78 @@ use lemmy_db_schema::{
|
|||
post,
|
||||
},
|
||||
source::community::CommunityFollower,
|
||||
utils::{actions, actions_alias, functions::coalesce, get_conn, DbPool},
|
||||
utils::{functions::coalesce, get_conn, DbPool},
|
||||
};
|
||||
|
||||
impl CommentReportView {
|
||||
#[diesel::dsl::auto_type(no_type_alias)]
|
||||
fn joins(my_person_id: PersonId) -> _ {
|
||||
let recipient_id = aliases::person1.field(person::id);
|
||||
let resolver_id = aliases::person2.field(person::id);
|
||||
|
||||
let post_join = post::table.on(comment::post_id.eq(post::id));
|
||||
|
||||
let community_join = community::table.on(post::community_id.eq(community::id));
|
||||
|
||||
let report_creator_join = person::table.on(comment_report::creator_id.eq(person::id));
|
||||
|
||||
let local_user_join = local_user::table.on(
|
||||
comment::creator_id
|
||||
.eq(local_user::person_id)
|
||||
.and(local_user::admin.eq(true)),
|
||||
);
|
||||
|
||||
let comment_creator_join = aliases::person1.on(comment::creator_id.eq(recipient_id));
|
||||
|
||||
let comment_aggregates_join =
|
||||
comment_aggregates::table.on(comment_report::comment_id.eq(comment_aggregates::comment_id));
|
||||
|
||||
let comment_actions_join = comment_actions::table.on(
|
||||
comment_actions::comment_id
|
||||
.eq(comment_report::comment_id)
|
||||
.and(comment_actions::person_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
let resolver_join = aliases::person2.on(comment_report::resolver_id.eq(resolver_id.nullable()));
|
||||
|
||||
let creator_community_actions_join = creator_community_actions.on(
|
||||
creator_community_actions
|
||||
.field(community_actions::community_id)
|
||||
.eq(post::community_id)
|
||||
.and(
|
||||
creator_community_actions
|
||||
.field(community_actions::person_id)
|
||||
.eq(comment::creator_id),
|
||||
),
|
||||
);
|
||||
|
||||
let person_actions_join = person_actions::table.on(
|
||||
person_actions::target_id
|
||||
.eq(comment::creator_id)
|
||||
.and(person_actions::person_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
let community_actions_join = community_actions::table.on(
|
||||
community_actions::community_id
|
||||
.eq(post::community_id)
|
||||
.and(community_actions::person_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
comment_report::table
|
||||
.inner_join(comment::table)
|
||||
.inner_join(post_join)
|
||||
.inner_join(community_join)
|
||||
.inner_join(report_creator_join)
|
||||
.inner_join(comment_creator_join)
|
||||
.inner_join(comment_aggregates_join)
|
||||
.left_join(comment_actions_join)
|
||||
.left_join(resolver_join)
|
||||
.left_join(creator_community_actions_join)
|
||||
.left_join(local_user_join)
|
||||
.left_join(person_actions_join)
|
||||
.left_join(community_actions_join)
|
||||
}
|
||||
|
||||
/// returns the CommentReportView for the provided report_id
|
||||
///
|
||||
/// * `report_id` - the report id to obtain
|
||||
|
@ -38,47 +106,8 @@ impl CommentReportView {
|
|||
my_person_id: PersonId,
|
||||
) -> Result<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
comment_report::table
|
||||
.find(report_id)
|
||||
.inner_join(comment::table)
|
||||
.inner_join(post::table.on(comment::post_id.eq(post::id)))
|
||||
.inner_join(community::table.on(post::community_id.eq(community::id)))
|
||||
.inner_join(person::table.on(comment_report::creator_id.eq(person::id)))
|
||||
.inner_join(aliases::person1.on(comment::creator_id.eq(aliases::person1.field(person::id))))
|
||||
.inner_join(
|
||||
comment_aggregates::table.on(comment_report::comment_id.eq(comment_aggregates::comment_id)),
|
||||
)
|
||||
.left_join(actions(
|
||||
comment_actions::table,
|
||||
Some(my_person_id),
|
||||
comment_report::comment_id,
|
||||
))
|
||||
.left_join(
|
||||
aliases::person2
|
||||
.on(comment_report::resolver_id.eq(aliases::person2.field(person::id).nullable())),
|
||||
)
|
||||
.left_join(actions_alias(
|
||||
creator_community_actions,
|
||||
comment::creator_id,
|
||||
post::community_id,
|
||||
))
|
||||
.left_join(
|
||||
local_user::table.on(
|
||||
comment::creator_id
|
||||
.eq(local_user::person_id)
|
||||
.and(local_user::admin.eq(true)),
|
||||
),
|
||||
)
|
||||
.left_join(actions(
|
||||
person_actions::table,
|
||||
Some(my_person_id),
|
||||
comment::creator_id,
|
||||
))
|
||||
.left_join(actions(
|
||||
community_actions::table,
|
||||
Some(my_person_id),
|
||||
post::community_id,
|
||||
))
|
||||
Self::joins(my_person_id)
|
||||
.filter(comment_report::id.eq(report_id))
|
||||
.select((
|
||||
comment_report::all_columns,
|
||||
comment::all_columns,
|
||||
|
|
|
@ -23,10 +23,75 @@ use lemmy_db_schema::{
|
|||
post_report,
|
||||
},
|
||||
source::community::CommunityFollower,
|
||||
utils::{actions, actions_alias, functions::coalesce, get_conn, DbPool},
|
||||
utils::{functions::coalesce, get_conn, DbPool},
|
||||
};
|
||||
|
||||
impl PostReportView {
|
||||
#[diesel::dsl::auto_type(no_type_alias)]
|
||||
fn joins(my_person_id: PersonId) -> _ {
|
||||
let recipient_id = aliases::person1.field(person::id);
|
||||
let resolver_id = aliases::person2.field(person::id);
|
||||
|
||||
let community_join = community::table.on(post::community_id.eq(community::id));
|
||||
|
||||
let report_creator_join = person::table.on(post_report::creator_id.eq(person::id));
|
||||
|
||||
let post_creator_join = aliases::person1.on(post::creator_id.eq(recipient_id));
|
||||
|
||||
let creator_community_actions_join = creator_community_actions.on(
|
||||
creator_community_actions
|
||||
.field(community_actions::community_id)
|
||||
.eq(post::community_id)
|
||||
.and(
|
||||
creator_community_actions
|
||||
.field(community_actions::person_id)
|
||||
.eq(post::creator_id),
|
||||
),
|
||||
);
|
||||
|
||||
let community_actions_join = community_actions::table.on(
|
||||
community_actions::community_id
|
||||
.eq(post::community_id)
|
||||
.and(community_actions::person_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
let local_user_join = local_user::table.on(
|
||||
post::creator_id
|
||||
.eq(local_user::person_id)
|
||||
.and(local_user::admin.eq(true)),
|
||||
);
|
||||
|
||||
let post_actions_join = post_actions::table.on(
|
||||
post_actions::post_id
|
||||
.eq(post::id)
|
||||
.and(post_actions::person_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
let person_actions_join = person_actions::table.on(
|
||||
person_actions::target_id
|
||||
.eq(post::creator_id)
|
||||
.and(person_actions::person_id.eq(my_person_id)),
|
||||
);
|
||||
|
||||
let post_aggregates_join =
|
||||
post_aggregates::table.on(post_report::post_id.eq(post_aggregates::post_id));
|
||||
|
||||
let resolver_join = aliases::person2.on(post_report::resolver_id.eq(resolver_id.nullable()));
|
||||
|
||||
post_report::table
|
||||
.inner_join(post::table)
|
||||
.inner_join(community_join)
|
||||
.inner_join(report_creator_join)
|
||||
.inner_join(post_creator_join)
|
||||
.left_join(creator_community_actions_join)
|
||||
.left_join(community_actions_join)
|
||||
.left_join(local_user_join)
|
||||
.left_join(post_actions_join)
|
||||
.left_join(person_actions_join)
|
||||
.inner_join(post_aggregates_join)
|
||||
.left_join(resolver_join)
|
||||
}
|
||||
|
||||
/// returns the PostReportView for the provided report_id
|
||||
///
|
||||
/// * `report_id` - the report id to obtain
|
||||
|
@ -37,40 +102,8 @@ impl PostReportView {
|
|||
) -> Result<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
|
||||
post_report::table
|
||||
.find(report_id)
|
||||
.inner_join(post::table)
|
||||
.inner_join(community::table.on(post::community_id.eq(community::id)))
|
||||
.inner_join(person::table.on(post_report::creator_id.eq(person::id)))
|
||||
.inner_join(aliases::person1.on(post::creator_id.eq(aliases::person1.field(person::id))))
|
||||
.left_join(actions_alias(
|
||||
creator_community_actions,
|
||||
post::creator_id,
|
||||
post::community_id,
|
||||
))
|
||||
.left_join(actions(
|
||||
community_actions::table,
|
||||
Some(my_person_id),
|
||||
post::community_id,
|
||||
))
|
||||
.left_join(
|
||||
local_user::table.on(
|
||||
post::creator_id
|
||||
.eq(local_user::person_id)
|
||||
.and(local_user::admin.eq(true)),
|
||||
),
|
||||
)
|
||||
.left_join(actions(post_actions::table, Some(my_person_id), post::id))
|
||||
.left_join(actions(
|
||||
person_actions::table,
|
||||
Some(my_person_id),
|
||||
post::creator_id,
|
||||
))
|
||||
.inner_join(post_aggregates::table.on(post_report::post_id.eq(post_aggregates::post_id)))
|
||||
.left_join(
|
||||
aliases::person2
|
||||
.on(post_report::resolver_id.eq(aliases::person2.field(person::id).nullable())),
|
||||
)
|
||||
Self::joins(my_person_id)
|
||||
.filter(post_report::id.eq(report_id))
|
||||
.select((
|
||||
post_report::all_columns,
|
||||
post::all_columns,
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
use crate::structs::CustomEmojiView;
|
||||
use diesel::{result::Error, ExpressionMethods, JoinOnDsl, NullableExpressionMethods, QueryDsl};
|
||||
use diesel::{
|
||||
dsl::Nullable,
|
||||
result::Error,
|
||||
ExpressionMethods,
|
||||
JoinOnDsl,
|
||||
NullableExpressionMethods,
|
||||
QueryDsl,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_db_schema::{
|
||||
newtypes::CustomEmojiId,
|
||||
|
@ -9,20 +16,33 @@ use lemmy_db_schema::{
|
|||
};
|
||||
use std::collections::HashMap;
|
||||
|
||||
type SelectionType = (
|
||||
<custom_emoji::table as diesel::Table>::AllColumns,
|
||||
Nullable<<custom_emoji_keyword::table as diesel::Table>::AllColumns>,
|
||||
);
|
||||
|
||||
fn selection() -> SelectionType {
|
||||
(
|
||||
custom_emoji::all_columns,
|
||||
custom_emoji_keyword::all_columns.nullable(), // (or all the columns if you want)
|
||||
)
|
||||
}
|
||||
type CustomEmojiTuple = (CustomEmoji, Option<CustomEmojiKeyword>);
|
||||
|
||||
// TODO this type is a mess, it should not be using vectors in a view.
|
||||
impl CustomEmojiView {
|
||||
#[diesel::dsl::auto_type(no_type_alias)]
|
||||
fn joins() -> _ {
|
||||
custom_emoji::table.left_join(
|
||||
custom_emoji_keyword::table.on(custom_emoji_keyword::custom_emoji_id.eq(custom_emoji::id)),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn get(pool: &mut DbPool<'_>, emoji_id: CustomEmojiId) -> Result<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let emojis = custom_emoji::table
|
||||
.find(emoji_id)
|
||||
.left_join(
|
||||
custom_emoji_keyword::table.on(custom_emoji_keyword::custom_emoji_id.eq(custom_emoji::id)),
|
||||
)
|
||||
.select((
|
||||
custom_emoji::all_columns,
|
||||
custom_emoji_keyword::all_columns.nullable(), // (or all the columns if you want)
|
||||
))
|
||||
let emojis = Self::joins()
|
||||
.filter(custom_emoji::id.eq(emoji_id))
|
||||
.select(selection())
|
||||
.load::<CustomEmojiTuple>(conn)
|
||||
.await?;
|
||||
if let Some(emoji) = CustomEmojiView::from_tuple_to_vec(emojis)
|
||||
|
@ -44,12 +64,7 @@ impl CustomEmojiView {
|
|||
) -> Result<Vec<Self>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
|
||||
let mut query = custom_emoji::table
|
||||
.left_join(
|
||||
custom_emoji_keyword::table.on(custom_emoji_keyword::custom_emoji_id.eq(custom_emoji::id)),
|
||||
)
|
||||
.order(custom_emoji::category)
|
||||
.into_boxed();
|
||||
let mut query = Self::joins().into_boxed();
|
||||
|
||||
if !ignore_page_limits {
|
||||
let (limit, offset) = limit_and_offset(page, limit)?;
|
||||
|
@ -60,13 +75,10 @@ impl CustomEmojiView {
|
|||
query = query.filter(custom_emoji::category.eq(category))
|
||||
}
|
||||
|
||||
query = query.then_order_by(custom_emoji::id);
|
||||
|
||||
let emojis = query
|
||||
.select((
|
||||
custom_emoji::all_columns,
|
||||
custom_emoji_keyword::all_columns.nullable(), // (or all the columns if you want)
|
||||
))
|
||||
.select(selection())
|
||||
.order(custom_emoji::category)
|
||||
.then_order_by(custom_emoji::id)
|
||||
.load::<CustomEmojiTuple>(conn)
|
||||
.await?;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::structs::LocalImageView;
|
||||
use diesel::{result::Error, ExpressionMethods, JoinOnDsl, QueryDsl};
|
||||
use diesel::{result::Error, ExpressionMethods, JoinOnDsl, QueryDsl, SelectableHelper};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_db_schema::{
|
||||
newtypes::LocalUserId,
|
||||
|
@ -8,31 +8,11 @@ use lemmy_db_schema::{
|
|||
};
|
||||
|
||||
impl LocalImageView {
|
||||
async fn get_all_helper(
|
||||
pool: &mut DbPool<'_>,
|
||||
user_id: Option<LocalUserId>,
|
||||
page: Option<i64>,
|
||||
limit: Option<i64>,
|
||||
ignore_page_limits: bool,
|
||||
) -> Result<Vec<Self>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let mut query = local_image::table
|
||||
#[diesel::dsl::auto_type(no_type_alias)]
|
||||
fn joins() -> _ {
|
||||
local_image::table
|
||||
.inner_join(local_user::table)
|
||||
.inner_join(person::table.on(local_user::person_id.eq(person::id)))
|
||||
.select((local_image::all_columns, person::all_columns))
|
||||
.order_by(local_image::published.desc())
|
||||
.into_boxed();
|
||||
|
||||
if let Some(user_id) = user_id {
|
||||
query = query.filter(local_image::local_user_id.eq(user_id))
|
||||
}
|
||||
|
||||
if !ignore_page_limits {
|
||||
let (limit, offset) = limit_and_offset(page, limit)?;
|
||||
query = query.limit(limit).offset(offset);
|
||||
}
|
||||
|
||||
query.load::<LocalImageView>(conn).await
|
||||
}
|
||||
|
||||
pub async fn get_all_paged_by_local_user_id(
|
||||
|
@ -41,14 +21,28 @@ impl LocalImageView {
|
|||
page: Option<i64>,
|
||||
limit: Option<i64>,
|
||||
) -> Result<Vec<Self>, Error> {
|
||||
Self::get_all_helper(pool, Some(user_id), page, limit, false).await
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let (limit, offset) = limit_and_offset(page, limit)?;
|
||||
|
||||
Self::joins()
|
||||
.filter(local_image::local_user_id.eq(user_id))
|
||||
.select(Self::as_select())
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
.load::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_all_by_local_user_id(
|
||||
pool: &mut DbPool<'_>,
|
||||
user_id: LocalUserId,
|
||||
) -> Result<Vec<Self>, Error> {
|
||||
Self::get_all_helper(pool, Some(user_id), None, None, true).await
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
Self::joins()
|
||||
.filter(local_image::local_user_id.eq(user_id))
|
||||
.select(Self::as_select())
|
||||
.load::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_all(
|
||||
|
@ -56,6 +50,13 @@ impl LocalImageView {
|
|||
page: Option<i64>,
|
||||
limit: Option<i64>,
|
||||
) -> Result<Vec<Self>, Error> {
|
||||
Self::get_all_helper(pool, None, page, limit, false).await
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let (limit, offset) = limit_and_offset(page, limit)?;
|
||||
Self::joins()
|
||||
.select(Self::as_select())
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
.load::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::structs::SiteView;
|
||||
use diesel::{ExpressionMethods, JoinOnDsl, OptionalExtension, QueryDsl};
|
||||
use diesel::{ExpressionMethods, JoinOnDsl, OptionalExtension, QueryDsl, SelectableHelper};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_db_schema::{
|
||||
schema::{local_site, local_site_rate_limit, site, site_aggregates},
|
||||
|
@ -17,12 +17,7 @@ impl SiteView {
|
|||
local_site_rate_limit::table.on(local_site::id.eq(local_site_rate_limit::local_site_id)),
|
||||
)
|
||||
.inner_join(site_aggregates::table)
|
||||
.select((
|
||||
site::all_columns,
|
||||
local_site::all_columns,
|
||||
local_site_rate_limit::all_columns,
|
||||
site_aggregates::all_columns,
|
||||
))
|
||||
.select(Self::as_select())
|
||||
.first(conn)
|
||||
.await
|
||||
.optional()?
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
use crate::structs::VoteView;
|
||||
use diesel::{result::Error, ExpressionMethods, NullableExpressionMethods, QueryDsl};
|
||||
use diesel::{
|
||||
result::Error,
|
||||
BoolExpressionMethods,
|
||||
ExpressionMethods,
|
||||
JoinOnDsl,
|
||||
NullableExpressionMethods,
|
||||
QueryDsl,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_db_schema::{
|
||||
aliases::creator_community_actions,
|
||||
newtypes::{CommentId, PostId},
|
||||
schema::{comment, comment_actions, community_actions, person, post, post_actions},
|
||||
utils::{action_query, actions_alias, get_conn, limit_and_offset, DbPool},
|
||||
utils::{get_conn, limit_and_offset, DbPool},
|
||||
};
|
||||
|
||||
impl VoteView {
|
||||
|
@ -18,14 +25,22 @@ impl VoteView {
|
|||
let conn = &mut get_conn(pool).await?;
|
||||
let (limit, offset) = limit_and_offset(page, limit)?;
|
||||
|
||||
action_query(post_actions::like_score)
|
||||
let creator_community_actions_join = creator_community_actions.on(
|
||||
creator_community_actions
|
||||
.field(community_actions::community_id)
|
||||
.eq(post::community_id)
|
||||
.and(
|
||||
creator_community_actions
|
||||
.field(community_actions::person_id)
|
||||
.eq(post_actions::person_id),
|
||||
),
|
||||
);
|
||||
|
||||
post_actions::table
|
||||
.filter(post_actions::like_score.is_not_null())
|
||||
.inner_join(person::table)
|
||||
.inner_join(post::table)
|
||||
.left_join(actions_alias(
|
||||
creator_community_actions,
|
||||
post_actions::person_id,
|
||||
post::community_id,
|
||||
))
|
||||
.left_join(creator_community_actions_join)
|
||||
.filter(post_actions::post_id.eq(post_id))
|
||||
.select((
|
||||
person::all_columns,
|
||||
|
@ -51,14 +66,22 @@ impl VoteView {
|
|||
let conn = &mut get_conn(pool).await?;
|
||||
let (limit, offset) = limit_and_offset(page, limit)?;
|
||||
|
||||
action_query(comment_actions::like_score)
|
||||
let creator_community_actions_join = creator_community_actions.on(
|
||||
creator_community_actions
|
||||
.field(community_actions::community_id)
|
||||
.eq(post::community_id)
|
||||
.and(
|
||||
creator_community_actions
|
||||
.field(community_actions::person_id)
|
||||
.eq(comment_actions::person_id),
|
||||
),
|
||||
);
|
||||
|
||||
comment_actions::table
|
||||
.filter(comment_actions::like_score.is_not_null())
|
||||
.inner_join(person::table)
|
||||
.inner_join(comment::table.inner_join(post::table))
|
||||
.left_join(actions_alias(
|
||||
creator_community_actions,
|
||||
comment_actions::person_id,
|
||||
post::community_id,
|
||||
))
|
||||
.left_join(creator_community_actions_join)
|
||||
.filter(comment_actions::comment_id.eq(comment_id))
|
||||
.select((
|
||||
person::all_columns,
|
||||
|
|
|
@ -1,7 +1,17 @@
|
|||
#[cfg(feature = "full")]
|
||||
use diesel::Queryable;
|
||||
#[cfg(feature = "full")]
|
||||
use diesel::{deserialize::FromSqlRow, expression::AsExpression, sql_types};
|
||||
use diesel::{
|
||||
deserialize::FromSqlRow,
|
||||
dsl::exists,
|
||||
dsl::Nullable,
|
||||
expression::AsExpression,
|
||||
sql_types,
|
||||
BoolExpressionMethods,
|
||||
ExpressionMethods,
|
||||
NullableExpressionMethods,
|
||||
QueryDsl,
|
||||
Queryable,
|
||||
Selectable,
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
aggregates::structs::{
|
||||
CommentAggregates,
|
||||
|
@ -60,6 +70,14 @@ use lemmy_db_schema::{
|
|||
},
|
||||
SubscribedType,
|
||||
};
|
||||
#[cfg(feature = "full")]
|
||||
use lemmy_db_schema::{
|
||||
aliases::{creator_community_actions, person1},
|
||||
schema::{comment, comment_actions, community_actions, local_user, person, person_actions},
|
||||
source::community::CommunityFollower,
|
||||
utils::functions::coalesce,
|
||||
Person1AliasAllColumnsTuple,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
#[cfg(feature = "full")]
|
||||
|
@ -93,24 +111,90 @@ pub struct CommentReportView {
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(TS, Queryable))]
|
||||
#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// A comment view.
|
||||
pub struct CommentView {
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub comment: Comment,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub creator: Person,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub post: Post,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub community: Community,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub counts: CommentAggregates,
|
||||
#[cfg_attr(feature = "full",
|
||||
diesel(
|
||||
select_expression =
|
||||
creator_community_actions
|
||||
.field(community_actions::received_ban)
|
||||
.nullable()
|
||||
.is_not_null()
|
||||
)
|
||||
)]
|
||||
pub creator_banned_from_community: bool,
|
||||
#[cfg_attr(feature = "full",
|
||||
diesel(
|
||||
select_expression =
|
||||
community_actions::received_ban.nullable().is_not_null()
|
||||
)
|
||||
)]
|
||||
pub banned_from_community: bool,
|
||||
#[cfg_attr(feature = "full",
|
||||
diesel(
|
||||
select_expression =
|
||||
creator_community_actions
|
||||
.field(community_actions::became_moderator)
|
||||
.nullable()
|
||||
.is_not_null()
|
||||
)
|
||||
)]
|
||||
pub creator_is_moderator: bool,
|
||||
#[cfg_attr(feature = "full",
|
||||
diesel(
|
||||
select_expression =
|
||||
exists(
|
||||
local_user::table.filter(
|
||||
comment::creator_id
|
||||
.eq(local_user::person_id)
|
||||
.and(local_user::admin.eq(true))
|
||||
)
|
||||
)
|
||||
)
|
||||
)]
|
||||
pub creator_is_admin: bool,
|
||||
#[cfg_attr(feature = "full",
|
||||
diesel(
|
||||
select_expression_type = Nullable<community_actions::follow_state>,
|
||||
select_expression =
|
||||
CommunityFollower::select_subscribed_type(),
|
||||
)
|
||||
)]
|
||||
pub subscribed: SubscribedType,
|
||||
#[cfg_attr(feature = "full",
|
||||
diesel(
|
||||
select_expression =
|
||||
comment_actions::saved.nullable().is_not_null()
|
||||
)
|
||||
)]
|
||||
pub saved: bool,
|
||||
#[cfg_attr(feature = "full",
|
||||
diesel(
|
||||
select_expression =
|
||||
person_actions::blocked.nullable().is_not_null()
|
||||
)
|
||||
)]
|
||||
pub creator_blocked: bool,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
#[cfg_attr(feature = "full",
|
||||
diesel(
|
||||
select_expression =
|
||||
comment_actions::like_score.nullable()
|
||||
)
|
||||
)]
|
||||
pub my_vote: Option<i16>,
|
||||
}
|
||||
|
||||
|
@ -152,14 +236,18 @@ pub struct CommunityReportView {
|
|||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(TS, Queryable))]
|
||||
#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// A local user view.
|
||||
pub struct LocalUserView {
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub local_user: LocalUser,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub local_user_vote_display_mode: LocalUserVoteDisplayMode,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub person: Person,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub counts: PersonAggregates,
|
||||
}
|
||||
|
||||
|
@ -263,27 +351,40 @@ pub struct PrivateMessageReportView {
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(TS, Queryable))]
|
||||
#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// A registration application view.
|
||||
pub struct RegistrationApplicationView {
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub registration_application: RegistrationApplication,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub creator_local_user: LocalUser,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub creator: Person,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
#[cfg_attr(feature = "full",
|
||||
diesel(
|
||||
select_expression_type = Nullable<Person1AliasAllColumnsTuple>,
|
||||
select_expression = person1.fields(person::all_columns).nullable()
|
||||
)
|
||||
)]
|
||||
pub admin: Option<Person>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(TS, Queryable))]
|
||||
#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// A site view.
|
||||
pub struct SiteView {
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub site: Site,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub local_site: LocalSite,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub local_site_rate_limit: LocalSiteRateLimit,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub counts: SiteAggregates,
|
||||
}
|
||||
|
||||
|
@ -311,12 +412,14 @@ pub struct VoteView {
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(TS, Queryable))]
|
||||
#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// A local image view.
|
||||
pub struct LocalImageView {
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub local_image: LocalImage,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub person: Person,
|
||||
}
|
||||
|
||||
|
@ -412,44 +515,68 @@ pub enum PersonContentCombinedView {
|
|||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(TS, Queryable))]
|
||||
#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// A community follower.
|
||||
pub struct CommunityFollowerView {
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub community: Community,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub follower: Person,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(TS, Queryable))]
|
||||
#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// A community moderator.
|
||||
pub struct CommunityModeratorView {
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub community: Community,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub moderator: Person,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
/// A community person ban.
|
||||
pub struct CommunityPersonBanView {
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub community: Community,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub person: Person,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(TS, Queryable))]
|
||||
#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// A community view.
|
||||
pub struct CommunityView {
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub community: Community,
|
||||
#[cfg_attr(feature = "full",
|
||||
diesel(
|
||||
select_expression_type = Nullable<community_actions::follow_state>,
|
||||
select_expression = CommunityFollower::select_subscribed_type()
|
||||
)
|
||||
)]
|
||||
pub subscribed: SubscribedType,
|
||||
#[cfg_attr(feature = "full",
|
||||
diesel(
|
||||
select_expression = community_actions::blocked.nullable().is_not_null()
|
||||
)
|
||||
)]
|
||||
pub blocked: bool,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub counts: CommunityAggregates,
|
||||
#[cfg_attr(feature = "full",
|
||||
diesel(
|
||||
select_expression = community_actions::received_ban.nullable().is_not_null()
|
||||
)
|
||||
)]
|
||||
pub banned_from_community: bool,
|
||||
}
|
||||
|
||||
|
@ -458,28 +585,20 @@ pub struct CommunityView {
|
|||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
pub enum CommunitySortType {
|
||||
ActiveSixMonths,
|
||||
#[default]
|
||||
Active,
|
||||
ActiveMonthly,
|
||||
ActiveWeekly,
|
||||
ActiveDaily,
|
||||
Hot,
|
||||
New,
|
||||
Old,
|
||||
TopDay,
|
||||
TopWeek,
|
||||
TopMonth,
|
||||
TopYear,
|
||||
TopAll,
|
||||
MostComments,
|
||||
NewComments,
|
||||
TopHour,
|
||||
TopSixHour,
|
||||
TopTwelveHour,
|
||||
TopThreeMonths,
|
||||
TopSixMonths,
|
||||
TopNineMonths,
|
||||
Controversial,
|
||||
Scaled,
|
||||
NameAsc,
|
||||
NameDesc,
|
||||
Comments,
|
||||
Posts,
|
||||
Subscribers,
|
||||
SubscribersLocal,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
|
@ -563,13 +682,21 @@ pub struct CommentReplyView {
|
|||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(TS, Queryable))]
|
||||
#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// A person view.
|
||||
pub struct PersonView {
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub person: Person,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub counts: PersonAggregates,
|
||||
#[cfg_attr(feature = "full",
|
||||
diesel(
|
||||
select_expression_type = coalesce<diesel::sql_types::Bool, Nullable<local_user::admin>, bool>,
|
||||
select_expression = coalesce(local_user::admin.nullable(), false)
|
||||
)
|
||||
)]
|
||||
pub is_admin: bool,
|
||||
}
|
||||
|
||||
|
@ -585,13 +712,21 @@ pub struct PendingFollow {
|
|||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(TS, Queryable))]
|
||||
#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// A private message view.
|
||||
pub struct PrivateMessageView {
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub private_message: PrivateMessage,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub creator: Person,
|
||||
#[cfg_attr(feature = "full",
|
||||
diesel(
|
||||
select_expression_type = Person1AliasAllColumnsTuple,
|
||||
select_expression = person1.fields(person::all_columns)
|
||||
)
|
||||
)]
|
||||
pub recipient: Person,
|
||||
}
|
||||
|
||||
|
@ -888,36 +1023,64 @@ pub struct AdminAllowInstanceView {
|
|||
pub struct ModlogCombinedPaginationCursor(pub String);
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
/// A combined modlog view
|
||||
pub struct ModlogCombinedViewInternal {
|
||||
// Specific
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub admin_allow_instance: Option<AdminAllowInstance>,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub admin_block_instance: Option<AdminBlockInstance>,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub admin_purge_comment: Option<AdminPurgeComment>,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub admin_purge_community: Option<AdminPurgeCommunity>,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub admin_purge_person: Option<AdminPurgePerson>,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub admin_purge_post: Option<AdminPurgePost>,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub mod_add: Option<ModAdd>,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub mod_add_community: Option<ModAddCommunity>,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub mod_ban: Option<ModBan>,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub mod_ban_from_community: Option<ModBanFromCommunity>,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub mod_feature_post: Option<ModFeaturePost>,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub mod_hide_community: Option<ModHideCommunity>,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub mod_lock_post: Option<ModLockPost>,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub mod_remove_comment: Option<ModRemoveComment>,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub mod_remove_community: Option<ModRemoveCommunity>,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub mod_remove_post: Option<ModRemovePost>,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub mod_transfer_community: Option<ModTransferCommunity>,
|
||||
// Specific fields
|
||||
|
||||
// Shared
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub moderator: Option<Person>,
|
||||
#[cfg_attr(feature = "full",
|
||||
diesel(
|
||||
select_expression_type = Nullable<Person1AliasAllColumnsTuple>,
|
||||
select_expression = person1.fields(person::all_columns).nullable()
|
||||
)
|
||||
)]
|
||||
pub other_person: Option<Person>,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub instance: Option<Instance>,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub community: Option<Community>,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub post: Option<Post>,
|
||||
#[cfg_attr(feature = "full", diesel(embed))]
|
||||
pub comment: Option<Comment>,
|
||||
}
|
||||
|
||||
|
|
|
@ -39,15 +39,7 @@ use lemmy_db_schema::{
|
|||
post::{Post, PostUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
utils::{
|
||||
find_action,
|
||||
functions::coalesce,
|
||||
get_conn,
|
||||
now,
|
||||
uplete,
|
||||
DbPool,
|
||||
DELETED_REPLACEMENT_TEXT,
|
||||
},
|
||||
utils::{functions::coalesce, get_conn, now, uplete, DbPool, DELETED_REPLACEMENT_TEXT},
|
||||
};
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||
use reqwest_middleware::ClientWithMiddleware;
|
||||
|
@ -435,6 +427,10 @@ async fn publish_scheduled_posts(context: &Data<LemmyContext>) -> LemmyResult<()
|
|||
let pool = &mut context.pool();
|
||||
let mut conn = get_conn(pool).await?;
|
||||
|
||||
let not_banned_action = community_actions::table
|
||||
.find((person::id, community::id))
|
||||
.filter(community_actions::received_ban.is_not_null());
|
||||
|
||||
let scheduled_posts: Vec<_> = post::table
|
||||
.inner_join(community::table)
|
||||
.inner_join(person::table)
|
||||
|
@ -446,10 +442,7 @@ async fn publish_scheduled_posts(context: &Data<LemmyContext>) -> LemmyResult<()
|
|||
.filter(not(person::banned.or(person::deleted)))
|
||||
.filter(not(community::removed.or(community::deleted)))
|
||||
// ensure that user isnt banned from community
|
||||
.filter(not(exists(find_action(
|
||||
community_actions::received_ban,
|
||||
(person::id, community::id),
|
||||
))))
|
||||
.filter(not(exists(not_banned_action)))
|
||||
.select((post::all_columns, community::all_columns))
|
||||
.get_results::<(Post, Community)>(&mut conn)
|
||||
.await?;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use cfg_if::cfg_if;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{backtrace::Backtrace, fmt::Debug};
|
||||
use std::fmt::Debug;
|
||||
use strum::{Display, EnumIter};
|
||||
|
||||
#[derive(Display, Debug, Serialize, Deserialize, Clone, PartialEq, Eq, EnumIter, Hash)]
|
||||
|
@ -192,7 +192,7 @@ pub enum FederationError {
|
|||
cfg_if! {
|
||||
if #[cfg(feature = "full")] {
|
||||
|
||||
use std::fmt;
|
||||
use std::{fmt, backtrace::Backtrace};
|
||||
pub type LemmyResult<T> = Result<T, LemmyError>;
|
||||
|
||||
pub struct LemmyError {
|
||||
|
|
Loading…
Reference in a new issue