11ccd88c8832a837247f48b0c63b528c72748b92
[philo.git] / philo / templatetags / containers.py
1 """
2 The container template tags are automatically included as builtins if :mod:`philo` is an installed app.
3
4 """
5
6 from django import template
7 from django.conf import settings
8 from django.contrib.contenttypes.models import ContentType
9 from django.core.exceptions import ObjectDoesNotExist
10 from django.db.models import Q
11 from django.utils.safestring import SafeUnicode, mark_safe
12
13
14 register = template.Library()
15
16
17 CONTAINER_CONTEXT_KEY = 'philo_container_context'
18
19
20 class ContainerContext(object):
21         def __init__(self, contentlets, references):
22                 self.contentlets = dict(((c.name, c) for c in contentlets))
23                 self.references = dict((((c.name, ContentType.objects.get_for_id(c.content_type_id)), c) for c in references))
24
25
26 class ContainerNode(template.Node):
27         def __init__(self, name, references=None, as_var=None):
28                 self.name = name
29                 self.as_var = as_var
30                 self.references = references
31         
32         def render(self, context):
33                 container_content = self.get_container_content(context)
34                 
35                 if self.as_var:
36                         context[self.as_var] = container_content
37                         return ''
38                 
39                 return container_content
40         
41         def get_container_content(self, context):
42                 try:
43                         container_context = context.render_context[CONTAINER_CONTEXT_KEY]
44                 except KeyError:
45                         try:
46                                 page = context['page']
47                         except KeyError:
48                                 return settings.TEMPLATE_STRING_IF_INVALID
49                         
50                         contentlet_specs, contentreference_specs = page.template.containers
51                         contentlets = page.contentlets.filter(name__in=contentlet_specs)
52                         q = Q()
53                         for name, ct in contentreference_specs.items():
54                                 q |= Q(name=name, content_type=ct)
55                         references = page.contentreferences.filter(q)
56                         
57                         container_context = ContainerContext(contentlets, references)
58                         context.render_context[CONTAINER_CONTEXT_KEY] = container_context
59                 
60                 if self.references:
61                         # Then it's a content reference.
62                         try:
63                                 contentreference = container_context.references[(self.name, self.references)]
64                         except KeyError:
65                                 content = ''
66                         else:
67                                 content = contentreference.content
68                 else:
69                         # Otherwise it's a contentlet.
70                         try:
71                                 contentlet = container_context.contentlets[self.name]
72                         except KeyError:
73                                 content = ''
74                         else:
75                                 content = contentlet.content
76                 return content
77
78
79 @register.tag
80 def container(parser, token):
81         """
82         If a template using this tag is used to render a :class:`.Page`, that :class:`.Page` will have associated content which can be set in the admin interface. If a content type is referenced, then a :class:`.ContentReference` object will be created; otherwise, a :class:`.Contentlet` object will be created.
83         
84         Usage::
85         
86                 {% container <name> [[references <app_label>.<model_name>] as <variable>] %}
87         
88         """
89         params = token.split_contents()
90         if len(params) >= 2:
91                 tag = params[0]
92                 name = params[1].strip('"')
93                 references = None
94                 as_var = None
95                 if len(params) > 2:
96                         remaining_tokens = params[2:]
97                         while remaining_tokens:
98                                 option_token = remaining_tokens.pop(0)
99                                 if option_token == 'references':
100                                         try:
101                                                 app_label, model = remaining_tokens.pop(0).strip('"').split('.')
102                                                 references = ContentType.objects.get_by_natural_key(app_label, model)
103                                         except IndexError:
104                                                 raise template.TemplateSyntaxError('"%s" template tag option "references" requires an argument specifying a content type' % tag)
105                                         except ValueError:
106                                                 raise template.TemplateSyntaxError('"%s" template tag option "references" requires an argument of the form app_label.model (see django.contrib.contenttypes)' % tag)
107                                         except ObjectDoesNotExist:
108                                                 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)
109                                 elif option_token == 'as':
110                                         try:
111                                                 as_var = remaining_tokens.pop(0)
112                                         except IndexError:
113                                                 raise template.TemplateSyntaxError('"%s" template tag option "as" requires an argument specifying a variable name' % tag)
114                         if references and not as_var:
115                                 raise template.TemplateSyntaxError('"%s" template tags using "references" option require additional use of the "as" option specifying a variable name' % tag)
116                 return ContainerNode(name, references, as_var)
117                 
118         else: # error
119                 raise template.TemplateSyntaxError('"%s" template tag provided without arguments (at least one required)' % tag)