NDOJ/judge/comments.py

185 lines
6.7 KiB
Python
Raw Normal View History

2020-01-21 06:35:58 +00:00
from django import forms
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError
from django.db.models import Count
from django.db.models.expressions import F, Value
from django.db.models.functions import Coalesce
from django.forms import ModelForm
2022-05-14 17:57:27 +00:00
from django.http import (
HttpResponseForbidden,
HttpResponseNotFound,
HttpResponseRedirect,
)
2020-01-21 06:35:58 +00:00
from django.urls import reverse_lazy
from django.utils.decorators import method_decorator
from django.utils.translation import gettext as _
from django.views.generic import View
from django.views.generic.base import TemplateResponseMixin
from django.views.generic.detail import SingleObjectMixin
from reversion import revisions
from reversion.models import Revision, Version
from judge.dblock import LockModel
2020-07-03 02:50:31 +00:00
from judge.models import Comment, CommentLock, CommentVote, Notification
2020-01-21 06:35:58 +00:00
from judge.utils.raw_sql import RawSQLColumn, unique_together_left_join
from judge.widgets import HeavyPreviewPageDownWidget
2020-07-03 02:50:31 +00:00
from judge.jinja2.reference import get_user_from_text
def add_mention_notifications(comment):
user_referred = get_user_from_text(comment.body).exclude(id=comment.author.id)
for user in user_referred:
2022-05-14 17:57:27 +00:00
notification_ref = Notification(owner=user, comment=comment, category="Mention")
2020-07-03 02:50:31 +00:00
notification_ref.save()
2022-05-14 17:57:27 +00:00
2020-07-03 02:50:31 +00:00
def del_mention_notifications(comment):
2022-05-14 17:57:27 +00:00
query = {"comment": comment, "category": "Mention"}
2020-07-03 02:50:31 +00:00
Notification.objects.filter(**query).delete()
2020-01-21 06:35:58 +00:00
class CommentForm(ModelForm):
class Meta:
model = Comment
2022-05-14 17:57:27 +00:00
fields = ["body", "parent"]
2020-01-21 06:35:58 +00:00
widgets = {
2022-05-14 17:57:27 +00:00
"parent": forms.HiddenInput(),
2020-01-21 06:35:58 +00:00
}
if HeavyPreviewPageDownWidget is not None:
2022-05-14 17:57:27 +00:00
widgets["body"] = HeavyPreviewPageDownWidget(
preview=reverse_lazy("comment_preview"),
preview_timeout=1000,
hide_preview_button=True,
)
2020-01-21 06:35:58 +00:00
def __init__(self, request, *args, **kwargs):
self.request = request
super(CommentForm, self).__init__(*args, **kwargs)
2022-05-14 17:57:27 +00:00
self.fields["body"].widget.attrs.update({"placeholder": _("Comment body")})
2020-01-21 06:35:58 +00:00
def clean(self):
if self.request is not None and self.request.user.is_authenticated:
profile = self.request.profile
if profile.mute:
2022-05-14 17:57:27 +00:00
raise ValidationError(_("Your part is silent, little toad."))
elif (
not self.request.user.is_staff
and not profile.submission_set.filter(
points=F("problem__points")
).exists()
):
raise ValidationError(
_(
"You need to have solved at least one problem "
"before your voice can be heard."
)
)
2020-01-21 06:35:58 +00:00
return super(CommentForm, self).clean()
class CommentedDetailView(TemplateResponseMixin, SingleObjectMixin, View):
comment_page = None
def get_comment_page(self):
if self.comment_page is None:
raise NotImplementedError()
return self.comment_page
def is_comment_locked(self):
2022-05-14 17:57:27 +00:00
if self.request.user.has_perm("judge.override_comment_lock"):
2021-12-17 21:46:13 +00:00
return False
2022-05-14 17:57:27 +00:00
return CommentLock.objects.filter(page=self.get_comment_page()).exists() or (
self.request.in_contest
and self.request.participation.contest.use_clarifications
)
2020-01-21 06:35:58 +00:00
@method_decorator(login_required)
def post(self, request, *args, **kwargs):
self.object = self.get_object()
page = self.get_comment_page()
if self.is_comment_locked():
return HttpResponseForbidden()
2022-05-14 17:57:27 +00:00
parent = request.POST.get("parent")
2020-01-21 06:35:58 +00:00
if parent:
try:
parent = int(parent)
except ValueError:
return HttpResponseNotFound()
else:
2022-05-14 17:57:27 +00:00
if not Comment.objects.filter(
hidden=False, id=parent, page=page
).exists():
2020-01-21 06:35:58 +00:00
return HttpResponseNotFound()
form = CommentForm(request, request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.author = request.profile
comment.page = page
2020-07-03 02:50:31 +00:00
2022-05-14 17:57:27 +00:00
with LockModel(
write=(Comment, Revision, Version), read=(ContentType,)
), revisions.create_revision():
2020-01-21 06:35:58 +00:00
revisions.set_user(request.user)
2022-05-14 17:57:27 +00:00
revisions.set_comment(_("Posted comment"))
2020-01-21 06:35:58 +00:00
comment.save()
2020-07-03 02:50:31 +00:00
# add notification for reply
if comment.parent and comment.parent.author != comment.author:
2022-05-14 17:57:27 +00:00
notification_rep = Notification(
owner=comment.parent.author, comment=comment, category="Reply"
)
2020-07-03 02:50:31 +00:00
notification_rep.save()
add_mention_notifications(comment)
2022-05-14 17:57:27 +00:00
2020-01-21 06:35:58 +00:00
return HttpResponseRedirect(request.path)
context = self.get_context_data(object=self.object, comment_form=form)
return self.render_to_response(context)
def get(self, request, *args, **kwargs):
self.object = self.get_object()
2022-05-14 17:57:27 +00:00
return self.render_to_response(
self.get_context_data(
object=self.object,
comment_form=CommentForm(
request, initial={"page": self.get_comment_page(), "parent": None}
),
)
)
2020-01-21 06:35:58 +00:00
def get_context_data(self, **kwargs):
context = super(CommentedDetailView, self).get_context_data(**kwargs)
queryset = Comment.objects.filter(hidden=False, page=self.get_comment_page())
2022-05-14 17:57:27 +00:00
context["has_comments"] = queryset.exists()
context["comment_lock"] = self.is_comment_locked()
queryset = (
queryset.select_related("author__user")
.defer("author__about")
.annotate(revisions=Count("versions"))
)
2020-01-21 06:35:58 +00:00
if self.request.user.is_authenticated:
2022-05-14 17:57:27 +00:00
queryset = queryset.annotate(
vote_score=Coalesce(RawSQLColumn(CommentVote, "score"), Value(0))
)
2020-01-21 06:35:58 +00:00
profile = self.request.profile
2022-05-14 17:57:27 +00:00
unique_together_left_join(
queryset, CommentVote, "comment", "voter", profile.id
)
context["is_new_user"] = (
not self.request.user.is_staff
and not profile.submission_set.filter(
points=F("problem__points")
).exists()
)
context["comment_list"] = queryset
context["vote_hide_threshold"] = settings.DMOJ_COMMENT_VOTE_HIDE_THRESHOLD
2020-01-21 06:35:58 +00:00
return context