|
|
import re
|
|
|
from captcha.fields import CaptchaField
|
|
|
from django import forms
|
|
|
from django.forms.util import ErrorList
|
|
|
from django.utils.translation import ugettext_lazy as _
|
|
|
import time
|
|
|
from boards.models import TITLE_MAX_LENGTH, User
|
|
|
from neboard import settings
|
|
|
from boards import utils
|
|
|
|
|
|
LAST_POST_TIME = "last_post_time"
|
|
|
LAST_LOGIN_TIME = "last_login_time"
|
|
|
|
|
|
LOGIN_DELAY = 60 * 60
|
|
|
|
|
|
MAX_TEXT_LENGTH = 30000
|
|
|
MAX_IMAGE_SIZE = 8 * 1024 * 1024
|
|
|
|
|
|
|
|
|
class PlainErrorList(ErrorList):
|
|
|
def __unicode__(self):
|
|
|
return self.as_text()
|
|
|
|
|
|
def as_text(self):
|
|
|
return ''.join([u'(!) %s ' % e for e in self])
|
|
|
|
|
|
|
|
|
class NeboardForm(forms.Form):
|
|
|
|
|
|
def as_p(self):
|
|
|
"Returns this form rendered as HTML <p>s."
|
|
|
return self._html_output(
|
|
|
normal_row='<div class="form-row">'
|
|
|
'<div class="form-label">'
|
|
|
'%(label)s'
|
|
|
'</div>'
|
|
|
'<div class="form-input">'
|
|
|
'%(field)s'
|
|
|
'</div>'
|
|
|
'%(help_text)s'
|
|
|
'</div>',
|
|
|
error_row='<div class="form-errors">%s</div>',
|
|
|
row_ender='</p>',
|
|
|
help_text_html=' <span class="helptext">%s</span>',
|
|
|
errors_on_separate_row=True)
|
|
|
|
|
|
|
|
|
class PostForm(NeboardForm):
|
|
|
|
|
|
title = forms.CharField(max_length=TITLE_MAX_LENGTH, required=False)
|
|
|
text = forms.CharField(widget=forms.Textarea, required=False)
|
|
|
image = forms.ImageField(required=False)
|
|
|
|
|
|
# This field is for spam prevention only
|
|
|
email = forms.CharField(max_length=100, required=False)
|
|
|
|
|
|
session = None
|
|
|
|
|
|
def clean_title(self):
|
|
|
title = self.cleaned_data['title']
|
|
|
if title:
|
|
|
if len(title) > TITLE_MAX_LENGTH:
|
|
|
raise forms.ValidationError(_('Title must have less than %s '
|
|
|
'characters') %
|
|
|
str(TITLE_MAX_LENGTH))
|
|
|
return title
|
|
|
|
|
|
def clean_text(self):
|
|
|
text = self.cleaned_data['text']
|
|
|
if text:
|
|
|
if len(text) > MAX_TEXT_LENGTH:
|
|
|
raise forms.ValidationError(_('Text must have less than %s '
|
|
|
'characters') %
|
|
|
str(MAX_TEXT_LENGTH))
|
|
|
return text
|
|
|
|
|
|
def clean_image(self):
|
|
|
image = self.cleaned_data['image']
|
|
|
if image:
|
|
|
if image._size > MAX_IMAGE_SIZE:
|
|
|
raise forms.ValidationError(_('Image must be less than %s '
|
|
|
'bytes') % str(MAX_IMAGE_SIZE))
|
|
|
return image
|
|
|
|
|
|
def clean(self):
|
|
|
cleaned_data = super(PostForm, self).clean()
|
|
|
|
|
|
if not self.session:
|
|
|
raise forms.ValidationError('Humans have sessions')
|
|
|
|
|
|
if cleaned_data['email']:
|
|
|
raise forms.ValidationError('A human cannot enter a hidden field')
|
|
|
|
|
|
if not self.errors:
|
|
|
self._clean_text_image()
|
|
|
|
|
|
if not self.errors and self.session:
|
|
|
self._validate_posting_speed()
|
|
|
|
|
|
return cleaned_data
|
|
|
|
|
|
def _clean_text_image(self):
|
|
|
text = self.cleaned_data.get('text')
|
|
|
image = self.cleaned_data.get('image')
|
|
|
|
|
|
if (not text) and (not image):
|
|
|
error_message = _('Either text or image must be entered.')
|
|
|
self._errors['text'] = self.error_class([error_message])
|
|
|
|
|
|
def _validate_posting_speed(self):
|
|
|
can_post = True
|
|
|
|
|
|
if LAST_POST_TIME in self.session:
|
|
|
now = time.time()
|
|
|
last_post_time = self.session[LAST_POST_TIME]
|
|
|
|
|
|
current_delay = int(now - last_post_time)
|
|
|
|
|
|
if current_delay < settings.POSTING_DELAY:
|
|
|
error_message = _('Wait %s seconds after last posting') % str(
|
|
|
settings.POSTING_DELAY - current_delay)
|
|
|
self._errors['text'] = self.error_class([error_message])
|
|
|
|
|
|
can_post = False
|
|
|
|
|
|
if can_post:
|
|
|
self.session[LAST_POST_TIME] = time.time()
|
|
|
|
|
|
|
|
|
class ThreadForm(PostForm):
|
|
|
regex_tags = re.compile(ur'^[\w\s\d]+$', re.UNICODE)
|
|
|
tags = forms.CharField(max_length=100)
|
|
|
|
|
|
def clean_tags(self):
|
|
|
tags = self.cleaned_data['tags']
|
|
|
|
|
|
if tags:
|
|
|
if not self.regex_tags.match(tags):
|
|
|
raise forms.ValidationError(
|
|
|
_('Inappropriate characters in tags.'))
|
|
|
|
|
|
return tags
|
|
|
|
|
|
def clean(self):
|
|
|
cleaned_data = super(ThreadForm, self).clean()
|
|
|
|
|
|
return cleaned_data
|
|
|
|
|
|
|
|
|
class PostCaptchaForm(PostForm):
|
|
|
captcha = CaptchaField()
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
self.request = kwargs['request']
|
|
|
del kwargs['request']
|
|
|
|
|
|
super(PostCaptchaForm, self).__init__(*args, **kwargs)
|
|
|
|
|
|
def clean(self):
|
|
|
cleaned_data = super(PostCaptchaForm, self).clean()
|
|
|
|
|
|
success = self.is_valid()
|
|
|
utils.update_captcha_access(self.request, success)
|
|
|
|
|
|
if success:
|
|
|
return cleaned_data
|
|
|
else:
|
|
|
raise forms.ValidationError(_("Captcha validation failed"))
|
|
|
|
|
|
|
|
|
class ThreadCaptchaForm(ThreadForm):
|
|
|
captcha = CaptchaField()
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
self.request = kwargs['request']
|
|
|
del kwargs['request']
|
|
|
|
|
|
super(ThreadCaptchaForm, self).__init__(*args, **kwargs)
|
|
|
|
|
|
def clean(self):
|
|
|
cleaned_data = super(ThreadCaptchaForm, self).clean()
|
|
|
|
|
|
success = self.is_valid()
|
|
|
utils.update_captcha_access(self.request, success)
|
|
|
|
|
|
if success:
|
|
|
return cleaned_data
|
|
|
else:
|
|
|
raise forms.ValidationError(_("Captcha validation failed"))
|
|
|
|
|
|
|
|
|
class SettingsForm(NeboardForm):
|
|
|
|
|
|
theme = forms.ChoiceField(choices=settings.THEMES,
|
|
|
label=_('Theme'))
|
|
|
|
|
|
|
|
|
class ModeratorSettingsForm(SettingsForm):
|
|
|
|
|
|
moderate = forms.BooleanField(required=False, label=_('Enable moderation '
|
|
|
'panel'))
|
|
|
|
|
|
|
|
|
class LoginForm(NeboardForm):
|
|
|
|
|
|
user_id = forms.CharField()
|
|
|
|
|
|
session = None
|
|
|
|
|
|
def clean_user_id(self):
|
|
|
user_id = self.cleaned_data['user_id']
|
|
|
if user_id:
|
|
|
users = User.objects.filter(user_id=user_id)
|
|
|
if len(users) == 0:
|
|
|
raise forms.ValidationError(_('No such user found'))
|
|
|
|
|
|
return user_id
|
|
|
|
|
|
def _validate_login_speed(self):
|
|
|
can_post = True
|
|
|
|
|
|
if LAST_LOGIN_TIME in self.session:
|
|
|
now = time.time()
|
|
|
last_login_time = self.session[LAST_LOGIN_TIME]
|
|
|
|
|
|
current_delay = int(now - last_login_time)
|
|
|
|
|
|
if current_delay < LOGIN_DELAY:
|
|
|
error_message = _('Wait %s minutes after last login') % str(
|
|
|
(LOGIN_DELAY - current_delay) / 60)
|
|
|
self._errors['user_id'] = self.error_class([error_message])
|
|
|
|
|
|
can_post = False
|
|
|
|
|
|
if can_post:
|
|
|
self.session[LAST_LOGIN_TIME] = time.time()
|
|
|
|
|
|
def clean(self):
|
|
|
if not self.session:
|
|
|
raise forms.ValidationError('Humans have sessions')
|
|
|
|
|
|
self._validate_login_speed()
|
|
|
|
|
|
cleaned_data = super(LoginForm, self).clean()
|
|
|
|
|
|
return cleaned_data
|