milestone: edit

This commit is contained in:
Unknwon 2015-08-05 18:26:18 +08:00
parent cf90312b8f
commit 74bd6b939c
10 changed files with 132 additions and 74 deletions

View File

@ -427,8 +427,8 @@ func runWeb(ctx *cli.Context) {
m.Group("/milestones", func() { m.Group("/milestones", func() {
m.Get("/new", repo.NewMilestone) m.Get("/new", repo.NewMilestone)
m.Post("/new", bindIgnErr(auth.CreateMilestoneForm{}), repo.NewMilestonePost) m.Post("/new", bindIgnErr(auth.CreateMilestoneForm{}), repo.NewMilestonePost)
m.Get("/:index/edit", repo.MilestoneActions) m.Get("/:index/edit", repo.EditMilestone)
m.Post("/:index/edit", bindIgnErr(auth.CreateMilestoneForm{}), repo.UpdateMilestonePost) m.Post("/:index/edit", bindIgnErr(auth.CreateMilestoneForm{}), repo.EditMilestonePost)
m.Get("/:index/:action", repo.MilestoneActions) m.Get("/:index/:action", repo.MilestoneActions)
}, reqRepoAdmin) }, reqRepoAdmin)

View File

@ -404,9 +404,14 @@ milestones.create = Create Milestone
milestones.title = Title milestones.title = Title
milestones.desc = Description milestones.desc = Description
milestones.due_date = Due Date (optional) milestones.due_date = Due Date (optional)
milestones.clear = clear milestones.clear = Clear
milestones.invalid_due_date_format = Due date format is invalid, must be 'mm/dd/year'. milestones.invalid_due_date_format = Due date format is invalid, must be 'mm/dd/year'.
milestones.create_success = Milestone '%s' has been created successfully! milestones.create_success = Milestone '%s' has been created successfully!
milestones.edit = Edit Milestone
milestones.edit_subheader = Use better description for milestones so people won't be confused.
milestones.cancel = Cancel
milestones.modify = Modify Milestone
milestones.edit_success = Changes of milestone '%s' has been saved successfully!
settings = Settings settings = Settings
settings.options = Options settings.options = Options

View File

@ -134,3 +134,24 @@ func IsErrRepoNotExist(err error) bool {
func (err ErrRepoNotExist) Error() string { func (err ErrRepoNotExist) Error() string {
return fmt.Sprintf("repository does not exist [id: %d, uid: %d, name: %s]", err.ID, err.UID, err.Name) return fmt.Sprintf("repository does not exist [id: %d, uid: %d, name: %s]", err.ID, err.UID, err.Name)
} }
// _____ .__.__ __
// / \ |__| | ____ _______/ |_ ____ ____ ____
// / \ / \| | | _/ __ \ / ___/\ __\/ _ \ / \_/ __ \
// / Y \ | |_\ ___/ \___ \ | | ( <_> ) | \ ___/
// \____|__ /__|____/\___ >____ > |__| \____/|___| /\___ >
// \/ \/ \/ \/ \/
type ErrMilestoneNotExist struct {
ID int64
Index int64
}
func IsErrMilestoneNotExist(err error) bool {
_, ok := err.(ErrMilestoneNotExist)
return ok
}
func (err ErrMilestoneNotExist) Error() string {
return fmt.Sprintf("milestone does not exist [id: %d, index: %d]", err.ID, err.Index)
}

View File

@ -23,7 +23,6 @@ import (
var ( var (
ErrIssueNotExist = errors.New("Issue does not exist") ErrIssueNotExist = errors.New("Issue does not exist")
ErrLabelNotExist = errors.New("Label does not exist") ErrLabelNotExist = errors.New("Label does not exist")
ErrMilestoneNotExist = errors.New("Milestone does not exist")
ErrWrongIssueCounter = errors.New("Invalid number of issues for this milestone") ErrWrongIssueCounter = errors.New("Invalid number of issues for this milestone")
ErrAttachmentNotExist = errors.New("Attachment does not exist") ErrAttachmentNotExist = errors.New("Attachment does not exist")
ErrAttachmentNotLinked = errors.New("Attachment does not belong to this issue") ErrAttachmentNotLinked = errors.New("Attachment does not belong to this issue")
@ -691,7 +690,7 @@ func MilestoneById(id int64) (*Milestone, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} else if !has { } else if !has {
return nil, ErrMilestoneNotExist return nil, ErrMilestoneNotExist{id, 0}
} }
return m, nil return m, nil
} }
@ -703,7 +702,7 @@ func GetMilestoneByIndex(repoId, idx int64) (*Milestone, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} else if !has { } else if !has {
return nil, ErrMilestoneNotExist return nil, ErrMilestoneNotExist{0, idx}
} }
return m, nil return m, nil
} }

