paulb@353 | 1 | #!/usr/bin/env python |
paulb@353 | 2 | |
paulb@353 | 3 | """ |
paulb@353 | 4 | Resources for use with WebStack. |
paulb@353 | 5 | |
paul@684 | 6 | Copyright (C) 2005, 2006, 2007, 2008, 2009 Paul Boddie <paul@boddie.org.uk> |
paulb@353 | 7 | |
paulb@600 | 8 | This program is free software; you can redistribute it and/or modify it under |
paulb@600 | 9 | the terms of the GNU Lesser General Public License as published by the Free |
paulb@600 | 10 | Software Foundation; either version 3 of the License, or (at your option) any |
paulb@600 | 11 | later version. |
paulb@353 | 12 | |
paulb@600 | 13 | This program is distributed in the hope that it will be useful, but WITHOUT |
paulb@600 | 14 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
paulb@600 | 15 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
paulb@600 | 16 | details. |
paulb@353 | 17 | |
paulb@600 | 18 | You should have received a copy of the GNU Lesser General Public License along |
paulb@600 | 19 | with this program. If not, see <http://www.gnu.org/licenses/>. |
paulb@353 | 20 | """ |
paulb@353 | 21 | |
paul@690 | 22 | from WebStack.Generic import EndOfResponse, ContentType |
paulb@353 | 23 | import XSLForms.Fields |
paulb@353 | 24 | import XSLForms.Prepare |
paulb@353 | 25 | import XSLForms.Output |
paulb@637 | 26 | import XSLForms.Utils |
paulb@354 | 27 | import XSLForms.Resources.Common |
paulb@353 | 28 | from XSLTools import XSLOutput |
paulb@353 | 29 | import os |
paulb@353 | 30 | |
paulb@354 | 31 | class XSLFormsResource(XSLForms.Resources.Common.CommonResource): |
paulb@353 | 32 | |
paulb@353 | 33 | """ |
paulb@353 | 34 | A generic XSLForms resource for use with WebStack. |
paulb@353 | 35 | |
paulb@353 | 36 | When overriding this class, define the following attributes appropriately: |
paulb@353 | 37 | |
paulb@353 | 38 | * template_resources - a dictionary mapping output identifiers to |
paulb@353 | 39 | (template_filename, output_filename) tuples, |
paulb@353 | 40 | indicating the template and stylesheet filenames |
paulb@353 | 41 | to be employed |
paulb@353 | 42 | |
paulb@353 | 43 | * in_page_resources - a dictionary mapping fragment identifiers to |
paulb@509 | 44 | (output_identifier, output_filename, |
paulb@509 | 45 | node_identifier) tuples, indicating the output |
paulb@509 | 46 | identifier for which the fragment applies, the |
paulb@509 | 47 | stylesheet filename to be employed, along with |
paulb@509 | 48 | the node identifier used in the original |
paulb@509 | 49 | template and output documents to mark a region |
paulb@509 | 50 | of those documents as the fragment to be updated |
paulb@509 | 51 | upon "in-page" requests |
paulb@353 | 52 | |
paulb@353 | 53 | * init_resources - a dictionary mapping initialiser/input |
paulb@353 | 54 | identifiers to (template_filename, |
paulb@353 | 55 | input_filename) tuples, indicating the template |
paulb@353 | 56 | and initialiser/input stylesheet filenames to be |
paulb@353 | 57 | employed |
paulb@353 | 58 | |
paulb@353 | 59 | * transform_resources - a dictionary mapping transform identifiers to |
paulb@353 | 60 | lists of stylesheet filenames for use with the |
paulb@353 | 61 | transformation methods |
paulb@353 | 62 | |
paulb@353 | 63 | * document_resources - a dictionary mapping document identifiers to |
paulb@353 | 64 | single filenames for use as source documents or |
paulb@353 | 65 | as references with the transformation methods |
paulb@353 | 66 | |
paulb@353 | 67 | * resource_dir - the absolute path of the directory in which |
paulb@353 | 68 | stylesheet resources are to reside |
paulb@353 | 69 | |
paulb@353 | 70 | All filenames shall be simple leafnames for files residing in the resource's |
paulb@353 | 71 | special resource directory 'resource_dir'. |
paulb@353 | 72 | |
paulb@353 | 73 | The following attributes may also be specified: |
paulb@353 | 74 | |
paulb@353 | 75 | * path_encoding - the assumed encoding of characters in request |
paulb@353 | 76 | paths |
paulb@353 | 77 | |
paulb@353 | 78 | * encoding - the assumed encoding of characters in request |
paulb@353 | 79 | bodies |
paulb@637 | 80 | |
paulb@637 | 81 | To provide actual functionality to resources, either override the |
paulb@637 | 82 | 'respond_to_form' method and write the code for obtaining input, |
paulb@637 | 83 | initialising documents, creating output, and so on in that method, or |
paulb@637 | 84 | provide implementations for the following methods: |
paulb@637 | 85 | |
paulb@637 | 86 | * select_activity - sets the activity name which will be used by the |
paulb@637 | 87 | default implementations of the other methods |
paulb@637 | 88 | |
paulb@637 | 89 | * create_document - creates or obtains a document for the resource's |
paulb@637 | 90 | activity (need not be overridden) |
paulb@637 | 91 | |
paulb@637 | 92 | * respond_to_input - application logic relying on any input from the |
paulb@637 | 93 | request, including submitted document |
paulb@637 | 94 | information |
paulb@637 | 95 | |
paulb@637 | 96 | * init_document - initialises the document according to the |
paulb@637 | 97 | 'init_resources' attribute described above (need |
paulb@637 | 98 | not be overridden) |
paulb@637 | 99 | |
paulb@637 | 100 | * respond_to_document - application logic relying on any information |
paulb@637 | 101 | from the initialised document |
paulb@637 | 102 | |
paulb@637 | 103 | * create_output - creates and sends final output to the user (need |
paulb@637 | 104 | not be overridden) |
paulb@353 | 105 | """ |
paulb@353 | 106 | |
paul@684 | 107 | EMPTY_NAMESPACE = XSLForms.Fields.EMPTY_NAMESPACE |
paul@684 | 108 | FILE_NAMESPACE = XSLForms.Fields.FILE_NAMESPACE |
paul@684 | 109 | |
paul@672 | 110 | #path_encoding = "utf-8" |
paul@672 | 111 | #encoding = "utf-8" |
paul@672 | 112 | |
paulb@353 | 113 | template_resources = {} |
paulb@353 | 114 | in_page_resources = {} |
paulb@354 | 115 | init_resources = {} |
paulb@353 | 116 | transform_resources = {} |
paul@690 | 117 | document_resources = {} |
paulb@353 | 118 | |
paulb@353 | 119 | def clean_parameters(self, parameters): |
paulb@353 | 120 | |
paulb@353 | 121 | """ |
paulb@353 | 122 | Workaround stray zero value characters from Konqueror in XMLHttpRequest |
paulb@353 | 123 | communications. |
paulb@353 | 124 | """ |
paulb@353 | 125 | |
paulb@353 | 126 | for name, values in parameters.items(): |
paulb@353 | 127 | new_values = [] |
paulb@353 | 128 | for value in values: |
paul@674 | 129 | if isinstance(value, (str, unicode)) and value.endswith("\x00"): |
paulb@353 | 130 | new_values.append(value[:-1]) |
paulb@353 | 131 | else: |
paulb@353 | 132 | new_values.append(value) |
paulb@353 | 133 | parameters[name] = new_values |
paulb@353 | 134 | |
paulb@353 | 135 | def prepare_output(self, output_identifier): |
paulb@353 | 136 | |
paulb@353 | 137 | """ |
paulb@353 | 138 | Prepare the output stylesheets using the given 'output_identifier' to |
paulb@353 | 139 | indicate which templates and stylesheets are to be employed in the |
paulb@353 | 140 | production of output from the resource. |
paulb@353 | 141 | |
paulb@353 | 142 | The 'output_identifier' is used as a key to the 'template_resources' |
paulb@353 | 143 | dictionary attribute. |
paulb@353 | 144 | |
paulb@353 | 145 | Return the full path to the output stylesheet for use with 'send_output' |
paulb@353 | 146 | or 'get_result'. |
paulb@353 | 147 | """ |
paulb@353 | 148 | |
paulb@509 | 149 | template_path, output_path = prepare_output(self, output_identifier) |
paulb@353 | 150 | return output_path |
paulb@353 | 151 | |
paulb@509 | 152 | def prepare_fragment(self, fragment_identifier): |
paulb@353 | 153 | |
paulb@353 | 154 | """ |
paulb@509 | 155 | Prepare the output stylesheets for the given 'fragment_identifier', |
paulb@509 | 156 | indicating which templates and stylesheets are to be employed in the |
paulb@509 | 157 | production of output from the resource. |
paulb@353 | 158 | |
paulb@509 | 159 | The 'fragment_identifier' is used as a key to the 'in_page_resources' |
paulb@509 | 160 | dictionary attribute which in turn obtains an 'output_identifier', which |
paulb@509 | 161 | is used as a key to the 'template_resources' dictionary attribute. |
paulb@353 | 162 | |
paulb@353 | 163 | Return the full path to the output stylesheet for use with 'send_output' |
paulb@353 | 164 | or 'get_result'. |
paulb@353 | 165 | """ |
paulb@353 | 166 | |
paulb@509 | 167 | template_path, fragment_path = prepare_fragment(self, fragment_identifier) |
paulb@353 | 168 | return fragment_path |
paulb@353 | 169 | |
paulb@353 | 170 | def prepare_parameters(self, parameters): |
paulb@353 | 171 | |
paulb@353 | 172 | """ |
paulb@353 | 173 | Prepare the stylesheet parameters from the given request 'parameters'. |
paulb@353 | 174 | This is most useful when preparing fragments for in-page update output. |
paulb@353 | 175 | """ |
paulb@353 | 176 | |
paulb@353 | 177 | element_path = parameters.get("element-path", [""])[0] |
paulb@353 | 178 | if element_path: |
paulb@353 | 179 | return {"element-path" : element_path} |
paulb@353 | 180 | else: |
paulb@353 | 181 | return {} |
paulb@353 | 182 | |
paulb@353 | 183 | def send_output(self, trans, stylesheet_filenames, document, stylesheet_parameters=None, |
paulb@353 | 184 | stylesheet_expressions=None, references=None): |
paulb@353 | 185 | |
paulb@353 | 186 | """ |
paulb@353 | 187 | Send the output from the resource to the user employing the transaction |
paulb@353 | 188 | 'trans', stylesheets having the given 'stylesheet_filenames', the |
paulb@353 | 189 | 'document' upon which the output will be based, the optional parameters |
paulb@353 | 190 | as defined in the 'stylesheet_parameters' dictionary, the optional |
paulb@353 | 191 | expressions are defined in the 'stylesheet_expressions' dictionary, and |
paulb@353 | 192 | the optional 'references' to external documents. |
paulb@353 | 193 | """ |
paulb@353 | 194 | |
paul@690 | 195 | # Provide support for deficient browsers like Internet Explorer. |
paul@690 | 196 | |
paul@690 | 197 | content_type = trans.content_type |
paul@690 | 198 | |
paul@690 | 199 | # NOTE: Introduce proper content type parsing into WebStack. |
paul@690 | 200 | |
paul@690 | 201 | accept = (trans.get_header_values("Accept") or [""])[0] |
paul@690 | 202 | xhtml = "application/xhtml+xml" |
paul@690 | 203 | |
paul@690 | 204 | if content_type is not None and content_type.media_type == xhtml and accept.find(xhtml) == -1: |
paul@690 | 205 | |
paul@690 | 206 | # Get the conversion template. |
paul@690 | 207 | |
paul@690 | 208 | stylesheet_filenames.append(os.path.join(XSLForms.Prepare.resource_dir, "XHTMLToHTML.xsl")) |
paul@690 | 209 | trans.set_content_type(ContentType("text/html", content_type.charset or trans.default_encoding)) |
paul@690 | 210 | |
paulb@353 | 211 | # Sanity check for the filenames list. |
paulb@353 | 212 | |
paulb@353 | 213 | if isinstance(stylesheet_filenames, str) or isinstance(stylesheet_filenames, unicode): |
paulb@353 | 214 | raise ValueError, stylesheet_filenames |
paulb@353 | 215 | |
paulb@353 | 216 | proc = XSLOutput.Processor(stylesheet_filenames, parameters=stylesheet_parameters, |
paulb@353 | 217 | expressions=stylesheet_expressions, references=references) |
paulb@353 | 218 | proc.send_output(trans.get_response_stream(), trans.get_response_stream_encoding(), |
paulb@353 | 219 | document) |
paulb@353 | 220 | |
paulb@353 | 221 | def get_result(self, stylesheet_filenames, document, stylesheet_parameters=None, |
paulb@353 | 222 | stylesheet_expressions=None, references=None): |
paulb@353 | 223 | |
paulb@353 | 224 | """ |
paulb@353 | 225 | Get the result of applying a transformation using stylesheets with the |
paulb@353 | 226 | given 'stylesheet_filenames', the 'document' upon which the result will |
paulb@353 | 227 | be based, the optional parameters as defined in the |
paulb@353 | 228 | 'stylesheet_parameters' dictionary, the optional parameters as defined |
paulb@534 | 229 | in the 'stylesheet_parameters' dictionary and the optional 'references' |
paulb@353 | 230 | to external documents. |
paulb@353 | 231 | """ |
paulb@353 | 232 | |
paulb@353 | 233 | # Sanity check for the filenames list. |
paulb@353 | 234 | |
paulb@353 | 235 | if isinstance(stylesheet_filenames, str) or isinstance(stylesheet_filenames, unicode): |
paulb@353 | 236 | raise ValueError, stylesheet_filenames |
paulb@353 | 237 | |
paulb@353 | 238 | proc = XSLOutput.Processor(stylesheet_filenames, parameters=stylesheet_parameters, |
paulb@353 | 239 | expressions=stylesheet_expressions, references=references) |
paulb@353 | 240 | return proc.get_result(document) |
paulb@353 | 241 | |
paulb@365 | 242 | def prepare_initialiser(self, input_identifier, init_enumerations=1): |
paulb@353 | 243 | |
paulb@353 | 244 | """ |
paulb@353 | 245 | Prepare an initialiser/input transformation using the given |
paulb@365 | 246 | 'input_identifier'. The optional 'init_enumerations' (defaulting to |
paulb@365 | 247 | true) may be used to indicate whether enumerations are to be initialised |
paulb@365 | 248 | from external documents. |
paulb@353 | 249 | |
paulb@353 | 250 | Return the full path to the input stylesheet for use with 'send_output' |
paulb@353 | 251 | or 'get_result'. |
paulb@353 | 252 | """ |
paulb@353 | 253 | |
paulb@509 | 254 | template_path, input_path = prepare_initialiser(self, input_identifier, init_enumerations) |
paulb@353 | 255 | return input_path |
paulb@353 | 256 | |
paulb@353 | 257 | def prepare_transform(self, transform_identifier): |
paulb@353 | 258 | |
paulb@353 | 259 | """ |
paulb@353 | 260 | Prepare a transformation using the given 'transform_identifier'. |
paulb@353 | 261 | |
paulb@353 | 262 | Return a list of full paths to the output stylesheets for use with |
paulb@353 | 263 | 'send_output' or 'get_result'. |
paulb@353 | 264 | """ |
paulb@353 | 265 | |
paulb@353 | 266 | filenames = self.transform_resources[transform_identifier] |
paulb@419 | 267 | |
paulb@419 | 268 | # Sanity check for the filenames list. |
paulb@419 | 269 | |
paulb@419 | 270 | if isinstance(filenames, str) or isinstance(filenames, unicode): |
paulb@419 | 271 | raise ValueError, filenames |
paulb@419 | 272 | |
paulb@353 | 273 | paths = [] |
paulb@353 | 274 | for filename in filenames: |
paulb@353 | 275 | paths.append(os.path.abspath(os.path.join(self.resource_dir, filename))) |
paulb@353 | 276 | return paths |
paulb@353 | 277 | |
paulb@666 | 278 | def _get_in_page_resource(self, trans): |
paulb@353 | 279 | |
paulb@353 | 280 | """ |
paulb@353 | 281 | Return the in-page resource being referred to in the given transaction |
paulb@353 | 282 | 'trans'. |
paulb@353 | 283 | """ |
paulb@353 | 284 | |
paulb@560 | 285 | if hasattr(self, "path_encoding"): |
paulb@560 | 286 | return trans.get_path_info(self.path_encoding).split("/")[-1] |
paulb@560 | 287 | else: |
paulb@560 | 288 | return trans.get_path_info().split("/")[-1] |
paulb@353 | 289 | |
paulb@666 | 290 | def get_in_page_resource(self, trans): |
paulb@666 | 291 | |
paulb@666 | 292 | """ |
paulb@666 | 293 | Return the in-page resource being referred to in the given transaction |
paulb@666 | 294 | 'trans' or None if no valid in-page resource is being referenced. |
paulb@666 | 295 | """ |
paulb@666 | 296 | |
paulb@666 | 297 | name = self._get_in_page_resource(trans) |
paulb@666 | 298 | if self.in_page_resources.has_key(name): |
paulb@666 | 299 | return name |
paulb@666 | 300 | else: |
paulb@666 | 301 | return None |
paulb@666 | 302 | |
paulb@353 | 303 | def respond(self, trans): |
paulb@353 | 304 | |
paulb@353 | 305 | """ |
paulb@353 | 306 | Respond to the request described by the given transaction 'trans'. |
paulb@353 | 307 | """ |
paulb@353 | 308 | |
paulb@353 | 309 | # Only obtain field information according to the stated method. |
paulb@353 | 310 | |
paulb@534 | 311 | content_type = trans.get_content_type() |
paulb@353 | 312 | method = trans.get_request_method() |
paulb@353 | 313 | in_page_resource = self.get_in_page_resource(trans) |
paulb@353 | 314 | |
paulb@353 | 315 | # Handle typical request methods, processing request information. |
paulb@353 | 316 | |
paulb@353 | 317 | if method == "GET": |
paulb@353 | 318 | |
paulb@353 | 319 | # Get the fields from the request path (URL). |
paulb@353 | 320 | |
paulb@560 | 321 | form = XSLForms.Fields.Form(encoding=None, values_are_lists=1) |
paulb@353 | 322 | parameters = trans.get_fields_from_path() |
paulb@353 | 323 | form.set_parameters(parameters) |
paulb@353 | 324 | |
paul@674 | 325 | elif method == "POST" and content_type.media_type in ( |
paul@674 | 326 | "application/x-www-form-urlencoded", "multipart/form-data"): |
paulb@353 | 327 | |
paulb@353 | 328 | # Get the fields from the request body. |
paulb@353 | 329 | |
paulb@560 | 330 | form = XSLForms.Fields.Form(encoding=None, values_are_lists=1) |
paulb@560 | 331 | if hasattr(self, "encoding"): |
paulb@560 | 332 | parameters = trans.get_fields_from_body(self.encoding) |
paulb@560 | 333 | else: |
paulb@560 | 334 | parameters = trans.get_fields_from_body() |
paulb@353 | 335 | |
paulb@353 | 336 | # NOTE: Konqueror workaround. |
paulb@353 | 337 | self.clean_parameters(parameters) |
paulb@353 | 338 | |
paulb@353 | 339 | form.set_parameters(parameters) |
paulb@353 | 340 | |
paulb@353 | 341 | else: |
paulb@353 | 342 | |
paulb@450 | 343 | # Initialise empty container. |
paulb@353 | 344 | |
paulb@560 | 345 | form = XSLForms.Fields.Form(encoding=None, values_are_lists=1) |
paulb@353 | 346 | |
paulb@353 | 347 | # Call an overridden method with the processed request information. |
paulb@353 | 348 | |
paulb@353 | 349 | self.respond_to_form(trans, form) |
paulb@353 | 350 | |
paulb@353 | 351 | def respond_to_form(self, trans, form): |
paulb@353 | 352 | |
paulb@353 | 353 | """ |
paulb@353 | 354 | Respond to the request described by the given transaction 'trans', using |
paulb@353 | 355 | the given 'form' object to conveniently retrieve field (request |
paulb@353 | 356 | parameter) information and structured form information (as DOM-style XML |
paulb@353 | 357 | documents). |
paulb@353 | 358 | """ |
paulb@353 | 359 | |
paulb@637 | 360 | self.select_activity(trans, form) |
paulb@637 | 361 | self.create_document(trans, form) |
paulb@637 | 362 | self.respond_to_input(trans, form) |
paulb@637 | 363 | self.init_document(trans, form) |
paulb@637 | 364 | self.respond_to_document(trans, form) |
paulb@637 | 365 | self.create_output(trans, form) |
paul@690 | 366 | raise EndOfResponse |
paulb@353 | 367 | |
paulb@637 | 368 | # Modular methods for responding to requests. |
paulb@637 | 369 | |
paulb@637 | 370 | def select_activity(self, trans, form): |
paulb@637 | 371 | |
paulb@637 | 372 | """ |
paulb@637 | 373 | Using the given transaction 'trans' and 'form' information, select the |
paulb@637 | 374 | activity being performed and set the 'current_activity' attribute in the |
paulb@637 | 375 | transaction. |
paulb@637 | 376 | """ |
paulb@637 | 377 | |
paulb@637 | 378 | pass |
paulb@637 | 379 | |
paulb@637 | 380 | def create_document(self, trans, form): |
paulb@637 | 381 | |
paulb@637 | 382 | """ |
paulb@637 | 383 | Using the given transaction 'trans' and 'form' information, create the |
paulb@637 | 384 | document involved in the current activity and set the 'current_document' |
paulb@637 | 385 | attribute in the transaction. |
paulb@637 | 386 | |
paulb@637 | 387 | Return whether a new document was created. |
paulb@637 | 388 | """ |
paulb@637 | 389 | |
paulb@637 | 390 | documents = form.get_documents() |
paulb@641 | 391 | activity = form.get_activity() |
paulb@637 | 392 | |
paulb@637 | 393 | if documents.has_key(activity): |
paulb@641 | 394 | form.set_document(documents[activity]) |
paulb@637 | 395 | return 0 |
paulb@637 | 396 | else: |
paulb@666 | 397 | form.new_document(activity) |
paulb@666 | 398 | form.new_documents.add(activity) |
paulb@637 | 399 | return 1 |
paulb@637 | 400 | |
paulb@637 | 401 | def respond_to_input(self, trans, form): |
paulb@637 | 402 | |
paulb@637 | 403 | """ |
paulb@637 | 404 | Using the given transaction 'trans' and 'form' information, perform the |
paulb@637 | 405 | parts of the current activity which rely on the information supplied in |
paulb@637 | 406 | the current document. |
paulb@637 | 407 | """ |
paulb@637 | 408 | |
paulb@637 | 409 | pass |
paulb@637 | 410 | |
paulb@637 | 411 | def init_document(self, trans, form, stylesheet_parameters=None, |
paulb@637 | 412 | stylesheet_expressions=None, references=None): |
paulb@637 | 413 | |
paulb@637 | 414 | """ |
paulb@637 | 415 | Using the given transaction 'trans' and 'form' information, initialise |
paulb@637 | 416 | the current document. |
paulb@637 | 417 | """ |
paulb@637 | 418 | |
paulb@646 | 419 | activity = form.get_activity() |
paulb@646 | 420 | |
paulb@637 | 421 | # Transform, adding enumerations/ranges. |
paulb@637 | 422 | |
paulb@646 | 423 | if self.init_resources.has_key(activity): |
paulb@646 | 424 | init_xsl = self.prepare_initialiser(activity) |
paulb@646 | 425 | form.set_document( |
paulb@646 | 426 | self.get_result( |
paulb@646 | 427 | [init_xsl], form.get_document(), stylesheet_parameters, |
paulb@646 | 428 | stylesheet_expressions, references |
paulb@646 | 429 | ) |
paulb@637 | 430 | ) |
paulb@637 | 431 | |
paulb@637 | 432 | def respond_to_document(self, trans, form): |
paulb@637 | 433 | |
paulb@637 | 434 | """ |
paulb@637 | 435 | Using the given transaction 'trans' and 'form' information, perform the |
paulb@637 | 436 | parts of the current activity which rely on a populated version of the |
paulb@637 | 437 | current document. |
paulb@637 | 438 | """ |
paulb@637 | 439 | |
paulb@637 | 440 | pass |
paulb@637 | 441 | |
paulb@637 | 442 | def create_output(self, trans, form, content_type=None, |
paulb@637 | 443 | stylesheet_parameters=None, stylesheet_expressions=None, references=None): |
paulb@637 | 444 | |
paulb@637 | 445 | """ |
paulb@637 | 446 | Using the given transaction 'trans' and 'form' information, create the |
paulb@637 | 447 | output for the current activity using the previously set attributes in |
paulb@637 | 448 | the transaction. |
paulb@637 | 449 | """ |
paulb@637 | 450 | |
paulb@637 | 451 | attributes = trans.get_attributes() |
paulb@637 | 452 | in_page_resource = self.get_in_page_resource(trans) |
paulb@637 | 453 | parameters = form.get_parameters() |
paulb@637 | 454 | |
paulb@637 | 455 | # Start the response. |
paulb@637 | 456 | |
paul@672 | 457 | if attributes.has_key("encoding"): |
paul@672 | 458 | encoding = attributes["encoding"] # NOTE: Potentially redundant. |
paul@672 | 459 | elif hasattr(self, "encoding"): |
paul@672 | 460 | encoding = self.encoding |
paul@672 | 461 | else: |
paul@672 | 462 | encoding = trans.default_charset |
paul@672 | 463 | |
paul@690 | 464 | content_type = content_type or ContentType("application/xhtml+xml", encoding) |
paulb@637 | 465 | trans.set_content_type(content_type) |
paulb@637 | 466 | |
paulb@637 | 467 | # Ensure that an output stylesheet exists. |
paulb@637 | 468 | |
paulb@637 | 469 | stylesheet_parameters = stylesheet_parameters or {} |
paulb@637 | 470 | |
paulb@666 | 471 | if in_page_resource: |
paulb@637 | 472 | trans_xsl = self.prepare_fragment(in_page_resource) |
paulb@637 | 473 | stylesheet_parameters.update(self.prepare_parameters(parameters)) |
paulb@637 | 474 | else: |
paulb@641 | 475 | trans_xsl = self.prepare_output(form.get_activity()) |
paulb@637 | 476 | |
paulb@637 | 477 | # Complete the response. |
paulb@637 | 478 | |
paulb@641 | 479 | self.send_output(trans, [trans_xsl], form.get_document(), |
paulb@637 | 480 | stylesheet_parameters, stylesheet_expressions, references) |
paulb@637 | 481 | |
paulb@637 | 482 | # General helper methods. |
paulb@637 | 483 | |
paulb@637 | 484 | def add_elements(self, positions, *element_names): |
paulb@637 | 485 | |
paulb@637 | 486 | """ |
paulb@637 | 487 | At the given 'positions', typically obtained as "selectors", add the |
paulb@637 | 488 | hierarchy of elements given in the 'element_names' parameters. |
paulb@637 | 489 | """ |
paulb@637 | 490 | |
paulb@637 | 491 | XSLForms.Utils.add_elements(positions, *element_names) |
paulb@637 | 492 | |
paulb@637 | 493 | def remove_elements(self, positions): |
paulb@637 | 494 | |
paulb@637 | 495 | """ |
paulb@637 | 496 | Remove elements at the given 'positions', typically obtained as |
paulb@637 | 497 | "selectors". |
paulb@637 | 498 | """ |
paulb@637 | 499 | |
paulb@637 | 500 | XSLForms.Utils.remove_elements(positions) |
paulb@637 | 501 | |
paulb@509 | 502 | def prepare_output(self, output_identifier): |
paulb@509 | 503 | |
paulb@509 | 504 | """ |
paulb@509 | 505 | Prepare the output stylesheet for the resource class or object 'self' |
paulb@509 | 506 | corresponding to the given 'output_identifier'. Return the template path |
paulb@509 | 507 | and the output stylesheet path in a 2-tuple. |
paulb@509 | 508 | """ |
paulb@509 | 509 | |
paulb@509 | 510 | template_filename, output_filename = self.template_resources[output_identifier] |
paulb@509 | 511 | output_path = os.path.abspath(os.path.join(self.resource_dir, output_filename)) |
paulb@509 | 512 | template_path = os.path.abspath(os.path.join(self.resource_dir, template_filename)) |
paulb@509 | 513 | XSLForms.Prepare.ensure_stylesheet(template_path, output_path) |
paulb@509 | 514 | return template_path, output_path |
paulb@509 | 515 | |
paulb@509 | 516 | def prepare_fragment(self, fragment_identifier): |
paulb@509 | 517 | |
paulb@509 | 518 | """ |
paulb@509 | 519 | Prepare the output stylesheet for the resource class or object 'self' |
paulb@509 | 520 | corresponding to the given 'fragment_identifier'. Return the template path |
paulb@509 | 521 | and the output stylesheet path in a 2-tuple. |
paulb@509 | 522 | """ |
paulb@509 | 523 | |
paulb@509 | 524 | output_identifier, fragment_filename, node_identifier = self.in_page_resources[fragment_identifier] |
paulb@509 | 525 | fragment_path = os.path.abspath(os.path.join(self.resource_dir, fragment_filename)) |
paulb@509 | 526 | template_filename, output_filename = self.template_resources[output_identifier] |
paulb@509 | 527 | template_path = os.path.abspath(os.path.join(self.resource_dir, template_filename)) |
paulb@509 | 528 | XSLForms.Prepare.ensure_stylesheet_fragment(template_path, fragment_path, node_identifier) |
paulb@509 | 529 | return template_path, fragment_path |
paulb@509 | 530 | |
paulb@509 | 531 | def prepare_initialiser(self, input_identifier, init_enumerations): |
paulb@509 | 532 | |
paulb@509 | 533 | """ |
paulb@509 | 534 | Prepare the initialising stylesheet for the resource class or object 'self' |
paulb@509 | 535 | corresponding to the given 'input_identifier' and 'init_enumerations' flag. |
paulb@509 | 536 | Return the template path and the initialising stylesheet path in a 2-tuple. |
paulb@509 | 537 | """ |
paulb@509 | 538 | |
paulb@509 | 539 | template_filename, input_filename = self.init_resources[input_identifier] |
paulb@509 | 540 | input_path = os.path.abspath(os.path.join(self.resource_dir, input_filename)) |
paulb@509 | 541 | template_path = os.path.abspath(os.path.join(self.resource_dir, template_filename)) |
paulb@509 | 542 | XSLForms.Prepare.ensure_input_stylesheet(template_path, input_path, init_enumerations) |
paulb@509 | 543 | return template_path, input_path |
paulb@509 | 544 | |
paulb@509 | 545 | def prepare_resources(cls): |
paulb@509 | 546 | |
paulb@509 | 547 | "Prepare the resources associated with the class 'cls'." |
paulb@509 | 548 | |
paulb@509 | 549 | for output_identifier in cls.template_resources.keys(): |
paulb@509 | 550 | prepare_output(cls, output_identifier) |
paulb@509 | 551 | for fragment_identifier in cls.in_page_resources.keys(): |
paulb@509 | 552 | prepare_fragment(cls, fragment_identifier) |
paulb@509 | 553 | |
paulb@509 | 554 | # NOTE: Using init_enumerations=1 here. |
paulb@509 | 555 | |
paulb@509 | 556 | for input_identifier in cls.init_resources.keys(): |
paulb@509 | 557 | prepare_initialiser(cls, input_identifier, 1) |
paulb@509 | 558 | |
paulb@634 | 559 | # Convenience methods for specifying resources. |
paulb@634 | 560 | |
paulb@634 | 561 | def split(filename): |
paulb@634 | 562 | |
paulb@634 | 563 | """ |
paulb@634 | 564 | Return a tuple containing the directory and filename without extension for |
paulb@634 | 565 | 'filename'. |
paulb@634 | 566 | """ |
paulb@634 | 567 | |
paulb@634 | 568 | d, leafname = os.path.split(filename) |
paulb@634 | 569 | name, ext = os.path.splitext(leafname) |
paulb@634 | 570 | return d, name |
paulb@634 | 571 | |
paulb@634 | 572 | def output(template_filename): |
paulb@634 | 573 | |
paulb@634 | 574 | """ |
paulb@634 | 575 | Return a tuple containing the 'template_filename' and a suitable output |
paulb@634 | 576 | stylesheet filename. |
paulb@634 | 577 | """ |
paulb@634 | 578 | |
paulb@634 | 579 | d, name = split(template_filename) |
paulb@634 | 580 | output_name = name.replace("_template", "_output") + os.path.extsep + "xsl" |
paulb@634 | 581 | return (template_filename, os.path.join(d, output_name)) |
paulb@634 | 582 | |
paulb@634 | 583 | def input(template_filename): |
paulb@634 | 584 | |
paulb@634 | 585 | """ |
paulb@634 | 586 | Return a tuple containing the 'template_filename' and a suitable output |
paulb@634 | 587 | stylesheet filename. |
paulb@634 | 588 | """ |
paulb@634 | 589 | |
paulb@634 | 590 | d, name = split(template_filename) |
paulb@634 | 591 | input_name = name.replace("_template", "_input") + os.path.extsep + "xsl" |
paulb@634 | 592 | return (template_filename, os.path.join(d, input_name)) |
paulb@634 | 593 | |
paulb@634 | 594 | def resources(filename, d="Resources"): |
paulb@634 | 595 | |
paulb@634 | 596 | """ |
paulb@634 | 597 | Return the resource directory for the given 'filename', using the optional |
paulb@634 | 598 | directory name 'd' to indicate the directory relative to the directory of |
paulb@634 | 599 | 'filename' (or the default directory name, indicating that the directory |
paulb@634 | 600 | called "Resources" - a sibling of 'filename' - is the resource directory). |
paulb@634 | 601 | |
paulb@634 | 602 | It is envisaged that callers provide the value of the __file__ special |
paulb@634 | 603 | variable to get the resource directory relative to a particular module. |
paulb@634 | 604 | """ |
paulb@634 | 605 | |
paulb@634 | 606 | return os.path.join(os.path.split(filename)[0], d) |
paulb@634 | 607 | |
paulb@353 | 608 | # vim: tabstop=4 expandtab shiftwidth=4 |