Compare commits

...
This repository has been archived on 2022-11-26. You can view files and clone it, but cannot push or open issues or pull requests.

2 Commits

7 changed files with 225 additions and 16 deletions

View File

@ -28,7 +28,7 @@ from django.utils.text import slugify
from guardian.decorators import permission_required_or_403 from guardian.decorators import permission_required_or_403
from LandingPage.models import Show, DiscussionBoard, DiscussionReply, DiscussionVote, Ban, Report from LandingPage.models import Show, DiscussionBoard, DiscussionReply, DiscussionVote, Ban, Report, UserAction
from . import forms from . import forms
import datetime import datetime
@ -176,6 +176,9 @@ def BoardForm(req, abbr):
new_post = DiscussionReply(user=user,board=new_board,body=form_data['body']) new_post = DiscussionReply(user=user,board=new_board,body=form_data['body'])
new_post.save() new_post.save()
act = UserAction(user=user,show=show,act_type=0,url='%s/discuss/board/%d-%s'%(show.url(), new_board.pk, slugify(form_data['title'])))
act.save()
return HttpResponseRedirect(show.url() + '/discuss/board/%d-%s'%(new_board.pk, slugify(form_data['title']))) return HttpResponseRedirect(show.url() + '/discuss/board/%d-%s'%(new_board.pk, slugify(form_data['title'])))
else: else:
ctx['error'] = 'Invalid fields!' ctx['error'] = 'Invalid fields!'
@ -227,13 +230,15 @@ def BoardReplyForm(req, abbr, bid):
if err_res: if err_res:
return render(req, "board_reply.html", ctx) return render(req, "board_reply.html", ctx)
print(form_data['body'])
new_reply = form.save(commit=False) new_reply = form.save(commit=False)
new_reply.user = user new_reply.user = user
new_reply.board = board new_reply.board = board
new_reply.save() new_reply.save()
act = UserAction(user=user,show=show,act_type=1,url='%s/discuss/board/%d-%s?findReply=%d'%(show.url(), new_board.pk, slugify(form_data['title']), new_reply.pk))
act.save()
return HttpResponseRedirect(show.url() + '/discuss/board/%d-%s?findReply=%d'%(board.pk, slugify(board.title), new_reply.pk)) return HttpResponseRedirect(show.url() + '/discuss/board/%d-%s?findReply=%d'%(board.pk, slugify(board.title), new_reply.pk))
else: else:
ctx['error'] = 'Invalid fields!' ctx['error'] = 'Invalid fields!'
@ -273,6 +278,10 @@ class BoardVoteSubmit(LoginRequiredMixin, View):
if not vote.positive == pos_bool: if not vote.positive == pos_bool:
vote.positive = pos_bool vote.positive = pos_bool
vote.save() vote.save()
act = UserAction(user=user,show=show,act_type=6 + int(positive),url='%s/discuss/board/%d-%s?findReply=%d'%(showurl,
reply.board.pk, slugify(reply.board.title), reply.pk))
act.save()
else: else:
vote.delete() vote.delete()
else: else:
@ -283,6 +292,10 @@ class BoardVoteSubmit(LoginRequiredMixin, View):
) )
new_vote.save() new_vote.save()
act = UserAction(user=user,show=show,act_type=6 + int(positive),url='%s/discuss/board/%d-%s?findReply=%d'%(showurl, reply.board.pk,
slugify(reply.board.title), reply.pk))
act.save()
return HttpResponseRedirect('%s/discuss/board/%d-%s?findReply=%d'%(showurl, reply.board.pk, slugify(reply.board.title), reply.pk)) return HttpResponseRedirect('%s/discuss/board/%d-%s?findReply=%d'%(showurl, reply.board.pk, slugify(reply.board.title), reply.pk))
@login_required @login_required
@ -318,7 +331,7 @@ def ReportForm(req, abbr, rid):
if not user.has_perm('LandingPage.can_moderate_board', show): if not user.has_perm('LandingPage.can_moderate_board', show):
# Check if there have been many reports by this user within the last 12 hours # Check if there have been many reports by this user within the last 12 hours
if Report.objects.filter(user=user,timestamp__gte=datetime.datetime.now() - datetime.timedelta(hours=12)).count() > 5: if Report.objects.filter(reporter=user,timestamp__gte=datetime.datetime.now() - datetime.timedelta(hours=12)).count() > 5:
ctx['error'] = 'You\'ve created too many reports recently!' ctx['error'] = 'You\'ve created too many reports recently!'
return render(req, "report_reply.html", ctx) return render(req, "report_reply.html", ctx)
@ -368,6 +381,9 @@ def BoardDelete(req, abbr, bid):
board = get_object_or_404(DiscussionBoard, pk=bid) board = get_object_or_404(DiscussionBoard, pk=bid)
showurl = get_show_url(abbr) showurl = get_show_url(abbr)
act = UserAction(user=req.user,show=board.show,act_type=4,url='#%s by %s'%(board.title, board.user.display_name))
act.save()
DiscussionBoard.objects.filter(pk=board.pk).delete() DiscussionBoard.objects.filter(pk=board.pk).delete()
return HttpResponseRedirect('%s/discuss' % (board.show.url())) return HttpResponseRedirect('%s/discuss' % (board.show.url()))
@ -376,8 +392,12 @@ def BoardDelete(req, abbr, bid):
def BoardDeleteReply(req, abbr, rid): def BoardDeleteReply(req, abbr, rid):
reply = get_object_or_404(DiscussionReply, pk=rid) reply = get_object_or_404(DiscussionReply, pk=rid)
act = UserAction(user=req.user,show=reply.board.show,act_type=4,url='%s/discuss/board/%d-%s?findReply=%d'%(reply.board.show.url(),
reply.board.pk, slugify(reply.board.title), reply.pk))
act.save()
delete = not reply.deleted delete = not reply.deleted
DiscussionReply.objects.filter(pk=reply.pk).update(deleted=delete) DiscussionReply.objects.filter(pk=reply.pk).update(deleted=delete)
return HttpResponseRedirect('%s/discuss/board/%d-%s'%(reply.show.url(), reply.board.pk, slugify(reply.board.title))) return HttpResponseRedirect('%s/discuss/board/%d-%s'%(reply.board.show.url(), reply.board.pk, slugify(reply.board.title)))

