App Engine Python SDK version 1.9.12
[gae.git] / python / lib / django-1.2 / django / contrib / comments / forms.py
blob0c4b28528bf41729f8536865937c472660dfb090
1 import time
2 import datetime
4 from django import forms
5 from django.forms.util import ErrorDict
6 from django.conf import settings
7 from django.contrib.contenttypes.models import ContentType
8 from models import Comment
9 from django.utils.encoding import force_unicode
10 from django.utils.hashcompat import sha_constructor
11 from django.utils.text import get_text_list
12 from django.utils.translation import ungettext, ugettext_lazy as _
14 COMMENT_MAX_LENGTH = getattr(settings,'COMMENT_MAX_LENGTH', 3000)
16 class CommentSecurityForm(forms.Form):
17 """
18 Handles the security aspects (anti-spoofing) for comment forms.
19 """
20 content_type = forms.CharField(widget=forms.HiddenInput)
21 object_pk = forms.CharField(widget=forms.HiddenInput)
22 timestamp = forms.IntegerField(widget=forms.HiddenInput)
23 security_hash = forms.CharField(min_length=40, max_length=40, widget=forms.HiddenInput)
25 def __init__(self, target_object, data=None, initial=None):
26 self.target_object = target_object
27 if initial is None:
28 initial = {}
29 initial.update(self.generate_security_data())
30 super(CommentSecurityForm, self).__init__(data=data, initial=initial)
32 def security_errors(self):
33 """Return just those errors associated with security"""
34 errors = ErrorDict()
35 for f in ["honeypot", "timestamp", "security_hash"]:
36 if f in self.errors:
37 errors[f] = self.errors[f]
38 return errors
40 def clean_security_hash(self):
41 """Check the security hash."""
42 security_hash_dict = {
43 'content_type' : self.data.get("content_type", ""),
44 'object_pk' : self.data.get("object_pk", ""),
45 'timestamp' : self.data.get("timestamp", ""),
47 expected_hash = self.generate_security_hash(**security_hash_dict)
48 actual_hash = self.cleaned_data["security_hash"]
49 if expected_hash != actual_hash:
50 raise forms.ValidationError("Security hash check failed.")
51 return actual_hash
53 def clean_timestamp(self):
54 """Make sure the timestamp isn't too far (> 2 hours) in the past."""
55 ts = self.cleaned_data["timestamp"]
56 if time.time() - ts > (2 * 60 * 60):
57 raise forms.ValidationError("Timestamp check failed")
58 return ts
60 def generate_security_data(self):
61 """Generate a dict of security data for "initial" data."""
62 timestamp = int(time.time())
63 security_dict = {
64 'content_type' : str(self.target_object._meta),
65 'object_pk' : str(self.target_object._get_pk_val()),
66 'timestamp' : str(timestamp),
67 'security_hash' : self.initial_security_hash(timestamp),
69 return security_dict
71 def initial_security_hash(self, timestamp):
72 """
73 Generate the initial security hash from self.content_object
74 and a (unix) timestamp.
75 """
77 initial_security_dict = {
78 'content_type' : str(self.target_object._meta),
79 'object_pk' : str(self.target_object._get_pk_val()),
80 'timestamp' : str(timestamp),
82 return self.generate_security_hash(**initial_security_dict)
84 def generate_security_hash(self, content_type, object_pk, timestamp):
85 """Generate a (SHA1) security hash from the provided info."""
86 info = (content_type, object_pk, timestamp, settings.SECRET_KEY)
87 return sha_constructor("".join(info)).hexdigest()
89 class CommentDetailsForm(CommentSecurityForm):
90 """
91 Handles the specific details of the comment (name, comment, etc.).
92 """
93 name = forms.CharField(label=_("Name"), max_length=50)
94 email = forms.EmailField(label=_("Email address"))
95 url = forms.URLField(label=_("URL"), required=False)
96 comment = forms.CharField(label=_('Comment'), widget=forms.Textarea,
97 max_length=COMMENT_MAX_LENGTH)
99 def get_comment_object(self):
101 Return a new (unsaved) comment object based on the information in this
102 form. Assumes that the form is already validated and will throw a
103 ValueError if not.
105 Does not set any of the fields that would come from a Request object
106 (i.e. ``user`` or ``ip_address``).
108 if not self.is_valid():
109 raise ValueError("get_comment_object may only be called on valid forms")
111 CommentModel = self.get_comment_model()
112 new = CommentModel(**self.get_comment_create_data())
113 new = self.check_for_duplicate_comment(new)
115 return new
117 def get_comment_model(self):
119 Get the comment model to create with this form. Subclasses in custom
120 comment apps should override this, get_comment_create_data, and perhaps
121 check_for_duplicate_comment to provide custom comment models.
123 return Comment
125 def get_comment_create_data(self):
127 Returns the dict of data to be used to create a comment. Subclasses in
128 custom comment apps that override get_comment_model can override this
129 method to add extra fields onto a custom comment model.
131 return dict(
132 content_type = ContentType.objects.get_for_model(self.target_object),
133 object_pk = force_unicode(self.target_object._get_pk_val()),
134 user_name = self.cleaned_data["name"],
135 user_email = self.cleaned_data["email"],
136 user_url = self.cleaned_data["url"],
137 comment = self.cleaned_data["comment"],
138 submit_date = datetime.datetime.now(),
139 site_id = settings.SITE_ID,
140 is_public = True,
141 is_removed = False,
144 def check_for_duplicate_comment(self, new):
146 Check that a submitted comment isn't a duplicate. This might be caused
147 by someone posting a comment twice. If it is a dup, silently return the *previous* comment.
149 possible_duplicates = self.get_comment_model()._default_manager.using(
150 self.target_object._state.db
151 ).filter(
152 content_type = new.content_type,
153 object_pk = new.object_pk,
154 user_name = new.user_name,
155 user_email = new.user_email,
156 user_url = new.user_url,
158 for old in possible_duplicates:
159 if old.submit_date.date() == new.submit_date.date() and old.comment == new.comment:
160 return old
162 return new
164 def clean_comment(self):
166 If COMMENTS_ALLOW_PROFANITIES is False, check that the comment doesn't
167 contain anything in PROFANITIES_LIST.
169 comment = self.cleaned_data["comment"]
170 if settings.COMMENTS_ALLOW_PROFANITIES == False:
171 bad_words = [w for w in settings.PROFANITIES_LIST if w in comment.lower()]
172 if bad_words:
173 plural = len(bad_words) > 1
174 raise forms.ValidationError(ungettext(
175 "Watch your mouth! The word %s is not allowed here.",
176 "Watch your mouth! The words %s are not allowed here.", plural) % \
177 get_text_list(['"%s%s%s"' % (i[0], '-'*(len(i)-2), i[-1]) for i in bad_words], 'and'))
178 return comment
180 class CommentForm(CommentDetailsForm):
181 honeypot = forms.CharField(required=False,
182 label=_('If you enter anything in this field '\
183 'your comment will be treated as spam'))
185 def clean_honeypot(self):
186 """Check that nothing's been entered into the honeypot."""
187 value = self.cleaned_data["honeypot"]
188 if value:
189 raise forms.ValidationError(self.fields["honeypot"].label)
190 return value