Show deleted and removed posts for profile views. Fixes #2624 (#2729)

* Show deleted and removed posts for profile views. Fixes #2624

* Only showing non-deleted/removed posts for creator.

* Add a admin or mod check to views, to show deleted and removed posts.

- Also removed the pointless "blanking" functions

* Fix clippy

* Make hidden posts comment clearer.

* Fixing federation tests.

* Fixing fmt.
This commit is contained in:
Dessalines 2023-02-28 22:46:15 -05:00 committed by GitHub
parent d9e7f0100a
commit 48f187188b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 215 additions and 243 deletions

View file

@ -120,7 +120,6 @@ test("Delete a comment", async () => {
commentRes.comment_view.comment.id commentRes.comment_view.comment.id
); );
expect(deleteCommentRes.comment_view.comment.deleted).toBe(true); expect(deleteCommentRes.comment_view.comment.deleted).toBe(true);
expect(deleteCommentRes.comment_view.comment.content).toBe("");
// Make sure that comment is undefined on beta // Make sure that comment is undefined on beta
let betaCommentRes = (await resolveComment( let betaCommentRes = (await resolveComment(
@ -159,7 +158,6 @@ test("Remove a comment from admin and community on the same instance", async ()
// The beta admin removes it (the community lives on beta) // The beta admin removes it (the community lives on beta)
let removeCommentRes = await removeComment(beta, true, betaCommentId); let removeCommentRes = await removeComment(beta, true, betaCommentId);
expect(removeCommentRes.comment_view.comment.removed).toBe(true); expect(removeCommentRes.comment_view.comment.removed).toBe(true);
expect(removeCommentRes.comment_view.comment.content).toBe("");
// 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 getComments(

View file

@ -388,7 +388,7 @@ test("Enforce site ban for federated user", async () => {
// 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 searchPostLocal(beta, postRes1.post_view.post);
expect(searchBeta2.posts[0]).toBeUndefined(); expect(searchBeta2.posts[0].post.removed).toBe(true);
// Unban alpha // Unban alpha
let unBanAlpha = await banPersonFromSite( let unBanAlpha = await banPersonFromSite(
@ -436,7 +436,7 @@ test("Enforce community ban for federated user", async () => {
// 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 searchPostLocal(alpha, postRes1.post_view.post);
expect(searchAlpha1.posts[0]).toBeUndefined(); expect(searchAlpha1.posts[0].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

@ -64,7 +64,6 @@ test("Delete a private message", async () => {
pmRes.private_message_view.private_message.id pmRes.private_message_view.private_message.id
); );
expect(deletedPmRes.private_message_view.private_message.deleted).toBe(true); expect(deletedPmRes.private_message_view.private_message.deleted).toBe(true);
expect(deletedPmRes.private_message_view.private_message.content).toBe("");
// The GetPrivateMessages filters out deleted, // The GetPrivateMessages filters out deleted,
// even though they are in the actual database. // even though they are in the actual database.

View file

@ -2363,10 +2363,10 @@ kleur@^3.0.3:
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
lemmy-js-client@0.17.0-rc.61: lemmy-js-client@0.17.2-rc.1:
version "0.17.0-rc.61" version "0.17.2-rc.1"
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.17.0-rc.61.tgz#c01e129a3d4c3483ecf337f1e4acf0ad91f9684f" resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.17.2-rc.1.tgz#fe8d1508311bbf245acc98c2c3e47e2165a95b14"
integrity sha512-xauBCD5i4vlUEWqsTMIXLCXeIjAK7ivVIN3C/g+RMAM7mD3CTcRkDZUerwnvLipIfr7V/4iYLWZW0orBaiV1CQ== integrity sha512-YrOXuCofgkqp28krmPTQZAfUWL5zEDA0sRJ0abKcgf/I8YYkYkUkPS9TOORN5Lv3bc8RAAz4+2/zLHqYL/Tnow==
dependencies: dependencies:
node-fetch "2.6.6" node-fetch "2.6.6"

View file

@ -57,7 +57,8 @@ impl Perform for BlockCommunity {
.map_err(|e| LemmyError::from_error_message(e, "community_block_already_exists"))?; .map_err(|e| LemmyError::from_error_message(e, "community_block_already_exists"))?;
} }
let community_view = CommunityView::read(context.pool(), community_id, Some(person_id)).await?; let community_view =
CommunityView::read(context.pool(), community_id, Some(person_id), None).await?;
Ok(BlockCommunityResponse { Ok(BlockCommunityResponse {
blocked: data.block, blocked: data.block,

View file

@ -53,7 +53,8 @@ impl Perform for FollowCommunity {
let community_id = data.community_id; let community_id = data.community_id;
let person_id = local_user_view.person.id; let person_id = local_user_view.person.id;
let community_view = CommunityView::read(context.pool(), community_id, Some(person_id)).await?; let community_view =
CommunityView::read(context.pool(), community_id, Some(person_id), None).await?;
let discussion_languages = CommunityLanguage::read(context.pool(), community_id).await?; let discussion_languages = CommunityLanguage::read(context.pool(), community_id).await?;
Ok(Self::Response { Ok(Self::Response {

View file

@ -83,7 +83,7 @@ impl Perform for TransferCommunity {
let community_id = data.community_id; let community_id = data.community_id;
let person_id = local_user_view.person.id; let person_id = local_user_view.person.id;
let community_view = CommunityView::read(context.pool(), community_id, Some(person_id)) let community_view = CommunityView::read(context.pool(), community_id, Some(person_id), None)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_find_community"))?; .map_err(|e| LemmyError::from_error_message(e, "couldnt_find_community"))?;

View file

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

View file

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

View file

@ -37,7 +37,7 @@ impl Perform for CreatePostReport {
let person_id = local_user_view.person.id; let person_id = local_user_view.person.id;
let post_id = data.post_id; let post_id = data.post_id;
let post_view = PostView::read(context.pool(), post_id, None).await?; let post_view = PostView::read(context.pool(), post_id, None, None).await?;
check_community_ban(person_id, post_view.community.id, context.pool()).await?; check_community_ban(person_id, post_view.community.id, context.pool()).await?;

View file

@ -61,6 +61,23 @@ pub async fn is_mod_or_admin(
Ok(()) Ok(())
} }
#[tracing::instrument(skip_all)]
pub async fn is_mod_or_admin_opt(
pool: &DbPool,
local_user_view: Option<&LocalUserView>,
community_id: Option<CommunityId>,
) -> Result<(), LemmyError> {
if let Some(local_user_view) = local_user_view {
if let Some(community_id) = community_id {
is_mod_or_admin(pool, local_user_view.person.id, community_id).await
} else {
is_admin(local_user_view)
}
} else {
Err(LemmyError::from_message("not_a_mod_or_admin"))
}
}
pub async fn is_top_admin(pool: &DbPool, person_id: PersonId) -> Result<(), LemmyError> { pub async fn is_top_admin(pool: &DbPool, person_id: PersonId) -> Result<(), LemmyError> {
let admins = PersonViewSafe::admins(pool).await?; let admins = PersonViewSafe::admins(pool).await?;
let top_admin = admins let top_admin = admins

View file

@ -17,7 +17,7 @@ use lemmy_db_schema::{
person_mention::{PersonMention, PersonMentionInsertForm}, person_mention::{PersonMention, PersonMentionInsertForm},
post::Post, post::Post,
}, },
traits::{Crud, DeleteableOrRemoveable}, traits::Crud,
SubscribedType, SubscribedType,
}; };
use lemmy_db_views::structs::{CommentView, LocalUserView, PostView, PrivateMessageView}; use lemmy_db_views::structs::{CommentView, LocalUserView, PostView, PrivateMessageView};
@ -32,7 +32,7 @@ pub async fn send_post_ws_message<OP: ToString + Send + OperationType + 'static>
person_id: Option<PersonId>, person_id: Option<PersonId>,
context: &LemmyContext, context: &LemmyContext,
) -> Result<PostResponse, LemmyError> { ) -> Result<PostResponse, LemmyError> {
let post_view = PostView::read(context.pool(), post_id, person_id).await?; let post_view = PostView::read(context.pool(), post_id, person_id, Some(true)).await?;
let res = PostResponse { post_view }; let res = PostResponse { post_view };
@ -65,11 +65,7 @@ pub async fn send_comment_ws_message<OP: ToString + Send + OperationType + 'stat
recipient_ids: Vec<LocalUserId>, recipient_ids: Vec<LocalUserId>,
context: &LemmyContext, context: &LemmyContext,
) -> Result<CommentResponse, LemmyError> { ) -> Result<CommentResponse, LemmyError> {
let mut view = CommentView::read(context.pool(), comment_id, person_id).await?; let view = CommentView::read(context.pool(), comment_id, person_id).await?;
if view.comment.deleted || view.comment.removed {
view.comment = view.comment.blank_out_deleted_or_removed_info();
}
let mut res = CommentResponse { let mut res = CommentResponse {
comment_view: view, comment_view: view,
@ -98,7 +94,8 @@ pub async fn send_community_ws_message<OP: ToString + Send + OperationType + 'st
person_id: Option<PersonId>, person_id: Option<PersonId>,
context: &LemmyContext, context: &LemmyContext,
) -> Result<CommunityResponse, LemmyError> { ) -> Result<CommunityResponse, LemmyError> {
let community_view = CommunityView::read(context.pool(), community_id, person_id).await?; let community_view =
CommunityView::read(context.pool(), community_id, person_id, Some(true)).await?;
let discussion_languages = CommunityLanguage::read(context.pool(), community_id).await?; let discussion_languages = CommunityLanguage::read(context.pool(), community_id).await?;
let mut res = CommunityResponse { let mut res = CommunityResponse {
@ -124,12 +121,7 @@ pub async fn send_pm_ws_message<OP: ToString + Send + OperationType + 'static>(
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
context: &LemmyContext, context: &LemmyContext,
) -> Result<PrivateMessageResponse, LemmyError> { ) -> Result<PrivateMessageResponse, LemmyError> {
let mut view = PrivateMessageView::read(context.pool(), private_message_id).await?; let view = PrivateMessageView::read(context.pool(), private_message_id).await?;
// Blank out deleted or removed info
if view.private_message.deleted {
view.private_message = view.private_message.blank_out_deleted_or_removed_info();
}
let res = PrivateMessageResponse { let res = PrivateMessageResponse {
private_message_view: view, private_message_view: view,

View file

@ -147,7 +147,7 @@ impl PerformCrud for CreateCommunity {
let person_id = local_user_view.person.id; let person_id = local_user_view.person.id;
let community_view = let community_view =
CommunityView::read(context.pool(), inserted_community.id, Some(person_id)).await?; CommunityView::read(context.pool(), inserted_community.id, Some(person_id), None).await?;
let discussion_languages = let discussion_languages =
CommunityLanguage::read(context.pool(), inserted_community.id).await?; CommunityLanguage::read(context.pool(), inserted_community.id).await?;

View file

@ -3,9 +3,9 @@ use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
community::{ListCommunities, ListCommunitiesResponse}, community::{ListCommunities, ListCommunitiesResponse},
context::LemmyContext, context::LemmyContext,
utils::{check_private_instance, get_local_user_view_from_jwt_opt}, utils::{check_private_instance, get_local_user_view_from_jwt_opt, is_admin},
}; };
use lemmy_db_schema::{source::local_site::LocalSite, traits::DeleteableOrRemoveable}; use lemmy_db_schema::source::local_site::LocalSite;
use lemmy_db_views_actor::community_view::CommunityQuery; use lemmy_db_views_actor::community_view::CommunityQuery;
use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_utils::{error::LemmyError, ConnectionId};
@ -24,37 +24,27 @@ impl PerformCrud for ListCommunities {
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret()) get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
.await?; .await?;
let local_site = LocalSite::read(context.pool()).await?; let local_site = LocalSite::read(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)?;
let person_id = local_user_view.clone().map(|l| l.person.id);
let sort = data.sort; let sort = data.sort;
let listing_type = data.type_; let listing_type = data.type_;
let page = data.page; let page = data.page;
let limit = data.limit; let limit = data.limit;
let local_user = local_user_view.map(|l| l.local_user); let local_user = local_user_view.map(|l| l.local_user);
let mut communities = CommunityQuery::builder() let communities = CommunityQuery::builder()
.pool(context.pool()) .pool(context.pool())
.listing_type(listing_type) .listing_type(listing_type)
.sort(sort) .sort(sort)
.local_user(local_user.as_ref()) .local_user(local_user.as_ref())
.page(page) .page(page)
.limit(limit) .limit(limit)
.is_mod_or_admin(is_admin)
.build() .build()
.list() .list()
.await?; .await?;
// Blank out deleted or removed info for non-logged in users
if person_id.is_none() {
for cv in communities
.iter_mut()
.filter(|cv| cv.community.deleted || cv.community.removed)
{
cv.community = cv.clone().community.blank_out_deleted_or_removed_info();
}
}
// Return the jwt // Return the jwt
Ok(ListCommunitiesResponse { communities }) Ok(ListCommunitiesResponse { communities })
} }

View file

@ -3,12 +3,17 @@ use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
context::LemmyContext, context::LemmyContext,
post::{GetPost, GetPostResponse}, post::{GetPost, GetPostResponse},
utils::{check_private_instance, get_local_user_view_from_jwt_opt, mark_post_as_read}, utils::{
check_private_instance,
get_local_user_view_from_jwt_opt,
is_mod_or_admin_opt,
mark_post_as_read,
},
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
aggregates::structs::{PersonPostAggregates, PersonPostAggregatesForm}, aggregates::structs::{PersonPostAggregates, PersonPostAggregatesForm},
source::{comment::Comment, local_site::LocalSite}, source::{comment::Comment, local_site::LocalSite, post::Post},
traits::{Crud, DeleteableOrRemoveable}, traits::Crud,
}; };
use lemmy_db_views::structs::PostView; use lemmy_db_views::structs::PostView;
use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView}; use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView};
@ -32,7 +37,7 @@ impl PerformCrud for GetPost {
check_private_instance(&local_user_view, &local_site)?; check_private_instance(&local_user_view, &local_site)?;
let person_id = local_user_view.map(|u| u.person.id); let person_id = local_user_view.as_ref().map(|u| u.person.id);
// I'd prefer fetching the post_view by a comment join, but it adds a lot of boilerplate // I'd prefer fetching the post_view by a comment join, but it adds a lot of boilerplate
let post_id = if let Some(id) = data.id { let post_id = if let Some(id) = data.id {
@ -46,7 +51,14 @@ impl PerformCrud for GetPost {
Err(LemmyError::from_message("couldnt_find_post"))? Err(LemmyError::from_message("couldnt_find_post"))?
}; };
let mut post_view = PostView::read(context.pool(), post_id, person_id) // Check to see if the person is a mod or admin, to show deleted / removed
let community_id = Post::read(context.pool(), post_id).await?.community_id;
let is_mod_or_admin =
is_mod_or_admin_opt(context.pool(), local_user_view.as_ref(), Some(community_id))
.await
.is_ok();
let post_view = PostView::read(context.pool(), post_id, person_id, Some(is_mod_or_admin))
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_find_post"))?; .map_err(|e| LemmyError::from_error_message(e, "couldnt_find_post"))?;
@ -57,8 +69,12 @@ impl PerformCrud for GetPost {
} }
// Necessary for the sidebar subscribed // Necessary for the sidebar subscribed
let community_id = post_view.community.id; let community_view = CommunityView::read(
let mut community_view = CommunityView::read(context.pool(), community_id, person_id) context.pool(),
community_id,
person_id,
Some(is_mod_or_admin),
)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_find_community"))?; .map_err(|e| LemmyError::from_error_message(e, "couldnt_find_community"))?;
@ -77,17 +93,6 @@ impl PerformCrud for GetPost {
.map_err(|e| LemmyError::from_error_message(e, "couldnt_find_post"))?; .map_err(|e| LemmyError::from_error_message(e, "couldnt_find_post"))?;
} }
// Blank out deleted or removed info for non-logged in users
if person_id.is_none() {
if post_view.post.deleted || post_view.post.removed {
post_view.post = post_view.post.blank_out_deleted_or_removed_info();
}
if community_view.community.deleted || community_view.community.removed {
community_view.community = community_view.community.blank_out_deleted_or_removed_info();
}
}
let moderators = CommunityModeratorView::for_community(context.pool(), community_id).await?; let moderators = CommunityModeratorView::for_community(context.pool(), community_id).await?;
let online = context.chat_server().get_post_users_online(post_id)?; let online = context.chat_server().get_post_users_online(post_id)?;

View file

@ -5,7 +5,6 @@ use lemmy_api_common::{
private_message::{GetPrivateMessages, PrivateMessagesResponse}, private_message::{GetPrivateMessages, PrivateMessagesResponse},
utils::get_local_user_view_from_jwt, utils::get_local_user_view_from_jwt,
}; };
use lemmy_db_schema::traits::DeleteableOrRemoveable;
use lemmy_db_views::private_message_view::PrivateMessageQuery; use lemmy_db_views::private_message_view::PrivateMessageQuery;
use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_utils::{error::LemmyError, ConnectionId};
@ -45,17 +44,6 @@ impl PerformCrud for GetPrivateMessages {
} }
}); });
// Blank out deleted or removed info
for pmv in messages
.iter_mut()
.filter(|pmv| pmv.private_message.deleted)
{
pmv.private_message = pmv
.clone()
.private_message
.blank_out_deleted_or_removed_info();
}
Ok(PrivateMessagesResponse { Ok(PrivateMessagesResponse {
private_messages: messages, private_messages: messages,
}) })

View file

@ -99,7 +99,8 @@ impl ActivityHandler for AcceptFollow {
// Send the Subscribed message over websocket // Send the Subscribed message over websocket
// Re-read the community_view to get the new SubscribedType // Re-read the community_view to get the new SubscribedType
let community_view = CommunityView::read(context.pool(), community_id, Some(person_id)).await?; let community_view =
CommunityView::read(context.pool(), community_id, Some(person_id), None).await?;
// Get the local_user_id // Get the local_user_id
let local_recipient_id = LocalUserView::read_person(context.pool(), person_id) let local_recipient_id = LocalUserView::read_person(context.pool(), person_id)

View file

@ -15,7 +15,7 @@ use lemmy_api_common::{
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{comment::Comment, community::Community, local_site::LocalSite}, source::{comment::Comment, community::Community, local_site::LocalSite},
traits::{Crud, DeleteableOrRemoveable}, traits::Crud,
}; };
use lemmy_db_views::comment_view::CommentQuery; use lemmy_db_views::comment_view::CommentQuery;
use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_utils::{error::LemmyError, ConnectionId};
@ -65,7 +65,7 @@ impl PerformApub for GetComments {
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 local_user = local_user_view.map(|l| l.local_user);
let mut comments = CommentQuery::builder() let comments = CommentQuery::builder()
.pool(context.pool()) .pool(context.pool())
.listing_type(Some(listing_type)) .listing_type(Some(listing_type))
.sort(sort) .sort(sort)
@ -83,14 +83,6 @@ impl PerformApub for GetComments {
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_get_comments"))?; .map_err(|e| LemmyError::from_error_message(e, "couldnt_get_comments"))?;
// Blank out deleted or removed info
for cv in comments
.iter_mut()
.filter(|cv| cv.comment.deleted || cv.comment.removed)
{
cv.comment = cv.clone().comment.blank_out_deleted_or_removed_info();
}
Ok(GetCommentsResponse { comments }) Ok(GetCommentsResponse { comments })
} }
} }

View file

@ -10,13 +10,11 @@ use lemmy_api_common::{
utils::{ utils::{
check_private_instance, check_private_instance,
get_local_user_view_from_jwt_opt, get_local_user_view_from_jwt_opt,
is_mod_or_admin_opt,
listing_type_with_site_default, listing_type_with_site_default,
}, },
}; };
use lemmy_db_schema::{ use lemmy_db_schema::source::{community::Community, local_site::LocalSite};
source::{community::Community, local_site::LocalSite},
traits::DeleteableOrRemoveable,
};
use lemmy_db_views::post_view::PostQuery; use lemmy_db_views::post_view::PostQuery;
use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_utils::{error::LemmyError, ConnectionId};
@ -38,8 +36,6 @@ impl PerformApub for GetPosts {
check_private_instance(&local_user_view, &local_site)?; check_private_instance(&local_user_view, &local_site)?;
let is_logged_in = local_user_view.is_some();
let sort = data.sort; let sort = data.sort;
let listing_type = listing_type_with_site_default(data.type_, &local_site)?; let listing_type = listing_type_with_site_default(data.type_, &local_site)?;
@ -56,7 +52,12 @@ impl PerformApub for GetPosts {
}; };
let saved_only = data.saved_only; let saved_only = data.saved_only;
let mut posts = PostQuery::builder() let is_mod_or_admin =
is_mod_or_admin_opt(context.pool(), local_user_view.as_ref(), community_id)
.await
.is_ok();
let posts = PostQuery::builder()
.pool(context.pool()) .pool(context.pool())
.local_user(local_user_view.map(|l| l.local_user).as_ref()) .local_user(local_user_view.map(|l| l.local_user).as_ref())
.listing_type(Some(listing_type)) .listing_type(Some(listing_type))
@ -66,28 +67,12 @@ impl PerformApub for GetPosts {
.saved_only(saved_only) .saved_only(saved_only)
.page(page) .page(page)
.limit(limit) .limit(limit)
.is_mod_or_admin(Some(is_mod_or_admin))
.build() .build()
.list() .list()
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_get_posts"))?; .map_err(|e| LemmyError::from_error_message(e, "couldnt_get_posts"))?;
// Blank out deleted or removed info for non-logged in users
if !is_logged_in {
for pv in posts
.iter_mut()
.filter(|p| p.post.deleted || p.post.removed)
{
pv.post = pv.clone().post.blank_out_deleted_or_removed_info();
}
for pv in posts
.iter_mut()
.filter(|p| p.community.deleted || p.community.removed)
{
pv.community = pv.clone().community.blank_out_deleted_or_removed_info();
}
}
Ok(GetPostsResponse { posts }) Ok(GetPostsResponse { posts })
} }
} }

View file

@ -7,7 +7,7 @@ use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
community::{GetCommunity, GetCommunityResponse}, community::{GetCommunity, GetCommunityResponse},
context::LemmyContext, context::LemmyContext,
utils::{check_private_instance, get_local_user_view_from_jwt_opt}, utils::{check_private_instance, get_local_user_view_from_jwt_opt, is_mod_or_admin_opt},
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
impls::actor_language::default_post_language, impls::actor_language::default_post_language,
@ -17,7 +17,6 @@ use lemmy_db_schema::{
local_site::LocalSite, local_site::LocalSite,
site::Site, site::Site,
}, },
traits::DeleteableOrRemoveable,
}; };
use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView}; use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView};
use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_utils::{error::LemmyError, ConnectionId};
@ -57,16 +56,20 @@ impl PerformApub for GetCommunity {
} }
}; };
let mut community_view = CommunityView::read(context.pool(), community_id, person_id) let is_mod_or_admin =
is_mod_or_admin_opt(context.pool(), local_user_view.as_ref(), Some(community_id))
.await
.is_ok();
let community_view = CommunityView::read(
context.pool(),
community_id,
person_id,
Some(is_mod_or_admin),
)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_find_community"))?; .map_err(|e| LemmyError::from_error_message(e, "couldnt_find_community"))?;
// Blank out deleted or removed info for non-logged in users
if person_id.is_none() && (community_view.community.deleted || community_view.community.removed)
{
community_view.community = community_view.community.blank_out_deleted_or_removed_info();
}
let moderators = CommunityModeratorView::for_community(context.pool(), community_id) let moderators = CommunityModeratorView::for_community(context.pool(), community_id)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_find_community"))?; .map_err(|e| LemmyError::from_error_message(e, "couldnt_find_community"))?;

