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