Add preliminary report system
The report system is pretty low-tech. However the scaffolding is there for a lot of new stuff. What we currently have: * Users can create reports * Staff can view reports * Admins can create report templates There's a post drop-down menu available on all posts now, too. This is where "report post" menu item lives and other things like that can be added too. Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
from django.conf import settings
|
||||
from django.forms import ModelForm
|
||||
from board.models import Post
|
||||
from board.models import Post, Report
|
||||
from hcaptcha.fields import hCaptchaField
|
||||
|
||||
|
||||
@@ -41,3 +41,20 @@ class ReplyForm(PostForm):
|
||||
super(ReplyForm, self).__init__(*args, **kwargs)
|
||||
self.instance.op = op
|
||||
self.instance.reply = reply
|
||||
|
||||
|
||||
class ReportForm(ModelForm):
|
||||
"""
|
||||
A form used to create reports on posts.
|
||||
|
||||
This requires the board and the IP address to be specified.
|
||||
"""
|
||||
|
||||
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.instance.post = op
|
||||
|
||||
@@ -212,7 +212,7 @@ class ReportReason(models.Model):
|
||||
# Urgency. If true, this post is probably reported as illegal content.
|
||||
urgent = models.BooleanField(default=False)
|
||||
# This is a board-specific report reason
|
||||
board = models.ForeignKey("Board", on_delete=models.CASCADE, null=True)
|
||||
board = models.ForeignKey("Board", on_delete=models.CASCADE, null=True, blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.reason
|
||||
|
||||
@@ -1,9 +1,81 @@
|
||||
function doQuote(sender) {
|
||||
let id_text = $("#id_text");
|
||||
let caret = id_text[0].selectionStart;
|
||||
let text = id_text.val();
|
||||
let to_add = ">>" + sender.target.innerText + "\n";
|
||||
id_text.val(text.substring(0, caret) + to_add + text.substring(caret));
|
||||
const OPEN = "open";
|
||||
const CLOSED = "closed";
|
||||
|
||||
function documentClick(e) {
|
||||
let sender = e.target;
|
||||
let id = sender.getAttribute("data-id");
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (id) {
|
||||
case "post_menu_button": {
|
||||
openMenu(e);
|
||||
}; break;
|
||||
}
|
||||
}
|
||||
|
||||
function doQuote(e) {
|
||||
let idText = $("#id_text");
|
||||
let caret = idText[0].selectionStart;
|
||||
let text = idText.val();
|
||||
let toAdd = ">>" + e.target.innerText + "\n";
|
||||
idText.val(text.substring(0, caret) + toAdd + text.substring(caret));
|
||||
}
|
||||
|
||||
function closeMenu(e) {
|
||||
$(document).off("click", closeMenu);
|
||||
$(".post_menu").remove();
|
||||
}
|
||||
|
||||
function openMenu(e) {
|
||||
e.preventDefault();
|
||||
let sender = e.target;
|
||||
let reportButton = $("<a>")
|
||||
.text("Report")
|
||||
.attr("href", "#")
|
||||
.on("click", (e) => { return openReportWindow(e, $(sender.parentElement)); });
|
||||
let menuList = $("<ul></ul>")
|
||||
.append($("<lh><strong>Actions</strong></lh>").addClass("post_menu_item"))
|
||||
.append(
|
||||
$('<li></li>')
|
||||
.addClass("post_menu_item")
|
||||
.append(reportButton)
|
||||
)
|
||||
.addClass("post_menu_items");
|
||||
let rect = sender.getBoundingClientRect();
|
||||
let menu = $("<div></div>")
|
||||
.addClass("post_menu")
|
||||
.css({
|
||||
top: rect.bottom + 3 + window.pageYOffset + "px",
|
||||
left: rect.left + 3 + window.pageXOffset + "px",
|
||||
})
|
||||
.append(menuList);
|
||||
$("body").append(menu);
|
||||
$(document).on("click", closeMenu);
|
||||
}
|
||||
|
||||
function openReportWindow(e, postElement) {
|
||||
e.preventDefault();
|
||||
// If there's already a report window open, close it and open this one.
|
||||
if (window.top.reportWindow) {
|
||||
window.top.reportWindow.close();
|
||||
}
|
||||
//let postId = sender.parentElement.getAttribute("id").substring(1);
|
||||
let reportUrl = postElement.attr("data-report-url");
|
||||
window.reportWindow = new WinBox("New Report", {
|
||||
url: reportUrl,
|
||||
modal: true,
|
||||
onclose: function (force) {
|
||||
window.top.reportWindow = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onLoad(e) {
|
||||
window.reportWindow = null;
|
||||
}
|
||||
|
||||
$(document).on("click", documentClick);
|
||||
$(document).on("click", ".post_id", doQuote);
|
||||
$(window).on("load", onLoad);
|
||||
@@ -1,3 +1,7 @@
|
||||
body {
|
||||
background-color: #ededed;
|
||||
}
|
||||
|
||||
hr {
|
||||
color: #ededed;
|
||||
}
|
||||
@@ -26,7 +30,29 @@ hr {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Posts */
|
||||
.post_menu {
|
||||
position: absolute;
|
||||
background: #ededed;
|
||||
outline: 1px solid #555;
|
||||
}
|
||||
|
||||
.post_menu_items {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.post_menu_item {
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.post_menu_button {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
Posts
|
||||
********************************************************************************/
|
||||
/*.post_body { }*/
|
||||
.post_image_info {
|
||||
font-size: small;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% load post_body %}
|
||||
{% load l10n %}
|
||||
<div id="p{{post.id}}">
|
||||
<div id="p{{post.id}}" data-report-url="{% url 'board:report_form' board.url post.id %}">
|
||||
{# Image #}
|
||||
{% if post.thumbnail %}
|
||||
{# Image info #}
|
||||
@@ -19,7 +19,6 @@
|
||||
{% endif %}
|
||||
|
||||
{# Post ID, username, time #}
|
||||
<div class="post_content">
|
||||
<a href="#p{{post.id}}">#.</a>
|
||||
<span class="post_id">{{post.id}}</span>
|
||||
{% if post.subject %}
|
||||
@@ -32,8 +31,10 @@
|
||||
{% if reply_link %}
|
||||
[<a href="{{post.get_absolute_url}}">{% localize on %}Reply{% endlocalize %}</a>]
|
||||
{% endif %}
|
||||
<a href="#" class="post_menu_button" data-id="post_menu_button">▶</a>
|
||||
|
||||
{# "X replies elided" dialog for OPs on the board #}
|
||||
<div class="post_content">
|
||||
{% if replies_elided > 0 %}
|
||||
<br/>
|
||||
<span class="replies_elided">
|
||||
|
||||
16
board/templates/board/report_form.html
Normal file
16
board/templates/board/report_form.html
Normal file
@@ -0,0 +1,16 @@
|
||||
{% extends "board/base.html" %}
|
||||
{% load l10n %}
|
||||
{# Title #}
|
||||
{% block title %}{% localize on %}Reporting post {{post.id}}{% endlocalize %}{% endblock %}
|
||||
{# Body #}
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<table>
|
||||
{{ form.as_table }}
|
||||
<tr><td> </td><td><input type="submit" value="Submit"></td></tr>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
29
board/templates/board/report_success.html
Normal file
29
board/templates/board/report_success.html
Normal file
@@ -0,0 +1,29 @@
|
||||
{% extends "board/base.html" %}
|
||||
{% load l10n %}
|
||||
{# Title #}
|
||||
{% block title %}{% localize on %}Report success{% endlocalize %}{% endblock %}
|
||||
{# Body #}
|
||||
{% block content %}
|
||||
<div class="row" id="message">
|
||||
{% localize on %}Post reported. 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.reportWindow.close();
|
||||
} else {
|
||||
window.close();
|
||||
}
|
||||
}, 5000);
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -1,6 +1,7 @@
|
||||
from django.urls import path
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
from django.views.generic.base import TemplateView
|
||||
from board.views import *
|
||||
|
||||
|
||||
@@ -9,6 +10,12 @@ 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/success/",
|
||||
TemplateView.as_view(template_name="board/report_success.html"),
|
||||
name="report_success",
|
||||
),
|
||||
]
|
||||
# TODO - make this conditional so we can serve images up with whatever server we want
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
||||
@@ -3,11 +3,11 @@ from django.http import Http404, HttpResponseRedirect
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
from django.views.generic import DetailView
|
||||
from django.views.generic.edit import CreateView
|
||||
from django.urls import reverse
|
||||
from board.models import Post, Board
|
||||
from board.forms import PostForm, ReplyForm
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from board.models import Post, Board, Report
|
||||
from board.forms import PostForm, ReplyForm, ReportForm
|
||||
|
||||
__all__ = ("BoardView", "PostView")
|
||||
__all__ = ("BoardView", "PostView", "ReportView")
|
||||
|
||||
|
||||
def get_client_ip(request):
|
||||
@@ -107,3 +107,27 @@ class PostView(CreatePostView):
|
||||
kwargs["op"] = post
|
||||
kwargs["reply"] = post
|
||||
return kwargs
|
||||
|
||||
|
||||
class ReportView(CreatePostView):
|
||||
model = Report
|
||||
form_class = ReportForm
|
||||
success_url = reverse_lazy("board:report_success")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
return super(ReportView, self).get_context_data(**kwargs)
|
||||
|
||||
@property
|
||||
def board_url(self) -> str:
|
||||
return self.kwargs["url"]
|
||||
|
||||
@property
|
||||
def post_id(self) -> int:
|
||||
return self.kwargs["id"]
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super(ReportView, self).get_form_kwargs()
|
||||
post_id = self.kwargs["id"]
|
||||
post = get_object_or_404(Post, id=post_id)
|
||||
kwargs["op"] = post
|
||||
return kwargs
|
||||
|
||||
Reference in New Issue
Block a user