Skip to content

Commit

Permalink
Begin rewrite
Browse files Browse the repository at this point in the history
  • Loading branch information
alex committed Aug 20, 2010
1 parent c8ee6a5 commit e569b58
Show file tree
Hide file tree
Showing 22 changed files with 120 additions and 1,063 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -0,0 +1 @@
*.py[co]
Empty file removed dialogos/admin.py
Empty file.
187 changes: 20 additions & 167 deletions dialogos/forms.py
@@ -1,177 +1,30 @@
import time
import datetime

from django import forms
from django.conf import settings

from django.contrib.contenttypes.models import ContentType
from django.forms.util import ErrorDict
from django.utils.encoding import force_unicode
from django.utils.hashcompat import sha_constructor
from django.utils.text import get_text_list
from django.utils.translation import ungettext, ugettext_lazy as _

from dialogos.models import Comment


COMMENT_MAX_LENGTH = getattr(settings, "DIALOGOS_COMMENT_MAX_LENGTH", 3000)
COMMENTS_ALLOW_PROFANITIES = getattr(settings, "DIALOGOS_ALLOW_PROFANITIES", True)
PROFANITIES_LIST = getattr(settings, "DIALOGOS_PROFANITIES_LIST", [])


class CommentSecurityForm(forms.Form):
"""
Handles the security aspects (anti-spoofing) for comment forms.
"""
content_type = forms.CharField(widget=forms.HiddenInput)
object_pk = forms.CharField(widget=forms.HiddenInput)
timestamp = forms.IntegerField(widget=forms.HiddenInput)
security_hash = forms.CharField(min_length=40, max_length=40, widget=forms.HiddenInput)

def __init__(self, target_object, data=None, initial=None):
self.target_object = target_object
if initial is None:
initial = {}
initial.update(self.generate_security_data())
super(CommentSecurityForm, self).__init__(data=data, initial=initial)

def security_errors(self):
"""Return just those errors associated with security"""
errors = ErrorDict()
for f in ["honeypot", "timestamp", "security_hash"]:
if f in self.errors:
errors[f] = self.errors[f]
return errors

def clean_security_hash(self):
"""Check the security hash."""
security_hash_dict = {
"content_type": self.data.get("content_type", ""),
"object_pk": self.data.get("object_pk", ""),
"timestamp": self.data.get("timestamp", ""),
}
expected_hash = self.generate_security_hash(**security_hash_dict)
actual_hash = self.cleaned_data["security_hash"]
if expected_hash != actual_hash:
raise forms.ValidationError("Security hash check failed.")
return actual_hash

def clean_timestamp(self):
"""Make sure the timestamp isn't too far (> 2 hours) in the past."""
ts = self.cleaned_data["timestamp"]
if time.time() - ts > (2 * 60 * 60):
raise forms.ValidationError("Timestamp check failed")
return ts

def generate_security_data(self):
"""Generate a dict of security data for "initial" data."""
timestamp = int(time.time())
security_dict = {
"content_type": str(self.target_object._meta),
"object_pk": str(self.target_object._get_pk_val()),
"timestamp": str(timestamp),
"security_hash": self.initial_security_hash(timestamp),
}
return security_dict

def initial_security_hash(self, timestamp):
"""
Generate the initial security hash from self.content_object
and a (unix) timestamp.
"""

initial_security_dict = {
"content_type": str(self.target_object._meta),
"object_pk": str(self.target_object._get_pk_val()),
"timestamp": str(timestamp),
}
return self.generate_security_hash(**initial_security_dict)

def generate_security_hash(self, content_type, object_pk, timestamp):
"""Generate a (SHA1) security hash from the provided info."""
info = (content_type, object_pk, timestamp, settings.SECRET_KEY)
return sha_constructor("".join(info)).hexdigest()


class CommentDetailsForm(CommentSecurityForm):
"""
Handles the specific details of the comment (name, comment, etc.).
"""
name = forms.CharField(label=_("Name"), max_length=50)
email = forms.EmailField(label=_("Email address"))
url = forms.URLField(label=_("URL"), required=False)
comment = forms.CharField(label=_("Comment"), widget=forms.Textarea, max_length=COMMENT_MAX_LENGTH)

def get_comment_object(self):
"""
Return a new (unsaved) comment object based on the information in this
form. Assumes that the form is already validated and will throw a
ValueError if not.
Does not set any of the fields that would come from a Request object
(i.e. ``user`` or ``ip_address``).
"""
if not self.is_valid():
raise ValueError("get_comment_object may only be called on valid forms")

