# HG changeset patch # User paulb # Date 1128818850 0 # Node ID 7f6292f1609fc00560ed342d9a5b93b2a784819a # Parent 507ff08438a98f56193b9b6aafae8c0810fb11fd [project @ 2005-10-09 00:47:30 by paulb] Added a guide to writing Web resources. Added some links to the overview. Changed the in-page update documentation to use prepare_parameters. diff -r 507ff08438a9 -r 7f6292f1609f docs/XSLForms-resource.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/XSLForms-resource.html Sun Oct 09 00:47:30 2005 +0000 @@ -0,0 +1,193 @@ + + + + + Using the XSLFormsResource API + + +

Using the XSLFormsResource API

+

The XSLForms toolkit provides a special WebStack resource class (described in the "Creating Applications: Write a Web Resource" +document),  and +from this class, XSLFormsResource, +you can derive your own application-specific resources and use the +class's API to obtain, manipulate and present form data. Although the +supplied API documentation provides details of the class's API, specifically in the XSLForms.Resources module, this document attempts to explain how the API is used in practice.

Resource Structure

The structure of a Web resource derived from XSLFormsResource should look like this:

class MyResource(XSLForms.Resources.XSLFormsResource):

[Resource definitions]

def respond_to_form(self, trans, form):
[Examine the form data, see if the user has added or removed anything.]
[Perform additional processing and initialise the form data.]
[Produce some kind of response to show the user the updated form data.]

Since XSLFormsResource builds on WebStack's resource mechanisms, we do have the transaction object, trans, available. However, most of the information we need to access and manipulate is generally available through the form object.

Defining Resources

Classes derived from XSLFormsResource +support the concept of resources which are used to produce output, +support processing and to provide access to useful information. At the +class level it is essential to define at least some of these resources +in order to make a working application.

The Resource Directory

Since +XSLForms relies on template files residing in the filesystem, along +with other files, we need to define where such files can be found (as +described in the "Creating Applications: Create +a Directory" document). Consequently, it is the convention that all resource classes define such information as follows:

class ConfiguratorResource(XSLForms.Resources.XSLFormsResource):

resource_dir = os.path.join(os.path.split(__file__)[0], "Resources")

All +filenames, defined in the various resource sections (as described +below) must be stated without leading path information - in other +words, as "leafnames" rather than "pathnames". Thus, an example of +an incorrect filename would be /home/paulb/templates/my_template.xhtml, whereas an example of a correct filename would be just my_template.xhtml when correcting the incorrect example.

Character Encoding

It +is also the convention to define the character encoding of the output +produced by an application and the way ambiguous or +insufficiently-specified input should be interpreted; this is done as +follows:

    # Continuing from above...

encoding = "utf-8"

Template Resources

The +main purpose of XSLForms is to produce Web page output containing a +visual representation of a form. Therefore, we need to define templates +(as described in the "Creating Applications: Design a Template" document) to express the representation of each kind of form, along with any intermediate files that may be produced. A special class-level template_resources dictionary is used to hold such definitions.

To +define a template resource, we first choose a name (which need not have +any special significance); we then associate with that name a +template filename and an output filename. Finally, we make an +entry for the name and its associated details in the special +class-level template_resources dictionary as follows:

    # Continuing from above...

template_resources = {
"configuration" : ("config_template.xhtml", "config_output.xsl"),
# More entries go here...
}

The purpose +of the output filename is to define where the intermediate +output-producing stylesheet is to be written, since the template itself +is not actually used to produce output, but knowing where the +intermediate stylesheet can be found is sometimes useful when debugging +templates and is thus defined explicitly to avoid confusion.

Initialisation Resources

The +XSLForms toolkit also support the initialisation of form data +documents. This document initialisation is useful when preparing a +document for output since some parts of a template may not be produced +unless certain elements are present in the form data document. For +example, a template may contain something like this:

<div template:element="hard-disks">
<input template:selector-field="add-hard-disk,hard-disk" type="submit" name="..." value="Add hard disk"/>
<p template:element="hard-disk">
...
</p>
</div>

In the above example, if no hard-disks element exists, the selector will not be displayed and there will be no way of adding hard-disk elements. With document initialisation, certain measures can be taken to ensure that the hard-disks element is added before output is generated.

At the class level, the init_resources +dictionary is used to hold definitions mapping initialiser names (which +need not have any special significance) to the initialiser details: the +filename of the template which defines the structure of the form data, +and an intermediate filename similar to the output filename described +in the context of template resources above. An example of this is as +follows:

    # Continuing from above...

init_resources = {
"configuration" : ("config_template.xhtml", "config_input.xsl"),
# More entries go here...
}

Note +that initialiser and template resources may (and should) share the same +template filename. As with the output filename for template resources, +the input filename provides firm information about the location of the +stylesheet which actually performs the initialisation process.

