Allow multiple projects per issue and pull requests (#36784)
Add ability to add and remove multiple projects per issue and pull request. Resolve #12974 --------- Signed-off-by: Icy Avocado <avocado@ovacoda.com> Co-authored-by: Tyrone Yeh <siryeh@gmail.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: OpenCode (gpt-5.2-codex) <opencode@openai.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: silverwind <me@silverwind.io> Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ package templates
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -36,7 +37,11 @@ func (r *pageRenderer) funcMapDummy() template.FuncMap {
|
||||
}
|
||||
|
||||
func (r *pageRenderer) TemplateLookup(tmpl string, templateCtx context.Context) (TemplateExecutor, error) { //nolint:revive // we don't use ctx, only pass it to the template executor
|
||||
return r.tmplRenderer.Templates().Executor(tmpl, r.funcMap(templateCtx))
|
||||
tmpls := r.tmplRenderer.Templates()
|
||||
if tmpls == nil {
|
||||
return nil, fmt.Errorf("no templates defined for %s", tmpl)
|
||||
}
|
||||
return tmpls.Executor(tmpl, r.funcMap(templateCtx))
|
||||
}
|
||||
|
||||
func (r *pageRenderer) HTML(w io.Writer, status int, tplName TplName, data any, templateCtx context.Context) error { //nolint:revive // we don't use ctx, only pass it to the template executor
|
||||
|
||||
@@ -6,6 +6,11 @@ package templates
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
type SliceUtils struct{}
|
||||
@@ -33,3 +38,29 @@ func (su *SliceUtils) Contains(s, v any) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// JoinInt64 joins a slice of int64 values into a comma-separated string.
|
||||
func (su *SliceUtils) JoinInt64(values []int64) string {
|
||||
if len(values) == 0 {
|
||||
return ""
|
||||
}
|
||||
strs := make([]string, len(values))
|
||||
for i, v := range values {
|
||||
strs[i] = strconv.FormatInt(v, 10)
|
||||
}
|
||||
return strings.Join(strs, ",")
|
||||
}
|
||||
|
||||
func (su *SliceUtils) JoinToggleIDs(values []int64, target int64) (ret struct {
|
||||
IsIncluded bool
|
||||
ToggledIDs string
|
||||
},
|
||||
) {
|
||||
ret.IsIncluded = slices.Contains(values, target)
|
||||
if ret.IsIncluded {
|
||||
ret.ToggledIDs = su.JoinInt64(util.SliceRemoveAll(slices.Clone(values), target))
|
||||
} else {
|
||||
ret.ToggledIDs = su.JoinInt64(append(values, target))
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
@@ -70,6 +70,16 @@ func TestUtils(t *testing.T) {
|
||||
actual = execTmpl("{{StringUtils.Contains .String .Value}}", map[string]any{"String": "abc", "Value": "x"})
|
||||
assert.Equal(t, "false", actual)
|
||||
|
||||
// Test JoinInt64
|
||||
actual = execTmpl("{{SliceUtils.JoinInt64 .Values}}", map[string]any{"Values": []int64{1, 2, 3}})
|
||||
assert.Equal(t, "1,2,3", actual)
|
||||
|
||||
actual = execTmpl("{{SliceUtils.JoinInt64 .Values}}", map[string]any{"Values": []int64{}})
|
||||
assert.Empty(t, actual)
|
||||
|
||||
actual = execTmpl("{{SliceUtils.JoinInt64 .Values}}", map[string]any{"Values": []int64{42}})
|
||||
assert.Equal(t, "42", actual)
|
||||
|
||||
tmpl := template.New("test")
|
||||
tmpl.Funcs(template.FuncMap{"SliceUtils": NewSliceUtils, "StringUtils": NewStringUtils})
|
||||
template.Must(tmpl.Parse("{{SliceUtils.Contains .Slice .Value}}"))
|
||||
|
||||
Reference in New Issue
Block a user