1
from django import template
2
from django.template.loader import render_to_string
3
from django.conf import settings
4
from django.contrib.contenttypes.models import ContentType
5
from django.contrib import comments
6
from django.utils.encoding import smart_unicode
8
register = template.Library()
10
class BaseCommentNode(template.Node):
12
Base helper class (abstract) for handling the get_comment_* template tags.
13
Looks a bit strange, but the subclasses below should make this a bit more
18
def handle_token(cls, parser, token):
19
"""Class method to parse get_comment_list/count/form and return a Node."""
20
tokens = token.contents.split()
21
if tokens[1] != 'for':
22
raise template.TemplateSyntaxError("Second argument in %r tag must be 'for'" % tokens[0])
24
# {% get_whatever for obj as varname %}
27
raise template.TemplateSyntaxError("Third argument in %r must be 'as'" % tokens[0])
29
object_expr = parser.compile_filter(tokens[2]),
30
as_varname = tokens[4],
33
# {% get_whatever for app.model pk as varname %}
34
elif len(tokens) == 6:
36
raise template.TemplateSyntaxError("Fourth argument in %r must be 'as'" % tokens[0])
38
ctype = BaseCommentNode.lookup_content_type(tokens[2], tokens[0]),
39
object_pk_expr = parser.compile_filter(tokens[3]),
40
as_varname = tokens[5]
44
raise template.TemplateSyntaxError("%r tag requires 4 or 5 arguments" % tokens[0])
46
handle_token = classmethod(handle_token)
49
def lookup_content_type(token, tagname):
51
app, model = token.split('.')
52
return ContentType.objects.get(app_label=app, model=model)
54
raise template.TemplateSyntaxError("Third argument in %r must be in the format 'app.model'" % tagname)
55
except ContentType.DoesNotExist:
56
raise template.TemplateSyntaxError("%r tag has non-existant content-type: '%s.%s'" % (tagname, app, model))
57
lookup_content_type = staticmethod(lookup_content_type)
59
def __init__(self, ctype=None, object_pk_expr=None, object_expr=None, as_varname=None, comment=None):
60
if ctype is None and object_expr is None:
61
raise template.TemplateSyntaxError("Comment nodes must be given either a literal object or a ctype and object pk.")
62
self.comment_model = comments.get_model()
63
self.as_varname = as_varname
65
self.object_pk_expr = object_pk_expr
66
self.object_expr = object_expr
67
self.comment = comment
69
def render(self, context):
70
qs = self.get_query_set(context)
71
context[self.as_varname] = self.get_context_value_from_queryset(context, qs)
74
def get_query_set(self, context):
75
ctype, object_pk = self.get_target_ctype_pk(context)
77
return self.comment_model.objects.none()
79
qs = self.comment_model.objects.filter(
81
object_pk = smart_unicode(object_pk),
82
site__pk = settings.SITE_ID,
85
if getattr(settings, 'COMMENTS_HIDE_REMOVED', True):
86
qs = qs.filter(is_removed=False)
90
def get_target_ctype_pk(self, context):
93
obj = self.object_expr.resolve(context)
94
except template.VariableDoesNotExist:
96
return ContentType.objects.get_for_model(obj), obj.pk
98
return self.ctype, self.object_pk_expr.resolve(context, ignore_failures=True)
100
def get_context_value_from_queryset(self, context, qs):
101
"""Subclasses should override this."""
102
raise NotImplementedError
104
class CommentListNode(BaseCommentNode):
105
"""Insert a list of comments into the context."""
106
def get_context_value_from_queryset(self, context, qs):
109
class CommentCountNode(BaseCommentNode):
110
"""Insert a count of comments into the context."""
111
def get_context_value_from_queryset(self, context, qs):
114
class CommentFormNode(BaseCommentNode):
115
"""Insert a form for the comment model into the context."""
117
def get_form(self, context):
118
ctype, object_pk = self.get_target_ctype_pk(context)
120
return comments.get_form()(ctype.get_object_for_this_type(pk=object_pk))
124
def render(self, context):
125
context[self.as_varname] = self.get_form(context)
128
class RenderCommentFormNode(CommentFormNode):
129
"""Render the comment form directly"""
132
def handle_token(cls, parser, token):
133
"""Class method to parse render_comment_form and return a Node."""
134
tokens = token.contents.split()
135
if tokens[1] != 'for':
136
raise template.TemplateSyntaxError("Second argument in %r tag must be 'for'" % tokens[0])
138
# {% render_comment_form for obj %}
140
return cls(object_expr=parser.compile_filter(tokens[2]))
142
# {% render_comment_form for app.models pk %}
143
elif len(tokens) == 4:
145
ctype = BaseCommentNode.lookup_content_type(tokens[2], tokens[0]),
146
object_pk_expr = parser.compile_filter(tokens[3])
148
handle_token = classmethod(handle_token)
150
def render(self, context):
151
ctype, object_pk = self.get_target_ctype_pk(context)
153
template_search_list = [
154
"comments/%s/%s/form.html" % (ctype.app_label, ctype.model),
155
"comments/%s/form.html" % ctype.app_label,
159
formstr = render_to_string(template_search_list, {"form" : self.get_form(context)}, context)
165
# We could just register each classmethod directly, but then we'd lose out on
166
# the automagic docstrings-into-admin-docs tricks. So each node gets a cute
167
# wrapper function that just exists to hold the docstring.
170
def get_comment_count(parser, token):
172
Gets the comment count for the given params and populates the template
173
context with a variable containing that value, whose name is defined by the
178
{% get_comment_count for [object] as [varname] %}
179
{% get_comment_count for [app].[model] [object_id] as [varname] %}
183
{% get_comment_count for event as comment_count %}
184
{% get_comment_count for calendar.event event.id as comment_count %}
185
{% get_comment_count for calendar.event 17 as comment_count %}
188
return CommentCountNode.handle_token(parser, token)
191
def get_comment_list(parser, token):
193
Gets the list of comments for the given params and populates the template
194
context with a variable containing that value, whose name is defined by the
199
{% get_comment_list for [object] as [varname] %}
200
{% get_comment_list for [app].[model] [object_id] as [varname] %}
204
{% get_comment_list for event as comment_list %}
205
{% for comment in comment_list %}
210
return CommentListNode.handle_token(parser, token)
213
def get_comment_form(parser, token):
215
Get a (new) form object to post a new comment.
219
{% get_comment_form for [object] as [varname] %}
220
{% get_comment_form for [app].[model] [object_id] as [varname] %}
222
return CommentFormNode.handle_token(parser, token)
225
def render_comment_form(parser, token):
227
Render the comment form (as returned by ``{% render_comment_form %}``) through
228
the ``comments/form.html`` template.
232
{% render_comment_form for [object] %}
233
{% render_comment_form for [app].[model] [object_id] %}
235
return RenderCommentFormNode.handle_token(parser, token)
237
#@register.simple_tag
238
def comment_form_target():
240
Get the target URL for the comment form.
244
<form action="{% comment_form_target %}" method="POST">
246
return comments.get_form_target()
248
register.tag(get_comment_count)
249
register.tag(get_comment_list)
250
register.tag(get_comment_form)
251
register.tag(render_comment_form)
252
register.simple_tag(comment_form_target)