From 245b5a8e6a73541c4ed93db3b3bcf8a99069b226 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 11 Jun 2026 21:46:24 -0500 Subject: [PATCH] feat(custom-fields): add required flag UI and API validation (#597) - Add required checkbox to org custom field settings form - Show red asterisk (*) indicator on required fields in the list - Validate required custom fields on API issue create (return 422) - Add locale strings for required field UI The Required column already exists in the DB (migration v343) and is already validated in the web form. This adds the missing org settings UI checkbox and API-side validation. Co-Authored-By: Moko Consulting --- options/locale/locale_en-US.json | 2 ++ routers/api/v1/repo/issue.go | 12 +++++++++++- templates/org/settings/custom_fields.tmpl | 9 ++++++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/options/locale/locale_en-US.json b/options/locale/locale_en-US.json index 6aac6baa77..97aa159461 100644 --- a/options/locale/locale_en-US.json +++ b/options/locale/locale_en-US.json @@ -2976,6 +2976,8 @@ "org.settings.custom_field_options": "Options (JSON)", "org.settings.custom_field_options_help": "For dropdown fields, enter options as a JSON array.", "org.settings.custom_field_description": "Description", + "org.settings.custom_field_required": "Required", + "org.settings.custom_field_required_help": "When checked, this field must be filled in when creating or editing issues.", "org.settings.custom_field_created": "Custom field created.", "org.settings.custom_field_updated": "Custom field updated.", "org.settings.custom_field_deleted": "Custom field deleted.", diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index 6dcc32a2b2..14c3b89da6 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -734,13 +734,23 @@ func CreateIssue(ctx *context.APIContext) { } // Save custom field values if provided (resolve field names to IDs). - if len(form.CustomFields) > 0 { + // Validate required fields are present. + { defs, defErr := issues_model.GetCustomFieldsByOwner(ctx, ctx.Repo.Repository.OwnerID, issues_model.CustomFieldScopeIssue) if defErr != nil { ctx.APIErrorInternal(defErr) return } if len(defs) > 0 { + // Check required fields. + for _, def := range defs { + if def.Required { + if v, ok := form.CustomFields[def.Name]; !ok || strings.TrimSpace(v) == "" { + ctx.APIError(http.StatusUnprocessableEntity, fmt.Errorf("custom field %q is required", def.Name)) + return + } + } + } vals := make(map[int64]string) for _, def := range defs { if v, ok := form.CustomFields[def.Name]; ok { diff --git a/templates/org/settings/custom_fields.tmpl b/templates/org/settings/custom_fields.tmpl index 37809fdab9..59d0c04ead 100644 --- a/templates/org/settings/custom_fields.tmpl +++ b/templates/org/settings/custom_fields.tmpl @@ -19,7 +19,7 @@ {{range .CustomFields}} - {{.Name}}{{if .Description}}
{{.Description}}{{end}} + {{.Name}}{{if .Required}} *{{end}}{{if .Description}}
{{.Description}}{{end}} {{if eq .Scope "issue"}}{{svg "octicon-issue-opened" 14}} Issue{{else}}{{svg "octicon-repo" 14}} Repo{{end}} {{.FieldType}} {{if .Options}}{{.Options}}{{else}}-{{end}} @@ -79,6 +79,13 @@ +
+
+ + +
+

{{ctx.Locale.Tr "org.settings.custom_field_required_help"}}

+
-- 2.52.0