1 #!/usr/bin/env python 2 3 """ 4 Resources for use with WebStack. 5 6 Copyright (C) 2005 Paul Boddie <paul@boddie.org.uk> 7 8 This library is free software; you can redistribute it and/or 9 modify it under the terms of the GNU Lesser General Public 10 License as published by the Free Software Foundation; either 11 version 2.1 of the License, or (at your option) any later version. 12 13 This library is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 Lesser General Public License for more details. 17 18 You should have received a copy of the GNU Lesser General Public 19 License along with this library; if not, write to the Free Software 20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 """ 22 23 import WebStack.Generic 24 import XSLForms.Fields 25 import XSLForms.Prepare 26 import XSLForms.Output 27 import XSLOutput 28 import os 29 30 class XSLFormsResource: 31 32 """ 33 A generic XSLForms resource for use with WebStack. 34 35 When overriding this class, define the following attributes appropriately: 36 37 * template_resources - a dictionary mapping output identifiers to 38 (template_filename, output_filename) tuples, 39 indicating the template and stylesheet filenames 40 to be employed 41 42 * in_page_resources - a dictionary mapping fragment identifiers to 43 (output_filename, node_identifier) tuples, 44 indicating the stylesheet filename to be 45 employed, along with the node identifier used in 46 the original template and output documents to 47 mark a region of those documents as the fragment 48 to be updated upon "in-page" requests 49 50 * resource_dir - the absolute path of the directory in which 51 stylesheet resources are to reside 52 53 All filenames shall be simple leafnames for files residing in the resource's 54 special resource directory 'resource_dir'. 55 56 The following attributes may also be specified: 57 58 * path_encoding - the assumed encoding of characters in request 59 paths 60 61 * encoding - the assumed encoding of characters in request 62 bodies 63 """ 64 65 path_encoding = "iso-8859-1" 66 encoding = "utf-8" 67 template_resources = {} 68 in_page_resources = {} 69 resource_dir = None 70 71 def get_fields_from_body(self, trans, encoding): 72 73 """ 74 From the given transaction 'trans' and using the stated text 'encoding' 75 get the field values from the request body and return a dictionary 76 mapping field names to lists of such values. 77 """ 78 79 text = trans.get_request_stream().read().decode(encoding) 80 parameters = {} 81 for text_line in text.split("\r\n"): 82 text_parts = text_line.split("=") 83 text_name, text_value = text_parts[0], "=".join(text_parts[1:]) 84 if not parameters.has_key(text_name): 85 parameters[text_name] = [] 86 # NOTE: Workaround from posted text. 87 if text_value[-1] == "\x00": 88 text_value = text_value[:-1] 89 parameters[text_name].append(text_value) 90 return parameters 91 92 def prepare_output(self, output_identifier): 93 94 """ 95 Prepare the output stylesheets using the given 'output_identifier' to 96 indicate which templates and stylesheets are to be employed in the 97 production of output from the resource. 98 99 The 'output_identifier' is used as a key to the 'template_resources' 100 dictionary attribute. 101 """ 102 103 template_filename, output_filename = self.template_resources[output_identifier] 104 output_path = os.path.join(self.resource_dir, output_filename) 105 template_path = os.path.join(self.resource_dir, template_filename) 106 XSLForms.Prepare.ensure_stylesheet(template_path, output_path) 107 return output_path 108 109 def prepare_fragment(self, output_identifier, fragment_identifier): 110 111 """ 112 Prepare the output stylesheets for the given 'output_identifier' and 113 'fragment_identifier', indicating which templates and stylesheets are to 114 be employed in the production of output from the resource. 115 116 The 'output_identifier' is used as a key to the 'template_resources' 117 dictionary attribute; the 'fragment_identifier' is used as a key to the 118 'in_page_resources' dictionary attribute. 119 """ 120 121 output_path = self.prepare_output(output_identifier) 122 fragment_filename, node_identifier = self.in_page_resources[fragment_identifier] 123 fragment_path = os.path.join(self.resource_dir, fragment_filename) 124 XSLForms.Prepare.ensure_stylesheet_fragment(output_path, fragment_path, node_identifier) 125 return fragment_path 126 127 def send_output(self, trans, stylesheet_filenames, document, stylesheet_parameters=None): 128 129 """ 130 Send the output from the resource to the user employing the transaction 131 'trans', stylesheets having the given 'stylesheet_filenames', the 132 'document' upon which the output will be based, and the optional 133 parameters as defined in the 'stylesheet_parameters' dictionary. 134 """ 135 136 proc = XSLOutput.Processor(stylesheet_filenames, parameters=stylesheet_parameters) 137 proc.send_output(trans.get_response_stream(), trans.get_response_stream_encoding(), 138 document) 139 140 def get_in_page_resource(self, trans): 141 142 """ 143 Return the in-page resource being referred to in the given transaction 144 'trans'. 145 """ 146 147 return trans.get_path_info().split("/")[-1] 148 149 def respond(self, trans): 150 151 """ 152 Respond to the request described by the given transaction 'trans'. 153 """ 154 155 # Only obtain field information according to the stated method. 156 157 method = trans.get_request_method() 158 in_page_resource = self.get_in_page_resource(trans) 159 160 # Handle typical request methods, processing request information. 161 162 if method == "GET": 163 164 # Get the fields from the request path (URL). 165 166 form = XSLForms.Fields.Form(encoding=self.path_encoding, values_are_lists=1) 167 parameters = trans.get_fields_from_path() 168 form.set_parameters(parameters) 169 170 elif method == "POST": 171 172 # Get the fields from the request body. 173 174 form = XSLForms.Fields.Form(encoding=self.encoding, values_are_lists=1) 175 176 # Handle requests for in-page updates. 177 178 if in_page_resource in self.in_page_resources.keys(): 179 parameters = self.get_fields_from_body(trans, self.encoding) 180 else: 181 parameters = trans.get_fields_from_body(self.encoding) 182 183 # Get the XML representation of the request. 184 185 form.set_parameters(parameters) 186 187 else: 188 189 # Initialise empty containers. 190 191 parameters = {} 192 documents = {} 193 194 # Call an overridden method with the processed request information. 195 196 self.respond_to_form(trans, form) 197 198 def respond_to_form(self, trans, form): 199 200 """ 201 Respond to the request described by the given transaction 'trans', using 202 the given 'form' object to conveniently retrieve field (request 203 parameter) information and structured form information (as DOM-style XML 204 documents). 205 """ 206 207 trans.set_response_code(500) 208 raise WebStack.Generic.EndOfResponse 209 210 # vim: tabstop=4 expandtab shiftwidth=4