Support issue template assignees (#31083)

Resolve #13955
pull/31683/head^2
Zettat123 3 months ago committed by GitHub
parent 63c5ac6cdb
commit 8883d99184
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 12
      modules/issue/template/template_test.go
  2. 23
      modules/structs/issue.go
  3. 4
      modules/structs/issue_test.go
  4. 11
      routers/web/repo/issue.go
  5. 8
      templates/repo/issue/new_form.tmpl
  6. 7
      templates/swagger/v1_json.tmpl

@ -466,6 +466,7 @@ name: Name
title: Title title: Title
about: About about: About
labels: ["label1", "label2"] labels: ["label1", "label2"]
assignees: ["user1", "user2"]
ref: Ref ref: Ref
body: body:
- type: markdown - type: markdown
@ -523,11 +524,12 @@ body:
visible: [form] visible: [form]
`, `,
want: &api.IssueTemplate{ want: &api.IssueTemplate{
Name: "Name", Name: "Name",
Title: "Title", Title: "Title",
About: "About", About: "About",
Labels: []string{"label1", "label2"}, Labels: []string{"label1", "label2"},
Ref: "Ref", Assignees: []string{"user1", "user2"},
Ref: "Ref",
Fields: []*api.IssueFormField{ Fields: []*api.IssueFormField{
{ {
Type: "markdown", Type: "markdown",

@ -177,19 +177,20 @@ const (
// IssueTemplate represents an issue template for a repository // IssueTemplate represents an issue template for a repository
// swagger:model // swagger:model
type IssueTemplate struct { type IssueTemplate struct {
Name string `json:"name" yaml:"name"` Name string `json:"name" yaml:"name"`
Title string `json:"title" yaml:"title"` Title string `json:"title" yaml:"title"`
About string `json:"about" yaml:"about"` // Using "description" in a template file is compatible About string `json:"about" yaml:"about"` // Using "description" in a template file is compatible
Labels IssueTemplateLabels `json:"labels" yaml:"labels"` Labels IssueTemplateStringSlice `json:"labels" yaml:"labels"`
Ref string `json:"ref" yaml:"ref"` Assignees IssueTemplateStringSlice `json:"assignees" yaml:"assignees"`
Content string `json:"content" yaml:"-"` Ref string `json:"ref" yaml:"ref"`
Fields []*IssueFormField `json:"body" yaml:"body"` Content string `json:"content" yaml:"-"`
FileName string `json:"file_name" yaml:"-"` Fields []*IssueFormField `json:"body" yaml:"body"`
FileName string `json:"file_name" yaml:"-"`
} }
type IssueTemplateLabels []string type IssueTemplateStringSlice []string
func (l *IssueTemplateLabels) UnmarshalYAML(value *yaml.Node) error { func (l *IssueTemplateStringSlice) UnmarshalYAML(value *yaml.Node) error {
var labels []string var labels []string
if value.IsZero() { if value.IsZero() {
*l = labels *l = labels
@ -217,7 +218,7 @@ func (l *IssueTemplateLabels) UnmarshalYAML(value *yaml.Node) error {
*l = labels *l = labels
return nil return nil
} }
return fmt.Errorf("line %d: cannot unmarshal %s into IssueTemplateLabels", value.Line, value.ShortTag()) return fmt.Errorf("line %d: cannot unmarshal %s into IssueTemplateStringSlice", value.Line, value.ShortTag())
} }
type IssueConfigContactLink struct { type IssueConfigContactLink struct {

@ -42,7 +42,7 @@ func TestIssueTemplate_Type(t *testing.T) {
} }
} }
func TestIssueTemplateLabels_UnmarshalYAML(t *testing.T) { func TestIssueTemplateStringSlice_UnmarshalYAML(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
content string content string
@ -88,7 +88,7 @@ labels:
b: bb b: bb
`, `,
tmpl: &IssueTemplate{}, tmpl: &IssueTemplate{},
wantErr: "line 3: cannot unmarshal !!map into IssueTemplateLabels", wantErr: "line 3: cannot unmarshal !!map into IssueTemplateStringSlice",
}, },
} }
for _, tt := range tests { for _, tt := range tests {

@ -939,12 +939,23 @@ func setTemplateIfExists(ctx *context.Context, ctxDataKey string, possibleFiles
} }
} }
} }
selectedAssigneeIDs := make([]int64, 0, len(template.Assignees))
selectedAssigneeIDStrings := make([]string, 0, len(template.Assignees))
if userIDs, err := user_model.GetUserIDsByNames(ctx, template.Assignees, false); err == nil {
for _, userID := range userIDs {
selectedAssigneeIDs = append(selectedAssigneeIDs, userID)
selectedAssigneeIDStrings = append(selectedAssigneeIDStrings, strconv.FormatInt(userID, 10))
}
}
if template.Ref != "" && !strings.HasPrefix(template.Ref, "refs/") { // Assume that the ref intended is always a branch - for tags users should use refs/tags/<ref> if template.Ref != "" && !strings.HasPrefix(template.Ref, "refs/") { // Assume that the ref intended is always a branch - for tags users should use refs/tags/<ref>
template.Ref = git.BranchPrefix + template.Ref template.Ref = git.BranchPrefix + template.Ref
} }
ctx.Data["HasSelectedLabel"] = len(labelIDs) > 0 ctx.Data["HasSelectedLabel"] = len(labelIDs) > 0
ctx.Data["label_ids"] = strings.Join(labelIDs, ",") ctx.Data["label_ids"] = strings.Join(labelIDs, ",")
ctx.Data["HasSelectedAssignee"] = len(selectedAssigneeIDs) > 0
ctx.Data["assignee_ids"] = strings.Join(selectedAssigneeIDStrings, ",")
ctx.Data["SelectedAssigneeIDs"] = selectedAssigneeIDs
ctx.Data["Reference"] = template.Ref ctx.Data["Reference"] = template.Ref
ctx.Data["RefEndName"] = git.RefName(template.Ref).ShortName() ctx.Data["RefEndName"] = git.RefName(template.Ref).ShortName()
return true, templateErrs return true, templateErrs

@ -155,8 +155,8 @@
</div> </div>
<div class="no-select item">{{ctx.Locale.Tr "repo.issues.new.clear_assignees"}}</div> <div class="no-select item">{{ctx.Locale.Tr "repo.issues.new.clear_assignees"}}</div>
{{range .Assignees}} {{range .Assignees}}
<a class="item muted" href="#" data-id="{{.ID}}" data-id-selector="#assignee_{{.ID}}"> <a class="{{if SliceUtils.Contains $.SelectedAssigneeIDs .ID}}checked{{end}} item muted" href="#" data-id="{{.ID}}" data-id-selector="#assignee_{{.ID}}">
<span class="octicon-check tw-invisible">{{svg "octicon-check"}}</span> <span class="octicon-check {{if not (SliceUtils.Contains $.SelectedAssigneeIDs .ID)}}tw-invisible{{end}}">{{svg "octicon-check"}}</span>
<span class="text"> <span class="text">
{{ctx.AvatarUtils.Avatar . 28 "tw-mr-2"}}{{template "repo/search_name" .}} {{ctx.AvatarUtils.Avatar . 28 "tw-mr-2"}}{{template "repo/search_name" .}}
</span> </span>
@ -165,12 +165,12 @@
</div> </div>
</div> </div>
<div class="ui assignees list"> <div class="ui assignees list">
<span class="no-select item {{if .HasSelectedLabel}}tw-hidden{{end}}"> <span class="no-select item {{if .HasSelectedAssignee}}tw-hidden{{end}}">
{{ctx.Locale.Tr "repo.issues.new.no_assignees"}} {{ctx.Locale.Tr "repo.issues.new.no_assignees"}}
</span> </span>
<div class="selected"> <div class="selected">
{{range .Assignees}} {{range .Assignees}}
<a class="item tw-p-1 muted tw-hidden" id="assignee_{{.ID}}" href="{{$.RepoLink}}/issues?assignee={{.ID}}"> <a class="item tw-p-1 muted {{if not (SliceUtils.Contains $.SelectedAssigneeIDs .ID)}}tw-hidden{{end}}" id="assignee_{{.ID}}" href="{{$.RepoLink}}/issues?assignee={{.ID}}">
{{ctx.AvatarUtils.Avatar . 28 "tw-mr-2 tw-align-middle"}}{{.GetDisplayName}} {{ctx.AvatarUtils.Avatar . 28 "tw-mr-2 tw-align-middle"}}{{.GetDisplayName}}
</a> </a>
{{end}} {{end}}

@ -22345,6 +22345,9 @@
"type": "string", "type": "string",
"x-go-name": "About" "x-go-name": "About"
}, },
"assignees": {
"$ref": "#/definitions/IssueTemplateStringSlice"
},
"body": { "body": {
"type": "array", "type": "array",
"items": { "items": {
@ -22361,7 +22364,7 @@
"x-go-name": "FileName" "x-go-name": "FileName"
}, },
"labels": { "labels": {
"$ref": "#/definitions/IssueTemplateLabels" "$ref": "#/definitions/IssueTemplateStringSlice"
}, },
"name": { "name": {
"type": "string", "type": "string",
@ -22378,7 +22381,7 @@
}, },
"x-go-package": "code.gitea.io/gitea/modules/structs" "x-go-package": "code.gitea.io/gitea/modules/structs"
}, },
"IssueTemplateLabels": { "IssueTemplateStringSlice": {
"type": "array", "type": "array",
"items": { "items": {
"type": "string" "type": "string"

Loading…
Cancel
Save