Add ability to create bans from reports
This is done in the admin view and opens a new iframed window. The ban form is pretty barebones and doesn't have full functionality yet, but that is coming. Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
@@ -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'<img src="{obj.post.thumbnail.url}" />')
|
||||
@@ -41,11 +54,19 @@ class ReportRecordAdmin(admin.ModelAdmin):
|
||||
else:
|
||||
html += "<div>"
|
||||
if obj.post.subject:
|
||||
html += f"<strong>{obj.post.subject}</strong>"
|
||||
html += f"<p><strong>{obj.post.subject}</strong></p>"
|
||||
html += f"<p>{obj.post.text}</p>"
|
||||
html += "</div>"
|
||||
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'<a href="#" data-ban-url="{ban_url}" class="ban_link">Ban</a>'
|
||||
)
|
||||
|
||||
|
||||
@admin.register(RangeBan)
|
||||
class RangeBanAdmin(admin.ModelAdmin):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends "admin/change_list.html" %}
|
||||
{% load static %}
|
||||
{% block extrastyle %}
|
||||
{{ block.super }}
|
||||
<style>
|
||||
@@ -9,5 +10,53 @@
|
||||
background-color: var(--message-error-bg);
|
||||
color: var(--error-fg);
|
||||
}
|
||||
|
||||
.wb-min {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wb-max {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wb-full {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
{% endblock extrastyle %}
|
||||
{% endblock extrastyle %}
|
||||
{% block extrahead %}
|
||||
{{block.super}}
|
||||
<script src="{% static 'board/jquery.js' %}"></script>
|
||||
<script src="{% static 'board/winbox.bundle.js' %}"></script>
|
||||
{% endblock extrahead %}
|
||||
|
||||
{% block footer %}
|
||||
{{block.super}}
|
||||
<script>
|
||||
|
||||
function openBanWindow(e) {
|
||||
e.preventDefault();
|
||||
let banUrl = e.target.getAttribute("data-ban-url");
|
||||
if (window.banWindow) {
|
||||
window.banWindow.close();
|
||||
}
|
||||
window.banWindow = new WinBox("New ban", {
|
||||
url: banUrl,
|
||||
x: "center",
|
||||
y: "center",
|
||||
root: document.body,
|
||||
onclose: function(force) {
|
||||
window.top.banWindow = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onLoad(e) {
|
||||
window.banWindow = null;
|
||||
}
|
||||
|
||||
$(".ban_link").on("click", openBanWindow);
|
||||
$(window).on("load", onLoad);
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
50
board/templates/board/ban_form.html
Normal file
50
board/templates/board/ban_form.html
Normal file
@@ -0,0 +1,50 @@
|
||||
{% extends "board/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<form id="form" action="{% url 'board:ban_create' url=board.url id=post.id %}" method="post">
|
||||
{% csrf_token %}
|
||||
<table>
|
||||
<tr>
|
||||
<th>{% translate 'Post ID' %}:</th>
|
||||
<td>{{post.id}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{% translate 'Board' %}:</th>
|
||||
<td>{{board.url}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>IP:</th>
|
||||
<td>{{post.ip}}</td>
|
||||
</tr>
|
||||
{{form.as_table}}
|
||||
<tr>
|
||||
<th>{% translate "Previous bans" %}</th>
|
||||
<td>{{previous_bans|length}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{% translate "Current bans" %}</th>
|
||||
<td id="current_bans">{{current_bans|length}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th></th>
|
||||
<td><input type="submit" id="submit" value="{% translate 'Create ban' %}" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function submitConfirm(e) {
|
||||
console.log($("#current_bans").text());
|
||||
if($("#current_bans").text() !== '0') {
|
||||
let result = window.confirm("There is already at least one ban for this IP address on this board, are you sure you want to continue?");
|
||||
if (!result) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$("#submit").on("click", submitConfirm);
|
||||
</script>
|
||||
{% endblock content %}
|
||||
29
board/templates/board/ban_success.html
Normal file
29
board/templates/board/ban_success.html
Normal file
@@ -0,0 +1,29 @@
|
||||
{% extends "board/base.html" %}
|
||||
{% load l10n %}
|
||||
{# Title #}
|
||||
{% block title %}{% localize on %}Ban success{% endlocalize %}{% endblock %}
|
||||
{# Body #}
|
||||
{% block content %}
|
||||
<div class="row" id="message">
|
||||
{% localize on %}A ban has been created. This window will close in 1 second.{% endlocalize %}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function isIframe() {
|
||||
try {
|
||||
return window.self !== window.top;
|
||||
} catch (_) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(function() {
|
||||
if(isIframe()) {
|
||||
window.top.banWindow.close();
|
||||
} else {
|
||||
window.close();
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -10,12 +10,18 @@ urlpatterns = [
|
||||
path("<slug:url>/", BoardView.as_view(), name="board_detail"),
|
||||
path("<slug:url>/page/<int:page>/", BoardView.as_view(), name="board_detail"),
|
||||
path("<slug:url>/post/<int:id>/", PostView.as_view(), name="post_detail"),
|
||||
path("<slug:url>/report/<int:id>/", ReportView.as_view(), name="report_form"),
|
||||
path("report/<slug:url>/<int:id>/", ReportView.as_view(), name="report_form"),
|
||||
path(
|
||||
"report/success/",
|
||||
TemplateView.as_view(template_name="board/report_success.html"),
|
||||
name="report_success",
|
||||
),
|
||||
path("ban/<slug:url>/<int:id>/", BanCreateView.as_view(), name="ban_create"),
|
||||
path(
|
||||
"ban/success/",
|
||||
BanSuccessView.as_view(),
|
||||
name="ban_success",
|
||||
),
|
||||
path(
|
||||
"banned",
|
||||
BannedView.as_view(),
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user