Add ActionSuccessView

ActionSuccessView is a a generic view that indicates that something was
successful, e.g. deleting a post or banning a user. This hopefully
reduces the amount of boilerplate code used for creating success pages
since most of them can derive from this generic view.

The report and delete success views are updated to use this directly.

The ban and modify success views are updated to derive from this class,
with special permissions required.

The post success view is updated to derive from this class, using a
different template.

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2022-07-17 15:08:33 -07:00
parent bfd3dedb09
commit e686c3b235
9 changed files with 78 additions and 112 deletions

View File

@@ -39,5 +39,8 @@ function openBanWindow(banUrl) {
window.innerHeight / 2, window.innerHeight / 2,
"RIGHT_CENTER" "RIGHT_CENTER"
); );
$(banWindow.iframe, "iframe").on("load", () => {
banWindow.iframe.contentWindow.thisWindow = banWindow;
});
banWindow.show(); banWindow.show();
} }

View File

@@ -34,6 +34,7 @@ function openModifyWindow(modifyUrl) {
$(modifyWindow.iframe, "iframe").on("load", () => { $(modifyWindow.iframe, "iframe").on("load", () => {
fitWindowToContent(modifyWindow); fitWindowToContent(modifyWindow);
modifyWindow.setResizable(false); modifyWindow.setResizable(false);
modifyWindow.iframe.contentWindow.thisWindow = modifyWindow;
}); });
modifyWindow.show(); modifyWindow.show();
} }

View File

@@ -121,6 +121,7 @@ function openReplyWindow(replyUrl) {
if (textarea) { if (textarea) {
textarea.focus(); textarea.focus();
} }
replyWindow.iframe.contentWindow.thisWindow = replyWindow;
}); });
replyWindow.show(); replyWindow.show();
return replyWindow; return replyWindow;
@@ -171,7 +172,11 @@ function openPostWindow(postUrl) {
}); });
$(postWindow.iframe, "iframe").on("load", () => { $(postWindow.iframe, "iframe").on("load", () => {
fitWindowToContent(postWindow); fitWindowToContent(postWindow);
postWindow.$("#id_text").focus(); let textbox = postWindow.$("#id_text");
if (textbox) {
textbox.focus();
}
postWindow.iframe.contentWindow.thisWindow = postWindow;
}); });
postWindow.show(); postWindow.show();
} }
@@ -201,6 +206,7 @@ function openReportWindow(reportUrl) {
}); });
$(reportWindow.iframe, "iframe").on("load", () => { $(reportWindow.iframe, "iframe").on("load", () => {
fitWindowToContent(reportWindow); fitWindowToContent(reportWindow);
reportWindow.iframe.contentWindow.thisWindow = reportWindow;
}); });
reportWindow.show(); reportWindow.show();
} }
@@ -226,6 +232,7 @@ function openDeleteWindow(deleteUrl) {
$(deleteWindow.iframe, "iframe").on("load", () => { $(deleteWindow.iframe, "iframe").on("load", () => {
fitWindowToContent(deleteWindow); fitWindowToContent(deleteWindow);
deleteWindow.setResizable(false); deleteWindow.setResizable(false);
deleteWindow.iframe.contentWindow.thisWindow = deleteWindow;
}); });
deleteWindow.show(); deleteWindow.show();
} }

View File

