try: from mod_python import apache, util except ImportError: from dummy import apache, util import py from base import handler_decorator class checknotfound(handler_decorator): """check if a file is found return a dict if not, return the original handler's return value if it is, doesn't change content-type (so should be used in pipes where HTML is produced only, decorate this in turn and use that as a decorator for pipes that want something else) """ def __init__(self, title='not found', body='File %(filename)s could not be found.'): self.title = title self.body = body def handler(self, orghandler, request): from templess.templess import elnode, textnode fpath = py.path.local(request.filename) if fpath.check(): return orghandler(request) intdata = { 'filename': request.filename, } intdata.update(fpath.__dict__) titlenode = elnode('title', {}, None) textnode(self.title % intdata, titlenode) return { 'head': [titlenode], 'body': [self.body % intdata], } class checkerrors(handler_decorator): def handler(self, orghandler, request): from templess.templess import elnode, textnode try: return orghandler(request) except: request.content_type = 'text/html; charset=UTF-8' exc, e, tb = py.std.sys.exc_info() tbstr = '\n'.join(py.std.traceback.format_tb(tb)) del tb title = elnode('title', {}, None) textnode('%s - %s' % (exc, e), title) h1 = elnode('h1', {}, None) textnode('%s - %s' % (exc, e), h1) pre = elnode('pre', {}, None) textnode(tbstr, pre) return { 'head': title, 'body': [h1, pre], } # XXX not thread-safe yet!! class cache(handler_decorator): def __init__(self, createkey=lambda r: r.filename, createmtime=lambda r: py.path.local(r.filename).mtime(), condition=lambda r: True, checktype=lambda d: True): self.cache = {} self.createkey = createkey self.createmtime = createmtime self.condition = condition self.checktype = checktype def handler(self, orghandler, request): try: key = self.createkey(request) newmtime = self.createmtime(request) usecache = self.condition(request) if usecache: if self.cache.has_key(key): orgmtime, ct, headers, data = self.cache[key] if orgmtime == newmtime: request.content_type = ct request.headers_out.update(headers) self.setheader(request, 'hit') return data else: self.setheader(request, 'miss - file changed') else: self.setheader(request, 'miss - no such key %r' % (key,)) else: self.setheader(request, 'miss - condition failed') except: exc, e, tb = py.std.sys.exc_info() self.setheader(request, 'miss - exception %s - %s' % (exc, e)) del tb return orghandler(request) data = orghandler(request) # cache only if conditions are met and status is 200 if usecache: if request.status == 200: if self.checktype(data): headerscopy = dict( [(k, v) for (k, v) in request.headers_out.iteritems() if k != 'X-mod_deco-cache'] ) self.cache[key] = (newmtime, request.content_type, headerscopy, data) self.setheader(request, 'cached') else: self.setheader(request, 'not cached - unexpected type %s' % (type(data),)) else: self.setheader(request, 'not cached - status != 200') else: self.setheader(request, ('not cached - condition failed')) return data def setheader(self, request, value): if request.headers_out.has_key('X-mod_deco-cache'): request.headers_out.add('X-mod_deco-cache', value) else: request.headers_out['X-mod_deco-cache'] = value class dictvars(handler_decorator): """add keys to a dict""" def __init__(self, **kwargs): self.kwargs = kwargs def handler(self, orghandler, request): data = orghandler(request) if isinstance(data, dict): data.update(self.kwargs) return data class directories(handler_decorator): """takes care of presenting views for directories provides default index ('index.html') functionality, and if there's no default index found, optionally of presenting a file listing """ def __init__(self, template, defaultindexes=['index.rst', 'index.html', 'index.txt'], show_listing=True): self.template = template self.defaultindexes = defaultindexes self.show_listing = show_listing def handler(self, orghandler, request): from mimetypes import guess_type fpath = py.path.local(request.filename) # if we're not a directory, pass the request on to a deeper level if not fpath.check(dir=True): return orghandler(request) # handle default indexes for d in self.defaultindexes: dpath = fpath / d if dpath.check(): mtype = ({ '.html': 'text/html', '.htm': 'text/html', '.rst': 'text/plain', '.txt': 'text/plain', }).get(dpath.ext, guess_type(dpath.basename)[0]) request.content_type = '%s; charset=UTF-8' % (mtype,) return dpath.read() if self.show_listing: return self.show_dir_listing(request, fpath) return self.listing_not_allowed(request) def show_dir_listing(self, request, fpath): from templess import templess from mimetypes import guess_type import time data = [] dlist = [x for x in sorted(fpath.listdir(), lambda a, b: cmp(a.basename, b.basename)) if not x.basename.startswith('.')] ftype = guess_type(fpath.basename)[0] iconsrc = '/icons/%s' % ({ 'image/png': 'image2.png', 'image/gif': 'image2.png', 'image/jpeg': 'image2.png', 'text/plain': 'text.png', 'text/html': 'text.png', 'application/pdf': 'pdf.png', }).get(ftype, 'generic.png') for f in dlist: mtime = fpath.mtime() data.append({ 'isdir': f.check(dir=True), 'isfile': f.check(file=True), 'fname': f.basename, 'size': f.size(), 'relpath': f.check(dir=True) and f.basename + '/' or f.basename, 'type': guess_type(f.basename)[0], 'lastmodlocal': time.strftime('%Y/%m/%d %H:%M', time.localtime(mtime)), 'lastmodgmt': time.strftime('%Y/%m/%d %H:%M', time.gmtime(mtime)), 'iconsrc': iconsrc, }) env = { 'basename': fpath.basename, 'items': data, } tpath = py.path.local(self.template) t = templess.template(tpath) tree = t.render(env) return { 'head': list(find(tree.find('head'))[0]), 'body': list(find(tree.find('body'))[0]), } def listing_not_allowed(self, request): from templess.templess import elnode, textnode request.content_type = 'text/html; charset=UTF-8' title = elnode('title', {}, None) textnode('Directory listing not allowed!', title) return { 'head': [title], 'body': 'Directory listing not allowed.', } class defaultindexes(handler_decorator): """more naive directory handling instead of reading the contents of the file itself, this just sets request.filename to the default index name if found """ def __init__(self, indexes=['index.html', 'index.txt', 'index.rst']): self.indexes = indexes def handler(self, orghandler, request): fpath = py.path.local(request.filename) if fpath.check(dir=True): for d in self.indexes: dpath = fpath / d if dpath.check(): request.filename = str(dpath) return orghandler(request)