fix: Fix some wrong code and follow 37347 (#37987)

This commit is contained in:
Lunny Xiao
2026-06-09 07:53:58 +02:00
committed by GitHub
parent 72c1e4c621
commit 611dfc9496
4 changed files with 52 additions and 65 deletions
+28 -31
View File
@@ -19,7 +19,6 @@ import (
"gitea.dev/modules/util" "gitea.dev/modules/util"
"xorm.io/builder" "xorm.io/builder"
"xorm.io/xorm"
) )
// ErrOrgNotExist represents a "OrgNotExist" kind of error. // ErrOrgNotExist represents a "OrgNotExist" kind of error.
@@ -181,45 +180,43 @@ func (org *Organization) HomeLink() string {
// FindOrgMembersOpts represents find org members conditions // FindOrgMembersOpts represents find org members conditions
type FindOrgMembersOpts struct { type FindOrgMembersOpts struct {
db.ListOptions db.ListOptions
Doer *user_model.User Doer *user_model.User
IsDoerMember bool IsDoerMember bool
OrgID int64 OrgID int64
Keyword string Keyword string
SearchByEmail bool
} }
func (opts FindOrgMembersOpts) PublicOnly() bool { func (opts FindOrgMembersOpts) PublicOnly() bool {
return opts.Doer == nil || !(opts.IsDoerMember || opts.Doer.IsAdmin) return opts.Doer == nil || !(opts.IsDoerMember || opts.Doer.IsAdmin)
} }
func (opts FindOrgMembersOpts) applyKeywordFilter(sess *xorm.Session) (*xorm.Session, bool) { // applyKeywordFilter adds keyword search conditions to session
func (opts FindOrgMembersOpts) applyKeywordFilter(sess db.Session) bool {
if opts.Keyword == "" { if opts.Keyword == "" {
return sess, false return false
} }
lowerKeyword := strings.ToLower(opts.Keyword)
keywordCond := builder.Or( keywordCond := builder.Or(
builder.Like{"`user`.lower_name", lowerKeyword}, db.BuildCaseInsensitiveLike("`user`.lower_name", opts.Keyword),
builder.Like{"LOWER(`user`.full_name)", lowerKeyword}, db.BuildCaseInsensitiveLike("`user`.full_name", opts.Keyword),
) )
if opts.SearchByEmail {
var emailCond builder.Cond = builder.Like{"LOWER(`user`.email)", lowerKeyword}
switch {
case opts.Doer == nil:
emailCond = emailCond.And(builder.Eq{"`user`.keep_email_private": false})
case !opts.Doer.IsAdmin:
emailCond = emailCond.And(
builder.Or(
builder.Eq{"`user`.keep_email_private": false},
builder.Eq{"`user`.id": opts.Doer.ID},
),
)
}
keywordCond = keywordCond.Or(emailCond)
}
sess = sess.Join("INNER", "`user`", "org_user.uid = `user`.id").And(keywordCond) emailCond := db.BuildCaseInsensitiveLike("`user`.email", opts.Keyword)
return sess, true switch {
case opts.Doer == nil:
emailCond = emailCond.And(builder.Eq{"`user`.keep_email_private": false})
case !opts.Doer.IsAdmin:
emailCond = emailCond.And(
builder.Or(
builder.Eq{"`user`.keep_email_private": false},
builder.Eq{"`user`.id": opts.Doer.ID},
),
)
}
keywordCond = keywordCond.Or(emailCond)
_ = sess.Join("INNER", "`user`", "org_user.uid = `user`.id").And(keywordCond)
return true
} }
// applyTeamMatesOnlyFilter make sure restricted users only see public team members and there own team mates // applyTeamMatesOnlyFilter make sure restricted users only see public team members and there own team mates
@@ -245,7 +242,7 @@ func CountOrgMembers(ctx context.Context, opts *FindOrgMembersOpts) (int64, erro
} else { } else {
opts.applyTeamMatesOnlyFilter(sess) opts.applyTeamMatesOnlyFilter(sess)
} }
sess, _ = opts.applyKeywordFilter(sess) _ = opts.applyKeywordFilter(sess)
return sess.Count(new(OrgUser)) return sess.Count(new(OrgUser))
} }
@@ -494,8 +491,8 @@ func GetOrgUsersByOrgID(ctx context.Context, opts *FindOrgMembersOpts) ([]*OrgUs
} else { } else {
opts.applyTeamMatesOnlyFilter(sess) opts.applyTeamMatesOnlyFilter(sess)
} }
if keywordSess, hasKeyword := opts.applyKeywordFilter(sess); hasKeyword { if opts.applyKeywordFilter(sess) {
sess = keywordSess.Select("org_user.*") sess = sess.Select("org_user.*")
} }
sess = sess.OrderBy("org_user.uid ASC") sess = sess.OrderBy("org_user.uid ASC")
+15 -19
View File
@@ -302,43 +302,39 @@ func TestOrgMembersSearch(t *testing.T) {
{ {
name: "match by username", name: "match by username",
opts: &organization.FindOrgMembersOpts{ opts: &organization.FindOrgMembersOpts{
OrgID: 3, OrgID: 3,
Doer: member, Doer: member,
IsDoerMember: true, IsDoerMember: true,
Keyword: "user4", Keyword: "user4",
SearchByEmail: true,
}, },
expectedUIDs: []int64{4}, expectedUIDs: []int64{4},
}, },
{ {
name: "match by full name", name: "match by full name",
opts: &organization.FindOrgMembersOpts{ opts: &organization.FindOrgMembersOpts{
OrgID: 3, OrgID: 3,
Doer: member, Doer: member,
IsDoerMember: true, IsDoerMember: true,
Keyword: "user27", Keyword: "user27",
SearchByEmail: true,
}, },
expectedUIDs: []int64{28}, expectedUIDs: []int64{28},
}, },
{ {
name: "private email hidden", name: "private email hidden",
opts: &organization.FindOrgMembersOpts{ opts: &organization.FindOrgMembersOpts{
OrgID: 3, OrgID: 3,
Doer: member, Doer: member,
IsDoerMember: true, IsDoerMember: true,
Keyword: "user2@example.com", Keyword: "user2@example.com",
SearchByEmail: true,
}, },
expectedUIDs: []int64{}, expectedUIDs: []int64{},
}, },
{ {
name: "admin can search private email", name: "admin can search private email",
opts: &organization.FindOrgMembersOpts{ opts: &organization.FindOrgMembersOpts{
OrgID: 3, OrgID: 3,
Doer: admin, Doer: admin,
Keyword: "user2@example.com", Keyword: "user2@example.com",
SearchByEmail: true,
}, },
expectedUIDs: []int64{2}, expectedUIDs: []int64{2},
}, },
+5 -8
View File
@@ -35,16 +35,15 @@ func Members(ctx *context.Context) {
ctx.Data["Keyword"] = keyword ctx.Data["Keyword"] = keyword
opts := &organization.FindOrgMembersOpts{ opts := &organization.FindOrgMembersOpts{
Doer: ctx.Doer, Doer: ctx.Doer,
OrgID: org.ID, OrgID: org.ID,
Keyword: keyword, Keyword: keyword,
SearchByEmail: true,
} }
if ctx.Doer != nil { if ctx.Doer != nil {
isMember, err := ctx.Org.Organization.IsOrgMember(ctx, ctx.Doer.ID) isMember, err := ctx.Org.Organization.IsOrgMember(ctx, ctx.Doer.ID)
if err != nil { if err != nil {
ctx.HTTPError(http.StatusInternalServerError, "IsOrgMember") ctx.ServerError("IsOrgMember", err)
return return
} }
opts.IsDoerMember = isMember opts.IsDoerMember = isMember
@@ -53,7 +52,7 @@ func Members(ctx *context.Context) {
total, err := organization.CountOrgMembers(ctx, opts) total, err := organization.CountOrgMembers(ctx, opts)
if err != nil { if err != nil {
ctx.HTTPError(http.StatusInternalServerError, "CountOrgMembers") ctx.ServerError("CountOrgMembers", err)
return return
} }
@@ -74,8 +73,6 @@ func Members(ctx *context.Context) {
} }
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.Data["Members"] = members ctx.Data["Members"] = members
ctx.Data["MembersShown"] = len(members)
ctx.Data["MembersTotal"] = total
ctx.Data["MembersIsPublicMember"] = membersIsPublic ctx.Data["MembersIsPublicMember"] = membersIsPublic
ctx.Data["MembersIsUserOrgOwner"] = organization.IsUserOrgOwner(ctx, members, org.ID) ctx.Data["MembersIsUserOrgOwner"] = organization.IsUserOrgOwner(ctx, members, org.ID)
ctx.Data["MembersTwoFaStatus"] = members.GetTwoFaStatus(ctx) ctx.Data["MembersTwoFaStatus"] = members.GetTwoFaStatus(ctx)
+4 -7
View File
@@ -11,16 +11,13 @@
</div> </div>
<div class="divider"></div> <div class="divider"></div>
{{end}} {{end}}
<div class="ui small secondary filter menu"> <div class="ui small secondary filter">
<form id="org-member-search-form" class="ui form ignore-dirty tw-flex-1 tw-flex tw-items-center tw-gap-x-2"> <form id="org-member-search-form" class="ui form ignore-dirty tw-flex-1 tw-flex tw-items-center">
<div class="ui small fluid action input tw-flex-1"> <div class="ui small fluid action input tw-flex-1">
{{template "shared/search/input" dict "Value" .Keyword "Placeholder" (ctx.Locale.Tr "search.user_kind")}} {{template "shared/search/input" dict "Value" .Keyword "Placeholder" (ctx.Locale.Tr "search.user_kind")}}
{{template "shared/search/button"}} {{template "shared/search/button"}}
</div> </div>
</form> </form>
<div class="item tw-text-text-light">
{{ctx.Locale.Tr "org.members"}}: {{CountFmt .MembersShown}} / {{CountFmt .MembersTotal}}
</div>
</div> </div>
<div class="flex-divided-list items-with-main"> <div class="flex-divided-list items-with-main">
{{range .Members}} {{range .Members}}
@@ -79,8 +76,8 @@
</div> </div>
</div> </div>
{{else}} {{else}}
<div class="flex-item"> <div class="item">
<div class="flex-item-main"> <div class="item-main">
{{ctx.Locale.Tr "search.no_results"}} {{ctx.Locale.Tr "search.no_results"}}
</div> </div>
</div> </div>