Introduce a commercial license option alongside AGPL-3.0-only, require a CLA for contributors, and document the terms in COMMERCIAL.md and NOTICE. Add a script to stamp SPDX headers on Go files and apply it across the tree.
88 lines
3.4 KiB
Go
88 lines
3.4 KiB
Go
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Commercial
|
|
// SPDX-FileCopyrightText: 2025-2026 Christian Galo
|
|
|
|
package forms
|
|
|
|
// Shared field values (design D3; spec form-library "Two forms that write
|
|
// one column share one field"). A Field written by more than one form to
|
|
// the same column is declared once here and included by reference, so its
|
|
// name, label, hint and rules are identical wherever it appears. A variant
|
|
// is a new value with a new name, never a copy with loosened rules
|
|
// (lesson L§45).
|
|
//
|
|
// This is what ends the "reason" collision the audit found (findings
|
|
// FA-28, FA-29): Issue grant keeps "reason" for the grant-reason enum and
|
|
// Extend writes its note through GrantNote, so one column has one field,
|
|
// one label, one hint and one 500-character cap on both paths.
|
|
|
|
// maxGrantText caps the free-text fields that feed grants.description and
|
|
// pool_provision_transitions.reason. Those columns are TEXT with no
|
|
// database bound; the cap is the console's own guard, applied by the
|
|
// declaration so the control and the handler cannot disagree.
|
|
const maxGrantText = 500
|
|
|
|
// GrantNote is the note written with a grant, by Issue grant and by Extend
|
|
// alike; the text is the grant's note, the column upstream is called
|
|
// description. Both are dense rows, where a line under one control lifts
|
|
// its label above its neighbours', so the explanation rides the help icon
|
|
// after the label rather than a hint (form-conventions "Dense form rows
|
|
// keep explanations behind a help icon"; the invariants refuse a hint in a
|
|
// dense form). Optional because grants.description is a nullable column
|
|
// (00001_init.sql) and pre-existing deliveries carry no description at all;
|
|
// an extension with no note and no expiry is a legitimate, complete
|
|
// submission, not a refused one (a lesson the DB-backed suite caught before
|
|
// this was corrected: TestExtendGrant_SucceedsForSinglePoolOrg and
|
|
// TestGrantsListDeliveryStateAgreesWithGoDerivation both submit with the
|
|
// field absent and expect success).
|
|
func GrantNote() Field {
|
|
return Field{
|
|
Name: "description",
|
|
Label: "Note",
|
|
Control: Text,
|
|
Optional: true,
|
|
Help: Help("Note", "Why this grant is issued. Shown with the grant in its history."),
|
|
MaxLen: maxGrantText,
|
|
Autocomplete: "off",
|
|
}
|
|
}
|
|
|
|
// GrantValidUntil is the optional end of a grant, on both grant forms.
|
|
// One label, because the two forms wrote one column under two names
|
|
// ("Valid until" and "Expires at") before this change.
|
|
func GrantValidUntil() Field {
|
|
return Field{
|
|
Name: "valid_until",
|
|
Label: "Valid until",
|
|
Control: DateTime,
|
|
Optional: true,
|
|
Help: Help("Valid until", "Leave blank for a grant with no end date."),
|
|
}
|
|
}
|
|
|
|
// RecordName is the name of a catalog record: a product, an entitlement
|
|
// set, a plan ladder. Required on every one of them, with no marker,
|
|
// because required is the default and only optional is marked.
|
|
func RecordName() Field {
|
|
return Field{
|
|
Name: "name",
|
|
Label: "Name",
|
|
Control: Text,
|
|
MaxLen: 200,
|
|
Autocomplete: "off",
|
|
}
|
|
}
|
|
|
|
// RecordDescription is the optional prose under a catalog record's name.
|
|
// The same column on three records carried three markings before this
|
|
// change (finding FA-4); it carries one now.
|
|
func RecordDescription() Field {
|
|
return Field{
|
|
Name: "description",
|
|
Label: "Description",
|
|
Control: Text,
|
|
Optional: true,
|
|
MaxLen: 500,
|
|
Autocomplete: "off",
|
|
}
|
|
}
|