* 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 (
 | 
			
		||||
	"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
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if issue.Labels == nil {
 | 
			
		||||
		issue.Labels, err = getLabelsByIssueID(e, issue.ID)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("getLabelsByIssueID [%d]: %v", issue.ID, err)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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,20 +432,76 @@ 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 {
 | 
			
		||||
	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)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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.use = Use this label set
 | 
			
		||||
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.close_tab = %d Closed
 | 
			
		||||
issues.filter_label = Label
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -501,6 +501,8 @@ issues.label_templates.info=此仓库还未创建任何标签,您可以通过
 | 
			
		|||
issues.label_templates.helper=选择标签模板
 | 
			
		||||
issues.label_templates.use=加载标签模板
 | 
			
		||||
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.close_tab=%d 个已关闭
 | 
			
		||||
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 () {
 | 
			
		||||
        if ($(this).hasClass('checked')) {
 | 
			
		||||
            $(this).removeClass('checked');
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -98,7 +98,7 @@ func DeleteIssueLabel(ctx *context.APIContext) {
 | 
			
		|||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := models.DeleteIssueLabel(issue, label); err != nil {
 | 
			
		||||
	if err := models.DeleteIssueLabel(issue, ctx.User, label); err != nil {
 | 
			
		||||
		ctx.Error(500, "DeleteIssueLabel", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -129,7 +129,7 @@ func ReplaceIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) {
 | 
			
		|||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := issue.ReplaceLabels(labels); err != nil {
 | 
			
		||||
	if err := issue.ReplaceLabels(labels, ctx.User); err != nil {
 | 
			
		||||
		ctx.Error(500, "ReplaceLabels", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,8 +34,6 @@ const (
 | 
			
		|||
	tplIssueNew  base.TplName = "repo/issue/new"
 | 
			
		||||
	tplIssueView base.TplName = "repo/issue/view"
 | 
			
		||||
 | 
			
		||||
	tplLabels base.TplName = "repo/issue/labels"
 | 
			
		||||
 | 
			
		||||
	tplMilestone     base.TplName = "repo/issue/milestones"
 | 
			
		||||
	tplMilestoneNew  base.TplName = "repo/issue/milestone_new"
 | 
			
		||||
	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
 | 
			
		||||
func Issues(ctx *context.Context) {
 | 
			
		||||
	isPullList := ctx.Params(":type") == "pulls"
 | 
			
		||||
| 
						 | 
				
			
			@ -629,6 +612,11 @@ func ViewIssue(ctx *context.Context) {
 | 
			
		|||
			if !isAdded && !issue.IsPoster(comment.Poster.ID) {
 | 
			
		||||
				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
 | 
			
		||||
func UpdateIssueMilestone(ctx *context.Context) {
 | 
			
		||||
	issue := getActionIssue(ctx)
 | 
			
		||||
| 
						 | 
				
			
			@ -966,103 +912,6 @@ func DeleteComment(ctx *context.Context) {
 | 
			
		|||
	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
 | 
			
		||||
func Milestones(ctx *context.Context) {
 | 
			
		||||
	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}}
 | 
			
		||||
				{{ $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}}
 | 
			
		||||
					<div class="comment" id="{{.HashTag}}">
 | 
			
		||||
						<a class="avatar" {{if gt .Poster.ID 0}}href="{{.Poster.HomeLink}}"{{end}}>
 | 
			
		||||
| 
						 | 
				
			
			@ -144,6 +144,15 @@
 | 
			
		|||
							<span class="text grey">{{.Content | Str2html}}</span>
 | 
			
		||||
						</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}}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue