Add post and image deletion
This one was kind of a doozy. This also adds a custom 403 error page and fixes some permission denied behavior that I was having issues with for a while. This is also set up in a way that hopefully will allow me to easily implement user post deletion. Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
@@ -155,6 +155,10 @@ class BanForm(ModelForm):
|
||||
self.instance.expires = expires
|
||||
|
||||
|
||||
class PostDeleteForm(forms.Form):
|
||||
image_only = forms.CharField(widget=forms.HiddenInput(), initial="0")
|
||||
|
||||
|
||||
class PostModifyForm(ModelForm):
|
||||
"""
|
||||
A form used to modify the attributes of a post (sticky, locked, sink, etc)
|
||||
|
||||
@@ -3,7 +3,8 @@ const OPEN = "open";
|
||||
const CLOSED = "closed";
|
||||
const replyWindowName = "reply-window";
|
||||
const postWindowName = "post-window";
|
||||
const reportWindowName = "report-window"
|
||||
const reportWindowName = "report-window";
|
||||
const deleteWindowName = "delete-window";
|
||||
const WINDOW_INNER_PADDING = 25;
|
||||
|
||||
|
||||
@@ -204,6 +205,31 @@ function openReportWindow(reportUrl) {
|
||||
reportWindow.show();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Post delete window
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
function getDeleteWindow() {
|
||||
return window.top.jsFrame.getWindowByName(deleteWindowName);
|
||||
}
|
||||
|
||||
function openDeleteWindow(deleteUrl) {
|
||||
if (window.top.jsFrame.containsWindowName(deleteWindowName)) {
|
||||
getDeleteWindow().closeFrame();
|
||||
}
|
||||
|
||||
let deleteWindow = window.top.jsFrame.create({
|
||||
title: "Deleting post",
|
||||
name: deleteWindowName,
|
||||
width: 475,
|
||||
url: deleteUrl,
|
||||
});
|
||||
$(deleteWindow.iframe, "iframe").on("load", () => {
|
||||
fitWindowToContent(deleteWindow);
|
||||
deleteWindow.setResizable(false);
|
||||
});
|
||||
deleteWindow.show();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Post hiding
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -366,3 +392,12 @@ window.menuItemFactories.push(
|
||||
}
|
||||
})
|
||||
);
|
||||
window.menuItemFactories.push(
|
||||
(postElement) => $("<a>")
|
||||
.text("Delete post")
|
||||
.attr("href", "#")
|
||||
.on("click", (e) => {
|
||||
e.preventDefault();
|
||||
openDeleteWindow($(postElement).attr("data-delete-url"));
|
||||
})
|
||||
);
|
||||
8
board/templates/403.html
Normal file
8
board/templates/403.html
Normal file
@@ -0,0 +1,8 @@
|
||||
{% extends "board/base.html" %}
|
||||
{% block title %}
|
||||
Permission denied
|
||||
{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<h3>You do not have permission to do this.</h3>
|
||||
{% endblock content %}
|
||||
23
board/templates/board/post_confirm_delete.html
Normal file
23
board/templates/board/post_confirm_delete.html
Normal file
@@ -0,0 +1,23 @@
|
||||
{% extends "board/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<form id="form" method="post" action="{% url 'board:post_delete' post.id %}">
|
||||
{% csrf_token %}
|
||||
<table>
|
||||
<tr>
|
||||
<td><input type="submit" value="{% translate 'Delete post' %}" /></td>
|
||||
<td><input id="delete_image" type="submit" value="{% translate 'Delete image only' %}" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
{{form.image_only}}
|
||||
</form>
|
||||
|
||||
<script>
|
||||
$(document).on("click","#delete_image", (e) => {
|
||||
e.preventDefault();
|
||||
$("#{{form.image_only.auto_id}}").val(1);
|
||||
$("#form").submit();
|
||||
});
|
||||
</script>
|
||||
{% endblock content %}
|
||||
33
board/templates/board/post_delete_success.html
Normal file
33
board/templates/board/post_delete_success.html
Normal file
@@ -0,0 +1,33 @@
|
||||
{% extends "board/base.html" %}
|
||||
{% load i18n static %}
|
||||
{# Title #}
|
||||
{% block title %}{% translate "Post delete success" %}{% endblock %}
|
||||
{# Body #}
|
||||
{% block content %}
|
||||
<div class="row" id="message">
|
||||
{# We do not use pluralize filter for "seconds" because it's a pain to get it to translate. #}
|
||||
{% blocktranslate %}Delete successful. This window will close in {{window_timeout}} second(s).{% endblocktranslate %}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function isIframe() {
|
||||
try {
|
||||
return window.self !== window.top;
|
||||
} catch (_) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(function() {
|
||||
if(isIframe()) {
|
||||
let deleteWindow = getDeleteWindow();
|
||||
if(deleteWindow) {
|
||||
deleteWindow.closeFrame();
|
||||
}
|
||||
} else {
|
||||
window.close();
|
||||
}
|
||||
}, 1000 * {{window_timeout}});
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -4,6 +4,7 @@
|
||||
id="p{{post.id}}"
|
||||
class="post"
|
||||
data-report-url="{% url 'board:report_form' board.url post.id %}"
|
||||
data-delete-url="{% url 'board:post_delete' post.id %}"
|
||||
{% if perms.board.add_ban %}
|
||||
data-ban-url="{% url 'board:ban_create' board.url post.id %}"
|
||||
{% endif %}
|
||||
|
||||
@@ -19,12 +19,18 @@ urlpatterns = [
|
||||
# Bans
|
||||
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(), name="banned"),
|
||||
path("banned/", BannedView.as_view(), name="banned"),
|
||||
# Other moderation pages
|
||||
path("modify/<int:pk>/", PostModifyView.as_view(), name="post_modify"),
|
||||
path(
|
||||
"modify/success/", PostModifySuccessView.as_view(), name="post_modify_success"
|
||||
),
|
||||
path("post/delete/<int:pk>/", PostDeleteView.as_view(), name="post_delete"),
|
||||
path(
|
||||
"post/delete/success/",
|
||||
PostDeleteSuccessView.as_view(),
|
||||
name="post_delete_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)
|
||||
|
||||
@@ -8,7 +8,7 @@ from django.http import Http404, HttpResponseRedirect
|
||||
from django.http.request import QueryDict
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.views.generic.base import TemplateView
|
||||
from django.views.generic import edit
|
||||
from django.views.generic import detail, edit
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils import timezone
|
||||
|
||||
@@ -28,6 +28,8 @@ __all__ = (
|
||||
"PostModifySuccessView",
|
||||
"PostView",
|
||||
"PostSuccessView",
|
||||
"PostDeleteView",
|
||||
"PostDeleteSuccessView",
|
||||
"ReplyCreateView",
|
||||
"ReportView",
|
||||
"ReportSuccessView",
|
||||
@@ -163,6 +165,7 @@ class PostModifyView(PermissionRequiredMixin, edit.UpdateView):
|
||||
form_class = PostModifyForm
|
||||
template_name = "board/post_modify.html"
|
||||
success_url = reverse_lazy("board:post_modify_success")
|
||||
raise_exception = True
|
||||
|
||||
def has_permission(self) -> bool:
|
||||
return can_modify(self.request.user)
|
||||
@@ -180,6 +183,7 @@ class PostModifyView(PermissionRequiredMixin, edit.UpdateView):
|
||||
|
||||
class PostModifySuccessView(PermissionRequiredMixin, TemplateView):
|
||||
template_name = "board/post_modify_success.html"
|
||||
raise_exception = True
|
||||
|
||||
def has_permission(self) -> bool:
|
||||
return can_modify(self.request.user)
|
||||
@@ -255,6 +259,33 @@ class PostSuccessView(TemplateView):
|
||||
return context
|
||||
|
||||
|
||||
class PostDeleteView(PermissionRequiredMixin, edit.DeleteView):
|
||||
model = Post
|
||||
form_class = PostDeleteForm
|
||||
permission_required = ("board.delete_post",)
|
||||
template_name = "board/post_confirm_delete.html"
|
||||
success_url = reverse_lazy("board:post_delete_success")
|
||||
raise_exception = True
|
||||
|
||||
def form_valid(self, form):
|
||||
success_url = self.get_success_url()
|
||||
if form["image_only"].value() != "0":
|
||||
self.object.image.delete()
|
||||
self.object.thumbnail.delete()
|
||||
else:
|
||||
self.object.delete()
|
||||
return HttpResponseRedirect(success_url)
|
||||
|
||||
|
||||
class PostDeleteSuccessView(TemplateView):
|
||||
template_name = "board/post_delete_success.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["window_timeout"] = settings.BAN_WINDOW_CLOSE_TIMEOUT
|
||||
return context
|
||||
|
||||
|
||||
class ReportView(CreateView):
|
||||
model = Report
|
||||
form_class = ReportForm
|
||||
@@ -290,6 +321,7 @@ class BanCreateView(PermissionRequiredMixin, edit.CreateView):
|
||||
form_class = BanForm
|
||||
permission_required = "board.add_ban"
|
||||
success_url = reverse_lazy("board:ban_success")
|
||||
raise_exception = True
|
||||
|
||||
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
|
||||
context = super().get_context_data(**kwargs)
|
||||
@@ -330,6 +362,7 @@ class BanCreateView(PermissionRequiredMixin, edit.CreateView):
|
||||
class BanSuccessView(PermissionRequiredMixin, TemplateView):
|
||||
permission_required = "ban.create"
|
||||
template_name = "board/ban_success.html"
|
||||
raise_exception = True
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
@@ -124,6 +124,7 @@ USE_I18N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
GUARDIAN_RENDER_403 = True
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/4.0/howto/static-files/
|
||||
|
||||
Reference in New Issue
Block a user