Document Resources

Since +it is the XSLForms convention to access files using a simple name, any +other document resources should be defined in the document_resources +dictionary at the class level. Such document resources may be used in +the initialisation process or in other transformations (as described +below), and are defined as entries mapping names to filenames such as +in the following example:

    # Continuing from above...

document_resources = {
"accessories" : "config_accessories.xml",
"base-system" : "config_base_system.xml",
# More entries go here...
}

There is no particular limitation on the types of files which can be referenced in the document_resources +dictionary, nor any insistence in the XSLForms toolkit to define such +files as resources - the dictionary is merely a convenience for +accessing files in the resources directory.

Transform Resources

It is sometimes the case that initialisation of a document +is not sufficient and that additional processing needs to be done. +Whilst various techniques exist for the processing of XML-based +information, since XSLForms is part of a wider toolkit based on XSL +transformations, it seems reasonable to provide certain facilities for +the usage of such transformations. Thus, stylesheet processing +pipelines may be defined at the class level in the transform_resources dictionary.

Entries in the transform_resources +dictionary map simple names (which need not have any special +significance) to collections of stylesheet filenames as in the +following example:

    # Continuing from above...

transform_resources = {
"filter" : ["filter.xsl"],
# More entries go here...
}

Where +more than one stylesheet filename is specified, the stylesheets are +applied from first to last in the transformation. Additional +information, such as stylesheet parameters and referenced documents, +are mentioned when the transformation is acquired and invoked, as +described below.

In-Page Update Resources

In certain +applications, a technique referred to within XSLForms as "in-page +updates" is employed to provide updates of details within a Web page +without refreshing the entire page itself (and this is described in the +"Creating Applications: In-Page Updates" +document). When such updates are requested, applications have to +identify the kind of update requested and then to select the correct +part of the Web page to generate as output. Consequently, the +application has to have some kind of record of the different kinds of +updates and the corresponding parts of the whole page template to use, +and this information is recorded in the class-level in_page_resources dictionary.

The form of an entry in the in_page_resources +dictionary is that of a mapping from a name identifying the kind of +update to the details of the part of the template to be employed in +producing the final output for the update: an intermediate filename +(distinct from that associated with the whole page template), and a +node identifier used to isolate the pertinent part of the whole page +template. Here is an example of an in-page resource definition:

    # Continuing from above...

in_page_resources = {
"cpu" : ("config_output_cpu.xsl", "cpu-node"),
# More entries go here...
}

Update Names

It +is important to note that, unlike other resources, the name identifying +the kind of update is significant: consider an application available at +the following location:

http://localhost/configurator/

An in-page update called cpu would be accessed through the following location:

http://localhost/configurator/cpu

Thus, +the availability of such an update "service" depends on the proper +configuration of the Web application to let such updates be handled by +the resource.

Update Nodes

The node identifier mentioned +in an in-page update resource definition must be a valid node +identifier in the whole page template document. Thus, if we wished to +use the identifier in the above example together with config_template.xhtml, we would have to ensure that the identifier appeared as a value of an id +node in that template document. Note that the choice of template +document is not defined here, but is instead made when handling an +in-page update request.

Examining the Form Data

The form data is available through the form object which exposes the XSLForms.Fields.Form API. The most interesting operations are as follows:

Obtain the Form Data Documents

Since +XSLForms is an XML-based toolkit, the form data is available as XML +documents which can be accessed and manipulated using a DOM-style API. +Upon receiving submitted form data, XSLForms converts the data to such +documents and then makes it available through the form +object by associating certain document names with the actual documents +themselves in a dictionary; this dictionary can be obtained as follows:

documents = form.get_documents()

Imagine that a template document has been written with items as the root (or topmost) element; such a document will consequently be made available via the form object's documents dictionary using the name items, and can be accessed as follows:

items = documents["items"]

However, +it may be the case that no form data has been submitted. To avoid +causing an exception, we should really test for the presence of such a +document first:

if documents.has_key("items"):
items = documents["items"]

Since +it is likely that we will want to work with such a document regardless +of whether one existed before - we must after all prepare such a +document in the first place in order to show it to the user and have it +submitted back to us - we really want to create it if it does not exist:

if documents.has_key("items"):
items = documents["items"]
else:
items = form.new_document("items")

The resulting items object is a genuine DOM-style document containing the form data.

Obtain the Form Data Selectors

As described in the "Creating Applications: Add Selectors" +document, XSLForms templates may define selectors - special form fields +which select parts of the form data documents and make those parts +available to applications; such selector information can be obtained as +follows:

selectors = form.get_selectors()

If a selector was defined with the name remove, then any selected elements that are associated with this selector may be obtained as follows:

removed_elements = selectors.get("remove") # which will return None if no such selector was defined

Since +the collection contains DOM-style elements, various XML libraries and +tools may be used to manipulate the data. However, XSLForms also +provides convenience functions to add and remove elements.

