diff --git a/board/admin.py b/board/admin.py
index 9207029..e4f29a8 100644
--- a/board/admin.py
+++ b/board/admin.py
@@ -1,8 +1,14 @@
+from django import forms
from django.contrib import admin
+from django.http.response import HttpResponseRedirect
+from django.shortcuts import render
+from django.urls import reverse
from django.utils.safestring import mark_safe
from board.models import Ban, Board, Post, RangeBan, ReportReason, ReportRecord
-
+#
+# Admin sites
+#
@admin.register(Board)
class BoardAdmin(admin.ModelAdmin):
pass
@@ -18,16 +24,23 @@ class ReportReasonAdmin(admin.ModelAdmin):
pass
+class BanFromReportForm(forms.ModelForm):
+ pass
+
+
@admin.register(ReportRecord)
class ReportRecordAdmin(admin.ModelAdmin):
ordering = (
"-urgent",
"-weight",
)
- readonly_fields = ("post",)
- list_display = ("post_thumbnail", "post_body")
+ readonly_fields = ("post", "weight", "urgent")
+ list_display = ("post_id", "post_thumbnail", "post_body", "create_ban")
save_as = False
+ def post_id(self, obj):
+ return obj.post.id
+
def post_thumbnail(self, obj):
if obj.post.thumbnail:
return mark_safe(f'
')
@@ -41,11 +54,19 @@ class ReportRecordAdmin(admin.ModelAdmin):
else:
html += "
"
if obj.post.subject:
- html += f"
{obj.post.subject}"
+ html += f"
{obj.post.subject}
"
html += f"
{obj.post.text}
"
html += "
"
return mark_safe(html)
+ def create_ban(self, obj):
+ post = obj.post
+ board = obj.post.board
+ ban_url = reverse("board:ban_create", kwargs={"url": board.url, "id": post.id})
+ return mark_safe(
+ f'Ban'
+ )
+
@admin.register(RangeBan)
class RangeBanAdmin(admin.ModelAdmin):
diff --git a/board/forms.py b/board/forms.py
index faf2c2d..2f8d039 100644
--- a/board/forms.py
+++ b/board/forms.py
@@ -1,8 +1,10 @@
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 board.models import Post, Report, ReportReason, ReportRecord
+from django.utils import timezone
+from board.models import Ban, Post, Report, ReportReason, ReportRecord
from hcaptcha.fields import hCaptchaField
@@ -74,3 +76,32 @@ class ReportForm(ModelForm):
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"]
+
+ def __init__(self, *args, op, **kwargs):
+ super(BanForm, self).__init__(*args, **kwargs)
+ self.op = op
+ self.instance.board = op.board
+ self.instance.ip = op.ip
+
+ 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
diff --git a/board/models.py b/board/models.py
index 5b09283..d32b33b 100644
--- a/board/models.py
+++ b/board/models.py
@@ -304,7 +304,7 @@ class RangeBan(BanCommon):
class Ban(BanCommon):
# IP address of the ban
- ip = models.GenericIPAddressField(unique=True)
+ ip = models.GenericIPAddressField()
def __str__(self):
return f"Ban for {self.ip}"
diff --git a/board/static/board/style.css b/board/static/board/style.css
index c47dd3d..8b1ac9e 100644
--- a/board/static/board/style.css
+++ b/board/static/board/style.css
@@ -6,11 +6,16 @@ hr {
color: #ededed;
}
+th {
+ text-align: left;
+ vertical-align: top;
+}
+
.column {
float: left;
width: 33.33%;
}
-
+
/* Clear floats after the columns */
.row:after {
content: "";
@@ -117,7 +122,7 @@ hr {
/* Misc */
a:link {
- color:#555;
+ color: #555;
}
a:visited {
diff --git a/board/templates/admin/board/reportrecord/change_list.html b/board/templates/admin/board/reportrecord/change_list.html
index e7c073c..766a1c0 100644
--- a/board/templates/admin/board/reportrecord/change_list.html
+++ b/board/templates/admin/board/reportrecord/change_list.html
@@ -1,4 +1,5 @@
{% extends "admin/change_list.html" %}
+{% load static %}
{% block extrastyle %}
{{ block.super }}
-{% endblock extrastyle %}
\ No newline at end of file
+{% endblock extrastyle %}
+{% block extrahead %}
+{{block.super}}
+
+
+{% endblock extrahead %}
+
+{% block footer %}
+{{block.super}}
+
+{% endblock %}
\ No newline at end of file
diff --git a/board/templates/board/ban_form.html b/board/templates/board/ban_form.html
new file mode 100644
index 0000000..918ff12
--- /dev/null
+++ b/board/templates/board/ban_form.html
@@ -0,0 +1,50 @@
+{% extends "board/base.html" %}
+{% load i18n %}
+
+{% block content %}
+
+
+
+{% endblock content %}
\ No newline at end of file
diff --git a/board/templates/board/ban_success.html b/board/templates/board/ban_success.html
new file mode 100644
index 0000000..21bb2e8
--- /dev/null
+++ b/board/templates/board/ban_success.html
@@ -0,0 +1,29 @@
+{% extends "board/base.html" %}
+{% load l10n %}
+{# Title #}
+{% block title %}{% localize on %}Ban success{% endlocalize %}{% endblock %}
+{# Body #}
+{% block content %}
+
+ {% localize on %}A ban has been created. This window will close in 1 second.{% endlocalize %}
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/board/urls.py b/board/urls.py
index c584be4..55bb1af 100644
--- a/board/urls.py
+++ b/board/urls.py
@@ -10,12 +10,18 @@ urlpatterns = [
path("/", BoardView.as_view(), name="board_detail"),
path("/page//", BoardView.as_view(), name="board_detail"),
path("/post//", PostView.as_view(), name="post_detail"),
- path("/report//", ReportView.as_view(), name="report_form"),
+ path("report///", ReportView.as_view(), name="report_form"),
path(
"report/success/",
TemplateView.as_view(template_name="board/report_success.html"),
name="report_success",
),
+ path("ban///", BanCreateView.as_view(), name="ban_create"),
+ path(
+ "ban/success/",
+ BanSuccessView.as_view(),
+ name="ban_success",
+ ),
path(
"banned",
BannedView.as_view(),
diff --git a/board/views.py b/board/views.py
index 377cb23..de82e65 100644
--- a/board/views.py
+++ b/board/views.py
@@ -1,15 +1,24 @@
+from typing import Any, Dict
from django.conf import settings
+from django.contrib.auth.mixins import PermissionRequiredMixin
from django.http import Http404, HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.views.generic.base import TemplateView
from django.views.generic.edit import CreateView
from django.urls import reverse, reverse_lazy
from django.utils import timezone
-from board.forms import PostForm, ReplyForm, ReportForm
-from board.models import Post, Board, Report
+from board.forms import BanForm, PostForm, ReplyForm, ReportForm
+from board.models import Ban, Board, Post, Report
from board.utils import get_client_ip, get_ip_bans
-__all__ = ("BannedView", "BoardView", "PostView", "ReportView")
+__all__ = (
+ "BanCreateView",
+ "BanSuccessView",
+ "BannedView",
+ "BoardView",
+ "PostView",
+ "ReportView",
+)
class BannedView(TemplateView):
@@ -37,18 +46,6 @@ class CreatePostView(CreateView):
* self.board
"""
- def get(self, request, *args, **kwargs):
- self._set_board(kwargs["url"])
- return super(CreatePostView, self).get(request, *args, **kwargs)
-
- def post(self, request, *args, **kwargs):
- self._set_board(kwargs["url"])
- return super(CreatePostView, self).post(request, *args, **kwargs)
-
- def _set_board(self, board_url: str):
- board = get_object_or_404(Board, url=board_url)
- self.board = board
-
def get_form_kwargs(self):
kwargs = super(CreatePostView, self).get_form_kwargs()
kwargs["board"] = self.board
@@ -56,7 +53,9 @@ class CreatePostView(CreateView):
return kwargs
def dispatch(self, request, *args, **kwargs):
- self._set_board(kwargs["url"])
+ # Set the board on this object
+ self.board = get_object_or_404(Board, url=kwargs["url"])
+
ip = get_client_ip(request)
# Filter bans by board first
bans = [
@@ -162,3 +161,47 @@ class ReportView(CreatePostView):
post = get_object_or_404(Post, id=post_id)
kwargs["op"] = post
return kwargs
+
+
+class BanCreateView(PermissionRequiredMixin, CreateView):
+ model = Ban
+ form_class = BanForm
+ permission_required = "ban.create"
+ success_url = reverse_lazy("board:ban_success")
+
+ def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
+ context = super().get_context_data(**kwargs)
+ context["board"] = self.board
+ context["post"] = self.post_obj
+ ip = self.post_obj.ip
+ now = timezone.now()
+ bans = [
+ ban
+ for ban in get_ip_bans(ip)
+ if ban.board == self.board or ban.board is None
+ ]
+ context["previous_bans"] = [
+ ban for ban in bans if ban.expires is not None and ban.expires < now
+ ]
+ context["current_bans"] = [
+ ban for ban in bans if ban.expires is None or ban.expires > now
+ ]
+
+ return context
+
+ def get_form_kwargs(self) -> Dict[str, Any]:
+ kwargs = super(CreateView, self).get_form_kwargs()
+ post_id = self.kwargs["id"]
+ post = get_object_or_404(Post, id=post_id)
+ kwargs["op"] = post
+ return kwargs
+
+ def dispatch(self, request, *args, **kwargs):
+ self.board = get_object_or_404(Board, url=kwargs["url"])
+ self.post_obj = get_object_or_404(Post, pk=kwargs["id"])
+ return super().dispatch(request, *args, **kwargs)
+
+
+class BanSuccessView(PermissionRequiredMixin, TemplateView):
+ permission_required = "ban.create"
+ template_name = "board/ban_success.html"