Skip to content

Commit f94869d

Browse files
authored
Track labels changed on issue view & resolved #542 (#788)
* track labels changed on issue view & resolved #542 * add missing head comment & sort & fix refresh
1 parent d078aa3 commit f94869d

File tree

10 files changed

+366
-195
lines changed

10 files changed

+366
-195
lines changed

models/issue.go

Lines changed: 101 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package models
77
import (
88
"errors"
99
"fmt"
10+
"sort"
1011
"strings"
1112
"time"
1213

@@ -103,11 +104,17 @@ func (issue *Issue) GetPullRequest() (pr *PullRequest, err error) {
103104
return
104105
}
105106

106-
func (issue *Issue) loadAttributes(e Engine) (err error) {
107-
if err := issue.loadRepo(e); err != nil {
108-
return err
107+
func (issue *Issue) loadLabels(e Engine) (err error) {
108+
if issue.Labels == nil {
109+
issue.Labels, err = getLabelsByIssueID(e, issue.ID)
110+
if err != nil {
111+
return fmt.Errorf("getLabelsByIssueID [%d]: %v", issue.ID, err)
112+
}
109113
}
114+
return nil
115+
}
110116

117+
func (issue *Issue) loadPoster(e Engine) (err error) {
111118
if issue.Poster == nil {
112119
issue.Poster, err = getUserByID(e, issue.PosterID)
113120
if err != nil {
@@ -120,12 +127,20 @@ func (issue *Issue) loadAttributes(e Engine) (err error) {
120127
return
121128
}
122129
}
130+
return
131+
}
123132

124-
if issue.Labels == nil {
125-
issue.Labels, err = getLabelsByIssueID(e, issue.ID)
126-
if err != nil {
127-
return fmt.Errorf("getLabelsByIssueID [%d]: %v", issue.ID, err)
128-
}
133+
func (issue *Issue) loadAttributes(e Engine) (err error) {
134+
if err = issue.loadRepo(e); err != nil {
135+
return
136+
}
137+
138+
if err = issue.loadPoster(e); err != nil {
139+
return
140+
}
141+
142+
if err = issue.loadLabels(e); err != nil {
143+
return
129144
}
130145

131146
if issue.Milestone == nil && issue.MilestoneID > 0 {
@@ -289,27 +304,27 @@ func (issue *Issue) sendLabelUpdatedWebhook(doer *User) {
289304
}
290305
}
291306

292-
func (issue *Issue) addLabel(e *xorm.Session, label *Label) error {
293-
return newIssueLabel(e, issue, label)
307+
func (issue *Issue) addLabel(e *xorm.Session, label *Label, doer *User) error {
308+
return newIssueLabel(e, issue, label, doer)
294309
}
295310

296311
// AddLabel adds a new label to the issue.
297312
func (issue *Issue) AddLabel(doer *User, label *Label) error {
298-
if err := NewIssueLabel(issue, label); err != nil {
313+
if err := NewIssueLabel(issue, label, doer); err != nil {
299314
return err
300315
}
301316

302317
issue.sendLabelUpdatedWebhook(doer)
303318
return nil
304319
}
305320

306-
func (issue *Issue) addLabels(e *xorm.Session, labels []*Label) error {
307-
return newIssueLabels(e, issue, labels)
321+
func (issue *Issue) addLabels(e *xorm.Session, labels []*Label, doer *User) error {
322+
return newIssueLabels(e, issue, labels, doer)
308323
}
309324

310325
// AddLabels adds a list of new labels to the issue.
311326
func (issue *Issue) AddLabels(doer *User, labels []*Label) error {
312-
if err := NewIssueLabels(issue, labels); err != nil {
327+
if err := NewIssueLabels(issue, labels, doer); err != nil {
313328
return err
314329
}
315330

@@ -329,8 +344,8 @@ func (issue *Issue) getLabels(e Engine) (err error) {
329344
return nil
330345
}
331346

332-
func (issue *Issue) removeLabel(e *xorm.Session, label *Label) error {
333-
return deleteIssueLabel(e, issue, label)
347+
func (issue *Issue) removeLabel(e *xorm.Session, doer *User, label *Label) error {
348+
return deleteIssueLabel(e, doer, issue, label)
334349
}
335350

336351
// RemoveLabel removes a label from issue by given ID.
@@ -345,21 +360,21 @@ func (issue *Issue) RemoveLabel(doer *User, label *Label) error {
345360
return ErrLabelNotExist{}
346361
}
347362

348-
if err := DeleteIssueLabel(issue, label); err != nil {
363+
if err := DeleteIssueLabel(issue, doer, label); err != nil {
349364
return err
350365
}
351366

352367
issue.sendLabelUpdatedWebhook(doer)
353368
return nil
354369
}
355370

356-
func (issue *Issue) clearLabels(e *xorm.Session) (err error) {
371+
func (issue *Issue) clearLabels(e *xorm.Session, doer *User) (err error) {
357372
if err = issue.getLabels(e); err != nil {
358373
return fmt.Errorf("getLabels: %v", err)
359374
}
360375

361376
for i := range issue.Labels {
362-
if err = issue.removeLabel(e, issue.Labels[i]); err != nil {
377+
if err = issue.removeLabel(e, doer, issue.Labels[i]); err != nil {
363378
return fmt.Errorf("removeLabel: %v", err)
364379
}
365380
}
@@ -386,7 +401,7 @@ func (issue *Issue) ClearLabels(doer *User) (err error) {
386401
return ErrLabelNotExist{}
387402
}
388403

389-
if err = issue.clearLabels(sess); err != nil {
404+
if err = issue.clearLabels(sess, doer); err != nil {
390405
return err
391406
}
392407

@@ -417,19 +432,75 @@ func (issue *Issue) ClearLabels(doer *User) (err error) {
417432
return nil
418433
}
419434

435+
type labelSorter []*Label
436+
437+
func (ts labelSorter) Len() int {
438+
return len([]*Label(ts))
439+
}
440+
441+
func (ts labelSorter) Less(i, j int) bool {
442+
return []*Label(ts)[i].ID < []*Label(ts)[j].ID
443+
}
444+
445+
func (ts labelSorter) Swap(i, j int) {
446+
[]*Label(ts)[i], []*Label(ts)[j] = []*Label(ts)[j], []*Label(ts)[i]
447+
}
448+
420449
// ReplaceLabels removes all current labels and add new labels to the issue.
421450
// Triggers appropriate WebHooks, if any.
422-
func (issue *Issue) ReplaceLabels(labels []*Label) (err error) {
451+
func (issue *Issue) ReplaceLabels(labels []*Label, doer *User) (err error) {
423452
sess := x.NewSession()
424453
defer sessionRelease(sess)
425454
if err = sess.Begin(); err != nil {
426455
return err
427456
}
428457

429-
if err = issue.clearLabels(sess); err != nil {
430-
return fmt.Errorf("clearLabels: %v", err)
431-
} else if err = issue.addLabels(sess, labels); err != nil {
432-
return fmt.Errorf("addLabels: %v", err)
458+
if err = issue.loadLabels(sess); err != nil {
459+
return err
460+
}
461+
462+
sort.Sort(labelSorter(labels))
463+
sort.Sort(labelSorter(issue.Labels))
464+
465+
var toAdd, toRemove []*Label
466+
for _, l := range labels {
467+
var exist bool
468+
for _, oriLabel := range issue.Labels {
469+
if oriLabel.ID == l.ID {
470+
exist = true
471+
break
472+
}
473+
}
474+
if !exist {
475+
toAdd = append(toAdd, l)
476+
}
477+
}
478+
479+
for _, oriLabel := range issue.Labels {
480+
var exist bool
481+
for _, l := range labels {
482+
if oriLabel.ID == l.ID {
483+
exist = true
484+
break
485+
}
486+
}
487+
if !exist {
488+
toRemove = append(toRemove, oriLabel)
489+
}
490+
}
491+
492+
if len(toAdd) > 0 {
493+
if err = issue.addLabels(sess, toAdd, doer); err != nil {
494+
return fmt.Errorf("addLabels: %v", err)
495+
}
496+
}
497+
498+
if len(toRemove) > 0 {
499+
for _, l := range toRemove {
500+
if err = issue.removeLabel(sess, doer, l); err != nil {
501+
return fmt.Errorf("removeLabel: %v", err)
502+
}
503+
}
433504
}
434505

435506
return sess.Commit()
@@ -731,13 +802,17 @@ func newIssue(e *xorm.Session, opts NewIssueOptions) (err error) {
731802
return fmt.Errorf("find all labels [label_ids: %v]: %v", opts.LableIDs, err)
732803
}
733804

805+
if err = opts.Issue.loadPoster(e); err != nil {
806+
return err
807+
}
808+
734809
for _, label := range labels {
735810
// Silently drop invalid labels.
736811
if label.RepoID != opts.Repo.ID {
737812
continue
738813
}
739814

740-
if err = opts.Issue.addLabel(e, label); err != nil {
815+
if err = opts.Issue.addLabel(e, label, opts.Issue.Poster); err != nil {
741816
return fmt.Errorf("addLabel [id: %d]: %v", label.ID, err)
742817
}
743818
}

models/issue_comment.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ const (
3636
CommentTypeCommentRef
3737
// Reference from a pull request
3838
CommentTypePullRef
39+
// Labels changed
40+
CommentTypeLabel
3941
)
4042

4143
// CommentTag defines comment tag type
@@ -57,6 +59,8 @@ type Comment struct {
5759
Poster *User `xorm:"-"`
5860
IssueID int64 `xorm:"INDEX"`
5961
CommitID int64
62+
LabelID int64
63+
Label *Label `xorm:"-"`
6064
Line int64
6165
Content string `xorm:"TEXT"`
6266
RenderedContent string `xorm:"-"`
@@ -185,6 +189,21 @@ func (c *Comment) EventTag() string {
185189
return "event-" + com.ToStr(c.ID)
186190
}
187191

192+
// LoadLabel if comment.Type is CommentTypeLabel, then load Label
193+
func (c *Comment) LoadLabel() error {
194+
var label Label
195+
has, err := x.ID(c.LabelID).Get(&label)
196+
if err != nil {
197+
return err
198+
} else if !has {
199+
return ErrLabelNotExist{
200+
LabelID: c.LabelID,
201+
}
202+
}
203+
c.Label = &label
204+
return nil
205+
}
206+
188207
// MailParticipants sends new comment emails to repository watchers
189208
// and mentioned people.
190209
func (c *Comment) MailParticipants(e Engine, opType ActionType, issue *Issue) (err error) {
@@ -209,11 +228,16 @@ func (c *Comment) MailParticipants(e Engine, opType ActionType, issue *Issue) (e
209228
}
210229

211230
func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err error) {
231+
var LabelID int64
232+
if opts.Label != nil {
233+
LabelID = opts.Label.ID
234+
}
212235
comment := &Comment{
213236
Type: opts.Type,
214237
PosterID: opts.Doer.ID,
215238
Poster: opts.Doer,
216239
IssueID: opts.Issue.ID,
240+
LabelID: LabelID,
217241
CommitID: opts.CommitID,
218242
CommitSHA: opts.CommitSHA,
219243
Line: opts.LineNum,
@@ -223,6 +247,10 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
223247
return nil, err
224248
}
225249

250+
if err = opts.Repo.getOwner(e); err != nil {
251+
return nil, err
252+
}
253+
226254
// Compose comment action, could be plain comment, close or reopen issue/pull request.
227255
// This object will be used to notify watchers in the end of function.
228256
act := &Action{
@@ -324,12 +352,28 @@ func createStatusComment(e *xorm.Session, doer *User, repo *Repository, issue *I
324352
})
325353
}
326354

355+
func createLabelComment(e *xorm.Session, doer *User, repo *Repository, issue *Issue, label *Label, add bool) (*Comment, error) {
356+
var content string
357+
if add {
358+
content = "1"
359+
}
360+
return createComment(e, &CreateCommentOptions{
361+
Type: CommentTypeLabel,
362+
Doer: doer,
363+
Repo: repo,
364+
Issue: issue,
365+
Label: label,
366+
Content: content,
367+
})
368+
}
369+
327370
// CreateCommentOptions defines options for creating comment
328371
type CreateCommentOptions struct {
329372
Type CommentType
330373
Doer *User
331374
Repo *Repository
332375
Issue *Issue
376+
Label *Label
333377

334378
CommitID int64
335379
CommitSHA string

0 commit comments

Comments
 (0)