| 1 | "Custom template tags for user comments"
|
|---|
| 2 |
|
|---|
| 3 | from django.core import template
|
|---|
| 4 | from django.core.exceptions import ObjectDoesNotExist
|
|---|
| 5 | from django.models.comments import comments, freecomments
|
|---|
| 6 | from django.models.core import contenttypes
|
|---|
| 7 | import re
|
|---|
| 8 |
|
|---|
| 9 | COMMENT_FORM = '''
|
|---|
| 10 | {% if display_form %}
|
|---|
| 11 | <form {% if photos_optional or photos_required %}enctype="multipart/form-data" {% endif %}action="/comments/post/" method="post">
|
|---|
| 12 |
|
|---|
| 13 | {% if user.is_anonymous %}
|
|---|
| 14 | <p>Username: <input type="text" name="username" id="id_username" /><br />Password: <input type="password" name="password" id="id_password" /> (<a href="/accounts/password_reset/">Forgotten your password?</a>)</p>
|
|---|
| 15 | {% else %}
|
|---|
| 16 | <p>Username: <strong>{{ user.username }}</strong> (<a href="/accounts/logout/">Log out</a>)</p>
|
|---|
| 17 | {% endif %}
|
|---|
| 18 |
|
|---|
| 19 | {% if ratings_optional or ratings_required %}
|
|---|
| 20 | <p>Ratings ({% if ratings_required %}Required{% else %}Optional{% endif %}):</p>
|
|---|
| 21 | <table>
|
|---|
| 22 | <tr><th> </th>{% for value in rating_range %}<th>{{ value }}</th>{% endfor %}</tr>
|
|---|
| 23 | {% for rating in rating_choices %}
|
|---|
| 24 | <tr><th>{{ rating }}</th>{% for value in rating_range %}<th><input type="radio" name="rating{{ forloop.parentloop.counter }}" value="{{ value }}" /></th>{% endfor %}</tr>
|
|---|
| 25 | {% endfor %}
|
|---|
| 26 | </table>
|
|---|
| 27 | <input type="hidden" name="rating_options" value="{{ rating_options }}" />
|
|---|
| 28 | {% endif %}
|
|---|
| 29 |
|
|---|
| 30 | {% if photos_optional or photos_required %}
|
|---|
| 31 | <p>Post a photo ({% if photos_required %}Required{% else %}Optional{% endif %}): <input type="file" name="photo" /></p>
|
|---|
| 32 | <input type="hidden" name="photo_options" value="{{ photo_options }}" />
|
|---|
| 33 | {% endif %}
|
|---|
| 34 |
|
|---|
| 35 | <p>Comment:<br /><textarea name="comment" id="id_comment" rows="10" cols="60"></textarea></p>
|
|---|
| 36 |
|
|---|
| 37 | <input type="hidden" name="options" value="{{ options }}" />
|
|---|
| 38 | <input type="hidden" name="target" value="{{ target }}" />
|
|---|
| 39 | <input type="hidden" name="gonzo" value="{{ hash }}" />
|
|---|
| 40 | <p><input type="submit" name="preview" value="Preview comment" /></p>
|
|---|
| 41 | </form>
|
|---|
| 42 | {% endif %}
|
|---|
| 43 | '''
|
|---|
| 44 |
|
|---|
| 45 | FREE_COMMENT_FORM = '''
|
|---|
| 46 | {% if display_form %}
|
|---|
| 47 | <form action="/comments/postfree/" method="post">
|
|---|
| 48 | <p>{% trans 'Your name' %}: <input type="text" id="id_person_name" name="person_name" /></p>
|
|---|
| 49 | <p>{% trans 'Comment' %}:<br /><textarea name="comment" id="id_comment" rows="10" cols="60"></textarea></p>
|
|---|
| 50 | <input type="hidden" name="options" value="{{ options }}" />
|
|---|
| 51 | <input type="hidden" name="target" value="{{ target }}" />
|
|---|
| 52 | <input type="hidden" name="gonzo" value="{{ hash }}" />
|
|---|
| 53 | <p><input type="submit" name="preview" value="{% trans 'Preview comment' %}" /></p>
|
|---|
| 54 | </form>
|
|---|
| 55 | {% endif %}
|
|---|
| 56 | '''
|
|---|
| 57 |
|
|---|
| 58 | class CommentFormNode(template.Node):
|
|---|
| 59 | def __init__(self, content_type, obj_id_lookup_var, obj_id, free,
|
|---|
| 60 | photos_optional=False, photos_required=False, photo_options='',
|
|---|
| 61 | ratings_optional=False, ratings_required=False, rating_options='',
|
|---|
| 62 | is_public=True):
|
|---|
| 63 | self.content_type = content_type
|
|---|
| 64 | self.obj_id_lookup_var, self.obj_id, self.free = obj_id_lookup_var, obj_id, free
|
|---|
| 65 | self.photos_optional, self.photos_required = photos_optional, photos_required
|
|---|
| 66 | self.ratings_optional, self.ratings_required = ratings_optional, ratings_required
|
|---|
| 67 | self.photo_options, self.rating_options = photo_options, rating_options
|
|---|
| 68 | self.is_public = is_public
|
|---|
| 69 |
|
|---|
| 70 | def render(self, context):
|
|---|
| 71 | from django.utils.text import normalize_newlines
|
|---|
| 72 | import base64
|
|---|
| 73 | context.push()
|
|---|
| 74 | if self.obj_id_lookup_var is not None:
|
|---|
| 75 | try:
|
|---|
| 76 | self.obj_id = template.resolve_variable(self.obj_id_lookup_var, context)
|
|---|
| 77 | except template.VariableDoesNotExist:
|
|---|
| 78 | return ''
|
|---|
| 79 | # Validate that this object ID is valid for this content-type.
|
|---|
| 80 | # We only have to do this validation if obj_id_lookup_var is provided,
|
|---|
| 81 | # because do_comment_form() validates hard-coded object IDs.
|
|---|
| 82 | try:
|
|---|
| 83 | self.content_type.get_object_for_this_type(pk=self.obj_id)
|
|---|
| 84 | except ObjectDoesNotExist:
|
|---|
| 85 | context['display_form'] = False
|
|---|
| 86 | else:
|
|---|
| 87 | context['display_form'] = True
|
|---|
| 88 | else:
|
|---|
| 89 | context['display_form'] = True
|
|---|
| 90 | context['target'] = '%s:%s' % (self.content_type.id, self.obj_id)
|
|---|
| 91 | options = []
|
|---|
| 92 | for var, abbr in (('photos_required', comments.PHOTOS_REQUIRED),
|
|---|
| 93 | ('photos_optional', comments.PHOTOS_OPTIONAL),
|
|---|
| 94 | ('ratings_required', comments.RATINGS_REQUIRED),
|
|---|
| 95 | ('ratings_optional', comments.RATINGS_OPTIONAL),
|
|---|
| 96 | ('is_public', comments.IS_PUBLIC)):
|
|---|
| 97 | context[var] = getattr(self, var)
|
|---|
| 98 | if getattr(self, var):
|
|---|
| 99 | options.append(abbr)
|
|---|
| 100 | context['options'] = ','.join(options)
|
|---|
| 101 | if self.free:
|
|---|
| 102 | context['hash'] = comments.get_security_hash(context['options'], '', '', context['target'])
|
|---|
| 103 | default_form = FREE_COMMENT_FORM
|
|---|
| 104 | else:
|
|---|
| 105 | context['photo_options'] = self.photo_options
|
|---|
| 106 | context['rating_options'] = normalize_newlines(base64.encodestring(self.rating_options).strip())
|
|---|
| 107 | if self.rating_options:
|
|---|
| 108 | context['rating_range'], context['rating_choices'] = comments.get_rating_options(self.rating_options)
|
|---|
| 109 | context['hash'] = comments.get_security_hash(context['options'], context['photo_options'], context['rating_options'], context['target'])
|
|---|
| 110 | default_form = COMMENT_FORM
|
|---|
| 111 | output = template.Template(default_form).render(context)
|
|---|
| 112 | context.pop()
|
|---|
| 113 | return output
|
|---|
| 114 |
|
|---|
| 115 | class CommentCountNode(template.Node):
|
|---|
| 116 | def __init__(self, package, module, context_var_name, obj_id, var_name, free):
|
|---|
| 117 | self.package, self.module = package, module
|
|---|
| 118 | self.context_var_name, self.obj_id = context_var_name, obj_id
|
|---|
| 119 | self.var_name, self.free = var_name, free
|
|---|
| 120 |
|
|---|
| 121 | def render(self, context):
|
|---|
| 122 | from django.conf.settings import SITE_ID
|
|---|
| 123 | get_count_function = self.free and freecomments.get_count or comments.get_count
|
|---|
| 124 | if self.context_var_name is not None:
|
|---|
| 125 | self.obj_id = template.resolve_variable(self.context_var_name, context)
|
|---|
| 126 | comment_count = get_count_function(object_id__exact=self.obj_id,
|
|---|
| 127 | content_type__package__label__exact=self.package,
|
|---|
| 128 | content_type__python_module_name__exact=self.module, site__id__exact=SITE_ID)
|
|---|
| 129 | context[self.var_name] = comment_count
|
|---|
| 130 | return ''
|
|---|
| 131 |
|
|---|
| 132 | class CommentListNode(template.Node):
|
|---|
| 133 | def __init__(self, package, module, context_var_name, obj_id, var_name, free, ordering, extra_kwargs=None):
|
|---|
| 134 | self.package, self.module = package, module
|
|---|
| 135 | self.context_var_name, self.obj_id = context_var_name, obj_id
|
|---|
| 136 | self.var_name, self.free = var_name, free
|
|---|
| 137 | self.ordering = ordering
|
|---|
| 138 | self.extra_kwargs = extra_kwargs or {}
|
|---|
| 139 |
|
|---|
| 140 | def render(self, context):
|
|---|
| 141 | from django.conf.settings import COMMENTS_BANNED_USERS_GROUP, SITE_ID
|
|---|
| 142 | get_list_function = self.free and freecomments.get_list or comments.get_list_with_karma
|
|---|
| 143 | if self.context_var_name is not None:
|
|---|
| 144 | try:
|
|---|
| 145 | self.obj_id = template.resolve_variable(self.context_var_name, context)
|
|---|
| 146 | except template.VariableDoesNotExist:
|
|---|
| 147 | return ''
|
|---|
| 148 | kwargs = {
|
|---|
| 149 | 'object_id__exact': self.obj_id,
|
|---|
| 150 | 'content_type__package__label__exact': self.package,
|
|---|
| 151 | 'content_type__python_module_name__exact': self.module,
|
|---|
| 152 | 'site__id__exact': SITE_ID,
|
|---|
| 153 | 'select_related': True,
|
|---|
| 154 | 'order_by': (self.ordering + 'submit_date',),
|
|---|
| 155 | }
|
|---|
| 156 | kwargs.update(self.extra_kwargs)
|
|---|
| 157 | if not self.free and COMMENTS_BANNED_USERS_GROUP:
|
|---|
| 158 | kwargs['select'] = {'is_hidden': 'user_id IN (SELECT user_id FROM auth_users_groups WHERE group_id = %s)' % COMMENTS_BANNED_USERS_GROUP}
|
|---|
| 159 | comment_list = get_list_function(**kwargs)
|
|---|
| 160 |
|
|---|
| 161 | if not self.free:
|
|---|
| 162 | if context.has_key('user') and not context['user'].is_anonymous():
|
|---|
| 163 | user_id = context['user'].id
|
|---|
| 164 | context['user_can_moderate_comments'] = comments.user_is_moderator(context['user'])
|
|---|
| 165 | else:
|
|---|
| 166 | user_id = None
|
|---|
| 167 | context['user_can_moderate_comments'] = False
|
|---|
| 168 | # Only display comments by banned users to those users themselves.
|
|---|
| 169 | if COMMENTS_BANNED_USERS_GROUP:
|
|---|
| 170 | comment_list = [c for c in comment_list if not c.is_hidden or (user_id == c.user_id)]
|
|---|
| 171 |
|
|---|
| 172 | context[self.var_name] = comment_list
|
|---|
| 173 | return ''
|
|---|
| 174 |
|
|---|
| 175 | class DoCommentForm:
|
|---|
| 176 | """
|
|---|
| 177 | Displays a comment form for the given params.
|
|---|
| 178 |
|
|---|
| 179 | Syntax::
|
|---|
| 180 |
|
|---|
| 181 | {% comment_form for [pkg].[py_module_name] [context_var_containing_obj_id] with [list of options] %}
|
|---|
| 182 |
|
|---|
| 183 | Example usage::
|
|---|
| 184 |
|
|---|
| 185 | {% comment_form for lcom.eventtimes event.id with is_public yes photos_optional thumbs,200,400 ratings_optional scale:1-5|first_option|second_option %}
|
|---|
| 186 |
|
|---|
| 187 | ``[context_var_containing_obj_id]`` can be a hard-coded integer or a variable containing the ID.
|
|---|
| 188 | """
|
|---|
| 189 | def __init__(self, free):
|
|---|
| 190 | self.free = free
|
|---|
| 191 |
|
|---|
| 192 | def __call__(self, parser, token):
|
|---|
| 193 | tokens = token.contents.split()
|
|---|
| 194 | if len(tokens) < 4:
|
|---|
| 195 | raise template.TemplateSyntaxError, "%r tag requires at least 3 arguments" % tokens[0]
|
|---|
| 196 | if tokens[1] != 'for':
|
|---|
| 197 | raise template.TemplateSyntaxError, "Second argument in %r tag must be 'for'" % tokens[0]
|
|---|
| 198 | try:
|
|---|
| 199 | package, module = tokens[2].split('.')
|
|---|
| 200 | except ValueError: # unpack list of wrong size
|
|---|
| 201 | raise template.TemplateSyntaxError, "Third argument in %r tag must be in the format 'package.module'" % tokens[0]
|
|---|
| 202 | try:
|
|---|
| 203 | content_type = contenttypes.get_object(package__label__exact=package, python_module_name__exact=module)
|
|---|
| 204 | except contenttypes.ContentTypeDoesNotExist:
|
|---|
| 205 | raise template.TemplateSyntaxError, "%r tag has invalid content-type '%s.%s'" % (tokens[0], package, module)
|
|---|
| 206 | obj_id_lookup_var, obj_id = None, None
|
|---|
| 207 | if tokens[3].isdigit():
|
|---|
| 208 | obj_id = tokens[3]
|
|---|
| 209 | try: # ensure the object ID is valid
|
|---|
| 210 | content_type.get_object_for_this_type(pk=obj_id)
|
|---|
| 211 | except ObjectDoesNotExist:
|
|---|
| 212 | raise template.TemplateSyntaxError, "%r tag refers to %s object with ID %s, which doesn't exist" % (tokens[0], content_type.name, obj_id)
|
|---|
| 213 | else:
|
|---|
| 214 | obj_id_lookup_var = tokens[3]
|
|---|
| 215 | kwargs = {}
|
|---|
| 216 | if len(tokens) > 4:
|
|---|
| 217 | if tokens[4] != 'with':
|
|---|
| 218 | raise template.TemplateSyntaxError, "Fourth argument in %r tag must be 'with'" % tokens[0]
|
|---|
| 219 | for option, args in zip(tokens[5::2], tokens[6::2]):
|
|---|
| 220 | if option in ('photos_optional', 'photos_required') and not self.free:
|
|---|
| 221 | # VALIDATION ##############################################
|
|---|
| 222 | option_list = args.split(',')
|
|---|
| 223 | if len(option_list) % 3 != 0:
|
|---|
| 224 | raise template.TemplateSyntaxError, "Incorrect number of comma-separated arguments to %r tag" % tokens[0]
|
|---|
| 225 | for opt in option_list[::3]:
|
|---|
| 226 | if not opt.isalnum():
|
|---|
| 227 | raise template.TemplateSyntaxError, "Invalid photo directory name in %r tag: '%s'" % (tokens[0], opt)
|
|---|
| 228 | for opt in option_list[1::3] + option_list[2::3]:
|
|---|
| 229 | if not opt.isdigit() or not (comments.MIN_PHOTO_DIMENSION <= int(opt) <= comments.MAX_PHOTO_DIMENSION):
|
|---|
| 230 | raise template.TemplateSyntaxError, "Invalid photo dimension in %r tag: '%s'. Only values between %s and %s are allowed." % (tokens[0], opt, comments.MIN_PHOTO_DIMENSION, comments.MAX_PHOTO_DIMENSION)
|
|---|
| 231 | # VALIDATION ENDS #########################################
|
|---|
| 232 | kwargs[option] = True
|
|---|
| 233 | kwargs['photo_options'] = args
|
|---|
| 234 | elif option in ('ratings_optional', 'ratings_required') and not self.free:
|
|---|
| 235 | # VALIDATION ##############################################
|
|---|
| 236 | if 2 < len(args.split('|')) > 9:
|
|---|
| 237 | raise template.TemplateSyntaxError, "Incorrect number of '%s' options in %r tag. Use between 2 and 8." % (option, tokens[0])
|
|---|
| 238 | if re.match('^scale:\d+\-\d+\:$', args.split('|')[0]):
|
|---|
| 239 | raise template.TemplateSyntaxError, "Invalid 'scale' in %r tag's '%s' options" % (tokens[0], option)
|
|---|
| 240 | # VALIDATION ENDS #########################################
|
|---|
| 241 | kwargs[option] = True
|
|---|
| 242 | kwargs['rating_options'] = args
|
|---|
| 243 | elif option in ('is_public'):
|
|---|
| 244 | kwargs[option] = (args == 'true')
|
|---|
| 245 | else:
|
|---|
| 246 | raise template.TemplateSyntaxError, "%r tag got invalid parameter '%s'" % (tokens[0], option)
|
|---|
| 247 | return CommentFormNode(content_type, obj_id_lookup_var, obj_id, self.free, **kwargs)
|
|---|
| 248 |
|
|---|
| 249 | class DoCommentCount:
|
|---|
| 250 | """
|
|---|
| 251 | Gets comment count for the given params and populates the template context
|
|---|
| 252 | with a variable containing that value, whose name is defined by the 'as'
|
|---|
| 253 | clause.
|
|---|
| 254 |
|
|---|
| 255 | Syntax::
|
|---|
| 256 |
|
|---|
| 257 | {% get_comment_count for [pkg].[py_module_name] [context_var_containing_obj_id] as [varname] %}
|
|---|
| 258 |
|
|---|
| 259 | Example usage::
|
|---|
| 260 |
|
|---|
| 261 | {% get_comment_count for lcom.eventtimes event.id as comment_count %}
|
|---|
| 262 |
|
|---|
| 263 | Note: ``[context_var_containing_obj_id]`` can also be a hard-coded integer, like this::
|
|---|
| 264 |
|
|---|
| 265 | {% get_comment_count for lcom.eventtimes 23 as comment_count %}
|
|---|
| 266 | """
|
|---|
| 267 | def __init__(self, free):
|
|---|
| 268 | self.free = free
|
|---|
| 269 |
|
|---|
| 270 | def __call__(self, parser, token):
|
|---|
| 271 | tokens = token.contents.split()
|
|---|
| 272 | # Now tokens is a list like this:
|
|---|
| 273 | # ['get_comment_list', 'for', 'lcom.eventtimes', 'event.id', 'as', 'comment_list']
|
|---|
| 274 | if len(tokens) != 6:
|
|---|
| 275 | raise template.TemplateSyntaxError, "%r tag requires 5 arguments" % tokens[0]
|
|---|
| 276 | if tokens[1] != 'for':
|
|---|
| 277 | raise template.TemplateSyntaxError, "Second argument in %r tag must be 'for'" % tokens[0]
|
|---|
| 278 | try:
|
|---|
| 279 | package, module = tokens[2].split('.')
|
|---|
| 280 | except ValueError: # unpack list of wrong size
|
|---|
| 281 | raise template.TemplateSyntaxError, "Third argument in %r tag must be in the format 'package.module'" % tokens[0]
|
|---|
| 282 | try:
|
|---|
| 283 | content_type = contenttypes.get_object(package__label__exact=package, python_module_name__exact=module)
|
|---|
| 284 | except contenttypes.ContentTypeDoesNotExist:
|
|---|
| 285 | raise template.TemplateSyntaxError, "%r tag has invalid content-type '%s.%s'" % (tokens[0], package, module)
|
|---|
| 286 | var_name, obj_id = None, None
|
|---|
| 287 | if tokens[3].isdigit():
|
|---|
| 288 | obj_id = tokens[3]
|
|---|
| 289 | try: # ensure the object ID is valid
|
|---|
| 290 | content_type.get_object_for_this_type(pk=obj_id)
|
|---|
| 291 | except ObjectDoesNotExist:
|
|---|
| 292 | raise template.TemplateSyntaxError, "%r tag refers to %s object with ID %s, which doesn't exist" % (tokens[0], content_type.name, obj_id)
|
|---|
| 293 | else:
|
|---|
| 294 | var_name = tokens[3]
|
|---|
| 295 | if tokens[4] != 'as':
|
|---|
| 296 | raise template.TemplateSyntaxError, "Fourth argument in %r must be 'as'" % tokens[0]
|
|---|
| 297 | return CommentCountNode(package, module, var_name, obj_id, tokens[5], self.free)
|
|---|
| 298 |
|
|---|
| 299 | class DoGetCommentList:
|
|---|
| 300 | """
|
|---|
| 301 | Gets comments for the given params and populates the template context with a
|
|---|
| 302 | special comment_package variable, whose name is defined by the ``as``
|
|---|
| 303 | clause.
|
|---|
| 304 |
|
|---|
| 305 | Syntax::
|
|---|
| 306 |
|
|---|
| 307 | {% get_comment_list for [pkg].[py_module_name] [context_var_containing_obj_id] as [varname] (reversed) %}
|
|---|
| 308 |
|
|---|
| 309 | Example usage::
|
|---|
| 310 |
|
|---|
| 311 | {% get_comment_list for lcom.eventtimes event.id as comment_list %}
|
|---|
| 312 |
|
|---|
| 313 | Note: ``[context_var_containing_obj_id]`` can also be a hard-coded integer, like this::
|
|---|
| 314 |
|
|---|
| 315 | {% get_comment_list for lcom.eventtimes 23 as comment_list %}
|
|---|
| 316 |
|
|---|
| 317 | To get a list of comments in reverse order -- that is, most recent first --
|
|---|
| 318 | pass ``reversed`` as the last param::
|
|---|
| 319 |
|
|---|
| 320 | {% get_comment_list for lcom.eventtimes event.id as comment_list reversed %}
|
|---|
| 321 | """
|
|---|
| 322 | def __init__(self, free):
|
|---|
| 323 | self.free = free
|
|---|
| 324 |
|
|---|
| 325 | def __call__(self, parser, token):
|
|---|
| 326 | tokens = token.contents.split()
|
|---|
| 327 | # Now tokens is a list like this:
|
|---|
| 328 | # ['get_comment_list', 'for', 'lcom.eventtimes', 'event.id', 'as', 'comment_list']
|
|---|
| 329 | if not len(tokens) in (6, 7):
|
|---|
| 330 | raise template.TemplateSyntaxError, "%r tag requires 5 or 6 arguments" % tokens[0]
|
|---|
| 331 | if tokens[1] != 'for':
|
|---|
| 332 | raise template.TemplateSyntaxError, "Second argument in %r tag must be 'for'" % tokens[0]
|
|---|
| 333 | try:
|
|---|
| 334 | package, module = tokens[2].split('.')
|
|---|
| 335 | except ValueError: # unpack list of wrong size
|
|---|
| 336 | raise template.TemplateSyntaxError, "Third argument in %r tag must be in the format 'package.module'" % tokens[0]
|
|---|
| 337 | try:
|
|---|
| 338 | content_type = contenttypes.get_object(package__label__exact=package, python_module_name__exact=module)
|
|---|
| 339 | except contenttypes.ContentTypeDoesNotExist:
|
|---|
| 340 | raise template.TemplateSyntaxError, "%r tag has invalid content-type '%s.%s'" % (tokens[0], package, module)
|
|---|
| 341 | var_name, obj_id = None, None
|
|---|
| 342 | if tokens[3].isdigit():
|
|---|
| 343 | obj_id = tokens[3]
|
|---|
| 344 | try: # ensure the object ID is valid
|
|---|
| 345 | content_type.get_object_for_this_type(pk=obj_id)
|
|---|
| 346 | except ObjectDoesNotExist:
|
|---|
| 347 | raise template.TemplateSyntaxError, "%r tag refers to %s object with ID %s, which doesn't exist" % (tokens[0], content_type.name, obj_id)
|
|---|
| 348 | else:
|
|---|
| 349 | var_name = tokens[3]
|
|---|
| 350 | if tokens[4] != 'as':
|
|---|
| 351 | raise template.TemplateSyntaxError, "Fourth argument in %r must be 'as'" % tokens[0]
|
|---|
| 352 | if len(tokens) == 7:
|
|---|
| 353 | if tokens[6] != 'reversed':
|
|---|
| 354 | raise template.TemplateSyntaxError, "Final argument in %r must be 'reversed' if given" % tokens[0]
|
|---|
| 355 | ordering = "-"
|
|---|
| 356 | else:
|
|---|
| 357 | ordering = ""
|
|---|
| 358 | return CommentListNode(package, module, var_name, obj_id, tokens[5], self.free, ordering)
|
|---|
| 359 |
|
|---|
| 360 | # registration comments
|
|---|
| 361 | template.register_tag('get_comment_list', DoGetCommentList(False))
|
|---|
| 362 | template.register_tag('comment_form', DoCommentForm(False))
|
|---|
| 363 | template.register_tag('get_comment_count', DoCommentCount(False))
|
|---|
| 364 | # free comments
|
|---|
| 365 | template.register_tag('get_free_comment_list', DoGetCommentList(True))
|
|---|
| 366 | template.register_tag('free_comment_form', DoCommentForm(True))
|
|---|
| 367 | template.register_tag('get_free_comment_count', DoCommentCount(True))
|
|---|