Compare commits

...

3 Commits

Author SHA1 Message Date
Jonathan Miller c67e7373fb fix: move required custom field validation before issue creation
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Universal: Auto Version Bump / Version Bump (push) Successful in 10s
Generic: Project CI / Tests (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Branch Policy Check / Verify merge target (pull_request) Failing after 2s
Universal: PR Check / Branch Policy (pull_request) Failing after 3s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 11s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Failing after 2s
Generic: Project CI / Lint & Validate (pull_request) Successful in 51s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 1m22s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 4m19s
Validation now runs before NewIssue() to prevent orphaned issues
when a required field is missing. Reuses the defs query for both
validation and saving.
2026-06-11 21:56:14 -05:00
Jonathan Miller 245b5a8e6a feat(custom-fields): add required flag UI and API validation (#597)
Generic: Project CI / Tests (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Branch Policy Check / Verify merge target (pull_request) Failing after 4s
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Universal: PR Check / Branch Policy (pull_request) Failing after 4s
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Validate PR (pull_request) Failing after 15s
Generic: Repo Health / Access control (pull_request) Successful in 5s
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Project CI / Lint & Validate (pull_request) Successful in 1m0s
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 3s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
PR RC Release / Build RC Release (pull_request) Failing after 1m30s
Universal: Auto Version Bump / Version Bump (push) Successful in 11s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been skipped
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 2m37s
- 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 <hello@mokoconsulting.tech>
2026-06-11 21:46:24 -05:00
Jonathan Miller d61680d5b3 fix: use string client values per Joomla update spec (#611)
Generic: Project CI / Tests (push) Blocked by required conditions
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Generic: Project CI / Lint & Validate (push) Successful in 46s
Deploy MokoGitea / deploy (push) Failing after 3m15s
2026-06-12 00:39:07 +00:00
3 changed files with 37 additions and 19 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.",
+27 -18
View File
@@ -722,6 +722,22 @@ func CreateIssue(ctx *context.APIContext) {
form.Labels = make([]int64, 0)
}
// Validate required custom fields BEFORE creating the issue to avoid
// leaving orphaned issues when validation fails.
customFieldDefs, defErr := issues_model.GetCustomFieldsByOwner(ctx, ctx.Repo.Repository.OwnerID, issues_model.CustomFieldScopeIssue)
if defErr != nil {
ctx.APIErrorInternal(defErr)
return
}
for _, def := range customFieldDefs {
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
}
}
}
if err := issue_service.NewIssue(ctx, ctx.Repo.Repository, issue, form.Labels, nil, assigneeIDs, form.Projects); err != nil {
if errors.Is(err, user_model.ErrBlockedUser) {
ctx.APIError(http.StatusForbidden, err)
@@ -733,25 +749,18 @@ func CreateIssue(ctx *context.APIContext) {
return
}
// Save custom field values if provided (resolve field names to IDs).
if len(form.CustomFields) > 0 {
defs, defErr := issues_model.GetCustomFieldsByOwner(ctx, ctx.Repo.Repository.OwnerID, issues_model.CustomFieldScopeIssue)
if defErr != nil {
ctx.APIErrorInternal(defErr)
return
}
if len(defs) > 0 {
vals := make(map[int64]string)
for _, def := range defs {
if v, ok := form.CustomFields[def.Name]; ok {
vals[def.ID] = v
}
// Save custom field values (reuse defs from validation above).
if len(customFieldDefs) > 0 && len(form.CustomFields) > 0 {
vals := make(map[int64]string)
for _, def := range customFieldDefs {
if v, ok := form.CustomFields[def.Name]; ok {
vals[def.ID] = v
}
if len(vals) > 0 {
if setErr := issues_model.SetCustomFieldValues(ctx, issue.ID, vals); setErr != nil {
ctx.APIErrorInternal(setErr)
return
}
}
if len(vals) > 0 {
if setErr := issues_model.SetCustomFieldValues(ctx, issue.ID, vals); setErr != nil {
ctx.APIErrorInternal(setErr)
return
}
}
}
+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>