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 | |
paulb@353 | 22 | import WebStack.Generic |
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 = {} |
paulb@353 | 117 | |
paulb@353 | 118 | def clean_parameters(self, parameters): |
paulb@353 | 119 | |
paulb@353 | 120 | """ |
paulb@353 | 121 | Workaround stray zero value characters from Konqueror in XMLHttpRequest |
paulb@353 | 122 | communications. |
paulb@353 | 123 | """ |
paulb@353 | 124 | |
paulb@353 | 125 | for name, values in parameters.items(): |
paulb@353 | 126 | new_values = [] |
paulb@353 | 127 | for value in values: |
paul@674 | 128 | if isinstance(value, (str, unicode)) and value.endswith("\x00"): |
paulb@353 | 129 | new_values.append(value[:-1]) |
paulb@353 | 130 | else: |
paulb@353 | 131 | new_values.append(value) |
paulb@353 | 132 | parameters[name] = new_values |
paulb@353 | 133 | |
paulb@353 | 134 | def prepare_output(self, output_identifier): |
paulb@353 | 135 | |
paulb@353 | 136 | """ |
paulb@353 | 137 | Prepare the output stylesheets using the given 'output_identifier' to |
paulb@353 | 138 | indicate which templates and stylesheets are to be employed in the |
paulb@353 | 139 | production of output from the resource. |
paulb@353 | 140 | |
paulb@353 | 141 | The 'output_identifier' is used as a key to the 'template_resources' |
paulb@353 | 142 | dictionary attribute. |
paulb@353 | 143 | |
paulb@353 | 144 | Return the full path to the output stylesheet for use with 'send_output' |
paulb@353 | 145 | or 'get_result'. |
paulb@353 | 146 | """ |
paulb@353 | 147 | |
paulb@509 | 148 | template_path, output_path = prepare_output(self, output_identifier) |
paulb@353 | 149 | return output_path |
paulb@353 | 150 | |
paulb@509 | 151 | def prepare_fragment(self, fragment_identifier): |
paulb@353 | 152 | |
paulb@353 | 153 | """ |
paulb@509 | 154 | Prepare the output stylesheets for the given 'fragment_identifier', |
paulb@509 | 155 | indicating which templates and stylesheets are to be employed in the |
paulb@509 | 156 | production of output from the resource. |
paulb@353 | 157 | |
paulb@509 | 158 | The 'fragment_identifier' is used as a key to the 'in_page_resources' |
paulb@509 | 159 | dictionary attribute which in turn obtains an 'output_identifier', which |
paulb@509 | 160 | is used as a key to the 'template_resources' dictionary attribute. |
paulb@353 | 161 | |
paulb@353 | 162 | Return the full path to the output stylesheet for use with 'send_output' |
paulb@353 | 163 | or 'get_result'. |
paulb@353 | 164 | """ |
paulb@353 | 165 | |
paulb@509 | 166 | template_path, fragment_path = prepare_fragment(self, fragment_identifier) |
paulb@353 | 167 | return fragment_path |
paulb@353 | 168 | |
paulb@353 | 169 | def prepare_parameters(self, parameters): |
paulb@353 | 170 | |
paulb@353 | 171 | """ |
paulb@353 | 172 | Prepare the stylesheet parameters from the given request 'parameters'. |
paulb@353 | 173 | This is most useful when preparing fragments for in-page update output. |
paulb@353 | 174 | """ |
paulb@353 | 175 | |
paulb@353 | 176 | element_path = parameters.get("element-path", [""])[0] |
paulb@353 | 177 | if element_path: |
paulb@353 | 178 | return {"element-path" : element_path} |
paulb@353 | 179 | else: |
paulb@353 | 180 | return {} |
paulb@353 | 181 | |
paulb@353 | 182 | def send_output(self, trans, stylesheet_filenames, document, stylesheet_parameters=None, |
paulb@353 | 183 | stylesheet_expressions=None, references=None): |
paulb@353 | 184 | |
paulb@353 | 185 | """ |
paulb@353 | 186 | Send the output from the resource to the user employing the transaction |
paulb@353 | 187 | 'trans', stylesheets having the given 'stylesheet_filenames', the |
paulb@353 | 188 | 'document' upon which the output will be based, the optional parameters |
paulb@353 | 189 | as defined in the 'stylesheet_parameters' dictionary, the optional |
paulb@353 | 190 | expressions are defined in the 'stylesheet_expressions' dictionary, and |
paulb@353 | 191 | the optional 'references' to external documents. |
paulb@353 | 192 | """ |
paulb@353 | 193 | |
paulb@353 | 194 | # Sanity check for the filenames list. |
paulb@353 | 195 | |
paulb@353 | 196 | if isinstance(stylesheet_filenames, str) or isinstance(stylesheet_filenames, unicode): |
paulb@353 | 197 | raise ValueError, stylesheet_filenames |
paulb@353 | 198 | |
paulb@353 | 199 | proc = XSLOutput.Processor(stylesheet_filenames, parameters=stylesheet_parameters, |
paulb@353 | 200 | expressions=stylesheet_expressions, references=references) |
paulb@353 | 201 | proc.send_output(trans.get_response_stream(), trans.get_response_stream_encoding(), |
paulb@353 | 202 | document) |
paulb@353 | 203 | |
paulb@353 | 204 | def get_result(self, stylesheet_filenames, document, stylesheet_parameters=None, |
paulb@353 | 205 | stylesheet_expressions=None, references=None): |
paulb@353 | 206 | |
paulb@353 | 207 | """ |
paulb@353 | 208 | Get the result of applying a transformation using stylesheets with the |
paulb@353 | 209 | given 'stylesheet_filenames', the 'document' upon which the result will |
paulb@353 | 210 | be based, the optional parameters as defined in the |
paulb@353 | 211 | 'stylesheet_parameters' dictionary, the optional parameters as defined |
paulb@534 | 212 | in the 'stylesheet_parameters' dictionary and the optional 'references' |
paulb@353 | 213 | to external documents. |
paulb@353 | 214 | """ |
paulb@353 | 215 | |
paulb@353 | 216 | # Sanity check for the filenames list. |
paulb@353 | 217 | |
paulb@353 | 218 | if isinstance(stylesheet_filenames, str) or isinstance(stylesheet_filenames, unicode): |
paulb@353 | 219 | raise ValueError, stylesheet_filenames |
paulb@353 | 220 | |
paulb@353 | 221 | proc = XSLOutput.Processor(stylesheet_filenames, parameters=stylesheet_parameters, |
paulb@353 | 222 | expressions=stylesheet_expressions, references=references) |
paulb@353 | 223 | return proc.get_result(document) |
paulb@353 | 224 | |
paulb@365 | 225 | def prepare_initialiser(self, input_identifier, init_enumerations=1): |
paulb@353 | 226 | |
paulb@353 | 227 | """ |
paulb@353 | 228 | Prepare an initialiser/input transformation using the given |
paulb@365 | 229 | 'input_identifier'. The optional 'init_enumerations' (defaulting to |
paulb@365 | 230 | true) may be used to indicate whether enumerations are to be initialised |
paulb@365 | 231 | from external documents. |
paulb@353 | 232 | |
paulb@353 | 233 | Return the full path to the input stylesheet for use with 'send_output' |
paulb@353 | 234 | or 'get_result'. |
paulb@353 | 235 | """ |
paulb@353 | 236 | |
paulb@509 | 237 | template_path, input_path = prepare_initialiser(self, input_identifier, init_enumerations) |
paulb@353 | 238 | return input_path |
paulb@353 | 239 | |
paulb@353 | 240 | def prepare_transform(self, transform_identifier): |
paulb@353 | 241 | |
paulb@353 | 242 | """ |
paulb@353 | 243 | Prepare a transformation using the given 'transform_identifier'. |
paulb@353 | 244 | |
paulb@353 | 245 | Return a list of full paths to the output stylesheets for use with |
paulb@353 | 246 | 'send_output' or 'get_result'. |
paulb@353 | 247 | """ |
paulb@353 | 248 | |
paulb@353 | 249 | filenames = self.transform_resources[transform_identifier] |
paulb@419 | 250 | |
paulb@419 | 251 | # Sanity check for the filenames list. |
paulb@419 | 252 | |
paulb@419 | 253 | if isinstance(filenames, str) or isinstance(filenames, unicode): |
paulb@419 | 254 | raise ValueError, filenames |
paulb@419 | 255 | |
paulb@353 | 256 | paths = [] |
paulb@353 | 257 | for filename in filenames: |
paulb@353 | 258 | paths.append(os.path.abspath(os.path.join(self.resource_dir, filename))) |
paulb@353 | 259 | return paths |
paulb@353 | 260 | |
paulb@666 | 261 | def _get_in_page_resource(self, trans): |
paulb@353 | 262 | |
paulb@353 | 263 | """ |
paulb@353 | 264 | Return the in-page resource being referred to in the given transaction |
paulb@353 | 265 | 'trans'. |
paulb@353 | 266 | """ |
paulb@353 | 267 | |
paulb@560 | 268 | if hasattr(self, "path_encoding"): |
paulb@560 | 269 | return trans.get_path_info(self.path_encoding).split("/")[-1] |
paulb@560 | 270 | else: |
paulb@560 | 271 | return trans.get_path_info().split("/")[-1] |
paulb@353 | 272 | |
paulb@666 | 273 | def get_in_page_resource(self, trans): |
paulb@666 | 274 | |
paulb@666 | 275 | """ |
paulb@666 | 276 | Return the in-page resource being referred to in the given transaction |
paulb@666 | 277 | 'trans' or None if no valid in-page resource is being referenced. |
paulb@666 | 278 | """ |
paulb@666 | 279 | |
paulb@666 | 280 | name = self._get_in_page_resource(trans) |
paulb@666 | 281 | if self.in_page_resources.has_key(name): |
paulb@666 | 282 | return name |
paulb@666 | 283 | else: |
paulb@666 | 284 | return None |
paulb@666 | 285 | |
paulb@353 | 286 | def respond(self, trans): |
paulb@353 | 287 | |
paulb@353 | 288 | """ |
paulb@353 | 289 | Respond to the request described by the given transaction 'trans'. |
paulb@353 | 290 | """ |
paulb@353 | 291 | |
paulb@353 | 292 | # Only obtain field information according to the stated method. |
paulb@353 | 293 | |
paulb@534 | 294 | content_type = trans.get_content_type() |
paulb@353 | 295 | method = trans.get_request_method() |
paulb@353 | 296 | in_page_resource = self.get_in_page_resource(trans) |
paulb@353 | 297 | |
paulb@353 | 298 | # Handle typical request methods, processing request information. |
paulb@353 | 299 | |
paulb@353 | 300 | if method == "GET": |
paulb@353 | 301 | |
paulb@353 | 302 | # Get the fields from the request path (URL). |
paulb@353 | 303 | |
paulb@560 | 304 | form = XSLForms.Fields.Form(encoding=None, values_are_lists=1) |
paulb@353 | 305 | parameters = trans.get_fields_from_path() |
paulb@353 | 306 | form.set_parameters(parameters) |
paulb@353 | 307 | |
paul@674 | 308 | elif method == "POST" and content_type.media_type in ( |
paul@674 | 309 | "application/x-www-form-urlencoded", "multipart/form-data"): |
paulb@353 | 310 | |
paulb@353 | 311 | # Get the fields from the request body. |
paulb@353 | 312 | |
paulb@560 | 313 | form = XSLForms.Fields.Form(encoding=None, values_are_lists=1) |
paulb@560 | 314 | if hasattr(self, "encoding"): |
paulb@560 | 315 | parameters = trans.get_fields_from_body(self.encoding) |
paulb@560 | 316 | else: |
paulb@560 | 317 | parameters = trans.get_fields_from_body() |
paulb@353 | 318 | |
paulb@353 | 319 | # NOTE: Konqueror workaround. |
paulb@353 | 320 | self.clean_parameters(parameters) |
paulb@353 | 321 | |
paulb@353 | 322 | form.set_parameters(parameters) |
paulb@353 | 323 | |
paulb@353 | 324 | else: |
paulb@353 | 325 | |
paulb@450 | 326 | # Initialise empty container. |
paulb@353 | 327 | |
paulb@560 | 328 | form = XSLForms.Fields.Form(encoding=None, values_are_lists=1) |
paulb@353 | 329 | |
paulb@353 | 330 | # Call an overridden method with the processed request information. |
paulb@353 | 331 | |
paulb@353 | 332 | self.respond_to_form(trans, form) |
paulb@353 | 333 | |
paulb@353 | 334 | def respond_to_form(self, trans, form): |
paulb@353 | 335 | |
paulb@353 | 336 | """ |
paulb@353 | 337 | Respond to the request described by the given transaction 'trans', using |
paulb@353 | 338 | the given 'form' object to conveniently retrieve field (request |
paulb@353 | 339 | parameter) information and structured form information (as DOM-style XML |
paulb@353 | 340 | documents). |
paulb@353 | 341 | """ |
paulb@353 | 342 | |
paulb@637 | 343 | self.select_activity(trans, form) |
paulb@637 | 344 | self.create_document(trans, form) |
paulb@637 | 345 | self.respond_to_input(trans, form) |
paulb@637 | 346 | self.init_document(trans, form) |
paulb@637 | 347 | self.respond_to_document(trans, form) |
paulb@637 | 348 | self.create_output(trans, form) |
paulb@353 | 349 | raise WebStack.Generic.EndOfResponse |
paulb@353 | 350 | |
paulb@637 | 351 | # Modular methods for responding to requests. |
paulb@637 | 352 | |
paulb@637 | 353 | def select_activity(self, trans, form): |
paulb@637 | 354 | |
paulb@637 | 355 | """ |
paulb@637 | 356 | Using the given transaction 'trans' and 'form' information, select the |
paulb@637 | 357 | activity being performed and set the 'current_activity' attribute in the |
paulb@637 | 358 | transaction. |
paulb@637 | 359 | """ |
paulb@637 | 360 | |
paulb@637 | 361 | pass |
paulb@637 | 362 | |
paulb@637 | 363 | def create_document(self, trans, form): |
paulb@637 | 364 | |
paulb@637 | 365 | """ |
paulb@637 | 366 | Using the given transaction 'trans' and 'form' information, create the |
paulb@637 | 367 | document involved in the current activity and set the 'current_document' |
paulb@637 | 368 | attribute in the transaction. |
paulb@637 | 369 | |
paulb@637 | 370 | Return whether a new document was created. |
paulb@637 | 371 | """ |
paulb@637 | 372 | |
paulb@637 | 373 | documents = form.get_documents() |
paulb@641 | 374 | activity = form.get_activity() |
paulb@637 | 375 | |
paulb@637 | 376 | if documents.has_key(activity): |
paulb@641 | 377 | form.set_document(documents[activity]) |
paulb@637 | 378 | return 0 |
paulb@637 | 379 | else: |
paulb@666 | 380 | form.new_document(activity) |
paulb@666 | 381 | form.new_documents.add(activity) |
paulb@637 | 382 | return 1 |
paulb@637 | 383 | |
paulb@637 | 384 | def respond_to_input(self, trans, form): |
paulb@637 | 385 | |
paulb@637 | 386 | """ |
paulb@637 | 387 | Using the given transaction 'trans' and 'form' information, perform the |
paulb@637 | 388 | parts of the current activity which rely on the information supplied in |
paulb@637 | 389 | the current document. |
paulb@637 | 390 | """ |
paulb@637 | 391 | |
paulb@637 | 392 | pass |
paulb@637 | 393 | |
paulb@637 | 394 | def init_document(self, trans, form, stylesheet_parameters=None, |
paulb@637 | 395 | stylesheet_expressions=None, references=None): |
paulb@637 | 396 | |
paulb@637 | 397 | """ |
paulb@637 | 398 | Using the given transaction 'trans' and 'form' information, initialise |
paulb@637 | 399 | the current document. |
paulb@637 | 400 | """ |
paulb@637 | 401 | |
paulb@646 | 402 | activity = form.get_activity() |
paulb@646 | 403 | |
paulb@637 | 404 | # Transform, adding enumerations/ranges. |
paulb@637 | 405 | |
paulb@646 | 406 | if self.init_resources.has_key(activity): |
paulb@646 | 407 | init_xsl = self.prepare_initialiser(activity) |
paulb@646 | 408 | form.set_document( |
paulb@646 | 409 | self.get_result( |
paulb@646 | 410 | [init_xsl], form.get_document(), stylesheet_parameters, |
paulb@646 | 411 | stylesheet_expressions, references |
paulb@646 | 412 | ) |
paulb@637 | 413 | ) |
paulb@637 | 414 | |
paulb@637 | 415 | def respond_to_document(self, trans, form): |
paulb@637 | 416 | |
paulb@637 | 417 | """ |
paulb@637 | 418 | Using the given transaction 'trans' and 'form' information, perform the |
paulb@637 | 419 | parts of the current activity which rely on a populated version of the |
paulb@637 | 420 | current document. |
paulb@637 | 421 | """ |
paulb@637 | 422 | |
paulb@637 | 423 | pass |
paulb@637 | 424 | |
paulb@637 | 425 | def create_output(self, trans, form, content_type=None, |
paulb@637 | 426 | stylesheet_parameters=None, stylesheet_expressions=None, references=None): |
paulb@637 | 427 | |
paulb@637 | 428 | """ |
paulb@637 | 429 | Using the given transaction 'trans' and 'form' information, create the |
paulb@637 | 430 | output for the current activity using the previously set attributes in |
paulb@637 | 431 | the transaction. |
paulb@637 | 432 | """ |
paulb@637 | 433 | |
paulb@637 | 434 | attributes = trans.get_attributes() |
paulb@637 | 435 | in_page_resource = self.get_in_page_resource(trans) |
paulb@637 | 436 | parameters = form.get_parameters() |
paulb@637 | 437 | |
paulb@637 | 438 | # Start the response. |
paulb@637 | 439 | |
paul@672 | 440 | if attributes.has_key("encoding"): |
paul@672 | 441 | encoding = attributes["encoding"] # NOTE: Potentially redundant. |
paul@672 | 442 | elif hasattr(self, "encoding"): |
paul@672 | 443 | encoding = self.encoding |
paul@672 | 444 | else: |
paul@672 | 445 | encoding = trans.default_charset |
paul@672 | 446 | |
paulb@637 | 447 | content_type = content_type or WebStack.Generic.ContentType("application/xhtml+xml", encoding) |
paulb@637 | 448 | trans.set_content_type(content_type) |
paulb@637 | 449 | |
paulb@637 | 450 | # Ensure that an output stylesheet exists. |
paulb@637 | 451 | |
paulb@637 | 452 | stylesheet_parameters = stylesheet_parameters or {} |
paulb@637 | 453 | |
paulb@666 | 454 | if in_page_resource: |
paulb@637 | 455 | trans_xsl = self.prepare_fragment(in_page_resource) |
paulb@637 | 456 | stylesheet_parameters.update(self.prepare_parameters(parameters)) |
paulb@637 | 457 | else: |
paulb@641 | 458 | trans_xsl = self.prepare_output(form.get_activity()) |
paulb@637 | 459 | |
paulb@637 | 460 | # Complete the response. |
paulb@637 | 461 | |
paulb@641 | 462 | self.send_output(trans, [trans_xsl], form.get_document(), |
paulb@637 | 463 | stylesheet_parameters, stylesheet_expressions, references) |
paulb@637 | 464 | |
paulb@637 | 465 | # General helper methods. |
paulb@637 | 466 | |
paulb@637 | 467 | def add_elements(self, positions, *element_names): |
paulb@637 | 468 | |
paulb@637 | 469 | """ |
paulb@637 | 470 | At the given 'positions', typically obtained as "selectors", add the |
paulb@637 | 471 | hierarchy of elements given in the 'element_names' parameters. |
paulb@637 | 472 | """ |
paulb@637 | 473 | |
paulb@637 | 474 | XSLForms.Utils.add_elements(positions, *element_names) |
paulb@637 | 475 | |
paulb@637 | 476 | def remove_elements(self, positions): |
paulb@637 | 477 | |
paulb@637 | 478 | """ |
paulb@637 | 479 | Remove elements at the given 'positions', typically obtained as |
paulb@637 | 480 | "selectors". |
paulb@637 | 481 | """ |
paulb@637 | 482 | |
paulb@637 | 483 | XSLForms.Utils.remove_elements(positions) |
paulb@637 | 484 | |
paulb@509 | 485 | def prepare_output(self, output_identifier): |
paulb@509 | 486 | |
paulb@509 | 487 | """ |
paulb@509 | 488 | Prepare the output stylesheet for the resource class or object 'self' |
paulb@509 | 489 | corresponding to the given 'output_identifier'. Return the template path |
paulb@509 | 490 | and the output stylesheet path in a 2-tuple. |
paulb@509 | 491 | """ |
paulb@509 | 492 | |
paulb@509 | 493 | template_filename, output_filename = self.template_resources[output_identifier] |
paulb@509 | 494 | output_path = os.path.abspath(os.path.join(self.resource_dir, output_filename)) |
paulb@509 | 495 | template_path = os.path.abspath(os.path.join(self.resource_dir, template_filename)) |
paulb@509 | 496 | XSLForms.Prepare.ensure_stylesheet(template_path, output_path) |
paulb@509 | 497 | return template_path, output_path |
paulb@509 | 498 | |
paulb@509 | 499 | def prepare_fragment(self, fragment_identifier): |
paulb@509 | 500 | |
paulb@509 | 501 | """ |
paulb@509 | 502 | Prepare the output stylesheet for the resource class or object 'self' |
paulb@509 | 503 | corresponding to the given 'fragment_identifier'. Return the template path |
paulb@509 | 504 | and the output stylesheet path in a 2-tuple. |
paulb@509 | 505 | """ |
paulb@509 | 506 | |
paulb@509 | 507 | output_identifier, fragment_filename, node_identifier = self.in_page_resources[fragment_identifier] |
paulb@509 | 508 | fragment_path = os.path.abspath(os.path.join(self.resource_dir, fragment_filename)) |
paulb@509 | 509 | template_filename, output_filename = self.template_resources[output_identifier] |
paulb@509 | 510 | template_path = os.path.abspath(os.path.join(self.resource_dir, template_filename)) |
paulb@509 | 511 | XSLForms.Prepare.ensure_stylesheet_fragment(template_path, fragment_path, node_identifier) |
paulb@509 | 512 | return template_path, fragment_path |
paulb@509 | 513 | |
paulb@509 | 514 | def prepare_initialiser(self, input_identifier, init_enumerations): |
paulb@509 | 515 | |
paulb@509 | 516 | """ |
paulb@509 | 517 | Prepare the initialising stylesheet for the resource class or object 'self' |
paulb@509 | 518 | corresponding to the given 'input_identifier' and 'init_enumerations' flag. |
paulb@509 | 519 | Return the template path and the initialising stylesheet path in a 2-tuple. |
paulb@509 | 520 | """ |
paulb@509 | 521 | |
paulb@509 | 522 | template_filename, input_filename = self.init_resources[input_identifier] |
paulb@509 | 523 | input_path = os.path.abspath(os.path.join(self.resource_dir, input_filename)) |
paulb@509 | 524 | template_path = os.path.abspath(os.path.join(self.resource_dir, template_filename)) |
paulb@509 | 525 | XSLForms.Prepare.ensure_input_stylesheet(template_path, input_path, init_enumerations) |
paulb@509 | 526 | return template_path, input_path |
paulb@509 | 527 | |
paulb@509 | 528 | def prepare_resources(cls): |
paulb@509 | 529 | |
paulb@509 | 530 | "Prepare the resources associated with the class 'cls'." |
paulb@509 | 531 | |
paulb@509 | 532 | for output_identifier in cls.template_resources.keys(): |
paulb@509 | 533 | prepare_output(cls, output_identifier) |
paulb@509 | 534 | for fragment_identifier in cls.in_page_resources.keys(): |
paulb@509 | 535 | prepare_fragment(cls, fragment_identifier) |
paulb@509 | 536 | |
paulb@509 | 537 | # NOTE: Using init_enumerations=1 here. |
paulb@509 | 538 | |
paulb@509 | 539 | for input_identifier in cls.init_resources.keys(): |
paulb@509 | 540 | prepare_initialiser(cls, input_identifier, 1) |
paulb@509 | 541 | |
paulb@634 | 542 | # Convenience methods for specifying resources. |
paulb@634 | 543 | |
paulb@634 | 544 | def split(filename): |
paulb@634 | 545 | |
paulb@634 | 546 | """ |
paulb@634 | 547 | Return a tuple containing the directory and filename without extension for |
paulb@634 | 548 | 'filename'. |
paulb@634 | 549 | """ |
paulb@634 | 550 | |
paulb@634 | 551 | d, leafname = os.path.split(filename) |
paulb@634 | 552 | name, ext = os.path.splitext(leafname) |
paulb@634 | 553 | return d, name |
paulb@634 | 554 | |
paulb@634 | 555 | def output(template_filename): |
paulb@634 | 556 | |
paulb@634 | 557 | """ |
paulb@634 | 558 | Return a tuple containing the 'template_filename' and a suitable output |
paulb@634 | 559 | stylesheet filename. |
paulb@634 | 560 | """ |
paulb@634 | 561 | |
paulb@634 | 562 | d, name = split(template_filename) |
paulb@634 | 563 | output_name = name.replace("_template", "_output") + os.path.extsep + "xsl" |
paulb@634 | 564 | return (template_filename, os.path.join(d, output_name)) |
paulb@634 | 565 | |
paulb@634 | 566 | def input(template_filename): |
paulb@634 | 567 | |
paulb@634 | 568 | """ |
paulb@634 | 569 | Return a tuple containing the 'template_filename' and a suitable output |
paulb@634 | 570 | stylesheet filename. |
paulb@634 | 571 | """ |
paulb@634 | 572 | |
paulb@634 | 573 | d, name = split(template_filename) |
paulb@634 | 574 | input_name = name.replace("_template", "_input") + os.path.extsep + "xsl" |
paulb@634 | 575 | return (template_filename, os.path.join(d, input_name)) |
paulb@634 | 576 | |
paulb@634 | 577 | def resources(filename, d="Resources"): |
paulb@634 | 578 | |
paulb@634 | 579 | """ |
paulb@634 | 580 | Return the resource directory for the given 'filename', using the optional |
paulb@634 | 581 | directory name 'd' to indicate the directory relative to the directory of |
paulb@634 | 582 | 'filename' (or the default directory name, indicating that the directory |
paulb@634 | 583 | called "Resources" - a sibling of 'filename' - is the resource directory). |
paulb@634 | 584 | |
paulb@634 | 585 | It is envisaged that callers provide the value of the __file__ special |
paulb@634 | 586 | variable to get the resource directory relative to a particular module. |
paulb@634 | 587 | """ |
paulb@634 | 588 | |
paulb@634 | 589 | return os.path.join(os.path.split(filename)[0], d) |
paulb@634 | 590 | |
paulb@353 | 591 | # vim: tabstop=4 expandtab shiftwidth=4 |