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 To define labels that contain commas or brackets, quote them as follows: 47 48 {{{ 49 <<FormField(finish,ExampleFormDict,"label=Finish, once again, with the form. (Yes, I am sure.)")>> 50 }}} 51 52 === Defining Form Regions === 53 54 {{{{ 55 {{{#!form fragment=<fragment> action=<action> 56 ... 57 }}} 58 }}}} 59 60 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. 61 62 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. 63 64 {{{{{ 65 {{{{#!form fragment=exampleform action=ExampleAction 66 67 Fill out the form below: 68 69 {{{#!wiki important 70 Be sure to fill out '''everything'''! 71 }}} 72 73 Name: <<FormField(name,ExampleFormDict)>> 74 Address: <<FormField(address,ExampleFormDict)>> 75 Done? <<FormField(finish,ExampleFormDict,label=Finish)>> 76 }}}} 77 }}}}} 78 79 Note how the form declaration uses `{{{{` and `}}}}` while the [[HelpOnAdmonitions|adminition]] uses `{{{` and `}}}`. 80 81 === Using the Form Field Macro === 82 83 {{{ 84 <<FormField(<name>,<WikiDict>,<option>...)>> 85 }}} 86 87 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]]. 88 89 Besides the name and !WikiDict, some other options can be employed to change the nature of the displayed control: 90 91 || '''Option''' || '''Effect''' || 92 || `label` || provides a label for `submit` fields (buttons) || 93 || `section` || indicates the kind of section to be added to a form by a selector field (having the `_add` name) || 94 95 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. 96 97 ==== Selector Fields ==== 98 99 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: 100 101 || '''Name''' || '''Purpose''' || 102 || `_add` || adds a section to the form (in conjunction with the `section` option) || 103 || `_remove` || removes a section from the form || 104 105 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. 106 107 Selector fields also employ labels which behave just as they do for `submit` fields. 108 109 === Defining Fields === 110 111 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: 112 113 {{{ 114 <field name>:: <type> <option>... 115 }}} 116 117 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: 118 119 || '''Type''' || '''Appearance''' || '''Options''' || 120 || `text` || text field || `size`, `required` || 121 || `textarea` || text area || `cols`, `rows`, `required` || 122 || `select` || pull-down menu || `maxselected`, `source`, `required` || 123 || `submit` || submit button || || 124 125 === Form Sections === 126 127 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: 128 129 {{{{{ 130 {{{{#!form fragment=exampleform2 131 132 || '''Name''' || <<FormField(name,ExampleFormDict)>> || 133 || '''Address''' || <<FormField(address,ExampleFormDict)>> || 134 || '''Country''' || <<FormField(country,ExampleFormDict)>> || 135 {{{#!form section=activity 136 || '''Activity/hobby''' || <<FormField(activity,ExampleFormDict)>> || 137 || '''Hours per week''' || <<FormField(duration,ExampleFormDict)>> || 138 || || <<FormField(_remove,label=Remove this activity)>> || 139 }}}|| || <<FormField(_add,section=activity,label=Add activity)>> || 140 || || <<FormField(finish,ExampleFormDict,label=Finish)>> || 141 142 }}}} 143 }}}}} 144 145 This produces the following form: 146 147 {{{{#!form fragment=exampleform2 148 149 || '''Name''' || <<FormField(name,ExampleFormDict)>> || 150 || '''Address''' || <<FormField(address,ExampleFormDict)>> || 151 || '''Country''' || <<FormField(country,ExampleFormDict)>> || 152 {{{#!form section=activity 153 || '''Activity/hobby''' || <<FormField(activity,ExampleFormDict)>> || 154 || '''Hours per week''' || <<FormField(duration,ExampleFormDict)>> || 155 || || <<FormField(_remove,label=Remove this activity)>> || 156 }}}|| || <<FormField(_add,section=activity,label=Add activity)>> || 157 || || <<FormField(finish,ExampleFormDict,label=Finish)>> || 158 159 }}}} 160 161 == Handling Form Data == 162 163 !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: 164 165 * Whether required fields have been specified or not 166 * Whether the correct number of choices have been specified for a field 167 168 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. 169 170 === Form Message Sections === 171 172 Message sections are introduced in forms like normal sections but using the `message` argument in the form region's declaration. For example: 173 174 {{{{{ 175 {{{{#!form fragment=exampleform3 176 177 || '''Name''' || <<FormField(name,ExampleFormDict)>> || 178 {{{#!form message=name-error 179 ||<-2> /!\ <<FormMessage(name-error)>> || 180 }}}|| '''Address''' || <<FormField(address,ExampleFormDict)>> || 181 || '''Country''' || <<FormField(country,ExampleFormDict)>> || 182 || || <<FormField(finish,ExampleFormDict,label=Finish)>> || 183 184 }}}} 185 }}}}} 186 187 The form described above should look like this: 188 189 {{{{#!form fragment=exampleform3 190 191 || '''Name''' || <<FormField(name,ExampleFormDict)>> || 192 {{{#!form message=name-error 193 ||<-2> /!\ <<FormMessage(name-error)>> || 194 }}}|| '''Address''' || <<FormField(address,ExampleFormDict)>> || 195 || '''Country''' || <<FormField(country,ExampleFormDict)>> || 196 || || <<FormField(finish,ExampleFormDict,label=Finish)>> || 197 198 }}}} 199 200 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. 201 202 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. 203 204 ==== Repeating Message Sections ==== 205 206 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: 207 208 {{{{{ 209 {{{{#!form fragment=exampleform4 210 211 || '''Name''' || <<FormField(name,ExampleFormDict)>> || 212 {{{#!form message=name-error repeating 213 ||<-2> /!\ <<FormMessage(name-error)>> || 214 }}}|| '''Address''' || <<FormField(address,ExampleFormDict)>> || 215 || '''Country''' || <<FormField(country,ExampleFormDict)>> || 216 || || <<FormField(finish,ExampleFormDict,label=Finish)>> || 217 218 }}}} 219 }}}}} 220 221 The form described above should look like this: 222 223 {{{{#!form fragment=exampleform4 224 225 || '''Name''' || <<FormField(name,ExampleFormDict)>> || 226 {{{#!form message=name-error repeating 227 ||<-2> /!\ <<FormMessage(name-error)>> || 228 }}}|| '''Address''' || <<FormField(address,ExampleFormDict)>> || 229 || '''Country''' || <<FormField(country,ExampleFormDict)>> || 230 || || <<FormField(finish,ExampleFormDict,label=Finish)>> || 231 232 }}}} 233 234 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. 235 236 === Using the Form Message Macro === 237 238 {{{ 239 <<FormMessage(<field name>)>> 240 }}} 241 242 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. 243 244 === Storing Submitted Form Data === 245 246 The default form handler will store submitted form data in a `forms` subdirectory of the page on which a particular form appears, with each submitted form being encoded as a dictionary represented as a value encoded using Python syntax. 247 248 === Restricting Access to Forms === 249 250 By default, the usage of forms and the storage of form data is restricted according to the permissions granted for a given user for the page on which each form appears. This is summarised in the following table: 251 252 || '''Page Permission''' || '''Access to Form and Form Data''' || 253 || `admin` || May read form data || 254 || `delete` || May delete form data (since the entire page may also be deleted) || 255 || `read` || ''Permission grants no additional access'' || 256 || `write` || May submit forms and store form data || 257 258 Thus, on any page for which a user only has read access, any form will by default be visible but not usable for submitting data. 259 260 Since granting write access to a user will also permit them to change the form definition, as discussed below, it is possible to override these restrictions specifically for each form. This is done by specifying an `access` keyword which defines a different set of permissions that applies to a user when using the form. For example: 261 262 {{{{ 263 {{{#!form fragment=exampleform5 access='All:write' 264 ... 265 }}} 266 }}}} 267 268 Here, unprivileged users - those who may have been forbidden from changing the page and thus changing the form definition - may submit the form and store their submissions. The above table also summarises the permissions that may be specified along with their effects. 269 270 The `access` keyword supports the conventional [[HelpOnAccessControlLists|ACL]] syntax, and where spaces are present in the specified value, quotes should be placed around the value itself and not the `access` keyword and equals sign as well. 271 272 {{{#!wiki important 273 Note that in practice, any user with write access to a page can change the `access` criteria and grant themselves admin access to a form. Therefore, any use of forms where users are not generally to be trusted with the submitted data or the integrity of the form definition should be protected by a page ACL that denies write access to all but privileged users. The general users of the form can then be granted write access to it specifically. 274 }}} 275 276 === Extending the Default Form Handler === 277 278 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: 279 280 {{{#!python 281 from MoinForms import MoinFormHandlerAction 282 283 class ExampleFormAction(MoinFormHandlerAction): 284 285 "An example form handler." 286 287 def finished(self, fields, form): 288 289 """ 290 Given a valid form with the given 'fields' and the request's 291 'form' dictionary, perform additional validation. 292 """ 293 294 _ = self.request.getText 295 296 # This defines the errors for the activity-error message field. 297 298 errors = [] 299 300 if not fields.get("activity"): 301 errors.append(_("Need at least one activity.")) 302 303 # Insist on the "finish" button being pressed and no errors. 304 # If these conditions are not met, report the errors and act 305 # as if the form is unfinished. 306 307 if errors or not fields.has_key("finish"): 308 fields["activity-error"] = errors 309 self.unfinished(fields, form) 310 311 # Upon successful validation, a special field is used to store 312 # messages and the underlying action method is called. 313 314 else: 315 fields["form-complete"] = [_("The form was correct.")] 316 MoinFormHandlerAction.finished(self, fields, form) 317 318 def execute(pagename, request): 319 ExampleFormAction(pagename, request).processForm() 320 }}} 321 322 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. 323 324 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. 325 326 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.