mirror of
https://github.com/go-gitea/gitea.git
synced 2025-07-31 01:36:00 +02:00
update per feedback
This commit is contained in:
parent
f601501fc1
commit
789b73bd55
@ -9,9 +9,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
"xorm.io/xorm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Badge represents a user badge
|
// Badge represents a user badge
|
||||||
@ -29,6 +29,50 @@ type UserBadge struct { //nolint:revive
|
|||||||
UserID int64 `xorm:"INDEX"`
|
UserID int64 `xorm:"INDEX"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrBadgeAlreadyExist represents a "badge already exists" error.
|
||||||
|
type ErrBadgeAlreadyExist struct {
|
||||||
|
Slug string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrBadgeAlreadyExist checks if an error is a ErrBadgeAlreadyExist.
|
||||||
|
func IsErrBadgeAlreadyExist(err error) bool {
|
||||||
|
_, ok := err.(ErrBadgeAlreadyExist)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrBadgeAlreadyExist) Error() string {
|
||||||
|
return fmt.Sprintf("badge already exists [slug: %s]", err.Slug)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap unwraps this error as a ErrExist error
|
||||||
|
func (err ErrBadgeAlreadyExist) Unwrap() error {
|
||||||
|
return util.ErrAlreadyExist
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrBadgeNotExist represents a "BadgeNotExist" kind of error.
|
||||||
|
type ErrBadgeNotExist struct {
|
||||||
|
Slug string
|
||||||
|
ID int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrBadgeNotExist) Error() string {
|
||||||
|
if err.ID > 0 {
|
||||||
|
return fmt.Sprintf("badge does not exist [id: %d]", err.ID)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("badge does not exist [slug: %s]", err.Slug)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrBadgeNotExist checks if an error is a ErrBadgeNotExist.
|
||||||
|
func IsErrBadgeNotExist(err error) bool {
|
||||||
|
_, ok := err.(ErrBadgeNotExist)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap unwraps this error as a ErrNotExist error
|
||||||
|
func (err ErrBadgeNotExist) Unwrap() error {
|
||||||
|
return util.ErrNotExist
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
db.RegisterModel(new(Badge))
|
db.RegisterModel(new(Badge))
|
||||||
db.RegisterModel(new(UserBadge))
|
db.RegisterModel(new(UserBadge))
|
||||||
@ -73,7 +117,6 @@ func GetBadgeUsers(ctx context.Context, opts *GetBadgeUsersOptions) ([]*User, in
|
|||||||
func CreateBadge(ctx context.Context, badge *Badge) error {
|
func CreateBadge(ctx context.Context, badge *Badge) error {
|
||||||
// this will fail if the badge already exists due to the UNIQUE constraint
|
// this will fail if the badge already exists due to the UNIQUE constraint
|
||||||
_, err := db.GetEngine(ctx).Insert(badge)
|
_, err := db.GetEngine(ctx).Insert(badge)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,11 +194,14 @@ func RemoveUserBadges(ctx context.Context, u *User, badges []*Badge) error {
|
|||||||
slugs[i] = badge.Slug
|
slugs[i] = badge.Slug
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var badgeIDs []int64
|
||||||
|
if err := db.GetEngine(ctx).Table("badge").In("slug", slugs).Cols("id").Find(&badgeIDs); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if _, err := db.GetEngine(ctx).
|
if _, err := db.GetEngine(ctx).
|
||||||
Table("user_badge").
|
Where("user_id = ?", u.ID).
|
||||||
Join("INNER", "badge", "`user_badge`.badge_id = badge.id").
|
In("badge_id", badgeIDs).
|
||||||
Where("`user_badge`.user_id = ?", u.ID).
|
|
||||||
And(builder.In("badge.slug", slugs)).
|
|
||||||
Delete(&UserBadge{}); err != nil {
|
Delete(&UserBadge{}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -184,66 +230,29 @@ func (opts *SearchBadgeOptions) ToConds() builder.Cond {
|
|||||||
cond := builder.NewCond()
|
cond := builder.NewCond()
|
||||||
|
|
||||||
if opts.Keyword != "" {
|
if opts.Keyword != "" {
|
||||||
cond = cond.And(builder.Like{"badge.slug", opts.Keyword})
|
|
||||||
}
|
|
||||||
|
|
||||||
return cond
|
|
||||||
}
|
|
||||||
|
|
||||||
func (opts *SearchBadgeOptions) ToOrders() string {
|
|
||||||
orderBy := "badge.slug"
|
|
||||||
return orderBy
|
|
||||||
}
|
|
||||||
|
|
||||||
func SearchBadges(ctx context.Context, opts *SearchBadgeOptions) (badges []*Badge, _ int64, _ error) {
|
|
||||||
sessCount := opts.toSearchQueryBase(ctx)
|
|
||||||
count, err := sessCount.Count(new(Badge))
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, fmt.Errorf("count: %w", err)
|
|
||||||
}
|
|
||||||
sessCount.Close()
|
|
||||||
|
|
||||||
if len(opts.OrderBy) == 0 {
|
|
||||||
opts.OrderBy = db.SearchOrderByID
|
|
||||||
}
|
|
||||||
|
|
||||||
sessQuery := opts.toSearchQueryBase(ctx).OrderBy(opts.OrderBy.String())
|
|
||||||
defer sessQuery.Close()
|
|
||||||
if opts.Page != 0 {
|
|
||||||
sessQuery = db.SetSessionPagination(sessQuery, opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
// the sql may contain JOIN, so we must only select Badge related columns
|
|
||||||
sessQuery = sessQuery.Select("`badge`.*")
|
|
||||||
badges = make([]*Badge, 0, opts.PageSize)
|
|
||||||
return badges, count, sessQuery.Find(&badges)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (opts *SearchBadgeOptions) toSearchQueryBase(ctx context.Context) *xorm.Session {
|
|
||||||
var cond builder.Cond
|
|
||||||
cond = builder.Neq{"id": -1}
|
|
||||||
|
|
||||||
if len(opts.Keyword) > 0 {
|
|
||||||
lowerKeyword := strings.ToLower(opts.Keyword)
|
lowerKeyword := strings.ToLower(opts.Keyword)
|
||||||
keywordCond := builder.Or(
|
keywordCond := builder.Or(
|
||||||
builder.Like{"slug", lowerKeyword},
|
builder.Like{"badge.slug", lowerKeyword},
|
||||||
builder.Like{"description", lowerKeyword},
|
builder.Like{"badge.description", lowerKeyword},
|
||||||
builder.Like{"id", lowerKeyword},
|
builder.Like{"badge.id", lowerKeyword},
|
||||||
)
|
)
|
||||||
cond = cond.And(keywordCond)
|
cond = cond.And(keywordCond)
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.ID > 0 {
|
if opts.ID > 0 {
|
||||||
cond = cond.And(builder.Eq{"id": opts.ID})
|
cond = cond.And(builder.Eq{"badge.id": opts.ID})
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(opts.Slug) > 0 {
|
if len(opts.Slug) > 0 {
|
||||||
cond = cond.And(builder.Eq{"slug": opts.Slug})
|
cond = cond.And(builder.Eq{"badge.slug": opts.Slug})
|
||||||
}
|
}
|
||||||
|
|
||||||
e := db.GetEngine(ctx)
|
return cond
|
||||||
|
}
|
||||||
|
|
||||||
return e.Where(cond)
|
// SearchBadges returns badges based on the provided SearchBadgeOptions options
|
||||||
|
func SearchBadges(ctx context.Context, opts *SearchBadgeOptions) ([]*Badge, int64, error) {
|
||||||
|
return db.FindAndCount[Badge](ctx, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBadgeByID returns a specific badge by ID
|
// GetBadgeByID returns a specific badge by ID
|
||||||
|
61
models/user/badge_test.go
Normal file
61
models/user/badge_test.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package user_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetBadgeUsers(t *testing.T) {
|
||||||
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
|
// Create a test badge
|
||||||
|
badge := &user_model.Badge{
|
||||||
|
Slug: "test-badge",
|
||||||
|
Description: "Test Badge",
|
||||||
|
ImageURL: "test.png",
|
||||||
|
}
|
||||||
|
assert.NoError(t, user_model.CreateBadge(db.DefaultContext, badge))
|
||||||
|
|
||||||
|
// Create test users and assign badges
|
||||||
|
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||||
|
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||||
|
|
||||||
|
assert.NoError(t, user_model.AddUserBadge(db.DefaultContext, user1, badge))
|
||||||
|
assert.NoError(t, user_model.AddUserBadge(db.DefaultContext, user2, badge))
|
||||||
|
|
||||||
|
// Test getting users with pagination
|
||||||
|
opts := &user_model.GetBadgeUsersOptions{
|
||||||
|
Badge: badge,
|
||||||
|
ListOptions: db.ListOptions{
|
||||||
|
Page: 1,
|
||||||
|
PageSize: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
users, count, err := user_model.GetBadgeUsers(db.DefaultContext, opts)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 2, count)
|
||||||
|
assert.Len(t, users, 1)
|
||||||
|
|
||||||
|
// Test second page
|
||||||
|
opts.Page = 2
|
||||||
|
users, count, err = user_model.GetBadgeUsers(db.DefaultContext, opts)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 2, count)
|
||||||
|
assert.Len(t, users, 1)
|
||||||
|
|
||||||
|
// Test with non-existent badge
|
||||||
|
opts.Badge = &user_model.Badge{Slug: "non-existent"}
|
||||||
|
users, count, err = user_model.GetBadgeUsers(db.DefaultContext, opts)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 0, count)
|
||||||
|
assert.Len(t, users, 0)
|
||||||
|
}
|
@ -107,47 +107,3 @@ func IsErrUserIsNotLocal(err error) bool {
|
|||||||
_, ok := err.(ErrUserIsNotLocal)
|
_, ok := err.(ErrUserIsNotLocal)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrBadgeAlreadyExist represents a "badge already exists" error.
|
|
||||||
type ErrBadgeAlreadyExist struct {
|
|
||||||
Slug string
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrBadgeAlreadyExist checks if an error is a ErrBadgeAlreadyExist.
|
|
||||||
func IsErrBadgeAlreadyExist(err error) bool {
|
|
||||||
_, ok := err.(ErrBadgeAlreadyExist)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrBadgeAlreadyExist) Error() string {
|
|
||||||
return fmt.Sprintf("badge already exists [slug: %s]", err.Slug)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unwrap unwraps this error as a ErrExist error
|
|
||||||
func (err ErrBadgeAlreadyExist) Unwrap() error {
|
|
||||||
return util.ErrAlreadyExist
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrBadgeNotExist represents a "BadgeNotExist" kind of error.
|
|
||||||
type ErrBadgeNotExist struct {
|
|
||||||
Slug string
|
|
||||||
ID int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrBadgeNotExist) Error() string {
|
|
||||||
if err.ID > 0 {
|
|
||||||
return fmt.Sprintf("badge does not exist [id: %d]", err.ID)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("badge does not exist [slug: %s]", err.Slug)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrBadgeNotExist checks if an error is a ErrBadgeNotExist.
|
|
||||||
func IsErrBadgeNotExist(err error) bool {
|
|
||||||
_, ok := err.(ErrBadgeNotExist)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unwrap unwraps this error as a ErrNotExist error
|
|
||||||
func (err ErrBadgeNotExist) Unwrap() error {
|
|
||||||
return util.ErrNotExist
|
|
||||||
}
|
|
||||||
|
@ -286,7 +286,7 @@ func DeleteBadgeUser(ctx *context.Context) {
|
|||||||
if err := user_model.RemoveUserBadge(ctx, user, &user_model.Badge{Slug: ctx.PathParam(":badge_slug")}); err == nil {
|
if err := user_model.RemoveUserBadge(ctx, user, &user_model.Badge{Slug: ctx.PathParam(":badge_slug")}); err == nil {
|
||||||
ctx.Flash.Success(ctx.Tr("admin.badges.user_remove_success"))
|
ctx.Flash.Success(ctx.Tr("admin.badges.user_remove_success"))
|
||||||
} else {
|
} else {
|
||||||
ctx.Flash.Error("DeleteUser: " + err.Error())
|
ctx.Flash.Error("DeleteBadgeUser: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.JSONRedirect(fmt.Sprintf("%s/-/admin/badges/%s/users", setting.AppSubURL, ctx.PathParam(":badge_slug")))
|
ctx.JSONRedirect(fmt.Sprintf("%s/-/admin/badges/%s/users", setting.AppSubURL, ctx.PathParam(":badge_slug")))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user