github.com/letsencrypt/boulder@v0.20251208.0/sfe/forms/fields.go (about) 1 package forms 2 3 import ( 4 "fmt" 5 "html/template" 6 "strings" 7 ) 8 9 type Field interface { 10 // RenderForm returns the HTML representation of the field. 11 RenderField() template.HTML 12 } 13 14 type InputField struct { 15 // displayName is the name displayed in the form UI. 16 displayName string 17 18 // name is the name of the field when submitted in the form. It is required 19 // and must be unique within the form. 20 name string 21 22 // description is a short description displayed below the field. It is 23 // required. 24 description string 25 26 // required indicates whether the field is required. 27 required bool 28 } 29 30 var _ Field = (*InputField)(nil) 31 32 func NewInputField(displayName, name, description string, required bool) *InputField { 33 return &InputField{ 34 displayName: displayName, 35 name: name, 36 description: description, 37 required: required, 38 } 39 } 40 41 func (field InputField) RenderField() template.HTML { 42 var reqAttr string 43 if field.required { 44 reqAttr = `required="required"` 45 } 46 fieldHTML := fmt.Sprintf(` 47 <div class="form-field"> 48 <label for="%[1]s">%[2]s</label> 49 <small class="field-description">%[3]s</small><br> 50 <input type="text" id="%[1]s" name="%[1]s" %[4]s> 51 <div class="error-message"></div> 52 </div>`, field.name, field.displayName, field.description, reqAttr) 53 return template.HTML(fieldHTML) //nolint:gosec // G203: html produced by html/template; no raw user HTML is injected. 54 } 55 56 type DropdownField struct { 57 // displayName is the name displayed in the form UI. 58 displayName string 59 60 // name is the name of the field when submitted in the form. It is required 61 // and must be unique within the form. 62 name string 63 64 // description is a short description displayed below the field. It is 65 // required. 66 description string 67 68 // options is the list of options available in the dropdown. 69 options []string 70 71 // required indicates whether the field is required. 72 required bool 73 } 74 75 var _ Field = (*DropdownField)(nil) 76 77 func NewDropdownField(displayName, name, description string, options []string, required bool) Field { 78 return &DropdownField{ 79 displayName: displayName, 80 name: name, 81 description: description, 82 options: options, 83 required: required, 84 } 85 } 86 87 func (field DropdownField) RenderField() template.HTML { 88 var reqAttr string 89 if field.required { 90 reqAttr = `required="required"` 91 } 92 var b strings.Builder 93 b.WriteString(fmt.Sprintf(` 94 <div class="form-field"> 95 <label for="%[1]s">%[2]s</label> 96 <small class="field-description">%[3]s</small><br> 97 <select id="%[1]s" name="%[1]s" %[4]s> 98 <option value="" selected></option>`, field.name, field.displayName, field.description, reqAttr)) 99 for _, o := range field.options { 100 b.WriteString(fmt.Sprintf(`<option value="%[1]s">%[1]s</option>`, o)) 101 } 102 b.WriteString(`</select> 103 <div class="error-message"></div> 104 </div>`) 105 return template.HTML(b.String()) //nolint:gosec // G203: html produced by html/template; no raw user HTML is injected. 106 } 107 108 type TextareaField struct { 109 // displayName is the name displayed in the form UI. 110 displayName string 111 112 // name is the name of the field when submitted in the form. It is required 113 // and must be unique within the form. 114 name string 115 116 // description is a short description displayed below the field. It is 117 // required. 118 description string 119 120 // rows is the number of lines to show in the textarea. Optional and 121 // defaults to 4. 122 rows int 123 124 // required indicates whether the field is required. 125 required bool 126 } 127 128 var _ Field = (*TextareaField)(nil) 129 130 func NewTextareaField(displayName, name, description string, rows int, required bool) *TextareaField { 131 return &TextareaField{ 132 displayName: displayName, 133 name: name, 134 description: description, 135 rows: rows, 136 required: required, 137 } 138 } 139 140 func (field TextareaField) RenderField() template.HTML { 141 numRows := field.rows 142 if numRows <= 0 { 143 numRows = 4 144 } 145 var reqAttr string 146 if field.required { 147 reqAttr = `required="required"` 148 } 149 fieldHTML := fmt.Sprintf(` 150 <div class="form-field"> 151 <label for="%[1]s">%[2]s</label> 152 <small class="field-description">%[3]s</small><br> 153 <textarea id="%[1]s" name="%[1]s" rows="%[4]d" %[5]s></textarea> 154 <div class="error-message"></div> 155 </div>`, field.name, field.displayName, field.description, numRows, reqAttr) 156 return template.HTML(fieldHTML) //nolint:gosec // G203: html produced by html/template; no raw user HTML is injected. 157 } 158 159 type CheckboxField struct { 160 // displayName is the name displayed in the form UI. 161 displayName string 162 163 // name is the name of the field when submitted in the form. It is required 164 // and must be unique within the form. 165 name string 166 167 // text is the text displayed to the right of the checkbox. It is required. 168 text string 169 170 // required indicates whether the checkbox must be checked. 171 required bool 172 } 173 174 var _ Field = (*CheckboxField)(nil) 175 176 func NewCheckboxField(displayName, name, text string, required bool) *CheckboxField { 177 return &CheckboxField{ 178 displayName: displayName, 179 name: name, 180 text: text, 181 required: required, 182 } 183 } 184 185 func (field CheckboxField) RenderField() template.HTML { 186 var reqAttr string 187 if field.required { 188 reqAttr = `required="required"` 189 } 190 fieldHTML := fmt.Sprintf(` 191 <div class="highlight form-field checkbox-field" id="%[1]s-wrapper"> 192 <label for="%[1]s">%[2]s</label> 193 <div class="checkbox-row"> 194 <input type="checkbox" id="%[1]s" name="%[1]s" %[4]s> 195 <span class="checkbox-text">%[3]s</span> 196 </div> 197 <div class="error-message"></div> 198 </div>`, field.name, field.displayName, field.text, reqAttr) 199 return template.HTML(fieldHTML) //nolint:gosec // G203: html produced by html/template; no raw user HTML is injected. 200 }