@@ -0,0 +1,23 @@
{% extends "board/base.html" %}
{% load i18n static %}
{# Title #}
{% block title %}{% translate "Action success" %}{% endblock %}
{# Body #}
{% block content %}
<div class="row" id="message">
{{message}}
{# We do not use pluralize filter for "seconds" because it's a pain to get it to translate. #}
{% blocktranslate %}This window will close in {{window_timeout}} second(s).{% endblocktranslate %}
</div>
<script>
setTimeout(function() {
if(typeof thisWindow !== "undefined") {
thisWindow.closeFrame();
} else {
window.close();
}
}, 1000 * {{window_timeout}});
</script>
{% endblock %}

View File

@@ -1,33 +0,0 @@
{% 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 %}

View File

@@ -1,33 +0,0 @@
{% extends "board/base.html" %}
{% load i18n static %}
{# Title #}
{% block title %}{% translate "Report 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 %}Post reported. 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 reportWindow = getReportWindow();
if(reportWindow) {
reportWindow.closeFrame();
}
} else {
window.close();
}
}, 1000 * {{window_timeout}});
</script>
{% endblock %}

View File

@@ -1,6 +1,7 @@
from django.urls import path
from django.conf import settings from django.conf import settings
from django.conf.urls.static import static from django.conf.urls.static import static
from django.urls import path
from django.utils.translation import gettext as _
from board.views import * from board.views import *
@@ -17,20 +18,35 @@ urlpatterns = [
path("post/success/", PostSuccessView.as_view(), name="post_success"), path("post/success/", PostSuccessView.as_view(), name="post_success"),
# Reports # Reports
path("report/<slug:url>/<int:id>/", ReportView.as_view(), name="report_form"), path("report/<slug:url>/<int:id>/", ReportView.as_view(), name="report_form"),
path("report/success/", ReportSuccessView.as_view(), name="report_success"), path(
"report/success/",
ActionSuccessView.as_view(
window_timeout=settings.REPORT_WINDOW_CLOSE_TIMEOUT,
message=_("Post reported."),
),
name="report_success",
),
# Bans # Bans
path("ban/<slug:url>/<int:id>/", BanCreateView.as_view(), name="ban_create"), path("ban/<slug:url>/<int:id>/", BanCreateView.as_view(), name="ban_create"),
path("ban/success/", BanSuccessView.as_view(), name="ban_success"), path(
"ban/success/",
BanSuccessView.as_view(window_timeout=settings.ACTION_SUCCESS_CLOSE_TIMEOUT),
name="ban_success",
),
path("banned/", BannedView.as_view(), name="banned"), path("banned/", BannedView.as_view(), name="banned"),
# Other moderation pages # Other moderation pages
path("modify/<int:pk>/", PostModifyView.as_view(), name="post_modify"), path("modify/<int:pk>/", PostModifyView.as_view(), name="post_modify"),
path( path(
"modify/success/", PostModifySuccessView.as_view(), name="post_modify_success" "modify/success/",
PostModifySuccessView.as_view(
window_timeout=settings.ACTION_SUCCESS_CLOSE_TIMEOUT
),
name="post_modify_success",
), ),
path("post/delete/<int:pk>/", PostDeleteView.as_view(), name="post_delete"), path("post/delete/<int:pk>/", PostDeleteView.as_view(), name="post_delete"),
path( path(
"post/delete/success/", "post/delete/success/",
PostDeleteSuccessView.as_view(), ActionSuccessView.as_view(window_timeout=settings.ACTION_SUCCESS_CLOSE_TIMEOUT),
name="post_delete_success", name="post_delete_success",
), ),
] ]

View File

@@ -19,6 +19,7 @@ from board.models import Ban, BanTemplate, Board, NewsPost, Post, Report
from board.utils import * from board.utils import *
__all__ = ( __all__ = (
"ActionSuccessView",
"BanCreateView", "BanCreateView",
"BanSuccessView", "BanSuccessView",
"BannedView", "BannedView",
@@ -30,10 +31,8 @@ __all__ = (
"PostView", "PostView",
"PostSuccessView", "PostSuccessView",
"PostDeleteView", "PostDeleteView",
"PostDeleteSuccessView",
"ReplyCreateView", "ReplyCreateView",
"ReportView", "ReportView",
"ReportSuccessView",
) )
@@ -48,6 +47,18 @@ def can_modify(user):
) )
class ActionSuccessView(TemplateView):
template_name = "board/action_success.html"
message = "Action completed."
window_timeout = 0
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
context = super().get_context_data(**kwargs)
context["message"] = self.message
context["window_timeout"] = self.window_timeout
return context
class BannedView(TemplateView): class BannedView(TemplateView):
template_name = "board/banned.html" template_name = "board/banned.html"
@@ -156,7 +167,8 @@ class PostCreateView(CreateView):
def get_success_url(self) -> str: def get_success_url(self) -> str:
query = QueryDict(mutable=True) query = QueryDict(mutable=True)
query["next"] = self.get_form().instance.get_absolute_url() query["next"] = self.get_form().instance.get_absolute_url().split("#")[0]
print(query["next"])
return reverse("board:post_success") + "?" + query.urlencode() return reverse("board:post_success") + "?" + query.urlencode()
@@ -181,8 +193,7 @@ class PostModifyView(PermissionRequiredMixin, edit.UpdateView):
return kwargs return kwargs
class PostModifySuccessView(PermissionRequiredMixin, TemplateView): class PostModifySuccessView(PermissionRequiredMixin, ActionSuccessView):
template_name = "board/post_modify_success.html"
raise_exception = True raise_exception = True
def has_permission(self) -> bool: def has_permission(self) -> bool:
@@ -190,7 +201,6 @@ class PostModifySuccessView(PermissionRequiredMixin, TemplateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context["window_timeout"] = settings.BAN_WINDOW_CLOSE_TIMEOUT
context["can_modify"] = self.has_permission() context["can_modify"] = self.has_permission()
return context return context
@@ -248,13 +258,9 @@ class PostView(TemplateView):
return super(PostView, self).dispatch(request, *args, **kwargs) return super(PostView, self).dispatch(request, *args, **kwargs)
class PostSuccessView(TemplateView): class PostSuccessView(ActionSuccessView):
template_name = "board/post_success.html" template_name = "board/post_success.html"
window_timeout = settings.POST_WINDOW_CLOSE_TIMEOUT
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["window_timeout"] = settings.POST_WINDOW_CLOSE_TIMEOUT
return context
class PostDeleteView(PermissionRequiredMixin, edit.DeleteView): class PostDeleteView(PermissionRequiredMixin, edit.DeleteView):
@@ -282,15 +288,6 @@ class PostDeleteView(PermissionRequiredMixin, edit.DeleteView):
return HttpResponseRedirect(success_url) 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): class ReportView(CreateView):
model = Report model = Report
form_class = ReportForm form_class = ReportForm
@@ -312,15 +309,6 @@ class ReportView(CreateView):
return kwargs return kwargs
class ReportSuccessView(TemplateView):
template_name = "board/report_success.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["window_timeout"] = settings.REPORT_WINDOW_CLOSE_TIMEOUT
return context
class BanCreateView(PermissionRequiredMixin, edit.CreateView): class BanCreateView(PermissionRequiredMixin, edit.CreateView):
model = Ban model = Ban
form_class = BanForm form_class = BanForm
@@ -364,16 +352,10 @@ class BanCreateView(PermissionRequiredMixin, edit.CreateView):
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
class BanSuccessView(PermissionRequiredMixin, TemplateView): class BanSuccessView(PermissionRequiredMixin, ActionSuccessView):
permission_required = "ban.create" permission_required = "ban.create"
template_name = "board/ban_success.html"
raise_exception = True raise_exception = True
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["window_timeout"] = settings.BAN_WINDOW_CLOSE_TIMEOUT
return context
class NewsListView(list.ListView): class NewsListView(list.ListView):
model = NewsPost model = NewsPost

View File

@@ -161,13 +161,13 @@ if USE_HCAPTCHA:
# How many seconds to wait before closing the report window after a report is # How many seconds to wait before closing the report window after a report is
# created. # created.
# By default, wait 5 seconds. This should give users plenty of time to read it. # By default, wait 3 seconds. This should give users plenty of time to read it.
REPORT_WINDOW_CLOSE_TIMEOUT = 5 REPORT_WINDOW_CLOSE_TIMEOUT = 3
# How many seconds to wait before closing the ban window after a ban is created. # How many seconds to wait before closing the ban window after a ban is created.
# By default, wait 0 seconds and close immediately. It is assumed that the ban # By default, wait 0 seconds and close immediately. It is assumed that the ban
# is created successfully, and if an error occurs, the window won't close anyway. # is created successfully, and if an error occurs, the window won't close anyway.
BAN_WINDOW_CLOSE_TIMEOUT = 0 ACTION_SUCCESS_CLOSE_TIMEOUT = 0
# How many seconds to wait before closing the new post window after a post is # How many seconds to wait before closing the new post window after a post is
# created. # created.