Add ability to block all users of an instance. (#5784)

* Fixing a few optionals.

* Fixing note.

* Starting to work on instance user blocking.

* Finishing up user instance blocking

* SQL fmt

* fmt.

* Fixing search_combined

* Fixing API tests.

* Fixing api-common imports.

* Fixing merge issues.

* Update crates/api/api_utils/src/utils.rs

Co-authored-by: Nutomic <me@nutomic.com>

* Making clearer alias names

* Submodules

* Submodules 2

* Format

* Remove api misc.

* Upgrading lemmy js client, fixing merge

* Prettier

* Fixing merge.

* Addressing PR comment.

* Fix some stack overflows.

* Fixing api tests.

* Fixing notification joins.

* Adding alias.

* Fixing api test files.

* Remove some tmp files.

* Change back to localhost.

* Remove instance_person and instance_communities actions from views.

These are only used for internal filtering, blocks and bans anyway.

* Removing a few unused joins.

* Fix API tests.

* Some lints.

* Updating js-client

* Try fixing test.

* Update crates/db_views/site/src/api.rs

Co-authored-by: dullbananas <dull.bananas0@gmail.com>

* Fixing blocked_instances save_user_settings.

---------

Co-authored-by: Nutomic <me@nutomic.com>
Co-authored-by: dullbananas <dull.bananas0@gmail.com>
This commit is contained in:
Dessalines 2025-07-29 03:52:37 -04:00 committed by GitHub
parent df76935fa7
commit f1ba2a2dbf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
43 changed files with 1811 additions and 1681 deletions

View file

@ -22,20 +22,20 @@
"api-test-tags": "jest -i tags.spec.ts"
},
"devDependencies": {
"@eslint/js": "^9.26.0",
"@types/jest": "^29.5.12",
"@types/node": "^22.15.14",
"@typescript-eslint/eslint-plugin": "^8.32.0",
"@typescript-eslint/parser": "^8.32.0",
"eslint": "^9.26.0",
"eslint-plugin-prettier": "^5.4.0",
"jest": "^29.5.0",
"@eslint/js": "^9.29.0",
"@types/jest": "^30.0.0",
"@types/node": "^24.0.3",
"@typescript-eslint/eslint-plugin": "^8.34.1",
"@typescript-eslint/parser": "^8.34.1",
"eslint": "^9.29.0",
"eslint-plugin-prettier": "^5.5.0",
"jest": "^30.0.0",
"joi": "^17.13.3",
"lemmy-js-client": "1.0.0-post-tags.3",
"lemmy-js-client": "1.0.0-instance-user-blocking.8",
"prettier": "^3.5.3",
"ts-jest": "^29.3.2",
"ts-jest": "^29.4.0",
"tsoa": "^6.6.0",
"typescript": "^5.8.3",
"typescript-eslint": "^8.32.0"
"typescript-eslint": "^8.34.1"
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,2 @@
onlyBuiltDependencies:
- unrs-resolver

View file

@ -30,7 +30,7 @@ import {
editCommunity,
unfollows,
getMyUser,
userBlockInstance,
userBlockInstanceCommunities,
resolveBetaCommunity,
reportCommunity,
randomString,
@ -41,7 +41,6 @@ import { AdminAllowInstanceParams } from "lemmy-js-client/dist/types/AdminAllowI
import {
CommunityReport,
CommunityReportView,
CommunityView,
EditCommunity,
FollowMultiCommunity,
GetPosts,
@ -431,7 +430,11 @@ test("User blocks instance, communities are hidden", async () => {
expect(listing_ids).toContain(postRes.post_view.post.ap_id);
// block the beta instance
await userBlockInstance(alpha, alphaPost!.community.instance_id, true);
await userBlockInstanceCommunities(
alpha,
alphaPost!.community.instance_id,
true,
);
// after blocking, post should not be in listing
let listing2 = await getPosts(alpha, "All");
@ -439,7 +442,11 @@ test("User blocks instance, communities are hidden", async () => {
expect(listing_ids2.indexOf(postRes.post_view.post.ap_id)).toBe(-1);
// unblock instance again
await userBlockInstance(alpha, alphaPost!.community.instance_id, false);
await userBlockInstanceCommunities(
alpha,
alphaPost!.community.instance_id,
false,
);
// post should be included in listing
let listing3 = await getPosts(alpha, "All");

View file

@ -40,7 +40,6 @@ import {
getMyUser,
listNotifications,
getModlog,
getCommunity,
} from "./shared";
import { PostView } from "lemmy-js-client/dist/types/PostView";
import { AdminBlockInstanceParams } from "lemmy-js-client/dist/types/AdminBlockInstanceParams";
@ -371,7 +370,11 @@ test("Delete a post", async () => {
// Make sure lemmy beta sees post is deleted
// This will be undefined because of the tombstone
await waitForPost(beta, postRes.post_view.post, p => !p || p.post.deleted);
await waitForPost(
beta,
postRes.post_view.post,
p => p?.post?.deleted || p == undefined,
);
// Undelete
let undeletedPost = await deletePost(alpha, false, postRes.post_view.post);
@ -531,7 +534,7 @@ test("Enforce site ban federation for local user", async () => {
// alpha ban should be federated to beta
let alphaUserOnBeta1 = await waitUntil(
() => resolvePerson(beta, alphaUserActorId!),
res => res?.creator_banned!,
res => res?.creator_banned == true,
);
expect(alphaUserOnBeta1?.creator_banned).toBe(true);
@ -624,15 +627,6 @@ test("Enforce site ban federation for federated user", async () => {
let alphaPerson2 = (await getMyUser(alphaUserHttp)).local_user_view;
expect(alphaPerson2.banned).toBe(false);
// but the ban should be indicated by beta community on alpha
let communityWithBan = await getCommunity(
alphaUserHttp,
betaCommunity.community.id,
);
expect(
communityWithBan.community_view.instance_actions?.received_ban_at,
).toBeDefined();
// post to beta community is rejected
await expect(
createPost(alphaUserHttp, betaCommunity.community.id),
@ -830,7 +824,7 @@ test("Report a post", async () => {
() =>
listReports(beta).then(p =>
p.reports.find(r => {
return checkPostReportName(r, gammaReport) && r.resolver != null;
return checkPostReportName(r, gammaReport) && !!r.resolver;
}),
),
res => !!res,

View file

@ -20,18 +20,18 @@ import {
PostView,
PrivateMessageReportResponse,
SuccessResponse,
UserBlockInstanceParams,
ListPersonContentResponse,
ListPersonContent,
PersonContentType,
InboxDataType,
GetModlogResponse,
GetModlog,
CommunityView,
CommentView,
PersonView,
UserBlockInstanceCommunitiesParams,
ListNotifications,
ListNotificationsResponse,
NotificationDataType,
} from "lemmy-js-client";
import { CreatePost } from "lemmy-js-client/dist/types/CreatePost";
import { DeletePost } from "lemmy-js-client/dist/types/DeletePost";
@ -386,7 +386,7 @@ export async function getUnreadCount(
export async function listNotifications(
api: LemmyHttp,
type_?: InboxDataType,
type_?: NotificationDataType,
unread_only: boolean = false,
): Promise<ListNotificationsResponse> {
let form: ListNotifications = {
@ -880,16 +880,16 @@ export function getPosts(
return api.getPosts(form);
}
export function userBlockInstance(
export function userBlockInstanceCommunities(
api: LemmyHttp,
instance_id: InstanceId,
block: boolean,
): Promise<SuccessResponse> {
let form: UserBlockInstanceParams = {
let form: UserBlockInstanceCommunitiesParams = {
instance_id,
block,
};
return api.userBlockInstance(form);
return api.userBlockInstanceCommunities(form);
}
export function blockCommunity(

View file

@ -18,7 +18,7 @@ import {
} from "./shared";
import { CreateCommunityTag } from "lemmy-js-client/dist/types/CreateCommunityTag";
import { DeleteCommunityTag } from "lemmy-js-client/dist/types/DeleteCommunityTag";
import { AddModToCommunity, EditPost } from "lemmy-js-client";
import { AddModToCommunity } from "lemmy-js-client";
beforeAll(setupLogins);
afterAll(unfollows);

View file

@ -4,7 +4,7 @@
"declarationDir": "./dist",
"module": "CommonJS",
"noImplicitAny": true,
"lib": ["es2017", "es7", "es6", "dom"],
"lib": ["es2022", "es7", "es6", "dom"],
"outDir": "./dist",
"target": "ES2020",
"strictNullChecks": true,

View file

@ -1,16 +1,21 @@
use activitypub_federation::config::Data;
use actix_web::web::Json;
use lemmy_api_utils::context::LemmyContext;
use lemmy_db_schema::{
source::instance::{InstanceActions, InstanceBlockForm},
traits::Blockable,
use lemmy_db_schema::source::instance::{
InstanceActions,
InstanceCommunitiesBlockForm,
InstancePersonsBlockForm,
};
use lemmy_db_views_local_user::LocalUserView;
use lemmy_db_views_site::api::{SuccessResponse, UserBlockInstanceParams};
use lemmy_db_views_site::api::{
SuccessResponse,
UserBlockInstanceCommunitiesParams,
UserBlockInstancePersonsParams,
};
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
pub async fn user_block_instance(
data: Json<UserBlockInstanceParams>,
pub async fn user_block_instance_communities(
data: Json<UserBlockInstanceCommunitiesParams>,
local_user_view: LocalUserView,
context: Data<LemmyContext>,
) -> LemmyResult<Json<SuccessResponse>> {
@ -20,12 +25,34 @@ pub async fn user_block_instance(
return Err(LemmyErrorType::CantBlockLocalInstance)?;
}
let instance_block_form = InstanceBlockForm::new(person_id, instance_id);
let block_form = InstanceCommunitiesBlockForm::new(person_id, instance_id);
if data.block {
InstanceActions::block(&mut context.pool(), &instance_block_form).await?;
InstanceActions::block_communities(&mut context.pool(), &block_form).await?;
} else {
InstanceActions::unblock(&mut context.pool(), &instance_block_form).await?;
InstanceActions::unblock_communities(&mut context.pool(), &block_form).await?;
}
Ok(Json(SuccessResponse::default()))
}
pub async fn user_block_instance_persons(
data: Json<UserBlockInstancePersonsParams>,
local_user_view: LocalUserView,
context: Data<LemmyContext>,
) -> LemmyResult<Json<SuccessResponse>> {
let instance_id = data.instance_id;
let person_id = local_user_view.person.id;
if local_user_view.person.instance_id == instance_id {
return Err(LemmyErrorType::CantBlockLocalInstance)?;
}
let block_form = InstancePersonsBlockForm::new(person_id, instance_id);
if data.block {
InstanceActions::block_persons(&mut context.pool(), &block_form).await?;
} else {
InstanceActions::unblock_persons(&mut context.pool(), &block_form).await?;
}
Ok(Json(SuccessResponse::default()))

View file

@ -14,7 +14,8 @@ pub use lemmy_db_views_site::api::{
GetFederatedInstancesResponse,
InstanceWithFederationState,
ResolveObject,
UserBlockInstanceParams,
UserBlockInstanceCommunitiesParams,
UserBlockInstancePersonsParams,
};
pub mod administration {

View file

@ -30,7 +30,8 @@ pub async fn get_my_user(
let (
follows,
community_blocks,
instance_blocks,
instance_communities_blocks,
instance_persons_blocks,
person_blocks,
moderates,
keyword_blocks,
@ -38,7 +39,8 @@ pub async fn get_my_user(
) = lemmy_db_schema::try_join_with_pool!(pool => (
|pool| CommunityFollowerView::for_person(pool, person_id),
|pool| CommunityActions::read_blocks_for_person(pool, person_id),
|pool| InstanceActions::read_blocks_for_person(pool, person_id),
|pool| InstanceActions::read_communities_block_for_person(pool, person_id),
|pool| InstanceActions::read_persons_block_for_person(pool, person_id),
|pool| PersonActions::read_blocks_for_person(pool, person_id),
|pool| CommunityModeratorView::for_person(pool, person_id, Some(&local_user_view.local_user)),
|pool| LocalUserKeywordBlock::read(pool, local_user_id),
@ -50,7 +52,8 @@ pub async fn get_my_user(
follows,
moderates,
community_blocks,
instance_blocks,
instance_communities_blocks,
instance_persons_blocks,
person_blocks,
keyword_blocks,
discussion_languages,

View file

@ -102,7 +102,10 @@ impl NotifyData {
let pool = &mut context.pool();
// TODO: this needs too many queries for each user
PersonActions::read_block(pool, potential_blocker_id, self.post.creator_id).await?;
InstanceActions::read_block(pool, potential_blocker_id, self.community.instance_id).await?;
InstanceActions::read_communities_block(pool, potential_blocker_id, self.community.instance_id)
.await?;
InstanceActions::read_persons_block(pool, potential_blocker_id, self.creator.instance_id)
.await?;
CommunityActions::read_block(pool, potential_blocker_id, self.post.community_id).await?;
let post_notifications = PostActions::read(pool, self.post.id, potential_blocker_id)
.await
@ -284,7 +287,7 @@ mod tests {
source::{
comment::{Comment, CommentInsertForm},
community::{Community, CommunityInsertForm},
instance::{Instance, InstanceActions, InstanceBlockForm},
instance::{Instance, InstanceActions, InstancePersonsBlockForm},
notification::{Notification, NotificationInsertForm},
person::{Person, PersonActions, PersonBlockForm, PersonInsertForm, PersonUpdateForm},
post::{Post, PostInsertForm},
@ -716,16 +719,19 @@ mod tests {
// Make sure instance_blocks are working
let timmy_blocks_instance_form =
InstanceBlockForm::new(data.timmy.person.id, data.sara.person.instance_id);
InstancePersonsBlockForm::new(data.timmy.person.id, data.sara.person.instance_id);
let inserted_instance_block = InstanceActions::block(pool, &timmy_blocks_instance_form).await?;
let inserted_instance_block =
InstanceActions::block_persons(pool, &timmy_blocks_instance_form).await?;
assert_eq!(data.timmy.person.id, inserted_instance_block.person_id);
assert_eq!(
data.sara.person.instance_id,
inserted_instance_block.instance_id
(data.timmy.person.id, data.sara.person.instance_id, true),
(
inserted_instance_block.person_id,
inserted_instance_block.instance_id,
inserted_instance_block.blocked_persons_at.is_some()
)
);
assert!(inserted_instance_block.blocked_at.is_some());
let timmy_messages: Vec<_> = NotificationQuery {
unread_only: Some(true),

View file

@ -13,7 +13,7 @@ use lemmy_db_schema::{
source::{
comment::{CommentActions, CommentSavedForm},
community::{CommunityActions, CommunityBlockForm, CommunityFollowerForm},
instance::{Instance, InstanceActions, InstanceBlockForm},
instance::{Instance, InstanceActions, InstanceCommunitiesBlockForm, InstancePersonsBlockForm},
local_user::{LocalUser, LocalUserUpdateForm},
person::{Person, PersonActions, PersonBlockForm, PersonUpdateForm},
post::{PostActions, PostSavedForm},
@ -98,7 +98,8 @@ pub async fn import_settings(
let url_count = data.followed_communities.len()
+ data.blocked_communities.len()
+ data.blocked_users.len()
+ data.blocked_instances.len()
+ data.blocked_instances_communities.len()
+ data.blocked_instances_persons.len()
+ data.saved_posts.len()
+ data.saved_comments.len();
check_api_elements_count(url_count)?;
@ -197,10 +198,23 @@ pub async fn import_settings(
)
.await?;
try_join_all(data.blocked_instances.iter().map(|domain| async {
try_join_all(
data
.blocked_instances_communities
.iter()
.map(|domain| async {
let instance = Instance::read_or_create(&mut context.pool(), domain.clone()).await?;
let form = InstanceBlockForm::new(person_id, instance.id);
InstanceActions::block(&mut context.pool(), &form).await?;
let form = InstanceCommunitiesBlockForm::new(person_id, instance.id);
InstanceActions::block_communities(&mut context.pool(), &form).await?;
LemmyResult::Ok(())
}),
)
.await?;
try_join_all(data.blocked_instances_persons.iter().map(|domain| async {
let instance = Instance::read_or_create(&mut context.pool(), domain.clone()).await?;
let form = InstancePersonsBlockForm::new(person_id, instance.id);
InstanceActions::block_persons(&mut context.pool(), &form).await?;
LemmyResult::Ok(())
}))
.await?;

View file

@ -3,9 +3,16 @@ use crate::{
newtypes::{InstanceId, PersonId},
source::{
federation_queue_state::FederationQueueState,
instance::{Instance, InstanceActions, InstanceBanForm, InstanceBlockForm, InstanceForm},
instance::{
Instance,
InstanceActions,
InstanceBanForm,
InstanceCommunitiesBlockForm,
InstanceForm,
InstancePersonsBlockForm,
},
traits::{Bannable, Blockable},
},
traits::Bannable,
utils::{
functions::{coalesce, lower},
get_conn,
@ -206,12 +213,11 @@ impl Instance {
}
}
impl Blockable for InstanceActions {
type Form = InstanceBlockForm;
type ObjectIdType = InstanceId;
type ObjectType = Instance;
async fn block(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<Self> {
impl InstanceActions {
pub async fn block_communities(
pool: &mut DbPool<'_>,
form: &InstanceCommunitiesBlockForm,
) -> LemmyResult<Self> {
let conn = &mut get_conn(pool).await?;
insert_into(instance_actions::table)
.values(form)
@ -221,27 +227,31 @@ impl Blockable for InstanceActions {
.returning(Self::as_select())
.get_result::<Self>(conn)
.await
.with_lemmy_type(LemmyErrorType::InstanceBlockAlreadyExists)
.with_lemmy_type(LemmyErrorType::InstanceBlockCommunitiesAlreadyExists)
}
async fn unblock(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<UpleteCount> {
pub async fn unblock_communities(
pool: &mut DbPool<'_>,
form: &InstanceCommunitiesBlockForm,
) -> LemmyResult<UpleteCount> {
let conn = &mut get_conn(pool).await?;
uplete(instance_actions::table.find((form.person_id, form.instance_id)))
.set_null(instance_actions::blocked_at)
.set_null(instance_actions::blocked_communities_at)
.get_result(conn)
.await
.with_lemmy_type(LemmyErrorType::InstanceBlockAlreadyExists)
.with_lemmy_type(LemmyErrorType::InstanceBlockCommunitiesAlreadyExists)
}
async fn read_block(
/// Checks to see if there's a block for the instances communities
pub async fn read_communities_block(
pool: &mut DbPool<'_>,
person_id: PersonId,
instance_id: Self::ObjectIdType,
instance_id: InstanceId,
) -> LemmyResult<()> {
let conn = &mut get_conn(pool).await?;
let find_action = instance_actions::table
.find((person_id, instance_id))
.filter(instance_actions::blocked_at.is_not_null());
.filter(instance_actions::blocked_communities_at.is_not_null());
select(not(exists(find_action)))
.get_result::<bool>(conn)
.await?
@ -249,24 +259,83 @@ impl Blockable for InstanceActions {
.ok_or(LemmyErrorType::InstanceIsBlocked.into())
}
async fn read_blocks_for_person(
pub async fn read_communities_block_for_person(
pool: &mut DbPool<'_>,
person_id: PersonId,
) -> LemmyResult<Vec<Self::ObjectType>> {
) -> LemmyResult<Vec<Instance>> {
let conn = &mut get_conn(pool).await?;
instance_actions::table
.filter(instance_actions::blocked_at.is_not_null())
.filter(instance_actions::blocked_communities_at.is_not_null())
.inner_join(instance::table)
.select(instance::all_columns)
.filter(instance_actions::person_id.eq(person_id))
.order_by(instance_actions::blocked_at)
.order_by(instance_actions::blocked_communities_at)
.load::<Instance>(conn)
.await
.with_lemmy_type(LemmyErrorType::NotFound)
}
pub async fn block_persons(
pool: &mut DbPool<'_>,
form: &InstancePersonsBlockForm,
) -> LemmyResult<Self> {
let conn = &mut get_conn(pool).await?;
insert_into(instance_actions::table)
.values(form)
.on_conflict((instance_actions::person_id, instance_actions::instance_id))
.do_update()
.set(form)
.returning(Self::as_select())
.get_result::<Self>(conn)
.await
.with_lemmy_type(LemmyErrorType::InstanceBlockPersonsAlreadyExists)
}
pub async fn unblock_persons(
pool: &mut DbPool<'_>,
form: &InstancePersonsBlockForm,
) -> LemmyResult<UpleteCount> {
let conn = &mut get_conn(pool).await?;
uplete(instance_actions::table.find((form.person_id, form.instance_id)))
.set_null(instance_actions::blocked_persons_at)
.get_result(conn)
.await
.with_lemmy_type(LemmyErrorType::InstanceBlockPersonsAlreadyExists)
}
/// Checks to see if there's a block either from the instance person.
pub async fn read_persons_block(
pool: &mut DbPool<'_>,
person_id: PersonId,
instance_id: InstanceId,
) -> LemmyResult<()> {
let conn = &mut get_conn(pool).await?;
let find_action = instance_actions::table
.find((person_id, instance_id))
.filter(instance_actions::blocked_persons_at.is_not_null());
select(not(exists(find_action)))
.get_result::<bool>(conn)
.await?
.then_some(())
.ok_or(LemmyErrorType::InstanceIsBlocked.into())
}
pub async fn read_persons_block_for_person(
pool: &mut DbPool<'_>,
person_id: PersonId,
) -> LemmyResult<Vec<Instance>> {
let conn = &mut get_conn(pool).await?;
instance_actions::table
.filter(instance_actions::blocked_persons_at.is_not_null())
.inner_join(instance::table)
.select(instance::all_columns)
.filter(instance_actions::person_id.eq(person_id))
.order_by(instance_actions::blocked_persons_at)
.load::<Instance>(conn)
.await
.with_lemmy_type(LemmyErrorType::NotFound)
}
}
impl InstanceActions {
pub async fn check_ban(
pool: &mut DbPool<'_>,
person_id: PersonId,

View file

@ -211,8 +211,16 @@ impl LocalUser {
.get_results(conn)
.await?;
let blocked_instances = instance_actions::table
.filter(instance_actions::blocked_at.is_not_null())
let blocked_instances_communities = instance_actions::table
.filter(instance_actions::blocked_communities_at.is_not_null())
.filter(instance_actions::person_id.eq(person_id_))
.inner_join(instance::table)
.select(instance::domain)
.get_results(conn)
.await?;
let blocked_instances_persons = instance_actions::table
.filter(instance_actions::blocked_persons_at.is_not_null())
.filter(instance_actions::person_id.eq(person_id_))
.inner_join(instance::table)
.select(instance::domain)
@ -227,7 +235,8 @@ impl LocalUser {
saved_comments,
blocked_communities,
blocked_users,
blocked_instances,
blocked_instances_communities,
blocked_instances_persons,
})
}
@ -382,7 +391,8 @@ pub struct UserBackupLists {
pub saved_comments: Vec<DbUrl>,
pub blocked_communities: Vec<DbUrl>,
pub blocked_users: Vec<DbUrl>,
pub blocked_instances: Vec<String>,
pub blocked_instances_communities: Vec<String>,
pub blocked_instances_persons: Vec<String>,
}
#[cfg(test)]

View file

@ -542,7 +542,7 @@ impl PostActions {
pub fn build_many_read_forms(post_ids: &[PostId], person_id: PersonId) -> Vec<PostReadForm> {
post_ids
.iter()
.map(|post_id| (PostReadForm::new(*post_id, person_id)))
.map(|post_id| PostReadForm::new(*post_id, person_id))
.collect::<Vec<_>>()
}

View file

@ -19,6 +19,7 @@ pub mod aliases {
instance_actions as creator_home_instance_actions: CreatorHomeInstanceActions,
instance_actions as creator_community_instance_actions: CreatorCommunityInstanceActions,
instance_actions as creator_local_instance_actions: CreatorLocalInstanceActions,
instance_actions as my_instance_persons_actions: MyInstancePersonsActions,
local_user as creator_local_user: CreatorLocalUser,
person as person1: Person1,
person as person2: Person2,
@ -33,7 +34,10 @@ pub mod utils;
use serde::{Deserialize, Serialize};
use strum::{Display, EnumString};
#[cfg(feature = "full")]
use {diesel::query_source::AliasedField, lemmy_db_schema_file::schema::person};
use {
diesel::query_source::AliasedField,
lemmy_db_schema_file::schema::{instance_actions, person},
};
#[derive(
EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default, Hash,
@ -233,3 +237,14 @@ pub type Person2AliasAllColumnsTuple = (
AliasedField<aliases::Person2, person::comment_count>,
AliasedField<aliases::Person2, person::comment_score>,
);
#[cfg(feature = "full")]
/// A helper tuple for more my instance persons actions
pub type MyInstancePersonsActionsAllColumnsTuple = (
AliasedField<aliases::MyInstancePersonsActions, instance_actions::person_id>,
AliasedField<aliases::MyInstancePersonsActions, instance_actions::instance_id>,
AliasedField<aliases::MyInstancePersonsActions, instance_actions::blocked_communities_at>,
AliasedField<aliases::MyInstancePersonsActions, instance_actions::received_ban_at>,
AliasedField<aliases::MyInstancePersonsActions, instance_actions::ban_expires_at>,
AliasedField<aliases::MyInstancePersonsActions, instance_actions::blocked_persons_at>,
);

View file

@ -59,22 +59,34 @@ pub struct InstanceActions {
pub person_id: PersonId,
#[serde(skip)]
pub instance_id: InstanceId,
/// When the instance was blocked.
pub blocked_at: Option<DateTime<Utc>>,
/// When the instance's communities were blocked.
pub blocked_communities_at: Option<DateTime<Utc>>,
/// When this user received a site ban.
pub received_ban_at: Option<DateTime<Utc>>,
/// When their ban expires.
pub ban_expires_at: Option<DateTime<Utc>>,
/// When the instance's persons were blocked.
pub blocked_persons_at: Option<DateTime<Utc>>,
}
#[derive(derive_new::new)]
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = instance_actions))]
pub struct InstanceBlockForm {
pub struct InstanceCommunitiesBlockForm {
pub person_id: PersonId,
pub instance_id: InstanceId,
#[new(value = "Utc::now()")]
pub blocked_at: DateTime<Utc>,
pub blocked_communities_at: DateTime<Utc>,
}
#[derive(derive_new::new)]
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = instance_actions))]
pub struct InstancePersonsBlockForm {
pub person_id: PersonId,
pub instance_id: InstanceId,
#[new(value = "Utc::now()")]
pub blocked_persons_at: DateTime<Utc>,
}
#[derive(derive_new::new)]

View file

@ -5,17 +5,19 @@ use crate::{
creator_home_instance_actions,
creator_local_instance_actions,
creator_local_user,
my_instance_persons_actions,
person1,
person2,
},
newtypes::{InstanceId, PersonId},
MyInstancePersonsActionsAllColumnsTuple,
Person1AliasAllColumnsTuple,
Person2AliasAllColumnsTuple,
};
use diesel::{
dsl::{case_when, exists, not},
expression::SqlLiteral,
helper_types::{Eq, NotEq},
helper_types::{Eq, NotEq, Nullable},
sql_types::Json,
BoolExpressionMethods,
ExpressionMethods,
@ -50,11 +52,16 @@ use lemmy_db_schema_file::{
/// hidden, unless the user followed the community explicitly.
#[diesel::dsl::auto_type]
pub fn filter_blocked() -> _ {
instance_actions::blocked_at
instance_actions::blocked_communities_at
.is_null()
.or(community_actions::followed_at.is_not_null())
.and(community_actions::blocked_at.is_null())
.and(person_actions::blocked_at.is_null())
.and(
my_instance_persons_actions
.field(instance_actions::blocked_persons_at)
.is_null(),
)
}
/// Checks that the creator_local_user is an admin.
@ -336,7 +343,7 @@ pub fn creator_local_instance_actions_join(local_instance_id: InstanceId) -> _ {
/// Your instance actions for the community's instance.
#[diesel::dsl::auto_type]
pub fn my_instance_actions_community_join(my_person_id: Option<PersonId>) -> _ {
pub fn my_instance_communities_actions_join(my_person_id: Option<PersonId>) -> _ {
instance_actions::table.on(
instance_actions::instance_id
.eq(community::instance_id)
@ -346,7 +353,7 @@ pub fn my_instance_actions_community_join(my_person_id: Option<PersonId>) -> _ {
/// Your instance actions for the person's instance.
#[diesel::dsl::auto_type]
pub fn my_instance_actions_person_join(my_person_id: Option<PersonId>) -> _ {
pub fn my_instance_persons_actions_join(my_person_id: Option<PersonId>) -> _ {
instance_actions::table.on(
instance_actions::instance_id
.eq(person::instance_id)
@ -354,6 +361,29 @@ pub fn my_instance_actions_person_join(my_person_id: Option<PersonId>) -> _ {
)
}
/// The select for the my_instance_persons_actions alias
pub fn my_instance_persons_actions_select() -> Nullable<MyInstancePersonsActionsAllColumnsTuple> {
my_instance_persons_actions
.fields(instance_actions::all_columns)
.nullable()
}
/// Your instance actions for the person's instance.
/// A dupe of the above function, but aliased
#[diesel::dsl::auto_type]
pub fn my_instance_persons_actions_join_1(my_person_id: Option<PersonId>) -> _ {
my_instance_persons_actions.on(
my_instance_persons_actions
.field(instance_actions::instance_id)
.eq(person::instance_id)
.and(
my_instance_persons_actions
.field(instance_actions::person_id)
.nullable()
.eq(my_person_id),
),
)
}
#[diesel::dsl::auto_type]
pub fn image_details_join() -> _ {
image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable()))

View file

@ -411,9 +411,10 @@ diesel::table! {
instance_actions (person_id, instance_id) {
person_id -> Int4,
instance_id -> Int4,
blocked_at -> Nullable<Timestamptz>,
blocked_communities_at -> Nullable<Timestamptz>,
received_ban_at -> Nullable<Timestamptz>,
ban_expires_at -> Nullable<Timestamptz>,
blocked_persons_at -> Nullable<Timestamptz>,
}
}

View file

@ -33,7 +33,8 @@ use lemmy_db_schema::{
filter_blocked,
my_comment_actions_join,
my_community_actions_join,
my_instance_actions_community_join,
my_instance_communities_actions_join,
my_instance_persons_actions_join_1,
my_local_user_admin_join,
my_person_actions_join,
suggested_communities,
@ -78,8 +79,10 @@ impl CommentView {
my_community_actions_join(my_person_id);
let my_comment_actions_join: my_comment_actions_join = my_comment_actions_join(my_person_id);
let my_local_user_admin_join: my_local_user_admin_join = my_local_user_admin_join(my_person_id);
let my_instance_actions_community_join: my_instance_actions_community_join =
my_instance_actions_community_join(my_person_id);
let my_instance_communities_actions_join: my_instance_communities_actions_join =
my_instance_communities_actions_join(my_person_id);
let my_instance_persons_actions_join_1: my_instance_persons_actions_join_1 =
my_instance_persons_actions_join_1(my_person_id);
let my_person_actions_join: my_person_actions_join = my_person_actions_join(my_person_id);
let creator_local_instance_actions_join: creator_local_instance_actions_join =
creator_local_instance_actions_join(local_instance_id);
@ -88,15 +91,16 @@ impl CommentView {
.inner_join(person::table)
.inner_join(post::table)
.inner_join(community_join)
.left_join(creator_home_instance_actions_join())
.left_join(creator_community_instance_actions_join())
.left_join(creator_community_actions_join())
.left_join(creator_local_instance_actions_join)
.left_join(my_community_actions_join)
.left_join(my_comment_actions_join)
.left_join(my_person_actions_join)
.left_join(my_local_user_admin_join)
.left_join(my_instance_actions_community_join)
.left_join(creator_home_instance_actions_join())
.left_join(creator_community_instance_actions_join())
.left_join(creator_local_instance_actions_join)
.left_join(creator_community_actions_join())
.left_join(my_instance_communities_actions_join)
.left_join(my_instance_persons_actions_join_1)
}
pub async fn read(
@ -138,7 +142,6 @@ impl CommentView {
creator: self.creator,
comment_actions: self.comment_actions,
person_actions: self.person_actions,
instance_actions: self.instance_actions,
creator_is_admin: self.creator_is_admin,
can_mod: self.can_mod,
creator_banned: self.creator_banned,

View file

@ -1,7 +1,6 @@
use lemmy_db_schema::source::{
comment::{Comment, CommentActions},
community::{Community, CommunityActions},
instance::InstanceActions,
person::{Person, PersonActions},
post::Post,
tag::TagsView,
@ -14,13 +13,11 @@ use {
lemmy_db_schema::utils::queries::{
comment_creator_is_admin,
comment_select_remove_deletes,
local_user_can_mod_comment,
post_tags_fragment,
},
lemmy_db_schema::utils::queries::{
creator_banned_from_community,
creator_banned_within_community,
creator_is_moderator,
local_user_can_mod_comment,
post_tags_fragment,
},
};
@ -54,8 +51,6 @@ pub struct CommentView {
pub comment_actions: Option<CommentActions>,
#[cfg_attr(feature = "full", diesel(embed))]
pub person_actions: Option<PersonActions>,
#[cfg_attr(feature = "full", diesel(embed))]
pub instance_actions: Option<InstanceActions>,
#[cfg_attr(feature = "full",
diesel(
select_expression = comment_creator_is_admin()
@ -106,7 +101,6 @@ pub struct CommentSlimView {
pub creator: Person,
pub comment_actions: Option<CommentActions>,
pub person_actions: Option<PersonActions>,
pub instance_actions: Option<InstanceActions>,
pub creator_is_admin: bool,
pub can_mod: bool,
pub creator_banned: bool,

View file

@ -20,7 +20,7 @@ use lemmy_db_schema::{
filter_is_subscribed,
filter_not_unlisted_or_is_subscribed,
my_community_actions_join,
my_instance_actions_community_join,
my_instance_communities_actions_join,
my_local_user_admin_join,
suggested_communities,
},
@ -48,8 +48,8 @@ impl CommunityView {
#[diesel::dsl::auto_type(no_type_alias)]
fn joins(person_id: Option<PersonId>) -> _ {
let community_actions_join: my_community_actions_join = my_community_actions_join(person_id);
let instance_actions_community_join: my_instance_actions_community_join =
my_instance_actions_community_join(person_id);
let instance_actions_community_join: my_instance_communities_actions_join =
my_instance_communities_actions_join(person_id);
let my_local_user_admin_join: my_local_user_admin_join = my_local_user_admin_join(person_id);
community::table
@ -150,7 +150,7 @@ impl CommunityQuery<'_> {
// Don't show blocked communities and communities on blocked instances. nsfw communities are
// also hidden (based on profile setting)
query = query.filter(instance_actions::blocked_at.is_null());
query = query.filter(instance_actions::blocked_communities_at.is_null());
query = query.filter(community_actions::blocked_at.is_null());
if !(o.local_user.show_nsfw(site) || o.show_nsfw.unwrap_or_default()) {
query = query.filter(community::nsfw.eq(false));

View file

@ -1,6 +1,5 @@
use lemmy_db_schema::source::{
community::{Community, CommunityActions},
instance::InstanceActions,
multi_community::MultiCommunity,
person::Person,
tag::TagsView,
@ -29,8 +28,6 @@ pub struct CommunityView {
pub community: Community,
#[cfg_attr(feature = "full", diesel(embed))]
pub community_actions: Option<CommunityActions>,
#[cfg_attr(feature = "full", diesel(embed))]
pub instance_actions: Option<InstanceActions>,
#[cfg_attr(feature = "full",
diesel(
select_expression = local_user_community_can_mod()

View file

@ -28,10 +28,12 @@ use lemmy_db_schema::{
creator_home_instance_actions_join,
creator_local_instance_actions_join,
creator_local_user_admin_join,
filter_blocked,
image_details_join,
my_comment_actions_join,
my_community_actions_join,
my_instance_actions_person_join,
my_instance_communities_actions_join,
my_instance_persons_actions_join_1,
my_local_user_admin_join,
my_person_actions_join,
my_post_actions_join,
@ -42,15 +44,7 @@ use lemmy_db_schema::{
};
use lemmy_db_schema_file::{
enums::NotificationTypes,
schema::{
comment,
instance_actions,
notification,
person,
person_actions,
post,
private_message,
},
schema::{comment, notification, person, post, private_message},
};
use lemmy_db_views_post::PostView;
use lemmy_db_views_private_message::PrivateMessageView;
@ -107,8 +101,10 @@ impl NotificationView {
my_comment_actions_join(Some(my_person.id));
let my_local_user_admin_join: my_local_user_admin_join =
my_local_user_admin_join(Some(my_person.id));
let my_instance_actions_person_join: my_instance_actions_person_join =
my_instance_actions_person_join(Some(my_person.id));
let my_instance_communities_actions_join: my_instance_communities_actions_join =
my_instance_communities_actions_join(Some(my_person.id));
let my_instance_persons_actions_join_1: my_instance_persons_actions_join_1 =
my_instance_persons_actions_join_1(Some(my_person.id));
let my_person_actions_join: my_person_actions_join = my_person_actions_join(Some(my_person.id));
let creator_local_instance_actions_join: creator_local_instance_actions_join =
creator_local_instance_actions_join(my_person.instance_id);
@ -122,12 +118,13 @@ impl NotificationView {
.inner_join(recipient_join)
.left_join(image_details_join())
.left_join(creator_community_actions_join())
.left_join(my_local_user_admin_join)
.left_join(creator_local_user_admin_join())
.left_join(my_community_actions_join)
.left_join(my_instance_actions_person_join)
.left_join(creator_home_instance_actions_join())
.left_join(creator_local_instance_actions_join)
.left_join(my_local_user_admin_join)
.left_join(my_community_actions_join)
.left_join(my_instance_communities_actions_join)
.left_join(my_instance_persons_actions_join_1)
.left_join(my_post_actions_join)
.left_join(my_person_actions_join)
.left_join(my_comment_actions_join)
@ -150,8 +147,7 @@ impl NotificationView {
// Filter unreads
.filter(unread_filter)
// Don't count replies from blocked users
.filter(person_actions::blocked_at.is_null())
.filter(instance_actions::blocked_at.is_null())
.filter(filter_blocked())
.select(count(notification::id))
.into_boxed();
@ -242,9 +238,7 @@ impl NotificationQuery {
};
// Dont show replies from blocked users or instances
query = query
.filter(person_actions::blocked_at.is_null())
.filter(instance_actions::blocked_at.is_null());
query = query.filter(filter_blocked());
if let Some(type_) = self.type_ {
query = match type_ {
@ -294,7 +288,6 @@ fn map_to_enum(v: NotificationViewInternal) -> Option<NotificationView> {
community,
creator: v.creator,
community_actions: v.community_actions,
instance_actions: v.instance_actions,
person_actions: v.person_actions,
comment_actions: v.comment_actions,
creator_is_admin: v.creator_is_admin,
@ -311,7 +304,6 @@ fn map_to_enum(v: NotificationViewInternal) -> Option<NotificationView> {
creator: v.creator,
image_details: v.image_details,
community_actions: v.community_actions,
instance_actions: v.instance_actions,
post_actions: v.post_actions,
person_actions: v.person_actions,
creator_is_admin: v.creator_is_admin,

View file

@ -4,7 +4,6 @@ use lemmy_db_schema::{
comment::{Comment, CommentActions},
community::{Community, CommunityActions},
images::ImageDetails,
instance::InstanceActions,
notification::Notification,
person::{Person, PersonActions},
post::{Post, PostActions},
@ -22,9 +21,15 @@ use serde_with::skip_serializing_none;
use {
diesel::{Queryable, Selectable},
lemmy_db_schema::{
utils::queries::person1_select,
utils::queries::{creator_banned, creator_is_admin, local_user_can_mod, post_tags_fragment},
utils::queries::{creator_banned_from_community, creator_is_moderator},
utils::queries::{
creator_banned,
creator_banned_from_community,
creator_is_admin,
creator_is_moderator,
local_user_can_mod,
person1_select,
post_tags_fragment,
},
Person1AliasAllColumnsTuple,
},
};
@ -61,8 +66,6 @@ struct NotificationViewInternal {
#[cfg_attr(feature = "full", diesel(embed))]
community_actions: Option<CommunityActions>,
#[cfg_attr(feature = "full", diesel(embed))]
instance_actions: Option<InstanceActions>,
#[cfg_attr(feature = "full", diesel(embed))]
post_actions: Option<PostActions>,
#[cfg_attr(feature = "full", diesel(embed))]
person_actions: Option<PersonActions>,

View file

@ -33,7 +33,6 @@ use lemmy_db_schema::{
image_details_join,
my_comment_actions_join,
my_community_actions_join,
my_instance_actions_person_join,
my_local_user_admin_join,
my_person_actions_join,
my_post_actions_join,
@ -76,8 +75,6 @@ impl PersonContentCombinedViewInternal {
let my_post_actions_join: my_post_actions_join = my_post_actions_join(my_person_id);
let my_comment_actions_join: my_comment_actions_join = my_comment_actions_join(my_person_id);
let my_local_user_admin_join: my_local_user_admin_join = my_local_user_admin_join(my_person_id);
let my_instance_actions_person_join: my_instance_actions_person_join =
my_instance_actions_person_join(my_person_id);
let my_person_actions_join: my_person_actions_join = my_person_actions_join(my_person_id);
let creator_local_instance_actions_join: creator_local_instance_actions_join =
creator_local_instance_actions_join(local_instance_id);
@ -87,17 +84,16 @@ impl PersonContentCombinedViewInternal {
.inner_join(post_join)
.inner_join(item_creator_join)
.inner_join(community_join())
.left_join(image_details_join())
.left_join(creator_community_actions_join())
.left_join(my_local_user_admin_join)
.left_join(creator_local_user_admin_join())
.left_join(my_community_actions_join)
.left_join(my_instance_actions_person_join)
.left_join(creator_home_instance_actions_join())
.left_join(creator_local_instance_actions_join)
.left_join(my_local_user_admin_join)
.left_join(my_community_actions_join)
.left_join(my_post_actions_join)
.left_join(my_person_actions_join)
.left_join(my_comment_actions_join)
.left_join(image_details_join())
}
}
@ -240,7 +236,6 @@ impl InternalToCombinedView for PersonContentCombinedViewInternal {
community_actions: v.community_actions,
comment_actions: v.comment_actions,
person_actions: v.person_actions,
instance_actions: v.instance_actions,
creator_is_admin: v.item_creator_is_admin,
post_tags: v.post_tags,
can_mod: v.can_mod,
@ -257,7 +252,6 @@ impl InternalToCombinedView for PersonContentCombinedViewInternal {
community_actions: v.community_actions,
post_actions: v.post_actions,
person_actions: v.person_actions,
instance_actions: v.instance_actions,
creator_is_admin: v.item_creator_is_admin,
tags: v.post_tags,
can_mod: v.can_mod,

View file

@ -5,7 +5,6 @@ use lemmy_db_schema::{
comment::{Comment, CommentActions},
community::{Community, CommunityActions},
images::ImageDetails,
instance::InstanceActions,
person::{Person, PersonActions},
post::{Post, PostActions},
tag::TagsView,
@ -21,11 +20,13 @@ use {
diesel::{Queryable, Selectable},
lemmy_db_schema::utils::queries::{
creator_banned,
creator_banned_from_community,
creator_is_admin,
creator_is_moderator,
local_user_can_mod,
post_tags_fragment,
},
lemmy_db_schema::utils::queries::{creator_banned_from_community, creator_is_moderator},
lemmy_db_views_local_user::LocalUserView,
};
@ -50,8 +51,6 @@ pub(crate) struct PersonContentCombinedViewInternal {
#[cfg_attr(feature = "full", diesel(embed))]
pub community_actions: Option<CommunityActions>,
#[cfg_attr(feature = "full", diesel(embed))]
pub instance_actions: Option<InstanceActions>,
#[cfg_attr(feature = "full", diesel(embed))]
pub post_actions: Option<PostActions>,
#[cfg_attr(feature = "full", diesel(embed))]
pub person_actions: Option<PersonActions>,

View file

@ -33,7 +33,6 @@ use lemmy_db_schema::{
image_details_join,
my_comment_actions_join,
my_community_actions_join,
my_instance_actions_person_join,
my_local_user_admin_join,
my_person_actions_join,
my_post_actions_join,
@ -122,8 +121,6 @@ impl PersonLikedCombinedViewInternal {
my_comment_actions_join(Some(my_person_id));
let my_local_user_admin_join: my_local_user_admin_join =
my_local_user_admin_join(Some(my_person_id));
let my_instance_actions_person_join: my_instance_actions_person_join =
my_instance_actions_person_join(Some(my_person_id));
let my_person_actions_join: my_person_actions_join = my_person_actions_join(Some(my_person_id));
let creator_local_instance_actions_join: creator_local_instance_actions_join =
creator_local_instance_actions_join(local_instance_id);
@ -131,20 +128,20 @@ impl PersonLikedCombinedViewInternal {
person_liked_combined::table
.left_join(comment_join)
.inner_join(post_join)
.inner_join(item_creator_join)
.inner_join(community_join())
.inner_join(item_creator_join)
.left_join(image_details_join())
.left_join(creator_community_actions_join())
.left_join(my_local_user_admin_join)
.left_join(creator_local_user_admin_join())
.left_join(my_community_actions_join)
.left_join(my_instance_actions_person_join)
.left_join(creator_home_instance_actions_join())
.left_join(creator_community_instance_actions_join())
.left_join(creator_local_instance_actions_join)
// The my_'s have to come last to avoid stack overflows
.left_join(my_post_actions_join)
.left_join(my_person_actions_join)
.left_join(my_comment_actions_join)
.left_join(image_details_join())
.left_join(my_community_actions_join)
.left_join(my_local_user_admin_join)
}
}
@ -229,7 +226,6 @@ impl InternalToCombinedView for PersonLikedCombinedViewInternal {
community_actions: v.community_actions,
comment_actions: v.comment_actions,
person_actions: v.person_actions,
instance_actions: v.instance_actions,
creator_is_admin: v.item_creator_is_admin,
post_tags: v.post_tags,
can_mod: v.can_mod,
@ -246,7 +242,6 @@ impl InternalToCombinedView for PersonLikedCombinedViewInternal {
community_actions: v.community_actions,
post_actions: v.post_actions,
person_actions: v.person_actions,
instance_actions: v.instance_actions,
creator_is_admin: v.item_creator_is_admin,
tags: v.post_tags,
can_mod: v.can_mod,

View file

@ -5,7 +5,6 @@ use lemmy_db_schema::{
comment::{Comment, CommentActions},
community::{Community, CommunityActions},
images::ImageDetails,
instance::InstanceActions,
person::{Person, PersonActions},
post::{Post, PostActions},
tag::TagsView,
@ -23,9 +22,11 @@ use {
lemmy_db_schema::utils::queries::{
creator_banned_from_community,
creator_banned_within_community,
creator_is_admin,
creator_is_moderator,
local_user_can_mod,
post_tags_fragment,
},
lemmy_db_schema::utils::queries::{creator_is_admin, local_user_can_mod, post_tags_fragment},
lemmy_db_views_local_user::LocalUserView,
};
@ -50,8 +51,6 @@ pub(crate) struct PersonLikedCombinedViewInternal {
#[cfg_attr(feature = "full", diesel(embed))]
pub community_actions: Option<CommunityActions>,
#[cfg_attr(feature = "full", diesel(embed))]
pub instance_actions: Option<InstanceActions>,
#[cfg_attr(feature = "full", diesel(embed))]
pub post_actions: Option<PostActions>,
#[cfg_attr(feature = "full", diesel(embed))]
pub person_actions: Option<PersonActions>,

View file

@ -33,7 +33,6 @@ use lemmy_db_schema::{
image_details_join,
my_comment_actions_join,
my_community_actions_join,
my_instance_actions_person_join,
my_local_user_admin_join,
my_person_actions_join,
my_post_actions_join,
@ -120,8 +119,6 @@ impl PersonSavedCombinedViewInternal {
my_comment_actions_join(Some(my_person_id));
let my_local_user_admin_join: my_local_user_admin_join =
my_local_user_admin_join(Some(my_person_id));
let my_instance_actions_person_join: my_instance_actions_person_join =
my_instance_actions_person_join(Some(my_person_id));
let my_person_actions_join: my_person_actions_join = my_person_actions_join(Some(my_person_id));
let creator_local_instance_actions_join: creator_local_instance_actions_join =
creator_local_instance_actions_join(local_instance_id);
@ -131,18 +128,17 @@ impl PersonSavedCombinedViewInternal {
.inner_join(post_join)
.inner_join(item_creator_join)
.inner_join(community_join())
.left_join(image_details_join())
.left_join(creator_community_actions_join())
.left_join(my_local_user_admin_join)
.left_join(creator_local_user_admin_join())
.left_join(my_community_actions_join)
.left_join(my_instance_actions_person_join)
.left_join(creator_home_instance_actions_join())
.left_join(creator_community_instance_actions_join())
.left_join(creator_local_instance_actions_join)
.left_join(my_community_actions_join)
.left_join(my_local_user_admin_join)
.left_join(my_post_actions_join)
.left_join(my_person_actions_join)
.left_join(my_comment_actions_join)
.left_join(image_details_join())
}
}
@ -219,7 +215,6 @@ impl InternalToCombinedView for PersonSavedCombinedViewInternal {
community_actions: v.community_actions,
comment_actions: v.comment_actions,
person_actions: v.person_actions,
instance_actions: v.instance_actions,
creator_is_admin: v.item_creator_is_admin,
post_tags: v.post_tags,
can_mod: v.can_mod,
@ -236,7 +231,6 @@ impl InternalToCombinedView for PersonSavedCombinedViewInternal {
community_actions: v.community_actions,
post_actions: v.post_actions,
person_actions: v.person_actions,
instance_actions: v.instance_actions,
creator_is_admin: v.item_creator_is_admin,
tags: v.post_tags,
can_mod: v.can_mod,

View file

@ -5,7 +5,6 @@ use lemmy_db_schema::{
comment::{Comment, CommentActions},
community::{Community, CommunityActions},
images::ImageDetails,
instance::InstanceActions,
person::{Person, PersonActions},
post::{Post, PostActions},
tag::TagsView,
@ -22,9 +21,12 @@ use {
lemmy_db_schema::utils::queries::{
creator_banned_from_community,
creator_banned_within_community,
creator_is_admin,
creator_is_moderator,
local_user_can_mod,
post_tags_fragment,
},
lemmy_db_schema::utils::queries::{creator_is_admin, local_user_can_mod, post_tags_fragment},
lemmy_db_views_local_user::LocalUserView,
};
@ -49,8 +51,6 @@ pub(crate) struct PersonSavedCombinedViewInternal {
#[cfg_attr(feature = "full", diesel(embed))]
pub community_actions: Option<CommunityActions>,
#[cfg_attr(feature = "full", diesel(embed))]
pub instance_actions: Option<InstanceActions>,
#[cfg_attr(feature = "full", diesel(embed))]
pub post_actions: Option<PostActions>,
#[cfg_attr(feature = "full", diesel(embed))]
pub person_actions: Option<PersonActions>,

View file

@ -41,7 +41,8 @@ use lemmy_db_schema::{
filter_not_unlisted_or_is_subscribed,
image_details_join,
my_community_actions_join,
my_instance_actions_community_join,
my_instance_communities_actions_join,
my_instance_persons_actions_join_1,
my_local_user_admin_join,
my_person_actions_join,
my_post_actions_join,
@ -98,8 +99,10 @@ impl PostView {
my_community_actions_join(my_person_id);
let my_post_actions_join: my_post_actions_join = my_post_actions_join(my_person_id);
let my_local_user_admin_join: my_local_user_admin_join = my_local_user_admin_join(my_person_id);
let my_instance_actions_community_join: my_instance_actions_community_join =
my_instance_actions_community_join(my_person_id);
let my_instance_communities_actions_join: my_instance_communities_actions_join =
my_instance_communities_actions_join(my_person_id);
let my_instance_persons_actions_join_1: my_instance_persons_actions_join_1 =
my_instance_persons_actions_join_1(my_person_id);
let my_person_actions_join: my_person_actions_join = my_person_actions_join(my_person_id);
let creator_local_instance_actions_join: creator_local_instance_actions_join =
creator_local_instance_actions_join(local_instance_id);
@ -108,15 +111,16 @@ impl PostView {
.inner_join(person::table)
.inner_join(community::table)
.left_join(image_details_join())
.left_join(my_community_actions_join)
.left_join(my_person_actions_join)
.left_join(my_post_actions_join)
.left_join(my_instance_actions_community_join)
.left_join(my_local_user_admin_join)
.left_join(creator_home_instance_actions_join())
.left_join(creator_community_instance_actions_join())
.left_join(creator_local_instance_actions_join)
.left_join(creator_community_actions_join())
.left_join(my_community_actions_join)
.left_join(my_person_actions_join)
.left_join(my_post_actions_join)
.left_join(my_instance_communities_actions_join)
.left_join(my_instance_persons_actions_join_1)
.left_join(my_local_user_admin_join)
}
pub async fn read(
@ -583,7 +587,13 @@ mod tests {
CommunityPersonBanForm,
CommunityUpdateForm,
},
instance::{Instance, InstanceActions, InstanceBanForm, InstanceBlockForm},
instance::{
Instance,
InstanceActions,
InstanceBanForm,
InstanceCommunitiesBlockForm,
InstancePersonsBlockForm,
},
keyword_block::LocalUserKeywordBlock,
language::Language,
local_site::{LocalSite, LocalSiteUpdateForm},
@ -1541,10 +1551,12 @@ mod tests {
#[test_context(Data)]
#[tokio::test]
#[serial]
async fn post_listing_instance_block(data: &mut Data) -> LemmyResult<()> {
const POST_FROM_BLOCKED_INSTANCE: &str = "post on blocked instance";
const POST_LISTING_WITH_BLOCKED: [&str; 4] = [
POST_FROM_BLOCKED_INSTANCE,
async fn post_listing_instance_block_communities(data: &mut Data) -> LemmyResult<()> {
const POST_FROM_BLOCKED_INSTANCE_COMMS: &str = "post on blocked instance";
const HOWARD_POST: &str = "howard post";
const POST_LISTING_WITH_BLOCKED: [&str; 5] = [
HOWARD_POST,
POST_FROM_BLOCKED_INSTANCE_COMMS,
POST_WITH_TAGS,
POST_BY_BOT,
POST,
@ -1553,10 +1565,11 @@ mod tests {
let pool = &data.pool();
let pool = &mut pool.into();
let blocked_instance = Instance::read_or_create(pool, "another_domain.tld".to_string()).await?;
let blocked_instance_comms =
Instance::read_or_create(pool, "another_domain.tld".to_string()).await?;
let community_form = CommunityInsertForm::new(
blocked_instance.id,
blocked_instance_comms.id,
"test_community_4".to_string(),
"none".to_owned(),
"pubkey".to_string(),
@ -1566,25 +1579,37 @@ mod tests {
let post_form = PostInsertForm {
language_id: Some(LanguageId(1)),
..PostInsertForm::new(
POST_FROM_BLOCKED_INSTANCE.to_string(),
POST_FROM_BLOCKED_INSTANCE_COMMS.to_string(),
data.bot.person.id,
inserted_community.id,
)
};
let post_from_blocked_instance = Post::create(pool, &post_form).await?;
// Create a person on that comm-blocked instance,
// have them create a post from a non-instance-comm blocked community.
// Make sure others can see it.
let howard_form = PersonInsertForm::test_form(blocked_instance_comms.id, "howard");
let howard = Person::create(pool, &howard_form).await?;
let howard_post_form = PostInsertForm {
language_id: Some(LanguageId(1)),
..PostInsertForm::new(HOWARD_POST.to_string(), howard.id, data.community.id)
};
let _post_from_blocked_instance_user = Post::create(pool, &howard_post_form).await?;
// no instance block, should return all posts
let post_listings_all = data.default_post_query().list(&data.site, pool).await?;
assert_eq!(POST_LISTING_WITH_BLOCKED, *names(&post_listings_all));
// block the instance
let block_form = InstanceBlockForm::new(data.tegan.person.id, blocked_instance.id);
InstanceActions::block(pool, &block_form).await?;
// block the instance communities
let block_form =
InstanceCommunitiesBlockForm::new(data.tegan.person.id, blocked_instance_comms.id);
InstanceActions::block_communities(pool, &block_form).await?;
// now posts from communities on that instance should be hidden
let post_listings_blocked = data.default_post_query().list(&data.site, pool).await?;
assert_eq!(
vec![POST_WITH_TAGS, POST_BY_BOT, POST],
vec![HOWARD_POST, POST_WITH_TAGS, POST_BY_BOT, POST],
names(&post_listings_blocked)
);
assert!(post_listings_blocked
@ -1603,11 +1628,92 @@ mod tests {
CommunityActions::unfollow(pool, data.tegan.person.id, inserted_community.id).await?;
// after unblocking it should return all posts again
InstanceActions::unblock(pool, &block_form).await?;
InstanceActions::unblock_communities(pool, &block_form).await?;
let post_listings_blocked = data.default_post_query().list(&data.site, pool).await?;
assert_eq!(POST_LISTING_WITH_BLOCKED, *names(&post_listings_blocked));
Instance::delete(pool, blocked_instance.id).await?;
Instance::delete(pool, blocked_instance_comms.id).await?;
Ok(())
}
#[test_context(Data)]
#[tokio::test]
#[serial]
async fn post_listing_instance_block_persons(data: &mut Data) -> LemmyResult<()> {
const POST_FROM_BLOCKED_INSTANCE_USERS: &str = "post from blocked instance user";
const POST_TO_UNBLOCKED_COMM: &str = "post to unblocked comm";
const POST_LISTING_WITH_BLOCKED: [&str; 5] = [
POST_TO_UNBLOCKED_COMM,
POST_FROM_BLOCKED_INSTANCE_USERS,
POST_WITH_TAGS,
POST_BY_BOT,
POST,
];
let pool = &data.pool();
let pool = &mut pool.into();
let blocked_instance_persons =
Instance::read_or_create(pool, "another_domain.tld".to_string()).await?;
let howard_form = PersonInsertForm::test_form(blocked_instance_persons.id, "howard");
let howard = Person::create(pool, &howard_form).await?;
let community_form = CommunityInsertForm::new(
blocked_instance_persons.id,
"test_community_8".to_string(),
"none".to_owned(),
"pubkey".to_string(),
);
let inserted_community = Community::create(pool, &community_form).await?;
// Create a post from the blocked user on a safe community
let blocked_post_form = PostInsertForm {
language_id: Some(LanguageId(1)),
..PostInsertForm::new(
POST_FROM_BLOCKED_INSTANCE_USERS.to_string(),
howard.id,
data.community.id,
)
};
let post_from_blocked_instance = Post::create(pool, &blocked_post_form).await?;
// Also create a post from an unblocked user
let unblocked_post_form = PostInsertForm {
language_id: Some(LanguageId(1)),
..PostInsertForm::new(
POST_TO_UNBLOCKED_COMM.to_string(),
data.bot.person.id,
inserted_community.id,
)
};
let _post_to_unblocked_comm = Post::create(pool, &unblocked_post_form).await?;
// no instance block, should return all posts
let post_listings_all = data.default_post_query().list(&data.site, pool).await?;
assert_eq!(POST_LISTING_WITH_BLOCKED, *names(&post_listings_all));
// block the instance communities
let block_form =
InstancePersonsBlockForm::new(data.tegan.person.id, blocked_instance_persons.id);
InstanceActions::block_persons(pool, &block_form).await?;
// now posts from users on that instance should be hidden
let post_listings_blocked = data.default_post_query().list(&data.site, pool).await?;
assert_eq!(
vec![POST_TO_UNBLOCKED_COMM, POST_WITH_TAGS, POST_BY_BOT, POST],
names(&post_listings_blocked)
);
assert!(post_listings_blocked
.iter()
.all(|p| p.post.id != post_from_blocked_instance.id));
// after unblocking it should return all posts again
InstanceActions::unblock_persons(pool, &block_form).await?;
let post_listings_blocked = data.default_post_query().list(&data.site, pool).await?;
assert_eq!(POST_LISTING_WITH_BLOCKED, *names(&post_listings_blocked));
Instance::delete(pool, blocked_instance_persons.id).await?;
Ok(())
}
@ -2011,11 +2117,6 @@ mod tests {
assert!(post_view.creator_banned);
// This should be none, since john wasn't banned, only the creator.
assert!(post_view.instance_actions.is_none());
assert!(post_view.creator_banned);
Person::delete(pool, banned_person.id).await?;
Ok(())
}

View file

@ -1,7 +1,6 @@
use lemmy_db_schema::source::{
community::{Community, CommunityActions},
images::ImageDetails,
instance::InstanceActions,
person::{Person, PersonActions},
post::{Post, PostActions},
tag::TagsView,
@ -16,8 +15,6 @@ use {
lemmy_db_schema::utils::queries::{
creator_banned_from_community,
creator_banned_within_community,
},
lemmy_db_schema::utils::queries::{
creator_is_moderator,
local_user_can_mod_post,
post_creator_is_admin,
@ -50,8 +47,6 @@ pub struct PostView {
pub person_actions: Option<PersonActions>,
#[cfg_attr(feature = "full", diesel(embed))]
pub post_actions: Option<PostActions>,
#[cfg_attr(feature = "full", diesel(embed))]
pub instance_actions: Option<InstanceActions>,
#[cfg_attr(feature = "full",
diesel(
select_expression = post_creator_is_admin()

View file

@ -43,7 +43,6 @@ use lemmy_db_schema::{
image_details_join,
my_comment_actions_join,
my_community_actions_join,
my_instance_actions_person_join,
my_local_user_admin_join,
my_person_actions_join,
my_post_actions_join,
@ -133,8 +132,6 @@ impl SearchCombinedViewInternal {
let my_post_actions_join: my_post_actions_join = my_post_actions_join(my_person_id);
let my_comment_actions_join: my_comment_actions_join = my_comment_actions_join(my_person_id);
let my_local_user_admin_join: my_local_user_admin_join = my_local_user_admin_join(my_person_id);
let my_instance_actions_person_join: my_instance_actions_person_join =
my_instance_actions_person_join(my_person_id);
let my_person_actions_join: my_person_actions_join = my_person_actions_join(my_person_id);
let creator_local_instance_actions_join: creator_local_instance_actions_join =
creator_local_instance_actions_join(local_instance_id);
@ -145,17 +142,16 @@ impl SearchCombinedViewInternal {
.left_join(multi_community_join)
.left_join(item_creator_join)
.left_join(community_join)
.left_join(image_details_join())
.left_join(creator_community_actions_join())
.left_join(my_local_user_admin_join)
.left_join(creator_local_user_admin_join())
.left_join(my_community_actions_join)
.left_join(my_instance_actions_person_join)
.left_join(creator_home_instance_actions_join())
.left_join(creator_local_instance_actions_join)
.left_join(my_local_user_admin_join)
.left_join(my_community_actions_join)
.left_join(my_post_actions_join)
.left_join(my_person_actions_join)
.left_join(my_comment_actions_join)
.left_join(image_details_join())
}
}
@ -428,7 +424,6 @@ impl InternalToCombinedView for SearchCombinedViewInternal {
community,
creator,
community_actions: v.community_actions,
instance_actions: v.instance_actions,
person_actions: v.person_actions,
comment_actions: v.comment_actions,
creator_is_admin: v.item_creator_is_admin,
@ -448,7 +443,6 @@ impl InternalToCombinedView for SearchCombinedViewInternal {
creator_is_admin: v.item_creator_is_admin,
image_details: v.image_details,
community_actions: v.community_actions,
instance_actions: v.instance_actions,
person_actions: v.person_actions,
post_actions: v.post_actions,
tags: v.post_tags,
@ -461,7 +455,6 @@ impl InternalToCombinedView for SearchCombinedViewInternal {
Some(SearchCombinedView::Community(CommunityView {
community,
community_actions: v.community_actions,
instance_actions: v.instance_actions,
can_mod: v.can_mod,
post_tags: v.community_post_tags,
}))

View file

@ -5,7 +5,6 @@ use lemmy_db_schema::{
comment::{Comment, CommentActions},
community::{Community, CommunityActions},
images::ImageDetails,
instance::InstanceActions,
multi_community::MultiCommunity,
person::{Person, PersonActions},
post::{Post, PostActions},
@ -58,8 +57,6 @@ pub(crate) struct SearchCombinedViewInternal {
#[cfg_attr(feature = "full", diesel(embed))]
pub community_actions: Option<CommunityActions>,
#[cfg_attr(feature = "full", diesel(embed))]
pub instance_actions: Option<InstanceActions>,
#[cfg_attr(feature = "full", diesel(embed))]
pub post_actions: Option<PostActions>,
#[cfg_attr(feature = "full", diesel(embed))]
pub person_actions: Option<PersonActions>,

View file

@ -439,7 +439,8 @@ pub struct MyUserInfo {
pub follows: Vec<CommunityFollowerView>,
pub moderates: Vec<CommunityModeratorView>,
pub community_blocks: Vec<Community>,
pub instance_blocks: Vec<Instance>,
pub instance_communities_blocks: Vec<Instance>,
pub instance_persons_blocks: Vec<Instance>,
pub person_blocks: Vec<Person>,
pub keyword_blocks: Vec<String>,
pub discussion_languages: Vec<LanguageId>,
@ -561,8 +562,17 @@ pub struct UpdateTotpResponse {
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[cfg_attr(feature = "ts-rs", ts(optional_fields, export))]
/// Block an instance as user
pub struct UserBlockInstanceParams {
/// Block an instance's persons.
pub struct UserBlockInstancePersonsParams {
pub instance_id: InstanceId,
pub block: bool,
}
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[cfg_attr(feature = "ts-rs", ts(optional_fields, export))]
/// Block an instance's communities.
pub struct UserBlockInstanceCommunitiesParams {
pub instance_id: InstanceId,
pub block: bool,
}
@ -713,6 +723,7 @@ pub enum PostOrCommentOrPrivateMessage {
/// Be careful with any changes to this struct, to avoid breaking changes which could prevent
/// importing older backups.
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
#[serde(deny_unknown_fields)]
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[cfg_attr(feature = "ts-rs", ts(optional_fields, export))]
pub struct UserSettingsBackup {
@ -736,7 +747,10 @@ pub struct UserSettingsBackup {
#[serde(default)]
pub blocked_users: Vec<Url>,
#[serde(default)]
pub blocked_instances: Vec<String>,
#[serde(alias = "blocked_instances")] // the name used by v0.19
pub blocked_instances_communities: Vec<String>,
#[serde(default)]
pub blocked_instances_persons: Vec<String>,
}
#[skip_serializing_none]

View file

@ -62,7 +62,8 @@ pub fn user_backup_list_to_user_settings_backup(
settings: Some(local_user_view.local_user),
followed_communities: vec_into(lists.followed_communities),
blocked_communities: vec_into(lists.blocked_communities),
blocked_instances: lists.blocked_instances,
blocked_instances_communities: lists.blocked_instances_communities,
blocked_instances_persons: lists.blocked_instances_persons,
blocked_users: vec_into(lists.blocked_users),
saved_posts: vec_into(lists.saved_posts),
saved_comments: vec_into(lists.saved_comments),

View file

@ -151,7 +151,7 @@ impl<T: DataSource> CommunityInboxCollector<T> {
// happens much later - by doing it here, we can ensure that in the happy case, this
// function returns 0 urls which means the system doesn't have to create a tokio
// task for sending at all (since that task has a fair amount of overhead)
.filter(|&u| (u.domain() == Some(&self.domain)))
.filter(|&u| u.domain() == Some(&self.domain))
.map(|u| u.inner().clone()),
);
tracing::trace!(

View file

@ -129,7 +129,8 @@ pub enum LemmyErrorType {
InvalidUrlScheme,
CouldntSendWebmention,
ContradictingFilters,
InstanceBlockAlreadyExists,
InstanceBlockCommunitiesAlreadyExists,
InstanceBlockPersonsAlreadyExists,
/// Thrown when an API call is submitted with more than 1000 array elements, see
/// [[MAX_API_PARAM_ELEMENTS]]
TooManyItems,

View file

@ -0,0 +1,5 @@
ALTER TABLE instance_actions RENAME COLUMN blocked_communities_at TO blocked_at;
ALTER TABLE instance_actions
DROP COLUMN blocked_persons_at;

View file

@ -0,0 +1,9 @@
-- Currently, the instance.blocked_at columns only blocks communities from the given instance.
--
-- This creates a new block type, to also be able to block persons.
-- Also changes the name of blocked_at to blocked_communities_at
ALTER TABLE instance_actions RENAME COLUMN blocked_at TO blocked_communities_at;
ALTER TABLE instance_actions
ADD COLUMN blocked_persons_at timestamptz;

View file

@ -52,7 +52,7 @@ use lemmy_api::{
reset_password::reset_password,
save_settings::save_user_settings,
update_totp::update_totp,
user_block_instance::user_block_instance,
user_block_instance::{user_block_instance_communities, user_block_instance_persons},
validate_auth::validate_auth,
verify_email::verify_email,
},
@ -382,7 +382,11 @@ pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimit) {
scope("/block")
.route("/person", post().to(user_block_person))
.route("/community", post().to(user_block_community))
.route("/instance", post().to(user_block_instance)),
.route(
"/instance/communities",
post().to(user_block_instance_communities),
)
.route("/instance/persons", post().to(user_block_instance_persons)),
)
.route("/saved", get().to(list_person_saved))
.route("/read", get().to(list_person_read))