1 from django.contrib.comments.models import Comment, FreeComment |
|
2 from django.contrib.comments.models import PHOTOS_REQUIRED, PHOTOS_OPTIONAL, RATINGS_REQUIRED, RATINGS_OPTIONAL, IS_PUBLIC |
|
3 from django.contrib.comments.models import MIN_PHOTO_DIMENSION, MAX_PHOTO_DIMENSION |
|
4 from django import template |
1 from django import template |
5 from django.template import loader |
2 from django.template.loader import render_to_string |
6 from django.core.exceptions import ObjectDoesNotExist |
3 from django.conf import settings |
7 from django.contrib.contenttypes.models import ContentType |
4 from django.contrib.contenttypes.models import ContentType |
8 from django.utils.encoding import smart_str |
5 from django.contrib import comments |
9 import re |
6 from django.utils.encoding import smart_unicode |
10 |
7 |
11 register = template.Library() |
8 register = template.Library() |
12 |
9 |
13 COMMENT_FORM = 'comments/form.html' |
10 class BaseCommentNode(template.Node): |
14 FREE_COMMENT_FORM = 'comments/freeform.html' |
11 """ |
15 |
12 Base helper class (abstract) for handling the get_comment_* template tags. |
16 class CommentFormNode(template.Node): |
13 Looks a bit strange, but the subclasses below should make this a bit more |
17 def __init__(self, content_type, obj_id_lookup_var, obj_id, free, |
14 obvious. |
18 photos_optional=False, photos_required=False, photo_options='', |
15 """ |
19 ratings_optional=False, ratings_required=False, rating_options='', |
16 |
20 is_public=True): |
17 #@classmethod |
21 self.content_type = content_type |
18 def handle_token(cls, parser, token): |
22 if obj_id_lookup_var is not None: |
19 """Class method to parse get_comment_list/count/form and return a Node.""" |
23 obj_id_lookup_var = template.Variable(obj_id_lookup_var) |
20 tokens = token.contents.split() |
24 self.obj_id_lookup_var, self.obj_id, self.free = obj_id_lookup_var, obj_id, free |
21 if tokens[1] != 'for': |
25 self.photos_optional, self.photos_required = photos_optional, photos_required |
22 raise template.TemplateSyntaxError("Second argument in %r tag must be 'for'" % tokens[0]) |
26 self.ratings_optional, self.ratings_required = ratings_optional, ratings_required |
23 |
27 self.photo_options, self.rating_options = photo_options, rating_options |
24 # {% get_whatever for obj as varname %} |
28 self.is_public = is_public |
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 |
29 |
68 |
30 def render(self, context): |
69 def render(self, context): |
31 from django.conf import settings |
70 qs = self.get_query_set(context) |
32 from django.utils.text import normalize_newlines |
71 context[self.as_varname] = self.get_context_value_from_queryset(context, qs) |
33 import base64 |
72 return '' |
34 context.push() |
73 |
35 if self.obj_id_lookup_var is not None: |
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: |
36 try: |
92 try: |
37 self.obj_id = self.obj_id_lookup_var.resolve(context) |
93 obj = self.object_expr.resolve(context) |
38 except template.VariableDoesNotExist: |
94 except template.VariableDoesNotExist: |
39 return '' |
95 return None, None |
40 # Validate that this object ID is valid for this content-type. |
96 return ContentType.objects.get_for_model(obj), obj.pk |
41 # We only have to do this validation if obj_id_lookup_var is provided, |
97 else: |
42 # because do_comment_form() validates hard-coded object IDs. |
98 return self.ctype, self.object_pk_expr.resolve(context, ignore_failures=True) |
43 try: |
99 |
44 self.content_type.get_object_for_this_type(pk=self.obj_id) |
100 def get_context_value_from_queryset(self, context, qs): |
45 except ObjectDoesNotExist: |
101 """Subclasses should override this.""" |
46 context['display_form'] = False |
102 raise NotImplementedError |
47 else: |
103 |
48 context['display_form'] = True |
104 class CommentListNode(BaseCommentNode): |
49 else: |
105 """Insert a list of comments into the context.""" |
50 context['display_form'] = True |
106 def get_context_value_from_queryset(self, context, qs): |
51 context['target'] = '%s:%s' % (self.content_type.id, self.obj_id) |
107 return list(qs) |
52 options = [] |
108 |
53 for var, abbr in (('photos_required', PHOTOS_REQUIRED), |
109 class CommentCountNode(BaseCommentNode): |
54 ('photos_optional', PHOTOS_OPTIONAL), |
110 """Insert a count of comments into the context.""" |
55 ('ratings_required', RATINGS_REQUIRED), |
111 def get_context_value_from_queryset(self, context, qs): |
56 ('ratings_optional', RATINGS_OPTIONAL), |
112 return qs.count() |
57 ('is_public', IS_PUBLIC)): |
113 |
58 context[var] = getattr(self, var) |
114 class CommentFormNode(BaseCommentNode): |
59 if getattr(self, var): |
115 """Insert a form for the comment model into the context.""" |
60 options.append(abbr) |
116 |
61 context['options'] = ','.join(options) |
117 def get_form(self, context): |
62 if self.free: |
118 ctype, object_pk = self.get_target_ctype_pk(context) |
63 context['hash'] = Comment.objects.get_security_hash(context['options'], '', '', context['target']) |
119 if object_pk: |
64 default_form = loader.get_template(FREE_COMMENT_FORM) |
120 return comments.get_form()(ctype.get_object_for_this_type(pk=object_pk)) |
65 else: |
121 else: |
66 context['photo_options'] = self.photo_options |
122 return None |
67 context['rating_options'] = normalize_newlines(base64.encodestring(self.rating_options).strip()) |
|
68 if self.rating_options: |
|
69 context['rating_range'], context['rating_choices'] = Comment.objects.get_rating_options(self.rating_options) |
|
70 context['hash'] = Comment.objects.get_security_hash(context['options'], context['photo_options'], context['rating_options'], context['target']) |
|
71 context['logout_url'] = settings.LOGOUT_URL |
|
72 default_form = loader.get_template(COMMENT_FORM) |
|
73 output = default_form.render(context) |
|
74 context.pop() |
|
75 return output |
|
76 |
|
77 class CommentCountNode(template.Node): |
|
78 def __init__(self, package, module, context_var_name, obj_id, var_name, free): |
|
79 self.package, self.module = package, module |
|
80 if context_var_name is not None: |
|
81 context_var_name = template.Variable(context_var_name) |
|
82 self.context_var_name, self.obj_id = context_var_name, obj_id |
|
83 self.var_name, self.free = var_name, free |
|
84 |
123 |
85 def render(self, context): |
124 def render(self, context): |
86 from django.conf import settings |
125 context[self.as_varname] = self.get_form(context) |
87 manager = self.free and FreeComment.objects or Comment.objects |
|
88 if self.context_var_name is not None: |
|
89 self.obj_id = self.context_var_name.resolve(context) |
|
90 comment_count = manager.filter(object_id__exact=self.obj_id, |
|
91 content_type__app_label__exact=self.package, |
|
92 content_type__model__exact=self.module, site__id__exact=settings.SITE_ID).count() |
|
93 context[self.var_name] = comment_count |
|
94 return '' |
126 return '' |
95 |
127 |
96 class CommentListNode(template.Node): |
128 class RenderCommentFormNode(CommentFormNode): |
97 def __init__(self, package, module, context_var_name, obj_id, var_name, free, ordering, extra_kwargs=None): |
129 """Render the comment form directly""" |
98 self.package, self.module = package, module |
130 |
99 if context_var_name is not None: |
131 #@classmethod |
100 context_var_name = template.Variable(context_var_name) |
132 def handle_token(cls, parser, token): |
101 self.context_var_name, self.obj_id = context_var_name, obj_id |
133 """Class method to parse render_comment_form and return a Node.""" |
102 self.var_name, self.free = var_name, free |
134 tokens = token.contents.split() |
103 self.ordering = ordering |
135 if tokens[1] != 'for': |
104 self.extra_kwargs = extra_kwargs or {} |
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) |
105 |
149 |
106 def render(self, context): |
150 def render(self, context): |
107 from django.conf import settings |
151 ctype, object_pk = self.get_target_ctype_pk(context) |
108 get_list_function = self.free and FreeComment.objects.filter or Comment.objects.get_list_with_karma |
152 if object_pk: |
109 if self.context_var_name is not None: |
153 template_search_list = [ |
110 try: |
154 "comments/%s/%s/form.html" % (ctype.app_label, ctype.model), |
111 self.obj_id = self.context_var_name.resolve(context) |
155 "comments/%s/form.html" % ctype.app_label, |
112 except template.VariableDoesNotExist: |
156 "comments/form.html" |
113 return '' |
157 ] |
114 kwargs = { |
158 context.push() |
115 'object_id__exact': self.obj_id, |
159 formstr = render_to_string(template_search_list, {"form" : self.get_form(context)}, context) |
116 'content_type__app_label__exact': self.package, |
160 context.pop() |
117 'content_type__model__exact': self.module, |
161 return formstr |
118 'site__id__exact': settings.SITE_ID, |
162 else: |
119 } |
163 return '' |
120 kwargs.update(self.extra_kwargs) |
164 |
121 comment_list = get_list_function(**kwargs).order_by(self.ordering + 'submit_date').select_related() |
165 # We could just register each classmethod directly, but then we'd lose out on |
122 if not self.free and settings.COMMENTS_BANNED_USERS_GROUP: |
166 # the automagic docstrings-into-admin-docs tricks. So each node gets a cute |
123 comment_list = comment_list.extra(select={'is_hidden': 'user_id IN (SELECT user_id FROM auth_user_groups WHERE group_id = %s)' % settings.COMMENTS_BANNED_USERS_GROUP}) |
167 # wrapper function that just exists to hold the docstring. |
124 |
168 |
125 if not self.free: |
169 #@register.tag |
126 if 'user' in context and context['user'].is_authenticated(): |
170 def get_comment_count(parser, token): |
127 user_id = context['user'].id |
171 """ |
128 context['user_can_moderate_comments'] = Comment.objects.user_is_moderator(context['user']) |
172 Gets the comment count for the given params and populates the template |
129 else: |
173 context with a variable containing that value, whose name is defined by the |
130 user_id = None |
174 'as' clause. |
131 context['user_can_moderate_comments'] = False |
175 |
132 # Only display comments by banned users to those users themselves. |
176 Syntax:: |
133 if settings.COMMENTS_BANNED_USERS_GROUP: |
177 |
134 comment_list = [c for c in comment_list if not c.is_hidden or (user_id == c.user_id)] |
178 {% get_comment_count for [object] as [varname] %} |
135 |
179 {% get_comment_count for [app].[model] [object_id] as [varname] %} |
136 context[self.var_name] = comment_list |
|
137 return '' |
|
138 |
|
139 class DoCommentForm: |
|
140 """ |
|
141 Displays a comment form for the given params. |
|
142 |
|
143 Syntax:: |
|
144 |
|
145 {% comment_form for [pkg].[py_module_name] [context_var_containing_obj_id] with [list of options] %} |
|
146 |
180 |
147 Example usage:: |
181 Example usage:: |
148 |
182 |
149 {% 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 %} |
183 {% get_comment_count for event as comment_count %} |
150 |
184 {% get_comment_count for calendar.event event.id as comment_count %} |
151 ``[context_var_containing_obj_id]`` can be a hard-coded integer or a variable containing the ID. |
185 {% get_comment_count for calendar.event 17 as comment_count %} |
152 """ |
186 |
153 def __init__(self, free): |
187 """ |
154 self.free = free |
188 return CommentCountNode.handle_token(parser, token) |
155 |
189 |
156 def __call__(self, parser, token): |
190 #@register.tag |
157 tokens = token.contents.split() |
191 def get_comment_list(parser, token): |
158 if len(tokens) < 4: |
192 """ |
159 raise template.TemplateSyntaxError, "%r tag requires at least 3 arguments" % tokens[0] |
193 Gets the list of comments for the given params and populates the template |
160 if tokens[1] != 'for': |
194 context with a variable containing that value, whose name is defined by the |
161 raise template.TemplateSyntaxError, "Second argument in %r tag must be 'for'" % tokens[0] |
195 'as' clause. |
162 try: |
196 |
163 package, module = tokens[2].split('.') |
197 Syntax:: |
164 except ValueError: # unpack list of wrong size |
198 |
165 raise template.TemplateSyntaxError, "Third argument in %r tag must be in the format 'package.module'" % tokens[0] |
199 {% get_comment_list for [object] as [varname] %} |
166 try: |
200 {% get_comment_list for [app].[model] [object_id] as [varname] %} |
167 content_type = ContentType.objects.get(app_label__exact=package, model__exact=module) |
|
168 except ContentType.DoesNotExist: |
|
169 raise template.TemplateSyntaxError, "%r tag has invalid content-type '%s.%s'" % (tokens[0], package, module) |
|
170 obj_id_lookup_var, obj_id = None, None |
|
171 if tokens[3].isdigit(): |
|
172 obj_id = tokens[3] |
|
173 try: # ensure the object ID is valid |
|
174 content_type.get_object_for_this_type(pk=obj_id) |
|
175 except ObjectDoesNotExist: |
|
176 raise template.TemplateSyntaxError, "%r tag refers to %s object with ID %s, which doesn't exist" % (tokens[0], content_type.name, obj_id) |
|
177 else: |
|
178 obj_id_lookup_var = tokens[3] |
|
179 kwargs = {} |
|
180 if len(tokens) > 4: |
|
181 if tokens[4] != 'with': |
|
182 raise template.TemplateSyntaxError, "Fourth argument in %r tag must be 'with'" % tokens[0] |
|
183 for option, args in zip(tokens[5::2], tokens[6::2]): |
|
184 option = smart_str(option) |
|
185 if option in ('photos_optional', 'photos_required') and not self.free: |
|
186 # VALIDATION ############################################## |
|
187 option_list = args.split(',') |
|
188 if len(option_list) % 3 != 0: |
|
189 raise template.TemplateSyntaxError, "Incorrect number of comma-separated arguments to %r tag" % tokens[0] |
|
190 for opt in option_list[::3]: |
|
191 if not opt.isalnum(): |
|
192 raise template.TemplateSyntaxError, "Invalid photo directory name in %r tag: '%s'" % (tokens[0], opt) |
|
193 for opt in option_list[1::3] + option_list[2::3]: |
|
194 if not opt.isdigit() or not (MIN_PHOTO_DIMENSION <= int(opt) <= MAX_PHOTO_DIMENSION): |
|
195 raise template.TemplateSyntaxError, "Invalid photo dimension in %r tag: '%s'. Only values between %s and %s are allowed." % (tokens[0], opt, MIN_PHOTO_DIMENSION, MAX_PHOTO_DIMENSION) |
|
196 # VALIDATION ENDS ######################################### |
|
197 kwargs[option] = True |
|
198 kwargs['photo_options'] = args |
|
199 elif option in ('ratings_optional', 'ratings_required') and not self.free: |
|
200 # VALIDATION ############################################## |
|
201 if 2 < len(args.split('|')) > 9: |
|
202 raise template.TemplateSyntaxError, "Incorrect number of '%s' options in %r tag. Use between 2 and 8." % (option, tokens[0]) |
|
203 if re.match('^scale:\d+\-\d+\:$', args.split('|')[0]): |
|
204 raise template.TemplateSyntaxError, "Invalid 'scale' in %r tag's '%s' options" % (tokens[0], option) |
|
205 # VALIDATION ENDS ######################################### |
|
206 kwargs[option] = True |
|
207 kwargs['rating_options'] = args |
|
208 elif option in ('is_public'): |
|
209 kwargs[option] = (args == 'true') |
|
210 else: |
|
211 raise template.TemplateSyntaxError, "%r tag got invalid parameter '%s'" % (tokens[0], option) |
|
212 return CommentFormNode(content_type, obj_id_lookup_var, obj_id, self.free, **kwargs) |
|
213 |
|
214 class DoCommentCount: |
|
215 """ |
|
216 Gets comment count for the given params and populates the template context |
|
217 with a variable containing that value, whose name is defined by the 'as' |
|
218 clause. |
|
219 |
|
220 Syntax:: |
|
221 |
|
222 {% get_comment_count for [pkg].[py_module_name] [context_var_containing_obj_id] as [varname] %} |
|
223 |
201 |
224 Example usage:: |
202 Example usage:: |
225 |
203 |
226 {% get_comment_count for lcom.eventtimes event.id as comment_count %} |
204 {% get_comment_list for event as comment_list %} |
227 |
205 {% for comment in comment_list %} |
228 Note: ``[context_var_containing_obj_id]`` can also be a hard-coded integer, like this:: |
206 ... |
229 |
207 {% endfor %} |
230 {% get_comment_count for lcom.eventtimes 23 as comment_count %} |
208 |
231 """ |
209 """ |
232 def __init__(self, free): |
210 return CommentListNode.handle_token(parser, token) |
233 self.free = free |
211 |
234 |
212 #@register.tag |
235 def __call__(self, parser, token): |
213 def get_comment_form(parser, token): |
236 tokens = token.contents.split() |
214 """ |
237 # Now tokens is a list like this: |
215 Get a (new) form object to post a new comment. |
238 # ['get_comment_list', 'for', 'lcom.eventtimes', 'event.id', 'as', 'comment_list'] |
216 |
239 if len(tokens) != 6: |
217 Syntax:: |
240 raise template.TemplateSyntaxError, "%r tag requires 5 arguments" % tokens[0] |
218 |
241 if tokens[1] != 'for': |
219 {% get_comment_form for [object] as [varname] %} |
242 raise template.TemplateSyntaxError, "Second argument in %r tag must be 'for'" % tokens[0] |
220 {% get_comment_form for [app].[model] [object_id] as [varname] %} |
243 try: |
221 """ |
244 package, module = tokens[2].split('.') |
222 return CommentFormNode.handle_token(parser, token) |
245 except ValueError: # unpack list of wrong size |
223 |
246 raise template.TemplateSyntaxError, "Third argument in %r tag must be in the format 'package.module'" % tokens[0] |
224 #@register.tag |
247 try: |
225 def render_comment_form(parser, token): |
248 content_type = ContentType.objects.get(app_label__exact=package, model__exact=module) |
226 """ |
249 except ContentType.DoesNotExist: |
227 Render the comment form (as returned by ``{% render_comment_form %}``) through |
250 raise template.TemplateSyntaxError, "%r tag has invalid content-type '%s.%s'" % (tokens[0], package, module) |
228 the ``comments/form.html`` template. |
251 var_name, obj_id = None, None |
229 |
252 if tokens[3].isdigit(): |
230 Syntax:: |
253 obj_id = tokens[3] |
231 |
254 try: # ensure the object ID is valid |
232 {% render_comment_form for [object] %} |
255 content_type.get_object_for_this_type(pk=obj_id) |
233 {% render_comment_form for [app].[model] [object_id] %} |
256 except ObjectDoesNotExist: |
234 """ |
257 raise template.TemplateSyntaxError, "%r tag refers to %s object with ID %s, which doesn't exist" % (tokens[0], content_type.name, obj_id) |
235 return RenderCommentFormNode.handle_token(parser, token) |
258 else: |
236 |
259 var_name = tokens[3] |
237 #@register.simple_tag |
260 if tokens[4] != 'as': |
238 def comment_form_target(): |
261 raise template.TemplateSyntaxError, "Fourth argument in %r must be 'as'" % tokens[0] |
239 """ |
262 return CommentCountNode(package, module, var_name, obj_id, tokens[5], self.free) |
240 Get the target URL for the comment form. |
263 |
241 |
264 class DoGetCommentList: |
242 Example:: |
265 """ |
243 |
266 Gets comments for the given params and populates the template context with a |
244 <form action="{% comment_form_target %}" method="POST"> |
267 special comment_package variable, whose name is defined by the ``as`` |
245 """ |
268 clause. |
246 return comments.get_form_target() |
269 |
247 |
270 Syntax:: |
248 register.tag(get_comment_count) |
271 |
249 register.tag(get_comment_list) |
272 {% get_comment_list for [pkg].[py_module_name] [context_var_containing_obj_id] as [varname] (reversed) %} |
250 register.tag(get_comment_form) |
273 |
251 register.tag(render_comment_form) |
274 Example usage:: |
252 register.simple_tag(comment_form_target) |
275 |
|
276 {% get_comment_list for lcom.eventtimes event.id as comment_list %} |
|
277 |
|
278 Note: ``[context_var_containing_obj_id]`` can also be a hard-coded integer, like this:: |
|
279 |
|
280 {% get_comment_list for lcom.eventtimes 23 as comment_list %} |
|
281 |
|
282 To get a list of comments in reverse order -- that is, most recent first -- |
|
283 pass ``reversed`` as the last param:: |
|
284 |
|
285 {% get_comment_list for lcom.eventtimes event.id as comment_list reversed %} |
|
286 """ |
|
287 def __init__(self, free): |
|
288 self.free = free |
|
289 |
|
290 def __call__(self, parser, token): |
|
291 tokens = token.contents.split() |
|
292 # Now tokens is a list like this: |
|
293 # ['get_comment_list', 'for', 'lcom.eventtimes', 'event.id', 'as', 'comment_list'] |
|
294 if not len(tokens) in (6, 7): |
|
295 raise template.TemplateSyntaxError, "%r tag requires 5 or 6 arguments" % tokens[0] |
|
296 if tokens[1] != 'for': |
|
297 raise template.TemplateSyntaxError, "Second argument in %r tag must be 'for'" % tokens[0] |
|
298 try: |
|
299 package, module = tokens[2].split('.') |
|
300 except ValueError: # unpack list of wrong size |
|
301 raise template.TemplateSyntaxError, "Third argument in %r tag must be in the format 'package.module'" % tokens[0] |
|
302 try: |
|
303 content_type = ContentType.objects.get(app_label__exact=package,model__exact=module) |
|
304 except ContentType.DoesNotExist: |
|
305 raise template.TemplateSyntaxError, "%r tag has invalid content-type '%s.%s'" % (tokens[0], package, module) |
|
306 var_name, obj_id = None, None |
|
307 if tokens[3].isdigit(): |
|
308 obj_id = tokens[3] |
|
309 try: # ensure the object ID is valid |
|
310 content_type.get_object_for_this_type(pk=obj_id) |
|
311 except ObjectDoesNotExist: |
|
312 raise template.TemplateSyntaxError, "%r tag refers to %s object with ID %s, which doesn't exist" % (tokens[0], content_type.name, obj_id) |
|
313 else: |
|
314 var_name = tokens[3] |
|
315 if tokens[4] != 'as': |
|
316 raise template.TemplateSyntaxError, "Fourth argument in %r must be 'as'" % tokens[0] |
|
317 if len(tokens) == 7: |
|
318 if tokens[6] != 'reversed': |
|
319 raise template.TemplateSyntaxError, "Final argument in %r must be 'reversed' if given" % tokens[0] |
|
320 ordering = "-" |
|
321 else: |
|
322 ordering = "" |
|
323 return CommentListNode(package, module, var_name, obj_id, tokens[5], self.free, ordering) |
|
324 |
|
325 # registration comments |
|
326 register.tag('get_comment_list', DoGetCommentList(False)) |
|
327 register.tag('comment_form', DoCommentForm(False)) |
|
328 register.tag('get_comment_count', DoCommentCount(False)) |
|
329 # free comments |
|
330 register.tag('get_free_comment_list', DoGetCommentList(True)) |
|
331 register.tag('free_comment_form', DoCommentForm(True)) |
|
332 register.tag('get_free_comment_count', DoCommentCount(True)) |
|