mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-10-01 01:36:12 -04:00
Merge remote-tracking branch 'origin/main' into vote_display_mode
This commit is contained in:
commit
04faedd458
@ -2,7 +2,7 @@
|
||||
# See https://github.com/woodpecker-ci/woodpecker/issues/1677
|
||||
|
||||
variables:
|
||||
- &rust_image "rust:1.75"
|
||||
- &rust_image "rust:1.76"
|
||||
- &install_pnpm "corepack enable pnpm"
|
||||
- &slow_check_paths
|
||||
- path:
|
||||
|
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -2683,6 +2683,7 @@ dependencies = [
|
||||
"lemmy_db_views",
|
||||
"lemmy_utils",
|
||||
"tokio",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2743,6 +2744,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tracing",
|
||||
"ts-rs",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2761,6 +2763,7 @@ dependencies = [
|
||||
"strum_macros",
|
||||
"tokio",
|
||||
"ts-rs",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5,7 +5,11 @@ use lemmy_api_common::{
|
||||
comment::{CommentReportResponse, CreateCommentReport},
|
||||
context::LemmyContext,
|
||||
send_activity::{ActivityChannel, SendActivityData},
|
||||
utils::{check_community_user_action, send_new_report_email_to_admins},
|
||||
utils::{
|
||||
check_comment_deleted_or_removed,
|
||||
check_community_user_action,
|
||||
send_new_report_email_to_admins,
|
||||
},
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
@ -40,6 +44,9 @@ pub async fn create_comment_report(
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Don't allow creating reports for removed / deleted comments
|
||||
check_comment_deleted_or_removed(&comment_view.comment)?;
|
||||
|
||||
let report_form = CommentReportForm {
|
||||
creator_id: person_id,
|
||||
comment_id,
|
||||
|
@ -5,7 +5,11 @@ use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
post::{CreatePostReport, PostReportResponse},
|
||||
send_activity::{ActivityChannel, SendActivityData},
|
||||
utils::{check_community_user_action, send_new_report_email_to_admins},
|
||||
utils::{
|
||||
check_community_user_action,
|
||||
check_post_deleted_or_removed,
|
||||
send_new_report_email_to_admins,
|
||||
},
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
@ -40,6 +44,8 @@ pub async fn create_post_report(
|
||||
)
|
||||
.await?;
|
||||
|
||||
check_post_deleted_or_removed(&post_view.post)?;
|
||||
|
||||
let report_form = PostReportForm {
|
||||
creator_id: person_id,
|
||||
post_id,
|
||||
|
@ -28,6 +28,9 @@ pub struct CreatePost {
|
||||
pub honeypot: Option<String>,
|
||||
pub nsfw: Option<bool>,
|
||||
pub language_id: Option<LanguageId>,
|
||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
||||
/// Instead of fetching a thumbnail, use a custom one.
|
||||
pub custom_thumbnail: Option<Url>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
@ -114,6 +117,9 @@ pub struct EditPost {
|
||||
pub body: Option<String>,
|
||||
pub nsfw: Option<bool>,
|
||||
pub language_id: Option<LanguageId>,
|
||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
||||
/// Instead of fetching a thumbnail, use a custom one.
|
||||
pub custom_thumbnail: Option<Url>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
||||
|
@ -10,6 +10,7 @@ use lemmy_db_schema::{
|
||||
},
|
||||
ListingType,
|
||||
ModlogActionType,
|
||||
PostListingMode,
|
||||
RegistrationMode,
|
||||
SearchType,
|
||||
SortType,
|
||||
@ -161,6 +162,7 @@ pub struct CreateSite {
|
||||
pub private_instance: Option<bool>,
|
||||
pub default_theme: Option<String>,
|
||||
pub default_post_listing_type: Option<ListingType>,
|
||||
pub default_sort_type: Option<SortType>,
|
||||
pub legal_information: Option<String>,
|
||||
pub application_email_admins: Option<bool>,
|
||||
pub hide_modlog_mod_names: Option<bool>,
|
||||
@ -187,6 +189,8 @@ pub struct CreateSite {
|
||||
pub blocked_instances: Option<Vec<String>>,
|
||||
pub taglines: Option<Vec<String>>,
|
||||
pub registration_mode: Option<RegistrationMode>,
|
||||
pub content_warning: Option<String>,
|
||||
pub default_post_listing_mode: Option<PostListingMode>,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
@ -218,6 +222,8 @@ pub struct EditSite {
|
||||
/// The default theme. Usually "browser"
|
||||
pub default_theme: Option<String>,
|
||||
pub default_post_listing_type: Option<ListingType>,
|
||||
/// The default sort, usually "active"
|
||||
pub default_sort_type: Option<SortType>,
|
||||
/// An optional page of legal information
|
||||
pub legal_information: Option<String>,
|
||||
/// Whether to email admins when receiving a new application.
|
||||
@ -265,6 +271,11 @@ pub struct EditSite {
|
||||
pub registration_mode: Option<RegistrationMode>,
|
||||
/// Whether to email admins for new reports.
|
||||
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)]
|
||||
|
@ -225,6 +225,7 @@ pub async fn check_community_mod_action(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Don't allow creating reports for removed / deleted posts
|
||||
pub fn check_post_deleted_or_removed(post: &Post) -> Result<(), LemmyError> {
|
||||
if post.deleted || post.removed {
|
||||
Err(LemmyErrorType::Deleted)?
|
||||
@ -233,6 +234,14 @@ pub fn check_post_deleted_or_removed(post: &Post) -> Result<(), LemmyError> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_comment_deleted_or_removed(comment: &Comment) -> Result<(), LemmyError> {
|
||||
if comment.deleted || comment.removed {
|
||||
Err(LemmyErrorType::Deleted)?
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Throws an error if a recipient has blocked a person.
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn check_person_block(
|
||||
|
@ -10,10 +10,11 @@ use lemmy_api_common::{
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
comment::{Comment, CommentUpdateForm},
|
||||
comment_report::CommentReport,
|
||||
moderator::{ModRemoveComment, ModRemoveCommentForm},
|
||||
post::Post,
|
||||
},
|
||||
traits::Crud,
|
||||
traits::{Crud, Reportable},
|
||||
};
|
||||
use lemmy_db_views::structs::{CommentView, LocalUserView};
|
||||
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
||||
@ -48,6 +49,9 @@ pub async fn remove_comment(
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
|
||||
|
||||
CommentReport::resolve_all_for_object(&mut context.pool(), comment_id, local_user_view.person.id)
|
||||
.await?;
|
||||
|
||||
// Mod tables
|
||||
let form = ModRemoveCommentForm {
|
||||
mod_person_id: local_user_view.person.id,
|
||||
|
@ -4,8 +4,7 @@ use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
utils::{check_private_instance, is_admin},
|
||||
};
|
||||
use lemmy_db_schema::source::local_site::LocalSite;
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
||||
use lemmy_db_views_actor::community_view::CommunityQuery;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
|
||||
@ -15,13 +14,13 @@ pub async fn list_communities(
|
||||
context: Data<LemmyContext>,
|
||||
local_user_view: Option<LocalUserView>,
|
||||
) -> 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
|
||||
.as_ref()
|
||||
.map(|luv| is_admin(luv).is_ok())
|
||||
.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 listing_type = data.type_;
|
||||
@ -39,7 +38,7 @@ pub async fn list_communities(
|
||||
is_mod_or_admin: is_admin,
|
||||
..Default::default()
|
||||
}
|
||||
.list(&mut context.pool())
|
||||
.list(&local_site.site, &mut context.pool())
|
||||
.await?;
|
||||
|
||||
// Return the jwt
|
||||
|
@ -57,10 +57,12 @@ pub async fn create_post(
|
||||
|
||||
let data_url = data.url.as_ref();
|
||||
let url = data_url.map(clean_url_params); // TODO no good way to handle a "clear"
|
||||
let custom_thumbnail = data.custom_thumbnail.as_ref().map(clean_url_params);
|
||||
|
||||
is_valid_post_title(&data.name)?;
|
||||
is_valid_body_field(&body, true)?;
|
||||
check_url_scheme(&data.url)?;
|
||||
check_url_scheme(&url)?;
|
||||
check_url_scheme(&custom_thumbnail)?;
|
||||
|
||||
check_community_user_action(
|
||||
&local_user_view.person,
|
||||
@ -84,9 +86,17 @@ pub async fn create_post(
|
||||
}
|
||||
}
|
||||
|
||||
// Only generate the thumbnail if there's no custom thumbnail provided,
|
||||
// otherwise it will save it in pictrs
|
||||
let generate_thumbnail = custom_thumbnail.is_none();
|
||||
|
||||
// Fetch post links and pictrs cached image
|
||||
let metadata = fetch_link_metadata_opt(url.as_ref(), true, &context).await;
|
||||
let metadata = fetch_link_metadata_opt(url.as_ref(), generate_thumbnail, &context).await;
|
||||
let url = proxy_image_link_opt_apub(url, &context).await?;
|
||||
let thumbnail_url = proxy_image_link_opt_apub(custom_thumbnail, &context)
|
||||
.await?
|
||||
.map(Into::into)
|
||||
.or(metadata.thumbnail);
|
||||
|
||||
// Only need to check if language is allowed in case user set it explicitly. When using default
|
||||
// language, it already only returns allowed languages.
|
||||
@ -121,7 +131,7 @@ pub async fn create_post(
|
||||
.embed_description(metadata.opengraph_data.description)
|
||||
.embed_video_url(metadata.opengraph_data.embed_video_url)
|
||||
.language_id(language_id)
|
||||
.thumbnail_url(metadata.thumbnail)
|
||||
.thumbnail_url(thumbnail_url)
|
||||
.build();
|
||||
|
||||
let inserted_post = Post::create(&mut context.pool(), &post_form)
|
||||
|
@ -6,12 +6,12 @@ use lemmy_api_common::{
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
aggregates::structs::{PersonPostAggregates, PersonPostAggregatesForm},
|
||||
source::{comment::Comment, local_site::LocalSite, post::Post},
|
||||
source::{comment::Comment, post::Post},
|
||||
traits::Crud,
|
||||
};
|
||||
use lemmy_db_views::{
|
||||
post_view::PostQuery,
|
||||
structs::{LocalUserView, PostView},
|
||||
structs::{LocalUserView, PostView, SiteView},
|
||||
};
|
||||
use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView};
|
||||
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
||||
@ -22,9 +22,9 @@ pub async fn get_post(
|
||||
context: Data<LemmyContext>,
|
||||
local_user_view: Option<LocalUserView>,
|
||||
) -> 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);
|
||||
|
||||
@ -93,7 +93,7 @@ pub async fn get_post(
|
||||
url_search: Some(url.inner().as_str().into()),
|
||||
..Default::default()
|
||||
}
|
||||
.list(&mut context.pool())
|
||||
.list(&local_site.site, &mut context.pool())
|
||||
.await?;
|
||||
|
||||
// Don't return this post as one of the cross_posts
|
||||
|
@ -11,8 +11,9 @@ use lemmy_db_schema::{
|
||||
source::{
|
||||
moderator::{ModRemovePost, ModRemovePostForm},
|
||||
post::{Post, PostUpdateForm},
|
||||
post_report::PostReport,
|
||||
},
|
||||
traits::Crud,
|
||||
traits::{Crud, Reportable},
|
||||
};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
@ -47,6 +48,9 @@ pub async fn remove_post(
|
||||
)
|
||||
.await?;
|
||||
|
||||
PostReport::resolve_all_for_object(&mut context.pool(), post_id, local_user_view.person.id)
|
||||
.await?;
|
||||
|
||||
// Mod tables
|
||||
let form = ModRemovePostForm {
|
||||
mod_person_id: local_user_view.person.id,
|
||||
|
@ -43,6 +43,7 @@ pub async fn update_post(
|
||||
// TODO No good way to handle a clear.
|
||||
// Issue link: https://github.com/LemmyNet/lemmy/issues/2287
|
||||
let url = data.url.as_ref().map(clean_url_params);
|
||||
let custom_thumbnail = data.custom_thumbnail.as_ref().map(clean_url_params);
|
||||
|
||||
let slur_regex = local_site_to_slur_regex(&local_site);
|
||||
check_slurs_opt(&data.name, &slur_regex)?;
|
||||
@ -53,7 +54,8 @@ pub async fn update_post(
|
||||
}
|
||||
|
||||
is_valid_body_field(&body, true)?;
|
||||
check_url_scheme(&data.url)?;
|
||||
check_url_scheme(&url)?;
|
||||
check_url_scheme(&custom_thumbnail)?;
|
||||
|
||||
let post_id = data.post_id;
|
||||
let orig_post = Post::read(&mut context.pool(), post_id).await?;
|
||||
@ -70,10 +72,14 @@ pub async fn update_post(
|
||||
Err(LemmyErrorType::NoPostEditAllowed)?
|
||||
}
|
||||
|
||||
// Fetch post links and Pictrs cached image if url was updated
|
||||
let (embed_title, embed_description, embed_video_url, thumbnail_url) = match &url {
|
||||
// Fetch post links and thumbnail if url was updated
|
||||
let (embed_title, embed_description, embed_video_url, metadata_thumbnail) = match &url {
|
||||
Some(url) => {
|
||||
let metadata = fetch_link_metadata(url, true, &context).await?;
|
||||
// Only generate the thumbnail if there's no custom thumbnail provided,
|
||||
// otherwise it will save it in pictrs
|
||||
let generate_thumbnail = custom_thumbnail.is_none();
|
||||
|
||||
let metadata = fetch_link_metadata(url, generate_thumbnail, &context).await?;
|
||||
(
|
||||
Some(metadata.opengraph_data.title),
|
||||
Some(metadata.opengraph_data.description),
|
||||
@ -83,11 +89,21 @@ pub async fn update_post(
|
||||
}
|
||||
_ => Default::default(),
|
||||
};
|
||||
|
||||
let url = match url {
|
||||
Some(url) => Some(proxy_image_link_opt_apub(Some(url), &context).await?),
|
||||
_ => Default::default(),
|
||||
};
|
||||
|
||||
let custom_thumbnail = match custom_thumbnail {
|
||||
Some(custom_thumbnail) => {
|
||||
Some(proxy_image_link_opt_apub(Some(custom_thumbnail), &context).await?)
|
||||
}
|
||||
_ => Default::default(),
|
||||
};
|
||||
|
||||
let thumbnail_url = custom_thumbnail.or(metadata_thumbnail);
|
||||
|
||||
let language_id = data.language_id;
|
||||
CommunityLanguage::is_allowed_community_language(
|
||||
&mut context.pool(),
|
||||
|
@ -73,6 +73,7 @@ pub async fn create_site(
|
||||
inbox_url,
|
||||
private_key: Some(Some(keypair.private_key)),
|
||||
public_key: Some(keypair.public_key),
|
||||
content_warning: diesel_option_overwrite(data.content_warning.clone()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@ -92,6 +93,7 @@ pub async fn create_site(
|
||||
private_instance: data.private_instance,
|
||||
default_theme: data.default_theme.clone(),
|
||||
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()),
|
||||
application_email_admins: data.application_email_admins,
|
||||
hide_modlog_mod_names: data.hide_modlog_mod_names,
|
||||
@ -101,6 +103,7 @@ pub async fn create_site(
|
||||
federation_enabled: data.federation_enabled,
|
||||
captcha_enabled: data.captcha_enabled,
|
||||
captcha_difficulty: data.captcha_difficulty.clone(),
|
||||
default_post_listing_mode: data.default_post_listing_mode,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@ -190,7 +193,7 @@ mod tests {
|
||||
|
||||
use crate::site::create::validate_create_payload;
|
||||
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;
|
||||
|
||||
#[test]
|
||||
@ -212,6 +215,7 @@ mod tests {
|
||||
None::<String>,
|
||||
None::<String>,
|
||||
None::<ListingType>,
|
||||
None::<SortType>,
|
||||
None::<String>,
|
||||
None::<bool>,
|
||||
None::<bool>,
|
||||
@ -235,6 +239,7 @@ mod tests {
|
||||
None::<String>,
|
||||
None::<String>,
|
||||
None::<ListingType>,
|
||||
None::<SortType>,
|
||||
None::<String>,
|
||||
None::<bool>,
|
||||
None::<bool>,
|
||||
@ -258,6 +263,7 @@ mod tests {
|
||||
None::<String>,
|
||||
None::<String>,
|
||||
None::<ListingType>,
|
||||
None::<SortType>,
|
||||
Some(String::from("(zeta|alpha)")),
|
||||
None::<bool>,
|
||||
None::<bool>,
|
||||
@ -281,6 +287,7 @@ mod tests {
|
||||
None::<String>,
|
||||
None::<String>,
|
||||
Some(ListingType::Subscribed),
|
||||
None::<SortType>,
|
||||
None::<String>,
|
||||
None::<bool>,
|
||||
None::<bool>,
|
||||
@ -304,6 +311,7 @@ mod tests {
|
||||
None::<String>,
|
||||
None::<String>,
|
||||
None::<ListingType>,
|
||||
None::<SortType>,
|
||||
None::<String>,
|
||||
Some(true),
|
||||
Some(true),
|
||||
@ -327,6 +335,7 @@ mod tests {
|
||||
None::<String>,
|
||||
None::<String>,
|
||||
None::<ListingType>,
|
||||
None::<SortType>,
|
||||
None::<String>,
|
||||
None::<bool>,
|
||||
Some(true),
|
||||
@ -350,6 +359,7 @@ mod tests {
|
||||
None::<String>,
|
||||
None::<String>,
|
||||
None::<ListingType>,
|
||||
None::<SortType>,
|
||||
None::<String>,
|
||||
None::<bool>,
|
||||
None::<bool>,
|
||||
@ -407,6 +417,7 @@ mod tests {
|
||||
None::<String>,
|
||||
None::<String>,
|
||||
None::<ListingType>,
|
||||
None::<SortType>,
|
||||
None::<String>,
|
||||
None::<bool>,
|
||||
None::<bool>,
|
||||
@ -429,6 +440,7 @@ mod tests {
|
||||
Some(String::new()),
|
||||
Some(String::new()),
|
||||
Some(ListingType::All),
|
||||
Some(SortType::Active),
|
||||
Some(String::new()),
|
||||
Some(false),
|
||||
Some(true),
|
||||
@ -451,6 +463,7 @@ mod tests {
|
||||
None::<String>,
|
||||
None::<String>,
|
||||
None::<ListingType>,
|
||||
None::<SortType>,
|
||||
Some(String::new()),
|
||||
None::<bool>,
|
||||
None::<bool>,
|
||||
@ -473,6 +486,7 @@ mod tests {
|
||||
None::<String>,
|
||||
None::<String>,
|
||||
None::<ListingType>,
|
||||
None::<SortType>,
|
||||
None::<String>,
|
||||
None::<bool>,
|
||||
None::<bool>,
|
||||
@ -522,6 +536,7 @@ mod tests {
|
||||
site_description: Option<String>,
|
||||
site_sidebar: Option<String>,
|
||||
site_listing_type: Option<ListingType>,
|
||||
site_sort_type: Option<SortType>,
|
||||
site_slur_filter_regex: Option<String>,
|
||||
site_is_private: Option<bool>,
|
||||
site_is_federated: Option<bool>,
|
||||
@ -542,6 +557,7 @@ mod tests {
|
||||
private_instance: site_is_private,
|
||||
default_theme: None,
|
||||
default_post_listing_type: site_listing_type,
|
||||
default_sort_type: site_sort_type,
|
||||
legal_information: None,
|
||||
application_email_admins: None,
|
||||
hide_modlog_mod_names: None,
|
||||
@ -568,6 +584,8 @@ mod tests {
|
||||
blocked_instances: None,
|
||||
taglines: None,
|
||||
registration_mode: site_registration_mode,
|
||||
content_warning: None,
|
||||
default_post_listing_mode: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -71,6 +71,7 @@ pub async fn update_site(
|
||||
description: diesel_option_overwrite(data.description.clone()),
|
||||
icon,
|
||||
banner,
|
||||
content_warning: diesel_option_overwrite(data.content_warning.clone()),
|
||||
updated: Some(Some(naive_now())),
|
||||
..Default::default()
|
||||
};
|
||||
@ -91,6 +92,7 @@ pub async fn update_site(
|
||||
private_instance: data.private_instance,
|
||||
default_theme: data.default_theme.clone(),
|
||||
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()),
|
||||
application_email_admins: data.application_email_admins,
|
||||
hide_modlog_mod_names: data.hide_modlog_mod_names,
|
||||
@ -101,6 +103,7 @@ pub async fn update_site(
|
||||
captcha_enabled: data.captcha_enabled,
|
||||
captcha_difficulty: data.captcha_difficulty.clone(),
|
||||
reports_email_admins: data.reports_email_admins,
|
||||
default_post_listing_mode: data.default_post_listing_mode,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@ -225,7 +228,7 @@ mod tests {
|
||||
|
||||
use crate::site::update::validate_update_payload;
|
||||
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;
|
||||
|
||||
#[test]
|
||||
@ -246,6 +249,7 @@ mod tests {
|
||||
None::<String>,
|
||||
None::<String>,
|
||||
None::<ListingType>,
|
||||
None::<SortType>,
|
||||
None::<String>,
|
||||
None::<bool>,
|
||||
None::<bool>,
|
||||
@ -268,6 +272,7 @@ mod tests {
|
||||
None::<String>,
|
||||
None::<String>,
|
||||
None::<ListingType>,
|
||||
None::<SortType>,
|
||||
Some(String::from("(zeta|alpha)")),
|
||||
None::<bool>,
|
||||
None::<bool>,
|
||||
@ -290,6 +295,7 @@ mod tests {
|
||||
None::<String>,
|
||||
None::<String>,
|
||||
Some(ListingType::Subscribed),
|
||||
None::<SortType>,
|
||||
None::<String>,
|
||||
None::<bool>,
|
||||
None::<bool>,
|
||||
@ -312,6 +318,7 @@ mod tests {
|
||||
None::<String>,
|
||||
None::<String>,
|
||||
None::<ListingType>,
|
||||
None::<SortType>,
|
||||
None::<String>,
|
||||
Some(true),
|
||||
Some(true),
|
||||
@ -334,6 +341,7 @@ mod tests {
|
||||
None::<String>,
|
||||
None::<String>,
|
||||
None::<ListingType>,
|
||||
None::<SortType>,
|
||||
None::<String>,
|
||||
None::<bool>,
|
||||
Some(true),
|
||||
@ -356,6 +364,7 @@ mod tests {
|
||||
None::<String>,
|
||||
None::<String>,
|
||||
None::<ListingType>,
|
||||
None::<SortType>,
|
||||
None::<String>,
|
||||
None::<bool>,
|
||||
None::<bool>,
|
||||
@ -409,6 +418,7 @@ mod tests {
|
||||
None::<String>,
|
||||
None::<String>,
|
||||
None::<ListingType>,
|
||||
None::<SortType>,
|
||||
None::<String>,
|
||||
None::<bool>,
|
||||
None::<bool>,
|
||||
@ -430,6 +440,7 @@ mod tests {
|
||||
Some(String::new()),
|
||||
Some(String::new()),
|
||||
Some(ListingType::All),
|
||||
Some(SortType::Active),
|
||||
Some(String::new()),
|
||||
Some(false),
|
||||
Some(true),
|
||||
@ -451,6 +462,7 @@ mod tests {
|
||||
None::<String>,
|
||||
None::<String>,
|
||||
None::<ListingType>,
|
||||
None::<SortType>,
|
||||
Some(String::new()),
|
||||
None::<bool>,
|
||||
None::<bool>,
|
||||
@ -472,6 +484,7 @@ mod tests {
|
||||
None::<String>,
|
||||
None::<String>,
|
||||
None::<ListingType>,
|
||||
None::<SortType>,
|
||||
None::<String>,
|
||||
None::<bool>,
|
||||
None::<bool>,
|
||||
@ -519,6 +532,7 @@ mod tests {
|
||||
site_description: Option<String>,
|
||||
site_sidebar: Option<String>,
|
||||
site_listing_type: Option<ListingType>,
|
||||
site_sort_type: Option<SortType>,
|
||||
site_slur_filter_regex: Option<String>,
|
||||
site_is_private: Option<bool>,
|
||||
site_is_federated: Option<bool>,
|
||||
@ -539,6 +553,7 @@ mod tests {
|
||||
private_instance: site_is_private,
|
||||
default_theme: None,
|
||||
default_post_listing_type: site_listing_type,
|
||||
default_sort_type: site_sort_type,
|
||||
legal_information: None,
|
||||
application_email_admins: None,
|
||||
hide_modlog_mod_names: None,
|
||||
@ -566,6 +581,8 @@ mod tests {
|
||||
taglines: None,
|
||||
registration_mode: site_registration_mode,
|
||||
reports_email_admins: None,
|
||||
content_warning: None,
|
||||
default_post_listing_mode: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -142,6 +142,7 @@ pub async fn register(
|
||||
.show_nsfw(Some(data.show_nsfw))
|
||||
.accepted_application(accepted_application)
|
||||
.default_listing_type(Some(local_site.default_post_listing_type))
|
||||
.post_listing_mode(Some(local_site.default_post_listing_mode))
|
||||
.interface_language(language_tag)
|
||||
// If its the initial site setup, they are an admin
|
||||
.admin(Some(!local_site.site_setup))
|
||||
|
@ -14,7 +14,10 @@ use activitypub_federation::{
|
||||
kinds::activity::FlagType,
|
||||
traits::{ActivityHandler, Actor},
|
||||
};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
utils::{check_comment_deleted_or_removed, check_post_deleted_or_removed},
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
activity::ActivitySendTargets,
|
||||
@ -104,6 +107,8 @@ impl ActivityHandler for Report {
|
||||
let reason = self.reason()?;
|
||||
match self.object.dereference(context).await? {
|
||||
PostOrComment::Post(post) => {
|
||||
check_post_deleted_or_removed(&post)?;
|
||||
|
||||
let report_form = PostReportForm {
|
||||
creator_id: actor.id,
|
||||
post_id: post.id,
|
||||
@ -115,6 +120,8 @@ impl ActivityHandler for Report {
|
||||
PostReport::report(&mut context.pool(), &report_form).await?;
|
||||
}
|
||||
PostOrComment::Comment(comment) => {
|
||||
check_comment_deleted_or_removed(&comment)?;
|
||||
|
||||
let report_form = CommentReportForm {
|
||||
creator_id: actor.id,
|
||||
comment_id: comment.id,
|
||||
|
@ -12,6 +12,7 @@ use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
comment::{Comment, CommentUpdateForm},
|
||||
comment_report::CommentReport,
|
||||
community::{Community, CommunityUpdateForm},
|
||||
moderator::{
|
||||
ModRemoveComment,
|
||||
@ -22,8 +23,9 @@ use lemmy_db_schema::{
|
||||
ModRemovePostForm,
|
||||
},
|
||||
post::{Post, PostUpdateForm},
|
||||
post_report::PostReport,
|
||||
},
|
||||
traits::Crud,
|
||||
traits::{Crud, Reportable},
|
||||
};
|
||||
use lemmy_utils::error::{LemmyError, LemmyErrorType};
|
||||
use url::Url;
|
||||
@ -136,6 +138,7 @@ pub(in crate::activities) async fn receive_remove_action(
|
||||
.await?;
|
||||
}
|
||||
DeletableObjects::Post(post) => {
|
||||
PostReport::resolve_all_for_object(&mut context.pool(), post.id, actor.id).await?;
|
||||
let form = ModRemovePostForm {
|
||||
mod_person_id: actor.id,
|
||||
post_id: post.id,
|
||||
@ -154,6 +157,7 @@ pub(in crate::activities) async fn receive_remove_action(
|
||||
.await?;
|
||||
}
|
||||
DeletableObjects::Comment(comment) => {
|
||||
CommentReport::resolve_all_for_object(&mut context.pool(), comment.id, actor.id).await?;
|
||||
let form = ModRemoveCommentForm {
|
||||
mod_person_id: actor.id,
|
||||
comment_id: comment.id,
|
||||
|
@ -48,9 +48,10 @@ pub async fn list_comments(
|
||||
|
||||
let listing_type = Some(listing_type_with_default(
|
||||
data.type_,
|
||||
local_user_view.as_ref().map(|u| &u.local_user),
|
||||
&local_site,
|
||||
community_id,
|
||||
)?);
|
||||
));
|
||||
|
||||
// If a parent_id is given, fetch the comment to get the path
|
||||
let parent_path = if let Some(parent_id) = parent_id {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
api::listing_type_with_default,
|
||||
api::{listing_type_with_default, sort_type_with_default},
|
||||
fetcher::resolve_actor_identifier,
|
||||
objects::community::ApubCommunity,
|
||||
};
|
||||
@ -10,10 +10,10 @@ use lemmy_api_common::{
|
||||
post::{GetPosts, GetPostsResponse},
|
||||
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::{
|
||||
post_view::PostQuery,
|
||||
structs::{LocalUserView, PaginationCursor},
|
||||
structs::{LocalUserView, PaginationCursor, SiteView},
|
||||
};
|
||||
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
||||
|
||||
@ -23,11 +23,9 @@ pub async fn list_posts(
|
||||
context: Data<LemmyContext>,
|
||||
local_user_view: Option<LocalUserView>,
|
||||
) -> 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)?;
|
||||
|
||||
let sort = data.sort;
|
||||
check_private_instance(&local_user_view, &local_site.local_site)?;
|
||||
|
||||
let page = data.page;
|
||||
let limit = data.limit;
|
||||
@ -45,11 +43,20 @@ pub async fn list_posts(
|
||||
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(
|
||||
data.type_,
|
||||
&local_site,
|
||||
local_user_ref,
|
||||
&local_site.local_site,
|
||||
community_id,
|
||||
)?);
|
||||
));
|
||||
|
||||
let sort = Some(sort_type_with_default(
|
||||
data.sort,
|
||||
local_user_ref,
|
||||
&local_site.local_site,
|
||||
));
|
||||
|
||||
// parse pagination token
|
||||
let page_after = if let Some(pa) = &data.page_cursor {
|
||||
Some(pa.read(&mut context.pool()).await?)
|
||||
@ -70,7 +77,7 @@ pub async fn list_posts(
|
||||
limit,
|
||||
..Default::default()
|
||||
}
|
||||
.list(&mut context.pool())
|
||||
.list(&local_site.site, &mut context.pool())
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::CouldntGetPosts)?;
|
||||
|
||||
|
@ -1,5 +1,9 @@
|
||||
use lemmy_db_schema::{newtypes::CommunityId, source::local_site::LocalSite, ListingType};
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use lemmy_db_schema::{
|
||||
newtypes::CommunityId,
|
||||
source::{local_site::LocalSite, local_user::LocalUser},
|
||||
ListingType,
|
||||
SortType,
|
||||
};
|
||||
|
||||
pub mod list_comments;
|
||||
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.
|
||||
fn listing_type_with_default(
|
||||
type_: Option<ListingType>,
|
||||
local_user: Option<&LocalUser>,
|
||||
local_site: &LocalSite,
|
||||
community_id: Option<CommunityId>,
|
||||
) -> Result<ListingType, LemmyError> {
|
||||
) -> ListingType {
|
||||
// On frontpage use listing type from param or admin configured default
|
||||
let listing_type = if community_id.is_none() {
|
||||
type_.unwrap_or(local_site.default_post_listing_type)
|
||||
if community_id.is_none() {
|
||||
type_.unwrap_or(
|
||||
local_user
|
||||
.map(|u| u.default_listing_type)
|
||||
.unwrap_or(local_site.default_post_listing_type),
|
||||
)
|
||||
} else {
|
||||
// inside of community show everything
|
||||
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),
|
||||
)
|
||||
}
|
||||
|
@ -6,11 +6,12 @@ use lemmy_api_common::{
|
||||
person::{GetPersonDetails, GetPersonDetailsResponse},
|
||||
utils::{check_private_instance, read_site_for_actor},
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
source::{local_site::LocalSite, person::Person},
|
||||
utils::post_to_comment_sort_type,
|
||||
use lemmy_db_schema::{source::person::Person, utils::post_to_comment_sort_type};
|
||||
use lemmy_db_views::{
|
||||
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_utils::error::{LemmyError, LemmyErrorExt2, LemmyErrorType};
|
||||
|
||||
@ -25,9 +26,9 @@ pub async fn read_person(
|
||||
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 {
|
||||
Some(id) => id,
|
||||
@ -70,7 +71,7 @@ pub async fn read_person(
|
||||
creator_id,
|
||||
..Default::default()
|
||||
}
|
||||
.list(&mut context.pool())
|
||||
.list(&local_site.site, &mut context.pool())
|
||||
.await?;
|
||||
|
||||
let comments = CommentQuery {
|
||||
|
@ -6,12 +6,12 @@ use lemmy_api_common::{
|
||||
site::{Search, SearchResponse},
|
||||
utils::{check_private_instance, is_admin},
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
source::{community::Community, local_site::LocalSite},
|
||||
utils::post_to_comment_sort_type,
|
||||
SearchType,
|
||||
use lemmy_db_schema::{source::community::Community, utils::post_to_comment_sort_type, SearchType};
|
||||
use lemmy_db_views::{
|
||||
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::{community_view::CommunityQuery, person_view::PersonQuery};
|
||||
use lemmy_utils::error::LemmyError;
|
||||
|
||||
@ -21,9 +21,9 @@ pub async fn search(
|
||||
context: Data<LemmyContext>,
|
||||
local_user_view: Option<LocalUserView>,
|
||||
) -> 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
|
||||
.as_ref()
|
||||
@ -68,7 +68,7 @@ pub async fn search(
|
||||
limit: (limit),
|
||||
..Default::default()
|
||||
}
|
||||
.list(&mut context.pool())
|
||||
.list(&local_site.site, &mut context.pool())
|
||||
.await?;
|
||||
}
|
||||
SearchType::Comments => {
|
||||
@ -97,7 +97,7 @@ pub async fn search(
|
||||
limit: (limit),
|
||||
..Default::default()
|
||||
}
|
||||
.list(&mut context.pool())
|
||||
.list(&local_site.site, &mut context.pool())
|
||||
.await?;
|
||||
}
|
||||
SearchType::Users => {
|
||||
@ -128,7 +128,7 @@ pub async fn search(
|
||||
limit: (limit),
|
||||
..Default::default()
|
||||
}
|
||||
.list(&mut context.pool())
|
||||
.list(&local_site.site, &mut context.pool())
|
||||
.await?;
|
||||
|
||||
let q = data.q.clone();
|
||||
@ -162,7 +162,7 @@ pub async fn search(
|
||||
limit: (limit),
|
||||
..Default::default()
|
||||
}
|
||||
.list(&mut context.pool())
|
||||
.list(&local_site.site, &mut context.pool())
|
||||
.await?
|
||||
};
|
||||
|
||||
@ -192,7 +192,7 @@ pub async fn search(
|
||||
limit: (limit),
|
||||
..Default::default()
|
||||
}
|
||||
.list(&mut context.pool())
|
||||
.list(&local_site.site, &mut context.pool())
|
||||
.await?;
|
||||
}
|
||||
};
|
||||
|
@ -106,6 +106,7 @@ impl Object for ApubSite {
|
||||
outbox: Url::parse(&format!("{}/site_outbox", self.actor_id))?,
|
||||
public_key: self.public_key(),
|
||||
language,
|
||||
content_warning: self.content_warning.clone(),
|
||||
published: self.published,
|
||||
updated: self.updated,
|
||||
};
|
||||
@ -154,6 +155,7 @@ impl Object for ApubSite {
|
||||
public_key: Some(apub.public_key.public_key_pem.clone()),
|
||||
private_key: None,
|
||||
instance_id: instance.id,
|
||||
content_warning: apub.content_warning,
|
||||
};
|
||||
let languages =
|
||||
LanguageTag::to_language_id_multiple(apub.language, &mut context.pool()).await?;
|
||||
|
@ -39,6 +39,8 @@ pub struct Instance {
|
||||
pub(crate) image: Option<ImageObject>,
|
||||
#[serde(default)]
|
||||
pub(crate) language: Vec<LanguageTag>,
|
||||
/// nonstandard field
|
||||
pub(crate) content_warning: Option<String>,
|
||||
pub(crate) published: DateTime<Utc>,
|
||||
pub(crate) updated: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
@ -21,3 +21,4 @@ lemmy_db_schema = { workspace = true }
|
||||
lemmy_db_views = { workspace = true, features = ["full"] }
|
||||
lemmy_utils = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
url = { workspace = true }
|
||||
|
@ -16,6 +16,7 @@ use lemmy_db_schema::{
|
||||
community::{Community, CommunityInsertForm},
|
||||
instance::Instance,
|
||||
person::{Person, PersonInsertForm},
|
||||
site::Site,
|
||||
},
|
||||
traits::Crud,
|
||||
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_utils::error::{LemmyErrorExt2, LemmyResult};
|
||||
use std::num::NonZeroU32;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
struct CmdArgs {
|
||||
@ -157,7 +159,7 @@ async fn try_main() -> LemmyResult<()> {
|
||||
page_after,
|
||||
..Default::default()
|
||||
}
|
||||
.list(&mut conn.into())
|
||||
.list(&site()?, &mut conn.into())
|
||||
.await?;
|
||||
|
||||
if let Some(post_view) = post_views.into_iter().last() {
|
||||
@ -181,3 +183,23 @@ async fn try_main() -> LemmyResult<()> {
|
||||
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
use crate::{
|
||||
newtypes::{CommentReportId, PersonId},
|
||||
schema::comment_report::dsl::{comment_report, resolved, resolver_id, updated},
|
||||
newtypes::{CommentId, CommentReportId, PersonId},
|
||||
schema::comment_report::{
|
||||
comment_id,
|
||||
dsl::{comment_report, resolved, resolver_id, updated},
|
||||
},
|
||||
source::comment_report::{CommentReport, CommentReportForm},
|
||||
traits::Reportable,
|
||||
utils::{get_conn, naive_now, DbPool},
|
||||
@ -17,6 +20,7 @@ use diesel_async::RunQueryDsl;
|
||||
impl Reportable for CommentReport {
|
||||
type Form = CommentReportForm;
|
||||
type IdType = CommentReportId;
|
||||
type ObjectIdType = CommentId;
|
||||
/// creates a comment report and returns it
|
||||
///
|
||||
/// * `conn` - the postgres connection
|
||||
@ -53,6 +57,22 @@ impl Reportable for CommentReport {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn resolve_all_for_object(
|
||||
pool: &mut DbPool<'_>,
|
||||
comment_id_: CommentId,
|
||||
by_resolver_id: PersonId,
|
||||
) -> Result<usize, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
update(comment_report.filter(comment_id.eq(comment_id_)))
|
||||
.set((
|
||||
resolved.eq(true),
|
||||
resolver_id.eq(by_resolver_id),
|
||||
updated.eq(naive_now()),
|
||||
))
|
||||
.execute(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
/// unresolve a comment report
|
||||
///
|
||||
/// * `conn` - the postgres connection
|
||||
|
@ -22,9 +22,10 @@ use crate::{
|
||||
use diesel::{
|
||||
deserialize,
|
||||
dsl,
|
||||
dsl::insert_into,
|
||||
dsl::{exists, insert_into},
|
||||
pg::Pg,
|
||||
result::Error,
|
||||
select,
|
||||
sql_types,
|
||||
ExpressionMethods,
|
||||
NullableExpressionMethods,
|
||||
@ -235,7 +236,6 @@ impl CommunityFollower {
|
||||
remote_community_id: CommunityId,
|
||||
) -> Result<bool, Error> {
|
||||
use crate::schema::community_follower::dsl::{community_follower, community_id};
|
||||
use diesel::dsl::{exists, select};
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
select(exists(
|
||||
community_follower.filter(community_id.eq(remote_community_id)),
|
||||
|
@ -1,12 +1,6 @@
|
||||
use crate::{
|
||||
newtypes::{DbUrl, LocalUserId, PersonId},
|
||||
schema::local_user::dsl::{
|
||||
accepted_application,
|
||||
email,
|
||||
email_verified,
|
||||
local_user,
|
||||
password_encrypted,
|
||||
},
|
||||
schema::{local_user, person, registration_application},
|
||||
source::{
|
||||
actor_language::{LocalUserLanguage, SiteLanguage},
|
||||
local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm},
|
||||
@ -15,11 +9,18 @@ use crate::{
|
||||
utils::{
|
||||
functions::{coalesce, lower},
|
||||
get_conn,
|
||||
now,
|
||||
DbPool,
|
||||
},
|
||||
};
|
||||
use bcrypt::{hash, DEFAULT_COST};
|
||||
use diesel::{dsl::insert_into, result::Error, ExpressionMethods, JoinOnDsl, QueryDsl};
|
||||
use diesel::{
|
||||
dsl::{insert_into, not, IntervalDsl},
|
||||
result::Error,
|
||||
ExpressionMethods,
|
||||
JoinOnDsl,
|
||||
QueryDsl,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
|
||||
impl LocalUser {
|
||||
@ -31,16 +32,16 @@ impl LocalUser {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let password_hash = hash(new_password, DEFAULT_COST).expect("Couldn't hash password");
|
||||
|
||||
diesel::update(local_user.find(local_user_id))
|
||||
.set((password_encrypted.eq(password_hash),))
|
||||
diesel::update(local_user::table.find(local_user_id))
|
||||
.set((local_user::password_encrypted.eq(password_hash),))
|
||||
.get_result::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn set_all_users_email_verified(pool: &mut DbPool<'_>) -> Result<Vec<Self>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
diesel::update(local_user)
|
||||
.set(email_verified.eq(true))
|
||||
diesel::update(local_user::table)
|
||||
.set(local_user::email_verified.eq(true))
|
||||
.get_results::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
@ -49,18 +50,43 @@ impl LocalUser {
|
||||
pool: &mut DbPool<'_>,
|
||||
) -> Result<Vec<Self>, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
diesel::update(local_user)
|
||||
.set(accepted_application.eq(true))
|
||||
diesel::update(local_user::table)
|
||||
.set(local_user::accepted_application.eq(true))
|
||||
.get_results::<Self>(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn is_email_taken(pool: &mut DbPool<'_>, email_: &str) -> Result<bool, Error> {
|
||||
pub async fn delete_old_denied_local_users(pool: &mut DbPool<'_>) -> Result<usize, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
|
||||
// Make sure:
|
||||
// - The deny reason exists
|
||||
// - The app is older than a week
|
||||
// - The accepted_application is false
|
||||
let old_denied_registrations = registration_application::table
|
||||
.filter(registration_application::deny_reason.is_not_null())
|
||||
.filter(registration_application::published.lt(now() - 1.week()))
|
||||
.select(registration_application::local_user_id);
|
||||
|
||||
// Delete based on join logic is here:
|
||||
// https://stackoverflow.com/questions/60836040/how-do-i-perform-a-delete-with-sub-query-in-diesel-against-a-postgres-database
|
||||
let local_users = local_user::table
|
||||
.filter(local_user::id.eq_any(old_denied_registrations))
|
||||
.filter(not(local_user::accepted_application))
|
||||
.select(local_user::person_id);
|
||||
|
||||
// Delete the person rows, which should automatically clear the local_user ones
|
||||
let persons = person::table.filter(person::id.eq_any(local_users));
|
||||
|
||||
diesel::delete(persons).execute(conn).await
|
||||
}
|
||||
|
||||
pub async fn is_email_taken(pool: &mut DbPool<'_>, email: &str) -> Result<bool, Error> {
|
||||
use diesel::dsl::{exists, select};
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
select(exists(
|
||||
local_user.filter(lower(coalesce(email, "")).eq(email_.to_lowercase())),
|
||||
))
|
||||
select(exists(local_user::table.filter(
|
||||
lower(coalesce(local_user::email, "")).eq(email.to_lowercase()),
|
||||
)))
|
||||
.get_result(conn)
|
||||
.await
|
||||
}
|
||||
@ -78,7 +104,6 @@ impl LocalUser {
|
||||
community_follower,
|
||||
instance,
|
||||
instance_block,
|
||||
person,
|
||||
person_block,
|
||||
post,
|
||||
post_saved,
|
||||
@ -140,6 +165,15 @@ impl LocalUser {
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalUserInsertForm {
|
||||
pub fn test_form(person_id: PersonId) -> Self {
|
||||
Self::builder()
|
||||
.person_id(person_id)
|
||||
.password_encrypted(String::new())
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UserBackupLists {
|
||||
pub followed_communities: Vec<DbUrl>,
|
||||
pub saved_posts: Vec<DbUrl>,
|
||||
@ -162,7 +196,7 @@ impl Crud for LocalUser {
|
||||
hash(&form.password_encrypted, DEFAULT_COST).expect("Couldn't hash password");
|
||||
form_with_encrypted_password.password_encrypted = password_hash;
|
||||
|
||||
let local_user_ = insert_into(local_user)
|
||||
let local_user_ = insert_into(local_user::table)
|
||||
.values(form_with_encrypted_password)
|
||||
.get_result::<Self>(conn)
|
||||
.await?;
|
||||
@ -185,7 +219,7 @@ impl Crud for LocalUser {
|
||||
form: &Self::UpdateForm,
|
||||
) -> Result<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
diesel::update(local_user.find(local_user_id))
|
||||
diesel::update(local_user::table.find(local_user_id))
|
||||
.set(form)
|
||||
.get_result::<Self>(conn)
|
||||
.await
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
newtypes::{CommunityId, DbUrl, PersonId},
|
||||
newtypes::{CommunityId, DbUrl, InstanceId, PersonId},
|
||||
schema::{instance, local_user, person, person_follower},
|
||||
source::person::{
|
||||
Person,
|
||||
@ -86,6 +86,16 @@ impl Person {
|
||||
}
|
||||
}
|
||||
|
||||
impl PersonInsertForm {
|
||||
pub fn test_form(instance_id: InstanceId, name: &str) -> Self {
|
||||
Self::builder()
|
||||
.name(name.to_owned())
|
||||
.public_key("pubkey".to_string())
|
||||
.instance_id(instance_id)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ApubActor for Person {
|
||||
async fn read_from_apub_id(
|
||||
|
@ -1,6 +1,9 @@
|
||||
use crate::{
|
||||
newtypes::{PersonId, PostReportId},
|
||||
schema::post_report::dsl::{post_report, resolved, resolver_id, updated},
|
||||
newtypes::{PersonId, PostId, PostReportId},
|
||||
schema::post_report::{
|
||||
dsl::{post_report, resolved, resolver_id, updated},
|
||||
post_id,
|
||||
},
|
||||
source::post_report::{PostReport, PostReportForm},
|
||||
traits::Reportable,
|
||||
utils::{get_conn, naive_now, DbPool},
|
||||
@ -17,6 +20,7 @@ use diesel_async::RunQueryDsl;
|
||||
impl Reportable for PostReport {
|
||||
type Form = PostReportForm;
|
||||
type IdType = PostReportId;
|
||||
type ObjectIdType = PostId;
|
||||
|
||||
async fn report(pool: &mut DbPool<'_>, post_report_form: &PostReportForm) -> Result<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
@ -42,6 +46,22 @@ impl Reportable for PostReport {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn resolve_all_for_object(
|
||||
pool: &mut DbPool<'_>,
|
||||
post_id_: PostId,
|
||||
by_resolver_id: PersonId,
|
||||
) -> Result<usize, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
update(post_report.filter(post_id.eq(post_id_)))
|
||||
.set((
|
||||
resolved.eq(true),
|
||||
resolver_id.eq(by_resolver_id),
|
||||
updated.eq(naive_now()),
|
||||
))
|
||||
.execute(conn)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn unresolve(
|
||||
pool: &mut DbPool<'_>,
|
||||
report_id: Self::IdType,
|
||||
@ -75,7 +95,6 @@ mod tests {
|
||||
traits::Crud,
|
||||
utils::build_db_pool_for_tests,
|
||||
};
|
||||
use pretty_assertions::assert_eq;
|
||||
use serial_test::serial;
|
||||
|
||||
async fn init(pool: &mut DbPool<'_>) -> (Person, PostReport) {
|
||||
@ -135,4 +154,21 @@ mod tests {
|
||||
Person::delete(pool, person.id).await.unwrap();
|
||||
Post::delete(pool, report.post_id).await.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_resolve_all_post_reports() {
|
||||
let pool = &build_db_pool_for_tests().await;
|
||||
let pool = &mut pool.into();
|
||||
|
||||
let (person, report) = init(pool).await;
|
||||
|
||||
let resolved_count = PostReport::resolve_all_for_object(pool, report.post_id, person.id)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(resolved_count, 1);
|
||||
|
||||
Person::delete(pool, person.id).await.unwrap();
|
||||
Post::delete(pool, report.post_id).await.unwrap();
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
newtypes::{PersonId, PrivateMessageReportId},
|
||||
newtypes::{PersonId, PrivateMessageId, PrivateMessageReportId},
|
||||
schema::private_message_report::dsl::{private_message_report, resolved, resolver_id, updated},
|
||||
source::private_message_report::{PrivateMessageReport, PrivateMessageReportForm},
|
||||
traits::Reportable,
|
||||
@ -17,6 +17,7 @@ use diesel_async::RunQueryDsl;
|
||||
impl Reportable for PrivateMessageReport {
|
||||
type Form = PrivateMessageReportForm;
|
||||
type IdType = PrivateMessageReportId;
|
||||
type ObjectIdType = PrivateMessageId;
|
||||
|
||||
async fn report(
|
||||
pool: &mut DbPool<'_>,
|
||||
@ -45,6 +46,15 @@ impl Reportable for PrivateMessageReport {
|
||||
.await
|
||||
}
|
||||
|
||||
// TODO: this is unused because private message doesnt have remove handler
|
||||
async fn resolve_all_for_object(
|
||||
_pool: &mut DbPool<'_>,
|
||||
_pm_id_: PrivateMessageId,
|
||||
_by_resolver_id: PersonId,
|
||||
) -> Result<usize, Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
async fn unresolve(
|
||||
pool: &mut DbPool<'_>,
|
||||
report_id: Self::IdType,
|
||||
|
@ -139,7 +139,9 @@ pub enum RegistrationMode {
|
||||
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",
|
||||
@ -150,6 +152,7 @@ pub enum RegistrationMode {
|
||||
/// A post-view mode that changes how multiple post listings look.
|
||||
pub enum PostListingMode {
|
||||
/// A compact, list-type view.
|
||||
#[default]
|
||||
List,
|
||||
/// A larger card-type view.
|
||||
Card,
|
||||
|
@ -6,6 +6,14 @@ use activitypub_federation::{
|
||||
traits::Object,
|
||||
};
|
||||
#[cfg(feature = "full")]
|
||||
use diesel::{
|
||||
backend::Backend,
|
||||
deserialize::FromSql,
|
||||
pg::Pg,
|
||||
serialize::{Output, ToSql},
|
||||
sql_types::Text,
|
||||
};
|
||||
#[cfg(feature = "full")]
|
||||
use diesel_ltree::Ltree;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
@ -239,6 +247,35 @@ impl TS for DbUrl {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "full")]
|
||||
impl ToSql<Text, Pg> for DbUrl {
|
||||
fn to_sql(&self, out: &mut Output<Pg>) -> diesel::serialize::Result {
|
||||
<std::string::String as ToSql<Text, Pg>>::to_sql(&self.0.to_string(), &mut out.reborrow())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "full")]
|
||||
impl<DB: Backend> FromSql<Text, DB> for DbUrl
|
||||
where
|
||||
String: FromSql<Text, DB>,
|
||||
{
|
||||
fn from_sql(value: DB::RawValue<'_>) -> diesel::deserialize::Result<Self> {
|
||||
let str = String::from_sql(value)?;
|
||||
Ok(DbUrl(Box::new(Url::parse(&str)?)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "full")]
|
||||
impl<Kind> From<ObjectId<Kind>> for DbUrl
|
||||
where
|
||||
Kind: Object + Send + 'static,
|
||||
for<'de2> <Kind as Object>::Kind: serde::Deserialize<'de2>,
|
||||
{
|
||||
fn from(id: ObjectId<Kind>) -> Self {
|
||||
DbUrl(Box::new(id.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl InstanceId {
|
||||
pub fn inner(self) -> i32 {
|
||||
self.0
|
||||
|
@ -357,6 +357,8 @@ diesel::table! {
|
||||
use diesel::sql_types::*;
|
||||
use super::sql_types::ListingTypeEnum;
|
||||
use super::sql_types::RegistrationModeEnum;
|
||||
use super::sql_types::PostListingModeEnum;
|
||||
use super::sql_types::SortTypeEnum;
|
||||
|
||||
local_site (id) {
|
||||
id -> Int4,
|
||||
@ -384,6 +386,8 @@ diesel::table! {
|
||||
registration_mode -> RegistrationModeEnum,
|
||||
reports_email_admins -> Bool,
|
||||
federation_signed_fetch -> Bool,
|
||||
default_post_listing_mode -> PostListingModeEnum,
|
||||
default_sort_type -> SortTypeEnum,
|
||||
}
|
||||
}
|
||||
|
||||
@ -875,6 +879,7 @@ diesel::table! {
|
||||
private_key -> Nullable<Text>,
|
||||
public_key -> Text,
|
||||
instance_id -> Int4,
|
||||
content_warning -> Nullable<Text>,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,9 @@ use crate::schema::local_site;
|
||||
use crate::{
|
||||
newtypes::{LocalSiteId, SiteId},
|
||||
ListingType,
|
||||
PostListingMode,
|
||||
RegistrationMode,
|
||||
SortType,
|
||||
};
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -64,6 +66,10 @@ pub struct LocalSite {
|
||||
/// Whether to sign outgoing Activitypub fetches with private key of local instance. Some
|
||||
/// Fediverse instances and platforms require this.
|
||||
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)]
|
||||
@ -93,6 +99,8 @@ pub struct LocalSiteInsertForm {
|
||||
pub registration_mode: Option<RegistrationMode>,
|
||||
pub reports_email_admins: Option<bool>,
|
||||
pub federation_signed_fetch: Option<bool>,
|
||||
pub default_post_listing_mode: Option<PostListingMode>,
|
||||
pub default_sort_type: Option<SortType>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
@ -120,4 +128,6 @@ pub struct LocalSiteUpdateForm {
|
||||
pub reports_email_admins: Option<bool>,
|
||||
pub updated: Option<Option<DateTime<Utc>>>,
|
||||
pub federation_signed_fetch: Option<bool>,
|
||||
pub default_post_listing_mode: Option<PostListingMode>,
|
||||
pub default_sort_type: Option<SortType>,
|
||||
}
|
||||
|
@ -37,6 +37,9 @@ pub struct Site {
|
||||
pub private_key: Option<String>,
|
||||
pub public_key: String,
|
||||
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)]
|
||||
@ -58,6 +61,7 @@ pub struct SiteInsertForm {
|
||||
pub public_key: Option<String>,
|
||||
#[builder(!default)]
|
||||
pub instance_id: InstanceId,
|
||||
pub content_warning: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
@ -76,4 +80,5 @@ pub struct SiteUpdateForm {
|
||||
pub inbox_url: Option<DbUrl>,
|
||||
pub private_key: Option<Option<String>>,
|
||||
pub public_key: Option<String>,
|
||||
pub content_warning: Option<Option<String>>,
|
||||
}
|
||||
|
@ -144,6 +144,7 @@ pub trait Blockable {
|
||||
pub trait Reportable {
|
||||
type Form;
|
||||
type IdType;
|
||||
type ObjectIdType;
|
||||
async fn report(pool: &mut DbPool<'_>, form: &Self::Form) -> Result<Self, Error>
|
||||
where
|
||||
Self: Sized;
|
||||
@ -152,6 +153,13 @@ pub trait Reportable {
|
||||
report_id: Self::IdType,
|
||||
resolver_id: PersonId,
|
||||
) -> Result<usize, Error>
|
||||
where
|
||||
Self: Sized;
|
||||
async fn resolve_all_for_object(
|
||||
pool: &mut DbPool<'_>,
|
||||
comment_id_: Self::ObjectIdType,
|
||||
by_resolver_id: PersonId,
|
||||
) -> Result<usize, Error>
|
||||
where
|
||||
Self: Sized;
|
||||
async fn unresolve(
|
||||
|
@ -5,20 +5,16 @@ use crate::{
|
||||
CommentSortType,
|
||||
SortType,
|
||||
};
|
||||
use activitypub_federation::{fetch::object_id::ObjectId, traits::Object};
|
||||
use anyhow::Context;
|
||||
use chrono::{DateTime, Utc};
|
||||
use deadpool::Runtime;
|
||||
use diesel::{
|
||||
backend::Backend,
|
||||
deserialize::FromSql,
|
||||
helper_types::AsExprOf,
|
||||
pg::Pg,
|
||||
query_builder::{Query, QueryFragment},
|
||||
query_dsl::methods::LimitDsl,
|
||||
result::{ConnectionError, ConnectionResult, Error as DieselError, Error::QueryBuilderError},
|
||||
serialize::{Output, ToSql},
|
||||
sql_types::{self, Text, Timestamptz},
|
||||
sql_types::{self, Timestamptz},
|
||||
IntoSql,
|
||||
PgConnection,
|
||||
};
|
||||
@ -236,7 +232,11 @@ impl<T: LimitDsl> LimitDsl for Commented<T> {
|
||||
}
|
||||
|
||||
pub fn fuzzy_search(q: &str) -> String {
|
||||
let replaced = q.replace('%', "\\%").replace('_', "\\_").replace(' ', "%");
|
||||
let replaced = q
|
||||
.replace('\\', "\\\\")
|
||||
.replace('%', "\\%")
|
||||
.replace('_', "\\_")
|
||||
.replace(' ', "%");
|
||||
format!("%{replaced}%")
|
||||
}
|
||||
|
||||
@ -369,8 +369,7 @@ pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!();
|
||||
|
||||
fn run_migrations(db_url: &str) -> Result<(), LemmyError> {
|
||||
// Needs to be a sync connection
|
||||
let mut conn =
|
||||
PgConnection::establish(db_url).with_context(|| format!("Error connecting to {db_url}"))?;
|
||||
let mut conn = PgConnection::establish(db_url).with_context(|| "Error connecting to database")?;
|
||||
|
||||
info!("Running Database migrations (This may take a long time)...");
|
||||
conn
|
||||
@ -472,32 +471,6 @@ pub mod functions {
|
||||
|
||||
pub const DELETED_REPLACEMENT_TEXT: &str = "*Permanently Deleted*";
|
||||
|
||||
impl ToSql<Text, Pg> for DbUrl {
|
||||
fn to_sql(&self, out: &mut Output<Pg>) -> diesel::serialize::Result {
|
||||
<std::string::String as ToSql<Text, Pg>>::to_sql(&self.0.to_string(), &mut out.reborrow())
|
||||
}
|
||||
}
|
||||
|
||||
impl<DB: Backend> FromSql<Text, DB> for DbUrl
|
||||
where
|
||||
String: FromSql<Text, DB>,
|
||||
{
|
||||
fn from_sql(value: DB::RawValue<'_>) -> diesel::deserialize::Result<Self> {
|
||||
let str = String::from_sql(value)?;
|
||||
Ok(DbUrl(Box::new(Url::parse(&str)?)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Kind> From<ObjectId<Kind>> for DbUrl
|
||||
where
|
||||
Kind: Object + Send + 'static,
|
||||
for<'de2> <Kind as Object>::Kind: serde::Deserialize<'de2>,
|
||||
{
|
||||
fn from(id: ObjectId<Kind>) -> Self {
|
||||
DbUrl(Box::new(id.into()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn now() -> AsExprOf<diesel::dsl::now, diesel::sql_types::Timestamptz> {
|
||||
// https://github.com/diesel-rs/diesel/issues/1514
|
||||
diesel::dsl::now.into_sql::<Timestamptz>()
|
||||
|
@ -45,3 +45,4 @@ serial_test = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
pretty_assertions = { workspace = true }
|
||||
url = { workspace = true }
|
||||
|
@ -52,41 +52,6 @@ fn queries<'a>() -> Queries<
|
||||
aliases::person2
|
||||
.on(comment_report::resolver_id.eq(aliases::person2.field(person::id).nullable())),
|
||||
)
|
||||
};
|
||||
|
||||
let selection = (
|
||||
comment_report::all_columns,
|
||||
comment::all_columns,
|
||||
post::all_columns,
|
||||
community::all_columns,
|
||||
person::all_columns,
|
||||
aliases::person1.fields(person::all_columns),
|
||||
comment_aggregates::all_columns,
|
||||
community_person_ban::community_id.nullable().is_not_null(),
|
||||
comment_like::score.nullable(),
|
||||
aliases::person2.fields(person::all_columns).nullable(),
|
||||
);
|
||||
|
||||
let read = move |mut conn: DbConn<'a>, (report_id, my_person_id): (CommentReportId, PersonId)| async move {
|
||||
all_joins(
|
||||
comment_report::table.find(report_id).into_boxed(),
|
||||
my_person_id,
|
||||
)
|
||||
.left_join(
|
||||
community_person_ban::table.on(
|
||||
community::id
|
||||
.eq(community_person_ban::community_id)
|
||||
.and(community_person_ban::person_id.eq(comment::creator_id)),
|
||||
),
|
||||
)
|
||||
.select(selection)
|
||||
.first::<CommentReportView>(&mut conn)
|
||||
.await
|
||||
};
|
||||
|
||||
let list = move |mut conn: DbConn<'a>,
|
||||
(options, user): (CommentReportQuery, &'a LocalUserView)| async move {
|
||||
let mut query = all_joins(comment_report::table.into_boxed(), user.person.id)
|
||||
.left_join(
|
||||
community_person_ban::table.on(
|
||||
community::id
|
||||
@ -99,7 +64,32 @@ fn queries<'a>() -> Queries<
|
||||
),
|
||||
),
|
||||
)
|
||||
.select(selection);
|
||||
.select((
|
||||
comment_report::all_columns,
|
||||
comment::all_columns,
|
||||
post::all_columns,
|
||||
community::all_columns,
|
||||
person::all_columns,
|
||||
aliases::person1.fields(person::all_columns),
|
||||
comment_aggregates::all_columns,
|
||||
community_person_ban::community_id.nullable().is_not_null(),
|
||||
comment_like::score.nullable(),
|
||||
aliases::person2.fields(person::all_columns).nullable(),
|
||||
))
|
||||
};
|
||||
|
||||
let read = move |mut conn: DbConn<'a>, (report_id, my_person_id): (CommentReportId, PersonId)| async move {
|
||||
all_joins(
|
||||
comment_report::table.find(report_id).into_boxed(),
|
||||
my_person_id,
|
||||
)
|
||||
.first::<CommentReportView>(&mut conn)
|
||||
.await
|
||||
};
|
||||
|
||||
let list = move |mut conn: DbConn<'a>,
|
||||
(options, user): (CommentReportQuery, &'a LocalUserView)| async move {
|
||||
let mut query = all_joins(comment_report::table.into_boxed(), user.person.id);
|
||||
|
||||
if let Some(community_id) = options.community_id {
|
||||
query = query.filter(post::community_id.eq(community_id));
|
||||
|
@ -201,7 +201,6 @@ mod tests {
|
||||
community::{Community, CommunityInsertForm, CommunityModerator, CommunityModeratorForm},
|
||||
instance::Instance,
|
||||
local_user::{LocalUser, LocalUserInsertForm},
|
||||
moderator::{ModRemovePost, ModRemovePostForm},
|
||||
person::{Person, PersonInsertForm},
|
||||
post::{Post, PostInsertForm},
|
||||
post_report::{PostReport, PostReportForm},
|
||||
@ -350,14 +349,11 @@ mod tests {
|
||||
.unwrap();
|
||||
assert_eq!(2, report_count);
|
||||
|
||||
// Writing post removal to mod log should automatically resolve reports
|
||||
let remove_form = ModRemovePostForm {
|
||||
mod_person_id: inserted_timmy.id,
|
||||
post_id: inserted_jessica_report.post_id,
|
||||
reason: None,
|
||||
removed: Some(true),
|
||||
};
|
||||
ModRemovePost::create(pool, &remove_form).await.unwrap();
|
||||
// Pretend the post was removed, and resolve all reports for that object.
|
||||
// This is called manually in the API for post removals
|
||||
PostReport::resolve_all_for_object(pool, inserted_jessica_report.post_id, inserted_timmy.id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let read_jessica_report_view_after_resolve =
|
||||
PostReportView::read(pool, inserted_jessica_report.id, inserted_timmy.id)
|
||||
|
@ -39,6 +39,7 @@ use lemmy_db_schema::{
|
||||
post_read,
|
||||
post_saved,
|
||||
},
|
||||
source::site::Site,
|
||||
utils::{
|
||||
functions::coalesce,
|
||||
fuzzy_search,
|
||||
@ -61,7 +62,7 @@ use tracing::debug;
|
||||
|
||||
fn queries<'a>() -> Queries<
|
||||
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(
|
||||
community_person_ban::table.filter(
|
||||
@ -270,7 +271,7 @@ fn queries<'a>() -> Queries<
|
||||
.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_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
|
||||
.local_user
|
||||
.map(|l| l.local_user.show_nsfw)
|
||||
.unwrap_or(false)
|
||||
.unwrap_or(has_content_warning)
|
||||
{
|
||||
query = query
|
||||
.filter(post::nsfw.eq(false))
|
||||
@ -571,7 +574,7 @@ impl PaginationCursor {
|
||||
#[derive(Clone)]
|
||||
pub struct PaginationCursorData(PostAggregates);
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PostQuery<'a> {
|
||||
pub listing_type: Option<ListingType>,
|
||||
pub sort: Option<SortType>,
|
||||
@ -595,6 +598,7 @@ pub struct PostQuery<'a> {
|
||||
impl<'a> PostQuery<'a> {
|
||||
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 the page end for the real query
|
||||
@ -645,11 +649,14 @@ impl<'a> PostQuery<'a> {
|
||||
let mut v = queries()
|
||||
.list(
|
||||
pool,
|
||||
PostQuery {
|
||||
community_id: Some(largest_subscribed),
|
||||
community_id_just_for_prefetch: true,
|
||||
..self.clone()
|
||||
},
|
||||
(
|
||||
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,
|
||||
@ -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)
|
||||
&& 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(pool).await? {
|
||||
queries().list(pool, query).await
|
||||
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).await
|
||||
queries().list(pool, (self, site)).await
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -698,7 +708,7 @@ mod tests {
|
||||
use lemmy_db_schema::{
|
||||
aggregates::structs::PostAggregates,
|
||||
impls::actor_language::UNDETERMINED_ID,
|
||||
newtypes::{InstanceId, LanguageId, PersonId},
|
||||
newtypes::LanguageId,
|
||||
source::{
|
||||
actor_language::LocalUserLanguage,
|
||||
comment::{Comment, CommentInsertForm},
|
||||
@ -717,6 +727,7 @@ mod tests {
|
||||
person::{Person, PersonInsertForm},
|
||||
person_block::{PersonBlock, PersonBlockForm},
|
||||
post::{Post, PostInsertForm, PostLike, PostLikeForm, PostRead, PostUpdateForm},
|
||||
site::Site,
|
||||
},
|
||||
traits::{Blockable, Crud, Joinable, Likeable},
|
||||
utils::{build_db_pool, build_db_pool_for_tests, DbPool, RANK_DEFAULT},
|
||||
@ -728,6 +739,7 @@ mod tests {
|
||||
use pretty_assertions::assert_eq;
|
||||
use serial_test::serial;
|
||||
use std::{collections::HashSet, time::Duration};
|
||||
use url::Url;
|
||||
|
||||
const POST_BY_BLOCKED_PERSON: &str = "post by blocked person";
|
||||
const POST_BY_BOT: &str = "post by bot";
|
||||
@ -745,6 +757,7 @@ mod tests {
|
||||
inserted_community: Community,
|
||||
inserted_post: Post,
|
||||
inserted_bot_post: Post,
|
||||
site: Site,
|
||||
}
|
||||
|
||||
impl Data {
|
||||
@ -757,37 +770,22 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn default_person_insert_form(instance_id: InstanceId, name: &str) -> PersonInsertForm {
|
||||
PersonInsertForm::builder()
|
||||
.name(name.to_owned())
|
||||
.public_key("pubkey".to_string())
|
||||
.instance_id(instance_id)
|
||||
.build()
|
||||
}
|
||||
|
||||
fn default_local_user_form(person_id: PersonId) -> LocalUserInsertForm {
|
||||
LocalUserInsertForm::builder()
|
||||
.person_id(person_id)
|
||||
.password_encrypted(String::new())
|
||||
.build()
|
||||
}
|
||||
|
||||
async fn init_data(pool: &mut DbPool<'_>) -> LemmyResult<Data> {
|
||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||
|
||||
let new_person = default_person_insert_form(inserted_instance.id, "tegan");
|
||||
let new_person = PersonInsertForm::test_form(inserted_instance.id, "tegan");
|
||||
|
||||
let inserted_person = Person::create(pool, &new_person).await?;
|
||||
|
||||
let local_user_form = LocalUserInsertForm {
|
||||
admin: Some(true),
|
||||
..default_local_user_form(inserted_person.id)
|
||||
..LocalUserInsertForm::test_form(inserted_person.id)
|
||||
};
|
||||
let inserted_local_user = LocalUser::create(pool, &local_user_form).await?;
|
||||
|
||||
let new_bot = PersonInsertForm {
|
||||
bot_account: Some(true),
|
||||
..default_person_insert_form(inserted_instance.id, "mybot")
|
||||
..PersonInsertForm::test_form(inserted_instance.id, "mybot")
|
||||
};
|
||||
|
||||
let inserted_bot = Person::create(pool, &new_bot).await?;
|
||||
@ -802,12 +800,15 @@ mod tests {
|
||||
let inserted_community = Community::create(pool, &new_community).await?;
|
||||
|
||||
// Test a person block, make sure the post query doesn't include their post
|
||||
let blocked_person = default_person_insert_form(inserted_instance.id, "john");
|
||||
let blocked_person = PersonInsertForm::test_form(inserted_instance.id, "john");
|
||||
|
||||
let inserted_blocked_person = Person::create(pool, &blocked_person).await?;
|
||||
|
||||
let inserted_blocked_local_user =
|
||||
LocalUser::create(pool, &default_local_user_form(inserted_blocked_person.id)).await?;
|
||||
let inserted_blocked_local_user = LocalUser::create(
|
||||
pool,
|
||||
&LocalUserInsertForm::test_form(inserted_blocked_person.id),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let post_from_blocked_person = PostInsertForm::builder()
|
||||
.name(POST_BY_BLOCKED_PERSON.to_string())
|
||||
@ -854,6 +855,24 @@ mod tests {
|
||||
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 {
|
||||
inserted_instance,
|
||||
local_user_view,
|
||||
@ -862,6 +881,7 @@ mod tests {
|
||||
inserted_community,
|
||||
inserted_post,
|
||||
inserted_bot_post,
|
||||
site,
|
||||
})
|
||||
}
|
||||
|
||||
@ -884,7 +904,7 @@ mod tests {
|
||||
community_id: Some(data.inserted_community.id),
|
||||
..data.default_post_query()
|
||||
}
|
||||
.list(pool)
|
||||
.list(&data.site, pool)
|
||||
.await?;
|
||||
|
||||
let post_listing_single_with_person = PostView::read(
|
||||
@ -919,7 +939,7 @@ mod tests {
|
||||
community_id: Some(data.inserted_community.id),
|
||||
..data.default_post_query()
|
||||
}
|
||||
.list(pool)
|
||||
.list(&data.site, pool)
|
||||
.await?;
|
||||
// should include bot post which has "undetermined" language
|
||||
assert_eq!(vec![POST_BY_BOT, POST], names(&post_listings_with_bots));
|
||||
@ -939,7 +959,7 @@ mod tests {
|
||||
local_user: None,
|
||||
..data.default_post_query()
|
||||
}
|
||||
.list(pool)
|
||||
.list(&data.site, pool)
|
||||
.await?;
|
||||
|
||||
let read_post_listing_single_no_person =
|
||||
@ -982,7 +1002,7 @@ mod tests {
|
||||
community_id: Some(data.inserted_community.id),
|
||||
..data.default_post_query()
|
||||
}
|
||||
.list(pool)
|
||||
.list(&data.site, pool)
|
||||
.await?;
|
||||
// Should be 0 posts after the community block
|
||||
assert_eq!(read_post_listings_with_person_after_block, vec![]);
|
||||
@ -1040,7 +1060,7 @@ mod tests {
|
||||
community_id: Some(data.inserted_community.id),
|
||||
..data.default_post_query()
|
||||
}
|
||||
.list(pool)
|
||||
.list(&data.site, pool)
|
||||
.await?;
|
||||
assert_eq!(vec![expected_post_with_upvote], read_post_listing);
|
||||
|
||||
@ -1049,7 +1069,7 @@ mod tests {
|
||||
liked_only: true,
|
||||
..data.default_post_query()
|
||||
}
|
||||
.list(pool)
|
||||
.list(&data.site, pool)
|
||||
.await?;
|
||||
assert_eq!(read_post_listing, read_liked_post_listing);
|
||||
|
||||
@ -1058,7 +1078,7 @@ mod tests {
|
||||
disliked_only: true,
|
||||
..data.default_post_query()
|
||||
}
|
||||
.list(pool)
|
||||
.list(&data.site, pool)
|
||||
.await?;
|
||||
assert_eq!(read_disliked_post_listing, vec![]);
|
||||
|
||||
@ -1088,7 +1108,7 @@ mod tests {
|
||||
community_id: Some(data.inserted_community.id),
|
||||
..data.default_post_query()
|
||||
}
|
||||
.list(pool)
|
||||
.list(&data.site, pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|p| (p.creator.name, p.creator_is_moderator, p.creator_is_admin))
|
||||
@ -1130,14 +1150,14 @@ mod tests {
|
||||
|
||||
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
|
||||
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?;
|
||||
|
||||
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
|
||||
assert_eq!(vec![POST_BY_BOT, POST], names(&post_listing_french));
|
||||
@ -1154,7 +1174,7 @@ mod tests {
|
||||
.await?;
|
||||
let post_listings_french_und = data
|
||||
.default_post_query()
|
||||
.list(pool)
|
||||
.list(&data.site, pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|p| (p.post.name, p.post.language_id))
|
||||
@ -1189,7 +1209,7 @@ mod tests {
|
||||
.await?;
|
||||
|
||||
// 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));
|
||||
|
||||
// Removed bot post is shown to admins on its profile page
|
||||
@ -1198,7 +1218,7 @@ mod tests {
|
||||
creator_id: Some(data.inserted_bot.id),
|
||||
..data.default_post_query()
|
||||
}
|
||||
.list(pool)
|
||||
.list(&data.site, pool)
|
||||
.await?;
|
||||
assert_eq!(vec![POST_BY_BOT], names(&post_listings_is_admin));
|
||||
|
||||
@ -1233,7 +1253,7 @@ mod tests {
|
||||
local_user,
|
||||
..data.default_post_query()
|
||||
}
|
||||
.list(pool)
|
||||
.list(&data.site, pool)
|
||||
.await?
|
||||
.iter()
|
||||
.any(|p| p.post.id == data.inserted_post.id);
|
||||
@ -1273,7 +1293,7 @@ mod tests {
|
||||
let post_from_blocked_instance = Post::create(pool, &post_form).await?;
|
||||
|
||||
// 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!(
|
||||
vec![POST_FROM_BLOCKED_INSTANCE, POST_BY_BOT, POST],
|
||||
names(&post_listings_all)
|
||||
@ -1287,7 +1307,7 @@ mod tests {
|
||||
InstanceBlock::block(pool, &block_form).await?;
|
||||
|
||||
// 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!(post_listings_blocked
|
||||
.iter()
|
||||
@ -1295,7 +1315,7 @@ mod tests {
|
||||
|
||||
// after unblocking it should return all posts again
|
||||
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!(
|
||||
vec![POST_FROM_BLOCKED_INSTANCE, POST_BY_BOT, POST],
|
||||
names(&post_listings_blocked)
|
||||
@ -1363,7 +1383,7 @@ mod tests {
|
||||
page_after,
|
||||
..options.clone()
|
||||
}
|
||||
.list(pool)
|
||||
.list(&data.site, pool)
|
||||
.await?;
|
||||
|
||||
listed_post_ids.extend(post_listings.iter().map(|p| p.post.id));
|
||||
@ -1384,7 +1404,7 @@ mod tests {
|
||||
page_back: true,
|
||||
..options.clone()
|
||||
}
|
||||
.list(pool)
|
||||
.list(&data.site, pool)
|
||||
.await?;
|
||||
|
||||
let listed_post_ids = post_listings.iter().map(|p| p.post.id).collect::<Vec<_>>();
|
||||
@ -1437,7 +1457,7 @@ mod tests {
|
||||
.await?;
|
||||
|
||||
// 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));
|
||||
|
||||
cleanup(data, pool).await
|
||||
@ -1589,7 +1609,7 @@ mod tests {
|
||||
let unauthenticated_query = PostQuery {
|
||||
..Default::default()
|
||||
}
|
||||
.list(pool)
|
||||
.list(&data.site, pool)
|
||||
.await?;
|
||||
assert_eq!(0, unauthenticated_query.len());
|
||||
|
||||
@ -1597,7 +1617,7 @@ mod tests {
|
||||
local_user: Some(&data.local_user_view),
|
||||
..Default::default()
|
||||
}
|
||||
.list(pool)
|
||||
.list(&data.site, pool)
|
||||
.await?;
|
||||
assert_eq!(2, authenticated_query.len());
|
||||
|
||||
|
@ -12,7 +12,7 @@ use diesel_async::RunQueryDsl;
|
||||
use lemmy_db_schema::{
|
||||
aliases,
|
||||
newtypes::{PersonId, PrivateMessageId},
|
||||
schema::{person, person_block, private_message},
|
||||
schema::{instance_block, person, person_block, private_message},
|
||||
utils::{get_conn, limit_and_offset, DbConn, DbPool, ListFn, Queries, ReadFn},
|
||||
};
|
||||
use tracing::debug;
|
||||
@ -34,6 +34,13 @@ fn queries<'a>() -> Queries<
|
||||
.and(person_block::person_id.eq(aliases::person1.field(person::id))),
|
||||
),
|
||||
)
|
||||
.left_join(
|
||||
instance_block::table.on(
|
||||
person::instance_id
|
||||
.eq(instance_block::instance_id)
|
||||
.and(instance_block::person_id.eq(aliases::person1.field(person::id))),
|
||||
),
|
||||
)
|
||||
};
|
||||
|
||||
let selection = (
|
||||
@ -55,7 +62,9 @@ fn queries<'a>() -> Queries<
|
||||
let mut query = all_joins(private_message::table.into_boxed())
|
||||
.select(selection)
|
||||
// Dont show replies from blocked users
|
||||
.filter(person_block::person_id.is_null());
|
||||
.filter(person_block::person_id.is_null())
|
||||
// Dont show replies from blocked instances
|
||||
.filter(instance_block::person_id.is_null());
|
||||
|
||||
// If its unread, I only want the ones to me
|
||||
if options.unread_only {
|
||||
@ -116,6 +125,8 @@ impl PrivateMessageView {
|
||||
use diesel::dsl::count;
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
private_message::table
|
||||
// Necessary to get the senders instance_id
|
||||
.inner_join(person::table.on(private_message::creator_id.eq(person::id)))
|
||||
.left_join(
|
||||
person_block::table.on(
|
||||
private_message::creator_id
|
||||
@ -123,8 +134,17 @@ impl PrivateMessageView {
|
||||
.and(person_block::person_id.eq(my_person_id)),
|
||||
),
|
||||
)
|
||||
.left_join(
|
||||
instance_block::table.on(
|
||||
person::instance_id
|
||||
.eq(instance_block::instance_id)
|
||||
.and(instance_block::person_id.eq(my_person_id)),
|
||||
),
|
||||
)
|
||||
// Dont count replies from blocked users
|
||||
.filter(person_block::person_id.is_null())
|
||||
// Dont count replies from blocked instances
|
||||
.filter(instance_block::person_id.is_null())
|
||||
.filter(private_message::read.eq(false))
|
||||
.filter(private_message::recipient_id.eq(my_person_id))
|
||||
.filter(private_message::deleted.eq(false))
|
||||
@ -160,24 +180,30 @@ mod tests {
|
||||
use crate::{private_message_view::PrivateMessageQuery, structs::PrivateMessageView};
|
||||
use lemmy_db_schema::{
|
||||
assert_length,
|
||||
newtypes::InstanceId,
|
||||
source::{
|
||||
instance::Instance,
|
||||
instance_block::{InstanceBlock, InstanceBlockForm},
|
||||
person::{Person, PersonInsertForm},
|
||||
person_block::{PersonBlock, PersonBlockForm},
|
||||
private_message::{PrivateMessage, PrivateMessageInsertForm},
|
||||
},
|
||||
traits::{Blockable, Crud},
|
||||
utils::build_db_pool_for_tests,
|
||||
utils::{build_db_pool_for_tests, DbPool},
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serial_test::serial;
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_crud() {
|
||||
struct Data {
|
||||
instance: Instance,
|
||||
timmy: Person,
|
||||
jess: Person,
|
||||
sara: Person,
|
||||
}
|
||||
|
||||
async fn init_data(pool: &mut DbPool<'_>) -> LemmyResult<Data> {
|
||||
let message_content = String::new();
|
||||
let pool = &build_db_pool_for_tests().await;
|
||||
let pool = &mut pool.into();
|
||||
|
||||
let instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
|
||||
.await
|
||||
@ -243,6 +269,32 @@ mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
Ok(Data {
|
||||
instance,
|
||||
timmy,
|
||||
jess,
|
||||
sara,
|
||||
})
|
||||
}
|
||||
|
||||
async fn cleanup(instance_id: InstanceId, pool: &mut DbPool<'_>) -> LemmyResult<()> {
|
||||
// This also deletes all persons and private messages thanks to sql `on delete cascade`
|
||||
Instance::delete(pool, instance_id).await.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn read_private_messages() -> LemmyResult<()> {
|
||||
let pool = &build_db_pool_for_tests().await;
|
||||
let pool = &mut pool.into();
|
||||
let Data {
|
||||
timmy,
|
||||
jess,
|
||||
sara,
|
||||
instance,
|
||||
} = init_data(pool).await?;
|
||||
|
||||
let timmy_messages = PrivateMessageQuery {
|
||||
unread_only: false,
|
||||
creator_id: None,
|
||||
@ -303,6 +355,21 @@ mod tests {
|
||||
assert_eq!(timmy_sara_unread_messages[0].creator.id, sara.id);
|
||||
assert_eq!(timmy_sara_unread_messages[0].recipient.id, timmy.id);
|
||||
|
||||
cleanup(instance.id, pool).await
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn ensure_person_block() -> LemmyResult<()> {
|
||||
let pool = &build_db_pool_for_tests().await;
|
||||
let pool = &mut pool.into();
|
||||
let Data {
|
||||
timmy,
|
||||
sara,
|
||||
instance,
|
||||
jess: _,
|
||||
} = init_data(pool).await?;
|
||||
|
||||
// Make sure blocks are working
|
||||
let timmy_blocks_sara_form = PersonBlockForm {
|
||||
person_id: timmy.id,
|
||||
@ -336,7 +403,52 @@ mod tests {
|
||||
.unwrap();
|
||||
assert_eq!(timmy_unread_messages, 1);
|
||||
|
||||
// This also deletes all persons and private messages thanks to sql `on delete cascade`
|
||||
Instance::delete(pool, instance.id).await.unwrap();
|
||||
cleanup(instance.id, pool).await
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn ensure_instance_block() -> LemmyResult<()> {
|
||||
let pool = &build_db_pool_for_tests().await;
|
||||
let pool = &mut pool.into();
|
||||
let Data {
|
||||
timmy,
|
||||
jess: _,
|
||||
sara,
|
||||
instance,
|
||||
} = init_data(pool).await?;
|
||||
// Make sure instance_blocks are working
|
||||
let timmy_blocks_instance_form = InstanceBlockForm {
|
||||
person_id: timmy.id,
|
||||
instance_id: sara.instance_id,
|
||||
};
|
||||
|
||||
let inserted_instance_block = InstanceBlock::block(pool, &timmy_blocks_instance_form)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let expected_instance_block = InstanceBlock {
|
||||
person_id: timmy.id,
|
||||
instance_id: sara.instance_id,
|
||||
published: inserted_instance_block.published,
|
||||
};
|
||||
assert_eq!(expected_instance_block, inserted_instance_block);
|
||||
|
||||
let timmy_messages = PrivateMessageQuery {
|
||||
unread_only: true,
|
||||
creator_id: None,
|
||||
..Default::default()
|
||||
}
|
||||
.list(pool, timmy.id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_length!(0, &timmy_messages);
|
||||
|
||||
let timmy_unread_messages = PrivateMessageView::get_unread_messages(pool, timmy.id)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(timmy_unread_messages, 0);
|
||||
cleanup(instance.id, pool).await
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ strum_macros = { workspace = true }
|
||||
serial_test = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
pretty_assertions = { workspace = true }
|
||||
url.workspace = true
|
||||
|
||||
[package.metadata.cargo-machete]
|
||||
ignored = ["strum"]
|
||||
|
@ -17,10 +17,11 @@ use lemmy_db_schema::{
|
||||
community_aggregates,
|
||||
community_block,
|
||||
community_follower,
|
||||
community_person_ban,
|
||||
instance_block,
|
||||
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},
|
||||
CommunityVisibility,
|
||||
ListingType,
|
||||
@ -29,7 +30,7 @@ use lemmy_db_schema::{
|
||||
|
||||
fn queries<'a>() -> Queries<
|
||||
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>| {
|
||||
// The left join below will return None in this case
|
||||
@ -58,6 +59,13 @@ fn queries<'a>() -> Queries<
|
||||
.and(community_block::person_id.eq(person_id_join)),
|
||||
),
|
||||
)
|
||||
.left_join(
|
||||
community_person_ban::table.on(
|
||||
community::id
|
||||
.eq(community_person_ban::community_id)
|
||||
.and(community_person_ban::person_id.eq(person_id_join)),
|
||||
),
|
||||
)
|
||||
};
|
||||
|
||||
let selection = (
|
||||
@ -65,6 +73,7 @@ fn queries<'a>() -> Queries<
|
||||
CommunityFollower::select_subscribed_type(),
|
||||
community_block::community_id.nullable().is_not_null(),
|
||||
community_aggregates::all_columns,
|
||||
community_person_ban::person_id.nullable().is_not_null(),
|
||||
);
|
||||
|
||||
let not_removed_or_deleted = community::removed
|
||||
@ -96,7 +105,7 @@ fn queries<'a>() -> Queries<
|
||||
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::*;
|
||||
|
||||
let my_person_id = options.local_user.map(|l| l.person_id);
|
||||
@ -158,8 +167,10 @@ fn queries<'a>() -> Queries<
|
||||
query = query.filter(community_block::person_id.is_null());
|
||||
query = query.filter(community::nsfw.eq(false).or(local_user::show_nsfw.eq(true)));
|
||||
} else {
|
||||
// No person in request, only show nsfw communities if show_nsfw is passed into request
|
||||
if !options.show_nsfw {
|
||||
// No person in request, only show nsfw communities if show_nsfw is passed into request or if
|
||||
// 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));
|
||||
}
|
||||
// Hide local only communities from unauthenticated users
|
||||
@ -233,8 +244,8 @@ pub struct CommunityQuery<'a> {
|
||||
}
|
||||
|
||||
impl<'a> CommunityQuery<'a> {
|
||||
pub async fn list(self, pool: &mut DbPool<'_>) -> Result<Vec<CommunityView>, Error> {
|
||||
queries().list(pool, self).await
|
||||
pub async fn list(self, site: &Site, pool: &mut DbPool<'_>) -> Result<Vec<CommunityView>, Error> {
|
||||
queries().list(pool, (self, site)).await
|
||||
}
|
||||
}
|
||||
|
||||
@ -250,17 +261,20 @@ mod tests {
|
||||
instance::Instance,
|
||||
local_user::{LocalUser, LocalUserInsertForm},
|
||||
person::{Person, PersonInsertForm},
|
||||
site::Site,
|
||||
},
|
||||
traits::Crud,
|
||||
utils::{build_db_pool_for_tests, DbPool},
|
||||
CommunityVisibility,
|
||||
};
|
||||
use serial_test::serial;
|
||||
use url::Url;
|
||||
|
||||
struct Data {
|
||||
inserted_instance: Instance,
|
||||
local_user: LocalUser,
|
||||
inserted_community: Community,
|
||||
site: Site,
|
||||
}
|
||||
|
||||
async fn init_data(pool: &mut DbPool<'_>) -> Data {
|
||||
@ -293,10 +307,30 @@ mod tests {
|
||||
|
||||
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 {
|
||||
inserted_instance,
|
||||
local_user,
|
||||
inserted_community,
|
||||
site,
|
||||
}
|
||||
}
|
||||
|
||||
@ -333,7 +367,7 @@ mod tests {
|
||||
let unauthenticated_query = CommunityQuery {
|
||||
..Default::default()
|
||||
}
|
||||
.list(pool)
|
||||
.list(&data.site, pool)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(0, unauthenticated_query.len());
|
||||
@ -342,7 +376,7 @@ mod tests {
|
||||
local_user: Some(&data.local_user),
|
||||
..Default::default()
|
||||
}
|
||||
.list(pool)
|
||||
.list(&data.site, pool)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(1, authenticated_query.len());
|
||||
|
@ -80,6 +80,7 @@ pub struct CommunityView {
|
||||
pub subscribed: SubscribedType,
|
||||
pub blocked: bool,
|
||||
pub counts: CommunityAggregates,
|
||||
pub banned_from_community: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
|
@ -29,6 +29,7 @@ use once_cell::sync::Lazy;
|
||||
use rss::{
|
||||
extension::{dublincore::DublinCoreExtension, ExtensionBuilder, ExtensionMap},
|
||||
Channel,
|
||||
EnclosureBuilder,
|
||||
Guid,
|
||||
Item,
|
||||
};
|
||||
@ -162,7 +163,7 @@ async fn get_feed_data(
|
||||
page: (Some(page)),
|
||||
..Default::default()
|
||||
}
|
||||
.list(&mut context.pool())
|
||||
.list(&site_view.site, &mut context.pool())
|
||||
.await?;
|
||||
|
||||
let items = create_post_items(posts, &context.settings().get_protocol_and_hostname())?;
|
||||
@ -269,7 +270,7 @@ async fn get_feed_user(
|
||||
page: (Some(*page)),
|
||||
..Default::default()
|
||||
}
|
||||
.list(&mut context.pool())
|
||||
.list(&site_view.site, &mut context.pool())
|
||||
.await?;
|
||||
|
||||
let items = create_post_items(posts, &context.settings().get_protocol_and_hostname())?;
|
||||
@ -307,7 +308,7 @@ async fn get_feed_community(
|
||||
page: (Some(*page)),
|
||||
..Default::default()
|
||||
}
|
||||
.list(&mut context.pool())
|
||||
.list(&site_view.site, &mut context.pool())
|
||||
.await?;
|
||||
|
||||
let items = create_post_items(posts, &context.settings().get_protocol_and_hostname())?;
|
||||
@ -348,7 +349,7 @@ async fn get_feed_front(
|
||||
page: (Some(*page)),
|
||||
..Default::default()
|
||||
}
|
||||
.list(&mut context.pool())
|
||||
.list(&site_view.site, &mut context.pool())
|
||||
.await?;
|
||||
|
||||
let protocol_and_hostname = context.settings().get_protocol_and_hostname();
|
||||
@ -495,7 +496,6 @@ fn create_post_items(
|
||||
let mut items: Vec<Item> = Vec::new();
|
||||
|
||||
for p in posts {
|
||||
// TODO add images
|
||||
let post_url = format!("{}/post/{}", protocol_and_hostname, p.post.id);
|
||||
let community_url = format!(
|
||||
"{}/c/{}",
|
||||
@ -520,12 +520,21 @@ fn create_post_items(
|
||||
p.counts.comments);
|
||||
|
||||
// If its a url post, add it to the description
|
||||
let link = Some(if let Some(url) = p.post.url {
|
||||
// and see if we can parse it as a media enclosure.
|
||||
let enclosure_opt = p.post.url.map(|url| {
|
||||
let link_html = format!("<br><a href=\"{url}\">{url}</a>");
|
||||
description.push_str(&link_html);
|
||||
url.to_string()
|
||||
} else {
|
||||
post_url.clone()
|
||||
|
||||
let mime_type = p
|
||||
.post
|
||||
.url_content_type
|
||||
.unwrap_or_else(|| "application/octet-stream".to_string());
|
||||
let mut enclosure_bld = EnclosureBuilder::default();
|
||||
|
||||
enclosure_bld.url(url.as_str().to_string());
|
||||
enclosure_bld.mime_type(mime_type);
|
||||
enclosure_bld.length("0".to_string());
|
||||
enclosure_bld.build()
|
||||
});
|
||||
|
||||
if let Some(body) = p.post.body {
|
||||
@ -558,8 +567,9 @@ fn create_post_items(
|
||||
guid,
|
||||
description: Some(sanitize_xml(description)),
|
||||
dublin_core_ext,
|
||||
link,
|
||||
link: Some(post_url.clone()),
|
||||
extensions,
|
||||
enclosure: enclosure_opt,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
# syntax=docker/dockerfile:1.6
|
||||
ARG RUST_VERSION=1.75
|
||||
ARG RUST_VERSION=1.76
|
||||
ARG CARGO_BUILD_FEATURES=default
|
||||
ARG RUST_RELEASE_MODE=debug
|
||||
|
||||
|
6
migrations/2024-01-22-105746_lemmynsfw-changes/down.sql
Normal file
6
migrations/2024-01-22-105746_lemmynsfw-changes/down.sql
Normal file
@ -0,0 +1,6 @@
|
||||
ALTER TABLE site
|
||||
DROP COLUMN content_warning;
|
||||
|
||||
ALTER TABLE local_site
|
||||
DROP COLUMN default_post_listing_mode;
|
||||
|
6
migrations/2024-01-22-105746_lemmynsfw-changes/up.sql
Normal file
6
migrations/2024-01-22-105746_lemmynsfw-changes/up.sql
Normal 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';
|
||||
|
@ -0,0 +1,48 @@
|
||||
-- Automatically resolve all reports for a given post once it is marked as removed
|
||||
CREATE OR REPLACE FUNCTION post_removed_resolve_reports ()
|
||||
RETURNS TRIGGER
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
UPDATE
|
||||
post_report
|
||||
SET
|
||||
resolved = TRUE,
|
||||
resolver_id = NEW.mod_person_id,
|
||||
updated = now()
|
||||
WHERE
|
||||
post_report.post_id = NEW.post_id;
|
||||
RETURN NULL;
|
||||
END
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE TRIGGER post_removed_resolve_reports
|
||||
AFTER INSERT ON mod_remove_post
|
||||
FOR EACH ROW
|
||||
WHEN (NEW.removed)
|
||||
EXECUTE PROCEDURE post_removed_resolve_reports ();
|
||||
|
||||
-- Same when comment is marked as removed
|
||||
CREATE OR REPLACE FUNCTION comment_removed_resolve_reports ()
|
||||
RETURNS TRIGGER
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
UPDATE
|
||||
comment_report
|
||||
SET
|
||||
resolved = TRUE,
|
||||
resolver_id = NEW.mod_person_id,
|
||||
updated = now()
|
||||
WHERE
|
||||
comment_report.comment_id = NEW.comment_id;
|
||||
RETURN NULL;
|
||||
END
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE TRIGGER comment_removed_resolve_reports
|
||||
AFTER INSERT ON mod_remove_comment
|
||||
FOR EACH ROW
|
||||
WHEN (NEW.removed)
|
||||
EXECUTE PROCEDURE comment_removed_resolve_reports ();
|
||||
|
@ -0,0 +1,8 @@
|
||||
DROP TRIGGER IF EXISTS post_removed_resolve_reports ON mod_remove_post;
|
||||
|
||||
DROP FUNCTION IF EXISTS post_removed_resolve_reports;
|
||||
|
||||
DROP TRIGGER IF EXISTS comment_removed_resolve_reports ON mod_remove_comment;
|
||||
|
||||
DROP FUNCTION IF EXISTS comment_removed_resolve_reports;
|
||||
|
@ -0,0 +1,3 @@
|
||||
ALTER TABLE local_site
|
||||
DROP COLUMN default_sort_type;
|
||||
|
@ -0,0 +1,3 @@
|
||||
ALTER TABLE local_site
|
||||
ADD COLUMN default_sort_type sort_type_enum DEFAULT 'Active' NOT NULL;
|
||||
|
@ -22,7 +22,10 @@ use lemmy_db_schema::{
|
||||
received_activity,
|
||||
sent_activity,
|
||||
},
|
||||
source::instance::{Instance, InstanceForm},
|
||||
source::{
|
||||
instance::{Instance, InstanceForm},
|
||||
local_user::LocalUser,
|
||||
},
|
||||
utils::{get_conn, naive_now, now, DbPool, DELETED_REPLACEMENT_TEXT},
|
||||
};
|
||||
use lemmy_routes::nodeinfo::NodeInfo;
|
||||
@ -79,24 +82,19 @@ pub async fn setup(context: LemmyContext) -> Result<(), LemmyError> {
|
||||
});
|
||||
|
||||
let context_1 = context.clone();
|
||||
// Overwrite deleted & removed posts and comments every day
|
||||
// Daily tasks:
|
||||
// - Overwrite deleted & removed posts and comments every day
|
||||
// - Delete old denied users
|
||||
// - Update instance software
|
||||
scheduler.every(CTimeUnits::days(1)).run(move || {
|
||||
let context = context_1.clone();
|
||||
|
||||
async move {
|
||||
overwrite_deleted_posts_and_comments(&mut context.pool()).await;
|
||||
}
|
||||
});
|
||||
|
||||
let context_1 = context.clone();
|
||||
// Update the Instance Software
|
||||
scheduler.every(CTimeUnits::days(1)).run(move || {
|
||||
let context = context_1.clone();
|
||||
|
||||
async move {
|
||||
delete_old_denied_users(&mut context.pool()).await;
|
||||
update_instance_software(&mut context.pool(), context.client())
|
||||
.await
|
||||
.map_err(|e| warn!("Failed to update instance software: {e}"))
|
||||
.inspect_err(|e| warn!("Failed to update instance software: {e}"))
|
||||
.ok();
|
||||
}
|
||||
});
|
||||
@ -115,6 +113,7 @@ async fn startup_jobs(pool: &mut DbPool<'_>) {
|
||||
update_banned_when_expired(pool).await;
|
||||
clear_old_activities(pool).await;
|
||||
overwrite_deleted_posts_and_comments(pool).await;
|
||||
delete_old_denied_users(pool).await;
|
||||
}
|
||||
|
||||
/// Update the hot_rank columns for the aggregates tables
|
||||
@ -277,10 +276,10 @@ async fn delete_expired_captcha_answers(pool: &mut DbPool<'_>) {
|
||||
)
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.map(|_| {
|
||||
.inspect(|_| {
|
||||
info!("Done.");
|
||||
})
|
||||
.map_err(|e| error!("Failed to clear old captcha answers: {e}"))
|
||||
.inspect_err(|e| error!("Failed to clear old captcha answers: {e}"))
|
||||
.ok();
|
||||
}
|
||||
Err(e) => {
|
||||
@ -301,7 +300,7 @@ async fn clear_old_activities(pool: &mut DbPool<'_>) {
|
||||
)
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.map_err(|e| error!("Failed to clear old sent activities: {e}"))
|
||||
.inspect_err(|e| error!("Failed to clear old sent activities: {e}"))
|
||||
.ok();
|
||||
|
||||
diesel::delete(
|
||||
@ -310,8 +309,8 @@ async fn clear_old_activities(pool: &mut DbPool<'_>) {
|
||||
)
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.map(|_| info!("Done."))
|
||||
.map_err(|e| error!("Failed to clear old received activities: {e}"))
|
||||
.inspect(|_| info!("Done."))
|
||||
.inspect_err(|e| error!("Failed to clear old received activities: {e}"))
|
||||
.ok();
|
||||
}
|
||||
Err(e) => {
|
||||
@ -320,6 +319,16 @@ async fn clear_old_activities(pool: &mut DbPool<'_>) {
|
||||
}
|
||||
}
|
||||
|
||||
async fn delete_old_denied_users(pool: &mut DbPool<'_>) {
|
||||
LocalUser::delete_old_denied_local_users(pool)
|
||||
.await
|
||||
.inspect(|_| {
|
||||
info!("Done.");
|
||||
})
|
||||
.inspect(|e| error!("Failed to deleted old denied users: {e}"))
|
||||
.ok();
|
||||
}
|
||||
|
||||
/// overwrite posts and comments 30d after deletion
|
||||
async fn overwrite_deleted_posts_and_comments(pool: &mut DbPool<'_>) {
|
||||
info!("Overwriting deleted posts...");
|
||||
@ -339,10 +348,10 @@ async fn overwrite_deleted_posts_and_comments(pool: &mut DbPool<'_>) {
|
||||
))
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.map(|_| {
|
||||
.inspect(|_| {
|
||||
info!("Done.");
|
||||
})
|
||||
.map_err(|e| error!("Failed to overwrite deleted posts: {e}"))
|
||||
.inspect_err(|e| error!("Failed to overwrite deleted posts: {e}"))
|
||||
.ok();
|
||||
|
||||
info!("Overwriting deleted comments...");
|
||||
@ -355,10 +364,10 @@ async fn overwrite_deleted_posts_and_comments(pool: &mut DbPool<'_>) {
|
||||
.set(comment::content.eq(DELETED_REPLACEMENT_TEXT))
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.map(|_| {
|
||||
.inspect(|_| {
|
||||
info!("Done.");
|
||||
})
|
||||
.map_err(|e| error!("Failed to overwrite deleted comments: {e}"))
|
||||
.inspect_err(|e| error!("Failed to overwrite deleted comments: {e}"))
|
||||
.ok();
|
||||
}
|
||||
Err(e) => {
|
||||
@ -390,14 +399,14 @@ async fn active_counts(pool: &mut DbPool<'_>) {
|
||||
sql_query(update_site_stmt)
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.map_err(|e| error!("Failed to update site stats: {e}"))
|
||||
.inspect_err(|e| error!("Failed to update site stats: {e}"))
|
||||
.ok();
|
||||
|
||||
let update_community_stmt = format!("update community_aggregates ca set users_active_{} = mv.count_ from community_aggregates_activity('{}') mv where ca.community_id = mv.community_id_", i.1, i.0);
|
||||
sql_query(update_community_stmt)
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.map_err(|e| error!("Failed to update community stats: {e}"))
|
||||
.inspect_err(|e| error!("Failed to update community stats: {e}"))
|
||||
.ok();
|
||||
}
|
||||
|
||||
@ -424,7 +433,7 @@ async fn update_banned_when_expired(pool: &mut DbPool<'_>) {
|
||||
.set(person::banned.eq(false))
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.map_err(|e| error!("Failed to update person.banned when expires: {e}"))
|
||||
.inspect_err(|e| error!("Failed to update person.banned when expires: {e}"))
|
||||
.ok();
|
||||
|
||||
diesel::delete(
|
||||
@ -432,7 +441,7 @@ async fn update_banned_when_expired(pool: &mut DbPool<'_>) {
|
||||
)
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.map_err(|e| error!("Failed to remove community_ban expired rows: {e}"))
|
||||
.inspect_err(|e| error!("Failed to remove community_ban expired rows: {e}"))
|
||||
.ok();
|
||||
}
|
||||
Err(e) => {
|
||||
|
Loading…
Reference in New Issue
Block a user