feat(custom-fields): add required flag UI and API validation (#597) #612

Merged
jmiller merged 1 commits from feature/597-required-custom-fields into main 2026-06-12 02:59:24 +00:00
3 changed files with 21 additions and 2 deletions
+2
View File
@@ -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.",
+11 -1
View File
@@ -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 {
+8 -1
View File
@@ -19,7 +19,7 @@
<tbody>
{{range .CustomFields}}
<tr>
<td><strong>{{.Name}}</strong>{{if .Description}}<br><small class="text grey">{{.Description}}</small>{{end}}</td>
<td><strong>{{.Name}}</strong>{{if .Required}} <span class="tw-text-red" data-tooltip-content="Required">*</span>{{end}}{{if .Description}}<br><small class="text grey">{{.Description}}</small>{{end}}</td>
<td>{{if eq .Scope "issue"}}{{svg "octicon-issue-opened" 14}} Issue{{else}}{{svg "octicon-repo" 14}} Repo{{end}}</td>
<td><code>{{.FieldType}}</code></td>
<td>{{if .Options}}<code class="tw-text-xs">{{.Options}}</code>{{else}}<span class="text grey">-</span>{{end}}</td>
@@ -79,6 +79,13 @@
<input name="description" placeholder="Help text shown to users">
</div>
</div>
<div class="field">
<div class="ui checkbox">
<input type="checkbox" name="required">
<label>{{ctx.Locale.Tr "org.settings.custom_field_required"}}</label>
</div>
<p class="help">{{ctx.Locale.Tr "org.settings.custom_field_required_help"}}</p>
</div>
<button class="ui primary button" type="submit">{{ctx.Locale.Tr "org.settings.custom_field_add"}}</button>
</form>
</div>