Skip to content

Commit cdf6318

Browse files
GustedGusted
authored andcommitted
[MODERATION] organization blocking a user (go-gitea#802)
- Resolves go-gitea#476 - Follow up for: go-gitea#540 - Ensure that the doer and blocked person cannot follow each other. - Ensure that the block person cannot watch doer's repositories. - Add unblock button to the blocked user list. - Add blocked since information to the blocked user list. - Add extra testing to moderation code. - Blocked user will unwatch doer's owned repository upon blocking. - Add flash messages to let the user know the block/unblock action was successful. - Add "You haven't blocked any users" message. - Add organization blocking a user. Co-authored-by: Gusted <postmaster@gusted.xyz> Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/802 (cherry picked from commit 0505a1042197bd9136b58bc70ec7400a23471585) (cherry picked from commit 37b4e6ef9b85e97d651cf350c9f3ea272ee8d76a) (cherry picked from commit 217475385a815298dcbd8029e0cc8cb2c5877bae) (cherry picked from commit f2c38ce5c2f6cf4008aa1929539063715b50562c) (cherry picked from commit 1edfb68137d8c322a7a9a7c7196fc8f01ff1a889) (cherry picked from commit 2cbc12dc740e6fefc196b7fea6ac8a0ffbbfbeef) (cherry picked from commit 79ff020f182327986dcfd874bc49d4fe32efc29a)
1 parent dc9499b commit cdf6318

File tree

26 files changed

+375
-18
lines changed

26 files changed

+375
-18
lines changed

models/fixtures/repository.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
lower_name: repo2
3838
name: repo2
3939
default_branch: master
40-
num_watches: 0
40+
num_watches: 1
4141
num_stars: 1
4242
num_forks: 0
4343
num_issues: 2

models/fixtures/watch.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,10 @@
2626
id: 5
2727
user_id: 11
2828
repo_id: 1
29-
mode: 3 # auto
29+
mode: 3 # auto
30+
31+
-
32+
id: 6
33+
user_id: 4
34+
repo_id: 2
35+
mode: 1 # normal

models/repo/user_repo.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,3 +177,16 @@ func GetIssuePostersWithSearch(ctx context.Context, repo *Repository, isPull boo
177177
Limit(30).
178178
Find(&users)
179179
}
180+
181+
// GetWatchedRepoIDsOwnedBy returns the repos owned by a particular user watched by a particular user
182+
func GetWatchedRepoIDsOwnedBy(ctx context.Context, userID, ownedByUserID int64) ([]int64, error) {
183+
repoIDs := make([]int64, 0, 10)
184+
err := db.GetEngine(ctx).
185+
Table("repository").
186+
Select("`repository`.id").
187+
Join("LEFT", "watch", "`repository`.id=`watch`.repo_id").
188+
Where("`watch`.user_id=?", userID).
189+
And("`watch`.mode<>?", WatchModeDont).
190+
And("`repository`.owner_id=?", ownedByUserID).Find(&repoIDs)
191+
return repoIDs, err
192+
}

models/repo/user_repo_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"code.gitea.io/gitea/models/db"
1010
repo_model "code.gitea.io/gitea/models/repo"
1111
"code.gitea.io/gitea/models/unittest"
12+
user_model "code.gitea.io/gitea/models/user"
1213

1314
"github.com/stretchr/testify/assert"
1415
)
@@ -71,3 +72,15 @@ func TestRepoGetReviewers(t *testing.T) {
7172
assert.NoError(t, err)
7273
assert.Len(t, reviewers, 1)
7374
}
75+
76+
func GetWatchedRepoIDsOwnedBy(t *testing.T) {
77+
assert.NoError(t, unittest.PrepareTestDatabase())
78+
79+
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 9})
80+
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
81+
82+
repoIDs, err := repo_model.GetWatchedRepoIDsOwnedBy(db.DefaultContext, user1.ID, user2.ID)
83+
assert.NoError(t, err)
84+
assert.Len(t, repoIDs, 1)
85+
assert.EqualValues(t, 1, repoIDs[0])
86+
}

models/repo/watch.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,3 +201,9 @@ func WatchIfAuto(ctx context.Context, userID, repoID int64, isWrite bool) error
201201
}
202202
return watchRepoMode(ctx, watch, WatchModeAuto)
203203
}
204+
205+
// UnwatchRepos will unwatch the user from all given repositories.
206+
func UnwatchRepos(ctx context.Context, userID int64, repoIDs []int64) error {
207+
_, err := db.GetEngine(ctx).Where("user_id=?", userID).In("repo_id", repoIDs).Delete(&Watch{})
208+
return err
209+
}

models/repo/watch_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,3 +155,16 @@ func TestWatchRepoMode(t *testing.T) {
155155
assert.NoError(t, repo_model.WatchRepoMode(12, 1, repo_model.WatchModeNone))
156156
unittest.AssertCount(t, &repo_model.Watch{UserID: 12, RepoID: 1}, 0)
157157
}
158+
159+
func TestUnwatchRepos(t *testing.T) {
160+
assert.NoError(t, unittest.PrepareTestDatabase())
161+
162+
unittest.AssertExistsAndLoadBean(t, &repo_model.Watch{UserID: 4, RepoID: 1})
163+
unittest.AssertExistsAndLoadBean(t, &repo_model.Watch{UserID: 4, RepoID: 2})
164+
165+
err := repo_model.UnwatchRepos(db.DefaultContext, 4, []int64{1, 2})
166+
assert.NoError(t, err)
167+
168+
unittest.AssertNotExistsBean(t, &repo_model.Watch{UserID: 4, RepoID: 1})
169+
unittest.AssertNotExistsBean(t, &repo_model.Watch{UserID: 4, RepoID: 2})
170+
}

