Skip to content

Commit f601501

Browse files
Update per feedback
1 parent 9234ef5 commit f601501

File tree

7 files changed

+148
-56
lines changed

7 files changed

+148
-56
lines changed

models/user/badge.go

Lines changed: 55 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,25 @@ func GetUserBadges(ctx context.Context, u *User) ([]*Badge, int64, error) {
4646
return badges, count, err
4747
}
4848

49-
// GetBadgeUsers returns the users that have a specific badge.
50-
func GetBadgeUsers(ctx context.Context, b *Badge) ([]*User, int64, error) {
49+
// GetBadgeUsersOptions contains options for getting users with a specific badge
50+
type GetBadgeUsersOptions struct {
51+
db.ListOptions
52+
Badge *Badge
53+
}
54+
55+
// GetBadgeUsers returns the users that have a specific badge with pagination support.
56+
func GetBadgeUsers(ctx context.Context, opts *GetBadgeUsersOptions) ([]*User, int64, error) {
5157
sess := db.GetEngine(ctx).
5258
Select("`user`.*").
5359
Join("INNER", "user_badge", "`user_badge`.user_id=user.id").
5460
Join("INNER", "badge", "`user_badge`.badge_id=badge.id").
55-
Where("badge.slug=?", b.Slug)
56-
users := make([]*User, 0, 8)
61+
Where("badge.slug=?", opts.Badge.Slug)
62+
63+
if opts.Page > 0 {
64+
sess = db.SetSessionPagination(sess, opts)
65+
}
66+
67+
users := make([]*User, 0, opts.PageSize)
5768
count, err := sess.FindAndCount(&users)
5869
return users, count, err
5970
}
@@ -82,10 +93,22 @@ func UpdateBadge(ctx context.Context, badge *Badge) error {
8293
return err
8394
}
8495

85-
// DeleteBadge deletes a badge.
96+
// DeleteBadge deletes a badge and all associated user_badge entries.
8697
func DeleteBadge(ctx context.Context, badge *Badge) error {
87-
_, err := db.GetEngine(ctx).Where("slug=?", badge.Slug).Delete(badge)
88-
return err
98+
return db.WithTx(ctx, func(ctx context.Context) error {
99+
// First delete all user_badge entries for this badge
100+
if _, err := db.GetEngine(ctx).
101+
Where("badge_id = (SELECT id FROM badge WHERE slug = ?)", badge.Slug).
102+
Delete(&UserBadge{}); err != nil {
103+
return err
104+
}
105+
106+
// Then delete the badge itself
107+
if _, err := db.GetEngine(ctx).Where("slug=?", badge.Slug).Delete(badge); err != nil {
108+
return err
109+
}
110+
return nil
111+
})
89112
}
90113

91114
// AddUserBadge adds a badge to a user.
@@ -123,18 +146,18 @@ func RemoveUserBadge(ctx context.Context, u *User, badge *Badge) error {
123146
// RemoveUserBadges removes specific badges from a user.
124147
func RemoveUserBadges(ctx context.Context, u *User, badges []*Badge) error {
125148
return db.WithTx(ctx, func(ctx context.Context) error {
126-
for _, badge := range badges {
127-
subQuery := builder.
128-
Select("id").
129-
From("badge").
130-
Where(builder.Eq{"slug": badge.Slug})
131-
132-
if _, err := db.GetEngine(ctx).
133-
Where("`user_badge`.user_id=?", u.ID).
134-
And(builder.In("badge_id", subQuery)).
135-
Delete(&UserBadge{}); err != nil {
136-
return err
137-
}
149+
slugs := make([]string, len(badges))
150+
for i, badge := range badges {
151+
slugs[i] = badge.Slug
152+
}
153+
154+
if _, err := db.GetEngine(ctx).
155+
Table("user_badge").
156+
Join("INNER", "badge", "`user_badge`.badge_id = badge.id").
157+
Where("`user_badge`.user_id = ?", u.ID).
158+
And(builder.In("badge.slug", slugs)).
159+
Delete(&UserBadge{}); err != nil {
160+
return err
138161
}
139162
return nil
140163
})
@@ -155,8 +178,6 @@ type SearchBadgeOptions struct {
155178
ID int64
156179
OrderBy db.SearchOrderBy
157180
Actor *User // The user doing the search
158-
159-
ExtraParamStrings map[string]string
160181
}
161182

162183
func (opts *SearchBadgeOptions) ToConds() builder.Cond {
@@ -174,15 +195,6 @@ func (opts *SearchBadgeOptions) ToOrders() string {
174195
return orderBy
175196
}
176197

177-
func (opts *SearchBadgeOptions) ToJoins() []db.JoinFunc {
178-
return []db.JoinFunc{
179-
func(e db.Engine) error {
180-
e.Join("INNER", "badge", "`user_badge`.badge_id=badge.id")
181-
return nil
182-
},
183-
}
184-
}
185-
186198
func SearchBadges(ctx context.Context, opts *SearchBadgeOptions) (badges []*Badge, _ int64, _ error) {
187199
sessCount := opts.toSearchQueryBase(ctx)
188200
count, err := sessCount.Count(new(Badge))
@@ -233,3 +245,16 @@ func (opts *SearchBadgeOptions) toSearchQueryBase(ctx context.Context) *xorm.Ses
233245

234246
return e.Where(cond)
235247
}
248+
249+
// GetBadgeByID returns a specific badge by ID
250+
func GetBadgeByID(ctx context.Context, id int64) (*Badge, error) {
251+
badge := new(Badge)
252+
has, err := db.GetEngine(ctx).ID(id).Get(badge)
253+
if err != nil {
254+
return nil, err
255+
}
256+
if !has {
257+
return nil, ErrBadgeNotExist{ID: id}
258+
}
259+
return badge, nil
260+
}

models/user/error.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,14 @@ func (err ErrBadgeAlreadyExist) Unwrap() error {
131131
// ErrBadgeNotExist represents a "BadgeNotExist" kind of error.
132132
type ErrBadgeNotExist struct {
133133
Slug string
134+
ID int64
135+
}
136+
137+
func (err ErrBadgeNotExist) Error() string {
138+
if err.ID > 0 {
139+
return fmt.Sprintf("badge does not exist [id: %d]", err.ID)
140+
}
141+
return fmt.Sprintf("badge does not exist [slug: %s]", err.Slug)
134142
}
135143

136144
// IsErrBadgeNotExist checks if an error is a ErrBadgeNotExist.
@@ -139,10 +147,6 @@ func IsErrBadgeNotExist(err error) bool {
139147
return ok
140148
}
141149

142-
func (err ErrBadgeNotExist) Error() string {
143-
return fmt.Sprintf("badge does not exist [slug: %s]", err.Slug)
144-
}
145-
146150
// Unwrap unwraps this error as a ErrNotExist error
147151
func (err ErrBadgeNotExist) Unwrap() error {
148152
return util.ErrNotExist

routers/web/admin/badges.go

Lines changed: 68 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@ import (
77
"fmt"
88
"net/http"
99
"net/url"
10-
"strconv"
1110
"strings"
1211

1312
"code.gitea.io/gitea/models/db"
1413
user_model "code.gitea.io/gitea/models/user"
15-
"code.gitea.io/gitea/modules/base"
1614
"code.gitea.io/gitea/modules/log"
1715
"code.gitea.io/gitea/modules/setting"
16+
"code.gitea.io/gitea/modules/templates"
1817
"code.gitea.io/gitea/modules/web"
1918
"code.gitea.io/gitea/routers/web/explore"
2019
"code.gitea.io/gitea/services/context"
@@ -23,11 +22,11 @@ import (
2322
)
2423

2524
const (
26-
tplBadges base.TplName = "admin/badge/list"
27-
tplBadgeNew base.TplName = "admin/badge/new"
28-
tplBadgeView base.TplName = "admin/badge/view"
29-
tplBadgeEdit base.TplName = "admin/badge/edit"
30-
tplBadgeUsers base.TplName = "admin/badge/users"
25+
tplBadges templates.TplName = "admin/badge/list"
26+
tplBadgeNew templates.TplName = "admin/badge/new"
27+
tplBadgeView templates.TplName = "admin/badge/view"
28+
tplBadgeEdit templates.TplName = "admin/badge/edit"
29+
tplBadgeUsers templates.TplName = "admin/badge/users"
3130
)
3231

3332
// BadgeSearchDefaultAdminSort is the default sort type for admin view
@@ -97,25 +96,31 @@ func NewBadgePost(ctx *context.Context) {
9796
log.Trace("Badge created by admin (%s): %s", ctx.Doer.Name, b.Slug)
9897

9998
ctx.Flash.Success(ctx.Tr("admin.badges.new_success", b.Slug))
100-
ctx.Redirect(setting.AppSubURL + "/admin/badges/" + strconv.FormatInt(b.ID, 10))
99+
ctx.Redirect(setting.AppSubURL + "/-/admin/badges/" + url.PathEscape(b.Slug))
101100
}
102101

103102
func prepareBadgeInfo(ctx *context.Context) *user_model.Badge {
104103
b, err := user_model.GetBadge(ctx, ctx.PathParam(":badge_slug"))
105104
if err != nil {
106105
if user_model.IsErrBadgeNotExist(err) {
107-
ctx.Redirect(setting.AppSubURL + "/admin/badges")
106+
ctx.Redirect(setting.AppSubURL + "/-/admin/badges")
108107
} else {
109108
ctx.ServerError("GetBadge", err)
110109
}
111110
return nil
112111
}
113112
ctx.Data["Badge"] = b
114113

115-
users, count, err := user_model.GetBadgeUsers(ctx, b)
114+
opts := &user_model.GetBadgeUsersOptions{
115+
ListOptions: db.ListOptions{
116+
PageSize: setting.UI.User.RepoPagingNum,
117+
},
118+
Badge: b,
119+
}
120+
users, count, err := user_model.GetBadgeUsers(ctx, opts)
116121
if err != nil {
117122
if user_model.IsErrUserNotExist(err) {
118-
ctx.Redirect(setting.AppSubURL + "/admin/badges")
123+
ctx.Redirect(setting.AppSubURL + "/-/admin/badges")
119124
} else {
120125
ctx.ServerError("GetBadgeUsers", err)
121126
}
@@ -187,7 +192,7 @@ func EditBadgePost(ctx *context.Context) {
187192
log.Trace("Badge updated by admin (%s): %s", ctx.Doer.Name, b.Slug)
188193

189194
ctx.Flash.Success(ctx.Tr("admin.badges.update_success"))
190-
ctx.Redirect(setting.AppSubURL + "/admin/badges/" + url.PathEscape(ctx.PathParam(":badge_slug")))
195+
ctx.Redirect(setting.AppSubURL + "/-/admin/badges/" + url.PathEscape(ctx.PathParam(":badge_slug")))
191196
}
192197

193198
// DeleteBadge response for deleting a badge
@@ -206,20 +211,35 @@ func DeleteBadge(ctx *context.Context) {
206211
log.Trace("Badge deleted by admin (%s): %s", ctx.Doer.Name, b.Slug)
207212

208213
ctx.Flash.Success(ctx.Tr("admin.badges.deletion_success"))
209-
ctx.Redirect(setting.AppSubURL + "/admin/badges")
214+
ctx.Redirect(setting.AppSubURL + "/-/admin/badges")
210215
}
211216

212217
func BadgeUsers(ctx *context.Context) {
213218
ctx.Data["Title"] = ctx.Tr("admin.badges.users_with_badge", ctx.PathParam(":badge_slug"))
214219
ctx.Data["PageIsAdminBadges"] = true
215220

216-
users, _, err := user_model.GetBadgeUsers(ctx, &user_model.Badge{Slug: ctx.PathParam(":badge_slug")})
221+
page := ctx.FormInt("page")
222+
if page <= 0 {
223+
page = 1
224+
}
225+
226+
badge := &user_model.Badge{Slug: ctx.PathParam(":badge_slug")}
227+
opts := &user_model.GetBadgeUsersOptions{
228+
ListOptions: db.ListOptions{
229+
Page: page,
230+
PageSize: setting.UI.User.RepoPagingNum,
231+
},
232+
Badge: badge,
233+
}
234+
users, count, err := user_model.GetBadgeUsers(ctx, opts)
217235
if err != nil {
218236
ctx.ServerError("GetBadgeUsers", err)
219237
return
220238
}
221239

222240
ctx.Data["Users"] = users
241+
ctx.Data["Total"] = count
242+
ctx.Data["Page"] = context.NewPagination(int(count), setting.UI.User.RepoPagingNum, page, 5)
223243

224244
ctx.HTML(http.StatusOK, tplBadgeUsers)
225245
}
@@ -267,8 +287,41 @@ func DeleteBadgeUser(ctx *context.Context) {
267287
ctx.Flash.Success(ctx.Tr("admin.badges.user_remove_success"))
268288
} else {
269289
ctx.Flash.Error("DeleteUser: " + err.Error())
290+
}
291+
292+
ctx.JSONRedirect(fmt.Sprintf("%s/-/admin/badges/%s/users", setting.AppSubURL, ctx.PathParam(":badge_slug")))
293+
}
294+
295+
// ViewBadgeUsers render badge's users page
296+
func ViewBadgeUsers(ctx *context.Context) {
297+
badge, err := user_model.GetBadge(ctx, ctx.PathParam(":slug"))
298+
if err != nil {
299+
ctx.ServerError("GetBadge", err)
270300
return
271301
}
272302

273-
ctx.JSONRedirect(fmt.Sprintf("%s/admin/badges/%s/users", setting.AppSubURL, ctx.PathParam(":badge_slug")))
303+
page := ctx.FormInt("page")
304+
if page <= 0 {
305+
page = 1
306+
}
307+
308+
opts := &user_model.GetBadgeUsersOptions{
309+
ListOptions: db.ListOptions{
310+
Page: page,
311+
PageSize: setting.UI.User.RepoPagingNum,
312+
},
313+
Badge: badge,
314+
}
315+
users, count, err := user_model.GetBadgeUsers(ctx, opts)
316+
if err != nil {
317+
ctx.ServerError("GetBadgeUsers", err)
318+
return
319+
}
320+
321+
ctx.Data["Title"] = badge.Description
322+
ctx.Data["Badge"] = badge
323+
ctx.Data["Users"] = users
324+
ctx.Data["Total"] = count
325+
ctx.Data["Pages"] = context.NewPagination(int(count), setting.UI.User.RepoPagingNum, page, 5)
326+
ctx.HTML(http.StatusOK, tplBadgeUsers)
274327
}

routers/web/explore/badge.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ import (
88

99
"code.gitea.io/gitea/models/db"
1010
user_model "code.gitea.io/gitea/models/user"
11-
"code.gitea.io/gitea/modules/base"
1211
"code.gitea.io/gitea/modules/setting"
12+
"code.gitea.io/gitea/modules/templates"
1313
"code.gitea.io/gitea/services/context"
1414
)
1515

16-
func RenderBadgeSearch(ctx *context.Context, opts *user_model.SearchBadgeOptions, tplName base.TplName) {
16+
func RenderBadgeSearch(ctx *context.Context, opts *user_model.SearchBadgeOptions, tplName templates.TplName) {
1717
// Sitemap index for sitemap paths
1818
opts.Page = int(ctx.PathParamInt64("idx"))
1919
if opts.Page <= 1 {
@@ -68,10 +68,7 @@ func RenderBadgeSearch(ctx *context.Context, opts *user_model.SearchBadgeOptions
6868
ctx.Data["Badges"] = badges
6969

7070
pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5)
71-
pager.SetDefaultParams(ctx)
72-
for paramKey, paramVal := range opts.ExtraParamStrings {
73-
pager.AddParamString(paramKey, paramVal)
74-
}
71+
pager.AddParamFromRequest(ctx.Req)
7572
ctx.Data["Page"] = pager
7673

7774
ctx.HTML(http.StatusOK, tplName)

services/user/badge.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,15 @@ func DeleteBadge(ctx context.Context, b *user_model.Badge) error {
4444

4545
return nil
4646
}
47+
48+
// GetBadgeUsers returns the users that have a specific badge
49+
func GetBadgeUsers(ctx context.Context, badge *user_model.Badge, page, pageSize int) ([]*user_model.User, int64, error) {
50+
opts := &user_model.GetBadgeUsersOptions{
51+
ListOptions: db.ListOptions{
52+
Page: page,
53+
PageSize: pageSize,
54+
},
55+
Badge: badge,
56+
}
57+
return user_model.GetBadgeUsers(ctx, opts)
58+
}

templates/admin/badge/list.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<h4 class="ui top attached header">
44
{{ctx.Locale.Tr "admin.badges.badges_manage_panel"}} ({{ctx.Locale.Tr "admin.total" .Total}})
55
<div class="ui right">
6-
<a class="ui primary tiny button" href="{{AppSubUrl}}/admin/badges/new">{{ctx.Locale.Tr "admin.badges.new_badge"}}</a>
6+
<a class="ui primary tiny button" href="{{AppSubUrl}}/-/admin/badges/new">{{ctx.Locale.Tr "admin.badges.new_badge"}}</a>
77
</div>
88
</h4>
99
<div class="ui attached segment">

templates/admin/badge/users.tmpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
</div>
2727
</div>
2828
{{end}}
29+
{{template "base/paginate" .}}
2930
<div class="ui bottom attached segment">
3031
<form class="ui form" id="search-badge-user-form" action="{{.Link}}" method="post">
3132
{{.CsrfTokenHtml}}

0 commit comments

Comments
 (0)