Add a bunch of moderation options to Show - add seasons, add episodes, add, change and delete submissions.

This commit is contained in:
Evert Prants 2017-12-15 13:33:01 +02:00
parent 94931900e5
commit fa594518c3
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
10 changed files with 306 additions and 55 deletions

View File

@ -20,6 +20,6 @@ from django.conf.urls.static import static
urlpatterns = [ urlpatterns = [
url(r'^admin/', admin.site.urls), url(r'^admin/', admin.site.urls),
url(r'^show/(?P<abbreviation>\w{1,5})/', include('Show.urls')), url(r'^show/(?P<abbr>\w{1,5})/', include('Show.urls')),
url(r'^', include('LandingPage.urls')) url(r'^', include('LandingPage.urls'))
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@ -13,6 +13,12 @@ body {
text-decoration: none; text-decoration: none;
color: #000; color: #000;
} }
.button.modbutton {
background-color: #ffe599;
border: 1px solid #a76300;
color: #945800;
font-weight: bold;
}
input[type="submit"] { input[type="submit"] {
font-size: 120%; font-size: 120%;
} }
@ -165,16 +171,22 @@ a.episode .submission_cnt {
.submission { .submission {
padding: 10px; padding: 10px;
} }
.submission .data {
font-size: 90%;
color: #b1b1b1;
}
.submission a.link { .submission a.link {
font-size: 180%; font-size: 180%;
text-decoration: none; text-decoration: none;
color: #191919; color: #191919;
font-style: italic; font-style: italic;
display: inline-block;
padding: 15px 0;
} }
.vote-btns { .vote-btns {
float: right; float: right;
} }
.vote-positive, .vote-negative { .vote-positive, .vote-negative, .sub-report {
padding: 10px; padding: 10px;
display: inline-block; display: inline-block;
min-width: 20px; min-width: 20px;
@ -191,7 +203,7 @@ a.episode .submission_cnt {
background-color: #a4ffa7; background-color: #a4ffa7;
color: #008005; color: #008005;
} }
.vote-negative { .vote-negative, .sub-report {
background-color: #ffa6a6; background-color: #ffa6a6;
color: #ab0000; color: #ab0000;
} }
@ -205,6 +217,15 @@ a.episode .submission_cnt {
border: 1px solid #b10000; border: 1px solid #b10000;
margin: 5px 0; margin: 5px 0;
} }
.actions {
float: right;
padding: 15px;
}
.divider {
margin: 0 5px;
opacity: 0.5;
user-select: none;
}
@media all and (max-width: 800px) { @media all and (max-width: 800px) {
.logo { .logo {
font-size: 5vw !important; font-size: 5vw !important;

View File

@ -1,5 +1,5 @@
from django import forms from django import forms
from LandingPage.models import Submission from LandingPage.models import Submission, Season, Episode
class SubmissionForm(forms.ModelForm): class SubmissionForm(forms.ModelForm):
class Meta(): class Meta():
@ -9,3 +9,13 @@ class SubmissionForm(forms.ModelForm):
'url': 'URL to your episode', 'url': 'URL to your episode',
'tags': 'Describe your link. Comma-separated list of keywords' 'tags': 'Describe your link. Comma-separated list of keywords'
} }
class SeasonForm(forms.ModelForm):
class Meta():
model = Season
fields = ('name','number','description','artwork',)
class EpisodeForm(forms.ModelForm):
class Meta():
model = Episode
fields = ('episode','name','summary','airdate',)

View File

@ -14,6 +14,8 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% load guardian_tags %}
{% get_obj_perms request.user for show as "show_perms" %}
<section class="show-details"> <section class="show-details">
<div class="banner" style="background-image: url(/media/uploaded_resources/{{show.banner}});"></div> <div class="banner" style="background-image: url(/media/uploaded_resources/{{show.banner}});"></div>
<div class="banner-cover"> <div class="banner-cover">
@ -31,7 +33,11 @@
<section class="submissions"> <section class="submissions">
<a href="/show/{{show.abbr}}" class="button"><i class="fa fa-fw fa-home"></i>&nbsp;Show Index</a> <a href="/show/{{show.abbr}}" class="button"><i class="fa fa-fw fa-home"></i>&nbsp;Show Index</a>
{% if user.is_authenticated %} {% if user.is_authenticated %}
{% if "change_show" in show_perms %}
<a href="/show/{{show.abbr}}/episode/{{episode.season.number}}/{{episode.episode}}/submit" class="button modbutton"><i class="fa fa-fw fa-plus"></i>&nbsp;Add New Link</a>
{% else %}
<a href="/show/{{show.abbr}}/episode/{{episode.season.number}}/{{episode.episode}}/submit" class="button"><i class="fa fa-fw fa-plus"></i>&nbsp;Submit New Link</a> <a href="/show/{{show.abbr}}/episode/{{episode.season.number}}/{{episode.episode}}/submit" class="button"><i class="fa fa-fw fa-plus"></i>&nbsp;Submit New Link</a>
{% endif %}
{% else %} {% else %}
<span class="fillertext"><a href="/login">Log in</a> to submit a link</span> <span class="fillertext"><a href="/login">Log in</a> to submit a link</span>
{% endif %} {% endif %}
@ -54,6 +60,14 @@
</button> </button>
</form> </form>
</div> </div>
<div class="data">
<span class="timestamp">Submitted {{sbm.timestamp}} by {{sbm.user.display_name}}</span><span class="divider">&middot;</span>
{% if "change_show" in show_perms %}
<a href="/show/{{show.abbr}}/submission/{{sbm.id}}/moderate" class="button modbutton"><i class="fa fa-fw fa-shield"></i>&nbsp;Change</a>
{% else %}
<a href="/report?url={{ request.get_full_path|urlencode }}&submission={{ sbm.id }}" class="report">Report Invalid or Spam</a>
{% endif %}
</div>
</div> </div>
{% empty %} {% empty %}
<h3>Nobody has submitted any links yet.</h3> <h3>Nobody has submitted any links yet.</h3>

View File

@ -0,0 +1,33 @@
{% extends "base.html" %}
{% block title %}
Add an Episode - Season {{season.number}} of {{show.name}} - Episodes.Community
{% endblock %}
{% block content %}
<section class="show-details">
<div class="banner" style="background-image: url(/media/uploaded_resources/{{show.banner}});"></div>
<div class="banner-cover">
<div class="artwork">
<img src="/media/uploaded_resources/{{show.artwork}}">
</div>
<div class="details">
<h1>{{show.name}}</h1>
<p class="description">
{{show.description}}
</p>
</div>
</div>
</section>
<section class="submissions">
<a href="/show/{{show.abbr}}" class="button"><i class="fa fa-fw fa-home"></i>&nbsp;Show Index</a>
<h1>Add an Episode</h1>
{% if error %}
<div class="message error">{{error}}</div>
{% endif %}
<form action="" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</form>
</div>
</section>
{% endblock %}

View File

@ -0,0 +1,33 @@
{% extends "base.html" %}
{% block title %}
Add a season - {{show.name}} - Episodes.Community
{% endblock %}
{% block content %}
<section class="show-details">
<div class="banner" style="background-image: url(/media/uploaded_resources/{{show.banner}});"></div>
<div class="banner-cover">
<div class="artwork">
<img src="/media/uploaded_resources/{{show.artwork}}">
</div>
<div class="details">
<h1>{{show.name}}</h1>
<p class="description">
{{show.description}}
</p>
</div>
</div>
</section>
<section class="submissions">
<a href="/show/{{show.abbr}}" class="button"><i class="fa fa-fw fa-home"></i>&nbsp;Show Index</a>
<h1>Add a Season</h1>
{% if error %}
<div class="message error">{{error}}</div>
{% endif %}
<form action="" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</form>
</div>
</section>
{% endblock %}

View File

@ -12,6 +12,8 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% load guardian_tags %}
{% get_obj_perms request.user for show as "show_perms" %}
<section class="show-details"> <section class="show-details">
<div class="banner" style="background-image: url(/media/uploaded_resources/{{show.banner}});"></div> <div class="banner" style="background-image: url(/media/uploaded_resources/{{show.banner}});"></div>
<div class="banner-cover"> <div class="banner-cover">
@ -36,9 +38,17 @@
</div> </div>
</section> </section>
<section class="seasons"> <section class="seasons">
{% if "change_show" in show_perms %}
<a href="/show/{{show.abbr}}/season/new" class="button modbutton"><i class="fa fa-fw fa-plus"></i>&nbsp;Add a Season</a>
{% endif %}
<h1>Watch Now</h1> <h1>Watch Now</h1>
{% for season in seasons %} {% for season in seasons %}
<div class="season" data-season="{{season.number}}"> <div class="season" data-season="{{season.number}}">
{% if "change_show" in show_perms %}
<div class="actions">
<a href="/show/{{show.abbr}}/season/{{season.number}}/append" class="action"><i class="fa fa-fw fa-plus"></i></a>
</div>
{% endif %}
<div class="season-name">{% if season.name %}{{season.number}} - {{season.name}}{% else %}Season {{season.number}}{%endif%}</div> <div class="season-name">{% if season.name %}{{season.number}} - {{season.name}}{% else %}Season {{season.number}}{%endif%}</div>
<div class="episodes"> <div class="episodes">
{% for episode in season.episodes.all %} {% for episode in season.episodes.all %}

View File

@ -0,0 +1,36 @@
{% extends "base.html" %}
{% block title %}
Change a Submission - Episodes.Community
{% endblock %}
{% block content %}
<section class="show-details">
<div class="banner" style="background-image: url(/media/uploaded_resources/{{show.banner}});"></div>
<div class="banner-cover">
<div class="artwork">
<img src="/media/uploaded_resources/{{show.artwork}}">
</div>
<div class="details">
<h1>{{show.name}}</h1>
<p class="description">
{{show.description}}
</p>
</div>
</div>
</section>
<section class="submissions">
<a href="/show/{{show.abbr}}" class="button"><i class="fa fa-fw fa-home"></i>&nbsp;Show Index</a>
<a href="/show/{{show.abbr}}/episode/{{episode.season.number}}/{{episode.episode}}-{{episode.name|slugify}}" class="button">Back to Episode</a>
<h1>Submission</h1>
{% if error %}
<div class="message error">{{error}}</div>
{% endif %}
<form action="" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Edit" />
<input type="submit" name="delete" value="Delete" />
<input type="submit" name="delete_ban" value="Delete and Ban User" />
</form>
</div>
</section>
{% endblock %}

View File

@ -20,6 +20,9 @@ from . import views
urlpatterns = [ urlpatterns = [
url(r'^$', views.IndexView.as_view()), url(r'^$', views.IndexView.as_view()),
url(r'^season/new$', views.SeasonSubmitForm),
url(r'^season/(?P<season>\d{1,4})/append$', views.EpisodeSubmitForm),
url(r'^submission/(?P<submission>\d{1,4})/moderate$', views.SubmissionModForm),
url(r'^episode/(?P<season>\d{1,4})/(?P<episode>\d{1,4})(-[\w-]+)?/?$', views.EpisodeView.as_view()), url(r'^episode/(?P<season>\d{1,4})/(?P<episode>\d{1,4})(-[\w-]+)?/?$', views.EpisodeView.as_view()),
url(r'^episode/(?P<season>\d{1,4})/(?P<episode>\d{1,4})(-[\w-]+)?/submit$', views.SubmissionForm), url(r'^episode/(?P<season>\d{1,4})/(?P<episode>\d{1,4})(-[\w-]+)?/submit$', views.SubmissionForm),
url(r'^vote/(?P<subid>\d+)/(?P<positive>[0-1])/?$', views.SubmissionVoteSubmit.as_view()) url(r'^vote/(?P<subid>\d+)/(?P<positive>[0-1])/?$', views.SubmissionVoteSubmit.as_view())

View File

@ -1,5 +1,5 @@
from django.template import RequestContext from django.template import RequestContext
from django.shortcuts import render from django.shortcuts import render, get_list_or_404, get_object_or_404
from django.views import View from django.views import View
from django.views.generic.base import TemplateView from django.views.generic.base import TemplateView
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
@ -10,11 +10,9 @@ from django.http import HttpResponseRedirect
from django.db.models import Case, When, Value, IntegerField, Count, F from django.db.models import Case, When, Value, IntegerField, Count, F
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from LandingPage.models import User from guardian.decorators import permission_required_or_403
from LandingPage.models import Show
from LandingPage.models import Season from LandingPage.models import User, Show, Season, Episode, Submission, SubmissionVote
from LandingPage.models import Episode
from LandingPage.models import Submission, SubmissionVote
from . import forms from . import forms
@ -26,15 +24,11 @@ import datetime
class IndexView(TemplateView): class IndexView(TemplateView):
template_name = "show.html" template_name = "show.html"
def get_context_data(self, abbreviation, **kwargs): def get_context_data(self, abbr, **kwargs):
ctx = super().get_context_data() ctx = super().get_context_data()
# Get show by abbreviation, add episode count to the show and return only the first object # Get show by abbr, add episode count to the show and return only the first object
show = Show.objects.filter(abbr=abbreviation).first() show = get_object_or_404(Show, abbr=abbr)
# 404
if not show:
raise Http404("Show does not exist")
# Get all seasons of the show and annotate episode counts onto them # Get all seasons of the show and annotate episode counts onto them
seasons = show.seasons.all() seasons = show.seasons.all()
@ -49,21 +43,12 @@ class IndexView(TemplateView):
class EpisodeView(TemplateView): class EpisodeView(TemplateView):
template_name = "episode.html" template_name = "episode.html"
def get_context_data(self, abbreviation, season, episode, **kwargs): def get_context_data(self, abbr, season, episode, **kwargs):
ctx = super().get_context_data() ctx = super().get_context_data()
# Get show by abbreviation # Get show by abbr
show = Show.objects.filter(abbr=abbreviation).first() show = get_object_or_404(Show, abbr=abbr)
episode = get_object_or_404(Episode, show=show,season__number=season,episode=episode)
# Get episode by season and episode number
episode = Episode.objects.filter(show=show,season__number=season,episode=episode).first()
# 404's
if not show:
raise Http404("Show does not exist")
if not episode:
raise Http404("Episode does not exist")
# I acknowledge that this is a mess. A functional mess. But a mess nonetheless. Hey, that rhymed! # I acknowledge that this is a mess. A functional mess. But a mess nonetheless. Hey, that rhymed!
submissions = episode.submissions.annotate( submissions = episode.submissions.annotate(
@ -88,18 +73,11 @@ class EpisodeView(TemplateView):
# Submission form GET and POST # Submission form GET and POST
@login_required @login_required
def SubmissionForm(req, abbreviation, season, episode): def SubmissionForm(req, abbr, season, episode):
show = Show.objects.get(abbr=abbreviation) show = get_object_or_404(Show, abbr=abbr)
episode = Episode.objects.filter(show=show,season__number=season,episode=episode).first() episode = get_object_or_404(Episode, show=show,season__number=season,episode=episode)
user = req.user user = req.user
# 404's
if not show:
raise Http404("Show does not exist")
if not episode:
raise Http404("Episode does not exist")
form = forms.SubmissionForm() form = forms.SubmissionForm()
# Request context # Request context
@ -122,39 +100,152 @@ def SubmissionForm(req, abbreviation, season, episode):
ctx['error'] = 'This URL has already been submitted!' ctx['error'] = 'This URL has already been submitted!'
return render(req, "submit.html", ctx) return render(req, "submit.html", ctx)
if not user.has_perm('show.change_show'):
# Check if there has been a submission by this user for this episode within the last 24 hours # Check if there has been a submission by this user for this episode within the last 24 hours
if Submission.objects.filter(user=user,episode=episode,timestamp__gte=datetime.datetime.now() - datetime.timedelta(hours=24)).count() > 0: if Submission.objects.filter(user=user,episode=episode,timestamp__gte=datetime.datetime.now() - datetime.timedelta(hours=24)).count() > 0:
ctx['error'] = 'You can only submit one link for an episode in 24 hours!' ctx['error'] = 'You can only submit one link for an episode in 24 hours!'
return render(req, "submit.html", ctx) return render(req, "submit.html", ctx)
# Have to do this because you can't add fields to a form
# If you know a better way of doing this, be my guest
new_submission = form.save(commit=False) new_submission = form.save(commit=False)
new_submission.user = user new_submission.user = user
new_submission.episode = episode new_submission.episode = episode
new_submission.save() new_submission.save()
return HttpResponseRedirect('/show/%s/episode/%d/%d'%(abbreviation, episode.season.number, episode.episode)) return HttpResponseRedirect('/show/%s/episode/%d/%d'%(abbr, episode.season.number, episode.episode))
else: else:
ctx['error'] = 'Invalid fields!' ctx['error'] = 'Invalid fields!'
return render(req, "submit.html", ctx) return render(req, "submit.html", ctx)
# Edit a submission - for moderators
@permission_required_or_403('show.change_show', (Show, 'abbr', 'abbr'), accept_global_perms=True)
def SubmissionModForm(req, abbr, submission):
show = get_object_or_404(Show, abbr=abbr)
submission = get_object_or_404(Submission, pk=submission)
episode = submission.episode
user = req.user
form = forms.SubmissionForm(instance=submission)
# Request context
ctx = {
'form': form,
'show': show,
'episode': episode
}
# Handle POST
if req.method == 'POST':
if 'delete' in req.POST:
submission.delete()
return HttpResponseRedirect('/show/%s/episode/%d/%d'%(abbr, episode.season.number, episode.episode))
if 'delete_ban' in req.POST:
submission.delete()
return HttpResponseRedirect('/ban?user=%d'%(submission.user.pk))
form = forms.SubmissionForm(req.POST, instance=submission)
ctx['form'] = form
if form.is_valid():
form_data = form.cleaned_data
form.save()
return HttpResponseRedirect('/show/%s/episode/%d/%d'%(abbr, episode.season.number, episode.episode))
else:
ctx['error'] = 'Invalid fields!'
return render(req, "submit_mod.html", ctx)
# Season form GET and POST
@permission_required_or_403('show.change_show', (Show, 'abbr', 'abbr'), accept_global_perms=True)
def SeasonSubmitForm(req, abbr):
show = get_object_or_404(Show, abbr=abbr)
user = req.user
form = forms.SeasonForm()
# Request context
ctx = {
'form': form,
'show': show
}
# Handle POST
if req.method == 'POST':
form = forms.SeasonForm(req.POST)
ctx['form'] = form
if form.is_valid():
form_data = form.cleaned_data
# Check if the URL has already been submitted
if Season.objects.filter(show=show,number=form_data['number']).count() > 0:
ctx['error'] = 'This season has already been submitted!'
return render(req, "season_add.html", ctx)
new_season = form.save(commit=False)
new_season.show = show
new_season.save()
return HttpResponseRedirect('/show/%s'%(abbr))
else:
ctx['error'] = 'Invalid fields!'
return render(req, "season_add.html", ctx)
# Episode form GET and POST
@permission_required_or_403('show.change_show', (Show, 'abbr', 'abbr'), accept_global_perms=True)
def EpisodeSubmitForm(req, abbr, season):
show = get_object_or_404(Show, abbr=abbr)
season = get_object_or_404(Season, show=show,number=season)
user = req.user
form = forms.EpisodeForm()
# Request context
ctx = {
'form': form,
'season': season,
'show': show
}
# Handle POST
if req.method == 'POST':
form = forms.EpisodeForm(req.POST)
ctx['form'] = form
if form.is_valid():
form_data = form.cleaned_data
# Check if the URL has already been submitted
if Episode.objects.filter(show=show,episode=form_data['episode'],season=season).count() > 0:
ctx['error'] = 'This episode has already been submitted!'
return render(req, "episode_add.html", ctx)
new_episode = form.save(commit=False)
new_episode.show = show
new_episode.season = season
new_episode.save()
return HttpResponseRedirect('/show/%s'%(abbr))
else:
ctx['error'] = 'Invalid fields!'
return render(req, "episode_add.html", ctx)
# Vote request # Vote request
# /show/{{abbr}}/vote/{{submission id}}/{{positive == 1}} # /show/{{abbr}}/vote/{{submission id}}/{{positive == 1}}
class SubmissionVoteSubmit(LoginRequiredMixin, View): class SubmissionVoteSubmit(LoginRequiredMixin, View):
def post (self, req, abbreviation, subid, positive): def post (self, req, abbr, subid, positive):
# Convert positive parameter into a boolean # Convert positive parameter into a boolean
pos_bool = int(positive) == 1 pos_bool = int(positive) == 1
user = req.user user = req.user
# Get the submission from the database # Get the submission from the database
submission = Submission.objects.filter(id=subid).first() submission = get_object_or_404(Submission, id=subid)
# 404
if not submission:
raise Http404("Submission does not exist")
# Prevent voting for own submission # Prevent voting for own submission
if submission.user == user: if submission.user == user:
@ -176,5 +267,5 @@ class SubmissionVoteSubmit(LoginRequiredMixin, View):
) )
new_vote.save() new_vote.save()
return HttpResponseRedirect('/show/%s/episode/%d/%d'%(abbreviation, submission.episode.season.number, submission.episode.episode)) return HttpResponseRedirect('/show/%s/episode/%d/%d'%(abbr, submission.episode.season.number, submission.episode.episode))