]> Untitled Git - lemmy.git/commitdiff
Show deleted and removed posts for profile views. Fixes #2624 (#2729)
authorDessalines <dessalines@users.noreply.github.com>
Wed, 1 Mar 2023 03:46:15 +0000 (22:46 -0500)
committerGitHub <noreply@github.com>
Wed, 1 Mar 2023 03:46:15 +0000 (22:46 -0500)
* 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.

30 files changed:
api_tests/src/comment.spec.ts
api_tests/src/post.spec.ts
api_tests/src/private_message.spec.ts
api_tests/yarn.lock
crates/api/src/community/block.rs
crates/api/src/community/follow.rs
crates/api/src/community/transfer.rs
crates/api/src/post/mark_read.rs
crates/api/src/post/save.rs
crates/api/src/post_report/create.rs
crates/api_common/src/utils.rs
crates/api_common/src/websocket/send.rs
crates/api_crud/src/community/create.rs
crates/api_crud/src/community/list.rs
crates/api_crud/src/post/read.rs
crates/api_crud/src/private_message/read.rs
crates/apub/src/activities/following/accept.rs
crates/apub/src/api/list_comments.rs
crates/apub/src/api/list_posts.rs
crates/apub/src/api/read_community.rs
crates/apub/src/api/read_person.rs
crates/apub/src/api/resolve_object.rs
crates/apub/src/api/search.rs
crates/db_schema/src/impls/comment.rs
crates/db_schema/src/impls/community.rs
crates/db_schema/src/impls/post.rs
crates/db_schema/src/impls/private_message.rs
crates/db_schema/src/traits.rs
crates/db_views/src/post_view.rs
crates/db_views_actor/src/community_view.rs

