diff options
Diffstat (limited to 'webapp/django/template/loader_tags.py')
-rw-r--r-- | webapp/django/template/loader_tags.py | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/webapp/django/template/loader_tags.py b/webapp/django/template/loader_tags.py new file mode 100644 index 0000000000..38b2fff9fd --- /dev/null +++ b/webapp/django/template/loader_tags.py @@ -0,0 +1,192 @@ +from django.template import TemplateSyntaxError, TemplateDoesNotExist, Variable +from django.template import Library, Node, TextNode +from django.template.loader import get_template, get_template_from_string, find_template_source +from django.conf import settings +from django.utils.safestring import mark_safe + +register = Library() + +class ExtendsError(Exception): + pass + +class BlockNode(Node): + def __init__(self, name, nodelist, parent=None): + self.name, self.nodelist, self.parent = name, nodelist, parent + + def __repr__(self): + return "<Block Node: %s. Contents: %r>" % (self.name, self.nodelist) + + def render(self, context): + context.push() + # Save context in case of block.super(). + self.context = context + context['block'] = self + result = self.nodelist.render(context) + context.pop() + return result + + def super(self): + if self.parent: + return mark_safe(self.parent.render(self.context)) + return '' + + def add_parent(self, nodelist): + if self.parent: + self.parent.add_parent(nodelist) + else: + self.parent = BlockNode(self.name, nodelist) + +class ExtendsNode(Node): + must_be_first = True + + def __init__(self, nodelist, parent_name, parent_name_expr, template_dirs=None): + self.nodelist = nodelist + self.parent_name, self.parent_name_expr = parent_name, parent_name_expr + self.template_dirs = template_dirs + + def __repr__(self): + if self.parent_name_expr: + return "<ExtendsNode: extends %s>" % self.parent_name_expr.token + return '<ExtendsNode: extends "%s">' % self.parent_name + + def get_parent(self, context): + if self.parent_name_expr: + self.parent_name = self.parent_name_expr.resolve(context) + parent = self.parent_name + if not parent: + error_msg = "Invalid template name in 'extends' tag: %r." % parent + if self.parent_name_expr: + error_msg += " Got this from the '%s' variable." % self.parent_name_expr.token + raise TemplateSyntaxError, error_msg + if hasattr(parent, 'render'): + return parent # parent is a Template object + try: + source, origin = find_template_source(parent, self.template_dirs) + except TemplateDoesNotExist: + raise TemplateSyntaxError, "Template %r cannot be extended, because it doesn't exist" % parent + else: + return get_template_from_string(source, origin, parent) + + def render(self, context): + compiled_parent = self.get_parent(context) + parent_blocks = dict([(n.name, n) for n in compiled_parent.nodelist.get_nodes_by_type(BlockNode)]) + for block_node in self.nodelist.get_nodes_by_type(BlockNode): + # Check for a BlockNode with this node's name, and replace it if found. + try: + parent_block = parent_blocks[block_node.name] + except KeyError: + # This BlockNode wasn't found in the parent template, but the + # parent block might be defined in the parent's *parent*, so we + # add this BlockNode to the parent's ExtendsNode nodelist, so + # it'll be checked when the parent node's render() is called. + + # Find out if the parent template has a parent itself + for node in compiled_parent.nodelist: + if not isinstance(node, TextNode): + # If the first non-text node is an extends, handle it. + if isinstance(node, ExtendsNode): + node.nodelist.append(block_node) + # Extends must be the first non-text node, so once you find + # the first non-text node you can stop looking. + break + else: + # Keep any existing parents and add a new one. Used by BlockNode. + parent_block.parent = block_node.parent + parent_block.add_parent(parent_block.nodelist) + parent_block.nodelist = block_node.nodelist + return compiled_parent.render(context) + +class ConstantIncludeNode(Node): + def __init__(self, template_path): + try: + t = get_template(template_path) + self.template = t + except: + if settings.TEMPLATE_DEBUG: + raise + self.template = None + + def render(self, context): + if self.template: + return self.template.render(context) + else: + return '' + +class IncludeNode(Node): + def __init__(self, template_name): + self.template_name = Variable(template_name) + + def render(self, context): + try: + template_name = self.template_name.resolve(context) + t = get_template(template_name) + return t.render(context) + except TemplateSyntaxError, e: + if settings.TEMPLATE_DEBUG: + raise + return '' + except: + return '' # Fail silently for invalid included templates. + +def do_block(parser, token): + """ + Define a block that can be overridden by child templates. + """ + bits = token.contents.split() + if len(bits) != 2: + raise TemplateSyntaxError, "'%s' tag takes only one argument" % bits[0] + block_name = bits[1] + # Keep track of the names of BlockNodes found in this template, so we can + # check for duplication. + try: + if block_name in parser.__loaded_blocks: + raise TemplateSyntaxError, "'%s' tag with name '%s' appears more than once" % (bits[0], block_name) + parser.__loaded_blocks.append(block_name) + except AttributeError: # parser.__loaded_blocks isn't a list yet + parser.__loaded_blocks = [block_name] + nodelist = parser.parse(('endblock', 'endblock %s' % block_name)) + parser.delete_first_token() + return BlockNode(block_name, nodelist) + +def do_extends(parser, token): + """ + Signal that this template extends a parent template. + + This tag may be used in two ways: ``{% extends "base" %}`` (with quotes) + uses the literal value "base" as the name of the parent template to extend, + or ``{% extends variable %}`` uses the value of ``variable`` as either the + name of the parent template to extend (if it evaluates to a string) or as + the parent tempate itelf (if it evaluates to a Template object). + """ + bits = token.contents.split() + if len(bits) != 2: + raise TemplateSyntaxError, "'%s' takes one argument" % bits[0] + parent_name, parent_name_expr = None, None + if bits[1][0] in ('"', "'") and bits[1][-1] == bits[1][0]: + parent_name = bits[1][1:-1] + else: + parent_name_expr = parser.compile_filter(bits[1]) + nodelist = parser.parse() + if nodelist.get_nodes_by_type(ExtendsNode): + raise TemplateSyntaxError, "'%s' cannot appear more than once in the same template" % bits[0] + return ExtendsNode(nodelist, parent_name, parent_name_expr) + +def do_include(parser, token): + """ + Loads a template and renders it with the current context. + + Example:: + + {% include "foo/some_include" %} + """ + bits = token.contents.split() + if len(bits) != 2: + raise TemplateSyntaxError, "%r tag takes one argument: the name of the template to be included" % bits[0] + path = bits[1] + if path[0] in ('"', "'") and path[-1] == path[0]: + return ConstantIncludeNode(path[1:-1]) + return IncludeNode(bits[1]) + +register.tag('block', do_block) +register.tag('extends', do_extends) +register.tag('include', do_include) |