2018-11-29 09:46:30 +08: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 17:30:52 +02:00
"net/http"
2018-11-29 09:46:30 +08:00
"time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
2021-04-20 06:25:08 +08:00
"code.gitea.io/gitea/modules/markup"
2018-11-29 09:46:30 +08:00
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
2020-07-28 13:30:40 +02:00
"code.gitea.io/gitea/modules/structs"
2019-08-15 22:46:21 +08:00
"code.gitea.io/gitea/modules/timeutil"
2018-11-29 09:46:30 +08:00
"code.gitea.io/gitea/modules/util"
2021-01-26 23:36:53 +08:00
"code.gitea.io/gitea/modules/web"
2021-04-06 20:44:05 +01:00
"code.gitea.io/gitea/services/forms"
2020-05-12 23:54:35 +02:00
"xorm.io/builder"
2018-11-29 09:46:30 +08: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
2021-08-11 02:31:13 +02:00
isShowClosed := ctx . FormString ( "state" ) == "closed"
2020-05-12 23:54:35 +02:00
stats , err := models . GetMilestonesStatsByRepoCond ( builder . And ( builder . Eq { "id" : ctx . Repo . Repository . ID } ) )
2018-11-29 09:46:30 +08:00
if err != nil {
ctx . ServerError ( "MilestoneStats" , err )
return
}
2020-05-12 23:54:35 +02:00
ctx . Data [ "OpenCount" ] = stats . OpenCount
ctx . Data [ "ClosedCount" ] = stats . ClosedCount
2018-11-29 09:46:30 +08:00
2021-08-11 02:31:13 +02:00
sortType := ctx . FormString ( "sort" )
2021-04-08 19:53:59 +08:00
2021-08-11 17:08:52 +02:00
keyword := ctx . FormTrim ( "q" )
2021-04-08 19:53:59 +08:00
2021-07-29 09:42:15 +08:00
page := ctx . FormInt ( "page" )
2018-11-29 09:46:30 +08:00
if page <= 1 {
page = 1
}
var total int
2020-07-28 13:30:40 +02:00
var state structs . StateType
2018-11-29 09:46:30 +08:00
if ! isShowClosed {
2020-05-12 23:54:35 +02:00
total = int ( stats . OpenCount )
2020-07-28 13:30:40 +02:00
state = structs . StateOpen
2018-11-29 09:46:30 +08:00
} else {
2020-05-12 23:54:35 +02:00
total = int ( stats . ClosedCount )
2020-07-28 13:30:40 +02:00
state = structs . StateClosed
2018-11-29 09:46:30 +08:00
}
2020-07-28 13:30:40 +02: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 ,
2021-04-08 19:53:59 +08:00
Name : keyword ,
2020-07-28 13:30:40 +02:00
} )
2018-11-29 09:46:30 +08:00
if err != nil {
ctx . ServerError ( "GetMilestones" , err )
return
}
if ctx . Repo . Repository . IsTimetrackerEnabled ( ) {
2019-06-12 21:41:28 +02:00
if err := miles . LoadTotalTrackedTimes ( ) ; err != nil {
2018-11-29 09:46:30 +08:00
ctx . ServerError ( "LoadTotalTrackedTimes" , err )
return
}
}
for _ , m := range miles {
2021-04-20 06:25:08 +08:00
m . RenderedContent , err = markdown . RenderString ( & markup . RenderContext {
URLPrefix : ctx . Repo . RepoLink ,
Metas : ctx . Repo . Repository . ComposeMetas ( ) ,
2021-06-20 23:39:12 +01:00
GitRepo : ctx . Repo . GitRepo ,
2021-04-20 06:25:08 +08:00
} , m . Content )
if err != nil {
ctx . ServerError ( "RenderString" , err )
return
}
2018-11-29 09:46:30 +08:00
}
ctx . Data [ "Milestones" ] = miles
if isShowClosed {
ctx . Data [ "State" ] = "closed"
} else {
ctx . Data [ "State" ] = "open"
}
ctx . Data [ "SortType" ] = sortType
2021-04-08 19:53:59 +08:00
ctx . Data [ "Keyword" ] = keyword
2018-11-29 09:46:30 +08:00
ctx . Data [ "IsShowClosed" ] = isShowClosed
2019-04-20 06:15:19 +02:00
pager := context . NewPagination ( total , setting . UI . IssuePagingNum , page , 5 )
pager . AddParam ( ctx , "state" , "State" )
2021-04-08 19:53:59 +08:00
pager . AddParam ( ctx , "q" , "Keyword" )
2019-04-20 06:15:19 +02:00
ctx . Data [ "Page" ] = pager
2021-04-05 17:30:52 +02:00
ctx . HTML ( http . StatusOK , tplMilestone )
2018-11-29 09:46:30 +08: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 17:30:52 +02:00
ctx . HTML ( http . StatusOK , tplMilestoneNew )
2018-11-29 09:46:30 +08:00
}
// NewMilestonePost response for creating milestone
2021-01-26 23:36:53 +08:00
func NewMilestonePost ( ctx * context . Context ) {
2021-04-06 20:44:05 +01:00
form := web . GetForm ( ctx ) . ( * forms . CreateMilestoneForm )
2018-11-29 09:46:30 +08:00
ctx . Data [ "Title" ] = ctx . Tr ( "repo.milestones.new" )
ctx . Data [ "PageIsIssueList" ] = true
ctx . Data [ "PageIsMilestones" ] = true
if ctx . HasError ( ) {
2021-04-05 17:30:52 +02:00
ctx . HTML ( http . StatusOK , tplMilestoneNew )
2018-11-29 09:46:30 +08: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 14:56:47 -03:00
deadline = time . Date ( deadline . Year ( ) , deadline . Month ( ) , deadline . Day ( ) , 23 , 59 , 59 , 0 , deadline . Location ( ) )
2018-11-29 09:46:30 +08:00
if err = models . NewMilestone ( & models . Milestone {
RepoID : ctx . Repo . Repository . ID ,
Name : form . Title ,
Content : form . Content ,
2019-08-15 22:46:21 +08:00
DeadlineUnix : timeutil . TimeStamp ( deadline . Unix ( ) ) ,
2018-11-29 09:46:30 +08: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 17:30:52 +02:00
ctx . HTML ( http . StatusOK , tplMilestoneNew )
2018-11-29 09:46:30 +08:00
}
// EditMilestonePost response for edting milestone
2021-01-26 23:36:53 +08:00
func EditMilestonePost ( ctx * context . Context ) {
2021-04-06 20:44:05 +01:00
form := web . GetForm ( ctx ) . ( * forms . CreateMilestoneForm )
2018-11-29 09:46:30 +08:00
ctx . Data [ "Title" ] = ctx . Tr ( "repo.milestones.edit" )
ctx . Data [ "PageIsMilestones" ] = true
ctx . Data [ "PageIsEditMilestone" ] = true
if ctx . HasError ( ) {
2021-04-05 17:30:52 +02:00
ctx . HTML ( http . StatusOK , tplMilestoneNew )
2018-11-29 09:46:30 +08: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 14:56:47 -03:00
deadline = time . Date ( deadline . Year ( ) , deadline . Month ( ) , deadline . Day ( ) , 23 , 59 , 59 , 0 , deadline . Location ( ) )
2018-11-29 09:46:30 +08: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 22:46:21 +08:00
m . DeadlineUnix = timeutil . TimeStamp ( deadline . Unix ( ) )
2020-01-29 14:36:32 +08:00
if err = models . UpdateMilestone ( m , m . IsClosed ) ; err != nil {
2018-11-29 09:46:30 +08: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 04:07:38 +01:00
// ChangeMilestoneStatus response for change a milestone's status
func ChangeMilestoneStatus ( ctx * context . Context ) {
toClose := false
2018-11-29 09:46:30 +08:00
switch ctx . Params ( ":action" ) {
case "open" :
2020-08-17 04:07:38 +01:00
toClose = false
2018-11-29 09:46:30 +08:00
case "close" :
2020-08-17 04:07:38 +01:00
toClose = true
2018-11-29 09:46:30 +08:00
default :
ctx . Redirect ( ctx . Repo . RepoLink + "/milestones" )
}
2020-08-17 04:07:38 +01: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 09:46:30 +08:00
}
// DeleteMilestone delete a milestone
func DeleteMilestone ( ctx * context . Context ) {
2021-07-29 09:42:15 +08:00
if err := models . DeleteMilestoneByRepoID ( ctx . Repo . Repository . ID , ctx . FormInt64 ( "id" ) ) ; err != nil {
2018-11-29 09:46:30 +08:00
ctx . Flash . Error ( "DeleteMilestoneByRepoID: " + err . Error ( ) )
} else {
ctx . Flash . Success ( ctx . Tr ( "repo.milestones.deletion_success" ) )
}
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusOK , map [ string ] interface { } {
2018-11-29 09:46:30 +08: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 00:43:50 +01:00
if models . IsErrMilestoneNotExist ( err ) {
ctx . NotFound ( "GetMilestoneByID" , err )
return
}
2018-11-29 09:46:30 +08:00
ctx . ServerError ( "GetMilestoneByID" , err )
return
}
2021-04-20 06:25:08 +08:00
milestone . RenderedContent , err = markdown . RenderString ( & markup . RenderContext {
URLPrefix : ctx . Repo . RepoLink ,
Metas : ctx . Repo . Repository . ComposeMetas ( ) ,
2021-06-20 23:39:12 +01:00
GitRepo : ctx . Repo . GitRepo ,
2021-04-20 06:25:08 +08:00
} , milestone . Content )
if err != nil {
ctx . ServerError ( "RenderString" , err )
return
}
2020-06-25 21:21:13 -04:00
2018-11-29 09:46:30 +08:00
ctx . Data [ "Title" ] = milestone . Name
ctx . Data [ "Milestone" ] = milestone
2020-08-17 04:07:38 +01:00
issues ( ctx , milestoneID , 0 , util . OptionalBoolNone )
2020-09-11 09:48:39 -05:00
ctx . Data [ "NewIssueChooseTemplate" ] = len ( ctx . IssueTemplatesFromDefaultBranch ( ) ) > 0
2018-11-29 09:46:30 +08:00
2020-01-16 22:18:30 +08:00
ctx . Data [ "CanWriteIssues" ] = ctx . Repo . CanWriteIssuesOrPulls ( false )
ctx . Data [ "CanWritePulls" ] = ctx . Repo . CanWriteIssuesOrPulls ( true )
2019-03-15 10:50:27 -05:00
2021-04-05 17:30:52 +02:00
ctx . HTML ( http . StatusOK , tplMilestoneIssues )
2018-11-29 09:46:30 +08:00
}