Handle displaying of deleted and removed posts/comments (fixes #2624) (#3286)

* Handle displaying of deleted and removed posts/comments (fixes #2624)

* remove duplicate test

* fix tests

* no show_removed/show_deleted

* merge

* partially fix tests

* fix tests

* clippy

* fix tests

* get rid of build_post_response_deleted_allowed
This commit is contained in:
Nutomic 2023-07-20 16:36:16 +02:00 committed by GitHub
parent d7051c40f8
commit 047db9ac85
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 273 additions and 178 deletions

View file

@ -29,6 +29,7 @@ import {
getComments, getComments,
getCommentParentId, getCommentParentId,
resolveCommunity, resolveCommunity,
getPersonDetails,
} from "./shared"; } from "./shared";
import { CommentView } from "lemmy-js-client/dist/types/CommentView"; import { CommentView } from "lemmy-js-client/dist/types/CommentView";
@ -160,10 +161,11 @@ test("Remove a comment from admin and community on the same instance", async ()
expect(removeCommentRes.comment_view.comment.removed).toBe(true); expect(removeCommentRes.comment_view.comment.removed).toBe(true);
// Make sure that comment is removed on alpha (it gets pushed since an admin from beta removed it) // Make sure that comment is removed on alpha (it gets pushed since an admin from beta removed it)
let refetchedPostComments = await getComments( let refetchedPostComments = await getPersonDetails(
alpha, alpha,
postRes.post_view.post.id, commentRes.comment_view.comment.creator_id,
); );
console.log(refetchedPostComments.comments[0].comment);
expect(refetchedPostComments.comments[0].comment.removed).toBe(true); expect(refetchedPostComments.comments[0].comment.removed).toBe(true);
let unremoveCommentRes = await removeComment(beta, false, betaCommentId); let unremoveCommentRes = await removeComment(beta, false, betaCommentId);

View file

@ -388,8 +388,8 @@ test("Enforce site ban for federated user", async () => {
expect(alphaUserOnBeta1.person?.person.banned).toBe(true); expect(alphaUserOnBeta1.person?.person.banned).toBe(true);
// existing alpha post should be removed on beta // existing alpha post should be removed on beta
let searchBeta2 = await searchPostLocal(beta, postRes1.post_view.post); let searchBeta2 = await getPost(beta, searchBeta1.posts[0].post.id);
expect(searchBeta2.posts[0].post.removed).toBe(true); expect(searchBeta2.post_view.post.removed).toBe(true);
// Unban alpha // Unban alpha
let unBanAlpha = await banPersonFromSite( let unBanAlpha = await banPersonFromSite(
@ -436,8 +436,8 @@ test("Enforce community ban for federated user", async () => {
expect(banAlpha.banned).toBe(true); expect(banAlpha.banned).toBe(true);
// ensure that the post by alpha got removed // ensure that the post by alpha got removed
let searchAlpha1 = await searchPostLocal(alpha, postRes1.post_view.post); let searchAlpha1 = await getPost(alpha, searchBeta1.posts[0].post.id);
expect(searchAlpha1.posts[0].post.removed).toBe(true); expect(searchAlpha1.post_view.post.removed).toBe(true);
// 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
let postRes2 = await createPost(alpha, betaCommunity.community.id); let postRes2 = await createPost(alpha, betaCommunity.community.id);

View file

@ -58,6 +58,8 @@ import { CommentReportResponse } from "lemmy-js-client/dist/types/CommentReportR
import { CreateCommentReport } from "lemmy-js-client/dist/types/CreateCommentReport"; import { CreateCommentReport } from "lemmy-js-client/dist/types/CreateCommentReport";
import { ListCommentReportsResponse } from "lemmy-js-client/dist/types/ListCommentReportsResponse"; import { ListCommentReportsResponse } from "lemmy-js-client/dist/types/ListCommentReportsResponse";
import { ListCommentReports } from "lemmy-js-client/dist/types/ListCommentReports"; import { ListCommentReports } from "lemmy-js-client/dist/types/ListCommentReports";
import { GetPersonDetailsResponse } from "lemmy-js-client/dist/types/GetPersonDetailsResponse";
import { GetPersonDetails } from "lemmy-js-client/dist/types/GetPersonDetails";
export interface API { export interface API {
client: LemmyHttp; client: LemmyHttp;
@ -646,6 +648,16 @@ export async function saveUserSettings(
): Promise<LoginResponse> { ): Promise<LoginResponse> {
return api.client.saveUserSettings(form); return api.client.saveUserSettings(form);
} }
export async function getPersonDetails(
api: API,
person_id: number,
): Promise<GetPersonDetailsResponse> {
let form: GetPersonDetails = {
auth: api.auth,
person_id: person_id,
};
return api.client.getPersonDetails(form);
}
export async function deleteUser(api: API): Promise<DeleteAccountResponse> { export async function deleteUser(api: API): Promise<DeleteAccountResponse> {
let form: DeleteAccount = { let form: DeleteAccount = {

View file

@ -82,19 +82,6 @@ pub async fn build_post_response(
Ok(PostResponse { post_view }) Ok(PostResponse { post_view })
} }
// this is a variation of build_post_response that returns post even if end-user-delted or mod-removed.
// Assumption is that before this function is ever called, the user is already confirmed to be authorized to view post.
// See GitHub Issue #3588
pub async fn build_post_response_deleted_allowed(
context: &Data<LemmyContext>,
_community_id: CommunityId,
person_id: PersonId,
post_id: PostId,
) -> Result<PostResponse, LemmyError> {
let post_view = PostView::read(&mut context.pool(), post_id, Some(person_id), Some(true)).await?;
Ok(PostResponse { post_view })
}
// TODO: this function is a mess and should be split up to handle email seperately // TODO: this function is a mess and should be split up to handle email seperately
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub async fn send_local_notifs( pub async fn send_local_notifs(

View file

@ -1,7 +1,7 @@
use crate::PerformCrud; use crate::PerformCrud;
use actix_web::web::Data; use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
build_response::build_post_response_deleted_allowed, build_response::build_post_response,
context::LemmyContext, context::LemmyContext,
post::{DeletePost, PostResponse}, post::{DeletePost, PostResponse},
utils::{check_community_ban, check_community_deleted_or_removed, local_user_view_from_jwt}, utils::{check_community_ban, check_community_deleted_or_removed, local_user_view_from_jwt},
@ -52,7 +52,7 @@ impl PerformCrud for DeletePost {
) )
.await?; .await?;
build_post_response_deleted_allowed( build_post_response(
context, context,
orig_post.community_id, orig_post.community_id,
local_user_view.person.id, local_user_view.person.id,

View file

@ -1,7 +1,7 @@
use crate::PerformCrud; use crate::PerformCrud;
use actix_web::web::Data; use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
build_response::build_post_response_deleted_allowed, build_response::build_post_response,
context::LemmyContext, context::LemmyContext,
post::{PostResponse, RemovePost}, post::{PostResponse, RemovePost},
utils::{check_community_ban, is_mod_or_admin, local_user_view_from_jwt}, utils::{check_community_ban, is_mod_or_admin, local_user_view_from_jwt},
@ -61,7 +61,7 @@ impl PerformCrud for RemovePost {
}; };
ModRemovePost::create(&mut context.pool(), &form).await?; ModRemovePost::create(&mut context.pool(), &form).await?;
build_post_response_deleted_allowed( build_post_response(
context, context,
orig_post.community_id, orig_post.community_id,
local_user_view.person.id, local_user_view.person.id,

View file

@ -54,7 +54,6 @@ pub async fn list_comments(
let parent_path_cloned = parent_path.clone(); let parent_path_cloned = parent_path.clone();
let post_id = data.post_id; let post_id = data.post_id;
let local_user = local_user_view.map(|l| l.local_user);
let comments = CommentQuery { let comments = CommentQuery {
listing_type, listing_type,
sort, sort,
@ -63,7 +62,7 @@ pub async fn list_comments(
community_id, community_id,
parent_path: parent_path_cloned, parent_path: parent_path_cloned,
post_id, post_id,
local_user: local_user.as_ref(), local_user: local_user_view.as_ref(),
page, page,
limit, limit,
..Default::default() ..Default::default()

View file

@ -8,7 +8,7 @@ use actix_web::web::{Json, Query};
use lemmy_api_common::{ use lemmy_api_common::{
context::LemmyContext, context::LemmyContext,
post::{GetPosts, GetPostsResponse}, post::{GetPosts, GetPostsResponse},
utils::{check_private_instance, is_mod_or_admin_opt, local_user_view_from_jwt_opt}, utils::{check_private_instance, local_user_view_from_jwt_opt},
}; };
use lemmy_db_schema::source::{community::Community, local_site::LocalSite}; use lemmy_db_schema::source::{community::Community, local_site::LocalSite};
use lemmy_db_views::post_view::PostQuery; use lemmy_db_views::post_view::PostQuery;
@ -42,21 +42,14 @@ pub async fn list_posts(
community_id, community_id,
)?); )?);
let is_mod_or_admin = Some(
is_mod_or_admin_opt(&mut context.pool(), local_user_view.as_ref(), community_id)
.await
.is_ok(),
);
let posts = PostQuery { let posts = PostQuery {
local_user: local_user_view.map(|l| l.local_user).as_ref(), local_user: local_user_view.as_ref(),
listing_type, listing_type,
sort, sort,
community_id, community_id,
saved_only, saved_only,
page, page,
limit, limit,
is_mod_or_admin,
..Default::default() ..Default::default()
} }
.list(&mut context.pool()) .list(&mut context.pool())

View file

@ -4,7 +4,7 @@ use actix_web::web::{Json, Query};
use lemmy_api_common::{ use lemmy_api_common::{
context::LemmyContext, context::LemmyContext,
person::{GetPersonDetails, GetPersonDetailsResponse}, person::{GetPersonDetails, GetPersonDetailsResponse},
utils::{check_private_instance, is_admin, local_user_view_from_jwt_opt}, utils::{check_private_instance, local_user_view_from_jwt_opt},
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{local_site::LocalSite, person::Person}, source::{local_site::LocalSite, person::Person},
@ -26,7 +26,6 @@ pub async fn read_person(
let local_user_view = local_user_view_from_jwt_opt(data.auth.as_ref(), &context).await; 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 local_site = LocalSite::read(&mut context.pool()).await?;
let is_admin = local_user_view.as_ref().map(|luv| is_admin(luv).is_ok());
check_private_instance(&local_user_view, &local_site)?; check_private_instance(&local_user_view, &local_site)?;
@ -53,48 +52,38 @@ pub async fn read_person(
let limit = data.limit; let limit = data.limit;
let saved_only = data.saved_only; let saved_only = data.saved_only;
let community_id = data.community_id; let community_id = data.community_id;
let local_user = local_user_view.map(|l| l.local_user); // If its saved only, you don't care what creator it was
let local_user_clone = local_user.clone(); // Or, if its not saved, then you only want it for that specific creator
let creator_id = if !saved_only.unwrap_or(false) {
Some(person_details_id)
} else {
None
};
let posts = PostQuery { let posts = PostQuery {
sort, sort,
saved_only, saved_only,
local_user:local_user.as_ref(), local_user: local_user_view.as_ref(),
community_id, community_id,
is_mod_or_admin: is_admin, is_profile_view: Some(true),
page, page,
limit, limit,
creator_id: creator_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
if !saved_only.unwrap_or(false) {
Some(person_details_id)
} else {
None
}
,
..Default::default() ..Default::default()
} }
.list(&mut context.pool()) .list(&mut context.pool())
.await?; .await?;
let comments = CommentQuery { let comments = CommentQuery {
local_user: (local_user_clone.as_ref()), local_user: (local_user_view.as_ref()),
sort: (sort.map(post_to_comment_sort_type)), sort: (sort.map(post_to_comment_sort_type)),
saved_only: (saved_only), saved_only: (saved_only),
show_deleted_and_removed: (Some(false)), show_deleted_and_removed: (Some(false)),
community_id: (community_id), community_id: (community_id),
is_profile_view: Some(true),
page: (page), page: (page),
limit: (limit), limit: (limit),
creator_id: ( creator_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
if !saved_only.unwrap_or(false) {
Some(person_details_id)
} else {
None
}
),
..Default::default() ..Default::default()
} }
.list(&mut context.pool()) .list(&mut context.pool())

View file

@ -50,7 +50,7 @@ pub async fn search(
data.community_id data.community_id
}; };
let creator_id = data.creator_id; let creator_id = data.creator_id;
let local_user = local_user_view.map(|l| l.local_user); let local_user = local_user_view.as_ref().map(|l| l.local_user.clone());
match search_type { match search_type {
SearchType::Posts => { SearchType::Posts => {
posts = PostQuery { posts = PostQuery {
@ -58,9 +58,8 @@ pub async fn search(
listing_type: (listing_type), listing_type: (listing_type),
community_id: (community_id), community_id: (community_id),
creator_id: (creator_id), creator_id: (creator_id),
local_user: (local_user.as_ref()), local_user: (local_user_view.as_ref()),
search_term: (Some(q)), search_term: (Some(q)),
is_mod_or_admin: (is_admin),
page: (page), page: (page),
limit: (limit), limit: (limit),
..Default::default() ..Default::default()
@ -75,7 +74,7 @@ pub async fn search(
search_term: (Some(q)), search_term: (Some(q)),
community_id: (community_id), community_id: (community_id),
creator_id: (creator_id), creator_id: (creator_id),
local_user: (local_user.as_ref()), local_user: (local_user_view.as_ref()),
page: (page), page: (page),
limit: (limit), limit: (limit),
..Default::default() ..Default::default()
@ -112,15 +111,15 @@ pub async fn search(
let community_or_creator_included = let community_or_creator_included =
data.community_id.is_some() || data.community_name.is_some() || data.creator_id.is_some(); data.community_id.is_some() || data.community_name.is_some() || data.creator_id.is_some();
let local_user_ = local_user.clone(); let q = data.q.clone();
posts = PostQuery { posts = PostQuery {
sort: (sort), sort: (sort),
listing_type: (listing_type), listing_type: (listing_type),
community_id: (community_id), community_id: (community_id),
creator_id: (creator_id), creator_id: (creator_id),
local_user: (local_user_.as_ref()), local_user: (local_user_view.as_ref()),
search_term: (Some(q)), search_term: (Some(q)),
is_mod_or_admin: (is_admin),
page: (page), page: (page),
limit: (limit), limit: (limit),
..Default::default() ..Default::default()
@ -130,14 +129,13 @@ pub async fn search(
let q = data.q.clone(); let q = data.q.clone();
let local_user_ = local_user.clone();
comments = CommentQuery { comments = CommentQuery {
sort: (sort.map(post_to_comment_sort_type)), sort: (sort.map(post_to_comment_sort_type)),
listing_type: (listing_type), listing_type: (listing_type),
search_term: (Some(q)), search_term: (Some(q)),
community_id: (community_id), community_id: (community_id),
creator_id: (creator_id), creator_id: (creator_id),
local_user: (local_user_.as_ref()), local_user: (local_user_view.as_ref()),
page: (page), page: (page),
limit: (limit), limit: (limit),
..Default::default() ..Default::default()
@ -186,7 +184,6 @@ pub async fn search(
community_id: (community_id), community_id: (community_id),
creator_id: (creator_id), creator_id: (creator_id),
url_search: (Some(q)), url_search: (Some(q)),
is_mod_or_admin: (is_admin),
page: (page), page: (page),
limit: (limit), limit: (limit),
..Default::default() ..Default::default()

View file

@ -1,4 +1,4 @@
use crate::structs::CommentView; use crate::structs::{CommentView, LocalUserView};
use diesel::{ use diesel::{
result::Error, result::Error,
BoolExpressionMethods, BoolExpressionMethods,
@ -30,7 +30,6 @@ use lemmy_db_schema::{
source::{ source::{
comment::{Comment, CommentSaved}, comment::{Comment, CommentSaved},
community::{Community, CommunityFollower, CommunityPersonBan}, community::{Community, CommunityFollower, CommunityPersonBan},
local_user::LocalUser,
person::Person, person::Person,
person_block::PersonBlock, person_block::PersonBlock,
post::Post, post::Post,
@ -163,9 +162,10 @@ pub struct CommentQuery<'a> {
pub post_id: Option<PostId>, pub post_id: Option<PostId>,
pub parent_path: Option<Ltree>, pub parent_path: Option<Ltree>,
pub creator_id: Option<PersonId>, pub creator_id: Option<PersonId>,
pub local_user: Option<&'a LocalUser>, pub local_user: Option<&'a LocalUserView>,
pub search_term: Option<String>, pub search_term: Option<String>,
pub saved_only: Option<bool>, pub saved_only: Option<bool>,
pub is_profile_view: Option<bool>,
pub show_deleted_and_removed: Option<bool>, pub show_deleted_and_removed: Option<bool>,
pub page: Option<i64>, pub page: Option<i64>,
pub limit: Option<i64>, pub limit: Option<i64>,
@ -177,8 +177,11 @@ impl<'a> CommentQuery<'a> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
// The left join below will return None in this case // The left join below will return None in this case
let person_id_join = self.local_user.map(|l| l.person_id).unwrap_or(PersonId(-1)); let person_id_join = self.local_user.map(|l| l.person.id).unwrap_or(PersonId(-1));
let local_user_id_join = self.local_user.map(|l| l.id).unwrap_or(LocalUserId(-1)); let local_user_id_join = self
.local_user
.map(|l| l.local_user.id)
.unwrap_or(LocalUserId(-1));
let mut query = comment::table let mut query = comment::table
.inner_join(person::table) .inner_join(person::table)
@ -294,12 +297,24 @@ impl<'a> CommentQuery<'a> {
query = query.filter(comment_saved::comment_id.is_not_null()); query = query.filter(comment_saved::comment_id.is_not_null());
} }
if !self.show_deleted_and_removed.unwrap_or(true) { let is_profile_view = self.is_profile_view.unwrap_or(false);
let is_creator = self.creator_id == self.local_user.map(|l| l.person.id);
// only show deleted comments to creator
if !is_creator {
query = query.filter(comment::deleted.eq(false)); query = query.filter(comment::deleted.eq(false));
}
let is_admin = self.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) {
query = query.filter(comment::removed.eq(false)); query = query.filter(comment::removed.eq(false));
} }
if !self.local_user.map(|l| l.show_bot_accounts).unwrap_or(true) { if !self
.local_user
.map(|l| l.local_user.show_bot_accounts)
.unwrap_or(true)
{
query = query.filter(person::bot_account.eq(false)); query = query.filter(person::bot_account.eq(false));
}; };
@ -385,17 +400,19 @@ mod tests {
#![allow(clippy::unwrap_used)] #![allow(clippy::unwrap_used)]
#![allow(clippy::indexing_slicing)] #![allow(clippy::indexing_slicing)]
use crate::comment_view::{ use crate::{
Comment, comment_view::{
CommentQuery, Comment,
CommentSortType, CommentQuery,
CommentView, CommentSortType,
Community, CommentView,
DbPool, Community,
LocalUser, DbPool,
Person, Person,
PersonBlock, PersonBlock,
Post, Post,
},
structs::LocalUserView,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
aggregates::structs::CommentAggregates, aggregates::structs::CommentAggregates,
@ -407,7 +424,7 @@ mod tests {
community::CommunityInsertForm, community::CommunityInsertForm,
instance::Instance, instance::Instance,
language::Language, language::Language,
local_user::LocalUserInsertForm, local_user::{LocalUser, LocalUserInsertForm},
person::PersonInsertForm, person::PersonInsertForm,
person_block::PersonBlockForm, person_block::PersonBlockForm,
post::PostInsertForm, post::PostInsertForm,
@ -424,8 +441,7 @@ mod tests {
inserted_comment_1: Comment, inserted_comment_1: Comment,
inserted_comment_2: Comment, inserted_comment_2: Comment,
inserted_post: Post, inserted_post: Post,
inserted_person: Person, local_user_view: LocalUserView,
inserted_local_user: LocalUser,
inserted_person_2: Person, inserted_person_2: Person,
inserted_community: Community, inserted_community: Community,
} }
@ -576,14 +592,18 @@ mod tests {
let _inserted_comment_like = CommentLike::like(pool, &comment_like_form).await.unwrap(); let _inserted_comment_like = CommentLike::like(pool, &comment_like_form).await.unwrap();
let local_user_view = LocalUserView {
local_user: inserted_local_user.clone(),
person: inserted_person.clone(),
counts: Default::default(),
};
Data { Data {
inserted_instance, inserted_instance,
inserted_comment_0, inserted_comment_0,
inserted_comment_1, inserted_comment_1,
inserted_comment_2, inserted_comment_2,
inserted_post, inserted_post,
inserted_person, local_user_view,
inserted_local_user,
inserted_person_2, inserted_person_2,
inserted_community, inserted_community,
} }
@ -618,7 +638,7 @@ mod tests {
let read_comment_views_with_person = CommentQuery { let read_comment_views_with_person = CommentQuery {
sort: (Some(CommentSortType::Old)), sort: (Some(CommentSortType::Old)),
post_id: (Some(data.inserted_post.id)), post_id: (Some(data.inserted_post.id)),
local_user: (Some(&data.inserted_local_user)), local_user: (Some(&data.local_user_view)),
..Default::default() ..Default::default()
} }
.list(pool) .list(pool)
@ -636,7 +656,7 @@ mod tests {
let read_comment_from_blocked_person = CommentView::read( let read_comment_from_blocked_person = CommentView::read(
pool, pool,
data.inserted_comment_1.id, data.inserted_comment_1.id,
Some(data.inserted_person.id), Some(data.local_user_view.person.id),
) )
.await .await
.unwrap(); .unwrap();
@ -734,7 +754,7 @@ mod tests {
// by default, user has all languages enabled and should see all comments // by default, user has all languages enabled and should see all comments
// (except from blocked user) // (except from blocked user)
let all_languages = CommentQuery { let all_languages = CommentQuery {
local_user: (Some(&data.inserted_local_user)), local_user: (Some(&data.local_user_view)),
..Default::default() ..Default::default()
} }
.list(pool) .list(pool)
@ -747,11 +767,11 @@ mod tests {
.await .await
.unwrap() .unwrap()
.unwrap(); .unwrap();
LocalUserLanguage::update(pool, vec![finnish_id], data.inserted_local_user.id) LocalUserLanguage::update(pool, vec![finnish_id], data.local_user_view.local_user.id)
.await .await
.unwrap(); .unwrap();
let finnish_comments = CommentQuery { let finnish_comments = CommentQuery {
local_user: (Some(&data.inserted_local_user)), local_user: (Some(&data.local_user_view)),
..Default::default() ..Default::default()
} }
.list(pool) .list(pool)
@ -768,11 +788,15 @@ mod tests {
); );
// now show all comments with undetermined language (which is the default value) // now show all comments with undetermined language (which is the default value)
LocalUserLanguage::update(pool, vec![UNDETERMINED_ID], data.inserted_local_user.id) LocalUserLanguage::update(
.await pool,
.unwrap(); vec![UNDETERMINED_ID],
data.local_user_view.local_user.id,
)
.await
.unwrap();
let undetermined_comment = CommentQuery { let undetermined_comment = CommentQuery {
local_user: (Some(&data.inserted_local_user)), local_user: (Some(&data.local_user_view)),
..Default::default() ..Default::default()
} }
.list(pool) .list(pool)
@ -784,9 +808,13 @@ mod tests {
} }
async fn cleanup(data: Data, pool: &mut DbPool<'_>) { async fn cleanup(data: Data, pool: &mut DbPool<'_>) {
CommentLike::remove(pool, data.inserted_person.id, data.inserted_comment_0.id) CommentLike::remove(
.await pool,
.unwrap(); data.local_user_view.person.id,
data.inserted_comment_0.id,
)
.await
.unwrap();
Comment::delete(pool, data.inserted_comment_0.id) Comment::delete(pool, data.inserted_comment_0.id)
.await .await
.unwrap(); .unwrap();
@ -797,7 +825,9 @@ mod tests {
Community::delete(pool, data.inserted_community.id) Community::delete(pool, data.inserted_community.id)
.await .await
.unwrap(); .unwrap();
Person::delete(pool, data.inserted_person.id).await.unwrap(); Person::delete(pool, data.local_user_view.person.id)
.await
.unwrap();
Person::delete(pool, data.inserted_person_2.id) Person::delete(pool, data.inserted_person_2.id)
.await .await
.unwrap(); .unwrap();
@ -819,7 +849,7 @@ mod tests {
comment: Comment { comment: Comment {
id: data.inserted_comment_0.id, id: data.inserted_comment_0.id,
content: "Comment 0".into(), content: "Comment 0".into(),
creator_id: data.inserted_person.id, creator_id: data.local_user_view.person.id,
post_id: data.inserted_post.id, post_id: data.inserted_post.id,
removed: false, removed: false,
deleted: false, deleted: false,
@ -832,12 +862,12 @@ mod tests {
language_id: LanguageId(37), language_id: LanguageId(37),
}, },
creator: Person { creator: Person {
id: data.inserted_person.id, id: data.local_user_view.person.id,
name: "timmy".into(), name: "timmy".into(),
display_name: None, display_name: None,
published: data.inserted_person.published, published: data.local_user_view.person.published,
avatar: None, avatar: None,
actor_id: data.inserted_person.actor_id.clone(), actor_id: data.local_user_view.person.actor_id.clone(),
local: true, local: true,
banned: false, banned: false,
deleted: false, deleted: false,
@ -846,19 +876,19 @@ mod tests {
bio: None, bio: None,
banner: None, banner: None,
updated: None, updated: None,
inbox_url: data.inserted_person.inbox_url.clone(), inbox_url: data.local_user_view.person.inbox_url.clone(),
shared_inbox_url: None, shared_inbox_url: None,
matrix_user_id: None, matrix_user_id: None,
ban_expires: None, ban_expires: None,
instance_id: data.inserted_instance.id, instance_id: data.inserted_instance.id,
private_key: data.inserted_person.private_key.clone(), private_key: data.local_user_view.person.private_key.clone(),
public_key: data.inserted_person.public_key.clone(), public_key: data.local_user_view.person.public_key.clone(),
last_refreshed_at: data.inserted_person.last_refreshed_at, last_refreshed_at: data.local_user_view.person.last_refreshed_at,
}, },
post: Post { post: Post {
id: data.inserted_post.id, id: data.inserted_post.id,
name: data.inserted_post.name.clone(), name: data.inserted_post.name.clone(),
creator_id: data.inserted_person.id, creator_id: data.local_user_view.person.id,
url: None, url: None,
body: None, body: None,
published: data.inserted_post.published, published: data.inserted_post.published,

View file

@ -1,4 +1,4 @@
use crate::structs::PostView; use crate::structs::{LocalUserView, PostView};
use diesel::{ use diesel::{
debug_query, debug_query,
dsl::{now, IntervalDsl}, dsl::{now, IntervalDsl},
@ -34,7 +34,6 @@ use lemmy_db_schema::{
}, },
source::{ source::{
community::{Community, CommunityFollower, CommunityPersonBan}, community::{Community, CommunityFollower, CommunityPersonBan},
local_user::LocalUser,
person::Person, person::Person,
person_block::PersonBlock, person_block::PersonBlock,
post::{Post, PostRead, PostSaved}, post::{Post, PostRead, PostSaved},
@ -146,13 +145,21 @@ impl PostView {
.into_boxed(); .into_boxed();
// Hide deleted and removed for non-admins or mods // Hide deleted and removed for non-admins or mods
// Note: one special use case for this flag variable is when end-user-delete post or mod-removed post.
if !is_mod_or_admin.unwrap_or(false) { if !is_mod_or_admin.unwrap_or(false) {
query = query query = query
.filter(community::removed.eq(false)) .filter(community::removed.eq(false))
.filter(community::deleted.eq(false))
.filter(post::removed.eq(false)) .filter(post::removed.eq(false))
.filter(post::deleted.eq(false)); // users can see their own deleted posts
.filter(
community::deleted
.eq(false)
.or(post::creator_id.eq(person_id_join)),
)
.filter(
post::deleted
.eq(false)
.or(post::creator_id.eq(person_id_join)),
);
} }
let ( let (
@ -199,12 +206,11 @@ pub struct PostQuery<'a> {
pub sort: Option<SortType>, pub sort: Option<SortType>,
pub creator_id: Option<PersonId>, pub creator_id: Option<PersonId>,
pub community_id: Option<CommunityId>, pub community_id: Option<CommunityId>,
pub local_user: Option<&'a LocalUser>, pub local_user: Option<&'a LocalUserView>,
pub search_term: Option<String>, pub search_term: Option<String>,
pub url_search: Option<String>, pub url_search: Option<String>,
pub saved_only: Option<bool>, pub saved_only: Option<bool>,
/// Used to show deleted or removed posts for admins pub is_profile_view: Option<bool>,
pub is_mod_or_admin: Option<bool>,
pub page: Option<i64>, pub page: Option<i64>,
pub limit: Option<i64>, pub limit: Option<i64>,
} }
@ -214,8 +220,11 @@ impl<'a> PostQuery<'a> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
// The left join below will return None in this case // The left join below will return None in this case
let person_id_join = self.local_user.map(|l| l.person_id).unwrap_or(PersonId(-1)); let person_id_join = self.local_user.map(|l| l.person.id).unwrap_or(PersonId(-1));
let local_user_id_join = self.local_user.map(|l| l.id).unwrap_or(LocalUserId(-1)); let local_user_id_join = self
.local_user
.map(|l| l.local_user.id)
.unwrap_or(LocalUserId(-1));
let mut query = post::table let mut query = post::table
.inner_join(person::table) .inner_join(person::table)
@ -302,14 +311,21 @@ impl<'a> PostQuery<'a> {
)) ))
.into_boxed(); .into_boxed();
// Hide deleted and removed for non-admins or mods let is_profile_view = self.is_profile_view.unwrap_or(false);
// TODO This eventually needs to show posts where you are the creator let is_creator = self.creator_id == self.local_user.map(|l| l.person.id);
if !self.is_mod_or_admin.unwrap_or(false) { // only show deleted posts to creator
if is_creator {
query = query
.filter(community::deleted.eq(false))
.filter(post::deleted.eq(false));
}
let is_admin = self.local_user.map(|l| l.person.admin).unwrap_or(false);
// only show removed posts to admin when viewing user profile
if !(is_profile_view && is_admin) {
query = query query = query
.filter(community::removed.eq(false)) .filter(community::removed.eq(false))
.filter(community::deleted.eq(false)) .filter(post::removed.eq(false));
.filter(post::removed.eq(false))
.filter(post::deleted.eq(false));
} }
if self.community_id.is_none() { if self.community_id.is_none() {
@ -359,13 +375,21 @@ impl<'a> PostQuery<'a> {
); );
} }
if !self.local_user.map(|l| l.show_nsfw).unwrap_or(false) { if !self
.local_user
.map(|l| l.local_user.show_nsfw)
.unwrap_or(false)
{
query = query query = query
.filter(post::nsfw.eq(false)) .filter(post::nsfw.eq(false))
.filter(community::nsfw.eq(false)); .filter(community::nsfw.eq(false));
}; };
if !self.local_user.map(|l| l.show_bot_accounts).unwrap_or(true) { if !self
.local_user
.map(|l| l.local_user.show_bot_accounts)
.unwrap_or(true)
{
query = query.filter(person::bot_account.eq(false)); query = query.filter(person::bot_account.eq(false));
}; };
@ -374,7 +398,11 @@ impl<'a> PostQuery<'a> {
} }
// Only hide the read posts, if the saved_only is false. Otherwise ppl with the hide_read // Only hide the read posts, if the saved_only is false. Otherwise ppl with the hide_read
// setting wont be able to see saved posts. // setting wont be able to see saved posts.
else if !self.local_user.map(|l| l.show_read_posts).unwrap_or(true) { else if !self
.local_user
.map(|l| l.local_user.show_read_posts)
.unwrap_or(true)
{
query = query.filter(post_read::post_id.is_null()); query = query.filter(post_read::post_id.is_null());
} }
@ -477,7 +505,10 @@ mod tests {
#![allow(clippy::unwrap_used)] #![allow(clippy::unwrap_used)]
#![allow(clippy::indexing_slicing)] #![allow(clippy::indexing_slicing)]
use crate::post_view::{PostQuery, PostView}; use crate::{
post_view::{PostQuery, PostView},
structs::LocalUserView,
};
use lemmy_db_schema::{ use lemmy_db_schema::{
aggregates::structs::PostAggregates, aggregates::structs::PostAggregates,
impls::actor_language::UNDETERMINED_ID, impls::actor_language::UNDETERMINED_ID,
@ -502,8 +533,7 @@ mod tests {
struct Data { struct Data {
inserted_instance: Instance, inserted_instance: Instance,
inserted_person: Person, local_user_view: LocalUserView,
inserted_local_user: LocalUser,
inserted_blocked_person: Person, inserted_blocked_person: Person,
inserted_bot: Person, inserted_bot: Person,
inserted_community: Community, inserted_community: Community,
@ -592,11 +622,15 @@ mod tests {
.build(); .build();
let _inserted_bot_post = Post::create(pool, &new_bot_post).await.unwrap(); let _inserted_bot_post = Post::create(pool, &new_bot_post).await.unwrap();
let local_user_view = LocalUserView {
local_user: inserted_local_user,
person: inserted_person,
counts: Default::default(),
};
Data { Data {
inserted_instance, inserted_instance,
inserted_person, local_user_view,
inserted_local_user,
inserted_blocked_person, inserted_blocked_person,
inserted_bot, inserted_bot,
inserted_community, inserted_community,
@ -609,20 +643,21 @@ mod tests {
async fn post_listing_with_person() { async fn post_listing_with_person() {
let pool = &build_db_pool_for_tests().await; let pool = &build_db_pool_for_tests().await;
let pool = &mut pool.into(); let pool = &mut pool.into();
let data = init_data(pool).await; let mut data = init_data(pool).await;
let local_user_form = LocalUserUpdateForm::builder() let local_user_form = LocalUserUpdateForm::builder()
.show_bot_accounts(Some(false)) .show_bot_accounts(Some(false))
.build(); .build();
let inserted_local_user = let inserted_local_user =
LocalUser::update(pool, data.inserted_local_user.id, &local_user_form) LocalUser::update(pool, data.local_user_view.local_user.id, &local_user_form)
.await .await
.unwrap(); .unwrap();
data.local_user_view.local_user = inserted_local_user;
let read_post_listing = PostQuery { let read_post_listing = PostQuery {
sort: (Some(SortType::New)), sort: (Some(SortType::New)),
community_id: (Some(data.inserted_community.id)), community_id: (Some(data.inserted_community.id)),
local_user: (Some(&inserted_local_user)), local_user: (Some(&data.local_user_view)),
..Default::default() ..Default::default()
} }
.list(pool) .list(pool)
@ -632,7 +667,7 @@ mod tests {
let post_listing_single_with_person = PostView::read( let post_listing_single_with_person = PostView::read(
pool, pool,
data.inserted_post.id, data.inserted_post.id,
Some(data.inserted_person.id), Some(data.local_user_view.person.id),
None, None,
) )
.await .await
@ -654,14 +689,15 @@ mod tests {
.show_bot_accounts(Some(true)) .show_bot_accounts(Some(true))
.build(); .build();
let inserted_local_user = let inserted_local_user =
LocalUser::update(pool, data.inserted_local_user.id, &local_user_form) LocalUser::update(pool, data.local_user_view.local_user.id, &local_user_form)
.await .await
.unwrap(); .unwrap();
data.local_user_view.local_user = inserted_local_user;
let post_listings_with_bots = PostQuery { let post_listings_with_bots = PostQuery {
sort: (Some(SortType::New)), sort: (Some(SortType::New)),
community_id: (Some(data.inserted_community.id)), community_id: (Some(data.inserted_community.id)),
local_user: (Some(&inserted_local_user)), local_user: (Some(&data.local_user_view)),
..Default::default() ..Default::default()
} }
.list(pool) .list(pool)
@ -719,7 +755,7 @@ mod tests {
let data = init_data(pool).await; let data = init_data(pool).await;
let community_block = CommunityBlockForm { let community_block = CommunityBlockForm {
person_id: data.inserted_person.id, person_id: data.local_user_view.person.id,
community_id: data.inserted_community.id, community_id: data.inserted_community.id,
}; };
CommunityBlock::block(pool, &community_block).await.unwrap(); CommunityBlock::block(pool, &community_block).await.unwrap();
@ -727,7 +763,7 @@ mod tests {
let read_post_listings_with_person_after_block = PostQuery { let read_post_listings_with_person_after_block = PostQuery {
sort: (Some(SortType::New)), sort: (Some(SortType::New)),
community_id: (Some(data.inserted_community.id)), community_id: (Some(data.inserted_community.id)),
local_user: (Some(&data.inserted_local_user)), local_user: (Some(&data.local_user_view)),
..Default::default() ..Default::default()
} }
.list(pool) .list(pool)
@ -747,11 +783,11 @@ mod tests {
async fn post_listing_like() { async fn post_listing_like() {
let pool = &build_db_pool_for_tests().await; let pool = &build_db_pool_for_tests().await;
let pool = &mut pool.into(); let pool = &mut pool.into();
let data = init_data(pool).await; let mut data = init_data(pool).await;
let post_like_form = PostLikeForm { let post_like_form = PostLikeForm {
post_id: data.inserted_post.id, post_id: data.inserted_post.id,
person_id: data.inserted_person.id, person_id: data.local_user_view.person.id,
score: 1, score: 1,
}; };
@ -760,7 +796,7 @@ mod tests {
let expected_post_like = PostLike { let expected_post_like = PostLike {
id: inserted_post_like.id, id: inserted_post_like.id,
post_id: data.inserted_post.id, post_id: data.inserted_post.id,
person_id: data.inserted_person.id, person_id: data.local_user_view.person.id,
published: inserted_post_like.published, published: inserted_post_like.published,
score: 1, score: 1,
}; };
@ -769,7 +805,7 @@ mod tests {
let post_listing_single_with_person = PostView::read( let post_listing_single_with_person = PostView::read(
pool, pool,
data.inserted_post.id, data.inserted_post.id,
Some(data.inserted_person.id), Some(data.local_user_view.person.id),
None, None,
) )
.await .await
@ -785,14 +821,15 @@ mod tests {
.show_bot_accounts(Some(false)) .show_bot_accounts(Some(false))
.build(); .build();
let inserted_local_user = let inserted_local_user =
LocalUser::update(pool, data.inserted_local_user.id, &local_user_form) LocalUser::update(pool, data.local_user_view.local_user.id, &local_user_form)
.await .await
.unwrap(); .unwrap();
data.local_user_view.local_user = inserted_local_user;
let read_post_listing = PostQuery { let read_post_listing = PostQuery {
sort: (Some(SortType::New)), sort: (Some(SortType::New)),
community_id: (Some(data.inserted_community.id)), community_id: (Some(data.inserted_community.id)),
local_user: (Some(&inserted_local_user)), local_user: (Some(&data.local_user_view)),
..Default::default() ..Default::default()
} }
.list(pool) .list(pool)
@ -802,9 +839,10 @@ mod tests {
assert_eq!(expected_post_with_upvote, read_post_listing[0]); assert_eq!(expected_post_with_upvote, read_post_listing[0]);
let like_removed = PostLike::remove(pool, data.inserted_person.id, data.inserted_post.id) let like_removed =
.await PostLike::remove(pool, data.local_user_view.person.id, data.inserted_post.id)
.unwrap(); .await
.unwrap();
assert_eq!(1, like_removed); assert_eq!(1, like_removed);
cleanup(data, pool).await; cleanup(data, pool).await;
} }
@ -822,7 +860,7 @@ mod tests {
.unwrap(); .unwrap();
let post_spanish = PostInsertForm::builder() let post_spanish = PostInsertForm::builder()
.name("asffgdsc".to_string()) .name("asffgdsc".to_string())
.creator_id(data.inserted_person.id) .creator_id(data.local_user_view.person.id)
.community_id(data.inserted_community.id) .community_id(data.inserted_community.id)
.language_id(Some(spanish_id)) .language_id(Some(spanish_id))
.build(); .build();
@ -831,7 +869,7 @@ mod tests {
let post_listings_all = PostQuery { let post_listings_all = PostQuery {
sort: (Some(SortType::New)), sort: (Some(SortType::New)),
local_user: (Some(&data.inserted_local_user)), local_user: (Some(&data.local_user_view)),
..Default::default() ..Default::default()
} }
.list(pool) .list(pool)
@ -845,13 +883,13 @@ mod tests {
.await .await
.unwrap() .unwrap()
.unwrap(); .unwrap();
LocalUserLanguage::update(pool, vec![french_id], data.inserted_local_user.id) LocalUserLanguage::update(pool, vec![french_id], data.local_user_view.local_user.id)
.await .await
.unwrap(); .unwrap();
let post_listing_french = PostQuery { let post_listing_french = PostQuery {
sort: (Some(SortType::New)), sort: (Some(SortType::New)),
local_user: (Some(&data.inserted_local_user)), local_user: (Some(&data.local_user_view)),
..Default::default() ..Default::default()
} }
.list(pool) .list(pool)
@ -867,13 +905,13 @@ mod tests {
LocalUserLanguage::update( LocalUserLanguage::update(
pool, pool,
vec![french_id, UNDETERMINED_ID], vec![french_id, UNDETERMINED_ID],
data.inserted_local_user.id, data.local_user_view.local_user.id,
) )
.await .await
.unwrap(); .unwrap();
let post_listings_french_und = PostQuery { let post_listings_french_und = PostQuery {
sort: (Some(SortType::New)), sort: (Some(SortType::New)),
local_user: (Some(&data.inserted_local_user)), local_user: (Some(&data.local_user_view)),
..Default::default() ..Default::default()
} }
.list(pool) .list(pool)
@ -891,6 +929,49 @@ mod tests {
cleanup(data, pool).await; cleanup(data, pool).await;
} }
#[tokio::test]
#[serial]
async fn post_listings_removed() {
let pool = &build_db_pool_for_tests().await;
let pool = &mut pool.into();
let mut data = init_data(pool).await;
// Remove the post
Post::update(
pool,
data.inserted_post.id,
&PostUpdateForm::builder().removed(Some(true)).build(),
)
.await
.unwrap();
// Make sure you don't see the removed post in the results
let post_listings_no_admin = PostQuery {
sort: Some(SortType::New),
local_user: Some(&data.local_user_view),
..Default::default()
}
.list(pool)
.await
.unwrap();
assert_eq!(1, post_listings_no_admin.len());
// Removed post is shown to admins on profile page
data.local_user_view.person.admin = true;
let post_listings_is_admin = PostQuery {
sort: Some(SortType::New),
local_user: Some(&data.local_user_view),
is_profile_view: Some(true),
..Default::default()
}
.list(pool)
.await
.unwrap();
assert_eq!(2, post_listings_is_admin.len());
cleanup(data, pool).await;
}
#[tokio::test] #[tokio::test]
#[serial] #[serial]
async fn post_listings_deleted() { async fn post_listings_deleted() {
@ -908,30 +989,33 @@ mod tests {
.unwrap(); .unwrap();
// Make sure you don't see the deleted post in the results // Make sure you don't see the deleted post in the results
let post_listings_no_admin = PostQuery { let post_listings_no_creator = PostQuery {
sort: (Some(SortType::New)), sort: Some(SortType::New),
local_user: (Some(&data.inserted_local_user)),
is_mod_or_admin: (Some(false)),
..Default::default() ..Default::default()
} }
.list(pool) .list(pool)
.await .await
.unwrap(); .unwrap();
let not_contains_deleted = post_listings_no_creator
.iter()
.map(|p| p.post.id)
.all(|p| p != data.inserted_post.id);
assert!(not_contains_deleted);
assert_eq!(1, post_listings_no_admin.len()); // Deleted post is shown to creator
let post_listings_is_creator = PostQuery {
// Make sure they see both sort: Some(SortType::New),
let post_listings_is_admin = PostQuery { local_user: Some(&data.local_user_view),
sort: (Some(SortType::New)),
local_user: (Some(&data.inserted_local_user)),
is_mod_or_admin: (Some(true)),
..Default::default() ..Default::default()
} }
.list(pool) .list(pool)
.await .await
.unwrap(); .unwrap();
let contains_deleted = post_listings_is_creator
assert_eq!(2, post_listings_is_admin.len()); .iter()
.map(|p| p.post.id)
.any(|p| p == data.inserted_post.id);
assert!(contains_deleted);
cleanup(data, pool).await; cleanup(data, pool).await;
} }
@ -941,7 +1025,9 @@ mod tests {
Community::delete(pool, data.inserted_community.id) Community::delete(pool, data.inserted_community.id)
.await .await
.unwrap(); .unwrap();
Person::delete(pool, data.inserted_person.id).await.unwrap(); Person::delete(pool, data.local_user_view.person.id)
.await
.unwrap();
Person::delete(pool, data.inserted_bot.id).await.unwrap(); Person::delete(pool, data.inserted_bot.id).await.unwrap();
Person::delete(pool, data.inserted_blocked_person.id) Person::delete(pool, data.inserted_blocked_person.id)
.await .await
@ -954,7 +1040,7 @@ mod tests {
async fn expected_post_view(data: &Data, pool: &mut DbPool<'_>) -> PostView { async fn expected_post_view(data: &Data, pool: &mut DbPool<'_>) -> PostView {
let (inserted_person, inserted_community, inserted_post) = ( let (inserted_person, inserted_community, inserted_post) = (
&data.inserted_person, &data.local_user_view.person,
&data.inserted_community, &data.inserted_community,
&data.inserted_post, &data.inserted_post,
); );

View file

@ -13,7 +13,7 @@ use lemmy_db_schema::{
}; };
use lemmy_db_views::{ use lemmy_db_views::{
post_view::PostQuery, post_view::PostQuery,
structs::{PostView, SiteView}, structs::{LocalUserView, PostView, SiteView},
}; };
use lemmy_db_views_actor::{ use lemmy_db_views_actor::{
comment_reply_view::CommentReplyQuery, comment_reply_view::CommentReplyQuery,
@ -326,7 +326,7 @@ async fn get_feed_front(
) -> Result<ChannelBuilder, LemmyError> { ) -> Result<ChannelBuilder, LemmyError> {
let site_view = SiteView::read_local(pool).await?; let site_view = SiteView::read_local(pool).await?;
let local_user_id = LocalUserId(Claims::decode(jwt, jwt_secret)?.claims.sub); let local_user_id = LocalUserId(Claims::decode(jwt, jwt_secret)?.claims.sub);
let local_user = LocalUser::read(pool, local_user_id).await?; let local_user = LocalUserView::read(pool, local_user_id).await?;
let posts = PostQuery { let posts = PostQuery {
listing_type: (Some(ListingType::Subscribed)), listing_type: (Some(ListingType::Subscribed)),