Package django :: Package contrib :: Package comments :: Package views :: Module comments
[hide private]
[frames] | no frames]

Source Code for Module django.contrib.comments.views.comments

  1  from django.core import validators 
  2  from django import oldforms 
  3  from django.core.mail import mail_admins, mail_managers 
  4  from django.http import Http404 
  5  from django.core.exceptions import ObjectDoesNotExist 
  6  from django.shortcuts import render_to_response 
  7  from django.template import RequestContext 
  8  from django.contrib.comments.models import Comment, FreeComment, RATINGS_REQUIRED, RATINGS_OPTIONAL, IS_PUBLIC 
  9  from django.contrib.contenttypes.models import ContentType 
 10  from django.contrib.auth.forms import AuthenticationForm 
 11  from django.http import HttpResponseRedirect 
 12  from django.utils.text import normalize_newlines 
 13  from django.conf import settings 
 14  from django.utils.translation import ungettext, ugettext as _ 
 15  from django.utils.encoding import smart_unicode 
 16  import base64, datetime 
 17   
 18  COMMENTS_PER_PAGE = 20 
 19   
20 -class PublicCommentManipulator(AuthenticationForm):
21 "Manipulator that handles public registered comments"
22 - def __init__(self, user, ratings_required, ratings_range, num_rating_choices):
23 AuthenticationForm.__init__(self) 24 self.ratings_range, self.num_rating_choices = ratings_range, num_rating_choices 25 choices = [(c, c) for c in ratings_range] 26 def get_validator_list(rating_num): 27 if rating_num <= num_rating_choices: 28 return [validators.RequiredIfOtherFieldsGiven(['rating%d' % i for i in range(1, 9) if i != rating_num], _("This rating is required because you've entered at least one other rating."))] 29 else: 30 return []
31 self.fields.extend([ 32 oldforms.LargeTextField(field_name="comment", max_length=3000, is_required=True, 33 validator_list=[self.hasNoProfanities]), 34 oldforms.RadioSelectField(field_name="rating1", choices=choices, 35 is_required=ratings_required and num_rating_choices > 0, 36 validator_list=get_validator_list(1), 37 ), 38 oldforms.RadioSelectField(field_name="rating2", choices=choices, 39 is_required=ratings_required and num_rating_choices > 1, 40 validator_list=get_validator_list(2), 41 ), 42 oldforms.RadioSelectField(field_name="rating3", choices=choices, 43 is_required=ratings_required and num_rating_choices > 2, 44 validator_list=get_validator_list(3), 45 ), 46 oldforms.RadioSelectField(field_name="rating4", choices=choices, 47 is_required=ratings_required and num_rating_choices > 3, 48 validator_list=get_validator_list(4), 49 ), 50 oldforms.RadioSelectField(field_name="rating5", choices=choices, 51 is_required=ratings_required and num_rating_choices > 4, 52 validator_list=get_validator_list(5), 53 ), 54 oldforms.RadioSelectField(field_name="rating6", choices=choices, 55 is_required=ratings_required and num_rating_choices > 5, 56 validator_list=get_validator_list(6), 57 ), 58 oldforms.RadioSelectField(field_name="rating7", choices=choices, 59 is_required=ratings_required and num_rating_choices > 6, 60 validator_list=get_validator_list(7), 61 ), 62 oldforms.RadioSelectField(field_name="rating8", choices=choices, 63 is_required=ratings_required and num_rating_choices > 7, 64 validator_list=get_validator_list(8), 65 ), 66 ]) 67 if user.is_authenticated(): 68 self["username"].is_required = False 69 self["username"].validator_list = [] 70 self["password"].is_required = False 71 self["password"].validator_list = [] 72 self.user_cache = user
73
74 - def hasNoProfanities(self, field_data, all_data):
75 if settings.COMMENTS_ALLOW_PROFANITIES: 76 return 77 return validators.hasNoProfanities(field_data, all_data)
78
79 - def get_comment(self, new_data):
80 "Helper function" 81 return Comment(None, self.get_user_id(), new_data["content_type_id"], 82 new_data["object_id"], new_data.get("headline", "").strip(), 83 new_data["comment"].strip(), new_data.get("rating1", None), 84 new_data.get("rating2", None), new_data.get("rating3", None), 85 new_data.get("rating4", None), new_data.get("rating5", None), 86 new_data.get("rating6", None), new_data.get("rating7", None), 87 new_data.get("rating8", None), new_data.get("rating1", None) is not None, 88 datetime.datetime.now(), new_data["is_public"], new_data["ip_address"], False, settings.SITE_ID)
89
90 - def save(self, new_data):
91 today = datetime.date.today() 92 c = self.get_comment(new_data) 93 for old in Comment.objects.filter(content_type__id__exact=new_data["content_type_id"], 94 object_id__exact=new_data["object_id"], user__id__exact=self.get_user_id()): 95 # Check that this comment isn't duplicate. (Sometimes people post 96 # comments twice by mistake.) If it is, fail silently by pretending 97 # the comment was posted successfully. 98 if old.submit_date.date() == today and old.comment == c.comment \ 99 and old.rating1 == c.rating1 and old.rating2 == c.rating2 \ 100 and old.rating3 == c.rating3 and old.rating4 == c.rating4 \ 101 and old.rating5 == c.rating5 and old.rating6 == c.rating6 \ 102 and old.rating7 == c.rating7 and old.rating8 == c.rating8: 103 return old 104 # If the user is leaving a rating, invalidate all old ratings. 105 if c.rating1 is not None: 106 old.valid_rating = False 107 old.save() 108 c.save() 109 # If the commentor has posted fewer than COMMENTS_FIRST_FEW comments, 110 # send the comment to the managers. 111 if self.user_cache.comment_set.count() <= settings.COMMENTS_FIRST_FEW: 112 message = ungettext('This comment was posted by a user who has posted fewer than %(count)s comment:\n\n%(text)s', 113 'This comment was posted by a user who has posted fewer than %(count)s comments:\n\n%(text)s', settings.COMMENTS_FIRST_FEW) % \ 114 {'count': settings.COMMENTS_FIRST_FEW, 'text': c.get_as_text()} 115 mail_managers("Comment posted by rookie user", message) 116 if settings.COMMENTS_SKETCHY_USERS_GROUP and settings.COMMENTS_SKETCHY_USERS_GROUP in [g.id for g in self.user_cache.groups.all()]: 117 message = _('This comment was posted by a sketchy user:\n\n%(text)s') % {'text': c.get_as_text()} 118 mail_managers("Comment posted by sketchy user (%s)" % self.user_cache.username, c.get_as_text()) 119 return c
120
121 -class PublicFreeCommentManipulator(oldforms.Manipulator):
122 "Manipulator that handles public free (unregistered) comments"
123 - def __init__(self):
124 self.fields = ( 125 oldforms.TextField(field_name="person_name", max_length=50, is_required=True, 126 validator_list=[self.hasNoProfanities]), 127 oldforms.LargeTextField(field_name="comment", max_length=3000, is_required=True, 128 validator_list=[self.hasNoProfanities]), 129 )
130
131 - def hasNoProfanities(self, field_data, all_data):
132 if settings.COMMENTS_ALLOW_PROFANITIES: 133 return 134 return validators.hasNoProfanities(field_data, all_data)
135
136 - def get_comment(self, new_data):
137 "Helper function" 138 return FreeComment(None, new_data["content_type_id"], 139 new_data["object_id"], new_data["comment"].strip(), 140 new_data["person_name"].strip(), datetime.datetime.now(), new_data["is_public"], 141 new_data["ip_address"], False, settings.SITE_ID)
142
143 - def save(self, new_data):
144 today = datetime.date.today() 145 c = self.get_comment(new_data) 146 # Check that this comment isn't duplicate. (Sometimes people post 147 # comments twice by mistake.) If it is, fail silently by pretending 148 # the comment was posted successfully. 149 for old_comment in FreeComment.objects.filter(content_type__id__exact=new_data["content_type_id"], 150 object_id__exact=new_data["object_id"], person_name__exact=new_data["person_name"], 151 submit_date__year=today.year, submit_date__month=today.month, 152 submit_date__day=today.day): 153 if old_comment.comment == c.comment: 154 return old_comment 155 c.save() 156 return c
157
158 -def post_comment(request, extra_context=None, context_processors=None):
159 """ 160 Post a comment 161 162 Redirects to the `comments.comments.comment_was_posted` view upon success. 163 164 Templates: `comment_preview` 165 Context: 166 comment 167 the comment being posted 168 comment_form 169 the comment form 170 options 171 comment options 172 target 173 comment target 174 hash 175 security hash (must be included in a posted form to succesfully 176 post a comment). 177 rating_options 178 comment ratings options 179 ratings_optional 180 are ratings optional? 181 ratings_required 182 are ratings required? 183 rating_range 184 range of ratings 185 rating_choices 186 choice of ratings 187 """ 188 if extra_context is None: extra_context = {} 189 if not request.POST: 190 raise Http404, _("Only POSTs are allowed") 191 try: 192 options, target, security_hash = request.POST['options'], request.POST['target'], request.POST['gonzo'] 193 except KeyError: 194 raise Http404, _("One or more of the required fields wasn't submitted") 195 photo_options = request.POST.get('photo_options', '') 196 rating_options = normalize_newlines(request.POST.get('rating_options', '')) 197 if Comment.objects.get_security_hash(options, photo_options, rating_options, target) != security_hash: 198 raise Http404, _("Somebody tampered with the comment form (security violation)") 199 # Now we can be assured the data is valid. 200 if rating_options: 201 rating_range, rating_choices = Comment.objects.get_rating_options(base64.decodestring(rating_options)) 202 else: 203 rating_range, rating_choices = [], [] 204 content_type_id, object_id = target.split(':') # target is something like '52:5157' 205 try: 206 obj = ContentType.objects.get(pk=content_type_id).get_object_for_this_type(pk=object_id) 207 except ObjectDoesNotExist: 208 raise Http404, _("The comment form had an invalid 'target' parameter -- the object ID was invalid") 209 option_list = options.split(',') # options is something like 'pa,ra' 210 new_data = request.POST.copy() 211 new_data['content_type_id'] = content_type_id 212 new_data['object_id'] = object_id 213 new_data['ip_address'] = request.META.get('REMOTE_ADDR') 214 new_data['is_public'] = IS_PUBLIC in option_list 215 manipulator = PublicCommentManipulator(request.user, 216 ratings_required=RATINGS_REQUIRED in option_list, 217 ratings_range=rating_range, 218 num_rating_choices=len(rating_choices)) 219 errors = manipulator.get_validation_errors(new_data) 220 # If user gave correct username/password and wasn't already logged in, log them in 221 # so they don't have to enter a username/password again. 222 if manipulator.get_user() and not manipulator.get_user().is_authenticated() and 'password' in new_data and manipulator.get_user().check_password(new_data['password']): 223 from django.contrib.auth import login 224 login(request, manipulator.get_user()) 225 if errors or 'preview' in request.POST: 226 class CommentFormWrapper(oldforms.FormWrapper): 227 def __init__(self, manipulator, new_data, errors, rating_choices): 228 oldforms.FormWrapper.__init__(self, manipulator, new_data, errors) 229 self.rating_choices = rating_choices
230 def ratings(self): 231 field_list = [self['rating%d' % (i+1)] for i in range(len(rating_choices))] 232 for i, f in enumerate(field_list): 233 f.choice = rating_choices[i] 234 return field_list 235 comment = errors and '' or manipulator.get_comment(new_data) 236 comment_form = CommentFormWrapper(manipulator, new_data, errors, rating_choices) 237 return render_to_response('comments/preview.html', { 238 'comment': comment, 239 'comment_form': comment_form, 240 'options': options, 241 'target': target, 242 'hash': security_hash, 243 'rating_options': rating_options, 244 'ratings_optional': RATINGS_OPTIONAL in option_list, 245 'ratings_required': RATINGS_REQUIRED in option_list, 246 'rating_range': rating_range, 247 'rating_choices': rating_choices, 248 }, context_instance=RequestContext(request, extra_context, context_processors)) 249 elif 'post' in request.POST: 250 # If the IP is banned, mail the admins, do NOT save the comment, and 251 # serve up the "Thanks for posting" page as if the comment WAS posted. 252 if request.META['REMOTE_ADDR'] in settings.BANNED_IPS: 253 mail_admins("Banned IP attempted to post comment", smart_unicode(request.POST) + "\n\n" + str(request.META)) 254 else: 255 manipulator.do_html2python(new_data) 256 comment = manipulator.save(new_data) 257 return HttpResponseRedirect("../posted/?c=%s:%s" % (content_type_id, object_id)) 258 else: 259 raise Http404, _("The comment form didn't provide either 'preview' or 'post'") 260
261 -def post_free_comment(request, extra_context=None, context_processors=None):
262 """ 263 Post a free comment (not requiring a log in) 264 265 Redirects to `comments.comments.comment_was_posted` view on success. 266 267 Templates: `comment_free_preview` 268 Context: 269 comment 270 comment being posted 271 comment_form 272 comment form object 273 options 274 comment options 275 target 276 comment target 277 hash 278 security hash (must be included in a posted form to succesfully 279 post a comment). 280 """ 281 if extra_context is None: extra_context = {} 282 if not request.POST: 283 raise Http404, _("Only POSTs are allowed") 284 try: 285 options, target, security_hash = request.POST['options'], request.POST['target'], request.POST['gonzo'] 286 except KeyError: 287 raise Http404, _("One or more of the required fields wasn't submitted") 288 if Comment.objects.get_security_hash(options, '', '', target) != security_hash: 289 raise Http404, _("Somebody tampered with the comment form (security violation)") 290 content_type_id, object_id = target.split(':') # target is something like '52:5157' 291 content_type = ContentType.objects.get(pk=content_type_id) 292 try: 293 obj = content_type.get_object_for_this_type(pk=object_id) 294 except ObjectDoesNotExist: 295 raise Http404, _("The comment form had an invalid 'target' parameter -- the object ID was invalid") 296 option_list = options.split(',') 297 new_data = request.POST.copy() 298 new_data['content_type_id'] = content_type_id 299 new_data['object_id'] = object_id 300 new_data['ip_address'] = request.META['REMOTE_ADDR'] 301 new_data['is_public'] = IS_PUBLIC in option_list 302 manipulator = PublicFreeCommentManipulator() 303 errors = manipulator.get_validation_errors(new_data) 304 if errors or 'preview' in request.POST: 305 comment = errors and '' or manipulator.get_comment(new_data) 306 return render_to_response('comments/free_preview.html', { 307 'comment': comment, 308 'comment_form': oldforms.FormWrapper(manipulator, new_data, errors), 309 'options': options, 310 'target': target, 311 'hash': security_hash, 312 }, context_instance=RequestContext(request, extra_context, context_processors)) 313 elif 'post' in request.POST: 314 # If the IP is banned, mail the admins, do NOT save the comment, and 315 # serve up the "Thanks for posting" page as if the comment WAS posted. 316 if request.META['REMOTE_ADDR'] in settings.BANNED_IPS: 317 from django.core.mail import mail_admins 318 mail_admins("Practical joker", smart_unicode(request.POST) + "\n\n" + str(request.META)) 319 else: 320 manipulator.do_html2python(new_data) 321 comment = manipulator.save(new_data) 322 return HttpResponseRedirect("../posted/?c=%s:%s" % (content_type_id, object_id)) 323 else: 324 raise Http404, _("The comment form didn't provide either 'preview' or 'post'")
325
326 -def comment_was_posted(request, extra_context=None, context_processors=None):
327 """ 328 Display "comment was posted" success page 329 330 Templates: `comment_posted` 331 Context: 332 object 333 The object the comment was posted on 334 """ 335 if extra_context is None: extra_context = {} 336 obj = None 337 if 'c' in request.GET: 338 content_type_id, object_id = request.GET['c'].split(':') 339 try: 340 content_type = ContentType.objects.get(pk=content_type_id) 341 obj = content_type.get_object_for_this_type(pk=object_id) 342 except ObjectDoesNotExist: 343 pass 344 return render_to_response('comments/posted.html', {'object': obj}, 345 context_instance=RequestContext(request, extra_context, context_processors))
346