-
Notifications
You must be signed in to change notification settings - Fork 1
/
alerts_controller.go
203 lines (189 loc) · 6.12 KB
/
alerts_controller.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
package webui
import (
"net/http"
"strconv"
"github.com/APTrust/registry/common"
"github.com/APTrust/registry/forms"
"github.com/APTrust/registry/pgmodels"
"github.com/gin-gonic/gin"
)
type AlertReadResult struct {
Succeeded []int64 `json:"succeeded"`
Failed []int64 `json:"failed"`
Error string `json:"error"`
}
func NewAlertReadResult() *AlertReadResult {
return &AlertReadResult{
Succeeded: make([]int64, 0),
Failed: make([]int64, 0),
}
}
// AlertShow shows the AlertView with the specified id for the specified
// user. Note that the AlertView table contains multiple entries for each
// Alert ID. But each alert goes only once to each recipient, so the
// combination of AlertID + UserID (recipient) is unique.
// GET /alerts/show/:id
func AlertShow(c *gin.Context) {
req := NewRequest(c)
alertView, err := alertLoad(req)
if AbortIfError(c, err) {
return
}
// Automatically mark this alert as read if it's being viewed
// by the intended user. Sys admin can also view this alert, but
// it should not be marked read unless viewed by intended recipient.
if req.CurrentUser.ID == alertView.UserID {
err = alertMarkAsRead(alertView.ID, alertView.UserID)
if AbortIfError(c, err) {
return
}
}
req.TemplateData["alert"] = alertView
c.HTML(http.StatusOK, "alerts/show.html", req.TemplateData)
}
// AlertIndex shows list of alerts for the logged-in user, unless
// that user is sys admin, who can see all alerts.
// GET /alerts
func AlertIndex(c *gin.Context) {
req := NewRequest(c)
var alerts []*pgmodels.AlertView
err := req.LoadResourceList(&alerts, "created_at", "desc", forms.NewAlertFilterForm)
if AbortIfError(c, err) {
return
}
c.HTML(http.StatusOK, "alerts/index.html", req.TemplateData)
}
// AlertsMarkAsReadXHR handles an AJAX request to mark alerts as read.
// The PUT body should contain a csrf token and param called "id__in"
// whose value is a list of alert ids. This will mark each of those
// alerts as read by the current user.
//
// Because this method acts only on alerts belonging to the current
// user, it's impossible for one user to mark another user's alerts
// as read. (Unless the user logs in under someone else's account.)
//
// PUT /alerts/mark_as_read
func AlertMarkAsReadXHR(c *gin.Context) {
markAlerts(c, "read")
}
// AlertsMarkAsUnreadXHR handles an AJAX request to mark alerts as unread.
// The PUT body should contain a csrf token and param called "id__in"
// whose value is a list of alert ids. This will mark each of those
// alerts as unread by the current user.
//
// Because this method acts only on alerts belonging to the current
// user, it's impossible for one user to mark another user's alerts
// as unread. (Unless the user logs in under someone else's account.)
//
// PUT /alerts/mark_as_unread
func AlertMarkAsUnreadXHR(c *gin.Context) {
markAlerts(c, "unread")
}
// AlertsMarkAllAsRead marks all of a user's unread alerts as read.
//
//
// Because this method acts only on alerts belonging to the current
// user, it's impossible for one user to mark another user's alerts
// as read. (Unless the user logs in under someone else's account.)
//
// POST /alerts/mark_all_as_read
func AlertMarkAllAsRead(c *gin.Context) {
req := NewRequest(c)
status := http.StatusFound
result := NewAlertReadResult()
query := pgmodels.NewQuery().
Columns("id").
Where("user_id", "=", req.CurrentUser.ID).
IsNull("read_at")
alertViews, err := pgmodels.AlertViewSelect(query)
if err != nil {
status = StatusCodeForError(err)
result.Error = err.Error()
c.JSON(status, result)
return
}
for _, alertView := range alertViews {
err = alertMarkAsRead(alertView.ID, req.CurrentUser.ID)
if err != nil {
result.Failed = append(result.Failed, alertView.ID)
status = StatusCodeForError(err)
result.Error += err.Error() + ", "
} else {
result.Succeeded = append(result.Succeeded, alertView.ID)
}
}
redirectUrl := c.Request.Referer()
if redirectUrl == "" {
redirectUrl = "/alerts"
}
c.Redirect(status, redirectUrl)
}
func markAlerts(c *gin.Context, markThemHow string) {
req := NewRequest(c)
status := http.StatusOK
result := NewAlertReadResult()
ids := getAlertIDsFromForm(c)
if len(ids) == 0 {
status = http.StatusBadRequest
result.Error = "You must specify one or more alerts."
c.JSON(status, result)
return
}
for _, id := range ids {
var err error
if markThemHow == "read" {
err = alertMarkAsRead(id, req.CurrentUser.ID)
} else {
err = alertMarkAsUnread(id, req.CurrentUser.ID)
}
if err != nil {
status = StatusCodeForError(err)
result.Failed = append(result.Failed, id)
result.Error += err.Error() + ", "
} else {
result.Succeeded = append(result.Succeeded, id)
}
}
c.JSON(status, result)
}
func alertLoad(req *Request) (*pgmodels.AlertView, error) {
recipientID, err := strconv.ParseInt(req.GinContext.Param("user_id"), 10, 64)
if err != nil {
return nil, err
}
if !req.CurrentUser.IsAdmin() && recipientID != req.CurrentUser.ID {
aptContext := common.Context()
aptContext.Log.Warn().Msgf("User %d illegally tried to access alert %d belonging to user %d. Permission was denied.", req.CurrentUser.ID, req.Auth.ResourceID, recipientID)
return nil, common.ErrPermissionDenied
}
return pgmodels.AlertViewForUser(req.Auth.ResourceID, recipientID)
}
func alertMarkAsRead(alertID, userID int64) error {
alert, err := pgmodels.AlertByID(alertID)
if err != nil {
common.Context().Log.Error().Msgf("Error marking alert %d as read for user %d: %v", alertID, userID, err)
return err
}
return alert.MarkAsRead(userID)
}
func alertMarkAsUnread(alertID, userID int64) error {
alert, err := pgmodels.AlertByID(alertID)
if err != nil {
common.Context().Log.Error().Msgf("Error marking alert %d as unread for user %d: %v", alertID, userID, err)
return err
}
return alert.MarkAsUnread(userID)
}
func getAlertIDsFromForm(c *gin.Context) []int64 {
idParams := c.PostFormArray("id__in")
// Use zero instead of len(idParams) on the longshot
// chance that some ids fail to parse.
ids := make([]int64, 0)
for _, idParam := range idParams {
id, err := strconv.ParseInt(idParam, 10, 64)
if err == nil && id > 0 {
ids = append(ids, id)
}
}
return ids
}