* track labels changed on issue view & resolved #542 * add missing head comment & sort & fix refresh
This commit is contained in:
		
					parent
					
						
							
								d078aa30d6
							
						
					
				
			
			
				commit
				
					
						f94869d2d1
					
				
			
		
					 10 changed files with 366 additions and 195 deletions
				
			
		
							
								
								
									
										125
									
								
								models/issue.go
									
										
									
									
									
								
							
							
						
						
									
										125
									
								
								models/issue.go
									
										
									
									
									
								
							| 
						 | 
					@ -7,6 +7,7 @@ package models
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"sort"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -103,11 +104,17 @@ func (issue *Issue) GetPullRequest() (pr *PullRequest, err error) {
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (issue *Issue) loadAttributes(e Engine) (err error) {
 | 
					func (issue *Issue) loadLabels(e Engine) (err error) {
 | 
				
			||||||
	if err := issue.loadRepo(e); err != nil {
 | 
						if issue.Labels == nil {
 | 
				
			||||||
		return err
 | 
							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 {
 | 
						if issue.Poster == nil {
 | 
				
			||||||
		issue.Poster, err = getUserByID(e, issue.PosterID)
 | 
							issue.Poster, err = getUserByID(e, issue.PosterID)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
| 
						 | 
					@ -120,12 +127,20 @@ func (issue *Issue) loadAttributes(e Engine) (err error) {
 | 
				
			||||||
			return
 | 
								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 {
 | 
						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 {
 | 
					func (issue *Issue) addLabel(e *xorm.Session, label *Label, doer *User) error {
 | 
				
			||||||
	return newIssueLabel(e, issue, label)
 | 
						return newIssueLabel(e, issue, label, doer)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AddLabel adds a new label to the issue.
 | 
					// AddLabel adds a new label to the issue.
 | 
				
			||||||
func (issue *Issue) AddLabel(doer *User, label *Label) error {
 | 
					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
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -303,13 +318,13 @@ func (issue *Issue) AddLabel(doer *User, label *Label) error {
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (issue *Issue) addLabels(e *xorm.Session, labels []*Label) error {
 | 
					func (issue *Issue) addLabels(e *xorm.Session, labels []*Label, doer *User) error {
 | 
				
			||||||
	return newIssueLabels(e, issue, labels)
 | 
						return newIssueLabels(e, issue, labels, doer)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AddLabels adds a list of new labels to the issue.
 | 
					// AddLabels adds a list of new labels to the issue.
 | 
				
			||||||
func (issue *Issue) AddLabels(doer *User, labels []*Label) error {
 | 
					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
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -329,8 +344,8 @@ func (issue *Issue) getLabels(e Engine) (err error) {
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (issue *Issue) removeLabel(e *xorm.Session, label *Label) error {
 | 
					func (issue *Issue) removeLabel(e *xorm.Session, doer *User, label *Label) error {
 | 
				
			||||||
	return deleteIssueLabel(e, issue, label)
 | 
						return deleteIssueLabel(e, doer, issue, label)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RemoveLabel removes a label from issue by given ID.
 | 
					// RemoveLabel removes a label from issue by given ID.
 | 
				
			||||||
| 
						 | 
					@ -345,7 +360,7 @@ func (issue *Issue) RemoveLabel(doer *User, label *Label) error {
 | 
				
			||||||
		return ErrLabelNotExist{}
 | 
							return ErrLabelNotExist{}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := DeleteIssueLabel(issue, label); err != nil {
 | 
						if err := DeleteIssueLabel(issue, doer, label); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -353,13 +368,13 @@ func (issue *Issue) RemoveLabel(doer *User, label *Label) error {
 | 
				
			||||||
	return nil
 | 
						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 {
 | 
						if err = issue.getLabels(e); err != nil {
 | 
				
			||||||
		return fmt.Errorf("getLabels: %v", err)
 | 
							return fmt.Errorf("getLabels: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for i := range issue.Labels {
 | 
						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)
 | 
								return fmt.Errorf("removeLabel: %v", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -386,7 +401,7 @@ func (issue *Issue) ClearLabels(doer *User) (err error) {
 | 
				
			||||||
		return ErrLabelNotExist{}
 | 
							return ErrLabelNotExist{}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err = issue.clearLabels(sess); err != nil {
 | 
						if err = issue.clearLabels(sess, doer); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -417,20 +432,76 @@ func (issue *Issue) ClearLabels(doer *User) (err error) {
 | 
				
			||||||
	return nil
 | 
						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.
 | 
					// ReplaceLabels removes all current labels and add new labels to the issue.
 | 
				
			||||||
// Triggers appropriate WebHooks, if any.
 | 
					// 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()
 | 
						sess := x.NewSession()
 | 
				
			||||||
	defer sessionRelease(sess)
 | 
						defer sessionRelease(sess)
 | 
				
			||||||
	if err = sess.Begin(); err != nil {
 | 
						if err = sess.Begin(); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err = issue.clearLabels(sess); err != nil {
 | 
						if err = issue.loadLabels(sess); err != nil {
 | 
				
			||||||
		return fmt.Errorf("clearLabels: %v", err)
 | 
							return err
 | 
				
			||||||
	} else if err = issue.addLabels(sess, labels); err != nil {
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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)
 | 
								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()
 | 
						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)
 | 
								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 {
 | 
							for _, label := range labels {
 | 
				
			||||||
			// Silently drop invalid labels.
 | 
								// Silently drop invalid labels.
 | 
				
			||||||
			if label.RepoID != opts.Repo.ID {
 | 
								if label.RepoID != opts.Repo.ID {
 | 
				
			||||||
				continue
 | 
									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)
 | 
									return fmt.Errorf("addLabel [id: %d]: %v", label.ID, err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,6 +36,8 @@ const (
 | 
				
			||||||
	CommentTypeCommentRef
 | 
						CommentTypeCommentRef
 | 
				
			||||||
	// Reference from a pull request
 | 
						// Reference from a pull request
 | 
				
			||||||
	CommentTypePullRef
 | 
						CommentTypePullRef
 | 
				
			||||||
 | 
						// Labels changed
 | 
				
			||||||
 | 
						CommentTypeLabel
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CommentTag defines comment tag type
 | 
					// CommentTag defines comment tag type
 | 
				
			||||||
| 
						 | 
					@ -57,6 +59,8 @@ type Comment struct {
 | 
				
			||||||
	Poster          *User `xorm:"-"`
 | 
						Poster          *User `xorm:"-"`
 | 
				
			||||||
	IssueID         int64 `xorm:"INDEX"`
 | 
						IssueID         int64 `xorm:"INDEX"`
 | 
				
			||||||
	CommitID        int64
 | 
						CommitID        int64
 | 
				
			||||||
 | 
						LabelID         int64
 | 
				
			||||||
 | 
						Label           *Label `xorm:"-"`
 | 
				
			||||||
	Line            int64
 | 
						Line            int64
 | 
				
			||||||
	Content         string `xorm:"TEXT"`
 | 
						Content         string `xorm:"TEXT"`
 | 
				
			||||||
	RenderedContent string `xorm:"-"`
 | 
						RenderedContent string `xorm:"-"`
 | 
				
			||||||
| 
						 | 
					@ -185,6 +189,21 @@ func (c *Comment) EventTag() string {
 | 
				
			||||||
	return "event-" + com.ToStr(c.ID)
 | 
						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
 | 
					// MailParticipants sends new comment emails to repository watchers
 | 
				
			||||||
// and mentioned people.
 | 
					// and mentioned people.
 | 
				
			||||||
func (c *Comment) MailParticipants(e Engine, opType ActionType, issue *Issue) (err error) {
 | 
					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) {
 | 
					func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err error) {
 | 
				
			||||||
 | 
						var LabelID int64
 | 
				
			||||||
 | 
						if opts.Label != nil {
 | 
				
			||||||
 | 
							LabelID = opts.Label.ID
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	comment := &Comment{
 | 
						comment := &Comment{
 | 
				
			||||||
		Type:      opts.Type,
 | 
							Type:      opts.Type,
 | 
				
			||||||
		PosterID:  opts.Doer.ID,
 | 
							PosterID:  opts.Doer.ID,
 | 
				
			||||||
		Poster:    opts.Doer,
 | 
							Poster:    opts.Doer,
 | 
				
			||||||
		IssueID:   opts.Issue.ID,
 | 
							IssueID:   opts.Issue.ID,
 | 
				
			||||||
 | 
							LabelID:   LabelID,
 | 
				
			||||||
		CommitID:  opts.CommitID,
 | 
							CommitID:  opts.CommitID,
 | 
				
			||||||
		CommitSHA: opts.CommitSHA,
 | 
							CommitSHA: opts.CommitSHA,
 | 
				
			||||||
		Line:      opts.LineNum,
 | 
							Line:      opts.LineNum,
 | 
				
			||||||
| 
						 | 
					@ -223,6 +247,10 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
 | 
				
			||||||
		return nil, 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.
 | 
						// 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.
 | 
						// This object will be used to notify watchers in the end of function.
 | 
				
			||||||
	act := &Action{
 | 
						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
 | 
					// CreateCommentOptions defines options for creating comment
 | 
				
			||||||
type CreateCommentOptions struct {
 | 
					type CreateCommentOptions struct {
 | 
				
			||||||
	Type  CommentType
 | 
						Type  CommentType
 | 
				
			||||||
	Doer  *User
 | 
						Doer  *User
 | 
				
			||||||
	Repo  *Repository
 | 
						Repo  *Repository
 | 
				
			||||||
	Issue *Issue
 | 
						Issue *Issue
 | 
				
			||||||
 | 
						Label *Label
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	CommitID    int64
 | 
						CommitID    int64
 | 
				
			||||||
	CommitSHA   string
 | 
						CommitSHA   string
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -276,7 +276,7 @@ func HasIssueLabel(issueID, labelID int64) bool {
 | 
				
			||||||
	return hasIssueLabel(x, issueID, labelID)
 | 
						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{
 | 
						if _, err = e.Insert(&IssueLabel{
 | 
				
			||||||
		IssueID: issue.ID,
 | 
							IssueID: issue.ID,
 | 
				
			||||||
		LabelID: label.ID,
 | 
							LabelID: label.ID,
 | 
				
			||||||
| 
						 | 
					@ -284,6 +284,14 @@ func newIssueLabel(e *xorm.Session, issue *Issue, label *Label) (err error) {
 | 
				
			||||||
		return err
 | 
							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++
 | 
						label.NumIssues++
 | 
				
			||||||
	if issue.IsClosed {
 | 
						if issue.IsClosed {
 | 
				
			||||||
		label.NumClosedIssues++
 | 
							label.NumClosedIssues++
 | 
				
			||||||
| 
						 | 
					@ -292,7 +300,7 @@ func newIssueLabel(e *xorm.Session, issue *Issue, label *Label) (err error) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewIssueLabel creates a new issue-label relation.
 | 
					// 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) {
 | 
						if HasIssueLabel(issue.ID, label.ID) {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -303,20 +311,20 @@ func NewIssueLabel(issue *Issue, label *Label) (err error) {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err = newIssueLabel(sess, issue, label); err != nil {
 | 
						if err = newIssueLabel(sess, issue, label, doer); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return sess.Commit()
 | 
						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 {
 | 
						for i := range labels {
 | 
				
			||||||
		if hasIssueLabel(e, issue.ID, labels[i].ID) {
 | 
							if hasIssueLabel(e, issue.ID, labels[i].ID) {
 | 
				
			||||||
			continue
 | 
								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)
 | 
								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.
 | 
					// 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()
 | 
						sess := x.NewSession()
 | 
				
			||||||
	defer sessionRelease(sess)
 | 
						defer sessionRelease(sess)
 | 
				
			||||||
	if err = sess.Begin(); err != nil {
 | 
						if err = sess.Begin(); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err = newIssueLabels(sess, issue, labels); err != nil {
 | 
						if err = newIssueLabels(sess, issue, labels, doer); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -352,7 +360,7 @@ func GetIssueLabels(issueID int64) ([]*IssueLabel, error) {
 | 
				
			||||||
	return getIssueLabels(x, issueID)
 | 
						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{
 | 
						if _, err = e.Delete(&IssueLabel{
 | 
				
			||||||
		IssueID: issue.ID,
 | 
							IssueID: issue.ID,
 | 
				
			||||||
		LabelID: label.ID,
 | 
							LabelID: label.ID,
 | 
				
			||||||
| 
						 | 
					@ -360,6 +368,14 @@ func deleteIssueLabel(e *xorm.Session, issue *Issue, label *Label) (err error) {
 | 
				
			||||||
		return err
 | 
							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--
 | 
						label.NumIssues--
 | 
				
			||||||
	if issue.IsClosed {
 | 
						if issue.IsClosed {
 | 
				
			||||||
		label.NumClosedIssues--
 | 
							label.NumClosedIssues--
 | 
				
			||||||
| 
						 | 
					@ -368,14 +384,14 @@ func deleteIssueLabel(e *xorm.Session, issue *Issue, label *Label) (err error) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DeleteIssueLabel deletes issue-label relation.
 | 
					// 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()
 | 
						sess := x.NewSession()
 | 
				
			||||||
	defer sessionRelease(sess)
 | 
						defer sessionRelease(sess)
 | 
				
			||||||
	if err = sess.Begin(); err != nil {
 | 
						if err = sess.Begin(); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err = deleteIssueLabel(sess, issue, label); err != nil {
 | 
						if err = deleteIssueLabel(sess, doer, issue, label); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -541,6 +541,8 @@ issues.label_templates.info = There aren't any labels yet. You can click on the
 | 
				
			||||||
issues.label_templates.helper = Select a label set
 | 
					issues.label_templates.helper = Select a label set
 | 
				
			||||||
issues.label_templates.use = Use this label set
 | 
					issues.label_templates.use = Use this label set
 | 
				
			||||||
issues.label_templates.fail_to_load_file = Failed to load label template file '%s': %v
 | 
					issues.label_templates.fail_to_load_file = Failed to load label template file '%s': %v
 | 
				
			||||||
 | 
					issues.add_label_at = `added the <div class="ui label" style="color: %s; background-color: %s">%s</div> label %s`
 | 
				
			||||||
 | 
					issues.remove_label_at = `removed the <div class="ui label" style="color: %s; background-color: %s">%s</div> label %s`
 | 
				
			||||||
issues.open_tab = %d Open
 | 
					issues.open_tab = %d Open
 | 
				
			||||||
issues.close_tab = %d Closed
 | 
					issues.close_tab = %d Closed
 | 
				
			||||||
issues.filter_label = Label
 | 
					issues.filter_label = Label
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -501,6 +501,8 @@ issues.label_templates.info=此仓库还未创建任何标签,您可以通过
 | 
				
			||||||
issues.label_templates.helper=选择标签模板
 | 
					issues.label_templates.helper=选择标签模板
 | 
				
			||||||
issues.label_templates.use=加载标签模板
 | 
					issues.label_templates.use=加载标签模板
 | 
				
			||||||
issues.label_templates.fail_to_load_file=加载标签模板文件 '%s' 时发生错误:%v
 | 
					issues.label_templates.fail_to_load_file=加载标签模板文件 '%s' 时发生错误:%v
 | 
				
			||||||
 | 
					issues.add_label_at = ` %[4]s 添加了标签 <div class="ui label" style="color: %[1]s; background-color: %[2]s">%[3]s</div>`
 | 
				
			||||||
 | 
					issues.remove_label_at = ` %[4]s 删除了标签 <div class="ui label" style="color: %[1]s; background-color: %[2]s">%[3]s</div>`
 | 
				
			||||||
issues.open_tab=%d 个开启中
 | 
					issues.open_tab=%d 个开启中
 | 
				
			||||||
issues.close_tab=%d 个已关闭
 | 
					issues.close_tab=%d 个已关闭
 | 
				
			||||||
issues.filter_label=标签筛选
 | 
					issues.filter_label=标签筛选
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -108,6 +108,10 @@ function initCommentForm() {
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $('.select-label').dropdown('setting', 'onHide', function(){
 | 
				
			||||||
 | 
					        location.reload();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    $labelMenu.find('.item:not(.no-select)').click(function () {
 | 
					    $labelMenu.find('.item:not(.no-select)').click(function () {
 | 
				
			||||||
        if ($(this).hasClass('checked')) {
 | 
					        if ($(this).hasClass('checked')) {
 | 
				
			||||||
            $(this).removeClass('checked');
 | 
					            $(this).removeClass('checked');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -98,7 +98,7 @@ func DeleteIssueLabel(ctx *context.APIContext) {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := models.DeleteIssueLabel(issue, label); err != nil {
 | 
						if err := models.DeleteIssueLabel(issue, ctx.User, label); err != nil {
 | 
				
			||||||
		ctx.Error(500, "DeleteIssueLabel", err)
 | 
							ctx.Error(500, "DeleteIssueLabel", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -129,7 +129,7 @@ func ReplaceIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := issue.ReplaceLabels(labels); err != nil {
 | 
						if err := issue.ReplaceLabels(labels, ctx.User); err != nil {
 | 
				
			||||||
		ctx.Error(500, "ReplaceLabels", err)
 | 
							ctx.Error(500, "ReplaceLabels", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,8 +34,6 @@ const (
 | 
				
			||||||
	tplIssueNew  base.TplName = "repo/issue/new"
 | 
						tplIssueNew  base.TplName = "repo/issue/new"
 | 
				
			||||||
	tplIssueView base.TplName = "repo/issue/view"
 | 
						tplIssueView base.TplName = "repo/issue/view"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tplLabels base.TplName = "repo/issue/labels"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	tplMilestone     base.TplName = "repo/issue/milestones"
 | 
						tplMilestone     base.TplName = "repo/issue/milestones"
 | 
				
			||||||
	tplMilestoneNew  base.TplName = "repo/issue/milestone_new"
 | 
						tplMilestoneNew  base.TplName = "repo/issue/milestone_new"
 | 
				
			||||||
	tplMilestoneEdit base.TplName = "repo/issue/milestone_edit"
 | 
						tplMilestoneEdit base.TplName = "repo/issue/milestone_edit"
 | 
				
			||||||
| 
						 | 
					@ -86,21 +84,6 @@ func MustAllowPulls(ctx *context.Context) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RetrieveLabels find all the labels of a repository
 | 
					 | 
				
			||||||
func RetrieveLabels(ctx *context.Context) {
 | 
					 | 
				
			||||||
	labels, err := models.GetLabelsByRepoID(ctx.Repo.Repository.ID, ctx.Query("sort"))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		ctx.Handle(500, "RetrieveLabels.GetLabels", err)
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for _, l := range labels {
 | 
					 | 
				
			||||||
		l.CalOpenIssues()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	ctx.Data["Labels"] = labels
 | 
					 | 
				
			||||||
	ctx.Data["NumLabels"] = len(labels)
 | 
					 | 
				
			||||||
	ctx.Data["SortType"] = ctx.Query("sort")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Issues render issues page
 | 
					// Issues render issues page
 | 
				
			||||||
func Issues(ctx *context.Context) {
 | 
					func Issues(ctx *context.Context) {
 | 
				
			||||||
	isPullList := ctx.Params(":type") == "pulls"
 | 
						isPullList := ctx.Params(":type") == "pulls"
 | 
				
			||||||
| 
						 | 
					@ -629,6 +612,11 @@ func ViewIssue(ctx *context.Context) {
 | 
				
			||||||
			if !isAdded && !issue.IsPoster(comment.Poster.ID) {
 | 
								if !isAdded && !issue.IsPoster(comment.Poster.ID) {
 | 
				
			||||||
				participants = append(participants, comment.Poster)
 | 
									participants = append(participants, comment.Poster)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
							} else if comment.Type == models.CommentTypeLabel {
 | 
				
			||||||
 | 
								if err = comment.LoadLabel(); err != nil {
 | 
				
			||||||
 | 
									ctx.Handle(500, "LoadLabel", err)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -723,48 +711,6 @@ func UpdateIssueContent(ctx *context.Context) {
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UpdateIssueLabel change issue's labels
 | 
					 | 
				
			||||||
func UpdateIssueLabel(ctx *context.Context) {
 | 
					 | 
				
			||||||
	issue := getActionIssue(ctx)
 | 
					 | 
				
			||||||
	if ctx.Written() {
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if ctx.Query("action") == "clear" {
 | 
					 | 
				
			||||||
		if err := issue.ClearLabels(ctx.User); err != nil {
 | 
					 | 
				
			||||||
			ctx.Handle(500, "ClearLabels", err)
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		isAttach := ctx.Query("action") == "attach"
 | 
					 | 
				
			||||||
		label, err := models.GetLabelByID(ctx.QueryInt64("id"))
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			if models.IsErrLabelNotExist(err) {
 | 
					 | 
				
			||||||
				ctx.Error(404, "GetLabelByID")
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				ctx.Handle(500, "GetLabelByID", err)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if isAttach && !issue.HasLabel(label.ID) {
 | 
					 | 
				
			||||||
			if err = issue.AddLabel(ctx.User, label); err != nil {
 | 
					 | 
				
			||||||
				ctx.Handle(500, "AddLabel", err)
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		} else if !isAttach && issue.HasLabel(label.ID) {
 | 
					 | 
				
			||||||
			if err = issue.RemoveLabel(ctx.User, label); err != nil {
 | 
					 | 
				
			||||||
				ctx.Handle(500, "RemoveLabel", err)
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ctx.JSON(200, map[string]interface{}{
 | 
					 | 
				
			||||||
		"ok": true,
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// UpdateIssueMilestone change issue's milestone
 | 
					// UpdateIssueMilestone change issue's milestone
 | 
				
			||||||
func UpdateIssueMilestone(ctx *context.Context) {
 | 
					func UpdateIssueMilestone(ctx *context.Context) {
 | 
				
			||||||
	issue := getActionIssue(ctx)
 | 
						issue := getActionIssue(ctx)
 | 
				
			||||||
| 
						 | 
					@ -966,103 +912,6 @@ func DeleteComment(ctx *context.Context) {
 | 
				
			||||||
	ctx.Status(200)
 | 
						ctx.Status(200)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Labels render issue's labels page
 | 
					 | 
				
			||||||
func Labels(ctx *context.Context) {
 | 
					 | 
				
			||||||
	ctx.Data["Title"] = ctx.Tr("repo.labels")
 | 
					 | 
				
			||||||
	ctx.Data["PageIsIssueList"] = true
 | 
					 | 
				
			||||||
	ctx.Data["PageIsLabels"] = true
 | 
					 | 
				
			||||||
	ctx.Data["RequireMinicolors"] = true
 | 
					 | 
				
			||||||
	ctx.Data["LabelTemplates"] = models.LabelTemplates
 | 
					 | 
				
			||||||
	ctx.HTML(200, tplLabels)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// InitializeLabels init labels for a repository
 | 
					 | 
				
			||||||
func InitializeLabels(ctx *context.Context, form auth.InitializeLabelsForm) {
 | 
					 | 
				
			||||||
	if ctx.HasError() {
 | 
					 | 
				
			||||||
		ctx.Redirect(ctx.Repo.RepoLink + "/labels")
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	list, err := models.GetLabelTemplateFile(form.TemplateName)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		ctx.Flash.Error(ctx.Tr("repo.issues.label_templates.fail_to_load_file", form.TemplateName, err))
 | 
					 | 
				
			||||||
		ctx.Redirect(ctx.Repo.RepoLink + "/labels")
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	labels := make([]*models.Label, len(list))
 | 
					 | 
				
			||||||
	for i := 0; i < len(list); i++ {
 | 
					 | 
				
			||||||
		labels[i] = &models.Label{
 | 
					 | 
				
			||||||
			RepoID: ctx.Repo.Repository.ID,
 | 
					 | 
				
			||||||
			Name:   list[i][0],
 | 
					 | 
				
			||||||
			Color:  list[i][1],
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if err := models.NewLabels(labels...); err != nil {
 | 
					 | 
				
			||||||
		ctx.Handle(500, "NewLabels", err)
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	ctx.Redirect(ctx.Repo.RepoLink + "/labels")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// NewLabel create new label for repository
 | 
					 | 
				
			||||||
func NewLabel(ctx *context.Context, form auth.CreateLabelForm) {
 | 
					 | 
				
			||||||
	ctx.Data["Title"] = ctx.Tr("repo.labels")
 | 
					 | 
				
			||||||
	ctx.Data["PageIsLabels"] = true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if ctx.HasError() {
 | 
					 | 
				
			||||||
		ctx.Flash.Error(ctx.Data["ErrorMsg"].(string))
 | 
					 | 
				
			||||||
		ctx.Redirect(ctx.Repo.RepoLink + "/labels")
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	l := &models.Label{
 | 
					 | 
				
			||||||
		RepoID: ctx.Repo.Repository.ID,
 | 
					 | 
				
			||||||
		Name:   form.Title,
 | 
					 | 
				
			||||||
		Color:  form.Color,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if err := models.NewLabels(l); err != nil {
 | 
					 | 
				
			||||||
		ctx.Handle(500, "NewLabel", err)
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	ctx.Redirect(ctx.Repo.RepoLink + "/labels")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// UpdateLabel update a label's name and color
 | 
					 | 
				
			||||||
func UpdateLabel(ctx *context.Context, form auth.CreateLabelForm) {
 | 
					 | 
				
			||||||
	l, err := models.GetLabelByID(form.ID)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		switch {
 | 
					 | 
				
			||||||
		case models.IsErrLabelNotExist(err):
 | 
					 | 
				
			||||||
			ctx.Error(404)
 | 
					 | 
				
			||||||
		default:
 | 
					 | 
				
			||||||
			ctx.Handle(500, "UpdateLabel", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	l.Name = form.Title
 | 
					 | 
				
			||||||
	l.Color = form.Color
 | 
					 | 
				
			||||||
	if err := models.UpdateLabel(l); err != nil {
 | 
					 | 
				
			||||||
		ctx.Handle(500, "UpdateLabel", err)
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	ctx.Redirect(ctx.Repo.RepoLink + "/labels")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// DeleteLabel delete a label
 | 
					 | 
				
			||||||
func DeleteLabel(ctx *context.Context) {
 | 
					 | 
				
			||||||
	if err := models.DeleteLabel(ctx.Repo.Repository.ID, ctx.QueryInt64("id")); err != nil {
 | 
					 | 
				
			||||||
		ctx.Flash.Error("DeleteLabel: " + err.Error())
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		ctx.Flash.Success(ctx.Tr("repo.issues.label_deletion_success"))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ctx.JSON(200, map[string]interface{}{
 | 
					 | 
				
			||||||
		"redirect": ctx.Repo.RepoLink + "/labels",
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	return
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Milestones render milestones page
 | 
					// Milestones render milestones page
 | 
				
			||||||
func Milestones(ctx *context.Context) {
 | 
					func Milestones(ctx *context.Context) {
 | 
				
			||||||
	ctx.Data["Title"] = ctx.Tr("repo.milestones")
 | 
						ctx.Data["Title"] = ctx.Tr("repo.milestones")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										170
									
								
								routers/repo/issue_label.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								routers/repo/issue_label.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,170 @@
 | 
				
			||||||
 | 
					// Copyright 2017 The Gitea Authors. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package repo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/auth"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/base"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						tplLabels base.TplName = "repo/issue/labels"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Labels render issue's labels page
 | 
				
			||||||
 | 
					func Labels(ctx *context.Context) {
 | 
				
			||||||
 | 
						ctx.Data["Title"] = ctx.Tr("repo.labels")
 | 
				
			||||||
 | 
						ctx.Data["PageIsIssueList"] = true
 | 
				
			||||||
 | 
						ctx.Data["PageIsLabels"] = true
 | 
				
			||||||
 | 
						ctx.Data["RequireMinicolors"] = true
 | 
				
			||||||
 | 
						ctx.Data["LabelTemplates"] = models.LabelTemplates
 | 
				
			||||||
 | 
						ctx.HTML(200, tplLabels)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// InitializeLabels init labels for a repository
 | 
				
			||||||
 | 
					func InitializeLabels(ctx *context.Context, form auth.InitializeLabelsForm) {
 | 
				
			||||||
 | 
						if ctx.HasError() {
 | 
				
			||||||
 | 
							ctx.Redirect(ctx.Repo.RepoLink + "/labels")
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						list, err := models.GetLabelTemplateFile(form.TemplateName)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.Flash.Error(ctx.Tr("repo.issues.label_templates.fail_to_load_file", form.TemplateName, err))
 | 
				
			||||||
 | 
							ctx.Redirect(ctx.Repo.RepoLink + "/labels")
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						labels := make([]*models.Label, len(list))
 | 
				
			||||||
 | 
						for i := 0; i < len(list); i++ {
 | 
				
			||||||
 | 
							labels[i] = &models.Label{
 | 
				
			||||||
 | 
								RepoID: ctx.Repo.Repository.ID,
 | 
				
			||||||
 | 
								Name:   list[i][0],
 | 
				
			||||||
 | 
								Color:  list[i][1],
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := models.NewLabels(labels...); err != nil {
 | 
				
			||||||
 | 
							ctx.Handle(500, "NewLabels", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.Redirect(ctx.Repo.RepoLink + "/labels")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RetrieveLabels find all the labels of a repository
 | 
				
			||||||
 | 
					func RetrieveLabels(ctx *context.Context) {
 | 
				
			||||||
 | 
						labels, err := models.GetLabelsByRepoID(ctx.Repo.Repository.ID, ctx.Query("sort"))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.Handle(500, "RetrieveLabels.GetLabels", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, l := range labels {
 | 
				
			||||||
 | 
							l.CalOpenIssues()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.Data["Labels"] = labels
 | 
				
			||||||
 | 
						ctx.Data["NumLabels"] = len(labels)
 | 
				
			||||||
 | 
						ctx.Data["SortType"] = ctx.Query("sort")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewLabel create new label for repository
 | 
				
			||||||
 | 
					func NewLabel(ctx *context.Context, form auth.CreateLabelForm) {
 | 
				
			||||||
 | 
						ctx.Data["Title"] = ctx.Tr("repo.labels")
 | 
				
			||||||
 | 
						ctx.Data["PageIsLabels"] = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ctx.HasError() {
 | 
				
			||||||
 | 
							ctx.Flash.Error(ctx.Data["ErrorMsg"].(string))
 | 
				
			||||||
 | 
							ctx.Redirect(ctx.Repo.RepoLink + "/labels")
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						l := &models.Label{
 | 
				
			||||||
 | 
							RepoID: ctx.Repo.Repository.ID,
 | 
				
			||||||
 | 
							Name:   form.Title,
 | 
				
			||||||
 | 
							Color:  form.Color,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := models.NewLabels(l); err != nil {
 | 
				
			||||||
 | 
							ctx.Handle(500, "NewLabel", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.Redirect(ctx.Repo.RepoLink + "/labels")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UpdateLabel update a label's name and color
 | 
				
			||||||
 | 
					func UpdateLabel(ctx *context.Context, form auth.CreateLabelForm) {
 | 
				
			||||||
 | 
						l, err := models.GetLabelByID(form.ID)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							switch {
 | 
				
			||||||
 | 
							case models.IsErrLabelNotExist(err):
 | 
				
			||||||
 | 
								ctx.Error(404)
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								ctx.Handle(500, "UpdateLabel", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						l.Name = form.Title
 | 
				
			||||||
 | 
						l.Color = form.Color
 | 
				
			||||||
 | 
						if err := models.UpdateLabel(l); err != nil {
 | 
				
			||||||
 | 
							ctx.Handle(500, "UpdateLabel", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.Redirect(ctx.Repo.RepoLink + "/labels")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeleteLabel delete a label
 | 
				
			||||||
 | 
					func DeleteLabel(ctx *context.Context) {
 | 
				
			||||||
 | 
						if err := models.DeleteLabel(ctx.Repo.Repository.ID, ctx.QueryInt64("id")); err != nil {
 | 
				
			||||||
 | 
							ctx.Flash.Error("DeleteLabel: " + err.Error())
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							ctx.Flash.Success(ctx.Tr("repo.issues.label_deletion_success"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.JSON(200, map[string]interface{}{
 | 
				
			||||||
 | 
							"redirect": ctx.Repo.RepoLink + "/labels",
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UpdateIssueLabel change issue's labels
 | 
				
			||||||
 | 
					func UpdateIssueLabel(ctx *context.Context) {
 | 
				
			||||||
 | 
						issue := getActionIssue(ctx)
 | 
				
			||||||
 | 
						if ctx.Written() {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ctx.Query("action") == "clear" {
 | 
				
			||||||
 | 
							if err := issue.ClearLabels(ctx.User); err != nil {
 | 
				
			||||||
 | 
								ctx.Handle(500, "ClearLabels", err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							isAttach := ctx.Query("action") == "attach"
 | 
				
			||||||
 | 
							label, err := models.GetLabelByID(ctx.QueryInt64("id"))
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								if models.IsErrLabelNotExist(err) {
 | 
				
			||||||
 | 
									ctx.Error(404, "GetLabelByID")
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									ctx.Handle(500, "GetLabelByID", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if isAttach && !issue.HasLabel(label.ID) {
 | 
				
			||||||
 | 
								if err = issue.AddLabel(ctx.User, label); err != nil {
 | 
				
			||||||
 | 
									ctx.Handle(500, "AddLabel", err)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else if !isAttach && issue.HasLabel(label.ID) {
 | 
				
			||||||
 | 
								if err = issue.RemoveLabel(ctx.User, label); err != nil {
 | 
				
			||||||
 | 
									ctx.Handle(500, "RemoveLabel", err)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.JSON(200, map[string]interface{}{
 | 
				
			||||||
 | 
							"ok": true,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -58,7 +58,7 @@
 | 
				
			||||||
			{{range .Issue.Comments}}
 | 
								{{range .Issue.Comments}}
 | 
				
			||||||
				{{ $createdStr:= TimeSince .Created $.Lang }}
 | 
									{{ $createdStr:= TimeSince .Created $.Lang }}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				<!-- 0 = COMMENT, 1 = REOPEN, 2 = CLOSE, 3 = ISSUE_REF, 4 = COMMIT_REF, 5 = COMMENT_REF, 6 = PULL_REF -->
 | 
									<!-- 0 = COMMENT, 1 = REOPEN, 2 = CLOSE, 3 = ISSUE_REF, 4 = COMMIT_REF, 5 = COMMENT_REF, 6 = PULL_REF, 7 = COMMENT_LABEL -->
 | 
				
			||||||
				{{if eq .Type 0}}
 | 
									{{if eq .Type 0}}
 | 
				
			||||||
					<div class="comment" id="{{.HashTag}}">
 | 
										<div class="comment" id="{{.HashTag}}">
 | 
				
			||||||
						<a class="avatar" {{if gt .Poster.ID 0}}href="{{.Poster.HomeLink}}"{{end}}>
 | 
											<a class="avatar" {{if gt .Poster.ID 0}}href="{{.Poster.HomeLink}}"{{end}}>
 | 
				
			||||||
| 
						 | 
					@ -144,6 +144,15 @@
 | 
				
			||||||
							<span class="text grey">{{.Content | Str2html}}</span>
 | 
												<span class="text grey">{{.Content | Str2html}}</span>
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
 | 
									{{else if eq .Type 7}}
 | 
				
			||||||
 | 
										<div class="event">
 | 
				
			||||||
 | 
											<span class="octicon octicon-primitive-dot"></span>
 | 
				
			||||||
 | 
											<a class="ui avatar image" href="{{.Poster.HomeLink}}">
 | 
				
			||||||
 | 
												<img src="{{.Poster.RelAvatarLink}}">
 | 
				
			||||||
 | 
											</a>
 | 
				
			||||||
 | 
											<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a>
 | 
				
			||||||
 | 
											{{if .Content}}{{$.i18n.Tr "repo.issues.add_label_at" .Label.ForegroundColor .Label.Color  .Label.Name $createdStr | Safe}}{{else}}{{$.i18n.Tr "repo.issues.remove_label_at" .Label.ForegroundColor .Label.Color .Label.Name $createdStr | Safe}}{{end}}</span>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
				{{end}}
 | 
									{{end}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			{{end}}
 | 
								{{end}}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue