Skip to content

Commit 7215d73

Browse files
Merge branch 'master' into team-permission-create-repo
2 parents 8e14c34 + 4b3fc74 commit 7215d73

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+383
-46
lines changed

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -438,9 +438,9 @@ js: npm
438438

439439
.PHONY: css
440440
css: npm
441-
npx stylelint public/less
442-
npx lessc --clean-css="--s0 -b" public/less/index.less public/css/index.css
443-
$(foreach file, $(filter-out public/less/themes/_base.less, $(wildcard public/less/themes/*)),npx lessc --clean-css="--s0 -b" public/less/themes/$(notdir $(file)) > public/css/theme-$(notdir $(call strip-suffix,$(file))).css;)
441+
npx stylelint web_src/less
442+
npx lessc --clean-css="--s0 -b" web_src/less/index.less public/css/index.css
443+
$(foreach file, $(filter-out web_src/less/themes/_base.less, $(wildcard web_src/less/themes/*)),npx lessc --clean-css="--s0 -b" web_src/less/themes/$(notdir $(file)) > public/css/theme-$(notdir $(call strip-suffix,$(file))).css;)
444444
npx postcss --use autoprefixer --no-map --replace public/css/*
445445

446446
@diff=$$(git diff public/css/*); \

models/migrations/migrations.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,8 @@ var migrations = []Migration{
271271
// v107 -> v108
272272
NewMigration("Add template options to repository", addTemplateToRepo),
273273
// v108 -> v109
274+
NewMigration("Add comment_id on table notification", addCommentIDOnNotification),
275+
// v109 -> v110
274276
NewMigration("add can_create_org_repo to team", addCanCreateOrgRepoColumnForTeam),
275277
}
276278

models/migrations/v108.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ import (
88
"xorm.io/xorm"
99
)
1010

11-
func addCanCreateOrgRepoColumnForTeam(x *xorm.Engine) error {
12-
type Team struct {
13-
CanCreateOrgRepo bool `xorm:"NOT NULL DEFAULT false"`
11+
func addCommentIDOnNotification(x *xorm.Engine) error {
12+
type Notification struct {
13+
ID int64 `xorm:"pk autoincr"`
14+
CommentID int64
1415
}
1516

16-
return x.Sync2(new(Team))
17+
return x.Sync2(new(Notification))
1718
}

models/migrations/v109.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright 2019 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package migrations
6+
7+
import (
8+
"xorm.io/xorm"
9+
)
10+
11+
func addCanCreateOrgRepoColumnForTeam(x *xorm.Engine) error {
12+
type Team struct {
13+
CanCreateOrgRepo bool `xorm:"NOT NULL DEFAULT false"`
14+
}
15+
16+
return x.Sync2(new(Team))
17+
}

models/notification.go

Lines changed: 230 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@ type Notification struct {
4444
Status NotificationStatus `xorm:"SMALLINT INDEX NOT NULL"`
4545
Source NotificationSource `xorm:"SMALLINT INDEX NOT NULL"`
4646

47-
IssueID int64 `xorm:"INDEX NOT NULL"`
48-
CommitID string `xorm:"INDEX"`
47+
IssueID int64 `xorm:"INDEX NOT NULL"`
48+
CommitID string `xorm:"INDEX"`
49+
CommentID int64
50+
Comment *Comment `xorm:"-"`
4951

5052
UpdatedBy int64 `xorm:"INDEX NOT NULL"`
5153

@@ -58,22 +60,27 @@ type Notification struct {
5860

5961
// CreateOrUpdateIssueNotifications creates an issue notification
6062
// for each watcher, or updates it if already exists
61-
func CreateOrUpdateIssueNotifications(issue *Issue, notificationAuthorID int64) error {
63+
func CreateOrUpdateIssueNotifications(issueID, commentID int64, notificationAuthorID int64) error {
6264
sess := x.NewSession()
6365
defer sess.Close()
6466
if err := sess.Begin(); err != nil {
6567
return err
6668
}
6769

68-
if err := createOrUpdateIssueNotifications(sess, issue, notificationAuthorID); err != nil {
70+
if err := createOrUpdateIssueNotifications(sess, issueID, commentID, notificationAuthorID); err != nil {
6971
return err
7072
}
7173

7274
return sess.Commit()
7375
}
7476

75-
func createOrUpdateIssueNotifications(e Engine, issue *Issue, notificationAuthorID int64) error {
76-
issueWatches, err := getIssueWatchers(e, issue.ID)
77+
func createOrUpdateIssueNotifications(e Engine, issueID, commentID int64, notificationAuthorID int64) error {
78+
issueWatches, err := getIssueWatchers(e, issueID)
79+
if err != nil {
80+
return err
81+
}
82+
83+
issue, err := getIssueByID(e, issueID)
7784
if err != nil {
7885
return err
7986
}
@@ -83,7 +90,7 @@ func createOrUpdateIssueNotifications(e Engine, issue *Issue, notificationAuthor
8390
return err
8491
}
8592

86-
notifications, err := getNotificationsByIssueID(e, issue.ID)
93+
notifications, err := getNotificationsByIssueID(e, issueID)
8794
if err != nil {
8895
return err
8996
}
@@ -102,9 +109,9 @@ func createOrUpdateIssueNotifications(e Engine, issue *Issue, notificationAuthor
102109
alreadyNotified[userID] = struct{}{}
103110

104111
if notificationExists(notifications, issue.ID, userID) {
105-
return updateIssueNotification(e, userID, issue.ID, notificationAuthorID)
112+
return updateIssueNotification(e, userID, issue.ID, commentID, notificationAuthorID)
106113
}
107-
return createIssueNotification(e, userID, issue, notificationAuthorID)
114+
return createIssueNotification(e, userID, issue, commentID, notificationAuthorID)
108115
}
109116

110117
for _, issueWatch := range issueWatches {
@@ -157,12 +164,13 @@ func notificationExists(notifications []*Notification, issueID, userID int64) bo
157164
return false
158165
}
159166

160-
func createIssueNotification(e Engine, userID int64, issue *Issue, updatedByID int64) error {
167+
func createIssueNotification(e Engine, userID int64, issue *Issue, commentID, updatedByID int64) error {
161168
notification := &Notification{
162169
UserID: userID,
163170
RepoID: issue.RepoID,
164171
Status: NotificationStatusUnread,
165172
IssueID: issue.ID,
173+
CommentID: commentID,
166174
UpdatedBy: updatedByID,
167175
}
168176

@@ -176,16 +184,25 @@ func createIssueNotification(e Engine, userID int64, issue *Issue, updatedByID i
176184
return err
177185
}
178186

179-
func updateIssueNotification(e Engine, userID, issueID, updatedByID int64) error {
187+
func updateIssueNotification(e Engine, userID, issueID, commentID, updatedByID int64) error {
180188
notification, err := getIssueNotification(e, userID, issueID)
181189
if err != nil {
182190
return err
183191
}
184192

185-
notification.Status = NotificationStatusUnread
186-
notification.UpdatedBy = updatedByID
193+
// NOTICE: Only update comment id when the before notification on this issue is read, otherwise you may miss some old comments.
194+
// But we need update update_by so that the notification will be reorder
195+
var cols []string
196+
if notification.Status == NotificationStatusRead {
197+
notification.Status = NotificationStatusUnread
198+
notification.CommentID = commentID
199+
cols = []string{"status", "update_by", "comment_id"}
200+
} else {
201+
notification.UpdatedBy = updatedByID
202+
cols = []string{"update_by"}
203+
}
187204

188-
_, err = e.ID(notification.ID).Update(notification)
205+
_, err = e.ID(notification.ID).Cols(cols...).Update(notification)
189206
return err
190207
}
191208

@@ -199,7 +216,7 @@ func getIssueNotification(e Engine, userID, issueID int64) (*Notification, error
199216
}
200217

201218
// NotificationsForUser returns notifications for a given user and status
202-
func NotificationsForUser(user *User, statuses []NotificationStatus, page, perPage int) ([]*Notification, error) {
219+
func NotificationsForUser(user *User, statuses []NotificationStatus, page, perPage int) (NotificationList, error) {
203220
return notificationsForUser(x, user, statuses, page, perPage)
204221
}
205222

@@ -239,6 +256,204 @@ func (n *Notification) GetIssue() (*Issue, error) {
239256
return n.Issue, err
240257
}
241258

259+
// HTMLURL formats a URL-string to the notification
260+
func (n *Notification) HTMLURL() string {
261+
if n.Comment != nil {
262+
return n.Comment.HTMLURL()
263+
}
264+
return n.Issue.HTMLURL()
265+
}
266+
267+
// NotificationList contains a list of notifications
268+
type NotificationList []*Notification
269+
270+
func (nl NotificationList) getPendingRepoIDs() []int64 {
271+
var ids = make(map[int64]struct{}, len(nl))
272+
for _, notification := range nl {
273+
if notification.Repository != nil {
274+
continue
275+
}
276+
if _, ok := ids[notification.RepoID]; !ok {
277+
ids[notification.RepoID] = struct{}{}
278+
}
279+
}
280+
return keysInt64(ids)
281+
}
282+
283+
// LoadRepos loads repositories from database
284+
func (nl NotificationList) LoadRepos() (RepositoryList, error) {
285+
if len(nl) == 0 {
286+
return RepositoryList{}, nil
287+
}
288+
289+
var repoIDs = nl.getPendingRepoIDs()
290+
var repos = make(map[int64]*Repository, len(repoIDs))
291+
var left = len(repoIDs)
292+
for left > 0 {
293+
var limit = defaultMaxInSize
294+
if left < limit {
295+
limit = left
296+
}
297+
rows, err := x.
298+
In("id", repoIDs[:limit]).
299+
Rows(new(Repository))
300+
if err != nil {
301+
return nil, err
302+
}
303+
304+
for rows.Next() {
305+
var repo Repository
306+
err = rows.Scan(&repo)
307+
if err != nil {
308+
rows.Close()
309+
return nil, err
310+
}
311+
312+
repos[repo.ID] = &repo
313+
}
314+
_ = rows.Close()
315+
316+
left -= limit
317+
repoIDs = repoIDs[limit:]
318+
}
319+
320+
var reposList = make(RepositoryList, 0, len(repoIDs))
321+
for _, notification := range nl {
322+
if notification.Repository == nil {
323+
notification.Repository = repos[notification.RepoID]
324+
}
325+
var found bool
326+
for _, r := range reposList {
327+
if r.ID == notification.Repository.ID {
328+
found = true
329+
break
330+
}
331+
}
332+
if !found {
333+
reposList = append(reposList, notification.Repository)
334+
}
335+
}
336+
return reposList, nil
337+
}
338+
339+
func (nl NotificationList) getPendingIssueIDs() []int64 {
340+
var ids = make(map[int64]struct{}, len(nl))
341+
for _, notification := range nl {
342+
if notification.Issue != nil {
343+
continue
344+
}
345+
if _, ok := ids[notification.IssueID]; !ok {
346+
ids[notification.IssueID] = struct{}{}
347+
}
348+
}
349+
return keysInt64(ids)
350+
}
351+
352+
// LoadIssues loads issues from database
353+
func (nl NotificationList) LoadIssues() error {
354+
if len(nl) == 0 {
355+
return nil
356+
}
357+
358+
var issueIDs = nl.getPendingIssueIDs()
359+
var issues = make(map[int64]*Issue, len(issueIDs))
360+
var left = len(issueIDs)
361+
for left > 0 {
362+
var limit = defaultMaxInSize
363+
if left < limit {
364+
limit = left
365+
}
366+
rows, err := x.
367+
In("id", issueIDs[:limit]).
368+
Rows(new(Issue))
369+
if err != nil {
370+
return err
371+
}
372+
373+
for rows.Next() {
374+
var issue Issue
375+
err = rows.Scan(&issue)
376+
if err != nil {
377+
rows.Close()
378+
return err
379+
}
380+
381+
issues[issue.ID] = &issue
382+
}
383+
_ = rows.Close()
384+
385+
left -= limit
386+
issueIDs = issueIDs[limit:]
387+
}
388+
389+
for _, notification := range nl {
390+
if notification.Issue == nil {
391+
notification.Issue = issues[notification.IssueID]
392+
notification.Issue.Repo = notification.Repository
393+
}
394+
}
395+
return nil
396+
}
397+
398+
func (nl NotificationList) getPendingCommentIDs() []int64 {
399+
var ids = make(map[int64]struct{}, len(nl))
400+
for _, notification := range nl {
401+
if notification.CommentID == 0 || notification.Comment != nil {
402+
continue
403+
}
404+
if _, ok := ids[notification.CommentID]; !ok {
405+
ids[notification.CommentID] = struct{}{}
406+
}
407+
}
408+
return keysInt64(ids)
409+
}
410+
411+
// LoadComments loads comments from database
412+
func (nl NotificationList) LoadComments() error {
413+
if len(nl) == 0 {
414+
return nil
415+
}
416+
417+
var commentIDs = nl.getPendingCommentIDs()
418+
var comments = make(map[int64]*Comment, len(commentIDs))
419+
var left = len(commentIDs)
420+
for left > 0 {
421+
var limit = defaultMaxInSize
422+
if left < limit {
423+
limit = left
424+
}
425+
rows, err := x.
426+
In("id", commentIDs[:limit]).
427+
Rows(new(Comment))
428+
if err != nil {
429+
return err
430+
}
431+
432+
for rows.Next() {
433+
var comment Comment
434+
err = rows.Scan(&comment)
435+
if err != nil {
436+
rows.Close()
437+
return err
438+
}
439+
440+
comments[comment.ID] = &comment
441+
}
442+
_ = rows.Close()
443+
444+
left -= limit
445+
commentIDs = commentIDs[limit:]
446+
}
447+
448+
for _, notification := range nl {
449+
if notification.CommentID > 0 && notification.Comment == nil {
450+
notification.Comment = comments[notification.CommentID]
451+
notification.Comment.Issue = notification.Issue
452+
}
453+
}
454+
return nil
455+
}
456+
242457
// GetNotificationCount returns the notification count for user
243458
func GetNotificationCount(user *User, status NotificationStatus) (int64, error) {
244459
return getNotificationCount(x, user, status)

models/notification_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ func TestCreateOrUpdateIssueNotifications(t *testing.T) {
1414
assert.NoError(t, PrepareTestDatabase())
1515
issue := AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue)
1616

17-
assert.NoError(t, CreateOrUpdateIssueNotifications(issue, 2))
17+
assert.NoError(t, CreateOrUpdateIssueNotifications(issue.ID, 0, 2))
1818

1919
// User 9 is inactive, thus notifications for user 1 and 4 are created
2020
notf := AssertExistsAndLoadBean(t, &Notification{UserID: 1, IssueID: issue.ID}).(*Notification)

models/repo_collaboration.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func (repo *Repository) addCollaborator(e Engine, u *User) error {
3434
return err
3535
}
3636

37-
return repo.recalculateUserAccess(e, u.ID)
37+
return repo.recalculateUserAccess(e, u.ID)
3838
}
3939

4040
// AddCollaborator adds new collaboration to a repository with default access mode.

0 commit comments

Comments
 (0)