| | 74 | tl = threading.local() |
|---|
| | 75 | |
|---|
| | 76 | def resolver(URL, ID, ctxt): |
|---|
| | 77 | scheme = URL.split(':', 2)[0] |
|---|
| | 78 | if scheme not in ['wiki', 'ticket', 'browser', 'file']: |
|---|
| | 79 | return None |
|---|
| | 80 | |
|---|
| | 81 | auth, path = re.match('[^:]*://([^/]*)/(.*)', URL).group(1, 2) |
|---|
| | 82 | obj = _get_src(tl.env, tl.hdf, scheme, auth.replace('%2F', '/'), path) |
|---|
| | 83 | return obj.getStream() |
|---|
| | 84 | |
|---|
| | 85 | libxml2.setEntityLoader(resolver) |
|---|
| | 86 | |
|---|
| 226 | | def _get_obj(env, hdf, module, id, file): |
|---|
| 227 | | """Returns a filename (str or unicode), a trac browser Node, or a urllib |
|---|
| 228 | | addinfourl. |
|---|
| 229 | | """ |
|---|
| 230 | | |
|---|
| | 238 | def _transform(style_obj, doc_obj, params, env, hdf): |
|---|
| | 239 | import libxslt |
|---|
| | 240 | |
|---|
| | 241 | tl.env = env |
|---|
| | 242 | tl.hdf = hdf |
|---|
| | 243 | |
|---|
| | 244 | doc = None |
|---|
| | 245 | style = None; |
|---|
| | 246 | result = None; |
|---|
| | 247 | |
|---|
| | 248 | try: |
|---|
| | 249 | try: |
|---|
| | 250 | doc = _parse_xml(doc_obj) |
|---|
| | 251 | except Exception, e: |
|---|
| | 252 | raise Exception("Error parsing %s: %s" % (doc_obj, e)) |
|---|
| | 253 | |
|---|
| | 254 | try: |
|---|
| | 255 | styledoc = _parse_xml(style_obj) |
|---|
| | 256 | except Exception, e: |
|---|
| | 257 | raise Exception("Error parsing %s: %s" % (style_obj, e)) |
|---|
| | 258 | |
|---|
| | 259 | style = libxslt.parseStylesheetDoc(styledoc) |
|---|
| | 260 | if not style: |
|---|
| | 261 | styledoc.freeDoc() |
|---|
| | 262 | raise Exception("%s is not a valid stylesheet" % style_obj) |
|---|
| | 263 | |
|---|
| | 264 | result = style.applyStylesheet(doc, params) |
|---|
| | 265 | try: |
|---|
| | 266 | output = style.saveResultToString(result) |
|---|
| | 267 | except Exception, e: |
|---|
| | 268 | # detect empty result doc |
|---|
| | 269 | if str(e) != 'error return without exception set': |
|---|
| | 270 | raise e |
|---|
| | 271 | output = '' |
|---|
| | 272 | |
|---|
| | 273 | if result.get_type() == 'document_xml': |
|---|
| | 274 | ct = 'text/xml' |
|---|
| | 275 | elif result.get_type() == 'document_html': |
|---|
| | 276 | ct = 'text/html' |
|---|
| | 277 | elif result.get_type() == 'document_text': |
|---|
| | 278 | ct = 'text/plain' |
|---|
| | 279 | else: |
|---|
| | 280 | ct = 'application/octet-stream' |
|---|
| | 281 | |
|---|
| | 282 | finally: |
|---|
| | 283 | if doc: doc.freeDoc() |
|---|
| | 284 | if style: style.freeStylesheet() |
|---|
| | 285 | if result: result.freeDoc() |
|---|
| | 286 | tl.env = None |
|---|
| | 287 | tl.hdf = None |
|---|
| | 288 | |
|---|
| | 289 | return output, ct |
|---|
| | 290 | |
|---|
| | 291 | def _parse_xml(obj): |
|---|
| | 292 | if obj.isFile(): |
|---|
| | 293 | return libxml2.parseFile(obj.getFile()) |
|---|
| | 294 | else: |
|---|
| | 295 | return libxml2.readDoc(obj.getStream().read(), obj.getUrl(), None, 0) |
|---|
| | 296 | |
|---|
| | 297 | def _get_src(env, hdf, module, id, file): |
|---|
| | 306 | return BrowserSource(env, hdf, file) |
|---|
| | 307 | if module == 'file': |
|---|
| | 308 | return FileSource(env, id, file) |
|---|
| | 309 | if module == 'wiki' or module == 'ticket': |
|---|
| | 310 | return AttachmentSource(env, module, id, file) |
|---|
| | 311 | if module == 'url': |
|---|
| | 312 | return UrlSource(file) |
|---|
| | 313 | |
|---|
| | 314 | raise Exception("unsupported module '%s'" % module) |
|---|
| | 315 | |
|---|
| | 316 | class TransformSource(object): |
|---|
| | 317 | """Represents the source of an input (stylesheet or xml-doc) to the transformer""" |
|---|
| | 318 | |
|---|
| | 319 | def __init__(self, module, id, file, obj): |
|---|
| | 320 | self.module = module |
|---|
| | 321 | self.id = id |
|---|
| | 322 | self.file = file |
|---|
| | 323 | self.obj = obj |
|---|
| | 324 | |
|---|
| | 325 | def isFile(self): |
|---|
| | 326 | return False |
|---|
| | 327 | |
|---|
| | 328 | def getFile(self): |
|---|
| | 329 | return None |
|---|
| | 330 | |
|---|
| | 331 | def getUrl(self): |
|---|
| | 332 | return "%s://%s/%s" % (self.module, self.id.replace("/", "%2F"), self.file) |
|---|
| | 333 | |
|---|
| | 334 | def get_last_modified(self): |
|---|
| | 335 | import time |
|---|
| | 336 | return time.time() |
|---|
| | 337 | |
|---|
| | 338 | def __str__(self): |
|---|
| | 339 | return str(self.obj) |
|---|
| | 340 | |
|---|
| | 341 | def __del__(self): |
|---|
| | 342 | if hasattr(self.obj, 'close') and callable(self.obj.close): |
|---|
| | 343 | self.obj.close() |
|---|
| | 344 | |
|---|
| | 345 | class CloseableStream(object): |
|---|
| | 346 | """Implement close even if underlying stream doesn't""" |
|---|
| | 347 | |
|---|
| | 348 | def __init__(self, stream): |
|---|
| | 349 | self.stream = stream |
|---|
| | 350 | |
|---|
| | 351 | def read(self, len=None): |
|---|
| | 352 | return self.stream.read(len) |
|---|
| | 353 | |
|---|
| | 354 | def close(self): |
|---|
| | 355 | if hasattr(self.stream, 'close') and callable(self.stream.close): |
|---|
| | 356 | self.stream.close() |
|---|
| | 357 | |
|---|
| | 358 | class BrowserSource(TransformSource): |
|---|
| | 359 | def __init__(self, env, hdf, file): |
|---|
| 245 | | elif module == 'file': |
|---|
| | 364 | TransformSource.__init__(self, "browser", "source", file, obj) |
|---|
| | 365 | |
|---|
| | 366 | def getStream(self): |
|---|
| | 367 | return self.CloseableStream(self.obj.get_content()) |
|---|
| | 368 | |
|---|
| | 369 | def __str__(self): |
|---|
| | 370 | return self.obj.path |
|---|
| | 371 | |
|---|
| | 372 | def get_last_modified(self): |
|---|
| | 373 | return self.obj.get_last_modified() |
|---|
| | 374 | |
|---|
| | 375 | class FileSource(TransformSource): |
|---|
| | 376 | def __init__(self, env, id, file): |
|---|
| 258 | | elif module == 'wiki' or module == 'ticket': |
|---|
| | 389 | TransformSource.__init__(self, "file", id, file, obj) |
|---|
| | 390 | |
|---|
| | 391 | def isFile(self): |
|---|
| | 392 | return True |
|---|
| | 393 | |
|---|
| | 394 | def getFile(self): |
|---|
| | 395 | return self.obj |
|---|
| | 396 | |
|---|
| | 397 | def getStream(self): |
|---|
| | 398 | import urllib |
|---|
| | 399 | return urllib.urlopen(self.obj) |
|---|
| | 400 | |
|---|
| | 401 | def get_last_modified(self): |
|---|
| | 402 | return os.stat(self.obj).st_mtime |
|---|
| | 403 | |
|---|
| | 404 | def __str__(self): |
|---|
| | 405 | return self.obj |
|---|
| | 406 | |
|---|
| | 407 | class AttachmentSource(TransformSource): |
|---|
| | 408 | def __init__(self, env, module, id, file): |
|---|
| 260 | | attachment = Attachment(env, module, id, file) |
|---|
| 261 | | obj = attachment.path |
|---|
| 262 | | |
|---|
| 263 | | elif module == 'url': |
|---|
| | 410 | obj = Attachment(env, module, id, file) |
|---|
| | 411 | |
|---|
| | 412 | TransformSource.__init__(self, module, id, file, obj) |
|---|
| | 413 | |
|---|
| | 414 | def getStream(self): |
|---|
| | 415 | return self.obj.open() |
|---|
| | 416 | |
|---|
| | 417 | def get_last_modified(self): |
|---|
| | 418 | return os.stat(self.obj.path).st_mtime |
|---|
| | 419 | |
|---|
| | 420 | def __str__(self): |
|---|
| | 421 | return self.obj.path |
|---|
| | 422 | |
|---|
| | 423 | class UrlSource(TransformSource): |
|---|
| | 424 | def __init__(self, url): |
|---|
| 274 | | else: |
|---|
| 275 | | raise Exception("unsupported module '%s'" % module) |
|---|
| 276 | | |
|---|
| 277 | | return obj |
|---|
| 278 | | |
|---|
| 279 | | def _close_obj(obj): |
|---|
| 280 | | if hasattr(obj, 'close') and callable(obj.close): |
|---|
| 281 | | obj.close() |
|---|
| 282 | | |
|---|
| 283 | | def _obj_tostr(obj): |
|---|
| 284 | | if isinstance(obj, str) or isinstance(obj, unicode): |
|---|
| 285 | | return obj |
|---|
| 286 | | if isinstance(obj, Node): |
|---|
| 287 | | return obj.path |
|---|
| 288 | | if hasattr(obj, 'url'): |
|---|
| 289 | | return obj.url |
|---|
| 290 | | return str(obj) |
|---|
| 291 | | |
|---|
| 292 | | def _transform(style_obj, doc_obj, params): |
|---|
| 293 | | import libxslt |
|---|
| 294 | | |
|---|
| 295 | | try: |
|---|
| 296 | | styledoc = _parse_xml(style_obj) |
|---|
| 297 | | except Exception, e: |
|---|
| 298 | | raise Exception("Error parsing %s: %s" % (_obj_tostr(style_obj), e)) |
|---|
| 299 | | |
|---|
| 300 | | try: |
|---|
| 301 | | doc = _parse_xml(doc_obj) |
|---|
| 302 | | except Exception, e: |
|---|
| 303 | | styledoc.freeDoc() |
|---|
| 304 | | raise Exception("Error parsing %s: %s" % (_obj_tostr(doc_obj), e)) |
|---|
| 305 | | |
|---|
| 306 | | style = libxslt.parseStylesheetDoc(styledoc) |
|---|
| 307 | | if not style: |
|---|
| 308 | | styledoc.freeDoc() |
|---|
| 309 | | doc.freeDoc() |
|---|
| 310 | | raise Exception("%s is not a valid stylesheet" % _obj_tostr(style_obj)) |
|---|
| 311 | | |
|---|
| 312 | | result = style.applyStylesheet(doc, params) |
|---|
| 313 | | try: |
|---|
| 314 | | output = style.saveResultToString(result) |
|---|
| 315 | | except Exception, e: |
|---|
| 316 | | # detect empty result doc |
|---|
| 317 | | if str(e) != 'error return without exception set': |
|---|
| 318 | | raise e |
|---|
| 319 | | output = '' |
|---|
| 320 | | |
|---|
| 321 | | if result.get_type() == 'document_xml': |
|---|
| 322 | | ct = 'text/xml' |
|---|
| 323 | | elif result.get_type() == 'document_html': |
|---|
| 324 | | ct = 'text/html' |
|---|
| 325 | | elif result.get_type() == 'document_text': |
|---|
| 326 | | ct = 'text/plain' |
|---|
| 327 | | else: |
|---|
| 328 | | ct = 'application/octet-stream' |
|---|
| 329 | | |
|---|
| 330 | | style.freeStylesheet() |
|---|
| 331 | | doc.freeDoc() |
|---|
| 332 | | result.freeDoc() |
|---|
| 333 | | |
|---|
| 334 | | return output, ct |
|---|
| 335 | | |
|---|
| 336 | | def _parse_xml(obj): |
|---|
| 337 | | import libxml2 |
|---|
| 338 | | |
|---|
| 339 | | if isinstance(obj, str) or isinstance(obj, unicode): |
|---|
| 340 | | return libxml2.parseFile(obj) |
|---|
| 341 | | if isinstance(obj, Node): |
|---|
| 342 | | return libxml2.parseDoc(obj.get_content().read()) |
|---|
| 343 | | if hasattr(obj, 'read') and callable(obj.read): |
|---|
| 344 | | return libxml2.parseDoc(obj.read()) |
|---|
| 345 | | |
|---|
| 346 | | raise Exception("unsupported object type '%s'" % type(obj)) |
|---|
| | 431 | TransformSource.__init__(self, "url", None, url, obj) |
|---|
| | 432 | |
|---|
| | 433 | def getStream(self): |
|---|
| | 434 | return self.obj |
|---|
| | 435 | |
|---|
| | 436 | def getUrl(self): |
|---|
| | 437 | return self.file |
|---|
| | 438 | |
|---|
| | 439 | def get_last_modified(self): |
|---|
| | 440 | import time |
|---|
| | 441 | |
|---|
| | 442 | lm = self.obj.info().getdate('Last-modified') |
|---|
| | 443 | if lm: |
|---|
| | 444 | return time.mktime(lm) |
|---|
| | 445 | return time.time() |
|---|
| | 446 | |
|---|
| | 447 | def __str__(self): |
|---|
| | 448 | return self.obj.url |
|---|
| 414 | | def _get_last_modified(self, obj): |
|---|
| 415 | | import time |
|---|
| 416 | | |
|---|
| 417 | | if isinstance(obj, str) or isinstance(obj, unicode): |
|---|
| 418 | | return os.stat(obj).st_mtime |
|---|
| 419 | | if isinstance(obj, Node): |
|---|
| 420 | | return obj.get_last_modified() |
|---|
| 421 | | if hasattr(obj, 'info') and callable(obj.info): |
|---|
| 422 | | lm = obj.info().getdate('Last-modified') |
|---|
| 423 | | if lm: |
|---|
| 424 | | return time.mktime(lm) |
|---|
| 425 | | return time.time() |
|---|
| 426 | | |
|---|