from typing import Any, Dict from django.conf import settings from django.contrib.auth.mixins import PermissionRequiredMixin from django.core.exceptions import ImproperlyConfigured from django.db.models import Q from django.http import Http404, HttpResponseRedirect from django.shortcuts import get_object_or_404 from django.views.generic.base import TemplateView from django.views.generic import edit from django.urls import reverse, reverse_lazy from django.utils import timezone from board.forms import BanForm, PostForm, ReplyForm, ReportForm from board.models import Ban, BanTemplate, Board, Post, Report from board.utils import * __all__ = ( "BanCreateView", "BanSuccessView", "BannedView", "BoardView", "PostView", "ReportView", "ReportSuccessView", ) 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 ban.expires > now] context["bans"] = bans context["active_bans"] = active_bans context["ip"] = ip return context class CreateView(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): # Set the board on this object self.board = get_object_or_404(Board, url=kwargs["url"]) # 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(CreateView): model = Post form_class = PostForm 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("-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 return super(BoardView, self).get_context_data(**kwargs) class PostView(CreateView): model = Post form_class = ReplyForm slug_field = "url" slug_url_kwarg = "url" 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 return super(PostView, self).get_context_data(**kwargs) def get_form_kwargs(self): kwargs = super(PostView, self).get_form_kwargs() post_id = self.kwargs["id"] post = get_object_or_404(Post, id=post_id) kwargs["op"] = post kwargs["reply"] = post return kwargs 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, CreateView): model = Ban form_class = BanForm permission_required = "ban.create" success_url = reverse_lazy("board:ban_success") 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 is not None and ban.expires < now ] context["current_bans"] = [ ban for ban in bans if ban.expires is None 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(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" def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["window_timeout"] = settings.BAN_WINDOW_CLOSE_TIMEOUT return context