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 from XSLTools 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 * init_resources - a dictionary mapping initialiser/input 51 identifiers to (template_filename, 52 input_filename) tuples, indicating the template 53 and initialiser/input stylesheet filenames to be 54 employed 55 56 * transform_resources - a dictionary mapping transform identifiers to 57 lists of stylesheet filenames for use with the 58 transformation methods 59 60 * document_resources - a dictionary mapping document identifiers to 61 single filenames for use as source documents or 62 as references with the transformation methods 63 64 * resource_dir - the absolute path of the directory in which 65 stylesheet resources are to reside 66 67 All filenames shall be simple leafnames for files residing in the resource's 68 special resource directory 'resource_dir'. 69 70 The following attributes may also be specified: 71 72 * path_encoding - the assumed encoding of characters in request 73 paths 74 75 * encoding - the assumed encoding of characters in request 76 bodies 77 """ 78 79 path_encoding = "utf-8" 80 encoding = "utf-8" 81 template_resources = {} 82 in_page_resources = {} 83 transform_resources = {} 84 document_resources = {} 85 resource_dir = None 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 output_path = self.prepare_output(output_identifier) 139 fragment_filename, node_identifier = self.in_page_resources[fragment_identifier] 140 fragment_path = os.path.abspath(os.path.join(self.resource_dir, fragment_filename)) 141 XSLForms.Prepare.ensure_stylesheet_fragment(output_path, fragment_path, node_identifier) 142 return fragment_path 143 144 def send_output(self, trans, stylesheet_filenames, document, stylesheet_parameters=None, 145 stylesheet_expressions=None, references=None): 146 147 """ 148 Send the output from the resource to the user employing the transaction 149 'trans', stylesheets having the given 'stylesheet_filenames', the 150 'document' upon which the output will be based, the optional parameters 151 as defined in the 'stylesheet_parameters' dictionary, the optional 152 expressions are defined in the 'stylesheet_expressions' dictionary, and 153 the optional 'references' to external documents. 154 """ 155 156 # Sanity check for the filenames list. 157 158 if isinstance(stylesheet_filenames, str) or isinstance(stylesheet_filenames, unicode): 159 raise ValueError, stylesheet_filenames 160 161 proc = XSLOutput.Processor(stylesheet_filenames, parameters=stylesheet_parameters, 162 expressions=stylesheet_expressions, references=references) 163 proc.send_output(trans.get_response_stream(), trans.get_response_stream_encoding(), 164 document) 165 166 def get_result(self, stylesheet_filenames, document, stylesheet_parameters=None, 167 stylesheet_expressions=None, references=None): 168 169 """ 170 Get the result of applying a transformation using stylesheets with the 171 given 'stylesheet_filenames', the 'document' upon which the result will 172 be based, the optional parameters as defined in the 173 'stylesheet_parameters' dictionary, the optional parameters as defined 174 in the 'stylesheet_parameters' dictionaryand the optional 'references' 175 to external documents. 176 """ 177 178 # Sanity check for the filenames list. 179 180 if isinstance(stylesheet_filenames, str) or isinstance(stylesheet_filenames, unicode): 181 raise ValueError, stylesheet_filenames 182 183 proc = XSLOutput.Processor(stylesheet_filenames, parameters=stylesheet_parameters, 184 expressions=stylesheet_expressions, references=references) 185 return proc.get_result(document) 186 187 def prepare_initialiser(self, input_identifier): 188 189 """ 190 Prepare an initialiser/input transformation using the given 191 'input_identifier'. 192 193 Return the full path to the input stylesheet for use with 'send_output' 194 or 'get_result'. 195 """ 196 197 template_filename, input_filename = self.init_resources[input_identifier] 198 input_path = os.path.abspath(os.path.join(self.resource_dir, input_filename)) 199 template_path = os.path.abspath(os.path.join(self.resource_dir, template_filename)) 200 XSLForms.Prepare.ensure_input_stylesheet(template_path, input_path) 201 return input_path 202 203 def prepare_transform(self, transform_identifier): 204 205 """ 206 Prepare a transformation using the given 'transform_identifier'. 207 208 Return a list of full paths to the output stylesheets for use with 209 'send_output' or 'get_result'. 210 """ 211 212 filenames = self.transform_resources[transform_identifier] 213 paths = [] 214 for filename in filenames: 215 paths.append(os.path.abspath(os.path.join(self.resource_dir, filename))) 216 return paths 217 218 def prepare_document(self, document_identifier): 219 220 """ 221 Prepare a document using the given 'document_identifier'. 222 223 Return the full path of the document for use either as the source 224 document or as a reference with 'send_output' or 'get_result'. 225 """ 226 227 filename = self.document_resources[document_identifier] 228 return os.path.abspath(os.path.join(self.resource_dir, filename)) 229 230 def get_in_page_resource(self, trans): 231 232 """ 233 Return the in-page resource being referred to in the given transaction 234 'trans'. 235 """ 236 237 return trans.get_path_info(self.path_encoding).split("/")[-1] 238 239 def respond(self, trans): 240 241 """ 242 Respond to the request described by the given transaction 'trans'. 243 """ 244 245 # Only obtain field information according to the stated method. 246 247 method = trans.get_request_method() 248 in_page_resource = self.get_in_page_resource(trans) 249 250 # Handle typical request methods, processing request information. 251 252 if method == "GET": 253 254 # Get the fields from the request path (URL). 255 # NOTE: The encoding is actually redundant since WebStack produces 256 # NOTE: Unicode values. 257 258 form = XSLForms.Fields.Form(encoding=self.path_encoding, values_are_lists=1) 259 parameters = trans.get_fields_from_path() 260 form.set_parameters(parameters) 261 262 elif method == "POST": 263 264 # Get the fields from the request body. 265 # NOTE: The encoding is actually redundant since WebStack produces 266 # NOTE: Unicode values. 267 268 form = XSLForms.Fields.Form(encoding=self.encoding, values_are_lists=1) 269 parameters = trans.get_fields_from_body(self.encoding) 270 271 # NOTE: Konqueror workaround. 272 self.clean_parameters(parameters) 273 274 form.set_parameters(parameters) 275 276 else: 277 278 # Initialise empty containers. 279 280 parameters = {} 281 documents = {} 282 283 # Call an overridden method with the processed request information. 284 285 self.respond_to_form(trans, form) 286 287 def respond_to_form(self, trans, form): 288 289 """ 290 Respond to the request described by the given transaction 'trans', using 291 the given 'form' object to conveniently retrieve field (request 292 parameter) information and structured form information (as DOM-style XML 293 documents). 294 """ 295 296 trans.set_response_code(500) 297 raise WebStack.Generic.EndOfResponse 298 299 # vim: tabstop=4 expandtab shiftwidth=4