1.1 --- a/MoinForms.py Sat Feb 09 19:50:37 2013 +0100
1.2 +++ b/MoinForms.py Sat Feb 09 19:55:38 2013 +0100
1.3 @@ -29,9 +29,16 @@
1.4 self.pagename = pagename
1.5 self.request = request
1.6 self.access_handler = None
1.7 + self.attributes = None
1.8
1.9 - def getAccessHandler(self, attributes):
1.10 - return FormAccess(self.pagename, self.request, attributes)
1.11 + def getAccessHandler(self):
1.12 +
1.13 + """
1.14 + Return an access handler for the form whose attributes have been
1.15 + obtained and stored in this instance.
1.16 + """
1.17 +
1.18 + return FormAccess(self.pagename, self.request, self.attributes)
1.19
1.20 def processForm(self):
1.21
1.22 @@ -47,35 +54,54 @@
1.23 form = get_form(self.request)
1.24 fields = getFields(form, remove=True)
1.25
1.26 - # Modify and validate the form.
1.27 -
1.28 - self.modifyFields(fields)
1.29 -
1.30 - # Get the form definition.
1.31 + # Detect any request to load data.
1.32
1.33 - attributes, text = self.getFormForFragment(fields)
1.34 - structure = getFormStructure(text, self.request)
1.35 - self.access_handler = self.getAccessHandler(attributes)
1.36 + if fields.has_key("load"):
1.37 + try:
1.38 + number = int(fields["load"][0])
1.39 + except ValueError:
1.40 + fields = {}
1.41 + else:
1.42 + self.attributes, text = self.getFormForFragment(fields)
1.43 + self.access_handler = self.getAccessHandler()
1.44 + fields = self.loadFields(number)
1.45
1.46 - # Check the permissions on the form.
1.47 + self.unfinished(fields, form)
1.48 +
1.49 + # Otherwise, process any supplied data.
1.50 +
1.51 + else:
1.52 + # Modify and validate the form.
1.53 +
1.54 + self.modifyFields(fields)
1.55 +
1.56 + # Get the form definition.
1.57
1.58 - if not self.checkPermissions("write"):
1.59 - self.request.theme.add_msg(_("You do not appear to have access to this form."), "error")
1.60 - do_show(self.pagename, self.request)
1.61 - return
1.62 + self.attributes, text = self.getFormForFragment(fields)
1.63 + self.access_handler = self.getAccessHandler()
1.64 + structure = getFormStructure(text, self.request)
1.65 +
1.66 + # Check the permissions on the form.
1.67
1.68 - # Without any form definition, the page is probably the wrong one.
1.69 + if not self.checkPermissions("write"):
1.70 + self.request.theme.add_msg(_("You do not appear to have access to this form."), "error")
1.71 + do_show(self.pagename, self.request)
1.72 + return
1.73 +
1.74 + # Without any form definition, the page is probably the wrong one.
1.75
1.76 - if not structure:
1.77 - self.request.theme.add_msg(_("This page does not provide a form."), "error")
1.78 - do_show(self.pagename, self.request)
1.79 - return
1.80 + if not structure:
1.81 + self.request.theme.add_msg(_("This page does not provide a form."), "error")
1.82 + do_show(self.pagename, self.request)
1.83 + return
1.84
1.85 - # With a form definition, attempt to validate the fields.
1.86 + # With a form definition, attempt to validate the fields.
1.87
1.88 - if self.validateFields(fields, structure):
1.89 - self.finished(fields, form)
1.90 - else:
1.91 + if self.validateFields(fields, structure):
1.92 + if self.shouldFinish(fields):
1.93 + self.finished(fields, form)
1.94 + return
1.95 +
1.96 self.unfinished(fields, form)
1.97
1.98 def finished(self, fields, form):
1.99 @@ -94,6 +120,23 @@
1.100 self.serialiseFields(fields, form)
1.101 do_show(self.pagename, self.request)
1.102
1.103 + def shouldFinish(self, fields):
1.104 +
1.105 + """
1.106 + Subject to the attributes stored for the form in this instance, return
1.107 + whether any field referenced by the "finishing" attribute is present
1.108 + and thus indicate whether the form handling should finish.
1.109 + """
1.110 +
1.111 + finishing = self.attributes.has_key("finishing") and self.attributes["finishing"].split(",")
1.112 +
1.113 + if finishing:
1.114 + for name in finishing:
1.115 + if fields.has_key(name):
1.116 + return True
1.117 +
1.118 + return False
1.119 +
1.120 def getFormForFragment(self, fields):
1.121
1.122 "Return the attributes and text of the form being handled."
1.123 @@ -249,10 +292,7 @@
1.124
1.125 def storeFields(self, fields):
1.126
1.127 - """
1.128 - Store the given 'fields' as a Python object representation, subject to
1.129 - the given 'attributes'.
1.130 - """
1.131 + "Store the given 'fields' as a Python object representation."
1.132
1.133 store = FormStore(self.access_handler)
1.134 store.append(repr(fields))
1.135 @@ -264,18 +304,6 @@
1.136 store = FormStore(self.access_handler)
1.137 return loadFields(store, number)
1.138
1.139 -def loadFieldsForRequest(number, attrs, request):
1.140 -
1.141 - """
1.142 - Load the fields corresponding to the given record 'number', using the form
1.143 - 'attrs' to control access to the form data, and using the 'request' for
1.144 - access and retrieval purposes.
1.145 - """
1.146 -
1.147 - access_handler = FormAccess(request.page.page_name, request, attrs)
1.148 - store = FormStore(access_handler)
1.149 - return loadFields(store, number)
1.150 -
1.151 def loadFields(store, number):
1.152
1.153 """
1.154 @@ -460,7 +488,7 @@
1.155
1.156 # Common formatting functions.
1.157
1.158 -def getFormOutput(text, fields, path=None, fragment=None, repeating=None, index=None):
1.159 +def getFormOutput(text, fields, form_fragment=None, path=None, fragment=None, repeating=None, index=None):
1.160
1.161 """
1.162 Combine regions found in the given 'text' and then return them as a single
1.163 @@ -472,16 +500,22 @@
1.164 The given 'fields' are used to populate fields provided in forms and to
1.165 control whether sections are populated or not.
1.166
1.167 + The optional 'form_fragment' is used to indicate the form to which the
1.168 + fields belong.
1.169 +
1.170 The optional 'path' is used to adjust form fields to refer to the correct
1.171 part of the form hierarchy.
1.172
1.173 - The optional 'fragment' is used to indicate the form to which the fields
1.174 - belong.
1.175 + The optional 'fragment' is used to indicate the form being output. If this
1.176 + value is different to 'form_fragment', the structure of the form should not
1.177 + be influenced by the 'fields'.
1.178
1.179 The optional 'repeating' and 'index' is used to refer to individual values
1.180 of a designated field.
1.181 """
1.182
1.183 + this_form = fragment and form_fragment == fragment or not fragment and not form_fragment
1.184 +
1.185 output = []
1.186 section = fields
1.187
1.188 @@ -501,9 +535,14 @@
1.189 message_name = attributes.get("message")
1.190 absent_message_name = attributes.get("not-message")
1.191
1.192 + # Ignore sections not related to the supplied field data.
1.193 +
1.194 + if not this_form:
1.195 + pass
1.196 +
1.197 # Sections are groups of fields in their own namespace.
1.198
1.199 - if section_name and section.has_key(section_name):
1.200 + elif section_name and section.has_key(section_name):
1.201
1.202 # Iterate over the section contents ignoring the given indexes.
1.203
1.204 @@ -512,7 +551,7 @@
1.205
1.206 # Get the output for the section.
1.207
1.208 - output.append(getFormOutput(body, element,
1.209 + output.append(getFormOutput(body, element, form_fragment,
1.210 path and ("%s/%s" % (path, element_ref)) or element_ref, fragment))
1.211
1.212 # Message regions are conditional on a particular field and
1.213 @@ -522,21 +561,21 @@
1.214
1.215 if attributes.get("repeating"):
1.216 for index in range(0, len(section[message_name])):
1.217 - output.append(getFormOutput(body, section, path, fragment, message_name, index))
1.218 + output.append(getFormOutput(body, section, form_fragment, path, fragment, message_name, index))
1.219 else:
1.220 - output.append(getFormOutput(body, section, path, fragment))
1.221 + output.append(getFormOutput(body, section, form_fragment, path, fragment))
1.222
1.223 # Not-message regions are conditional on a particular field being
1.224 # absent. They reference the current namespace.
1.225
1.226 elif absent_message_name and not section.has_key(absent_message_name):
1.227 - output.append(getFormOutput(body, section, path, fragment))
1.228 + output.append(getFormOutput(body, section, form_fragment, path, fragment))
1.229
1.230 # Inspect and include other regions.
1.231
1.232 else:
1.233 output.append(header)
1.234 - output.append(getFormOutput(body, section, path, fragment, repeating, index))
1.235 + output.append(getFormOutput(body, section, form_fragment, path, fragment, repeating, index))
1.236 output.append(close)
1.237
1.238 return "".join(output)
1.239 @@ -837,7 +876,9 @@
1.240 write = write or request.write
1.241 page = request.page
1.242
1.243 - fields = getFields(get_form(request))
1.244 + form = get_form(request)
1.245 + form_fragment = form.get("fragment", [None])[0]
1.246 + fields = getFields(form)
1.247
1.248 # Prepare the query string for the form action URL.
1.249
1.250 @@ -861,7 +902,7 @@
1.251 # Obtain page text for the form, incorporating subregions and applicable
1.252 # sections.
1.253
1.254 - output = getFormOutput(text, fields, fragment=fragment)
1.255 + output = getFormOutput(text, fields, form_fragment=form_fragment, fragment=fragment)
1.256 write(formatText(output, request, fmt, inhibit_p=False))
1.257
1.258 write(fmt.rawHTML('</form>'))
2.1 --- a/pages/HelpOnMoinForms Sat Feb 09 19:50:37 2013 +0100
2.2 +++ b/pages/HelpOnMoinForms Sat Feb 09 19:55:38 2013 +0100
2.3 @@ -14,7 +14,7 @@
2.4 To embed a form within a page, a special region must be declared as in the following example:
2.5
2.6 {{{{
2.7 -{{{#!form fragment=exampleform
2.8 +{{{#!form fragment=exampleform finishing=finish
2.9
2.10 || '''Name''' || <<FormField(name,ExampleFormDict)>> ||
2.11 || '''Address''' || <<FormField(address,ExampleFormDict)>> ||
2.12 @@ -34,7 +34,7 @@
2.13
2.14 The above form definition produces the following form:
2.15
2.16 -{{{#!form fragment=exampleform
2.17 +{{{#!form fragment=exampleform finishing=finish
2.18
2.19 || '''Name''' || <<FormField(name,ExampleFormDict)>> ||
2.20 || '''Address''' || <<FormField(address,ExampleFormDict)>> ||
2.21 @@ -52,17 +52,17 @@
2.22 === Defining Form Regions ===
2.23
2.24 {{{{
2.25 -{{{#!form fragment=<fragment> action=<action>
2.26 +{{{#!form fragment=<fragment> action=<action> finishing=<field>[,<field>...]
2.27 ...
2.28 }}}
2.29 }}}}
2.30
2.31 -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.
2.32 +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).
2.33
2.34 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.
2.35
2.36 {{{{{
2.37 -{{{{#!form fragment=exampleform action=ExampleAction
2.38 +{{{{#!form fragment=exampleform action=ExampleAction finishing=finish
2.39
2.40 Fill out the form below:
2.41
2.42 @@ -127,7 +127,7 @@
2.43 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:
2.44
2.45 {{{{{
2.46 -{{{{#!form fragment=exampleform2
2.47 +{{{{#!form fragment=exampleform2 finishing=finish
2.48
2.49 || '''Name''' || <<FormField(name,ExampleFormDict)>> ||
2.50 || '''Address''' || <<FormField(address,ExampleFormDict)>> ||
2.51 @@ -144,7 +144,7 @@
2.52
2.53 This produces the following form:
2.54
2.55 -{{{{#!form fragment=exampleform2
2.56 +{{{{#!form fragment=exampleform2 finishing=finish
2.57
2.58 || '''Name''' || <<FormField(name,ExampleFormDict)>> ||
2.59 || '''Address''' || <<FormField(address,ExampleFormDict)>> ||
2.60 @@ -172,7 +172,7 @@
2.61 Message sections are introduced in forms like normal sections but using the `message` argument in the form region's declaration. For example:
2.62
2.63 {{{{{
2.64 -{{{{#!form fragment=exampleform3
2.65 +{{{{#!form fragment=exampleform3 finishing=finish
2.66
2.67 || '''Name''' || <<FormField(name,ExampleFormDict)>> ||
2.68 {{{#!form message=name-error
2.69 @@ -186,7 +186,7 @@
2.70
2.71 The form described above should look like this:
2.72
2.73 -{{{{#!form fragment=exampleform3
2.74 +{{{{#!form fragment=exampleform3 finishing=finish
2.75
2.76 || '''Name''' || <<FormField(name,ExampleFormDict)>> ||
2.77 {{{#!form message=name-error
2.78 @@ -206,7 +206,7 @@
2.79 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:
2.80
2.81 {{{{{
2.82 -{{{{#!form fragment=exampleform4
2.83 +{{{{#!form fragment=exampleform4 finishing=finish
2.84
2.85 || '''Name''' || <<FormField(name,ExampleFormDict)>> ||
2.86 {{{#!form message=name-error repeating
2.87 @@ -220,7 +220,7 @@
2.88
2.89 The form described above should look like this:
2.90
2.91 -{{{{#!form fragment=exampleform4
2.92 +{{{{#!form fragment=exampleform4 finishing=finish
2.93
2.94 || '''Name''' || <<FormField(name,ExampleFormDict)>> ||
2.95 {{{#!form message=name-error repeating