Obtaining Other Parameters

Sometimes, +there is a need to obtain the "raw" request parameters submitted by the +Web client or browser which sent the form data in to the application. +Such parameters could be obtained using the trans object, but it is also possible to use the following approach:

parameters = form.get_parameters()
some_parameter = parameters.get("something") # which returns None if no such parameter exists; a list otherwise
another_parameter = parameters.get("another", [""])[0] # which will always return a string, empty if no such parameter was found

Performing Additional Processing

To take advantage of the defined transform_resources, we can call a method on the resource itself to prepare such resources:

filter_stylesheets = self.prepare_transform("filter")

Then, +with the result of this call (a list of stylesheet filenames), we can +then perform a transformation on a document, producing a new document +from the results:

configuration_document = self.get_result(filter_stylesheets, configuration_document)

This new document is different from the document supplied to the get_result +method. It should therefore be noted that any references to elements in +the old document will not affect the new document; thus selectors +previously obtained from the form object will not refer to elements in the new document. However, by setting the new document in the form object, new selectors may be obtained referring to elements in the new document:

form.set_document("configuration", configuration_document)
selectors = form.get_selectors()

Care +must be taken doing this, however, since the selectors may now not +refer to valid elements - the transformation may have removed or moved +elements previously referred to by the selectors.

The get_result +method also supports stylesheet parameters, document references and +stylesheet expressions; these are described in the "Additional +Stylesheet Parameters" section below.

Document Initialisation

The initialisation of a document, using information defined in the init_resources +attribute, is similar to the transformation of a document as described +above. First, we obtain a reference to an initialisation stylesheet:

init_stylesheet = self.prepare_initialiser("configuration")

Note +that only a single stylesheet is returned. With the result of the call, +we then perform a transformation similar to the above activity, +although we have to supply the returned stylesheet in a list to be +compatible with the get_result method:

configuration_document = self.get_result([init_stylesheet], configuration_document)

In practice, the above call will probably not suffice: if multiple-choice fields +are used in the template, there will be a need to initialise such +elements using references to other documents containing the values of +such fields; for example:

configuration_document = self.get_result([init_stylesheet], configuration_document,
references={
"cpu" : self.prepare_document("cpu")
})

The +use of document references and other stylesheet parameter information +is described in the "Additional Stylesheet Parameters" section below.

Preparing Responses

The process of preparing a response involves three main steps:

  1. Setting a content type.
  2. Defining an output stylesheet and parameter information.
  3. Sending the output to the user.

Setting a Content Type

In +the examples supplied with XSLTools, the content type is generally +defined as that of XHTML, meaning that the resulting output should be +accessible to most modern Web browsers. When writing resources based +on XSLFormsResource, we can just use the WebStack API to set the content type:

trans.set_content_type(WebStack.Generic.ContentType("application/xhtml+xml", self.encoding))

Note that the encoding attribute is used here to make the character encoding clear to the user's Web browser or client.

Defining an Output Stylesheet

In most cases, the output stylesheet can be chosen by selecting a template name and invoking a method on the resource:

output_stylesheet = self.prepare_output("configuration")

However, +where in-page updates are handled, we may need to check to see if we +should be sending a fragment of the whole page instead. First, we must +check to see if an in-page update is being requested:

in_page_resource = self.get_in_page_resource(trans)

The +result of calling the above method should be a string identifying an +"in-page resource" - that is, a kind of in-page update related to part +of the whole page - if such a "resource" is actually being +requested. We can thus check to see if such a request is taking place:

if in_page_resource in self.in_page_resources.keys():
[Handle the in-page update request]

If +so, instead of getting a stylesheet which produces output for the whole +page, we get a "fragment" which produces output only for the part of +the page being updated:

    # Continued from above...

output_stylesheet = self.prepare_fragment("configuration", in_page_resource)
stylesheet_parameters = self.prepare_parameters(parameters) # from form.get_parameters()

An +additional step when handling in-page updates is the usage of +stylesheet parameters to send in some required information about the +location of the update in the page. The prepare_parameters +method on the resource is used to discover this information and return +it as a dictionary to be passed to the final output generation activity.

Sending the Output to the User

Given +an output stylesheet reference and possibly some parameters, the output +is sent to the user with a single call to a method on the resource +object:

self.send_output(trans, [output_stylesheet], configuration_document, stylesheet_parameters)

This method should, using the encoding +attribute on the resource class, ensure that the generated output is +correct and consistent for the user's Web browser or client.

Additional Stylesheet Parameters

In addition to a collection of stylesheets and a document to process, the get_result and send_output methods can accept a number of possible sources of information:

