mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-11-23 12:51:18 +00:00
Merge remote-tracking branch 'origin/main' into remove_success_responses
This commit is contained in:
commit
a5eaad7afd
102 changed files with 622 additions and 497 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2538,6 +2538,7 @@ dependencies = [
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bcrypt",
|
"bcrypt",
|
||||||
|
"chrono",
|
||||||
"futures",
|
"futures",
|
||||||
"lemmy_api_common",
|
"lemmy_api_common",
|
||||||
"lemmy_db_schema",
|
"lemmy_db_schema",
|
||||||
|
|
|
@ -21,16 +21,16 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^29.5.12",
|
"@types/jest": "^29.5.12",
|
||||||
"@types/node": "^22.0.2",
|
"@types/node": "^22.3.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
"@typescript-eslint/eslint-plugin": "^8.1.0",
|
||||||
"@typescript-eslint/parser": "^8.0.0",
|
"@typescript-eslint/parser": "^8.1.0",
|
||||||
"eslint": "^9.8.0",
|
"eslint": "^9.9.0",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"jest": "^29.5.0",
|
"jest": "^29.5.0",
|
||||||
"lemmy-js-client": "0.20.0-alpha.12",
|
"lemmy-js-client": "0.20.0-alpha.12",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"ts-jest": "^29.1.0",
|
"ts-jest": "^29.1.0",
|
||||||
"typescript": "^5.5.4",
|
"typescript": "^5.5.4",
|
||||||
"typescript-eslint": "^8.0.0"
|
"typescript-eslint": "^8.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,16 +12,16 @@ importers:
|
||||||
specifier: ^29.5.12
|
specifier: ^29.5.12
|
||||||
version: 29.5.12
|
version: 29.5.12
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^22.0.2
|
specifier: ^22.3.0
|
||||||
version: 22.3.0
|
version: 22.3.0
|
||||||
'@typescript-eslint/eslint-plugin':
|
'@typescript-eslint/eslint-plugin':
|
||||||
specifier: ^8.0.0
|
specifier: ^8.1.0
|
||||||
version: 8.1.0(@typescript-eslint/parser@8.1.0(eslint@9.9.0)(typescript@5.5.4))(eslint@9.9.0)(typescript@5.5.4)
|
version: 8.1.0(@typescript-eslint/parser@8.1.0(eslint@9.9.0)(typescript@5.5.4))(eslint@9.9.0)(typescript@5.5.4)
|
||||||
'@typescript-eslint/parser':
|
'@typescript-eslint/parser':
|
||||||
specifier: ^8.0.0
|
specifier: ^8.1.0
|
||||||
version: 8.1.0(eslint@9.9.0)(typescript@5.5.4)
|
version: 8.1.0(eslint@9.9.0)(typescript@5.5.4)
|
||||||
eslint:
|
eslint:
|
||||||
specifier: ^9.8.0
|
specifier: ^9.9.0
|
||||||
version: 9.9.0
|
version: 9.9.0
|
||||||
eslint-plugin-prettier:
|
eslint-plugin-prettier:
|
||||||
specifier: ^5.1.3
|
specifier: ^5.1.3
|
||||||
|
@ -42,7 +42,7 @@ importers:
|
||||||
specifier: ^5.5.4
|
specifier: ^5.5.4
|
||||||
version: 5.5.4
|
version: 5.5.4
|
||||||
typescript-eslint:
|
typescript-eslint:
|
||||||
specifier: ^8.0.0
|
specifier: ^8.1.0
|
||||||
version: 8.1.0(eslint@9.9.0)(typescript@5.5.4)
|
version: 8.1.0(eslint@9.9.0)(typescript@5.5.4)
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
|
@ -628,7 +628,7 @@ test("Enforce community ban for federated user", async () => {
|
||||||
// Alpha tries to make post on beta, but it fails because of ban
|
// Alpha tries to make post on beta, but it fails because of ban
|
||||||
await expect(
|
await expect(
|
||||||
createPost(alpha, betaCommunity.community.id),
|
createPost(alpha, betaCommunity.community.id),
|
||||||
).rejects.toStrictEqual(Error("banned_from_community"));
|
).rejects.toStrictEqual(Error("person_is_banned_from_community"));
|
||||||
|
|
||||||
// Unban alpha
|
// Unban alpha
|
||||||
let unBanAlpha = await banPersonFromCommunity(
|
let unBanAlpha = await banPersonFromCommunity(
|
||||||
|
|
|
@ -52,15 +52,12 @@ pub async fn add_mod_to_community(
|
||||||
// moderator. This is necessary because otherwise the action would be rejected
|
// moderator. This is necessary because otherwise the action would be rejected
|
||||||
// by the community's home instance.
|
// by the community's home instance.
|
||||||
if local_user_view.local_user.admin && !community.local {
|
if local_user_view.local_user.admin && !community.local {
|
||||||
let is_mod = CommunityModeratorView::is_community_moderator(
|
CommunityModeratorView::check_is_community_moderator(
|
||||||
&mut context.pool(),
|
&mut context.pool(),
|
||||||
community.id,
|
community.id,
|
||||||
local_user_view.person.id,
|
local_user_view.person.id,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
if !is_mod {
|
|
||||||
Err(LemmyErrorType::NotAModerator)?
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update in local database
|
// Update in local database
|
||||||
|
|
|
@ -265,8 +265,6 @@ pub async fn local_user_view_from_jwt(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -63,9 +63,7 @@ pub async fn save_user_settings(
|
||||||
let previous_email = local_user_view.local_user.email.clone().unwrap_or_default();
|
let previous_email = local_user_view.local_user.email.clone().unwrap_or_default();
|
||||||
// if email was changed, check that it is not taken and send verification mail
|
// if email was changed, check that it is not taken and send verification mail
|
||||||
if previous_email.deref() != email {
|
if previous_email.deref() != email {
|
||||||
if LocalUser::is_email_taken(&mut context.pool(), email).await? {
|
LocalUser::check_is_email_taken(&mut context.pool(), email).await?;
|
||||||
return Err(LemmyErrorType::EmailAlreadyExists)?;
|
|
||||||
}
|
|
||||||
send_verification_email(
|
send_verification_email(
|
||||||
&local_user_view,
|
&local_user_view,
|
||||||
email,
|
email,
|
||||||
|
@ -132,7 +130,6 @@ pub async fn save_user_settings(
|
||||||
send_notifications_to_email: data.send_notifications_to_email,
|
send_notifications_to_email: data.send_notifications_to_email,
|
||||||
show_nsfw: data.show_nsfw,
|
show_nsfw: data.show_nsfw,
|
||||||
blur_nsfw: data.blur_nsfw,
|
blur_nsfw: data.blur_nsfw,
|
||||||
auto_expand: data.auto_expand,
|
|
||||||
show_bot_accounts: data.show_bot_accounts,
|
show_bot_accounts: data.show_bot_accounts,
|
||||||
default_post_sort_type,
|
default_post_sort_type,
|
||||||
default_comment_sort_type,
|
default_comment_sort_type,
|
||||||
|
|
|
@ -34,7 +34,7 @@ use lemmy_db_views::structs::LocalUserView;
|
||||||
use lemmy_utils::{error::LemmyResult, LemmyErrorType, CACHE_DURATION_API};
|
use lemmy_utils::{error::LemmyResult, LemmyErrorType, CACHE_DURATION_API};
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
async fn create_test_site(context: &Data<LemmyContext>) -> LemmyResult<(Instance, LocalUserView)> {
|
async fn create_test_site(context: &Data<LemmyContext>) -> LemmyResult<(Instance, LocalUserView)> {
|
||||||
let pool = &mut context.pool();
|
let pool = &mut context.pool();
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ async fn signup(
|
||||||
Ok((local_user, application))
|
Ok((local_user, application))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
async fn get_application_statuses(
|
async fn get_application_statuses(
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
admin: LocalUserView,
|
admin: LocalUserView,
|
||||||
|
@ -138,10 +138,9 @@ async fn get_application_statuses(
|
||||||
Ok((application_count, unread_applications, all_applications))
|
Ok((application_count, unread_applications, all_applications))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[tokio::test]
|
|
||||||
#[serial]
|
#[serial]
|
||||||
|
#[tokio::test]
|
||||||
|
#[expect(clippy::indexing_slicing)]
|
||||||
async fn test_application_approval() -> LemmyResult<()> {
|
async fn test_application_approval() -> LemmyResult<()> {
|
||||||
let context = LemmyContext::init_test_context().await;
|
let context = LemmyContext::init_test_context().await;
|
||||||
let pool = &mut context.pool();
|
let pool = &mut context.pool();
|
||||||
|
|
|
@ -42,7 +42,7 @@ pub async fn get_sitemap(context: Data<LemmyContext>) -> LemmyResult<HttpRespons
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
pub(crate) mod tests {
|
pub(crate) mod tests {
|
||||||
|
|
||||||
use crate::sitemap::generate_urlset;
|
use crate::sitemap::generate_urlset;
|
||||||
|
|
|
@ -29,12 +29,8 @@ impl Claims {
|
||||||
let claims =
|
let claims =
|
||||||
decode::<Claims>(jwt, &key, &validation).with_lemmy_type(LemmyErrorType::NotLoggedIn)?;
|
decode::<Claims>(jwt, &key, &validation).with_lemmy_type(LemmyErrorType::NotLoggedIn)?;
|
||||||
let user_id = LocalUserId(claims.claims.sub.parse()?);
|
let user_id = LocalUserId(claims.claims.sub.parse()?);
|
||||||
let is_valid = LoginToken::validate(&mut context.pool(), user_id, jwt).await?;
|
LoginToken::validate(&mut context.pool(), user_id, jwt).await?;
|
||||||
if !is_valid {
|
Ok(user_id)
|
||||||
Err(LemmyErrorType::NotLoggedIn)?
|
|
||||||
} else {
|
|
||||||
Ok(user_id)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn generate(
|
pub async fn generate(
|
||||||
|
@ -73,8 +69,7 @@ impl Claims {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{claims::Claims, context::LemmyContext};
|
use crate::{claims::Claims, context::LemmyContext};
|
||||||
|
|
|
@ -19,11 +19,12 @@ pub struct CreateOAuthProvider {
|
||||||
pub client_id: String,
|
pub client_id: String,
|
||||||
pub client_secret: String,
|
pub client_secret: String,
|
||||||
pub scopes: String,
|
pub scopes: String,
|
||||||
pub auto_verify_email: bool,
|
pub auto_verify_email: Option<bool>,
|
||||||
pub account_linking_enabled: bool,
|
pub account_linking_enabled: Option<bool>,
|
||||||
pub enabled: bool,
|
pub enabled: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[skip_serializing_none]
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
#[cfg_attr(feature = "full", derive(TS))]
|
#[cfg_attr(feature = "full", derive(TS))]
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
|
|
|
@ -84,8 +84,8 @@ pub struct CaptchaResponse {
|
||||||
pub struct SaveUserSettings {
|
pub struct SaveUserSettings {
|
||||||
/// Show nsfw posts.
|
/// Show nsfw posts.
|
||||||
pub show_nsfw: Option<bool>,
|
pub show_nsfw: Option<bool>,
|
||||||
|
/// Blur nsfw posts.
|
||||||
pub blur_nsfw: Option<bool>,
|
pub blur_nsfw: Option<bool>,
|
||||||
pub auto_expand: Option<bool>,
|
|
||||||
/// Your user's theme.
|
/// Your user's theme.
|
||||||
pub theme: Option<String>,
|
pub theme: Option<String>,
|
||||||
/// The default post listing type, usually "local"
|
/// The default post listing type, usually "local"
|
||||||
|
|
|
@ -30,6 +30,8 @@ pub struct CreatePost {
|
||||||
pub language_id: Option<LanguageId>,
|
pub language_id: Option<LanguageId>,
|
||||||
/// Instead of fetching a thumbnail, use a custom one.
|
/// Instead of fetching a thumbnail, use a custom one.
|
||||||
pub custom_thumbnail: Option<String>,
|
pub custom_thumbnail: Option<String>,
|
||||||
|
/// Time when this post should be scheduled. Null means publish immediately.
|
||||||
|
pub scheduled_publish_time: Option<i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
@ -124,6 +126,8 @@ pub struct EditPost {
|
||||||
pub language_id: Option<LanguageId>,
|
pub language_id: Option<LanguageId>,
|
||||||
/// Instead of fetching a thumbnail, use a custom one.
|
/// Instead of fetching a thumbnail, use a custom one.
|
||||||
pub custom_thumbnail: Option<String>,
|
pub custom_thumbnail: Option<String>,
|
||||||
|
/// Time when this post should be scheduled. Null means publish immediately.
|
||||||
|
pub scheduled_publish_time: Option<i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)]
|
||||||
|
|
|
@ -471,8 +471,7 @@ pub async fn replace_image(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -78,7 +78,7 @@ pub struct Search {
|
||||||
pub listing_type: Option<ListingType>,
|
pub listing_type: Option<ListingType>,
|
||||||
pub page: Option<i64>,
|
pub page: Option<i64>,
|
||||||
pub limit: Option<i64>,
|
pub limit: Option<i64>,
|
||||||
pub post_title_only: Option<bool>,
|
pub title_only: Option<bool>,
|
||||||
pub post_url_only: Option<bool>,
|
pub post_url_only: Option<bool>,
|
||||||
pub saved_only: Option<bool>,
|
pub saved_only: Option<bool>,
|
||||||
pub liked_only: Option<bool>,
|
pub liked_only: Option<bool>,
|
||||||
|
|
|
@ -73,13 +73,7 @@ pub async fn is_mod_or_admin(
|
||||||
community_id: CommunityId,
|
community_id: CommunityId,
|
||||||
) -> LemmyResult<()> {
|
) -> LemmyResult<()> {
|
||||||
check_user_valid(person)?;
|
check_user_valid(person)?;
|
||||||
|
CommunityView::check_is_mod_or_admin(pool, person.id, community_id).await
|
||||||
let is_mod_or_admin = CommunityView::is_mod_or_admin(pool, person.id, community_id).await?;
|
|
||||||
if !is_mod_or_admin {
|
|
||||||
Err(LemmyErrorType::NotAModOrAdmin)?
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
|
@ -110,13 +104,7 @@ pub async fn check_community_mod_of_any_or_admin_action(
|
||||||
let person = &local_user_view.person;
|
let person = &local_user_view.person;
|
||||||
|
|
||||||
check_user_valid(person)?;
|
check_user_valid(person)?;
|
||||||
|
CommunityView::check_is_mod_of_any_or_admin(pool, person.id).await
|
||||||
let is_mod_of_any_or_admin = CommunityView::is_mod_of_any_or_admin(pool, person.id).await?;
|
|
||||||
if !is_mod_of_any_or_admin {
|
|
||||||
Err(LemmyErrorType::NotAModOrAdmin)?
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_admin(local_user_view: &LocalUserView) -> LemmyResult<()> {
|
pub fn is_admin(local_user_view: &LocalUserView) -> LemmyResult<()> {
|
||||||
|
@ -242,7 +230,7 @@ pub async fn check_community_user_action(
|
||||||
) -> LemmyResult<()> {
|
) -> LemmyResult<()> {
|
||||||
check_user_valid(person)?;
|
check_user_valid(person)?;
|
||||||
check_community_deleted_removed(community_id, pool).await?;
|
check_community_deleted_removed(community_id, pool).await?;
|
||||||
check_community_ban(person, community_id, pool).await?;
|
CommunityPersonBanView::check(pool, person.id, community_id).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,19 +245,6 @@ async fn check_community_deleted_removed(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn check_community_ban(
|
|
||||||
person: &Person,
|
|
||||||
community_id: CommunityId,
|
|
||||||
pool: &mut DbPool<'_>,
|
|
||||||
) -> LemmyResult<()> {
|
|
||||||
// check if user was banned from site or community
|
|
||||||
let is_banned = CommunityPersonBanView::get(pool, person.id, community_id).await?;
|
|
||||||
if is_banned {
|
|
||||||
Err(LemmyErrorType::BannedFromCommunity)?
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check that the given user can perform a mod action in the community.
|
/// Check that the given user can perform a mod action in the community.
|
||||||
///
|
///
|
||||||
/// In particular it checks that he is an admin or mod, wasn't banned and the community isn't
|
/// In particular it checks that he is an admin or mod, wasn't banned and the community isn't
|
||||||
|
@ -281,7 +256,7 @@ pub async fn check_community_mod_action(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
) -> LemmyResult<()> {
|
) -> LemmyResult<()> {
|
||||||
is_mod_or_admin(pool, person, community_id).await?;
|
is_mod_or_admin(pool, person, community_id).await?;
|
||||||
check_community_ban(person, community_id, pool).await?;
|
CommunityPersonBanView::check(pool, person.id, community_id).await?;
|
||||||
|
|
||||||
// it must be possible to restore deleted community
|
// it must be possible to restore deleted community
|
||||||
if !allow_deleted {
|
if !allow_deleted {
|
||||||
|
@ -307,51 +282,6 @@ pub fn check_comment_deleted_or_removed(comment: &Comment) -> LemmyResult<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Throws an error if a recipient has blocked a person.
|
|
||||||
#[tracing::instrument(skip_all)]
|
|
||||||
pub async fn check_person_block(
|
|
||||||
my_id: PersonId,
|
|
||||||
potential_blocker_id: PersonId,
|
|
||||||
pool: &mut DbPool<'_>,
|
|
||||||
) -> LemmyResult<()> {
|
|
||||||
let is_blocked = PersonBlock::read(pool, potential_blocker_id, my_id).await?;
|
|
||||||
if is_blocked {
|
|
||||||
Err(LemmyErrorType::PersonIsBlocked)?
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Throws an error if a recipient has blocked a community.
|
|
||||||
#[tracing::instrument(skip_all)]
|
|
||||||
async fn check_community_block(
|
|
||||||
community_id: CommunityId,
|
|
||||||
person_id: PersonId,
|
|
||||||
pool: &mut DbPool<'_>,
|
|
||||||
) -> LemmyResult<()> {
|
|
||||||
let is_blocked = CommunityBlock::read(pool, person_id, community_id).await?;
|
|
||||||
if is_blocked {
|
|
||||||
Err(LemmyErrorType::CommunityIsBlocked)?
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Throws an error if a recipient has blocked an instance.
|
|
||||||
#[tracing::instrument(skip_all)]
|
|
||||||
async fn check_instance_block(
|
|
||||||
instance_id: InstanceId,
|
|
||||||
person_id: PersonId,
|
|
||||||
pool: &mut DbPool<'_>,
|
|
||||||
) -> LemmyResult<()> {
|
|
||||||
let is_blocked = InstanceBlock::read(pool, person_id, instance_id).await?;
|
|
||||||
if is_blocked {
|
|
||||||
Err(LemmyErrorType::InstanceIsBlocked)?
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn check_person_instance_community_block(
|
pub async fn check_person_instance_community_block(
|
||||||
my_id: PersonId,
|
my_id: PersonId,
|
||||||
|
@ -360,9 +290,9 @@ pub async fn check_person_instance_community_block(
|
||||||
community_id: CommunityId,
|
community_id: CommunityId,
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
) -> LemmyResult<()> {
|
) -> LemmyResult<()> {
|
||||||
check_person_block(my_id, potential_blocker_id, pool).await?;
|
PersonBlock::read(pool, potential_blocker_id, my_id).await?;
|
||||||
check_instance_block(community_instance_id, potential_blocker_id, pool).await?;
|
InstanceBlock::read(pool, potential_blocker_id, community_instance_id).await?;
|
||||||
check_community_block(community_id, potential_blocker_id, pool).await?;
|
CommunityBlock::read(pool, potential_blocker_id, community_id).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -846,12 +776,13 @@ pub async fn remove_or_restore_user_data_in_community(
|
||||||
|
|
||||||
// Comments
|
// Comments
|
||||||
// TODO Diesel doesn't allow updates with joins, so this has to be a loop
|
// TODO Diesel doesn't allow updates with joins, so this has to be a loop
|
||||||
|
let site = Site::read_local(pool).await?;
|
||||||
let comments = CommentQuery {
|
let comments = CommentQuery {
|
||||||
creator_id: Some(banned_person_id),
|
creator_id: Some(banned_person_id),
|
||||||
community_id: Some(community_id),
|
community_id: Some(community_id),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(&site, pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
for comment_view in &comments {
|
for comment_view in &comments {
|
||||||
|
@ -1136,8 +1067,7 @@ fn build_proxied_image_url(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -27,6 +27,7 @@ futures.workspace = true
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true }
|
||||||
moka.workspace = true
|
moka.workspace = true
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
|
chrono.workspace = true
|
||||||
webmention = "0.6.0"
|
webmention = "0.6.0"
|
||||||
accept-language = "3.1.0"
|
accept-language = "3.1.0"
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use super::convert_published_time;
|
||||||
use activitypub_federation::config::Data;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Json;
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
|
@ -95,15 +96,12 @@ pub async fn create_post(
|
||||||
let community = Community::read(&mut context.pool(), community_id).await?;
|
let community = Community::read(&mut context.pool(), community_id).await?;
|
||||||
if community.posting_restricted_to_mods {
|
if community.posting_restricted_to_mods {
|
||||||
let community_id = data.community_id;
|
let community_id = data.community_id;
|
||||||
let is_mod = CommunityModeratorView::is_community_moderator(
|
CommunityModeratorView::check_is_community_moderator(
|
||||||
&mut context.pool(),
|
&mut context.pool(),
|
||||||
community_id,
|
community_id,
|
||||||
local_user_view.local_user.person_id,
|
local_user_view.local_user.person_id,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
if !is_mod {
|
|
||||||
Err(LemmyErrorType::OnlyModsCanPostInCommunity)?
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only need to check if language is allowed in case user set it explicitly. When using default
|
// Only need to check if language is allowed in case user set it explicitly. When using default
|
||||||
|
@ -128,12 +126,15 @@ pub async fn create_post(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let scheduled_publish_time =
|
||||||
|
convert_published_time(data.scheduled_publish_time, &local_user_view, &context).await?;
|
||||||
let post_form = PostInsertForm {
|
let post_form = PostInsertForm {
|
||||||
url: url.map(Into::into),
|
url: url.map(Into::into),
|
||||||
body,
|
body,
|
||||||
alt_text: data.alt_text.clone(),
|
alt_text: data.alt_text.clone(),
|
||||||
nsfw: data.nsfw,
|
nsfw: data.nsfw,
|
||||||
language_id,
|
language_id,
|
||||||
|
scheduled_publish_time,
|
||||||
..PostInsertForm::new(
|
..PostInsertForm::new(
|
||||||
data.name.trim().to_string(),
|
data.name.trim().to_string(),
|
||||||
local_user_view.person.id,
|
local_user_view.person.id,
|
||||||
|
@ -145,10 +146,16 @@ pub async fn create_post(
|
||||||
.await
|
.await
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntCreatePost)?;
|
.with_lemmy_type(LemmyErrorType::CouldntCreatePost)?;
|
||||||
|
|
||||||
|
let federate_post = if scheduled_publish_time.is_none() {
|
||||||
|
send_webmention(inserted_post.clone(), community);
|
||||||
|
|post| Some(SendActivityData::CreatePost(post))
|
||||||
|
} else {
|
||||||
|
|_| None
|
||||||
|
};
|
||||||
generate_post_link_metadata(
|
generate_post_link_metadata(
|
||||||
inserted_post.clone(),
|
inserted_post.clone(),
|
||||||
custom_thumbnail.map(Into::into),
|
custom_thumbnail.map(Into::into),
|
||||||
|post| Some(SendActivityData::CreatePost(post)),
|
federate_post,
|
||||||
context.reset_request_count(),
|
context.reset_request_count(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -168,11 +175,14 @@ pub async fn create_post(
|
||||||
|
|
||||||
mark_post_as_read(person_id, post_id, &mut context.pool()).await?;
|
mark_post_as_read(person_id, post_id, &mut context.pool()).await?;
|
||||||
|
|
||||||
if let Some(url) = inserted_post.url.clone() {
|
build_post_response(&context, community_id, local_user_view, post_id).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_webmention(post: Post, community: Community) {
|
||||||
|
if let Some(url) = post.url.clone() {
|
||||||
if community.visibility == CommunityVisibility::Public {
|
if community.visibility == CommunityVisibility::Public {
|
||||||
spawn_try_task(async move {
|
spawn_try_task(async move {
|
||||||
let mut webmention =
|
let mut webmention = Webmention::new::<Url>(post.ap_id.clone().into(), url.clone().into())?;
|
||||||
Webmention::new::<Url>(inserted_post.ap_id.clone().into(), url.clone().into())?;
|
|
||||||
webmention.set_checked(true);
|
webmention.set_checked(true);
|
||||||
match webmention
|
match webmention
|
||||||
.send()
|
.send()
|
||||||
|
@ -186,6 +196,4 @@ pub async fn create_post(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
build_post_response(&context, community_id, local_user_view, post_id).await
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,38 @@
|
||||||
|
use chrono::{DateTime, TimeZone, Utc};
|
||||||
|
use lemmy_api_common::context::LemmyContext;
|
||||||
|
use lemmy_db_schema::source::post::Post;
|
||||||
|
use lemmy_db_views::structs::LocalUserView;
|
||||||
|
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
||||||
|
|
||||||
pub mod create;
|
pub mod create;
|
||||||
pub mod delete;
|
pub mod delete;
|
||||||
pub mod read;
|
pub mod read;
|
||||||
pub mod remove;
|
pub mod remove;
|
||||||
pub mod update;
|
pub mod update;
|
||||||
|
|
||||||
|
async fn convert_published_time(
|
||||||
|
scheduled_publish_time: Option<i64>,
|
||||||
|
local_user_view: &LocalUserView,
|
||||||
|
context: &LemmyContext,
|
||||||
|
) -> LemmyResult<Option<DateTime<Utc>>> {
|
||||||
|
const MAX_SCHEDULED_POSTS: i64 = 10;
|
||||||
|
if let Some(scheduled_publish_time) = scheduled_publish_time {
|
||||||
|
let converted = Utc
|
||||||
|
.timestamp_opt(scheduled_publish_time, 0)
|
||||||
|
.single()
|
||||||
|
.ok_or(LemmyErrorType::InvalidUnixTime)?;
|
||||||
|
if converted < Utc::now() {
|
||||||
|
Err(LemmyErrorType::PostScheduleTimeMustBeInFuture)?;
|
||||||
|
}
|
||||||
|
if !local_user_view.local_user.admin {
|
||||||
|
let count =
|
||||||
|
Post::user_scheduled_post_count(local_user_view.person.id, &mut context.pool()).await?;
|
||||||
|
if count >= MAX_SCHEDULED_POSTS {
|
||||||
|
Err(LemmyErrorType::TooManyScheduledPosts)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Some(converted))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use super::{convert_published_time, create::send_webmention};
|
||||||
use activitypub_federation::config::Data;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Json;
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
|
@ -16,6 +17,7 @@ use lemmy_api_common::{
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
actor_language::CommunityLanguage,
|
actor_language::CommunityLanguage,
|
||||||
|
community::Community,
|
||||||
local_site::LocalSite,
|
local_site::LocalSite,
|
||||||
post::{Post, PostUpdateForm},
|
post::{Post, PostUpdateForm},
|
||||||
},
|
},
|
||||||
|
@ -107,6 +109,21 @@ pub async fn update_post(
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
// handle changes to scheduled_publish_time
|
||||||
|
let scheduled_publish_time = match (
|
||||||
|
orig_post.scheduled_publish_time,
|
||||||
|
data.scheduled_publish_time,
|
||||||
|
) {
|
||||||
|
// schedule time can be changed if post is still scheduled (and not published yet)
|
||||||
|
(Some(_), Some(_)) => {
|
||||||
|
Some(convert_published_time(data.scheduled_publish_time, &local_user_view, &context).await?)
|
||||||
|
}
|
||||||
|
// post was scheduled, gets changed to publish immediately
|
||||||
|
(Some(_), None) => Some(None),
|
||||||
|
// unchanged
|
||||||
|
(_, _) => None,
|
||||||
|
};
|
||||||
|
|
||||||
let post_form = PostUpdateForm {
|
let post_form = PostUpdateForm {
|
||||||
name: data.name.clone(),
|
name: data.name.clone(),
|
||||||
url,
|
url,
|
||||||
|
@ -115,6 +132,7 @@ pub async fn update_post(
|
||||||
nsfw: data.nsfw,
|
nsfw: data.nsfw,
|
||||||
language_id: data.language_id,
|
language_id: data.language_id,
|
||||||
updated: Some(Some(naive_now())),
|
updated: Some(Some(naive_now())),
|
||||||
|
scheduled_publish_time,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -123,13 +141,36 @@ pub async fn update_post(
|
||||||
.await
|
.await
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntUpdatePost)?;
|
.with_lemmy_type(LemmyErrorType::CouldntUpdatePost)?;
|
||||||
|
|
||||||
generate_post_link_metadata(
|
// send out federation/webmention if necessary
|
||||||
updated_post.clone(),
|
match (
|
||||||
custom_thumbnail.flatten().map(Into::into),
|
orig_post.scheduled_publish_time,
|
||||||
|post| Some(SendActivityData::UpdatePost(post)),
|
data.scheduled_publish_time,
|
||||||
context.reset_request_count(),
|
) {
|
||||||
)
|
// schedule was removed, send create activity and webmention
|
||||||
.await?;
|
(Some(_), None) => {
|
||||||
|
let community = Community::read(&mut context.pool(), orig_post.community_id).await?;
|
||||||
|
send_webmention(updated_post.clone(), community);
|
||||||
|
generate_post_link_metadata(
|
||||||
|
updated_post.clone(),
|
||||||
|
custom_thumbnail.flatten().map(Into::into),
|
||||||
|
|post| Some(SendActivityData::CreatePost(post)),
|
||||||
|
context.reset_request_count(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
// post was already public, send update
|
||||||
|
(None, _) => {
|
||||||
|
generate_post_link_metadata(
|
||||||
|
updated_post.clone(),
|
||||||
|
custom_thumbnail.flatten().map(Into::into),
|
||||||
|
|post| Some(SendActivityData::UpdatePost(post)),
|
||||||
|
context.reset_request_count(),
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
// schedule was changed, do nothing
|
||||||
|
(Some(_), Some(_)) => {}
|
||||||
|
};
|
||||||
|
|
||||||
build_post_response(
|
build_post_response(
|
||||||
context.deref(),
|
context.deref(),
|
||||||
|
|
|
@ -5,7 +5,6 @@ use lemmy_api_common::{
|
||||||
private_message::{CreatePrivateMessage, PrivateMessageResponse},
|
private_message::{CreatePrivateMessage, PrivateMessageResponse},
|
||||||
send_activity::{ActivityChannel, SendActivityData},
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{
|
utils::{
|
||||||
check_person_block,
|
|
||||||
get_interface_language,
|
get_interface_language,
|
||||||
get_url_blocklist,
|
get_url_blocklist,
|
||||||
local_site_to_slur_regex,
|
local_site_to_slur_regex,
|
||||||
|
@ -16,6 +15,7 @@ use lemmy_api_common::{
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
local_site::LocalSite,
|
local_site::LocalSite,
|
||||||
|
person_block::PersonBlock,
|
||||||
private_message::{PrivateMessage, PrivateMessageInsertForm},
|
private_message::{PrivateMessage, PrivateMessageInsertForm},
|
||||||
},
|
},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
|
@ -39,10 +39,10 @@ pub async fn create_private_message(
|
||||||
let content = process_markdown(&data.content, &slur_regex, &url_blocklist, &context).await?;
|
let content = process_markdown(&data.content, &slur_regex, &url_blocklist, &context).await?;
|
||||||
is_valid_body_field(&content, false)?;
|
is_valid_body_field(&content, false)?;
|
||||||
|
|
||||||
check_person_block(
|
PersonBlock::read(
|
||||||
local_user_view.person.id,
|
|
||||||
data.recipient_id,
|
|
||||||
&mut context.pool(),
|
&mut context.pool(),
|
||||||
|
data.recipient_id,
|
||||||
|
local_user_view.person.id,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|
|
@ -189,8 +189,6 @@ fn validate_create_payload(local_site: &LocalSite, create_site: &CreateSite) ->
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::site::create::validate_create_payload;
|
use crate::site::create::validate_create_payload;
|
||||||
|
|
|
@ -48,8 +48,6 @@ fn not_zero(val: Option<i32>) -> Option<i32> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::site::{application_question_check, not_zero, site_default_post_listing_type_check};
|
use crate::site::{application_question_check, not_zero, site_default_post_listing_type_check};
|
||||||
|
|
|
@ -241,8 +241,6 @@ fn validate_update_payload(local_site: &LocalSite, edit_site: &EditSite) -> Lemm
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::site::update::validate_update_payload;
|
use crate::site::update::validate_update_payload;
|
||||||
|
|
|
@ -92,36 +92,25 @@ pub async fn register(
|
||||||
}
|
}
|
||||||
|
|
||||||
if local_site.site_setup && local_site.captcha_enabled {
|
if local_site.site_setup && local_site.captcha_enabled {
|
||||||
if let Some(captcha_uuid) = &data.captcha_uuid {
|
let uuid = uuid::Uuid::parse_str(&data.captcha_uuid.clone().unwrap_or_default())?;
|
||||||
let uuid = uuid::Uuid::parse_str(captcha_uuid)?;
|
CaptchaAnswer::check_captcha(
|
||||||
let check = CaptchaAnswer::check_captcha(
|
&mut context.pool(),
|
||||||
&mut context.pool(),
|
CheckCaptchaAnswer {
|
||||||
CheckCaptchaAnswer {
|
uuid,
|
||||||
uuid,
|
answer: data.captcha_answer.clone().unwrap_or_default(),
|
||||||
answer: data.captcha_answer.clone().unwrap_or_default(),
|
},
|
||||||
},
|
)
|
||||||
)
|
.await?;
|
||||||
.await?;
|
|
||||||
if !check {
|
|
||||||
Err(LemmyErrorType::CaptchaIncorrect)?
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(LemmyErrorType::CaptchaIncorrect)?
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let slur_regex = local_site_to_slur_regex(&local_site);
|
let slur_regex = local_site_to_slur_regex(&local_site);
|
||||||
check_slurs(&data.username, &slur_regex)?;
|
check_slurs(&data.username, &slur_regex)?;
|
||||||
check_slurs_opt(&data.answer, &slur_regex)?;
|
check_slurs_opt(&data.answer, &slur_regex)?;
|
||||||
|
|
||||||
if Person::is_username_taken(&mut context.pool(), &data.username).await? {
|
Person::check_username_taken(&mut context.pool(), &data.username).await?;
|
||||||
return Err(LemmyErrorType::UsernameAlreadyExists)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(email) = &data.email {
|
if let Some(email) = &data.email {
|
||||||
if LocalUser::is_email_taken(&mut context.pool(), email).await? {
|
LocalUser::check_is_email_taken(&mut context.pool(), email).await?;
|
||||||
Err(LemmyErrorType::EmailAlreadyExists)?
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have to create both a person, and local_user
|
// We have to create both a person, and local_user
|
||||||
|
@ -338,9 +327,7 @@ pub async fn authenticate_with_oauth(
|
||||||
check_slurs(username, &slur_regex)?;
|
check_slurs(username, &slur_regex)?;
|
||||||
check_slurs_opt(&data.answer, &slur_regex)?;
|
check_slurs_opt(&data.answer, &slur_regex)?;
|
||||||
|
|
||||||
if Person::is_username_taken(&mut context.pool(), username).await? {
|
Person::check_username_taken(&mut context.pool(), username).await?;
|
||||||
return Err(LemmyErrorType::UsernameAlreadyExists)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We have to create a person, a local_user, and an oauth_account
|
// We have to create a person, a local_user, and an oauth_account
|
||||||
person = create_person(
|
person = create_person(
|
||||||
|
|
|
@ -213,15 +213,13 @@ async fn can_accept_activity_in_community(
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
) -> LemmyResult<()> {
|
) -> LemmyResult<()> {
|
||||||
if let Some(community) = community {
|
if let Some(community) = community {
|
||||||
if !community.local
|
|
||||||
&& !CommunityFollower::has_local_followers(&mut context.pool(), community.id).await?
|
|
||||||
{
|
|
||||||
Err(LemmyErrorType::CommunityHasNoFollowers)?
|
|
||||||
}
|
|
||||||
// Local only community can't federate
|
// Local only community can't federate
|
||||||
if community.visibility != CommunityVisibility::Public {
|
if community.visibility != CommunityVisibility::Public {
|
||||||
return Err(LemmyErrorType::NotFound.into());
|
return Err(LemmyErrorType::NotFound.into());
|
||||||
}
|
}
|
||||||
|
if !community.local {
|
||||||
|
CommunityFollower::check_has_local_followers(&mut context.pool(), community.id).await?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,12 +87,7 @@ pub(crate) async fn verify_person_in_community(
|
||||||
}
|
}
|
||||||
let person_id = person.id;
|
let person_id = person.id;
|
||||||
let community_id = community.id;
|
let community_id = community.id;
|
||||||
let is_banned = CommunityPersonBanView::get(&mut context.pool(), person_id, community_id).await?;
|
CommunityPersonBanView::check(&mut context.pool(), person_id, community_id).await
|
||||||
if is_banned {
|
|
||||||
Err(LemmyErrorType::PersonIsBannedFromCommunity)?
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify that mod action in community was performed by a moderator.
|
/// Verify that mod action in community was performed by a moderator.
|
||||||
|
@ -106,14 +101,6 @@ pub(crate) async fn verify_mod_action(
|
||||||
community: &Community,
|
community: &Community,
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
) -> LemmyResult<()> {
|
) -> LemmyResult<()> {
|
||||||
let mod_ = mod_id.dereference(context).await?;
|
|
||||||
|
|
||||||
let is_mod_or_admin =
|
|
||||||
CommunityView::is_mod_or_admin(&mut context.pool(), mod_.id, community.id).await?;
|
|
||||||
if is_mod_or_admin {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
// mod action comes from the same instance as the community, so it was presumably done
|
// mod action comes from the same instance as the community, so it was presumably done
|
||||||
// by an instance admin.
|
// by an instance admin.
|
||||||
// TODO: federate instance admin status and check it here
|
// TODO: federate instance admin status and check it here
|
||||||
|
@ -121,7 +108,8 @@ pub(crate) async fn verify_mod_action(
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(LemmyErrorType::NotAModerator)?
|
let mod_ = mod_id.dereference(context).await?;
|
||||||
|
CommunityView::check_is_mod_or_admin(&mut context.pool(), mod_.id, community.id).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn verify_is_public(to: &[Url], cc: &[Url]) -> LemmyResult<()> {
|
pub(crate) fn verify_is_public(to: &[Url], cc: &[Url]) -> LemmyResult<()> {
|
||||||
|
|
|
@ -123,7 +123,6 @@ impl InCommunity for AnnouncableActivities {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -12,10 +12,13 @@ use lemmy_api_common::{
|
||||||
utils::check_private_instance,
|
utils::check_private_instance,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{comment::Comment, community::Community, local_site::LocalSite},
|
source::{comment::Comment, community::Community},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::{comment_view::CommentQuery, structs::LocalUserView};
|
use lemmy_db_views::{
|
||||||
|
comment_view::CommentQuery,
|
||||||
|
structs::{LocalUserView, SiteView},
|
||||||
|
};
|
||||||
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult};
|
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
#[tracing::instrument(skip(context))]
|
||||||
|
@ -24,8 +27,8 @@ pub async fn list_comments(
|
||||||
context: Data<LemmyContext>,
|
context: Data<LemmyContext>,
|
||||||
local_user_view: Option<LocalUserView>,
|
local_user_view: Option<LocalUserView>,
|
||||||
) -> LemmyResult<Json<GetCommentsResponse>> {
|
) -> LemmyResult<Json<GetCommentsResponse>> {
|
||||||
let local_site = LocalSite::read(&mut context.pool()).await?;
|
let site_view = SiteView::read_local(&mut context.pool()).await?;
|
||||||
check_private_instance(&local_user_view, &local_site)?;
|
check_private_instance(&local_user_view, &site_view.local_site)?;
|
||||||
|
|
||||||
let community_id = if let Some(name) = &data.community_name {
|
let community_id = if let Some(name) = &data.community_name {
|
||||||
Some(
|
Some(
|
||||||
|
@ -40,7 +43,7 @@ pub async fn list_comments(
|
||||||
let sort = Some(comment_sort_type_with_default(
|
let sort = Some(comment_sort_type_with_default(
|
||||||
data.sort,
|
data.sort,
|
||||||
local_user_ref,
|
local_user_ref,
|
||||||
&local_site,
|
&site_view.local_site,
|
||||||
));
|
));
|
||||||
let max_depth = data.max_depth;
|
let max_depth = data.max_depth;
|
||||||
let saved_only = data.saved_only;
|
let saved_only = data.saved_only;
|
||||||
|
@ -58,7 +61,7 @@ pub async fn list_comments(
|
||||||
let listing_type = Some(listing_type_with_default(
|
let listing_type = Some(listing_type_with_default(
|
||||||
data.type_,
|
data.type_,
|
||||||
local_user_view.as_ref().map(|u| &u.local_user),
|
local_user_view.as_ref().map(|u| &u.local_user),
|
||||||
&local_site,
|
&site_view.local_site,
|
||||||
community_id,
|
community_id,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -88,7 +91,7 @@ pub async fn list_comments(
|
||||||
limit,
|
limit,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(&mut context.pool())
|
.list(&site_view.site, &mut context.pool())
|
||||||
.await
|
.await
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntGetComments)?;
|
.with_lemmy_type(LemmyErrorType::CouldntGetComments)?;
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,7 @@ pub async fn read_person(
|
||||||
creator_id,
|
creator_id,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(&mut context.pool())
|
.list(&local_site.site, &mut context.pool())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let moderates = CommunityModeratorView::for_person(
|
let moderates = CommunityModeratorView::for_person(
|
||||||
|
|
|
@ -47,7 +47,7 @@ pub async fn search(
|
||||||
listing_type,
|
listing_type,
|
||||||
page,
|
page,
|
||||||
limit,
|
limit,
|
||||||
post_title_only,
|
title_only,
|
||||||
post_url_only,
|
post_url_only,
|
||||||
saved_only,
|
saved_only,
|
||||||
liked_only,
|
liked_only,
|
||||||
|
@ -78,7 +78,7 @@ pub async fn search(
|
||||||
search_term: Some(q.clone()),
|
search_term: Some(q.clone()),
|
||||||
page,
|
page,
|
||||||
limit,
|
limit,
|
||||||
title_only: post_title_only,
|
title_only,
|
||||||
url_only: post_url_only,
|
url_only: post_url_only,
|
||||||
liked_only,
|
liked_only,
|
||||||
disliked_only,
|
disliked_only,
|
||||||
|
@ -105,6 +105,7 @@ pub async fn search(
|
||||||
sort,
|
sort,
|
||||||
listing_type,
|
listing_type,
|
||||||
search_term: Some(q.clone()),
|
search_term: Some(q.clone()),
|
||||||
|
title_only,
|
||||||
local_user,
|
local_user,
|
||||||
is_mod_or_admin: is_admin,
|
is_mod_or_admin: is_admin,
|
||||||
page,
|
page,
|
||||||
|
@ -127,7 +128,9 @@ pub async fn search(
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
SearchType::Comments => {
|
SearchType::Comments => {
|
||||||
comments = comment_query.list(&mut context.pool()).await?;
|
comments = comment_query
|
||||||
|
.list(&local_site.site, &mut context.pool())
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
SearchType::Communities => {
|
SearchType::Communities => {
|
||||||
communities = community_query
|
communities = community_query
|
||||||
|
@ -146,7 +149,9 @@ pub async fn search(
|
||||||
.list(&local_site.site, &mut context.pool())
|
.list(&local_site.site, &mut context.pool())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
comments = comment_query.list(&mut context.pool()).await?;
|
comments = comment_query
|
||||||
|
.list(&local_site.site, &mut context.pool())
|
||||||
|
.await?;
|
||||||
|
|
||||||
communities = if community_or_creator_included {
|
communities = if community_or_creator_included {
|
||||||
vec![]
|
vec![]
|
||||||
|
|
|
@ -127,7 +127,6 @@ pub async fn import_settings(
|
||||||
show_read_posts: data.settings.as_ref().map(|s| s.show_read_posts),
|
show_read_posts: data.settings.as_ref().map(|s| s.show_read_posts),
|
||||||
open_links_in_new_tab: data.settings.as_ref().map(|s| s.open_links_in_new_tab),
|
open_links_in_new_tab: data.settings.as_ref().map(|s| s.open_links_in_new_tab),
|
||||||
blur_nsfw: data.settings.as_ref().map(|s| s.blur_nsfw),
|
blur_nsfw: data.settings.as_ref().map(|s| s.blur_nsfw),
|
||||||
auto_expand: data.settings.as_ref().map(|s| s.auto_expand),
|
|
||||||
infinite_scroll_enabled: data.settings.as_ref().map(|s| s.infinite_scroll_enabled),
|
infinite_scroll_enabled: data.settings.as_ref().map(|s| s.infinite_scroll_enabled),
|
||||||
post_listing_mode: data.settings.as_ref().map(|s| s.post_listing_mode),
|
post_listing_mode: data.settings.as_ref().map(|s| s.post_listing_mode),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -308,8 +307,9 @@ where
|
||||||
});
|
});
|
||||||
Ok(failed_items.into_iter().join(","))
|
Ok(failed_items.into_iter().join(","))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
#[expect(clippy::indexing_slicing)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::api::user_settings_backup::{export_settings, import_settings, UserSettingsBackup};
|
use crate::api::user_settings_backup::{export_settings, import_settings, UserSettingsBackup};
|
||||||
|
|
|
@ -98,7 +98,7 @@ impl Collection for ApubCommunityModerators {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
#[expect(clippy::indexing_slicing)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -120,8 +120,7 @@ pub(crate) async fn get_apub_community_featured(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
pub(crate) mod tests {
|
pub(crate) mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -39,7 +39,7 @@ use lemmy_db_schema::{
|
||||||
};
|
};
|
||||||
use lemmy_db_views_actor::structs::CommunityModeratorView;
|
use lemmy_db_views_actor::structs::CommunityModeratorView;
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
error::{LemmyError, LemmyErrorType, LemmyResult},
|
error::{LemmyError, LemmyResult},
|
||||||
spawn_try_task,
|
spawn_try_task,
|
||||||
utils::{
|
utils::{
|
||||||
markdown::markdown_to_html,
|
markdown::markdown_to_html,
|
||||||
|
@ -180,15 +180,12 @@ impl Object for ApubPost {
|
||||||
let creator = page.creator()?.dereference(context).await?;
|
let creator = page.creator()?.dereference(context).await?;
|
||||||
let community = page.community(context).await?;
|
let community = page.community(context).await?;
|
||||||
if community.posting_restricted_to_mods {
|
if community.posting_restricted_to_mods {
|
||||||
let is_mod = CommunityModeratorView::is_community_moderator(
|
CommunityModeratorView::check_is_community_moderator(
|
||||||
&mut context.pool(),
|
&mut context.pool(),
|
||||||
community.id,
|
community.id,
|
||||||
creator.id,
|
creator.id,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
if !is_mod {
|
|
||||||
Err(LemmyErrorType::OnlyModsCanPostInCommunity)?
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let mut name = page
|
let mut name = page
|
||||||
.name
|
.name
|
||||||
|
|
|
@ -15,12 +15,13 @@ use activitypub_federation::{
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
utils::{check_person_block, get_url_blocklist, local_site_opt_to_slur_regex, process_markdown},
|
utils::{get_url_blocklist, local_site_opt_to_slur_regex, process_markdown},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
local_site::LocalSite,
|
local_site::LocalSite,
|
||||||
person::Person,
|
person::Person,
|
||||||
|
person_block::PersonBlock,
|
||||||
private_message::{PrivateMessage, PrivateMessageInsertForm},
|
private_message::{PrivateMessage, PrivateMessageInsertForm},
|
||||||
},
|
},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
|
@ -126,7 +127,7 @@ impl Object for ApubPrivateMessage {
|
||||||
) -> LemmyResult<ApubPrivateMessage> {
|
) -> LemmyResult<ApubPrivateMessage> {
|
||||||
let creator = note.attributed_to.dereference(context).await?;
|
let creator = note.attributed_to.dereference(context).await?;
|
||||||
let recipient = note.to[0].dereference(context).await?;
|
let recipient = note.to[0].dereference(context).await?;
|
||||||
check_person_block(creator.id, recipient.id, &mut context.pool()).await?;
|
PersonBlock::read(&mut context.pool(), recipient.id, creator.id).await?;
|
||||||
|
|
||||||
let local_site = LocalSite::read(&mut context.pool()).await.ok();
|
let local_site = LocalSite::read(&mut context.pool()).await.ok();
|
||||||
let slur_regex = &local_site_opt_to_slur_regex(&local_site);
|
let slur_regex = &local_site_opt_to_slur_regex(&local_site);
|
||||||
|
|
|
@ -75,7 +75,7 @@ impl<S: ValidGrouping<(), IsAggregate = is_aggregate::No>> ValidGrouping<()>
|
||||||
type IsAggregate = is_aggregate::No;
|
type IsAggregate = is_aggregate::No;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
#[expect(non_camel_case_types)]
|
||||||
#[derive(QueryId, Clone, Copy, Debug)]
|
#[derive(QueryId, Clone, Copy, Debug)]
|
||||||
pub struct current_value;
|
pub struct current_value;
|
||||||
|
|
||||||
|
|
|
@ -30,8 +30,7 @@ impl CommentAggregates {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -36,8 +36,7 @@ impl CommunityAggregates {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -20,8 +20,7 @@ impl PersonAggregates {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -49,8 +49,8 @@ impl PostAggregates {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -15,8 +15,8 @@ impl SiteAggregates {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -58,8 +58,7 @@ impl ReceivedActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -392,8 +392,8 @@ async fn convert_read_languages(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
#[expect(clippy::indexing_slicing)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -13,6 +13,7 @@ use diesel::{
|
||||||
QueryDsl,
|
QueryDsl,
|
||||||
};
|
};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
|
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
||||||
|
|
||||||
impl CaptchaAnswer {
|
impl CaptchaAnswer {
|
||||||
pub async fn insert(pool: &mut DbPool<'_>, captcha: &CaptchaAnswerForm) -> Result<Self, Error> {
|
pub async fn insert(pool: &mut DbPool<'_>, captcha: &CaptchaAnswerForm) -> Result<Self, Error> {
|
||||||
|
@ -27,7 +28,7 @@ impl CaptchaAnswer {
|
||||||
pub async fn check_captcha(
|
pub async fn check_captcha(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
to_check: CheckCaptchaAnswer,
|
to_check: CheckCaptchaAnswer,
|
||||||
) -> Result<bool, Error> {
|
) -> LemmyResult<()> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
|
||||||
// fetch requested captcha
|
// fetch requested captcha
|
||||||
|
@ -43,13 +44,13 @@ impl CaptchaAnswer {
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(captcha_exists)
|
captcha_exists
|
||||||
|
.then_some(())
|
||||||
|
.ok_or(LemmyErrorType::CaptchaIncorrect.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -83,7 +84,6 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
assert!(result.unwrap());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
@ -119,7 +119,6 @@ mod tests {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
assert!(result_repeat.is_ok());
|
assert!(result_repeat.is_err());
|
||||||
assert!(!result_repeat.unwrap());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -196,8 +196,7 @@ impl Saveable for CommentSaved {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -35,8 +35,7 @@ use crate::{
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use diesel::{
|
use diesel::{
|
||||||
deserialize,
|
deserialize,
|
||||||
dsl,
|
dsl::{self, exists, insert_into},
|
||||||
dsl::{exists, insert_into},
|
|
||||||
pg::Pg,
|
pg::Pg,
|
||||||
result::Error,
|
result::Error,
|
||||||
select,
|
select,
|
||||||
|
@ -320,16 +319,18 @@ impl CommunityFollower {
|
||||||
|
|
||||||
/// Check if a remote instance has any followers on local instance. For this it is enough to check
|
/// Check if a remote instance has any followers on local instance. For this it is enough to check
|
||||||
/// if any follow relation is stored. Dont use this for local community.
|
/// if any follow relation is stored. Dont use this for local community.
|
||||||
pub async fn has_local_followers(
|
pub async fn check_has_local_followers(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
remote_community_id: CommunityId,
|
remote_community_id: CommunityId,
|
||||||
) -> Result<bool, Error> {
|
) -> LemmyResult<()> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
select(exists(community_follower::table.filter(
|
select(exists(community_follower::table.filter(
|
||||||
community_follower::community_id.eq(remote_community_id),
|
community_follower::community_id.eq(remote_community_id),
|
||||||
)))
|
)))
|
||||||
.get_result(conn)
|
.get_result::<bool>(conn)
|
||||||
.await
|
.await?
|
||||||
|
.then_some(())
|
||||||
|
.ok_or(LemmyErrorType::CommunityHasNoFollowers.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,7 +431,6 @@ impl ApubActor for Community {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
source::{
|
source::{
|
||||||
|
|
|
@ -9,26 +9,29 @@ use crate::{
|
||||||
utils::{get_conn, DbPool},
|
utils::{get_conn, DbPool},
|
||||||
};
|
};
|
||||||
use diesel::{
|
use diesel::{
|
||||||
dsl::{exists, insert_into},
|
dsl::{exists, insert_into, not},
|
||||||
result::Error,
|
result::Error,
|
||||||
select,
|
select,
|
||||||
ExpressionMethods,
|
ExpressionMethods,
|
||||||
QueryDsl,
|
QueryDsl,
|
||||||
};
|
};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
|
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
||||||
|
|
||||||
impl CommunityBlock {
|
impl CommunityBlock {
|
||||||
pub async fn read(
|
pub async fn read(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
for_person_id: PersonId,
|
for_person_id: PersonId,
|
||||||
for_community_id: CommunityId,
|
for_community_id: CommunityId,
|
||||||
) -> Result<bool, Error> {
|
) -> LemmyResult<()> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
select(exists(
|
select(not(exists(
|
||||||
community_block::table.find((for_person_id, for_community_id)),
|
community_block::table.find((for_person_id, for_community_id)),
|
||||||
))
|
)))
|
||||||
.get_result(conn)
|
.get_result::<bool>(conn)
|
||||||
.await
|
.await?
|
||||||
|
.then_some(())
|
||||||
|
.ok_or(LemmyErrorType::CommunityIsBlocked.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn for_person(
|
pub async fn for_person(
|
||||||
|
|
|
@ -48,8 +48,7 @@ impl FederationAllowList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -9,26 +9,29 @@ use crate::{
|
||||||
utils::{get_conn, DbPool},
|
utils::{get_conn, DbPool},
|
||||||
};
|
};
|
||||||
use diesel::{
|
use diesel::{
|
||||||
dsl::{exists, insert_into},
|
dsl::{exists, insert_into, not},
|
||||||
result::Error,
|
result::Error,
|
||||||
select,
|
select,
|
||||||
ExpressionMethods,
|
ExpressionMethods,
|
||||||
QueryDsl,
|
QueryDsl,
|
||||||
};
|
};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
|
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
||||||
|
|
||||||
impl InstanceBlock {
|
impl InstanceBlock {
|
||||||
pub async fn read(
|
pub async fn read(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
for_person_id: PersonId,
|
for_person_id: PersonId,
|
||||||
for_instance_id: InstanceId,
|
for_instance_id: InstanceId,
|
||||||
) -> Result<bool, Error> {
|
) -> LemmyResult<()> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
select(exists(
|
select(not(exists(
|
||||||
instance_block::table.find((for_person_id, for_instance_id)),
|
instance_block::table.find((for_person_id, for_instance_id)),
|
||||||
))
|
)))
|
||||||
.get_result(conn)
|
.get_result::<bool>(conn)
|
||||||
.await
|
.await?
|
||||||
|
.then_some(())
|
||||||
|
.ok_or(LemmyErrorType::InstanceIsBlocked.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn for_person(
|
pub async fn for_person(
|
||||||
|
|
|
@ -41,8 +41,8 @@ impl Language {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
#[expect(clippy::indexing_slicing)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{source::language::Language, utils::build_db_pool_for_tests};
|
use crate::{source::language::Language, utils::build_db_pool_for_tests};
|
||||||
|
|
|
@ -136,14 +136,16 @@ impl LocalUser {
|
||||||
diesel::delete(persons).execute(conn).await
|
diesel::delete(persons).execute(conn).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn is_email_taken(pool: &mut DbPool<'_>, email: &str) -> Result<bool, Error> {
|
pub async fn check_is_email_taken(pool: &mut DbPool<'_>, email: &str) -> LemmyResult<()> {
|
||||||
use diesel::dsl::{exists, select};
|
use diesel::dsl::{exists, select};
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
select(exists(local_user::table.filter(
|
select(not(exists(local_user::table.filter(
|
||||||
lower(coalesce(local_user::email, "")).eq(email.to_lowercase()),
|
lower(coalesce(local_user::email, "")).eq(email.to_lowercase()),
|
||||||
)))
|
))))
|
||||||
.get_result(conn)
|
.get_result::<bool>(conn)
|
||||||
.await
|
.await?
|
||||||
|
.then_some(())
|
||||||
|
.ok_or(LemmyErrorType::EmailAlreadyExists.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: maybe move this and pass in LocalUserView
|
// TODO: maybe move this and pass in LocalUserView
|
||||||
|
@ -367,7 +369,6 @@ pub struct UserBackupLists {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
source::{
|
source::{
|
||||||
|
@ -419,4 +420,32 @@ mod tests {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[serial]
|
||||||
|
async fn test_email_taken() -> LemmyResult<()> {
|
||||||
|
let pool = &build_db_pool_for_tests().await;
|
||||||
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
|
let darwin_email = "charles.darwin@gmail.com";
|
||||||
|
|
||||||
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
|
|
||||||
|
let darwin_person = PersonInsertForm::test_form(inserted_instance.id, "darwin");
|
||||||
|
let inserted_darwin_person = Person::create(pool, &darwin_person).await?;
|
||||||
|
|
||||||
|
let mut darwin_local_user_form =
|
||||||
|
LocalUserInsertForm::test_form_admin(inserted_darwin_person.id);
|
||||||
|
darwin_local_user_form.email = Some(darwin_email.into());
|
||||||
|
let _inserted_darwin_local_user =
|
||||||
|
LocalUser::create(pool, &darwin_local_user_form, vec![]).await?;
|
||||||
|
|
||||||
|
let check = LocalUser::check_is_email_taken(pool, darwin_email).await;
|
||||||
|
assert!(check.is_err());
|
||||||
|
|
||||||
|
let passed_check = LocalUser::check_is_email_taken(pool, "not_charles@gmail.com").await;
|
||||||
|
assert!(passed_check.is_ok());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use diesel::{delete, dsl::exists, insert_into, result::Error, select};
|
use diesel::{delete, dsl::exists, insert_into, result::Error, select};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
|
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
||||||
|
|
||||||
impl LoginToken {
|
impl LoginToken {
|
||||||
pub async fn create(pool: &mut DbPool<'_>, form: LoginTokenCreateForm) -> Result<Self, Error> {
|
pub async fn create(pool: &mut DbPool<'_>, form: LoginTokenCreateForm) -> Result<Self, Error> {
|
||||||
|
@ -22,13 +23,15 @@ impl LoginToken {
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
user_id_: LocalUserId,
|
user_id_: LocalUserId,
|
||||||
token_: &str,
|
token_: &str,
|
||||||
) -> Result<bool, Error> {
|
) -> LemmyResult<()> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
select(exists(
|
select(exists(
|
||||||
login_token.find(token_).filter(user_id.eq(user_id_)),
|
login_token.find(token_).filter(user_id.eq(user_id_)),
|
||||||
))
|
))
|
||||||
.get_result(conn)
|
.get_result::<bool>(conn)
|
||||||
.await
|
.await?
|
||||||
|
.then_some(())
|
||||||
|
.ok_or(LemmyErrorType::NotLoggedIn.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn list(
|
pub async fn list(
|
||||||
|
|
|
@ -465,8 +465,7 @@ impl Crud for AdminPurgeComment {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -1,32 +1,13 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
newtypes::{LocalUserId, OAuthProviderId},
|
newtypes::LocalUserId,
|
||||||
schema::{oauth_account, oauth_account::dsl::local_user_id},
|
schema::{oauth_account, oauth_account::dsl::local_user_id},
|
||||||
source::oauth_account::{OAuthAccount, OAuthAccountInsertForm},
|
source::oauth_account::{OAuthAccount, OAuthAccountInsertForm},
|
||||||
utils::{get_conn, DbPool},
|
utils::{get_conn, DbPool},
|
||||||
};
|
};
|
||||||
use diesel::{
|
use diesel::{insert_into, result::Error, ExpressionMethods, QueryDsl};
|
||||||
dsl::{exists, insert_into},
|
|
||||||
result::Error,
|
|
||||||
select,
|
|
||||||
ExpressionMethods,
|
|
||||||
QueryDsl,
|
|
||||||
};
|
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
|
|
||||||
impl OAuthAccount {
|
impl OAuthAccount {
|
||||||
pub async fn read(
|
|
||||||
pool: &mut DbPool<'_>,
|
|
||||||
for_oauth_provider_id: OAuthProviderId,
|
|
||||||
for_local_user_id: LocalUserId,
|
|
||||||
) -> Result<bool, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
select(exists(
|
|
||||||
oauth_account::table.find((for_oauth_provider_id, for_local_user_id)),
|
|
||||||
))
|
|
||||||
.get_result(conn)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn create(pool: &mut DbPool<'_>, form: &OAuthAccountInsertForm) -> Result<Self, Error> {
|
pub async fn create(pool: &mut DbPool<'_>, form: &OAuthAccountInsertForm) -> Result<Self, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
insert_into(oauth_account::table)
|
insert_into(oauth_account::table)
|
||||||
|
@ -35,17 +16,6 @@ impl OAuthAccount {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete(
|
|
||||||
pool: &mut DbPool<'_>,
|
|
||||||
for_oauth_provider_id: OAuthProviderId,
|
|
||||||
for_local_user_id: LocalUserId,
|
|
||||||
) -> Result<usize, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
diesel::delete(oauth_account::table.find((for_oauth_provider_id, for_local_user_id)))
|
|
||||||
.execute(conn)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn delete_user_accounts(
|
pub async fn delete_user_accounts(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
for_local_user_id: LocalUserId,
|
for_local_user_id: LocalUserId,
|
||||||
|
|
|
@ -42,8 +42,6 @@ impl PasswordResetRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -21,6 +21,7 @@ use diesel::{
|
||||||
QueryDsl,
|
QueryDsl,
|
||||||
};
|
};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
|
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Crud for Person {
|
impl Crud for Person {
|
||||||
|
@ -121,16 +122,18 @@ impl Person {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn is_username_taken(pool: &mut DbPool<'_>, username: &str) -> Result<bool, Error> {
|
pub async fn check_username_taken(pool: &mut DbPool<'_>, username: &str) -> LemmyResult<()> {
|
||||||
use diesel::dsl::{exists, select};
|
use diesel::dsl::{exists, select};
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
select(exists(
|
select(not(exists(
|
||||||
person::table
|
person::table
|
||||||
.filter(lower(person::name).eq(username.to_lowercase()))
|
.filter(lower(person::name).eq(username.to_lowercase()))
|
||||||
.filter(person::local.eq(true)),
|
.filter(person::local.eq(true)),
|
||||||
))
|
)))
|
||||||
.get_result(conn)
|
.get_result::<bool>(conn)
|
||||||
.await
|
.await?
|
||||||
|
.then_some(())
|
||||||
|
.ok_or(LemmyErrorType::UsernameAlreadyExists.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,7 +235,6 @@ impl PersonFollower {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
||||||
utils::{get_conn, DbPool},
|
utils::{get_conn, DbPool},
|
||||||
};
|
};
|
||||||
use diesel::{
|
use diesel::{
|
||||||
dsl::{exists, insert_into},
|
dsl::{exists, insert_into, not},
|
||||||
result::Error,
|
result::Error,
|
||||||
select,
|
select,
|
||||||
ExpressionMethods,
|
ExpressionMethods,
|
||||||
|
@ -17,19 +17,22 @@ use diesel::{
|
||||||
QueryDsl,
|
QueryDsl,
|
||||||
};
|
};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
|
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
||||||
|
|
||||||
impl PersonBlock {
|
impl PersonBlock {
|
||||||
pub async fn read(
|
pub async fn read(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
for_person_id: PersonId,
|
for_person_id: PersonId,
|
||||||
for_recipient_id: PersonId,
|
for_recipient_id: PersonId,
|
||||||
) -> Result<bool, Error> {
|
) -> LemmyResult<()> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
select(exists(
|
select(not(exists(
|
||||||
person_block::table.find((for_person_id, for_recipient_id)),
|
person_block::table.find((for_person_id, for_recipient_id)),
|
||||||
))
|
)))
|
||||||
.get_result(conn)
|
.get_result::<bool>(conn)
|
||||||
.await
|
.await?
|
||||||
|
.then_some(())
|
||||||
|
.ok_or(LemmyErrorType::PersonIsBlocked.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn for_person(
|
pub async fn for_person(
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
diesel::OptionalExtension,
|
diesel::{BoolExpressionMethods, OptionalExtension},
|
||||||
newtypes::{CommunityId, DbUrl, PersonId, PostId},
|
newtypes::{CommunityId, DbUrl, PersonId, PostId},
|
||||||
schema::{post, post_hide, post_like, post_read, post_saved},
|
schema::{community, person, post, post_hide, post_like, post_read, post_saved},
|
||||||
source::post::{
|
source::post::{
|
||||||
Post,
|
Post,
|
||||||
PostHide,
|
PostHide,
|
||||||
|
@ -20,6 +20,7 @@ use crate::{
|
||||||
functions::coalesce,
|
functions::coalesce,
|
||||||
get_conn,
|
get_conn,
|
||||||
naive_now,
|
naive_now,
|
||||||
|
now,
|
||||||
DbPool,
|
DbPool,
|
||||||
DELETED_REPLACEMENT_TEXT,
|
DELETED_REPLACEMENT_TEXT,
|
||||||
FETCH_LIMIT_MAX,
|
FETCH_LIMIT_MAX,
|
||||||
|
@ -30,7 +31,7 @@ use crate::{
|
||||||
use ::url::Url;
|
use ::url::Url;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use diesel::{
|
use diesel::{
|
||||||
dsl::insert_into,
|
dsl::{count, insert_into, not},
|
||||||
result::Error,
|
result::Error,
|
||||||
DecoratableTarget,
|
DecoratableTarget,
|
||||||
ExpressionMethods,
|
ExpressionMethods,
|
||||||
|
@ -172,6 +173,7 @@ impl Post {
|
||||||
let object_id: DbUrl = object_id.into();
|
let object_id: DbUrl = object_id.into();
|
||||||
post::table
|
post::table
|
||||||
.filter(post::ap_id.eq(object_id))
|
.filter(post::ap_id.eq(object_id))
|
||||||
|
.filter(post::scheduled_publish_time.is_null())
|
||||||
.first(conn)
|
.first(conn)
|
||||||
.await
|
.await
|
||||||
.optional()
|
.optional()
|
||||||
|
@ -245,6 +247,28 @@ impl Post {
|
||||||
.get_results::<Self>(conn)
|
.get_results::<Self>(conn)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn user_scheduled_post_count(
|
||||||
|
person_id: PersonId,
|
||||||
|
pool: &mut DbPool<'_>,
|
||||||
|
) -> Result<i64, Error> {
|
||||||
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
|
||||||
|
post::table
|
||||||
|
.inner_join(person::table)
|
||||||
|
.inner_join(community::table)
|
||||||
|
// find all posts which have scheduled_publish_time that is in the past
|
||||||
|
.filter(post::scheduled_publish_time.is_not_null())
|
||||||
|
.filter(coalesce(post::scheduled_publish_time, now()).lt(now()))
|
||||||
|
// make sure the post and community are still around
|
||||||
|
.filter(not(post::deleted.or(post::removed)))
|
||||||
|
.filter(not(community::removed.or(community::deleted)))
|
||||||
|
// only posts by specified user
|
||||||
|
.filter(post::creator_id.eq(person_id))
|
||||||
|
.select(count(post::id))
|
||||||
|
.first::<i64>(conn)
|
||||||
|
.await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
@ -444,6 +468,7 @@ mod tests {
|
||||||
featured_community: false,
|
featured_community: false,
|
||||||
featured_local: false,
|
featured_local: false,
|
||||||
url_content_type: None,
|
url_content_type: None,
|
||||||
|
scheduled_publish_time: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Post Like
|
// Post Like
|
||||||
|
|
|
@ -80,8 +80,7 @@ impl Reportable for PostReport {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -85,8 +85,7 @@ impl PrivateMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -27,7 +27,6 @@ pub mod newtypes;
|
||||||
pub mod sensitive;
|
pub mod sensitive;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
#[allow(clippy::wildcard_imports)]
|
|
||||||
pub mod schema;
|
pub mod schema;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
pub mod aliases {
|
pub mod aliases {
|
||||||
|
|
|
@ -191,13 +191,13 @@ impl Display for DbUrl {
|
||||||
}
|
}
|
||||||
|
|
||||||
// the project doesn't compile with From
|
// the project doesn't compile with From
|
||||||
#[allow(clippy::from_over_into)]
|
#[expect(clippy::from_over_into)]
|
||||||
impl Into<DbUrl> for Url {
|
impl Into<DbUrl> for Url {
|
||||||
fn into(self) -> DbUrl {
|
fn into(self) -> DbUrl {
|
||||||
DbUrl(Box::new(self))
|
DbUrl(Box::new(self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[allow(clippy::from_over_into)]
|
#[expect(clippy::from_over_into)]
|
||||||
impl Into<Url> for DbUrl {
|
impl Into<Url> for DbUrl {
|
||||||
fn into(self) -> Url {
|
fn into(self) -> Url {
|
||||||
*self.0
|
*self.0
|
||||||
|
|
|
@ -459,7 +459,6 @@ diesel::table! {
|
||||||
totp_2fa_secret -> Nullable<Text>,
|
totp_2fa_secret -> Nullable<Text>,
|
||||||
open_links_in_new_tab -> Bool,
|
open_links_in_new_tab -> Bool,
|
||||||
blur_nsfw -> Bool,
|
blur_nsfw -> Bool,
|
||||||
auto_expand -> Bool,
|
|
||||||
infinite_scroll_enabled -> Bool,
|
infinite_scroll_enabled -> Bool,
|
||||||
admin -> Bool,
|
admin -> Bool,
|
||||||
post_listing_mode -> PostListingModeEnum,
|
post_listing_mode -> PostListingModeEnum,
|
||||||
|
@ -770,6 +769,7 @@ diesel::table! {
|
||||||
featured_local -> Bool,
|
featured_local -> Bool,
|
||||||
url_content_type -> Nullable<Text>,
|
url_content_type -> Nullable<Text>,
|
||||||
alt_text -> Nullable<Text>,
|
alt_text -> Nullable<Text>,
|
||||||
|
scheduled_publish_time -> Nullable<Timestamptz>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,6 @@ pub struct LocalUser {
|
||||||
/// Open links in a new tab.
|
/// Open links in a new tab.
|
||||||
pub open_links_in_new_tab: bool,
|
pub open_links_in_new_tab: bool,
|
||||||
pub blur_nsfw: bool,
|
pub blur_nsfw: bool,
|
||||||
pub auto_expand: bool,
|
|
||||||
/// Whether infinite scroll is enabled.
|
/// Whether infinite scroll is enabled.
|
||||||
pub infinite_scroll_enabled: bool,
|
pub infinite_scroll_enabled: bool,
|
||||||
/// Whether the person is an admin.
|
/// Whether the person is an admin.
|
||||||
|
@ -104,8 +103,6 @@ pub struct LocalUserInsertForm {
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
pub blur_nsfw: Option<bool>,
|
pub blur_nsfw: Option<bool>,
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
pub auto_expand: Option<bool>,
|
|
||||||
#[new(default)]
|
|
||||||
pub infinite_scroll_enabled: Option<bool>,
|
pub infinite_scroll_enabled: Option<bool>,
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
pub admin: Option<bool>,
|
pub admin: Option<bool>,
|
||||||
|
@ -143,7 +140,6 @@ pub struct LocalUserUpdateForm {
|
||||||
pub totp_2fa_secret: Option<Option<String>>,
|
pub totp_2fa_secret: Option<Option<String>>,
|
||||||
pub open_links_in_new_tab: Option<bool>,
|
pub open_links_in_new_tab: Option<bool>,
|
||||||
pub blur_nsfw: Option<bool>,
|
pub blur_nsfw: Option<bool>,
|
||||||
pub auto_expand: Option<bool>,
|
|
||||||
pub infinite_scroll_enabled: Option<bool>,
|
pub infinite_scroll_enabled: Option<bool>,
|
||||||
pub admin: Option<bool>,
|
pub admin: Option<bool>,
|
||||||
pub post_listing_mode: Option<PostListingMode>,
|
pub post_listing_mode: Option<PostListingMode>,
|
||||||
|
|
|
@ -87,39 +87,30 @@ impl Serialize for PublicOAuthProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset, TS))]
|
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = oauth_provider))]
|
#[cfg_attr(feature = "full", diesel(table_name = oauth_provider))]
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
|
||||||
pub struct OAuthProviderInsertForm {
|
pub struct OAuthProviderInsertForm {
|
||||||
pub display_name: String,
|
pub display_name: String,
|
||||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
|
||||||
pub issuer: DbUrl,
|
pub issuer: DbUrl,
|
||||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
|
||||||
pub authorization_endpoint: DbUrl,
|
pub authorization_endpoint: DbUrl,
|
||||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
|
||||||
pub token_endpoint: DbUrl,
|
pub token_endpoint: DbUrl,
|
||||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
|
||||||
pub userinfo_endpoint: DbUrl,
|
pub userinfo_endpoint: DbUrl,
|
||||||
pub id_claim: String,
|
pub id_claim: String,
|
||||||
pub client_id: String,
|
pub client_id: String,
|
||||||
pub client_secret: String,
|
pub client_secret: String,
|
||||||
pub scopes: String,
|
pub scopes: String,
|
||||||
pub auto_verify_email: bool,
|
pub auto_verify_email: Option<bool>,
|
||||||
pub account_linking_enabled: bool,
|
pub account_linking_enabled: Option<bool>,
|
||||||
pub enabled: bool,
|
pub enabled: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset, TS))]
|
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = oauth_provider))]
|
#[cfg_attr(feature = "full", diesel(table_name = oauth_provider))]
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
|
||||||
pub struct OAuthProviderUpdateForm {
|
pub struct OAuthProviderUpdateForm {
|
||||||
pub display_name: Option<String>,
|
pub display_name: Option<String>,
|
||||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
|
||||||
pub authorization_endpoint: Option<DbUrl>,
|
pub authorization_endpoint: Option<DbUrl>,
|
||||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
|
||||||
pub token_endpoint: Option<DbUrl>,
|
pub token_endpoint: Option<DbUrl>,
|
||||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
|
||||||
pub userinfo_endpoint: Option<DbUrl>,
|
pub userinfo_endpoint: Option<DbUrl>,
|
||||||
pub id_claim: Option<String>,
|
pub id_claim: Option<String>,
|
||||||
pub client_secret: Option<String>,
|
pub client_secret: Option<String>,
|
||||||
|
|
|
@ -57,6 +57,8 @@ pub struct Post {
|
||||||
pub url_content_type: Option<String>,
|
pub url_content_type: Option<String>,
|
||||||
/// An optional alt_text, usable for image posts.
|
/// An optional alt_text, usable for image posts.
|
||||||
pub alt_text: Option<String>,
|
pub alt_text: Option<String>,
|
||||||
|
/// Time at which the post will be published. None means publish immediately.
|
||||||
|
pub scheduled_publish_time: Option<DateTime<Utc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, derive_new::new)]
|
#[derive(Debug, Clone, derive_new::new)]
|
||||||
|
@ -104,6 +106,8 @@ pub struct PostInsertForm {
|
||||||
pub url_content_type: Option<String>,
|
pub url_content_type: Option<String>,
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
pub alt_text: Option<String>,
|
pub alt_text: Option<String>,
|
||||||
|
#[new(default)]
|
||||||
|
pub scheduled_publish_time: Option<DateTime<Utc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
|
@ -130,6 +134,7 @@ pub struct PostUpdateForm {
|
||||||
pub featured_local: Option<bool>,
|
pub featured_local: Option<bool>,
|
||||||
pub url_content_type: Option<Option<String>>,
|
pub url_content_type: Option<Option<String>>,
|
||||||
pub alt_text: Option<Option<String>>,
|
pub alt_text: Option<Option<String>>,
|
||||||
|
pub scheduled_publish_time: Option<Option<DateTime<Utc>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
|
|
|
@ -595,7 +595,6 @@ impl<RF, LF> Queries<RF, LF> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -259,8 +259,8 @@ impl CommentReportQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
#[expect(clippy::indexing_slicing)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -35,7 +35,7 @@ use lemmy_db_schema::{
|
||||||
person_block,
|
person_block,
|
||||||
post,
|
post,
|
||||||
},
|
},
|
||||||
source::local_user::LocalUser,
|
source::{local_user::LocalUser, site::Site},
|
||||||
utils::{fuzzy_search, limit_and_offset, DbConn, DbPool, ListFn, Queries, ReadFn},
|
utils::{fuzzy_search, limit_and_offset, DbConn, DbPool, ListFn, Queries, ReadFn},
|
||||||
CommentSortType,
|
CommentSortType,
|
||||||
ListingType,
|
ListingType,
|
||||||
|
@ -43,7 +43,7 @@ use lemmy_db_schema::{
|
||||||
|
|
||||||
fn queries<'a>() -> Queries<
|
fn queries<'a>() -> Queries<
|
||||||
impl ReadFn<'a, CommentView, (CommentId, Option<&'a LocalUser>)>,
|
impl ReadFn<'a, CommentView, (CommentId, Option<&'a LocalUser>)>,
|
||||||
impl ListFn<'a, CommentView, CommentQuery<'a>>,
|
impl ListFn<'a, CommentView, (CommentQuery<'a>, &'a Site)>,
|
||||||
> {
|
> {
|
||||||
let is_creator_banned_from_community = exists(
|
let is_creator_banned_from_community = exists(
|
||||||
community_person_ban::table.filter(
|
community_person_ban::table.filter(
|
||||||
|
@ -182,7 +182,7 @@ fn queries<'a>() -> Queries<
|
||||||
query.first(&mut conn).await
|
query.first(&mut conn).await
|
||||||
};
|
};
|
||||||
|
|
||||||
let list = move |mut conn: DbConn<'a>, options: CommentQuery<'a>| async move {
|
let list = move |mut conn: DbConn<'a>, (options, site): (CommentQuery<'a>, &'a Site)| async move {
|
||||||
// The left join below will return None in this case
|
// The left join below will return None in this case
|
||||||
let person_id_join = options.local_user.person_id().unwrap_or(PersonId(-1));
|
let person_id_join = options.local_user.person_id().unwrap_or(PersonId(-1));
|
||||||
let local_user_id_join = options
|
let local_user_id_join = options
|
||||||
|
@ -295,6 +295,12 @@ fn queries<'a>() -> Queries<
|
||||||
query = query.filter(not(is_creator_blocked(person_id_join)));
|
query = query.filter(not(is_creator_blocked(person_id_join)));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if !options.local_user.show_nsfw(site) {
|
||||||
|
query = query
|
||||||
|
.filter(post::nsfw.eq(false))
|
||||||
|
.filter(community::nsfw.eq(false));
|
||||||
|
};
|
||||||
|
|
||||||
query = options.local_user.visible_communities_only(query);
|
query = options.local_user.visible_communities_only(query);
|
||||||
|
|
||||||
// A Max depth given means its a tree fetch
|
// A Max depth given means its a tree fetch
|
||||||
|
@ -398,10 +404,10 @@ pub struct CommentQuery<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CommentQuery<'a> {
|
impl<'a> CommentQuery<'a> {
|
||||||
pub async fn list(self, pool: &mut DbPool<'_>) -> Result<Vec<CommentView>, Error> {
|
pub async fn list(self, site: &Site, pool: &mut DbPool<'_>) -> Result<Vec<CommentView>, Error> {
|
||||||
Ok(
|
Ok(
|
||||||
queries()
|
queries()
|
||||||
.list(pool, self)
|
.list(pool, (self, site))
|
||||||
.await?
|
.await?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|mut c| {
|
.map(|mut c| {
|
||||||
|
@ -416,8 +422,8 @@ impl<'a> CommentQuery<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
#[expect(clippy::indexing_slicing)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -455,7 +461,8 @@ mod tests {
|
||||||
local_user_vote_display_mode::LocalUserVoteDisplayMode,
|
local_user_vote_display_mode::LocalUserVoteDisplayMode,
|
||||||
person::{Person, PersonInsertForm},
|
person::{Person, PersonInsertForm},
|
||||||
person_block::{PersonBlock, PersonBlockForm},
|
person_block::{PersonBlock, PersonBlockForm},
|
||||||
post::{Post, PostInsertForm},
|
post::{Post, PostInsertForm, PostUpdateForm},
|
||||||
|
site::{Site, SiteInsertForm},
|
||||||
},
|
},
|
||||||
traits::{Bannable, Blockable, Crud, Joinable, Likeable, Saveable},
|
traits::{Bannable, Blockable, Crud, Joinable, Likeable, Saveable},
|
||||||
utils::{build_db_pool_for_tests, RANK_DEFAULT},
|
utils::{build_db_pool_for_tests, RANK_DEFAULT},
|
||||||
|
@ -475,6 +482,7 @@ mod tests {
|
||||||
timmy_local_user_view: LocalUserView,
|
timmy_local_user_view: LocalUserView,
|
||||||
inserted_sara_person: Person,
|
inserted_sara_person: Person,
|
||||||
inserted_community: Community,
|
inserted_community: Community,
|
||||||
|
site: Site,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn init_data(pool: &mut DbPool<'_>) -> LemmyResult<Data> {
|
async fn init_data(pool: &mut DbPool<'_>) -> LemmyResult<Data> {
|
||||||
|
@ -611,6 +619,8 @@ mod tests {
|
||||||
person: inserted_timmy_person.clone(),
|
person: inserted_timmy_person.clone(),
|
||||||
counts: Default::default(),
|
counts: Default::default(),
|
||||||
};
|
};
|
||||||
|
let site_form = SiteInsertForm::new("test site".to_string(), inserted_instance.id);
|
||||||
|
let site = Site::create(pool, &site_form).await?;
|
||||||
Ok(Data {
|
Ok(Data {
|
||||||
inserted_instance,
|
inserted_instance,
|
||||||
inserted_comment_0,
|
inserted_comment_0,
|
||||||
|
@ -620,6 +630,7 @@ mod tests {
|
||||||
timmy_local_user_view,
|
timmy_local_user_view,
|
||||||
inserted_sara_person,
|
inserted_sara_person,
|
||||||
inserted_community,
|
inserted_community,
|
||||||
|
site,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -640,7 +651,7 @@ mod tests {
|
||||||
post_id: (Some(data.inserted_post.id)),
|
post_id: (Some(data.inserted_post.id)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(&data.site, pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -654,7 +665,7 @@ mod tests {
|
||||||
local_user: (Some(&data.timmy_local_user_view.local_user)),
|
local_user: (Some(&data.timmy_local_user_view.local_user)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(&data.site, pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -706,7 +717,7 @@ mod tests {
|
||||||
liked_only: Some(true),
|
liked_only: Some(true),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(&data.site, pool)
|
||||||
.await?
|
.await?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|c| c.comment.content)
|
.map(|c| c.comment.content)
|
||||||
|
@ -722,7 +733,7 @@ mod tests {
|
||||||
disliked_only: Some(true),
|
disliked_only: Some(true),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(&data.site, pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
assert!(read_disliked_comment_views.is_empty());
|
assert!(read_disliked_comment_views.is_empty());
|
||||||
|
@ -743,7 +754,7 @@ mod tests {
|
||||||
parent_path: (Some(top_path)),
|
parent_path: (Some(top_path)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(&data.site, pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let child_path = data.inserted_comment_1.path.clone();
|
let child_path = data.inserted_comment_1.path.clone();
|
||||||
|
@ -752,7 +763,7 @@ mod tests {
|
||||||
parent_path: (Some(child_path)),
|
parent_path: (Some(child_path)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(&data.site, pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Make sure the comment parent-limited fetch is correct
|
// Make sure the comment parent-limited fetch is correct
|
||||||
|
@ -772,7 +783,7 @@ mod tests {
|
||||||
max_depth: (Some(1)),
|
max_depth: (Some(1)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(&data.site, pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Make sure a depth limited one only has the top comment
|
// Make sure a depth limited one only has the top comment
|
||||||
|
@ -790,7 +801,7 @@ mod tests {
|
||||||
sort: (Some(CommentSortType::New)),
|
sort: (Some(CommentSortType::New)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(&data.site, pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Make sure a depth limited one, and given child comment 1, has 3
|
// Make sure a depth limited one, and given child comment 1, has 3
|
||||||
|
@ -816,7 +827,7 @@ mod tests {
|
||||||
local_user: (Some(&data.timmy_local_user_view.local_user)),
|
local_user: (Some(&data.timmy_local_user_view.local_user)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(&data.site, pool)
|
||||||
.await?;
|
.await?;
|
||||||
assert_length!(5, all_languages);
|
assert_length!(5, all_languages);
|
||||||
|
|
||||||
|
@ -834,7 +845,7 @@ mod tests {
|
||||||
local_user: (Some(&data.timmy_local_user_view.local_user)),
|
local_user: (Some(&data.timmy_local_user_view.local_user)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(&data.site, pool)
|
||||||
.await?;
|
.await?;
|
||||||
assert_length!(2, finnish_comments);
|
assert_length!(2, finnish_comments);
|
||||||
let finnish_comment = finnish_comments
|
let finnish_comment = finnish_comments
|
||||||
|
@ -857,7 +868,7 @@ mod tests {
|
||||||
local_user: (Some(&data.timmy_local_user_view.local_user)),
|
local_user: (Some(&data.timmy_local_user_view.local_user)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(&data.site, pool)
|
||||||
.await?;
|
.await?;
|
||||||
assert_length!(1, undetermined_comment);
|
assert_length!(1, undetermined_comment);
|
||||||
|
|
||||||
|
@ -881,7 +892,7 @@ mod tests {
|
||||||
post_id: Some(data.inserted_comment_2.post_id),
|
post_id: Some(data.inserted_comment_2.post_id),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(&data.site, pool)
|
||||||
.await?;
|
.await?;
|
||||||
assert_eq!(comments[0].comment.id, data.inserted_comment_2.id);
|
assert_eq!(comments[0].comment.id, data.inserted_comment_2.id);
|
||||||
assert!(comments[0].comment.distinguished);
|
assert!(comments[0].comment.distinguished);
|
||||||
|
@ -910,7 +921,7 @@ mod tests {
|
||||||
sort: (Some(CommentSortType::Old)),
|
sort: (Some(CommentSortType::Old)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(&data.site, pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
assert_eq!(comments[1].creator.name, "sara");
|
assert_eq!(comments[1].creator.name, "sara");
|
||||||
|
@ -931,7 +942,7 @@ mod tests {
|
||||||
sort: (Some(CommentSortType::Old)),
|
sort: (Some(CommentSortType::Old)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(&data.site, pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Timmy is an admin, and make sure that field is true
|
// Timmy is an admin, and make sure that field is true
|
||||||
|
@ -971,7 +982,7 @@ mod tests {
|
||||||
saved_only: Some(true),
|
saved_only: Some(true),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(&data.site, pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// There should only be two comments
|
// There should only be two comments
|
||||||
|
@ -1001,6 +1012,7 @@ mod tests {
|
||||||
LocalUser::delete(pool, data.timmy_local_user_view.local_user.id).await?;
|
LocalUser::delete(pool, data.timmy_local_user_view.local_user.id).await?;
|
||||||
Person::delete(pool, data.inserted_sara_person.id).await?;
|
Person::delete(pool, data.inserted_sara_person.id).await?;
|
||||||
Instance::delete(pool, data.inserted_instance.id).await?;
|
Instance::delete(pool, data.inserted_instance.id).await?;
|
||||||
|
Site::delete(pool, data.site.id).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1078,6 +1090,7 @@ mod tests {
|
||||||
featured_community: false,
|
featured_community: false,
|
||||||
featured_local: false,
|
featured_local: false,
|
||||||
url_content_type: None,
|
url_content_type: None,
|
||||||
|
scheduled_publish_time: None,
|
||||||
},
|
},
|
||||||
community: Community {
|
community: Community {
|
||||||
id: data.inserted_community.id,
|
id: data.inserted_community.id,
|
||||||
|
@ -1139,7 +1152,7 @@ mod tests {
|
||||||
let unauthenticated_query = CommentQuery {
|
let unauthenticated_query = CommentQuery {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(&data.site, pool)
|
||||||
.await?;
|
.await?;
|
||||||
assert_eq!(0, unauthenticated_query.len());
|
assert_eq!(0, unauthenticated_query.len());
|
||||||
|
|
||||||
|
@ -1147,7 +1160,7 @@ mod tests {
|
||||||
local_user: Some(&data.timmy_local_user_view.local_user),
|
local_user: Some(&data.timmy_local_user_view.local_user),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(&data.site, pool)
|
||||||
.await?;
|
.await?;
|
||||||
assert_eq!(5, authenticated_query.len());
|
assert_eq!(5, authenticated_query.len());
|
||||||
|
|
||||||
|
@ -1225,4 +1238,33 @@ mod tests {
|
||||||
|
|
||||||
cleanup(data, pool).await
|
cleanup(data, pool).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[serial]
|
||||||
|
async fn comment_listings_hide_nsfw() -> LemmyResult<()> {
|
||||||
|
let pool = &build_db_pool_for_tests().await;
|
||||||
|
let pool = &mut pool.into();
|
||||||
|
let data = init_data(pool).await?;
|
||||||
|
|
||||||
|
// Mark a post as nsfw
|
||||||
|
let update_form = PostUpdateForm {
|
||||||
|
nsfw: Some(true),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
Post::update(pool, data.inserted_post.id, &update_form).await?;
|
||||||
|
|
||||||
|
// Make sure comments of this post are not returned
|
||||||
|
let comments = CommentQuery::default().list(&data.site, pool).await?;
|
||||||
|
assert_eq!(0, comments.len());
|
||||||
|
|
||||||
|
// Mark site as nsfw
|
||||||
|
let mut site = data.site.clone();
|
||||||
|
site.content_warning = Some("nsfw".to_string());
|
||||||
|
|
||||||
|
// Now comments of nsfw post are returned
|
||||||
|
let comments = CommentQuery::default().list(&site, pool).await?;
|
||||||
|
assert_eq!(6, comments.len());
|
||||||
|
|
||||||
|
cleanup(data, pool).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -284,8 +284,8 @@ impl PostReportQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
#[expect(clippy::indexing_slicing)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -318,11 +318,18 @@ fn queries<'a>() -> Queries<
|
||||||
// hide posts from deleted communities
|
// hide posts from deleted communities
|
||||||
query = query.filter(community::deleted.eq(false));
|
query = query.filter(community::deleted.eq(false));
|
||||||
|
|
||||||
// only show deleted posts to creator
|
// only creator can see deleted posts and unpublished scheduled posts
|
||||||
if let Some(person_id) = options.local_user.person_id() {
|
if let Some(person_id) = options.local_user.person_id() {
|
||||||
query = query.filter(post::deleted.eq(false).or(post::creator_id.eq(person_id)));
|
query = query.filter(post::deleted.eq(false).or(post::creator_id.eq(person_id)));
|
||||||
|
query = query.filter(
|
||||||
|
post::scheduled_publish_time
|
||||||
|
.is_null()
|
||||||
|
.or(post::creator_id.eq(person_id)),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
query = query.filter(post::deleted.eq(false));
|
query = query
|
||||||
|
.filter(post::deleted.eq(false))
|
||||||
|
.filter(post::scheduled_publish_time.is_null());
|
||||||
}
|
}
|
||||||
|
|
||||||
// only show removed posts to admin when viewing user profile
|
// only show removed posts to admin when viewing user profile
|
||||||
|
@ -387,14 +394,12 @@ fn queries<'a>() -> Queries<
|
||||||
query = query.filter(post::url.eq(search_term));
|
query = query.filter(post::url.eq(search_term));
|
||||||
} else {
|
} else {
|
||||||
let searcher = fuzzy_search(search_term);
|
let searcher = fuzzy_search(search_term);
|
||||||
|
let name_filter = post::name.ilike(searcher.clone());
|
||||||
|
let body_filter = post::body.ilike(searcher.clone());
|
||||||
query = if options.title_only.unwrap_or_default() {
|
query = if options.title_only.unwrap_or_default() {
|
||||||
query.filter(post::name.ilike(searcher))
|
query.filter(name_filter)
|
||||||
} else {
|
} else {
|
||||||
query.filter(
|
query.filter(name_filter.or(body_filter))
|
||||||
post::name
|
|
||||||
.ilike(searcher.clone())
|
|
||||||
.or(post::body.ilike(searcher)),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
.filter(not(post::removed.or(post::deleted)));
|
.filter(not(post::removed.or(post::deleted)));
|
||||||
}
|
}
|
||||||
|
@ -734,7 +739,7 @@ impl<'a> PostQuery<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
post_view::{PaginationCursorData, PostQuery, PostView},
|
post_view::{PaginationCursorData, PostQuery, PostView},
|
||||||
|
@ -1771,6 +1776,7 @@ mod tests {
|
||||||
featured_community: false,
|
featured_community: false,
|
||||||
featured_local: false,
|
featured_local: false,
|
||||||
url_content_type: None,
|
url_content_type: None,
|
||||||
|
scheduled_publish_time: None,
|
||||||
},
|
},
|
||||||
my_vote: None,
|
my_vote: None,
|
||||||
unread_comments: 0,
|
unread_comments: 0,
|
||||||
|
|
|
@ -111,8 +111,8 @@ impl PrivateMessageReportQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
#[expect(clippy::indexing_slicing)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::private_message_report_view::PrivateMessageReportQuery;
|
use crate::private_message_report_view::PrivateMessageReportQuery;
|
||||||
|
|
|
@ -173,8 +173,8 @@ impl PrivateMessageQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
#[expect(clippy::indexing_slicing)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{private_message_view::PrivateMessageQuery, structs::PrivateMessageView};
|
use crate::{private_message_view::PrivateMessageQuery, structs::PrivateMessageView};
|
||||||
|
|
|
@ -135,8 +135,7 @@ impl RegistrationApplicationQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::registration_application_view::{
|
use crate::registration_application_view::{
|
||||||
|
@ -235,7 +234,6 @@ mod tests {
|
||||||
person_id: inserted_sara_local_user.person_id,
|
person_id: inserted_sara_local_user.person_id,
|
||||||
email: inserted_sara_local_user.email,
|
email: inserted_sara_local_user.email,
|
||||||
show_nsfw: inserted_sara_local_user.show_nsfw,
|
show_nsfw: inserted_sara_local_user.show_nsfw,
|
||||||
auto_expand: inserted_sara_local_user.auto_expand,
|
|
||||||
blur_nsfw: inserted_sara_local_user.blur_nsfw,
|
blur_nsfw: inserted_sara_local_user.blur_nsfw,
|
||||||
theme: inserted_sara_local_user.theme,
|
theme: inserted_sara_local_user.theme,
|
||||||
default_post_sort_type: inserted_sara_local_user.default_post_sort_type,
|
default_post_sort_type: inserted_sara_local_user.default_post_sort_type,
|
||||||
|
|
|
@ -83,8 +83,7 @@ impl VoteView {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::structs::VoteView;
|
use crate::structs::VoteView;
|
||||||
|
|
|
@ -15,7 +15,13 @@ doctest = false
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
full = ["lemmy_db_schema/full", "diesel", "diesel-async", "ts-rs"]
|
full = [
|
||||||
|
"lemmy_db_schema/full",
|
||||||
|
"lemmy_utils/full",
|
||||||
|
"diesel",
|
||||||
|
"diesel-async",
|
||||||
|
"ts-rs",
|
||||||
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
lemmy_db_schema = { workspace = true }
|
lemmy_db_schema = { workspace = true }
|
||||||
|
@ -33,6 +39,7 @@ serde_with = { workspace = true }
|
||||||
ts-rs = { workspace = true, optional = true }
|
ts-rs = { workspace = true, optional = true }
|
||||||
chrono.workspace = true
|
chrono.workspace = true
|
||||||
strum = { workspace = true }
|
strum = { workspace = true }
|
||||||
|
lemmy_utils = { workspace = true, optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serial_test = { workspace = true }
|
serial_test = { workspace = true }
|
||||||
|
|
|
@ -303,7 +303,6 @@ impl CommentReplyQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{comment_reply_view::CommentReplyQuery, structs::CommentReplyView};
|
use crate::{comment_reply_view::CommentReplyQuery, structs::CommentReplyView};
|
||||||
|
|
|
@ -8,13 +8,14 @@ use lemmy_db_schema::{
|
||||||
source::local_user::LocalUser,
|
source::local_user::LocalUser,
|
||||||
utils::{get_conn, DbPool},
|
utils::{get_conn, DbPool},
|
||||||
};
|
};
|
||||||
|
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
||||||
|
|
||||||
impl CommunityModeratorView {
|
impl CommunityModeratorView {
|
||||||
pub async fn is_community_moderator(
|
pub async fn check_is_community_moderator(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
find_community_id: CommunityId,
|
find_community_id: CommunityId,
|
||||||
find_person_id: PersonId,
|
find_person_id: PersonId,
|
||||||
) -> Result<bool, Error> {
|
) -> LemmyResult<()> {
|
||||||
use lemmy_db_schema::schema::community_moderator::dsl::{
|
use lemmy_db_schema::schema::community_moderator::dsl::{
|
||||||
community_id,
|
community_id,
|
||||||
community_moderator,
|
community_moderator,
|
||||||
|
@ -27,20 +28,24 @@ impl CommunityModeratorView {
|
||||||
.filter(person_id.eq(find_person_id)),
|
.filter(person_id.eq(find_person_id)),
|
||||||
))
|
))
|
||||||
.get_result::<bool>(conn)
|
.get_result::<bool>(conn)
|
||||||
.await
|
.await?
|
||||||
|
.then_some(())
|
||||||
|
.ok_or(LemmyErrorType::NotAModerator.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn is_community_moderator_of_any(
|
pub(crate) async fn is_community_moderator_of_any(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
find_person_id: PersonId,
|
find_person_id: PersonId,
|
||||||
) -> Result<bool, Error> {
|
) -> LemmyResult<()> {
|
||||||
use lemmy_db_schema::schema::community_moderator::dsl::{community_moderator, person_id};
|
use lemmy_db_schema::schema::community_moderator::dsl::{community_moderator, person_id};
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
select(exists(
|
select(exists(
|
||||||
community_moderator.filter(person_id.eq(find_person_id)),
|
community_moderator.filter(person_id.eq(find_person_id)),
|
||||||
))
|
))
|
||||||
.get_result::<bool>(conn)
|
.get_result::<bool>(conn)
|
||||||
.await
|
.await?
|
||||||
|
.then_some(())
|
||||||
|
.ok_or(LemmyErrorType::NotAModerator.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn for_community(
|
pub async fn for_community(
|
||||||
|
|
|
@ -1,25 +1,33 @@
|
||||||
use crate::structs::CommunityPersonBanView;
|
use crate::structs::CommunityPersonBanView;
|
||||||
use diesel::{dsl::exists, result::Error, select, ExpressionMethods, QueryDsl};
|
use diesel::{
|
||||||
|
dsl::{exists, not},
|
||||||
|
select,
|
||||||
|
ExpressionMethods,
|
||||||
|
QueryDsl,
|
||||||
|
};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
newtypes::{CommunityId, PersonId},
|
newtypes::{CommunityId, PersonId},
|
||||||
schema::community_person_ban,
|
schema::community_person_ban,
|
||||||
utils::{get_conn, DbPool},
|
utils::{get_conn, DbPool},
|
||||||
};
|
};
|
||||||
|
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
||||||
|
|
||||||
impl CommunityPersonBanView {
|
impl CommunityPersonBanView {
|
||||||
pub async fn get(
|
pub async fn check(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
from_person_id: PersonId,
|
from_person_id: PersonId,
|
||||||
from_community_id: CommunityId,
|
from_community_id: CommunityId,
|
||||||
) -> Result<bool, Error> {
|
) -> LemmyResult<()> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
select(exists(
|
select(not(exists(
|
||||||
community_person_ban::table
|
community_person_ban::table
|
||||||
.filter(community_person_ban::community_id.eq(from_community_id))
|
.filter(community_person_ban::community_id.eq(from_community_id))
|
||||||
.filter(community_person_ban::person_id.eq(from_person_id)),
|
.filter(community_person_ban::person_id.eq(from_person_id)),
|
||||||
))
|
)))
|
||||||
.get_result::<bool>(conn)
|
.get_result::<bool>(conn)
|
||||||
.await
|
.await?
|
||||||
|
.then_some(())
|
||||||
|
.ok_or(LemmyErrorType::PersonIsBannedFromCommunity.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ use lemmy_db_schema::{
|
||||||
ListingType,
|
ListingType,
|
||||||
PostSortType,
|
PostSortType,
|
||||||
};
|
};
|
||||||
|
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
||||||
|
|
||||||
fn queries<'a>() -> Queries<
|
fn queries<'a>() -> Queries<
|
||||||
impl ReadFn<'a, CommunityView, (CommunityId, Option<&'a LocalUser>, bool)>,
|
impl ReadFn<'a, CommunityView, (CommunityId, Option<&'a LocalUser>, bool)>,
|
||||||
|
@ -111,9 +112,14 @@ fn queries<'a>() -> Queries<
|
||||||
|
|
||||||
if let Some(search_term) = options.search_term {
|
if let Some(search_term) = options.search_term {
|
||||||
let searcher = fuzzy_search(&search_term);
|
let searcher = fuzzy_search(&search_term);
|
||||||
query = query
|
let name_filter = community::name.ilike(searcher.clone());
|
||||||
.filter(community::name.ilike(searcher.clone()))
|
let title_filter = community::title.ilike(searcher.clone());
|
||||||
.or_filter(community::title.ilike(searcher))
|
let description_filter = community::description.ilike(searcher.clone());
|
||||||
|
query = if options.title_only.unwrap_or_default() {
|
||||||
|
query.filter(name_filter.or(title_filter))
|
||||||
|
} else {
|
||||||
|
query.filter(name_filter.or(title_filter.or(description_filter)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hide deleted and removed for non-admins or mods
|
// Hide deleted and removed for non-admins or mods
|
||||||
|
@ -185,35 +191,39 @@ impl CommunityView {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn is_mod_or_admin(
|
pub async fn check_is_mod_or_admin(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
person_id: PersonId,
|
person_id: PersonId,
|
||||||
community_id: CommunityId,
|
community_id: CommunityId,
|
||||||
) -> Result<bool, Error> {
|
) -> LemmyResult<()> {
|
||||||
let is_mod =
|
let is_mod =
|
||||||
CommunityModeratorView::is_community_moderator(pool, community_id, person_id).await?;
|
CommunityModeratorView::check_is_community_moderator(pool, community_id, person_id).await;
|
||||||
if is_mod {
|
if is_mod.is_ok()
|
||||||
Ok(true)
|
|| PersonView::read(pool, person_id)
|
||||||
} else if let Ok(person_view) = PersonView::read(pool, person_id).await {
|
.await
|
||||||
Ok(person_view.is_admin)
|
.is_ok_and(|t| t.is_admin)
|
||||||
|
{
|
||||||
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Ok(false)
|
Err(LemmyErrorType::NotAModOrAdmin)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if a person is an admin, or moderator of any community.
|
/// Checks if a person is an admin, or moderator of any community.
|
||||||
pub async fn is_mod_of_any_or_admin(
|
pub async fn check_is_mod_of_any_or_admin(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
person_id: PersonId,
|
person_id: PersonId,
|
||||||
) -> Result<bool, Error> {
|
) -> LemmyResult<()> {
|
||||||
let is_mod_of_any =
|
let is_mod_of_any =
|
||||||
CommunityModeratorView::is_community_moderator_of_any(pool, person_id).await?;
|
CommunityModeratorView::is_community_moderator_of_any(pool, person_id).await;
|
||||||
if is_mod_of_any {
|
if is_mod_of_any.is_ok()
|
||||||
Ok(true)
|
|| PersonView::read(pool, person_id)
|
||||||
} else if let Ok(person_view) = PersonView::read(pool, person_id).await {
|
.await
|
||||||
Ok(person_view.is_admin)
|
.is_ok_and(|t| t.is_admin)
|
||||||
|
{
|
||||||
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Ok(false)
|
Err(LemmyErrorType::NotAModOrAdmin)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -224,6 +234,7 @@ pub struct CommunityQuery<'a> {
|
||||||
pub sort: Option<PostSortType>,
|
pub sort: Option<PostSortType>,
|
||||||
pub local_user: Option<&'a LocalUser>,
|
pub local_user: Option<&'a LocalUser>,
|
||||||
pub search_term: Option<String>,
|
pub search_term: Option<String>,
|
||||||
|
pub title_only: Option<bool>,
|
||||||
pub is_mod_or_admin: bool,
|
pub is_mod_or_admin: bool,
|
||||||
pub show_nsfw: bool,
|
pub show_nsfw: bool,
|
||||||
pub page: Option<i64>,
|
pub page: Option<i64>,
|
||||||
|
@ -237,8 +248,7 @@ impl<'a> CommunityQuery<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{community_view::CommunityQuery, structs::CommunityView};
|
use crate::{community_view::CommunityQuery, structs::CommunityView};
|
||||||
|
|
|
@ -303,7 +303,6 @@ impl PersonMentionQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{person_mention_view::PersonMentionQuery, structs::PersonMentionView};
|
use crate::{person_mention_view::PersonMentionQuery, structs::PersonMentionView};
|
||||||
|
|
|
@ -164,7 +164,7 @@ impl PersonQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
#[expect(clippy::indexing_slicing)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -222,8 +222,8 @@ impl<T: DataSource> CommunityInboxCollector<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
#[expect(clippy::indexing_slicing)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
|
|
@ -192,8 +192,8 @@ impl SendManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
#[expect(clippy::indexing_slicing)]
|
||||||
mod test {
|
mod test {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -439,8 +439,8 @@ impl InstanceWorker {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
#[expect(clippy::indexing_slicing)]
|
||||||
mod test {
|
mod test {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -172,6 +172,8 @@ pub enum LemmyErrorType {
|
||||||
Unknown(String),
|
Unknown(String),
|
||||||
CantDeleteSite,
|
CantDeleteSite,
|
||||||
UrlLengthOverflow,
|
UrlLengthOverflow,
|
||||||
|
PostScheduleTimeMustBeInFuture,
|
||||||
|
TooManyScheduledPosts,
|
||||||
NotFound,
|
NotFound,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -221,8 +221,6 @@ fn parse_ip(addr: &str) -> Option<IpAddr> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -136,7 +136,6 @@ impl<K: Eq + Hash, C: MapLevel> MapLevel for Map<K, C> {
|
||||||
.entry(addr_part)
|
.entry(addr_part)
|
||||||
.or_insert(RateLimitedGroup::new(now, adjusted_configs));
|
.or_insert(RateLimitedGroup::new(now, adjusted_configs));
|
||||||
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
let total_passes = group.check_total(action_type, now, adjusted_configs[action_type]);
|
let total_passes = group.check_total(action_type, now, adjusted_configs[action_type]);
|
||||||
|
|
||||||
let children_pass = group.children.check(
|
let children_pass = group.children.check(
|
||||||
|
@ -161,7 +160,6 @@ impl<K: Eq + Hash, C: MapLevel> MapLevel for Map<K, C> {
|
||||||
// Evaluated if `some_children_remaining` is false
|
// Evaluated if `some_children_remaining` is false
|
||||||
let total_has_refill_in_future = || {
|
let total_has_refill_in_future = || {
|
||||||
group.total.into_iter().any(|(action_type, bucket)| {
|
group.total.into_iter().any(|(action_type, bucket)| {
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
let config = configs[action_type];
|
let config = configs[action_type];
|
||||||
bucket.update(now, config).tokens != config.capacity
|
bucket.update(now, config).tokens != config.capacity
|
||||||
})
|
})
|
||||||
|
@ -214,7 +212,6 @@ impl<C: Default> RateLimitedGroup<C> {
|
||||||
now: InstantSecs,
|
now: InstantSecs,
|
||||||
config: BucketConfig,
|
config: BucketConfig,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
#[allow(clippy::indexing_slicing)] // `EnumMap` has no `get` function
|
|
||||||
let bucket = &mut self.total[action_type];
|
let bucket = &mut self.total[action_type];
|
||||||
|
|
||||||
let new_bucket = bucket.update(now, config);
|
let new_bucket = bucket.update(now, config);
|
||||||
|
@ -311,8 +308,7 @@ fn split_ipv6(ip: Ipv6Addr) -> ([u8; 6], u8, u8) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use super::{ActionType, BucketConfig, InstantSecs, RateLimitState, RateLimitedGroup};
|
use super::{ActionType, BucketConfig, InstantSecs, RateLimitState, RateLimitedGroup};
|
||||||
|
@ -361,7 +357,6 @@ mod tests {
|
||||||
assert!(post_passed);
|
assert!(post_passed);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
let expected_buckets = |factor: u32, tokens_consumed: u32| {
|
let expected_buckets = |factor: u32, tokens_consumed: u32| {
|
||||||
let adjusted_configs = bucket_configs.map(|_, config| BucketConfig {
|
let adjusted_configs = bucket_configs.map(|_, config| BucketConfig {
|
||||||
capacity: config.capacity.saturating_mul(factor),
|
capacity: config.capacity.saturating_mul(factor),
|
||||||
|
|
|
@ -107,8 +107,7 @@ pub fn markdown_check_for_blocked_urls(text: &str, blocklist: &RegexSet) -> Lemm
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -134,8 +134,6 @@ pub fn add(markdown_parser: &mut MarkdownIt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::utils::markdown::spoiler_rule::add;
|
use crate::utils::markdown::spoiler_rule::add;
|
||||||
|
|
|
@ -34,8 +34,7 @@ pub fn scrape_text_for_mentions(text: &str) -> Vec<MentionData> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::indexing_slicing)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod test {
|
mod test {
|
||||||
|
|
||||||
use crate::utils::mention::scrape_text_for_mentions;
|
use crate::utils::mention::scrape_text_for_mentions;
|
||||||
|
|
|
@ -61,8 +61,7 @@ pub(crate) fn slurs_vec_to_str(slurs: &[&str]) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod test {
|
mod test {
|
||||||
|
|
||||||
use crate::utils::slurs::{remove_slurs, slur_check, slurs_vec_to_str};
|
use crate::utils::slurs::{remove_slurs, slur_check, slurs_vec_to_str};
|
||||||
|
|
|
@ -351,7 +351,6 @@ pub fn build_url_str_without_scheme(url_str: &str) -> LemmyResult<String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
3
migrations/2024-09-16-095656_schedule-post/down.sql
Normal file
3
migrations/2024-09-16-095656_schedule-post/down.sql
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
ALTER TABLE post
|
||||||
|
DROP COLUMN scheduled_publish_time;
|
||||||
|
|
5
migrations/2024-09-16-095656_schedule-post/up.sql
Normal file
5
migrations/2024-09-16-095656_schedule-post/up.sql
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
ALTER TABLE post
|
||||||
|
ADD COLUMN scheduled_publish_time timestamptz;
|
||||||
|
|
||||||
|
CREATE INDEX idx_post_scheduled_publish_time ON post (scheduled_publish_time);
|
||||||
|
|
|
@ -14,7 +14,7 @@ CREATE TABLE oauth_provider (
|
||||||
scopes text NOT NULL,
|
scopes text NOT NULL,
|
||||||
auto_verify_email boolean DEFAULT TRUE NOT NULL,
|
auto_verify_email boolean DEFAULT TRUE NOT NULL,
|
||||||
account_linking_enabled boolean DEFAULT FALSE NOT NULL,
|
account_linking_enabled boolean DEFAULT FALSE NOT NULL,
|
||||||
enabled boolean DEFAULT FALSE NOT NULL,
|
enabled boolean DEFAULT TRUE NOT NULL,
|
||||||
published timestamp with time zone DEFAULT now() NOT NULL,
|
published timestamp with time zone DEFAULT now() NOT NULL,
|
||||||
updated timestamp with time zone
|
updated timestamp with time zone
|
||||||
);
|
);
|
||||||
|
|
3
migrations/2024-09-23-133038_remove_auto_expand/down.sql
Normal file
3
migrations/2024-09-23-133038_remove_auto_expand/down.sql
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
ALTER TABLE local_user
|
||||||
|
ADD COLUMN auto_expand boolean NOT NULL DEFAULT FALSE;
|
||||||
|
|
3
migrations/2024-09-23-133038_remove_auto_expand/up.sql
Normal file
3
migrations/2024-09-23-133038_remove_auto_expand/up.sql
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
ALTER TABLE local_user
|
||||||
|
DROP COLUMN auto_expand;
|
||||||
|
|
14
src/lib.rs
14
src/lib.rs
|
@ -157,11 +157,6 @@ pub async fn start_lemmy_server(args: CmdArgs) -> LemmyResult<()> {
|
||||||
rate_limit_cell.clone(),
|
rate_limit_cell.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let scheduled_tasks = (!args.disable_scheduled_tasks).then(|| {
|
|
||||||
// Schedules various cleanup tasks for the DB
|
|
||||||
tokio::task::spawn(scheduled_tasks::setup(context.clone()))
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(prometheus) = SETTINGS.prometheus.clone() {
|
if let Some(prometheus) = SETTINGS.prometheus.clone() {
|
||||||
serve_prometheus(prometheus, context.clone())?;
|
serve_prometheus(prometheus, context.clone())?;
|
||||||
}
|
}
|
||||||
|
@ -187,7 +182,14 @@ pub async fn start_lemmy_server(args: CmdArgs) -> LemmyResult<()> {
|
||||||
}))
|
}))
|
||||||
.expect("set function pointer");
|
.expect("set function pointer");
|
||||||
let request_data = federation_config.to_request_data();
|
let request_data = federation_config.to_request_data();
|
||||||
let outgoing_activities_task = tokio::task::spawn(handle_outgoing_activities(request_data));
|
let outgoing_activities_task = tokio::task::spawn(handle_outgoing_activities(
|
||||||
|
request_data.reset_request_count(),
|
||||||
|
));
|
||||||
|
|
||||||
|
let scheduled_tasks = (!args.disable_scheduled_tasks).then(|| {
|
||||||
|
// Schedules various cleanup tasks for the DB
|
||||||
|
tokio::task::spawn(scheduled_tasks::setup(request_data.reset_request_count()))
|
||||||
|
});
|
||||||
|
|
||||||
let server = if !args.disable_http_server {
|
let server = if !args.disable_http_server {
|
||||||
if let Some(startup_server_handle) = startup_server_handle {
|
if let Some(startup_server_handle) = startup_server_handle {
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue