2014-03-22 13:50:50 -04:00
// Copyright 2014 The Gogs Authors. All rights reserved.
2018-11-28 19:26:14 +08:00
// Copyright 2018 The Gitea Authors. All rights reserved.
2014-03-22 13:50:50 -04:00
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package repo
2014-07-26 02:28:04 -04:00
import (
2017-01-24 21:43:02 -05:00
"bytes"
2014-07-26 02:28:04 -04:00
"errors"
"fmt"
2021-09-22 13:38:34 +08:00
"io"
2022-01-21 18:59:26 +01:00
"math/big"
2018-07-17 23:23:58 +02:00
"net/http"
2021-11-16 18:18:25 +00:00
"net/url"
2020-09-11 09:48:39 -05:00
"path"
2017-03-14 21:10:35 -04:00
"strconv"
2014-07-26 02:28:04 -04:00
"strings"
2016-11-10 17:24:48 +01:00
"code.gitea.io/gitea/models"
2021-09-24 19:32:56 +08:00
"code.gitea.io/gitea/models/db"
2022-03-31 17:20:39 +08:00
issues_model "code.gitea.io/gitea/models/issues"
2022-03-29 14:29:02 +08:00
"code.gitea.io/gitea/models/organization"
2022-03-29 22:16:31 +08:00
project_model "code.gitea.io/gitea/models/project"
2021-11-19 21:39:57 +08:00
repo_model "code.gitea.io/gitea/models/repo"
2021-11-10 03:57:58 +08:00
"code.gitea.io/gitea/models/unit"
2021-11-24 17:49:20 +08:00
user_model "code.gitea.io/gitea/models/user"
2016-11-10 17:24:48 +01:00
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
2020-10-17 06:23:08 +02:00
"code.gitea.io/gitea/modules/convert"
2019-03-27 17:33:00 +08:00
"code.gitea.io/gitea/modules/git"
2019-02-21 08:54:05 +08:00
issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
2016-11-10 17:24:48 +01:00
"code.gitea.io/gitea/modules/log"
2019-12-07 05:21:18 +01:00
"code.gitea.io/gitea/modules/markup"
2017-09-21 13:20:14 +08:00
"code.gitea.io/gitea/modules/markup/markdown"
2016-11-10 17:24:48 +01:00
"code.gitea.io/gitea/modules/setting"
2019-06-06 01:37:45 +01:00
api "code.gitea.io/gitea/modules/structs"
2020-10-05 07:49:33 +02:00
"code.gitea.io/gitea/modules/upload"
2017-01-24 21:43:02 -05:00
"code.gitea.io/gitea/modules/util"
2021-01-26 23:36:53 +08:00
"code.gitea.io/gitea/modules/web"
2021-12-10 16:14:24 +08:00
asymkey_service "code.gitea.io/gitea/services/asymkey"
2019-09-25 01:39:50 +08:00
comment_service "code.gitea.io/gitea/services/comments"
2021-04-06 20:44:05 +01:00
"code.gitea.io/gitea/services/forms"
2019-09-30 21:50:44 +08:00
issue_service "code.gitea.io/gitea/services/issue"
2019-12-07 10:44:10 +08:00
pull_service "code.gitea.io/gitea/services/pull"
2019-03-27 17:33:00 +08:00
2019-08-23 09:40:30 -07:00
"github.com/unknwon/com"
2014-07-26 02:28:04 -04:00
)
const (
2019-10-15 20:19:32 +08:00
tplAttachment base . TplName = "repo/issue/view_content/attachments"
2020-09-11 09:48:39 -05:00
tplIssues base . TplName = "repo/issue/list"
tplIssueNew base . TplName = "repo/issue/new"
tplIssueChoose base . TplName = "repo/issue/choose"
tplIssueView base . TplName = "repo/issue/view"
2014-07-26 02:28:04 -04:00
2017-12-04 01:14:26 +02:00
tplReactions base . TplName = "repo/issue/view_content/reactions"
2020-09-11 09:48:39 -05:00
issueTemplateKey = "IssueTemplate"
issueTemplateTitleKey = "IssueTemplateTitle"
2014-07-26 02:28:04 -04:00
)
2022-01-20 18:46:10 +01:00
// IssueTemplateCandidates issue templates
var IssueTemplateCandidates = [ ] string {
"ISSUE_TEMPLATE.md" ,
"issue_template.md" ,
".gitea/ISSUE_TEMPLATE.md" ,
".gitea/issue_template.md" ,
".github/ISSUE_TEMPLATE.md" ,
".github/issue_template.md" ,
}
2014-07-26 02:28:04 -04:00
2019-02-18 21:55:04 +01:00
// MustAllowUserComment checks to make sure if an issue is locked.
// If locked and user has permissions to write to the repository,
// then the comment is allowed, else it is blocked
func MustAllowUserComment ( ctx * context . Context ) {
issue := GetActionIssue ( ctx )
if ctx . Written ( ) {
return
}
2022-03-22 08:03:22 +01:00
if issue . IsLocked && ! ctx . Repo . CanWriteIssuesOrPulls ( issue . IsPull ) && ! ctx . Doer . IsAdmin {
2019-02-18 21:55:04 +01:00
ctx . Flash . Error ( ctx . Tr ( "repo.issues.comment_on_locked" ) )
ctx . Redirect ( issue . HTMLURL ( ) )
return
}
}
2016-11-24 15:04:31 +08:00
// MustEnableIssues check if repository enable internal issues
2016-03-11 11:56:52 -05:00
func MustEnableIssues ( ctx * context . Context ) {
2021-11-10 03:57:58 +08:00
if ! ctx . Repo . CanRead ( unit . TypeIssues ) &&
! ctx . Repo . CanRead ( unit . TypeExternalTracker ) {
2018-01-10 22:34:17 +01:00
ctx . NotFound ( "MustEnableIssues" , nil )
2016-03-06 23:57:46 -05:00
return
2015-12-04 21:30:33 -05:00
}
2016-11-04 09:06:54 +01:00
2021-11-10 03:57:58 +08:00
unit , err := ctx . Repo . Repository . GetUnit ( unit . TypeExternalTracker )
2017-02-04 23:53:46 +08:00
if err == nil {
ctx . Redirect ( unit . ExternalTrackerConfig ( ) . ExternalTrackerURL )
2016-11-04 09:06:54 +01:00
return
}
2015-12-04 21:30:33 -05:00
}
2018-11-28 19:26:14 +08:00
// MustAllowPulls check if repository enable pull requests and user have right to do that
2016-03-11 11:56:52 -05:00
func MustAllowPulls ( ctx * context . Context ) {
2021-11-10 03:57:58 +08:00
if ! ctx . Repo . Repository . CanEnablePulls ( ) || ! ctx . Repo . CanRead ( unit . TypePullRequests ) {
2018-01-10 22:34:17 +01:00
ctx . NotFound ( "MustAllowPulls" , nil )
2016-03-06 23:57:46 -05:00
return
2015-12-04 21:30:33 -05:00
}
2015-12-19 22:07:06 -05:00
2016-03-06 23:57:46 -05:00
// User can send pull request if owns a forked repository.
2022-03-22 08:03:22 +01:00
if ctx . IsSigned && repo_model . HasForkedRepo ( ctx . Doer . ID , ctx . Repo . Repository . ID ) {
2016-03-06 23:57:46 -05:00
ctx . Repo . PullRequest . Allowed = true
2022-03-22 08:03:22 +01:00
ctx . Repo . PullRequest . HeadInfoSubURL = url . PathEscape ( ctx . Doer . Name ) + ":" + util . PathEscapeSegments ( ctx . Repo . BranchName )
2016-03-06 23:57:46 -05:00
}
2015-12-04 21:30:33 -05:00
}
2020-08-17 04:07:38 +01:00
func issues ( ctx * context . Context , milestoneID , projectID int64 , isPullOption util . OptionalBool ) {
2018-11-29 09:46:30 +08:00
var err error
2021-08-11 02:31:13 +02:00
viewType := ctx . FormString ( "type" )
sortType := ctx . FormString ( "sort" )
2021-01-17 17:34:19 +01:00
types := [ ] string { "all" , "your_repositories" , "assigned" , "created_by" , "mentioned" , "review_requested" }
2020-12-25 09:59:32 +00:00
if ! util . IsStringInSlice ( viewType , types , true ) {
2014-07-26 02:28:04 -04:00
viewType = "all"
}
2015-08-15 12:07:08 +08:00
var (
2021-07-29 09:42:15 +08:00
assigneeID = ctx . FormInt64 ( "assignee" )
2021-01-17 17:34:19 +01:00
posterID int64
mentionedID int64
reviewRequestedID int64
forceEmpty bool
2015-08-15 12:07:08 +08:00
)
2014-07-26 02:28:04 -04:00
2017-06-14 23:09:03 -04:00
if ctx . IsSigned {
switch viewType {
case "created_by" :
2022-03-22 08:03:22 +01:00
posterID = ctx . Doer . ID
2017-06-14 23:09:03 -04:00
case "mentioned" :
2022-03-22 08:03:22 +01:00
mentionedID = ctx . Doer . ID
2020-11-19 22:39:55 +01:00
case "assigned" :
2022-03-22 08:03:22 +01:00
assigneeID = ctx . Doer . ID
2021-01-17 17:34:19 +01:00
case "review_requested" :
2022-03-22 08:03:22 +01:00
reviewRequestedID = ctx . Doer . ID
2017-06-14 23:09:03 -04:00
}
}
2015-07-25 02:52:25 +08:00
repo := ctx . Repo . Repository
2019-01-23 06:10:38 +02:00
var labelIDs [ ] int64
2021-08-11 02:31:13 +02:00
selectLabels := ctx . FormString ( "labels" )
2019-01-23 06:10:38 +02:00
if len ( selectLabels ) > 0 && selectLabels != "0" {
labelIDs , err = base . StringsToInt64s ( strings . Split ( selectLabels , "," ) )
if err != nil {
ctx . ServerError ( "StringsToInt64s" , err )
return
}
}
2016-12-24 05:33:21 -05:00
2021-08-11 02:31:13 +02:00
keyword := strings . Trim ( ctx . FormString ( "q" ) , " " )
2017-01-24 21:43:02 -05:00
if bytes . Contains ( [ ] byte ( keyword ) , [ ] byte { 0x00 } ) {
keyword = ""
}
var issueIDs [ ] int64
if len ( keyword ) > 0 {
2022-01-27 10:30:51 +02:00
issueIDs , err = issue_indexer . SearchIssuesByKeyword ( ctx , [ ] int64 { repo . ID } , keyword )
2019-02-19 22:39:39 +08:00
if err != nil {
2022-01-27 10:30:51 +02:00
if issue_indexer . IsAvailable ( ) {
ctx . ServerError ( "issueIndexer.Search" , err )
return
}
ctx . Data [ "IssueIndexerUnavailable" ] = true
2019-02-19 22:39:39 +08:00
}
2017-01-24 21:43:02 -05:00
if len ( issueIDs ) == 0 {
forceEmpty = true
}
}
2016-12-24 05:33:21 -05:00
var issueStats * models . IssueStats
if forceEmpty {
issueStats = & models . IssueStats { }
} else {
2017-01-24 21:43:02 -05:00
issueStats , err = models . GetIssueStats ( & models . IssueStatsOptions {
2021-01-17 17:34:19 +01:00
RepoID : repo . ID ,
Labels : selectLabels ,
MilestoneID : milestoneID ,
AssigneeID : assigneeID ,
MentionedID : mentionedID ,
PosterID : posterID ,
ReviewRequestedID : reviewRequestedID ,
IsPull : isPullOption ,
IssueIDs : issueIDs ,
2016-12-24 05:33:21 -05:00
} )
2017-01-24 21:43:02 -05:00
if err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "GetIssueStats" , err )
2017-01-24 21:43:02 -05:00
return
}
2016-12-24 05:33:21 -05:00
}
2020-07-09 23:13:06 +02:00
2021-08-11 02:31:13 +02:00
isShowClosed := ctx . FormString ( "state" ) == "closed"
2020-07-09 23:13:06 +02:00
// if open issues are zero and close don't, use closed as default
2021-08-11 02:31:13 +02:00
if len ( ctx . FormString ( "state" ) ) == 0 && issueStats . OpenCount == 0 && issueStats . ClosedCount != 0 {
2020-07-09 23:13:06 +02:00
isShowClosed = true
}
2021-07-29 09:42:15 +08:00
page := ctx . FormInt ( "page" )
2015-07-24 16:42:47 +08:00
if page <= 1 {
page = 1
}
2015-07-28 03:14:37 +08:00
var total int
if ! isShowClosed {
total = int ( issueStats . OpenCount )
} else {
total = int ( issueStats . ClosedCount )
2015-07-24 16:42:47 +08:00
}
2019-04-20 06:15:19 +02:00
pager := context . NewPagination ( total , setting . UI . IssuePagingNum , page , 5 )
2014-07-26 02:28:04 -04:00
2020-04-30 06:15:39 +02:00
var mileIDs [ ] int64
if milestoneID > 0 {
mileIDs = [ ] int64 { milestoneID }
}
2016-12-24 05:33:21 -05:00
var issues [ ] * models . Issue
if forceEmpty {
issues = [ ] * models . Issue { }
} else {
issues , err = models . Issues ( & models . IssuesOptions {
2021-09-24 19:32:56 +08:00
ListOptions : db . ListOptions {
2020-01-24 19:00:29 +00:00
Page : pager . Paginater . Current ( ) ,
PageSize : setting . UI . IssuePagingNum ,
} ,
2021-01-17 17:34:19 +01:00
RepoIDs : [ ] int64 { repo . ID } ,
AssigneeID : assigneeID ,
PosterID : posterID ,
MentionedID : mentionedID ,
ReviewRequestedID : reviewRequestedID ,
MilestoneIDs : mileIDs ,
ProjectID : projectID ,
IsClosed : util . OptionalBoolOf ( isShowClosed ) ,
IsPull : isPullOption ,
LabelIDs : labelIDs ,
SortType : sortType ,
IssueIDs : issueIDs ,
2016-12-24 05:33:21 -05:00
} )
if err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "Issues" , err )
2016-12-24 05:33:21 -05:00
return
}
2014-07-26 02:28:04 -04:00
}
2022-01-20 18:46:10 +01:00
issueList := models . IssueList ( issues )
2021-04-16 01:34:43 +08:00
approvalCounts , err := issueList . GetApprovalCounts ( )
2020-03-06 03:44:06 +00:00
if err != nil {
ctx . ServerError ( "ApprovalCounts" , err )
return
}
2014-07-26 02:28:04 -04:00
// Get posters.
for i := range issues {
2017-02-03 02:22:39 -05:00
// Check read status
2015-07-25 02:52:25 +08:00
if ! ctx . IsSigned {
issues [ i ] . IsRead = true
2022-03-22 08:03:22 +01:00
} else if err = issues [ i ] . GetIsRead ( ctx . Doer . ID ) ; err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "GetIsRead" , err )
2017-02-03 02:22:39 -05:00
return
2014-07-26 02:28:04 -04:00
}
2021-04-16 01:34:43 +08:00
}
2019-04-02 21:54:29 +02:00
2022-01-19 23:26:57 +00:00
commitStatus , err := pull_service . GetIssuesLastCommitStatus ( ctx , issues )
2021-04-16 01:34:43 +08:00
if err != nil {
ctx . ServerError ( "GetIssuesLastCommitStatus" , err )
return
2014-07-26 02:28:04 -04:00
}
2019-04-02 21:54:29 +02:00
2015-08-05 20:23:08 +08:00
ctx . Data [ "Issues" ] = issues
2019-04-02 21:54:29 +02:00
ctx . Data [ "CommitStatus" ] = commitStatus
2015-08-05 20:23:08 +08:00
2018-11-29 09:46:30 +08:00
// Get assignees.
2021-12-10 09:27:50 +08:00
ctx . Data [ "Assignees" ] , err = models . GetRepoAssignees ( repo )
2015-08-05 20:23:08 +08:00
if err != nil {
2018-11-29 09:46:30 +08:00
ctx . ServerError ( "GetAssignees" , err )
2015-08-05 20:23:08 +08:00
return
}
2015-08-15 11:24:41 +08:00
2020-12-21 23:39:28 +08:00
handleTeamMentions ( ctx )
if ctx . Written ( ) {
return
}
2021-09-24 19:32:56 +08:00
labels , err := models . GetLabelsByRepoID ( repo . ID , "" , db . ListOptions { } )
2015-08-15 11:24:41 +08:00
if err != nil {
2018-11-29 09:46:30 +08:00
ctx . ServerError ( "GetLabelsByRepoID" , err )
2015-08-15 11:24:41 +08:00
return
}
2020-04-01 00:14:46 -04:00
if repo . Owner . IsOrganization ( ) {
2021-09-24 19:32:56 +08:00
orgLabels , err := models . GetLabelsByOrgID ( repo . Owner . ID , ctx . FormString ( "sort" ) , db . ListOptions { } )
2020-04-01 00:14:46 -04:00
if err != nil {
ctx . ServerError ( "GetLabelsByOrgID" , err )
return
}
ctx . Data [ "OrgLabels" ] = orgLabels
labels = append ( labels , orgLabels ... )
}
2019-01-23 06:10:38 +02:00
for _ , l := range labels {
l . LoadSelectedLabelsAfterClick ( labelIDs )
}
2018-11-29 09:46:30 +08:00
ctx . Data [ "Labels" ] = labels
2019-01-23 06:10:38 +02:00
ctx . Data [ "NumLabels" ] = len ( labels )
2014-07-26 02:28:04 -04:00
2021-07-29 09:42:15 +08:00
if ctx . FormInt64 ( "assignee" ) == 0 {
2016-07-17 09:25:30 +08:00
assigneeID = 0 // Reset ID to prevent unexpected selection of assignee.
}
2022-01-20 18:46:10 +01:00
ctx . Data [ "IssueRefEndNames" ] , ctx . Data [ "IssueRefURLs" ] = issue_service . GetRefEndNamesAndURLs ( issues , ctx . Repo . RepoLink )
2020-05-15 00:55:43 +02:00
2020-03-06 03:44:06 +00:00
ctx . Data [ "ApprovalCounts" ] = func ( issueID int64 , typ string ) int64 {
counts , ok := approvalCounts [ issueID ]
if ! ok || len ( counts ) == 0 {
return 0
}
reviewTyp := models . ReviewTypeApprove
if typ == "reject" {
reviewTyp = models . ReviewTypeReject
2020-04-07 00:33:34 +08:00
} else if typ == "waiting" {
reviewTyp = models . ReviewTypeRequest
2020-03-06 03:44:06 +00:00
}
for _ , count := range counts {
if count . Type == reviewTyp {
return count . Count
}
}
return 0
}
2021-10-08 00:00:02 +02:00
if ctx . Repo . CanWriteIssuesOrPulls ( ctx . Params ( ":type" ) == "pulls" ) {
2022-03-29 22:16:31 +08:00
projects , _ , err := project_model . GetProjects ( project_model . SearchOptions {
2021-10-08 00:00:02 +02:00
RepoID : repo . ID ,
2022-03-29 22:16:31 +08:00
Type : project_model . TypeRepository ,
2021-10-08 00:00:02 +02:00
IsClosed : util . OptionalBoolOf ( isShowClosed ) ,
} )
if err != nil {
ctx . ServerError ( "GetProjects" , err )
return
}
ctx . Data [ "Projects" ] = projects
}
2014-07-26 02:28:04 -04:00
ctx . Data [ "IssueStats" ] = issueStats
2019-12-28 22:43:46 +08:00
ctx . Data [ "SelLabelIDs" ] = labelIDs
ctx . Data [ "SelectLabels" ] = selectLabels
2014-07-26 02:28:04 -04:00
ctx . Data [ "ViewType" ] = viewType
2015-08-15 12:07:08 +08:00
ctx . Data [ "SortType" ] = sortType
2015-08-05 20:23:08 +08:00
ctx . Data [ "MilestoneID" ] = milestoneID
2015-08-15 11:24:41 +08:00
ctx . Data [ "AssigneeID" ] = assigneeID
2014-07-26 02:28:04 -04:00
ctx . Data [ "IsShowClosed" ] = isShowClosed
2017-01-24 21:43:02 -05:00
ctx . Data [ "Keyword" ] = keyword
2014-07-26 02:28:04 -04:00
if isShowClosed {
ctx . Data [ "State" ] = "closed"
} else {
2015-07-25 02:52:25 +08:00
ctx . Data [ "State" ] = "open"
2014-07-26 02:28:04 -04:00
}
2019-04-20 06:15:19 +02:00
pager . AddParam ( ctx , "q" , "Keyword" )
pager . AddParam ( ctx , "type" , "ViewType" )
pager . AddParam ( ctx , "sort" , "SortType" )
pager . AddParam ( ctx , "state" , "State" )
pager . AddParam ( ctx , "labels" , "SelectLabels" )
pager . AddParam ( ctx , "milestone" , "MilestoneID" )
pager . AddParam ( ctx , "assignee" , "AssigneeID" )
ctx . Data [ "Page" ] = pager
2018-11-29 09:46:30 +08:00
}
// Issues render issues page
func Issues ( ctx * context . Context ) {
isPullList := ctx . Params ( ":type" ) == "pulls"
if isPullList {
MustAllowPulls ( ctx )
if ctx . Written ( ) {
return
}
ctx . Data [ "Title" ] = ctx . Tr ( "repo.pulls" )
ctx . Data [ "PageIsPullList" ] = true
} else {
MustEnableIssues ( ctx )
if ctx . Written ( ) {
return
}
ctx . Data [ "Title" ] = ctx . Tr ( "repo.issues" )
ctx . Data [ "PageIsIssueList" ] = true
2020-09-11 09:48:39 -05:00
ctx . Data [ "NewIssueChooseTemplate" ] = len ( ctx . IssueTemplatesFromDefaultBranch ( ) ) > 0
2018-11-29 09:46:30 +08:00
}
2021-07-29 09:42:15 +08:00
issues ( ctx , ctx . FormInt64 ( "milestone" ) , ctx . FormInt64 ( "project" ) , util . OptionalBoolOf ( isPullList ) )
2021-05-09 03:33:49 +01:00
if ctx . Written ( ) {
return
}
2018-11-29 09:46:30 +08:00
var err error
2020-07-28 13:30:40 +02:00
// Get milestones
2021-08-12 14:43:08 +02:00
ctx . Data [ "Milestones" ] , _ , err = models . GetMilestones ( models . GetMilestonesOption {
2020-07-28 13:30:40 +02:00
RepoID : ctx . Repo . Repository . ID ,
2021-08-11 02:31:13 +02:00
State : api . StateType ( ctx . FormString ( "state" ) ) ,
2020-07-28 13:30:40 +02:00
} )
2018-11-29 09:46:30 +08:00
if err != nil {
ctx . ServerError ( "GetAllRepoMilestones" , err )
return
}
2015-07-25 02:52:25 +08:00
2020-01-16 22:18:30 +08:00
ctx . Data [ "CanWriteIssuesOrPulls" ] = ctx . Repo . CanWriteIssuesOrPulls ( isPullList )
2019-02-19 17:09:47 -06:00
2021-04-05 17:30:52 +02:00
ctx . HTML ( http . StatusOK , tplIssues )
2014-07-26 02:28:04 -04:00
}
2016-11-24 15:04:31 +08:00
// RetrieveRepoMilestonesAndAssignees find all the milestones and assignees of a repository
2021-12-10 09:27:50 +08:00
func RetrieveRepoMilestonesAndAssignees ( ctx * context . Context , repo * repo_model . Repository ) {
2015-09-01 19:07:02 -04:00
var err error
2021-08-12 14:43:08 +02:00
ctx . Data [ "OpenMilestones" ] , _ , err = models . GetMilestones ( models . GetMilestonesOption {
2020-07-28 13:30:40 +02:00
RepoID : repo . ID ,
State : api . StateOpen ,
} )
2015-09-01 19:07:02 -04:00
if err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "GetMilestones" , err )
2015-09-01 19:07:02 -04:00
return
}
2021-08-12 14:43:08 +02:00
ctx . Data [ "ClosedMilestones" ] , _ , err = models . GetMilestones ( models . GetMilestonesOption {
2020-07-28 13:30:40 +02:00
RepoID : repo . ID ,
State : api . StateClosed ,
} )
2015-09-01 19:07:02 -04:00
if err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "GetMilestones" , err )
2015-09-01 19:07:02 -04:00
return
}
2021-12-10 09:27:50 +08:00
ctx . Data [ "Assignees" ] , err = models . GetRepoAssignees ( repo )
2015-09-01 19:07:02 -04:00
if err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "GetAssignees" , err )
2015-09-01 19:07:02 -04:00
return
}
2020-12-21 23:39:28 +08:00
handleTeamMentions ( ctx )
2015-09-01 19:07:02 -04:00
}
2021-12-10 09:27:50 +08:00
func retrieveProjects ( ctx * context . Context , repo * repo_model . Repository ) {
2020-08-17 04:07:38 +01:00
var err error
2022-03-29 22:16:31 +08:00
ctx . Data [ "OpenProjects" ] , _ , err = project_model . GetProjects ( project_model . SearchOptions {
2020-08-17 04:07:38 +01:00
RepoID : repo . ID ,
Page : - 1 ,
IsClosed : util . OptionalBoolFalse ,
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
}
2022-03-29 22:16:31 +08:00
ctx . Data [ "ClosedProjects" ] , _ , err = project_model . GetProjects ( project_model . SearchOptions {
2020-08-17 04:07:38 +01:00
RepoID : repo . ID ,
Page : - 1 ,
IsClosed : util . OptionalBoolTrue ,
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
}
}
2020-10-13 03:55:13 +08:00
// repoReviewerSelection items to bee shown
type repoReviewerSelection struct {
IsTeam bool
2022-03-29 14:29:02 +08:00
Team * organization . Team
2021-11-24 17:49:20 +08:00
User * user_model . User
2020-10-13 03:55:13 +08:00
Review * models . Review
CanChange bool
Checked bool
ItemID int64
}
2020-04-07 00:33:34 +08:00
// RetrieveRepoReviewers find all reviewers of a repository
2021-12-10 09:27:50 +08:00
func RetrieveRepoReviewers ( ctx * context . Context , repo * repo_model . Repository , issue * models . Issue , canChooseReviewer bool ) {
2020-10-13 03:55:13 +08:00
ctx . Data [ "CanChooseReviewer" ] = canChooseReviewer
2020-10-14 20:11:11 +08:00
originalAuthorReviews , err := models . GetReviewersFromOriginalAuthorsByIssueID ( issue . ID )
if err != nil {
ctx . ServerError ( "GetReviewersFromOriginalAuthorsByIssueID" , err )
return
}
ctx . Data [ "OriginalReviews" ] = originalAuthorReviews
2020-10-13 03:55:13 +08:00
reviews , err := models . GetReviewersByIssueID ( issue . ID )
2020-04-07 00:33:34 +08:00
if err != nil {
2020-10-13 03:55:13 +08:00
ctx . ServerError ( "GetReviewersByIssueID" , err )
return
}
if len ( reviews ) == 0 && ! canChooseReviewer {
2020-04-07 00:33:34 +08:00
return
}
2020-10-13 03:55:13 +08:00
var (
pullReviews [ ] * repoReviewerSelection
reviewersResult [ ] * repoReviewerSelection
teamReviewersResult [ ] * repoReviewerSelection
2022-03-29 14:29:02 +08:00
teamReviewers [ ] * organization . Team
2021-11-24 17:49:20 +08:00
reviewers [ ] * user_model . User
2020-10-13 03:55:13 +08:00
)
if canChooseReviewer {
posterID := issue . PosterID
if issue . OriginalAuthorID > 0 {
posterID = 0
}
2022-03-22 08:03:22 +01:00
reviewers , err = models . GetReviewers ( repo , ctx . Doer . ID , posterID )
2020-10-13 03:55:13 +08:00
if err != nil {
ctx . ServerError ( "GetReviewers" , err )
return
}
2021-12-10 09:27:50 +08:00
teamReviewers , err = models . GetReviewerTeams ( repo )
2020-10-13 03:55:13 +08:00
if err != nil {
ctx . ServerError ( "GetReviewerTeams" , err )
return
}
if len ( reviewers ) > 0 {
reviewersResult = make ( [ ] * repoReviewerSelection , 0 , len ( reviewers ) )
}
if len ( teamReviewers ) > 0 {
teamReviewersResult = make ( [ ] * repoReviewerSelection , 0 , len ( teamReviewers ) )
}
}
pullReviews = make ( [ ] * repoReviewerSelection , 0 , len ( reviews ) )
for _ , review := range reviews {
tmp := & repoReviewerSelection {
Checked : review . Type == models . ReviewTypeRequest ,
Review : review ,
ItemID : review . ReviewerID ,
}
if review . ReviewerTeamID > 0 {
tmp . IsTeam = true
tmp . ItemID = - review . ReviewerTeamID
}
if ctx . Repo . IsAdmin ( ) {
// Admin can dismiss or re-request any review requests
tmp . CanChange = true
2022-03-22 08:03:22 +01:00
} else if ctx . Doer != nil && ctx . Doer . ID == review . ReviewerID && review . Type == models . ReviewTypeRequest {
2020-10-13 03:55:13 +08:00
// A user can refuse review requests
tmp . CanChange = true
2022-03-22 08:03:22 +01:00
} else if ( canChooseReviewer || ( ctx . Doer != nil && ctx . Doer . ID == issue . PosterID ) ) && review . Type != models . ReviewTypeRequest &&
ctx . Doer . ID != review . ReviewerID {
2020-10-13 03:55:13 +08:00
// The poster of the PR, a manager, or official reviewers can re-request review from other reviewers
tmp . CanChange = true
}
pullReviews = append ( pullReviews , tmp )
if canChooseReviewer {
if tmp . IsTeam {
teamReviewersResult = append ( teamReviewersResult , tmp )
} else {
reviewersResult = append ( reviewersResult , tmp )
}
}
}
if len ( pullReviews ) > 0 {
// Drop all non-existing users and teams from the reviews
currentPullReviewers := make ( [ ] * repoReviewerSelection , 0 , len ( pullReviews ) )
for _ , item := range pullReviews {
if item . Review . ReviewerID > 0 {
if err = item . Review . LoadReviewer ( ) ; err != nil {
2021-11-24 17:49:20 +08:00
if user_model . IsErrUserNotExist ( err ) {
2020-10-13 03:55:13 +08:00
continue
}
ctx . ServerError ( "LoadReviewer" , err )
return
}
item . User = item . Review . Reviewer
} else if item . Review . ReviewerTeamID > 0 {
if err = item . Review . LoadReviewerTeam ( ) ; err != nil {
2022-03-29 14:29:02 +08:00
if organization . IsErrTeamNotExist ( err ) {
2020-10-13 03:55:13 +08:00
continue
}
ctx . ServerError ( "LoadReviewerTeam" , err )
return
}
item . Team = item . Review . ReviewerTeam
} else {
continue
}
currentPullReviewers = append ( currentPullReviewers , item )
}
ctx . Data [ "PullReviewers" ] = currentPullReviewers
}
if canChooseReviewer && reviewersResult != nil {
preadded := len ( reviewersResult )
for _ , reviewer := range reviewers {
found := false
reviewAddLoop :
for _ , tmp := range reviewersResult [ : preadded ] {
if tmp . ItemID == reviewer . ID {
tmp . User = reviewer
found = true
break reviewAddLoop
}
}
if found {
continue
}
reviewersResult = append ( reviewersResult , & repoReviewerSelection {
IsTeam : false ,
CanChange : true ,
User : reviewer ,
ItemID : reviewer . ID ,
} )
}
ctx . Data [ "Reviewers" ] = reviewersResult
}
if canChooseReviewer && teamReviewersResult != nil {
preadded := len ( teamReviewersResult )
for _ , team := range teamReviewers {
found := false
teamReviewAddLoop :
for _ , tmp := range teamReviewersResult [ : preadded ] {
if tmp . ItemID == - team . ID {
tmp . Team = team
found = true
break teamReviewAddLoop
}
}
if found {
continue
}
teamReviewersResult = append ( teamReviewersResult , & repoReviewerSelection {
IsTeam : true ,
CanChange : true ,
Team : team ,
ItemID : - team . ID ,
} )
}
ctx . Data [ "TeamReviewers" ] = teamReviewersResult
}
2020-04-07 00:33:34 +08:00
}
2016-11-24 15:04:31 +08:00
// RetrieveRepoMetas find all the meta information of a repository
2021-12-10 09:27:50 +08:00
func RetrieveRepoMetas ( ctx * context . Context , repo * repo_model . Repository , isPull bool ) [ ] * models . Label {
2020-01-20 20:00:32 +08:00
if ! ctx . Repo . CanWriteIssuesOrPulls ( isPull ) {
2015-08-31 16:24:28 +09:00
return nil
}
2021-09-24 19:32:56 +08:00
labels , err := models . GetLabelsByRepoID ( repo . ID , "" , db . ListOptions { } )
2015-08-31 16:24:28 +09:00
if err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "GetLabelsByRepoID" , err )
2015-08-31 16:24:28 +09:00
return nil
}
ctx . Data [ "Labels" ] = labels
2020-04-01 00:14:46 -04:00
if repo . Owner . IsOrganization ( ) {
2021-09-24 19:32:56 +08:00
orgLabels , err := models . GetLabelsByOrgID ( repo . Owner . ID , ctx . FormString ( "sort" ) , db . ListOptions { } )
2020-04-01 00:14:46 -04:00
if err != nil {
return nil
}
ctx . Data [ "OrgLabels" ] = orgLabels
labels = append ( labels , orgLabels ... )
}
2015-08-31 16:24:28 +09:00
2015-09-01 19:07:02 -04:00
RetrieveRepoMilestonesAndAssignees ( ctx , repo )
if ctx . Written ( ) {
2015-08-31 16:24:28 +09:00
return nil
}
2020-08-17 04:07:38 +01:00
retrieveProjects ( ctx , repo )
if ctx . Written ( ) {
return nil
}
2021-12-08 19:08:16 +00:00
brs , _ , err := ctx . Repo . GitRepo . GetBranchNames ( 0 , 0 )
2017-08-24 14:30:27 +02:00
if err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "GetBranches" , err )
2017-08-24 14:30:27 +02:00
return nil
}
ctx . Data [ "Branches" ] = brs
2018-07-17 23:23:58 +02:00
// Contains true if the user can create issue dependencies
2022-03-22 08:03:22 +01:00
ctx . Data [ "CanCreateIssueDependencies" ] = ctx . Repo . CanCreateIssueDependencies ( ctx . Doer , isPull )
2018-07-17 23:23:58 +02:00
2015-08-31 16:24:28 +09:00
return labels
}
2016-03-11 11:56:52 -05:00
func getFileContentFromDefaultBranch ( ctx * context . Context , filename string ) ( string , bool ) {
2016-02-17 20:21:31 -02:00
var bytes [ ] byte
if ctx . Repo . Commit == nil {
var err error
ctx . Repo . Commit , err = ctx . Repo . GitRepo . GetBranchCommit ( ctx . Repo . Repository . DefaultBranch )
if err != nil {
return "" , false
}
}
entry , err := ctx . Repo . Commit . GetTreeEntryByPath ( filename )
if err != nil {
return "" , false
}
2017-11-29 02:50:39 +01:00
if entry . Blob ( ) . Size ( ) >= setting . UI . MaxDisplayFileSize {
return "" , false
}
2019-04-19 14:17:27 +02:00
r , err := entry . Blob ( ) . DataAsync ( )
2016-02-17 20:21:31 -02:00
if err != nil {
return "" , false
}
2019-04-19 14:17:27 +02:00
defer r . Close ( )
2021-09-22 13:38:34 +08:00
bytes , err = io . ReadAll ( r )
2016-02-17 20:21:31 -02:00
if err != nil {
return "" , false
}
return string ( bytes ) , true
}
2021-12-20 05:41:31 +01:00
func setTemplateIfExists ( ctx * context . Context , ctxDataKey string , possibleDirs , possibleFiles [ ] string ) {
2020-09-11 09:48:39 -05:00
templateCandidates := make ( [ ] string , 0 , len ( possibleFiles ) )
2021-08-11 02:31:13 +02:00
if ctx . FormString ( "template" ) != "" {
2020-09-11 09:48:39 -05:00
for _ , dirName := range possibleDirs {
2021-08-11 02:31:13 +02:00
templateCandidates = append ( templateCandidates , path . Join ( dirName , ctx . FormString ( "template" ) ) )
2020-09-11 09:48:39 -05:00
}
}
templateCandidates = append ( templateCandidates , possibleFiles ... ) // Append files to the end because they should be fallback
for _ , filename := range templateCandidates {
templateContent , found := getFileContentFromDefaultBranch ( ctx , filename )
2016-02-17 20:21:31 -02:00
if found {
2020-09-11 09:48:39 -05:00
var meta api . IssueTemplate
templateBody , err := markdown . ExtractMetadata ( templateContent , & meta )
if err != nil {
log . Debug ( "could not extract metadata from %s [%s]: %v" , filename , ctx . Repo . Repository . FullName ( ) , err )
ctx . Data [ ctxDataKey ] = templateContent
return
}
ctx . Data [ issueTemplateTitleKey ] = meta . Title
ctx . Data [ ctxDataKey ] = templateBody
labelIDs := make ( [ ] string , 0 , len ( meta . Labels ) )
2021-09-24 19:32:56 +08:00
if repoLabels , err := models . GetLabelsByRepoID ( ctx . Repo . Repository . ID , "" , db . ListOptions { } ) ; err == nil {
2021-02-10 17:18:22 +00:00
ctx . Data [ "Labels" ] = repoLabels
if ctx . Repo . Owner . IsOrganization ( ) {
2021-09-24 19:32:56 +08:00
if orgLabels , err := models . GetLabelsByOrgID ( ctx . Repo . Owner . ID , ctx . FormString ( "sort" ) , db . ListOptions { } ) ; err == nil {
2021-02-10 17:18:22 +00:00
ctx . Data [ "OrgLabels" ] = orgLabels
repoLabels = append ( repoLabels , orgLabels ... )
}
}
2020-09-11 09:48:39 -05:00
for _ , metaLabel := range meta . Labels {
for _ , repoLabel := range repoLabels {
if strings . EqualFold ( repoLabel . Name , metaLabel ) {
repoLabel . IsChecked = true
2021-11-16 18:18:25 +00:00
labelIDs = append ( labelIDs , strconv . FormatInt ( repoLabel . ID , 10 ) )
2020-09-11 09:48:39 -05:00
break
}
}
}
}
ctx . Data [ "HasSelectedLabel" ] = len ( labelIDs ) > 0
ctx . Data [ "label_ids" ] = strings . Join ( labelIDs , "," )
2021-12-17 22:29:09 +01:00
ctx . Data [ "Reference" ] = meta . Ref
ctx . Data [ "RefEndName" ] = git . RefEndName ( meta . Ref )
2016-02-17 20:21:31 -02:00
return
}
}
}
2019-01-21 12:45:32 +01:00
// NewIssue render creating issue page
2016-03-11 11:56:52 -05:00
func NewIssue ( ctx * context . Context ) {
2015-08-09 15:23:02 +08:00
ctx . Data [ "Title" ] = ctx . Tr ( "repo.issues.new" )
ctx . Data [ "PageIsIssueList" ] = true
2020-09-11 09:48:39 -05:00
ctx . Data [ "NewIssueChooseTemplate" ] = len ( ctx . IssueTemplatesFromDefaultBranch ( ) ) > 0
2016-08-24 21:35:03 -07:00
ctx . Data [ "RequireHighlightJS" ] = true
2017-12-11 14:03:04 +08:00
ctx . Data [ "RequireTribute" ] = true
2018-08-13 21:04:39 +02:00
ctx . Data [ "PullRequestWorkInProgressPrefixes" ] = setting . Repository . PullRequest . WorkInProgressPrefixes
2021-08-11 02:31:13 +02:00
title := ctx . FormString ( "title" )
2020-09-11 09:48:39 -05:00
ctx . Data [ "TitleQuery" ] = title
2021-08-11 02:31:13 +02:00
body := ctx . FormString ( "body" )
2019-01-28 18:23:59 +03:00
ctx . Data [ "BodyQuery" ] = body
2021-03-18 10:02:38 +08:00
2021-11-10 03:57:58 +08:00
ctx . Data [ "IsProjectsEnabled" ] = ctx . Repo . CanRead ( unit . TypeProjects )
2020-10-05 07:49:33 +02:00
ctx . Data [ "IsAttachmentEnabled" ] = setting . Attachment . Enabled
upload . AddUploadContext ( ctx , "comment" )
2018-11-29 09:46:30 +08:00
2021-07-29 09:42:15 +08:00
milestoneID := ctx . FormInt64 ( "milestone" )
2019-06-10 22:16:02 +08:00
if milestoneID > 0 {
2022-01-26 20:01:35 +00:00
milestone , err := models . GetMilestoneByRepoID ( ctx . Repo . Repository . ID , milestoneID )
2019-06-10 22:16:02 +08:00
if err != nil {
log . Error ( "GetMilestoneByID: %d: %v" , milestoneID , err )
} else {
ctx . Data [ "milestone_id" ] = milestoneID
ctx . Data [ "Milestone" ] = milestone
}
2018-11-29 09:46:30 +08:00
}
2021-07-29 09:42:15 +08:00
projectID := ctx . FormInt64 ( "project" )
2020-08-17 04:07:38 +01:00
if projectID > 0 {
2022-03-29 22:16:31 +08:00
project , err := project_model . GetProjectByID ( projectID )
2020-08-17 04:07:38 +01:00
if err != nil {
log . Error ( "GetProjectByID: %d: %v" , projectID , err )
} else if project . RepoID != ctx . Repo . Repository . ID {
log . Error ( "GetProjectByID: %d: %v" , projectID , fmt . Errorf ( "project[%d] not in repo [%d]" , project . ID , ctx . Repo . Repository . ID ) )
} else {
ctx . Data [ "project_id" ] = projectID
ctx . Data [ "Project" ] = project
}
2021-10-05 21:21:52 +02:00
if len ( ctx . Req . URL . Query ( ) . Get ( "project" ) ) > 0 {
ctx . Data [ "redirect_after_creation" ] = "project"
}
2020-08-17 04:07:38 +01:00
}
2020-01-19 14:43:38 +08:00
RetrieveRepoMetas ( ctx , ctx . Repo . Repository , false )
2020-09-11 09:48:39 -05:00
setTemplateIfExists ( ctx , issueTemplateKey , context . IssueTemplateDirCandidates , IssueTemplateCandidates )
2015-08-31 16:24:28 +09:00
if ctx . Written ( ) {
return
2015-08-10 18:57:57 +08:00
}
2015-08-09 15:23:02 +08:00
2021-11-10 03:57:58 +08:00
ctx . Data [ "HasIssuesOrPullsWritePermission" ] = ctx . Repo . CanWrite ( unit . TypeIssues )
2020-04-04 13:39:48 +08:00
2021-04-05 17:30:52 +02:00
ctx . HTML ( http . StatusOK , tplIssueNew )
2014-07-26 02:28:04 -04:00
}
2020-09-11 09:48:39 -05:00
// NewIssueChooseTemplate render creating issue from template page
func NewIssueChooseTemplate ( ctx * context . Context ) {
ctx . Data [ "Title" ] = ctx . Tr ( "repo.issues.new" )
ctx . Data [ "PageIsIssueList" ] = true
issueTemplates := ctx . IssueTemplatesFromDefaultBranch ( )
ctx . Data [ "IssueTemplates" ] = issueTemplates
2022-03-22 19:54:24 +00:00
if len ( issueTemplates ) == 0 {
// The "issues/new" and "issues/new/choose" share the same query parameters "project" and "milestone", if no template here, just redirect to the "issues/new" page with these parameters.
ctx . Redirect ( fmt . Sprintf ( "%s/issues/new?%s" , ctx . Repo . Repository . HTMLURL ( ) , ctx . Req . URL . RawQuery ) , http . StatusSeeOther )
return
}
ctx . Data [ "milestone" ] = ctx . FormInt64 ( "milestone" )
ctx . Data [ "project" ] = ctx . FormInt64 ( "project" )
2021-04-05 17:30:52 +02:00
ctx . HTML ( http . StatusOK , tplIssueChoose )
2020-09-11 09:48:39 -05:00
}
2022-03-09 01:38:11 +01:00
// DeleteIssue deletes an issue
func DeleteIssue ( ctx * context . Context ) {
issue := GetActionIssue ( ctx )
if ctx . Written ( ) {
return
}
2022-03-22 08:03:22 +01:00
if err := issue_service . DeleteIssue ( ctx . Doer , ctx . Repo . GitRepo , issue ) ; err != nil {
2022-03-09 01:38:11 +01:00
ctx . ServerError ( "DeleteIssueByID" , err )
return
}
ctx . Redirect ( fmt . Sprintf ( "%s/issues" , ctx . Repo . Repository . HTMLURL ( ) ) , http . StatusSeeOther )
}
2021-07-08 07:38:13 -04:00
// ValidateRepoMetas check and returns repository's meta information
2021-04-06 20:44:05 +01:00
func ValidateRepoMetas ( ctx * context . Context , form forms . CreateIssueForm , isPull bool ) ( [ ] int64 , [ ] int64 , int64 , int64 ) {
2015-09-01 19:07:02 -04:00
var (
repo = ctx . Repo . Repository
err error
)
2020-01-19 14:43:38 +08:00
labels := RetrieveRepoMetas ( ctx , ctx . Repo . Repository , isPull )
2015-09-01 19:07:02 -04:00
if ctx . Written ( ) {
2020-08-17 04:07:38 +01:00
return nil , nil , 0 , 0
2015-09-01 19:07:02 -04:00
}
2017-02-01 10:36:08 +08:00
var labelIDs [ ] int64
2015-09-01 19:07:02 -04:00
hasSelected := false
2017-02-01 10:36:08 +08:00
// Check labels.
if len ( form . LabelIDs ) > 0 {
labelIDs , err = base . StringsToInt64s ( strings . Split ( form . LabelIDs , "," ) )
if err != nil {
2020-08-17 04:07:38 +01:00
return nil , nil , 0 , 0
2017-02-01 10:36:08 +08:00
}
labelIDMark := base . Int64sToMap ( labelIDs )
for i := range labels {
if labelIDMark [ labels [ i ] . ID ] {
labels [ i ] . IsChecked = true
hasSelected = true
}
2015-09-01 19:07:02 -04:00
}
}
2017-02-01 10:36:08 +08:00
ctx . Data [ "Labels" ] = labels
2015-09-01 19:07:02 -04:00
ctx . Data [ "HasSelectedLabel" ] = hasSelected
ctx . Data [ "label_ids" ] = form . LabelIDs
// Check milestone.
milestoneID := form . MilestoneID
if milestoneID > 0 {
2022-01-26 20:01:35 +00:00
milestone , err := models . GetMilestoneByRepoID ( ctx . Repo . Repository . ID , milestoneID )
2015-09-01 19:07:02 -04:00
if err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "GetMilestoneByID" , err )
2020-08-17 04:07:38 +01:00
return nil , nil , 0 , 0
2015-09-01 19:07:02 -04:00
}
2021-12-10 09:27:50 +08:00
if milestone . RepoID != repo . ID {
ctx . ServerError ( "GetMilestoneByID" , err )
return nil , nil , 0 , 0
}
ctx . Data [ "Milestone" ] = milestone
2015-09-01 19:07:02 -04:00
ctx . Data [ "milestone_id" ] = milestoneID
}
2020-08-17 04:07:38 +01:00
if form . ProjectID > 0 {
2022-03-29 22:16:31 +08:00
p , err := project_model . GetProjectByID ( form . ProjectID )
2020-08-17 04:07:38 +01:00
if err != nil {
ctx . ServerError ( "GetProjectByID" , err )
return nil , nil , 0 , 0
}
if p . RepoID != ctx . Repo . Repository . ID {
ctx . NotFound ( "" , nil )
return nil , nil , 0 , 0
}
ctx . Data [ "Project" ] = p
ctx . Data [ "project_id" ] = form . ProjectID
}
2018-05-09 18:29:04 +02:00
// Check assignees
var assigneeIDs [ ] int64
if len ( form . AssigneeIDs ) > 0 {
assigneeIDs , err = base . StringsToInt64s ( strings . Split ( form . AssigneeIDs , "," ) )
2015-09-01 19:07:02 -04:00
if err != nil {
2020-08-17 04:07:38 +01:00
return nil , nil , 0 , 0
2018-05-09 18:29:04 +02:00
}
2019-10-25 16:46:37 +02:00
// Check if the passed assignees actually exists and is assignable
2018-05-09 18:29:04 +02:00
for _ , aID := range assigneeIDs {
2021-11-24 17:49:20 +08:00
assignee , err := user_model . GetUserByID ( aID )
2018-11-28 19:26:14 +08:00
if err != nil {
ctx . ServerError ( "GetUserByID" , err )
2020-08-17 04:07:38 +01:00
return nil , nil , 0 , 0
2018-11-28 19:26:14 +08:00
}
2019-10-25 16:46:37 +02:00
valid , err := models . CanBeAssigned ( assignee , repo , isPull )
2018-05-09 18:29:04 +02:00
if err != nil {
2020-08-17 04:07:38 +01:00
ctx . ServerError ( "CanBeAssigned" , err )
return nil , nil , 0 , 0
2018-11-28 19:26:14 +08:00
}
2020-08-17 04:07:38 +01:00
2019-10-25 16:46:37 +02:00
if ! valid {
ctx . ServerError ( "canBeAssigned" , models . ErrUserDoesNotHaveAccessToRepo { UserID : aID , RepoName : repo . Name } )
2020-08-17 04:07:38 +01:00
return nil , nil , 0 , 0
2018-05-09 18:29:04 +02:00
}
2015-09-01 19:07:02 -04:00
}
}
2018-05-09 18:29:04 +02:00
// Keep the old assignee id thingy for compatibility reasons
if form . AssigneeID > 0 {
assigneeIDs = append ( assigneeIDs , form . AssigneeID )
}
2020-08-17 04:07:38 +01:00
return labelIDs , assigneeIDs , milestoneID , form . ProjectID
2015-09-01 19:07:02 -04:00
}
2016-11-24 15:04:31 +08:00
// NewIssuePost response for creating new issue
2021-01-26 23:36:53 +08:00
func NewIssuePost ( ctx * context . Context ) {
2021-04-06 20:44:05 +01:00
form := web . GetForm ( ctx ) . ( * forms . CreateIssueForm )
2015-08-09 15:23:02 +08:00
ctx . Data [ "Title" ] = ctx . Tr ( "repo.issues.new" )
ctx . Data [ "PageIsIssueList" ] = true
2020-09-11 09:48:39 -05:00
ctx . Data [ "NewIssueChooseTemplate" ] = len ( ctx . IssueTemplatesFromDefaultBranch ( ) ) > 0
2016-08-11 05:48:08 -07:00
ctx . Data [ "RequireHighlightJS" ] = true
2018-08-13 21:04:39 +02:00
ctx . Data [ "PullRequestWorkInProgressPrefixes" ] = setting . Repository . PullRequest . WorkInProgressPrefixes
2020-10-05 07:49:33 +02:00
ctx . Data [ "IsAttachmentEnabled" ] = setting . Attachment . Enabled
upload . AddUploadContext ( ctx , "comment" )
2014-07-26 02:28:04 -04:00
2015-08-10 16:52:08 +08:00
var (
2015-08-10 18:57:57 +08:00
repo = ctx . Repo . Repository
2015-08-11 23:24:40 +08:00
attachments [ ] string
2015-08-10 16:52:08 +08:00
)
2015-08-31 16:24:28 +09:00
2021-01-26 23:36:53 +08:00
labelIDs , assigneeIDs , milestoneID , projectID := ValidateRepoMetas ( ctx , * form , false )
2015-09-01 19:07:02 -04:00
if ctx . Written ( ) {
return
2015-08-10 16:52:08 +08:00
}
2020-08-18 12:23:45 +08:00
if setting . Attachment . Enabled {
2016-08-11 05:48:08 -07:00
attachments = form . Files
2015-08-11 23:24:40 +08:00
}
2014-07-26 02:28:04 -04:00
if ctx . HasError ( ) {
2021-04-05 17:30:52 +02:00
ctx . HTML ( http . StatusOK , tplIssueNew )
2014-07-26 02:28:04 -04:00
return
}
2019-01-21 12:45:32 +01:00
if util . IsEmptyString ( form . Title ) {
ctx . RenderWithErr ( ctx . Tr ( "repo.issues.new.title_empty" ) , tplIssueNew , form )
return
}
2014-07-26 02:28:04 -04:00
issue := & models . Issue {
2016-03-13 23:20:22 -04:00
RepoID : repo . ID ,
2021-11-16 18:18:25 +00:00
Repo : repo ,
2016-08-14 03:32:24 -07:00
Title : form . Title ,
2022-03-22 08:03:22 +01:00
PosterID : ctx . Doer . ID ,
Poster : ctx . Doer ,
2015-08-10 18:57:57 +08:00
MilestoneID : milestoneID ,
2015-08-10 21:47:23 +08:00
Content : form . Content ,
2017-08-24 14:30:27 +02:00
Ref : form . Ref ,
2014-07-26 02:28:04 -04:00
}
2020-04-01 00:14:46 -04:00
2019-10-29 00:45:43 +08:00
if err := issue_service . NewIssue ( repo , issue , labelIDs , attachments , assigneeIDs ) ; err != nil {
2018-05-09 18:29:04 +02:00
if models . IsErrUserDoesNotHaveAccessToRepo ( err ) {
2021-04-05 17:30:52 +02:00
ctx . Error ( http . StatusBadRequest , "UserDoesNotHaveAccessToRepo" , err . Error ( ) )
2018-05-09 18:29:04 +02:00
return
}
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "NewIssue" , err )
2014-07-26 02:28:04 -04:00
return
2015-08-10 23:31:59 +08:00
}
2020-08-17 04:07:38 +01:00
if projectID > 0 {
2022-03-22 08:03:22 +01:00
if err := models . ChangeProjectAssign ( issue , ctx . Doer , projectID ) ; err != nil {
2020-08-17 04:07:38 +01:00
ctx . ServerError ( "ChangeProjectAssign" , err )
return
}
}
2015-09-01 19:07:02 -04:00
log . Trace ( "Issue created: %d/%d" , repo . ID , issue . ID )
2021-10-05 21:21:52 +02:00
if ctx . FormString ( "redirect_after_creation" ) == "project" {
2021-11-16 18:18:25 +00:00
ctx . Redirect ( ctx . Repo . RepoLink + "/projects/" + strconv . FormatInt ( form . ProjectID , 10 ) )
2021-10-05 21:21:52 +02:00
} else {
2021-11-16 18:18:25 +00:00
ctx . Redirect ( issue . Link ( ) )
2021-10-05 21:21:52 +02:00
}
2014-07-26 02:28:04 -04:00
}
2022-01-10 04:32:37 -05:00
// roleDescriptor returns the Role Descriptor for a comment in/with the given repo, poster and issue
2021-12-10 09:27:50 +08:00
func roleDescriptor ( repo * repo_model . Repository , poster * user_model . User , issue * models . Issue ) ( models . RoleDescriptor , error ) {
2018-11-28 19:26:14 +08:00
perm , err := models . GetUserRepoPermission ( repo , poster )
if err != nil {
2021-11-11 07:29:30 +01:00
return models . RoleDescriptorNone , err
2017-12-20 23:43:26 -08:00
}
2020-11-28 23:52:29 +08:00
2021-11-11 07:29:30 +01:00
// By default the poster has no roles on the comment.
roleDescriptor := models . RoleDescriptorNone
2020-11-28 23:52:29 +08:00
2021-11-11 07:29:30 +01:00
// Check if the poster is owner of the repo.
if perm . IsOwner ( ) {
// If the poster isn't a admin, enable the owner role.
if ! poster . IsAdmin {
roleDescriptor = roleDescriptor . WithRole ( models . RoleDescriptorOwner )
} else {
2020-11-28 23:52:29 +08:00
2021-11-11 07:29:30 +01:00
// Otherwise check if poster is the real repo admin.
ok , err := models . IsUserRealRepoAdmin ( repo , poster )
if err != nil {
return models . RoleDescriptorNone , err
}
if ok {
roleDescriptor = roleDescriptor . WithRole ( models . RoleDescriptorOwner )
}
2020-11-28 23:52:29 +08:00
}
2021-11-11 07:29:30 +01:00
}
2020-11-28 23:52:29 +08:00
2021-11-11 07:29:30 +01:00
// Is the poster can write issues or pulls to the repo, enable the Writer role.
// Only enable this if the poster doesn't have the owner role already.
if ! roleDescriptor . HasRole ( "Owner" ) && perm . CanWriteIssuesOrPulls ( issue . IsPull ) {
roleDescriptor = roleDescriptor . WithRole ( models . RoleDescriptorWriter )
2020-11-28 23:52:29 +08:00
}
2021-11-11 07:29:30 +01:00
// If the poster is the actual poster of the issue, enable Poster role.
if issue . IsPoster ( poster . ID ) {
roleDescriptor = roleDescriptor . WithRole ( models . RoleDescriptorPoster )
2017-12-20 23:43:26 -08:00
}
2018-11-28 19:26:14 +08:00
2021-11-11 07:29:30 +01:00
return roleDescriptor , nil
2017-12-20 23:43:26 -08:00
}
2019-12-16 07:20:25 +01:00
func getBranchData ( ctx * context . Context , issue * models . Issue ) {
ctx . Data [ "BaseBranch" ] = nil
ctx . Data [ "HeadBranch" ] = nil
ctx . Data [ "HeadUserName" ] = nil
ctx . Data [ "BaseName" ] = ctx . Repo . Repository . OwnerName
if issue . IsPull {
pull := issue . PullRequest
ctx . Data [ "BaseBranch" ] = pull . BaseBranch
ctx . Data [ "HeadBranch" ] = pull . HeadBranch
ctx . Data [ "HeadUserName" ] = pull . MustHeadUserName ( )
}
}
2016-11-24 15:04:31 +08:00
// ViewIssue render issue view page
2016-03-11 11:56:52 -05:00
func ViewIssue ( ctx * context . Context ) {
2019-12-14 01:53:32 +01:00
if ctx . Params ( ":type" ) == "issues" {
// If issue was requested we check if repo has external tracker and redirect
2021-11-10 03:57:58 +08:00
extIssueUnit , err := ctx . Repo . Repository . GetUnit ( unit . TypeExternalTracker )
2019-12-14 01:53:32 +01:00
if err == nil && extIssueUnit != nil {
if extIssueUnit . ExternalTrackerConfig ( ) . ExternalTrackerStyle == markup . IssueNameStyleNumeric || extIssueUnit . ExternalTrackerConfig ( ) . ExternalTrackerStyle == "" {
metas := ctx . Repo . Repository . ComposeMetas ( )
metas [ "index" ] = ctx . Params ( ":index" )
ctx . Redirect ( com . Expand ( extIssueUnit . ExternalTrackerConfig ( ) . ExternalTrackerFormat , metas ) )
return
}
2021-12-10 09:27:50 +08:00
} else if err != nil && ! repo_model . IsErrUnitTypeNotExist ( err ) {
2019-12-14 01:53:32 +01:00
ctx . ServerError ( "GetUnit" , err )
2019-12-07 05:21:18 +01:00
return
}
}
2015-08-12 17:04:23 +08:00
issue , err := models . GetIssueByIndex ( ctx . Repo . Repository . ID , ctx . ParamsInt64 ( ":index" ) )
2014-07-26 02:28:04 -04:00
if err != nil {
2015-08-12 17:04:23 +08:00
if models . IsErrIssueNotExist ( err ) {
2018-01-10 22:34:17 +01:00
ctx . NotFound ( "GetIssueByIndex" , err )
2014-07-26 02:28:04 -04:00
} else {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "GetIssueByIndex" , err )
2014-07-26 02:28:04 -04:00
}
return
}
2021-11-16 18:18:25 +00:00
if issue . Repo == nil {
issue . Repo = ctx . Repo . Repository
}
2017-03-29 20:31:47 -03:00
2015-09-01 19:07:02 -04:00
// Make sure type and URL matches.
if ctx . Params ( ":type" ) == "issues" && issue . IsPull {
2021-11-16 18:18:25 +00:00
ctx . Redirect ( issue . Link ( ) )
2015-09-01 19:07:02 -04:00
return
} else if ctx . Params ( ":type" ) == "pulls" && ! issue . IsPull {
2021-11-16 18:18:25 +00:00
ctx . Redirect ( issue . Link ( ) )
2015-09-01 19:07:02 -04:00
return
}
2015-09-02 04:08:05 -04:00
if issue . IsPull {
2016-03-06 23:57:46 -05:00
MustAllowPulls ( ctx )
if ctx . Written ( ) {
return
}
ctx . Data [ "PageIsPullList" ] = true
2015-09-02 04:08:05 -04:00
ctx . Data [ "PageIsPullConversation" ] = true
} else {
2015-12-04 21:30:33 -05:00
MustEnableIssues ( ctx )
if ctx . Written ( ) {
return
}
2015-09-02 04:08:05 -04:00
ctx . Data [ "PageIsIssueList" ] = true
2020-09-11 09:48:39 -05:00
ctx . Data [ "NewIssueChooseTemplate" ] = len ( ctx . IssueTemplatesFromDefaultBranch ( ) ) > 0
2015-09-02 04:08:05 -04:00
}
2021-11-10 03:57:58 +08:00
if issue . IsPull && ! ctx . Repo . CanRead ( unit . TypeIssues ) {
2020-01-19 14:43:38 +08:00
ctx . Data [ "IssueType" ] = "pulls"
2021-11-10 03:57:58 +08:00
} else if ! issue . IsPull && ! ctx . Repo . CanRead ( unit . TypePullRequests ) {
2020-01-19 14:43:38 +08:00
ctx . Data [ "IssueType" ] = "issues"
} else {
ctx . Data [ "IssueType" ] = "all"
}
2018-11-28 19:26:14 +08:00
ctx . Data [ "RequireHighlightJS" ] = true
ctx . Data [ "RequireTribute" ] = true
2021-11-10 03:57:58 +08:00
ctx . Data [ "IsProjectsEnabled" ] = ctx . Repo . CanRead ( unit . TypeProjects )
2020-10-05 07:49:33 +02:00
ctx . Data [ "IsAttachmentEnabled" ] = setting . Attachment . Enabled
upload . AddUploadContext ( ctx , "comment" )
2018-11-28 19:26:14 +08:00
2019-09-20 02:45:38 -03:00
if err = issue . LoadAttributes ( ) ; err != nil {
ctx . ServerError ( "LoadAttributes" , err )
return
}
if err = filterXRefComments ( ctx , issue ) ; err != nil {
ctx . ServerError ( "filterXRefComments" , err )
2018-12-13 23:55:43 +08:00
return
}
2018-11-28 19:26:14 +08:00
ctx . Data [ "Title" ] = fmt . Sprintf ( "#%d - %s" , issue . Index , issue . Title )
2020-04-21 15:48:53 +02:00
iw := new ( models . IssueWatch )
2022-03-22 08:03:22 +01:00
if ctx . Doer != nil {
iw . UserID = ctx . Doer . ID
2020-04-21 15:48:53 +02:00
iw . IssueID = issue . ID
2022-03-22 08:03:22 +01:00
iw . IsWatching , err = models . CheckIssueWatch ( ctx . Doer , issue )
2018-11-28 19:26:14 +08:00
if err != nil {
2021-01-15 04:27:22 +08:00
ctx . ServerError ( "CheckIssueWatch" , err )
2018-11-28 19:26:14 +08:00
return
}
}
ctx . Data [ "IssueWatch" ] = iw
2021-04-20 06:25:08 +08:00
issue . 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
} , issue . Content )
if err != nil {
ctx . ServerError ( "RenderString" , err )
return
}
2014-07-26 02:28:04 -04:00
2015-08-15 00:42:43 +08:00
repo := ctx . Repo . Repository
2015-09-01 19:07:02 -04:00
// Get more information if it's a pull request.
if issue . IsPull {
2016-08-16 10:19:09 -07:00
if issue . PullRequest . HasMerged {
ctx . Data [ "DisableStatusChange" ] = issue . PullRequest . HasMerged
2015-09-02 09:26:56 -04:00
PrepareMergedViewPullInfo ( ctx , issue )
} else {
PrepareViewPullInfo ( ctx , issue )
2019-04-20 22:50:34 +02:00
ctx . Data [ "DisableStatusChange" ] = ctx . Data [ "IsPullRequestBroken" ] == true && issue . IsClosed
2015-09-02 09:26:56 -04:00
}
2015-09-02 04:08:05 -04:00
if ctx . Written ( ) {
2015-09-01 19:07:02 -04:00
return
}
}
2015-08-12 17:04:23 +08:00
// Metas.
2015-08-15 00:42:43 +08:00
// Check labels.
labelIDMark := make ( map [ int64 ] bool )
for i := range issue . Labels {
labelIDMark [ issue . Labels [ i ] . ID ] = true
}
2021-09-24 19:32:56 +08:00
labels , err := models . GetLabelsByRepoID ( repo . ID , "" , db . ListOptions { } )
2015-08-15 00:42:43 +08:00
if err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "GetLabelsByRepoID" , err )
2015-08-15 00:42:43 +08:00
return
}
2020-04-01 00:14:46 -04:00
ctx . Data [ "Labels" ] = labels
if repo . Owner . IsOrganization ( ) {
2021-09-24 19:32:56 +08:00
orgLabels , err := models . GetLabelsByOrgID ( repo . Owner . ID , ctx . FormString ( "sort" ) , db . ListOptions { } )
2020-04-01 00:14:46 -04:00
if err != nil {
ctx . ServerError ( "GetLabelsByOrgID" , err )
return
}
ctx . Data [ "OrgLabels" ] = orgLabels
labels = append ( labels , orgLabels ... )
}
2015-08-15 00:42:43 +08:00
hasSelected := false
for i := range labels {
if labelIDMark [ labels [ i ] . ID ] {
labels [ i ] . IsChecked = true
hasSelected = true
}
}
ctx . Data [ "HasSelectedLabel" ] = hasSelected
// Check milestone and assignee.
2018-11-28 19:26:14 +08:00
if ctx . Repo . CanWriteIssuesOrPulls ( issue . IsPull ) {
2015-09-01 19:07:02 -04:00
RetrieveRepoMilestonesAndAssignees ( ctx , repo )
2020-08-17 04:07:38 +01:00
retrieveProjects ( ctx , repo )
2015-09-01 19:07:02 -04:00
if ctx . Written ( ) {
2015-08-15 00:42:43 +08:00
return
}
}
2014-07-26 02:28:04 -04:00
2020-04-07 00:33:34 +08:00
if issue . IsPull {
2021-11-10 03:57:58 +08:00
canChooseReviewer := ctx . Repo . CanWrite ( unit . TypePullRequests )
2022-03-22 08:03:22 +01:00
if ! canChooseReviewer && ctx . Doer != nil && ctx . IsSigned {
canChooseReviewer , err = models . IsOfficialReviewer ( issue , ctx . Doer )
2020-04-07 00:33:34 +08:00
if err != nil {
ctx . ServerError ( "IsOfficialReviewer" , err )
return
}
}
2020-10-13 03:55:13 +08:00
RetrieveRepoReviewers ( ctx , repo , issue , canChooseReviewer )
2020-04-07 00:33:34 +08:00
if ctx . Written ( ) {
return
}
}
2015-08-12 17:04:23 +08:00
if ctx . IsSigned {
2015-08-12 18:44:09 +08:00
// Update issue-user.
2022-03-22 08:03:22 +01:00
if err = issue . ReadBy ( ctx . Doer . ID ) ; err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "ReadBy" , err )
2015-08-12 18:44:09 +08:00
return
}
2015-08-12 17:04:23 +08:00
}
2015-08-14 02:43:40 +08:00
var (
2021-11-11 07:29:30 +01:00
role models . RoleDescriptor
2016-01-19 14:04:24 +01:00
ok bool
2021-11-11 07:29:30 +01:00
marked = make ( map [ int64 ] models . RoleDescriptor )
2016-01-19 14:04:24 +01:00
comment * models . Comment
2021-11-24 17:49:20 +08:00
participants = make ( [ ] * user_model . User , 1 , 10 )
2015-08-14 02:43:40 +08:00
)
2017-09-12 08:48:13 +02:00
if ctx . Repo . Repository . IsTimetrackerEnabled ( ) {
if ctx . IsSigned {
// Deal with the stopwatch
2022-03-22 08:03:22 +01:00
ctx . Data [ "IsStopwatchRunning" ] = models . StopwatchExists ( ctx . Doer . ID , issue . ID )
2017-09-12 08:48:13 +02:00
if ! ctx . Data [ "IsStopwatchRunning" ] . ( bool ) {
var exists bool
var sw * models . Stopwatch
2022-03-22 08:03:22 +01:00
if exists , sw , err = models . HasUserStopwatch ( ctx . Doer . ID ) ; err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "HasUserStopwatch" , err )
2017-09-12 08:48:13 +02:00
return
}
ctx . Data [ "HasUserStopwatch" ] = exists
if exists {
// Add warning if the user has already a stopwatch
var otherIssue * models . Issue
if otherIssue , err = models . GetIssueByID ( sw . IssueID ) ; err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "GetIssueByID" , err )
2017-09-12 08:48:13 +02:00
return
}
2018-12-13 23:55:43 +08:00
if err = otherIssue . LoadRepo ( ) ; err != nil {
ctx . ServerError ( "LoadRepo" , err )
return
}
2017-09-12 08:48:13 +02:00
// Add link to the issue of the already running stopwatch
ctx . Data [ "OtherStopwatchURL" ] = otherIssue . HTMLURL ( )
}
}
2022-03-22 08:03:22 +01:00
ctx . Data [ "CanUseTimetracker" ] = ctx . Repo . CanUseTimetracker ( issue , ctx . Doer )
2017-09-12 08:48:13 +02:00
} else {
ctx . Data [ "CanUseTimetracker" ] = false
}
2021-08-12 14:43:08 +02:00
if ctx . Data [ "WorkingUsers" ] , err = models . TotalTimes ( & models . FindTrackedTimesOptions { IssueID : issue . ID } ) ; err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "TotalTimes" , err )
2017-09-12 08:48:13 +02:00
return
}
}
2016-02-01 20:55:12 -05:00
2018-07-17 23:23:58 +02:00
// Check if the user can use the dependencies
2022-03-22 08:03:22 +01:00
ctx . Data [ "CanCreateIssueDependencies" ] = ctx . Repo . CanCreateIssueDependencies ( ctx . Doer , issue . IsPull )
2018-07-17 23:23:58 +02:00
2019-10-31 00:06:10 -05:00
// check if dependencies can be created across repositories
ctx . Data [ "AllowCrossRepositoryDependencies" ] = setting . Service . AllowCrossRepositoryDependencies
2021-11-11 07:29:30 +01:00
if issue . ShowRole , err = roleDescriptor ( repo , issue . Poster , issue ) ; err != nil {
ctx . ServerError ( "roleDescriptor" , err )
2020-09-11 02:09:14 +08:00
return
}
2021-11-11 07:29:30 +01:00
marked [ issue . PosterID ] = issue . ShowRole
2020-09-11 02:09:14 +08:00
2016-02-01 20:55:12 -05:00
// Render comments and and fetch participants.
participants [ 0 ] = issue . Poster
2015-08-14 02:43:40 +08:00
for _ , comment = range issue . Comments {
2019-05-06 20:09:31 +08:00
comment . Issue = issue
2018-12-13 23:55:43 +08:00
if err := comment . LoadPoster ( ) ; err != nil {
ctx . ServerError ( "LoadPoster" , err )
return
}
2022-01-19 01:28:38 +08:00
if comment . Type == models . CommentTypeComment || comment . Type == models . CommentTypeReview {
2018-12-13 23:55:43 +08:00
if err := comment . LoadAttachments ( ) ; err != nil {
ctx . ServerError ( "LoadAttachments" , err )
return
}
2021-04-20 06:25:08 +08:00
comment . 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
} , comment . Content )
if err != nil {
ctx . ServerError ( "RenderString" , err )
return
}
2015-08-14 02:43:40 +08:00
// Check tag.
2021-11-11 07:29:30 +01:00
role , ok = marked [ comment . PosterID ]
2015-08-14 02:43:40 +08:00
if ok {
2021-11-11 07:29:30 +01:00
comment . ShowRole = role
2015-08-14 02:43:40 +08:00
continue
}
2021-11-11 07:29:30 +01:00
comment . ShowRole , err = roleDescriptor ( repo , comment . Poster , issue )
2017-12-20 23:43:26 -08:00
if err != nil {
2021-11-11 07:29:30 +01:00
ctx . ServerError ( "roleDescriptor" , err )
2017-12-20 23:43:26 -08:00
return
2015-08-14 02:43:40 +08:00
}
2021-11-11 07:29:30 +01:00
marked [ comment . PosterID ] = comment . ShowRole
2019-09-07 11:53:35 -03:00
participants = addParticipant ( comment . Poster , participants )
2017-01-30 20:46:45 +08:00
} else if comment . Type == models . CommentTypeLabel {
if err = comment . LoadLabel ( ) ; err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "LoadLabel" , err )
2017-01-30 20:46:45 +08:00
return
}
2017-02-01 10:36:08 +08:00
} else if comment . Type == models . CommentTypeMilestone {
if err = comment . LoadMilestone ( ) ; err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "LoadMilestone" , err )
2017-02-01 10:36:08 +08:00
return
}
2017-06-17 00:51:28 -04:00
ghostMilestone := & models . Milestone {
ID : - 1 ,
Name : ctx . Tr ( "repo.issues.deleted_milestone" ) ,
}
if comment . OldMilestoneID > 0 && comment . OldMilestone == nil {
comment . OldMilestone = ghostMilestone
}
if comment . MilestoneID > 0 && comment . Milestone == nil {
comment . Milestone = ghostMilestone
}
2020-08-17 04:07:38 +01:00
} else if comment . Type == models . CommentTypeProject {
if err = comment . LoadProject ( ) ; err != nil {
ctx . ServerError ( "LoadProject" , err )
return
}
2022-03-29 22:16:31 +08:00
ghostProject := & project_model . Project {
2020-08-17 04:07:38 +01:00
ID : - 1 ,
Title : ctx . Tr ( "repo.issues.deleted_project" ) ,
}
if comment . OldProjectID > 0 && comment . OldProject == nil {
comment . OldProject = ghostProject
}
if comment . ProjectID > 0 && comment . Project == nil {
comment . Project = ghostProject
}
2020-04-07 00:33:34 +08:00
} else if comment . Type == models . CommentTypeAssignees || comment . Type == models . CommentTypeReviewRequest {
2020-10-13 03:55:13 +08:00
if err = comment . LoadAssigneeUserAndTeam ( ) ; err != nil {
ctx . ServerError ( "LoadAssigneeUserAndTeam" , err )
2017-02-03 23:09:10 +08:00
return
}
2018-07-17 23:23:58 +02:00
} else if comment . Type == models . CommentTypeRemoveDependency || comment . Type == models . CommentTypeAddDependency {
if err = comment . LoadDepIssueDetails ( ) ; err != nil {
2020-09-04 03:36:56 +02:00
if ! models . IsErrIssueNotExist ( err ) {
ctx . ServerError ( "LoadDepIssueDetails" , err )
return
}
2018-07-17 23:23:58 +02:00
}
2021-02-12 01:32:25 +08:00
} else if comment . Type == models . CommentTypeCode || comment . Type == models . CommentTypeReview || comment . Type == models . CommentTypeDismissReview {
2021-04-20 06:25:08 +08:00
comment . 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
} , comment . Content )
if err != nil {
ctx . ServerError ( "RenderString" , err )
return
}
2018-08-06 07:43:22 +03:00
if err = comment . LoadReview ( ) ; err != nil && ! models . IsErrReviewNotExist ( err ) {
ctx . ServerError ( "LoadReview" , err )
return
}
2019-09-07 11:53:35 -03:00
participants = addParticipant ( comment . Poster , participants )
2018-08-06 07:43:22 +03:00
if comment . Review == nil {
continue
}
2022-01-19 23:26:57 +00:00
if err = comment . Review . LoadAttributes ( ctx ) ; err != nil {
2021-11-24 17:49:20 +08:00
if ! user_model . IsErrUserNotExist ( err ) {
2019-05-06 20:09:31 +08:00
ctx . ServerError ( "Review.LoadAttributes" , err )
return
}
2021-11-24 17:49:20 +08:00
comment . Review . Reviewer = user_model . NewGhostUser ( )
2018-08-06 07:43:22 +03:00
}
2022-01-19 23:26:57 +00:00
if err = comment . Review . LoadCodeComments ( ctx ) ; err != nil {
2018-08-06 07:43:22 +03:00
ctx . ServerError ( "Review.LoadCodeComments" , err )
return
}
2021-01-17 19:29:10 +02:00
for _ , codeComments := range comment . Review . CodeComments {
for _ , lineComments := range codeComments {
for _ , c := range lineComments {
// Check tag.
2021-11-11 07:29:30 +01:00
role , ok = marked [ c . PosterID ]
2021-01-17 19:29:10 +02:00
if ok {
2021-11-11 07:29:30 +01:00
c . ShowRole = role
2021-01-17 19:29:10 +02:00
continue
}
2020-04-18 21:50:25 +08:00
2021-11-11 07:29:30 +01:00
c . ShowRole , err = roleDescriptor ( repo , c . Poster , issue )
2021-01-17 19:29:10 +02:00
if err != nil {
2021-11-11 07:29:30 +01:00
ctx . ServerError ( "roleDescriptor" , err )
2021-01-17 19:29:10 +02:00
return
}
2021-11-11 07:29:30 +01:00
marked [ c . PosterID ] = c . ShowRole
2021-01-17 19:29:10 +02:00
participants = addParticipant ( c . Poster , participants )
}
}
}
2020-04-18 21:50:25 +08:00
if err = comment . LoadResolveDoer ( ) ; err != nil {
ctx . ServerError ( "LoadResolveDoer" , err )
return
}
2022-01-21 18:59:26 +01:00
} else if comment . Type == models . CommentTypePullRequestPush {
2020-05-20 20:47:24 +08:00
participants = addParticipant ( comment . Poster , participants )
2022-01-19 23:26:57 +00:00
if err = comment . LoadPushCommits ( ctx ) ; err != nil {
2020-05-20 20:47:24 +08:00
ctx . ServerError ( "LoadPushCommits" , err )
return
}
2021-02-19 10:52:11 +00:00
} else if comment . Type == models . CommentTypeAddTimeManual ||
comment . Type == models . CommentTypeStopTracking {
// drop error since times could be pruned from DB..
_ = comment . LoadTime ( )
2015-08-13 16:07:11 +08:00
}
}
2014-07-26 02:28:04 -04:00
2020-10-25 21:49:48 +00:00
// Combine multiple label assignments into a single comment
combineLabelComments ( issue )
2019-12-16 07:20:25 +01:00
getBranchData ( ctx , issue )
2016-12-25 23:27:25 +08:00
if issue . IsPull {
pull := issue . PullRequest
2018-12-12 07:49:33 +08:00
pull . Issue = issue
2016-12-25 17:19:25 +01:00
canDelete := false
2020-01-11 08:29:34 +01:00
ctx . Data [ "AllowMerge" ] = false
2016-12-25 17:19:25 +01:00
2017-06-21 04:00:03 +03:00
if ctx . IsSigned {
2020-03-03 06:31:55 +08:00
if err := pull . LoadHeadRepo ( ) ; err != nil {
log . Error ( "LoadHeadRepo: %v" , err )
2018-11-28 19:26:14 +08:00
} else if pull . HeadRepo != nil && pull . HeadBranch != pull . HeadRepo . DefaultBranch {
2022-03-22 08:03:22 +01:00
perm , err := models . GetUserRepoPermission ( pull . HeadRepo , ctx . Doer )
2018-11-28 19:26:14 +08:00
if err != nil {
ctx . ServerError ( "GetUserRepoPermission" , err )
return
}
2021-11-10 03:57:58 +08:00
if perm . CanWrite ( unit . TypeCode ) {
2018-11-28 19:26:14 +08:00
// Check if branch is not protected
2021-12-10 09:27:50 +08:00
if protected , err := models . IsProtectedBranch ( pull . HeadRepo . ID , pull . HeadBranch ) ; err != nil {
2019-04-02 08:48:31 +01:00
log . Error ( "IsProtectedBranch: %v" , err )
2018-11-28 19:26:14 +08:00
} else if ! protected {
canDelete = true
2021-11-16 18:18:25 +00:00
ctx . Data [ "DeleteBranchLink" ] = issue . Link ( ) + "/cleanup"
2018-11-28 19:26:14 +08:00
}
2017-06-21 04:00:03 +03:00
}
2016-12-25 17:19:25 +01:00
}
2020-01-11 08:29:34 +01:00
2020-03-03 06:31:55 +08:00
if err := pull . LoadBaseRepo ( ) ; err != nil {
log . Error ( "LoadBaseRepo: %v" , err )
2020-01-11 08:29:34 +01:00
}
2022-03-22 08:03:22 +01:00
perm , err := models . GetUserRepoPermission ( pull . BaseRepo , ctx . Doer )
2020-01-11 08:29:34 +01:00
if err != nil {
ctx . ServerError ( "GetUserRepoPermission" , err )
return
}
2022-03-22 08:03:22 +01:00
ctx . Data [ "AllowMerge" ] , err = pull_service . IsUserAllowedToMerge ( pull , perm , ctx . Doer )
2020-01-11 08:29:34 +01:00
if err != nil {
ctx . ServerError ( "IsUserAllowedToMerge" , err )
return
}
2020-04-18 21:50:25 +08:00
2022-03-22 08:03:22 +01:00
if ctx . Data [ "CanMarkConversation" ] , err = models . CanMarkConversation ( issue , ctx . Doer ) ; err != nil {
2020-04-18 21:50:25 +08:00
ctx . ServerError ( "CanMarkConversation" , err )
return
}
2016-12-25 17:19:25 +01:00
}
2016-12-25 23:27:25 +08:00
2021-11-10 03:57:58 +08:00
prUnit , err := repo . GetUnit ( unit . TypePullRequests )
2018-01-05 20:56:50 +02:00
if err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "GetUnit" , err )
2018-01-05 20:56:50 +02:00
return
}
prConfig := prUnit . PullRequestsConfig ( )
// Check correct values and select default
2021-12-10 09:27:50 +08:00
if ms , ok := ctx . Data [ "MergeStyle" ] . ( repo_model . MergeStyle ) ; ! ok ||
2018-01-05 20:56:50 +02:00
! prConfig . IsMergeStyleAllowed ( ms ) {
2021-03-27 09:55:40 -05:00
defaultMergeStyle := prConfig . GetDefaultMergeStyle ( )
if prConfig . IsMergeStyleAllowed ( defaultMergeStyle ) && ! ok {
ctx . Data [ "MergeStyle" ] = defaultMergeStyle
} else if prConfig . AllowMerge {
2021-12-10 09:27:50 +08:00
ctx . Data [ "MergeStyle" ] = repo_model . MergeStyleMerge
2018-01-05 20:56:50 +02:00
} else if prConfig . AllowRebase {
2021-12-10 09:27:50 +08:00
ctx . Data [ "MergeStyle" ] = repo_model . MergeStyleRebase
2018-12-27 11:27:08 +01:00
} else if prConfig . AllowRebaseMerge {
2021-12-10 09:27:50 +08:00
ctx . Data [ "MergeStyle" ] = repo_model . MergeStyleRebaseMerge
2018-01-05 20:56:50 +02:00
} else if prConfig . AllowSquash {
2021-12-10 09:27:50 +08:00
ctx . Data [ "MergeStyle" ] = repo_model . MergeStyleSquash
2021-03-04 11:41:23 +08:00
} else if prConfig . AllowManualMerge {
2021-12-10 09:27:50 +08:00
ctx . Data [ "MergeStyle" ] = repo_model . MergeStyleManuallyMerged
2018-01-05 20:56:50 +02:00
} else {
ctx . Data [ "MergeStyle" ] = ""
}
}
2018-12-11 12:28:37 +01:00
if err = pull . LoadProtectedBranch ( ) ; err != nil {
ctx . ServerError ( "LoadProtectedBranch" , err )
return
}
2021-10-17 18:58:36 +02:00
ctx . Data [ "ShowMergeInstructions" ] = true
2018-12-11 12:28:37 +01:00
if pull . ProtectedBranch != nil {
2021-11-09 21:14:12 +08:00
var showMergeInstructions bool
2022-03-22 08:03:22 +01:00
if ctx . Doer != nil {
showMergeInstructions = pull . ProtectedBranch . CanUserPush ( ctx . Doer . ID )
2021-11-09 21:14:12 +08:00
}
2018-12-12 07:49:33 +08:00
cnt := pull . ProtectedBranch . GetGrantedApprovalsCount ( pull )
2020-01-03 18:47:10 +01:00
ctx . Data [ "IsBlockedByApprovals" ] = ! pull . ProtectedBranch . HasEnoughApprovals ( pull )
ctx . Data [ "IsBlockedByRejection" ] = pull . ProtectedBranch . MergeBlockedByRejectedReview ( pull )
2020-11-29 03:30:46 +08:00
ctx . Data [ "IsBlockedByOfficialReviewRequests" ] = pull . ProtectedBranch . MergeBlockedByOfficialReviewRequests ( pull )
2020-04-17 03:00:36 +02:00
ctx . Data [ "IsBlockedByOutdatedBranch" ] = pull . ProtectedBranch . MergeBlockedByOutdatedBranch ( pull )
2018-12-12 07:49:33 +08:00
ctx . Data [ "GrantedApprovals" ] = cnt
2020-01-15 08:32:57 +00:00
ctx . Data [ "RequireSigned" ] = pull . ProtectedBranch . RequireSignedCommits
2020-10-14 02:50:57 +08:00
ctx . Data [ "ChangedProtectedFiles" ] = pull . ChangedProtectedFiles
ctx . Data [ "IsBlockedByChangedProtectedFiles" ] = len ( pull . ChangedProtectedFiles ) != 0
ctx . Data [ "ChangedProtectedFilesNum" ] = len ( pull . ChangedProtectedFiles )
2021-11-09 21:14:12 +08:00
ctx . Data [ "ShowMergeInstructions" ] = showMergeInstructions
2020-01-15 08:32:57 +00:00
}
ctx . Data [ "WillSign" ] = false
2022-03-22 08:03:22 +01:00
if ctx . Doer != nil {
sign , key , _ , err := asymkey_service . SignMerge ( ctx , pull , ctx . Doer , pull . BaseRepo . RepoPath ( ) , pull . BaseBranch , pull . GetGitRefName ( ) )
2020-01-15 08:32:57 +00:00
ctx . Data [ "WillSign" ] = sign
ctx . Data [ "SigningKey" ] = key
if err != nil {
2021-12-10 16:14:24 +08:00
if asymkey_service . IsErrWontSign ( err ) {
ctx . Data [ "WontSignReason" ] = err . ( * asymkey_service . ErrWontSign ) . Reason
2020-01-15 08:32:57 +00:00
} else {
ctx . Data [ "WontSignReason" ] = "error"
log . Error ( "Error whilst checking if could sign pr %d in repo %s. Error: %v" , pull . ID , pull . BaseRepo . FullName ( ) , err )
}
}
2020-08-23 22:59:41 +01:00
} else {
ctx . Data [ "WontSignReason" ] = "not_signed_in"
2018-12-11 12:28:37 +01:00
}
2022-01-04 03:45:58 +08:00
isPullBranchDeletable := canDelete &&
2020-01-07 17:06:14 +00:00
pull . HeadRepo != nil &&
2021-11-30 20:06:32 +00:00
git . IsBranchExist ( ctx , pull . HeadRepo . RepoPath ( ) , pull . HeadBranch ) &&
2020-01-07 17:06:14 +00:00
( ! pull . HasMerged || ctx . Data [ "HeadBranchCommitID" ] == ctx . Data [ "PullHeadCommitID" ] )
2021-03-04 11:41:23 +08:00
2022-01-04 03:45:58 +08:00
if isPullBranchDeletable && pull . HasMerged {
exist , err := models . HasUnmergedPullRequestsByHeadInfo ( pull . HeadRepoID , pull . HeadBranch )
if err != nil {
ctx . ServerError ( "HasUnmergedPullRequestsByHeadInfo" , err )
return
}
isPullBranchDeletable = ! exist
}
ctx . Data [ "IsPullBranchDeletable" ] = isPullBranchDeletable
2021-03-04 11:41:23 +08:00
stillCanManualMerge := func ( ) bool {
if pull . HasMerged || issue . IsClosed || ! ctx . IsSigned {
return false
}
if pull . CanAutoMerge ( ) || pull . IsWorkInProgress ( ) || pull . IsChecking ( ) {
return false
}
2022-03-22 08:03:22 +01:00
if ( ctx . Doer . IsAdmin || ctx . Repo . IsAdmin ( ) ) && prConfig . AllowManualMerge {
2021-03-04 11:41:23 +08:00
return true
}
return false
}
ctx . Data [ "StillCanManualMerge" ] = stillCanManualMerge ( )
2016-12-25 23:27:25 +08:00
}
2018-07-17 23:23:58 +02:00
// Get Dependencies
ctx . Data [ "BlockedByDependencies" ] , err = issue . BlockedByDependencies ( )
2019-06-12 21:41:28 +02:00
if err != nil {
ctx . ServerError ( "BlockedByDependencies" , err )
return
}
2018-07-17 23:23:58 +02:00
ctx . Data [ "BlockingDependencies" ] , err = issue . BlockingDependencies ( )
2019-06-12 21:41:28 +02:00
if err != nil {
ctx . ServerError ( "BlockingDependencies" , err )
return
}
2018-07-17 23:23:58 +02:00
2016-01-19 14:04:24 +01:00
ctx . Data [ "Participants" ] = participants
2016-02-01 20:55:12 -05:00
ctx . Data [ "NumParticipants" ] = len ( participants )
2014-07-26 02:28:04 -04:00
ctx . Data [ "Issue" ] = issue
2021-12-17 22:29:09 +01:00
ctx . Data [ "Reference" ] = issue . Ref
2021-11-16 18:18:25 +00:00
ctx . Data [ "SignInLink" ] = setting . AppSubURL + "/user/login?redirect_to=" + url . QueryEscape ( ctx . Data [ "Link" ] . ( string ) )
2022-03-22 08:03:22 +01:00
ctx . Data [ "IsIssuePoster" ] = ctx . IsSigned && issue . IsPoster ( ctx . Doer . ID )
2020-04-04 13:39:48 +08:00
ctx . Data [ "HasIssuesOrPullsWritePermission" ] = ctx . Repo . CanWriteIssuesOrPulls ( issue . IsPull )
2021-11-10 03:57:58 +08:00
ctx . Data [ "HasProjectsWritePermission" ] = ctx . Repo . CanWrite ( unit . TypeProjects )
2022-03-22 08:03:22 +01:00
ctx . Data [ "IsRepoAdmin" ] = ctx . IsSigned && ( ctx . Repo . IsAdmin ( ) || ctx . Doer . IsAdmin )
2019-02-18 21:55:04 +01:00
ctx . Data [ "LockReasons" ] = setting . Repository . Issue . LockReasons
2020-05-15 00:55:43 +02:00
ctx . Data [ "RefEndName" ] = git . RefEndName ( issue . Ref )
2022-01-21 18:59:26 +01:00
var hiddenCommentTypes * big . Int
if ctx . IsSigned {
2022-03-22 08:03:22 +01:00
val , err := user_model . GetUserSetting ( ctx . Doer . ID , user_model . SettingsKeyHiddenCommentTypes )
2022-01-21 18:59:26 +01:00
if err != nil {
ctx . ServerError ( "GetUserSetting" , err )
return
}
hiddenCommentTypes , _ = new ( big . Int ) . SetString ( val , 10 ) // we can safely ignore the failed conversion here
}
ctx . Data [ "ShouldShowCommentType" ] = func ( commentType models . CommentType ) bool {
return hiddenCommentTypes == nil || hiddenCommentTypes . Bit ( int ( commentType ) ) == 0
}
2021-04-05 17:30:52 +02:00
ctx . HTML ( http . StatusOK , tplIssueView )
2014-07-26 02:28:04 -04:00
}
2017-09-12 08:48:13 +02:00
// GetActionIssue will return the issue which is used in the context.
func GetActionIssue ( ctx * context . Context ) * models . Issue {
2015-08-19 23:14:57 +08:00
issue , err := models . GetIssueByIndex ( ctx . Repo . Repository . ID , ctx . ParamsInt64 ( ":index" ) )
2014-07-26 02:28:04 -04:00
if err != nil {
2017-10-16 10:55:43 +03:00
ctx . NotFoundOrServerError ( "GetIssueByIndex" , models . IsErrIssueNotExist , err )
return nil
}
2018-12-13 23:55:43 +08:00
issue . Repo = ctx . Repo . Repository
2017-12-04 01:14:26 +02:00
checkIssueRights ( ctx , issue )
if ctx . Written ( ) {
2017-10-16 10:55:43 +03:00
return nil
}
if err = issue . LoadAttributes ( ) ; err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "LoadAttributes" , nil )
2015-08-19 23:14:57 +08:00
return nil
}
return issue
}
2017-12-04 01:14:26 +02:00
func checkIssueRights ( ctx * context . Context , issue * models . Issue ) {
2021-11-10 03:57:58 +08:00
if issue . IsPull && ! ctx . Repo . CanRead ( unit . TypePullRequests ) ||
! issue . IsPull && ! ctx . Repo . CanRead ( unit . TypeIssues ) {
2018-01-10 22:34:17 +01:00
ctx . NotFound ( "IssueOrPullRequestUnitNotAllowed" , nil )
2017-12-04 01:14:26 +02:00
}
}
2017-03-14 21:10:35 -04:00
func getActionIssues ( ctx * context . Context ) [ ] * models . Issue {
2021-08-11 02:31:13 +02:00
commaSeparatedIssueIDs := ctx . FormString ( "issue_ids" )
2017-03-14 21:10:35 -04:00
if len ( commaSeparatedIssueIDs ) == 0 {
return nil
}
issueIDs := make ( [ ] int64 , 0 , 10 )
for _ , stringIssueID := range strings . Split ( commaSeparatedIssueIDs , "," ) {
issueID , err := strconv . ParseInt ( stringIssueID , 10 , 64 )
if err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "ParseInt" , err )
2017-03-14 21:10:35 -04:00
return nil
}
issueIDs = append ( issueIDs , issueID )
}
issues , err := models . GetIssuesByIDs ( issueIDs )
if err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "GetIssuesByIDs" , err )
2017-03-14 21:10:35 -04:00
return nil
}
2017-10-16 10:55:43 +03:00
// Check access rights for all issues
2021-11-10 03:57:58 +08:00
issueUnitEnabled := ctx . Repo . CanRead ( unit . TypeIssues )
prUnitEnabled := ctx . Repo . CanRead ( unit . TypePullRequests )
2017-10-16 10:55:43 +03:00
for _ , issue := range issues {
if issue . IsPull && ! prUnitEnabled || ! issue . IsPull && ! issueUnitEnabled {
2018-01-10 22:34:17 +01:00
ctx . NotFound ( "IssueOrPullRequestUnitNotAllowed" , nil )
2017-10-16 10:55:43 +03:00
return nil
}
if err = issue . LoadAttributes ( ) ; err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "LoadAttributes" , err )
2017-10-16 10:55:43 +03:00
return nil
}
}
2017-03-14 21:10:35 -04:00
return issues
}
2016-11-24 15:04:31 +08:00
// UpdateIssueTitle change issue's title
2016-03-11 11:56:52 -05:00
func UpdateIssueTitle ( ctx * context . Context ) {
2017-09-12 08:48:13 +02:00
issue := GetActionIssue ( ctx )
2015-08-19 23:14:57 +08:00
if ctx . Written ( ) {
2014-07-26 02:28:04 -04:00
return
}
2022-03-22 08:03:22 +01:00
if ! ctx . IsSigned || ( ! issue . IsPoster ( ctx . Doer . ID ) && ! ctx . Repo . CanWriteIssuesOrPulls ( issue . IsPull ) ) {
2021-04-05 17:30:52 +02:00
ctx . Error ( http . StatusForbidden )
2014-07-26 02:28:04 -04:00
return
}
2021-07-29 09:42:15 +08:00
title := ctx . FormTrim ( "title" )
2016-08-14 03:32:24 -07:00
if len ( title ) == 0 {
2021-04-05 17:30:52 +02:00
ctx . Error ( http . StatusNoContent )
2015-08-19 23:14:57 +08:00
return
2014-07-26 02:28:04 -04:00
}
2015-08-19 23:14:57 +08:00
2022-03-22 08:03:22 +01:00
if err := issue_service . ChangeTitle ( issue , ctx . Doer , title ) ; err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "ChangeTitle" , err )
2014-07-26 02:28:04 -04:00
return
}
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusOK , map [ string ] interface { } {
2016-08-14 03:32:24 -07:00
"title" : issue . Title ,
2014-07-26 02:28:04 -04:00
} )
}
2020-09-08 18:29:51 +02:00
// UpdateIssueRef change issue's ref (branch)
func UpdateIssueRef ( ctx * context . Context ) {
issue := GetActionIssue ( ctx )
if ctx . Written ( ) {
return
}
2022-03-22 08:03:22 +01:00
if ! ctx . IsSigned || ( ! issue . IsPoster ( ctx . Doer . ID ) && ! ctx . Repo . CanWriteIssuesOrPulls ( issue . IsPull ) ) || issue . IsPull {
2021-04-05 17:30:52 +02:00
ctx . Error ( http . StatusForbidden )
2020-09-08 18:29:51 +02:00
return
}
2021-07-29 09:42:15 +08:00
ref := ctx . FormTrim ( "ref" )
2020-09-08 18:29:51 +02:00
2022-03-22 08:03:22 +01:00
if err := issue_service . ChangeIssueRef ( issue , ctx . Doer , ref ) ; err != nil {
2020-09-08 18:29:51 +02:00
ctx . ServerError ( "ChangeRef" , err )
return
}
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusOK , map [ string ] interface { } {
2020-09-08 18:29:51 +02:00
"ref" : ref ,
} )
}
2016-11-24 15:04:31 +08:00
// UpdateIssueContent change issue's content
2016-03-11 11:56:52 -05:00
func UpdateIssueContent ( ctx * context . Context ) {
2017-09-12 08:48:13 +02:00
issue := GetActionIssue ( ctx )
2015-08-20 04:31:28 +08:00
if ctx . Written ( ) {
return
}
2022-03-22 08:03:22 +01:00
if ! ctx . IsSigned || ( ctx . Doer . ID != issue . PosterID && ! ctx . Repo . CanWriteIssuesOrPulls ( issue . IsPull ) ) {
2021-04-05 17:30:52 +02:00
ctx . Error ( http . StatusForbidden )
2015-08-20 04:31:28 +08:00
return
}
2022-03-22 08:03:22 +01:00
if err := issue_service . ChangeContent ( issue , ctx . Doer , ctx . Req . FormValue ( "content" ) ) ; err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "ChangeContent" , err )
2015-08-20 04:31:28 +08:00
return
}
2021-08-21 21:04:47 +08:00
// when update the request doesn't intend to update attachments (eg: change checkbox state), ignore attachment updates
if ! ctx . FormBool ( "ignore_attachments" ) {
if err := updateAttachments ( issue , ctx . FormStrings ( "files[]" ) ) ; err != nil {
ctx . ServerError ( "UpdateAttachments" , err )
return
}
2021-04-20 06:25:08 +08:00
}
content , err := markdown . RenderString ( & markup . RenderContext {
2021-11-16 18:18:25 +00:00
URLPrefix : ctx . FormString ( "context" ) , // FIXME: <- IS THIS SAFE ?
2021-04-20 06:25:08 +08:00
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
} , issue . Content )
if err != nil {
ctx . ServerError ( "RenderString" , err )
return
2019-10-15 20:19:32 +08:00
}
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusOK , map [ string ] interface { } {
2021-04-20 06:25:08 +08:00
"content" : content ,
2020-12-13 19:12:27 +00:00
"attachments" : attachmentsHTML ( ctx , issue . Attachments , issue . Content ) ,
2015-08-20 04:31:28 +08:00
} )
}
2016-11-24 15:04:31 +08:00
// UpdateIssueMilestone change issue's milestone
2016-03-11 11:56:52 -05:00
func UpdateIssueMilestone ( ctx * context . Context ) {
2017-03-14 21:10:35 -04:00
issues := getActionIssues ( ctx )
2015-08-15 00:42:43 +08:00
if ctx . Written ( ) {
2014-07-26 02:28:04 -04:00
return
}
2021-07-29 09:42:15 +08:00
milestoneID := ctx . FormInt64 ( "id" )
2017-03-14 21:10:35 -04:00
for _ , issue := range issues {
oldMilestoneID := issue . MilestoneID
if oldMilestoneID == milestoneID {
continue
}
issue . MilestoneID = milestoneID
2022-03-22 08:03:22 +01:00
if err := issue_service . ChangeMilestoneAssign ( issue , ctx . Doer , oldMilestoneID ) ; err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "ChangeMilestoneAssign" , err )
2017-03-14 21:10:35 -04:00
return
}
2014-07-26 02:28:04 -04:00
}
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusOK , map [ string ] interface { } {
2014-07-26 02:28:04 -04:00
"ok" : true ,
} )
}
2019-10-25 16:46:37 +02:00
// UpdateIssueAssignee change issue's or pull's assignee
2016-03-11 11:56:52 -05:00
func UpdateIssueAssignee ( ctx * context . Context ) {
2017-03-14 21:10:35 -04:00
issues := getActionIssues ( ctx )
2015-08-15 00:42:43 +08:00
if ctx . Written ( ) {
2014-07-26 02:28:04 -04:00
return
}
2021-07-29 09:42:15 +08:00
assigneeID := ctx . FormInt64 ( "id" )
2021-08-11 02:31:13 +02:00
action := ctx . FormString ( "action" )
2018-05-09 18:29:04 +02:00
2017-03-14 21:10:35 -04:00
for _ , issue := range issues {
2018-05-09 18:29:04 +02:00
switch action {
case "clear" :
2022-03-22 08:03:22 +01:00
if err := issue_service . DeleteNotPassedAssignee ( issue , ctx . Doer , [ ] * user_model . User { } ) ; err != nil {
2018-05-09 18:29:04 +02:00
ctx . ServerError ( "ClearAssignees" , err )
return
}
default :
2021-11-24 17:49:20 +08:00
assignee , err := user_model . GetUserByID ( assigneeID )
2019-10-25 16:46:37 +02:00
if err != nil {
ctx . ServerError ( "GetUserByID" , err )
return
}
valid , err := models . CanBeAssigned ( assignee , issue . Repo , issue . IsPull )
if err != nil {
ctx . ServerError ( "canBeAssigned" , err )
2018-05-09 18:29:04 +02:00
return
}
2019-10-25 16:46:37 +02:00
if ! valid {
ctx . ServerError ( "canBeAssigned" , models . ErrUserDoesNotHaveAccessToRepo { UserID : assigneeID , RepoName : issue . Repo . Name } )
return
}
2022-03-22 08:03:22 +01:00
_ , _ , err = issue_service . ToggleAssignee ( issue , ctx . Doer , assigneeID )
2019-10-25 16:46:37 +02:00
if err != nil {
ctx . ServerError ( "ToggleAssignee" , err )
return
}
2017-03-14 21:10:35 -04:00
}
2014-07-26 02:28:04 -04:00
}
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusOK , map [ string ] interface { } {
2017-03-14 21:10:35 -04:00
"ok" : true ,
} )
}
2014-07-26 02:28:04 -04:00
2020-10-13 03:55:13 +08:00
// UpdatePullReviewRequest add or remove review request
func UpdatePullReviewRequest ( ctx * context . Context ) {
2020-04-07 00:33:34 +08:00
issues := getActionIssues ( ctx )
if ctx . Written ( ) {
return
}
2021-07-29 09:42:15 +08:00
reviewID := ctx . FormInt64 ( "id" )
2021-08-11 02:31:13 +02:00
action := ctx . FormString ( "action" )
2020-04-07 00:33:34 +08:00
2020-09-03 00:55:13 +08:00
// TODO: Not support 'clear' now
if action != "attach" && action != "detach" {
2022-03-23 05:54:07 +01:00
ctx . Status ( http . StatusForbidden )
2020-04-07 00:33:34 +08:00
return
}
for _ , issue := range issues {
2020-10-13 03:55:13 +08:00
if err := issue . LoadRepo ( ) ; err != nil {
ctx . ServerError ( "issue.LoadRepo" , err )
return
}
if ! issue . IsPull {
log . Warn (
"UpdatePullReviewRequest: refusing to add review request for non-PR issue %-v#%d" ,
issue . Repo , issue . Index ,
)
2022-03-23 05:54:07 +01:00
ctx . Status ( http . StatusForbidden )
2020-10-13 03:55:13 +08:00
return
}
if reviewID < 0 {
// negative reviewIDs represent team requests
2022-03-22 23:22:54 +08:00
if err := issue . Repo . GetOwner ( ctx ) ; err != nil {
2020-10-13 03:55:13 +08:00
ctx . ServerError ( "issue.Repo.GetOwner" , err )
return
}
if ! issue . Repo . Owner . IsOrganization ( ) {
log . Warn (
"UpdatePullReviewRequest: refusing to add team review request for %s#%d owned by non organization UID[%d]" ,
issue . Repo . FullName ( ) , issue . Index , issue . Repo . ID ,
)
2022-03-23 05:54:07 +01:00
ctx . Status ( http . StatusForbidden )
2020-10-13 03:55:13 +08:00
return
}
2020-04-07 00:33:34 +08:00
2022-03-29 14:29:02 +08:00
team , err := organization . GetTeamByID ( - reviewID )
2020-04-07 00:33:34 +08:00
if err != nil {
2022-03-29 14:29:02 +08:00
ctx . ServerError ( "GetTeamByID" , err )
2020-04-07 00:33:34 +08:00
return
}
2020-10-13 03:55:13 +08:00
if team . OrgID != issue . Repo . OwnerID {
log . Warn (
"UpdatePullReviewRequest: refusing to add team review request for UID[%d] team %s to %s#%d owned by UID[%d]" ,
team . OrgID , team . Name , issue . Repo . FullName ( ) , issue . Index , issue . Repo . ID )
2022-03-23 05:54:07 +01:00
ctx . Status ( http . StatusForbidden )
2020-10-13 03:55:13 +08:00
return
}
2022-03-22 08:03:22 +01:00
err = issue_service . IsValidTeamReviewRequest ( team , ctx . Doer , action == "attach" , issue )
2020-04-07 00:33:34 +08:00
if err != nil {
2020-10-13 03:55:13 +08:00
if models . IsErrNotValidReviewRequest ( err ) {
log . Warn (
"UpdatePullReviewRequest: refusing to add invalid team review request for UID[%d] team %s to %s#%d owned by UID[%d]: Error: %v" ,
team . OrgID , team . Name , issue . Repo . FullName ( ) , issue . Index , issue . Repo . ID ,
err ,
)
2022-03-23 05:54:07 +01:00
ctx . Status ( http . StatusForbidden )
2020-10-13 03:55:13 +08:00
return
}
2020-10-21 02:18:25 +08:00
ctx . ServerError ( "IsValidTeamReviewRequest" , err )
2020-04-07 00:33:34 +08:00
return
}
2022-03-22 08:03:22 +01:00
_ , err = issue_service . TeamReviewRequest ( issue , ctx . Doer , team , action == "attach" )
2020-04-07 00:33:34 +08:00
if err != nil {
2020-10-13 03:55:13 +08:00
ctx . ServerError ( "TeamReviewRequest" , err )
2020-04-07 00:33:34 +08:00
return
}
2020-10-13 03:55:13 +08:00
continue
}
2021-11-24 17:49:20 +08:00
reviewer , err := user_model . GetUserByID ( reviewID )
2020-10-13 03:55:13 +08:00
if err != nil {
2021-11-24 17:49:20 +08:00
if user_model . IsErrUserNotExist ( err ) {
2020-10-13 03:55:13 +08:00
log . Warn (
"UpdatePullReviewRequest: requested reviewer [%d] for %-v to %-v#%d is not exist: Error: %v" ,
reviewID , issue . Repo , issue . Index ,
err ,
)
2022-03-23 05:54:07 +01:00
ctx . Status ( http . StatusForbidden )
2020-10-13 03:55:13 +08:00
return
}
ctx . ServerError ( "GetUserByID" , err )
return
}
2022-03-22 08:03:22 +01:00
err = issue_service . IsValidReviewRequest ( reviewer , ctx . Doer , action == "attach" , issue , nil )
2020-10-13 03:55:13 +08:00
if err != nil {
if models . IsErrNotValidReviewRequest ( err ) {
log . Warn (
"UpdatePullReviewRequest: refusing to add invalid review request for %-v to %-v#%d: Error: %v" ,
reviewer , issue . Repo , issue . Index ,
err ,
)
2022-03-23 05:54:07 +01:00
ctx . Status ( http . StatusForbidden )
2020-10-13 03:55:13 +08:00
return
}
ctx . ServerError ( "isValidReviewRequest" , err )
return
}
2022-03-22 08:03:22 +01:00
_ , err = issue_service . ReviewRequest ( issue , ctx . Doer , reviewer , action == "attach" )
2020-10-13 03:55:13 +08:00
if err != nil {
ctx . ServerError ( "ReviewRequest" , err )
2020-09-03 00:55:13 +08:00
return
2020-04-07 00:33:34 +08:00
}
}
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusOK , map [ string ] interface { } {
2020-04-07 00:33:34 +08:00
"ok" : true ,
} )
}
2017-03-14 21:10:35 -04:00
// UpdateIssueStatus change issue's status
func UpdateIssueStatus ( ctx * context . Context ) {
issues := getActionIssues ( ctx )
if ctx . Written ( ) {
2014-07-26 02:28:04 -04:00
return
}
2017-03-14 21:10:35 -04:00
var isClosed bool
2021-08-11 02:31:13 +02:00
switch action := ctx . FormString ( "action" ) ; action {
2017-03-14 21:10:35 -04:00
case "open" :
isClosed = false
case "close" :
isClosed = true
default :
log . Warn ( "Unrecognized action: %s" , action )
}
if _ , err := models . IssueList ( issues ) . LoadRepositories ( ) ; err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "LoadRepositories" , err )
2017-03-14 21:10:35 -04:00
return
}
for _ , issue := range issues {
2018-10-18 19:23:05 +08:00
if issue . IsClosed != isClosed {
2022-03-22 08:03:22 +01:00
if err := issue_service . ChangeStatus ( issue , ctx . Doer , isClosed ) ; err != nil {
2018-10-18 19:23:05 +08:00
if models . IsErrDependenciesLeft ( err ) {
ctx . JSON ( http . StatusPreconditionFailed , map [ string ] interface { } {
"error" : "cannot close this issue because it still has open dependencies" ,
} )
return
}
ctx . ServerError ( "ChangeStatus" , err )
2018-07-17 23:23:58 +02:00
return
}
2017-03-14 21:10:35 -04:00
}
}
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusOK , map [ string ] interface { } {
2014-07-26 02:28:04 -04:00
"ok" : true ,
} )
}
2016-11-24 15:04:31 +08:00
// NewComment create a comment for issue
2021-01-26 23:36:53 +08:00
func NewComment ( ctx * context . Context ) {
2021-04-06 20:44:05 +01:00
form := web . GetForm ( ctx ) . ( * forms . CreateCommentForm )
2017-10-16 10:55:43 +03:00
issue := GetActionIssue ( ctx )
if ctx . Written ( ) {
2014-07-26 02:28:04 -04:00
return
}
2022-03-22 08:03:22 +01:00
if ! ctx . IsSigned || ( ctx . Doer . ID != issue . PosterID && ! ctx . Repo . CanReadIssuesOrPulls ( issue . IsPull ) ) {
2019-04-22 21:40:51 +01:00
if log . IsTrace ( ) {
if ctx . IsSigned {
issueType := "issues"
if issue . IsPull {
issueType = "pulls"
}
log . Trace ( "Permission Denied: User %-v not the Poster (ID: %d) and cannot read %s in Repo %-v.\n" +
"User in Repo has Permissions: %-+v" ,
2022-03-22 08:03:22 +01:00
ctx . Doer ,
2019-04-22 21:40:51 +01:00
log . NewColoredIDValue ( issue . PosterID ) ,
issueType ,
ctx . Repo . Repository ,
ctx . Repo . Permission )
} else {
log . Trace ( "Permission Denied: Not logged in" )
}
}
2021-04-05 17:30:52 +02:00
ctx . Error ( http . StatusForbidden )
2020-01-20 20:00:32 +08:00
return
2019-02-18 21:55:04 +01:00
}
2022-03-22 08:03:22 +01:00
if issue . IsLocked && ! ctx . Repo . CanWriteIssuesOrPulls ( issue . IsPull ) && ! ctx . Doer . IsAdmin {
2019-02-18 21:55:04 +01:00
ctx . Flash . Error ( ctx . Tr ( "repo.issues.comment_on_locked" ) )
2022-03-23 05:54:07 +01:00
ctx . Redirect ( issue . HTMLURL ( ) )
2018-11-28 19:26:14 +08:00
return
}
2015-08-13 16:07:11 +08:00
var attachments [ ] string
2020-08-18 12:23:45 +08:00
if setting . Attachment . Enabled {
2016-08-11 05:48:08 -07:00
attachments = form . Files
2014-07-26 02:28:04 -04:00
}
2015-08-13 16:07:11 +08:00
if ctx . HasError ( ) {
ctx . Flash . Error ( ctx . Data [ "ErrorMsg" ] . ( string ) )
2019-12-14 01:53:32 +01:00
ctx . Redirect ( issue . HTMLURL ( ) )
2015-08-13 16:07:11 +08:00
return
2014-07-26 02:28:04 -04:00
}
2015-10-18 19:30:39 -04:00
var comment * models . Comment
2015-09-13 11:26:25 -04:00
defer func ( ) {
2015-10-31 18:59:07 -04:00
// Check if issue admin/poster changes the status of issue.
2022-03-22 08:03:22 +01:00
if ( ctx . Repo . CanWriteIssuesOrPulls ( issue . IsPull ) || ( ctx . IsSigned && issue . IsPoster ( ctx . Doer . ID ) ) ) &&
2015-09-13 11:26:25 -04:00
( form . Status == "reopen" || form . Status == "close" ) &&
2016-08-16 10:19:09 -07:00
! ( issue . IsPull && issue . PullRequest . HasMerged ) {
2015-10-18 19:30:39 -04:00
2015-10-25 03:10:22 -04:00
// Duplication and conflict check should apply to reopen pull request.
2015-10-18 19:30:39 -04:00
var pr * models . PullRequest
2015-10-23 10:31:13 -04:00
if form . Status == "reopen" && issue . IsPull {
2015-10-18 19:30:39 -04:00
pull := issue . PullRequest
2019-06-12 21:41:28 +02:00
var err error
2021-07-28 17:42:56 +08:00
pr , err = models . GetUnmergedPullRequest ( pull . HeadRepoID , pull . BaseRepoID , pull . HeadBranch , pull . BaseBranch , pull . Flow )
2015-10-18 19:30:39 -04:00
if err != nil {
if ! models . IsErrPullRequestNotExist ( err ) {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "GetUnmergedPullRequest" , err )
2015-10-18 19:30:39 -04:00
return
}
}
2015-10-25 03:10:22 -04:00
// Regenerate patch and test conflict.
if pr == nil {
2021-07-28 17:42:56 +08:00
issue . PullRequest . HeadCommitID = ""
2019-12-07 10:44:10 +08:00
pull_service . AddToTaskQueue ( issue . PullRequest )
2015-10-25 03:10:22 -04:00
}
2015-10-18 19:30:39 -04:00
}
if pr != nil {
ctx . Flash . Info ( ctx . Tr ( "repo.pulls.open_unmerged_pull_exists" , pr . Index ) )
2015-09-13 11:26:25 -04:00
} else {
2018-10-18 19:23:05 +08:00
isClosed := form . Status == "close"
2022-03-22 08:03:22 +01:00
if err := issue_service . ChangeStatus ( issue , ctx . Doer , isClosed ) ; err != nil {
2019-04-02 08:48:31 +01:00
log . Error ( "ChangeStatus: %v" , err )
2018-07-17 23:23:58 +02:00
if models . IsErrDependenciesLeft ( err ) {
if issue . IsPull {
ctx . Flash . Error ( ctx . Tr ( "repo.issues.dependency.pr_close_blocked" ) )
2022-03-23 05:54:07 +01:00
ctx . Redirect ( fmt . Sprintf ( "%s/pulls/%d" , ctx . Repo . RepoLink , issue . Index ) )
2018-07-17 23:23:58 +02:00
} else {
ctx . Flash . Error ( ctx . Tr ( "repo.issues.dependency.issue_close_blocked" ) )
2022-03-23 05:54:07 +01:00
ctx . Redirect ( fmt . Sprintf ( "%s/issues/%d" , ctx . Repo . RepoLink , issue . Index ) )
2018-07-17 23:23:58 +02:00
}
return
}
2015-10-18 19:30:39 -04:00
} else {
2022-03-22 08:03:22 +01:00
if err := stopTimerIfAvailable ( ctx . Doer , issue ) ; err != nil {
2019-02-05 12:38:11 +01:00
ctx . ServerError ( "CreateOrStopIssueStopwatch" , err )
return
}
2016-02-22 12:40:00 -05:00
log . Trace ( "Issue [%d] status changed to closed: %v" , issue . ID , issue . IsClosed )
2015-10-18 19:30:39 -04:00
}
2015-09-13 11:26:25 -04:00
}
}
2015-10-18 19:30:39 -04:00
// Redirect to comment hashtag if there is any actual content.
typeName := "issues"
if issue . IsPull {
typeName = "pulls"
}
if comment != nil {
ctx . Redirect ( fmt . Sprintf ( "%s/%s/%d#%s" , ctx . Repo . RepoLink , typeName , issue . Index , comment . HashTag ( ) ) )
} else {
ctx . Redirect ( fmt . Sprintf ( "%s/%s/%d" , ctx . Repo . RepoLink , typeName , issue . Index ) )
}
2015-09-13 11:26:25 -04:00
} ( )
2015-08-13 16:07:11 +08:00
// Fix #321: Allow empty comments, as long as we have attachments.
if len ( form . Content ) == 0 && len ( attachments ) == 0 {
return
2014-07-26 02:28:04 -04:00
}
2022-03-22 08:03:22 +01:00
comment , err := comment_service . CreateIssueComment ( ctx . Doer , ctx . Repo . Repository , issue , form . Content , attachments )
2015-08-13 16:07:11 +08:00
if err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "CreateIssueComment" , err )
2014-07-26 02:28:04 -04:00
return
}
2015-08-13 16:07:11 +08:00
log . Trace ( "Comment created: %d/%d/%d" , ctx . Repo . Repository . ID , issue . ID , comment . ID )
2014-07-26 02:28:04 -04:00
}
2016-11-24 15:04:31 +08:00
// UpdateCommentContent change comment of issue's content
2016-03-11 11:56:52 -05:00
func UpdateCommentContent ( ctx * context . Context ) {
2015-08-20 04:31:28 +08:00
comment , err := models . GetCommentByID ( ctx . ParamsInt64 ( ":id" ) )
if err != nil {
2016-08-30 02:08:38 -07:00
ctx . NotFoundOrServerError ( "GetCommentByID" , models . IsErrCommentNotExist , err )
2015-08-20 04:31:28 +08:00
return
}
2018-11-28 19:26:14 +08:00
if err := comment . LoadIssue ( ) ; err != nil {
ctx . NotFoundOrServerError ( "LoadIssue" , models . IsErrIssueNotExist , err )
return
}
2022-03-22 08:03:22 +01:00
if ! ctx . IsSigned || ( ctx . Doer . ID != comment . PosterID && ! ctx . Repo . CanWriteIssuesOrPulls ( comment . Issue . IsPull ) ) {
2021-04-05 17:30:52 +02:00
ctx . Error ( http . StatusForbidden )
2015-08-20 04:31:28 +08:00
return
2022-01-19 01:28:38 +08:00
}
if comment . Type != models . CommentTypeComment && comment . Type != models . CommentTypeReview && comment . Type != models . CommentTypeCode {
2021-04-05 17:30:52 +02:00
ctx . Error ( http . StatusNoContent )
2015-08-20 04:31:28 +08:00
return
}
2018-05-16 22:01:55 +08:00
oldContent := comment . Content
2021-08-11 02:31:13 +02:00
comment . Content = ctx . FormString ( "content" )
2015-08-20 04:31:28 +08:00
if len ( comment . Content ) == 0 {
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusOK , map [ string ] interface { } {
2015-08-20 04:31:28 +08:00
"content" : "" ,
} )
return
}
2022-03-22 08:03:22 +01:00
if err = comment_service . UpdateComment ( comment , ctx . Doer , oldContent ) ; err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "UpdateComment" , err )
2015-08-20 04:31:28 +08:00
return
}
2022-01-19 01:28:38 +08:00
if err := comment . LoadAttachments ( ) ; err != nil {
ctx . ServerError ( "LoadAttachments" , err )
return
2021-08-20 21:26:19 +02:00
}
2021-08-21 21:04:47 +08:00
// when the update request doesn't intend to update attachments (eg: change checkbox state), ignore attachment updates
if ! ctx . FormBool ( "ignore_attachments" ) {
if err := updateAttachments ( comment , ctx . FormStrings ( "files[]" ) ) ; err != nil {
ctx . ServerError ( "UpdateAttachments" , err )
return
}
2021-04-20 06:25:08 +08:00
}
content , err := markdown . RenderString ( & markup . RenderContext {
2021-11-16 18:18:25 +00:00
URLPrefix : ctx . FormString ( "context" ) , // FIXME: <- IS THIS SAFE ?
2021-04-20 06:25:08 +08:00
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
} , comment . Content )
if err != nil {
ctx . ServerError ( "RenderString" , err )
return
2019-10-15 20:19:32 +08:00
}
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusOK , map [ string ] interface { } {
2021-04-20 06:25:08 +08:00
"content" : content ,
2020-12-13 19:12:27 +00:00
"attachments" : attachmentsHTML ( ctx , comment . Attachments , comment . Content ) ,
2015-08-20 04:31:28 +08:00
} )
}
2016-11-24 15:04:31 +08:00
// DeleteComment delete comment of issue
2016-07-26 02:48:17 +08:00
func DeleteComment ( ctx * context . Context ) {
comment , err := models . GetCommentByID ( ctx . ParamsInt64 ( ":id" ) )
if err != nil {
2016-08-30 02:08:38 -07:00
ctx . NotFoundOrServerError ( "GetCommentByID" , models . IsErrCommentNotExist , err )
2016-07-26 02:48:17 +08:00
return
}
2018-11-28 19:26:14 +08:00
if err := comment . LoadIssue ( ) ; err != nil {
ctx . NotFoundOrServerError ( "LoadIssue" , models . IsErrIssueNotExist , err )
return
}
2022-03-22 08:03:22 +01:00
if ! ctx . IsSigned || ( ctx . Doer . ID != comment . PosterID && ! ctx . Repo . CanWriteIssuesOrPulls ( comment . Issue . IsPull ) ) {
2021-04-05 17:30:52 +02:00
ctx . Error ( http . StatusForbidden )
2016-07-26 02:48:17 +08:00
return
2018-08-06 07:43:22 +03:00
} else if comment . Type != models . CommentTypeComment && comment . Type != models . CommentTypeCode {
2021-04-05 17:30:52 +02:00
ctx . Error ( http . StatusNoContent )
2016-07-26 02:48:17 +08:00
return
}
2022-03-22 08:03:22 +01:00
if err = comment_service . DeleteComment ( ctx . Doer , comment ) ; err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "DeleteCommentByID" , err )
2016-07-26 02:48:17 +08:00
return
}
2022-03-23 05:54:07 +01:00
ctx . Status ( http . StatusOK )
2016-07-26 02:48:17 +08:00
}
2017-12-04 01:14:26 +02:00
// ChangeIssueReaction create a reaction for issue
2021-01-26 23:36:53 +08:00
func ChangeIssueReaction ( ctx * context . Context ) {
2021-04-06 20:44:05 +01:00
form := web . GetForm ( ctx ) . ( * forms . ReactionForm )
2017-12-04 01:14:26 +02:00
issue := GetActionIssue ( ctx )
if ctx . Written ( ) {
return
}
2022-03-22 08:03:22 +01:00
if ! ctx . IsSigned || ( ctx . Doer . ID != issue . PosterID && ! ctx . Repo . CanReadIssuesOrPulls ( issue . IsPull ) ) {
2019-04-22 21:40:51 +01:00
if log . IsTrace ( ) {
if ctx . IsSigned {
issueType := "issues"
if issue . IsPull {
issueType = "pulls"
}
log . Trace ( "Permission Denied: User %-v not the Poster (ID: %d) and cannot read %s in Repo %-v.\n" +
"User in Repo has Permissions: %-+v" ,
2022-03-22 08:03:22 +01:00
ctx . Doer ,
2019-04-22 21:40:51 +01:00
log . NewColoredIDValue ( issue . PosterID ) ,
issueType ,
ctx . Repo . Repository ,
ctx . Repo . Permission )
} else {
log . Trace ( "Permission Denied: Not logged in" )
}
}
2021-04-05 17:30:52 +02:00
ctx . Error ( http . StatusForbidden )
2018-11-28 19:26:14 +08:00
return
}
2017-12-04 01:14:26 +02:00
if ctx . HasError ( ) {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "ChangeIssueReaction" , errors . New ( ctx . GetErrMsg ( ) ) )
2017-12-04 01:14:26 +02:00
return
}
switch ctx . Params ( ":action" ) {
case "react" :
2022-03-31 17:20:39 +08:00
reaction , err := issues_model . CreateIssueReaction ( ctx . Doer . ID , issue . ID , form . Content )
2017-12-04 01:14:26 +02:00
if err != nil {
2022-03-31 17:20:39 +08:00
if issues_model . IsErrForbiddenIssueReaction ( err ) {
2019-12-07 23:04:19 +01:00
ctx . ServerError ( "ChangeIssueReaction" , err )
return
}
2017-12-04 01:14:26 +02:00
log . Info ( "CreateIssueReaction: %s" , err )
break
}
// Reload new reactions
issue . Reactions = nil
if err = issue . LoadAttributes ( ) ; err != nil {
log . Info ( "issue.LoadAttributes: %s" , err )
break
}
log . Trace ( "Reaction for issue created: %d/%d/%d" , ctx . Repo . Repository . ID , issue . ID , reaction . ID )
case "unreact" :
2022-03-31 17:20:39 +08:00
if err := issues_model . DeleteIssueReaction ( ctx . Doer . ID , issue . ID , form . Content ) ; err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "DeleteIssueReaction" , err )
2017-12-04 01:14:26 +02:00
return
}
// Reload new reactions
issue . Reactions = nil
if err := issue . LoadAttributes ( ) ; err != nil {
log . Info ( "issue.LoadAttributes: %s" , err )
break
}
log . Trace ( "Reaction for issue removed: %d/%d" , ctx . Repo . Repository . ID , issue . ID )
default :
2018-01-10 22:34:17 +01:00
ctx . NotFound ( fmt . Sprintf ( "Unknown action %s" , ctx . Params ( ":action" ) ) , nil )
2017-12-04 01:14:26 +02:00
return
}
if len ( issue . Reactions ) == 0 {
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusOK , map [ string ] interface { } {
2017-12-04 01:14:26 +02:00
"empty" : true ,
"html" : "" ,
} )
return
}
2021-12-15 14:59:57 +08:00
html , err := ctx . RenderToString ( tplReactions , map [ string ] interface { } {
2017-12-04 01:14:26 +02:00
"ctx" : ctx . Data ,
"ActionURL" : fmt . Sprintf ( "%s/issues/%d/reactions" , ctx . Repo . RepoLink , issue . Index ) ,
"Reactions" : issue . Reactions . GroupByType ( ) ,
} )
if err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "ChangeIssueReaction.HTMLString" , err )
2017-12-04 01:14:26 +02:00
return
}
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusOK , map [ string ] interface { } {
2017-12-04 01:14:26 +02:00
"html" : html ,
} )
}
// ChangeCommentReaction create a reaction for comment
2021-01-26 23:36:53 +08:00
func ChangeCommentReaction ( ctx * context . Context ) {
2021-04-06 20:44:05 +01:00
form := web . GetForm ( ctx ) . ( * forms . ReactionForm )
2017-12-04 01:14:26 +02:00
comment , err := models . GetCommentByID ( ctx . ParamsInt64 ( ":id" ) )
if err != nil {
ctx . NotFoundOrServerError ( "GetCommentByID" , models . IsErrCommentNotExist , err )
return
}
2018-11-28 19:26:14 +08:00
if err := comment . LoadIssue ( ) ; err != nil {
ctx . NotFoundOrServerError ( "LoadIssue" , models . IsErrIssueNotExist , err )
2017-12-04 01:14:26 +02:00
return
}
2022-03-22 08:03:22 +01:00
if ! ctx . IsSigned || ( ctx . Doer . ID != comment . PosterID && ! ctx . Repo . CanReadIssuesOrPulls ( comment . Issue . IsPull ) ) {
2019-04-22 21:40:51 +01:00
if log . IsTrace ( ) {
if ctx . IsSigned {
issueType := "issues"
if comment . Issue . IsPull {
issueType = "pulls"
}
log . Trace ( "Permission Denied: User %-v not the Poster (ID: %d) and cannot read %s in Repo %-v.\n" +
"User in Repo has Permissions: %-+v" ,
2022-03-22 08:03:22 +01:00
ctx . Doer ,
2019-04-22 21:40:51 +01:00
log . NewColoredIDValue ( comment . Issue . PosterID ) ,
issueType ,
ctx . Repo . Repository ,
ctx . Repo . Permission )
} else {
log . Trace ( "Permission Denied: Not logged in" )
}
}
2021-04-05 17:30:52 +02:00
ctx . Error ( http . StatusForbidden )
2018-11-28 19:26:14 +08:00
return
2022-01-19 01:28:38 +08:00
}
if comment . Type != models . CommentTypeComment && comment . Type != models . CommentTypeCode && comment . Type != models . CommentTypeReview {
2021-04-05 17:30:52 +02:00
ctx . Error ( http . StatusNoContent )
2017-12-04 01:14:26 +02:00
return
}
switch ctx . Params ( ":action" ) {
case "react" :
2022-03-31 17:20:39 +08:00
reaction , err := issues_model . CreateCommentReaction ( ctx . Doer . ID , comment . Issue . ID , comment . ID , form . Content )
2017-12-04 01:14:26 +02:00
if err != nil {
2022-03-31 17:20:39 +08:00
if issues_model . IsErrForbiddenIssueReaction ( err ) {
2019-12-07 23:04:19 +01:00
ctx . ServerError ( "ChangeIssueReaction" , err )
return
}
2017-12-04 01:14:26 +02:00
log . Info ( "CreateCommentReaction: %s" , err )
break
}
// Reload new reactions
comment . Reactions = nil
2020-01-15 19:14:07 +08:00
if err = comment . LoadReactions ( ctx . Repo . Repository ) ; err != nil {
2017-12-04 01:14:26 +02:00
log . Info ( "comment.LoadReactions: %s" , err )
break
}
2018-11-28 19:26:14 +08:00
log . Trace ( "Reaction for comment created: %d/%d/%d/%d" , ctx . Repo . Repository . ID , comment . Issue . ID , comment . ID , reaction . ID )
2017-12-04 01:14:26 +02:00
case "unreact" :
2022-03-31 17:20:39 +08:00
if err := issues_model . DeleteCommentReaction ( ctx . Doer . ID , comment . Issue . ID , comment . ID , form . Content ) ; err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "DeleteCommentReaction" , err )
2017-12-04 01:14:26 +02:00
return
}
// Reload new reactions
comment . Reactions = nil
2020-01-15 19:14:07 +08:00
if err = comment . LoadReactions ( ctx . Repo . Repository ) ; err != nil {
2017-12-04 01:14:26 +02:00
log . Info ( "comment.LoadReactions: %s" , err )
break
}
2018-11-28 19:26:14 +08:00
log . Trace ( "Reaction for comment removed: %d/%d/%d" , ctx . Repo . Repository . ID , comment . Issue . ID , comment . ID )
2017-12-04 01:14:26 +02:00
default :
2018-01-10 22:34:17 +01:00
ctx . NotFound ( fmt . Sprintf ( "Unknown action %s" , ctx . Params ( ":action" ) ) , nil )
2017-12-04 01:14:26 +02:00
return
}
if len ( comment . Reactions ) == 0 {
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusOK , map [ string ] interface { } {
2017-12-04 01:14:26 +02:00
"empty" : true ,
"html" : "" ,
} )
return
}
2021-12-15 14:59:57 +08:00
html , err := ctx . RenderToString ( tplReactions , map [ string ] interface { } {
2017-12-04 01:14:26 +02:00
"ctx" : ctx . Data ,
"ActionURL" : fmt . Sprintf ( "%s/comments/%d/reactions" , ctx . Repo . RepoLink , comment . ID ) ,
"Reactions" : comment . Reactions . GroupByType ( ) ,
} )
if err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "ChangeCommentReaction.HTMLString" , err )
2017-12-04 01:14:26 +02:00
return
}
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusOK , map [ string ] interface { } {
2017-12-04 01:14:26 +02:00
"html" : html ,
} )
}
2019-09-07 11:53:35 -03:00
2021-11-24 17:49:20 +08:00
func addParticipant ( poster * user_model . User , participants [ ] * user_model . User ) [ ] * user_model . User {
2019-09-07 11:53:35 -03:00
for _ , part := range participants {
if poster . ID == part . ID {
return participants
}
}
return append ( participants , poster )
}
2019-09-20 02:45:38 -03:00
func filterXRefComments ( ctx * context . Context , issue * models . Issue ) error {
// Remove comments that the user has no permissions to see
for i := 0 ; i < len ( issue . Comments ) ; {
c := issue . Comments [ i ]
if models . CommentTypeIsRef ( c . Type ) && c . RefRepoID != issue . RepoID && c . RefRepoID != 0 {
var err error
// Set RefRepo for description in template
2021-12-10 09:27:50 +08:00
c . RefRepo , err = repo_model . GetRepositoryByID ( c . RefRepoID )
2019-09-20 02:45:38 -03:00
if err != nil {
return err
}
2022-03-22 08:03:22 +01:00
perm , err := models . GetUserRepoPermission ( c . RefRepo , ctx . Doer )
2019-09-20 02:45:38 -03:00
if err != nil {
return err
}
if ! perm . CanReadIssuesOrPulls ( c . RefIsPull ) {
issue . Comments = append ( issue . Comments [ : i ] , issue . Comments [ i + 1 : ] ... )
continue
}
}
i ++
}
return nil
}
2019-10-15 20:19:32 +08:00
// GetIssueAttachments returns attachments for the issue
func GetIssueAttachments ( ctx * context . Context ) {
issue := GetActionIssue ( ctx )
2022-01-20 18:46:10 +01:00
attachments := make ( [ ] * api . Attachment , len ( issue . Attachments ) )
2019-10-15 20:19:32 +08:00
for i := 0 ; i < len ( issue . Attachments ) ; i ++ {
2020-10-17 06:23:08 +02:00
attachments [ i ] = convert . ToReleaseAttachment ( issue . Attachments [ i ] )
2019-10-15 20:19:32 +08:00
}
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusOK , attachments )
2019-10-15 20:19:32 +08:00
}
// GetCommentAttachments returns attachments for the comment
func GetCommentAttachments ( ctx * context . Context ) {
comment , err := models . GetCommentByID ( ctx . ParamsInt64 ( ":id" ) )
if err != nil {
ctx . NotFoundOrServerError ( "GetCommentByID" , models . IsErrCommentNotExist , err )
return
}
2022-01-20 18:46:10 +01:00
attachments := make ( [ ] * api . Attachment , 0 )
2019-10-15 20:19:32 +08:00
if comment . Type == models . CommentTypeComment {
if err := comment . LoadAttachments ( ) ; err != nil {
ctx . ServerError ( "LoadAttachments" , err )
return
}
for i := 0 ; i < len ( comment . Attachments ) ; i ++ {
2020-10-17 06:23:08 +02:00
attachments = append ( attachments , convert . ToReleaseAttachment ( comment . Attachments [ i ] ) )
2019-10-15 20:19:32 +08:00
}
}
2021-04-05 17:30:52 +02:00
ctx . JSON ( http . StatusOK , attachments )
2019-10-15 20:19:32 +08:00
}
func updateAttachments ( item interface { } , files [ ] string ) error {
2021-11-19 21:39:57 +08:00
var attachments [ ] * repo_model . Attachment
2019-10-15 20:19:32 +08:00
switch content := item . ( type ) {
case * models . Issue :
attachments = content . Attachments
case * models . Comment :
attachments = content . Attachments
default :
2022-02-26 12:15:32 +00:00
return fmt . Errorf ( "unknown Type: %T" , content )
2019-10-15 20:19:32 +08:00
}
for i := 0 ; i < len ( attachments ) ; i ++ {
if util . IsStringInSlice ( attachments [ i ] . UUID , files ) {
continue
}
2021-11-19 21:39:57 +08:00
if err := repo_model . DeleteAttachment ( attachments [ i ] , true ) ; err != nil {
2019-10-15 20:19:32 +08:00
return err
}
}
var err error
if len ( files ) > 0 {
switch content := item . ( type ) {
case * models . Issue :
2022-03-29 22:57:33 +08:00
err = models . UpdateIssueAttachments ( content . ID , files )
2019-10-15 20:19:32 +08:00
case * models . Comment :
err = content . UpdateAttachments ( files )
default :
2022-02-26 12:15:32 +00:00
return fmt . Errorf ( "unknown Type: %T" , content )
2019-10-15 20:19:32 +08:00
}
if err != nil {
return err
}
}
switch content := item . ( type ) {
case * models . Issue :
2021-11-19 21:39:57 +08:00
content . Attachments , err = repo_model . GetAttachmentsByIssueID ( content . ID )
2019-10-15 20:19:32 +08:00
case * models . Comment :
2021-11-19 21:39:57 +08:00
content . Attachments , err = repo_model . GetAttachmentsByCommentID ( content . ID )
2019-10-15 20:19:32 +08:00
default :
2022-02-26 12:15:32 +00:00
return fmt . Errorf ( "unknown Type: %T" , content )
2019-10-15 20:19:32 +08:00
}
return err
}
2021-11-19 21:39:57 +08:00
func attachmentsHTML ( ctx * context . Context , attachments [ ] * repo_model . Attachment , content string ) string {
2021-12-15 14:59:57 +08:00
attachHTML , err := ctx . RenderToString ( tplAttachment , map [ string ] interface { } {
2019-10-15 20:19:32 +08:00
"ctx" : ctx . Data ,
"Attachments" : attachments ,
2020-12-13 19:12:27 +00:00
"Content" : content ,
2019-10-15 20:19:32 +08:00
} )
if err != nil {
ctx . ServerError ( "attachmentsHTML.HTMLString" , err )
return ""
}
return attachHTML
}
2020-10-25 21:49:48 +00:00
2021-03-05 23:17:32 +08:00
// combineLabelComments combine the nearby label comments as one.
2020-10-25 21:49:48 +00:00
func combineLabelComments ( issue * models . Issue ) {
2021-03-05 23:17:32 +08:00
var prev , cur * models . Comment
2020-11-21 06:29:09 +08:00
for i := 0 ; i < len ( issue . Comments ) ; i ++ {
2021-03-05 23:17:32 +08:00
cur = issue . Comments [ i ]
2020-11-21 06:29:09 +08:00
if i > 0 {
2020-10-25 21:49:48 +00:00
prev = issue . Comments [ i - 1 ]
}
2020-11-21 06:29:09 +08:00
if i == 0 || cur . Type != models . CommentTypeLabel ||
( prev != nil && prev . PosterID != cur . PosterID ) ||
( prev != nil && cur . CreatedUnix - prev . CreatedUnix >= 60 ) {
2021-02-10 02:50:44 +00:00
if cur . Type == models . CommentTypeLabel && cur . Label != nil {
2020-11-21 06:29:09 +08:00
if cur . Content != "1" {
cur . RemovedLabels = append ( cur . RemovedLabels , cur . Label )
2020-10-25 21:49:48 +00:00
} else {
2020-11-21 06:29:09 +08:00
cur . AddedLabels = append ( cur . AddedLabels , cur . Label )
2020-10-25 21:49:48 +00:00
}
}
2020-11-21 06:29:09 +08:00
continue
2020-10-25 21:49:48 +00:00
}
2020-11-21 06:29:09 +08:00
2021-03-05 23:17:32 +08:00
if cur . Label != nil { // now cur MUST be label comment
if prev . Type == models . CommentTypeLabel { // we can combine them only prev is a label comment
if cur . Content != "1" {
2021-11-04 15:51:30 +01:00
// remove labels from the AddedLabels list if the label that was removed is already
// in this list, and if it's not in this list, add the label to RemovedLabels
addedAndRemoved := false
for i , label := range prev . AddedLabels {
if cur . Label . ID == label . ID {
prev . AddedLabels = append ( prev . AddedLabels [ : i ] , prev . AddedLabels [ i + 1 : ] ... )
addedAndRemoved = true
break
}
}
if ! addedAndRemoved {
prev . RemovedLabels = append ( prev . RemovedLabels , cur . Label )
}
2021-03-05 23:17:32 +08:00
} else {
2021-11-04 15:51:30 +01:00
// remove labels from the RemovedLabels list if the label that was added is already
// in this list, and if it's not in this list, add the label to AddedLabels
removedAndAdded := false
for i , label := range prev . RemovedLabels {
if cur . Label . ID == label . ID {
prev . RemovedLabels = append ( prev . RemovedLabels [ : i ] , prev . RemovedLabels [ i + 1 : ] ... )
removedAndAdded = true
break
}
}
if ! removedAndAdded {
prev . AddedLabels = append ( prev . AddedLabels , cur . Label )
}
2021-03-05 23:17:32 +08:00
}
prev . CreatedUnix = cur . CreatedUnix
// remove the current comment since it has been combined to prev comment
issue . Comments = append ( issue . Comments [ : i ] , issue . Comments [ i + 1 : ] ... )
i --
} else { // if prev is not a label comment, start a new group
if cur . Content != "1" {
cur . RemovedLabels = append ( cur . RemovedLabels , cur . Label )
} else {
cur . AddedLabels = append ( cur . AddedLabels , cur . Label )
}
2021-02-10 02:50:44 +00:00
}
2020-11-21 06:29:09 +08:00
}
2020-10-25 21:49:48 +00:00
}
}
2020-12-21 23:39:28 +08:00
// get all teams that current user can mention
func handleTeamMentions ( ctx * context . Context ) {
2022-03-22 08:03:22 +01:00
if ctx . Doer == nil || ! ctx . Repo . Owner . IsOrganization ( ) {
2020-12-21 23:39:28 +08:00
return
}
2021-11-19 19:41:40 +08:00
var isAdmin bool
2020-12-21 23:39:28 +08:00
var err error
2022-03-29 14:29:02 +08:00
var teams [ ] * organization . Team
org := organization . OrgFromUser ( ctx . Repo . Owner )
2020-12-21 23:39:28 +08:00
// Admin has super access.
2022-03-22 08:03:22 +01:00
if ctx . Doer . IsAdmin {
2020-12-21 23:39:28 +08:00
isAdmin = true
} else {
2022-03-22 08:03:22 +01:00
isAdmin , err = org . IsOwnedBy ( ctx . Doer . ID )
2020-12-21 23:39:28 +08:00
if err != nil {
ctx . ServerError ( "IsOwnedBy" , err )
return
}
}
if isAdmin {
2021-11-19 19:41:40 +08:00
teams , err = org . LoadTeams ( )
if err != nil {
2021-08-12 14:43:08 +02:00
ctx . ServerError ( "LoadTeams" , err )
2020-12-21 23:39:28 +08:00
return
}
} else {
2022-03-22 08:03:22 +01:00
teams , err = org . GetUserTeams ( ctx . Doer . ID )
2020-12-21 23:39:28 +08:00
if err != nil {
ctx . ServerError ( "GetUserTeams" , err )
return
}
}
2021-11-19 19:41:40 +08:00
ctx . Data [ "MentionableTeams" ] = teams
2020-12-21 23:39:28 +08:00
ctx . Data [ "MentionableTeamsOrg" ] = ctx . Repo . Owner . Name
2021-10-06 07:25:46 +08:00
ctx . Data [ "MentionableTeamsOrgAvatar" ] = ctx . Repo . Owner . AvatarLink ( )
2020-12-21 23:39:28 +08:00
}