View file

@ -3,7 +3,7 @@ use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
context::LemmyContext, context::LemmyContext,
person::{GetPersonDetails, GetPersonDetailsResponse}, person::{GetPersonDetails, GetPersonDetailsResponse},
utils::{check_private_instance, get_local_user_view_from_jwt_opt}, utils::{check_private_instance, get_local_user_view_from_jwt_opt, is_admin},
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{local_site::LocalSite, person::Person}, source::{local_site::LocalSite, person::Person},
@ -34,6 +34,7 @@ impl PerformApub for GetPersonDetails {
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret()) get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
.await?; .await?;
let local_site = LocalSite::read(context.pool()).await?; let local_site = LocalSite::read(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)?;
@ -71,6 +72,7 @@ impl PerformApub for GetPersonDetails {
.saved_only(saved_only) .saved_only(saved_only)
.local_user(local_user.as_ref()) .local_user(local_user.as_ref())
.community_id(community_id) .community_id(community_id)
.is_mod_or_admin(is_admin)
.page(page) .page(page)
.limit(limit); .limit(limit);

View file

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

View file

@ -7,11 +7,10 @@ use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
context::LemmyContext, context::LemmyContext,
site::{Search, SearchResponse}, site::{Search, SearchResponse},
utils::{check_private_instance, get_local_user_view_from_jwt_opt}, utils::{check_private_instance, get_local_user_view_from_jwt_opt, is_admin},
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{community::Community, local_site::LocalSite}, source::{community::Community, local_site::LocalSite},
traits::DeleteableOrRemoveable,
utils::post_to_comment_sort_type, utils::post_to_comment_sort_type,
SearchType, SearchType,
}; };
@ -38,7 +37,8 @@ impl PerformApub for Search {
check_private_instance(&local_user_view, &local_site)?; check_private_instance(&local_user_view, &local_site)?;
let person_id = local_user_view.as_ref().map(|u| u.person.id); let is_admin = local_user_view.as_ref().map(|luv| is_admin(luv).is_ok());
let local_user = local_user_view.map(|l| l.local_user); let local_user = local_user_view.map(|l| l.local_user);
let mut posts = Vec::new(); let mut posts = Vec::new();
@ -75,6 +75,7 @@ impl PerformApub for Search {
.creator_id(creator_id) .creator_id(creator_id)
.local_user(local_user.as_ref()) .local_user(local_user.as_ref())
.search_term(Some(q)) .search_term(Some(q))
.is_mod_or_admin(is_admin)
.page(page) .page(page)
.limit(limit) .limit(limit)
.build() .build()
@ -104,6 +105,7 @@ impl PerformApub for Search {
.listing_type(listing_type) .listing_type(listing_type)
.search_term(Some(q)) .search_term(Some(q))
.local_user(local_user.as_ref()) .local_user(local_user.as_ref())
.is_mod_or_admin(is_admin)
.page(page) .page(page)
.limit(limit) .limit(limit)
.build() .build()
@ -137,6 +139,7 @@ impl PerformApub for Search {
.creator_id(creator_id) .creator_id(creator_id)
.local_user(local_user_.as_ref()) .local_user(local_user_.as_ref())
.search_term(Some(q)) .search_term(Some(q))
.is_mod_or_admin(is_admin)
.page(page) .page(page)
.limit(limit) .limit(limit)
.build() .build()
@ -173,6 +176,7 @@ impl PerformApub for Search {
.listing_type(listing_type) .listing_type(listing_type)
.search_term(Some(q)) .search_term(Some(q))
.local_user(local_user.as_ref()) .local_user(local_user.as_ref())
.is_mod_or_admin(is_admin)
.page(page) .page(page)
.limit(limit) .limit(limit)
.build() .build()
@ -205,6 +209,7 @@ impl PerformApub for Search {
.community_actor_id(community_actor_id) .community_actor_id(community_actor_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)
.build() .build()
@ -213,30 +218,6 @@ impl PerformApub for Search {
} }
}; };
// Blank out deleted or removed info for non logged in users
if person_id.is_none() {
for cv in communities
.iter_mut()
.filter(|cv| cv.community.deleted || cv.community.removed)
{
cv.community = cv.clone().community.blank_out_deleted_or_removed_info();
}
for pv in posts
.iter_mut()
.filter(|p| p.post.deleted || p.post.removed)
{
pv.post = pv.clone().post.blank_out_deleted_or_removed_info();
}
for cv in comments
.iter_mut()
.filter(|cv| cv.comment.deleted || cv.comment.removed)
{
cv.comment = cv.clone().comment.blank_out_deleted_or_removed_info();
}
}
// Return the jwt // Return the jwt
Ok(SearchResponse { Ok(SearchResponse {
type_: search_type.to_string(), type_: search_type.to_string(),

View file

@ -10,7 +10,7 @@ use crate::{
CommentSavedForm, CommentSavedForm,
CommentUpdateForm, CommentUpdateForm,
}, },
traits::{Crud, DeleteableOrRemoveable, Likeable, Saveable}, traits::{Crud, Likeable, Saveable},
utils::{get_conn, naive_now, DbPool}, utils::{get_conn, naive_now, DbPool},
}; };
use diesel::{ use diesel::{
@ -240,13 +240,6 @@ impl Saveable for CommentSaved {
} }
} }
impl DeleteableOrRemoveable for Comment {
fn blank_out_deleted_or_removed_info(mut self) -> Self {
self.content = String::new();
self
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{

View file

@ -12,11 +12,10 @@ use crate::{
CommunityModeratorForm, CommunityModeratorForm,
CommunityPersonBan, CommunityPersonBan,
CommunityPersonBanForm, CommunityPersonBanForm,
CommunitySafe,
CommunityUpdateForm, CommunityUpdateForm,
}, },
}, },
traits::{ApubActor, Bannable, Crud, DeleteableOrRemoveable, Followable, Joinable}, traits::{ApubActor, Bannable, Crud, Followable, Joinable},
utils::{functions::lower, get_conn, DbPool}, utils::{functions::lower, get_conn, DbPool},
SubscribedType, SubscribedType,
}; };
@ -174,26 +173,6 @@ impl Joinable for CommunityModerator {
} }
} }
impl DeleteableOrRemoveable for CommunitySafe {
fn blank_out_deleted_or_removed_info(mut self) -> Self {
self.title = String::new();
self.description = None;
self.icon = None;
self.banner = None;
self
}
}
impl DeleteableOrRemoveable for Community {
fn blank_out_deleted_or_removed_info(mut self) -> Self {
self.title = String::new();
self.description = None;
self.icon = None;
self.banner = None;
self
}
}
pub enum CollectionType { pub enum CollectionType {
Moderators, Moderators,
Featured, Featured,

View file

@ -26,7 +26,7 @@ use crate::{
PostSavedForm, PostSavedForm,
PostUpdateForm, PostUpdateForm,
}, },
traits::{Crud, DeleteableOrRemoveable, Likeable, Readable, Saveable}, traits::{Crud, Likeable, Readable, Saveable},
utils::{get_conn, naive_now, DbPool, FETCH_LIMIT_MAX}, utils::{get_conn, naive_now, DbPool, FETCH_LIMIT_MAX},
}; };
use ::url::Url; use ::url::Url;
@ -317,20 +317,6 @@ impl Readable for PostRead {
} }
} }
impl DeleteableOrRemoveable for Post {
fn blank_out_deleted_or_removed_info(mut self) -> Self {
self.name = String::new();
self.url = None;
self.body = None;
self.embed_title = None;
self.embed_description = None;
self.embed_video_url = None;
self.thumbnail_url = None;
self
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{

View file

@ -2,7 +2,7 @@ use crate::{
newtypes::{DbUrl, PersonId, PrivateMessageId}, newtypes::{DbUrl, PersonId, PrivateMessageId},
schema::private_message::dsl::{ap_id, private_message, read, recipient_id}, schema::private_message::dsl::{ap_id, private_message, read, recipient_id},
source::private_message::{PrivateMessage, PrivateMessageInsertForm, PrivateMessageUpdateForm}, source::private_message::{PrivateMessage, PrivateMessageInsertForm, PrivateMessageUpdateForm},
traits::{Crud, DeleteableOrRemoveable}, traits::Crud,
utils::{get_conn, DbPool}, utils::{get_conn, DbPool},
}; };
use diesel::{dsl::insert_into, result::Error, ExpressionMethods, QueryDsl}; use diesel::{dsl::insert_into, result::Error, ExpressionMethods, QueryDsl};
@ -86,13 +86,6 @@ impl PrivateMessage {
} }
} }
impl DeleteableOrRemoveable for PrivateMessage {
fn blank_out_deleted_or_removed_info(mut self) -> Self {
self.content = String::new();
self
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{

View file

@ -140,11 +140,6 @@ pub trait Reportable {
Self: Sized; Self: Sized;
} }
// TODO these should be removed, there should be another way to do this
pub trait DeleteableOrRemoveable {
fn blank_out_deleted_or_removed_info(self) -> Self;
}
pub trait ToSafe { pub trait ToSafe {
type SafeColumns; type SafeColumns;
fn safe_columns_tuple() -> Self::SafeColumns; fn safe_columns_tuple() -> Self::SafeColumns;

View file

@ -68,24 +68,14 @@ impl PostView {
pool: &DbPool, pool: &DbPool,
post_id: PostId, post_id: PostId,
my_person_id: Option<PersonId>, my_person_id: Option<PersonId>,
is_mod_or_admin: Option<bool>,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
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 = my_person_id.unwrap_or(PersonId(-1)); let person_id_join = my_person_id.unwrap_or(PersonId(-1));
let ( let person_alias_1 = diesel::alias!(person as person1);
post, let mut query = post::table
creator,
community,
creator_banned_from_community,
counts,
follower,
saved,
read,
creator_blocked,
post_like,
unread_comments,
) = post::table
.find(post_id) .find(post_id)
.inner_join(person::table) .inner_join(person::table)
.inner_join(community::table) .inner_join(community::table)
@ -144,6 +134,14 @@ impl PostView {
.and(person_post_aggregates::person_id.eq(person_id_join)), .and(person_post_aggregates::person_id.eq(person_id_join)),
), ),
) )
// Used to check if you are the post creator
.left_join(
person_alias_1.on(
post::creator_id
.eq(person_alias_1.field(person::id))
.and(person_alias_1.field(person::id).eq(person_id_join)),
),
)
.select(( .select((
post::all_columns, post::all_columns,
Person::safe_columns_tuple(), Person::safe_columns_tuple(),
@ -160,8 +158,38 @@ impl PostView {
post_aggregates::comments, post_aggregates::comments,
), ),
)) ))
.first::<PostViewTuple>(conn) .into_boxed();
.await?;
// If you are not a moderator, exclude deleted or removed content
if !is_mod_or_admin.unwrap_or(true) {
// If you are not the creator, then remove the other fields.
query = query
.filter(
person_alias_1.field(person::id).is_null().and(
post::removed
.eq(false)
.and(post::deleted.eq(false))
.and(community::removed.eq(false))
.and(community::deleted.eq(false)),
),
)
// If you are the creator, keep them
.or_filter(person_alias_1.field(person::id).is_not_null())
}
let (
post,
creator,
community,
creator_banned_from_community,
counts,
follower,
saved,
read,
creator_blocked,
post_like,
unread_comments,
) = query.first::<PostViewTuple>(conn).await?;
// If a person is given, then my_vote, if None, should be 0, not null // If a person is given, then my_vote, if None, should be 0, not null
// Necessary to differentiate between other person's votes // Necessary to differentiate between other person's votes
@ -201,6 +229,8 @@ pub struct PostQuery<'a> {
search_term: Option<String>, search_term: Option<String>,
url_search: Option<String>, url_search: Option<String>,
saved_only: Option<bool>, saved_only: Option<bool>,
/// Used to show deleted or removed posts for admins
is_mod_or_admin: Option<bool>,
page: Option<i64>, page: Option<i64>,
limit: Option<i64>, limit: Option<i64>,
} }
@ -212,6 +242,7 @@ impl<'a> PostQuery<'a> {
// 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.id).unwrap_or(LocalUserId(-1));
let person_alias_1 = diesel::alias!(person as person1);
let mut query = post::table let mut query = post::table
.inner_join(person::table) .inner_join(person::table)
@ -285,6 +316,14 @@ impl<'a> PostQuery<'a> {
.and(local_user_language::local_user_id.eq(local_user_id_join)), .and(local_user_language::local_user_id.eq(local_user_id_join)),
), ),
) )
// Used to check if you are the post creator
.left_join(
person_alias_1.on(
post::creator_id
.eq(person_alias_1.field(person::id))
.and(person_alias_1.field(person::id).eq(person_id_join)),
),
)
.select(( .select((
post::all_columns, post::all_columns,
Person::safe_columns_tuple(), Person::safe_columns_tuple(),
@ -303,6 +342,23 @@ impl<'a> PostQuery<'a> {
)) ))
.into_boxed(); .into_boxed();
// If you are not a moderator, exclude deleted or removed content
if !self.is_mod_or_admin.unwrap_or(true) {
// If you are not the creator, then remove the other fields.
query = query
.filter(
person_alias_1.field(person::id).is_null().and(
post::removed
.eq(false)
.and(post::deleted.eq(false))
.and(community::removed.eq(false))
.and(community::deleted.eq(false)),
),
)
// If you are the creator, keep them
.or_filter(person_alias_1.field(person::id).is_not_null())
}
if let Some(listing_type) = self.listing_type { if let Some(listing_type) = self.listing_type {
match listing_type { match listing_type {
ListingType::Subscribed => { ListingType::Subscribed => {
@ -349,7 +405,6 @@ impl<'a> PostQuery<'a> {
); );
} }
// If its for a specific person, show the removed / deleted
if let Some(creator_id) = self.creator_id { if let Some(creator_id) = self.creator_id {
query = query.filter(post::creator_id.eq(creator_id)); query = query.filter(post::creator_id.eq(creator_id));
} }
@ -424,13 +479,7 @@ impl<'a> PostQuery<'a> {
let (limit, offset) = limit_and_offset(self.page, self.limit)?; let (limit, offset) = limit_and_offset(self.page, self.limit)?;
query = query query = query.limit(limit).offset(offset);
.limit(limit)
.offset(offset)
.filter(post::removed.eq(false))
.filter(post::deleted.eq(false))
.filter(community::removed.eq(false))
.filter(community::deleted.eq(false));
debug!("Post View Query: {:?}", debug_query::<Pg, _>(&query)); debug!("Post View Query: {:?}", debug_query::<Pg, _>(&query));
@ -615,8 +664,12 @@ mod tests {
.await .await
.unwrap(); .unwrap();
let post_listing_single_with_person = let post_listing_single_with_person = PostView::read(
PostView::read(pool, data.inserted_post.id, Some(data.inserted_person.id)) pool,
data.inserted_post.id,
Some(data.inserted_person.id),
None,
)
.await .await
.unwrap(); .unwrap();
@ -670,7 +723,8 @@ mod tests {
.await .await
.unwrap(); .unwrap();
let read_post_listing_single_no_person = PostView::read(pool, data.inserted_post.id, None) let read_post_listing_single_no_person =
PostView::read(pool, data.inserted_post.id, None, None)
.await .await
.unwrap(); .unwrap();

View file

@ -37,12 +37,13 @@ impl CommunityView {
pool: &DbPool, pool: &DbPool,
community_id: CommunityId, community_id: CommunityId,
my_person_id: Option<PersonId>, my_person_id: Option<PersonId>,
is_mod_or_admin: Option<bool>,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
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 = my_person_id.unwrap_or(PersonId(-1)); let person_id_join = my_person_id.unwrap_or(PersonId(-1));
let (community, counts, follower, blocked) = community::table let mut query = community::table
.find(community_id) .find(community_id)
.inner_join(community_aggregates::table) .inner_join(community_aggregates::table)
.left_join( .left_join(
@ -65,8 +66,16 @@ impl CommunityView {
community_follower::all_columns.nullable(), community_follower::all_columns.nullable(),
community_block::all_columns.nullable(), community_block::all_columns.nullable(),
)) ))
.first::<CommunityViewTuple>(conn) .into_boxed();
.await?;
// Hide deleted and removed for non-admins or mods
if !is_mod_or_admin.unwrap_or(true) {
query = query
.filter(community::removed.eq(false))
.filter(community::deleted.eq(false));
}
let (community, counts, follower, blocked) = query.first::<CommunityViewTuple>(conn).await?;
Ok(CommunityView { Ok(CommunityView {
community, community,
@ -116,6 +125,7 @@ pub struct CommunityQuery<'a> {
sort: Option<SortType>, sort: Option<SortType>,
local_user: Option<&'a LocalUser>, local_user: Option<&'a LocalUser>,
search_term: Option<String>, search_term: Option<String>,
is_mod_or_admin: Option<bool>,
page: Option<i64>, page: Option<i64>,
limit: Option<i64>, limit: Option<i64>,
} }
@ -159,6 +169,13 @@ impl<'a> CommunityQuery<'a> {
.or_filter(community::title.ilike(searcher)); .or_filter(community::title.ilike(searcher));
}; };
// Hide deleted and removed for non-admins or mods
if !self.is_mod_or_admin.unwrap_or(true) {
query = query
.filter(community::removed.eq(false))
.filter(community::deleted.eq(false));
}
match self.sort.unwrap_or(SortType::Hot) { match self.sort.unwrap_or(SortType::Hot) {
SortType::New => query = query.order_by(community::published.desc()), SortType::New => query = query.order_by(community::published.desc()),
SortType::TopAll => query = query.order_by(community_aggregates::subscribers.desc()), SortType::TopAll => query = query.order_by(community_aggregates::subscribers.desc()),