With a completed template after the design, structure annotation and selector annotation, we may now write a Web resource which will expose our form as a Web application, allowing users to input information and to manipulate that information using their Web browser. Whilst XSLForms is just a normal Python package which can be used from many kinds of programs and environments, we shall concentrate on using the built-in WebStack support to build a WebStack application around our form template.
In the directory structure created
earlier, we now want to edit the __init__.py
file and
add code which will do most of the work of the form-editing
application. Here is the start of this code:
#!/usr/bin/env python
"A very simple example application."
import WebStack.Generic
import XSLForms.Resources.WebResources
import XSLForms.Utils
import os
# Resource classes.
class VerySimpleResource(XSLForms.Resources.WebResources.XSLFormsResource):
# To be continued.
The above import statements just include in our application everything that it is likely to need from WebStack, XSLForms and the standard library. Then, we define a class inheriting from a special XSLForms class which does some of the tedious Web application housekeeping that we would otherwise need to do ourselves.
We may expand the above class definition as follows:
class VerySimpleResource(XSLForms.Resources.WebResources.XSLFormsResource):
"A very simple resource providing a hierarchy of editable fields."
resource_dir = os.path.join(os.path.split(__file__)[0], "Resources")
encoding = "utf-8"
template_resources = {
"structure" : ("structure_template.xhtml", "structure_output.xsl")
}
def respond_to_form(self, trans, form):
"""
Respond to a request having the given transaction 'trans' and the given
'form' information.
"""
# To be continued.
The class is started with some attribute definitions:
resource_dir
attribute is used to locate
the template, stylesheet and other non-Python resources. We calculate
this attribute by taking the location of the Python package itself and
finding the Resources
subdirectory, just as described
in the directory structure document.encoding
attribute is not strictly
necessary, but it states which character encoding will be used in the
Web pages generated by the template, and UTF-8 is a safe choice in most
situations.template_resources
attribute is a
dictionary mapping a name onto details about our template and the
stylesheet that will actually produce the Web pages for each form being
edited.structure
(since
the root element of the form data is always structure
)Resources
directory: structure_template.xhtml
(if the suggested
name was used)structure_output.xsl
The class also has a method which resembles the typical respond
method of normal WebStack
resources: the respond_to_form
method is, in fact, a
special version of that method providing ready-to-use information about
the form (or forms) being edited.
We may now add to the above method definition by considering what the resource needs to do when being sent a request by a user of the application.
First of all, we need to inspect the form
object
to see if any form data is available. Since the data is provided
throughout XSLForms as XML documents, we call the get_documents
method on the form
object:
documents = form.get_documents()
As a result of this method, we should now have a dictionary mapping
form names to XML documents containing form data. However, it is not
guaranteed that the form data for our chosen form, structure
,
even exists since a user may be visiting the resource for the first
time.
Therefore, we test to see if the structure
document exists, creating a new document if it did not:
# Ensure the presence of a document.
if documents.has_key("structure"):
structure = documents["structure"]
else:
structure = form.new_instance("structure")
Now we should have a document containing the data for the form being edited, regardless of whether any form was filled out and submitted or whether we have created a new one for that purpose.
It may be the case that a user pressed a button in order to add or
remove items or subitems from the form. We must respond to such things
by examining the selector information to see which parts of the structure
document are affected:
# Add and remove elements according to the selectors found.
selectors = form.get_selectors()
The result of get_selectors
is a dictionary mapping
selector names to lists of nodes affected by each particular
selector. In the selector annotation
process, we defined selectors for the addition and removal of items and
subitems, and for convenience we pass the results for each selector to
a special function to perform the appropriate operation for us:
XSLForms.Utils.remove_elements(selectors.get("remove2"))
XSLForms.Utils.add_elements(selectors.get("add2"), "subitem")
XSLForms.Utils.remove_elements(selectors.get("remove"))
XSLForms.Utils.add_elements(selectors.get("add"), "item")
Finally, we are ready to present the edited form data. In typical WebStack fashion, we emit the content type of the final output along with our chosen character encoding:
# Start the response.
trans.set_content_type(WebStack.Generic.ContentType("application/xhtml+xml", self.encoding))
Then, we ensure that our template is ready to use by calling the
superclass's prepare_output
method with the name of
the form:
# Ensure that an output stylesheet exists.
trans_xsl = self.prepare_output("structure")
This prepares the stylesheet whose file is named in the template_resources
attribute entry, and this stylesheet is then sent to the
superclass's send_output
method as part of a list of
stylesheets (although we only use a single stylesheet in this example)
along with the form data itself:
# Complete the response.
self.send_output(trans, [trans_xsl], structure)
At this point, the user should receive their edited form and be able to make more modifications.
Some additional code is required to deploy this example application using WebStack. We have chosen to expose the above resource class using a special function which can be called from outside the package to obtain an instance of the class:
# Site map initialisation.
def get_site():
"Return a simple Web site resource."
return VerySimpleResource()
To actually deploy the application, we could choose one of many
server environments supported by WebStack. For clarity, we choose here
to write the following separate program which we can save under the
name VerySimpleApp.py
(for example):
#!/usr/bin/env python
from WebStack.Adapters.BaseHTTPRequestHandler import deploy
import VerySimple
# Get a simple Web site.
resource = VerySimple.get_site()
# Special magic incantation.
print "Serving..."
deploy(resource, handle_errors=0)
Ensuring that the example application's package (which we
called VerySimple
in the directory structure
document), WebStack, libxml2dom and XSLForms are available to the above
program, we may now run this program:
python VerySimpleApp.py
It should then be possible to visit the URL http://localhost:8080/
and edit the form in your Web browser.
We should now have an application which can be deployed and tested using the usual WebStack techniques. However, more advanced templates can be designed, and we shall consider multiple-choice fields in the next activity in the development process.