add milestone changed traker on issue view (#804)

release/v1.15
Lunny Xiao 2017-02-01 10:36:08 +08:00 committed by GitHub
parent 10644d6dd7
commit 081485ecfd
10 changed files with 134 additions and 40 deletions

View File

@ -739,7 +739,7 @@ type NewIssueOptions struct {
IsPull bool 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.Title = strings.TrimSpace(opts.Issue.Title)
opts.Issue.Index = opts.Repo.NextIssueIndex() opts.Issue.Index = opts.Repo.NextIssueIndex()
@ -754,9 +754,6 @@ func newIssue(e *xorm.Session, opts NewIssueOptions) (err error) {
if milestone != nil { if milestone != nil {
opts.Issue.MilestoneID = milestone.ID opts.Issue.MilestoneID = milestone.ID
opts.Issue.Milestone = milestone 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 return err
} }
if opts.Issue.MilestoneID > 0 {
if err = changeMilestoneAssign(e, doer, opts.Issue, -1); err != nil {
return err
}
}
if opts.IsPull { if opts.IsPull {
_, err = e.Exec("UPDATE `repository` SET num_pulls = num_pulls + 1 WHERE id = ?", opts.Issue.RepoID) _, err = e.Exec("UPDATE `repository` SET num_pulls = num_pulls + 1 WHERE id = ?", opts.Issue.RepoID)
} else { } else {
@ -849,7 +852,7 @@ func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string)
return err return err
} }
if err = newIssue(sess, NewIssueOptions{ if err = newIssue(sess, issue.Poster, NewIssueOptions{
Repo: repo, Repo: repo,
Issue: issue, Issue: issue,
LableIDs: labelIDs, LableIDs: labelIDs,
@ -1773,7 +1776,7 @@ func ChangeMilestoneIssueStats(issue *Issue) (err error) {
return sess.Commit() 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 { if oldMilestoneID > 0 {
m, err := getMilestoneByRepoID(e, issue.RepoID, oldMilestoneID) m, err := getMilestoneByRepoID(e, issue.RepoID, oldMilestoneID)
if err != nil { 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) return updateIssue(e, issue)
} }
// ChangeMilestoneAssign changes assignment of milestone for 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() sess := x.NewSession()
defer sess.Close() defer sess.Close()
if err = sess.Begin(); err != nil { if err = sess.Begin(); err != nil {
return err return err
} }
if err = changeMilestoneAssign(sess, issue, oldMilestoneID); err != nil { if err = changeMilestoneAssign(sess, doer, issue, oldMilestoneID); err != nil {
return err return err
} }
return sess.Commit() return sess.Commit()

View File

@ -38,6 +38,8 @@ const (
CommentTypePullRef CommentTypePullRef
// Labels changed // Labels changed
CommentTypeLabel CommentTypeLabel
// Milestone changed
CommentTypeMilestone
) )
// CommentTag defines comment tag type // CommentTag defines comment tag type
@ -58,9 +60,13 @@ type Comment struct {
PosterID int64 `xorm:"INDEX"` PosterID int64 `xorm:"INDEX"`
Poster *User `xorm:"-"` Poster *User `xorm:"-"`
IssueID int64 `xorm:"INDEX"` IssueID int64 `xorm:"INDEX"`
CommitID int64
LabelID int64 LabelID int64
Label *Label `xorm:"-"` Label *Label `xorm:"-"`
OldMilestoneID int64
MilestoneID int64
OldMilestone *Milestone `xorm:"-"`
Milestone *Milestone `xorm:"-"`
CommitID int64
Line int64 Line int64
Content string `xorm:"TEXT"` Content string `xorm:"TEXT"`
RenderedContent string `xorm:"-"` RenderedContent string `xorm:"-"`
@ -204,6 +210,36 @@ func (c *Comment) LoadLabel() error {
return nil 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 // 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) {
@ -233,15 +269,17 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
LabelID = opts.Label.ID 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, LabelID: LabelID,
CommitID: opts.CommitID, OldMilestoneID: opts.OldMilestoneID,
CommitSHA: opts.CommitSHA, MilestoneID: opts.MilestoneID,
Line: opts.LineNum, CommitID: opts.CommitID,
Content: opts.Content, CommitSHA: opts.CommitSHA,
Line: opts.LineNum,
Content: opts.Content,
} }
if _, err = e.Insert(comment); err != nil { if _, err = e.Insert(comment); err != nil {
return nil, err 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 // CreateCommentOptions defines options for creating comment
type CreateCommentOptions struct { type CreateCommentOptions struct {
Type CommentType Type CommentType
@ -375,11 +424,13 @@ type CreateCommentOptions struct {
Issue *Issue Issue *Issue
Label *Label Label *Label
CommitID int64 OldMilestoneID int64
CommitSHA string MilestoneID int64
LineNum int64 CommitID int64
Content string CommitSHA string
Attachments []string // UUIDs of attachments LineNum int64
Content string
Attachments []string // UUIDs of attachments
} }
// CreateComment creates comment of issue or commit. // CreateComment creates comment of issue or commit.

View File

@ -470,7 +470,7 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str
return err return err
} }
if err = newIssue(sess, NewIssueOptions{ if err = newIssue(sess, pull.Poster, NewIssueOptions{
Repo: repo, Repo: repo,
Issue: pull, Issue: pull,
LableIDs: labelIDs, LableIDs: labelIDs,

View File

@ -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.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.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.remove_label_at = `removed the <div class="ui label" style="color: %s; background-color: %s">%s</div> label %s`
issues.add_milestone_at = `added this to the <b>%s</b> milestone %s`
issues.change_milestone_at = `modified the milestone from <b>%s</b> to <b>%s</b> %s`
issues.remove_milestone_at = `removed this from the <b>%s</b> milestone %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

View File

@ -503,6 +503,9 @@ 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.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.remove_label_at = ` %[4]s 删除了标签 <div class="ui label" style="color: %[1]s; background-color: %[2]s">%[3]s</div>`
issues.add_milestone_at = ` %[2]s 添加了里程碑 <b>%[1]s</b>`
issues.change_milestone_at = `%[3]s 修改了里程碑从 <b>%[1]s</b> 到 <b>%[2]s</b>`
issues.remove_milestone_at = `%[2]s 删除了里程碑 <b>%[1]s</b>`
issues.open_tab=%d 个开启中 issues.open_tab=%d 个开启中
issues.close_tab=%d 个已关闭 issues.close_tab=%d 个已关闭
issues.filter_label=标签筛选 issues.filter_label=标签筛选

View File

@ -168,6 +168,12 @@ function initCommentForm() {
var $list = $('.ui' + select_id + '.list'); var $list = $('.ui' + select_id + '.list');
var hasUpdateAction = $menu.data('action') == 'update'; var hasUpdateAction = $menu.data('action') == 'update';
$(select_id).dropdown('setting', 'onHide', function(){
if (hasUpdateAction) {
location.reload();
}
});
$menu.find('.item:not(.no-select)').click(function () { $menu.find('.item:not(.no-select)').click(function () {
$(this).parent().find('.item').each(function () { $(this).parent().find('.item').each(function () {
$(this).removeClass('selected active') $(this).removeClass('selected active')

View File

@ -168,7 +168,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
issue.MilestoneID != *form.Milestone { issue.MilestoneID != *form.Milestone {
oldMilestoneID := issue.MilestoneID oldMilestoneID := issue.MilestoneID
issue.MilestoneID = *form.Milestone 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) ctx.Error(500, "ChangeMilestoneAssign", err)
return return
} }

View File

@ -241,7 +241,7 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
issue.MilestoneID != form.Milestone { issue.MilestoneID != form.Milestone {
oldMilestoneID := issue.MilestoneID oldMilestoneID := issue.MilestoneID
issue.MilestoneID = form.Milestone 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) ctx.Error(500, "ChangeMilestoneAssign", err)
return return
} }

View File

@ -384,22 +384,27 @@ func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm) ([]int64
return nil, 0, 0 return nil, 0, 0
} }
// Check labels. var labelIDs []int64
labelIDs, err := base.StringsToInt64s(strings.Split(form.LabelIDs, ","))
if err != nil {
return nil, 0, 0
}
labelIDMark := base.Int64sToMap(labelIDs)
hasSelected := false hasSelected := false
for i := range labels { // Check labels.
if labelIDMark[labels[i].ID] { if len(form.LabelIDs) > 0 {
labels[i].IsChecked = true labelIDs, err = base.StringsToInt64s(strings.Split(form.LabelIDs, ","))
hasSelected = true 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["HasSelectedLabel"] = hasSelected
ctx.Data["label_ids"] = form.LabelIDs ctx.Data["label_ids"] = form.LabelIDs
ctx.Data["Labels"] = labels
// Check milestone. // Check milestone.
milestoneID := form.MilestoneID milestoneID := form.MilestoneID
@ -617,6 +622,11 @@ func ViewIssue(ctx *context.Context) {
ctx.Handle(500, "LoadLabel", err) ctx.Handle(500, "LoadLabel", err)
return 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 canDelete := false
if ctx.IsSigned && pull.HeadBranch != "master" { if ctx.IsSigned && pull.HeadBranch != "master" {
if err := pull.GetHeadRepo(); err != nil { if err := pull.GetHeadRepo(); err != nil {
log.Error(4, "GetHeadRepo: %v", err) log.Error(4, "GetHeadRepo: %v", err)
} else if ctx.User.IsWriterOfRepo(pull.HeadRepo) { } 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. // Not check for invalid milestone id and give responsibility to owners.
issue.MilestoneID = milestoneID 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) ctx.Handle(500, "ChangeMilestoneAssign", err)
return return
} }

View File

@ -151,7 +151,16 @@
<img src="{{.Poster.RelAvatarLink}}"> <img src="{{.Poster.RelAvatarLink}}">
</a> </a>
<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</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> {{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>
{{else if eq .Type 8}}
<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 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}}</span>
</div> </div>
{{end}} {{end}}