PageRenderTime 168ms CodeModel.GetById 30ms app.highlight 97ms RepoModel.GetById 14ms app.codeStats 1ms

/django/contrib/comments/templatetags/comments.py

https://code.google.com/p/mango-py/
Python | 333 lines | 292 code | 19 blank | 22 comment | 15 complexity | 42faa5e0f2a4d1dc5f2e6ff762395111 MD5 | raw file
  1from django import template
  2from django.template.loader import render_to_string
  3from django.conf import settings
  4from django.contrib.contenttypes.models import ContentType
  5from django.contrib import comments
  6from django.utils.encoding import smart_unicode
  7
  8register = template.Library()
  9
 10class 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    """
 16
 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])
 23
 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],
 31            )
 32
 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]
 41            )
 42
 43        else:
 44            raise template.TemplateSyntaxError("%r tag requires 4 or 5 arguments" % tokens[0])
 45
 46    handle_token = classmethod(handle_token)
 47
 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)
 58
 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
 68
 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 ''
 73
 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()
 78
 79        qs = self.comment_model.objects.filter(
 80            content_type = ctype,
 81            object_pk    = smart_unicode(object_pk),
 82            site__pk     = settings.SITE_ID,
 83        )
 84
 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)
 94
 95        return qs
 96
 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)
106
107    def get_context_value_from_queryset(self, context, qs):
108        """Subclasses should override this."""
109        raise NotImplementedError
110
111class 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)
115
116class 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()
120
121class CommentFormNode(BaseCommentNode):
122    """Insert a form for the comment model into the context."""
123
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
130
131    def render(self, context):
132        context[self.as_varname] = self.get_form(context)
133        return ''
134
135class RenderCommentFormNode(CommentFormNode):
136    """Render the comment form directly"""
137
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])
144
145        # {% render_comment_form for obj %}
146        if len(tokens) == 3:
147            return cls(object_expr=parser.compile_filter(tokens[2]))
148
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])
154            )
155    handle_token = classmethod(handle_token)
156
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"
164            ]
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 ''
171
172class RenderCommentListNode(CommentListNode):
173    """Render the comment list directly"""
174
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])
181
182        # {% render_comment_list for obj %}
183        if len(tokens) == 3:
184            return cls(object_expr=parser.compile_filter(tokens[2]))
185
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])
191            )
192    handle_token = classmethod(handle_token)
193
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"
201            ]
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 ''
211
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.
215
216#@register.tag
217def get_comment_count(parser, token):
218    """
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.
222
223    Syntax::
224
225        {% get_comment_count for [object] as [varname]  %}
226        {% get_comment_count for [app].[model] [object_id] as [varname]  %}
227
228    Example usage::
229
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 %}
233
234    """
235    return CommentCountNode.handle_token(parser, token)
236
237#@register.tag
238def get_comment_list(parser, token):
239    """
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.
243
244    Syntax::
245
246        {% get_comment_list for [object] as [varname]  %}
247        {% get_comment_list for [app].[model] [object_id] as [varname]  %}
248
249    Example usage::
250
251        {% get_comment_list for event as comment_list %}
252        {% for comment in comment_list %}
253            ...
254        {% endfor %}
255
256    """
257    return CommentListNode.handle_token(parser, token)
258
259#@register.tag
260def render_comment_list(parser, token):
261    """
262    Render the comment list (as returned by ``{% get_comment_list %}``)
263    through the ``comments/list.html`` template
264
265    Syntax::
266
267        {% render_comment_list for [object] %}
268        {% render_comment_list for [app].[model] [object_id] %}
269
270    Example usage::
271
272        {% render_comment_list for event %}
273
274    """
275    return RenderCommentListNode.handle_token(parser, token)
276
277#@register.tag
278def get_comment_form(parser, token):
279    """
280    Get a (new) form object to post a new comment.
281
282    Syntax::
283
284        {% get_comment_form for [object] as [varname] %}
285        {% get_comment_form for [app].[model] [object_id] as [varname] %}
286    """
287    return CommentFormNode.handle_token(parser, token)
288
289#@register.tag
290def render_comment_form(parser, token):
291    """
292    Render the comment form (as returned by ``{% render_comment_form %}``) through
293    the ``comments/form.html`` template.
294
295    Syntax::
296
297        {% render_comment_form for [object] %}
298        {% render_comment_form for [app].[model] [object_id] %}
299    """
300    return RenderCommentFormNode.handle_token(parser, token)
301
302#@register.simple_tag
303def comment_form_target():
304    """
305    Get the target URL for the comment form.
306
307    Example::
308
309        <form action="{% comment_form_target %}" method="post">
310    """
311    return comments.get_form_target()
312
313#@register.simple_tag
314def get_comment_permalink(comment, anchor_pattern=None):
315    """
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.
318
319    Example::
320        {{ get_comment_permalink comment "#c%(id)s-by-%(user_name)s" }}
321    """
322
323    if anchor_pattern:
324        return comment.get_absolute_url(anchor_pattern)
325    return comment.get_absolute_url()
326
327register.tag(get_comment_count)
328register.tag(get_comment_list)
329register.tag(get_comment_form)
330register.tag(render_comment_form)
331register.simple_tag(comment_form_target)
332register.simple_tag(get_comment_permalink)
333register.tag(render_comment_list)