* track labels changed on issue view & resolved #542 * add missing head comment & sort & fix refresh
This commit is contained in:
		
							parent
							
								
									d078aa30d6
								
							
						
					
					
						commit
						f94869d2d1
					
				
							
								
								
									
										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…
	
		Reference in New Issue
	
	Block a user