View File

@ -46,3 +46,4 @@ admin.site.register(Watch)
admin.site.register(DiscussionBoard) admin.site.register(DiscussionBoard)
admin.site.register(DiscussionReply) admin.site.register(DiscussionReply)
admin.site.register(DiscussionVote) admin.site.register(DiscussionVote)
admin.site.register(UserAction)

View File

@ -467,3 +467,48 @@ class DiscussionVote(TimestampedModel):
) )
def __str__(self): def __str__(self):
return "%s %s reply %d"%(self.user, '\U0001f592' if self.positive else '\U0001f44e', self.reply.pk) return "%s %s reply %d"%(self.user, '\U0001f592' if self.positive else '\U0001f44e', self.reply.pk)
class UserAction(TimestampedModel):
user = models.ForeignKey(
User,
on_delete=models.CASCADE,
help_text='The user who committed this action',
)
url = models.URLField(
max_length=100,
help_text='The URL of the content',
verbose_name = 'Content URL'
)
show = models.ForeignKey(
Show,
on_delete=models.CASCADE,
null=True,
blank=True,
help_text='The show associated with the action',
)
act_type = models.IntegerField(
help_text='Type of this action',
default=0,
verbose_name = 'Action Type'
)
def act_str(self):
action = 'created'
if self.act_type == 1:
action = 'submitted'
elif self.act_type == 2:
action = 'replied'
elif self.act_type == 3:
action = 'modified'
elif self.act_type == 4:
action = 'deleted'
elif self.act_type == 5:
action = 'banned'
elif self.act_type == 6:
action = 'voted down'
elif self.act_type == 7:
action = 'voted up'
return action
def __str__(self):
return "%s %s %s"%(self.user, self.act_str(), self.url)

View File

@ -0,0 +1,47 @@
{% extends "base.html" %}
{% block title %}
Moderator area for {{show.name}} - Episodes.Community
{% endblock %}
{% block content %}
{% load guardian_tags %}
{% get_obj_perms request.user for show as "show_perms" %}
{% include "partials/show_banner.html" %}
<section class="mb-5 mt-2 container">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{{show.url}}">{{show.name}}</a></li>
<li class="breadcrumb-item active" aria-current="page">Moderator Area</li>
</ol>
</nav>
<h1>Moderator Area</h1>
<div class="row">
<div class="col">
<h2>Recent Activity</h2>
{% for action in actions %}
<div class="action">
<div class="font-weight-bold">
<a href="/community/user/{{action.user.id}}-{{action.user.display_name|slugify}}">{{action.user.display_name}}</a>
</div>
<p class="text-muted">{{action.act_str}} <a href="{{action.url}}" target="_blank">{{action.url}}</a></p>
</div>
{% empty %}
<p>Nothing to show.</p>
{% endfor %}
</div>
<div class="col">
<h2>Unresolved Reports</h2>
{% for report in reports %}
<div class="report border-bottom">
<a class="font-weight-bold" href="{{show.url}}/moderator/report/{{report.pk}}">{{report.title}}</a>
<p class="font-weight-light text-muted">
<a href="/community/user/{{report.reporter.id}}-{{report.reporter.display_name|slugify}}">{{report.reporter.display_name}}</a> · {{report.timestamp}}
</p>
</div>
{% empty %}
<p>Nothing to show.</p>
{% endfor %}
</div>
</div>
</section>
{% endblock %}

