Files
interchan/board/views.py

367 lines
12 KiB
Python
Raw Normal View History

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 detail, edit
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, Post, Report
from board.utils import *
__all__ = (
"BanCreateView",
"BanSuccessView",
"BannedView",
"BoardView",
"PostCreateView",
"PostModifyView",
"PostModifySuccessView",
"PostView",
"PostSuccessView",
"PostDeleteView",
"PostDeleteSuccessView",
"ReplyCreateView",
"ReportView",
"ReportSuccessView",
)
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 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):
model = Board
slug_field = "url"
slug_url_kwarg = "url"
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
slug_field = "url"
slug_url_kwarg = "url"
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
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 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, TemplateView):
template_name = "board/post_modify_success.html"
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["window_timeout"] = settings.BAN_WINDOW_CLOSE_TIMEOUT
context["can_modify"] = self.has_permission()
return context
class ReplyCreateView(CreateView):
model = Post
form_class = ReplyForm
slug_field = "url"
slug_url_kwarg = "url"
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
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(TemplateView):
template_name = "board/post_success.html"
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):
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
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 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):
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, 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)
context["window_timeout"] = settings.BAN_WINDOW_CLOSE_TIMEOUT
return context