# $Id: __init__.py,v 1.17 2003/04/16 10:53:28 jribbens Exp $ """Web Template system""" import sys, os, re import jon.cgi as cgi _code_cache = {} _replace_validchunk = re.compile("^[A-Za-z0-9_]+$") def replace(wt, template, namespace): """Substitute variables in template and output with jcgi.write Variables are indicated by surrounding them with "$$". If the name is suffixed with "()" then it must be a function not a variable. The default encoding is to call jcgi.html_encode unless the variable is prefixed with "%" in which case it is jcgi.url_encode or the variable is prefixed with "=" in which case no encoding is used. If the variable is empty (i.e. the template contains "$$$$") then a literal "$$" is output. """ html = 0 for chunk in template.split("$$"): html = not html if html: wt.req.write(chunk) else: if chunk == "": wt.req.write("$$") else: encode = cgi.html_encode if chunk[0] == "=": encode = lambda x: x chunk = chunk[1:] elif chunk[0] == "%": encode = cgi.url_encode chunk = chunk[1:] if not _replace_validchunk.match(chunk): raise ValueError, "'%s' is not a valid identifier" % chunk if callable(getattr(namespace, chunk)): out = getattr(namespace, chunk)() else: out = getattr(namespace, chunk) if not isinstance(out, unicode): out = str(out) wt.req.write(encode(out)) class TemplateCode: template_as_file = 0 def __init__(self, outer, wt=None): self.outer = outer if wt: self.wt = wt else: self.wt = self.outer.wt self.req = self.wt.req def process(self, template, selected=None): process(self.wt, template, self, selected) def main(self, template): self.process(template) class GlobalTemplate(TemplateCode): def main(self, template): self._pageTemplate = template if hasattr(self, "get_template"): self.process(self.get_template()) else: if self.template_as_file: self.process(open(self.template_name(), "rb")) else: self.process(open(self.template_name(), "rb").read()) def template_name(self): return self.wt.etc + "/template.html" class page(TemplateCode): pass class _page(TemplateCode): def main(self, template): # Contents of the page block are ignored, the original template # is substituted instead obj = self.outer.page(self.outer) if obj.template_as_file: import StringIO as cStringIO obj.main(StringIO.StringIO(self.outer._pageTemplate)) else: obj.main(self.outer._pageTemplate) _process_sb = re.compile("") def process(wt, template, namespace, selected=None): pos = 0 while 1: start = _process_sb.search(template, pos) if not start: break name = start.group(1) if start.lastindex == 2: # shorttag end = start.end() endmark = "" else: endmark = "" % name end = template.find(endmark, start.end()) if end == -1: raise ValueError, "No end block for %s" % name replace(wt, template[pos:start.start()], namespace) if name != "_null" and (selected == None or selected == name or (type(selected) == type([]) and name in selected) or (type(selected) == type(()) and name in selected)): obj = getattr(namespace, name)(namespace, wt) if obj.template_as_file: import cStringIO as StringIO obj.main(StringIO.StringIO(template[start.end():end])) else: obj.main(template[start.end():end]) del obj pos = end + len(endmark) replace(wt, template[pos:], namespace) class Handler(cgi.Handler): cache_code = 0 def _get_template(self): for i in xrange(0, 4): template = self.req.environ.get("REDIRECT_" * i + "WT_TEMPLATE_FILENAME") if template: return template raise Exception, "Couldn't determine template filename" def _get_etc(self): sp = os.path.split(self.req.environ["DOCUMENT_ROOT"]) if sp[1] == "": sp = os.path.split(sp[0]) return sp[0] + "/etc" def _get_code(self): code = self.req.environ.get("PATH_TRANSLATED") if code is None: code = self.req.environ["SCRIPT_FILENAME"] return code def pre_request(self, obj): pass def post_request(self, obj): pass def process(self, req): self.req = req self.template = self._get_template() self.etc = self._get_etc() codefname = self._get_code() try: namespace = _code_cache[codefname] except KeyError: namespace = { "wt": sys.modules[__name__] } code = compile(open(codefname, "r").read(), codefname, "exec") exec code in namespace del code if self.cache_code: _code_cache[codefname] = namespace obj = namespace["main"](None, self) self.pre_request(obj) if obj.template_as_file: obj.main(open(self.template, "rb")) else: obj.main(open(self.template, "rb").read()) self.post_request(obj) class DebugHandler(cgi.DebugHandlerMixIn, Handler): pass