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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 21 """ 22 23 import WebStack.Generic 24 import XSLForms.Fields 25 import XSLForms.Prepare 26 import XSLForms.Output 27 import XSLForms.Resources.Common 28 from XSLTools import XSLOutput 29 import os 30 31 class XSLFormsResource(XSLForms.Resources.Common.CommonResource): 32 33 """ 34 A generic XSLForms resource for use with WebStack. 35 36 When overriding this class, define the following attributes appropriately: 37 38 * template_resources - a dictionary mapping output identifiers to 39 (template_filename, output_filename) tuples, 40 indicating the template and stylesheet filenames 41 to be employed 42 43 * in_page_resources - a dictionary mapping fragment identifiers to 44 (output_filename, node_identifier) tuples, 45 indicating the stylesheet filename to be 46 employed, along with the node identifier used in 47 the original template and output documents to 48 mark a region of those documents as the fragment 49 to be updated upon "in-page" requests 50 51 * init_resources - a dictionary mapping initialiser/input 52 identifiers to (template_filename, 53 input_filename) tuples, indicating the template 54 and initialiser/input stylesheet filenames to be 55 employed 56 57 * transform_resources - a dictionary mapping transform identifiers to 58 lists of stylesheet filenames for use with the 59 transformation methods 60 61 * document_resources - a dictionary mapping document identifiers to 62 single filenames for use as source documents or 63 as references with the transformation methods 64 65 * resource_dir - the absolute path of the directory in which 66 stylesheet resources are to reside 67 68 All filenames shall be simple leafnames for files residing in the resource's 69 special resource directory 'resource_dir'. 70 71 The following attributes may also be specified: 72 73 * path_encoding - the assumed encoding of characters in request 74 paths 75 76 * encoding - the assumed encoding of characters in request 77 bodies 78 """ 79 80 path_encoding = "utf-8" 81 encoding = "utf-8" 82 template_resources = {} 83 in_page_resources = {} 84 init_resources = {} 85 transform_resources = {} 86 87 def clean_parameters(self, parameters): 88 89 """ 90 Workaround stray zero value characters from Konqueror in XMLHttpRequest 91 communications. 92 """ 93 94 for name, values in parameters.items(): 95 new_values = [] 96 for value in values: 97 if value.endswith("\x00"): 98 new_values.append(value[:-1]) 99 else: 100 new_values.append(value) 101 parameters[name] = new_values 102 103 def prepare_output(self, output_identifier): 104 105 """ 106 Prepare the output stylesheets using the given 'output_identifier' to 107 indicate which templates and stylesheets are to be employed in the 108 production of output from the resource. 109 110 The 'output_identifier' is used as a key to the 'template_resources' 111 dictionary attribute. 112 113 Return the full path to the output stylesheet for use with 'send_output' 114 or 'get_result'. 115 """ 116 117 template_filename, output_filename = self.template_resources[output_identifier] 118 output_path = os.path.abspath(os.path.join(self.resource_dir, output_filename)) 119 template_path = os.path.abspath(os.path.join(self.resource_dir, template_filename)) 120 XSLForms.Prepare.ensure_stylesheet(template_path, output_path) 121 return output_path 122 123 def prepare_fragment(self, output_identifier, fragment_identifier): 124 125 """ 126 Prepare the output stylesheets for the given 'output_identifier' and 127 'fragment_identifier', indicating which templates and stylesheets are to 128 be employed in the production of output from the resource. 129 130 The 'output_identifier' is used as a key to the 'template_resources' 131 dictionary attribute; the 'fragment_identifier' is used as a key to the 132 'in_page_resources' dictionary attribute. 133 134 Return the full path to the output stylesheet for use with 'send_output' 135 or 'get_result'. 136 """ 137 138 template_filename, output_filename = self.template_resources[output_identifier] 139 template_path = os.path.abspath(os.path.join(self.resource_dir, template_filename)) 140 fragment_filename, node_identifier = self.in_page_resources[fragment_identifier] 141 fragment_path = os.path.abspath(os.path.join(self.resource_dir, fragment_filename)) 142 XSLForms.Prepare.ensure_stylesheet_fragment(template_path, fragment_path, node_identifier) 143 return fragment_path 144 145 def prepare_parameters(self, parameters): 146 147 """ 148 Prepare the stylesheet parameters from the given request 'parameters'. 149 This is most useful when preparing fragments for in-page update output. 150 """ 151 152 element_path = parameters.get("element-path", [""])[0] 153 if element_path: 154 return {"element-path" : element_path} 155 else: 156 return {} 157 158 def send_output(self, trans, stylesheet_filenames, document, stylesheet_parameters=None, 159 stylesheet_expressions=None, references=None): 160 161 """ 162 Send the output from the resource to the user employing the transaction 163 'trans', stylesheets having the given 'stylesheet_filenames', the 164 'document' upon which the output will be based, the optional parameters 165 as defined in the 'stylesheet_parameters' dictionary, the optional 166 expressions are defined in the 'stylesheet_expressions' dictionary, and 167 the optional 'references' to external documents. 168 """ 169 170 # Sanity check for the filenames list. 171 172 if isinstance(stylesheet_filenames, str) or isinstance(stylesheet_filenames, unicode): 173 raise ValueError, stylesheet_filenames 174 175 proc = XSLOutput.Processor(stylesheet_filenames, parameters=stylesheet_parameters, 176 expressions=stylesheet_expressions, references=references) 177 proc.send_output(trans.get_response_stream(), trans.get_response_stream_encoding(), 178 document) 179 180 def get_result(self, stylesheet_filenames, document, stylesheet_parameters=None, 181 stylesheet_expressions=None, references=None): 182 183 """ 184 Get the result of applying a transformation using stylesheets with the 185 given 'stylesheet_filenames', the 'document' upon which the result will 186 be based, the optional parameters as defined in the 187 'stylesheet_parameters' dictionary, the optional parameters as defined 188 in the 'stylesheet_parameters' dictionaryand the optional 'references' 189 to external documents. 190 """ 191 192 # Sanity check for the filenames list. 193 194 if isinstance(stylesheet_filenames, str) or isinstance(stylesheet_filenames, unicode): 195 raise ValueError, stylesheet_filenames 196 197 proc = XSLOutput.Processor(stylesheet_filenames, parameters=stylesheet_parameters, 198 expressions=stylesheet_expressions, references=references) 199 return proc.get_result(document) 200 201 def prepare_initialiser(self, input_identifier, init_enumerations=1): 202 203 """ 204 Prepare an initialiser/input transformation using the given 205 'input_identifier'. The optional 'init_enumerations' (defaulting to 206 true) may be used to indicate whether enumerations are to be initialised 207 from external documents. 208 209 Return the full path to the input stylesheet for use with 'send_output' 210 or 'get_result'. 211 """ 212 213 template_filename, input_filename = self.init_resources[input_identifier] 214 input_path = os.path.abspath(os.path.join(self.resource_dir, input_filename)) 215 template_path = os.path.abspath(os.path.join(self.resource_dir, template_filename)) 216 XSLForms.Prepare.ensure_input_stylesheet(template_path, input_path, init_enumerations) 217 return input_path 218 219 def prepare_transform(self, transform_identifier): 220 221 """ 222 Prepare a transformation using the given 'transform_identifier'. 223 224 Return a list of full paths to the output stylesheets for use with 225 'send_output' or 'get_result'. 226 """ 227 228 filenames = self.transform_resources[transform_identifier] 229 230 # Sanity check for the filenames list. 231 232 if isinstance(filenames, str) or isinstance(filenames, unicode): 233 raise ValueError, filenames 234 235 paths = [] 236 for filename in filenames: 237 paths.append(os.path.abspath(os.path.join(self.resource_dir, filename))) 238 return paths 239 240 def get_in_page_resource(self, trans): 241 242 """ 243 Return the in-page resource being referred to in the given transaction 244 'trans'. 245 """ 246 247 return trans.get_path_info(self.path_encoding).split("/")[-1] 248 249 def respond(self, trans): 250 251 """ 252 Respond to the request described by the given transaction 'trans'. 253 """ 254 255 # Only obtain field information according to the stated method. 256 257 method = trans.get_request_method() 258 in_page_resource = self.get_in_page_resource(trans) 259 260 # Handle typical request methods, processing request information. 261 262 if method == "GET": 263 264 # Get the fields from the request path (URL). 265 # NOTE: The encoding is actually redundant since WebStack produces 266 # NOTE: Unicode values. 267 268 form = XSLForms.Fields.Form(encoding=self.path_encoding, values_are_lists=1) 269 parameters = trans.get_fields_from_path() 270 form.set_parameters(parameters) 271 272 elif method == "POST": 273 274 # Get the fields from the request body. 275 # NOTE: The encoding is actually redundant since WebStack produces 276 # NOTE: Unicode values. 277 278 form = XSLForms.Fields.Form(encoding=self.encoding, values_are_lists=1) 279 parameters = trans.get_fields_from_body(self.encoding) 280 281 # NOTE: Konqueror workaround. 282 self.clean_parameters(parameters) 283 284 form.set_parameters(parameters) 285 286 else: 287 288 # Initialise empty container. 289 290 form = XSLForms.Fields.Form(encoding=self.encoding, values_are_lists=1) 291 292 # Call an overridden method with the processed request information. 293 294 self.respond_to_form(trans, form) 295 296 def respond_to_form(self, trans, form): 297 298 """ 299 Respond to the request described by the given transaction 'trans', using 300 the given 'form' object to conveniently retrieve field (request 301 parameter) information and structured form information (as DOM-style XML 302 documents). 303 """ 304 305 trans.set_response_code(500) 306 trans.set_content_type(WebStack.Generic.ContentType("text/plain")) 307 out = trans.get_response_stream() 308 out.write("Resource not fully defined to respond.") 309 raise WebStack.Generic.EndOfResponse 310 311 # vim: tabstop=4 expandtab shiftwidth=4