Browse Source

finish new milestone page

tags/v0.9.99
Unknwon 8 years ago
parent
commit
0705f55ce0

+ 1
- 1
README.md View File



![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



+ 2
- 1
cmd/web.go View File

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)

+ 4
- 0
conf/locale/locale_en-US.ini View File

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

+ 1
- 1
gogs.go View File

"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())

+ 3
- 3
models/action.go View File



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
} }

+ 89
- 41
models/issue.go View File

// 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()

+ 1
- 1
models/repo.go View File

} }
} }


if _, err = sess.Delete(&Issue{RepoId: repoID}); err != nil {
if _, err = sess.Delete(&Issue{RepoID: repoID}); err != nil {
return err return err
} }



+ 2
- 2
modules/bindata/bindata.go
File diff suppressed because it is too large
View File


+ 1
- 1
public/css/gogs.min.css
File diff suppressed because it is too large
View File


+ 20
- 15
public/js/gogs.js View File

$('.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();
}); });

+ 11
- 3
public/less/_repository.less View File

.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;
}
}
} }
} }
} }

+ 52
- 105
routers/repo/issue.go View File

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) {

+ 3
- 3
routers/user/home.go View File

} }
} }


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
} }
} }

+ 1
- 1
templates/.VERSION View File

0.6.3.0805 Beta
0.6.4.0805 Beta

+ 1
- 1
templates/repo/issue/create.tmpl View File

{{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>

+ 2
- 2
templates/repo/issue/labels.tmpl View File

<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>

+ 30
- 17
templates/repo/issue/list.tmpl View File

<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>

+ 35
- 7
templates/repo/issue/milestones.tmpl View File

{{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" .}}

+ 1
- 1
templates/repo/issue/view.tmpl View File

{{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>

Loading…
Cancel
Save