File diff suppressed because one or more lines are too long

View File

@ -88,9 +88,9 @@ function initRepository() {
inline: true, inline: true,
timepicker: false, timepicker: false,
startDate: $datepicker.data('start-date'), startDate: $datepicker.data('start-date'),
formatDate: 'm/d/Y', formatDate: 'Y-m-d',
onSelectDate: function (ct) { onSelectDate: function (ct) {
$('#deadline').val(ct.dateFormat('m/d/Y')); $('#deadline').val(ct.dateFormat('Y-m-d'));
} }
}); });
$('#clear-date').click(function () { $('#clear-date').click(function () {

View File

@ -351,21 +351,21 @@ func ViewIssue(ctx *middleware.Context) {
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.Id, idx) issue, err := models.GetIssueByIndex(ctx.Repo.Repository.Id, idx)
if err != nil { if err != nil {
if err == models.ErrIssueNotExist { if err == models.ErrIssueNotExist {
ctx.Handle(404, "issue.ViewIssue(GetIssueByIndex)", err) ctx.Handle(404, "GetIssueByIndex", err)
} else { } else {
ctx.Handle(500, "issue.ViewIssue(GetIssueByIndex)", err) ctx.Handle(500, "GetIssueByIndex", err)
} }
return return
} }
// Get labels. // Get labels.
if err = issue.GetLabels(); err != nil { if err = issue.GetLabels(); err != nil {
ctx.Handle(500, "issue.ViewIssue(GetLabels)", err) ctx.Handle(500, "GetLabels", err)
return return
} }
labels, err := models.GetLabels(ctx.Repo.Repository.Id) labels, err := models.GetLabels(ctx.Repo.Repository.Id)
if err != nil { if err != nil {
ctx.Handle(500, "issue.ViewIssue(GetLabels.2)", err) ctx.Handle(500, "GetLabels.2", err)
return return
} }
checkLabels(issue.Labels, labels) checkLabels(issue.Labels, labels)
@ -375,10 +375,10 @@ func ViewIssue(ctx *middleware.Context) {
if issue.MilestoneId > 0 { if issue.MilestoneId > 0 {
ctx.Data["Milestone"], err = models.MilestoneById(issue.MilestoneId) ctx.Data["Milestone"], err = models.MilestoneById(issue.MilestoneId)
if err != nil { if err != nil {
if err == models.ErrMilestoneNotExist { if models.IsErrMilestoneNotExist(err) {
log.Warn("issue.ViewIssue(GetMilestoneById): %v", err) log.Warn("GetMilestoneById: %v", err)
} else { } else {
ctx.Handle(500, "issue.ViewIssue(GetMilestoneById)", err) ctx.Handle(500, "GetMilestoneById", err)
return return
} }
} }
@ -387,36 +387,36 @@ func ViewIssue(ctx *middleware.Context) {
// Get all milestones. // Get all milestones.
ctx.Data["OpenMilestones"], err = models.GetMilestones(ctx.Repo.Repository.Id, -1, false) ctx.Data["OpenMilestones"], err = models.GetMilestones(ctx.Repo.Repository.Id, -1, false)
if err != nil { if err != nil {
ctx.Handle(500, "issue.ViewIssue(GetMilestones.1): %v", err) ctx.Handle(500, "GetMilestones.1: %v", err)
return return
} }
ctx.Data["ClosedMilestones"], err = models.GetMilestones(ctx.Repo.Repository.Id, -1, true) ctx.Data["ClosedMilestones"], err = models.GetMilestones(ctx.Repo.Repository.Id, -1, true)
if err != nil { if err != nil {
ctx.Handle(500, "issue.ViewIssue(GetMilestones.2): %v", err) ctx.Handle(500, "GetMilestones.2: %v", err)
return return
} }
// Get all collaborators. // Get all collaborators.
ctx.Data["Collaborators"], err = ctx.Repo.Repository.GetCollaborators() ctx.Data["Collaborators"], err = ctx.Repo.Repository.GetCollaborators()
if err != nil { if err != nil {
ctx.Handle(500, "issue.CreateIssue(GetCollaborators)", err) ctx.Handle(500, "GetCollaborators", err)
return return
} }
if ctx.IsSigned { if ctx.IsSigned {
// Update issue-user. // Update issue-user.
if err = models.UpdateIssueUserPairByRead(ctx.User.Id, issue.ID); err != nil { if err = models.UpdateIssueUserPairByRead(ctx.User.Id, issue.ID); err != nil {
ctx.Handle(500, "issue.ViewIssue(UpdateIssueUserPairByRead): %v", err) ctx.Handle(500, "UpdateIssueUserPairByRead: %v", err)
return return
} }
} }
// Get poster and Assignee. // Get poster and Assignee.
if err = issue.GetPoster(); err != nil { if err = issue.GetPoster(); err != nil {
ctx.Handle(500, "issue.ViewIssue(GetPoster): %v", err) ctx.Handle(500, "GetPoster: %v", err)
return return
} else if err = issue.GetAssignee(); err != nil { } else if err = issue.GetAssignee(); err != nil {
ctx.Handle(500, "issue.ViewIssue(GetAssignee): %v", err) ctx.Handle(500, "GetAssignee: %v", err)
return return
} }
issue.RenderedContent = string(base.RenderMarkdown([]byte(issue.Content), ctx.Repo.RepoLink)) issue.RenderedContent = string(base.RenderMarkdown([]byte(issue.Content), ctx.Repo.RepoLink))
@ -424,7 +424,7 @@ func ViewIssue(ctx *middleware.Context) {
// Get comments. // Get comments.
comments, err := models.GetIssueComments(issue.ID) comments, err := models.GetIssueComments(issue.ID)
if err != nil { if err != nil {
ctx.Handle(500, "issue.ViewIssue(GetIssueComments): %v", err) ctx.Handle(500, "GetIssueComments: %v", err)
return return
} }
@ -432,7 +432,7 @@ func ViewIssue(ctx *middleware.Context) {
for i := range comments { for i := range comments {
u, err := models.GetUserById(comments[i].PosterId) u, err := models.GetUserById(comments[i].PosterId)
if err != nil { if err != nil {
ctx.Handle(500, "issue.ViewIssue(GetUserById.2): %v", err) ctx.Handle(500, "GetUserById.2: %v", err)
return return
} }
comments[i].Poster = u comments[i].Poster = u
@ -1051,8 +1051,70 @@ func NewMilestonePost(ctx *middleware.Context, form auth.CreateMilestoneForm) {
ctx.Redirect(ctx.Repo.RepoLink + "/milestones") ctx.Redirect(ctx.Repo.RepoLink + "/milestones")
} }
func EditMilestone(ctx *middleware.Context) {} func EditMilestone(ctx *middleware.Context) {
func EditMilestonePost(ctx *middleware.Context) {} ctx.Data["Title"] = ctx.Tr("repo.milestones.edit")
ctx.Data["PageIsMilestones"] = true
ctx.Data["PageIsEditMilestone"] = true
ctx.Data["DateLang"] = setting.DateLang(ctx.Locale.Language())
m, err := models.GetMilestoneByIndex(ctx.Repo.Repository.Id, ctx.ParamsInt64(":index"))
if err != nil {
if models.IsErrMilestoneNotExist(err) {
ctx.Handle(404, "GetMilestoneByIndex", nil)
} else {
ctx.Handle(500, "GetMilestoneByIndex", err)
}
return
}
ctx.Data["title"] = m.Name
ctx.Data["content"] = m.Content
if len(m.DeadlineString) > 0 {
ctx.Data["deadline"] = m.DeadlineString
}
ctx.HTML(200, MILESTONE_NEW)
}
func EditMilestonePost(ctx *middleware.Context, form auth.CreateMilestoneForm) {
ctx.Data["Title"] = ctx.Tr("repo.milestones.edit")
ctx.Data["PageIsMilestones"] = true
ctx.Data["PageIsEditMilestone"] = true
ctx.Data["DateLang"] = setting.DateLang(ctx.Locale.Language())
if ctx.HasError() {
ctx.HTML(200, MILESTONE_NEW)
return
}
if len(form.Deadline) == 0 {
form.Deadline = "9999-12-31"
}
deadline, err := time.Parse("2006-01-02", form.Deadline)
if err != nil {
ctx.Data["Err_Deadline"] = true
ctx.RenderWithErr(ctx.Tr("repo.milestones.invalid_due_date_format"), MILESTONE_NEW, &form)
return
}
m, err := models.GetMilestoneByIndex(ctx.Repo.Repository.Id, ctx.ParamsInt64(":index"))
if err != nil {
if models.IsErrMilestoneNotExist(err) {
ctx.Handle(404, "GetMilestoneByIndex", nil)
} else {
ctx.Handle(500, "GetMilestoneByIndex", err)
}
return
}
m.Name = form.Title
m.Content = form.Content
m.Deadline = deadline
if err = models.UpdateMilestone(m); err != nil {
ctx.Handle(500, "UpdateMilestone", err)
return
}
ctx.Flash.Success(ctx.Tr("repo.milestones.edit_success", m.Name))
ctx.Redirect(ctx.Repo.RepoLink + "/milestones")
}
func MilestoneActions(ctx *middleware.Context) { func MilestoneActions(ctx *middleware.Context) {
ctx.Data["Title"] = "Update Milestone" ctx.Data["Title"] = "Update Milestone"
@ -1067,7 +1129,7 @@ func MilestoneActions(ctx *middleware.Context) {
mile, err := models.GetMilestoneByIndex(ctx.Repo.Repository.Id, idx) mile, err := models.GetMilestoneByIndex(ctx.Repo.Repository.Id, idx)
if err != nil { if err != nil {
if err == models.ErrMilestoneNotExist { if models.IsErrMilestoneNotExist(err) {
ctx.Handle(404, "GetMilestoneByIndex", err) ctx.Handle(404, "GetMilestoneByIndex", err)
} else { } else {
ctx.Handle(500, "GetMilestoneByIndex", err) ctx.Handle(500, "GetMilestoneByIndex", err)
@ -1125,7 +1187,7 @@ func UpdateMilestonePost(ctx *middleware.Context, form auth.CreateMilestoneForm)
mile, err := models.GetMilestoneByIndex(ctx.Repo.Repository.Id, idx) mile, err := models.GetMilestoneByIndex(ctx.Repo.Repository.Id, idx)
if err != nil { if err != nil {
if err == models.ErrMilestoneNotExist { if models.IsErrMilestoneNotExist(err) {
ctx.Handle(404, "GetMilestoneByIndex", err) ctx.Handle(404, "GetMilestoneByIndex", err)
} else { } else {
ctx.Handle(500, "GetMilestoneByIndex", err) ctx.Handle(500, "GetMilestoneByIndex", err)

View File

@ -8,8 +8,13 @@
<div class="ui divider"></div> <div class="ui divider"></div>
<div class="sixteen wide column page grid"> <div class="sixteen wide column page grid">
<h2 class="ui dividing header"> <h2 class="ui dividing header">
{{if .PageIsEditMilestone}}
{{.i18n.Tr "repo.milestones.edit"}}
<div class="sub header">{{.i18n.Tr "repo.milestones.edit_subheader"}}</div>
{{else}}
{{.i18n.Tr "repo.milestones.new"}} {{.i18n.Tr "repo.milestones.new"}}
<div class="sub header">{{.i18n.Tr "repo.milestones.new_subheader"}}</div> <div class="sub header">{{.i18n.Tr "repo.milestones.new_subheader"}}</div>
{{end}}
</h2> </h2>
<form class="ui form grid" action="{{.Link}}" method="post"> <form class="ui form grid" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}} {{.CsrfTokenHtml}}
@ -21,7 +26,7 @@
<div class="eleven wide column"> <div class="eleven wide column">
<div class="field {{if .Err_Title}}error{{end}}"> <div class="field {{if .Err_Title}}error{{end}}">
<label>{{.i18n.Tr "repo.milestones.title"}}</label> <label>{{.i18n.Tr "repo.milestones.title"}}</label>
<input name="title" placeholder="{{.i18n.Tr "repo.milestones.title"}}" value="{{.title}}" required> <input name="title" placeholder="{{.i18n.Tr "repo.milestones.title"}}" value="{{.title}}" autofocus required>
</div> </div>
<div class="field"> <div class="field">
<label>{{.i18n.Tr "repo.milestones.desc"}}</label> <label>{{.i18n.Tr "repo.milestones.desc"}}</label>
@ -41,9 +46,18 @@
</div> </div>
</div> </div>
<div class="ui divider"></div> <div class="ui divider"></div>
{{if .PageIsEditMilestone}}
<button class="ui right green button">
{{.i18n.Tr "repo.milestones.modify"}}
</button>
<a class="ui right blue basic button" href="{{.RepoLink}}/milestones">
{{.i18n.Tr "repo.milestones.cancel"}}
</a>
{{else}}
<button class="ui right green button"> <button class="ui right green button">
{{.i18n.Tr "repo.milestones.create"}} {{.i18n.Tr "repo.milestones.create"}}
</button> </button>
{{end}}
</form> </form>
</div> </div>
</div> </div>

View File

@ -1,43 +0,0 @@
{{template "base/head_old" .}}
{{template "base/navbar" .}}
{{template "repo/nav" .}}
{{template "repo/toolbar" .}}
<div id="body" class="container">
<div id="issue">
<div class="col-md-3 filter-list">
<ul class="list-unstyled">
<li><a href="{{.RepoLink}}/milestones"{{if eq .State "open"}} class="active"{{end}}>Open Milestones <strong class="pull-right">{{.Repository.NumOpenMilestones}}</strong></a></li>
<li><a href="{{.RepoLink}}/milestones?state=closed"{{if eq .State "closed"}} class="active"{{end}}>Close Milestones <strong class="pull-right">{{.Repository.NumClosedMilestones}}</strong></a></li>
</ul>
<hr/>
<a href="{{.RepoLink}}/milestones/new" class="text-center">
<button class="btn btn-default btn-block">Create new milestone</button>
</a>
</div>
<div class="col-md-9">
<div class="milestones list-group">
{{range .Milestones}}
<div class="list-group-item milestone-item">
<h4 class="title pull-left"><a href="{{$.RepoLink}}/issues?milestone={{.Index}}{{if .IsClosed}}&state=closed{{end}}">{{.Name}}</a></h4>
<span class="issue-open label label-success">{{.NumOpenIssues}}</span>
<span class="issue-close label label-warning">{{.NumClosedIssues}}</span>
<p class="actions pull-right">
<a href="{{$.RepoLink}}/milestones/{{.Index}}/edit">Edit</a>
{{if .IsClosed}}
<a href="{{$.RepoLink}}/milestones/{{.Index}}/open">Open</a>
{{else}}
<a href="{{$.RepoLink}}/milestones/{{.Index}}/close">Close</a>
{{end}}
<a class="text-danger" href="{{$.RepoLink}}/milestones/{{.Index}}/delete">Delete</a>
<a href="{{$.RepoLink}}/issues?milestone={{.Index}}{{if .IsClosed}}&state=closed{{end}}">Issues</a>
</p>
<hr/>
<p class="description">{{.RenderedContent | Str2html}}</p>
</div>
{{end}}
</div>
</div>
</div>
</div>
</div>
{{template "base/footer_old" .}}