Files
interchan/board/forms.py

168 lines
5.4 KiB
Python
Raw Normal View History

from django.core.exceptions import ValidationError
from django.conf import settings
from django.db import transaction
from django.db.models import Q
from django import forms
from django.forms import ModelForm, ModelChoiceField
from django.forms.models import fields_for_model
from django.utils import timezone
from django.utils.translation import gettext as _
from guardian.shortcuts import get_objects_for_user
from board.models import Ban, Post, Report, ReportReason, ReportRecord
from hcaptcha.fields import hCaptchaField
class PostForm(ModelForm):
"""
A form used for new threads for posts.
This requires the board and the IP address to be specified.
"""
hcaptcha = hCaptchaField() if settings.USE_HCAPTCHA else None
class Meta:
model = Post
fields = ["subject", "name", "text", "capcode", "image"]
def __init__(self, *args, user, board, ip, **kwargs):
super(PostForm, self).__init__(*args, **kwargs)
self.user = user
self.instance.board = board
self.instance.ip = ip
self.fields["capcode"].queryset = get_objects_for_user(
self.user, "board.use_capcode"
)
def clean(self):
super().clean()
capcode = self.cleaned_data["capcode"]
if capcode:
if not self.user or not self.user.has_perm("board.use_capcode", capcode):
raise ValidationError(_("Could not create post"))
class ReplyForm(PostForm):
"""
A form used for replies to posts.
This requires the OP post, the reply post, the board, and the IP address be
specified.
"""
hcaptcha = hCaptchaField() if settings.USE_HCAPTCHA else None
class Meta:
model = Post
fields = ["name", "text", "bump", "capcode", "image"]
def __init__(self, *args, op, **kwargs):
super(ReplyForm, self).__init__(*args, **kwargs)
# Get the true OP of this post
while op.op:
op = op.op
self.instance.op = op
self.fields["capcode"].queryset = get_objects_for_user(
self.user, "board.use_capcode"
)
def clean(self):
super().clean()
capcode = self.cleaned_data["capcode"]
if capcode:
if not self.user or not self.user.has_perm("board.use_capcode", capcode):
raise ValidationError(_("Could not create post"))
class ReportForm(ModelForm):
"""
A form used to create reports on posts.
This requires the board and the IP address to be specified.
"""
# uses report_form.html
class Meta:
model = Report
fields = ["reason"]
def __init__(self, *args, op, board, ip, **kwargs):
super(ReportForm, self).__init__(*args, **kwargs)
self.instance.ip = ip
self.op = op
queryset = ReportReason.objects.filter(Q(board=None) | Q(board=board))
self.fields["reason"] = ModelChoiceField(queryset=queryset)
def clean(self):
# Get or create the record before creating the model
with transaction.atomic():
try:
record = ReportRecord.objects.get(post=self.op)
except ReportRecord.DoesNotExist:
record = ReportRecord.objects.create(post=self.op)
self.instance.record = record
return super(ReportForm, self).clean()
class BanForm(ModelForm):
"""
A form used to create bans based on specific posts.
"""
# uses ban_form.html
duration = forms.IntegerField(label="Duration (days)", min_value=1, required=False)
class Meta:
model = Ban
fields = ["ban_reason", "board"]
def __init__(self, *args, op, **kwargs):
super(BanForm, self).__init__(*args, **kwargs)
self.op = op
self.instance.ip = op.ip
self.instance.post_id = op.id
def clean(self):
super(BanForm, self).clean()
now = timezone.now()
duration = self.cleaned_data["duration"]
if duration:
expires = now + timezone.timedelta(days=duration)
else:
expires = None
self.instance.expires = expires
class PostModifyForm(ModelForm):
"""
A form used to modify the attributes of a post (sticky, locked, sink, etc)
"""
class Meta:
model = Post
# we specify fields up here too because otherwise they won't be
# recognized by the form to update values
fields = ["sticky", "bump", "lock"]
def __init__(self, *args, user, **kwargs):
super(PostModifyForm, self).__init__(*args, **kwargs)
self.user = user
fields = []
if self.user.has_perm("board.set_sticky"):
fields += ["sticky"]
if self.user.has_perm("board.set_bump"):
fields += ["bump"]
if self.user.has_perm("board.set_lock") and not self.instance.op:
fields += ["lock"]
# NOTE:
# We do *not* need to check permissions against these fields we're
# setting down here in the self.clean() function in the case that a
# malicious actor has access to the modify form and injects a "sticky"
# value to their modify request.
#
# We specify fields up in the Meta class, but we reset them down here.
# If the field isn't present in this list, then it doesn't get updated.
# If the field isn't present in the above list, then it doesn't get updated.
self.fields = fields_for_model(Post, fields)