diff --git a/models/issue.go b/models/issue.go index 1727da1d50..05b17e4da5 100644 --- a/models/issue.go +++ b/models/issue.go @@ -739,7 +739,7 @@ type NewIssueOptions struct { IsPull bool } -func newIssue(e *xorm.Session, opts NewIssueOptions) (err error) { +func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) { opts.Issue.Title = strings.TrimSpace(opts.Issue.Title) opts.Issue.Index = opts.Repo.NextIssueIndex() @@ -754,9 +754,6 @@ func newIssue(e *xorm.Session, opts NewIssueOptions) (err error) { if milestone != nil { opts.Issue.MilestoneID = milestone.ID opts.Issue.Milestone = milestone - if err = changeMilestoneAssign(e, opts.Issue, -1); err != nil { - return err - } } } @@ -785,6 +782,12 @@ func newIssue(e *xorm.Session, opts NewIssueOptions) (err error) { return err } + if opts.Issue.MilestoneID > 0 { + if err = changeMilestoneAssign(e, doer, opts.Issue, -1); err != nil { + return err + } + } + if opts.IsPull { _, err = e.Exec("UPDATE `repository` SET num_pulls = num_pulls + 1 WHERE id = ?", opts.Issue.RepoID) } else { @@ -849,7 +852,7 @@ func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string) return err } - if err = newIssue(sess, NewIssueOptions{ + if err = newIssue(sess, issue.Poster, NewIssueOptions{ Repo: repo, Issue: issue, LableIDs: labelIDs, @@ -1773,7 +1776,7 @@ func ChangeMilestoneIssueStats(issue *Issue) (err error) { return sess.Commit() } -func changeMilestoneAssign(e *xorm.Session, issue *Issue, oldMilestoneID int64) error { +func changeMilestoneAssign(e *xorm.Session, doer *User, issue *Issue, oldMilestoneID int64) error { if oldMilestoneID > 0 { m, err := getMilestoneByRepoID(e, issue.RepoID, oldMilestoneID) if err != nil { @@ -1810,18 +1813,28 @@ func changeMilestoneAssign(e *xorm.Session, issue *Issue, oldMilestoneID int64) } } + if err := issue.loadRepo(e); err != nil { + return err + } + + if oldMilestoneID > 0 || issue.MilestoneID > 0 { + if _, err := createMilestoneComment(e, doer, issue.Repo, issue, oldMilestoneID, issue.MilestoneID); err != nil { + return err + } + } + return updateIssue(e, issue) } // ChangeMilestoneAssign changes assignment of milestone for issue. -func ChangeMilestoneAssign(issue *Issue, oldMilestoneID int64) (err error) { +func ChangeMilestoneAssign(issue *Issue, doer *User, oldMilestoneID int64) (err error) { sess := x.NewSession() defer sess.Close() if err = sess.Begin(); err != nil { return err } - if err = changeMilestoneAssign(sess, issue, oldMilestoneID); err != nil { + if err = changeMilestoneAssign(sess, doer, issue, oldMilestoneID); err != nil { return err } return sess.Commit() diff --git a/models/issue_comment.go b/models/issue_comment.go index be7044a8e7..d128e2ebab 100644 --- a/models/issue_comment.go +++ b/models/issue_comment.go @@ -38,6 +38,8 @@ const ( CommentTypePullRef // Labels changed CommentTypeLabel + // Milestone changed + CommentTypeMilestone ) // CommentTag defines comment tag type @@ -58,9 +60,13 @@ type Comment struct { PosterID int64 `xorm:"INDEX"` Poster *User `xorm:"-"` IssueID int64 `xorm:"INDEX"` - CommitID int64 LabelID int64 Label *Label `xorm:"-"` + OldMilestoneID int64 + MilestoneID int64 + OldMilestone *Milestone `xorm:"-"` + Milestone *Milestone `xorm:"-"` + CommitID int64 Line int64 Content string `xorm:"TEXT"` RenderedContent string `xorm:"-"` @@ -204,6 +210,36 @@ func (c *Comment) LoadLabel() error { return nil } +// LoadMilestone if comment.Type is CommentTypeMilestone, then load milestone +func (c *Comment) LoadMilestone() error { + if c.OldMilestoneID > 0 { + var oldMilestone Milestone + has, err := x.ID(c.OldMilestoneID).Get(&oldMilestone) + if err != nil { + return err + } else if !has { + return ErrMilestoneNotExist{ + ID: c.OldMilestoneID, + } + } + c.OldMilestone = &oldMilestone + } + + if c.MilestoneID > 0 { + var milestone Milestone + has, err := x.ID(c.MilestoneID).Get(&milestone) + if err != nil { + return err + } else if !has { + return ErrMilestoneNotExist{ + ID: c.MilestoneID, + } + } + c.Milestone = &milestone + } + 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) { @@ -233,15 +269,17 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err 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, - Content: opts.Content, + Type: opts.Type, + PosterID: opts.Doer.ID, + Poster: opts.Doer, + IssueID: opts.Issue.ID, + LabelID: LabelID, + OldMilestoneID: opts.OldMilestoneID, + MilestoneID: opts.MilestoneID, + CommitID: opts.CommitID, + CommitSHA: opts.CommitSHA, + Line: opts.LineNum, + Content: opts.Content, } if _, err = e.Insert(comment); err != nil { return nil, err @@ -367,6 +405,17 @@ func createLabelComment(e *xorm.Session, doer *User, repo *Repository, issue *Is }) } +func createMilestoneComment(e *xorm.Session, doer *User, repo *Repository, issue *Issue, oldMilestoneID, milestoneID int64) (*Comment, error) { + return createComment(e, &CreateCommentOptions{ + Type: CommentTypeMilestone, + Doer: doer, + Repo: repo, + Issue: issue, + OldMilestoneID: oldMilestoneID, + MilestoneID: milestoneID, + }) +} + // CreateCommentOptions defines options for creating comment type CreateCommentOptions struct { Type CommentType @@ -375,11 +424,13 @@ type CreateCommentOptions struct { Issue *Issue Label *Label - CommitID int64 - CommitSHA string - LineNum int64 - Content string - Attachments []string // UUIDs of attachments + OldMilestoneID int64 + MilestoneID int64 + CommitID int64 + CommitSHA string + LineNum int64 + Content string + Attachments []string // UUIDs of attachments } // CreateComment creates comment of issue or commit. diff --git a/models/pull.go b/models/pull.go index 382738bf29..e680480978 100644 --- a/models/pull.go +++ b/models/pull.go @@ -470,7 +470,7 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str return err } - if err = newIssue(sess, NewIssueOptions{ + if err = newIssue(sess, pull.Poster, NewIssueOptions{ Repo: repo, Issue: pull, LableIDs: labelIDs, diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 546b9489b1..cd2b67f8da 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -543,6 +543,9 @@ 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
%s
label %s` issues.remove_label_at = `removed the
%s
label %s` +issues.add_milestone_at = `added this to the %s milestone %s` +issues.change_milestone_at = `modified the milestone from %s to %s %s` +issues.remove_milestone_at = `removed this from the %s milestone %s` issues.open_tab = %d Open issues.close_tab = %d Closed issues.filter_label = Label diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 840ff36b5f..dfb9bbb172 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -503,6 +503,9 @@ issues.label_templates.use=加载标签模板 issues.label_templates.fail_to_load_file=加载标签模板文件 '%s' 时发生错误:%v issues.add_label_at = ` %[4]s 添加了标签
%[3]s
` issues.remove_label_at = ` %[4]s 删除了标签
%[3]s
` +issues.add_milestone_at = ` %[2]s 添加了里程碑 %[1]s` +issues.change_milestone_at = `%[3]s 修改了里程碑从 %[1]s%[2]s` +issues.remove_milestone_at = `%[2]s 删除了里程碑 %[1]s` issues.open_tab=%d 个开启中 issues.close_tab=%d 个已关闭 issues.filter_label=标签筛选 diff --git a/public/js/index.js b/public/js/index.js index e52da6e5ba..086ec7656f 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -168,6 +168,12 @@ function initCommentForm() { var $list = $('.ui' + select_id + '.list'); var hasUpdateAction = $menu.data('action') == 'update'; + $(select_id).dropdown('setting', 'onHide', function(){ + if (hasUpdateAction) { + location.reload(); + } + }); + $menu.find('.item:not(.no-select)').click(function () { $(this).parent().find('.item').each(function () { $(this).removeClass('selected active') diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index ac0289c412..c65f4b7063 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -168,7 +168,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) { issue.MilestoneID != *form.Milestone { oldMilestoneID := issue.MilestoneID issue.MilestoneID = *form.Milestone - if err = models.ChangeMilestoneAssign(issue, oldMilestoneID); err != nil { + if err = models.ChangeMilestoneAssign(issue, ctx.User, oldMilestoneID); err != nil { ctx.Error(500, "ChangeMilestoneAssign", err) return } diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 4c10024bfb..9a38d37528 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -241,7 +241,7 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) { issue.MilestoneID != form.Milestone { oldMilestoneID := issue.MilestoneID issue.MilestoneID = form.Milestone - if err = models.ChangeMilestoneAssign(issue, oldMilestoneID); err != nil { + if err = models.ChangeMilestoneAssign(issue, ctx.User, oldMilestoneID); err != nil { ctx.Error(500, "ChangeMilestoneAssign", err) return } diff --git a/routers/repo/issue.go b/routers/repo/issue.go index b2490e242a..76440359ef 100644 --- a/routers/repo/issue.go +++ b/routers/repo/issue.go @@ -384,22 +384,27 @@ func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm) ([]int64 return nil, 0, 0 } - // Check labels. - labelIDs, err := base.StringsToInt64s(strings.Split(form.LabelIDs, ",")) - if err != nil { - return nil, 0, 0 - } - labelIDMark := base.Int64sToMap(labelIDs) + var labelIDs []int64 hasSelected := false - for i := range labels { - if labelIDMark[labels[i].ID] { - labels[i].IsChecked = true - hasSelected = true + // Check labels. + if len(form.LabelIDs) > 0 { + labelIDs, err = base.StringsToInt64s(strings.Split(form.LabelIDs, ",")) + if err != nil { + return nil, 0, 0 + } + labelIDMark := base.Int64sToMap(labelIDs) + + for i := range labels { + if labelIDMark[labels[i].ID] { + labels[i].IsChecked = true + hasSelected = true + } } } + + ctx.Data["Labels"] = labels ctx.Data["HasSelectedLabel"] = hasSelected ctx.Data["label_ids"] = form.LabelIDs - ctx.Data["Labels"] = labels // Check milestone. milestoneID := form.MilestoneID @@ -617,6 +622,11 @@ func ViewIssue(ctx *context.Context) { ctx.Handle(500, "LoadLabel", err) return } + } else if comment.Type == models.CommentTypeMilestone { + if err = comment.LoadMilestone(); err != nil { + ctx.Handle(500, "LoadMilestone", err) + return + } } } @@ -625,7 +635,6 @@ func ViewIssue(ctx *context.Context) { canDelete := false if ctx.IsSigned && pull.HeadBranch != "master" { - if err := pull.GetHeadRepo(); err != nil { log.Error(4, "GetHeadRepo: %v", err) } else if ctx.User.IsWriterOfRepo(pull.HeadRepo) { @@ -729,7 +738,7 @@ func UpdateIssueMilestone(ctx *context.Context) { // Not check for invalid milestone id and give responsibility to owners. issue.MilestoneID = milestoneID - if err := models.ChangeMilestoneAssign(issue, oldMilestoneID); err != nil { + if err := models.ChangeMilestoneAssign(issue, ctx.User, oldMilestoneID); err != nil { ctx.Handle(500, "ChangeMilestoneAssign", err) return } diff --git a/templates/repo/issue/view_content.tmpl b/templates/repo/issue/view_content.tmpl index ef32d0ba1e..39e0587d94 100644 --- a/templates/repo/issue/view_content.tmpl +++ b/templates/repo/issue/view_content.tmpl @@ -151,7 +151,16 @@ {{.Poster.Name}} - {{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}} + {{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}} + + {{else if eq .Type 8}} +
+ + + + + {{.Poster.Name}} + {{if gt .OldMilestoneID 0}}{{if gt .MilestoneID 0}}{{$.i18n.Tr "repo.issues.change_milestone_at" .OldMilestone.Name .Milestone.Name $createdStr | Safe}}{{else}}{{$.i18n.Tr "repo.issues.remove_milestone_at" .OldMilestone.Name $createdStr | Safe}}{{end}}{{else if gt .MilestoneID 0}}{{$.i18n.Tr "repo.issues.add_milestone_at" .Milestone.Name $createdStr | Safe}}{{end}}
{{end}}