2020-08-17 04:07:38 +01:00
// Copyright 2020 The Gitea Authors. All rights reserved.
2022-11-27 13:20:29 -05:00
// SPDX-License-Identifier: MIT
2020-08-17 04:07:38 +01:00
package repo
import (
2022-06-30 23:55:08 +08:00
"errors"
2020-08-17 04:07:38 +01:00
"fmt"
2021-04-05 17:30:52 +02:00
"net/http"
2021-11-16 18:18:25 +00:00
"net/url"
2020-08-17 04:07:38 +01:00
"strings"
2022-06-13 17:37:59 +08:00
issues_model "code.gitea.io/gitea/models/issues"
2021-11-28 19:58:28 +08:00
"code.gitea.io/gitea/models/perm"
2022-03-29 22:16:31 +08:00
project_model "code.gitea.io/gitea/models/project"
2021-11-10 03:57:58 +08:00
"code.gitea.io/gitea/models/unit"
2020-08-17 04:07:38 +01:00
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
2021-12-17 02:15:02 +01:00
"code.gitea.io/gitea/modules/json"
2021-04-20 06:25:08 +08:00
"code.gitea.io/gitea/modules/markup"
2020-08-17 04:07:38 +01:00
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
"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-08-17 04:07:38 +01:00
)
const (
tplProjects base . TplName = "repo/projects/list"
tplProjectsNew base . TplName = "repo/projects/new"
tplProjectsView base . TplName = "repo/projects/view"
tplGenericProjectsNew base . TplName = "user/project"
)
// MustEnableProjects check if projects are enabled in settings
func MustEnableProjects ( ctx * context . Context ) {
2021-11-10 03:57:58 +08:00
if unit . TypeProjects . UnitGlobalDisabled ( ) {
2020-08-17 04:07:38 +01:00
ctx . NotFound ( "EnableKanbanBoard" , nil )
return
}
if ctx . Repo . Repository != nil {
2021-11-10 03:57:58 +08:00
if ! ctx . Repo . CanRead ( unit . TypeProjects ) {
2020-08-17 04:07:38 +01:00
ctx . NotFound ( "MustEnableProjects" , nil )
return
}
}
}
// Projects renders the home page of projects
func Projects ( ctx * context . Context ) {
ctx . Data [ "Title" ] = ctx . Tr ( "repo.project_board" )
2021-07-29 09:42:15 +08:00
sortType := ctx . FormTrim ( "sort" )
2020-08-17 04:07:38 +01:00
2021-07-29 09:42:15 +08:00
isShowClosed := strings . ToLower ( ctx . FormTrim ( "state" ) ) == "closed"
2020-08-17 04:07:38 +01:00
repo := ctx . Repo . Repository
2021-07-29 09:42:15 +08:00
page := ctx . FormInt ( "page" )
2020-08-17 04:07:38 +01:00
if page <= 1 {
page = 1
}
ctx . Data [ "OpenCount" ] = repo . NumOpenProjects
ctx . Data [ "ClosedCount" ] = repo . NumClosedProjects
var total int
if ! isShowClosed {
total = repo . NumOpenProjects
} else {
total = repo . NumClosedProjects
}
2022-05-20 22:08:52 +08:00
projects , count , err := project_model . GetProjects ( ctx , project_model . SearchOptions {
2020-08-17 04:07:38 +01:00
RepoID : repo . ID ,
Page : page ,
IsClosed : util . OptionalBoolOf ( isShowClosed ) ,
SortType : sortType ,
2022-03-29 22:16:31 +08:00
Type : project_model . TypeRepository ,
2020-08-17 04:07:38 +01:00
} )
if err != nil {
ctx . ServerError ( "GetProjects" , err )
return
}
for i := range projects {
2021-04-20 06:25:08 +08:00
projects [ i ] . 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-08-28 21:15:56 +01:00
Ctx : ctx ,
2021-04-20 06:25:08 +08:00
} , projects [ i ] . Description )
if err != nil {
ctx . ServerError ( "RenderString" , err )
return
}
2020-08-17 04:07:38 +01:00
}
ctx . Data [ "Projects" ] = projects
if isShowClosed {
ctx . Data [ "State" ] = "closed"
} else {
ctx . Data [ "State" ] = "open"
}
numPages := 0
if count > 0 {
2022-06-20 12:02:49 +02:00
numPages = ( int ( count ) - 1 / setting . UI . IssuePagingNum )
2020-08-17 04:07:38 +01:00
}
pager := context . NewPagination ( total , setting . UI . IssuePagingNum , page , numPages )
pager . AddParam ( ctx , "state" , "State" )
ctx . Data [ "Page" ] = pager
2021-11-10 03:57:58 +08:00
ctx . Data [ "CanWriteProjects" ] = ctx . Repo . Permission . CanWrite ( unit . TypeProjects )
2020-08-17 04:07:38 +01:00
ctx . Data [ "IsShowClosed" ] = isShowClosed
ctx . Data [ "IsProjectsPage" ] = true
ctx . Data [ "SortType" ] = sortType
2021-04-05 17:30:52 +02:00
ctx . HTML ( http . StatusOK , tplProjects )
2020-08-17 04:07:38 +01:00
}
// NewProject render creating a project page
func NewProject ( ctx * context . Context ) {
ctx . Data [ "Title" ] = ctx . Tr ( "repo.projects.new" )
2022-03-29 22:16:31 +08:00
ctx . Data [ "ProjectTypes" ] = project_model . GetProjectsConfig ( )
2021-11-10 03:57:58 +08:00
ctx . Data [ "CanWriteProjects" ] = ctx . Repo . Permission . CanWrite ( unit . TypeProjects )
2021-04-05 17:30:52 +02:00
ctx . HTML ( http . StatusOK , tplProjectsNew )
2020-08-17 04:07:38 +01:00
}
2020-08-22 08:58:59 +02:00
// NewProjectPost creates a new project
2021-01-26 23:36:53 +08:00
func NewProjectPost ( ctx * context . Context ) {
2021-04-06 20:44:05 +01:00
form := web . GetForm ( ctx ) . ( * forms . CreateProjectForm )
2020-08-17 04:07:38 +01:00
ctx . Data [ "Title" ] = ctx . Tr ( "repo.projects.new" )
if ctx . HasError ( ) {
2021-11-10 03:57:58 +08:00
ctx . Data [ "CanWriteProjects" ] = ctx . Repo . Permission . CanWrite ( unit . TypeProjects )
2022-03-29 22:16:31 +08:00
ctx . Data [ "ProjectTypes" ] = project_model . GetProjectsConfig ( )
2021-04-05 17:30:52 +02:00
ctx . HTML ( http . StatusOK , tplProjectsNew )
2020-08-17 04:07:38 +01:00
return
}
2022-03-29 22:16:31 +08:00
if err := project_model . NewProject ( & project_model . Project {
2020-08-17 04:07:38 +01:00
RepoID : ctx . Repo . Repository . ID ,
Title : form . Title ,
Description : form . Content ,
2022-03-22 08:03:22 +01:00
CreatorID : ctx . Doer . ID ,
2020-08-17 04:07:38 +01:00
BoardType : form . BoardType ,
2022-03-29 22:16:31 +08:00
Type : project_model . TypeRepository ,
2020-08-17 04:07:38 +01:00
} ) ; err != nil {
ctx . ServerError ( "NewProject" , err )
return
}
ctx . Flash . Success ( ctx . Tr ( "repo.projects.create_success" , form . Title ) )
ctx . Redirect ( ctx . Repo . RepoLink + "/projects" )
}
// ChangeProjectStatus updates the status of a project between "open" and "close"
func ChangeProjectStatus ( ctx * context . Context ) {
toClose := false
switch ctx . Params ( ":action" ) {
case "open" :
toClose = false
case "close" :
toClose = true
default :
ctx . Redirect ( ctx . Repo . RepoLink + "/projects" )
}
id := ctx . ParamsInt64 ( ":id" )
2022-03-29 22:16:31 +08:00
if err := project_model . ChangeProjectStatusByRepoIDAndID ( ctx . Repo . Repository . ID , id , toClose ) ; err != nil {
if project_model . IsErrProjectNotExist ( err ) {
2020-08-17 04:07:38 +01:00
ctx . NotFound ( "" , err )
} else {
ctx . ServerError ( "ChangeProjectStatusByIDAndRepoID" , err )
}
return
}
2021-11-16 18:18:25 +00:00
ctx . Redirect ( ctx . Repo . RepoLink + "/projects?state=" + url . QueryEscape ( ctx . Params ( ":action" ) ) )
2020-08-17 04:07:38 +01:00
}
// DeleteProject delete a project
func DeleteProject ( ctx * context . Context ) {
2022-05-20 22:08:52 +08:00
p , err := project_model . GetProjectByID ( ctx , ctx . ParamsInt64 ( ":id" ) )
2020-08-17 04:07:38 +01:00
if err != nil {
2022-03-29 22:16:31 +08:00
if project_model . IsErrProjectNotExist ( err ) {
2020-08-17 04:07:38 +01:00
ctx . NotFound ( "" , nil )
} else {
ctx . ServerError ( "GetProjectByID" , err )
}
return
}
if p . RepoID != ctx . Repo . Repository . ID {
ctx . NotFound ( "" , nil )
return
}
2022-12-10 10:46:31 +08:00
if err := project_model . DeleteProjectByID ( ctx , p . ID ) ; err != nil {
2020-08-17 04:07:38 +01:00
ctx . Flash . Error ( "DeleteProjectByID: " + err . Error ( ) )
} else {
ctx . Flash . Success ( ctx . Tr ( "repo.projects.deletion_success" ) )
}
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusOK , map [ string ] interface { } {
2020-08-17 04:07:38 +01:00
"redirect" : ctx . Repo . RepoLink + "/projects" ,
} )
}
// EditProject allows a project to be edited
func EditProject ( ctx * context . Context ) {
ctx . Data [ "Title" ] = ctx . Tr ( "repo.projects.edit" )
ctx . Data [ "PageIsEditProjects" ] = true
2021-11-10 03:57:58 +08:00
ctx . Data [ "CanWriteProjects" ] = ctx . Repo . Permission . CanWrite ( unit . TypeProjects )
2020-08-17 04:07:38 +01:00
2022-05-20 22:08:52 +08:00
p , err := project_model . GetProjectByID ( ctx , ctx . ParamsInt64 ( ":id" ) )
2020-08-17 04:07:38 +01:00
if err != nil {
2022-03-29 22:16:31 +08:00
if project_model . IsErrProjectNotExist ( err ) {
2020-08-17 04:07:38 +01:00
ctx . NotFound ( "" , nil )
} else {
ctx . ServerError ( "GetProjectByID" , err )
}
return
}
if p . RepoID != ctx . Repo . Repository . ID {
ctx . NotFound ( "" , nil )
return
}
ctx . Data [ "title" ] = p . Title
ctx . Data [ "content" ] = p . Description
2021-04-05 17:30:52 +02:00
ctx . HTML ( http . StatusOK , tplProjectsNew )
2020-08-17 04:07:38 +01:00
}
// EditProjectPost response for editing a project
2021-01-26 23:36:53 +08:00
func EditProjectPost ( ctx * context . Context ) {
2021-04-06 20:44:05 +01:00
form := web . GetForm ( ctx ) . ( * forms . CreateProjectForm )
2020-08-17 04:07:38 +01:00
ctx . Data [ "Title" ] = ctx . Tr ( "repo.projects.edit" )
ctx . Data [ "PageIsEditProjects" ] = true
2021-11-10 03:57:58 +08:00
ctx . Data [ "CanWriteProjects" ] = ctx . Repo . Permission . CanWrite ( unit . TypeProjects )
2020-08-17 04:07:38 +01:00
if ctx . HasError ( ) {
2021-04-05 17:30:52 +02:00
ctx . HTML ( http . StatusOK , tplProjectsNew )
2020-08-17 04:07:38 +01:00
return
}
2022-05-20 22:08:52 +08:00
p , err := project_model . GetProjectByID ( ctx , ctx . ParamsInt64 ( ":id" ) )
2020-08-17 04:07:38 +01:00
if err != nil {
2022-03-29 22:16:31 +08:00
if project_model . IsErrProjectNotExist ( err ) {
2020-08-17 04:07:38 +01:00
ctx . NotFound ( "" , nil )
} else {
ctx . ServerError ( "GetProjectByID" , err )
}
return
}
if p . RepoID != ctx . Repo . Repository . ID {
ctx . NotFound ( "" , nil )
return
}
p . Title = form . Title
p . Description = form . Content
2022-05-20 22:08:52 +08:00
if err = project_model . UpdateProject ( ctx , p ) ; err != nil {
2020-08-17 04:07:38 +01:00
ctx . ServerError ( "UpdateProjects" , err )
return
}
ctx . Flash . Success ( ctx . Tr ( "repo.projects.edit_success" , p . Title ) )
ctx . Redirect ( ctx . Repo . RepoLink + "/projects" )
}
// ViewProject renders the project board for a project
func ViewProject ( ctx * context . Context ) {
2022-05-20 22:08:52 +08:00
project , err := project_model . GetProjectByID ( ctx , ctx . ParamsInt64 ( ":id" ) )
2020-08-17 04:07:38 +01:00
if err != nil {
2022-03-29 22:16:31 +08:00
if project_model . IsErrProjectNotExist ( err ) {
2020-08-17 04:07:38 +01:00
ctx . NotFound ( "" , nil )
} else {
ctx . ServerError ( "GetProjectByID" , err )
}
return
}
if project . RepoID != ctx . Repo . Repository . ID {
ctx . NotFound ( "" , nil )
return
}
2022-05-20 22:08:52 +08:00
boards , err := project_model . GetBoards ( ctx , project . ID )
2020-08-17 04:07:38 +01:00
if err != nil {
ctx . ServerError ( "GetProjectBoards" , err )
return
}
2021-01-15 21:29:32 +01:00
if boards [ 0 ] . ID == 0 {
boards [ 0 ] . Title = ctx . Tr ( "repo.projects.type.uncategorized" )
}
2020-08-17 04:07:38 +01:00
2022-11-19 09:12:33 +01:00
issuesMap , err := issues_model . LoadIssuesFromBoardList ( ctx , boards )
2021-01-20 20:53:48 +01:00
if err != nil {
2020-08-17 04:07:38 +01:00
ctx . ServerError ( "LoadIssuesOfBoards" , err )
return
}
2021-01-20 20:53:48 +01:00
2022-06-13 17:37:59 +08:00
linkedPrsMap := make ( map [ int64 ] [ ] * issues_model . Issue )
2022-03-29 22:16:31 +08:00
for _ , issuesList := range issuesMap {
for _ , issue := range issuesList {
var referencedIds [ ] int64
for _ , comment := range issue . Comments {
if comment . RefIssueID != 0 && comment . RefIsPull {
referencedIds = append ( referencedIds , comment . RefIssueID )
}
2021-01-20 20:53:48 +01:00
}
2022-03-29 22:16:31 +08:00
if len ( referencedIds ) > 0 {
2022-11-19 09:12:33 +01:00
if linkedPrs , err := issues_model . Issues ( ctx , & issues_model . IssuesOptions {
2022-03-29 22:16:31 +08:00
IssueIDs : referencedIds ,
IsPull : util . OptionalBoolTrue ,
} ) ; err == nil {
linkedPrsMap [ issue . ID ] = linkedPrs
}
2021-01-20 20:53:48 +01:00
}
}
}
ctx . Data [ "LinkedPRs" ] = linkedPrsMap
2020-08-17 04:07:38 +01:00
2021-04-20 06:25:08 +08:00
project . 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-08-28 21:15:56 +01:00
Ctx : ctx ,
2021-04-20 06:25:08 +08:00
} , project . Description )
if err != nil {
ctx . ServerError ( "RenderString" , err )
return
}
2020-11-10 07:16:19 +03:30
2021-11-22 10:57:05 +00:00
ctx . Data [ "IsProjectsPage" ] = true
2021-11-10 03:57:58 +08:00
ctx . Data [ "CanWriteProjects" ] = ctx . Repo . Permission . CanWrite ( unit . TypeProjects )
2020-08-17 04:07:38 +01:00
ctx . Data [ "Project" ] = project
2022-03-29 22:16:31 +08:00
ctx . Data [ "IssuesMap" ] = issuesMap
2021-01-15 21:29:32 +01:00
ctx . Data [ "Boards" ] = boards
2020-08-17 04:07:38 +01:00
2021-04-05 17:30:52 +02:00
ctx . HTML ( http . StatusOK , tplProjectsView )
2020-08-17 04:07:38 +01:00
}
// UpdateIssueProject change an issue's project
func UpdateIssueProject ( ctx * context . Context ) {
issues := getActionIssues ( ctx )
if ctx . Written ( ) {
return
}
2021-07-29 09:42:15 +08:00
projectID := ctx . FormInt64 ( "id" )
2020-08-17 04:07:38 +01:00
for _ , issue := range issues {
oldProjectID := issue . ProjectID ( )
if oldProjectID == projectID {
continue
}
2022-06-13 17:37:59 +08:00
if err := issues_model . ChangeProjectAssign ( issue , ctx . Doer , projectID ) ; err != nil {
2020-08-17 04:07:38 +01:00
ctx . ServerError ( "ChangeProjectAssign" , err )
return
}
}
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusOK , map [ string ] interface { } {
2020-08-17 04:07:38 +01:00
"ok" : true ,
} )
}
// DeleteProjectBoard allows for the deletion of a project board
func DeleteProjectBoard ( ctx * context . Context ) {
2022-03-22 08:03:22 +01:00
if ctx . Doer == nil {
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusForbidden , map [ string ] string {
2020-08-17 04:07:38 +01:00
"message" : "Only signed in users are allowed to perform this action." ,
} )
return
}
2021-11-28 19:58:28 +08:00
if ! ctx . Repo . IsOwner ( ) && ! ctx . Repo . IsAdmin ( ) && ! ctx . Repo . CanAccess ( perm . AccessModeWrite , unit . TypeProjects ) {
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusForbidden , map [ string ] string {
2020-08-17 04:07:38 +01:00
"message" : "Only authorized users are allowed to perform this action." ,
} )
return
}
2022-05-20 22:08:52 +08:00
project , err := project_model . GetProjectByID ( ctx , ctx . ParamsInt64 ( ":id" ) )
2020-08-17 04:07:38 +01:00
if err != nil {
2022-03-29 22:16:31 +08:00
if project_model . IsErrProjectNotExist ( err ) {
2020-08-17 04:07:38 +01:00
ctx . NotFound ( "" , nil )
} else {
ctx . ServerError ( "GetProjectByID" , err )
}
return
}
2022-05-20 22:08:52 +08:00
pb , err := project_model . GetBoard ( ctx , ctx . ParamsInt64 ( ":boardID" ) )
2020-08-17 04:07:38 +01:00
if err != nil {
2021-01-15 04:27:22 +08:00
ctx . ServerError ( "GetProjectBoard" , err )
2020-08-17 04:07:38 +01:00
return
}
if pb . ProjectID != ctx . ParamsInt64 ( ":id" ) {
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusUnprocessableEntity , map [ string ] string {
2020-08-17 04:07:38 +01:00
"message" : fmt . Sprintf ( "ProjectBoard[%d] is not in Project[%d] as expected" , pb . ID , project . ID ) ,
} )
return
}
if project . RepoID != ctx . Repo . Repository . ID {
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusUnprocessableEntity , map [ string ] string {
2020-08-17 04:07:38 +01:00
"message" : fmt . Sprintf ( "ProjectBoard[%d] is not in Repository[%d] as expected" , pb . ID , ctx . Repo . Repository . ID ) ,
} )
return
}
2022-03-29 22:16:31 +08:00
if err := project_model . DeleteBoardByID ( ctx . ParamsInt64 ( ":boardID" ) ) ; err != nil {
2020-08-17 04:07:38 +01:00
ctx . ServerError ( "DeleteProjectBoardByID" , err )
return
}
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusOK , map [ string ] interface { } {
2020-08-17 04:07:38 +01:00
"ok" : true ,
} )
}
// AddBoardToProjectPost allows a new board to be added to a project.
2021-01-26 23:36:53 +08:00
func AddBoardToProjectPost ( ctx * context . Context ) {
2021-04-06 20:44:05 +01:00
form := web . GetForm ( ctx ) . ( * forms . EditProjectBoardForm )
2021-11-28 19:58:28 +08:00
if ! ctx . Repo . IsOwner ( ) && ! ctx . Repo . IsAdmin ( ) && ! ctx . Repo . CanAccess ( perm . AccessModeWrite , unit . TypeProjects ) {
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusForbidden , map [ string ] string {
2020-08-17 04:07:38 +01:00
"message" : "Only authorized users are allowed to perform this action." ,
} )
return
}
2022-05-20 22:08:52 +08:00
project , err := project_model . GetProjectByID ( ctx , ctx . ParamsInt64 ( ":id" ) )
2020-08-17 04:07:38 +01:00
if err != nil {
2022-03-29 22:16:31 +08:00
if project_model . IsErrProjectNotExist ( err ) {
2020-08-17 04:07:38 +01:00
ctx . NotFound ( "" , nil )
} else {
ctx . ServerError ( "GetProjectByID" , err )
}
return
}
2022-03-29 22:16:31 +08:00
if err := project_model . NewBoard ( & project_model . Board {
2020-08-17 04:07:38 +01:00
ProjectID : project . ID ,
Title : form . Title ,
2021-09-29 22:53:12 +02:00
Color : form . Color ,
2022-03-22 08:03:22 +01:00
CreatorID : ctx . Doer . ID ,
2020-08-17 04:07:38 +01:00
} ) ; err != nil {
ctx . ServerError ( "NewProjectBoard" , err )
return
}
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusOK , map [ string ] interface { } {
2020-08-17 04:07:38 +01:00
"ok" : true ,
} )
}
2022-03-29 22:16:31 +08:00
func checkProjectBoardChangePermissions ( ctx * context . Context ) ( * project_model . Project , * project_model . Board ) {
2022-03-22 08:03:22 +01:00
if ctx . Doer == nil {
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusForbidden , map [ string ] string {
2020-08-17 04:07:38 +01:00
"message" : "Only signed in users are allowed to perform this action." ,
} )
2021-01-15 21:29:32 +01:00
return nil , nil
2020-08-17 04:07:38 +01:00
}
2021-11-28 19:58:28 +08:00
if ! ctx . Repo . IsOwner ( ) && ! ctx . Repo . IsAdmin ( ) && ! ctx . Repo . CanAccess ( perm . AccessModeWrite , unit . TypeProjects ) {
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusForbidden , map [ string ] string {
2020-08-17 04:07:38 +01:00
"message" : "Only authorized users are allowed to perform this action." ,
} )
2021-01-15 21:29:32 +01:00
return nil , nil
2020-08-17 04:07:38 +01:00
}
2022-05-20 22:08:52 +08:00
project , err := project_model . GetProjectByID ( ctx , ctx . ParamsInt64 ( ":id" ) )
2020-08-17 04:07:38 +01:00
if err != nil {
2022-03-29 22:16:31 +08:00
if project_model . IsErrProjectNotExist ( err ) {
2020-08-17 04:07:38 +01:00
ctx . NotFound ( "" , nil )
} else {
ctx . ServerError ( "GetProjectByID" , err )
}
2021-01-15 21:29:32 +01:00
return nil , nil
2020-08-17 04:07:38 +01:00
}
2022-05-20 22:08:52 +08:00
board , err := project_model . GetBoard ( ctx , ctx . ParamsInt64 ( ":boardID" ) )
2020-08-17 04:07:38 +01:00
if err != nil {
2021-01-15 04:27:22 +08:00
ctx . ServerError ( "GetProjectBoard" , err )
2021-01-15 21:29:32 +01:00
return nil , nil
2020-08-17 04:07:38 +01:00
}
if board . ProjectID != ctx . ParamsInt64 ( ":id" ) {
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusUnprocessableEntity , map [ string ] string {
2020-08-17 04:07:38 +01:00
"message" : fmt . Sprintf ( "ProjectBoard[%d] is not in Project[%d] as expected" , board . ID , project . ID ) ,
} )
2021-01-15 21:29:32 +01:00
return nil , nil
2020-08-17 04:07:38 +01:00
}
if project . RepoID != ctx . Repo . Repository . ID {
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusUnprocessableEntity , map [ string ] string {
2020-08-17 04:07:38 +01:00
"message" : fmt . Sprintf ( "ProjectBoard[%d] is not in Repository[%d] as expected" , board . ID , ctx . Repo . Repository . ID ) ,
} )
2021-01-15 21:29:32 +01:00
return nil , nil
}
return project , board
}
2021-02-11 17:32:27 +01:00
// EditProjectBoard allows a project board's to be updated
func EditProjectBoard ( ctx * context . Context ) {
2021-04-06 20:44:05 +01:00
form := web . GetForm ( ctx ) . ( * forms . EditProjectBoardForm )
2021-01-15 21:29:32 +01:00
_ , board := checkProjectBoardChangePermissions ( ctx )
if ctx . Written ( ) {
2020-08-17 04:07:38 +01:00
return
}
if form . Title != "" {
board . Title = form . Title
}
2021-09-29 22:53:12 +02:00
board . Color = form . Color
2021-02-11 17:32:27 +01:00
if form . Sorting != 0 {
board . Sorting = form . Sorting
}
2022-05-20 22:08:52 +08:00
if err := project_model . UpdateBoard ( ctx , board ) ; err != nil {
2020-08-17 04:07:38 +01:00
ctx . ServerError ( "UpdateProjectBoard" , err )
2021-01-15 21:29:32 +01:00
return
}
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusOK , map [ string ] interface { } {
2021-01-15 21:29:32 +01:00
"ok" : true ,
} )
}
// SetDefaultProjectBoard set default board for uncategorized issues/pulls
func SetDefaultProjectBoard ( ctx * context . Context ) {
project , board := checkProjectBoardChangePermissions ( ctx )
if ctx . Written ( ) {
return
}
2022-03-29 22:16:31 +08:00
if err := project_model . SetDefaultBoard ( project . ID , board . ID ) ; err != nil {
2021-01-15 21:29:32 +01:00
ctx . ServerError ( "SetDefaultBoard" , err )
2020-08-17 04:07:38 +01:00
return
}
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusOK , map [ string ] interface { } {
2020-08-17 04:07:38 +01:00
"ok" : true ,
} )
}
2021-12-08 07:57:18 +01:00
// MoveIssues moves or keeps issues in a column and sorts them inside that column
func MoveIssues ( ctx * context . Context ) {
2022-03-22 08:03:22 +01:00
if ctx . Doer == nil {
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusForbidden , map [ string ] string {
2020-08-17 04:07:38 +01:00
"message" : "Only signed in users are allowed to perform this action." ,
} )
return
}
2021-11-28 19:58:28 +08:00
if ! ctx . Repo . IsOwner ( ) && ! ctx . Repo . IsAdmin ( ) && ! ctx . Repo . CanAccess ( perm . AccessModeWrite , unit . TypeProjects ) {
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusForbidden , map [ string ] string {
2020-08-17 04:07:38 +01:00
"message" : "Only authorized users are allowed to perform this action." ,
} )
return
}
2022-05-20 22:08:52 +08:00
project , err := project_model . GetProjectByID ( ctx , ctx . ParamsInt64 ( ":id" ) )
2020-08-17 04:07:38 +01:00
if err != nil {
2022-03-29 22:16:31 +08:00
if project_model . IsErrProjectNotExist ( err ) {
2021-12-08 07:57:18 +01:00
ctx . NotFound ( "ProjectNotExist" , nil )
2020-08-17 04:07:38 +01:00
} else {
ctx . ServerError ( "GetProjectByID" , err )
}
return
}
2021-12-08 07:57:18 +01:00
if project . RepoID != ctx . Repo . Repository . ID {
ctx . NotFound ( "InvalidRepoID" , nil )
2020-08-17 04:07:38 +01:00
return
}
2022-03-29 22:16:31 +08:00
var board * project_model . Board
2020-08-17 04:07:38 +01:00
if ctx . ParamsInt64 ( ":boardID" ) == 0 {
2022-03-29 22:16:31 +08:00
board = & project_model . Board {
2020-08-17 04:07:38 +01:00
ID : 0 ,
2021-12-08 07:57:18 +01:00
ProjectID : project . ID ,
2020-08-17 04:07:38 +01:00
Title : ctx . Tr ( "repo.projects.type.uncategorized" ) ,
}
} else {
2022-05-20 22:08:52 +08:00
board , err = project_model . GetBoard ( ctx , ctx . ParamsInt64 ( ":boardID" ) )
2020-08-17 04:07:38 +01:00
if err != nil {
2022-03-29 22:16:31 +08:00
if project_model . IsErrProjectBoardNotExist ( err ) {
2021-12-08 07:57:18 +01:00
ctx . NotFound ( "ProjectBoardNotExist" , nil )
2020-08-17 04:07:38 +01:00
} else {
ctx . ServerError ( "GetProjectBoard" , err )
}
return
}
2021-12-08 07:57:18 +01:00
if board . ProjectID != project . ID {
ctx . NotFound ( "BoardNotInProject" , nil )
2020-08-17 04:07:38 +01:00
return
}
}
2021-12-08 07:57:18 +01:00
type movedIssuesForm struct {
Issues [ ] struct {
IssueID int64 ` json:"issueID" `
Sorting int64 ` json:"sorting" `
} ` json:"issues" `
}
form := & movedIssuesForm { }
if err = json . NewDecoder ( ctx . Req . Body ) . Decode ( & form ) ; err != nil {
ctx . ServerError ( "DecodeMovedIssuesForm" , err )
}
issueIDs := make ( [ ] int64 , 0 , len ( form . Issues ) )
sortedIssueIDs := make ( map [ int64 ] int64 )
for _ , issue := range form . Issues {
issueIDs = append ( issueIDs , issue . IssueID )
sortedIssueIDs [ issue . Sorting ] = issue . IssueID
}
2022-06-13 17:37:59 +08:00
movedIssues , err := issues_model . GetIssuesByIDs ( ctx , issueIDs )
2020-08-17 04:07:38 +01:00
if err != nil {
2022-06-13 17:37:59 +08:00
if issues_model . IsErrIssueNotExist ( err ) {
2021-12-08 07:57:18 +01:00
ctx . NotFound ( "IssueNotExisting" , nil )
2020-08-17 04:07:38 +01:00
} else {
ctx . ServerError ( "GetIssueByID" , err )
}
2021-12-08 07:57:18 +01:00
return
}
2020-08-17 04:07:38 +01:00
2021-12-08 07:57:18 +01:00
if len ( movedIssues ) != len ( form . Issues ) {
2022-06-30 23:55:08 +08:00
ctx . ServerError ( "some issues do not exist" , errors . New ( "some issues do not exist" ) )
2020-08-17 04:07:38 +01:00
return
}
2022-06-30 23:55:08 +08:00
for _ , issue := range movedIssues {
if issue . RepoID != project . RepoID {
ctx . ServerError ( "Some issue's repoID is not equal to project's repoID" , errors . New ( "Some issue's repoID is not equal to project's repoID" ) )
return
}
}
2022-03-29 22:16:31 +08:00
if err = project_model . MoveIssuesOnProjectBoard ( board , sortedIssueIDs ) ; err != nil {
2021-12-08 07:57:18 +01:00
ctx . ServerError ( "MoveIssuesOnProjectBoard" , err )
2020-08-17 04:07:38 +01:00
return
}
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusOK , map [ string ] interface { } {
2020-08-17 04:07:38 +01:00
"ok" : true ,
} )
}
// CreateProject renders the generic project creation page
func CreateProject ( ctx * context . Context ) {
ctx . Data [ "Title" ] = ctx . Tr ( "repo.projects.new" )
2022-03-29 22:16:31 +08:00
ctx . Data [ "ProjectTypes" ] = project_model . GetProjectsConfig ( )
2021-11-10 03:57:58 +08:00
ctx . Data [ "CanWriteProjects" ] = ctx . Repo . Permission . CanWrite ( unit . TypeProjects )
2020-08-17 04:07:38 +01:00
2021-04-05 17:30:52 +02:00
ctx . HTML ( http . StatusOK , tplGenericProjectsNew )
2020-08-17 04:07:38 +01:00
}
2022-03-29 22:16:31 +08:00
// CreateProjectPost creates an individual and/or organization project
func CreateProjectPost ( ctx * context . Context , form forms . UserCreateProjectForm ) {
user := checkContextUser ( ctx , form . UID )
if ctx . Written ( ) {
return
}
ctx . Data [ "ContextUser" ] = user
if ctx . HasError ( ) {
ctx . Data [ "CanWriteProjects" ] = ctx . Repo . Permission . CanWrite ( unit . TypeProjects )
ctx . HTML ( http . StatusOK , tplGenericProjectsNew )
return
}
projectType := project_model . TypeIndividual
if user . IsOrganization ( ) {
projectType = project_model . TypeOrganization
}
if err := project_model . NewProject ( & project_model . Project {
Title : form . Title ,
Description : form . Content ,
CreatorID : user . ID ,
BoardType : form . BoardType ,
Type : projectType ,
} ) ; err != nil {
ctx . ServerError ( "NewProject" , err )
return
}
ctx . Flash . Success ( ctx . Tr ( "repo.projects.create_success" , form . Title ) )
ctx . Redirect ( setting . AppSubURL + "/" )
}