# Episodes.Community - Community-Driven TV Show Episode Link Sharing Site # Copyright (C) 2017 Evert "Diamond" Prants , Taizo "Tsa6" Simpson # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . from django.template import RequestContext from django.shortcuts import render, redirect, get_list_or_404, get_object_or_404 from django.views import View from django.views.generic.base import TemplateView from django.contrib.auth.decorators import login_required from django.conf import settings from django.http import Http404, HttpResponseForbidden, HttpResponse, HttpResponseRedirect from django.db.models import Case, When, Value, IntegerField, Count, F, Q, Max from django.contrib.auth.mixins import LoginRequiredMixin from django.shortcuts import render from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage from django.utils.text import slugify from guardian.decorators import permission_required_or_403 from LandingPage.models import Show, DiscussionBoard, DiscussionReply, DiscussionVote, Ban from . import forms import datetime import re class Boards(TemplateView): template_name = "boards.html" def get_context_data(self, abbr, **kwargs): ctx = super().get_context_data() show = get_object_or_404(Show, abbr=abbr) page = self.request.GET.get('page', 1) boards_list = DiscussionBoard.objects.filter(show=show).annotate( num_replies=Count('replies'), recency=Case( When( num_replies=0, then=Max('timestamp') ), When( num_replies__gt=0, then=Max('replies__timestamp') ) ), ).order_by('-recency') paginator = Paginator(boards_list, getattr(settings, "DISCUSSIONS_PER_PAGE", 26)) try: boards = paginator.page(page) except PageNotAnInteger: boards = paginator.page(1) except EmptyPage: boards = paginator.page(paginator.num_pages) ctx['boards'] = boards ctx['show'] = show return ctx class Board(TemplateView): template_name = "board.html" def get_context_data(self, abbr, bid, **kwargs): ctx = super().get_context_data() show = get_object_or_404(Show, abbr=abbr) board = get_object_or_404(DiscussionBoard, pk=bid) page = self.request.GET.get('page', 1) find = self.request.GET.get('findReply', None) reply_list = DiscussionReply.objects.filter(board=board).order_by('timestamp').annotate( positives=Count( Case( When( votes__positive=True, then=Value(1) ) ) ), negatives=Count('votes') - F('positives'), score=F('positives') - F('negatives') ) perpage = getattr(settings, "DISCUSSIONS_REPLIES_PER_PAGE", 10) paginator = Paginator(reply_list, perpage) if find and find.isnumeric(): item = get_object_or_404(DiscussionReply, pk=find) if item.board == board: found = DiscussionReply.objects.filter(timestamp__lt=item.timestamp,board=board).count() page = int(found / perpage) + 1 index = int(found % perpage) + 1 ctx['url'] = '/show/%s/discuss/board/%d-%s?page=%d#reply-%d'%(abbr, board.pk, slugify(board.title), page, index) return ctx try: replies = paginator.page(page) except PageNotAnInteger: replies = paginator.page(1) except EmptyPage: replies = paginator.page(paginator.num_pages) ctx['board'] = board ctx['replies'] = replies ctx['show'] = show ctx['form'] = forms.ReplyForm() return ctx def render_to_response(self, context): if 'url' in context: return redirect(context['url']) return super(Board, self).render_to_response(context) # Board form GET and POST @login_required def BoardForm(req, abbr): show = get_object_or_404(Show, abbr=abbr) user = req.user form = forms.BoardForm() # Request context ctx = { 'form': form, 'show': show } # Get bans for this user regarding this show bans = Ban.objects.filter(Q(scope=show) | Q(site_wide=True), Q(expiration__gte=datetime.datetime.now()) | Q(permanent=True), user=user) if bans.count() > 0: return HttpResponseForbidden('You are banned from discussing this show.
Reason: %s'%(bans.first().reason)) # Handle POST if req.method == 'POST': form = forms.BoardForm(req.POST) ctx['form'] = form if form.is_valid(): form_data = form.cleaned_data # Check if the Title has already been posted if DiscussionBoard.objects.filter(show=show,title=form_data['title']).count() > 0: ctx['error'] = 'A board with this title already exists!' return render(req, "boards_new.html", ctx) if not user.has_perm('LandingPage.can_moderate_board', show): # Check if there has been a board by this user for this show within the last 24 hours if DiscussionBoard.objects.filter(user=user,show=show,timestamp__gte=datetime.datetime.now() - datetime.timedelta(hours=24)).count() > 8: ctx['error'] = 'You can only create 8 boards for a show in 24 hours!' return render(req, "boards_new.html", ctx) new_board = form.save(commit=False) new_board.user = user new_board.show = show new_board.save() new_post = DiscussionReply(user=user,board=new_board,body=form_data['body']) new_post.save() return HttpResponseRedirect('/show/%s/discuss/board/%d-%s'%(abbr, new_board.pk, slugify(form_data['title']))) else: ctx['error'] = 'Invalid fields!' return render(req, "boards_new.html", ctx) # Reply form GET and POST @login_required def BoardReplyForm(req, abbr, bid): show = get_object_or_404(Show, abbr=abbr) board = get_object_or_404(DiscussionBoard, pk=bid) user = req.user form = forms.ReplyForm() # Request context ctx = { 'form': form, 'board': board, 'show': show } # Get bans for this user regarding this show bans = Ban.objects.filter(Q(scope=show) | Q(site_wide=True), Q(expiration__gte=datetime.datetime.now()) | Q(permanent=True), user=user) if bans.count() > 0: return HttpResponseForbidden('You are banned from discussing this show.
Reason: %s'%(bans.first().reason)) # Handle POST if req.method == 'POST': form = forms.ReplyForm(req.POST) ctx['form'] = form if form.is_valid(): form_data = form.cleaned_data # Body Content Filter real_content = re.sub(r'[\s\W]+', '', form_data['body']) err_res = False if len(real_content) < 10: ctx['error'] = 'The content is too small! Please write more meaningful replies.' err_res = True elif len(real_content) > 4000: ctx['error'] = 'The content body is too large! Please write less in a single reply.' err_res = True # TODO: Apply word filtering here # TODO: Apply markdown if err_res: return render(req, "board_reply.html", ctx) print(form_data['body']) new_reply = form.save(commit=False) new_reply.user = user new_reply.board = board new_reply.save() return HttpResponseRedirect('/show/%s/discuss/board/%d-%s?findReply=%d'%(abbr, board.pk, slugify(board.title), new_reply.pk)) else: ctx['error'] = 'Invalid fields!' return render(req, "board_reply.html", ctx) # Vote request # /show/{{abbr}}/vote/{{submission id}}/{{positive == 1}} class BoardVoteSubmit(LoginRequiredMixin, View): def post (self, req, abbr, replyid, positive): # Convert positive parameter into a boolean pos_bool = int(positive) == 1 user = req.user # Get the reply from the database reply = get_object_or_404(DiscussionReply, id=replyid) # Prevent voting for own reply if reply.user == user: return HttpResponse('

Error

You cannot vote for your own reply.

' 'Return to board

' % (abbr, reply.board.pk, slugify(reply.board.title)), status=400) show = reply.board.show # Get bans for this user regarding this show bans = Ban.objects.filter(Q(scope=show) | Q(site_wide=True), Q(expiration__gte=datetime.datetime.now()) | Q(permanent=True), user=user) if bans.count() > 0: return HttpResponseForbidden('You are banned from voting on this show\'s discussion boards.
Reason: %s'%(bans.first().reason)) # Allow changing a vote from positive to negative or vice-versa. Delete vote if its a re-vote vote = reply.votes.filter(user=user,reply=reply).first() if vote: if not vote.positive == pos_bool: vote.positive = pos_bool vote.save() else: vote.delete() else: new_vote = DiscussionVote( user=user, reply=reply, positive=pos_bool ) new_vote.save() return HttpResponseRedirect('/show/%s/discuss/board/%d-%s?findReply=%d'%(abbr, reply.board.pk, slugify(reply.board.title), reply.pk))