Skip to content

Commit 4cb00a8

Browse files
author
Oleg Komarov
committed
UI refinements
1 parent 6b47750 commit 4cb00a8

File tree

5 files changed

+119
-42
lines changed

5 files changed

+119
-42
lines changed

models/user/spamreport.go

+37-15
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,24 @@ import (
2020
type SpamReportStatusType int
2121

2222
const (
23-
SpamReportStatusTypePending = iota // 0
24-
SpamReportStatusTypeProcessing // 1
25-
SpamReportStatusTypeProcessed // 2
26-
SpamReportStatusTypeDismissed // 3
23+
SpamReportStatusTypePending = iota // 0
24+
SpamReportStatusTypeLocked // 1
25+
SpamReportStatusTypeProcessed // 2
26+
SpamReportStatusTypeDismissed // 3
2727
)
2828

2929
func (t SpamReportStatusType) String() string {
3030
switch t {
3131
case SpamReportStatusTypePending:
32-
return "Pending"
33-
case SpamReportStatusTypeProcessing:
34-
return "Processing"
32+
return "pending"
33+
case SpamReportStatusTypeLocked:
34+
return "locked"
3535
case SpamReportStatusTypeProcessed:
36-
return "Processed"
36+
return "processed"
3737
case SpamReportStatusTypeDismissed:
38-
return "Dismissed"
39-
default:
40-
panic(fmt.Sprintf("unknown SpamReportStatusType: %d", t))
38+
return "dismissed"
4139
}
40+
return "unknown"
4241
}
4342

4443
type SpamReport struct {
@@ -58,26 +57,49 @@ func init() {
5857
db.RegisterModel(new(SpamReport))
5958
}
6059

61-
type ListSpamReportResults struct {
60+
type ListSpamReportsOptions struct {
61+
db.ListOptions
62+
Status SpamReportStatusType
63+
}
64+
65+
type ListSpamReportsResults struct {
6266
ID int64
67+
CreatedUnix timeutil.TimeStamp
68+
UpdatedUnix timeutil.TimeStamp
6369
Status SpamReportStatusType
6470
UserName string
6571
ReporterName string
6672
}
6773

68-
func ListSpamReports(ctx context.Context, opts *db.ListOptions) ([]*ListSpamReportResults, int64, error) {
74+
func ListSpamReports(ctx context.Context, opts *ListSpamReportsOptions) ([]*ListSpamReportsResults, int64, error) {
6975
opts.SetDefaultValues()
7076
count, err := db.GetEngine(ctx).Count(new(SpamReport))
7177
if err != nil {
7278
return nil, 0, fmt.Errorf("Count: %w", err)
7379
}
74-
spamReports := make([]*ListSpamReportResults, 0, opts.PageSize)
80+
spamReports := make([]*ListSpamReportsResults, 0, opts.PageSize)
7581
err = db.GetEngine(ctx).Table("user_spamreport").
76-
Select("user_spamreport.id, user_spamreport.status, user.name as user_name, reporter.name as reporter_name").
82+
Select("user_spamreport.id, user_spamreport.created_unix, user_spamreport.updated_unix, user_spamreport.status, user.name as user_name, reporter.name as reporter_name").
7783
Join("LEFT", "`user`", "`user`.id = user_spamreport.user_id").
7884
Join("LEFT", "`user` as reporter", "`reporter`.id = user_spamreport.reporter_id").
85+
Where("status = ?", opts.Status).
7986
Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).
8087
Find(&spamReports)
8188

8289
return spamReports, count, err
8390
}
91+
92+
type SpamReportStatusCounts struct {
93+
Count int64
94+
Status SpamReportStatusType
95+
}
96+
97+
func GetSpamReportStatusCounts(ctx context.Context) ([]*SpamReportStatusCounts, error) {
98+
statusCounts := make([]*SpamReportStatusCounts, 0, 4) // 4 status types
99+
err := db.GetEngine(ctx).Table("user_spamreport").
100+
Select("count(*) as count, status").
101+
GroupBy("status").
102+
Find(&statusCounts)
103+
104+
return statusCounts, err
105+
}

options/locale/locale_en-US.ini

+2
Original file line numberDiff line numberDiff line change
@@ -3061,6 +3061,8 @@ emails.delete_primary_email_error = You can not delete the primary email.
30613061
spamreports.spamreport_manage_panel = Spam Report Management
30623062
spamreports.user = Reported for spam
30633063
spamreports.reporter = Reporter
3064+
spamreports.created = Created
3065+
spamreports.updated = Updated
30643066
spamreports.status = Report Status
30653067
30663068
orgs.org_manage_panel = Organization Management

routers/web/admin/spamreports.go

+24-8
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,33 @@ const (
2121
tplSpamReports base.TplName = "admin/spamreports/list"
2222
)
2323

24-
// Emails show all emails
24+
// SpamReports show spam reports
2525
func SpamReports(ctx *context.Context) {
2626
ctx.Data["Title"] = ctx.Tr("admin.spamreports")
2727
ctx.Data["PageIsSpamReports"] = true
2828

29-
opts := &db.ListOptions{
30-
PageSize: setting.UI.Admin.UserPagingNum,
31-
Page: ctx.FormInt("page"),
29+
var (
30+
count int64
31+
err error
32+
filterStatus user_model.SpamReportStatusType
33+
)
34+
35+
// When no value is specified Pending (status=0) reports are shown,
36+
// which luckily makes sense as a default view.
37+
filterStatus = user_model.SpamReportStatusType(ctx.FormInt("status"))
38+
ctx.Data["FilterStatus"] = filterStatus
39+
opts := &user_model.ListSpamReportsOptions{
40+
ListOptions: db.ListOptions{
41+
PageSize: setting.UI.Admin.UserPagingNum,
42+
Page: ctx.FormInt("page"),
43+
},
44+
Status: filterStatus,
3245
}
3346

3447
if opts.Page <= 1 {
3548
opts.Page = 1
3649
}
3750

38-
var (
39-
count int64
40-
err error
41-
)
4251
spamReports, count, err := user_model.ListSpamReports(ctx, opts)
4352
if err != nil {
4453
ctx.ServerError("SpamReports", err)
@@ -52,6 +61,13 @@ func SpamReports(ctx *context.Context) {
5261
pager.SetDefaultParams(ctx)
5362
ctx.Data["Page"] = pager
5463

64+
statusCounts, err := user_model.GetSpamReportStatusCounts(ctx)
65+
if err != nil {
66+
ctx.ServerError("GetSpamReportStatusCounts", err)
67+
return
68+
}
69+
ctx.Data["StatusCounts"] = statusCounts
70+
5571
ctx.HTML(http.StatusOK, tplSpamReports)
5672
}
5773

services/user/spamreport.go

+7-7
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,9 @@ func ProcessSpamReports(ctx context.Context, doer *user_model.User, spamReportID
6565
for _, spamReport := range spamReports {
6666
id := spamReport.ID
6767
count, err := db.GetEngine(ctx).ID(id).And("status = ?", user_model.SpamReportStatusTypePending).
68-
Update(&user_model.SpamReport{Status: user_model.SpamReportStatusTypeProcessing})
68+
Update(&user_model.SpamReport{Status: user_model.SpamReportStatusTypeLocked})
6969
if err != nil {
70-
return fmt.Errorf("failed to set SpamReport.Status to Processing for id=%d: %w", id, err)
70+
return fmt.Errorf("failed to set SpamReport.Status to locked for id=%d: %w", id, err)
7171
}
7272
if count < 1 {
7373
log.Info("Skipping SpamReport id=%d, status wasn't Pending", id)
@@ -85,7 +85,7 @@ func ProcessSpamReports(ctx context.Context, doer *user_model.User, spamReportID
8585
}
8686

8787
// Clean up everything and update report status if there were no errors.
88-
// On failure the transaction will be rolled back, and the report will be stuck in Processing status.
88+
// On failure the transaction will be rolled back, and the report will be stuck in locked status.
8989
log.Info("Processing SpamReport id=%d for user %s", id, user.Name)
9090
err = db.WithTx(ctx, func(ctx context.Context) error {
9191
// UpdateUser and UpdateAuth to clean the profile and prohibit logins.
@@ -150,14 +150,14 @@ func ProcessSpamReports(ctx context.Context, doer *user_model.User, spamReportID
150150
}
151151
}
152152

153-
// Everything is cleaned up, marking the spam report as Processed.
154-
count, err = db.GetEngine(ctx).ID(id).And("status = ?", user_model.SpamReportStatusTypeProcessing).
153+
// Everything is cleaned up, marking the spam report as processed.
154+
count, err = db.GetEngine(ctx).ID(id).And("status = ?", user_model.SpamReportStatusTypeLocked).
155155
Update(&user_model.SpamReport{Status: user_model.SpamReportStatusTypeProcessed})
156156
if err != nil {
157-
return fmt.Errorf("failed to set SpamReport.Status to Processed for id=%d: %w", id, err)
157+
return fmt.Errorf("failed to set SpamReport.Status to processed for id=%d: %w", id, err)
158158
}
159159
if count < 1 {
160-
return fmt.Errorf("SpamReport id=%d status wasn't Processing, rolling back the transaction", id)
160+
return fmt.Errorf("SpamReport id=%d status wasn't locked, rolling back the transaction", id)
161161
}
162162
return nil
163163
})

templates/admin/spamreports/list.tmpl

+49-12
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{{template "admin/layout_head" (dict "ctxData" . "pageClass" "admin user")}}
2+
23
<script>
34
document.addEventListener('DOMContentLoaded', function(event) {
45
const selectAll = document.getElementsByClassName('select-all')[0];
@@ -10,40 +11,76 @@ document.addEventListener('DOMContentLoaded', function(event) {
1011
});
1112
});
1213
</script>
13-
<div class="admin-setting-content">
14-
<h4 class="ui top attached header">
15-
{{ctx.Locale.Tr "admin.spamreports.spamreport_manage_panel"}} ({{ctx.Locale.Tr "admin.total" .Total}})
16-
</h4>
14+
<div class="admin-setting-content">
15+
<h4 class="ui top attached header">
16+
{{ctx.Locale.Tr "admin.spamreports.spamreport_manage_panel"}}
17+
<div class="small-menu-items ui compact tiny menu">
18+
{{range .StatusCounts}}
19+
<a class="{{if eq .Status $.FilterStatus}}active {{end}}item flex-text-inline" href="?status={{printf "%d" .Status}}">
20+
{{.Count}} {{.Status}}
21+
</a>
22+
{{end}}
23+
</div>
24+
</h4>
25+
{{if .SpamReports}}
1726
<form method="post">
1827
{{.CsrfTokenHtml}}
1928
<div class="ui attached table segment">
2029
<table class="ui very basic striped table unstackable">
2130
<thead>
2231
<tr>
23-
<th><input type="checkbox" class="select-all" /></th>
32+
{{if eq $.FilterStatus 0}}
33+
<th><input type="checkbox" class="select-all" /></th>
34+
{{end}}
2435
<th>{{ctx.Locale.Tr "admin.spamreports.user"}}</th>
2536
<th>{{ctx.Locale.Tr "admin.spamreports.reporter"}}</th>
37+
<th>{{ctx.Locale.Tr "admin.spamreports.created"}}</th>
38+
<th>{{ctx.Locale.Tr "admin.spamreports.updated"}}</th>
2639
<th>{{ctx.Locale.Tr "admin.spamreports.status"}}</th>
2740
</tr>
2841
</thead>
2942
<tbody>
3043
{{range .SpamReports}}
3144
<tr>
32-
<td><input type="checkbox" class="managed-by-select-all" name="spamreport_id" value="{{.ID}}" /></td>
45+
{{if eq $.FilterStatus 0}}
46+
<td><input type="checkbox" class="managed-by-select-all" name="spamreport_id" value="{{.ID}}" /></td>
47+
{{end}}
3348
<td><a href="{{AppSubUrl}}/{{.UserName | PathEscape}}">{{.UserName}}</a></td>
3449
<td><a href="{{AppSubUrl}}/{{.ReporterName | PathEscape}}">{{.ReporterName}}</a></td>
35-
<td>{{.Status}}</td>
50+
<td title="{{DateUtils.FullTime .CreatedUnix}}">{{DateUtils.TimeSince .CreatedUnix}}</td>
51+
<td title="{{DateUtils.FullTime .UpdatedUnix}}">{{DateUtils.TimeSince .UpdatedUnix}}</td>
52+
<td>
53+
{{if eq .Status 0}}
54+
{{svg "octicon-clock" 16 "tw-mr-2 text primary"}}
55+
{{end}}
56+
{{if eq .Status 1}}
57+
{{svg "octicon-lock" 16 "tw-mr-2 text grey"}}
58+
{{end}}
59+
{{if eq .Status 2}}
60+
{{svg "octicon-check" 16 "tw-mr-2 text green"}}
61+
{{end}}
62+
{{if eq .Status 3}}
63+
{{svg "octicon-trash" 16 "tw-mr-2 text red"}}
64+
{{end}}
65+
{{.Status}}
66+
</td>
3667
</tr>
3768
{{end}}
3869
</tbody>
3970
</table>
4071
</div>
41-
<br />
42-
<button type="submit" name="action" value="process" class="ui submit primary button btn-submit">Process</button>
43-
<button type="submit" name="action" value="dismiss" class="ui submit primary button btn-submit red">Dismiss</button>
72+
{{if eq $.FilterStatus 0}}
73+
<br />
74+
<button type="submit" name="action" value="process" class="ui submit primary button btn-submit">Process</button>
75+
<button type="submit" name="action" value="dismiss" class="ui submit primary button btn-submit red">Dismiss</button>
76+
{{end}}
4477
</form>
45-
4678
{{template "base/paginate" .}}
47-
</div>
79+
{{else}}
80+
<div class="ui info message align center">
81+
No {{$.FilterStatus}} reports.
82+
</div>
83+
{{end}}
84+
</div>
4885

4986
{{template "admin/layout_footer" .}}

0 commit comments

Comments
 (0)