Merge branch 'main' into error-expose

error-expose
SleeplessOne1917 2024-02-17 00:00:40 +00:00 committed by GitHub
commit 0daa5492d8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 309 additions and 103 deletions

3
Cargo.lock generated
View File

@ -2683,6 +2683,7 @@ dependencies = [
"lemmy_db_views", "lemmy_db_views",
"lemmy_utils", "lemmy_utils",
"tokio", "tokio",
"url",
] ]
[[package]] [[package]]
@ -2743,6 +2744,7 @@ dependencies = [
"tokio", "tokio",
"tracing", "tracing",
"ts-rs", "ts-rs",
"url",
] ]
[[package]] [[package]]
@ -2761,6 +2763,7 @@ dependencies = [
"strum_macros", "strum_macros",
"tokio", "tokio",
"ts-rs", "ts-rs",
"url",
] ]
[[package]] [[package]]

View File

@ -10,6 +10,7 @@ use lemmy_db_schema::{
}, },
ListingType, ListingType,
ModlogActionType, ModlogActionType,
PostListingMode,
RegistrationMode, RegistrationMode,
SearchType, SearchType,
SortType, SortType,
@ -161,6 +162,7 @@ pub struct CreateSite {
pub private_instance: Option<bool>, pub private_instance: Option<bool>,
pub default_theme: Option<String>, pub default_theme: Option<String>,
pub default_post_listing_type: Option<ListingType>, pub default_post_listing_type: Option<ListingType>,
pub default_sort_type: Option<SortType>,
pub legal_information: Option<String>, pub legal_information: Option<String>,
pub application_email_admins: Option<bool>, pub application_email_admins: Option<bool>,
pub hide_modlog_mod_names: Option<bool>, pub hide_modlog_mod_names: Option<bool>,
@ -187,6 +189,8 @@ pub struct CreateSite {
pub blocked_instances: Option<Vec<String>>, pub blocked_instances: Option<Vec<String>>,
pub taglines: Option<Vec<String>>, pub taglines: Option<Vec<String>>,
pub registration_mode: Option<RegistrationMode>, pub registration_mode: Option<RegistrationMode>,
pub content_warning: Option<String>,
pub default_post_listing_mode: Option<PostListingMode>,
} }
#[skip_serializing_none] #[skip_serializing_none]
@ -218,6 +222,8 @@ pub struct EditSite {
/// The default theme. Usually "browser" /// The default theme. Usually "browser"
pub default_theme: Option<String>, pub default_theme: Option<String>,
pub default_post_listing_type: Option<ListingType>, pub default_post_listing_type: Option<ListingType>,
/// The default sort, usually "active"
pub default_sort_type: Option<SortType>,
/// An optional page of legal information /// An optional page of legal information
pub legal_information: Option<String>, pub legal_information: Option<String>,
/// Whether to email admins when receiving a new application. /// Whether to email admins when receiving a new application.
@ -265,6 +271,11 @@ pub struct EditSite {
pub registration_mode: Option<RegistrationMode>, pub registration_mode: Option<RegistrationMode>,
/// Whether to email admins for new reports. /// Whether to email admins for new reports.
pub reports_email_admins: Option<bool>, pub reports_email_admins: Option<bool>,
/// If present, nsfw content is visible by default. Should be displayed by frontends/clients
/// when the site is first opened by a user.
pub content_warning: Option<String>,
/// Default value for [LocalUser.post_listing_mode]
pub default_post_listing_mode: Option<PostListingMode>,
} }
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]

View File

@ -4,8 +4,7 @@ use lemmy_api_common::{
context::LemmyContext, context::LemmyContext,
utils::{check_private_instance, is_admin}, utils::{check_private_instance, is_admin},
}; };
use lemmy_db_schema::source::local_site::LocalSite; use lemmy_db_views::structs::{LocalUserView, SiteView};
use lemmy_db_views::structs::LocalUserView;
use lemmy_db_views_actor::community_view::CommunityQuery; use lemmy_db_views_actor::community_view::CommunityQuery;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::LemmyError;
@ -15,13 +14,13 @@ pub async fn list_communities(
context: Data<LemmyContext>, context: Data<LemmyContext>,
local_user_view: Option<LocalUserView>, local_user_view: Option<LocalUserView>,
) -> Result<Json<ListCommunitiesResponse>, LemmyError> { ) -> Result<Json<ListCommunitiesResponse>, LemmyError> {
let local_site = LocalSite::read(&mut context.pool()).await?; let local_site = SiteView::read_local(&mut context.pool()).await?;
let is_admin = local_user_view let is_admin = local_user_view
.as_ref() .as_ref()
.map(|luv| is_admin(luv).is_ok()) .map(|luv| is_admin(luv).is_ok())
.unwrap_or_default(); .unwrap_or_default();
check_private_instance(&local_user_view, &local_site)?; check_private_instance(&local_user_view, &local_site.local_site)?;
let sort = data.sort; let sort = data.sort;
let listing_type = data.type_; let listing_type = data.type_;
@ -39,7 +38,7 @@ pub async fn list_communities(
is_mod_or_admin: is_admin, is_mod_or_admin: is_admin,
..Default::default() ..Default::default()
} }
.list(&mut context.pool()) .list(&local_site.site, &mut context.pool())
.await?; .await?;
// Return the jwt // Return the jwt

View File

@ -6,12 +6,12 @@ use lemmy_api_common::{
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
aggregates::structs::{PersonPostAggregates, PersonPostAggregatesForm}, aggregates::structs::{PersonPostAggregates, PersonPostAggregatesForm},
source::{comment::Comment, local_site::LocalSite, post::Post}, source::{comment::Comment, post::Post},
traits::Crud, traits::Crud,
}; };
use lemmy_db_views::{ use lemmy_db_views::{
post_view::PostQuery, post_view::PostQuery,
structs::{LocalUserView, PostView}, structs::{LocalUserView, PostView, SiteView},
}; };
use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView}; use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView};
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
@ -22,9 +22,9 @@ pub async fn get_post(
context: Data<LemmyContext>, context: Data<LemmyContext>,
local_user_view: Option<LocalUserView>, local_user_view: Option<LocalUserView>,
) -> Result<Json<GetPostResponse>, LemmyError> { ) -> Result<Json<GetPostResponse>, LemmyError> {
let local_site = LocalSite::read(&mut context.pool()).await?; let local_site = SiteView::read_local(&mut context.pool()).await?;
check_private_instance(&local_user_view, &local_site)?; check_private_instance(&local_user_view, &local_site.local_site)?;
let person_id = local_user_view.as_ref().map(|u| u.person.id); let person_id = local_user_view.as_ref().map(|u| u.person.id);
@ -93,7 +93,7 @@ pub async fn get_post(
url_search: Some(url.inner().as_str().into()), url_search: Some(url.inner().as_str().into()),
..Default::default() ..Default::default()
} }
.list(&mut context.pool()) .list(&local_site.site, &mut context.pool())
.await?; .await?;
// Don't return this post as one of the cross_posts // Don't return this post as one of the cross_posts

View File

