XSLTools

Annotated docs/Web-resource.html

687:ae7bdbf61e32
2009-06-22 Paul Boddie Added some error handling to the questionnaire application.
paulb@615 1
<?xml version="1.0" encoding="iso-8859-1"?>
paulb@134 2
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
paulb@270 3
<html xmlns="http://www.w3.org/1999/xhtml"><head>
paulb@134 4
  <meta content="text/html;charset=ISO-8859-1" http-equiv="Content-Type" />
paulb@615 5
  <title>Creating Applications: Write a Web Resource</title>
paulb@270 6
  <link href="styles.css" rel="stylesheet" type="text/css" /></head>
paulb@134 7
<body>
paulb@134 8
<h1>Creating Applications: Write a Web Resource</h1>
paulb@134 9
<p>With a completed template after the <a href="design.html">design</a>,
paulb@270 10
<a href="structure.html">structure annotation</a> and <a href="selectors.html">selector annotation</a>, we may now write a Web
paulb@134 11
resource which will expose our form as a Web application, allowing
paulb@134 12
users to input information and to manipulate that information using
paulb@134 13
their Web browser. Whilst XSLForms is just a normal Python package
paulb@134 14
which can be used from many kinds of programs and environments, we
paulb@270 15
shall concentrate on using the built-in <a href="http://www.boddie.org.uk/python/WebStack.html">WebStack</a>
paulb@134 16
support to build a
paulb@134 17
WebStack application around our form template.</p>
paulb@134 18
<h2>XSLForms Meets WebStack </h2>
paulb@134 19
<p>In the <a href="directory.html">directory structure</a> created
paulb@615 20
earlier, we now want to edit the <code>__init__.py</code> file and
paulb@134 21
add code which will do most of the work of the form-editing
paulb@134 22
application. Here is the start of this code:</p>
paulb@353 23
<pre>#!/usr/bin/env python<br /><br />"A very simple example application."<br /><br />import WebStack.Generic<br />import XSLForms.Resources.WebResources<br />import XSLForms.Utils<br />import os<br /><br /># Resource classes.<br /><br />class VerySimpleResource(XSLForms.Resources.WebResources.XSLFormsResource):<br /><br />    # To be continued.</pre>
paulb@134 24
<p>The above import statements just include in our application
paulb@134 25
everything that it is likely to need from WebStack, XSLForms and the
paulb@134 26
standard library. Then, we define a class inheriting from a special
paulb@134 27
XSLForms class which does some of the tedious Web application
paulb@134 28
housekeeping that we would otherwise need to do ourselves.</p>
paulb@134 29
<p>We may expand the above class definition as follows:</p>
paulb@353 30
<pre>class VerySimpleResource(XSLForms.Resources.WebResources.XSLFormsResource):<br /><br />    "A very simple resource providing a hierarchy of editable fields."<br /><br />    resource_dir = os.path.join(os.path.split(__file__)[0], "Resources")<br />    encoding = "utf-8"<br />    template_resources = {<br />        "structure" : ("structure_template.xhtml", "structure_output.xsl")<br />        }<br /><br />    def respond_to_form(self, trans, form):<br /><br />        """<br />        Respond to a request having the given transaction 'trans' and the given<br />        'form' information.<br />        """<br /><br />        # To be continued.</pre>
paulb@134 31
<p>The class is started with some attribute definitions:</p>
paulb@134 32
<ul>
paulb@615 33
  <li>The <code>resource_dir</code> attribute is used to locate
paulb@134 34
the template, stylesheet and other non-Python resources. We calculate
paulb@134 35
this attribute by taking the location of the Python package itself and
paulb@615 36
finding the <code>Resources</code> subdirectory, just as described
paulb@134 37
in the <a href="directory.html">directory structure</a> document.</li>
paulb@615 38
  <li>The <code>encoding</code> attribute is not strictly
