~canonical-django/canonical-django/project-template

« back to all changes in this revision

Viewing changes to trunk/python-packages/django/contrib/comments/templatetags/comments.py

  • Committer: Matthew Nuzum
  • Date: 2008-11-13 05:46:03 UTC
  • Revision ID: matthew.nuzum@canonical.com-20081113054603-v0kvr6z6xyexvqt3
adding to version control

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
7
 
 
8
register = template.Library()
 
9
 
 
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
    """
 
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
            is_public    = True,
 
84
        )
 
85
        if getattr(settings, 'COMMENTS_HIDE_REMOVED', True):
 
86
            qs = qs.filter(is_removed=False)
 
87
 
 
88
        return qs
 
89
 
 
90
    def get_target_ctype_pk(self, context):
 
91
        if self.object_expr:
 
92
            try:
 
93
                obj = self.object_expr.resolve(context)
 
94
            except template.VariableDoesNotExist:
 
95
                return None, None
 
96
            return ContentType.objects.get_for_model(obj), obj.pk
 
97
        else:
 
98
            return self.ctype, self.object_pk_expr.resolve(context, ignore_failures=True)
 
99
 
 
100
    def get_context_value_from_queryset(self, context, qs):
 
101
        """Subclasses should override this."""
 
102
        raise NotImplementedError
 
103
 
 
104
class CommentListNode(BaseCommentNode):
 
105
    """Insert a list of comments into the context."""
 
106
    def get_context_value_from_queryset(self, context, qs):
 
107
        return list(qs)
 
108
 
 
109
class CommentCountNode(BaseCommentNode):
 
110
    """Insert a count of comments into the context."""
 
111
    def get_context_value_from_queryset(self, context, qs):
 
112
        return qs.count()
 
113
 
 
114
class CommentFormNode(BaseCommentNode):
 
115
    """Insert a form for the comment model into the context."""
 
116
 
 
117
    def get_form(self, context):
 
118
        ctype, object_pk = self.get_target_ctype_pk(context)
 
119
        if object_pk:
 
120
            return comments.get_form()(ctype.get_object_for_this_type(pk=object_pk))
 
121
        else:
 
122
            return None
 
123
 
 
124
    def render(self, context):
 
125
        context[self.as_varname] = self.get_form(context)
 
126
        return ''
 
127
 
 
128
class RenderCommentFormNode(CommentFormNode):
 
129
    """Render the comment form directly"""
 
130
 
 
131
    #@classmethod
 
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])
 
137
 
 
138
        # {% render_comment_form for obj %}
 
139
        if len(tokens) == 3:
 
140
            return cls(object_expr=parser.compile_filter(tokens[2]))
 
141
 
 
142
        # {% render_comment_form for app.models pk %}
 
143
        elif len(tokens) == 4:
 
144
            return cls(
 
145
                ctype = BaseCommentNode.lookup_content_type(tokens[2], tokens[0]),
 
146
                object_pk_expr = parser.compile_filter(tokens[3])
 
147
            )
 
148
    handle_token = classmethod(handle_token)
 
149
 
 
150
    def render(self, context):
 
151
        ctype, object_pk = self.get_target_ctype_pk(context)
 
152
        if object_pk:
 
153
            template_search_list = [
 
154
                "comments/%s/%s/form.html" % (ctype.app_label, ctype.model),
 
155
                "comments/%s/form.html" % ctype.app_label,
 
156
                "comments/form.html"
 
157
            ]
 
158
            context.push()
 
159
            formstr = render_to_string(template_search_list, {"form" : self.get_form(context)}, context)
 
160
            context.pop()
 
161
            return formstr
 
162
        else:
 
163
            return ''
 
164
 
 
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.
 
168
 
 
169
#@register.tag
 
170
def get_comment_count(parser, token):
 
171
    """
 
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
 
174
    'as' clause.
 
175
 
 
176
    Syntax::
 
177
 
 
178
        {% get_comment_count for [object] as [varname]  %}
 
179
        {% get_comment_count for [app].[model] [object_id] as [varname]  %}
 
180
 
 
181
    Example usage::
 
182
 
 
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 %}
 
186
 
 
187
    """
 
188
    return CommentCountNode.handle_token(parser, token)
 
189
 
 
190
#@register.tag
 
191
def get_comment_list(parser, token):
 
192
    """
 
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
 
195
    'as' clause.
 
196
 
 
197
    Syntax::
 
198
 
 
199
        {% get_comment_list for [object] as [varname]  %}
 
200
        {% get_comment_list for [app].[model] [object_id] as [varname]  %}
 
201
 
 
202
    Example usage::
 
203
 
 
204
        {% get_comment_list for event as comment_list %}
 
205
        {% for comment in comment_list %}
 
206
            ...
 
207
        {% endfor %}
 
208
 
 
209
    """
 
210
    return CommentListNode.handle_token(parser, token)
 
211
 
 
212
#@register.tag
 
213
def get_comment_form(parser, token):
 
214
    """
 
215
    Get a (new) form object to post a new comment.
 
216
 
 
217
    Syntax::
 
218
 
 
219
        {% get_comment_form for [object] as [varname] %}
 
220
        {% get_comment_form for [app].[model] [object_id] as [varname] %}
 
221
    """
 
222
    return CommentFormNode.handle_token(parser, token)
 
223
 
 
224
#@register.tag
 
225
def render_comment_form(parser, token):
 
226
    """
 
227
    Render the comment form (as returned by ``{% render_comment_form %}``) through
 
228
    the ``comments/form.html`` template.
 
229
 
 
230
    Syntax::
 
231
 
 
232
        {% render_comment_form for [object] %}
 
233
        {% render_comment_form for [app].[model] [object_id] %}
 
234
    """
 
235
    return RenderCommentFormNode.handle_token(parser, token)
 
236
 
 
237
#@register.simple_tag
 
238
def comment_form_target():
 
239
    """
 
240
    Get the target URL for the comment form.
 
241
 
 
242
    Example::
 
243
 
 
244
        <form action="{% comment_form_target %}" method="POST">
 
245
    """
 
246
    return comments.get_form_target()
 
247
 
 
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)