new = Comment(
content_type = ContentType.objects.get_for_model(self.target_object),
object_pk = force_unicode(self.target_object._get_pk_val()),
author = self.cleaned_data["name"],
email = self.cleaned_data["email"],
website = self.cleaned_data["url"],
comment = self.cleaned_data["comment"],
is_public = True,
is_removed = False
)
new = self.check_for_duplicate_comment(new)

return new


def check_for_duplicate_comment(self, new):
"""
Check that a submitted comment isn't a duplicate. This might be caused
by someone posting a comment twice. If it is a dup, silently return the *previous* comment.
"""
possible_duplicates = Comment._default_manager.using(
self.target_object._state.db
).filter(
content_type = new.content_type,
object_pk = new.object_pk,
author = new.author,
email = new.email,
website = new.website,
)
for old in possible_duplicates:
if old.submit_date.date() == new.submit_date.date() and old.comment == new.comment:
return old

return new

def clean_comment(self):
"""
If DIALOGOS_ALLOW_PROFANITIES is False, check that the comment doesn't
contain anything in DIALOGOS_PROFANITIES_LIST.
"""
comment = self.cleaned_data["comment"]
if COMMENTS_ALLOW_PROFANITIES == False:
bad_words = [w for w in PROFANITIES_LIST if w in comment.lower()]
if bad_words:
plural = len(bad_words) > 1
raise forms.ValidationError(ungettext(
"Watch your mouth! The word %s is not allowed here.",
"Watch your mouth! The words %s are not allowed here.", plural) % \
get_text_list(['"%s%s%s"' % (i[0], "-"*(len(i)-2), i[-1]) for i in bad_words], "and"))
class BaseCommentForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop("request")
self.obj = kwargs.pop("obj")
super(BaseCommentForm, self).__init__(*args, **kwargs)

def save(self, commit=True):
comment = super(BaseCommentForm, self).save(commit=False)
comment.ip_address = self.request.META.get("REMOTE_ADDR", None)
comment.content_type = ContentType.objects.get_for_model(self.obj)
comment.object_id = self.obj.pk
if commit:
comment.save()
return comment


class CommentForm(CommentDetailsForm):
honeypot = forms.CharField(required=False,
label=_("If you enter anything in this field your comment will be treated as spam"))

def clean_honeypot(self):
"""Check that nothing's been entered into the honeypot."""
value = self.cleaned_data["honeypot"]
if value:
raise forms.ValidationError(self.fields["honeypot"].label)
return value
class UnauthenticatedCommentForm(BaseCommentForm):
class Meta:
model = Comment
fields = [
"name", "email", "website", "comment"
]
30 changes: 11 additions & 19 deletions dialogos/models.py
@@ -1,31 +1,23 @@
from datetime import datetime

from django.db import models
from django.conf import settings

from django.contrib.auth.models import User
from django.contrib.contenttypes import generic
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext_lazy as _


COMMENT_MAX_LENGTH = getattr(settings,'DIALOGOS_COMMENT_MAX_LENGTH', 3000)


class Comment(models.Model):
author = models.ForeignKey(User, null=True, related_name="comments")

author = models.ForeignKey(User, null=True, blank=True, related_name="comments")
name = models.CharField(max_length=50, null=True, blank=True)
email = models.CharField(max_length=50, null=True, blank=True)
website = models.CharField(max_length=50, null=True, blank=True)
name = models.CharField(max_length=100, blank=True)
email = models.CharField(max_length=255, blank=True)
website = models.CharField(max_length=255, blank=True)

content_type = models.ForeignKey(ContentType, related_name="content_type_set_for_%(class)s")
object_pk = models.TextField(_("object ID"))
content_object = generic.GenericForeignKey(ct_field="content_type", fk_field="object_pk")
content_type = models.ForeignKey(ContentType)
object_id = models.IntegerField()

comment = models.TextField(_("comment"), max_length=COMMENT_MAX_LENGTH)
comment = models.TextField()

submit_date = models.DateTimeField(_("date/time submitted"), default=datetime.now)
ip_address = models.IPAddressField(_("IP address"), blank=True, null=True)
is_public = models.BooleanField(_("is public"), default=True)
is_removed = models.BooleanField(_("is removed"), default=False)

submit_date = models.DateTimeField(default=datetime.now)
ip_address = models.IPAddressField(null=True)
public = models.BooleanField(default=True)
Empty file removed dialogos/templatetags/__init__.py
Empty file.

0 comments on commit e569b58

Please sign in to comment.