![Demo](http://gogs.qiniudn.com/gogs_demo.gif) | ![Demo](http://gogs.qiniudn.com/gogs_demo.gif) | ||||
##### Current version: 0.6.3 Beta | |||||
##### Current version: 0.6.4 Beta | |||||
### NOTICES | ### NOTICES | ||||
m.Post("/new", bindIgnErr(auth.CreateMilestoneForm{}), repo.NewMilestonePost) | m.Post("/new", bindIgnErr(auth.CreateMilestoneForm{}), repo.NewMilestonePost) | ||||
m.Get("/:index/edit", repo.EditMilestone) | m.Get("/:index/edit", repo.EditMilestone) | ||||
m.Post("/:index/edit", bindIgnErr(auth.CreateMilestoneForm{}), repo.EditMilestonePost) | m.Post("/:index/edit", bindIgnErr(auth.CreateMilestoneForm{}), repo.EditMilestonePost) | ||||
m.Get("/:index/:action", repo.MilestoneActions) | |||||
m.Get("/:index/:action", repo.ChangeMilestonStatus) | |||||
m.Post("/delete", repo.DeleteMilestone) | |||||
}, reqRepoAdmin) | }, reqRepoAdmin) | ||||
m.Post("/comment/:action", repo.Comment) | m.Post("/comment/:action", repo.Comment) |
issues.filter_label = Label | issues.filter_label = Label | ||||
issues.filter_label_no_select = No selected label | issues.filter_label_no_select = No selected label | ||||
issues.filter_milestone = Milestone | issues.filter_milestone = Milestone | ||||
issues.filter_milestone_no_select = No selected milestone | |||||
issues.filter_assignee = Assignee | issues.filter_assignee = Assignee | ||||
issues.filter_type = Type | issues.filter_type = Type | ||||
issues.filter_type.all_issues = All issues | issues.filter_type.all_issues = All issues | ||||
milestones.cancel = Cancel | milestones.cancel = Cancel | ||||
milestones.modify = Modify Milestone | milestones.modify = Modify Milestone | ||||
milestones.edit_success = Changes of milestone '%s' has been saved successfully! | milestones.edit_success = Changes of milestone '%s' has been saved successfully! | ||||
milestones.deletion = Milestone Deletion | |||||
milestones.deletion_desc = Delete milestone will remove its information in all related issues. Do you want to continue? | |||||
milestones.deletion_success = Milestone has been deleted successfully! | |||||
settings = Settings | settings = Settings | ||||
settings.options = Options | settings.options = Options |
"github.com/gogits/gogs/modules/setting" | "github.com/gogits/gogs/modules/setting" | ||||
) | ) | ||||
const APP_VER = "0.6.3.0805 Beta" | |||||
const APP_VER = "0.6.4.0805 Beta" | |||||
func init() { | func init() { | ||||
runtime.GOMAXPROCS(runtime.NumCPU()) | runtime.GOMAXPROCS(runtime.NumCPU()) |
url := fmt.Sprintf("%s/%s/%s/commit/%s", setting.AppSubUrl, repoUserName, repoName, c.Sha1) | url := fmt.Sprintf("%s/%s/%s/commit/%s", setting.AppSubUrl, repoUserName, repoName, c.Sha1) | ||||
message := fmt.Sprintf(`<a href="%s">%s</a>`, url, c.Message) | message := fmt.Sprintf(`<a href="%s">%s</a>`, url, c.Message) | ||||
if _, err = CreateComment(userId, issue.RepoId, issue.ID, 0, 0, COMMENT_TYPE_COMMIT, message, nil); err != nil { | |||||
if _, err = CreateComment(userId, issue.RepoID, issue.ID, 0, 0, COMMENT_TYPE_COMMIT, message, nil); err != nil { | |||||
return err | return err | ||||
} | } | ||||
} | } | ||||
return err | return err | ||||
} | } | ||||
if issue.RepoId == repoId { | |||||
if issue.RepoID == repoId { | |||||
if issue.IsClosed { | if issue.IsClosed { | ||||
continue | continue | ||||
} | } | ||||
return err | return err | ||||
} | } | ||||
if issue.RepoId == repoId { | |||||
if issue.RepoID == repoId { | |||||
if !issue.IsClosed { | if !issue.IsClosed { | ||||
continue | continue | ||||
} | } |
// Issue represents an issue or pull request of repository. | // Issue represents an issue or pull request of repository. | ||||
type Issue struct { | type Issue struct { | ||||
ID int64 `xorm:"pk autoincr"` | ID int64 `xorm:"pk autoincr"` | ||||
RepoId int64 `xorm:"INDEX"` | |||||
RepoID int64 `xorm:"INDEX"` | |||||
Index int64 // Index in one repository. | Index int64 // Index in one repository. | ||||
Name string | Name string | ||||
Repo *Repository `xorm:"-"` | Repo *Repository `xorm:"-"` | ||||
PosterId int64 | |||||
PosterID int64 | |||||
Poster *User `xorm:"-"` | Poster *User `xorm:"-"` | ||||
LabelIds string `xorm:"TEXT"` | LabelIds string `xorm:"TEXT"` | ||||
Labels []*Label `xorm:"-"` | Labels []*Label `xorm:"-"` | ||||
MilestoneId int64 | |||||
AssigneeId int64 | |||||
MilestoneID int64 | |||||
Milestone *Milestone `xorm:"-"` | |||||
AssigneeID int64 | |||||
Assignee *User `xorm:"-"` | Assignee *User `xorm:"-"` | ||||
IsRead bool `xorm:"-"` | IsRead bool `xorm:"-"` | ||||
IsPull bool // Indicates whether is a pull request or not. | IsPull bool // Indicates whether is a pull request or not. | ||||
Updated time.Time `xorm:"UPDATED"` | Updated time.Time `xorm:"UPDATED"` | ||||
} | } | ||||
func (i *Issue) BeforeSet(colName string, val xorm.Cell) { | |||||
var err error | |||||
switch colName { | |||||
case "milestone_id": | |||||
mid := (*val).(int64) | |||||
if mid <= 0 { | |||||
return | |||||
} | |||||
i.Milestone, err = GetMilestoneById(mid) | |||||
if err != nil { | |||||
log.Error(3, "GetMilestoneById: %v", err) | |||||
} | |||||
} | |||||
} | |||||
func (i *Issue) GetPoster() (err error) { | func (i *Issue) GetPoster() (err error) { | ||||
i.Poster, err = GetUserById(i.PosterId) | |||||
i.Poster, err = GetUserById(i.PosterID) | |||||
if err == ErrUserNotExist { | if err == ErrUserNotExist { | ||||
i.Poster = &User{Name: "FakeUser"} | i.Poster = &User{Name: "FakeUser"} | ||||
return nil | return nil | ||||
} | } | ||||
func (i *Issue) GetAssignee() (err error) { | func (i *Issue) GetAssignee() (err error) { | ||||
if i.AssigneeId == 0 { | |||||
if i.AssigneeID == 0 { | |||||
return nil | return nil | ||||
} | } | ||||
i.Assignee, err = GetUserById(i.AssigneeId) | |||||
i.Assignee, err = GetUserById(i.AssigneeID) | |||||
if err == ErrUserNotExist { | if err == ErrUserNotExist { | ||||
return nil | return nil | ||||
} | } | ||||
if _, err = sess.Insert(issue); err != nil { | if _, err = sess.Insert(issue); err != nil { | ||||
return err | return err | ||||
} else if _, err = sess.Exec("UPDATE `repository` SET num_issues = num_issues + 1 WHERE id = ?", issue.RepoId); err != nil { | |||||
} else if _, err = sess.Exec("UPDATE `repository` SET num_issues = num_issues + 1 WHERE id = ?", issue.RepoID); err != nil { | |||||
return err | return err | ||||
} | } | ||||
return err | return err | ||||
} | } | ||||
if issue.MilestoneId > 0 { | |||||
if issue.MilestoneID > 0 { | |||||
// FIXES(280): Update milestone counter. | // FIXES(280): Update milestone counter. | ||||
return ChangeMilestoneAssign(0, issue.MilestoneId, issue) | |||||
return ChangeMilestoneAssign(0, issue.MilestoneID, issue) | |||||
} | } | ||||
return | return | ||||
// GetIssueByIndex returns issue by given index in repository. | // GetIssueByIndex returns issue by given index in repository. | ||||
func GetIssueByIndex(rid, index int64) (*Issue, error) { | func GetIssueByIndex(rid, index int64) (*Issue, error) { | ||||
issue := &Issue{RepoId: rid, Index: index} | |||||
issue := &Issue{RepoID: rid, Index: index} | |||||
has, err := x.Get(issue) | has, err := x.Get(issue) | ||||
if err != nil { | if err != nil { | ||||
return nil, err | return nil, err | ||||
return sess.Commit() | return sess.Commit() | ||||
} | } | ||||
// MilestoneById returns the milestone by given ID. | |||||
func MilestoneById(id int64) (*Milestone, error) { | |||||
// GetMilestoneById returns the milestone by given ID. | |||||
func GetMilestoneById(id int64) (*Milestone, error) { | |||||
m := &Milestone{ID: id} | m := &Milestone{ID: id} | ||||
has, err := x.Get(m) | has, err := x.Get(m) | ||||
if err != nil { | if err != nil { | ||||
return m, nil | return m, nil | ||||
} | } | ||||
// GetAllRepoMilestones returns all milestones of given repository. | |||||
func GetAllRepoMilestones(repoID int64) ([]*Milestone, error) { | |||||
miles := make([]*Milestone, 0, 10) | |||||
return miles, x.Where("repo_id=?", repoID).Find(&miles) | |||||
} | |||||
// GetMilestones returns a list of milestones of given repository and status. | // GetMilestones returns a list of milestones of given repository and status. | ||||
func GetMilestones(repoID int64, page int, isClosed bool) ([]*Milestone, error) { | func GetMilestones(repoID int64, page int, isClosed bool) ([]*Milestone, error) { | ||||
miles := make([]*Milestone, 0, setting.IssuePagingNum) | miles := make([]*Milestone, 0, setting.IssuePagingNum) | ||||
} | } | ||||
func updateMilestone(e Engine, m *Milestone) error { | |||||
_, err := e.Id(m.ID).AllCols().Update(m) | |||||
return err | |||||
} | |||||
// UpdateMilestone updates information of given milestone. | // UpdateMilestone updates information of given milestone. | ||||
func UpdateMilestone(m *Milestone) error { | func UpdateMilestone(m *Milestone) error { | ||||
_, err := x.Id(m.ID).AllCols().Update(m) | |||||
return err | |||||
return updateMilestone(x, m) | |||||
} | } | ||||
// CountClosedMilestones returns number of closed milestones in given repository. | |||||
func CountClosedMilestones(repoID int64) int64 { | |||||
closed, _ := x.Where("repo_id=? AND is_closed=?", repoID, true).Count(new(Milestone)) | |||||
func countRepoMilestones(e Engine, repoID int64) int64 { | |||||
count, _ := e.Where("repo_id=?", repoID).Count(new(Milestone)) | |||||
return count | |||||
} | |||||
// CountRepoMilestones returns number of milestones in given repository. | |||||
func CountRepoMilestones(repoID int64) int64 { | |||||
return countRepoMilestones(x, repoID) | |||||
} | |||||
func countRepoClosedMilestones(e Engine, repoID int64) int64 { | |||||
closed, _ := e.Where("repo_id=? AND is_closed=?", repoID, true).Count(new(Milestone)) | |||||
return closed | return closed | ||||
} | } | ||||
// CountRepoClosedMilestones returns number of closed milestones in given repository. | |||||
func CountRepoClosedMilestones(repoID int64) int64 { | |||||
return countRepoClosedMilestones(x, repoID) | |||||
} | |||||
// MilestoneStats returns number of open and closed milestones of given repository. | // MilestoneStats returns number of open and closed milestones of given repository. | ||||
func MilestoneStats(repoID int64) (open int64, closed int64) { | func MilestoneStats(repoID int64) (open int64, closed int64) { | ||||
open, _ = x.Where("repo_id=? AND is_closed=?", repoID, false).Count(new(Milestone)) | open, _ = x.Where("repo_id=? AND is_closed=?", repoID, false).Count(new(Milestone)) | ||||
return open, CountClosedMilestones(repoID) | |||||
return open, CountRepoClosedMilestones(repoID) | |||||
} | } | ||||
// ChangeMilestoneStatus changes the milestone open/closed status. | // ChangeMilestoneStatus changes the milestone open/closed status. | ||||
} | } | ||||
m.IsClosed = isClosed | m.IsClosed = isClosed | ||||
if err = UpdateMilestone(m); err != nil { | |||||
if err = updateMilestone(sess, m); err != nil { | |||||
return err | return err | ||||
} | } | ||||
repo.NumClosedMilestones = int(CountClosedMilestones(repo.Id)) | |||||
repo.NumMilestones = int(countRepoMilestones(sess, repo.Id)) | |||||
repo.NumClosedMilestones = int(countRepoClosedMilestones(sess, repo.Id)) | |||||
if _, err = sess.Id(repo.Id).AllCols().Update(repo); err != nil { | if _, err = sess.Id(repo.Id).AllCols().Update(repo); err != nil { | ||||
return err | return err | ||||
} | } | ||||
// ChangeMilestoneIssueStats updates the open/closed issues counter and progress | // ChangeMilestoneIssueStats updates the open/closed issues counter and progress | ||||
// for the milestone associated witht the given issue. | // for the milestone associated witht the given issue. | ||||
func ChangeMilestoneIssueStats(issue *Issue) error { | func ChangeMilestoneIssueStats(issue *Issue) error { | ||||
if issue.MilestoneId == 0 { | |||||
if issue.MilestoneID == 0 { | |||||
return nil | return nil | ||||
} | } | ||||
m, err := MilestoneById(issue.MilestoneId) | |||||
m, err := GetMilestoneById(issue.MilestoneID) | |||||
if err != nil { | if err != nil { | ||||
return err | return err | ||||
} | } | ||||
} | } | ||||
if oldMid > 0 { | if oldMid > 0 { | ||||
m, err := MilestoneById(oldMid) | |||||
m, err := GetMilestoneById(oldMid) | |||||
if err != nil { | if err != nil { | ||||
return err | return err | ||||
} | } | ||||
} | } | ||||
if mid > 0 { | if mid > 0 { | ||||
m, err := MilestoneById(mid) | |||||
m, err := GetMilestoneById(mid) | |||||
if err != nil { | if err != nil { | ||||
return err | return err | ||||
} | } | ||||
return sess.Commit() | return sess.Commit() | ||||
} | } | ||||
// DeleteMilestone deletes a milestone. | |||||
func DeleteMilestone(m *Milestone) (err error) { | |||||
sess := x.NewSession() | |||||
defer sess.Close() | |||||
if err = sess.Begin(); err != nil { | |||||
// DeleteMilestoneByID deletes a milestone by given ID. | |||||
func DeleteMilestoneByID(mid int64) error { | |||||
m, err := GetMilestoneById(mid) | |||||
if err != nil { | |||||
if IsErrMilestoneNotExist(err) { | |||||
return nil | |||||
} | |||||
return err | return err | ||||
} | } | ||||
if _, err = sess.Delete(m); err != nil { | |||||
sess.Rollback() | |||||
repo, err := GetRepositoryById(m.RepoID) | |||||
if err != nil { | |||||
return err | return err | ||||
} | } | ||||
rawSql := "UPDATE `repository` SET num_milestones = num_milestones - 1 WHERE id = ?" | |||||
if _, err = sess.Exec(rawSql, m.RepoID); err != nil { | |||||
sess.Rollback() | |||||
sess := x.NewSession() | |||||
defer sessionRelease(sess) | |||||
if err = sess.Begin(); err != nil { | |||||
return err | return err | ||||
} | } | ||||
rawSql = "UPDATE `issue` SET milestone_id = 0 WHERE milestone_id = ?" | |||||
if _, err = sess.Exec(rawSql, m.ID); err != nil { | |||||
sess.Rollback() | |||||
if _, err = sess.Id(m.ID).Delete(m); err != nil { | |||||
return err | return err | ||||
} | } | ||||
rawSql = "UPDATE `issue_user` SET milestone_id = 0 WHERE milestone_id = ?" | |||||
if _, err = sess.Exec(rawSql, m.ID); err != nil { | |||||
sess.Rollback() | |||||
repo.NumMilestones = int(countRepoMilestones(sess, repo.Id)) | |||||
repo.NumClosedMilestones = int(countRepoClosedMilestones(sess, repo.Id)) | |||||
if _, err = sess.Id(repo.Id).AllCols().Update(repo); err != nil { | |||||
return err | |||||
} | |||||
if _, err = sess.Exec("UPDATE `issue` SET milestone_id=0 WHERE milestone_id=?", m.ID); err != nil { | |||||
return err | |||||
} else if _, err = sess.Exec("UPDATE `issue_user` SET milestone_id=0 WHERE milestone_id=?", m.ID); err != nil { | |||||
return err | return err | ||||
} | } | ||||
return sess.Commit() | return sess.Commit() |
} | } | ||||
} | } | ||||
if _, err = sess.Delete(&Issue{RepoId: repoID}); err != nil { | |||||
if _, err = sess.Delete(&Issue{RepoID: repoID}); err != nil { | |||||
return err | return err | ||||
} | } | ||||
$('.color-picker').val(color_hex); | $('.color-picker').val(color_hex); | ||||
$('.minicolors-swatch-color').css("background-color", color_hex); | $('.minicolors-swatch-color').css("background-color", color_hex); | ||||
}); | }); | ||||
$('.delete-label-button').click(function () { | |||||
var $this = $(this); | |||||
$('.delete-label.modal').modal({ | |||||
closable: false, | |||||
onApprove: function () { | |||||
$.post($this.data('url'), { | |||||
"_csrf": csrf, | |||||
"id": $this.data("id") | |||||
}).done(function (data) { | |||||
window.location.href = data.redirect; | |||||
}); | |||||
} | |||||
}).modal('show'); | |||||
return false; | |||||
}); | |||||
$('.edit-label-button').click(function () { | $('.edit-label-button').click(function () { | ||||
$('#label-modal-id').val($(this).data('id')); | $('#label-modal-id').val($(this).data('id')); | ||||
$('#label-modal-title').val($(this).data('title')); | $('#label-modal-title').val($(this).data('title')); | ||||
} | } | ||||
// Milestones | // Milestones | ||||
if ($('.repository.milestones').length > 0) { | |||||
} | |||||
if ($('.repository.new.milestone').length > 0) { | if ($('.repository.new.milestone').length > 0) { | ||||
var $datepicker = $('.milestone.datepicker') | var $datepicker = $('.milestone.datepicker') | ||||
$datepicker.datetimepicker({ | $datepicker.datetimepicker({ | ||||
}); | }); | ||||
$('.poping.up').popup(); | $('.poping.up').popup(); | ||||
// Helpers. | |||||
$('.delete-button').click(function () { | |||||
var $this = $(this); | |||||
$('.delete.modal').modal({ | |||||
closable: false, | |||||
onApprove: function () { | |||||
$.post($this.data('url'), { | |||||
"_csrf": csrf, | |||||
"id": $this.data("id") | |||||
}).done(function (data) { | |||||
window.location.href = data.redirect; | |||||
}); | |||||
} | |||||
}).modal('show'); | |||||
return false; | |||||
}); | |||||
initInstall(); | initInstall(); | ||||
initRepository(); | initRepository(); | ||||
}); | }); |
.menu { | .menu { | ||||
max-height: 300px; | max-height: 300px; | ||||
overflow-x: auto; | overflow-x: auto; | ||||
} | |||||
} | |||||
.type.item .menu { | |||||
right: 0!important; | right: 0!important; | ||||
left: auto!important; | left: auto!important; | ||||
.clickable .name { | |||||
padding-left: 15px!important; | |||||
} | |||||
} | |||||
} | } | ||||
.page.buttons { | .page.buttons { | ||||
.desc { | .desc { | ||||
padding-top: 5px; | padding-top: 5px; | ||||
color: #999; | color: #999; | ||||
a.milestone { | |||||
padding-left: 5px; | |||||
color: #999!important; | |||||
&:hover { | |||||
color: #000!important; | |||||
} | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } |
issues[i].IsRead = true | issues[i].IsRead = true | ||||
} | } | ||||
} | } | ||||
ctx.Data["Issues"] = issues | |||||
// Get milestones. | |||||
miles, err := models.GetAllRepoMilestones(repo.Id) | |||||
if err != nil { | |||||
ctx.Handle(500, "GetAllRepoMilestones: %v", err) | |||||
return | |||||
} | |||||
ctx.Data["Milestones"] = miles | |||||
ctx.Data["IssueStats"] = issueStats | ctx.Data["IssueStats"] = issueStats | ||||
ctx.Data["SelectLabels"] = com.StrTo(selectLabels).MustInt64() | ctx.Data["SelectLabels"] = com.StrTo(selectLabels).MustInt64() | ||||
ctx.Data["ViewType"] = viewType | ctx.Data["ViewType"] = viewType | ||||
ctx.Data["Issues"] = issues | |||||
ctx.Data["MilestoneID"] = milestoneID | |||||
ctx.Data["IsShowClosed"] = isShowClosed | ctx.Data["IsShowClosed"] = isShowClosed | ||||
if isShowClosed { | if isShowClosed { | ||||
ctx.Data["State"] = "closed" | ctx.Data["State"] = "closed" | ||||
form.AssigneeId = 0 | form.AssigneeId = 0 | ||||
} | } | ||||
issue := &models.Issue{ | issue := &models.Issue{ | ||||
RepoId: ctx.Repo.Repository.Id, | |||||
RepoID: ctx.Repo.Repository.Id, | |||||
Index: int64(ctx.Repo.Repository.NumIssues) + 1, | Index: int64(ctx.Repo.Repository.NumIssues) + 1, | ||||
Name: form.IssueName, | Name: form.IssueName, | ||||
PosterId: ctx.User.Id, | |||||
MilestoneId: form.MilestoneId, | |||||
AssigneeId: form.AssigneeId, | |||||
PosterID: ctx.User.Id, | |||||
MilestoneID: form.MilestoneId, | |||||
AssigneeID: form.AssigneeId, | |||||
LabelIds: form.Labels, | LabelIds: form.Labels, | ||||
Content: form.Content, | Content: form.Content, | ||||
} | } | ||||
ctx.Data["Labels"] = labels | ctx.Data["Labels"] = labels | ||||
// Get assigned milestone. | // Get assigned milestone. | ||||
if issue.MilestoneId > 0 { | |||||
ctx.Data["Milestone"], err = models.MilestoneById(issue.MilestoneId) | |||||
if issue.MilestoneID > 0 { | |||||
ctx.Data["Milestone"], err = models.GetMilestoneById(issue.MilestoneID) | |||||
if err != nil { | if err != nil { | ||||
if models.IsErrMilestoneNotExist(err) { | if models.IsErrMilestoneNotExist(err) { | ||||
log.Warn("GetMilestoneById: %v", err) | log.Warn("GetMilestoneById: %v", err) | ||||
ctx.Data["Title"] = issue.Name | ctx.Data["Title"] = issue.Name | ||||
ctx.Data["Issue"] = issue | ctx.Data["Issue"] = issue | ||||
ctx.Data["Comments"] = comments | ctx.Data["Comments"] = comments | ||||
ctx.Data["IsIssueOwner"] = ctx.Repo.IsOwner() || (ctx.IsSigned && issue.PosterId == ctx.User.Id) | |||||
ctx.Data["IsIssueOwner"] = ctx.Repo.IsOwner() || (ctx.IsSigned && issue.PosterID == ctx.User.Id) | |||||
ctx.Data["IsRepoToolbarIssues"] = true | ctx.Data["IsRepoToolbarIssues"] = true | ||||
ctx.Data["IsRepoToolbarIssuesList"] = false | ctx.Data["IsRepoToolbarIssuesList"] = false | ||||
ctx.HTML(200, ISSUE_VIEW) | ctx.HTML(200, ISSUE_VIEW) | ||||
return | return | ||||
} | } | ||||
if ctx.User.Id != issue.PosterId && !ctx.Repo.IsOwner() { | |||||
if ctx.User.Id != issue.PosterID && !ctx.Repo.IsOwner() { | |||||
ctx.Error(403) | ctx.Error(403) | ||||
return | return | ||||
} | } | ||||
return | return | ||||
} | } | ||||
oldMid := issue.MilestoneId | |||||
oldMid := issue.MilestoneID | |||||
mid := com.StrTo(ctx.Query("milestoneid")).MustInt64() | mid := com.StrTo(ctx.Query("milestoneid")).MustInt64() | ||||
if oldMid == mid { | if oldMid == mid { | ||||
ctx.JSON(200, map[string]interface{}{ | ctx.JSON(200, map[string]interface{}{ | ||||
} | } | ||||
// Not check for invalid milestone id and give responsibility to owners. | // Not check for invalid milestone id and give responsibility to owners. | ||||
issue.MilestoneId = mid | |||||
issue.MilestoneID = mid | |||||
if err = models.ChangeMilestoneAssign(oldMid, mid, issue); err != nil { | if err = models.ChangeMilestoneAssign(oldMid, mid, issue); err != nil { | ||||
ctx.Handle(500, "issue.UpdateIssueMilestone(ChangeMilestoneAssign)", err) | ctx.Handle(500, "issue.UpdateIssueMilestone(ChangeMilestoneAssign)", err) | ||||
return | return | ||||
aid := com.StrTo(ctx.Query("assigneeid")).MustInt64() | aid := com.StrTo(ctx.Query("assigneeid")).MustInt64() | ||||
// Not check for invalid assignee id and give responsibility to owners. | // Not check for invalid assignee id and give responsibility to owners. | ||||
issue.AssigneeId = aid | |||||
issue.AssigneeID = aid | |||||
if err = models.UpdateIssueUserPairByAssignee(aid, issue.ID); err != nil { | if err = models.UpdateIssueUserPairByAssignee(aid, issue.ID); err != nil { | ||||
ctx.Handle(500, "UpdateIssueUserPairByAssignee: %v", err) | ctx.Handle(500, "UpdateIssueUserPairByAssignee: %v", err) | ||||
return | return | ||||
// Check if issue owner changes the status of issue. | // Check if issue owner changes the status of issue. | ||||
var newStatus string | var newStatus string | ||||
if ctx.Repo.IsOwner() || issue.PosterId == ctx.User.Id { | |||||
if ctx.Repo.IsOwner() || issue.PosterID == ctx.User.Id { | |||||
newStatus = ctx.Query("change_status") | newStatus = ctx.Query("change_status") | ||||
} | } | ||||
if len(newStatus) > 0 { | if len(newStatus) > 0 { | ||||
} | } | ||||
// Change open/closed issue counter for the associated milestone | // Change open/closed issue counter for the associated milestone | ||||
if issue.MilestoneId > 0 { | |||||
if issue.MilestoneID > 0 { | |||||
if err = models.ChangeMilestoneIssueStats(issue); err != nil { | if err = models.ChangeMilestoneIssueStats(issue); err != nil { | ||||
send(500, nil, err) | send(500, nil, err) | ||||
} | } | ||||
} | } | ||||
func DeleteLabel(ctx *middleware.Context) { | func DeleteLabel(ctx *middleware.Context) { | ||||
id := ctx.QueryInt64("id") | |||||
if id > 0 { | |||||
if err := models.DeleteLabel(ctx.Repo.Repository.Id, id); err != nil { | |||||
ctx.Flash.Error("DeleteLabel: " + err.Error()) | |||||
} else { | |||||
ctx.Flash.Success(ctx.Tr("repo.issues.label_deletion_success")) | |||||
} | |||||
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{}{ | ctx.JSON(200, map[string]interface{}{ | ||||
ctx.Redirect(ctx.Repo.RepoLink + "/milestones") | ctx.Redirect(ctx.Repo.RepoLink + "/milestones") | ||||
} | } | ||||
func MilestoneActions(ctx *middleware.Context) { | |||||
ctx.Data["Title"] = "Update Milestone" | |||||
ctx.Data["IsRepoToolbarIssues"] = true | |||||
ctx.Data["IsRepoToolbarIssuesList"] = true | |||||
idx := ctx.ParamsInt64(":index") | |||||
if idx == 0 { | |||||
ctx.Handle(404, "get milestone index", nil) | |||||
return | |||||
} | |||||
mile, err := models.GetMilestoneByIndex(ctx.Repo.Repository.Id, idx) | |||||
func ChangeMilestonStatus(ctx *middleware.Context) { | |||||
m, err := models.GetMilestoneByIndex(ctx.Repo.Repository.Id, ctx.ParamsInt64(":index")) | |||||
if err != nil { | if err != nil { | ||||
if models.IsErrMilestoneNotExist(err) { | if models.IsErrMilestoneNotExist(err) { | ||||
ctx.Handle(404, "GetMilestoneByIndex", err) | ctx.Handle(404, "GetMilestoneByIndex", err) | ||||
return | return | ||||
} | } | ||||
action := ctx.Params(":action") | |||||
if len(action) > 0 { | |||||
switch action { | |||||
case "open": | |||||
if mile.IsClosed { | |||||
if err = models.ChangeMilestoneStatus(mile, false); err != nil { | |||||
ctx.Handle(500, "ChangeMilestoneStatus", err) | |||||
return | |||||
} | |||||
} | |||||
case "close": | |||||
if !mile.IsClosed { | |||||
mile.ClosedDate = time.Now() | |||||
if err = models.ChangeMilestoneStatus(mile, true); err != nil { | |||||
ctx.Handle(500, "ChangeMilestoneStatus", err) | |||||
return | |||||
} | |||||
switch ctx.Params(":action") { | |||||
case "open": | |||||
if m.IsClosed { | |||||
if err = models.ChangeMilestoneStatus(m, false); err != nil { | |||||
ctx.Handle(500, "ChangeMilestoneStatus", err) | |||||
return | |||||
} | } | ||||
case "delete": | |||||
if err = models.DeleteMilestone(mile); err != nil { | |||||
ctx.Handle(500, "DeleteMilestone", err) | |||||
} | |||||
ctx.Redirect(ctx.Repo.RepoLink + "/milestones?state=open") | |||||
case "close": | |||||
if !m.IsClosed { | |||||
m.ClosedDate = time.Now() | |||||
if err = models.ChangeMilestoneStatus(m, true); err != nil { | |||||
ctx.Handle(500, "ChangeMilestoneStatus", err) | |||||
return | return | ||||
} | } | ||||
} | } | ||||
ctx.Redirect(ctx.Repo.RepoLink + "/milestones?state=closed") | |||||
default: | |||||
ctx.Redirect(ctx.Repo.RepoLink + "/milestones") | ctx.Redirect(ctx.Repo.RepoLink + "/milestones") | ||||
return | |||||
} | |||||
mile.DeadlineString = mile.Deadline.UTC().Format("01/02/2006") | |||||
if mile.DeadlineString == "12/31/9999" { | |||||
mile.DeadlineString = "" | |||||
} | } | ||||
ctx.Data["Milestone"] = mile | |||||
ctx.HTML(200, MILESTONE_EDIT) | |||||
} | } | ||||
func UpdateMilestonePost(ctx *middleware.Context, form auth.CreateMilestoneForm) { | |||||
ctx.Data["Title"] = "Update Milestone" | |||||
ctx.Data["IsRepoToolbarIssues"] = true | |||||
ctx.Data["IsRepoToolbarIssuesList"] = true | |||||
idx := ctx.ParamsInt64(":index") | |||||
if idx == 0 { | |||||
ctx.Handle(404, "issue.UpdateMilestonePost", nil) | |||||
return | |||||
} | |||||
mile, err := models.GetMilestoneByIndex(ctx.Repo.Repository.Id, idx) | |||||
if err != nil { | |||||
if models.IsErrMilestoneNotExist(err) { | |||||
ctx.Handle(404, "GetMilestoneByIndex", err) | |||||
} else { | |||||
ctx.Handle(500, "GetMilestoneByIndex", err) | |||||
} | |||||
return | |||||
} | |||||
if ctx.HasError() { | |||||
ctx.HTML(200, MILESTONE_EDIT) | |||||
return | |||||
} | |||||
var deadline time.Time | |||||
if len(form.Deadline) == 0 { | |||||
form.Deadline = "12/31/9999" | |||||
} | |||||
deadline, err = time.Parse("01/02/2006", form.Deadline) | |||||
if err != nil { | |||||
ctx.Handle(500, "time.Parse", err) | |||||
return | |||||
} | |||||
mile.Name = form.Title | |||||
mile.Content = form.Content | |||||
mile.Deadline = deadline | |||||
if err = models.UpdateMilestone(mile); err != nil { | |||||
ctx.Handle(500, "UpdateMilestone", err) | |||||
return | |||||
func DeleteMilestone(ctx *middleware.Context) { | |||||
if err := models.DeleteMilestoneByID(ctx.QueryInt64("id")); err != nil { | |||||
ctx.Flash.Error("DeleteMilestone: " + err.Error()) | |||||
} else { | |||||
ctx.Flash.Success(ctx.Tr("repo.milestones.deletion_success")) | |||||
} | } | ||||
ctx.Redirect(ctx.Repo.RepoLink + "/milestones") | |||||
ctx.JSON(200, map[string]interface{}{ | |||||
"redirect": ctx.Repo.RepoLink + "/milestones", | |||||
}) | |||||
} | } | ||||
func IssueGetAttachment(ctx *middleware.Context) { | func IssueGetAttachment(ctx *middleware.Context) { |
} | } | ||||
} | } | ||||
issues[i].Repo, err = models.GetRepositoryById(issues[i].RepoId) | |||||
issues[i].Repo, err = models.GetRepositoryById(issues[i].RepoID) | |||||
if err != nil { | if err != nil { | ||||
if models.IsErrRepoNotExist(err) { | if models.IsErrRepoNotExist(err) { | ||||
log.Warn("user.Issues(GetRepositoryById #%d): repository not exist", issues[i].RepoId) | |||||
log.Warn("GetRepositoryById[%d]: repository not exist", issues[i].RepoID) | |||||
continue | continue | ||||
} else { | } else { | ||||
ctx.Handle(500, fmt.Sprintf("user.Issues(GetRepositoryById #%d)", issues[i].RepoId), err) | |||||
ctx.Handle(500, fmt.Sprintf("GetRepositoryById[%d]", issues[i].RepoID), err) | |||||
return | return | ||||
} | } | ||||
} | } |
0.6.3.0805 Beta | |||||
0.6.4.0805 Beta |
{{else}} | {{else}} | ||||
<ul class="list-unstyled"> | <ul class="list-unstyled"> | ||||
{{range .ClosedMilestones}} | {{range .ClosedMilestones}} | ||||
<li class="milestone-item" data-id="{{.Id}}"> | |||||
<li class="milestone-item" data-id="{{.ID}}"> | |||||
<p><strong>{{.Name}}</strong></p> | <p><strong>{{.Name}}</strong></p> | ||||
<p>Closed {{TimeSince .ClosedDate $.Lang}}</p> | <p>Closed {{TimeSince .ClosedDate $.Lang}}</p> | ||||
</li> | </li> |
<li class="item"> | <li class="item"> | ||||
<div class="ui label" style="background-color: {{.Color}}"><i class="octicon octicon-tag"></i> {{.Name}}</div> | <div class="ui label" style="background-color: {{.Color}}"><i class="octicon octicon-tag"></i> {{.Name}}</div> | ||||
{{if $.IsRepositoryAdmin}} | {{if $.IsRepositoryAdmin}} | ||||
<a class="ui right delete-label-button" href="#" data-url="{{$.RepoLink}}/labels/delete" data-id="{{.ID}}"><i class="octicon octicon-trashcan"></i> {{$.i18n.Tr "repo.issues.label_delete"}}</a> | |||||
<a class="ui right delete-button" href="#" data-url="{{$.RepoLink}}/labels/delete" data-id="{{.ID}}"><i class="octicon octicon-trashcan"></i> {{$.i18n.Tr "repo.issues.label_delete"}}</a> | |||||
<a class="ui right edit-label-button" href="#" data-id={{.ID}} data-title={{.Name}} data-color={{.Color}}><i class="octicon octicon-pencil"></i> {{$.i18n.Tr "repo.issues.label_edit"}}</a> | <a class="ui right edit-label-button" href="#" data-id={{.ID}} data-title={{.Name}} data-color={{.Color}}><i class="octicon octicon-pencil"></i> {{$.i18n.Tr "repo.issues.label_edit"}}</a> | ||||
{{end}} | {{end}} | ||||
<a class="ui right open-issues" href="{{$.RepoLink}}/issues?labels={{.ID}}"><i class="octicon octicon-issue-opened"></i> {{$.i18n.Tr "repo.issues.label_open_issues" .NumOpenIssues}}</a> | <a class="ui right open-issues" href="{{$.RepoLink}}/issues?labels={{.ID}}"><i class="octicon octicon-issue-opened"></i> {{$.i18n.Tr "repo.issues.label_open_issues" .NumOpenIssues}}</a> | ||||
</div> | </div> | ||||
{{if .IsRepositoryAdmin}} | {{if .IsRepositoryAdmin}} | ||||
<div class="ui basic delete-label modal"> | |||||
<div class="ui basic delete modal"> | |||||
<div class="header"> | <div class="header"> | ||||
{{.i18n.Tr "repo.issues.label_deletion"}} | {{.i18n.Tr "repo.issues.label_deletion"}} | ||||
</div> | </div> |
<div class="ui divider"></div> | <div class="ui divider"></div> | ||||
<div class="ui left"> | <div class="ui left"> | ||||
<div class="ui tiny buttons"> | <div class="ui tiny buttons"> | ||||
<a class="ui green basic button {{if not .IsShowClosed}}active{{end}}" href="{{.RepoLink}}/issues?type={{$.ViewType}}&state=open&labels={{.SelectLabels}}"> | |||||
<a class="ui green basic button {{if not .IsShowClosed}}active{{end}}" href="{{.RepoLink}}/issues?type={{$.ViewType}}&state=open&labels={{.SelectLabels}}&milestone={{.MilestoneID}}"> | |||||
<i class="octicon octicon-issue-opened"></i> | <i class="octicon octicon-issue-opened"></i> | ||||
{{.i18n.Tr "repo.issues.open_tab" .IssueStats.OpenCount}} | {{.i18n.Tr "repo.issues.open_tab" .IssueStats.OpenCount}} | ||||
</a> | </a> | ||||
<a class="ui red basic button {{if .IsShowClosed}}active{{end}}" href="{{.RepoLink}}/issues?type={{.ViewType}}&state=closed&labels={{.SelectLabels}}"> | |||||
<a class="ui red basic button {{if .IsShowClosed}}active{{end}}" href="{{.RepoLink}}/issues?type={{.ViewType}}&state=closed&labels={{.SelectLabels}}&milestone={{.MilestoneID}}"> | |||||
<i class="octicon octicon-issue-closed"></i> | <i class="octicon octicon-issue-closed"></i> | ||||
{{.i18n.Tr "repo.issues.close_tab" .IssueStats.ClosedCount}} | {{.i18n.Tr "repo.issues.close_tab" .IssueStats.ClosedCount}} | ||||
</a> | </a> | ||||
<i class="dropdown icon"></i> | <i class="dropdown icon"></i> | ||||
</span> | </span> | ||||
<div class="menu"> | <div class="menu"> | ||||
<a class="item" href="{{$.RepoLink}}/issues?type={{$.ViewType}}&state={{$.State}}">{{.i18n.Tr "repo.issues.filter_label_no_select"}}</a> | |||||
<a class="item" href="{{$.RepoLink}}/issues?type={{$.ViewType}}&state={{$.State}}&milestone={{$.MilestoneID}}">{{.i18n.Tr "repo.issues.filter_label_no_select"}}</a> | |||||
{{range .Labels}} | {{range .Labels}} | ||||
<a class="item" href="{{$.RepoLink}}/issues?type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}"><span class="octicon {{if eq $.SelectLabels .ID}}octicon-check{{end}}"></span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name}}</a> | |||||
<a class="item" href="{{$.RepoLink}}/issues?type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}&milestone={{$.MilestoneID}}"><span class="octicon {{if eq $.SelectLabels .ID}}octicon-check{{end}}"></span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name}}</a> | |||||
{{end}} | {{end}} | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<!-- <div class="ui {{if not .Milestones}}disabled{{end}} pointing dropdown jump item"> | |||||
<div class="ui {{if not .Milestones}}disabled{{end}} pointing dropdown jump item"> | |||||
<span class="text"> | <span class="text"> | ||||
{{.i18n.Tr "repo.issues.filter_milestone"}} | {{.i18n.Tr "repo.issues.filter_milestone"}} | ||||
<i class="dropdown icon"></i> | <i class="dropdown icon"></i> | ||||
</span> | </span> | ||||
<div class="menu"> | <div class="menu"> | ||||
<a class="item" href="{{$.RepoLink}}/issues?type={{$.ViewType}}&state={{$.State}}&labels={{.SelectLabels}}">{{.i18n.Tr "repo.issues.filter_milestone_no_select"}}</a> | |||||
{{range .Milestones}} | {{range .Milestones}} | ||||
<a class="item" href="{{$.RepoLink}}/issues">{{.Name}}</a> | |||||
<a class="clickable item" href="{{$.RepoLink}}/issues?type={{$.ViewType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{.ID}}"><span class="octicon {{if eq $.MilestoneID .ID}}octicon-check{{end}}"></span> <span class="name">{{.Name}}</span></a> | |||||
{{end}} | {{end}} | ||||
</div> | </div> | ||||
</div> --> | |||||
</div> | |||||
<!-- <div class="ui {{if not .Assignees}}disabled{{end}} pointing dropdown jump item"> | <!-- <div class="ui {{if not .Assignees}}disabled{{end}} pointing dropdown jump item"> | ||||
<span class="text"> | <span class="text"> | ||||
{{.i18n.Tr "repo.issues.filter_assignee"}} | {{.i18n.Tr "repo.issues.filter_assignee"}} | ||||
<i class="dropdown icon"></i> | <i class="dropdown icon"></i> | ||||
</span> | </span> | ||||
<div class="menu"> | <div class="menu"> | ||||
<a class="{{if eq .ViewType "all"}}active{{end}} item" href="{{$.RepoLink}}/issues?type=all&state={{$.State}}&labels={{.SelectLabels}}">{{.i18n.Tr "repo.issues.filter_type.all_issues"}}</a> | |||||
<a class="{{if eq .ViewType "assigned"}}active{{end}} item" href="{{$.RepoLink}}/issues?type=assigned&state={{$.State}}&labels={{.SelectLabels}}">{{.i18n.Tr "repo.issues.filter_type.assigned_to_you"}}</a> | |||||
<a class="{{if eq .ViewType "created_by"}}active{{end}} item" href="{{$.RepoLink}}/issues?type=created_by&state={{$.State}}&labels={{.SelectLabels}}">{{.i18n.Tr "repo.issues.filter_type.created_by_you"}}</a> | |||||
<a class="{{if eq .ViewType "mentioned"}}active{{end}} item" href="{{$.RepoLink}}/issues?type=mentioned&state={{$.State}}&labels={{.SelectLabels}}">{{.i18n.Tr "repo.issues.filter_type.mentioning_you"}}</a> | |||||
<a class="{{if eq .ViewType "all"}}active{{end}} item" href="{{$.RepoLink}}/issues?type=all&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}">{{.i18n.Tr "repo.issues.filter_type.all_issues"}}</a> | |||||
<a class="{{if eq .ViewType "assigned"}}active{{end}} item" href="{{$.RepoLink}}/issues?type=assigned&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}">{{.i18n.Tr "repo.issues.filter_type.assigned_to_you"}}</a> | |||||
<a class="{{if eq .ViewType "created_by"}}active{{end}} item" href="{{$.RepoLink}}/issues?type=created_by&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}">{{.i18n.Tr "repo.issues.filter_type.created_by_you"}}</a> | |||||
<a class="{{if eq .ViewType "mentioned"}}active{{end}} item" href="{{$.RepoLink}}/issues?type=mentioned&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}">{{.i18n.Tr "repo.issues.filter_type.mentioning_you"}}</a> | |||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<a class="title" href="{{$.RepoLink}}/issues/{{.Index}}">{{.Name}}</a> | <a class="title" href="{{$.RepoLink}}/issues/{{.Index}}">{{.Name}}</a> | ||||
{{range .Labels}} | {{range .Labels}} | ||||
<a class="ui label" href="{{$.RepoLink}}/issues?type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}" style="background-color: {{.Color}}">{{.Name}}</a> | |||||
<a class="ui label" href="{{$.RepoLink}}/issues?type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}&milestone={{$.MilestoneID}}" style="background-color: {{.Color}}">{{.Name}}</a> | |||||
{{end}} | {{end}} | ||||
{{if .NumComments}}<span class="comment ui right"><i class="octicon octicon-comment"></i> {{.NumComments}}</span>{{end}} | |||||
<p class="desc">{{$.i18n.Tr "repo.issues.opened_by" $timeStr .Poster.Name|Str2html}}</p> | |||||
{{if .NumComments}} | |||||
<span class="comment ui right"><i class="octicon octicon-comment"></i> {{.NumComments}}</span> | |||||
{{end}} | |||||
<p class="desc"> | |||||
{{$.i18n.Tr "repo.issues.opened_by" $timeStr .Poster.Name|Str2html}} | |||||
{{if .Milestone}} | |||||
{{with .Milestone}} | |||||
<a class="milestone" href="{{$.RepoLink}}/issues?type={{$.ViewType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{.ID}}"> | |||||
<span class="octicon octicon-milestone"></span> {{.Name}} | |||||
</a> | |||||
{{end}} | |||||
{{end}} | |||||
</p> | |||||
</li> | </li> | ||||
{{end}} | {{end}} | ||||
{{if gt .TotalPages 1}} | {{if gt .TotalPages 1}} | ||||
<div class="center page buttons"> | <div class="center page buttons"> | ||||
<div class="ui borderless pagination menu"> | <div class="ui borderless pagination menu"> | ||||
<a class="{{if not .HasPrevious}}disabled{{end}} item" {{if .HasPrevious}}href="{{$.Link}}?type={{$.ViewType}}&state={{$.State}}&labels={{$.SelectLabels}}&page={{.Previous}}"{{end}}> | |||||
<a class="{{if not .HasPrevious}}disabled{{end}} item" {{if .HasPrevious}}href="{{$.Link}}?type={{$.ViewType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&page={{.Previous}}"{{end}}> | |||||
<i class="left arrow icon"></i> {{$.i18n.Tr "repo.issues.previous"}} | <i class="left arrow icon"></i> {{$.i18n.Tr "repo.issues.previous"}} | ||||
</a> | </a> | ||||
{{range .Pages}} | {{range .Pages}} | ||||
{{if eq .Num -1}} | {{if eq .Num -1}} | ||||
<a class="disabled item">...</a> | <a class="disabled item">...</a> | ||||
{{else}} | {{else}} | ||||
<a class="{{if .IsCurrent}}active{{end}} item" {{if not .IsCurrent}}href="{{$.Link}}?type={{$.ViewType}}&state={{$.State}}&labels={{$.SelectLabels}}&page={{.Num}}"{{end}}>{{.Num}}</a> | |||||
<a class="{{if .IsCurrent}}active{{end}} item" {{if not .IsCurrent}}href="{{$.Link}}?type={{$.ViewType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&page={{.Num}}"{{end}}>{{.Num}}</a> | |||||
{{end}} | {{end}} | ||||
{{end}} | {{end}} | ||||
<a class="{{if not .HasNext}}disabled{{end}} item" {{if .HasNext}}href="{{$.Link}}?type={{$.ViewType}}&state={{$.State}}&labels={{$.SelectLabels}}&page={{.Next}}"{{end}}> | |||||
<a class="{{if not .HasNext}}disabled{{end}} item" {{if .HasNext}}href="{{$.Link}}?type={{$.ViewType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&page={{.Next}}"{{end}}> | |||||
{{$.i18n.Tr "repo.issues.next"}} <i class="icon right arrow"></i> | {{$.i18n.Tr "repo.issues.next"}} <i class="icon right arrow"></i> | ||||
</a> | </a> | ||||
</div> | </div> |
{{template "base/head" .}} | {{template "base/head" .}} | ||||
<div class="repository"> | |||||
<div class="repository milestones"> | |||||
{{template "repo/header" .}} | {{template "repo/header" .}} | ||||
<div class="ui middle page grid body"> | <div class="ui middle page grid body"> | ||||
<div class="navbar"> | <div class="navbar"> | ||||
{{template "repo/issue/navbar" .}} | {{template "repo/issue/navbar" .}} | ||||
{{if .IsRepositoryAdmin}} | {{if .IsRepositoryAdmin}} | ||||
<div class="ui right floated secondary menu"> | <div class="ui right floated secondary menu"> | ||||
<a class="ui green button" href="{{$.RepoLink}}/milestones/new">{{.i18n.Tr "repo.milestones.new"}}</a> | |||||
<a class="ui green button" href="{{$.Link}}/new">{{.i18n.Tr "repo.milestones.new"}}</a> | |||||
</div> | </div> | ||||
{{end}} | {{end}} | ||||
</div> | </div> | ||||
<div class="milestone list"> | <div class="milestone list"> | ||||
{{range .Milestones}} | {{range .Milestones}} | ||||
<li class="item"> | <li class="item"> | ||||
<i class="octicon octicon-milestone"></i> <a href="{{$.RepoLink}}/issues?state={{$.State}}&midx={{.Index}}">{{.Name}}</a> | |||||
<i class="octicon octicon-milestone"></i> <a href="{{$.RepoLink}}/issues?state={{$.State}}&milestone={{.ID}}">{{.Name}}</a> | |||||
<div class="ui right blue progress" data-percent="{{.Completeness}}"> | <div class="ui right blue progress" data-percent="{{.Completeness}}"> | ||||
<div class="bar" {{if not .Completeness}}style="background-color: transparent"{{end}}> | <div class="bar" {{if not .Completeness}}style="background-color: transparent"{{end}}> | ||||
<div class="progress"></div> | <div class="progress"></div> | ||||
</div> | </div> | ||||
{{if $.IsRepositoryAdmin}} | {{if $.IsRepositoryAdmin}} | ||||
<div class="ui right operate"> | <div class="ui right operate"> | ||||
<a href="{{$.RepoLink}}/milestones/{{.Index}}/edit" data-id={{.ID}} data-title={{.Name}}><i class="octicon octicon-pencil"></i> {{$.i18n.Tr "repo.issues.label_edit"}}</a> | |||||
<a href="{{$.Link}}/{{.Index}}/edit" data-id={{.ID}} data-title={{.Name}}><i class="octicon octicon-pencil"></i> {{$.i18n.Tr "repo.issues.label_edit"}}</a> | |||||
{{if .IsClosed}} | {{if .IsClosed}} | ||||
<a href="{{$.RepoLink}}/milestones/{{.Index}}/open" data-id={{.ID}} data-title={{.Name}}><i class="octicon octicon-check"></i> {{$.i18n.Tr "repo.milestones.open"}}</a> | |||||
<a href="{{$.Link}}/{{.Index}}/open" data-id={{.ID}} data-title={{.Name}}><i class="octicon octicon-check"></i> {{$.i18n.Tr "repo.milestones.open"}}</a> | |||||
{{else}} | {{else}} | ||||
<a href="{{$.RepoLink}}/milestones/{{.Index}}/close" data-id={{.ID}} data-title={{.Name}}><i class="octicon octicon-x"></i> {{$.i18n.Tr "repo.milestones.close"}}</a> | |||||
<a href="{{$.Link}}/{{.Index}}/close" data-id={{.ID}} data-title={{.Name}}><i class="octicon octicon-x"></i> {{$.i18n.Tr "repo.milestones.close"}}</a> | |||||
{{end}} | {{end}} | ||||
<a class="delete-milestone-button" href="#" data-url="{{$.RepoLink}}/milestone/delete" data-id="{{.ID}}"><i class="octicon octicon-trashcan"></i> {{$.i18n.Tr "repo.issues.label_delete"}}</a> | |||||
<a class="delete-button" href="#" data-url="{{$.RepoLink}}/milestones/delete" data-id="{{.ID}}"><i class="octicon octicon-trashcan"></i> {{$.i18n.Tr "repo.issues.label_delete"}}</a> | |||||
</div> | </div> | ||||
{{if .Content}} | {{if .Content}} | ||||
<div class="content"> | <div class="content"> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
{{if .IsRepositoryAdmin}} | |||||
<div class="ui basic delete modal"> | |||||
<div class="header"> | |||||
{{.i18n.Tr "repo.milestones.deletion"}} | |||||
</div> | |||||
<div class="content"> | |||||
<div class="image"> | |||||
<i class="trash icon"></i> | |||||
</div> | |||||
<div class="description"> | |||||
<p>{{.i18n.Tr "repo.milestones.deletion_desc"}}</p> | |||||
</div> | |||||
</div> | |||||
<div class="actions"> | |||||
<div class="two fluid ui inverted buttons"> | |||||
<div class="ui red basic inverted button"> | |||||
<i class="remove icon"></i> | |||||
{{.i18n.Tr "modal.no"}} | |||||
</div> | |||||
<div class="ui green basic inverted positive button"> | |||||
<i class="checkmark icon"></i> | |||||
{{.i18n.Tr "modal.yes"}} | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
{{end}} | |||||
{{template "base/footer" .}} | {{template "base/footer" .}} |
{{else}} | {{else}} | ||||
<ul class="list-unstyled"> | <ul class="list-unstyled"> | ||||
{{range .ClosedMilestones}} | {{range .ClosedMilestones}} | ||||
<li class="milestone-item" data-id="{{.Id}}"> | |||||
<li class="milestone-item" data-id="{{.ID}}"> | |||||
<p><strong>{{.Name}}</strong></p> | <p><strong>{{.Name}}</strong></p> | ||||
<p>Closed {{TimeSince .ClosedDate $.Lang}}</p> | <p>Closed {{TimeSince .ClosedDate $.Lang}}</p> | ||||
</li> | </li> |