NDOJ/judge/views/comment.py

177 lines
6.1 KiB
Python
Raw Permalink Normal View History

2020-01-21 06:35:58 +00:00
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.core.exceptions import PermissionDenied
from django.db import IntegrityError, transaction
from django.db.models import F
from django.forms.models import ModelForm
from django.http import Http404, HttpResponse, HttpResponseBadRequest, HttpResponseForbidden
from django.shortcuts import get_object_or_404
from django.utils.translation import gettext as _
from django.views.decorators.http import require_POST
from django.views.generic import DetailView, UpdateView
from reversion import revisions
from reversion.models import Version
from judge.dblock import LockModel
2020-07-03 02:50:31 +00:00
from judge.models import Comment, CommentVote, Notification
2020-01-21 06:35:58 +00:00
from judge.utils.views import TitleMixin
from judge.widgets import MathJaxPagedownWidget
2020-07-03 02:50:31 +00:00
from judge.comments import add_mention_notifications, del_mention_notifications
2020-01-21 06:35:58 +00:00
__all__ = ['upvote_comment', 'downvote_comment', 'CommentEditAjax', 'CommentContent',
'CommentEdit']
@login_required
def vote_comment(request, delta):
if abs(delta) != 1:
return HttpResponseBadRequest(_('Messing around, are we?'), content_type='text/plain')
if request.method != 'POST':
return HttpResponseForbidden()
if 'id' not in request.POST:
return HttpResponseBadRequest()
if not request.user.is_staff and not request.profile.submission_set.filter(points=F('problem__points')).exists():
return HttpResponseBadRequest(_('You must solve at least one problem before you can vote.'),
content_type='text/plain')
try:
comment_id = int(request.POST['id'])
except ValueError:
return HttpResponseBadRequest()
else:
if not Comment.objects.filter(id=comment_id).exists():
raise Http404()
vote = CommentVote()
vote.comment_id = comment_id
vote.voter = request.profile
vote.score = delta
while True:
try:
vote.save()
except IntegrityError:
with LockModel(write=(CommentVote,)):
try:
vote = CommentVote.objects.get(comment_id=comment_id, voter=request.profile)
except CommentVote.DoesNotExist:
# We must continue racing in case this is exploited to manipulate votes.
continue
if -vote.score != delta:
return HttpResponseBadRequest(_('You already voted.'), content_type='text/plain')
vote.delete()
Comment.objects.filter(id=comment_id).update(score=F('score') - vote.score)
else:
Comment.objects.filter(id=comment_id).update(score=F('score') + delta)
break
return HttpResponse('success', content_type='text/plain')
def upvote_comment(request):
return vote_comment(request, 1)
def downvote_comment(request):
return vote_comment(request, -1)
class CommentMixin(object):
model = Comment
pk_url_kwarg = 'id'
context_object_name = 'comment'
class CommentRevisionAjax(CommentMixin, DetailView):
template_name = 'comments/revision-ajax.html'
def get_context_data(self, **kwargs):
context = super(CommentRevisionAjax, self).get_context_data(**kwargs)
revisions = Version.objects.get_for_object(self.object).order_by('-revision')
try:
wanted = min(max(int(self.request.GET.get('revision', 0)), 0), len(revisions) - 1)
except ValueError:
raise Http404
context['revision'] = revisions[wanted]
return context
def get_object(self, queryset=None):
comment = super(CommentRevisionAjax, self).get_object(queryset)
if comment.hidden and not self.request.user.has_perm('judge.change_comment'):
raise Http404()
return comment
class CommentEditForm(ModelForm):
class Meta:
model = Comment
fields = ['body']
if MathJaxPagedownWidget is not None:
widgets = {'body': MathJaxPagedownWidget(attrs={'id': 'id-edit-comment-body'})}
class CommentEditAjax(LoginRequiredMixin, CommentMixin, UpdateView):
template_name = 'comments/edit-ajax.html'
form_class = CommentEditForm
def form_valid(self, form):
2020-07-03 02:50:31 +00:00
# update notifications
comment = form.instance
del_mention_notifications(comment)
add_mention_notifications(comment)
2020-01-21 06:35:58 +00:00
with transaction.atomic(), revisions.create_revision():
revisions.set_comment(_('Edited from site'))
revisions.set_user(self.request.user)
return super(CommentEditAjax, self).form_valid(form)
def get_success_url(self):
return self.object.get_absolute_url()
def get_object(self, queryset=None):
comment = super(CommentEditAjax, self).get_object(queryset)
if self.request.user.has_perm('judge.change_comment'):
return comment
profile = self.request.profile
if profile != comment.author or profile.mute or comment.hidden:
raise Http404()
return comment
class CommentEdit(TitleMixin, CommentEditAjax):
template_name = 'comments/edit.html'
def get_title(self):
return _('Editing comment')
class CommentContent(CommentMixin, DetailView):
template_name = 'comments/content.html'
class CommentVotesAjax(PermissionRequiredMixin, CommentMixin, DetailView):
template_name = 'comments/votes.html'
permission_required = 'judge.change_commentvote'
def get_context_data(self, **kwargs):
context = super(CommentVotesAjax, self).get_context_data(**kwargs)
context['votes'] = (self.object.votes.select_related('voter__user')
.only('id', 'voter__display_rank', 'voter__user__username', 'score'))
return context
@require_POST
def comment_hide(request):
if not request.user.has_perm('judge.change_comment'):
raise PermissionDenied()
try:
comment_id = int(request.POST['id'])
except ValueError:
return HttpResponseBadRequest()
comment = get_object_or_404(Comment, id=comment_id)
comment.get_descendants(include_self=True).update(hidden=True)
return HttpResponse('ok')