diff --git a/models/user/badge.go b/models/user/badge.go index 1497981d99..f5dff4eeae 100644 --- a/models/user/badge.go +++ b/models/user/badge.go @@ -46,14 +46,25 @@ func GetUserBadges(ctx context.Context, u *User) ([]*Badge, int64, error) { return badges, count, err } -// GetBadgeUsers returns the users that have a specific badge. -func GetBadgeUsers(ctx context.Context, b *Badge) ([]*User, int64, error) { +// GetBadgeUsersOptions contains options for getting users with a specific badge +type GetBadgeUsersOptions struct { + db.ListOptions + Badge *Badge +} + +// GetBadgeUsers returns the users that have a specific badge with pagination support. +func GetBadgeUsers(ctx context.Context, opts *GetBadgeUsersOptions) ([]*User, int64, error) { sess := db.GetEngine(ctx). Select("`user`.*"). Join("INNER", "user_badge", "`user_badge`.user_id=user.id"). Join("INNER", "badge", "`user_badge`.badge_id=badge.id"). - Where("badge.slug=?", b.Slug) - users := make([]*User, 0, 8) + Where("badge.slug=?", opts.Badge.Slug) + + if opts.Page > 0 { + sess = db.SetSessionPagination(sess, opts) + } + + users := make([]*User, 0, opts.PageSize) count, err := sess.FindAndCount(&users) return users, count, err } @@ -82,10 +93,22 @@ func UpdateBadge(ctx context.Context, badge *Badge) error { return err } -// DeleteBadge deletes a badge. +// DeleteBadge deletes a badge and all associated user_badge entries. func DeleteBadge(ctx context.Context, badge *Badge) error { - _, err := db.GetEngine(ctx).Where("slug=?", badge.Slug).Delete(badge) - return err + return db.WithTx(ctx, func(ctx context.Context) error { + // First delete all user_badge entries for this badge + if _, err := db.GetEngine(ctx). + Where("badge_id = (SELECT id FROM badge WHERE slug = ?)", badge.Slug). + Delete(&UserBadge{}); err != nil { + return err + } + + // Then delete the badge itself + if _, err := db.GetEngine(ctx).Where("slug=?", badge.Slug).Delete(badge); err != nil { + return err + } + return nil + }) } // AddUserBadge adds a badge to a user. @@ -123,18 +146,18 @@ func RemoveUserBadge(ctx context.Context, u *User, badge *Badge) error { // RemoveUserBadges removes specific badges from a user. func RemoveUserBadges(ctx context.Context, u *User, badges []*Badge) error { return db.WithTx(ctx, func(ctx context.Context) error { - for _, badge := range badges { - subQuery := builder. - Select("id"). - From("badge"). - Where(builder.Eq{"slug": badge.Slug}) + slugs := make([]string, len(badges)) + for i, badge := range badges { + slugs[i] = badge.Slug + } - if _, err := db.GetEngine(ctx). - Where("`user_badge`.user_id=?", u.ID). - And(builder.In("badge_id", subQuery)). - Delete(&UserBadge{}); err != nil { - return err - } + if _, err := db.GetEngine(ctx). + Table("user_badge"). + Join("INNER", "badge", "`user_badge`.badge_id = badge.id"). + Where("`user_badge`.user_id = ?", u.ID). + And(builder.In("badge.slug", slugs)). + Delete(&UserBadge{}); err != nil { + return err } return nil }) @@ -155,8 +178,6 @@ type SearchBadgeOptions struct { ID int64 OrderBy db.SearchOrderBy Actor *User // The user doing the search - - ExtraParamStrings map[string]string } func (opts *SearchBadgeOptions) ToConds() builder.Cond { @@ -174,15 +195,6 @@ func (opts *SearchBadgeOptions) ToOrders() string { return orderBy } -func (opts *SearchBadgeOptions) ToJoins() []db.JoinFunc { - return []db.JoinFunc{ - func(e db.Engine) error { - e.Join("INNER", "badge", "`user_badge`.badge_id=badge.id") - return nil - }, - } -} - func SearchBadges(ctx context.Context, opts *SearchBadgeOptions) (badges []*Badge, _ int64, _ error) { sessCount := opts.toSearchQueryBase(ctx) count, err := sessCount.Count(new(Badge)) @@ -233,3 +245,16 @@ func (opts *SearchBadgeOptions) toSearchQueryBase(ctx context.Context) *xorm.Ses return e.Where(cond) } + +// GetBadgeByID returns a specific badge by ID +func GetBadgeByID(ctx context.Context, id int64) (*Badge, error) { + badge := new(Badge) + has, err := db.GetEngine(ctx).ID(id).Get(badge) + if err != nil { + return nil, err + } + if !has { + return nil, ErrBadgeNotExist{ID: id} + } + return badge, nil +} diff --git a/models/user/error.go b/models/user/error.go index 88986ee93c..7843558aaf 100644 --- a/models/user/error.go +++ b/models/user/error.go @@ -131,6 +131,14 @@ func (err ErrBadgeAlreadyExist) Unwrap() error { // 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. @@ -139,10 +147,6 @@ func IsErrBadgeNotExist(err error) bool { return ok } -func (err ErrBadgeNotExist) Error() string { - return fmt.Sprintf("badge does not exist [slug: %s]", err.Slug) -} - // Unwrap unwraps this error as a ErrNotExist error func (err ErrBadgeNotExist) Unwrap() error { return util.ErrNotExist diff --git a/routers/web/admin/badges.go b/routers/web/admin/badges.go index 4bcf20cf30..19d3ae6a85 100644 --- a/routers/web/admin/badges.go +++ b/routers/web/admin/badges.go @@ -7,14 +7,13 @@ import ( "fmt" "net/http" "net/url" - "strconv" "strings" "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/web/explore" "code.gitea.io/gitea/services/context" @@ -23,11 +22,11 @@ import ( ) const ( - tplBadges base.TplName = "admin/badge/list" - tplBadgeNew base.TplName = "admin/badge/new" - tplBadgeView base.TplName = "admin/badge/view" - tplBadgeEdit base.TplName = "admin/badge/edit" - tplBadgeUsers base.TplName = "admin/badge/users" + tplBadges templates.TplName = "admin/badge/list" + tplBadgeNew templates.TplName = "admin/badge/new" + tplBadgeView templates.TplName = "admin/badge/view" + tplBadgeEdit templates.TplName = "admin/badge/edit" + tplBadgeUsers templates.TplName = "admin/badge/users" ) // BadgeSearchDefaultAdminSort is the default sort type for admin view @@ -97,14 +96,14 @@ func NewBadgePost(ctx *context.Context) { log.Trace("Badge created by admin (%s): %s", ctx.Doer.Name, b.Slug) ctx.Flash.Success(ctx.Tr("admin.badges.new_success", b.Slug)) - ctx.Redirect(setting.AppSubURL + "/admin/badges/" + strconv.FormatInt(b.ID, 10)) + ctx.Redirect(setting.AppSubURL + "/-/admin/badges/" + url.PathEscape(b.Slug)) } func prepareBadgeInfo(ctx *context.Context) *user_model.Badge { b, err := user_model.GetBadge(ctx, ctx.PathParam(":badge_slug")) if err != nil { if user_model.IsErrBadgeNotExist(err) { - ctx.Redirect(setting.AppSubURL + "/admin/badges") + ctx.Redirect(setting.AppSubURL + "/-/admin/badges") } else { ctx.ServerError("GetBadge", err) } @@ -112,10 +111,16 @@ func prepareBadgeInfo(ctx *context.Context) *user_model.Badge { } ctx.Data["Badge"] = b - users, count, err := user_model.GetBadgeUsers(ctx, b) + opts := &user_model.GetBadgeUsersOptions{ + ListOptions: db.ListOptions{ + PageSize: setting.UI.User.RepoPagingNum, + }, + Badge: b, + } + users, count, err := user_model.GetBadgeUsers(ctx, opts) if err != nil { if user_model.IsErrUserNotExist(err) { - ctx.Redirect(setting.AppSubURL + "/admin/badges") + ctx.Redirect(setting.AppSubURL + "/-/admin/badges") } else { ctx.ServerError("GetBadgeUsers", err) } @@ -187,7 +192,7 @@ func EditBadgePost(ctx *context.Context) { log.Trace("Badge updated by admin (%s): %s", ctx.Doer.Name, b.Slug) ctx.Flash.Success(ctx.Tr("admin.badges.update_success")) - ctx.Redirect(setting.AppSubURL + "/admin/badges/" + url.PathEscape(ctx.PathParam(":badge_slug"))) + ctx.Redirect(setting.AppSubURL + "/-/admin/badges/" + url.PathEscape(ctx.PathParam(":badge_slug"))) } // DeleteBadge response for deleting a badge @@ -206,20 +211,35 @@ func DeleteBadge(ctx *context.Context) { log.Trace("Badge deleted by admin (%s): %s", ctx.Doer.Name, b.Slug) ctx.Flash.Success(ctx.Tr("admin.badges.deletion_success")) - ctx.Redirect(setting.AppSubURL + "/admin/badges") + ctx.Redirect(setting.AppSubURL + "/-/admin/badges") } func BadgeUsers(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("admin.badges.users_with_badge", ctx.PathParam(":badge_slug")) ctx.Data["PageIsAdminBadges"] = true - users, _, err := user_model.GetBadgeUsers(ctx, &user_model.Badge{Slug: ctx.PathParam(":badge_slug")}) + page := ctx.FormInt("page") + if page <= 0 { + page = 1 + } + + badge := &user_model.Badge{Slug: ctx.PathParam(":badge_slug")} + opts := &user_model.GetBadgeUsersOptions{ + ListOptions: db.ListOptions{ + Page: page, + PageSize: setting.UI.User.RepoPagingNum, + }, + Badge: badge, + } + users, count, err := user_model.GetBadgeUsers(ctx, opts) if err != nil { ctx.ServerError("GetBadgeUsers", err) return } ctx.Data["Users"] = users + ctx.Data["Total"] = count + ctx.Data["Page"] = context.NewPagination(int(count), setting.UI.User.RepoPagingNum, page, 5) ctx.HTML(http.StatusOK, tplBadgeUsers) } @@ -267,8 +287,41 @@ func DeleteBadgeUser(ctx *context.Context) { ctx.Flash.Success(ctx.Tr("admin.badges.user_remove_success")) } else { ctx.Flash.Error("DeleteUser: " + err.Error()) + } + + ctx.JSONRedirect(fmt.Sprintf("%s/-/admin/badges/%s/users", setting.AppSubURL, ctx.PathParam(":badge_slug"))) +} + +// ViewBadgeUsers render badge's users page +func ViewBadgeUsers(ctx *context.Context) { + badge, err := user_model.GetBadge(ctx, ctx.PathParam(":slug")) + if err != nil { + ctx.ServerError("GetBadge", err) return } - ctx.JSONRedirect(fmt.Sprintf("%s/admin/badges/%s/users", setting.AppSubURL, ctx.PathParam(":badge_slug"))) + page := ctx.FormInt("page") + if page <= 0 { + page = 1 + } + + opts := &user_model.GetBadgeUsersOptions{ + ListOptions: db.ListOptions{ + Page: page, + PageSize: setting.UI.User.RepoPagingNum, + }, + Badge: badge, + } + users, count, err := user_model.GetBadgeUsers(ctx, opts) + if err != nil { + ctx.ServerError("GetBadgeUsers", err) + return + } + + ctx.Data["Title"] = badge.Description + ctx.Data["Badge"] = badge + ctx.Data["Users"] = users + ctx.Data["Total"] = count + ctx.Data["Pages"] = context.NewPagination(int(count), setting.UI.User.RepoPagingNum, page, 5) + ctx.HTML(http.StatusOK, tplBadgeUsers) } diff --git a/routers/web/explore/badge.go b/routers/web/explore/badge.go index cc6bd2dc63..1aecd0ce72 100644 --- a/routers/web/explore/badge.go +++ b/routers/web/explore/badge.go @@ -8,12 +8,12 @@ import ( "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/services/context" ) -func RenderBadgeSearch(ctx *context.Context, opts *user_model.SearchBadgeOptions, tplName base.TplName) { +func RenderBadgeSearch(ctx *context.Context, opts *user_model.SearchBadgeOptions, tplName templates.TplName) { // Sitemap index for sitemap paths opts.Page = int(ctx.PathParamInt64("idx")) if opts.Page <= 1 { @@ -68,10 +68,7 @@ func RenderBadgeSearch(ctx *context.Context, opts *user_model.SearchBadgeOptions ctx.Data["Badges"] = badges pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5) - pager.SetDefaultParams(ctx) - for paramKey, paramVal := range opts.ExtraParamStrings { - pager.AddParamString(paramKey, paramVal) - } + pager.AddParamFromRequest(ctx.Req) ctx.Data["Page"] = pager ctx.HTML(http.StatusOK, tplName) diff --git a/services/user/badge.go b/services/user/badge.go index d4c6fe88ef..efbf42f943 100644 --- a/services/user/badge.go +++ b/services/user/badge.go @@ -44,3 +44,15 @@ func DeleteBadge(ctx context.Context, b *user_model.Badge) error { return nil } + +// GetBadgeUsers returns the users that have a specific badge +func GetBadgeUsers(ctx context.Context, badge *user_model.Badge, page, pageSize int) ([]*user_model.User, int64, error) { + opts := &user_model.GetBadgeUsersOptions{ + ListOptions: db.ListOptions{ + Page: page, + PageSize: pageSize, + }, + Badge: badge, + } + return user_model.GetBadgeUsers(ctx, opts) +} diff --git a/templates/admin/badge/list.tmpl b/templates/admin/badge/list.tmpl index 0f4db0fc9e..6313fa825a 100644 --- a/templates/admin/badge/list.tmpl +++ b/templates/admin/badge/list.tmpl @@ -3,7 +3,7 @@

{{ctx.Locale.Tr "admin.badges.badges_manage_panel"}} ({{ctx.Locale.Tr "admin.total" .Total}})
- {{ctx.Locale.Tr "admin.badges.new_badge"}} + {{ctx.Locale.Tr "admin.badges.new_badge"}}

diff --git a/templates/admin/badge/users.tmpl b/templates/admin/badge/users.tmpl index 41088170cc..7060b7bcff 100644 --- a/templates/admin/badge/users.tmpl +++ b/templates/admin/badge/users.tmpl @@ -26,6 +26,7 @@
{{end}} + {{template "base/paginate" .}}
{{.CsrfTokenHtml}}