MoinForms

pages/HelpOnMoinForms

38:4765fa33c891
2013-07-16 Paul Boddie "Wiki" -> "wiki"
     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 finishing=finish    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 finishing=finish    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> finishing=<field>[,<field>...]    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. The `finishing` argument should list field names separated by commas that cause a validated form to be finished and thus stored (in the default action).    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 finishing=finish    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 finishing=finish   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 finishing=finish   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 finishing=finish   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 finishing=finish   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 finishing=finish   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 finishing=finish   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 validateFields(self, fields, structure):   288    289         """   290         Determine whether the submission is valid, first using the 'fields' and   291         'structure' in a basic validation of the form structure, then using   292         rules specific to this action.   293         """   294    295         valid = MoinFormHandlerAction.validateFields(self, fields, structure)   296    297         # This defines the errors for the activity-error message field and   298         # overrides any underlying validity decision.   299    300         if not fields.get("activity"):   301             fields["activity-error"] = [_("Need at least one activity.")]   302             return False   303    304         return valid   305    306     def finished(self, fields, form):   307    308         "Handle the apparently finished 'fields' and 'form'."   309    310         _ = self.request.getText   311    312         # Upon successful validation, a special field is used to store   313         # messages and the underlying action method is called.   314    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 `validateFields` 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 By overriding the `finished` method, additional messages can be added to the form as well as operations that might modify the default behaviour and prevent the underlying `finished` method from being called. It is not necessary to override this method in an application-specific action, but usually some form of feedback is desirable to indicate that the form was submitted successfully.   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.