View File

@ -0,0 +1,30 @@
{% extends "base.html" %}
{% block title %}
Moderator area for {{show.name}} - Episodes.Community
{% endblock %}
{% block content %}
{% load guardian_tags %}
{% get_obj_perms request.user for show as "show_perms" %}
{% include "partials/show_banner.html" %}
<section class="mb-5 mt-2 container">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{{show.url}}">{{show.name}}</a></li>
<li class="breadcrumb-item"><a href="{{show.url}}/moderator">Moderator Area</a></li>
<li class="breadcrumb-item active" aria-current="page">Read Report</li>
</ol>
</nav>
<h1>{{report.title}}</h1>
<div class="user-content">{{report.details}}</div>
<p>URL: <a href="{{report.url}}">{{report.url}}</a></p>
<form method="POST">
{%csrf_token%}
{% if not report.resolved %}
<button name="resolve" class="btn btn-primary">Mark as resolved</button>
{% endif %}
<button name="delete" class="btn btn-danger">Delete</button>
<button name="delete_ban" class="btn btn-danger">Delete and Ban User</button>
</form>
</section>
{% endblock %}

View File

@ -36,6 +36,8 @@ from . import views
urlpatterns = [ urlpatterns = [
url(r'^$', views.IndexView.as_view()), url(r'^$', views.IndexView.as_view()),
url(r'^moderator/report/(?P<repid>\d{1,4})$', views.ModReport),
url(r'^moderator/$', views.ModDashboard),
url(r'^create_ban$', views.BanFromShowForm), url(r'^create_ban$', views.BanFromShowForm),
url(r'^season/new$', views.SeasonSubmitForm), url(r'^season/new$', views.SeasonSubmitForm),
url(r'^season/(?P<season>\d{1,4})/append$', views.EpisodeSubmitForm), url(r'^season/(?P<season>\d{1,4})/append$', views.EpisodeSubmitForm),

View File

@ -20,13 +20,14 @@ 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
from django.conf import settings from django.conf import settings
from django.utils.text import slugify
from django.http import Http404, HttpResponseForbidden, HttpResponse, HttpResponseRedirect from django.http import Http404, HttpResponseForbidden, HttpResponse, HttpResponseRedirect
from django.db.models import Case, When, Value, IntegerField, Count, F, Q from django.db.models import Case, When, Value, IntegerField, Count, F, Q
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from guardian.decorators import permission_required_or_403 from guardian.decorators import permission_required_or_403
from LandingPage.models import User, Show, Season, Episode, Submission, SubmissionVote, Ban, Report from LandingPage.models import User, Show, Season, Episode, Submission, SubmissionVote, Ban, Report, UserAction
from . import forms from . import forms
@ -180,6 +181,9 @@ def SubmissionForm(req, abbr, season, episode):
new_submission.episode = episode new_submission.episode = episode
new_submission.save() new_submission.save()
act = UserAction(user=user,show=show,act_type=1,url='%s/episode/%d/%d?submission=%d'%(show.url(), episode.season.number, episode.episode, new_submission.pk))
act.save()
return HttpResponseRedirect('%s/episode/%d/%d'%(show.url(), episode.season.number, episode.episode)) return HttpResponseRedirect('%s/episode/%d/%d'%(show.url(), episode.season.number, episode.episode))
else: else:
ctx['error'] = 'Invalid fields!' ctx['error'] = 'Invalid fields!'
@ -206,10 +210,14 @@ def SubmissionModForm(req, abbr, submission):
# Handle POST # Handle POST
if req.method == 'POST': if req.method == 'POST':
if 'delete' in req.POST: if 'delete' in req.POST:
act = UserAction(user=user,show=show,act_type=4,url='%s/episode/%d/%d?submission=%d'%(show.url(), episode.season.number, episode.episode, submission.pk))
act.save()
submission.delete() submission.delete()
return HttpResponseRedirect('%s/episode/%d/%d'%(show.url(), episode.season.number, episode.episode)) return HttpResponseRedirect('%s/episode/%d/%d'%(show.url(), episode.season.number, episode.episode))
if 'delete_ban' in req.POST: if 'delete_ban' in req.POST:
act = UserAction(user=user,show=show,act_type=4,url='%s/episode/%d/%d?submission=%d'%(show.url(), episode.season.number, episode.episode, submission.pk))
act.save()
submission.delete() submission.delete()
return HttpResponseRedirect('%s/create_ban?user=%s'%(show.url(),submission.user.username)) return HttpResponseRedirect('%s/create_ban?user=%s'%(show.url(),submission.user.username))
@ -220,6 +228,9 @@ def SubmissionModForm(req, abbr, submission):
form_data = form.cleaned_data form_data = form.cleaned_data
form.save() form.save()
act = UserAction(user=user,show=show,act_type=3,url='%s/episode/%d/%d?submission=%d'%(show.url(), episode.season.number, episode.episode, submission.pk))
act.save()
return HttpResponseRedirect('%s/episode/%d/%d'%(show.url(), episode.season.number, episode.episode)) return HttpResponseRedirect('%s/episode/%d/%d'%(show.url(), episode.season.number, episode.episode))
else: else:
ctx['error'] = 'Invalid fields!' ctx['error'] = 'Invalid fields!'
@ -257,6 +268,9 @@ def SeasonSubmitForm(req, abbr):
new_season.show = show new_season.show = show
new_season.save() new_season.save()
act = UserAction(user=user,show=show,act_type=0,url='%s#season-%d'%(show.url(), new_season.number))
act.save()
return HttpResponseRedirect(show.url()) return HttpResponseRedirect(show.url())
else: else:
ctx['error'] = 'Invalid fields!' ctx['error'] = 'Invalid fields!'
@ -297,6 +311,9 @@ def EpisodeSubmitForm(req, abbr, season):
new_episode.season = season new_episode.season = season
new_episode.save() new_episode.save()
act = UserAction(user=user,show=show,act_type=0,url='%s/episode/%d/%d'%(show.url(), season.number, new_episode.episode))
act.save()
return HttpResponseRedirect(show.url()) return HttpResponseRedirect(show.url())
else: else:
ctx['error'] = 'Invalid fields!' ctx['error'] = 'Invalid fields!'
@ -333,6 +350,10 @@ class SubmissionVoteSubmit(LoginRequiredMixin, View):
if not vote.positive == pos_bool: if not vote.positive == pos_bool:
vote.positive = pos_bool vote.positive = pos_bool
vote.save() vote.save()
act = UserAction(user=user,show=show,act_type=6 + int(positive),url='%s/episode/%d/%d?submission=%d'%(show.url(),
submission.episode.season.number, submission.episode.episode, submission.pk))
act.save()
else: else:
vote.delete() vote.delete()
else: else:
@ -342,6 +363,9 @@ class SubmissionVoteSubmit(LoginRequiredMixin, View):
positive=pos_bool positive=pos_bool
) )
new_vote.save() new_vote.save()
act = UserAction(user=user,show=show,act_type=6 + int(positive),url='%s/episode/%d/%d?submission=%d'%(show.url(),
submission.episode.season.number, submission.episode.episode, submission.pk))
act.save()
return HttpResponseRedirect('%s/episode/%d/%d'%(show.url(), submission.episode.season.number, submission.episode.episode)) return HttpResponseRedirect('%s/episode/%d/%d'%(show.url(), submission.episode.season.number, submission.episode.episode))
@ -351,15 +375,15 @@ def BanFromShowForm(req, abbr):
show = get_object_or_404(Show, abbr=abbr) show = get_object_or_404(Show, abbr=abbr)
user = req.user user = req.user
banTarget = get_object_or_404(User, username=req.GET.get('user', None)) ban_target = get_object_or_404(User, username=req.GET.get('user', None))
if banTarget == user: if ban_target == user:
return HttpResponseForbidden('You cannot ban yourself!') return HttpResponseForbidden('You cannot ban yourself!')
if banTarget.is_staff: if ban_target.is_staff:
return HttpResponseForbidden('You cannot ban a staff member!') return HttpResponseForbidden('You cannot ban a staff member!')
if banTarget.has_perm('LandingPage.can_moderate_show', show): if ban_target.has_perm('LandingPage.can_moderate_show', show):
return HttpResponseForbidden('You cannot ban another moderator!') return HttpResponseForbidden('You cannot ban another moderator!')
form = forms.BanForm() form = forms.BanForm()
@ -368,8 +392,7 @@ def BanFromShowForm(req, abbr):
ctx = { ctx = {
'form': form, 'form': form,
'show': show, 'show': show,
'target': banTarget, 'target': ban_target
'showurl': get_show_url(abbr)
} }
# Handle POST # Handle POST
@ -387,16 +410,19 @@ def BanFromShowForm(req, abbr):
new_ban.expiration = datetime.datetime.now() new_ban.expiration = datetime.datetime.now()
new_ban.site_wide = False new_ban.site_wide = False
new_ban.user = banTarget new_ban.user = ban_target
new_ban.admin = user new_ban.admin = user
new_ban.save() new_ban.save()
# Add show to scope # Add show to scope
new_ban.scope.add(show) new_ban.scope.add(show)
act = UserAction(user=user,show=show,act_type=5,url='/community/user/%d-%s'%(ban_target.pk, slugify(ban_target.display_name)))
act.save()
# Delete all of the user's submissions for this show # Delete all of the user's submissions for this show
if 'delete' in req.POST: if 'delete' in req.POST:
Submission.objects.filter(episode__show=show,user=banTarget).delete() Submission.objects.filter(episode__show=show,user=ban_target).delete()
return HttpResponseRedirect(show.url()) return HttpResponseRedirect(show.url())
else: else:
@ -424,8 +450,7 @@ def ReportSubmission(req, abbr, submission):
'form': form, 'form': form,
'show': show, 'show': show,
'episode': episode, 'episode': episode,
'submission': submission, 'submission': submission
'showurl': get_show_url(abbr)
} }
url = '%s/episode/%d/%d?submission=%s'%(show.url(), episode.season.number, episode.episode, submission.pk) url = '%s/episode/%d/%d?submission=%s'%(show.url(), episode.season.number, episode.episode, submission.pk)
@ -440,7 +465,7 @@ def ReportSubmission(req, abbr, submission):
if not user.has_perm('LandingPage.can_moderate_show', show): if not user.has_perm('LandingPage.can_moderate_show', show):
# Check if there have been many reports by this user within the last 12 hours # Check if there have been many reports by this user within the last 12 hours
if Report.objects.filter(user=user,timestamp__gte=datetime.datetime.now() - datetime.timedelta(hours=12)).count() > 5: if Report.objects.filter(reporter=user,timestamp__gte=datetime.datetime.now() - datetime.timedelta(hours=12)).count() > 5:
ctx['error'] = 'You\'ve created too many reports recently!' ctx['error'] = 'You\'ve created too many reports recently!'
return render(req, "report_reply.html", ctx) return render(req, "report_reply.html", ctx)
@ -460,3 +485,42 @@ def ReportSubmission(req, abbr, submission):
ctx['error'] = 'Invalid fields!' ctx['error'] = 'Invalid fields!'
return render(req, "report.html", ctx) return render(req, "report.html", ctx)
@permission_required_or_403('LandingPage.can_create_show_ban', (Show, 'abbr', 'abbr'), accept_global_perms=True)
def ModDashboard(req, abbr):
show = get_object_or_404(Show, abbr=abbr)
activity = UserAction.objects.filter(show=show).order_by("-timestamp")[:20]
reports = Report.objects.filter(show=show,resolved=False).order_by("-timestamp")
ctx = {}
ctx['actions'] = activity
ctx['reports'] = reports
ctx['show'] = show
return render(req, "mod_dash.html", ctx)
@permission_required_or_403('LandingPage.can_create_show_ban', (Show, 'abbr', 'abbr'), accept_global_perms=True)
def ModReport(req, abbr, repid):
show = get_object_or_404(Show, abbr=abbr)
report = get_object_or_404(Report, pk=repid)
user = req.user
ctx = {}
ctx['report'] = report
ctx['show'] = show
if req.method == 'POST':
if 'delete' in req.POST:
Report.objects.filter(pk=report.pk).delete()
return HttpResponseRedirect('%s/moderator'%(show.url()))
elif 'delete_ban' in req.POST:
Report.objects.filter(pk=report.pk).delete()
return HttpResponseRedirect('%s/create_ban?user=%s'%(show.url(),report.reporter.username))
else:
Report.objects.filter(pk=report.pk).update(read_at=datetime.datetime.now(),read_by=user,resolved=True)
return HttpResponseRedirect('%s/moderator'%(show.url()))
if not report.resolved:
Report.objects.filter(pk=report.pk).update(read_at=datetime.datetime.now(),read_by=user)
return render(req, "mod_report.html", ctx)