#! /usr/bin/env python
import cgi
import os
import re
import medusa.script_handler
import medusa.xmlrpc_handler
import medusa.producers
import PageTemplates.PageTemplate
import PageTemplates.Expressions
class MedusaPageTemplate(PageTemplates.PageTemplate.PageTemplate):
""" MedusaPageTemplate -> PageTemplate type for use by Medusa handlers
This type constructs a PT context from a Medusa request when
instantiated. Instances also read in their content from disk when
created. Clients supply the path to the page template file,
along with the HTTP query string and header data.
The built-ins that this type provides may differ from the built-ins
provided by ZopePageTemplates. Specifically:
* request - Constructed from the query string and form data if present.
* here - This mapping may be provided by the client. Default is empty.
* root - This mapping may be provided by the client. Default is empty.
* user - This mapping may be provided by the client. Default is empty.
* container - By default, a custom mapping filled with the results of
stat and other os module functions. Clients can specify that this data
not be made available.
* modules - Instances reuse PageTemplates.Expressions.SecureModuleImporter
for importing modules. The behavior of SecureModuleImporter allows an
unrestricted import of any module, but in practice, modules sometimes
cannot be imported.
"""
modules = PageTemplates.Expressions.SecureModuleImporter
context_keys = ('template', 'nothing', 'options', 'request', 'modules',
'here', 'container', 'root', 'user', )
stat_keys = ('st_mode', 'st_ino', 'st_dev', 'st_nlink', 'st_uid', 'st_gid',
'st_size', 'st_atime', 'st_mtime', 'st_ctime')
def __init__(self, path, query, header, here={}, options={}, root={},
user={}, restricted=0, edit_content_type='text/html'):
head, tail = os.path.split(path)
## these two attributes are so common in PT markup that it would be a
## shame to not set them.
self.id = tail
self.title = "Page Template '%s'" % (tail, )
self.here = here
self.options = options
self.root = root
self.template = self
self.user = user
## gather data about the folder that contains this script
self.container = {}
if not restricted:
self.container.update({'is_mount' : os.path.ismount(head),
'is_link' : os.path.islink(head),
'files' : os.listdir(head),
'name' : os.path.split(head)[-1], })
self.container.update(dict(zip(self.stat_keys, os.stat(head))))
## turn a query string and/or form data in the header into a mapping
self.request = r = {}
if query:
if query.startswith('?'):
query = query[1:]
r.update(cgi.parse_qs(query, 0, 0))
if header:
r.update(cgi.parse_qs(header, 0, 0))
[r.__setitem__(k, v[0]) for k, v in r.items() if len(v) == 1]
## finally, read in the file as a convenience for the client
self.pt_edit(open(path, 'r').read(), edit_content_type)
def pt_getContext(self):
""" redefine pt_getContext() to produce our kind of context """
return dict([(k, getattr(self, k, None)) for k in self.context_keys])
class pagetemplate_handler_base(medusa.script_handler.script_handler):
""" pagetemplate_handler_base -> handler that serves up rendered PT files
This handler type reuses the majority of the behavior defined by
medusa.script_handler.script_handler. The 'match' function is the most
notable of the reused methods.
The 'handle_request' method defined below mirrors the script_handler
behavior. It also allows POST verbs, and uses the collector from
medusa.xmlrpc_handler to gather POST data.
Clients should not use this class directly. Instead, clients should
instantiate or subclass the two concrete classes defined below,
pagetemplate_handler and pagetemplate_xml_handler.
"""
pt_exceptions = (PageTemplates.PageTemplate.PTRuntimeError,
PageTemplates.TALES.TALESError,)
def __init__(self, filesystem):
## explicit call to the super; don't want clients to forget to do this
medusa.script_handler.script_handler.__init__(self, filesystem)
def handle_request(self, request):
[path, params, query, fragment] = request.split_uri()
if not self.filesystem.isfile(path):
request.error(404)
return
self.hits.increment()
request.script_filename = self.filesystem.translate(path)
if request.command in ('post', 'put'):
cl = request.get_header('content-length')
length = int(cl)
if not cl:
request.error(411)
return
else:
col = medusa.xmlrpc_handler.collector(self, request)
request.collector = col
elif request.command in ('get', ):
self.continue_request(None, request)
else:
request.error(405)
return
def continue_request(self, data, request):
""" implements the signature expected by xmlrpc_handler.collector """
path, params, query, fragment = request.split_uri()
try:
pt_obj = self.get_pagetemplate(request.script_filename, query, data)
response = self.render_pagetemplate(request, pt_obj)
except self.pt_exceptions, ex:
print '%r' % ex, ex
response = '%s' % (ex, )
request['Content-Type'] = self.content_type
request['Content-Length'] = len(response)
request.push(response)
request.done()
return
def get_pagetemplate(self, filename, query, data):
""" get_pagetemplate -> return a PT object. subclasses can override """
pt = MedusaPageTemplate(filename, query, data)
return pt
def render_pagetemplate(self, request, pagetemplate):
""" render_pagetemplate -> render a PT object. subclasses can override """
options = {}
res = pagetemplate(**options)
return res
def __repr__(self):
return '' % id(self)
def status(self):
return medusa.producers.simple_producer("""
PageTemplate Handler
""" % (self.hits, self.exceptions, ))
class pagetemplate_handler(pagetemplate_handler_base):
""" pagetemplate_handler -> Medusa HTTP handler
Concrete handler that returns rendered PT markup as HTML.
"""
content_type = 'text/html'
extension = 'pt'
script_regex = re.compile(r'.*/([^/]+\.%s)' % extension, re.IGNORECASE)
class pagetemplate_xml_handler(pagetemplate_handler_base):
""" pagetemplate_xml_handler -> Medusa HTTP handler
Concrete handler that returns rendered PT markup as XML.
"""
content_type = 'text/xml'
extension = 'ptx'
script_regex = re.compile(r'.*/([^/]+\.%s)' % extension, re.IGNORECASE)