2022-06-28 21:37:45 -07:00
|
|
|
from django.core.exceptions import ValidationError
|
2022-05-07 14:51:26 -07:00
|
|
|
from django.conf import settings
|
2022-06-19 22:03:01 -07:00
|
|
|
from django.db import transaction
|
2022-06-19 22:41:02 -07:00
|
|
|
from django.db.models import Q
|
2022-06-23 16:03:09 -07:00
|
|
|
from django import forms
|
2022-06-19 22:41:02 -07:00
|
|
|
from django.forms import ModelForm, ModelChoiceField
|
2022-06-30 18:16:08 -07:00
|
|
|
from django.forms.models import fields_for_model
|
2022-06-23 16:03:09 -07:00
|
|
|
from django.utils import timezone
|
2022-07-08 21:55:25 -07:00
|
|
|
from django.utils.translation import gettext as _
|
2022-07-01 18:41:40 -07:00
|
|
|
from guardian.shortcuts import get_objects_for_user
|
2022-06-23 16:03:09 -07:00
|
|
|
from board.models import Ban, Post, Report, ReportReason, ReportRecord
|
2022-05-07 14:51:26 -07:00
|
|
|
from hcaptcha.fields import hCaptchaField
|
2022-05-07 13:24:43 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class PostForm(ModelForm):
|
|
|
|
|
"""
|
|
|
|
|
A form used for new threads for posts.
|
|
|
|
|
|
|
|
|
|
This requires the board and the IP address to be specified.
|
|
|
|
|
"""
|
|
|
|
|
|
2022-05-07 14:51:26 -07:00
|
|
|
hcaptcha = hCaptchaField() if settings.USE_HCAPTCHA else None
|
|
|
|
|
|
2022-05-07 13:24:43 -07:00
|
|
|
class Meta:
|
|
|
|
|
model = Post
|
2022-07-01 18:41:40 -07:00
|
|
|
fields = ["subject", "name", "text", "capcode", "image"]
|
2022-05-07 13:24:43 -07:00
|
|
|
|
2022-06-28 23:55:44 -07:00
|
|
|
def __init__(self, *args, user, board, ip, **kwargs):
|
2022-05-07 13:24:43 -07:00
|
|
|
super(PostForm, self).__init__(*args, **kwargs)
|
2022-06-28 23:55:44 -07:00
|
|
|
self.user = user
|
2022-05-07 13:24:43 -07:00
|
|
|
self.instance.board = board
|
|
|
|
|
self.instance.ip = ip
|
2022-07-01 18:41:40 -07:00
|
|
|
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):
|
2022-07-08 21:55:25 -07:00
|
|
|
raise ValidationError(_("Could not create post"))
|
2022-05-07 13:24:43 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
"""
|
|
|
|
|
|
2022-05-07 14:51:26 -07:00
|
|
|
hcaptcha = hCaptchaField() if settings.USE_HCAPTCHA else None
|
|
|
|
|
|
2022-05-07 13:24:43 -07:00
|
|
|
class Meta:
|
|
|
|
|
model = Post
|
2022-06-28 21:37:45 -07:00
|
|
|
fields = ["name", "text", "bump", "capcode", "image"]
|
2022-05-07 13:24:43 -07:00
|
|
|
|
2022-06-28 23:55:44 -07:00
|
|
|
def __init__(self, *args, op, **kwargs):
|
2022-05-07 13:24:43 -07:00
|
|
|
super(ReplyForm, self).__init__(*args, **kwargs)
|
2022-07-01 18:41:40 -07:00
|
|
|
# Get the true OP of this post
|
|
|
|
|
while op.op:
|
|
|
|
|
op = op.op
|
2022-05-07 13:24:43 -07:00
|
|
|
self.instance.op = op
|
2022-07-01 18:41:40 -07:00
|
|
|
self.fields["capcode"].queryset = get_objects_for_user(
|
|
|
|
|
self.user, "board.use_capcode"
|
|
|
|
|
)
|
2022-06-28 21:37:45 -07:00
|
|
|
|
|
|
|
|
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):
|
2022-07-08 21:55:25 -07:00
|
|
|
raise ValidationError(_("Could not create post"))
|
2022-06-14 14:56:50 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class ReportForm(ModelForm):
|
|
|
|
|
"""
|
|
|
|
|
A form used to create reports on posts.
|
|
|
|
|
|
|
|
|
|
This requires the board and the IP address to be specified.
|
|
|
|
|
"""
|
|
|
|
|
|
2022-06-19 22:03:01 -07:00
|
|
|
# uses report_form.html
|
|
|
|
|
|
2022-06-14 14:56:50 -07:00
|
|
|
class Meta:
|
|
|
|
|
model = Report
|
|
|
|
|
fields = ["reason"]
|
|
|
|
|
|
|
|
|
|
def __init__(self, *args, op, board, ip, **kwargs):
|
|
|
|
|
super(ReportForm, self).__init__(*args, **kwargs)
|
|
|
|
|
self.instance.ip = ip
|
2022-06-19 22:03:01 -07:00
|
|
|
self.op = op
|
2022-06-19 22:41:02 -07:00
|
|
|
queryset = ReportReason.objects.filter(Q(board=None) | Q(board=board))
|
|
|
|
|
self.fields["reason"] = ModelChoiceField(queryset=queryset)
|
2022-06-19 22:03:01 -07:00
|
|
|
|
|
|
|
|
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)
|
2022-07-13 13:06:33 -07:00
|
|
|
|
|
|
|
|
ip = self.instance.ip
|
|
|
|
|
if record.report_set.filter(ip=ip):
|
2022-07-13 12:35:57 -07:00
|
|
|
raise ValidationError(_("You have already reported this post."))
|
2022-07-13 13:06:33 -07:00
|
|
|
|
|
|
|
|
# Not sure if it's possible to use the ORM to get a related field of a
|
|
|
|
|
# related field - report.record.post.board
|
|
|
|
|
board = self.op.board
|
|
|
|
|
all_reports = [
|
|
|
|
|
r
|
|
|
|
|
for r in Report.objects.filter(ip=ip).order_by("-created")
|
|
|
|
|
if r.record.post.board == board
|
|
|
|
|
]
|
|
|
|
|
if all_reports:
|
|
|
|
|
last_report = all_reports[0]
|
|
|
|
|
now = timezone.now()
|
|
|
|
|
delta = now - last_report.created
|
|
|
|
|
if delta < board.report_cooldown:
|
|
|
|
|
raise ValidationError(
|
|
|
|
|
_(f"Please wait a few seconds before submitting another report"),
|
|
|
|
|
)
|
2022-06-19 22:03:01 -07:00
|
|
|
self.instance.record = record
|
|
|
|
|
return super(ReportForm, self).clean()
|
2022-06-23 16:03:09 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
2022-06-23 18:57:23 -07:00
|
|
|
fields = ["ban_reason", "board"]
|
2022-06-23 16:03:09 -07:00
|
|
|
|
|
|
|
|
def __init__(self, *args, op, **kwargs):
|
|
|
|
|
super(BanForm, self).__init__(*args, **kwargs)
|
|
|
|
|
self.op = op
|
|
|
|
|
self.instance.ip = op.ip
|
2022-06-23 18:57:23 -07:00
|
|
|
self.instance.post_id = op.id
|
2022-06-23 16:03:09 -07:00
|
|
|
|
|
|
|
|
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
|
2022-06-30 16:21:36 -07:00
|
|
|
|
|
|
|
|
|
2022-07-13 15:19:20 -07:00
|
|
|
class PostDeleteForm(forms.Form):
|
|
|
|
|
image_only = forms.CharField(widget=forms.HiddenInput(), initial="0")
|
|
|
|
|
|
|
|
|
|
|
2022-06-30 16:21:36 -07:00
|
|
|
class PostModifyForm(ModelForm):
|
|
|
|
|
"""
|
|
|
|
|
A form used to modify the attributes of a post (sticky, locked, sink, etc)
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
model = Post
|
2022-06-30 18:16:08 -07:00
|
|
|
# we specify fields up here too because otherwise they won't be
|
|
|
|
|
# recognized by the form to update values
|
2022-07-08 21:55:25 -07:00
|
|
|
fields = ["sticky", "bump", "lock"]
|
2022-06-30 16:21:36 -07:00
|
|
|
|
2022-06-30 18:16:08 -07:00
|
|
|
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"]
|
2022-06-30 18:42:16 -07:00
|
|
|
if self.user.has_perm("board.set_bump"):
|
|
|
|
|
fields += ["bump"]
|
2022-07-08 21:55:25 -07:00
|
|
|
if self.user.has_perm("board.set_lock") and not self.instance.op:
|
|
|
|
|
fields += ["lock"]
|
2022-06-30 18:16:08 -07:00
|
|
|
# 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)
|