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>
364 lines
12 KiB
Python
364 lines
12 KiB
Python
import functools
|
|
from typing import Any, Dict
|
|
from django.conf import settings
|
|
from django.contrib.auth import get_user
|
|
from django.contrib.auth.mixins import PermissionRequiredMixin
|
|
from django.db.models import Q
|
|
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, list
|
|
from django.urls import reverse, reverse_lazy
|
|
from django.utils import timezone
|
|
|
|
from guardian.shortcuts import get_objects_for_user
|
|
|
|
from board.forms import *
|
|
from board.models import Ban, BanTemplate, Board, NewsPost, Post, Report
|
|
from board.utils import *
|
|
|
|
__all__ = (
|
|
"ActionSuccessView",
|
|
"BanCreateView",
|
|
"BanSuccessView",
|
|
"BannedView",
|
|
"BoardView",
|
|
"NewsListView",
|
|
"PostCreateView",
|
|
"PostModifyView",
|
|
"PostModifySuccessView",
|
|
"PostView",
|
|
"PostSuccessView",
|
|
"PostDeleteView",
|
|
"ReplyCreateView",
|
|
"ReportView",
|
|
)
|
|
|
|
|
|
def can_modify(user):
|
|
if not user:
|
|
return False
|
|
# TODO add more permissions as required
|
|
return (
|
|
user.has_perm("board.set_sticky")
|
|
or user.has_perm("board.set_bump")
|
|
or user.has_perm("board.set_lock")
|
|
)
|
|
|
|
|
|
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):
|
|
template_name = "board/banned.html"
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(TemplateView, self).get_context_data(**kwargs)
|
|
ip = get_client_ip(self.request)
|
|
bans = get_ip_bans(ip)
|
|
|
|
now = timezone.now()
|
|
active_bans = [ban for ban in bans if not ban.expires or ban.expires > now]
|
|
context["bans"] = bans
|
|
context["active_bans"] = active_bans
|
|
context["ip"] = ip
|
|
|
|
return context
|
|
|
|
|
|
class BoardMixin:
|
|
@functools.cached_property
|
|
def board(self) -> Board:
|
|
return get_object_or_404(Board, url=self.kwargs["url"]) # type: ignore
|
|
|
|
|
|
class CreateView(BoardMixin, edit.CreateView):
|
|
"""
|
|
Helper class that sets a few variables for posts and check against bans.
|
|
This should not be used by itself.
|
|
|
|
This class sets the following variables on GET and POST:
|
|
* self.board
|
|
"""
|
|
|
|
def get_form_kwargs(self):
|
|
kwargs = super(CreateView, self).get_form_kwargs()
|
|
kwargs["board"] = self.board
|
|
kwargs["ip"] = get_client_ip(self.request)
|
|
return kwargs
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
# Check for bans
|
|
ip = get_client_ip(request)
|
|
if request.method == "POST" and is_banned(ip, self.board):
|
|
return HttpResponseRedirect(reverse("board:banned"))
|
|
else:
|
|
return super(CreateView, self).dispatch(request, *args, **kwargs)
|
|
|
|
|
|
class BoardView(BoardMixin, TemplateView):
|
|
template_name = "board/board_detail.html"
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
# If the page isn't set, then redirect to the /page/1 url
|
|
if "page" not in kwargs:
|
|
return HttpResponseRedirect(
|
|
reverse("board:board_detail", kwargs={"url": kwargs["url"], "page": 1})
|
|
)
|
|
return super(BoardView, self).get(request, *args, **kwargs)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
page = self.kwargs["page"]
|
|
|
|
if page not in range(1, self.board.max_pages + 1):
|
|
raise Http404()
|
|
|
|
threads_per_page = self.board.threads_per_page
|
|
start = (page - 1) * threads_per_page
|
|
end = start + threads_per_page
|
|
thread_count = self.board.threads.count()
|
|
last_page = (thread_count // threads_per_page) + 1
|
|
|
|
kwargs["board"] = self.board
|
|
kwargs["threads"] = self.board.threads.order_by("-sticky", "-last_bump")[
|
|
start:end
|
|
]
|
|
kwargs["current_page"] = page
|
|
kwargs["pages"] = range(1, last_page + 1)
|
|
kwargs["last_page"] = last_page
|
|
kwargs["max_upload_size"] = settings.MAX_UPLOAD_SIZE
|
|
kwargs["can_modify"] = can_modify(self.request.user)
|
|
|
|
return super(BoardView, self).get_context_data(**kwargs)
|
|
|
|
|
|
class PostCreateView(CreateView):
|
|
model = Post
|
|
form_class = PostForm
|
|
template_name = "board/post_create.html"
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context["board"] = self.board
|
|
context["max_upload_size"] = settings.MAX_UPLOAD_SIZE
|
|
context["capcodes"] = get_objects_for_user(
|
|
get_user(self.request), "board.use_capcode"
|
|
)
|
|
return context
|
|
|
|
def get_form_kwargs(self):
|
|
kwargs = super(PostCreateView, self).get_form_kwargs()
|
|
kwargs["user"] = self.request.user
|
|
if "user_token" not in self.request.session:
|
|
# Generate a user token
|
|
self.request.session["user_token"] = generate_user_token()
|
|
kwargs["user_token"] = self.request.session["user_token"]
|
|
return kwargs
|
|
|
|
def get_success_url(self) -> str:
|
|
query = QueryDict(mutable=True)
|
|
query["next"] = self.get_form().instance.get_absolute_url().split("#")[0]
|
|
print(query["next"])
|
|
return reverse("board:post_success") + "?" + query.urlencode()
|
|
|
|
|
|
class PostModifyView(PermissionRequiredMixin, edit.UpdateView):
|
|
model = Post
|
|
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)
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
self.post_obj = get_object_or_404(Post, id=kwargs["pk"])
|
|
self.board = self.post_obj.board
|
|
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, ActionSuccessView):
|
|
raise_exception = True
|
|
|
|
def has_permission(self) -> bool:
|
|
return can_modify(self.request.user)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context["can_modify"] = self.has_permission()
|
|
return context
|
|
|
|
|
|
class ReplyCreateView(CreateView):
|
|
model = Post
|
|
form_class = ReplyForm
|
|
template_name = "board/reply_create.html"
|
|
success_url = reverse_lazy("board:post_success")
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context["board"] = self.board
|
|
post_id = self.kwargs["id"]
|
|
context["post"] = get_object_or_404(Post, id=post_id)
|
|
context["max_upload_size"] = settings.MAX_UPLOAD_SIZE
|
|
context["capcodes"] = get_objects_for_user(
|
|
get_user(self.request), "board.use_capcode"
|
|
)
|
|
return context
|
|
|
|
def get_form_kwargs(self):
|
|
kwargs = super(ReplyCreateView, self).get_form_kwargs()
|
|
post_id = self.kwargs["id"]
|
|
post = get_object_or_404(Post, id=post_id)
|
|
kwargs["op"] = post
|
|
kwargs["user"] = self.request.user
|
|
if "user_token" not in self.request.session:
|
|
# Generate a user token
|
|
self.request.session["user_token"] = generate_user_token()
|
|
kwargs["user_token"] = self.request.session["user_token"]
|
|
return kwargs
|
|
|
|
def get_success_url(self) -> str:
|
|
query = QueryDict(mutable=True)
|
|
query["next"] = self.get_form().instance.get_absolute_url()
|
|
return reverse("board:post_success") + "?" + query.urlencode()
|
|
|
|
|
|
class PostView(TemplateView):
|
|
template_name = "board/post_detail.html"
|
|
|
|
def get_context_data(self, **kwargs):
|
|
kwargs["board"] = self.board
|
|
post_id = self.kwargs["id"]
|
|
kwargs["post"] = get_object_or_404(Post, id=post_id)
|
|
kwargs["max_upload_size"] = settings.MAX_UPLOAD_SIZE
|
|
kwargs["can_modify"] = can_modify(self.request.user)
|
|
|
|
return super(PostView, self).get_context_data(**kwargs)
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
# Set the board on this object
|
|
self.board = get_object_or_404(Board, url=kwargs["url"])
|
|
return super(PostView, self).dispatch(request, *args, **kwargs)
|
|
|
|
|
|
class PostSuccessView(ActionSuccessView):
|
|
template_name = "board/post_success.html"
|
|
window_timeout = settings.POST_WINDOW_CLOSE_TIMEOUT
|
|
|
|
|
|
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 has_permission(self) -> bool:
|
|
object = self.get_object()
|
|
user_token = self.request.session.get("user_token", None)
|
|
return self.request.user.has_perm("board.delete_post") or (
|
|
user_token and object.user_token == user_token
|
|
)
|
|
|
|
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 ReportView(CreateView):
|
|
model = Report
|
|
form_class = ReportForm
|
|
success_url = reverse_lazy("board:report_success")
|
|
|
|
@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
|
|
|
|
|
|
class BanCreateView(PermissionRequiredMixin, edit.CreateView):
|
|
model = Ban
|
|
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)
|
|
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 and ban.expires < now
|
|
]
|
|
context["current_bans"] = [
|
|
ban for ban in bans if not ban.expires or ban.expires > now
|
|
]
|
|
context["templates"] = BanTemplate.objects.filter(
|
|
Q(board=self.board) | Q(board=None)
|
|
)
|
|
|
|
return context
|
|
|
|
def get_form_kwargs(self) -> Dict[str, Any]:
|
|
kwargs = super(edit.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, ActionSuccessView):
|
|
permission_required = "ban.create"
|
|
raise_exception = True
|
|
|
|
|
|
class NewsListView(list.ListView):
|
|
model = NewsPost
|
|
template_name = "board/news_list.html"
|
|
ordering = ("-created",)
|