Skip to content

Commit

Permalink
anti-double-post for topics
Browse files Browse the repository at this point in the history
* core.utils.get_hash -> get_file_hash
  • Loading branch information
nitely committed May 7, 2016
1 parent a772238 commit dde2f59
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 22 deletions.
12 changes: 5 additions & 7 deletions spirit/comment/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ def __init__(self, user=None, topic=None, *args, **kwargs):
self.fields['comment'].widget.attrs['placeholder'] = _("Write comment...")

def get_comment_hash(self):
assert not self.instance.pk
assert self.topic

# This gets saved into
Expand All @@ -52,11 +51,10 @@ def get_comment_hash(self):
if comment_hash:
return comment_hash

md5 = hashlib.md5()
md5.update(smart_bytes(self.cleaned_data['comment']))
md5.update(smart_bytes(
'thread-{}'.format(self.topic.pk)))
return md5.hexdigest()
return utils.get_hash((
smart_bytes(self.cleaned_data['comment']),
smart_bytes('thread-{}'.format(self.topic.pk))
))

def _get_comment_html(self):
user = self.user or self.instance.user
Expand Down Expand Up @@ -137,7 +135,7 @@ def save(self):
# todo: use DEFAULT_FILE_STORAGE and MEDIA_URL

file = self.cleaned_data['image']
file_hash = utils.get_hash(file)
file_hash = utils.get_file_hash(file)
file.name = ''.join((file_hash, '.', file.image.format.lower()))
upload_to = os.path.join('spirit', 'images', str(self.user.pk))
file.url = os.path.join(settings.MEDIA_URL, upload_to, file.name).replace("\\", "/")
Expand Down
3 changes: 2 additions & 1 deletion spirit/comment/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ def publish(request, topic_id, pk=None):
if not request.is_limited and form.is_valid():
if not request.user.st.update_post_hash(form.get_comment_hash()):
return redirect(request.POST.get('next', None) or
Comment.get_last_for_topic(topic_id).get_absolute_url())
Comment.get_last_for_topic(topic_id)
.get_absolute_url())

comment = form.save()
comment_posted(comment=comment, mentions=form.mentions)
Expand Down
15 changes: 12 additions & 3 deletions spirit/core/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from django.template.loader import render_to_string
from django.http import HttpResponse
from django.utils import six


def render_form_errors(form):
Expand All @@ -29,16 +30,24 @@ def mkdir_p(path):
raise


def get_hash(file):
def get_hash(bytes_iter):
assert not isinstance(
bytes_iter,
(six.text_type, six.binary_type)) # Avoid gotcha

# todo: test!
md5 = hashlib.md5()

for c in file.chunks():
md5.update(c)
for b in bytes_iter:
md5.update(b)

return md5.hexdigest()


def get_file_hash(file):
return get_hash(file.chunks())


@contextmanager
def pushd(new_dir):
"""
Expand Down
35 changes: 29 additions & 6 deletions spirit/topic/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,56 @@

from __future__ import unicode_literals

import hashlib

from django import forms
from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import smart_bytes

from ..core import utils
from ..core.utils.forms import NestedModelChoiceField
from ..category.models import Category
from .models import Topic


class TopicForm(forms.ModelForm):

topic_hash = forms.CharField(
max_length=128,
widget=forms.HiddenInput,
required=False)

class Meta:
model = Topic
fields = ('title', 'category')

def __init__(self, user, *args, **kwargs):
super(TopicForm, self).__init__(*args, **kwargs)
self.user = user
self.fields['category'] = NestedModelChoiceField(queryset=Category.objects.visible().opened(),
related_name='category_set',
parent_field='parent_id',
label_field='title',
label=_("Category"),
empty_label=_("Chose a category"))
self.fields['category'] = NestedModelChoiceField(
queryset=Category.objects.visible().opened(),
related_name='category_set',
parent_field='parent_id',
label_field='title',
label=_("Category"),
empty_label=_("Chose a category"))

if self.instance.pk and not user.st.is_moderator:
del self.fields['category']

def get_category(self):
return self.cleaned_data['category']

def get_topic_hash(self):
topic_hash = self.cleaned_data.get('topic_hash', None)

if topic_hash:
return topic_hash

return utils.get_hash((
smart_bytes(self.cleaned_data['title']),
smart_bytes('category-{}'.format(self.cleaned_data['category'].pk))))

def save(self, commit=True):
if not self.instance.pk:
self.instance.user = self.user
Expand Down
14 changes: 9 additions & 5 deletions spirit/topic/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,32 @@
@ratelimit(rate='1/10s')
def publish(request, category_id=None):
if category_id:
get_object_or_404(Category.objects.visible(),
pk=category_id)
get_object_or_404(
Category.objects.visible(),
pk=category_id)

if request.method == 'POST':
form = TopicForm(user=request.user, data=request.POST)
cform = CommentForm(user=request.user, data=request.POST)

if not request.is_limited and all([form.is_valid(), cform.is_valid()]): # TODO: test!
if not request.user.st.update_post_hash(form.get_topic_hash()):
return redirect(request.POST.get('next', None) or
form.get_category().get_absolute_url())

# wrap in transaction.atomic?
topic = form.save()
cform.topic = topic
comment = cform.save()
comment_posted(comment=comment, mentions=cform.mentions)
return redirect(topic.get_absolute_url())
else:
form = TopicForm(user=request.user, initial={'category': category_id, })
form = TopicForm(user=request.user, initial={'category': category_id})
cform = CommentForm()

context = {
'form': form,
'cform': cform
}
'cform': cform}

return render(request, 'spirit/topic/publish.html', context)

Expand Down

0 comments on commit dde2f59

Please sign in to comment.