@ -73,6 +73,7 @@ pub async fn create_site(
inbox_url, inbox_url,
private_key: Some(Some(keypair.private_key)), private_key: Some(Some(keypair.private_key)),
public_key: Some(keypair.public_key), public_key: Some(keypair.public_key),
content_warning: diesel_option_overwrite(data.content_warning.clone()),
..Default::default() ..Default::default()
}; };
@ -92,6 +93,7 @@ pub async fn create_site(
private_instance: data.private_instance, private_instance: data.private_instance,
default_theme: data.default_theme.clone(), default_theme: data.default_theme.clone(),
default_post_listing_type: data.default_post_listing_type, default_post_listing_type: data.default_post_listing_type,
default_sort_type: data.default_sort_type,
legal_information: diesel_option_overwrite(data.legal_information.clone()), legal_information: diesel_option_overwrite(data.legal_information.clone()),
application_email_admins: data.application_email_admins, application_email_admins: data.application_email_admins,
hide_modlog_mod_names: data.hide_modlog_mod_names, hide_modlog_mod_names: data.hide_modlog_mod_names,
@ -101,6 +103,7 @@ pub async fn create_site(
federation_enabled: data.federation_enabled, federation_enabled: data.federation_enabled,
captcha_enabled: data.captcha_enabled, captcha_enabled: data.captcha_enabled,
captcha_difficulty: data.captcha_difficulty.clone(), captcha_difficulty: data.captcha_difficulty.clone(),
default_post_listing_mode: data.default_post_listing_mode,
..Default::default() ..Default::default()
}; };
@ -190,7 +193,7 @@ mod tests {
use crate::site::create::validate_create_payload; use crate::site::create::validate_create_payload;
use lemmy_api_common::site::CreateSite; use lemmy_api_common::site::CreateSite;
use lemmy_db_schema::{source::local_site::LocalSite, ListingType, RegistrationMode}; use lemmy_db_schema::{source::local_site::LocalSite, ListingType, RegistrationMode, SortType};
use lemmy_utils::error::LemmyErrorType; use lemmy_utils::error::LemmyErrorType;
#[test] #[test]
@ -212,6 +215,7 @@ mod tests {
None::<String>, None::<String>,
None::<String>, None::<String>,
None::<ListingType>, None::<ListingType>,
None::<SortType>,
None::<String>, None::<String>,
None::<bool>, None::<bool>,
None::<bool>, None::<bool>,
@ -235,6 +239,7 @@ mod tests {
None::<String>, None::<String>,
None::<String>, None::<String>,
None::<ListingType>, None::<ListingType>,
None::<SortType>,
None::<String>, None::<String>,
None::<bool>, None::<bool>,
None::<bool>, None::<bool>,
@ -258,6 +263,7 @@ mod tests {
None::<String>, None::<String>,
None::<String>, None::<String>,
None::<ListingType>, None::<ListingType>,
None::<SortType>,
Some(String::from("(zeta|alpha)")), Some(String::from("(zeta|alpha)")),
None::<bool>, None::<bool>,
None::<bool>, None::<bool>,
@ -281,6 +287,7 @@ mod tests {
None::<String>, None::<String>,
None::<String>, None::<String>,
Some(ListingType::Subscribed), Some(ListingType::Subscribed),
None::<SortType>,
None::<String>, None::<String>,
None::<bool>, None::<bool>,
None::<bool>, None::<bool>,
@ -304,6 +311,7 @@ mod tests {
None::<String>, None::<String>,
None::<String>, None::<String>,
None::<ListingType>, None::<ListingType>,
None::<SortType>,
None::<String>, None::<String>,
Some(true), Some(true),
Some(true), Some(true),
@ -327,6 +335,7 @@ mod tests {
None::<String>, None::<String>,
None::<String>, None::<String>,
None::<ListingType>, None::<ListingType>,
None::<SortType>,
None::<String>, None::<String>,
None::<bool>, None::<bool>,
Some(true), Some(true),
@ -350,6 +359,7 @@ mod tests {
None::<String>, None::<String>,
None::<String>, None::<String>,
None::<ListingType>, None::<ListingType>,
None::<SortType>,
None::<String>, None::<String>,
None::<bool>, None::<bool>,
None::<bool>, None::<bool>,
@ -407,6 +417,7 @@ mod tests {
None::<String>, None::<String>,
None::<String>, None::<String>,
None::<ListingType>, None::<ListingType>,
None::<SortType>,
None::<String>, None::<String>,
None::<bool>, None::<bool>,
None::<bool>, None::<bool>,
@ -429,6 +440,7 @@ mod tests {
Some(String::new()), Some(String::new()),
Some(String::new()), Some(String::new()),
Some(ListingType::All), Some(ListingType::All),
Some(SortType::Active),
Some(String::new()), Some(String::new()),
Some(false), Some(false),
Some(true), Some(true),
@ -451,6 +463,7 @@ mod tests {
None::<String>, None::<String>,
None::<String>, None::<String>,
None::<ListingType>, None::<ListingType>,
None::<SortType>,
Some(String::new()), Some(String::new()),
None::<bool>, None::<bool>,
None::<bool>, None::<bool>,
@ -473,6 +486,7 @@ mod tests {
None::<String>, None::<String>,
None::<String>, None::<String>,
None::<ListingType>, None::<ListingType>,
None::<SortType>,
None::<String>, None::<String>,
None::<bool>, None::<bool>,
None::<bool>, None::<bool>,
@ -522,6 +536,7 @@ mod tests {
site_description: Option<String>, site_description: Option<String>,
site_sidebar: Option<String>, site_sidebar: Option<String>,
site_listing_type: Option<ListingType>, site_listing_type: Option<ListingType>,
site_sort_type: Option<SortType>,
site_slur_filter_regex: Option<String>, site_slur_filter_regex: Option<String>,
site_is_private: Option<bool>, site_is_private: Option<bool>,
site_is_federated: Option<bool>, site_is_federated: Option<bool>,
@ -542,6 +557,7 @@ mod tests {
private_instance: site_is_private, private_instance: site_is_private,
default_theme: None, default_theme: None,
default_post_listing_type: site_listing_type, default_post_listing_type: site_listing_type,
default_sort_type: site_sort_type,
legal_information: None, legal_information: None,
application_email_admins: None, application_email_admins: None,
hide_modlog_mod_names: None, hide_modlog_mod_names: None,
@ -568,6 +584,8 @@ mod tests {
blocked_instances: None, blocked_instances: None,
taglines: None, taglines: None,
registration_mode: site_registration_mode, registration_mode: site_registration_mode,
content_warning: None,
default_post_listing_mode: None,
} }
} }
} }

View File

@ -71,6 +71,7 @@ pub async fn update_site(
description: diesel_option_overwrite(data.description.clone()), description: diesel_option_overwrite(data.description.clone()),
icon, icon,
banner, banner,
content_warning: diesel_option_overwrite(data.content_warning.clone()),
updated: Some(Some(naive_now())), updated: Some(Some(naive_now())),
..Default::default() ..Default::default()
}; };
@ -91,6 +92,7 @@ pub async fn update_site(
private_instance: data.private_instance, private_instance: data.private_instance,
default_theme: data.default_theme.clone(), default_theme: data.default_theme.clone(),
default_post_listing_type: data.default_post_listing_type, default_post_listing_type: data.default_post_listing_type,
default_sort_type: data.default_sort_type,
legal_information: diesel_option_overwrite(data.legal_information.clone()), legal_information: diesel_option_overwrite(data.legal_information.clone()),
application_email_admins: data.application_email_admins, application_email_admins: data.application_email_admins,
hide_modlog_mod_names: data.hide_modlog_mod_names, hide_modlog_mod_names: data.hide_modlog_mod_names,
@ -101,6 +103,7 @@ pub async fn update_site(
captcha_enabled: data.captcha_enabled, captcha_enabled: data.captcha_enabled,
captcha_difficulty: data.captcha_difficulty.clone(), captcha_difficulty: data.captcha_difficulty.clone(),
reports_email_admins: data.reports_email_admins, reports_email_admins: data.reports_email_admins,
default_post_listing_mode: data.default_post_listing_mode,
..Default::default() ..Default::default()
}; };
@ -225,7 +228,7 @@ mod tests {
use crate::site::update::validate_update_payload; use crate::site::update::validate_update_payload;
use lemmy_api_common::site::EditSite; use lemmy_api_common::site::EditSite;
use lemmy_db_schema::{source::local_site::LocalSite, ListingType, RegistrationMode}; use lemmy_db_schema::{source::local_site::LocalSite, ListingType, RegistrationMode, SortType};
use lemmy_utils::error::LemmyErrorType; use lemmy_utils::error::LemmyErrorType;
#[test] #[test]
@ -246,6 +249,7 @@ mod tests {
None::<String>, None::<String>,
None::<String>, None::<String>,
None::<ListingType>, None::<ListingType>,
None::<SortType>,
None::<String>, None::<String>,
None::<bool>, None::<bool>,
None::<bool>, None::<bool>,
@ -268,6 +272,7 @@ mod tests {
None::<String>, None::<String>,
None::<String>, None::<String>,
None::<ListingType>, None::<ListingType>,
None::<SortType>,
Some(String::from("(zeta|alpha)")), Some(String::from("(zeta|alpha)")),
None::<bool>, None::<bool>,
None::<bool>, None::<bool>,
@ -290,6 +295,7 @@ mod tests {
None::<String>, None::<String>,
None::<String>, None::<String>,
Some(ListingType::Subscribed), Some(ListingType::Subscribed),
None::<SortType>,
None::<String>, None::<String>,
None::<bool>, None::<bool>,
None::<bool>, None::<bool>,
@ -312,6 +318,7 @@ mod tests {
None::<String>, None::<String>,
None::<String>, None::<String>,
None::<ListingType>, None::<ListingType>,
None::<SortType>,
None::<String>, None::<String>,
Some(true), Some(true),
Some(true), Some(true),
@ -334,6 +341,7 @@ mod tests {
None::<String>, None::<String>,
None::<String>, None::<String>,
None::<ListingType>, None::<ListingType>,
None::<SortType>,
None::<String>, None::<String>,
None::<bool>, None::<bool>,
Some(true), Some(true),
@ -356,6 +364,7 @@ mod tests {
None::<String>, None::<String>,
None::<String>, None::<String>,
None::<ListingType>, None::<ListingType>,
None::<SortType>,
None::<String>, None::<String>,
None::<bool>, None::<bool>,
None::<bool>, None::<bool>,
@ -409,6 +418,7 @@ mod tests {
None::<String>, None::<String>,
None::<String>, None::<String>,
None::<ListingType>, None::<ListingType>,
None::<SortType>,
None::<String>, None::<String>,
None::<bool>, None::<bool>,
None::<bool>, None::<bool>,
@ -430,6 +440,7 @@ mod tests {
Some(String::new()), Some(String::new()),
Some(String::new()), Some(String::new()),
Some(ListingType::All), Some(ListingType::All),
Some(SortType::Active),
Some(String::new()), Some(String::new()),
Some(false), Some(false),
Some(true), Some(true),
@ -451,6 +462,7 @@ mod tests {
None::<String>, None::<String>,
None::<String>, None::<String>,
None::<ListingType>, None::<ListingType>,
None::<SortType>,
Some(String::new()), Some(String::new()),
None::<bool>, None::<bool>,
None::<bool>, None::<bool>,
@ -472,6 +484,7 @@ mod tests {
None::<String>, None::<String>,
None::<String>, None::<String>,
None::<ListingType>, None::<ListingType>,
None::<SortType>,
None::<String>, None::<String>,
None::<bool>, None::<bool>,
None::<bool>, None::<bool>,
@ -519,6 +532,7 @@ mod tests {
site_description: Option<String>, site_description: Option<String>,
site_sidebar: Option<String>, site_sidebar: Option<String>,
site_listing_type: Option<ListingType>, site_listing_type: Option<ListingType>,
site_sort_type: Option<SortType>,
site_slur_filter_regex: Option<String>, site_slur_filter_regex: Option<String>,
site_is_private: Option<bool>, site_is_private: Option<bool>,
site_is_federated: Option<bool>, site_is_federated: Option<bool>,
@ -539,6 +553,7 @@ mod tests {
private_instance: site_is_private, private_instance: site_is_private,
default_theme: None, default_theme: None,
default_post_listing_type: site_listing_type, default_post_listing_type: site_listing_type,
default_sort_type: site_sort_type,
legal_information: None, legal_information: None,
application_email_admins: None, application_email_admins: None,
hide_modlog_mod_names: None, hide_modlog_mod_names: None,
@ -566,6 +581,8 @@ mod tests {
taglines: None, taglines: None,
registration_mode: site_registration_mode, registration_mode: site_registration_mode,
reports_email_admins: None, reports_email_admins: None,
content_warning: None,
default_post_listing_mode: None,
} }
} }
} }

View File

@ -142,6 +142,7 @@ pub async fn register(
.show_nsfw(Some(data.show_nsfw)) .show_nsfw(Some(data.show_nsfw))
.accepted_application(accepted_application) .accepted_application(accepted_application)
.default_listing_type(Some(local_site.default_post_listing_type)) .default_listing_type(Some(local_site.default_post_listing_type))
.post_listing_mode(Some(local_site.default_post_listing_mode))
.interface_language(language_tag) .interface_language(language_tag)
// If its the initial site setup, they are an admin // If its the initial site setup, they are an admin
.admin(Some(!local_site.site_setup)) .admin(Some(!local_site.site_setup))

View File

@ -48,9 +48,10 @@ pub async fn list_comments(
let listing_type = Some(listing_type_with_default( let listing_type = Some(listing_type_with_default(
data.type_, data.type_,
local_user_view.as_ref().map(|u| &u.local_user),
&local_site, &local_site,
community_id, community_id,
)?); ));
// If a parent_id is given, fetch the comment to get the path // If a parent_id is given, fetch the comment to get the path
let parent_path = if let Some(parent_id) = parent_id { let parent_path = if let Some(parent_id) = parent_id {

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
api::listing_type_with_default, api::{listing_type_with_default, sort_type_with_default},
fetcher::resolve_actor_identifier, fetcher::resolve_actor_identifier,
objects::community::ApubCommunity, objects::community::ApubCommunity,
}; };
@ -10,10 +10,10 @@ use lemmy_api_common::{
post::{GetPosts, GetPostsResponse}, post::{GetPosts, GetPostsResponse},
utils::check_private_instance, utils::check_private_instance,
}; };
use lemmy_db_schema::source::{community::Community, local_site::LocalSite}; use lemmy_db_schema::source::community::Community;
use lemmy_db_views::{ use lemmy_db_views::{
post_view::PostQuery, post_view::PostQuery,
structs::{LocalUserView, PaginationCursor}, structs::{LocalUserView, PaginationCursor, SiteView},
}; };
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
@ -23,11 +23,9 @@ pub async fn list_posts(
context: Data<LemmyContext>, context: Data<LemmyContext>,
local_user_view: Option<LocalUserView>, local_user_view: Option<LocalUserView>,
) -> Result<Json<GetPostsResponse>, LemmyError> { ) -> Result<Json<GetPostsResponse>, LemmyError> {
let local_site = LocalSite::read(&mut context.pool()).await?; let local_site = SiteView::read_local(&mut context.pool()).await?;
check_private_instance(&local_user_view, &local_site)?; check_private_instance(&local_user_view, &local_site.local_site)?;
let sort = data.sort;
let page = data.page; let page = data.page;
let limit = data.limit; let limit = data.limit;
@ -45,11 +43,20 @@ pub async fn list_posts(
return Err(LemmyError::from(LemmyErrorType::ContradictingFilters)); return Err(LemmyError::from(LemmyErrorType::ContradictingFilters));
} }
let local_user_ref = local_user_view.as_ref().map(|u| &u.local_user);
let listing_type = Some(listing_type_with_default( let listing_type = Some(listing_type_with_default(
data.type_, data.type_,
&local_site, local_user_ref,
&local_site.local_site,
community_id, community_id,
)?); ));
let sort = Some(sort_type_with_default(
data.sort,
local_user_ref,
&local_site.local_site,
));
// parse pagination token // parse pagination token
let page_after = if let Some(pa) = &data.page_cursor { let page_after = if let Some(pa) = &data.page_cursor {
Some(pa.read(&mut context.pool()).await?) Some(pa.read(&mut context.pool()).await?)
@ -70,7 +77,7 @@ pub async fn list_posts(
limit, limit,
..Default::default() ..Default::default()
} }
.list(&mut context.pool()) .list(&local_site.site, &mut context.pool())
.await .await
.with_lemmy_type(LemmyErrorType::CouldntGetPosts)?; .with_lemmy_type(LemmyErrorType::CouldntGetPosts)?;

View File

@ -1,5 +1,9 @@
use lemmy_db_schema::{newtypes::CommunityId, source::local_site::LocalSite, ListingType}; use lemmy_db_schema::{
use lemmy_utils::error::LemmyError; newtypes::CommunityId,
source::{local_site::LocalSite, local_user::LocalUser},
ListingType,
SortType,
};
pub mod list_comments; pub mod list_comments;
pub mod list_posts; pub mod list_posts;
@ -12,15 +16,33 @@ pub mod user_settings_backup;
/// Returns default listing type, depending if the query is for frontpage or community. /// Returns default listing type, depending if the query is for frontpage or community.
fn listing_type_with_default( fn listing_type_with_default(
type_: Option<ListingType>, type_: Option<ListingType>,
local_user: Option<&LocalUser>,
local_site: &LocalSite, local_site: &LocalSite,
community_id: Option<CommunityId>, community_id: Option<CommunityId>,
) -> Result<ListingType, LemmyError> { ) -> ListingType {
// On frontpage use listing type from param or admin configured default // On frontpage use listing type from param or admin configured default
let listing_type = if community_id.is_none() { if community_id.is_none() {
type_.unwrap_or(local_site.default_post_listing_type) type_.unwrap_or(
local_user
.map(|u| u.default_listing_type)
.unwrap_or(local_site.default_post_listing_type),
)
} else { } else {
// inside of community show everything // inside of community show everything
ListingType::All ListingType::All
}; }
Ok(listing_type) }
/// Returns a default instance-level sort type, if none is given by the user.
/// Order is type, local user default, then site default.
fn sort_type_with_default(
type_: Option<SortType>,
local_user: Option<&LocalUser>,
local_site: &LocalSite,
) -> SortType {
type_.unwrap_or(
local_user
.map(|u| u.default_sort_type)
.unwrap_or(local_site.default_sort_type),
)
} }

View File

@ -6,11 +6,12 @@ use lemmy_api_common::{
person::{GetPersonDetails, GetPersonDetailsResponse}, person::{GetPersonDetails, GetPersonDetailsResponse},
utils::{check_private_instance, read_site_for_actor}, utils::{check_private_instance, read_site_for_actor},
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{source::person::Person, utils::post_to_comment_sort_type};
source::{local_site::LocalSite, person::Person}, use lemmy_db_views::{
utils::post_to_comment_sort_type, comment_view::CommentQuery,
post_view::PostQuery,
structs::{LocalUserView, SiteView},
}; };
use lemmy_db_views::{comment_view::CommentQuery, post_view::PostQuery, structs::LocalUserView};
use lemmy_db_views_actor::structs::{CommunityModeratorView, PersonView}; use lemmy_db_views_actor::structs::{CommunityModeratorView, PersonView};
use lemmy_utils::error::{LemmyError, LemmyErrorExt2, LemmyErrorType}; use lemmy_utils::error::{LemmyError, LemmyErrorExt2, LemmyErrorType};
@ -25,9 +26,9 @@ pub async fn read_person(
Err(LemmyErrorType::NoIdGiven)? Err(LemmyErrorType::NoIdGiven)?
} }
let local_site = LocalSite::read(&mut context.pool()).await?; let local_site = SiteView::read_local(&mut context.pool()).await?;
check_private_instance(&local_user_view, &local_site)?; check_private_instance(&local_user_view, &local_site.local_site)?;
let person_details_id = match data.person_id { let person_details_id = match data.person_id {
Some(id) => id, Some(id) => id,
@ -70,7 +71,7 @@ pub async fn read_person(
creator_id, creator_id,
..Default::default() ..Default::default()
} }
.list(&mut context.pool()) .list(&local_site.site, &mut context.pool())
.await?; .await?;
let comments = CommentQuery { let comments = CommentQuery {

View File

@ -6,12 +6,12 @@ use lemmy_api_common::{
site::{Search, SearchResponse}, site::{Search, SearchResponse},
utils::{check_private_instance, is_admin}, utils::{check_private_instance, is_admin},
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{source::community::Community, utils::post_to_comment_sort_type, SearchType};
source::{community::Community, local_site::LocalSite}, use lemmy_db_views::{
utils::post_to_comment_sort_type, comment_view::CommentQuery,
SearchType, post_view::PostQuery,
structs::{LocalUserView, SiteView},
}; };
use lemmy_db_views::{comment_view::CommentQuery, post_view::PostQuery, structs::LocalUserView};
use lemmy_db_views_actor::{community_view::CommunityQuery, person_view::PersonQuery}; use lemmy_db_views_actor::{community_view::CommunityQuery, person_view::PersonQuery};
use lemmy_utils::error::LemmyError; use lemmy_utils::error::LemmyError;
@ -21,9 +21,9 @@ pub async fn search(
context: Data<LemmyContext>, context: Data<LemmyContext>,
local_user_view: Option<LocalUserView>, local_user_view: Option<LocalUserView>,
) -> Result<Json<SearchResponse>, LemmyError> { ) -> Result<Json<SearchResponse>, LemmyError> {
let local_site = LocalSite::read(&mut context.pool()).await?; let local_site = SiteView::read_local(&mut context.pool()).await?;
check_private_instance(&local_user_view, &local_site)?; check_private_instance(&local_user_view, &local_site.local_site)?;
let is_admin = local_user_view let is_admin = local_user_view
.as_ref() .as_ref()
@ -68,7 +68,7 @@ pub async fn search(
limit: (limit), limit: (limit),
..Default::default() ..Default::default()
} }
.list(&mut context.pool()) .list(&local_site.site, &mut context.pool())
.await?; .await?;
} }
SearchType::Comments => { SearchType::Comments => {
@ -97,7 +97,7 @@ pub async fn search(
limit: (limit), limit: (limit),
..Default::default() ..Default::default()
} }
.list(&mut context.pool()) .list(&local_site.site, &mut context.pool())
.await?; .await?;
} }
SearchType::Users => { SearchType::Users => {
@ -128,7 +128,7 @@ pub async fn search(
limit: (limit), limit: (limit),
..Default::default() ..Default::default()
} }
.list(&mut context.pool()) .list(&local_site.site, &mut context.pool())
.await?; .await?;
let q = data.q.clone(); let q = data.q.clone();
@ -162,7 +162,7 @@ pub async fn search(
limit: (limit), limit: (limit),
..Default::default() ..Default::default()
} }
.list(&mut context.pool()) .list(&local_site.site, &mut context.pool())
.await? .await?
}; };
@ -192,7 +192,7 @@ pub async fn search(
limit: (limit), limit: (limit),
..Default::default() ..Default::default()
} }
.list(&mut context.pool()) .list(&local_site.site, &mut context.pool())
.await?; .await?;
} }
}; };

View File

@ -106,6 +106,7 @@ impl Object for ApubSite {
outbox: Url::parse(&format!("{}/site_outbox", self.actor_id))?, outbox: Url::parse(&format!("{}/site_outbox", self.actor_id))?,
public_key: self.public_key(), public_key: self.public_key(),
language, language,
content_warning: self.content_warning.clone(),
published: self.published, published: self.published,
updated: self.updated, updated: self.updated,
}; };
@ -154,6 +155,7 @@ impl Object for ApubSite {
public_key: Some(apub.public_key.public_key_pem.clone()), public_key: Some(apub.public_key.public_key_pem.clone()),
private_key: None, private_key: None,
instance_id: instance.id, instance_id: instance.id,
content_warning: apub.content_warning,
}; };
let languages = let languages =
LanguageTag::to_language_id_multiple(apub.language, &mut context.pool()).await?; LanguageTag::to_language_id_multiple(apub.language, &mut context.pool()).await?;

View File

@ -39,6 +39,8 @@ pub struct Instance {
pub(crate) image: Option<ImageObject>, pub(crate) image: Option<ImageObject>,
#[serde(default)] #[serde(default)]
pub(crate) language: Vec<LanguageTag>, pub(crate) language: Vec<LanguageTag>,
/// nonstandard field
pub(crate) content_warning: Option<String>,
pub(crate) published: DateTime<Utc>, pub(crate) published: DateTime<Utc>,
pub(crate) updated: Option<DateTime<Utc>>, pub(crate) updated: Option<DateTime<Utc>>,
} }

View File

@ -21,3 +21,4 @@ lemmy_db_schema = { workspace = true }
lemmy_db_views = { workspace = true, features = ["full"] } lemmy_db_views = { workspace = true, features = ["full"] }
lemmy_utils = { workspace = true, features = ["default"] } lemmy_utils = { workspace = true, features = ["default"] }
tokio = { workspace = true } tokio = { workspace = true }
url = { workspace = true }

View File

@ -16,6 +16,7 @@ use lemmy_db_schema::{
community::{Community, CommunityInsertForm}, community::{Community, CommunityInsertForm},
instance::Instance, instance::Instance,
person::{Person, PersonInsertForm}, person::{Person, PersonInsertForm},
site::Site,
}, },
traits::Crud, traits::Crud,
utils::{build_db_pool, get_conn, now}, utils::{build_db_pool, get_conn, now},
@ -24,6 +25,7 @@ use lemmy_db_schema::{
use lemmy_db_views::{post_view::PostQuery, structs::PaginationCursor}; use lemmy_db_views::{post_view::PostQuery, structs::PaginationCursor};
use lemmy_utils::error::{LemmyErrorExt2, LemmyResult}; use lemmy_utils::error::{LemmyErrorExt2, LemmyResult};
use std::num::NonZeroU32; use std::num::NonZeroU32;
use url::Url;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
struct CmdArgs { struct CmdArgs {
@ -157,7 +159,7 @@ async fn try_main() -> LemmyResult<()> {
page_after, page_after,
..Default::default() ..Default::default()
} }
.list(&mut conn.into()) .list(&site()?, &mut conn.into())
.await?; .await?;
if let Some(post_view) = post_views.into_iter().last() { if let Some(post_view) = post_views.into_iter().last() {
@ -181,3 +183,23 @@ async fn try_main() -> LemmyResult<()> {
Ok(()) Ok(())
} }
fn site() -> LemmyResult<Site> {
Ok(Site {
id: Default::default(),
name: String::new(),
sidebar: None,
published: Default::default(),
updated: None,
icon: None,
banner: None,
description: None,
actor_id: Url::parse("http://example.com")?.into(),
last_refreshed_at: Default::default(),
inbox_url: Url::parse("http://example.com")?.into(),
private_key: None,
public_key: String::new(),
instance_id: Default::default(),
content_warning: None,
})
}

View File

@ -22,9 +22,10 @@ use crate::{
use diesel::{ use diesel::{
deserialize, deserialize,
dsl, dsl,
dsl::insert_into, dsl::{exists, insert_into},
pg::Pg, pg::Pg,
result::Error, result::Error,
select,
sql_types, sql_types,
ExpressionMethods, ExpressionMethods,
NullableExpressionMethods, NullableExpressionMethods,
@ -235,7 +236,6 @@ impl CommunityFollower {
remote_community_id: CommunityId, remote_community_id: CommunityId,
) -> Result<bool, Error> { ) -> Result<bool, Error> {
use crate::schema::community_follower::dsl::{community_follower, community_id}; use crate::schema::community_follower::dsl::{community_follower, community_id};
use diesel::dsl::{exists, select};
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
select(exists( select(exists(
community_follower.filter(community_id.eq(remote_community_id)), community_follower.filter(community_id.eq(remote_community_id)),

View File

@ -139,7 +139,9 @@ pub enum RegistrationMode {
Open, Open,
} }
#[derive(EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)] #[derive(
EnumString, Display, Debug, Serialize, Deserialize, Default, Clone, Copy, PartialEq, Eq, Hash,
)]
#[cfg_attr(feature = "full", derive(DbEnum, TS))] #[cfg_attr(feature = "full", derive(DbEnum, TS))]
#[cfg_attr( #[cfg_attr(
feature = "full", feature = "full",
@ -150,6 +152,7 @@ pub enum RegistrationMode {
/// A post-view mode that changes how multiple post listings look. /// A post-view mode that changes how multiple post listings look.
pub enum PostListingMode { pub enum PostListingMode {
/// A compact, list-type view. /// A compact, list-type view.
#[default]
List, List,
/// A larger card-type view. /// A larger card-type view.
Card, Card,

View File

@ -353,6 +353,8 @@ diesel::table! {
use diesel::sql_types::*; use diesel::sql_types::*;
use super::sql_types::ListingTypeEnum; use super::sql_types::ListingTypeEnum;
use super::sql_types::RegistrationModeEnum; use super::sql_types::RegistrationModeEnum;
use super::sql_types::PostListingModeEnum;
use super::sql_types::SortTypeEnum;
local_site (id) { local_site (id) {
id -> Int4, id -> Int4,
@ -380,6 +382,8 @@ diesel::table! {
registration_mode -> RegistrationModeEnum, registration_mode -> RegistrationModeEnum,
reports_email_admins -> Bool, reports_email_admins -> Bool,
federation_signed_fetch -> Bool, federation_signed_fetch -> Bool,
default_post_listing_mode -> PostListingModeEnum,
default_sort_type -> SortTypeEnum,
} }
} }
@ -869,6 +873,7 @@ diesel::table! {
private_key -> Nullable<Text>, private_key -> Nullable<Text>,
public_key -> Text, public_key -> Text,
instance_id -> Int4, instance_id -> Int4,
content_warning -> Nullable<Text>,
} }
} }

View File

@ -3,7 +3,9 @@ use crate::schema::local_site;
use crate::{ use crate::{
newtypes::{LocalSiteId, SiteId}, newtypes::{LocalSiteId, SiteId},
ListingType, ListingType,
PostListingMode,
RegistrationMode, RegistrationMode,
SortType,
}; };
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -64,6 +66,10 @@ pub struct LocalSite {
/// Whether to sign outgoing Activitypub fetches with private key of local instance. Some /// Whether to sign outgoing Activitypub fetches with private key of local instance. Some
/// Fediverse instances and platforms require this. /// Fediverse instances and platforms require this.
pub federation_signed_fetch: bool, pub federation_signed_fetch: bool,
/// Default value for [LocalSite.post_listing_mode]
pub default_post_listing_mode: PostListingMode,
/// Default value for [LocalUser.post_listing_mode]
pub default_sort_type: SortType,
} }
#[derive(Clone, TypedBuilder)] #[derive(Clone, TypedBuilder)]
@ -93,6 +99,8 @@ pub struct LocalSiteInsertForm {
pub registration_mode: Option<RegistrationMode>, pub registration_mode: Option<RegistrationMode>,
pub reports_email_admins: Option<bool>, pub reports_email_admins: Option<bool>,
pub federation_signed_fetch: Option<bool>, pub federation_signed_fetch: Option<bool>,
pub default_post_listing_mode: Option<PostListingMode>,
pub default_sort_type: Option<SortType>,
} }
#[derive(Clone, Default)] #[derive(Clone, Default)]
@ -120,4 +128,6 @@ pub struct LocalSiteUpdateForm {
pub reports_email_admins: Option<bool>, pub reports_email_admins: Option<bool>,
pub updated: Option<Option<DateTime<Utc>>>, pub updated: Option<Option<DateTime<Utc>>>,
pub federation_signed_fetch: Option<bool>, pub federation_signed_fetch: Option<bool>,
pub default_post_listing_mode: Option<PostListingMode>,
pub default_sort_type: Option<SortType>,
} }

View File

@ -37,6 +37,9 @@ pub struct Site {
pub private_key: Option<String>, pub private_key: Option<String>,
pub public_key: String, pub public_key: String,
pub instance_id: InstanceId, pub instance_id: InstanceId,
/// If present, nsfw content is visible by default. Should be displayed by frontends/clients
/// when the site is first opened by a user.
pub content_warning: Option<String>,
} }
#[derive(Clone, TypedBuilder)] #[derive(Clone, TypedBuilder)]
@ -58,6 +61,7 @@ pub struct SiteInsertForm {
pub public_key: Option<String>, pub public_key: Option<String>,
#[builder(!default)] #[builder(!default)]
pub instance_id: InstanceId, pub instance_id: InstanceId,
pub content_warning: Option<String>,
} }
#[derive(Clone, Default)] #[derive(Clone, Default)]
@ -76,4 +80,5 @@ pub struct SiteUpdateForm {
pub inbox_url: Option<DbUrl>, pub inbox_url: Option<DbUrl>,
pub private_key: Option<Option<String>>, pub private_key: Option<Option<String>>,
pub public_key: Option<String>, pub public_key: Option<String>,
pub content_warning: Option<Option<String>>,
} }

View File

@ -369,8 +369,7 @@ pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!();
fn run_migrations(db_url: &str) -> Result<(), LemmyError> { fn run_migrations(db_url: &str) -> Result<(), LemmyError> {
// Needs to be a sync connection // Needs to be a sync connection
let mut conn = let mut conn = PgConnection::establish(db_url).with_context(|| "Error connecting to database")?;
PgConnection::establish(db_url).with_context(|| format!("Error connecting to {db_url}"))?;
info!("Running Database migrations (This may take a long time)..."); info!("Running Database migrations (This may take a long time)...");
conn conn

View File

@ -45,3 +45,4 @@ serial_test = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }
chrono = { workspace = true } chrono = { workspace = true }
pretty_assertions = { workspace = true } pretty_assertions = { workspace = true }
url = { workspace = true }

View File

@ -39,6 +39,7 @@ use lemmy_db_schema::{
post_read, post_read,
post_saved, post_saved,
}, },
source::site::Site,
utils::{ utils::{
functions::coalesce, functions::coalesce,
fuzzy_search, fuzzy_search,
@ -61,7 +62,7 @@ use tracing::debug;
fn queries<'a>() -> Queries< fn queries<'a>() -> Queries<
impl ReadFn<'a, PostView, (PostId, Option<PersonId>, bool)>, impl ReadFn<'a, PostView, (PostId, Option<PersonId>, bool)>,
impl ListFn<'a, PostView, PostQuery<'a>>, impl ListFn<'a, PostView, (PostQuery<'a>, &'a Site)>,
> { > {
let is_creator_banned_from_community = exists( let is_creator_banned_from_community = exists(
community_person_ban::table.filter( community_person_ban::table.filter(
@ -270,7 +271,7 @@ fn queries<'a>() -> Queries<
.await .await
}; };
let list = move |mut conn: DbConn<'a>, options: PostQuery<'a>| async move { let list = move |mut conn: DbConn<'a>, (options, site): (PostQuery<'a>, &'a Site)| async move {
let my_person_id = options.local_user.map(|l| l.person.id); let my_person_id = options.local_user.map(|l| l.person.id);
let my_local_user_id = options.local_user.map(|l| l.local_user.id); let my_local_user_id = options.local_user.map(|l| l.local_user.id);
@ -368,10 +369,12 @@ fn queries<'a>() -> Queries<
); );
} }
// If there is a content warning, show nsfw content by default.
let has_content_warning = site.content_warning.is_some();
if !options if !options
.local_user .local_user
.map(|l| l.local_user.show_nsfw) .map(|l| l.local_user.show_nsfw)
.unwrap_or(false) .unwrap_or(has_content_warning)
{ {
query = query query = query
.filter(post::nsfw.eq(false)) .filter(post::nsfw.eq(false))
@ -571,7 +574,7 @@ impl PaginationCursor {
#[derive(Clone)] #[derive(Clone)]
pub struct PaginationCursorData(PostAggregates); pub struct PaginationCursorData(PostAggregates);
#[derive(Default, Clone)] #[derive(Clone, Default)]
pub struct PostQuery<'a> { pub struct PostQuery<'a> {
pub listing_type: Option<ListingType>, pub listing_type: Option<ListingType>,
pub sort: Option<SortType>, pub sort: Option<SortType>,
@ -595,6 +598,7 @@ pub struct PostQuery<'a> {
impl<'a> PostQuery<'a> { impl<'a> PostQuery<'a> {
async fn prefetch_upper_bound_for_page_before( async fn prefetch_upper_bound_for_page_before(
&self, &self,
site: &Site,
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
) -> Result<Option<PostQuery<'a>>, Error> { ) -> Result<Option<PostQuery<'a>>, Error> {
// first get one page for the most popular community to get an upper bound for the the page end for the real query // first get one page for the most popular community to get an upper bound for the the page end for the real query
@ -645,11 +649,14 @@ impl<'a> PostQuery<'a> {
let mut v = queries() let mut v = queries()
.list( .list(
pool, pool,
(
PostQuery { PostQuery {
community_id: Some(largest_subscribed), community_id: Some(largest_subscribed),
community_id_just_for_prefetch: true, community_id_just_for_prefetch: true,
..self.clone() ..self.clone()
}, },
site,
),
) )
.await?; .await?;
// take last element of array. if this query returned less than LIMIT elements, // take last element of array. if this query returned less than LIMIT elements,
@ -671,19 +678,22 @@ impl<'a> PostQuery<'a> {
} }
} }
pub async fn list(self, pool: &mut DbPool<'_>) -> Result<Vec<PostView>, Error> { pub async fn list(self, site: &Site, pool: &mut DbPool<'_>) -> Result<Vec<PostView>, Error> {
if self.listing_type == Some(ListingType::Subscribed) if self.listing_type == Some(ListingType::Subscribed)
&& self.community_id.is_none() && self.community_id.is_none()
&& self.local_user.is_some() && self.local_user.is_some()
&& self.page_before_or_equal.is_none() && self.page_before_or_equal.is_none()
{ {
if let Some(query) = self.prefetch_upper_bound_for_page_before(pool).await? { if let Some(query) = self
queries().list(pool, query).await .prefetch_upper_bound_for_page_before(site, pool)
.await?
{
queries().list(pool, (query, site)).await
} else { } else {
Ok(vec![]) Ok(vec![])
} }
} else { } else {
queries().list(pool, self).await queries().list(pool, (self, site)).await
} }
} }
} }
@ -717,6 +727,7 @@ mod tests {
person::{Person, PersonInsertForm}, person::{Person, PersonInsertForm},
person_block::{PersonBlock, PersonBlockForm}, person_block::{PersonBlock, PersonBlockForm},
post::{Post, PostInsertForm, PostLike, PostLikeForm, PostRead, PostUpdateForm}, post::{Post, PostInsertForm, PostLike, PostLikeForm, PostRead, PostUpdateForm},
site::Site,
}, },
traits::{Blockable, Crud, Joinable, Likeable}, traits::{Blockable, Crud, Joinable, Likeable},
utils::{build_db_pool, build_db_pool_for_tests, DbPool, RANK_DEFAULT}, utils::{build_db_pool, build_db_pool_for_tests, DbPool, RANK_DEFAULT},
@ -728,6 +739,7 @@ mod tests {
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use serial_test::serial; use serial_test::serial;
use std::{collections::HashSet, time::Duration}; use std::{collections::HashSet, time::Duration};
use url::Url;
const POST_BY_BLOCKED_PERSON: &str = "post by blocked person"; const POST_BY_BLOCKED_PERSON: &str = "post by blocked person";
const POST_BY_BOT: &str = "post by bot"; const POST_BY_BOT: &str = "post by bot";
@ -745,6 +757,7 @@ mod tests {
inserted_community: Community, inserted_community: Community,
inserted_post: Post, inserted_post: Post,
inserted_bot_post: Post, inserted_bot_post: Post,
site: Site,
} }
impl Data { impl Data {
@ -842,6 +855,24 @@ mod tests {
counts: Default::default(), counts: Default::default(),
}; };
let site = Site {
id: Default::default(),
name: String::new(),
sidebar: None,
published: Default::default(),
updated: None,
icon: None,
banner: None,
description: None,
actor_id: Url::parse("http://example.com")?.into(),
last_refreshed_at: Default::default(),
inbox_url: Url::parse("http://example.com")?.into(),
private_key: None,
public_key: String::new(),
instance_id: Default::default(),
content_warning: None,
};
Ok(Data { Ok(Data {
inserted_instance, inserted_instance,
local_user_view, local_user_view,
@ -850,6 +881,7 @@ mod tests {
inserted_community, inserted_community,
inserted_post, inserted_post,
inserted_bot_post, inserted_bot_post,
site,
}) })
} }
@ -872,7 +904,7 @@ mod tests {
community_id: Some(data.inserted_community.id), community_id: Some(data.inserted_community.id),
..data.default_post_query() ..data.default_post_query()
} }
.list(pool) .list(&data.site, pool)
.await?; .await?;
let post_listing_single_with_person = PostView::read( let post_listing_single_with_person = PostView::read(
@ -907,7 +939,7 @@ mod tests {
community_id: Some(data.inserted_community.id), community_id: Some(data.inserted_community.id),
..data.default_post_query() ..data.default_post_query()
} }
.list(pool) .list(&data.site, pool)
.await?; .await?;
// should include bot post which has "undetermined" language // should include bot post which has "undetermined" language
assert_eq!(vec![POST_BY_BOT, POST], names(&post_listings_with_bots)); assert_eq!(vec![POST_BY_BOT, POST], names(&post_listings_with_bots));
@ -927,7 +959,7 @@ mod tests {
local_user: None, local_user: None,
..data.default_post_query() ..data.default_post_query()
} }
.list(pool) .list(&data.site, pool)
.await?; .await?;
let read_post_listing_single_no_person = let read_post_listing_single_no_person =
@ -970,7 +1002,7 @@ mod tests {
community_id: Some(data.inserted_community.id), community_id: Some(data.inserted_community.id),
..data.default_post_query() ..data.default_post_query()
} }
.list(pool) .list(&data.site, pool)
.await?; .await?;
// Should be 0 posts after the community block // Should be 0 posts after the community block
assert_eq!(read_post_listings_with_person_after_block, vec![]); assert_eq!(read_post_listings_with_person_after_block, vec![]);
@ -1028,7 +1060,7 @@ mod tests {
community_id: Some(data.inserted_community.id), community_id: Some(data.inserted_community.id),
..data.default_post_query() ..data.default_post_query()
} }
.list(pool) .list(&data.site, pool)
.await?; .await?;
assert_eq!(vec![expected_post_with_upvote], read_post_listing); assert_eq!(vec![expected_post_with_upvote], read_post_listing);
@ -1037,7 +1069,7 @@ mod tests {
liked_only: true, liked_only: true,
..data.default_post_query() ..data.default_post_query()
} }
.list(pool) .list(&data.site, pool)
.await?; .await?;
assert_eq!(read_post_listing, read_liked_post_listing); assert_eq!(read_post_listing, read_liked_post_listing);
@ -1046,7 +1078,7 @@ mod tests {
disliked_only: true, disliked_only: true,
..data.default_post_query() ..data.default_post_query()
} }
.list(pool) .list(&data.site, pool)
.await?; .await?;
assert_eq!(read_disliked_post_listing, vec![]); assert_eq!(read_disliked_post_listing, vec![]);
@ -1076,7 +1108,7 @@ mod tests {
community_id: Some(data.inserted_community.id), community_id: Some(data.inserted_community.id),
..data.default_post_query() ..data.default_post_query()
} }
.list(pool) .list(&data.site, pool)
.await? .await?
.into_iter() .into_iter()
.map(|p| (p.creator.name, p.creator_is_moderator, p.creator_is_admin)) .map(|p| (p.creator.name, p.creator_is_moderator, p.creator_is_admin))
@ -1118,14 +1150,14 @@ mod tests {
Post::create(pool, &post_spanish).await?; Post::create(pool, &post_spanish).await?;
let post_listings_all = data.default_post_query().list(pool).await?; let post_listings_all = data.default_post_query().list(&data.site, pool).await?;
// no language filters specified, all posts should be returned // no language filters specified, all posts should be returned
assert_eq!(vec![EL_POSTO, POST_BY_BOT, POST], names(&post_listings_all)); assert_eq!(vec![EL_POSTO, POST_BY_BOT, POST], names(&post_listings_all));
LocalUserLanguage::update(pool, vec![french_id], data.local_user_view.local_user.id).await?; LocalUserLanguage::update(pool, vec![french_id], data.local_user_view.local_user.id).await?;
let post_listing_french = data.default_post_query().list(pool).await?; let post_listing_french = data.default_post_query().list(&data.site, pool).await?;
// only one post in french and one undetermined should be returned // only one post in french and one undetermined should be returned
assert_eq!(vec![POST_BY_BOT, POST], names(&post_listing_french)); assert_eq!(vec![POST_BY_BOT, POST], names(&post_listing_french));
@ -1142,7 +1174,7 @@ mod tests {
.await?; .await?;
let post_listings_french_und = data let post_listings_french_und = data
.default_post_query() .default_post_query()
.list(pool) .list(&data.site, pool)
.await? .await?
.into_iter() .into_iter()
.map(|p| (p.post.name, p.post.language_id)) .map(|p| (p.post.name, p.post.language_id))
@ -1177,7 +1209,7 @@ mod tests {
.await?; .await?;
// Make sure you don't see the removed post in the results // Make sure you don't see the removed post in the results
let post_listings_no_admin = data.default_post_query().list(pool).await?; let post_listings_no_admin = data.default_post_query().list(&data.site, pool).await?;
assert_eq!(vec![POST], names(&post_listings_no_admin)); assert_eq!(vec![POST], names(&post_listings_no_admin));
// Removed bot post is shown to admins on its profile page // Removed bot post is shown to admins on its profile page
@ -1186,7 +1218,7 @@ mod tests {
creator_id: Some(data.inserted_bot.id), creator_id: Some(data.inserted_bot.id),
..data.default_post_query() ..data.default_post_query()
} }
.list(pool) .list(&data.site, pool)
.await?; .await?;
assert_eq!(vec![POST_BY_BOT], names(&post_listings_is_admin)); assert_eq!(vec![POST_BY_BOT], names(&post_listings_is_admin));
@ -1221,7 +1253,7 @@ mod tests {
local_user, local_user,
..data.default_post_query() ..data.default_post_query()
} }
.list(pool) .list(&data.site, pool)
.await? .await?
.iter() .iter()
.any(|p| p.post.id == data.inserted_post.id); .any(|p| p.post.id == data.inserted_post.id);
@ -1261,7 +1293,7 @@ mod tests {
let post_from_blocked_instance = Post::create(pool, &post_form).await?; let post_from_blocked_instance = Post::create(pool, &post_form).await?;
// no instance block, should return all posts // no instance block, should return all posts
let post_listings_all = data.default_post_query().list(pool).await?; let post_listings_all = data.default_post_query().list(&data.site, pool).await?;
assert_eq!( assert_eq!(
vec![POST_FROM_BLOCKED_INSTANCE, POST_BY_BOT, POST], vec![POST_FROM_BLOCKED_INSTANCE, POST_BY_BOT, POST],
names(&post_listings_all) names(&post_listings_all)
@ -1275,7 +1307,7 @@ mod tests {
InstanceBlock::block(pool, &block_form).await?; InstanceBlock::block(pool, &block_form).await?;
// now posts from communities on that instance should be hidden // now posts from communities on that instance should be hidden
let post_listings_blocked = data.default_post_query().list(pool).await?; let post_listings_blocked = data.default_post_query().list(&data.site, pool).await?;
assert_eq!(vec![POST_BY_BOT, POST], names(&post_listings_blocked)); assert_eq!(vec![POST_BY_BOT, POST], names(&post_listings_blocked));
assert!(post_listings_blocked assert!(post_listings_blocked
.iter() .iter()
@ -1283,7 +1315,7 @@ mod tests {
// after unblocking it should return all posts again // after unblocking it should return all posts again
InstanceBlock::unblock(pool, &block_form).await?; InstanceBlock::unblock(pool, &block_form).await?;
let post_listings_blocked = data.default_post_query().list(pool).await?; let post_listings_blocked = data.default_post_query().list(&data.site, pool).await?;
assert_eq!( assert_eq!(
vec![POST_FROM_BLOCKED_INSTANCE, POST_BY_BOT, POST], vec![POST_FROM_BLOCKED_INSTANCE, POST_BY_BOT, POST],
names(&post_listings_blocked) names(&post_listings_blocked)
@ -1351,7 +1383,7 @@ mod tests {
page_after, page_after,
..options.clone() ..options.clone()
} }
.list(pool) .list(&data.site, pool)
.await?; .await?;
listed_post_ids.extend(post_listings.iter().map(|p| p.post.id)); listed_post_ids.extend(post_listings.iter().map(|p| p.post.id));
@ -1372,7 +1404,7 @@ mod tests {
page_back: true, page_back: true,
..options.clone() ..options.clone()
} }
.list(pool) .list(&data.site, pool)
.await?; .await?;
let listed_post_ids = post_listings.iter().map(|p| p.post.id).collect::<Vec<_>>(); let listed_post_ids = post_listings.iter().map(|p| p.post.id).collect::<Vec<_>>();
@ -1425,7 +1457,7 @@ mod tests {
.await?; .await?;
// Make sure you don't see the read post in the results // Make sure you don't see the read post in the results
let post_listings_hide_read = data.default_post_query().list(pool).await?; let post_listings_hide_read = data.default_post_query().list(&data.site, pool).await?;
assert_eq!(vec![POST], names(&post_listings_hide_read)); assert_eq!(vec![POST], names(&post_listings_hide_read));
cleanup(data, pool).await cleanup(data, pool).await
@ -1577,7 +1609,7 @@ mod tests {
let unauthenticated_query = PostQuery { let unauthenticated_query = PostQuery {
..Default::default() ..Default::default()
} }
.list(pool) .list(&data.site, pool)
.await?; .await?;
assert_eq!(0, unauthenticated_query.len()); assert_eq!(0, unauthenticated_query.len());
@ -1585,7 +1617,7 @@ mod tests {
local_user: Some(&data.local_user_view), local_user: Some(&data.local_user_view),
..Default::default() ..Default::default()
} }
.list(pool) .list(&data.site, pool)
.await?; .await?;
assert_eq!(2, authenticated_query.len()); assert_eq!(2, authenticated_query.len());

View File

@ -39,6 +39,7 @@ strum_macros = { workspace = true }
serial_test = { workspace = true } serial_test = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }
pretty_assertions = { workspace = true } pretty_assertions = { workspace = true }
url.workspace = true
[package.metadata.cargo-machete] [package.metadata.cargo-machete]
ignored = ["strum"] ignored = ["strum"]

View File

@ -20,7 +20,7 @@ use lemmy_db_schema::{
instance_block, instance_block,
local_user, local_user,
}, },
source::{community::CommunityFollower, local_user::LocalUser}, source::{community::CommunityFollower, local_user::LocalUser, site::Site},
utils::{fuzzy_search, limit_and_offset, DbConn, DbPool, ListFn, Queries, ReadFn}, utils::{fuzzy_search, limit_and_offset, DbConn, DbPool, ListFn, Queries, ReadFn},
CommunityVisibility, CommunityVisibility,
ListingType, ListingType,
@ -29,7 +29,7 @@ use lemmy_db_schema::{
fn queries<'a>() -> Queries< fn queries<'a>() -> Queries<
impl ReadFn<'a, CommunityView, (CommunityId, Option<PersonId>, bool)>, impl ReadFn<'a, CommunityView, (CommunityId, Option<PersonId>, bool)>,
impl ListFn<'a, CommunityView, CommunityQuery<'a>>, impl ListFn<'a, CommunityView, (CommunityQuery<'a>, &'a Site)>,
> { > {
let all_joins = |query: community::BoxedQuery<'a, Pg>, my_person_id: Option<PersonId>| { let all_joins = |query: community::BoxedQuery<'a, Pg>, my_person_id: Option<PersonId>| {
// The left join below will return None in this case // The left join below will return None in this case
@ -96,7 +96,7 @@ fn queries<'a>() -> Queries<
query.first::<CommunityView>(&mut conn).await query.first::<CommunityView>(&mut conn).await
}; };
let list = move |mut conn: DbConn<'a>, options: CommunityQuery<'a>| async move { let list = move |mut conn: DbConn<'a>, (options, site): (CommunityQuery<'a>, &'a Site)| async move {
use SortType::*; use SortType::*;
let my_person_id = options.local_user.map(|l| l.person_id); let my_person_id = options.local_user.map(|l| l.person_id);
@ -158,8 +158,10 @@ fn queries<'a>() -> Queries<
query = query.filter(community_block::person_id.is_null()); query = query.filter(community_block::person_id.is_null());
query = query.filter(community::nsfw.eq(false).or(local_user::show_nsfw.eq(true))); query = query.filter(community::nsfw.eq(false).or(local_user::show_nsfw.eq(true)));
} else { } else {
// No person in request, only show nsfw communities if show_nsfw is passed into request // No person in request, only show nsfw communities if show_nsfw is passed into request or if
if !options.show_nsfw { // site has content warning.
let has_content_warning = site.content_warning.is_some();
if !options.show_nsfw && !has_content_warning {
query = query.filter(community::nsfw.eq(false)); query = query.filter(community::nsfw.eq(false));
} }
// Hide local only communities from unauthenticated users // Hide local only communities from unauthenticated users
@ -233,8 +235,8 @@ pub struct CommunityQuery<'a> {
} }
impl<'a> CommunityQuery<'a> { impl<'a> CommunityQuery<'a> {
pub async fn list(self, pool: &mut DbPool<'_>) -> Result<Vec<CommunityView>, Error> { pub async fn list(self, site: &Site, pool: &mut DbPool<'_>) -> Result<Vec<CommunityView>, Error> {
queries().list(pool, self).await queries().list(pool, (self, site)).await
} }
} }
@ -250,17 +252,20 @@ mod tests {
instance::Instance, instance::Instance,
local_user::{LocalUser, LocalUserInsertForm}, local_user::{LocalUser, LocalUserInsertForm},
person::{Person, PersonInsertForm}, person::{Person, PersonInsertForm},
site::Site,
}, },
traits::Crud, traits::Crud,
utils::{build_db_pool_for_tests, DbPool}, utils::{build_db_pool_for_tests, DbPool},
CommunityVisibility, CommunityVisibility,
}; };
use serial_test::serial; use serial_test::serial;
use url::Url;
struct Data { struct Data {
inserted_instance: Instance, inserted_instance: Instance,
local_user: LocalUser, local_user: LocalUser,
inserted_community: Community, inserted_community: Community,
site: Site,
} }
async fn init_data(pool: &mut DbPool<'_>) -> Data { async fn init_data(pool: &mut DbPool<'_>) -> Data {
@ -293,10 +298,30 @@ mod tests {
let inserted_community = Community::create(pool, &new_community).await.unwrap(); let inserted_community = Community::create(pool, &new_community).await.unwrap();
let url = Url::parse("http://example.com").unwrap();
let site = Site {
id: Default::default(),
name: String::new(),
sidebar: None,
published: Default::default(),
updated: None,
icon: None,
banner: None,
description: None,
actor_id: url.clone().into(),
last_refreshed_at: Default::default(),
inbox_url: url.into(),
private_key: None,
public_key: String::new(),
instance_id: Default::default(),
content_warning: None,
};
Data { Data {
inserted_instance, inserted_instance,
local_user, local_user,
inserted_community, inserted_community,
site,
} }
} }
@ -333,7 +358,7 @@ mod tests {
let unauthenticated_query = CommunityQuery { let unauthenticated_query = CommunityQuery {
..Default::default() ..Default::default()
} }
.list(pool) .list(&data.site, pool)
.await .await
.unwrap(); .unwrap();
assert_eq!(0, unauthenticated_query.len()); assert_eq!(0, unauthenticated_query.len());
@ -342,7 +367,7 @@ mod tests {
local_user: Some(&data.local_user), local_user: Some(&data.local_user),
..Default::default() ..Default::default()
} }
.list(pool) .list(&data.site, pool)
.await .await
.unwrap(); .unwrap();
assert_eq!(1, authenticated_query.len()); assert_eq!(1, authenticated_query.len());

View File

@ -163,7 +163,7 @@ async fn get_feed_data(
page: (Some(page)), page: (Some(page)),
..Default::default() ..Default::default()
} }
.list(&mut context.pool()) .list(&site_view.site, &mut context.pool())
.await?; .await?;
let items = create_post_items(posts, &context.settings().get_protocol_and_hostname())?; let items = create_post_items(posts, &context.settings().get_protocol_and_hostname())?;
@ -270,7 +270,7 @@ async fn get_feed_user(
page: (Some(*page)), page: (Some(*page)),
..Default::default() ..Default::default()
} }
.list(&mut context.pool()) .list(&site_view.site, &mut context.pool())
.await?; .await?;
let items = create_post_items(posts, &context.settings().get_protocol_and_hostname())?; let items = create_post_items(posts, &context.settings().get_protocol_and_hostname())?;
@ -308,7 +308,7 @@ async fn get_feed_community(
page: (Some(*page)), page: (Some(*page)),
..Default::default() ..Default::default()
} }
.list(&mut context.pool()) .list(&site_view.site, &mut context.pool())
.await?; .await?;
let items = create_post_items(posts, &context.settings().get_protocol_and_hostname())?; let items = create_post_items(posts, &context.settings().get_protocol_and_hostname())?;
@ -349,7 +349,7 @@ async fn get_feed_front(
page: (Some(*page)), page: (Some(*page)),
..Default::default() ..Default::default()
} }
.list(&mut context.pool()) .list(&site_view.site, &mut context.pool())
.await?; .await?;
let protocol_and_hostname = context.settings().get_protocol_and_hostname(); let protocol_and_hostname = context.settings().get_protocol_and_hostname();

View File

@ -0,0 +1,6 @@
ALTER TABLE site
DROP COLUMN content_warning;
ALTER TABLE local_site
DROP COLUMN default_post_listing_mode;

View File

@ -0,0 +1,6 @@
ALTER TABLE site
ADD COLUMN content_warning text;
ALTER TABLE local_site
ADD COLUMN default_post_listing_mode post_listing_mode_enum NOT NULL DEFAULT 'List';

View File

@ -0,0 +1,3 @@
ALTER TABLE local_site
DROP COLUMN default_sort_type;

View File

@ -0,0 +1,3 @@
ALTER TABLE local_site
ADD COLUMN default_sort_type sort_type_enum DEFAULT 'Active' NOT NULL;