Update PostModifyForm to use permissions to create its fields

Fields are only displayed via the PostModifyForm if the user has
specific permissions to do things, like set stickies.

Also, add PostModifySuccessView that will close the modify window when
the process is complete.

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2022-06-30 18:16:08 -07:00
parent 6f99472f16
commit 0ac383ce6c
7 changed files with 96 additions and 15 deletions

View File

@@ -4,6 +4,7 @@ from django.db import transaction
from django.db.models import Q from django.db.models import Q
from django import forms from django import forms
from django.forms import ModelForm, ModelChoiceField from django.forms import ModelForm, ModelChoiceField
from django.forms.models import fields_for_model
from django.utils import timezone from django.utils import timezone
from board.models import Ban, Post, Report, ReportReason, ReportRecord from board.models import Ban, Post, Report, ReportReason, ReportRecord
from hcaptcha.fields import hCaptchaField from hcaptcha.fields import hCaptchaField
@@ -124,8 +125,23 @@ class PostModifyForm(ModelForm):
class Meta: class Meta:
model = Post model = Post
fields = ["bump", "sticky"] # we specify fields up here too because otherwise they won't be
# recognized by the form to update values
fields: list[str] = ["sticky"]
def clean(self): def __init__(self, *args, user, **kwargs):
super(PostModifyForm, self).clean() super(PostModifyForm, self).__init__(*args, **kwargs)
print(self.fields["sticky"]) self.user = user
fields = []
if self.user.has_perm("board.set_sticky"):
fields += ["sticky"]
# NOTE:
# We do *not* need to check permissions against these fields we're
# setting down here in the self.clean() function in the case that a
# malicious actor has access to the modify form and injects a "sticky"
# value to their modify request.
#
# We specify fields up in the Meta class, but we reset them down here.
# If the field isn't present in this list, then it doesn't get updated.
# If the field isn't present in the above list, then it doesn't get updated.
self.fields = fields_for_model(Post, fields)

View File

@@ -12,8 +12,11 @@
<script src="{% static 'board/jquery.js' %}"></script> <script src="{% static 'board/jquery.js' %}"></script>
<script src="{% static 'board/jsframe.min.js' %}"></script> <script src="{% static 'board/jsframe.min.js' %}"></script>
<script src="{% static 'board/post.js' %}"></script> <script src="{% static 'board/post.js' %}"></script>
{% if perms.board.create_ban %} {% if perms.board.add_ban %}
<script src="{% static 'board/ban_window.js' %}"></script> <script src="{% static 'board/ban.js' %}"></script>
{% endif %}
{% if can_modify %}
<script src="{% static 'board/modify.js' %}"></script>
{% endif %} {% endif %}
{% block extrajs %}{% endblock %} {% block extrajs %}{% endblock %}
</head> </head>

View File

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

View File

@@ -5,7 +5,7 @@
<form method="post" action="{% url 'board:post_modify' post.id %}"> <form method="post" action="{% url 'board:post_modify' post.id %}">
{% csrf_token %} {% csrf_token %}
<table> <table>
{% if perms.board.can_sticky %} {% if perms.board.set_sticky %}
<tr> <tr>
<th>{{form.sticky.label_tag}}</th> <th>{{form.sticky.label_tag}}</th>
<td>{{form.sticky}}</td> <td>{{form.sticky}}</td>

View File

@@ -3,10 +3,10 @@
<div <div
id="p{{post.id}}" id="p{{post.id}}"
data-report-url="{% url 'board:report_form' board.url post.id %}" data-report-url="{% url 'board:report_form' board.url post.id %}"
{% if perms.board.create_ban %} {% if perms.board.add_ban %}
data-ban-url="{% url 'board:ban_create' board.url post.id %}" data-ban-url="{% url 'board:ban_create' board.url post.id %}"
{% endif %} {% endif %}
{% if perms.board.can_sticky %} {% if can_modify %}
data-modify-url="{% url 'board:post_modify' post.id %}" data-modify-url="{% url 'board:post_modify' post.id %}"
{% endif %} {% endif %}
> >

View File

