Add Django-1.2.1
[frozenviper.git] / Django-1.2.1 / build / lib.linux-i686-2.6 / django / contrib / comments / templatetags / comments.py
blob42691c63e459bb29322eef8f1d8b6fd23a3e30f3
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):
11 """
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
14 obvious.
15 """
17 #@classmethod
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 %}
25 if len(tokens) == 5:
26 if tokens[3] != 'as':
27 raise template.TemplateSyntaxError("Third argument in %r must be 'as'" % tokens[0])
28 return cls(
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:
35 if tokens[4] != 'as':
36 raise template.TemplateSyntaxError("Fourth argument in %r must be 'as'" % tokens[0])
37 return cls(
38 ctype = BaseCommentNode.lookup_content_type(tokens[2], tokens[0]),
39 object_pk_expr = parser.compile_filter(tokens[3]),
40 as_varname = tokens[5]
43 else:
44 raise template.TemplateSyntaxError("%r tag requires 4 or 5 arguments" % tokens[0])
46 handle_token = classmethod(handle_token)
48 #@staticmethod
49 def lookup_content_type(token, tagname):
50 try:
51 app, model = token.split('.')
52 return ContentType.objects.get(app_label=app, model=model)
53 except ValueError:
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
64 self.ctype = ctype
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)
72 return ''
74 def get_query_set(self, context):
75 ctype, object_pk = self.get_target_ctype_pk(context)
76 if not object_pk:
77 return self.comment_model.objects.none()
79 qs = self.comment_model.objects.filter(
80 content_type = ctype,
81 object_pk = smart_unicode(object_pk),
82 site__pk = settings.SITE_ID,
85 # The is_public and is_removed fields are implementation details of the
86 # built-in comment model's spam filtering system, so they might not
87 # be present on a custom comment model subclass. If they exist, we
88 # should filter on them.
89 field_names = [f.name for f in self.comment_model._meta.fields]
90 if 'is_public' in field_names:
91 qs = qs.filter(is_public=True)
92 if getattr(settings, 'COMMENTS_HIDE_REMOVED', True) and 'is_removed' in field_names:
93 qs = qs.filter(is_removed=False)
95 return qs
97 def get_target_ctype_pk(self, context):
98 if self.object_expr:
99 try:
100 obj = self.object_expr.resolve(context)
101 except template.VariableDoesNotExist:
102 return None, None
103 return ContentType.objects.get_for_model(obj), obj.pk
104 else:
105 return self.ctype, self.object_pk_expr.resolve(context, ignore_failures=True)
107 def get_context_value_from_queryset(self, context, qs):
108 """Subclasses should override this."""
109 raise NotImplementedError
111 class CommentListNode(BaseCommentNode):
112 """Insert a list of comments into the context."""
113 def get_context_value_from_queryset(self, context, qs):
114 return list(qs)
116 class CommentCountNode(BaseCommentNode):
117 """Insert a count of comments into the context."""
118 def get_context_value_from_queryset(self, context, qs):
119 return qs.count()
121 class CommentFormNode(BaseCommentNode):
122 """Insert a form for the comment model into the context."""
124 def get_form(self, context):
125 ctype, object_pk = self.get_target_ctype_pk(context)
126 if object_pk:
127 return comments.get_form()(ctype.get_object_for_this_type(pk=object_pk))
128 else:
129 return None
131 def render(self, context):
132 context[self.as_varname] = self.get_form(context)
133 return ''
135 class RenderCommentFormNode(CommentFormNode):
136 """Render the comment form directly"""
138 #@classmethod
139 def handle_token(cls, parser, token):
140 """Class method to parse render_comment_form and return a Node."""
141 tokens = token.contents.split()
142 if tokens[1] != 'for':
143 raise template.TemplateSyntaxError("Second argument in %r tag must be 'for'" % tokens[0])
145 # {% render_comment_form for obj %}
146 if len(tokens) == 3:
147 return cls(object_expr=parser.compile_filter(tokens[2]))
149 # {% render_comment_form for app.models pk %}
150 elif len(tokens) == 4:
151 return cls(
152 ctype = BaseCommentNode.lookup_content_type(tokens[2], tokens[0]),
153 object_pk_expr = parser.compile_filter(tokens[3])
155 handle_token = classmethod(handle_token)
157 def render(self, context):
158 ctype, object_pk = self.get_target_ctype_pk(context)
159 if object_pk:
160 template_search_list = [
161 "comments/%s/%s/form.html" % (ctype.app_label, ctype.model),
162 "comments/%s/form.html" % ctype.app_label,
163 "comments/form.html"
165 context.push()
166 formstr = render_to_string(template_search_list, {"form" : self.get_form(context)}, context)
167 context.pop()
168 return formstr
169 else:
170 return ''
172 class RenderCommentListNode(CommentListNode):
173 """Render the comment list directly"""
175 #@classmethod
176 def handle_token(cls, parser, token):
177 """Class method to parse render_comment_list and return a Node."""
178 tokens = token.contents.split()
179 if tokens[1] != 'for':
180 raise template.TemplateSyntaxError("Second argument in %r tag must be 'for'" % tokens[0])
182 # {% render_comment_list for obj %}
183 if len(tokens) == 3:
184 return cls(object_expr=parser.compile_filter(tokens[2]))
186 # {% render_comment_list for app.models pk %}
187 elif len(tokens) == 4:
188 return cls(
189 ctype = BaseCommentNode.lookup_content_type(tokens[2], tokens[0]),
190 object_pk_expr = parser.compile_filter(tokens[3])
192 handle_token = classmethod(handle_token)
194 def render(self, context):
195 ctype, object_pk = self.get_target_ctype_pk(context)
196 if object_pk:
197 template_search_list = [
198 "comments/%s/%s/list.html" % (ctype.app_label, ctype.model),
199 "comments/%s/list.html" % ctype.app_label,
200 "comments/list.html"
202 qs = self.get_query_set(context)
203 context.push()
204 liststr = render_to_string(template_search_list, {
205 "comment_list" : self.get_context_value_from_queryset(context, qs)
206 }, context)
207 context.pop()
208 return liststr
209 else:
210 return ''
212 # We could just register each classmethod directly, but then we'd lose out on
213 # the automagic docstrings-into-admin-docs tricks. So each node gets a cute
214 # wrapper function that just exists to hold the docstring.
216 #@register.tag
217 def get_comment_count(parser, token):
219 Gets the comment count for the given params and populates the template
220 context with a variable containing that value, whose name is defined by the
221 'as' clause.
223 Syntax::
225 {% get_comment_count for [object] as [varname] %}
226 {% get_comment_count for [app].[model] [object_id] as [varname] %}
228 Example usage::
230 {% get_comment_count for event as comment_count %}
231 {% get_comment_count for calendar.event event.id as comment_count %}
232 {% get_comment_count for calendar.event 17 as comment_count %}
235 return CommentCountNode.handle_token(parser, token)
237 #@register.tag
238 def get_comment_list(parser, token):
240 Gets the list of comments for the given params and populates the template
241 context with a variable containing that value, whose name is defined by the
242 'as' clause.
244 Syntax::
246 {% get_comment_list for [object] as [varname] %}
247 {% get_comment_list for [app].[model] [object_id] as [varname] %}
249 Example usage::
251 {% get_comment_list for event as comment_list %}
252 {% for comment in comment_list %}
254 {% endfor %}
257 return CommentListNode.handle_token(parser, token)
259 #@register.tag
260 def render_comment_list(parser, token):
262 Render the comment list (as returned by ``{% get_comment_list %}``)
263 through the ``comments/list.html`` template
265 Syntax::
267 {% render_comment_list for [object] %}
268 {% render_comment_list for [app].[model] [object_id] %}
270 Example usage::
272 {% render_comment_list for event %}
275 return RenderCommentListNode.handle_token(parser, token)
277 #@register.tag
278 def get_comment_form(parser, token):
280 Get a (new) form object to post a new comment.
282 Syntax::
284 {% get_comment_form for [object] as [varname] %}
285 {% get_comment_form for [app].[model] [object_id] as [varname] %}
287 return CommentFormNode.handle_token(parser, token)
289 #@register.tag
290 def render_comment_form(parser, token):
292 Render the comment form (as returned by ``{% render_comment_form %}``) through
293 the ``comments/form.html`` template.
295 Syntax::
297 {% render_comment_form for [object] %}
298 {% render_comment_form for [app].[model] [object_id] %}
300 return RenderCommentFormNode.handle_token(parser, token)
302 #@register.simple_tag
303 def comment_form_target():
305 Get the target URL for the comment form.
307 Example::
309 <form action="{% comment_form_target %}" method="post">
311 return comments.get_form_target()
313 #@register.simple_tag
314 def get_comment_permalink(comment, anchor_pattern=None):
316 Get the permalink for a comment, optionally specifying the format of the
317 named anchor to be appended to the end of the URL.
319 Example::
320 {{ get_comment_permalink comment "#c%(id)s-by-%(user_name)s" }}
323 if anchor_pattern:
324 return comment.get_absolute_url(anchor_pattern)
325 return comment.get_absolute_url()
327 register.tag(get_comment_count)
328 register.tag(get_comment_list)
329 register.tag(get_comment_form)
330 register.tag(render_comment_form)
331 register.simple_tag(comment_form_target)
332 register.simple_tag(get_comment_permalink)
333 register.tag(render_comment_list)