summaryrefslogtreecommitdiffstats
path: root/routers
diff options
context:
space:
mode:
authorJohn Olheiser <john.olheiser@gmail.com>2020-09-11 09:48:39 -0500
committerGitHub <noreply@github.com>2020-09-11 10:48:39 -0400
commit26c4a049da178993e5ccddcb50e7edc70a6bde5d (patch)
tree494106117720ff3ad5f9e77a380c9397c3cfe10b /routers
parentdd1a651b5895cfdb8a141a56aa824ed4d082c41a (diff)
downloadgitea-26c4a049da178993e5ccddcb50e7edc70a6bde5d.tar.gz
gitea-26c4a049da178993e5ccddcb50e7edc70a6bde5d.zip
Issue templates directory (#11450)
* Issue templates Signed-off-by: jolheiser <john.olheiser@gmail.com> * Add some comments, appease the linter Signed-off-by: jolheiser <john.olheiser@gmail.com> * Add docs and re-use dir candidates Signed-off-by: jolheiser <john.olheiser@gmail.com> * Add default labels to issue templates Signed-off-by: jolheiser <john.olheiser@gmail.com> * Generate swagger Signed-off-by: jolheiser <john.olheiser@gmail.com> * Suggested changes Signed-off-by: jolheiser <john.olheiser@gmail.com> * Update issue.go * Suggestions Signed-off-by: jolheiser <john.olheiser@gmail.com> * Extract metadata from legacy if possible Signed-off-by: jolheiser <john.olheiser@gmail.com> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Diffstat (limited to 'routers')
-rw-r--r--routers/api/v1/api.go1
-rw-r--r--routers/api/v1/repo/repo.go25
-rw-r--r--routers/api/v1/swagger/issue.go7
-rw-r--r--routers/repo/compare.go2
-rw-r--r--routers/repo/issue.go70
-rw-r--r--routers/repo/milestone.go1
-rw-r--r--routers/routes/routes.go7
7 files changed, 101 insertions, 12 deletions
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index 9e85625770..8b3a7545c6 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -866,6 +866,7 @@ func RegisterRoutes(m *macaron.Macaron) {
Delete(reqToken(), repo.DeleteTopic)
}, reqAdmin())
}, reqAnyRepoReader())
+ m.Get("/issue_templates", context.ReferencesGitRepo(false), repo.GetIssueTemplates)
m.Get("/languages", reqRepoReader(models.UnitTypeCode), repo.GetLanguages)
}, repoAssignment())
})
diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go
index 5ebc7f251b..35062500f7 100644
--- a/routers/api/v1/repo/repo.go
+++ b/routers/api/v1/repo/repo.go
@@ -812,3 +812,28 @@ func Delete(ctx *context.APIContext) {
log.Trace("Repository deleted: %s/%s", owner.Name, repo.Name)
ctx.Status(http.StatusNoContent)
}
+
+// GetIssueTemplates returns the issue templates for a repository
+func GetIssueTemplates(ctx *context.APIContext) {
+ // swagger:operation GET /repos/{owner}/{repo}/issue_templates repository repoGetIssueTemplates
+ // ---
+ // summary: Get available issue templates for a repository
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // responses:
+ // "200":
+ // "$ref": "#/responses/IssueTemplates"
+
+ ctx.JSON(http.StatusOK, ctx.IssueTemplatesFromDefaultBranch())
+}
diff --git a/routers/api/v1/swagger/issue.go b/routers/api/v1/swagger/issue.go
index b12ea0096a..0f2f572020 100644
--- a/routers/api/v1/swagger/issue.go
+++ b/routers/api/v1/swagger/issue.go
@@ -85,6 +85,13 @@ type swaggerIssueDeadline struct {
Body api.IssueDeadline `json:"body"`
}
+// IssueTemplates
+// swagger:response IssueTemplates
+type swaggerIssueTemplates struct {
+ // in:body
+ Body []api.IssueTemplate `json:"body"`
+}
+
// StopWatch
// swagger:response StopWatch
type swaggerResponseStopWatch struct {
diff --git a/routers/repo/compare.go b/routers/repo/compare.go
index f8a18f0696..9329b5a1d2 100644
--- a/routers/repo/compare.go
+++ b/routers/repo/compare.go
@@ -577,7 +577,7 @@ func CompareDiff(ctx *context.Context) {
ctx.Data["RequireTribute"] = true
ctx.Data["RequireSimpleMDE"] = true
ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes
- setTemplateIfExists(ctx, pullRequestTemplateKey, pullRequestTemplateCandidates)
+ setTemplateIfExists(ctx, pullRequestTemplateKey, nil, pullRequestTemplateCandidates)
renderAttachmentSettings(ctx)
ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.CanWrite(models.UnitTypePullRequests)
diff --git a/routers/repo/issue.go b/routers/repo/issue.go
index 71c0488972..7c4f2cea9b 100644
--- a/routers/repo/issue.go
+++ b/routers/repo/issue.go
@@ -11,6 +11,7 @@ import (
"fmt"
"io/ioutil"
"net/http"
+ "path"
"strconv"
"strings"
@@ -36,13 +37,15 @@ import (
const (
tplAttachment base.TplName = "repo/issue/view_content/attachments"
- tplIssues base.TplName = "repo/issue/list"
- tplIssueNew base.TplName = "repo/issue/new"
- tplIssueView base.TplName = "repo/issue/view"
+ tplIssues base.TplName = "repo/issue/list"
+ tplIssueNew base.TplName = "repo/issue/new"
+ tplIssueChoose base.TplName = "repo/issue/choose"
+ tplIssueView base.TplName = "repo/issue/view"
tplReactions base.TplName = "repo/issue/view_content/reactions"
- issueTemplateKey = "IssueTemplate"
+ issueTemplateKey = "IssueTemplate"
+ issueTemplateTitleKey = "IssueTemplateTitle"
)
var (
@@ -356,6 +359,7 @@ func Issues(ctx *context.Context) {
}
ctx.Data["Title"] = ctx.Tr("repo.issues")
ctx.Data["PageIsIssueList"] = true
+ ctx.Data["NewIssueChooseTemplate"] = len(ctx.IssueTemplatesFromDefaultBranch()) > 0
}
issues(ctx, ctx.QueryInt64("milestone"), ctx.QueryInt64("project"), util.OptionalBoolOf(isPullList))
@@ -515,11 +519,41 @@ func getFileContentFromDefaultBranch(ctx *context.Context, filename string) (str
return string(bytes), true
}
-func setTemplateIfExists(ctx *context.Context, ctxDataKey string, possibleFiles []string) {
- for _, filename := range possibleFiles {
- content, found := getFileContentFromDefaultBranch(ctx, filename)
+func setTemplateIfExists(ctx *context.Context, ctxDataKey string, possibleDirs []string, possibleFiles []string) {
+ templateCandidates := make([]string, 0, len(possibleFiles))
+ if ctx.Query("template") != "" {
+ for _, dirName := range possibleDirs {
+ templateCandidates = append(templateCandidates, path.Join(dirName, ctx.Query("template")))
+ }
+ }
+ templateCandidates = append(templateCandidates, possibleFiles...) // Append files to the end because they should be fallback
+ for _, filename := range templateCandidates {
+ templateContent, found := getFileContentFromDefaultBranch(ctx, filename)
if found {
- ctx.Data[ctxDataKey] = content
+ 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))
+ if repoLabels, err := models.GetLabelsByRepoID(ctx.Repo.Repository.ID, "", models.ListOptions{}); err == nil {
+ for _, metaLabel := range meta.Labels {
+ for _, repoLabel := range repoLabels {
+ if strings.EqualFold(repoLabel.Name, metaLabel) {
+ repoLabel.IsChecked = true
+ labelIDs = append(labelIDs, fmt.Sprintf("%d", repoLabel.ID))
+ break
+ }
+ }
+ }
+ ctx.Data["Labels"] = repoLabels
+ }
+ ctx.Data["HasSelectedLabel"] = len(labelIDs) > 0
+ ctx.Data["label_ids"] = strings.Join(labelIDs, ",")
return
}
}
@@ -529,10 +563,13 @@ func setTemplateIfExists(ctx *context.Context, ctxDataKey string, possibleFiles
func NewIssue(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.issues.new")
ctx.Data["PageIsIssueList"] = true
+ ctx.Data["NewIssueChooseTemplate"] = len(ctx.IssueTemplatesFromDefaultBranch()) > 0
ctx.Data["RequireHighlightJS"] = true
ctx.Data["RequireSimpleMDE"] = true
ctx.Data["RequireTribute"] = true
ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes
+ title := ctx.Query("title")
+ ctx.Data["TitleQuery"] = title
body := ctx.Query("body")
ctx.Data["BodyQuery"] = body
ctx.Data["IsProjectsEnabled"] = ctx.Repo.CanRead(models.UnitTypeProjects)
@@ -562,10 +599,10 @@ func NewIssue(ctx *context.Context) {
}
- setTemplateIfExists(ctx, issueTemplateKey, IssueTemplateCandidates)
renderAttachmentSettings(ctx)
RetrieveRepoMetas(ctx, ctx.Repo.Repository, false)
+ setTemplateIfExists(ctx, issueTemplateKey, context.IssueTemplateDirCandidates, IssueTemplateCandidates)
if ctx.Written() {
return
}
@@ -575,6 +612,19 @@ func NewIssue(ctx *context.Context) {
ctx.HTML(200, tplIssueNew)
}
+// NewIssueChooseTemplate render creating issue from template page
+func NewIssueChooseTemplate(ctx *context.Context) {
+ ctx.Data["Title"] = ctx.Tr("repo.issues.new")
+ ctx.Data["PageIsIssueList"] = true
+ ctx.Data["milestone"] = ctx.QueryInt64("milestone")
+
+ issueTemplates := ctx.IssueTemplatesFromDefaultBranch()
+ ctx.Data["NewIssueChooseTemplate"] = len(issueTemplates) > 0
+ ctx.Data["IssueTemplates"] = issueTemplates
+
+ ctx.HTML(200, tplIssueChoose)
+}
+
// ValidateRepoMetas check and returns repository's meta informations
func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm, isPull bool) ([]int64, []int64, int64, int64) {
var (
@@ -676,6 +726,7 @@ func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm, isPull b
func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) {
ctx.Data["Title"] = ctx.Tr("repo.issues.new")
ctx.Data["PageIsIssueList"] = true
+ ctx.Data["NewIssueChooseTemplate"] = len(ctx.IssueTemplatesFromDefaultBranch()) > 0
ctx.Data["RequireHighlightJS"] = true
ctx.Data["RequireSimpleMDE"] = true
ctx.Data["ReadOnly"] = false
@@ -814,6 +865,7 @@ func ViewIssue(ctx *context.Context) {
return
}
ctx.Data["PageIsIssueList"] = true
+ ctx.Data["NewIssueChooseTemplate"] = len(ctx.IssueTemplatesFromDefaultBranch()) > 0
}
if issue.IsPull && !ctx.Repo.CanRead(models.UnitTypeIssues) {
diff --git a/routers/repo/milestone.go b/routers/repo/milestone.go
index f48c5de12e..96f5b4e5f0 100644
--- a/routers/repo/milestone.go
+++ b/routers/repo/milestone.go
@@ -264,6 +264,7 @@ func MilestoneIssuesAndPulls(ctx *context.Context) {
ctx.Data["Milestone"] = milestone
issues(ctx, milestoneID, 0, util.OptionalBoolNone)
+ ctx.Data["NewIssueChooseTemplate"] = len(ctx.IssueTemplatesFromDefaultBranch()) > 0
ctx.Data["CanWriteIssues"] = ctx.Repo.CanWriteIssuesOrPulls(false)
ctx.Data["CanWritePulls"] = ctx.Repo.CanWriteIssuesOrPulls(true)
diff --git a/routers/routes/routes.go b/routers/routes/routes.go
index 779e8614b3..247835c062 100644
--- a/routers/routes/routes.go
+++ b/routers/routes/routes.go
@@ -723,8 +723,11 @@ func RegisterRoutes(m *macaron.Macaron) {
// Grouping for those endpoints that do require authentication
m.Group("/:username/:reponame", func() {
m.Group("/issues", func() {
- m.Combo("/new").Get(context.RepoRef(), repo.NewIssue).
- Post(bindIgnErr(auth.CreateIssueForm{}), repo.NewIssuePost)
+ m.Group("/new", func() {
+ m.Combo("").Get(context.RepoRef(), repo.NewIssue).
+ Post(bindIgnErr(auth.CreateIssueForm{}), repo.NewIssuePost)
+ m.Get("/choose", context.RepoRef(), repo.NewIssueChooseTemplate)
+ })
}, context.RepoMustNotBeArchived(), reqRepoIssueReader)
// FIXME: should use different URLs but mostly same logic for comments of issue and pull reuqest.
// So they can apply their own enable/disable logic on routers.