Generally, +stylesheet parameters are used to configure the output in some way, +whilst document references and stylesheet expressions typically offer a +means of accessing additional information that is to be merged in or +included in the processed document. The most common need to introduce +additional information arises from the use of multiple-choice elements; +consider the list of values given in the "Creating Applications: Adding Multiple-Choice Fields and Values" document:

<?xml version="1.0"?>
<type>
<type-enum value="(Not selected)"/>
<type-enum value="Important"/>
<type-enum value="Not important"/>
<type-enum value="Personal"/>
</type>

Such +information needs to reside somewhere and then be referenced in order +to be included in the processing operation being performed, which would +either be a document initialisation or just a normal transformation.

Document References

To refer to an externally-defined information, we define the document resource as described above:

    # At class attribute level...

document_resources = {
"types" : "types.xml",
# Other documents...
}

Then, we access the resource, getting a reference to the document:

types_xml = self.prepare_document("types")

To bring this document into the processing operation, we add an entry to a dictionary passed as the references parameter to get_result (or send_output, if appropriate). In the above example, the information is referenced as follows in the template document:

    <select template:multiple-choice-field="type,value" name="...">
<option template:multiple-choice-value="type-enum,value,selected" value="..." />
</select>

Therefore, we take the element name, type, from the field and use it to refer to the external document in the dictionary of references:

structure = self.get_result([structure_xsl], structure, references={"type" : types_xml})

This should result in the stylesheet incorporating the information from the types document into the transformation.

Stylesheet Expressions

In +more advanced cases, referencing external documents does not provide +enough flexibility when introducing additional information into a +transformation. An alternative approach involves copying data into the +document to be processed and then to supply references to the data in +its new location within the document.

\ No newline at end of file diff -r 507ff08438a9 -r 7f6292f1609f docs/in-page-updates.html --- a/docs/in-page-updates.html Sun Oct 09 00:46:35 2005 +0000 +++ b/docs/in-page-updates.html Sun Oct 09 00:47:30 2005 +0000 @@ -4,7 +4,6 @@ Creating Applications: In-Page Updates -

Creating Applications: In-Page Updates

One fashionable avenue in Web application design has been that of @@ -191,13 +190,11 @@

        # Ensure the presence of a document.

if documents.has_key("structure"):
structure = documents["structure"]
else:
structure = form.new_instance("structure")

# Add and remove elements according to the selectors found.

selectors = form.get_selectors()
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")

# Initialise the document, adding enumerations/ranges.

structure_xsl = self.prepare_initialiser("structure")
types_xml = self.prepare_document("types")
structure = self.get_result([structure_xsl], structure, references={"type" : types_xml})

# Add the comments.

comments_xsl_list = self.prepare_transform("comments")
structure = self.get_result(comments_xsl_list, structure)

The significant changes begin when presenting the result of the request processing:

-
        # Start the response.

trans.set_content_type(WebStack.Generic.ContentType("application/xhtml+xml", self.encoding))

# Define the stylesheet parameters.

stylesheet_parameters = {}

# Ensure that an output stylesheet exists.

if in_page_resource in self.in_page_resources.keys():
trans_xsl = self.prepare_fragment("structure", in_page_resource)
element_path = parameters.get("element-path", [""])[0]
stylesheet_parameters["element-path"] = element_path
else:
trans_xsl = self.prepare_output("structure")
+
        # Start the response.

trans.set_content_type(WebStack.Generic.ContentType("application/xhtml+xml", self.encoding))

# Ensure that an output stylesheet exists.

if in_page_resource in self.in_page_resources.keys():
trans_xsl = self.prepare_fragment("structure", in_page_resource)
stylesheet_parameters = self.prepare_parameters(parameters)
else:
trans_xsl = self.prepare_output("structure")
stylesheet_parameters = {}

Instead of just obtaining a stylesheet for the structure document, we instead check to see if an in-page update is being requested and, if so, prepare the stylesheet representing the fragment -of the Web form to be presented. Additionally, we obtain a special element-path -parameter directly from the request parameters; this parameter is added -to a collection of parameters that will be used to control the +of the Web form to be presented. Additionally, we obtain special stylesheet parameters using the raw request parameters; this introduces information that will be used to control the stylesheet when making the final Web page output.

Finally, we send the output to the user but employing the additional stylesheet parameters to configure the result:

diff -r 507ff08438a9 -r 7f6292f1609f docs/overview.html --- a/docs/overview.html Sun Oct 09 00:46:35 2005 +0000 +++ b/docs/overview.html Sun Oct 09 00:47:30 2005 +0000 @@ -4,7 +4,6 @@ Creating Applications: An Overview -

Creating Applications: An Overview

The following steps briefly describe how to make a new application:
@@ -21,5 +20,5 @@

  • Adding multivalued fields
  • Recommendations and advice
  • Adding in-page updates
  • -

    Some other resources:

    +

    A topic-by-topic guide to XSLTools:

    Some other resources:

    \ No newline at end of file