mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-21 04:21:19 +02:00
chore: form binding trim space (#38978)
Use "binding:TrimSpace" instead of fragile IsEmptyString And fix a bug in locale's `HasKey`: it should also try the default language if current language doesn't have the translation key, a new test is added.
This commit is contained in:
@@ -6,7 +6,7 @@ toolchain go1.26.6
|
||||
|
||||
require (
|
||||
connectrpc.com/connect v1.20.0
|
||||
gitea.com/go-chi/binding v0.0.0-20260818211407-ac8602c87be9
|
||||
gitea.com/go-chi/binding v0.0.0-20260819122636-082915a69981
|
||||
gitea.com/go-chi/cache v0.2.1
|
||||
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098
|
||||
gitea.com/go-chi/session v0.0.0-20260708011333-ebced8a7a2d6
|
||||
|
||||
@@ -8,8 +8,8 @@ dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||
filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
|
||||
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
|
||||
gitea.com/go-chi/binding v0.0.0-20260818211407-ac8602c87be9 h1:J/NzRmGh7olgthuZ096FNdJcQWuEpJb3ycEi6yArlGY=
|
||||
gitea.com/go-chi/binding v0.0.0-20260818211407-ac8602c87be9/go.mod h1:q1SSPpkC9A0gfNnoqqZ3My6kEHIpKN6QsTI+Zx73B/o=
|
||||
gitea.com/go-chi/binding v0.0.0-20260819122636-082915a69981 h1:LmdlwGbzgZFZA3bK3R1q8QrNabaSz3KpnOJBLmzhN6E=
|
||||
gitea.com/go-chi/binding v0.0.0-20260819122636-082915a69981/go.mod h1:q1SSPpkC9A0gfNnoqqZ3My6kEHIpKN6QsTI+Zx73B/o=
|
||||
gitea.com/go-chi/cache v0.2.1 h1:bfAPkvXlbcZxPCpcmDVCWoHgiBSBmZN/QosnZvEC0+g=
|
||||
gitea.com/go-chi/cache v0.2.1/go.mod h1:Qic0HZ8hOHW62ETGbonpwz8WYypj9NieU9659wFUJ8Q=
|
||||
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098 h1:p2ki+WK0cIeNQuqjR98IP2KZQKRzJJiV7aTeMAFwaWo=
|
||||
|
||||
@@ -58,7 +58,7 @@ type CreateProjectOption struct {
|
||||
// EditProjectOption represents options for editing a project
|
||||
// swagger:model
|
||||
type EditProjectOption struct {
|
||||
Title *string `json:"title,omitempty"`
|
||||
Title *string `json:"title,omitempty" binding:"TrimSpace;Required"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
// Card type: "text_only" or "images_and_text"
|
||||
CardType *string `json:"card_type,omitempty"`
|
||||
@@ -94,7 +94,7 @@ type CreateProjectColumnOption struct {
|
||||
// EditProjectColumnOption represents options for editing a project column
|
||||
// swagger:model
|
||||
type EditProjectColumnOption struct {
|
||||
Title *string `json:"title,omitempty"`
|
||||
Title *string `json:"title,omitempty" binding:"TrimSpace;Required"`
|
||||
// Column color in 6-digit hex format, e.g. #FF0000
|
||||
Color *string `json:"color,omitempty"`
|
||||
// Position of the column within the project, between -128 and 127
|
||||
|
||||
@@ -43,7 +43,7 @@ type WikiPageMetaData struct {
|
||||
// CreateWikiPageOptions form for creating wiki
|
||||
type CreateWikiPageOptions struct {
|
||||
// page title. leave empty to keep unchanged
|
||||
Title string `json:"title"`
|
||||
Title string `json:"title" binding:"TrimSpace;Required"`
|
||||
// content must be base64 encoded
|
||||
ContentBase64 string `json:"content_base64"`
|
||||
// optional commit message summarizing the change
|
||||
|
||||
@@ -16,6 +16,7 @@ func TestLocaleStore(t *testing.T) {
|
||||
{
|
||||
".dot.name": "Dot Name",
|
||||
"fmt": "%[1]s %[2]s",
|
||||
"only_in_default": "",
|
||||
|
||||
"section.sub": "Sub String",
|
||||
"section.mixed": "test value; <span style=\"color: red; background: none;\">%s</span>"
|
||||
@@ -60,6 +61,9 @@ func TestLocaleStore(t *testing.T) {
|
||||
|
||||
found := lang1.HasKey("no-such")
|
||||
assert.False(t, found)
|
||||
found = lang2.HasKey("only_in_default")
|
||||
assert.True(t, found)
|
||||
|
||||
assert.NoError(t, ls.Close())
|
||||
|
||||
res := lang1.TrHTML("<no-such>")
|
||||
|
||||
@@ -177,5 +177,8 @@ func (l *locale) HasKey(trKey string) bool {
|
||||
return false
|
||||
}
|
||||
_, ok = l.idxToMsgMap[idx]
|
||||
if !ok {
|
||||
_, ok = l.store.localeMap[l.store.defaultLang]
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
rand2 "math/rand/v2"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"gitea.dev/modules/container"
|
||||
@@ -22,11 +21,6 @@ import (
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
// IsEmptyString checks if the provided string is empty
|
||||
func IsEmptyString(s string) bool {
|
||||
return len(strings.TrimSpace(s)) == 0
|
||||
}
|
||||
|
||||
// ParseYamlBool parses YAML 1.2 boolean values into bool
|
||||
func ParseYamlBool(s string) bool {
|
||||
return s == "true" || s == "True" || s == "TRUE"
|
||||
|
||||
@@ -11,22 +11,6 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIsEmptyString(t *testing.T) {
|
||||
cases := []struct {
|
||||
s string
|
||||
expected bool
|
||||
}{
|
||||
{"", true},
|
||||
{" ", true},
|
||||
{" ", true},
|
||||
{" a", false},
|
||||
}
|
||||
|
||||
for _, v := range cases {
|
||||
assert.Equal(t, v.expected, IsEmptyString(v.s))
|
||||
}
|
||||
}
|
||||
|
||||
func Test_NormalizeEOL(t *testing.T) {
|
||||
data1 := []string{
|
||||
"",
|
||||
|
||||
@@ -50,7 +50,6 @@ func newFieldError(field reflect.StructField, cls, msg string) *BindingError {
|
||||
|
||||
// AddBindingRules adds additional binding rules
|
||||
func AddBindingRules(b *binding.Binder) {
|
||||
binding.JSONProvider = jsonProvider{}
|
||||
b.AddRuleNonZero("GitRefName", func(ctx context.Context, f *binding.ValidationField) *binding.Error {
|
||||
if !git.IsValidRefPattern(f.ValueMustString()) {
|
||||
return newFieldError(f.StructField, ErrGitRefName, "GitRefName")
|
||||
@@ -140,7 +139,7 @@ func validPort(p string) bool {
|
||||
}
|
||||
|
||||
var Binder = sync.OnceValue(func() *binding.Binder {
|
||||
b := binding.NewBinder().WithDefaultRules().WithNameMapper(util.ToSnakeCase)
|
||||
b := binding.NewBinder().WithJSONProvider(jsonProvider{}).WithDefaultRules().WithNameMapper(util.ToSnakeCase)
|
||||
AddBindingRules(b)
|
||||
return b
|
||||
})
|
||||
|
||||
@@ -532,6 +532,7 @@
|
||||
"form.CommitMessage": "Commit message",
|
||||
"form.CommitChoice": "Commit choice",
|
||||
"form.TreeName": "File path",
|
||||
"form.Title": "Title",
|
||||
"form.Content": "Content",
|
||||
"form.SSPISeparatorReplacement": "Separator",
|
||||
"form.SSPIDefaultLanguage": "Default Language",
|
||||
@@ -1418,7 +1419,6 @@
|
||||
"repo.issues.filter_no_results": "No results",
|
||||
"repo.issues.filter_no_results_placeholder": "Try adjusting your search filters.",
|
||||
"repo.issues.new": "New Issue",
|
||||
"repo.issues.new.title_empty": "Title cannot be empty",
|
||||
"repo.issues.new.labels": "Labels",
|
||||
"repo.issues.new.no_labels": "No labels",
|
||||
"repo.issues.new.clear_labels": "Clear labels",
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"gitea.dev/modules/git"
|
||||
"gitea.dev/modules/setting"
|
||||
api "gitea.dev/modules/structs"
|
||||
"gitea.dev/modules/util"
|
||||
"gitea.dev/modules/web"
|
||||
"gitea.dev/services/context"
|
||||
"gitea.dev/services/convert"
|
||||
@@ -56,12 +55,6 @@ func NewWikiPage(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/repoArchivedError"
|
||||
|
||||
form := web.GetForm[*api.CreateWikiPageOptions](ctx)
|
||||
|
||||
if util.IsEmptyString(form.Title) {
|
||||
ctx.APIError(http.StatusBadRequest, "title is required")
|
||||
return
|
||||
}
|
||||
|
||||
wikiName := wiki_service.UserTitleToWebPath("", form.Title)
|
||||
|
||||
if len(form.Message) == 0 {
|
||||
|
||||
@@ -612,10 +612,6 @@ func EditProject(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
form := web.GetForm[*api.EditProjectOption](ctx)
|
||||
if form.Title != nil && util.IsEmptyString(*form.Title) {
|
||||
ctx.APIError(http.StatusUnprocessableEntity, "title must not be empty")
|
||||
return
|
||||
}
|
||||
opts := project_service.UpdateProjectOptions{
|
||||
Title: optional.FromPtr(form.Title),
|
||||
Description: optional.FromPtr(form.Description),
|
||||
@@ -1187,10 +1183,6 @@ func EditProjectColumn(ctx *context.APIContext) {
|
||||
|
||||
form := web.GetForm[*api.EditProjectColumnOption](ctx)
|
||||
if form.Title != nil {
|
||||
if util.IsEmptyString(*form.Title) {
|
||||
ctx.APIError(http.StatusUnprocessableEntity, "title must not be empty")
|
||||
return
|
||||
}
|
||||
column.Title = *form.Title
|
||||
}
|
||||
if form.Color != nil {
|
||||
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/templates"
|
||||
"gitea.dev/modules/util"
|
||||
"gitea.dev/modules/web"
|
||||
auth_service "gitea.dev/services/auth"
|
||||
"gitea.dev/services/auth/source/ldap"
|
||||
@@ -210,7 +209,7 @@ func parseOAuth2Config(form forms.AuthenticationForm) *oauth2.Source {
|
||||
}
|
||||
|
||||
func parseSSPIConfig(ctx *context.Context, form forms.AuthenticationForm) (*sspi.Source, error) {
|
||||
if util.IsEmptyString(form.SSPISeparatorReplacement) {
|
||||
if form.SSPISeparatorReplacement == "" {
|
||||
ctx.Data["Err_SSPISeparatorReplacement"] = true
|
||||
return nil, errors.New(ctx.Locale.TrString("form.require_error", ctx.Locale.TrString("form.SSPISeparatorReplacement")))
|
||||
}
|
||||
|
||||
@@ -351,11 +351,6 @@ func NewIssuePost(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if util.IsEmptyString(form.Title) {
|
||||
ctx.JSONError(ctx.Tr("repo.issues.new.title_empty"))
|
||||
return
|
||||
}
|
||||
|
||||
content := form.Content
|
||||
if filename := ctx.Req.Form.Get("template-file"); filename != "" {
|
||||
if template, err := issue_template.UnmarshalFromRepo(ctx, ctx.Repo.GitRepo, ctx.Repo.Repository.DefaultBranch, filename); err == nil {
|
||||
|
||||
@@ -1353,11 +1353,6 @@ func CompareAndPullRequestPost(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if util.IsEmptyString(form.Title) {
|
||||
ctx.JSONError(ctx.Tr("repo.issues.new.title_empty"))
|
||||
return
|
||||
}
|
||||
|
||||
// Check if a pull request already exists with the same head and base branch.
|
||||
pr, err := issues_model.GetUnmergedPullRequest(ctx, ci.HeadRepo.ID, repo.ID, ci.HeadRef.ShortName(), ci.BaseRef.ShortName(), issues_model.PullRequestFlowGithub)
|
||||
if err != nil && !issues_model.IsErrPullRequestNotExist(err) {
|
||||
|
||||
@@ -87,6 +87,6 @@ type AuthenticationForm struct {
|
||||
SSPIAutoCreateUsers bool
|
||||
SSPIAutoActivateUsers bool
|
||||
SSPIStripDomainNames bool
|
||||
SSPISeparatorReplacement string `binding:"AlphaDashDot;MaxSize(5)"`
|
||||
SSPISeparatorReplacement string `binding:"TrimSpace;AlphaDashDot;MaxSize(5)"`
|
||||
SSPIDefaultLanguage string
|
||||
}
|
||||
|
||||
@@ -336,7 +336,7 @@ type NewPackagistHookForm struct {
|
||||
// CreateIssueForm form for creating issue
|
||||
type CreateIssueForm struct {
|
||||
middleware.FormDefaultValidator
|
||||
Title string `binding:"Required;MaxSize(255)"`
|
||||
Title string `binding:"TrimSpace;Required;MaxSize(255)"`
|
||||
AssigneeIDs string `form:"assignee_ids"`
|
||||
ReviewerIDs string `form:"reviewer_ids"`
|
||||
Ref string `form:"ref"`
|
||||
@@ -550,7 +550,7 @@ type GenerateReleaseNotesForm struct {
|
||||
// EditReleaseForm form for changing release
|
||||
type EditReleaseForm struct {
|
||||
middleware.FormDefaultValidator
|
||||
Title string `form:"title" binding:"Required;MaxSize(255)"`
|
||||
Title string `form:"title" binding:"TrimSpace;Required;MaxSize(255)"`
|
||||
Content string `form:"content"`
|
||||
Draft string `form:"draft"`
|
||||
Prerelease bool `form:"prerelease"`
|
||||
@@ -558,20 +558,12 @@ type EditReleaseForm struct {
|
||||
}
|
||||
|
||||
type WikiEditForm struct {
|
||||
Title string
|
||||
middleware.FormDefaultValidator
|
||||
Title string `binding:"TrimSpace;Required"`
|
||||
Content string
|
||||
Message string
|
||||
}
|
||||
|
||||
func (f *WikiEditForm) Validate(ctx *middleware.ValidateContext, errs validation.BindingErrors) validation.BindingErrors {
|
||||
f.Title = strings.TrimSpace(f.Title)
|
||||
if f.Title == "" {
|
||||
errs = middleware.AddValidationError(errs, "title", ctx.Locale.TrString("repo.issues.new.title_empty"))
|
||||
}
|
||||
f.Message = strings.TrimSpace(f.Message)
|
||||
return errs
|
||||
}
|
||||
|
||||
// AddTimeManuallyForm form that adds spent time manually.
|
||||
type AddTimeManuallyForm struct {
|
||||
middleware.FormDefaultValidator
|
||||
|
||||
Reference in New Issue
Block a user