1 ##master-page:HelpTemplate 2 ##master-date:Unknown-Date 3 #format wiki 4 #language en 5 6 == MoinForms == 7 8 The !MoinForms support for !MoinMoin provides a way of describing Web forms that can be embedded in Wiki pages and handled by special Wiki actions. 9 10 <<TableOfContents(3)>> 11 12 == Creating Forms == 13 14 To embed a form within a page, a special region must be declared as in the following example: 15 16 {{{{ 17 {{{#!form fragment=exampleform 18 19 || '''Name''' || <<FormField(name,ExampleFormDict)>> || 20 || '''Address''' || <<FormField(address,ExampleFormDict)>> || 21 || '''Country''' || <<FormField(country,ExampleFormDict)>> || 22 || || <<FormField(finish,ExampleFormDict,label=Finish)>> || 23 24 }}} 25 }}}} 26 27 This example demonstrates... 28 29 * A region providing a form 30 * Within which form fields can be inserted using the `FormField` macro 31 * Using normal Wiki syntax, meaning that normal formatting facilities can be used to present forms (with a table being used in this case) 32 33 Here, four form fields are used, but their definitions are not provided in the form region itself. Instead, each of the fields references a !WikiDict providing such definition details, and this is described [[#Defining_Fields|below]]. 34 35 The above form definition produces the following form: 36 37 {{{#!form fragment=exampleform 38 39 || '''Name''' || <<FormField(name,ExampleFormDict)>> || 40 || '''Address''' || <<FormField(address,ExampleFormDict)>> || 41 || '''Country''' || <<FormField(country,ExampleFormDict)>> || 42 || || <<FormField(finish,ExampleFormDict,label=Finish)>> || 43 44 }}} 45 46 === Defining Form Regions === 47 48 {{{{ 49 {{{#!form fragment=<fragment> action=<action> 50 ... 51 }}} 52 }}}} 53 54 Form regions must be defined using the special `#!form` notation and should provide a `fragment` identifier that uniquely identifies the form on the page, along with an `action` identifier indicating which action should be used to interpret and process the form data. If the `action` argument is missing, the default `MoinFormHandlerAction` is used. 55 56 Normal Wiki markup can be used within form regions, but care must be taken to ensure that where other regions are embedded within form regions, the number of brackets used to declare the outer regions is greater than the number used to declare the embedded regions. This technique is described on the HelpOnParsers page, but is also illustrated below. 57 58 {{{{{ 59 {{{{#!form fragment=exampleform action=ExampleAction 60 61 Fill out the form below: 62 63 {{{#!wiki important 64 Be sure to fill out '''everything'''! 65 }}} 66 67 Name: <<FormField(name,ExampleFormDict)>> 68 Address: <<FormField(address,ExampleFormDict)>> 69 Done? <<FormField(finish,ExampleFormDict,label=Finish)>> 70 }}}} 71 }}}}} 72 73 Note how the form declaration uses `{{{{` and `}}}}` while the [[HelpOnAdmonitions|adminition]] uses `{{{` and `}}}`. 74 75 === Using the Form Field Macro === 76 77 {{{ 78 <<FormField(<name>,<WikiDict>,<option>...)>> 79 }}} 80 81 The `FormField` macro causes a form field to appear in the page having a particular name and employing a particular form control (text field, text area, pull-down menu, button) depending on the definition of the field. The !WikiDict argument must be the name of a page providing a collection of form definitions as described [[#Defining_Fields|below]]. 82 83 Besides the name and !WikiDict, some other options can be employed to change the nature of the displayed control: 84 85 || '''Option''' || '''Effect''' || 86 || `label` || provides a label for `submit` fields (buttons) || 87 || `section` || indicates the kind of section to be added to a form by a selector field (having the `_add` name) || 88 89 The label specified for a `submit` field will be localised according to the user's language settings. If no label is specified, an attempt will be made to localise the field name, but it is recommended that a label always be specified and any required translations be defined in user-specified translation pages (or other appropriate resources) as described on the HelpOnLanguages page. 90 91 ==== Selector Fields ==== 92 93 Most fields will rely on a separate definition recorded on the specified !WikiDict page, but a special class of fields known as ''selector'' fields that manipulate the form structure can be included by specifying one of the special names given below: 94 95 || '''Name''' || '''Purpose''' || 96 || `_add` || adds a section to the form (in conjunction with the `section` option) || 97 || `_remove` || removes a section from the form || 98 99 The !WikiDict information need not then be specified, but where a [[#Form_Sections|form section]] is to be added, the `section` option may need to be defined to indicate which kind of section is to be added to the form. 100 101 Selector fields also employ labels which behave just as they do for `submit` fields. 102 103 === Defining Fields === 104 105 Each form definition is provided as an entry in a !WikiDict, where each entry in the dictionary corresponds to a particular field name, and where each entry describes the details of any field of that name referencing that dictionary. A !WikiDict describing form fields has the following definition list syntax: 106 107 {{{ 108 <field name>:: <type> <option>... 109 }}} 110 111 In the above example, fields reference the ExampleFormDict page, and as a result the `name` field appears as a simple text field, the `address` field as a textarea, the `country` field as a pull-down menu, and the `finish` field as a submit button. How this is achieved can be seen on the ExampleFormDict page, but a summary of the different field types and options is provided here: 112 113 || '''Type''' || '''Appearance''' || '''Options''' || 114 || `text` || text field || `size`, `required` || 115 || `textarea` || text area || `cols`, `rows`, `required` || 116 || `select` || pull-down menu || `maxselected`, `source`, `required` || 117 || `submit` || submit button || || 118 119 === Form Sections === 120 121 Some kinds of forms require collections of fields that may be optional or repeating and that may be logically grouped. Form sections permit fields to be declared within their own namespace or group such that they may be collectively added, removed or replicated. For example: 122 123 {{{{{ 124 {{{{#!form fragment=exampleform2 125 126 || '''Name''' || <<FormField(name,ExampleFormDict)>> || 127 || '''Address''' || <<FormField(address,ExampleFormDict)>> || 128 || '''Country''' || <<FormField(country,ExampleFormDict)>> || 129 {{{#!form section=activity 130 || '''Activity/hobby''' || <<FormField(activity,ExampleFormDict)>> || 131 || '''Hours per week''' || <<FormField(duration,ExampleFormDict)>> || 132 || || <<FormField(_remove,label=Remove this activity)>> || 133 }}}|| || <<FormField(_add,section=activity,label=Add activity)>> || 134 || || <<FormField(finish,ExampleFormDict,label=Finish)>> || 135 136 }}}} 137 }}}}} 138 139 This produces the following form: 140 141 {{{{#!form fragment=exampleform2 142 143 || '''Name''' || <<FormField(name,ExampleFormDict)>> || 144 || '''Address''' || <<FormField(address,ExampleFormDict)>> || 145 || '''Country''' || <<FormField(country,ExampleFormDict)>> || 146 {{{#!form section=activity 147 || '''Activity/hobby''' || <<FormField(activity,ExampleFormDict)>> || 148 || '''Hours per week''' || <<FormField(duration,ExampleFormDict)>> || 149 || || <<FormField(_remove,label=Remove this activity)>> || 150 }}}|| || <<FormField(_add,section=activity,label=Add activity)>> || 151 || || <<FormField(finish,ExampleFormDict,label=Finish)>> || 152 153 }}}} 154 155 == Handling Form Data == 156 157 !MoinForms supplies a default form handler called `MoinFormHandlerAction` that supports the manipulation of forms by selector fields and provides a framework for applications to implement their own validation logic. The basic validation logic provided by the default handler is limited to detecting and reporting the following: 158 159 * Whether required fields have been specified or not 160 * Whether the correct number of choices have been specified for a field 161 162 Where validation is unsuccessful for a field, it is possible to signal such a condition using a variant of the form section intended to communicate messages to the user, with message information inserted into the page using a special `FormMessage` macro as described below. 163 164 === Form Message Sections === 165 166 Message sections are introduced in forms like normal sections but using the `message` argument in the form region's declaration. For example: 167 168 {{{{{ 169 {{{{#!form fragment=exampleform3 170 171 || '''Name''' || <<FormField(name,ExampleFormDict)>> || 172 {{{#!form message=name-error 173 ||<-2> /!\ <<FormMessage(name-error)>> || 174 }}}|| '''Address''' || <<FormField(address,ExampleFormDict)>> || 175 || '''Country''' || <<FormField(country,ExampleFormDict)>> || 176 || || <<FormField(finish,ExampleFormDict,label=Finish)>> || 177 178 }}}} 179 }}}}} 180 181 The form described above should look like this: 182 183 {{{{#!form fragment=exampleform3 184 185 || '''Name''' || <<FormField(name,ExampleFormDict)>> || 186 {{{#!form message=name-error 187 ||<-2> /!\ <<FormMessage(name-error)>> || 188 }}}|| '''Address''' || <<FormField(address,ExampleFormDict)>> || 189 || '''Country''' || <<FormField(country,ExampleFormDict)>> || 190 || || <<FormField(finish,ExampleFormDict,label=Finish)>> || 191 192 }}}} 193 194 Here, a message section has been introduced below the `name` field for a field called `name-error`. When validation produces errors, it will typically store them in fields whose names are a combination of the name of the erroneous field and the suffix `-error`. So in the above example, when `name` has an error associated with it, the error will be displayed in a new table row provided by the message section and inserted into the row using the `FormMessage` macro. 195 196 The errors can only be seen if the above form is submitted without a name, so you can try this out to reassure yourself that it does work as described. 197 198 ==== Repeating Message Sections ==== 199 200 Each field can potentially have many errors associated with it and stored in a separate error field. To be able to show all the errors, as opposed to only the first one, a `repeating` argument can be specified in the declaration of the message section. For example: 201 202 {{{{{ 203 {{{{#!form fragment=exampleform4 204 205 || '''Name''' || <<FormField(name,ExampleFormDict)>> || 206 {{{#!form message=name-error repeating 207 ||<-2> /!\ <<FormMessage(name-error)>> || 208 }}}|| '''Address''' || <<FormField(address,ExampleFormDict)>> || 209 || '''Country''' || <<FormField(country,ExampleFormDict)>> || 210 || || <<FormField(finish,ExampleFormDict,label=Finish)>> || 211 212 }}}} 213 }}}}} 214 215 The form described above should look like this: 216 217 {{{{#!form fragment=exampleform4 218 219 || '''Name''' || <<FormField(name,ExampleFormDict)>> || 220 {{{#!form message=name-error repeating 221 ||<-2> /!\ <<FormMessage(name-error)>> || 222 }}}|| '''Address''' || <<FormField(address,ExampleFormDict)>> || 223 || '''Country''' || <<FormField(country,ExampleFormDict)>> || 224 || || <<FormField(finish,ExampleFormDict,label=Finish)>> || 225 226 }}}} 227 228 Since the default handler doesn't tend to report multiple errors, this is not particularly instructive, but [[#Extending_the_Default_Form_Handler|specialised handlers]] could associate more errors with fields and this would then be exploited above. 229 230 === Using the Form Message Macro === 231 232 {{{ 233 <<FormMessage(<field name>)>> 234 }}} 235 236 The `FormMessage` macro causes a message associated with a field to appear in the page. Although the macro is most likely to be used with error fields, it can insert the value of any field into the page. 237 238 === Extending the Default Form Handler === 239 240 Specific applications will probably need to provide more sophisticated validation and handling of forms than the default action. This is most easily done by writing an action with the following general form: 241 242 {{{#!python 243 from MoinForms import MoinFormHandlerAction 244 245 class ExampleFormAction(MoinFormHandlerAction): 246 247 "An example form handler." 248 249 def finished(self, fields, form): 250 251 """ 252 Given a valid form with the given 'fields' and the request's 253 'form' dictionary, perform additional validation. 254 """ 255 256 _ = self.request.getText 257 258 # This defines the errors for the activity-error message field. 259 260 errors = [] 261 262 if not fields.get("activity"): 263 errors.append(_("Need at least one activity.")) 264 265 # Insist on the "finish" button being pressed and no errors. 266 # If these conditions are not met, report the errors and act 267 # as if the form is unfinished. 268 269 if errors or not fields.has_key("finish"): 270 fields["activity-error"] = errors 271 self.unfinished(fields, form) 272 273 # Upon successful validation, a special field is used to store 274 # messages and the underlying action method is called. 275 276 else: 277 fields["form-complete"] = [_("The form was correct.")] 278 MoinFormHandlerAction.finished(self, fields, form) 279 280 def execute(pagename, request): 281 ExampleFormAction(pagename, request).processForm() 282 }}} 283 284 By overriding the `finished` method, it is possible to take advantage of the field-level validation and form manipulation supported by the default form handler whilst adding logic and preventing forms from being accepted until such additional logic considers them to be correct. 285 286 Where a form is considered to be unfinished, the corresponding `unfinished` method should be invoked. Unless additional messages need to be added to the form, it is not necessary to override this method in an application-specific action. 287 288 Like normal form fields, the fields populated with error messages also use collections of values instead of single values. To show all values, use repeating message sections as described above.