2018-11-29 04:46:30 +03:00
// Copyright 2018 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package repo
import (
2021-04-05 18:30:52 +03:00
"net/http"
2018-11-29 04:46:30 +03:00
"time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
2020-07-28 14:30:40 +03:00
"code.gitea.io/gitea/modules/structs"
2019-08-15 17:46:21 +03:00
"code.gitea.io/gitea/modules/timeutil"
2018-11-29 04:46:30 +03:00
"code.gitea.io/gitea/modules/util"
2021-01-26 18:36:53 +03:00
"code.gitea.io/gitea/modules/web"
2021-04-06 22:44:05 +03:00
"code.gitea.io/gitea/services/forms"
2020-05-13 00:54:35 +03:00
"xorm.io/builder"
2018-11-29 04:46:30 +03:00
)
const (
tplMilestone base . TplName = "repo/issue/milestones"
tplMilestoneNew base . TplName = "repo/issue/milestone_new"
tplMilestoneIssues base . TplName = "repo/issue/milestone_issues"
)
// Milestones render milestones page
func Milestones ( ctx * context . Context ) {
ctx . Data [ "Title" ] = ctx . Tr ( "repo.milestones" )
ctx . Data [ "PageIsIssueList" ] = true
ctx . Data [ "PageIsMilestones" ] = true
isShowClosed := ctx . Query ( "state" ) == "closed"
2020-05-13 00:54:35 +03:00
stats , err := models . GetMilestonesStatsByRepoCond ( builder . And ( builder . Eq { "id" : ctx . Repo . Repository . ID } ) )
2018-11-29 04:46:30 +03:00
if err != nil {
ctx . ServerError ( "MilestoneStats" , err )
return
}
2020-05-13 00:54:35 +03:00
ctx . Data [ "OpenCount" ] = stats . OpenCount
ctx . Data [ "ClosedCount" ] = stats . ClosedCount
2018-11-29 04:46:30 +03:00
sortType := ctx . Query ( "sort" )
page := ctx . QueryInt ( "page" )
if page <= 1 {
page = 1
}
var total int
2020-07-28 14:30:40 +03:00
var state structs . StateType
2018-11-29 04:46:30 +03:00
if ! isShowClosed {
2020-05-13 00:54:35 +03:00
total = int ( stats . OpenCount )
2020-07-28 14:30:40 +03:00
state = structs . StateOpen
2018-11-29 04:46:30 +03:00
} else {
2020-05-13 00:54:35 +03:00
total = int ( stats . ClosedCount )
2020-07-28 14:30:40 +03:00
state = structs . StateClosed
2018-11-29 04:46:30 +03:00
}
2020-07-28 14:30:40 +03:00
miles , err := models . GetMilestones ( models . GetMilestonesOption {
ListOptions : models . ListOptions {
Page : page ,
PageSize : setting . UI . IssuePagingNum ,
} ,
RepoID : ctx . Repo . Repository . ID ,
State : state ,
SortType : sortType ,
} )
2018-11-29 04:46:30 +03:00
if err != nil {
ctx . ServerError ( "GetMilestones" , err )
return
}
if ctx . Repo . Repository . IsTimetrackerEnabled ( ) {
2019-06-12 22:41:28 +03:00
if err := miles . LoadTotalTrackedTimes ( ) ; err != nil {
2018-11-29 04:46:30 +03:00
ctx . ServerError ( "LoadTotalTrackedTimes" , err )
return
}
}
for _ , m := range miles {
m . RenderedContent = string ( markdown . Render ( [ ] byte ( m . Content ) , ctx . Repo . RepoLink , ctx . Repo . Repository . ComposeMetas ( ) ) )
}
ctx . Data [ "Milestones" ] = miles
if isShowClosed {
ctx . Data [ "State" ] = "closed"
} else {
ctx . Data [ "State" ] = "open"
}
ctx . Data [ "SortType" ] = sortType
ctx . Data [ "IsShowClosed" ] = isShowClosed
2019-04-20 07:15:19 +03:00
pager := context . NewPagination ( total , setting . UI . IssuePagingNum , page , 5 )
pager . AddParam ( ctx , "state" , "State" )
ctx . Data [ "Page" ] = pager
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tplMilestone )
2018-11-29 04:46:30 +03:00
}
// NewMilestone render creating milestone page
func NewMilestone ( ctx * context . Context ) {
ctx . Data [ "Title" ] = ctx . Tr ( "repo.milestones.new" )
ctx . Data [ "PageIsIssueList" ] = true
ctx . Data [ "PageIsMilestones" ] = true
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tplMilestoneNew )
2018-11-29 04:46:30 +03:00
}
// NewMilestonePost response for creating milestone
2021-01-26 18:36:53 +03:00
func NewMilestonePost ( ctx * context . Context ) {
2021-04-06 22:44:05 +03:00
form := web . GetForm ( ctx ) . ( * forms . CreateMilestoneForm )
2018-11-29 04:46:30 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "repo.milestones.new" )
ctx . Data [ "PageIsIssueList" ] = true
ctx . Data [ "PageIsMilestones" ] = true
if ctx . HasError ( ) {
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tplMilestoneNew )
2018-11-29 04:46:30 +03:00
return
}
if len ( form . Deadline ) == 0 {
form . Deadline = "9999-12-31"
}
deadline , err := time . ParseInLocation ( "2006-01-02" , form . Deadline , time . Local )
if err != nil {
ctx . Data [ "Err_Deadline" ] = true
ctx . RenderWithErr ( ctx . Tr ( "repo.milestones.invalid_due_date_format" ) , tplMilestoneNew , & form )
return
}
2019-01-01 20:56:47 +03:00
deadline = time . Date ( deadline . Year ( ) , deadline . Month ( ) , deadline . Day ( ) , 23 , 59 , 59 , 0 , deadline . Location ( ) )
2018-11-29 04:46:30 +03:00
if err = models . NewMilestone ( & models . Milestone {
RepoID : ctx . Repo . Repository . ID ,
Name : form . Title ,
Content : form . Content ,
2019-08-15 17:46:21 +03:00
DeadlineUnix : timeutil . TimeStamp ( deadline . Unix ( ) ) ,
2018-11-29 04:46:30 +03:00
} ) ; err != nil {
ctx . ServerError ( "NewMilestone" , err )
return
}
ctx . Flash . Success ( ctx . Tr ( "repo.milestones.create_success" , form . Title ) )
ctx . Redirect ( ctx . Repo . RepoLink + "/milestones" )
}
// EditMilestone render edting milestone page
func EditMilestone ( ctx * context . Context ) {
ctx . Data [ "Title" ] = ctx . Tr ( "repo.milestones.edit" )
ctx . Data [ "PageIsMilestones" ] = true
ctx . Data [ "PageIsEditMilestone" ] = true
m , err := models . GetMilestoneByRepoID ( ctx . Repo . Repository . ID , ctx . ParamsInt64 ( ":id" ) )
if err != nil {
if models . IsErrMilestoneNotExist ( err ) {
ctx . NotFound ( "" , nil )
} else {
ctx . ServerError ( "GetMilestoneByRepoID" , err )
}
return
}
ctx . Data [ "title" ] = m . Name
ctx . Data [ "content" ] = m . Content
if len ( m . DeadlineString ) > 0 {
ctx . Data [ "deadline" ] = m . DeadlineString
}
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tplMilestoneNew )
2018-11-29 04:46:30 +03:00
}
// EditMilestonePost response for edting milestone
2021-01-26 18:36:53 +03:00
func EditMilestonePost ( ctx * context . Context ) {
2021-04-06 22:44:05 +03:00
form := web . GetForm ( ctx ) . ( * forms . CreateMilestoneForm )
2018-11-29 04:46:30 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "repo.milestones.edit" )
ctx . Data [ "PageIsMilestones" ] = true
ctx . Data [ "PageIsEditMilestone" ] = true
if ctx . HasError ( ) {
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tplMilestoneNew )
2018-11-29 04:46:30 +03:00
return
}
if len ( form . Deadline ) == 0 {
form . Deadline = "9999-12-31"
}
deadline , err := time . ParseInLocation ( "2006-01-02" , form . Deadline , time . Local )
if err != nil {
ctx . Data [ "Err_Deadline" ] = true
ctx . RenderWithErr ( ctx . Tr ( "repo.milestones.invalid_due_date_format" ) , tplMilestoneNew , & form )
return
}
2019-01-01 20:56:47 +03:00
deadline = time . Date ( deadline . Year ( ) , deadline . Month ( ) , deadline . Day ( ) , 23 , 59 , 59 , 0 , deadline . Location ( ) )
2018-11-29 04:46:30 +03:00
m , err := models . GetMilestoneByRepoID ( ctx . Repo . Repository . ID , ctx . ParamsInt64 ( ":id" ) )
if err != nil {
if models . IsErrMilestoneNotExist ( err ) {
ctx . NotFound ( "" , nil )
} else {
ctx . ServerError ( "GetMilestoneByRepoID" , err )
}
return
}
m . Name = form . Title
m . Content = form . Content
2019-08-15 17:46:21 +03:00
m . DeadlineUnix = timeutil . TimeStamp ( deadline . Unix ( ) )
2020-01-29 09:36:32 +03:00
if err = models . UpdateMilestone ( m , m . IsClosed ) ; err != nil {
2018-11-29 04:46:30 +03:00
ctx . ServerError ( "UpdateMilestone" , err )
return
}
ctx . Flash . Success ( ctx . Tr ( "repo.milestones.edit_success" , m . Name ) )
ctx . Redirect ( ctx . Repo . RepoLink + "/milestones" )
}
2020-08-17 06:07:38 +03:00
// ChangeMilestoneStatus response for change a milestone's status
func ChangeMilestoneStatus ( ctx * context . Context ) {
toClose := false
2018-11-29 04:46:30 +03:00
switch ctx . Params ( ":action" ) {
case "open" :
2020-08-17 06:07:38 +03:00
toClose = false
2018-11-29 04:46:30 +03:00
case "close" :
2020-08-17 06:07:38 +03:00
toClose = true
2018-11-29 04:46:30 +03:00
default :
ctx . Redirect ( ctx . Repo . RepoLink + "/milestones" )
}
2020-08-17 06:07:38 +03:00
id := ctx . ParamsInt64 ( ":id" )
if err := models . ChangeMilestoneStatusByRepoIDAndID ( ctx . Repo . Repository . ID , id , toClose ) ; err != nil {
if models . IsErrMilestoneNotExist ( err ) {
ctx . NotFound ( "" , err )
} else {
ctx . ServerError ( "ChangeMilestoneStatusByIDAndRepoID" , err )
}
return
}
ctx . Redirect ( ctx . Repo . RepoLink + "/milestones?state=" + ctx . Params ( ":action" ) )
2018-11-29 04:46:30 +03:00
}
// DeleteMilestone delete a milestone
func DeleteMilestone ( ctx * context . Context ) {
if err := models . DeleteMilestoneByRepoID ( ctx . Repo . Repository . ID , ctx . QueryInt64 ( "id" ) ) ; err != nil {
ctx . Flash . Error ( "DeleteMilestoneByRepoID: " + err . Error ( ) )
} else {
ctx . Flash . Success ( ctx . Tr ( "repo.milestones.deletion_success" ) )
}
2021-04-05 18:30:52 +03:00
ctx . JSON ( http . StatusOK , map [ string ] interface { } {
2018-11-29 04:46:30 +03:00
"redirect" : ctx . Repo . RepoLink + "/milestones" ,
} )
}
// MilestoneIssuesAndPulls lists all the issues and pull requests of the milestone
func MilestoneIssuesAndPulls ( ctx * context . Context ) {
milestoneID := ctx . ParamsInt64 ( ":id" )
milestone , err := models . GetMilestoneByID ( milestoneID )
if err != nil {
2019-08-15 02:43:50 +03:00
if models . IsErrMilestoneNotExist ( err ) {
ctx . NotFound ( "GetMilestoneByID" , err )
return
}
2018-11-29 04:46:30 +03:00
ctx . ServerError ( "GetMilestoneByID" , err )
return
}
2020-06-26 04:21:13 +03:00
milestone . RenderedContent = string ( markdown . Render ( [ ] byte ( milestone . Content ) , ctx . Repo . RepoLink , ctx . Repo . Repository . ComposeMetas ( ) ) )
2018-11-29 04:46:30 +03:00
ctx . Data [ "Title" ] = milestone . Name
ctx . Data [ "Milestone" ] = milestone
2020-08-17 06:07:38 +03:00
issues ( ctx , milestoneID , 0 , util . OptionalBoolNone )
2020-09-11 17:48:39 +03:00
ctx . Data [ "NewIssueChooseTemplate" ] = len ( ctx . IssueTemplatesFromDefaultBranch ( ) ) > 0
2018-11-29 04:46:30 +03:00
2020-01-16 17:18:30 +03:00
ctx . Data [ "CanWriteIssues" ] = ctx . Repo . CanWriteIssuesOrPulls ( false )
ctx . Data [ "CanWritePulls" ] = ctx . Repo . CanWriteIssuesOrPulls ( true )
2019-03-15 18:50:27 +03:00
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tplMilestoneIssues )
2018-11-29 04:46:30 +03:00
}