@@ -22,6 +22,9 @@ urlpatterns = [
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(
"modify/success/", PostModifySuccessView.as_view(), name="post_modify_success"
),
] ]
# TODO - make this conditional so we can serve images up with whatever server we want # 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) urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@@ -24,6 +24,7 @@ __all__ = (
"BoardView", "BoardView",
"PostCreateView", "PostCreateView",
"PostModifyView", "PostModifyView",
"PostModifySuccessView",
"PostView", "PostView",
"PostSuccessView", "PostSuccessView",
"ReplyCreateView", "ReplyCreateView",
@@ -35,6 +36,13 @@ __all__ = (
User = get_user_model() User = get_user_model()
def can_modify(user):
if not user:
return False
# TODO add more permissions as required
return user.has_perm("board.set_sticky")
class BannedView(TemplateView): class BannedView(TemplateView):
template_name = "board/banned.html" template_name = "board/banned.html"
@@ -117,6 +125,7 @@ class BoardView(TemplateView):
kwargs["pages"] = range(1, last_page + 1) kwargs["pages"] = range(1, last_page + 1)
kwargs["last_page"] = last_page kwargs["last_page"] = last_page
kwargs["max_upload_size"] = settings.MAX_UPLOAD_SIZE kwargs["max_upload_size"] = settings.MAX_UPLOAD_SIZE
kwargs["can_modify"] = can_modify(self.request.user)
return super(BoardView, self).get_context_data(**kwargs) return super(BoardView, self).get_context_data(**kwargs)
@@ -135,6 +144,7 @@ class PostCreateView(CreateView):
context["capcodes"] = get_objects_for_user( context["capcodes"] = get_objects_for_user(
get_user(self.request), "board.use_capcode" get_user(self.request), "board.use_capcode"
) )
kwargs["can_modify"] = can_modify(self.request.user)
return context return context
def get_form_kwargs(self): def get_form_kwargs(self):
@@ -152,19 +162,34 @@ class PostModifyView(PermissionRequiredMixin, edit.UpdateView):
model = Post model = Post
form_class = PostModifyForm form_class = PostModifyForm
template_name = "board/post_modify_view.html" template_name = "board/post_modify_view.html"
success_url = reverse_lazy("board:post_modify_success")
def has_permission(self) -> bool: def has_permission(self) -> bool:
user = self.request.user return can_modify(self.request.user)
if not user:
return False
# TODO add more permissions as required
return user.has_perm("board.can_sticky")
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
self.post_obj = get_object_or_404(Post, id=kwargs["pk"]) self.post_obj = get_object_or_404(Post, id=kwargs["pk"])
self.board = self.post_obj.board self.board = self.post_obj.board
return super(PostModifyView, self).dispatch(request, *args, **kwargs) return super(PostModifyView, self).dispatch(request, *args, **kwargs)
def get_form_kwargs(self) -> Dict[str, Any]:
kwargs = super(PostModifyView, self).get_form_kwargs()
kwargs["user"] = self.request.user
return kwargs
class PostModifySuccessView(PermissionRequiredMixin, TemplateView):
template_name = "board/post_modify_success.html"
def has_permission(self) -> bool:
return can_modify(self.request.user)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["window_timeout"] = settings.BAN_WINDOW_CLOSE_TIMEOUT
context["can_modify"] = self.has_permission()
return context
class ReplyCreateView(CreateView): class ReplyCreateView(CreateView):
model = Post model = Post
@@ -183,6 +208,7 @@ class ReplyCreateView(CreateView):
context["capcodes"] = get_objects_for_user( context["capcodes"] = get_objects_for_user(
get_user(self.request), "board.use_capcode" get_user(self.request), "board.use_capcode"
) )
kwargs["can_modify"] = can_modify(self.request.user)
return context return context
def get_form_kwargs(self): def get_form_kwargs(self):
@@ -262,7 +288,7 @@ class ReportSuccessView(TemplateView):
class BanCreateView(PermissionRequiredMixin, edit.CreateView): class BanCreateView(PermissionRequiredMixin, edit.CreateView):
model = Ban model = Ban
form_class = BanForm form_class = BanForm
permission_required = "ban.create" permission_required = "board.add_ban"
success_url = reverse_lazy("board:ban_success") success_url = reverse_lazy("board:ban_success")
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]: def get_context_data(self, **kwargs: Any) -> Dict[str, Any]: