Merge branch 'main' into persistent-queue

pull/3605/head
Dessalines 2023-08-22 09:34:33 -04:00 committed by GitHub
commit 574e23cc61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
114 changed files with 1293 additions and 669 deletions

View File

@ -6,12 +6,12 @@ body:
- type: markdown
attributes:
value: |
Have a question about Lemmy's UI?
Have a question about Lemmy?
Please check the docs first: https://join-lemmy.org/docs/en/index.html
- type: textarea
id: question
attributes:
label: Question
description: What's the question you have about Lemmy's UI?
description: What's the question you have about Lemmy?
validations:
required: true

View File

@ -29,7 +29,7 @@ variables:
# recursive: true
# submodule_update_remote: true
pipeline:
steps:
prepare_repo:
image: alpine:3
commands:
@ -38,10 +38,35 @@ pipeline:
- git submodule update
prettier_check:
group: format
image: tmknom/prettier:3.0.0
commands:
- prettier -c . '!**/volumes' '!**/dist' '!target' '!**/translations'
toml_fmt:
group: format
image: tamasfe/taplo:0.8.1
commands:
- taplo format --check
sql_fmt:
group: format
image: backplane/pgformatter:latest
commands:
- ./scripts/sql_format_check.sh
cargo_fmt:
group: format
image: *muslrust_image
environment:
# store cargo data in repo folder so that it gets cached between steps
CARGO_HOME: .cargo
commands:
# need make existing toolchain available
- cp -n ~/.cargo . -r
- rustup toolchain install nightly-2023-07-10 --no-self-update --profile minimal --component rustfmt
- cargo +nightly-2023-07-10 fmt -- --check
restore-cache:
image: meltwater/drone-cache:v1
pull: true
@ -66,35 +91,6 @@ pipeline:
[MINIO_ENDPOINT, MINIO_WRITE_USER, MINIO_WRITE_PASSWORD, MINIO_BUCKET]
when: *slow_check_paths
toml_fmt:
image: tamasfe/taplo:0.8.1
commands:
- taplo format --check
cargo_fmt:
image: *muslrust_image
environment:
# store cargo data in repo folder so that it gets cached between steps
CARGO_HOME: .cargo
commands:
# need make existing toolchain available
- cp -n ~/.cargo . -r
- rustup toolchain install nightly-2023-07-10
- rustup component add rustfmt --toolchain nightly-2023-07-10
- cargo +nightly-2023-07-10 fmt -- --check
sql_fmt:
image: alpine:3
commands:
- apk add bash wget perl make git
- wget https://github.com/darold/pgFormatter/archive/refs/tags/v5.5.tar.gz
- tar xzf v5.5.tar.gz
- cd pgFormatter-5.5
- perl Makefile.PL
- make && make install
- cd ..
- ./scripts/./sql_format_check.sh
# make sure api builds with default features (used by other crates relying on lemmy api)
check_api_common_default_features:
image: *muslrust_image
@ -176,7 +172,17 @@ pipeline:
-D clippy::indexing_slicing
when: *slow_check_paths
cargo_build:
image: *muslrust_image
environment:
CARGO_HOME: .cargo
commands:
- cargo build
- mv target/x86_64-unknown-linux-musl/debug/lemmy_server target/lemmy_server
when: *slow_check_paths
cargo_test:
group: tests
image: *muslrust_image
environment:
LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy
@ -187,16 +193,8 @@ pipeline:
- cargo test --workspace --no-fail-fast
when: *slow_check_paths
cargo_build:
image: *muslrust_image
environment:
CARGO_HOME: .cargo
commands:
- cargo build
- mv target/x86_64-unknown-linux-musl/debug/lemmy_server target/lemmy_server
when: *slow_check_paths
run_federation_tests:
group: tests
image: node:alpine
environment:
LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432
@ -231,7 +229,9 @@ pipeline:
- "api_tests/node_modules"
secrets:
[MINIO_ENDPOINT, MINIO_WRITE_USER, MINIO_WRITE_PASSWORD, MINIO_BUCKET]
when: *slow_check_paths
when:
- event: push
branch: main
publish_release_docker:
image: woodpeckerci/plugin-docker-buildx
@ -263,7 +263,7 @@ pipeline:
image: alpine:3
commands:
- apk add curl
- "curl -d'Lemmy CI build failed: ${CI_BUILD_LINK}' ntfy.sh/lemmy_drone_ci"
- "curl -d'Lemmy CI build failed: ${CI_PIPELINE_URL}' ntfy.sh/lemmy_drone_ci"
when:
status: [failure]

View File

@ -1,3 +1,51 @@
# Lemmy v0.18.4 Release (2023-08-08)
## What is Lemmy?
Lemmy is a self-hosted social link aggregation and discussion platform. It is completely free and open, and not controlled by any company. This means that there is no advertising, tracking, or secret algorithms. Content is organized into communities, so it is easy to subscribe to topics that you are interested in, and ignore others. Voting is used to bring the most interesting items to the top.
## Major Changes
This version fixes the problem of comment context not loading properly. It also fixes a couple other bugs.
## Support development
@dessalines and @nutomic are working full-time on Lemmy to integrate community contributions, fix bugs, optimize performance and much more. This work is funded exclusively through donations.
If you like using Lemmy, and want to make sure that we will always be available to work full time building it, consider [donating to support its development](https://join-lemmy.org/donate). No one likes recurring donations, but theyve proven to be the only way that open-source software like Lemmy can stay independent and alive.
- [Liberapay](https://liberapay.com/Lemmy) (preferred option)
- [Open Collective](https://opencollective.com/lemmy)
- [Patreon](https://www.patreon.com/dessalines)
- [Cryptocurrency](https://join-lemmy.org/donate) (scroll to bottom of page)
## Upgrade instructions
Follow the upgrade instructions for [ansible](https://github.com/LemmyNet/lemmy-ansible#upgrading) or [docker](https://join-lemmy.org/docs/en/administration/install_docker.html#updating). There are no config or API changes with this release.
If you need help with the upgrade, you can ask in our [support forum](https://lemmy.ml/c/lemmy_support) or on the [Matrix Chat](https://matrix.to/#/#lemmy-admin-support-topics:discuss.online).
## Changes
### Lemmy
- Fix fetch instance software version from nodeinfo ([#3772](https://github.com/LemmyNet/lemmy/issues/3772))
- Correct logic to meet join-lemmy requirement, don't have closed signups. Allows Open and Applications. ([#3761](https://github.com/LemmyNet/lemmy/issues/3761))
- Fix ordering when doing a comment_parent type `list_comments` ([#3823](https://github.com/LemmyNet/lemmy/issues/3823))
### Lemmy-UI
- Mark post as read when clicking "Expand here" on the preview image on the post listing page ([#1600](https://github.com/LemmyNet/lemmy/issues/1600)) ([#1978](https://github.com/LemmyNet/lemmy/issues/1978))
- Update translation submodule ([#2023](https://github.com/LemmyNet/lemmy/issues/2023))
- Fix comment insertion from context views. Fixes #2030 ([#2031](https://github.com/LemmyNet/lemmy/issues/2031))
- Fix password autocomplete ([#2033](https://github.com/LemmyNet/lemmy/issues/2033))
- Fix suggested title " " spaces ([#2037](https://github.com/LemmyNet/lemmy/issues/2037))
- Expanded the RegEx to check if the title contains new line caracters. Should fix issue #1962 ([#1965](https://github.com/LemmyNet/lemmy/issues/1965))
- ES-Lint tweak ([#2001](https://github.com/LemmyNet/lemmy/issues/2001))
- Upgrading deps, running prettier. ([#1987](https://github.com/LemmyNet/lemmy/issues/1987))
- Fix document title of admin settings being overwritten by tagline and emoji forms ([#2003](https://github.com/LemmyNet/lemmy/issues/2003))
- Use proper modifier key in markdown text input on macOS ([#1995](https://github.com/LemmyNet/lemmy/issues/1995))
# Lemmy v0.18.3 Release (2023-07-28)
## What is Lemmy?

View File

@ -38,9 +38,10 @@ pub async fn distinguish_comment(
// Update the Comment
let comment_id = data.comment_id;
let form = CommentUpdateForm::builder()
.distinguished(Some(data.distinguished))
.build();
let form = CommentUpdateForm {
distinguished: Some(data.distinguished),
..Default::default()
};
Comment::update(&mut context.pool(), comment_id, &form)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
@ -52,6 +53,5 @@ pub async fn distinguish_comment(
Ok(Json(CommentResponse {
comment_view,
recipient_ids: Vec::new(),
form_id: None,
}))
}

View File

@ -89,7 +89,6 @@ pub async fn like_comment(
context.deref(),
comment_id,
Some(local_user_view),
None,
recipient_ids,
)
.await?,

View File

@ -40,6 +40,5 @@ pub async fn save_comment(
Ok(Json(CommentResponse {
comment_view,
recipient_ids: Vec::new(),
form_id: None,
}))
}

View File

@ -17,7 +17,7 @@ pub async fn list_comment_reports(
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
let community_id = data.community_id;
let unresolved_only = data.unresolved_only;
let unresolved_only = data.unresolved_only.unwrap_or_default();
let page = data.page;
let limit = data.limit;

View File

@ -52,7 +52,7 @@ pub async fn block_community(
}
let community_view =
CommunityView::read(&mut context.pool(), community_id, Some(person_id), None).await?;
CommunityView::read(&mut context.pool(), community_id, Some(person_id), false).await?;
ActivityChannel::submit_activity(
SendActivityData::FollowCommunity(

View File

@ -61,7 +61,7 @@ pub async fn follow_community(
let community_id = data.community_id;
let person_id = local_user_view.person.id;
let community_view =
CommunityView::read(&mut context.pool(), community_id, Some(person_id), None).await?;
CommunityView::read(&mut context.pool(), community_id, Some(person_id), false).await?;
let discussion_languages = CommunityLanguage::read(&mut context.pool(), community_id).await?;
Ok(Json(CommunityResponse {

View File

@ -25,9 +25,10 @@ pub async fn hide_community(
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
is_admin(&local_user_view)?;
let community_form = CommunityUpdateForm::builder()
.hidden(Some(data.hidden))
.build();
let community_form = CommunityUpdateForm {
hidden: Some(data.hidden),
..Default::default()
};
let mod_hide_community_form = ModHideCommunityForm {
community_id: data.community_id,

View File

@ -84,7 +84,7 @@ impl Perform for TransferCommunity {
let community_id = data.community_id;
let person_id = local_user_view.person.id;
let community_view =
CommunityView::read(&mut context.pool(), community_id, Some(person_id), None)
CommunityView::read(&mut context.pool(), community_id, Some(person_id), false)
.await
.with_lemmy_type(LemmyErrorType::CouldntFindCommunity)?;

View File

@ -40,7 +40,7 @@ pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> Result<String, LemmyEr
if let Some(samples16) = samples.as_sixteen() {
concat_samples.extend(samples16);
} else {
return Err(LemmyErrorType::CouldntCreateAudioCaptcha)?;
Err(LemmyErrorType::CouldntCreateAudioCaptcha)?;
}
}
@ -66,10 +66,10 @@ pub(crate) fn check_report_reason(reason: &str, local_site: &LocalSite) -> Resul
check_slurs(reason, slur_regex)?;
if reason.is_empty() {
return Err(LemmyErrorType::ReportReasonRequired)?;
Err(LemmyErrorType::ReportReasonRequired)?;
}
if reason.chars().count() > 1000 {
return Err(LemmyErrorType::ReportTooLong)?;
Err(LemmyErrorType::ReportTooLong)?;
}
Ok(())
}

View File

@ -32,7 +32,10 @@ impl Perform for AddAdmin {
let added_admin = Person::update(
&mut context.pool(),
added_person_id,
&PersonUpdateForm::builder().admin(Some(added)).build(),
&PersonUpdateForm {
admin: Some(added),
..Default::default()
},
)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdateUser)?;

View File

@ -35,10 +35,11 @@ pub async fn ban_from_site(
let person = Person::update(
&mut context.pool(),
data.person_id,
&PersonUpdateForm::builder()
.banned(Some(data.ban))
.ban_expires(Some(expires))
.build(),
&PersonUpdateForm {
banned: Some(data.ban),
ban_expires: Some(expires),
..Default::default()
},
)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdateUser)?;

View File

@ -23,9 +23,9 @@ impl Perform for GetPersonMentions {
let sort = data.sort;
let page = data.page;
let limit = data.limit;
let unread_only = data.unread_only;
let unread_only = data.unread_only.unwrap_or_default();
let person_id = Some(local_user_view.person.id);
let show_bot_accounts = Some(local_user_view.local_user.show_bot_accounts);
let show_bot_accounts = local_user_view.local_user.show_bot_accounts;
let mentions = PersonMentionQuery {
recipient_id: person_id,

View File

@ -20,9 +20,9 @@ impl Perform for GetReplies {
let sort = data.sort;
let page = data.page;
let limit = data.limit;
let unread_only = data.unread_only;
let unread_only = data.unread_only.unwrap_or_default();
let person_id = Some(local_user_view.person.id);
let show_bot_accounts = Some(local_user_view.local_user.show_bot_accounts);
let show_bot_accounts = local_user_view.local_user.show_bot_accounts;
let replies = CommentReplyQuery {
recipient_id: person_id,

View File

@ -90,14 +90,15 @@ impl Perform for SaveUserSettings {
let default_sort_type = data.default_sort_type;
let theme = sanitize_html_opt(&data.theme);
let person_form = PersonUpdateForm::builder()
.display_name(display_name)
.bio(bio)
.matrix_user_id(matrix_user_id)
.bot_account(data.bot_account)
.avatar(avatar)
.banner(banner)
.build();
let person_form = PersonUpdateForm {
display_name,
bio,
matrix_user_id,
bot_account: data.bot_account,
avatar,
banner,
..Default::default()
};
Person::update(&mut context.pool(), person_id, &person_form)
.await
@ -121,26 +122,27 @@ impl Perform for SaveUserSettings {
(None, None)
};
let local_user_form = LocalUserUpdateForm::builder()
.email(email)
.show_avatars(data.show_avatars)
.show_read_posts(data.show_read_posts)
.show_new_post_notifs(data.show_new_post_notifs)
.send_notifications_to_email(data.send_notifications_to_email)
.show_nsfw(data.show_nsfw)
.blur_nsfw(data.blur_nsfw)
.auto_expand(data.auto_expand)
.show_bot_accounts(data.show_bot_accounts)
.show_scores(data.show_scores)
.default_sort_type(default_sort_type)
.default_listing_type(default_listing_type)
.theme(theme)
.interface_language(data.interface_language.clone())
.totp_2fa_secret(totp_2fa_secret)
.totp_2fa_url(totp_2fa_url)
.open_links_in_new_tab(data.open_links_in_new_tab)
.infinite_scroll_enabled(data.infinite_scroll_enabled)
.build();
let local_user_form = LocalUserUpdateForm {
email,
show_avatars: data.show_avatars,
show_read_posts: data.show_read_posts,
show_new_post_notifs: data.show_new_post_notifs,
send_notifications_to_email: data.send_notifications_to_email,
show_nsfw: data.show_nsfw,
blur_nsfw: data.blur_nsfw,
auto_expand: data.auto_expand,
show_bot_accounts: data.show_bot_accounts,
show_scores: data.show_scores,
default_sort_type,
default_listing_type,
theme,
interface_language: data.interface_language.clone(),
totp_2fa_secret,
totp_2fa_url,
open_links_in_new_tab: data.open_links_in_new_tab,
infinite_scroll_enabled: data.infinite_scroll_enabled,
..Default::default()
};
let local_user_res =
LocalUser::update(&mut context.pool(), local_user_id, &local_user_form).await;

View File

@ -23,12 +23,13 @@ impl Perform for VerifyEmail {
.await
.with_lemmy_type(LemmyErrorType::TokenNotFound)?;
let form = LocalUserUpdateForm::builder()
let form = LocalUserUpdateForm {
// necessary in case this is a new signup
.email_verified(Some(true))
email_verified: Some(true),
// necessary in case email of an existing user was changed
.email(Some(Some(verification.email)))
.build();
email: Some(Some(verification.email)),
..Default::default()
};
let local_user_id = verification.local_user_id;
LocalUser::update(&mut context.pool(), local_user_id, &form).await?;

View File

@ -56,13 +56,15 @@ pub async fn feature_post(
// Update the post
let post_id = data.post_id;
let new_post: PostUpdateForm = if data.feature_type == PostFeatureType::Community {
PostUpdateForm::builder()
.featured_community(Some(data.featured))
.build()
PostUpdateForm {
featured_community: Some(data.featured),
..Default::default()
}
} else {
PostUpdateForm::builder()
.featured_local(Some(data.featured))
.build()
PostUpdateForm {
featured_local: Some(data.featured),
..Default::default()
}
};
let post = Post::update(&mut context.pool(), post_id, &new_post).await?;

View File

@ -53,7 +53,10 @@ pub async fn lock_post(
let post = Post::update(
&mut context.pool(),
post_id,
&PostUpdateForm::builder().locked(Some(locked)).build(),
&PostUpdateForm {
locked: Some(locked),
..Default::default()
},
)
.await?;

View File

@ -28,7 +28,7 @@ impl Perform for MarkPostAsRead {
}
// Fetch it
let post_view = PostView::read(&mut context.pool(), post_id, Some(person_id), None).await?;
let post_view = PostView::read(&mut context.pool(), post_id, Some(person_id), false).await?;
Ok(Self::Response { post_view })
}

View File

@ -38,7 +38,7 @@ impl Perform for SavePost {
let post_id = data.post_id;
let person_id = local_user_view.person.id;
let post_view = PostView::read(&mut context.pool(), post_id, Some(person_id), None).await?;
let post_view = PostView::read(&mut context.pool(), post_id, Some(person_id), false).await?;
// Mark the post as read
mark_post_as_read(person_id, post_id, &mut context.pool()).await?;

View File

@ -36,7 +36,7 @@ pub async fn create_post_report(
let person_id = local_user_view.person.id;
let post_id = data.post_id;
let post_view = PostView::read(&mut context.pool(), post_id, None, None).await?;
let post_view = PostView::read(&mut context.pool(), post_id, None, false).await?;
check_community_ban(person_id, post_view.community.id, &mut context.pool()).await?;

View File

@ -23,7 +23,7 @@ impl Perform for ListPostReports {
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
let community_id = data.community_id;
let unresolved_only = data.unresolved_only;
let unresolved_only = data.unresolved_only.unwrap_or_default();
let page = data.page;
let limit = data.limit;

View File

@ -38,7 +38,10 @@ impl Perform for MarkPrivateMessageAsRead {
PrivateMessage::update(
&mut context.pool(),
private_message_id,
&PrivateMessageUpdateForm::builder().read(Some(read)).build(),
&PrivateMessageUpdateForm {
read: Some(read),
..Default::default()
},
)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdatePrivateMessage)?;

View File

@ -18,7 +18,7 @@ impl Perform for ListPrivateMessageReports {
is_admin(&local_user_view)?;
let unresolved_only = self.unresolved_only;
let unresolved_only = self.unresolved_only.unwrap_or_default();
let page = self.page;
let limit = self.limit;
let private_message_reports = PrivateMessageReportQuery {

View File

@ -43,7 +43,10 @@ impl Perform for LeaveAdmin {
Person::update(
&mut context.pool(),
person_id,
&PersonUpdateForm::builder().admin(Some(false)).build(),
&PersonUpdateForm {
admin: Some(false),
..Default::default()
},
)
.await?;

View File

@ -40,9 +40,10 @@ impl Perform for ApproveRegistrationApplication {
RegistrationApplication::update(&mut context.pool(), app_id, &app_form).await?;
// Update the local_user row
let local_user_form = LocalUserUpdateForm::builder()
.accepted_application(Some(data.approve))
.build();
let local_user_form = LocalUserUpdateForm {
accepted_application: Some(data.approve),
..Default::default()
};
let approved_user_id = registration_application.local_user_id;
LocalUser::update(&mut context.pool(), approved_user_id, &local_user_form).await?;

View File

@ -22,8 +22,8 @@ impl Perform for ListRegistrationApplications {
// Make sure user is an admin
is_admin(&local_user_view)?;
let unread_only = data.unread_only;
let verified_email_only = Some(local_site.require_email_verification);
let unread_only = data.unread_only.unwrap_or_default();
let verified_email_only = local_site.require_email_verification;
let page = data.page;
let limit = data.limit;

View File

@ -26,7 +26,6 @@ pub async fn build_comment_response(
context: &LemmyContext,
comment_id: CommentId,
local_user_view: Option<LocalUserView>,
form_id: Option<String>,
recipient_ids: Vec<LocalUserId>,
) -> Result<CommentResponse, LemmyError> {
let person_id = local_user_view.map(|l| l.person.id);
@ -34,7 +33,6 @@ pub async fn build_comment_response(
Ok(CommentResponse {
comment_view,
recipient_ids,
form_id,
})
}
@ -52,7 +50,7 @@ pub async fn build_community_response(
&mut context.pool(),
community_id,
Some(person_id),
Some(is_mod_or_admin),
is_mod_or_admin,
)
.await?;
let discussion_languages = CommunityLanguage::read(&mut context.pool(), community_id).await?;
@ -76,7 +74,7 @@ pub async fn build_post_response(
&mut context.pool(),
post_id,
Some(person_id),
Some(is_mod_or_admin),
is_mod_or_admin,
)
.await?;
Ok(Json(PostResponse { post_view }))

View File

@ -20,8 +20,6 @@ pub struct CreateComment {
pub post_id: PostId,
pub parent_id: Option<CommentId>,
pub language_id: Option<LanguageId>,
/// An optional front-end ID, to help UIs determine where the comment should go.
pub form_id: Option<String>,
pub auth: Sensitive<String>,
}
@ -44,7 +42,6 @@ pub struct EditComment {
pub comment_id: CommentId,
pub content: Option<String>,
pub language_id: Option<LanguageId>,
pub form_id: Option<String>,
pub auth: Sensitive<String>,
}
@ -100,8 +97,6 @@ pub struct SaveComment {
pub struct CommentResponse {
pub comment_view: CommentView,
pub recipient_ids: Vec<LocalUserId>,
/// An optional front end ID, to tell which is coming back
pub form_id: Option<String>,
}
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
@ -131,6 +126,8 @@ pub struct GetComments {
pub post_id: Option<PostId>,
pub parent_id: Option<CommentId>,
pub saved_only: Option<bool>,
pub liked_only: Option<bool>,
pub disliked_only: Option<bool>,
pub auth: Option<Sensitive<String>>,
}

View File

@ -75,6 +75,8 @@ pub struct GetPosts {
pub community_id: Option<CommunityId>,
pub community_name: Option<String>,
pub saved_only: Option<bool>,
pub liked_only: Option<bool>,
pub disliked_only: Option<bool>,
pub moderator_view: Option<bool>,
pub auth: Option<Sensitive<String>>,
}

View File

@ -55,6 +55,7 @@ pub struct GetPrivateMessages {
pub unread_only: Option<bool>,
pub page: Option<i64>,
pub limit: Option<i64>,
pub creator_id: Option<PersonId>,
pub auth: Sensitive<String>,
}

View File

@ -44,7 +44,7 @@ fn html_to_site_metadata(html_bytes: &[u8], url: &Url) -> Result<SiteMetadata, L
.to_lowercase();
if !first_line.starts_with("<!doctype html>") {
return Err(LemmyErrorType::SiteMetadataPageIsNotDoctypeHtml)?;
Err(LemmyErrorType::SiteMetadataPageIsNotDoctypeHtml)?;
}
let mut page = HTML::from_string(html.to_string(), None)?;

View File

@ -79,7 +79,7 @@ pub async fn is_mod_or_admin_opt(
pub fn is_admin(local_user_view: &LocalUserView) -> Result<(), LemmyError> {
if !local_user_view.person.admin {
return Err(LemmyErrorType::NotAnAdmin)?;
Err(LemmyErrorType::NotAnAdmin)?;
}
Ok(())
}
@ -94,7 +94,7 @@ pub fn is_top_mod(
.map(|cm| cm.moderator.id)
.unwrap_or(PersonId(0))
{
return Err(LemmyErrorType::NotTopMod)?;
Err(LemmyErrorType::NotTopMod)?;
}
Ok(())
}
@ -181,12 +181,12 @@ pub fn check_user_valid(
) -> Result<(), LemmyError> {
// Check for a site ban
if is_banned(banned, ban_expires) {
return Err(LemmyErrorType::SiteBan)?;
Err(LemmyErrorType::SiteBan)?;
}
// check for account deletion
if deleted {
return Err(LemmyErrorType::Deleted)?;
Err(LemmyErrorType::Deleted)?;
}
Ok(())
@ -250,7 +250,7 @@ pub async fn check_person_block(
#[tracing::instrument(skip_all)]
pub fn check_downvotes_enabled(score: i16, local_site: &LocalSite) -> Result<(), LemmyError> {
if score == -1 && !local_site.enable_downvotes {
return Err(LemmyErrorType::DownvotesAreDisabled)?;
Err(LemmyErrorType::DownvotesAreDisabled)?;
}
Ok(())
}
@ -261,7 +261,7 @@ pub fn check_private_instance(
local_site: &LocalSite,
) -> Result<(), LemmyError> {
if local_user_view.is_none() && local_site.private_instance {
return Err(LemmyErrorType::InstanceIsPrivate)?;
Err(LemmyErrorType::InstanceIsPrivate)?;
}
Ok(())
}
@ -522,7 +522,7 @@ pub fn check_private_instance_and_federation_enabled(
local_site: &LocalSite,
) -> Result<(), LemmyError> {
if local_site.private_instance && local_site.federation_enabled {
return Err(LemmyErrorType::CantEnablePrivateInstanceAndFederationTogether)?;
Err(LemmyErrorType::CantEnablePrivateInstanceAndFederationTogether)?;
}
Ok(())
}
@ -596,10 +596,11 @@ pub async fn remove_user_data(
Person::update(
pool,
banned_person_id,
&PersonUpdateForm::builder()
.avatar(Some(None))
.banner(Some(None))
.build(),
&PersonUpdateForm {
avatar: Some(None),
banner: Some(None),
..Default::default()
},
)
.await?;
@ -625,7 +626,10 @@ pub async fn remove_user_data(
Community::update(
pool,
community_id,
&CommunityUpdateForm::builder().removed(Some(true)).build(),
&CommunityUpdateForm {
removed: Some(true),
..Default::default()
},
)
.await?;
@ -642,10 +646,11 @@ pub async fn remove_user_data(
Community::update(
pool,
community_id,
&CommunityUpdateForm::builder()
.icon(Some(None))
.banner(Some(None))
.build(),
&CommunityUpdateForm {
icon: Some(None),
banner: Some(None),
..Default::default()
},
)
.await?;
}
@ -679,7 +684,10 @@ pub async fn remove_user_data_in_community(
Comment::update(
pool,
comment_id,
&CommentUpdateForm::builder().removed(Some(true)).build(),
&CommentUpdateForm {
removed: Some(true),
..Default::default()
},
)
.await?;
}
@ -797,12 +805,14 @@ pub fn generate_moderators_url(community_id: &DbUrl) -> Result<DbUrl, LemmyError
/// Sanitize HTML with default options. Additionally, dont allow bypassing markdown
/// links and images
pub fn sanitize_html(data: &str) -> String {
let sanitized = ammonia::Builder::default()
ammonia::Builder::default()
.rm_tags(&["a", "img"])
.clean(data)
.to_string();
// restore markdown quotes
sanitized.replace("&gt;", ">")
.to_string()
// restore markdown quotes
.replace("&gt;", ">")
// restore white space
.replace("&nbsp;", " ")
}
pub fn sanitize_html_opt(data: &Option<String>) -> Option<String> {
@ -839,5 +849,7 @@ mod tests {
assert_eq!(sanitized, " hello");
let sanitized = sanitize_html("<img src='http://example.com'> test");
assert_eq!(sanitized, " test");
let sanitized = sanitize_html("Hello&nbsp;World");
assert_eq!(sanitized, "Hello World");
}
}

View File

@ -129,7 +129,10 @@ pub async fn create_comment(
let updated_comment = Comment::update(
&mut context.pool(),
inserted_comment_id,
&CommentUpdateForm::builder().ap_id(Some(apub_id)).build(),
&CommentUpdateForm {
ap_id: Some(apub_id),
..Default::default()
},
)
.await
.with_lemmy_type(LemmyErrorType::CouldntCreateComment)?;
@ -198,7 +201,6 @@ pub async fn create_comment(
&context,
inserted_comment.id,
Some(local_user_view),
data.form_id.clone(),
recipient_ids,
)
.await?,

View File

@ -49,7 +49,10 @@ pub async fn delete_comment(
let updated_comment = Comment::update(
&mut context.pool(),
comment_id,
&CommentUpdateForm::builder().deleted(Some(deleted)).build(),
&CommentUpdateForm {
deleted: Some(deleted),
..Default::default()
},
)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
@ -82,7 +85,6 @@ pub async fn delete_comment(
&context,
updated_comment_id,
Some(local_user_view),
None,
recipient_ids,
)
.await?,

View File

@ -19,6 +19,6 @@ pub async fn get_comment(
check_private_instance(&local_user_view, &local_site)?;
Ok(Json(
build_comment_response(&context, data.id, local_user_view, None, vec![]).await?,
build_comment_response(&context, data.id, local_user_view, vec![]).await?,
))
}

View File

@ -10,10 +10,11 @@ use lemmy_api_common::{
use lemmy_db_schema::{
source::{
comment::{Comment, CommentUpdateForm},
comment_report::CommentReport,
moderator::{ModRemoveComment, ModRemoveCommentForm},
post::Post,
},
traits::Crud,
traits::{Crud, Reportable},
};
use lemmy_db_views::structs::CommentView;
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
@ -48,11 +49,17 @@ pub async fn remove_comment(
let updated_comment = Comment::update(
&mut context.pool(),
comment_id,
&CommentUpdateForm::builder().removed(Some(removed)).build(),
&CommentUpdateForm {
removed: Some(removed),
..Default::default()
},
)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
CommentReport::resolve_all_for_object(&mut context.pool(), comment_id, local_user_view.person.id)
.await?;
// Mod tables
let form = ModRemoveCommentForm {
mod_person_id: local_user_view.person.id,
@ -91,7 +98,6 @@ pub async fn remove_comment(
&context,
updated_comment_id,
Some(local_user_view),
None,
recipient_ids,
)
.await?,

View File

@ -71,11 +71,12 @@ pub async fn update_comment(
let content = sanitize_html_opt(&content);
let comment_id = data.comment_id;
let form = CommentUpdateForm::builder()
.content(content)
.language_id(data.language_id)
.updated(Some(Some(naive_now())))
.build();
let form = CommentUpdateForm {
content,
language_id: data.language_id,
updated: Some(Some(naive_now())),
..Default::default()
};
let updated_comment = Comment::update(&mut context.pool(), comment_id, &form)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
@ -104,7 +105,6 @@ pub async fn update_comment(
&context,
updated_comment.id,
Some(local_user_view),
data.form_id.clone(),
recipient_ids,
)
.await?,

View File

@ -35,9 +35,10 @@ pub async fn delete_community(
let community = Community::update(
&mut context.pool(),
community_id,
&CommunityUpdateForm::builder()
.deleted(Some(deleted))
.build(),
&CommunityUpdateForm {
deleted: Some(deleted),
..Default::default()
},
)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdateCommunity)?;

View File

@ -15,13 +15,16 @@ pub async fn list_communities(
) -> Result<Json<ListCommunitiesResponse>, LemmyError> {
let local_user_view = local_user_view_from_jwt_opt(data.auth.as_ref(), &context).await;
let local_site = LocalSite::read(&mut context.pool()).await?;
let is_admin = local_user_view.as_ref().map(|luv| is_admin(luv).is_ok());
let is_admin = local_user_view
.as_ref()
.map(|luv| is_admin(luv).is_ok())
.unwrap_or_default();
check_private_instance(&local_user_view, &local_site)?;
let sort = data.sort;
let listing_type = data.type_;
let show_nsfw = data.show_nsfw;
let show_nsfw = data.show_nsfw.unwrap_or_default();
let page = data.page;
let limit = data.limit;
let local_user = local_user_view.map(|l| l.local_user);

View File

@ -35,9 +35,10 @@ pub async fn remove_community(
let community = Community::update(
&mut context.pool(),
community_id,
&CommunityUpdateForm::builder()
.removed(Some(removed))
.build(),
&CommunityUpdateForm {
removed: Some(removed),
..Default::default()
},
)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdateCommunity)?;

View File

@ -65,15 +65,16 @@ pub async fn update_community(
CommunityLanguage::update(&mut context.pool(), languages, community_id).await?;
}
let community_form = CommunityUpdateForm::builder()
.title(title)
.description(description)
.icon(icon)
.banner(banner)
.nsfw(data.nsfw)
.posting_restricted_to_mods(data.posting_restricted_to_mods)
.updated(Some(Some(naive_now())))
.build();
let community_form = CommunityUpdateForm {
title,
description,
icon,
banner,
nsfw: data.nsfw,
posting_restricted_to_mods: data.posting_restricted_to_mods,
updated: Some(Some(naive_now())),
..Default::default()
};
let community_id = data.community_id;
let community = Community::update(&mut context.pool(), community_id, &community_form)

View File

@ -148,7 +148,10 @@ pub async fn create_post(
let updated_post = Post::update(
&mut context.pool(),
inserted_post_id,
&PostUpdateForm::builder().ap_id(Some(apub_id)).build(),
&PostUpdateForm {
ap_id: Some(apub_id),
..Default::default()
},
)
.await
.with_lemmy_type(LemmyErrorType::CouldntCreatePost)?;

View File

@ -45,9 +45,10 @@ pub async fn delete_post(
let post = Post::update(
&mut context.pool(),
data.post_id,
&PostUpdateForm::builder()
.deleted(Some(data.deleted))
.build(),
&PostUpdateForm {
deleted: Some(data.deleted),
..Default::default()
},
)
.await?;

View File

@ -52,14 +52,9 @@ pub async fn get_post(
.await
.is_ok();
let post_view = PostView::read(
&mut context.pool(),
post_id,
person_id,
Some(is_mod_or_admin),
)
.await
.with_lemmy_type(LemmyErrorType::CouldntFindPost)?;
let post_view = PostView::read(&mut context.pool(), post_id, person_id, is_mod_or_admin)
.await
.with_lemmy_type(LemmyErrorType::CouldntFindPost)?;
// Mark the post as read
let post_id = post_view.post.id;
@ -72,7 +67,7 @@ pub async fn get_post(
&mut context.pool(),
community_id,
person_id,
Some(is_mod_or_admin),
is_mod_or_admin,
)
.await
.with_lemmy_type(LemmyErrorType::CouldntFindCommunity)?;

View File

@ -11,8 +11,9 @@ use lemmy_db_schema::{
source::{
moderator::{ModRemovePost, ModRemovePostForm},
post::{Post, PostUpdateForm},
post_report::PostReport,
},
traits::Crud,
traits::{Crud, Reportable},
};
use lemmy_utils::error::LemmyError;
@ -47,10 +48,16 @@ pub async fn remove_post(
let post = Post::update(
&mut context.pool(),
post_id,
&PostUpdateForm::builder().removed(Some(removed)).build(),
&PostUpdateForm {
removed: Some(removed),
..Default::default()
},
)
.await?;
PostReport::resolve_all_for_object(&mut context.pool(), post_id, local_user_view.person.id)
.await?;
// Mod tables
let form = ModRemovePostForm {
mod_person_id: local_user_view.person.id,

View File

@ -93,18 +93,19 @@ pub async fn update_post(
)
.await?;
let post_form = PostUpdateForm::builder()
.name(name)
.url(url)
.body(body)
.nsfw(data.nsfw)
.embed_title(embed_title)
.embed_description(embed_description)
.embed_video_url(embed_video_url)
.language_id(data.language_id)
.thumbnail_url(Some(thumbnail_url))
.updated(Some(Some(naive_now())))
.build();
let post_form = PostUpdateForm {
name,
url,
body,
nsfw: data.nsfw,
embed_title,
embed_description,
embed_video_url,
language_id: data.language_id,
thumbnail_url: Some(thumbnail_url),
updated: Some(Some(naive_now())),
..Default::default()
};
let post_id = data.post_id;
let updated_post = Post::update(&mut context.pool(), post_id, &post_form)

View File

@ -67,9 +67,10 @@ pub async fn create_private_message(
PrivateMessage::update(
&mut context.pool(),
inserted_private_message.id,
&PrivateMessageUpdateForm::builder()
.ap_id(Some(apub_id))
.build(),
&PrivateMessageUpdateForm {
ap_id: Some(apub_id),
..Default::default()
},
)
.await
.with_lemmy_type(LemmyErrorType::CouldntCreatePrivateMessage)?;

View File

@ -33,9 +33,10 @@ pub async fn delete_private_message(
let private_message = PrivateMessage::update(
&mut context.pool(),
private_message_id,
&PrivateMessageUpdateForm::builder()
.deleted(Some(deleted))
.build(),
&PrivateMessageUpdateForm {
deleted: Some(deleted),
..Default::default()
},
)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdatePrivateMessage)?;

View File

@ -17,11 +17,13 @@ pub async fn get_private_message(
let page = data.page;
let limit = data.limit;
let unread_only = data.unread_only;
let unread_only = data.unread_only.unwrap_or_default();
let creator_id = data.creator_id;
let mut messages = PrivateMessageQuery {
page,
limit,
unread_only,
creator_id,
}
.list(&mut context.pool(), person_id)
.await?;

View File

@ -44,10 +44,11 @@ pub async fn update_private_message(
PrivateMessage::update(
&mut context.pool(),
private_message_id,
&PrivateMessageUpdateForm::builder()
.content(Some(content))
.updated(Some(Some(naive_now())))
.build(),
&PrivateMessageUpdateForm {
content: Some(content),
updated: Some(Some(naive_now())),
..Default::default()
},
)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdatePrivateMessage)?;

View File

@ -60,18 +60,19 @@ pub async fn create_site(
let sidebar = sanitize_html_opt(&data.sidebar);
let description = sanitize_html_opt(&data.description);
let site_form = SiteUpdateForm::builder()
.name(Some(name))
.sidebar(diesel_option_overwrite(sidebar))
.description(diesel_option_overwrite(description))
.icon(diesel_option_overwrite_to_url(&data.icon)?)
.banner(diesel_option_overwrite_to_url(&data.banner)?)
.actor_id(Some(actor_id))
.last_refreshed_at(Some(naive_now()))
.inbox_url(inbox_url)
.private_key(Some(Some(keypair.private_key)))
.public_key(Some(keypair.public_key))
.build();
let site_form = SiteUpdateForm {
name: Some(name),
sidebar: diesel_option_overwrite(sidebar),
description: diesel_option_overwrite(description),
icon: diesel_option_overwrite_to_url(&data.icon)?,
banner: diesel_option_overwrite_to_url(&data.banner)?,
actor_id: Some(actor_id),
last_refreshed_at: Some(naive_now()),
inbox_url,
private_key: Some(Some(keypair.private_key)),
public_key: Some(keypair.public_key),
..Default::default()
};
let site_id = local_site.site_id;
@ -81,45 +82,47 @@ pub async fn create_site(
let default_theme = sanitize_html_opt(&data.default_theme);
let legal_information = sanitize_html_opt(&data.legal_information);
let local_site_form = LocalSiteUpdateForm::builder()
let local_site_form = LocalSiteUpdateForm {
// Set the site setup to true
.site_setup(Some(true))
.enable_downvotes(data.enable_downvotes)
.registration_mode(data.registration_mode)
.enable_nsfw(data.enable_nsfw)
.community_creation_admin_only(data.community_creation_admin_only)
.require_email_verification(data.require_email_verification)
.application_question(diesel_option_overwrite(application_question))
.private_instance(data.private_instance)
.default_theme(default_theme)
.default_post_listing_type(data.default_post_listing_type)
.legal_information(diesel_option_overwrite(legal_information))
.application_email_admins(data.application_email_admins)
.hide_modlog_mod_names(data.hide_modlog_mod_names)
.updated(Some(Some(naive_now())))
.slur_filter_regex(diesel_option_overwrite(data.slur_filter_regex.clone()))
.actor_name_max_length(data.actor_name_max_length)
.federation_enabled(data.federation_enabled)
.captcha_enabled(data.captcha_enabled)
.captcha_difficulty(data.captcha_difficulty.clone())
.build();
site_setup: Some(true),
enable_downvotes: data.enable_downvotes,
registration_mode: data.registration_mode,
enable_nsfw: data.enable_nsfw,
community_creation_admin_only: data.community_creation_admin_only,
require_email_verification: data.require_email_verification,
application_question: diesel_option_overwrite(application_question),
private_instance: data.private_instance,
default_theme,
default_post_listing_type: data.default_post_listing_type,
legal_information: diesel_option_overwrite(legal_information),
application_email_admins: data.application_email_admins,
hide_modlog_mod_names: data.hide_modlog_mod_names,
updated: Some(Some(naive_now())),
slur_filter_regex: diesel_option_overwrite(data.slur_filter_regex.clone()),
actor_name_max_length: data.actor_name_max_length,
federation_enabled: data.federation_enabled,
captcha_enabled: data.captcha_enabled,
captcha_difficulty: data.captcha_difficulty.clone(),
..Default::default()
};
LocalSite::update(&mut context.pool(), &local_site_form).await?;
let local_site_rate_limit_form = LocalSiteRateLimitUpdateForm::builder()
.message(data.rate_limit_message)
.message_per_second(data.rate_limit_message_per_second)
.post(data.rate_limit_post)
.post_per_second(data.rate_limit_post_per_second)
.register(data.rate_limit_register)
.register_per_second(data.rate_limit_register_per_second)
.image(data.rate_limit_image)
.image_per_second(data.rate_limit_image_per_second)
.comment(data.rate_limit_comment)
.comment_per_second(data.rate_limit_comment_per_second)
.search(data.rate_limit_search)
.search_per_second(data.rate_limit_search_per_second)
.build();
let local_site_rate_limit_form = LocalSiteRateLimitUpdateForm {
message: data.rate_limit_message,
message_per_second: data.rate_limit_message_per_second,
post: data.rate_limit_post,
post_per_second: data.rate_limit_post_per_second,
register: data.rate_limit_register,
register_per_second: data.rate_limit_register_per_second,
image: data.rate_limit_image,
image_per_second: data.rate_limit_image_per_second,
comment: data.rate_limit_comment,
comment_per_second: data.rate_limit_comment_per_second,
search: data.rate_limit_search,
search_per_second: data.rate_limit_search_per_second,
..Default::default()
};
LocalSiteRateLimit::update(&mut context.pool(), &local_site_rate_limit_form).await?;
@ -144,7 +147,7 @@ pub async fn create_site(
fn validate_create_payload(local_site: &LocalSite, create_site: &CreateSite) -> LemmyResult<()> {
// Make sure the site hasn't already been set up...
if local_site.site_setup {
return Err(LemmyErrorType::SiteAlreadyExists)?;
Err(LemmyErrorType::SiteAlreadyExists)?;
};
// Check that the slur regex compiles, and returns the regex if valid...

View File

@ -63,14 +63,15 @@ pub async fn update_site(
let sidebar = sanitize_html_opt(&data.sidebar);
let description = sanitize_html_opt(&data.description);
let site_form = SiteUpdateForm::builder()
.name(name)
.sidebar(diesel_option_overwrite(sidebar))
.description(diesel_option_overwrite(description))
.icon(diesel_option_overwrite_to_url(&data.icon)?)
.banner(diesel_option_overwrite_to_url(&data.banner)?)
.updated(Some(Some(naive_now())))
.build();
let site_form = SiteUpdateForm {
name,
sidebar: diesel_option_overwrite(sidebar),
description: diesel_option_overwrite(description),
icon: diesel_option_overwrite_to_url(&data.icon)?,
banner: diesel_option_overwrite_to_url(&data.banner)?,
updated: Some(Some(naive_now())),
..Default::default()
};
Site::update(&mut context.pool(), site.id, &site_form)
.await
@ -82,46 +83,48 @@ pub async fn update_site(
let default_theme = sanitize_html_opt(&data.default_theme);
let legal_information = sanitize_html_opt(&data.legal_information);
let local_site_form = LocalSiteUpdateForm::builder()
.enable_downvotes(data.enable_downvotes)
.registration_mode(data.registration_mode)
.enable_nsfw(data.enable_nsfw)
.community_creation_admin_only(data.community_creation_admin_only)
.require_email_verification(data.require_email_verification)
.application_question(diesel_option_overwrite(application_question))
.private_instance(data.private_instance)
.default_theme(default_theme)
.default_post_listing_type(data.default_post_listing_type)
.legal_information(diesel_option_overwrite(legal_information))
.application_email_admins(data.application_email_admins)
.hide_modlog_mod_names(data.hide_modlog_mod_names)
.updated(Some(Some(naive_now())))
.slur_filter_regex(diesel_option_overwrite(data.slur_filter_regex.clone()))
.actor_name_max_length(data.actor_name_max_length)
.federation_enabled(data.federation_enabled)
.captcha_enabled(data.captcha_enabled)
.captcha_difficulty(data.captcha_difficulty.clone())
.reports_email_admins(data.reports_email_admins)
.build();
let local_site_form = LocalSiteUpdateForm {
enable_downvotes: data.enable_downvotes,
registration_mode: data.registration_mode,
enable_nsfw: data.enable_nsfw,
community_creation_admin_only: data.community_creation_admin_only,
require_email_verification: data.require_email_verification,
application_question: diesel_option_overwrite(application_question),
private_instance: data.private_instance,
default_theme,
default_post_listing_type: data.default_post_listing_type,
legal_information: diesel_option_overwrite(legal_information),
application_email_admins: data.application_email_admins,
hide_modlog_mod_names: data.hide_modlog_mod_names,
updated: Some(Some(naive_now())),
slur_filter_regex: diesel_option_overwrite(data.slur_filter_regex.clone()),
actor_name_max_length: data.actor_name_max_length,
federation_enabled: data.federation_enabled,
captcha_enabled: data.captcha_enabled,
captcha_difficulty: data.captcha_difficulty.clone(),
reports_email_admins: data.reports_email_admins,
..Default::default()
};
let update_local_site = LocalSite::update(&mut context.pool(), &local_site_form)
.await
.ok();
let local_site_rate_limit_form = LocalSiteRateLimitUpdateForm::builder()
.message(data.rate_limit_message)
.message_per_second(data.rate_limit_message_per_second)
.post(data.rate_limit_post)
.post_per_second(data.rate_limit_post_per_second)
.register(data.rate_limit_register)
.register_per_second(data.rate_limit_register_per_second)
.image(data.rate_limit_image)
.image_per_second(data.rate_limit_image_per_second)
.comment(data.rate_limit_comment)
.comment_per_second(data.rate_limit_comment_per_second)
.search(data.rate_limit_search)
.search_per_second(data.rate_limit_search_per_second)
.build();
let local_site_rate_limit_form = LocalSiteRateLimitUpdateForm {
message: data.rate_limit_message,
message_per_second: data.rate_limit_message_per_second,
post: data.rate_limit_post,
post_per_second: data.rate_limit_post_per_second,
register: data.rate_limit_register,
register_per_second: data.rate_limit_register_per_second,
image: data.rate_limit_image,
image_per_second: data.rate_limit_image_per_second,
comment: data.rate_limit_comment,
comment_per_second: data.rate_limit_comment_per_second,
search: data.rate_limit_search,
search_per_second: data.rate_limit_search_per_second,
..Default::default()
};
LocalSiteRateLimit::update(&mut context.pool(), &local_site_rate_limit_form)
.await

View File

@ -158,10 +158,11 @@ impl ActivityHandler for BlockUser {
let blocked_person = Person::update(
&mut context.pool(),
blocked_person.id,
&PersonUpdateForm::builder()
.banned(Some(true))
.ban_expires(Some(expires))
.build(),
&PersonUpdateForm {
banned: Some(true),
ban_expires: Some(expires),
..Default::default()
},
)
.await?;
if self.remove_data.unwrap_or(false) {

View File

@ -106,10 +106,11 @@ impl ActivityHandler for UndoBlockUser {
let blocked_person = Person::update(
&mut context.pool(),
blocked_person.id,
&PersonUpdateForm::builder()
.banned(Some(false))
.ban_expires(Some(expires))
.build(),
&PersonUpdateForm {
banned: Some(false),
ban_expires: Some(expires),
..Default::default()
},
)
.await?;

View File

@ -162,9 +162,10 @@ impl ActivityHandler for CollectionAdd {
let post = ObjectId::<ApubPost>::from(self.object)
.dereference(context)
.await?;
let form = PostUpdateForm::builder()
.featured_community(Some(true))
.build();
let form = PostUpdateForm {
featured_community: Some(true),
..Default::default()
};
Post::update(&mut context.pool(), post.id, &form).await?;
}
}

View File

@ -150,9 +150,10 @@ impl ActivityHandler for CollectionRemove {
let post = ObjectId::<ApubPost>::from(self.object)
.dereference(context)
.await?;
let form = PostUpdateForm::builder()
.featured_community(Some(false))
.build();
let form = PostUpdateForm {
featured_community: Some(false),
..Default::default()
};
Post::update(&mut context.pool(), post.id, &form).await?;
}
}

View File

@ -57,7 +57,10 @@ impl ActivityHandler for LockPage {
}
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), Self::Error> {
let form = PostUpdateForm::builder().locked(Some(true)).build();
let form = PostUpdateForm {
locked: Some(true),
..Default::default()
};
let post = self.object.dereference(context).await?;
Post::update(&mut context.pool(), post.id, &form).await?;
Ok(())
@ -94,7 +97,10 @@ impl ActivityHandler for UndoLockPage {
}
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), Self::Error> {
let form = PostUpdateForm::builder().locked(Some(false)).build();
let form = PostUpdateForm {
locked: Some(false),
..Default::default()
};
let post = self.object.object.dereference(context).await?;
Post::update(&mut context.pool(), post.id, &form).await?;
Ok(())

View File

@ -12,6 +12,7 @@ use lemmy_api_common::{context::LemmyContext, utils::sanitize_html_opt};
use lemmy_db_schema::{
source::{
comment::{Comment, CommentUpdateForm},
comment_report::CommentReport,
community::{Community, CommunityUpdateForm},
moderator::{
ModRemoveComment,
@ -22,8 +23,9 @@ use lemmy_db_schema::{
ModRemovePostForm,
},
post::{Post, PostUpdateForm},
post_report::PostReport,
},
traits::Crud,
traits::{Crud, Reportable},
};
use lemmy_utils::error::{LemmyError, LemmyErrorType};
use url::Url;
@ -123,11 +125,15 @@ pub(in crate::activities) async fn receive_remove_action(
Community::update(
&mut context.pool(),
community.id,
&CommunityUpdateForm::builder().removed(Some(true)).build(),
&CommunityUpdateForm {
removed: Some(true),
..Default::default()
},
)
.await?;
}
DeletableObjects::Post(post) => {
PostReport::resolve_all_for_object(&mut context.pool(), post.id, actor.id).await?;
let form = ModRemovePostForm {
mod_person_id: actor.id,
post_id: post.id,
@ -138,11 +144,15 @@ pub(in crate::activities) async fn receive_remove_action(
Post::update(
&mut context.pool(),
post.id,
&PostUpdateForm::builder().removed(Some(true)).build(),
&PostUpdateForm {
removed: Some(true),
..Default::default()
},
)
.await?;
}
DeletableObjects::Comment(comment) => {
CommentReport::resolve_all_for_object(&mut context.pool(), comment.id, actor.id).await?;
let form = ModRemoveCommentForm {
mod_person_id: actor.id,
comment_id: comment.id,
@ -153,7 +163,10 @@ pub(in crate::activities) async fn receive_remove_action(
Comment::update(
&mut context.pool(),
comment.id,
&CommentUpdateForm::builder().removed(Some(true)).build(),
&CommentUpdateForm {
removed: Some(true),
..Default::default()
},
)
.await?;
}

View File

@ -252,16 +252,17 @@ async fn receive_delete_action(
if community.local {
let mod_: Person = actor.dereference(context).await?.deref().clone();
let object = DeletableObjects::Community(community.clone());
let c: Community = community.deref().deref().clone();
let c: Community = community.deref().clone();
send_apub_delete_in_community(mod_, c, object, None, true, context).await?;
}
Community::update(
&mut context.pool(),
community.id,
&CommunityUpdateForm::builder()
.deleted(Some(deleted))
.build(),
&CommunityUpdateForm {
deleted: Some(deleted),
..Default::default()
},
)
.await?;
}
@ -270,7 +271,10 @@ async fn receive_delete_action(
Post::update(
&mut context.pool(),
post.id,
&PostUpdateForm::builder().deleted(Some(deleted)).build(),
&PostUpdateForm {
deleted: Some(deleted),
..Default::default()
},
)
.await?;
}
@ -280,7 +284,10 @@ async fn receive_delete_action(
Comment::update(
&mut context.pool(),
comment.id,
&CommentUpdateForm::builder().deleted(Some(deleted)).build(),
&CommentUpdateForm {
deleted: Some(deleted),
..Default::default()
},
)
.await?;
}
@ -289,9 +296,10 @@ async fn receive_delete_action(
PrivateMessage::update(
&mut context.pool(),
pm.id,
&PrivateMessageUpdateForm::builder()
.deleted(Some(deleted))
.build(),
&PrivateMessageUpdateForm {
deleted: Some(deleted),
..Default::default()
},
)
.await?;
}

View File

@ -113,7 +113,10 @@ impl UndoDelete {
Community::update(
&mut context.pool(),
community.id,
&CommunityUpdateForm::builder().removed(Some(false)).build(),
&CommunityUpdateForm {
removed: Some(false),
..Default::default()
},
)
.await?;
}
@ -128,7 +131,10 @@ impl UndoDelete {
Post::update(
&mut context.pool(),
post.id,
&PostUpdateForm::builder().removed(Some(false)).build(),
&PostUpdateForm {
removed: Some(false),
..Default::default()
},
)
.await?;
}
@ -143,7 +149,10 @@ impl UndoDelete {
Comment::update(
&mut context.pool(),
comment.id,
&CommentUpdateForm::builder().removed(Some(false)).build(),
&CommentUpdateForm {
removed: Some(false),
..Default::default()
},
)
.await?;
}

View File

@ -93,7 +93,9 @@ pub(crate) async fn verify_person_in_community(
) -> Result<(), LemmyError> {
let person = person_id.dereference(context).await?;
if person.banned {
return Err(LemmyErrorType::PersonIsBannedFromSite)?;
return Err(LemmyErrorType::PersonIsBannedFromSite(
person.actor_id.to_string(),
))?;
}
let person_id = person.id;
let community_id = community.id;
@ -139,7 +141,7 @@ pub(crate) async fn verify_mod_action(
pub(crate) fn verify_is_public(to: &[Url], cc: &[Url]) -> Result<(), LemmyError> {
if ![to, cc].iter().any(|set| set.contains(&public())) {
return Err(LemmyErrorType::ObjectIsNotPublic)?;
Err(LemmyErrorType::ObjectIsNotPublic)?;
}
Ok(())
}
@ -153,7 +155,7 @@ where
{
let b: ObjectId<ApubCommunity> = b.into();
if a != &b {
return Err(LemmyErrorType::InvalidCommunity)?;
Err(LemmyErrorType::InvalidCommunity)?;
}
Ok(())
}

View File

@ -34,7 +34,14 @@ pub async fn list_comments(
};
let sort = data.sort;
let max_depth = data.max_depth;
let saved_only = data.saved_only;
let saved_only = data.saved_only.unwrap_or_default();
let liked_only = data.liked_only.unwrap_or_default();
let disliked_only = data.disliked_only.unwrap_or_default();
if liked_only && disliked_only {
return Err(LemmyError::from(LemmyErrorType::ContradictingFilters));
}
let page = data.page;
let limit = data.limit;
let parent_id = data.parent_id;
@ -59,6 +66,8 @@ pub async fn list_comments(
sort,
max_depth,
saved_only,
liked_only,
disliked_only,
community_id,
parent_path: parent_path_cloned,
post_id,

View File

@ -34,9 +34,15 @@ pub async fn list_posts(
} else {
data.community_id
};
let saved_only = data.saved_only;
let saved_only = data.saved_only.unwrap_or_default();
let moderator_view = data.moderator_view;
let liked_only = data.liked_only.unwrap_or_default();
let disliked_only = data.disliked_only.unwrap_or_default();
if liked_only && disliked_only {
return Err(LemmyError::from(LemmyErrorType::ContradictingFilters));
}
let moderator_view = data.moderator_view.unwrap_or_default();
let listing_type = Some(listing_type_with_default(
data.type_,
@ -50,6 +56,8 @@ pub async fn list_posts(
sort,
community_id,
saved_only,
liked_only,
disliked_only,
moderator_view,
page,
limit,

View File

@ -54,7 +54,7 @@ pub async fn get_community(
&mut context.pool(),
community_id,
person_id,
Some(is_mod_or_admin),
is_mod_or_admin,
)
.await
.with_lemmy_type(LemmyErrorType::CouldntFindCommunity)?;

View File

@ -50,11 +50,11 @@ pub async fn read_person(
let sort = data.sort;
let page = data.page;
let limit = data.limit;
let saved_only = data.saved_only;
let saved_only = data.saved_only.unwrap_or_default();
let community_id = data.community_id;
// If its saved only, you don't care what creator it was
// Or, if its not saved, then you only want it for that specific creator
let creator_id = if !saved_only.unwrap_or(false) {
let creator_id = if !saved_only {
Some(person_details_id)
} else {
None
@ -65,7 +65,7 @@ pub async fn read_person(
saved_only,
local_user: local_user_view.as_ref(),
community_id,
is_profile_view: Some(true),
is_profile_view: true,
page,
limit,
creator_id,
@ -75,14 +75,13 @@ pub async fn read_person(
.await?;
let comments = CommentQuery {
local_user: (local_user_view.as_ref()),
sort: (sort.map(post_to_comment_sort_type)),
saved_only: (saved_only),
show_deleted_and_removed: (Some(false)),
community_id: (community_id),
is_profile_view: Some(true),
page: (page),
limit: (limit),
local_user: local_user_view.as_ref(),
sort: sort.map(post_to_comment_sort_type),
saved_only,
community_id,
is_profile_view: true,
page,
limit,
creator_id,
..Default::default()
}

View File

@ -58,11 +58,11 @@ async fn convert_response(
}
Community(c) => {
removed_or_deleted = c.deleted || c.removed;
res.community = Some(CommunityView::read(pool, c.id, user_id, None).await?)
res.community = Some(CommunityView::read(pool, c.id, user_id, false).await?)
}
Post(p) => {
removed_or_deleted = p.deleted || p.removed;
res.post = Some(PostView::read(pool, p.id, user_id, None).await?)
res.post = Some(PostView::read(pool, p.id, user_id, false).await?)
}
Comment(c) => {
removed_or_deleted = c.deleted || c.removed;

View File

@ -25,7 +25,10 @@ pub async fn search(
check_private_instance(&local_user_view, &local_site)?;
let is_admin = local_user_view.as_ref().map(|luv| is_admin(luv).is_ok());
let is_admin = local_user_view
.as_ref()
.map(|luv| is_admin(luv).is_ok())
.unwrap_or_default();
let mut posts = Vec::new();
let mut comments = Vec::new();

View File

@ -78,7 +78,7 @@ fn check_apub_id_valid(apub_id: &Url, local_site_data: &LocalSiteData) -> Result
.map(|l| l.federation_enabled)
.unwrap_or(true)
{
return Err(LemmyErrorType::FederationDisabled)?;
Err(LemmyErrorType::FederationDisabled)?;
}
if local_site_data
@ -86,7 +86,7 @@ fn check_apub_id_valid(apub_id: &Url, local_site_data: &LocalSiteData) -> Result
.iter()
.any(|i| domain.eq(&i.domain))
{
return Err(LemmyErrorType::DomainBlocked(domain))?;
Err(LemmyErrorType::DomainBlocked(domain.clone()))?;
}
// Only check this if there are instances in the allowlist
@ -96,7 +96,7 @@ fn check_apub_id_valid(apub_id: &Url, local_site_data: &LocalSiteData) -> Result
.iter()
.any(|i| domain.eq(&i.domain))
{
return Err(LemmyErrorType::DomainNotInAllowList(domain))?;
Err(LemmyErrorType::DomainNotInAllowList(domain))?;
}
Ok(())

View File

@ -78,7 +78,10 @@ impl Object for ApubComment {
#[tracing::instrument(skip_all)]
async fn delete(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
if !self.deleted {
let form = CommentUpdateForm::builder().deleted(Some(true)).build();
let form = CommentUpdateForm {
deleted: Some(true),
..Default::default()
};
Comment::update(&mut context.pool(), self.id, &form).await?;
}
Ok(())

View File

@ -76,7 +76,10 @@ impl Object for ApubCommunity {
#[tracing::instrument(skip_all)]
async fn delete(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
let form = CommunityUpdateForm::builder().deleted(Some(true)).build();
let form = CommunityUpdateForm {
deleted: Some(true),
..Default::default()
};
Community::update(&mut context.pool(), self.id, &form).await?;
Ok(())
}

View File

@ -81,7 +81,10 @@ impl Object for ApubPerson {
#[tracing::instrument(skip_all)]
async fn delete(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
let form = PersonUpdateForm::builder().deleted(Some(true)).build();
let form = PersonUpdateForm {
deleted: Some(true),
..Default::default()
};
DbPerson::update(&mut context.pool(), self.id, &form).await?;
Ok(())
}

View File

@ -99,7 +99,10 @@ impl Object for ApubPost {
#[tracing::instrument(skip_all)]
async fn delete(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
if !self.deleted {
let form = PostUpdateForm::builder().deleted(Some(true)).build();
let form = PostUpdateForm {
deleted: Some(true),
..Default::default()
};
Post::update(&mut context.pool(), self.id, &form).await?;
}
Ok(())

View File

@ -107,7 +107,9 @@ impl Object for ApubPrivateMessage {
check_apub_id_valid_with_strictness(note.id.inner(), false, context).await?;
let person = note.attributed_to.dereference(context).await?;
if person.banned {
return Err(LemmyErrorType::PersonIsBannedFromSite)?;
return Err(LemmyErrorType::PersonIsBannedFromSite(
person.actor_id.to_string(),
))?;
}
Ok(())
}

View File

@ -145,14 +145,20 @@ mod tests {
Comment::update(
pool,
inserted_comment.id,
&CommentUpdateForm::builder().removed(Some(true)).build(),
&CommentUpdateForm {
removed: Some(true),
..Default::default()
},
)
.await
.unwrap();
Comment::update(
pool,
inserted_child_comment.id,
&CommentUpdateForm::builder().removed(Some(true)).build(),
&CommentUpdateForm {
removed: Some(true),
..Default::default()
},
)
.await
.unwrap();

View File

@ -235,7 +235,10 @@ mod tests {
Comment::update(
pool,
inserted_comment.id,
&CommentUpdateForm::builder().removed(Some(true)).build(),
&CommentUpdateForm {
removed: Some(true),
..Default::default()
},
)
.await
.unwrap();
@ -246,7 +249,10 @@ mod tests {
Comment::update(
pool,
inserted_comment.id,
&CommentUpdateForm::builder().removed(Some(false)).build(),
&CommentUpdateForm {
removed: Some(false),
..Default::default()
},
)
.await
.unwrap();
@ -254,7 +260,10 @@ mod tests {
Comment::update(
pool,
inserted_comment.id,
&CommentUpdateForm::builder().deleted(Some(true)).build(),
&CommentUpdateForm {
deleted: Some(true),
..Default::default()
},
)
.await
.unwrap();
@ -265,7 +274,10 @@ mod tests {
Comment::update(
pool,
inserted_comment.id,
&CommentUpdateForm::builder().removed(Some(true)).build(),
&CommentUpdateForm {
removed: Some(true),
..Default::default()
},
)
.await
.unwrap();

View File

@ -160,7 +160,10 @@ mod tests {
Community::update(
pool,
inserted_community.id,
&CommunityUpdateForm::builder().deleted(Some(true)).build(),
&CommunityUpdateForm {
deleted: Some(true),
..Default::default()
},
)
.await
.unwrap();
@ -171,7 +174,10 @@ mod tests {
Community::update(
pool,
inserted_community.id,
&CommunityUpdateForm::builder().deleted(Some(false)).build(),
&CommunityUpdateForm {
deleted: Some(false),
..Default::default()
},
)
.await
.unwrap();
@ -179,7 +185,10 @@ mod tests {
Community::update(
pool,
inserted_community.id,
&CommunityUpdateForm::builder().removed(Some(true)).build(),
&CommunityUpdateForm {
removed: Some(true),
..Default::default()
},
)
.await
.unwrap();
@ -190,7 +199,10 @@ mod tests {
Community::update(
pool,
inserted_community.id,
&CommunityUpdateForm::builder().deleted(Some(true)).build(),
&CommunityUpdateForm {
deleted: Some(true),
..Default::default()
},
)
.await
.unwrap();

View File

@ -372,9 +372,10 @@ mod tests {
published: inserted_comment_saved.published,
};
let comment_update_form = CommentUpdateForm::builder()
.content(Some("A test comment".into()))
.build();
let comment_update_form = CommentUpdateForm {
content: Some("A test comment".into()),
..Default::default()
};
let updated_comment = Comment::update(pool, inserted_comment.id, &comment_update_form)
.await

View File

@ -1,6 +1,9 @@
use crate::{
newtypes::{CommentReportId, PersonId},
schema::comment_report::dsl::{comment_report, resolved, resolver_id, updated},
newtypes::{CommentId, CommentReportId, PersonId},
schema::comment_report::{
comment_id,
dsl::{comment_report, resolved, resolver_id, updated},
},
source::comment_report::{CommentReport, CommentReportForm},
traits::Reportable,
utils::{get_conn, naive_now, DbPool},
@ -17,6 +20,7 @@ use diesel_async::RunQueryDsl;
impl Reportable for CommentReport {
type Form = CommentReportForm;
type IdType = CommentReportId;
type ObjectIdType = CommentId;
/// creates a comment report and returns it
///
/// * `conn` - the postgres connection
@ -53,6 +57,22 @@ impl Reportable for CommentReport {
.await
}
async fn resolve_all_for_object(
pool: &mut DbPool<'_>,
comment_id_: CommentId,
by_resolver_id: PersonId,
) -> Result<usize, Error> {
let conn = &mut get_conn(pool).await?;
update(comment_report.filter(comment_id.eq(comment_id_)))
.set((
resolved.eq(true),
resolver_id.eq(by_resolver_id),
updated.eq(naive_now()),
))
.execute(conn)
.await
}
/// unresolve a comment report
///
/// * `conn` - the postgres connection

View File

@ -1,6 +1,6 @@
use crate::{
newtypes::{CommunityId, DbUrl, PersonId},
schema::{community, instance},
schema::{community, community_follower, instance},
source::{
actor_language::CommunityLanguage,
community::{
@ -19,7 +19,18 @@ use crate::{
utils::{functions::lower, get_conn, DbPool},
SubscribedType,
};
use diesel::{dsl::insert_into, result::Error, ExpressionMethods, QueryDsl};
use diesel::{
deserialize,
dsl,
dsl::insert_into,
pg::Pg,
result::Error,
sql_types,
ExpressionMethods,
NullableExpressionMethods,
QueryDsl,
Queryable,
};
use diesel_async::RunQueryDsl;
#[async_trait]
@ -214,6 +225,21 @@ impl CommunityFollower {
None => SubscribedType::NotSubscribed,
}
}
pub fn select_subscribed_type() -> dsl::Nullable<community_follower::pending> {
community_follower::pending.nullable()
}
}
impl Queryable<sql_types::Nullable<sql_types::Bool>, Pg> for SubscribedType {
type Row = Option<bool>;
fn build(row: Self::Row) -> deserialize::Result<Self> {
Ok(match row {
Some(true) => SubscribedType::Pending,
Some(false) => SubscribedType::Subscribed,
None => SubscribedType::NotSubscribed,
})
}
}
#[async_trait]
@ -449,9 +475,10 @@ mod tests {
let read_community = Community::read(pool, inserted_community.id).await.unwrap();
let update_community_form = CommunityUpdateForm::builder()
.title(Some("nada".to_owned()))
.build();
let update_community_form = CommunityUpdateForm {
title: Some("nada".to_owned()),
..Default::default()
};
let updated_community = Community::update(pool, inserted_community.id, &update_community_form)
.await
.unwrap();

View File

@ -253,9 +253,10 @@ mod tests {
let read_person = Person::read(pool, inserted_person.id).await.unwrap();
let update_person_form = PersonUpdateForm::builder()
.actor_id(Some(inserted_person.actor_id.clone()))
.build();
let update_person_form = PersonUpdateForm {
actor_id: Some(inserted_person.actor_id.clone()),
..Default::default()
};
let updated_person = Person::update(pool, inserted_person.id, &update_person_form)
.await
.unwrap();

View File

@ -453,9 +453,10 @@ mod tests {
let read_post = Post::read(pool, inserted_post.id).await.unwrap();
let new_post_update = PostUpdateForm::builder()
.name(Some("A test post".into()))
.build();
let new_post_update = PostUpdateForm {
name: Some("A test post".into()),
..Default::default()
};
let updated_post = Post::update(pool, inserted_post.id, &new_post_update)
.await
.unwrap();

View File

@ -1,6 +1,9 @@
use crate::{
newtypes::{PersonId, PostReportId},
schema::post_report::dsl::{post_report, resolved, resolver_id, updated},
newtypes::{PersonId, PostId, PostReportId},
schema::post_report::{
dsl::{post_report, resolved, resolver_id, updated},
post_id,
},
source::post_report::{PostReport, PostReportForm},
traits::Reportable,
utils::{get_conn, naive_now, DbPool},
@ -17,6 +20,7 @@ use diesel_async::RunQueryDsl;
impl Reportable for PostReport {
type Form = PostReportForm;
type IdType = PostReportId;
type ObjectIdType = PostId;
async fn report(pool: &mut DbPool<'_>, post_report_form: &PostReportForm) -> Result<Self, Error> {
let conn = &mut get_conn(pool).await?;
@ -42,6 +46,22 @@ impl Reportable for PostReport {
.await
}
async fn resolve_all_for_object(
pool: &mut DbPool<'_>,
post_id_: PostId,
by_resolver_id: PersonId,
) -> Result<usize, Error> {
let conn = &mut get_conn(pool).await?;
update(post_report.filter(post_id.eq(post_id_)))
.set((
resolved.eq(true),
resolver_id.eq(by_resolver_id),
updated.eq(naive_now()),
))
.execute(conn)
.await
}
async fn unresolve(
pool: &mut DbPool<'_>,
report_id: Self::IdType,
@ -58,3 +78,97 @@ impl Reportable for PostReport {
.await
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
#![allow(clippy::indexing_slicing)]
use super::*;
use crate::{
source::{
community::{Community, CommunityInsertForm},
instance::Instance,
person::{Person, PersonInsertForm},
post::{Post, PostInsertForm},
},
traits::Crud,
utils::build_db_pool_for_tests,
};
use serial_test::serial;
async fn init(pool: &mut DbPool<'_>) -> (Person, PostReport) {
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
.await
.unwrap();
let person_form = PersonInsertForm::builder()
.name("jim".into())
.public_key("pubkey".to_string())
.instance_id(inserted_instance.id)
.build();
let person = Person::create(pool, &person_form).await.unwrap();
let community_form = CommunityInsertForm::builder()
.name("test community_4".to_string())
.title("nada".to_owned())
.public_key("pubkey".to_string())
.instance_id(inserted_instance.id)
.build();
let community = Community::create(pool, &community_form).await.unwrap();
let form = PostInsertForm::builder()
.name("A test post".into())
.creator_id(person.id)
.community_id(community.id)
.build();
let post = Post::create(pool, &form).await.unwrap();
let report_form = PostReportForm {
post_id: post.id,
creator_id: person.id,
reason: "my reason".to_string(),
..Default::default()
};
let report = PostReport::report(pool, &report_form).await.unwrap();
(person, report)
}
#[tokio::test]
#[serial]
async fn test_resolve_post_report() {
let pool = &build_db_pool_for_tests().await;
let pool = &mut pool.into();
let (person, report) = init(pool).await;
let resolved_count = PostReport::resolve(pool, report.id, person.id)
.await
.unwrap();
assert_eq!(resolved_count, 1);
let unresolved_count = PostReport::unresolve(pool, report.id, person.id)
.await
.unwrap();
assert_eq!(unresolved_count, 1);
Person::delete(pool, person.id).await.unwrap();
Post::delete(pool, report.post_id).await.unwrap();
}
#[tokio::test]
#[serial]
async fn test_resolve_all_post_reports() {
let pool = &build_db_pool_for_tests().await;
let pool = &mut pool.into();
let (person, report) = init(pool).await;
let resolved_count = PostReport::resolve_all_for_object(pool, report.post_id, person.id)
.await
.unwrap();
assert_eq!(resolved_count, 1);
Person::delete(pool, person.id).await.unwrap();
Post::delete(pool, report.post_id).await.unwrap();
}
}

View File

@ -142,9 +142,10 @@ mod tests {
.await
.unwrap();
let private_message_update_form = PrivateMessageUpdateForm::builder()
.content(Some("A test private message".into()))
.build();
let private_message_update_form = PrivateMessageUpdateForm {
content: Some("A test private message".into()),
..Default::default()
};
let updated_private_message = PrivateMessage::update(
pool,
inserted_private_message.id,
@ -156,16 +157,20 @@ mod tests {
let deleted_private_message = PrivateMessage::update(
pool,
inserted_private_message.id,
&PrivateMessageUpdateForm::builder()
.deleted(Some(true))
.build(),
&PrivateMessageUpdateForm {
deleted: Some(true),
..Default::default()
},
)
.await
.unwrap();
let marked_read_private_message = PrivateMessage::update(
pool,
inserted_private_message.id,
&PrivateMessageUpdateForm::builder().read(Some(true)).build(),
&PrivateMessageUpdateForm {
read: Some(true),
..Default::default()
},
)
.await
.unwrap();

View File

@ -1,5 +1,5 @@
use crate::{
newtypes::{PersonId, PrivateMessageReportId},
newtypes::{PersonId, PrivateMessageId, PrivateMessageReportId},
schema::private_message_report::dsl::{private_message_report, resolved, resolver_id, updated},
source::private_message_report::{PrivateMessageReport, PrivateMessageReportForm},
traits::Reportable,
@ -17,6 +17,7 @@ use diesel_async::RunQueryDsl;
impl Reportable for PrivateMessageReport {
type Form = PrivateMessageReportForm;
type IdType = PrivateMessageReportId;
type ObjectIdType = PrivateMessageId;
async fn report(
pool: &mut DbPool<'_>,
@ -45,6 +46,15 @@ impl Reportable for PrivateMessageReport {
.await
}
// TODO: this is unused because private message doesnt have remove handler
async fn resolve_all_for_object(
_pool: &mut DbPool<'_>,
_pm_id_: PrivateMessageId,
_by_resolver_id: PersonId,
) -> Result<usize, Error> {
unimplemented!()
}
async fn unresolve(
pool: &mut DbPool<'_>,
report_id: Self::IdType,

View File

@ -66,8 +66,7 @@ pub struct CommentInsertForm {
pub language_id: Option<LanguageId>,
}
#[derive(Debug, Clone, TypedBuilder)]
#[builder(field_defaults(default))]
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "full", derive(AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = comment))]
pub struct CommentUpdateForm {

View File

@ -97,8 +97,7 @@ pub struct CommunityInsertForm {
pub instance_id: InstanceId,
}
#[derive(Debug, Clone, TypedBuilder)]
#[builder(field_defaults(default))]
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "full", derive(AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = community))]
pub struct CommunityUpdateForm {

View File

@ -89,8 +89,7 @@ pub struct LocalSiteInsertForm {
pub reports_email_admins: Option<bool>,
}
#[derive(Clone, TypedBuilder)]
#[builder(field_defaults(default))]
#[derive(Clone, Default)]
#[cfg_attr(feature = "full", derive(AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = local_site))]
pub struct LocalSiteUpdateForm {

View File

@ -57,8 +57,7 @@ pub struct LocalSiteRateLimitInsertForm {
pub search_per_second: Option<i32>,
}
#[derive(Clone, TypedBuilder)]
#[builder(field_defaults(default))]
#[derive(Clone, Default)]
#[cfg_attr(feature = "full", derive(AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = local_site_rate_limit))]
pub struct LocalSiteRateLimitUpdateForm {

View File

@ -90,8 +90,7 @@ pub struct LocalUserInsertForm {
pub infinite_scroll_enabled: Option<bool>,
}
#[derive(Clone, TypedBuilder)]
#[builder(field_defaults(default))]
#[derive(Clone, Default)]
#[cfg_attr(feature = "full", derive(AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = local_user))]
pub struct LocalUserUpdateForm {

View File

@ -89,10 +89,9 @@ pub struct PersonInsertForm {
pub ban_expires: Option<chrono::NaiveDateTime>,
}
#[derive(Clone, TypedBuilder)]
#[derive(Clone, Default)]
#[cfg_attr(feature = "full", derive(AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = person))]
#[builder(field_defaults(default))]
pub struct PersonUpdateForm {
pub display_name: Option<Option<String>>,
pub avatar: Option<Option<DbUrl>>,

View File

@ -85,8 +85,7 @@ pub struct PostInsertForm {
pub featured_local: Option<bool>,
}
#[derive(Debug, Clone, TypedBuilder)]
#[builder(field_defaults(default))]
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "full", derive(AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = post))]
pub struct PostUpdateForm {

View File

@ -30,7 +30,7 @@ pub struct PostReport {
pub updated: Option<chrono::NaiveDateTime>,
}
#[derive(Clone)]
#[derive(Clone, Default)]
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = post_report))]
pub struct PostReportForm {

View File

@ -49,8 +49,7 @@ pub struct PrivateMessageInsertForm {
pub local: Option<bool>,
}
#[derive(Clone, TypedBuilder)]
#[builder(field_defaults(default))]
#[derive(Clone, Default)]
#[cfg_attr(feature = "full", derive(AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = private_message))]
pub struct PrivateMessageUpdateForm {

View File

@ -59,8 +59,7 @@ pub struct SiteInsertForm {
pub instance_id: InstanceId,
}
#[derive(Clone, TypedBuilder)]
#[builder(field_defaults(default))]
#[derive(Clone, Default)]
#[cfg_attr(feature = "full", derive(AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = site))]
pub struct SiteUpdateForm {

View File

@ -155,6 +155,7 @@ pub trait Readable {
pub trait Reportable {
type Form;
type IdType;
type ObjectIdType;
async fn report(pool: &mut DbPool<'_>, form: &Self::Form) -> Result<Self, Error>
where
Self: Sized;
@ -163,6 +164,13 @@ pub trait Reportable {
report_id: Self::IdType,
resolver_id: PersonId,
) -> Result<usize, Error>
where
Self: Sized;
async fn resolve_all_for_object(
pool: &mut DbPool<'_>,
comment_id_: Self::ObjectIdType,
by_resolver_id: PersonId,
) -> Result<usize, Error>
where
Self: Sized;
async fn unresolve(

View File

@ -28,7 +28,7 @@ use lemmy_db_schema::{
source::{
comment::Comment,
comment_report::CommentReport,
community::{Community, CommunityPersonBan},
community::Community,
person::Person,
post::Post,
},
@ -71,7 +71,7 @@ fn queries<'a>() -> Queries<
person::all_columns,
aliases::person1.fields(person::all_columns),
comment_aggregates::all_columns,
community_person_ban::all_columns.nullable(),
community_person_ban::id.nullable().is_not_null(),
comment_like::score.nullable(),
aliases::person2.fields(person::all_columns).nullable(),
);
@ -113,7 +113,7 @@ fn queries<'a>() -> Queries<
query = query.filter(post::community_id.eq(community_id));
}
if options.unresolved_only.unwrap_or(false) {
if options.unresolved_only {
query = query.filter(comment_report::resolved.eq(false));
}
@ -206,7 +206,7 @@ pub struct CommentReportQuery {
pub community_id: Option<CommunityId>,
pub page: Option<i64>,
pub limit: Option<i64>,
pub unresolved_only: Option<bool>,
pub unresolved_only: bool,
}
impl CommentReportQuery {
@ -228,7 +228,7 @@ impl JoinView for CommentReportView {
Person,
Person,
CommentAggregates,
Option<CommunityPersonBan>,
bool,
Option<i16>,
Option<Person>,
);
@ -242,7 +242,7 @@ impl JoinView for CommentReportView {
creator: a.4,
comment_creator: a.5,
counts: a.6,
creator_banned_from_community: a.7.is_some(),
creator_banned_from_community: a.7,
my_vote: a.8,
resolver: a.9,
}
@ -569,7 +569,7 @@ mod tests {
// Do a batch read of timmys reports
// It should only show saras, which is unresolved
let reports_after_resolve = CommentReportQuery {
unresolved_only: (Some(true)),
unresolved_only: (true),
..Default::default()
}
.list(pool, &inserted_timmy)

View File

@ -29,16 +29,16 @@ use lemmy_db_schema::{
post,
},
source::{
comment::{Comment, CommentSaved},
community::{Community, CommunityFollower, CommunityPersonBan},
comment::Comment,
community::{Community, CommunityFollower},
person::Person,
person_block::PersonBlock,
post::Post,
},
traits::JoinView,
utils::{fuzzy_search, limit_and_offset, DbConn, DbPool, ListFn, Queries, ReadFn},
CommentSortType,
ListingType,
SubscribedType,
};
type CommentViewTuple = (
@ -47,10 +47,10 @@ type CommentViewTuple = (
Post,
Community,
CommentAggregates,
Option<CommunityPersonBan>,
Option<CommunityFollower>,
Option<CommentSaved>,
Option<PersonBlock>,
bool,
SubscribedType,
bool,
bool,
Option<i16>,
);
@ -109,10 +109,10 @@ fn queries<'a>() -> Queries<
post::all_columns,
community::all_columns,
comment_aggregates::all_columns,
community_person_ban::all_columns.nullable(),
community_follower::all_columns.nullable(),
comment_saved::all_columns.nullable(),
person_block::all_columns.nullable(),
community_person_ban::id.nullable().is_not_null(),
CommunityFollower::select_subscribed_type(),
comment_saved::id.nullable().is_not_null(),
person_block::id.nullable().is_not_null(),
comment_like::score.nullable(),
);
@ -171,9 +171,7 @@ fn queries<'a>() -> Queries<
if let Some(listing_type) = options.listing_type {
match listing_type {
ListingType::Subscribed => {
query = query.filter(community_follower::person_id.is_not_null())
} // TODO could be this: and(community_follower::person_id.eq(person_id_join)),
ListingType::Subscribed => query = query.filter(community_follower::pending.is_not_null()), // TODO could be this: and(community_follower::person_id.eq(person_id_join)),
ListingType::Local => {
query = query.filter(community::local.eq(true)).filter(
community::hidden
@ -191,11 +189,16 @@ fn queries<'a>() -> Queries<
}
}
if options.saved_only.unwrap_or(false) {
if options.saved_only {
query = query.filter(comment_saved::comment_id.is_not_null());
}
let is_profile_view = options.is_profile_view.unwrap_or(false);
if options.liked_only {
query = query.filter(comment_like::score.eq(1));
} else if options.disliked_only {
query = query.filter(comment_like::score.eq(-1));
}
let is_creator = options.creator_id == options.local_user.map(|l| l.person.id);
// only show deleted comments to creator
if !is_creator {
@ -204,7 +207,7 @@ fn queries<'a>() -> Queries<
let is_admin = options.local_user.map(|l| l.person.admin).unwrap_or(false);
// only show removed comments to admin when viewing user profile
if !(is_profile_view && is_admin) {
if !(options.is_profile_view && is_admin) {
query = query.filter(comment::removed.eq(false));
}
@ -238,8 +241,8 @@ fn queries<'a>() -> Queries<
query = query.filter(nlevel(comment::path).le(depth_limit));
// only order if filtering by a post id. DOS potential otherwise and max_depth + !post_id isn't used anyways (afaik)
if options.post_id.is_some() {
// only order if filtering by a post id, or parent_path. DOS potential otherwise and max_depth + !post_id isn't used anyways (afaik)
if options.post_id.is_some() || options.parent_path.is_some() {
// Always order by the parent path first
query = query.order_by(subpath(comment::path, 0, -1));
}
@ -309,9 +312,10 @@ pub struct CommentQuery<'a> {
pub creator_id: Option<PersonId>,
pub local_user: Option<&'a LocalUserView>,
pub search_term: Option<String>,
pub saved_only: Option<bool>,
pub is_profile_view: Option<bool>,
pub show_deleted_and_removed: Option<bool>,
pub saved_only: bool,
pub liked_only: bool,
pub disliked_only: bool,
pub is_profile_view: bool,
pub page: Option<i64>,
pub limit: Option<i64>,
pub max_depth: Option<i32>,
@ -332,10 +336,10 @@ impl JoinView for CommentView {
post: a.2,
community: a.3,
counts: a.4,
creator_banned_from_community: a.5.is_some(),
subscribed: CommunityFollower::to_subscribed_type(&a.6),
saved: a.7.is_some(),
creator_blocked: a.8.is_some(),
creator_banned_from_community: a.5,
subscribed: a.6,
saved: a.7,
creator_blocked: a.8,
my_vote: a.9,
}
}
@ -355,7 +359,6 @@ mod tests {
Community,
DbPool,
Person,
PersonBlock,
Post,
},
structs::LocalUserView,
@ -372,7 +375,7 @@ mod tests {
language::Language,
local_user::{LocalUser, LocalUserInsertForm},
person::PersonInsertForm,
person_block::PersonBlockForm,
person_block::{PersonBlock, PersonBlockForm},
post::PostInsertForm,
},
traits::{Blockable, Crud, Likeable},
@ -610,6 +613,33 @@ mod tests {
// Make sure block set the creator blocked
assert!(read_comment_from_blocked_person.creator_blocked);
let read_liked_comment_views = CommentQuery {
local_user: (Some(&data.local_user_view)),
liked_only: (true),
..Default::default()
}
.list(pool)
.await
.unwrap();
assert_eq!(
expected_comment_view_with_person,
read_liked_comment_views[0]
);
assert_eq!(1, read_liked_comment_views.len());
let read_disliked_comment_views: Vec<CommentView> = CommentQuery {
local_user: (Some(&data.local_user_view)),
disliked_only: (true),
..Default::default()
}
.list(pool)
.await
.unwrap();
assert!(read_disliked_comment_views.is_empty());
cleanup(data, pool).await;
}

Some files were not shown because too many files have changed in this diff Show More