This repository has been archived on 2025-05-04. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
s2forums/djangobb_forum/forms.py
Matthew Taylor 343368dcd7 Filter out bad language from signature
Like we do for other text fields. Should fix .
2015-01-15 11:03:34 -05:00

564 lines
21 KiB
Python

# coding: utf-8
import os.path
from datetime import timedelta
from django import forms
from django.conf import settings
from django.contrib.auth.models import User
from django.db.models.expressions import F
from django.utils.translation import ugettext_lazy as _
from django.utils.timezone import now
from djangobb_forum.models import Topic, Post, Profile, Reputation, Report, \
Attachment, Poll, PollChoice, PostStatus
from djangobb_forum import settings as forum_settings
from djangobb_forum.util import smiles, convert_text_to_html, filter_language, \
set_language, UnapprovedImageError
# scratchr2
from base_comments.models import BaseComment
import logging
logger = logging.getLogger(__name__)
SORT_USER_BY_CHOICES = (
('username', _(u'Username')),
('registered', _(u'Registered')),
('num_posts', _(u'Number of posts')),
)
SORT_POST_BY_CHOICES = (
('0', _(u'Post time')),
('1', _(u'Author')),
('2', _(u'Subject')),
('3', _(u'Forum')),
)
SORT_DIR_CHOICES = (
('ASC', _(u'Ascending')),
('DESC', _(u'Descending')),
)
SHOW_AS_CHOICES = (
('topics', _(u'Topics')),
('posts', _(u'Posts')),
)
SEARCH_IN_CHOICES = (
('all', _(u'Message text and topic subject')),
('message', _(u'Message text only')),
('topic', _(u'Topic subject only')),
)
class AddPostForm(forms.ModelForm):
FORM_NAME = "AddPostForm" # used in view and template submit button
name = forms.CharField(label=_('Subject'), max_length=155,
widget=forms.TextInput(attrs={'size':'115'}))
attachment = forms.FileField(label=_('Attachment'), required=False)
subscribe = forms.BooleanField(label=_('Subscribe'), help_text=_("Subscribe to this topic."), required=False)
class Meta:
model = Post
fields = ['body']
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
self.topic = kwargs.pop('topic', None)
self.forum = kwargs.pop('forum', None)
self.ip = kwargs.pop('ip', None)
self.is_ip_banned = kwargs.pop('is_ip_banned')
self.request_data = kwargs.pop('request_data', {})
self.url = kwargs.pop('url', None)
super(AddPostForm, self).__init__(*args, **kwargs)
if self.topic:
self.fields['name'].widget = forms.HiddenInput()
self.fields['name'].required = False
self.fields['body'].widget = forms.Textarea(attrs={'class':'markup', 'rows':'20', 'cols':'95'})
if not forum_settings.ATTACHMENT_SUPPORT:
self.fields['attachment'].widget = forms.HiddenInput()
self.fields['attachment'].required = False
def clean(self):
'''
checking is post subject and body contains not only space characters
'''
errmsg = _('Can\'t be empty nor contain only whitespace characters')
cleaned_data = self.cleaned_data
body = cleaned_data.get('body')
subject = cleaned_data.get('name')
if subject:
if not subject.strip():
self._errors['name'] = self.error_class([errmsg])
del cleaned_data['name']
cleaned_data['name'] = filter_language(subject)
if BaseComment.user_is_muted(self.user):
self._errors['body'] = self.error_class([_("Hmm, the filterbot is pretty sure your recent comments weren't ok for Scratch, so your account has been muted for the rest of the day. :/")])
if self.is_ip_banned:
error = """Sorry, the Scratch Team had to prevent your network from
sharing comments or projects because it was used to break our
community guidelines too many times. You can still share comments
and projects from another network. If you'd like to appeal this
block, you can contact {appeal_email}.
""".format(appeal_email=settings.BAN_APPEAL_EMAIL)
self._errors['body'] = self.error_class([_(error)])
if body:
if not body.strip():
self._errors['body'] = self.error_class([errmsg])
del cleaned_data['body']
try:
convert_text_to_html(body, self.user.forum_profile)
except UnapprovedImageError as e:
self._errors['body'] = self.error_class([e.user_error()])
del cleaned_data['body']
cleaned_data['body'] = filter_language(body)
try:
recent_post = Post.objects.filter(user=self.user).latest()
lastpost_diff = now() - recent_post.created
except Post.DoesNotExist:
lastpost_diff = timedelta(1) # one day if first post
if forum_settings.POST_FLOOD and not self.user.has_perm('djangobb_forum.fast_post'):
if self.user.has_perm('djangobb_forum.med_post'):
if lastpost_diff.total_seconds() < forum_settings.POST_FLOOD_MED:
self._errors['body'] = self.error_class([_("Sorry, you have to wait %d seconds between posts." % forum_settings.POST_FLOOD_MED)])
else:
if lastpost_diff.total_seconds() < forum_settings.POST_FLOOD_SLOW:
self._errors['body'] = self.error_class([_("Sorry, you have to wait %d seconds between posts." % forum_settings.POST_FLOOD_SLOW)])
return cleaned_data
def clean_attachment(self):
if self.cleaned_data['attachment']:
memfile = self.cleaned_data['attachment']
if memfile.size > forum_settings.ATTACHMENT_SIZE_LIMIT:
raise forms.ValidationError(_('Attachment is too big'))
return self.cleaned_data['attachment']
def save(self):
if self.forum:
topic = Topic(forum=self.forum,
user=self.user,
name=self.cleaned_data['name'])
topic.save()
else:
topic = self.topic
if self.cleaned_data['subscribe']:
# User would like to subscripe to this topic
topic.subscribers.add(self.user)
post = Post(topic=topic, user=self.user, user_ip=self.ip,
markup=self.user.forum_profile.markup,
body=self.cleaned_data['body'])
post.save()
if self.user.groups.filter(name="New Scratchers").exists():
tracking_data = dict(**self.request_data)
tracking_data['permalink'] = self.url
status = PostStatus.objects.create_for_post(post, **tracking_data)
if forum_settings.ATTACHMENT_SUPPORT:
self.save_attachment(post, self.cleaned_data['attachment'])
return post
def save_attachment(self, post, memfile):
if memfile:
obj = Attachment(size=memfile.size, content_type=memfile.content_type,
name=memfile.name, post=post)
dir = os.path.join(settings.MEDIA_ROOT, forum_settings.ATTACHMENT_UPLOAD_TO)
fname = '%d.0' % post.id
path = os.path.join(dir, fname)
file(path, 'wb').write(memfile.read())
obj.path = fname
obj.save()
class EditPostForm(forms.ModelForm):
name = forms.CharField(required=False, label=_('Subject'), max_length=155,
widget=forms.TextInput(attrs={'size':'115'}))
silent_edit = forms.BooleanField(required=False, label=_('Silent edit?'))
class Meta:
model = Post
fields = ['body']
def __init__(self, *args, **kwargs):
self.topic = kwargs.pop('topic', None)
super(EditPostForm, self).__init__(*args, **kwargs)
self.fields['name'].initial = self.topic
self.fields['body'].widget = forms.Textarea(attrs={'class':'markup'})
def clean(self):
cleaned_data = self.cleaned_data
subject = cleaned_data.get('name')
if subject:
cleaned_data['name'] = filter_language(subject)
body = cleaned_data.get('body')
if body:
try:
convert_text_to_html(body, self.instance)
except UnapprovedImageError as e:
self._errors['body'] = self.error_class([e.user_error()])
del cleaned_data['body']
cleaned_data['body'] = filter_language(body)
return cleaned_data
def save(self, commit=True):
post = super(EditPostForm, self).save(commit=False)
topic_name = self.cleaned_data['name']
if topic_name:
post.topic.name = topic_name
if commit:
post.topic.save()
post.save()
return post
class EssentialsProfileForm(forms.ModelForm):
username = forms.CharField(label=_('Username'))
email = forms.CharField(label=_('E-mail'))
class Meta:
model = Profile
fields = ['auto_subscribe', 'time_zone', 'language']
def __init__(self, *args, **kwargs):
extra_args = kwargs.pop('extra_args', {})
self.request = extra_args.pop('request', None)
self.profile = kwargs['instance']
super(EssentialsProfileForm, self).__init__(*args, **kwargs)
self.fields['username'].initial = self.profile.user.username
if not self.request.user.is_superuser:
self.fields['username'].widget = forms.HiddenInput()
self.fields['email'].initial = self.profile.user.email
def save(self, commit=True):
if self.cleaned_data:
if self.request.user.is_superuser:
self.profile.user.username = self.cleaned_data['username']
self.profile.user.email = self.cleaned_data['email']
self.profile.time_zone = self.cleaned_data['time_zone']
self.profile.language = self.cleaned_data['language']
self.profile.user.save()
if commit:
self.profile.save()
set_language(self.request, self.profile.language)
return self.profile
class PersonalProfileForm(forms.ModelForm):
name = forms.CharField(label=_('Real name'), required=False)
class Meta:
model = Profile
fields = ['status', 'location', 'site']
def __init__(self, *args, **kwargs):
extra_args = kwargs.pop('extra_args', {})
self.profile = kwargs['instance']
super(PersonalProfileForm, self).__init__(*args, **kwargs)
self.fields['name'].initial = "%s %s" % (self.profile.user.first_name, self.profile.user.last_name)
def save(self, commit=True):
self.profile.status = self.cleaned_data['status']
self.profile.location = self.cleaned_data['location']
self.profile.site = self.cleaned_data['site']
if self.cleaned_data['name']:
cleaned_name = self.cleaned_data['name'].strip()
if ' ' in cleaned_name:
self.profile.user.first_name, self.profile.user.last_name = cleaned_name.split(None, 1)
else:
self.profile.user.first_name = cleaned_name
self.profile.user.last_name = ''
self.profile.user.save()
if commit:
self.profile.save()
return self.profile
class MessagingProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['jabber', 'icq', 'msn', 'aim', 'yahoo']
def __init__(self, *args, **kwargs):
extra_args = kwargs.pop('extra_args', {})
super(MessagingProfileForm, self).__init__(*args, **kwargs)
class PersonalityProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['show_avatar', 'signature']
def __init__(self, *args, **kwargs):
extra_args = kwargs.pop('extra_args', {})
self.profile = kwargs['instance']
super(PersonalityProfileForm, self).__init__(*args, **kwargs)
self.fields['signature'].widget = forms.Textarea(attrs={'class':'markup', 'rows':'10', 'cols':'75'})
def clean(self):
cleaned_data = self.cleaned_data
signature = cleaned_data.get('signature')
if signature:
try:
convert_text_to_html(signature, self.profile)
except UnapprovedImageError as e:
self._errors['signature'] = self.error_class([e.user_error()])
del cleaned_data['signature']
cleaned_data['signature'] = filter_language(signature)
return cleaned_data
def save(self, commit=True):
profile = super(PersonalityProfileForm, self).save(commit=False)
profile.signature_html = convert_text_to_html(profile.signature, self.profile)
if forum_settings.SMILES_SUPPORT:
profile.signature_html = smiles(profile.signature_html)
if commit:
profile.save()
return profile
class DisplayProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['theme', 'markup', 'show_smilies']
def __init__(self, *args, **kwargs):
extra_args = kwargs.pop('extra_args', {})
super(DisplayProfileForm, self).__init__(*args, **kwargs)
class PrivacyProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['privacy_permission']
def __init__(self, *args, **kwargs):
extra_args = kwargs.pop('extra_args', {})
super(PrivacyProfileForm, self).__init__(*args, **kwargs)
self.fields['privacy_permission'].widget = forms.RadioSelect(
choices=self.fields['privacy_permission'].choices
)
class UploadAvatarForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['avatar']
def __init__(self, *args, **kwargs):
extra_args = kwargs.pop('extra_args', {})
super(UploadAvatarForm, self).__init__(*args, **kwargs)
class UserSearchForm(forms.Form):
username = forms.CharField(required=False, label=_('Username'))
#show_group = forms.ChoiceField(choices=SHOW_GROUP_CHOICES)
sort_by = forms.ChoiceField(choices=SORT_USER_BY_CHOICES, label=_('Sort by'))
sort_dir = forms.ChoiceField(choices=SORT_DIR_CHOICES, label=_('Sort order'))
def filter(self, qs):
if self.is_valid():
username = self.cleaned_data['username']
#show_group = self.cleaned_data['show_group']
sort_by = self.cleaned_data['sort_by']
sort_dir = self.cleaned_data['sort_dir']
qs = qs.filter(username__contains=username, forum_profile__post_count__gte=forum_settings.POST_USER_SEARCH)
if sort_by == 'username':
if sort_dir == 'ASC':
return qs.order_by('username')
elif sort_dir == 'DESC':
return qs.order_by('-username')
elif sort_by == 'registered':
if sort_dir == 'ASC':
return qs.order_by('date_joined')
elif sort_dir == 'DESC':
return qs.order_by('-date_joined')
elif sort_by == 'num_posts':
if sort_dir == 'ASC':
return qs.order_by('forum_profile__post_count')
elif sort_dir == 'DESC':
return qs.order_by('-forum_profile__post_count')
else:
return qs
class PostSearchForm(forms.Form):
keywords = forms.CharField(required=False, label=_('Keyword search'),
widget=forms.TextInput(attrs={'size':'40', 'maxlength':'100'}))
author = forms.CharField(required=False, label=_('Author search'),
widget=forms.TextInput(attrs={'size':'25', 'maxlength':'25'}))
forum = forms.CharField(required=False, label=_('Forum'))
search_in = forms.ChoiceField(choices=SEARCH_IN_CHOICES, label=_('Search in'))
sort_by = forms.ChoiceField(choices=SORT_POST_BY_CHOICES, label=_('Sort by'))
sort_dir = forms.ChoiceField(choices=SORT_DIR_CHOICES, initial='DESC', label=_('Sort order'))
show_as = forms.ChoiceField(choices=SHOW_AS_CHOICES, label=_('Show results as'))
class ReputationForm(forms.ModelForm):
class Meta:
model = Reputation
fields = ['reason', 'post', 'sign']
def __init__(self, *args, **kwargs):
self.from_user = kwargs.pop('from_user', None)
self.to_user = kwargs.pop('to_user', None)
self.post = kwargs.pop('post', None)
self.sign = kwargs.pop('sign', None)
super(ReputationForm, self).__init__(*args, **kwargs)
self.fields['post'].widget = forms.HiddenInput()
self.fields['sign'].widget = forms.HiddenInput()
self.fields['reason'].widget = forms.Textarea(attrs={'class':'markup'})
def clean_to_user(self):
name = self.cleaned_data['to_user']
try:
user = User.objects.get(username=name)
except User.DoesNotExist:
raise forms.ValidationError(_('User "%s" does not exist') % name)
else:
return user
def clean(self):
try:
Reputation.objects.get(from_user=self.from_user, post=self.cleaned_data['post'])
except Reputation.DoesNotExist:
pass
else:
raise forms.ValidationError(_('You already voted for this post'))
# check if this post really belong to `from_user`
if not Post.objects.filter(pk=self.cleaned_data['post'].id, user=self.to_user).exists():
raise forms.ValidationError(_('This post does\'t belong to that user'))
return self.cleaned_data
def save(self, commit=True):
reputation = super(ReputationForm, self).save(commit=False)
reputation.from_user = self.from_user
reputation.to_user = self.to_user
if commit:
reputation.save()
return reputation
class MailToForm(forms.Form):
subject = forms.CharField(label=_('Subject'),
widget=forms.TextInput(attrs={'size':'75', 'maxlength':'70', 'class':'longinput'}))
body = forms.CharField(required=False, label=_('Message'),
widget=forms.Textarea(attrs={'rows':'10', 'cols':'75'}))
class ReportForm(forms.ModelForm):
class Meta:
model = Report
fields = ['reason', 'post']
def __init__(self, *args, **kwargs):
self.reported_by = kwargs.pop('reported_by', None)
self.post = kwargs.pop('post', None)
super(ReportForm, self).__init__(*args, **kwargs)
self.fields['post'].widget = forms.HiddenInput()
self.fields['post'].initial = self.post
self.fields['reason'].widget = forms.Textarea(attrs={'rows':'5', 'cols':'75', 'maxlength':'500'})
def save(self, commit=True):
report = super(ReportForm, self).save(commit=False)
report.created = now()
report.reported_by = self.reported_by
if commit:
report.save()
return report
class VotePollForm(forms.Form):
"""
Dynamic form for the poll.
"""
FORM_NAME = "VotePollForm" # used in view and template submit button
choice = forms.MultipleChoiceField()
def __init__(self, poll, *args, **kwargs):
self.poll = poll
super(VotePollForm, self).__init__(*args, **kwargs)
choices = self.poll.choices.all().values_list("id", "choice")
if self.poll.choice_count == 1:
self.fields["choice"] = forms.ChoiceField(
choices=choices, widget=forms.RadioSelect
)
else:
self.fields["choice"] = forms.MultipleChoiceField(
choices=choices, widget=forms.CheckboxSelectMultiple
)
def clean_choice(self):
ids = self.cleaned_data["choice"]
count = len(ids)
if count > self.poll.choice_count:
raise forms.ValidationError(
_(u'You have selected too many choices. You may only select %i.') % self.poll.choice_count
)
return ids
class PollForm(forms.ModelForm):
answers = forms.CharField(min_length=2, widget=forms.Textarea,
help_text=_("Write each answer on a new line.")
)
days = forms.IntegerField(required=False, min_value=1,
help_text=_("Number of days for this poll to run. Leave empty for an indefinite poll.")
)
class Meta:
model = Poll
fields = ['question', 'choice_count']
def create_poll(self):
"""
return True if one field filled with data -> the user wants to create a poll
"""
return any(self.data.get(key) for key in ('question', 'answers', 'days'))
def clean_answers(self):
# validate if there is more than whitespaces ;)
raw_answers = self.cleaned_data["answers"]
answers = [answer.strip() for answer in raw_answers.splitlines() if answer.strip()]
if len(answers) == 0:
raise forms.ValidationError(_(u"There is no valid answer."))
# validate length of all answers
is_max_length = max([len(answer) for answer in answers])
should_max_length = PollChoice._meta.get_field("choice").max_length
if is_max_length > should_max_length:
raise forms.ValidationError(_(u"One of the answers is too long."))
return answers
def save(self, post):
"""
Create poll and all answers in PollChoice model.
"""
poll = super(PollForm, self).save(commit=False)
poll.topic = post.topic
days = self.cleaned_data["days"]
if days:
now = now()
poll.deactivate_date = now + timedelta(days=days)
poll.save()
answers = self.cleaned_data["answers"]
for answer in answers:
PollChoice.objects.create(poll=poll, choice=answer)