1.1 --- a/docs/in-page-updates.html Fri Jul 22 18:27:00 2005 +0000
1.2 +++ b/docs/in-page-updates.html Fri Jul 22 20:32:13 2005 +0000
1.3 @@ -128,26 +128,41 @@
1.4 added or removed, we add an event attribute on the form field
1.5 responsible for displaying the type values:</p>
1.6 <pre> <p><br /> Item type:<br /> <select template:element="type" name="{template:list-attribute('type-enum', 'value')}" multiple="multiple"<br /> <span
1.7 - style="font-weight: bold;">onchange="requestUpdate('{$application-url}comments', '{template:list-attribute('type-enum', 'value')}',<br /> '{template:other-elements(../options)}',<br /> '{template:child-attribute('value', template:child-element('comment', 1, template:other-elements(../options)))}',<br /> '/structure/item/options')"</span>><br /> <option template:element="type-enum" template:expr="@value-is-set" template:expr-attr="selected"<br /> template:value="@value" value="{@value}" /><br /> </select><br /> </p></pre>
1.8 + style="font-weight: bold;">onchange="requestUpdate(</span><br
1.9 + style="font-weight: bold;" /><span style="font-weight: bold;"> '{$application-url}comments',</span><br
1.10 + style="font-weight: bold;" /><span style="font-weight: bold;"> '{template:list-attribute('type-enum', 'value')}',</span><br
1.11 + style="font-weight: bold;" /><span style="font-weight: bold;"> '{template:other-elements(../options)}',</span><br
1.12 + style="font-weight: bold;" /><span style="font-weight: bold;"> '{template:child-attribute('value', template:child-element('comment', 1, template:other-elements(../options)))}',</span><br
1.13 + style="font-weight: bold;" /><span style="font-weight: bold;"> '/structure/item/options')"</span>><br /> <option template:element="type-enum" template:expr="@value-is-set" template:expr-attr="selected"<br /> template:value="@value" value="{@value}" /><br /> </select><br /> </p></pre>
1.14 <p>This complicated string calls a special update request JavaScript
1.15 function which triggers the in-page update, and it specifies the
1.16 following things:</p>
1.17 -<ol>
1.18 - <li>The URL which will serve the in-page update requested by this
1.19 +<dl>
1.20 + <dt><span style="font-weight: bold;">'{$application-url}comments'</span></dt>
1.21 + <dd>The URL which will serve the in-page update requested by this
1.22 field. We use a special variable called <code>application-url</code>
1.23 which will need to be provided to the template when generating the Web
1.24 -page.</li>
1.25 - <li>The fields which are going to be used in the processing of the
1.26 +page. This will produce something like the following:<br />
1.27 + <pre>http://localhost:8080/comments</pre>
1.28 +So the request for an in-page update will be sent to this
1.29 +generated URL.</dd>
1.30 + <dt><span style="font-weight: bold;">'{template:list-attribute('type-enum',
1.31 +'value')}'</span></dt>
1.32 + <dd>The fields which are going to be used in the processing of the
1.33 update. Since the presence of the comment field depends on a
1.34 specific <code>type</code> element and its <code>type-enum</code>
1.35 elements' <code>value</code> attributes, we specify the names of
1.36 -the fields which yield these values.</li>
1.37 - <li>The region which is to be updated. Here, we recall that we
1.38 +the fields which yield these values.</dd>
1.39 + <dt><span style="font-weight: bold;">'{template:other-elements(../options)}'</span></dt>
1.40 + <dd>The region which is to be updated. Here, we recall that we
1.41 defined the region using a special reference to the <code>options</code>
1.42 element holding <code>comment</code> element. Thus, we use a
1.43 special value which also refers to that element from the context of
1.44 -the <code>type</code> element.</li>
1.45 - <li>Even when the types are changed, it may be the case that an
1.46 +the <code>type</code> element.</dd>
1.47 + <dt><span style="font-weight: bold;">'{template:child-attribute('value',
1.48 +template:child-element('comment', 1,
1.49 +template:other-elements(../options)))}'</span></dt>
1.50 + <dd>Even when the types are changed, it may be the case that an
1.51 exposed comment field does not disappear (for example, if we already
1.52 have "Personal" selected but select "Important" in addition), and so we
1.53 need to provide the details of the field which holds the value of the
1.54 @@ -159,10 +174,128 @@
1.55 being after an update, but not be referenced here in this parameter;
1.56 therefore, we need to make up the final part of the reference using the
1.57 special <code>template:child-attribute</code>
1.58 -and <code>template:child-element</code> functions.</li>
1.59 - <li>Finally, we need to provide some context to the application to
1.60 +and <code>template:child-element</code> functions.</dd>
1.61 + <dt><span style="font-weight: bold;">'/structure/item/options'</span></dt>
1.62 + <dd>Finally, we need to provide some context to the application to
1.63 tell it something about where in the complete form data structure the
1.64 -updated information resides. </li>
1.65 -</ol>
1.66 +updated information resides.</dd>
1.67 +</dl>
1.68 +<p>Of course, all this is pretty complicated and at some point in the
1.69 +future, a simplified way of triggering in-page updates will be
1.70 +introduced.</p>
1.71 +<h3>Updating the Web Application</h3>
1.72 +<p>To support both normal requests for Web pages and the special
1.73 +in-page requests, we must make some modifications to the Web
1.74 +application. First, we must introduce some infrastructure to handle the
1.75 +requests for the JavaScript files separately from the requests for
1.76 +pages from our application. Some standard WebStack resources can be
1.77 +used to help with this, and we add some imports at the top of our
1.78 +source file:</p>
1.79 +<pre>#!/usr/bin/env python<br /><br />"A very simple example application."<br /><br />import WebStack.Generic<br />import XSLForms.Resources<br />import XSLForms.Utils<br />import os<br /><br /><span
1.80 + style="font-weight: bold;"># Site map imports.</span><br
1.81 + style="font-weight: bold;" /><br style="font-weight: bold;" /><span
1.82 + style="font-weight: bold;">from WebStack.Resources.ResourceMap import MapResource</span><br
1.83 + style="font-weight: bold;" /><span style="font-weight: bold;">from WebStack.Resources.Static import DirectoryResource</span></pre>
1.84 +<p>Then, we define the resource class as <a href="Web-resource.html">before</a>,
1.85 +but with an additional attribute:</p>
1.86 +<pre># Resource classes.<br /><br />class VerySimpleResource(XSLForms.Resources.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_multivalue_template.xhtml", "structure_output.xsl")<br /> }<br /> transform_resources = {<br /> "types" : ["structure_multivalue_types.xsl", "structure_comments.xsl"]<br /> }<br /> document_resources = {<br /> "types" : "structure_types.xml"<br /> }<br /><span
1.87 + style="font-weight: bold;"> in_page_resources = {</span><br
1.88 + style="font-weight: bold;" /><span style="font-weight: bold;"> "comments" : ("structure_output_comments.xsl", "comment-node")</span><br
1.89 + style="font-weight: bold;" /><span style="font-weight: bold;"> }</span></pre>
1.90 +<p>This new attribute provides information about the in-page request to
1.91 +retrieve comment regions of the Web form, and it consists of the
1.92 +stylesheet filename that will be generated to produce the page
1.93 +fragments for such comment regions, along with the region marker that
1.94 +we defined above.</p>
1.95 +<p>The <code>respond_to_form</code> method now also includes some
1.96 +additional code:</p>
1.97 +<pre> 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 /> <span
1.98 + style="font-weight: bold;">in_page_resource = self.get_in_page_resource(trans)</span><br
1.99 + style="font-weight: bold;" /><span style="font-weight: bold;"> parameters = form.get_parameters()</span><br /> documents = form.get_documents()<br /></pre>
1.100 +<p>Here, we find out whether an in-page update is requested, along with
1.101 +the raw parameters of the request, some of which will be used later on
1.102 +in the method.</p>
1.103 +<p>The discovery of the form data structure and the addition and
1.104 +removal of elements happens as before, as does the merging of type
1.105 +values and the comment field, if applicable:</p>
1.106 +<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 /><br /> # Add and remove elements according to the selectors found.<br /><br /> selectors = form.get_selectors()<br /> 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 /><br /> # Transform, adding enumerations/ranges.<br /><br /> types_xsl_list = self.prepare_transform("types")<br /> types_xml = self.prepare_document("types")<br /> structure = self.get_result(types_xsl_list, structure, references={"types" : types_xml})</pre>
1.107 +<p>The significant changes begin when presenting the result of the
1.108 +request processing:</p>
1.109 +<pre> # Start the response.<br /><br /> trans.set_content_type(WebStack.Generic.ContentType("application/xhtml+xml", self.encoding))<br /><br /><span
1.110 + style="font-weight: bold;"> # Define the stylesheet parameters.</span><br
1.111 + style="font-weight: bold;" /><br style="font-weight: bold;" /><span
1.112 + style="font-weight: bold;"> stylesheet_parameters = {}</span><br
1.113 + style="font-weight: bold;" /><br style="font-weight: bold;" /><span
1.114 + style="font-weight: bold;"> # Ensure that an output stylesheet exists.</span><br
1.115 + style="font-weight: bold;" /><br style="font-weight: bold;" /><span
1.116 + style="font-weight: bold;"> if in_page_resource in self.in_page_resources.keys():</span><br
1.117 + style="font-weight: bold;" /><span style="font-weight: bold;"> trans_xsl = self.prepare_fragment("structure", in_page_resource)</span><br
1.118 + style="font-weight: bold;" /><span style="font-weight: bold;"> element_path = parameters.get("element-path", [""])[0]</span><br
1.119 + style="font-weight: bold;" /><span style="font-weight: bold;"> stylesheet_parameters["element-path"] = element_path</span><br
1.120 + style="font-weight: bold;" /><span style="font-weight: bold;"> else:</span><br
1.121 + style="font-weight: bold;" /><span style="font-weight: bold;"> trans_xsl = self.prepare_output("structure")</span></pre>
1.122 +<p>Instead of just obtaining a stylesheet for the <code>structure</code>
1.123 +document, we instead check to see if an in-page update is being
1.124 +requested and, if so, prepare the stylesheet representing the fragment
1.125 +of the Web form to be presented. Additionally, we obtain a special <code>element-path</code>
1.126 +parameter directly from the request parameters; this parameter is added
1.127 +to a collection of parameters that will be used to control the
1.128 +stylesheet when making the final Web page output.</p>
1.129 +<p>Another parameter that will be used in stylesheet processing is
1.130 +the <code>application-url</code> parameter mentioned above. We
1.131 +obtain the address and port of the Web server environment and add the
1.132 +result as a simple URL to the <code>application-url</code>
1.133 +stylesheet parameter. Finally, we send the output to the user but
1.134 +employing the additional stylesheet parameters to configure the result:</p>
1.135 +<pre><span style="font-weight: bold;"> # Add information essential for in-page requests.</span><br
1.136 + style="font-weight: bold;" /><br style="font-weight: bold;" /><span
1.137 + style="font-weight: bold;"> if trans.get_server_port() == "80":</span><br
1.138 + style="font-weight: bold;" /><span style="font-weight: bold;"> stylesheet_parameters["application-url"] = \</span><br
1.139 + style="font-weight: bold;" /><span style="font-weight: bold;"> "http://%s%s" % (trans.get_server_name(), trans.get_path_without_query())</span><br
1.140 + style="font-weight: bold;" /><span style="font-weight: bold;"> else:</span><br
1.141 + style="font-weight: bold;" /><span style="font-weight: bold;"> stylesheet_parameters["application-url"] = \</span><br
1.142 + style="font-weight: bold;" /><span style="font-weight: bold;"> "http://%s:%s%s" % (trans.get_server_name(), trans.get_server_port(), trans.get_path_without_query())</span><br /><br /> # Complete the response.<br /><br /> self.send_output(trans, [trans_xsl], structure<span
1.143 + style="font-weight: bold;">, stylesheet_parameters</span>)</pre>
1.144 +<p>In order to introduce the infrastructure mentioned above which
1.145 +separates requests for Web pages from requests for JavaScript files, we
1.146 +need to provide a more sophisticated implementation of the <code>get_site</code>
1.147 +function:</p>
1.148 +<pre><span style="font-weight: bold;"># Site map initialisation.</span><br
1.149 + style="font-weight: bold;" /><br style="font-weight: bold;" /><span
1.150 + style="font-weight: bold;">def get_site():</span><br
1.151 + style="font-weight: bold;" /><br style="font-weight: bold;" /><span
1.152 + style="font-weight: bold;"> "Return a simple Web site resource."</span><br
1.153 + style="font-weight: bold;" /><br style="font-weight: bold;" /><span
1.154 + style="font-weight: bold;"> # Get the main resource and the directory used by the application.</span><br
1.155 + style="font-weight: bold;" /><br style="font-weight: bold;" /><span
1.156 + style="font-weight: bold;"> very_simple_resource = VerySimpleResource()</span><br
1.157 + style="font-weight: bold;" /><span style="font-weight: bold;"> directory = very_simple_resource.resource_dir</span><br
1.158 + style="font-weight: bold;" /><br style="font-weight: bold;" /><span
1.159 + style="font-weight: bold;"> # Make a simple Web site.</span><br
1.160 + style="font-weight: bold;" /><br style="font-weight: bold;" /><span
1.161 + style="font-weight: bold;"> resource = MapResource({</span><br
1.162 + style="font-weight: bold;" /><span style="font-weight: bold;"> # Static resources:</span><br
1.163 + style="font-weight: bold;" /><span style="font-weight: bold;"> "scripts" : DirectoryResource(os.path.join(directory, "scripts"), {"js" : "text/javascript"}),</span><br
1.164 + style="font-weight: bold;" /><span style="font-weight: bold;"> # Main page and in-page resources:</span><br
1.165 + style="font-weight: bold;" /><span style="font-weight: bold;"> None : very_simple_resource</span><br
1.166 + style="font-weight: bold;" /><span style="font-weight: bold;"> })</span><br
1.167 + style="font-weight: bold;" /><br style="font-weight: bold;" /><span
1.168 + style="font-weight: bold;"> return resource</span></pre>
1.169 +<p>What this does is to create a resource for the application, as
1.170 +before, but then to place the resource into a special WebStack resource
1.171 +which examines the path or URL on the incoming requests and directs
1.172 +such requests according to the following scheme:</p>
1.173 +<ul>
1.174 + <li>If the request mentions something under <code>scripts</code>
1.175 +in its URL, we employ the WebStack <code>DirectoryResource</code> to
1.176 +send the file from the <code>scripts</code> subdirectory of the
1.177 +application's <code>Resources</code> directory.</li>
1.178 + <li>Otherwise, we pass the request on to our application resource in
1.179 +order to produce a Web page for the user.</li>
1.180 +</ul>
1.181 +<p>Thus, when the user's browser asks for a script file, it gets a
1.182 +script file; otherwise it gets a Web page showing either all of the
1.183 +form (if a normal request is received), or a part of the form (if an
1.184 +in-page request is received).</p>
1.185 </body>
1.186 </html>