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> |