index 01b6efd52cb742f9480d4ae399659691c3abf7db..a951c1ee07f6976e8572c7b159cc40696b00f567 100644 (file)
@@ -120,7 +120,6 @@ test("Delete a comment", async () => {
     commentRes.comment_view.comment.id
   );
   expect(deleteCommentRes.comment_view.comment.deleted).toBe(true);
-  expect(deleteCommentRes.comment_view.comment.content).toBe("");
 
   // Make sure that comment is undefined on beta
   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)
   let removeCommentRes = await removeComment(beta, true, betaCommentId);
   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)
   let refetchedPostComments = await getComments(
index 5aac0f5118721d60051b5323526825f7c3491601..24ce44b8451a935ba4c2267cf6dc3f2259b9cfae 100644 (file)
@@ -388,7 +388,7 @@ test("Enforce site ban for federated user", async () => {
 
   // existing alpha post should be removed on beta
   let searchBeta2 = await searchPostLocal(beta, postRes1.post_view.post);
-  expect(searchBeta2.posts[0]).toBeUndefined();
+  expect(searchBeta2.posts[0].post.removed).toBe(true);
 
   // Unban alpha
   let unBanAlpha = await banPersonFromSite(
@@ -436,7 +436,7 @@ test("Enforce community ban for federated user", async () => {
 
   // ensure that the post by alpha got removed
   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
   let postRes2 = await createPost(alpha, betaCommunity.community.id);
index 751f722303445c4fbd62235f7619b5e614a1b75e..045805a6b2ca95f31ba777f5da7a6bdb1a8d1feb 100644 (file)
@@ -64,7 +64,6 @@ test("Delete a private message", async () => {
     pmRes.private_message_view.private_message.id
   );
   expect(deletedPmRes.private_message_view.private_message.deleted).toBe(true);
-  expect(deletedPmRes.private_message_view.private_message.content).toBe("");
 
   // The GetPrivateMessages filters out deleted,
   // even though they are in the actual database.
index 3701e2f1ade4c82faea2fe546f2fac2dd243b941..1a1d7979f617b54e68495203218d6b79f7ec14ac 100644 (file)
@@ -2363,10 +2363,10 @@ kleur@^3.0.3:
   resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
   integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
 
-lemmy-js-client@0.17.0-rc.61:
-  version "0.17.0-rc.61"
-  resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.17.0-rc.61.tgz#c01e129a3d4c3483ecf337f1e4acf0ad91f9684f"
-  integrity sha512-xauBCD5i4vlUEWqsTMIXLCXeIjAK7ivVIN3C/g+RMAM7mD3CTcRkDZUerwnvLipIfr7V/4iYLWZW0orBaiV1CQ==
+lemmy-js-client@0.17.2-rc.1:
+  version "0.17.2-rc.1"
+  resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.17.2-rc.1.tgz#fe8d1508311bbf245acc98c2c3e47e2165a95b14"
+  integrity sha512-YrOXuCofgkqp28krmPTQZAfUWL5zEDA0sRJ0abKcgf/I8YYkYkUkPS9TOORN5Lv3bc8RAAz4+2/zLHqYL/Tnow==
   dependencies:
     node-fetch "2.6.6"
 
index 914b5238e5c1bdb769c32ba4c33583c529c78133..58dabf1c07a79593351c75b41cf6f235aa09ac75 100644 (file)
@@ -57,7 +57,8 @@ impl Perform for BlockCommunity {
         .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 {
       blocked: data.block,
index b0729bdc6848132082a2b7ba91f7ba83d08c4f64..b519d214b29e2b21d516a73d071bad926dc8d7b7 100644 (file)
@@ -53,7 +53,8 @@ impl Perform for FollowCommunity {
 
     let community_id = data.community_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?;
 
     Ok(Self::Response {
index e46ed351863617c070b8a500434966107d875a91..c163b1950a2e8926d0d3552c484b4c5d6039068d 100644 (file)
@@ -83,7 +83,7 @@ impl Perform for TransferCommunity {
 
     let community_id = data.community_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
       .map_err(|e| LemmyError::from_error_message(e, "couldnt_find_community"))?;
 
index 9fdf01b1281a4a44c15100a144594ffa3781e8ed..858d71d2211efcb5c0d0ee9ef0864649d9287434 100644 (file)
@@ -33,7 +33,7 @@ impl Perform for MarkPostAsRead {
     }
 
     // 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 };
 
index b36f844b63793af05dd4361794e52940ac1684d3..e0c7c8168381a2c240388d0c1b966f73050318c0 100644 (file)
@@ -43,7 +43,7 @@ impl Perform for SavePost {
 
     let post_id = data.post_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_post_as_read(person_id, post_id, context.pool()).await?;
index a297f22ae94e0803436f6f889b9c9033d60deb61..9ada7b1d639d6733329a10668fb6f7d79d844fb8 100644 (file)
@@ -37,7 +37,7 @@ impl Perform for CreatePostReport {
 
     let person_id = local_user_view.person.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?;
 
index 722aacc4f82ce352d2a467d4fa8abcfaf2ad3a6f..0058128c1b9ad7b10dcd13dbbe5eb98f6df46a85 100644 (file)
@@ -61,6 +61,23 @@ pub async fn is_mod_or_admin(
   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> {
   let admins = PersonViewSafe::admins(pool).await?;
   let top_admin = admins
index 44d380d34b82bdc425d0050526ab478004584955..0e8a78a209695e7822a886345dc8da0f798e91e4 100644 (file)
@@ -17,7 +17,7 @@ use lemmy_db_schema::{
     person_mention::{PersonMention, PersonMentionInsertForm},
     post::Post,
   },
-  traits::{Crud, DeleteableOrRemoveable},
+  traits::Crud,
   SubscribedType,
 };
 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>,
   context: &LemmyContext,
 ) -> 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 };
 
@@ -65,11 +65,7 @@ pub async fn send_comment_ws_message<OP: ToString + Send + OperationType + 'stat
   recipient_ids: Vec<LocalUserId>,
   context: &LemmyContext,
 ) -> Result<CommentResponse, LemmyError> {
-  let mut 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 view = CommentView::read(context.pool(), comment_id, person_id).await?;
 
   let mut res = CommentResponse {
     comment_view: view,
@@ -98,7 +94,8 @@ pub async fn send_community_ws_message<OP: ToString + Send + OperationType + 'st
   person_id: Option<PersonId>,
   context: &LemmyContext,
 ) -> 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 mut res = CommunityResponse {
@@ -124,12 +121,7 @@ pub async fn send_pm_ws_message<OP: ToString + Send + OperationType + 'static>(
   websocket_id: Option<ConnectionId>,
   context: &LemmyContext,
 ) -> Result<PrivateMessageResponse, LemmyError> {
-  let mut 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 view = PrivateMessageView::read(context.pool(), private_message_id).await?;
 
   let res = PrivateMessageResponse {
     private_message_view: view,
index 1bf7ba800c08c099817c95db53369d8f54e26e6b..7eb81067d3f7589fccef9b62dc56a2b281e342cf 100644 (file)
@@ -147,7 +147,7 @@ impl PerformCrud for CreateCommunity {
 
     let person_id = local_user_view.person.id;
     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 =
       CommunityLanguage::read(context.pool(), inserted_community.id).await?;
 
index 4d2e7046fe42a02f0b29524272f2cef773e41d3f..ef4c46d81f75163668c3b2ba7db1a23b0fd11cbd 100644 (file)
@@ -3,9 +3,9 @@ use actix_web::web::Data;
 use lemmy_api_common::{
   community::{ListCommunities, ListCommunitiesResponse},
   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_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())
         .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)?;
 
-    let person_id = local_user_view.clone().map(|l| l.person.id);
-
     let sort = data.sort;
     let listing_type = data.type_;
     let page = data.page;
     let limit = data.limit;
     let local_user = local_user_view.map(|l| l.local_user);
-    let mut communities = CommunityQuery::builder()
+    let communities = CommunityQuery::builder()
       .pool(context.pool())
       .listing_type(listing_type)
       .sort(sort)
       .local_user(local_user.as_ref())
       .page(page)
       .limit(limit)
+      .is_mod_or_admin(is_admin)
       .build()
       .list()
       .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
     Ok(ListCommunitiesResponse { communities })
   }
index 49bb3765dfb491506a559943c5f7e4d71c2b77c6..a6612cbb44cd4e15c9523c7e4c075d5222b9b082 100644 (file)
@@ -3,12 +3,17 @@ use actix_web::web::Data;
 use lemmy_api_common::{
   context::LemmyContext,
   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::{
   aggregates::structs::{PersonPostAggregates, PersonPostAggregatesForm},
-  source::{comment::Comment, local_site::LocalSite},
-  traits::{Crud, DeleteableOrRemoveable},
+  source::{comment::Comment, local_site::LocalSite, post::Post},
+  traits::Crud,
 };
 use lemmy_db_views::structs::PostView;
 use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView};
@@ -32,7 +37,7 @@ impl PerformCrud for GetPost {
 
     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
     let post_id = if let Some(id) = data.id {
@@ -46,7 +51,14 @@ impl PerformCrud for GetPost {
       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
       .map_err(|e| LemmyError::from_error_message(e, "couldnt_find_post"))?;
 
@@ -57,10 +69,14 @@ impl PerformCrud for GetPost {
     }
 
     // Necessary for the sidebar subscribed
-    let community_id = post_view.community.id;
-    let mut community_view = CommunityView::read(context.pool(), community_id, person_id)
-      .await
-      .map_err(|e| LemmyError::from_error_message(e, "couldnt_find_community"))?;
+    let community_view = CommunityView::read(
+      context.pool(),
+      community_id,
+      person_id,
+      Some(is_mod_or_admin),
+    )
+    .await
+    .map_err(|e| LemmyError::from_error_message(e, "couldnt_find_community"))?;
 
     // Insert into PersonPostAggregates
     // to update the read_comments count
@@ -77,17 +93,6 @@ impl PerformCrud for GetPost {
         .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 online = context.chat_server().get_post_users_online(post_id)?;
index 28680274524788fa15a1ee6636293fb529c105be..c9a3775337810fd3134cdb35ae6c6090bc0126d5 100644 (file)
@@ -5,7 +5,6 @@ use lemmy_api_common::{
   private_message::{GetPrivateMessages, PrivateMessagesResponse},
   utils::get_local_user_view_from_jwt,
 };
-use lemmy_db_schema::traits::DeleteableOrRemoveable;
 use lemmy_db_views::private_message_view::PrivateMessageQuery;
 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 {
       private_messages: messages,
     })
index 08853c50df77e8c1bd6f897eb0593ee95971d59f..bd3e5cad2558f3972b7d4f7bc38aa02ff3b7a606 100644 (file)
@@ -99,7 +99,8 @@ impl ActivityHandler for AcceptFollow {
 
     // Send the Subscribed message over websocket
     // 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
     let local_recipient_id = LocalUserView::read_person(context.pool(), person_id)
index c4af6900aa3c7d1102326706c96bb24077488429..3d44baf0e9552e4b50e7ae3b0ec3dcc6d4457e36 100644 (file)
@@ -15,7 +15,7 @@ use lemmy_api_common::{
 };
 use lemmy_db_schema::{
   source::{comment::Comment, community::Community, local_site::LocalSite},
-  traits::{Crud, DeleteableOrRemoveable},
+  traits::Crud,
 };
 use lemmy_db_views::comment_view::CommentQuery;
 use lemmy_utils::{error::LemmyError, ConnectionId};
@@ -65,7 +65,7 @@ impl PerformApub for GetComments {
     let parent_path_cloned = parent_path.clone();
     let post_id = data.post_id;
     let local_user = local_user_view.map(|l| l.local_user);
-    let mut comments = CommentQuery::builder()
+    let comments = CommentQuery::builder()
       .pool(context.pool())
       .listing_type(Some(listing_type))
       .sort(sort)
@@ -83,14 +83,6 @@ impl PerformApub for GetComments {
       .await
       .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 })
   }
 }
index 7a1f815c78f48fba77de0c1cb50409f240c9de2d..2908c4ba76853e9d42221b9ce18f28d6fd67bba3 100644 (file)
@@ -10,13 +10,11 @@ use lemmy_api_common::{
   utils::{
     check_private_instance,
     get_local_user_view_from_jwt_opt,
+    is_mod_or_admin_opt,
     listing_type_with_site_default,
   },
 };
-use lemmy_db_schema::{
-  source::{community::Community, local_site::LocalSite},
-  traits::DeleteableOrRemoveable,
-};
+use lemmy_db_schema::source::{community::Community, local_site::LocalSite};
 use lemmy_db_views::post_view::PostQuery;
 use lemmy_utils::{error::LemmyError, ConnectionId};
 
@@ -38,8 +36,6 @@ impl PerformApub for GetPosts {
 
     check_private_instance(&local_user_view, &local_site)?;
 
-    let is_logged_in = local_user_view.is_some();
-
     let sort = data.sort;
     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 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())
       .local_user(local_user_view.map(|l| l.local_user).as_ref())
       .listing_type(Some(listing_type))
@@ -66,28 +67,12 @@ impl PerformApub for GetPosts {
       .saved_only(saved_only)
       .page(page)
       .limit(limit)
+      .is_mod_or_admin(Some(is_mod_or_admin))
       .build()
       .list()
       .await
       .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 })
   }
 }
index 8ffa48ba79c1adcdf53c7d094feb80c5232f6d0c..738c8a3f1370e25f476e8984d6e5e3857f8f3a72 100644 (file)
@@ -7,7 +7,7 @@ use actix_web::web::Data;
 use lemmy_api_common::{
   community::{GetCommunity, GetCommunityResponse},
   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::{
   impls::actor_language::default_post_language,
@@ -17,7 +17,6 @@ use lemmy_db_schema::{
     local_site::LocalSite,
     site::Site,
   },
-  traits::DeleteableOrRemoveable,
 };
 use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView};
 use lemmy_utils::{error::LemmyError, ConnectionId};
@@ -57,15 +56,19 @@ impl PerformApub for GetCommunity {
       }
     };
 
-    let mut community_view = CommunityView::read(context.pool(), community_id, person_id)
-      .await
-      .map_err(|e| LemmyError::from_error_message(e, "couldnt_find_community"))?;
+    let is_mod_or_admin =
+      is_mod_or_admin_opt(context.pool(), local_user_view.as_ref(), Some(community_id))
+        .await
+        .is_ok();
 
-    // 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 community_view = CommunityView::read(
+      context.pool(),
+      community_id,
+      person_id,
+      Some(is_mod_or_admin),
+    )
+    .await
+    .map_err(|e| LemmyError::from_error_message(e, "couldnt_find_community"))?;
 
     let moderators = CommunityModeratorView::for_community(context.pool(), community_id)
       .await
index 514bcfccc1c3ada70279917a81f149fa696e15eb..c73d710fe2d77b4a286728b5a7f6eae936ff7cfb 100644 (file)
@@ -3,7 +3,7 @@ use actix_web::web::Data;
 use lemmy_api_common::{
   context::LemmyContext,
   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::{
   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())
         .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)?;
 
@@ -71,6 +72,7 @@ impl PerformApub for GetPersonDetails {
       .saved_only(saved_only)
       .local_user(local_user.as_ref())
       .community_id(community_id)
+      .is_mod_or_admin(is_admin)
       .page(page)
       .limit(limit);
 
index c4fb2e9b8c0a236e006233747ee4d93e575a777c..537e1adb315b226bcb53230c064ff4d2649ac7f0 100644 (file)
@@ -56,11 +56,11 @@ async fn convert_response(
     }
     Community(c) => {
       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) => {
       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) => {
       removed_or_deleted = c.deleted || c.removed;
index cad41c5497ab323b682a7380c5ddfda424ebe5cd..dee8c8027bb36862d7f5c40b3abab8986f0f83fd 100644 (file)
@@ -7,11 +7,10 @@ use actix_web::web::Data;
 use lemmy_api_common::{
   context::LemmyContext,
   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::{
   source::{community::Community, local_site::LocalSite},
-  traits::DeleteableOrRemoveable,
   utils::post_to_comment_sort_type,
   SearchType,
 };
@@ -38,7 +37,8 @@ impl PerformApub for Search {
 
     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 mut posts = Vec::new();
@@ -75,6 +75,7 @@ impl PerformApub for Search {
           .creator_id(creator_id)
           .local_user(local_user.as_ref())
           .search_term(Some(q))
+          .is_mod_or_admin(is_admin)
           .page(page)
           .limit(limit)
           .build()
@@ -104,6 +105,7 @@ impl PerformApub for Search {
           .listing_type(listing_type)
           .search_term(Some(q))
           .local_user(local_user.as_ref())
+          .is_mod_or_admin(is_admin)
           .page(page)
           .limit(limit)
           .build()
@@ -137,6 +139,7 @@ impl PerformApub for Search {
           .creator_id(creator_id)
           .local_user(local_user_.as_ref())
           .search_term(Some(q))
+          .is_mod_or_admin(is_admin)
           .page(page)
           .limit(limit)
           .build()
@@ -173,6 +176,7 @@ impl PerformApub for Search {
             .listing_type(listing_type)
             .search_term(Some(q))
             .local_user(local_user.as_ref())
+            .is_mod_or_admin(is_admin)
             .page(page)
             .limit(limit)
             .build()
@@ -205,6 +209,7 @@ impl PerformApub for Search {
           .community_actor_id(community_actor_id)
           .creator_id(creator_id)
           .url_search(Some(q))
+          .is_mod_or_admin(is_admin)
           .page(page)
           .limit(limit)
           .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
     Ok(SearchResponse {
       type_: search_type.to_string(),
index 2a9eeafedbdbaa15e13c8d56ac275a595889fd88..46045cd10afb68ac834c9e734b6c8ca99abd7d99 100644 (file)
@@ -10,7 +10,7 @@ use crate::{
     CommentSavedForm,
     CommentUpdateForm,
   },
-  traits::{Crud, DeleteableOrRemoveable, Likeable, Saveable},
+  traits::{Crud, Likeable, Saveable},
   utils::{get_conn, naive_now, DbPool},
 };
 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)]
 mod tests {
   use crate::{
index f341b5ed9eb21dfdd7151836fa543212c88f9a79..d4dd2ebc71e85a708e2b9f72921d4e1cff38a703 100644 (file)
@@ -12,11 +12,10 @@ use crate::{
       CommunityModeratorForm,
       CommunityPersonBan,
       CommunityPersonBanForm,
-      CommunitySafe,
       CommunityUpdateForm,
     },
   },
-  traits::{ApubActor, Bannable, Crud, DeleteableOrRemoveable, Followable, Joinable},
+  traits::{ApubActor, Bannable, Crud, Followable, Joinable},
   utils::{functions::lower, get_conn, DbPool},
   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 {
   Moderators,
   Featured,
index 8c5ffa8ad795a2a332ab97fe7ca28004bd652590..a3309428cfd406d347d44be3d93a870c13146df3 100644 (file)
@@ -26,7 +26,7 @@ use crate::{
     PostSavedForm,
     PostUpdateForm,
   },
-  traits::{Crud, DeleteableOrRemoveable, Likeable, Readable, Saveable},
+  traits::{Crud, Likeable, Readable, Saveable},
   utils::{get_conn, naive_now, DbPool, FETCH_LIMIT_MAX},
 };
 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)]
 mod tests {
   use crate::{
index a30ac20e534f521365cf23f652672bc132646931..cf67822706f17c736ac53746c7b1ecdf774d096c 100644 (file)
@@ -2,7 +2,7 @@ use crate::{
   newtypes::{DbUrl, PersonId, PrivateMessageId},
   schema::private_message::dsl::{ap_id, private_message, read, recipient_id},
   source::private_message::{PrivateMessage, PrivateMessageInsertForm, PrivateMessageUpdateForm},
-  traits::{Crud, DeleteableOrRemoveable},
+  traits::Crud,
   utils::{get_conn, DbPool},
 };
 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)]
 mod tests {
   use crate::{
index 2055ab644b1fd1eab4c62cd9dfebae5a3c9a8d44..adbbf8026b1124f21f0a2997bfb7a39567328f01 100644 (file)
@@ -140,11 +140,6 @@ pub trait Reportable {
     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 {
   type SafeColumns;
   fn safe_columns_tuple() -> Self::SafeColumns;
index 1427d948360cf5640dd153769339bb9b36335b80..ed2711c445e326f2a6e344b49ac3be06a8909134 100644 (file)
@@ -68,24 +68,14 @@ impl PostView {
     pool: &DbPool,
     post_id: PostId,
     my_person_id: Option<PersonId>,
+    is_mod_or_admin: Option<bool>,
   ) -> Result<Self, Error> {
     let conn = &mut get_conn(pool).await?;
 
     // The left join below will return None in this case
     let person_id_join = my_person_id.unwrap_or(PersonId(-1));
-    let (
-      post,
-      creator,
-      community,
-      creator_banned_from_community,
-      counts,
-      follower,
-      saved,
-      read,
-      creator_blocked,
-      post_like,
-      unread_comments,
-    ) = post::table
+    let person_alias_1 = diesel::alias!(person as person1);
+    let mut query = post::table
       .find(post_id)
       .inner_join(person::table)
       .inner_join(community::table)
@@ -144,6 +134,14 @@ impl PostView {
             .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((
         post::all_columns,
         Person::safe_columns_tuple(),
@@ -160,8 +158,38 @@ impl PostView {
           post_aggregates::comments,
         ),
       ))
-      .first::<PostViewTuple>(conn)
-      .await?;
+      .into_boxed();
+
+    // 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
     // Necessary to differentiate between other person's votes
@@ -201,6 +229,8 @@ pub struct PostQuery<'a> {
   search_term: Option<String>,
   url_search: Option<String>,
   saved_only: Option<bool>,
+  /// Used to show deleted or removed posts for admins
+  is_mod_or_admin: Option<bool>,
   page: Option<i64>,
   limit: Option<i64>,
 }
@@ -212,6 +242,7 @@ impl<'a> PostQuery<'a> {
     // 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 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
       .inner_join(person::table)
@@ -285,6 +316,14 @@ impl<'a> PostQuery<'a> {
             .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((
         post::all_columns,
         Person::safe_columns_tuple(),
@@ -303,6 +342,23 @@ impl<'a> PostQuery<'a> {
       ))
       .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 {
       match listing_type {
         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 {
       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)?;
 
-    query = query
-      .limit(limit)
-      .offset(offset)
-      .filter(post::removed.eq(false))
-      .filter(post::deleted.eq(false))
-      .filter(community::removed.eq(false))
-      .filter(community::deleted.eq(false));
+    query = query.limit(limit).offset(offset);
 
     debug!("Post View Query: {:?}", debug_query::<Pg, _>(&query));
 
@@ -615,10 +664,14 @@ mod tests {
       .await
       .unwrap();
 
-    let post_listing_single_with_person =
-      PostView::read(pool, data.inserted_post.id, Some(data.inserted_person.id))
-        .await
-        .unwrap();
+    let post_listing_single_with_person = PostView::read(
+      pool,
+      data.inserted_post.id,
+      Some(data.inserted_person.id),
+      None,
+    )
+    .await
+    .unwrap();
 
     let mut expected_post_listing_with_user = expected_post_view(&data, pool).await;
 
@@ -670,9 +723,10 @@ mod tests {
       .await
       .unwrap();
 
-    let read_post_listing_single_no_person = PostView::read(pool, data.inserted_post.id, None)
-      .await
-      .unwrap();
+    let read_post_listing_single_no_person =
+      PostView::read(pool, data.inserted_post.id, None, None)
+        .await
+        .unwrap();
 
     let expected_post_listing_no_person = expected_post_view(&data, pool).await;
 
index e59025a5f84242e0d16f01cadaad1ab49e12e71b..ff30d5dc57e77bc5bc5e0a2ba7979288684dd63a 100644 (file)
@@ -37,12 +37,13 @@ impl CommunityView {
     pool: &DbPool,
     community_id: CommunityId,
     my_person_id: Option<PersonId>,
+    is_mod_or_admin: Option<bool>,
   ) -> Result<Self, Error> {
     let conn = &mut get_conn(pool).await?;
     // The left join below will return None in this case
     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)
       .inner_join(community_aggregates::table)
       .left_join(
@@ -65,8 +66,16 @@ impl CommunityView {
         community_follower::all_columns.nullable(),
         community_block::all_columns.nullable(),
       ))
-      .first::<CommunityViewTuple>(conn)
-      .await?;
+      .into_boxed();
+
+    // 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 {
       community,
@@ -116,6 +125,7 @@ pub struct CommunityQuery<'a> {
   sort: Option<SortType>,
   local_user: Option<&'a LocalUser>,
   search_term: Option<String>,
+  is_mod_or_admin: Option<bool>,
   page: Option<i64>,
   limit: Option<i64>,
 }
@@ -159,6 +169,13 @@ impl<'a> CommunityQuery<'a> {
         .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) {
       SortType::New => query = query.order_by(community::published.desc()),
       SortType::TopAll => query = query.order_by(community_aggregates::subscribers.desc()),