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 * transform_resources - a dictionary mapping transform identifiers to 51 lists of stylesheet filenames for use with the 52 transformation methods 53 54 * document_resources - a dictionary mapping document identifiers to 55 single filenames for use as source documents or 56 as references with the transformation methods 57 58 * resource_dir - the absolute path of the directory in which 59 stylesheet resources are to reside 60 61 All filenames shall be simple leafnames for files residing in the resource's 62 special resource directory 'resource_dir'. 63 64 The following attributes may also be specified: 65 66 * path_encoding - the assumed encoding of characters in request 67 paths 68 69 * encoding - the assumed encoding of characters in request 70 bodies 71 """ 72 73 path_encoding = "iso-8859-1" 74 encoding = "utf-8" 75 template_resources = {} 76 in_page_resources = {} 77 transform_resources = {} 78 document_resources = {} 79 resource_dir = None 80 81 def get_fields_from_body(self, trans, encoding): 82 83 """ 84 From the given transaction 'trans' and using the stated text 'encoding' 85 get the field values from the request body and return a dictionary 86 mapping field names to lists of such values. 87 """ 88 89 text = trans.get_request_stream().read().decode(encoding) 90 parameters = {} 91 for text_line in text.split("\r\n"): 92 text_parts = text_line.split("=") 93 text_name, text_value = text_parts[0], "=".join(text_parts[1:]) 94 if not parameters.has_key(text_name): 95 parameters[text_name] = [] 96 # NOTE: Workaround from posted text. 97 if text_value[-1] == "\x00": 98 text_value = text_value[:-1] 99 parameters[text_name].append(text_value) 100 return parameters 101 102 def prepare_output(self, output_identifier): 103 104 """ 105 Prepare the output stylesheets using the given 'output_identifier' to 106 indicate which templates and stylesheets are to be employed in the 107 production of output from the resource. 108 109 The 'output_identifier' is used as a key to the 'template_resources' 110 dictionary attribute. 111 112 Return the full path to the output stylesheet for use with 'send_output' 113 or 'get_result'. 114 """ 115 116 template_filename, output_filename = self.template_resources[output_identifier] 117 output_path = os.path.join(self.resource_dir, output_filename) 118 template_path = os.path.join(self.resource_dir, template_filename) 119 XSLForms.Prepare.ensure_stylesheet(template_path, output_path) 120 return output_path 121 122 def prepare_fragment(self, output_identifier, fragment_identifier): 123 124 """ 125 Prepare the output stylesheets for the given 'output_identifier' and 126 'fragment_identifier', indicating which templates and stylesheets are to 127 be employed in the production of output from the resource. 128 129 The 'output_identifier' is used as a key to the 'template_resources' 130 dictionary attribute; the 'fragment_identifier' is used as a key to the 131 'in_page_resources' dictionary attribute. 132 133 Return the full path to the output stylesheet for use with 'send_output' 134 or 'get_result'. 135 """ 136 137 output_path = self.prepare_output(output_identifier) 138 fragment_filename, node_identifier = self.in_page_resources[fragment_identifier] 139 fragment_path = os.path.join(self.resource_dir, fragment_filename) 140 XSLForms.Prepare.ensure_stylesheet_fragment(output_path, fragment_path, node_identifier) 141 return fragment_path 142 143 def send_output(self, trans, stylesheet_filenames, document, stylesheet_parameters=None, references=None): 144 145 """ 146 Send the output from the resource to the user employing the transaction 147 'trans', stylesheets having the given 'stylesheet_filenames', the 148 'document' upon which the output will be based, the optional parameters 149 as defined in the 'stylesheet_parameters' dictionary, and the optional 150 'references' to external documents. 151 """ 152 153 proc = XSLOutput.Processor(stylesheet_filenames, parameters=stylesheet_parameters, references=references) 154 proc.send_output(trans.get_response_stream(), trans.get_response_stream_encoding(), 155 document) 156 157 def get_result(self, stylesheet_filenames, document, stylesheet_parameters=None, references=None): 158 159 """ 160 Get the result of applying a transformation using stylesheets with the 161 given 'stylesheet_filenames', the 'document' upon which the result will 162 be based, the optional parameters as defined in the 163 'stylesheet_parameters' dictionary, and the optional 'references' to 164 external documents. 165 """ 166 167 proc = XSLOutput.Processor(stylesheet_filenames, parameters=stylesheet_parameters, references=references) 168 return proc.get_result(document) 169 170 def prepare_transform(self, transform_identifier): 171 172 """ 173 Prepare a transformation using the given 'transform_identifier'. 174 175 Return a list of full paths to the output stylesheets for use with 176 'send_output' or 'get_result'. 177 """ 178 179 filenames = self.transform_resources[transform_identifier] 180 paths = [] 181 for filename in filenames: 182 paths.append(os.path.join(self.resource_dir, filename)) 183 return paths 184 185 def prepare_document(self, document_identifier): 186 187 """ 188 Prepare a document using the given 'document_identifier'. 189 190 Return the full path of the document for use either as the source 191 document or as a reference with 'send_output' or 'get_result'. 192 """ 193 194 filename = self.document_resources[document_identifier] 195 return os.path.join(self.resource_dir, filename) 196 197 def get_in_page_resource(self, trans): 198 199 """ 200 Return the in-page resource being referred to in the given transaction 201 'trans'. 202 """ 203 204 return trans.get_path_info().split("/")[-1] 205 206 def respond(self, trans): 207 208 """ 209 Respond to the request described by the given transaction 'trans'. 210 """ 211 212 # Only obtain field information according to the stated method. 213 214 method = trans.get_request_method() 215 in_page_resource = self.get_in_page_resource(trans) 216 217 # Handle typical request methods, processing request information. 218 219 if method == "GET": 220 221 # Get the fields from the request path (URL). 222 223 form = XSLForms.Fields.Form(encoding=self.path_encoding, values_are_lists=1) 224 parameters = trans.get_fields_from_path() 225 form.set_parameters(parameters) 226 227 elif method == "POST": 228 229 # Get the fields from the request body. 230 231 form = XSLForms.Fields.Form(encoding=self.encoding, values_are_lists=1) 232 233 # Handle requests for in-page updates. 234 235 if in_page_resource in self.in_page_resources.keys(): 236 parameters = self.get_fields_from_body(trans, self.encoding) 237 else: 238 parameters = trans.get_fields_from_body(self.encoding) 239 240 # Get the XML representation of the request. 241 242 form.set_parameters(parameters) 243 244 else: 245 246 # Initialise empty containers. 247 248 parameters = {} 249 documents = {} 250 251 # Call an overridden method with the processed request information. 252 253 self.respond_to_form(trans, form) 254 255 def respond_to_form(self, trans, form): 256 257 """ 258 Respond to the request described by the given transaction 'trans', using 259 the given 'form' object to conveniently retrieve field (request 260 parameter) information and structured form information (as DOM-style XML 261 documents). 262 """ 263 264 trans.set_response_code(500) 265 raise WebStack.Generic.EndOfResponse 266 267 # vim: tabstop=4 expandtab shiftwidth=4