/django/contrib/comments/templatetags/comments.py
https://code.google.com/p/mango-py/ · Python · 333 lines · 265 code · 33 blank · 35 comment · 32 complexity · 42faa5e0f2a4d1dc5f2e6ff762395111 MD5 · raw file
- from django import template
- from django.template.loader import render_to_string
- from django.conf import settings
- from django.contrib.contenttypes.models import ContentType
- from django.contrib import comments
- from django.utils.encoding import smart_unicode
- register = template.Library()
- class BaseCommentNode(template.Node):
- """
- Base helper class (abstract) for handling the get_comment_* template tags.
- Looks a bit strange, but the subclasses below should make this a bit more
- obvious.
- """
- #@classmethod
- def handle_token(cls, parser, token):
- """Class method to parse get_comment_list/count/form and return a Node."""
- tokens = token.contents.split()
- if tokens[1] != 'for':
- raise template.TemplateSyntaxError("Second argument in %r tag must be 'for'" % tokens[0])
- # {% get_whatever for obj as varname %}
- if len(tokens) == 5:
- if tokens[3] != 'as':
- raise template.TemplateSyntaxError("Third argument in %r must be 'as'" % tokens[0])
- return cls(
- object_expr = parser.compile_filter(tokens[2]),
- as_varname = tokens[4],
- )
- # {% get_whatever for app.model pk as varname %}
- elif len(tokens) == 6:
- if tokens[4] != 'as':
- raise template.TemplateSyntaxError("Fourth argument in %r must be 'as'" % tokens[0])
- return cls(
- ctype = BaseCommentNode.lookup_content_type(tokens[2], tokens[0]),
- object_pk_expr = parser.compile_filter(tokens[3]),
- as_varname = tokens[5]
- )
- else:
- raise template.TemplateSyntaxError("%r tag requires 4 or 5 arguments" % tokens[0])
- handle_token = classmethod(handle_token)
- #@staticmethod
- def lookup_content_type(token, tagname):
- try:
- app, model = token.split('.')
- return ContentType.objects.get(app_label=app, model=model)
- except ValueError:
- raise template.TemplateSyntaxError("Third argument in %r must be in the format 'app.model'" % tagname)
- except ContentType.DoesNotExist:
- raise template.TemplateSyntaxError("%r tag has non-existant content-type: '%s.%s'" % (tagname, app, model))
- lookup_content_type = staticmethod(lookup_content_type)
- def __init__(self, ctype=None, object_pk_expr=None, object_expr=None, as_varname=None, comment=None):
- if ctype is None and object_expr is None:
- raise template.TemplateSyntaxError("Comment nodes must be given either a literal object or a ctype and object pk.")
- self.comment_model = comments.get_model()
- self.as_varname = as_varname
- self.ctype = ctype
- self.object_pk_expr = object_pk_expr
- self.object_expr = object_expr
- self.comment = comment
- def render(self, context):
- qs = self.get_query_set(context)
- context[self.as_varname] = self.get_context_value_from_queryset(context, qs)
- return ''
- def get_query_set(self, context):
- ctype, object_pk = self.get_target_ctype_pk(context)
- if not object_pk:
- return self.comment_model.objects.none()
- qs = self.comment_model.objects.filter(
- content_type = ctype,
- object_pk = smart_unicode(object_pk),
- site__pk = settings.SITE_ID,
- )
- # The is_public and is_removed fields are implementation details of the
- # built-in comment model's spam filtering system, so they might not
- # be present on a custom comment model subclass. If they exist, we
- # should filter on them.
- field_names = [f.name for f in self.comment_model._meta.fields]
- if 'is_public' in field_names:
- qs = qs.filter(is_public=True)
- if getattr(settings, 'COMMENTS_HIDE_REMOVED', True) and 'is_removed' in field_names:
- qs = qs.filter(is_removed=False)
- return qs
- def get_target_ctype_pk(self, context):
- if self.object_expr:
- try:
- obj = self.object_expr.resolve(context)
- except template.VariableDoesNotExist:
- return None, None
- return ContentType.objects.get_for_model(obj), obj.pk
- else:
- return self.ctype, self.object_pk_expr.resolve(context, ignore_failures=True)
- def get_context_value_from_queryset(self, context, qs):
- """Subclasses should override this."""
- raise NotImplementedError
- class CommentListNode(BaseCommentNode):
- """Insert a list of comments into the context."""
- def get_context_value_from_queryset(self, context, qs):
- return list(qs)
- class CommentCountNode(BaseCommentNode):
- """Insert a count of comments into the context."""
- def get_context_value_from_queryset(self, context, qs):
- return qs.count()
- class CommentFormNode(BaseCommentNode):
- """Insert a form for the comment model into the context."""
- def get_form(self, context):
- ctype, object_pk = self.get_target_ctype_pk(context)
- if object_pk:
- return comments.get_form()(ctype.get_object_for_this_type(pk=object_pk))
- else:
- return None
- def render(self, context):
- context[self.as_varname] = self.get_form(context)
- return ''
- class RenderCommentFormNode(CommentFormNode):
- """Render the comment form directly"""
- #@classmethod
- def handle_token(cls, parser, token):
- """Class method to parse render_comment_form and return a Node."""
- tokens = token.contents.split()
- if tokens[1] != 'for':
- raise template.TemplateSyntaxError("Second argument in %r tag must be 'for'" % tokens[0])
- # {% render_comment_form for obj %}
- if len(tokens) == 3:
- return cls(object_expr=parser.compile_filter(tokens[2]))
- # {% render_comment_form for app.models pk %}
- elif len(tokens) == 4:
- return cls(
- ctype = BaseCommentNode.lookup_content_type(tokens[2], tokens[0]),
- object_pk_expr = parser.compile_filter(tokens[3])
- )
- handle_token = classmethod(handle_token)
- def render(self, context):
- ctype, object_pk = self.get_target_ctype_pk(context)
- if object_pk:
- template_search_list = [
- "comments/%s/%s/form.html" % (ctype.app_label, ctype.model),
- "comments/%s/form.html" % ctype.app_label,
- "comments/form.html"
- ]
- context.push()
- formstr = render_to_string(template_search_list, {"form" : self.get_form(context)}, context)
- context.pop()
- return formstr
- else:
- return ''
- class RenderCommentListNode(CommentListNode):
- """Render the comment list directly"""
- #@classmethod
- def handle_token(cls, parser, token):
- """Class method to parse render_comment_list and return a Node."""
- tokens = token.contents.split()
- if tokens[1] != 'for':
- raise template.TemplateSyntaxError("Second argument in %r tag must be 'for'" % tokens[0])
- # {% render_comment_list for obj %}
- if len(tokens) == 3:
- return cls(object_expr=parser.compile_filter(tokens[2]))
- # {% render_comment_list for app.models pk %}
- elif len(tokens) == 4:
- return cls(
- ctype = BaseCommentNode.lookup_content_type(tokens[2], tokens[0]),
- object_pk_expr = parser.compile_filter(tokens[3])
- )
- handle_token = classmethod(handle_token)
- def render(self, context):
- ctype, object_pk = self.get_target_ctype_pk(context)
- if object_pk:
- template_search_list = [
- "comments/%s/%s/list.html" % (ctype.app_label, ctype.model),
- "comments/%s/list.html" % ctype.app_label,
- "comments/list.html"
- ]
- qs = self.get_query_set(context)
- context.push()
- liststr = render_to_string(template_search_list, {
- "comment_list" : self.get_context_value_from_queryset(context, qs)
- }, context)
- context.pop()
- return liststr
- else:
- return ''
- # We could just register each classmethod directly, but then we'd lose out on
- # the automagic docstrings-into-admin-docs tricks. So each node gets a cute
- # wrapper function that just exists to hold the docstring.
- #@register.tag
- def get_comment_count(parser, token):
- """
- Gets the comment count for the given params and populates the template
- context with a variable containing that value, whose name is defined by the
- 'as' clause.
- Syntax::
- {% get_comment_count for [object] as [varname] %}
- {% get_comment_count for [app].[model] [object_id] as [varname] %}
- Example usage::
- {% get_comment_count for event as comment_count %}
- {% get_comment_count for calendar.event event.id as comment_count %}
- {% get_comment_count for calendar.event 17 as comment_count %}
- """
- return CommentCountNode.handle_token(parser, token)
- #@register.tag
- def get_comment_list(parser, token):
- """
- Gets the list of comments for the given params and populates the template
- context with a variable containing that value, whose name is defined by the
- 'as' clause.
- Syntax::
- {% get_comment_list for [object] as [varname] %}
- {% get_comment_list for [app].[model] [object_id] as [varname] %}
- Example usage::
- {% get_comment_list for event as comment_list %}
- {% for comment in comment_list %}
- ...
- {% endfor %}
- """
- return CommentListNode.handle_token(parser, token)
- #@register.tag
- def render_comment_list(parser, token):
- """
- Render the comment list (as returned by ``{% get_comment_list %}``)
- through the ``comments/list.html`` template
- Syntax::
- {% render_comment_list for [object] %}
- {% render_comment_list for [app].[model] [object_id] %}
- Example usage::
- {% render_comment_list for event %}
- """
- return RenderCommentListNode.handle_token(parser, token)
- #@register.tag
- def get_comment_form(parser, token):
- """
- Get a (new) form object to post a new comment.
- Syntax::
- {% get_comment_form for [object] as [varname] %}
- {% get_comment_form for [app].[model] [object_id] as [varname] %}
- """
- return CommentFormNode.handle_token(parser, token)
- #@register.tag
- def render_comment_form(parser, token):
- """
- Render the comment form (as returned by ``{% render_comment_form %}``) through
- the ``comments/form.html`` template.
- Syntax::
- {% render_comment_form for [object] %}
- {% render_comment_form for [app].[model] [object_id] %}
- """
- return RenderCommentFormNode.handle_token(parser, token)
- #@register.simple_tag
- def comment_form_target():
- """
- Get the target URL for the comment form.
- Example::
- <form action="{% comment_form_target %}" method="post">
- """
- return comments.get_form_target()
- #@register.simple_tag
- def get_comment_permalink(comment, anchor_pattern=None):
- """
- Get the permalink for a comment, optionally specifying the format of the
- named anchor to be appended to the end of the URL.
- Example::
- {{ get_comment_permalink comment "#c%(id)s-by-%(user_name)s" }}
- """
- if anchor_pattern:
- return comment.get_absolute_url(anchor_pattern)
- return comment.get_absolute_url()
- register.tag(get_comment_count)
- register.tag(get_comment_list)
- register.tag(get_comment_form)
- register.tag(render_comment_form)
- register.simple_tag(comment_form_target)
- register.simple_tag(get_comment_permalink)
- register.tag(render_comment_list)