paulb@134 39
necessary, but it states which character encoding will be used in the
paulb@134 40
Web pages generated by the template, and UTF-8 is a safe choice in most
paulb@134 41
situations.</li>
paulb@615 42
  <li>The <code>template_resources</code> attribute is a
paulb@134 43
dictionary mapping a name onto details about our template and the
paulb@134 44
stylesheet that will actually produce the Web pages for each form being
paulb@134 45
edited.<br />
paulb@134 46
    <ol>
paulb@134 47
      <li>For the key, we choose a name that can easily be remembered
paulb@615 48
and associated with our template: <code>structure</code> (since
paulb@615 49
the root element of the form data is always <code>structure</code>)</li>
paulb@615 50
      <li>Then, we specify the filename of our template in the <code>Resources</code>
paulb@615 51
directory: <code>structure_template.xhtml</code> (if the suggested
paulb@134 52
name was used)</li>
paulb@134 53
      <li>Finally, we choose a filename for the stylesheet. Since this
paulb@134 54
is automatically produced from the template, we only need to choose a
paulb@134 55
name which is not already in use by another file, and for clarity a
paulb@615 56
name similar to that of the template is recommended: <code>structure_output.xsl</code></li>
paulb@270 57
    </ol></li></ul>
paulb@615 58
<p>The class also has a method which resembles the typical <code>respond</code>
paulb@270 59
method of normal <a href="http://www.boddie.org.uk/python/WebStack.html">WebStack</a>
paulb@615 60
resources: the <code>respond_to_form</code> method is, in fact, a
paulb@134 61
special version of that method providing ready-to-use information about
paulb@134 62
the form (or forms) being edited.</p>
paulb@134 63
<p>We may now add to the above method definition by considering what
paulb@134 64
the resource needs to do when being sent a request by a user of the
paulb@134 65
application.</p>
paulb@134 66
<h3>Defining the Method</h3>
paulb@615 67
<p>First of all, we need to inspect the <code>form</code> object
paulb@134 68
to see if any form data is available. Since the data is provided
paulb@615 69
throughout XSLForms as XML documents, we call the <code>get_documents</code>
paulb@615 70
method on the <code>form</code> object:</p>
paulb@134 71
<pre>        documents = form.get_documents()</pre>
paulb@134 72
<p>As a result of this method, we should now have a dictionary mapping
paulb@134 73
form names to XML documents containing form data. However, it is not
paulb@615 74
guaranteed that the form data for our chosen form, <code>structure</code>,
paulb@134 75
even exists since a user may be visiting the resource for the first
paulb@134 76
time.</p>
paulb@615 77
<p>Therefore, we test to see if the <code>structure</code>
paulb@134 78
document exists, creating a new document if it did not:</p>
paulb@134 79
<pre>        # Ensure the presence of a document.<br /><br />        if documents.has_key("structure"):<br />            structure = documents["structure"]<br />        else:<br />            structure = form.new_instance("structure")<br /></pre>
paulb@134 80
<p>Now we should have a document containing the data for the form being
paulb@134 81
edited, regardless of whether any form was filled out and submitted or
paulb@134 82
whether we have created a new one for that purpose.</p>
paulb@134 83
<p>It may be the case that a user pressed a button in order to add or
paulb@134 84
remove items or subitems from the form. We must respond to such things
paulb@615 85
by examining the selector information to see which parts of the <code>structure</code>
paulb@134 86
document are affected:</p>
paulb@134 87
<pre>        # Add and remove elements according to the selectors found.<br /><br />        selectors = form.get_selectors()<br /></pre>
paulb@134 88
<p>The result of <code>get_selectors</code> is a dictionary mapping
paulb@615 89
selector names to lists of nodes affected by each particular
paulb@134 90
selector. In the <a href="selectors.html">selector annotation</a>
paulb@134 91
process, we defined selectors for the addition and removal of items and
paulb@134 92
subitems, and for convenience we pass the results for each selector to
paulb@134 93
a special function to perform the appropriate operation for us:</p>
paulb@134 94
<pre>        XSLForms.Utils.remove_elements(selectors.get("remove2"))<br />        XSLForms.Utils.add_elements(selectors.get("add2"), "subitem")<br />        XSLForms.Utils.remove_elements(selectors.get("remove"))<br />        XSLForms.Utils.add_elements(selectors.get("add"), "item")<br /></pre>
paulb@134 95
<p>Finally, we are ready to present the edited form data. In typical
paulb@134 96
WebStack fashion, we emit the content type of the final output along
paulb@134 97
with our chosen character encoding:</p>
paulb@134 98
<pre>        # Start the response.<br /><br />        trans.set_content_type(WebStack.Generic.ContentType("application/xhtml+xml", self.encoding))<br /></pre>
paulb@134 99
<p>Then, we ensure that our template is ready to use by calling the
paulb@615 100
superclass's <code>prepare_output</code> method with the name of
paulb@134 101
the form:</p>
paulb@134 102
<pre>        # Ensure that an output stylesheet exists.<br /><br />        trans_xsl = self.prepare_output("structure")<br /></pre>
paulb@615 103
<p>This prepares the stylesheet whose file is named in the <code>template_resources</code>
paulb@134 104
attribute entry, and this stylesheet is then sent to the
paulb@615 105
superclass's <code>send_output</code> method as part of a list of
paulb@134 106
stylesheets (although we only use a single stylesheet in this example)
paulb@134 107
along with the form data itself:</p>
paulb@134 108
<pre>        # Complete the response.<br /><br />        self.send_output(trans, [trans_xsl], structure)</pre>
paulb@134 109
<p>At this point, the user should receive their edited form and be able
paulb@134 110
to make more modifications.</p>
paulb@135 111
<h3>Deployment Details</h3>
paulb@135 112
<p>Some additional code is required to deploy this example application
paulb@135 113
using WebStack. We have chosen to expose the above resource class using
paulb@135 114
a special function which can be called from outside the package to
paulb@135 115
obtain an instance of the class:</p>
paulb@135 116
<pre># Site map initialisation.<br /><br />def get_site():<br /><br />    "Return a simple Web site resource."<br /><br />    return VerySimpleResource()</pre>
paulb@135 117
<p>To actually deploy the application, we could choose one of many
paulb@135 118
server environments supported by WebStack. For clarity, we choose here
paulb@135 119
to write the following separate program which we can save under the
paulb@615 120
name <code>VerySimpleApp.py</code> (for example):</p>
paulb@135 121
<pre>#!/usr/bin/env python<br /><br />from WebStack.Adapters.BaseHTTPRequestHandler import deploy<br />import VerySimple<br /><br /># Get a simple Web site.<br /><br />resource = VerySimple.get_site()<br /><br /># Special magic incantation.<br /><br />print "Serving..."<br />deploy(resource, handle_errors=0)</pre>
paulb@135 122
<p>Ensuring that the example application's package (which we
paulb@615 123
called <code>VerySimple</code> in the directory structure
paulb@135 124
document), WebStack, libxml2dom and XSLForms are available to the above
paulb@135 125
program, we may now run this program:</p>
paulb@135 126
<pre>python VerySimpleApp.py</pre>
paulb@615 127
<p>It should then be possible to visit the URL <code>http://localhost:8080/</code>
paulb@135 128
and edit the form in your Web browser.<br />
paulb@135 129
</p>
paulb@134 130
<h2>Further Enhancements</h2>
paulb@134 131
<p>We should now have an application which can be deployed and tested
paulb@134 132
using the usual WebStack techniques. However, more advanced templates
paulb@134 133
can be designed, and we shall consider <a href="multiple.html">multiple-choice
paulb@615 134
fields</a> in the next activity in the development <a href="overview.html">process</a>.</p>
paulb@353 135
</body></html>