summaryrefslogtreecommitdiffstats
path: root/models
diff options
context:
space:
mode:
authorLunny Xiao <xiaolunwen@gmail.com>2017-01-30 20:46:45 +0800
committerGitHub <noreply@github.com>2017-01-30 20:46:45 +0800
commitf94869d2d19afd7110a0b996a7e6da339fb4e161 (patch)
tree8fe91beb0dcfce8e78515ebd525ee1decb633e68 /models
parentd078aa30d6e6f40978ea68e9ae6eee53dc028ada (diff)
downloadgitea-f94869d2d19afd7110a0b996a7e6da339fb4e161.tar.gz
gitea-f94869d2d19afd7110a0b996a7e6da339fb4e161.zip
Track labels changed on issue view & resolved #542 (#788)
* track labels changed on issue view & resolved #542 * add missing head comment & sort & fix refresh
Diffstat (limited to 'models')
-rw-r--r--models/issue.go127
-rw-r--r--models/issue_comment.go44
-rw-r--r--models/issue_label.go36
3 files changed, 171 insertions, 36 deletions
diff --git a/models/issue.go b/models/issue.go
index 6d557ad4ef..0102656f02 100644
--- a/models/issue.go
+++ b/models/issue.go
@@ -7,6 +7,7 @@ package models
import (
"errors"
"fmt"
+ "sort"
"strings"
"time"
@@ -103,11 +104,17 @@ func (issue *Issue) GetPullRequest() (pr *PullRequest, err error) {
return
}
-func (issue *Issue) loadAttributes(e Engine) (err error) {
- if err := issue.loadRepo(e); err != nil {
- return err
+func (issue *Issue) loadLabels(e Engine) (err error) {
+ if issue.Labels == nil {
+ issue.Labels, err = getLabelsByIssueID(e, issue.ID)
+ if err != nil {
+ return fmt.Errorf("getLabelsByIssueID [%d]: %v", issue.ID, err)
+ }
}
+ return nil
+}
+func (issue *Issue) loadPoster(e Engine) (err error) {
if issue.Poster == nil {
issue.Poster, err = getUserByID(e, issue.PosterID)
if err != nil {
@@ -120,12 +127,20 @@ func (issue *Issue) loadAttributes(e Engine) (err error) {
return
}
}
+ return
+}
- if issue.Labels == nil {
- issue.Labels, err = getLabelsByIssueID(e, issue.ID)
- if err != nil {
- return fmt.Errorf("getLabelsByIssueID [%d]: %v", issue.ID, err)
- }
+func (issue *Issue) loadAttributes(e Engine) (err error) {
+ if err = issue.loadRepo(e); err != nil {
+ return
+ }
+
+ if err = issue.loadPoster(e); err != nil {
+ return
+ }
+
+ if err = issue.loadLabels(e); err != nil {
+ return
}
if issue.Milestone == nil && issue.MilestoneID > 0 {
@@ -289,13 +304,13 @@ func (issue *Issue) sendLabelUpdatedWebhook(doer *User) {
}
}
-func (issue *Issue) addLabel(e *xorm.Session, label *Label) error {
- return newIssueLabel(e, issue, label)
+func (issue *Issue) addLabel(e *xorm.Session, label *Label, doer *User) error {
+ return newIssueLabel(e, issue, label, doer)
}
// AddLabel adds a new label to the issue.
func (issue *Issue) AddLabel(doer *User, label *Label) error {
- if err := NewIssueLabel(issue, label); err != nil {
+ if err := NewIssueLabel(issue, label, doer); err != nil {
return err
}
@@ -303,13 +318,13 @@ func (issue *Issue) AddLabel(doer *User, label *Label) error {
return nil
}
-func (issue *Issue) addLabels(e *xorm.Session, labels []*Label) error {
- return newIssueLabels(e, issue, labels)
+func (issue *Issue) addLabels(e *xorm.Session, labels []*Label, doer *User) error {
+ return newIssueLabels(e, issue, labels, doer)
}
// AddLabels adds a list of new labels to the issue.
func (issue *Issue) AddLabels(doer *User, labels []*Label) error {
- if err := NewIssueLabels(issue, labels); err != nil {
+ if err := NewIssueLabels(issue, labels, doer); err != nil {
return err
}
@@ -329,8 +344,8 @@ func (issue *Issue) getLabels(e Engine) (err error) {
return nil
}
-func (issue *Issue) removeLabel(e *xorm.Session, label *Label) error {
- return deleteIssueLabel(e, issue, label)
+func (issue *Issue) removeLabel(e *xorm.Session, doer *User, label *Label) error {
+ return deleteIssueLabel(e, doer, issue, label)
}
// RemoveLabel removes a label from issue by given ID.
@@ -345,7 +360,7 @@ func (issue *Issue) RemoveLabel(doer *User, label *Label) error {
return ErrLabelNotExist{}
}
- if err := DeleteIssueLabel(issue, label); err != nil {
+ if err := DeleteIssueLabel(issue, doer, label); err != nil {
return err
}
@@ -353,13 +368,13 @@ func (issue *Issue) RemoveLabel(doer *User, label *Label) error {
return nil
}
-func (issue *Issue) clearLabels(e *xorm.Session) (err error) {
+func (issue *Issue) clearLabels(e *xorm.Session, doer *User) (err error) {
if err = issue.getLabels(e); err != nil {
return fmt.Errorf("getLabels: %v", err)
}
for i := range issue.Labels {
- if err = issue.removeLabel(e, issue.Labels[i]); err != nil {
+ if err = issue.removeLabel(e, doer, issue.Labels[i]); err != nil {
return fmt.Errorf("removeLabel: %v", err)
}
}
@@ -386,7 +401,7 @@ func (issue *Issue) ClearLabels(doer *User) (err error) {
return ErrLabelNotExist{}
}
- if err = issue.clearLabels(sess); err != nil {
+ if err = issue.clearLabels(sess, doer); err != nil {
return err
}
@@ -417,19 +432,75 @@ func (issue *Issue) ClearLabels(doer *User) (err error) {
return nil
}
+type labelSorter []*Label
+
+func (ts labelSorter) Len() int {
+ return len([]*Label(ts))
+}
+
+func (ts labelSorter) Less(i, j int) bool {
+ return []*Label(ts)[i].ID < []*Label(ts)[j].ID
+}
+
+func (ts labelSorter) Swap(i, j int) {
+ []*Label(ts)[i], []*Label(ts)[j] = []*Label(ts)[j], []*Label(ts)[i]
+}
+
// ReplaceLabels removes all current labels and add new labels to the issue.
// Triggers appropriate WebHooks, if any.
-func (issue *Issue) ReplaceLabels(labels []*Label) (err error) {
+func (issue *Issue) ReplaceLabels(labels []*Label, doer *User) (err error) {
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
- if err = issue.clearLabels(sess); err != nil {
- return fmt.Errorf("clearLabels: %v", err)
- } else if err = issue.addLabels(sess, labels); err != nil {
- return fmt.Errorf("addLabels: %v", err)
+ if err = issue.loadLabels(sess); err != nil {
+ return err
+ }
+
+ sort.Sort(labelSorter(labels))
+ sort.Sort(labelSorter(issue.Labels))
+
+ var toAdd, toRemove []*Label
+ for _, l := range labels {
+ var exist bool
+ for _, oriLabel := range issue.Labels {
+ if oriLabel.ID == l.ID {
+ exist = true
+ break
+ }
+ }
+ if !exist {
+ toAdd = append(toAdd, l)
+ }
+ }
+
+ for _, oriLabel := range issue.Labels {
+ var exist bool
+ for _, l := range labels {
+ if oriLabel.ID == l.ID {
+ exist = true
+ break
+ }
+ }
+ if !exist {
+ toRemove = append(toRemove, oriLabel)
+ }
+ }
+
+ if len(toAdd) > 0 {
+ if err = issue.addLabels(sess, toAdd, doer); err != nil {
+ return fmt.Errorf("addLabels: %v", err)
+ }
+ }
+
+ if len(toRemove) > 0 {
+ for _, l := range toRemove {
+ if err = issue.removeLabel(sess, doer, l); err != nil {
+ return fmt.Errorf("removeLabel: %v", err)
+ }
+ }
}
return sess.Commit()
@@ -731,13 +802,17 @@ func newIssue(e *xorm.Session, opts NewIssueOptions) (err error) {
return fmt.Errorf("find all labels [label_ids: %v]: %v", opts.LableIDs, err)
}
+ if err = opts.Issue.loadPoster(e); err != nil {
+ return err
+ }
+
for _, label := range labels {
// Silently drop invalid labels.
if label.RepoID != opts.Repo.ID {
continue
}
- if err = opts.Issue.addLabel(e, label); err != nil {
+ if err = opts.Issue.addLabel(e, label, opts.Issue.Poster); err != nil {
return fmt.Errorf("addLabel [id: %d]: %v", label.ID, err)
}
}
diff --git a/models/issue_comment.go b/models/issue_comment.go
index bab002fcad..be7044a8e7 100644
--- a/models/issue_comment.go
+++ b/models/issue_comment.go
@@ -36,6 +36,8 @@ const (
CommentTypeCommentRef
// Reference from a pull request
CommentTypePullRef
+ // Labels changed
+ CommentTypeLabel
)
// CommentTag defines comment tag type
@@ -57,6 +59,8 @@ type Comment struct {
Poster *User `xorm:"-"`
IssueID int64 `xorm:"INDEX"`
CommitID int64
+ LabelID int64
+ Label *Label `xorm:"-"`
Line int64
Content string `xorm:"TEXT"`
RenderedContent string `xorm:"-"`
@@ -185,6 +189,21 @@ func (c *Comment) EventTag() string {
return "event-" + com.ToStr(c.ID)
}
+// LoadLabel if comment.Type is CommentTypeLabel, then load Label
+func (c *Comment) LoadLabel() error {
+ var label Label
+ has, err := x.ID(c.LabelID).Get(&label)
+ if err != nil {
+ return err
+ } else if !has {
+ return ErrLabelNotExist{
+ LabelID: c.LabelID,
+ }
+ }
+ c.Label = &label
+ return nil
+}
+
// MailParticipants sends new comment emails to repository watchers
// and mentioned people.
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
}
func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err error) {
+ var LabelID int64
+ if opts.Label != nil {
+ LabelID = opts.Label.ID
+ }
comment := &Comment{
Type: opts.Type,
PosterID: opts.Doer.ID,
Poster: opts.Doer,
IssueID: opts.Issue.ID,
+ LabelID: LabelID,
CommitID: opts.CommitID,
CommitSHA: opts.CommitSHA,
Line: opts.LineNum,
@@ -223,6 +247,10 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
return nil, err
}
+ if err = opts.Repo.getOwner(e); err != nil {
+ return nil, err
+ }
+
// Compose comment action, could be plain comment, close or reopen issue/pull request.
// This object will be used to notify watchers in the end of function.
act := &Action{
@@ -324,12 +352,28 @@ func createStatusComment(e *xorm.Session, doer *User, repo *Repository, issue *I
})
}
+func createLabelComment(e *xorm.Session, doer *User, repo *Repository, issue *Issue, label *Label, add bool) (*Comment, error) {
+ var content string
+ if add {
+ content = "1"
+ }
+ return createComment(e, &CreateCommentOptions{
+ Type: CommentTypeLabel,
+ Doer: doer,
+ Repo: repo,
+ Issue: issue,
+ Label: label,
+ Content: content,
+ })
+}
+
// CreateCommentOptions defines options for creating comment
type CreateCommentOptions struct {
Type CommentType
Doer *User
Repo *Repository
Issue *Issue
+ Label *Label
CommitID int64
CommitSHA string
diff --git a/models/issue_label.go b/models/issue_label.go
index 0e1c6d6c4e..02397f146f 100644
--- a/models/issue_label.go
+++ b/models/issue_label.go
@@ -276,7 +276,7 @@ func HasIssueLabel(issueID, labelID int64) bool {
return hasIssueLabel(x, issueID, labelID)
}
-func newIssueLabel(e *xorm.Session, issue *Issue, label *Label) (err error) {
+func newIssueLabel(e *xorm.Session, issue *Issue, label *Label, doer *User) (err error) {
if _, err = e.Insert(&IssueLabel{
IssueID: issue.ID,
LabelID: label.ID,
@@ -284,6 +284,14 @@ func newIssueLabel(e *xorm.Session, issue *Issue, label *Label) (err error) {
return err
}
+ if err = issue.loadRepo(e); err != nil {
+ return
+ }
+
+ if _, err = createLabelComment(e, doer, issue.Repo, issue, label, true); err != nil {
+ return err
+ }
+
label.NumIssues++
if issue.IsClosed {
label.NumClosedIssues++
@@ -292,7 +300,7 @@ func newIssueLabel(e *xorm.Session, issue *Issue, label *Label) (err error) {
}
// NewIssueLabel creates a new issue-label relation.
-func NewIssueLabel(issue *Issue, label *Label) (err error) {
+func NewIssueLabel(issue *Issue, label *Label, doer *User) (err error) {
if HasIssueLabel(issue.ID, label.ID) {
return nil
}
@@ -303,20 +311,20 @@ func NewIssueLabel(issue *Issue, label *Label) (err error) {
return err
}
- if err = newIssueLabel(sess, issue, label); err != nil {
+ if err = newIssueLabel(sess, issue, label, doer); err != nil {
return err
}
return sess.Commit()
}
-func newIssueLabels(e *xorm.Session, issue *Issue, labels []*Label) (err error) {
+func newIssueLabels(e *xorm.Session, issue *Issue, labels []*Label, doer *User) (err error) {
for i := range labels {
if hasIssueLabel(e, issue.ID, labels[i].ID) {
continue
}
- if err = newIssueLabel(e, issue, labels[i]); err != nil {
+ if err = newIssueLabel(e, issue, labels[i], doer); err != nil {
return fmt.Errorf("newIssueLabel: %v", err)
}
}
@@ -325,14 +333,14 @@ func newIssueLabels(e *xorm.Session, issue *Issue, labels []*Label) (err error)
}
// NewIssueLabels creates a list of issue-label relations.
-func NewIssueLabels(issue *Issue, labels []*Label) (err error) {
+func NewIssueLabels(issue *Issue, labels []*Label, doer *User) (err error) {
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
- if err = newIssueLabels(sess, issue, labels); err != nil {
+ if err = newIssueLabels(sess, issue, labels, doer); err != nil {
return err
}
@@ -352,7 +360,7 @@ func GetIssueLabels(issueID int64) ([]*IssueLabel, error) {
return getIssueLabels(x, issueID)
}
-func deleteIssueLabel(e *xorm.Session, issue *Issue, label *Label) (err error) {
+func deleteIssueLabel(e *xorm.Session, doer *User, issue *Issue, label *Label) (err error) {
if _, err = e.Delete(&IssueLabel{
IssueID: issue.ID,
LabelID: label.ID,
@@ -360,6 +368,14 @@ func deleteIssueLabel(e *xorm.Session, issue *Issue, label *Label) (err error) {
return err
}
+ if err = issue.loadRepo(e); err != nil {
+ return
+ }
+
+ if _, err = createLabelComment(e, doer, issue.Repo, issue, label, false); err != nil {
+ return err
+ }
+
label.NumIssues--
if issue.IsClosed {
label.NumClosedIssues--
@@ -368,14 +384,14 @@ func deleteIssueLabel(e *xorm.Session, issue *Issue, label *Label) (err error) {
}
// DeleteIssueLabel deletes issue-label relation.
-func DeleteIssueLabel(issue *Issue, label *Label) (err error) {
+func DeleteIssueLabel(issue *Issue, doer *User, label *Label) (err error) {
sess := x.NewSession()
defer sessionRelease(sess)
if err = sess.Begin(); err != nil {
return err
}
- if err = deleteIssueLabel(sess, issue, label); err != nil {
+ if err = deleteIssueLabel(sess, doer, issue, label); err != nil {
return err
}