models/user/block.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,12 @@ func UnblockUser(ctx context.Context, userID, blockID int64) error {
5353
}
5454

5555
// ListBlockedUsers returns the users that the user has blocked.
56+
// The created_unix field of the user struct is overridden by the creation_unix
57+
// field of blockeduser.
5658
func ListBlockedUsers(ctx context.Context, userID int64) ([]*User, error) {
5759
users := make([]*User, 0, 8)
5860
err := db.GetEngine(ctx).
59-
Select("`user`.*").
61+
Select("`forgejo_blocked_user`.created_unix, `user`.*").
6062
Join("INNER", "forgejo_blocked_user", "`user`.id=`forgejo_blocked_user`.block_id").
6163
Where("`forgejo_blocked_user`.user_id=?", userID).
6264
Find(&users)

models/user/follow.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,25 @@ func init() {
2424

2525
// IsFollowing returns true if user is following followID.
2626
func IsFollowing(userID, followID int64) bool {
27-
has, _ := db.GetEngine(db.DefaultContext).Get(&Follow{UserID: userID, FollowID: followID})
27+
return IsFollowingCtx(db.DefaultContext, userID, followID)
28+
}
29+
30+
// IsFollowingCtx returns true if user is following followID.
31+
func IsFollowingCtx(ctx context.Context, userID, followID int64) bool {
32+
has, _ := db.GetEngine(ctx).Get(&Follow{UserID: userID, FollowID: followID})
2833
return has
2934
}
3035

3136
// FollowUser marks someone be another's follower.
3237
func FollowUser(ctx context.Context, userID, followID int64) (err error) {
33-
if userID == followID || IsFollowing(userID, followID) {
38+
if userID == followID || IsFollowingCtx(ctx, userID, followID) {
3439
return nil
3540
}
3641

42+
if IsBlocked(ctx, userID, followID) || IsBlocked(ctx, followID, userID) {
43+
return ErrBlockedByUser
44+
}
45+
3746
ctx, committer, err := db.TxContext(ctx)
3847
if err != nil {
3948
return err
@@ -56,7 +65,7 @@ func FollowUser(ctx context.Context, userID, followID int64) (err error) {
5665

5766
// UnfollowUser unmarks someone as another's follower.
5867
func UnfollowUser(ctx context.Context, userID, followID int64) (err error) {
59-
if userID == followID || !IsFollowing(userID, followID) {
68+
if userID == followID || !IsFollowingCtx(ctx, userID, followID) {
6069
return nil
6170
}
6271

models/user/user_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,12 @@ func TestFollowUser(t *testing.T) {
457457

458458
assert.NoError(t, user_model.FollowUser(db.DefaultContext, 2, 2))
459459

460+
// Blocked user.
461+
assert.ErrorIs(t, user_model.ErrBlockedByUser, user_model.FollowUser(db.DefaultContext, 1, 4))
462+
assert.ErrorIs(t, user_model.ErrBlockedByUser, user_model.FollowUser(db.DefaultContext, 4, 1))
463+
unittest.AssertNotExistsBean(t, &user_model.Follow{UserID: 1, FollowID: 4})
464+
unittest.AssertNotExistsBean(t, &user_model.Follow{UserID: 4, FollowID: 1})
465+
460466
unittest.CheckConsistencyFor(t, &user_model.User{})
461467
}
462468

options/locale/locale_en-US.ini

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,7 @@ block_user = Block User
601601
block_user.detail = Please understand that if you block this user, other actions will be taken. Such as:
602602
block_user.detail_1 = You are being unfollowed from this user.
603603
block_user.detail_2 = This user cannot interact with your repositories, created issues and comments.
604+
follow_blocked_user = You cannot follow this user because you have blocked this user or this user has blocked you.
604605

605606
form.name_reserved = The username "%s" is reserved.
606607
form.name_pattern_not_allowed = The pattern "%s" is not allowed in a username.
@@ -892,6 +893,7 @@ hooks.desc = Add webhooks which will be triggered for <strong>all repositories</
892893

893894
orgs_none = You are not a member of any organizations.
894895
repos_none = You do not own any repositories
896+
blocked_users_none = You haven't blocked any users.
895897
896898
delete_account = Delete Your Account
897899
delete_prompt = This operation will permanently delete your user account. It <strong>CANNOT</strong> be undone.
@@ -914,6 +916,10 @@ visibility.limited_tooltip = Visible to authenticated users only
914916
visibility.private = Private
915917
visibility.private_tooltip = Visible only to organization members
916918
919+
blocked_since = Blocked since %s
920+
user_unblock_success = The user has been unblocked successfully.
921+
user_block_success = The user has been blocked successfully.
922+
917923
[repo]
918924
new_repo_helper = A repository contains all project files, including revision history. Already have it elsewhere? <a href="%s">Migrate repository.</a>
919925
owner = Owner
@@ -2524,6 +2530,7 @@ team_access_desc = Repository access
25242530
team_permission_desc = Permission
25252531
team_unit_desc = Allow Access to Repository Sections
25262532
team_unit_disabled = (Disabled)
2533+
follow_blocked_user = You cannot follow this organisation because this organisation has blocked you.
25272534
25282535
form.name_reserved = The organization name "%s" is reserved.
25292536
form.name_pattern_not_allowed = The pattern "%s" is not allowed in an organization name.

0 commit comments

Comments
 (0)