Added kwarg capability to embed tag. Adjusted reference substitution to account for...
[philo.git] / contrib / penfield / templatetags / embed.py
1 from django import template
2 from django.contrib.contenttypes.models import ContentType
3 from django.conf import settings
4 from philo.utils import LOADED_TEMPLATE_ATTR
5
6
7 register = template.Library()
8
9
10 class EmbedNode(template.Node):
11         def __init__(self, content_type, varname, object_pk=None, template_name=None, kwargs=None):
12                 assert template_name is not None or object_pk is not None
13                 self.content_type = content_type
14                 self.varname = varname
15                 
16                 kwargs = kwargs or {}
17                 for k, v in kwargs.items():
18                         kwargs[k] = template.Variable(v)
19                 self.kwargs = kwargs
20                 
21                 if object_pk is not None:
22                         self.object_pk = object_pk
23                         try:
24                                 self.instance = content_type.get_object_for_this_type(pk=object_pk)
25                         except content_type.model_class().DoesNotExist:
26                                 self.instance = False
27                 else:
28                         self.instance = None
29                 
30                 if template_name is not None:
31                         try:
32                                 self.template = template.loader.get_template(template_name)
33                         except template.TemplateDoesNotExist:
34                                 self.template = False
35                 else:
36                         self.template = None
37         
38         def render(self, context):
39                 if self.template_name is not None:
40                         if self.template is False:
41                                 return settings.TEMPLATE_STRING_IF_INVALID
42                         
43                         if self.varname not in context:
44                                 context[self.varname] = {}
45                         context[self.varname][self.content_type] = self.template
46                         
47                         return ''
48                 
49                 # Otherwise self.instance should be set. Render the instance with the appropriate template!
50                 if self.instance is None or self.instance is False:
51                         return settings.TEMPLATE_STRING_IF_INVALID
52                 
53                 try:
54                         t = context[self.varname][self.content_type]
55                 except KeyError:
56                         return settings.TEMPLATE_STRING_IF_INVALID
57                 
58                 context.push()
59                 context['embedded'] = self.instance
60                 for k, v in self.kwargs.items():
61                         self.kwargs[k] = v.resolve(context)
62                 context.update(self.kwargs)
63                 t_rendered = t.render(context)
64                 context.pop()
65                 return t_rendered
66
67
68 def get_embedded(self):
69         return template.loader.get_template(self.template_name)
70
71
72 setattr(EmbedNode, LOADED_TEMPLATE_ATTR, property(get_embedded))
73
74
75 def do_embed(parser, token):
76         """
77         The {% embed %} tag can be used in three ways:
78         {% embed as <varname> %} :: This sets which variable will be used to track embedding template names for the current context. Default: "embed"
79         {% embed <app_label>.<model_name> with <template> %} :: Sets which template will be used to render a particular model.
80         {% embed <app_label>.<model_name> <object_pk> [<argname>=<value> ...]%} :: Embeds the instance specified by the given parameters in the document with the previously-specified template. Any kwargs provided will be passed into the context of the template.
81         """
82         args = token.split_contents()
83         tag = args[0]
84         
85         if len(args) < 2:
86                 raise template.TemplateSyntaxError('"%s" template tag must have at least three arguments.' % tag)
87         elif len(args) == 3 and args[1] == "as":
88                 parser._embedNodeVarName = args[2]
89                 return template.defaulttags.CommentNode()
90         else:
91                 if '.' not in args[1]:
92                         raise template.TemplateSyntaxError('"%s" template tag expects the first argument to be of the form app_label.model' % tag)
93                 
94                 app_label, model = args[1].split('.')
95                 try:
96                         ct = ContentType.objects.get(app_label=app_label, model=model)
97                 except ContentType.DoesNotExist:
98                         raise template.TemplateSyntaxError('"%s" template tag option "references" requires an argument of the form app_label.model which refers to an installed content type (see django.contrib.contenttypes)' % tag)
99                 
100                 if args[2] == "with":
101                         if len(args) > 4:
102                                 raise template.TemplateSyntaxError('"%s" template tag may have no more than four arguments.' % tag)
103                         
104                         if args[3][0] not in ['"', "'"] and args[3][-1] not in ['"', "'"]:
105                                 raise template.TemplateSyntaxError('"%s" template tag expects the template name to be in quotes.' % tag)
106                         if args[3][0] != args[3][-1]:
107                                 raise template.TemplateSyntaxError('"%s" template tag called with non-matching quotes.' % tag)
108                         
109                         template_name = args[3].strip('"\'')
110                         
111                         return EmbedNode(ct, template_name=template_name, varname=getattr(parser, '_embedNodeVarName', 'embed'))
112                 object_pk = args[2]
113                 varname = getattr(parser, '_embedNodeVarName', 'embed')
114                 
115                 remaining_args = args[3:]
116                 kwargs = {}
117                 for arg in remaining_args:
118                         if '=' not in arg:
119                                 raise template.TemplateSyntaxError("Invalid keyword argument for '%s' template tag: %s" % (tag, arg))
120                         k, v = arg.split('=')
121                         kwargs[k] = v
122                 
123                 return EmbedNode(ct, object_pk=object_pk, varname=varname, kwargs=kwargs)
124
125
126 register.tag('embed', do_embed)