Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set issue labels based on conditions in issue form #27676

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 137 additions & 0 deletions modules/issue/template/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@
package template

import (
"context"
"fmt"
"net/url"
"reflect"
"regexp"
"slices"
"strconv"
"strings"

issue_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/modules/container"
api "code.gitea.io/gitea/modules/structs"

Expand Down Expand Up @@ -247,6 +251,33 @@ func RenderToMarkdown(template *api.IssueTemplate, values url.Values) string {
return builder.String()
}

func GetTemplateFieldLabels(ctx context.Context, template *api.IssueTemplate, values url.Values, repoID int64) ([]int64, error) {
labelList := make([]int64, 0)

for _, field := range template.Fields {
f := &valuedField{
IssueFormField: field,
Values: values,
}
if f.ID == "" {
continue
}

fieldLabels, err := f.GetLabels(ctx, repoID)
if err != nil {
return nil, err
}

for _, labelID := range fieldLabels {
if !slices.Contains(labelList, labelID) {
labelList = append(labelList, labelID)
}
}
}

return labelList, nil
}

type valuedField struct {
*api.IssueFormField
url.Values
Expand Down Expand Up @@ -306,6 +337,28 @@ func (f *valuedField) WriteTo(builder *strings.Builder) {
_, _ = fmt.Fprintln(builder)
}

func (f *valuedField) GetLabels(ctx context.Context, repoID int64) ([]int64, error) {
labelList := make([]int64, 0)

switch f.Type {
case api.IssueFormFieldTypeCheckboxes, api.IssueFormFieldTypeDropdown:
for _, option := range f.Options() {
fieldLabels, err := option.GetLabels(ctx, repoID)
if err != nil {
return nil, err
}

for _, labelID := range fieldLabels {
if !slices.Contains(labelList, labelID) {
labelList = append(labelList, labelID)
}
}
}
}

return labelList, nil
}

func (f *valuedField) Label() string {
if label, ok := f.Attributes["label"].(string); ok {
return label
Expand Down Expand Up @@ -385,6 +438,90 @@ func (o *valuedOption) IsChecked() bool {
return false
}

func (o *valuedOption) getDropdownLabels() map[string][]string {
optionsLabels := make(map[string][]string)

optionsAny, ok := o.field.Attributes["options_labels"]
if !ok {
return optionsLabels
}

optionsMap, ok := optionsAny.(map[string]any)
if !ok {
return optionsLabels
}

for key, value := range optionsMap {
optionsLabels[key] = make([]string, 0)

if reflect.TypeOf(value).Kind() != reflect.Slice {
continue
}

stringSlice := reflect.ValueOf(value)
for i := 0; i < stringSlice.Len(); i++ {
str, ok := stringSlice.Index(i).Interface().(string)
if ok {
if !slices.Contains(optionsLabels[key], str) {
optionsLabels[key] = append(optionsLabels[key], str)
}
}
}
}

return optionsLabels
}

func (o *valuedOption) getCheckboxLabels() []string {
labelList := make([]string, 0)

vs, ok := o.data.(map[string]any)
if !ok {
return labelList
}

value, ok := vs["labels"]
if !ok {
return labelList
}

if reflect.TypeOf(value).Kind() != reflect.Slice {
return labelList
}

stringSlice := reflect.ValueOf(value)
for i := 0; i < stringSlice.Len(); i++ {
str, ok := stringSlice.Index(i).Interface().(string)
if ok {
if !slices.Contains(labelList, str) {
labelList = append(labelList, str)
}
}
}

return labelList
}

func (o *valuedOption) GetLabels(ctx context.Context, repoID int64) ([]int64, error) {
if !o.IsChecked() {
return make([]int64, 0), nil
}

switch o.field.Type {
case api.IssueFormFieldTypeDropdown:
dropdownLabels := o.getDropdownLabels()

labels, ok := dropdownLabels[o.Label()]
if ok {
return issue_model.GetLabelIDsInRepoByNames(ctx, repoID, labels)
}
case api.IssueFormFieldTypeCheckboxes:
return issue_model.GetLabelIDsInRepoByNames(ctx, repoID, o.getCheckboxLabels())
}

return make([]int64, 0), nil
}

var minQuotesRegex = regexp.MustCompilePOSIX("^`{3,}")

// minQuotes return 3 or more back-quotes.
Expand Down
11 changes: 11 additions & 0 deletions routers/web/repo/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -1209,6 +1209,17 @@ func NewIssuePost(ctx *context.Context) {
if filename := ctx.Req.Form.Get("template-file"); filename != "" {
if template, err := issue_template.UnmarshalFromRepo(ctx.Repo.GitRepo, ctx.Repo.Repository.DefaultBranch, filename); err == nil {
content = issue_template.RenderToMarkdown(template, ctx.Req.Form)

templateLabels, err := issue_template.GetTemplateFieldLabels(ctx, template, ctx.Req.Form, repo.ID)
if err != nil {
ctx.ServerError("GetTemplateFieldLabels", err)
}

for _, labelID := range templateLabels {
if !slices.Contains(labelIDs, labelID) {
labelIDs = append(labelIDs, labelID)
}
}
}
}

Expand Down