Skip to content
Snippets Groups Projects
Verified Commit 252fdd27 authored by Jonathan Weth's avatar Jonathan Weth :keyboard:
Browse files

Make comments function usable with review process

parent 7978400e
No related branches found
No related tags found
No related merge requests found
......@@ -8,9 +8,10 @@ from django.http import HttpRequest
from django.utils.translation import gettext as _
from django_select2.forms import ModelSelect2MultipleWidget, ModelSelect2Widget
from guardian.shortcuts import get_objects_for_user
from material import Fieldset, Layout, Row
from aleksis.core.models import Group
from aleksis.core.models import Group, Notification
from .models import AbiGroup, AbiPerson, Comment, ProfileFieldValue, Quote, Ranking, RankingCategory
......@@ -256,14 +257,58 @@ class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ["person", "text"]
widgets = {
"text": forms.Textarea(attrs={"rows": 4}),
"person": ModelSelect2Widget(
search_fields=["name__icontains"],
attrs={"data-minimum-input-length": 0, "class": "browser-default"},
),
}
def __init__(self, *args, **kwargs):
self.person = kwargs.pop("person")
self.edit = kwargs.pop("edit") if "edit" in kwargs else False
super().__init__(*args, **kwargs)
self.allowed_groups = get_objects_for_user(
self.person.user, "abi.write_comment_for", klass=AbiGroup
)
self.allowed_persons = (
AbiPerson.objects.filter(member_of__in=self.allowed_groups)
.exclude(pk=self.person.pk)
.distinct()
)
self.allowed_groups_with_review = get_objects_for_user(
self.person.user, "abi.write_comment_for_with_review", klass=AbiGroup
)
self.allowed_persons_with_review = (
AbiPerson.objects.filter(member_of__in=self.allowed_groups_with_review)
.exclude(pk=self.person.pk)
.distinct()
)
allowed_persons = self.allowed_persons | self.allowed_persons_with_review
self.fields["person"].queryset = allowed_persons
if self.edit:
self.fields["person"].disabled = True
def save(self, commit=True):
instance = super().save(commit=False)
instance.author = self.person.abi_person
if instance.person in self.allowed_persons_with_review:
instance.needs_review = True
n = Notification(
sender=_("Abi magazine"),
recipient=instance.person.person,
title=_("There are comments to review."),
description=_(
"There are new comments for your profile"
" page in the abi magazine we need you to review."
" Please decide whether you want to use the comment"
" or not by using the menu item 'Abi magazine > Comments'."
),
)
n.save()
if commit:
instance.save()
return instance
......
......@@ -114,7 +114,7 @@ MENUS = {
"validators": [
(
"aleksis.core.util.predicates.permission_validator",
"abi.view_own_comments_rule",
"abi.view_comments_rule",
),
],
},
......
# Generated by Django 3.2.8 on 2021-11-04 16:52
import django.contrib.sites.managers
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('abi', '0012_alter_profilefieldvalue_profile'),
]
operations = [
migrations.AlterModelOptions(
name='profile',
options={'permissions': (('use_profile', 'Can fill their profile'),), 'verbose_name': 'Personal profile', 'verbose_name_plural': 'Personal profiles'},
),
migrations.AlterModelOptions(
name='ranking',
options={'permissions': (('rank_persons', 'Can rank persons'),), 'verbose_name': 'Ranking', 'verbose_name_plural': 'Rankings'},
),
migrations.AlterModelManagers(
name='abiperson',
managers=[
],
),
migrations.AlterModelManagers(
name='ranking',
managers=[
('objects', django.contrib.sites.managers.CurrentSiteManager()),
],
),
migrations.RemoveField(
model_name='comment',
name='requested',
),
migrations.AddField(
model_name='comment',
name='accepted',
field=models.BooleanField(default=False, verbose_name='Accepted'),
),
migrations.AddField(
model_name='comment',
name='reviewed',
field=models.BooleanField(default=False, verbose_name='Reviewed'),
),
migrations.AlterField(
model_name='additionalprofilefield',
name='profile',
field=models.ManyToManyField(related_name='additional_fields', through='abi.ProfileFieldValue', to='abi.Profile', verbose_name='Additional Fields'),
),
migrations.AlterField(
model_name='comment',
name='person',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='received_comments', to='abi.abiperson', verbose_name='Recipient'),
),
migrations.AlterField(
model_name='comment',
name='text',
field=models.TextField(blank=True, help_text='This text field supports multiple lines.', verbose_name='Comment'),
),
migrations.AlterField(
model_name='profilefieldvalue',
name='field',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='values', to='abi.additionalprofilefield', verbose_name='Fields'),
),
migrations.AlterModelOptions(
name='abigroup',
options={'permissions': [('write_comment_for', 'Can write a comment for a person in this group'), ('write_comment_for_with_review', 'Can write a comment for a person in this group which has to be reviewed by the person')], 'verbose_name': 'Group (Abi)', 'verbose_name_plural': 'Groups (Abi)'},
),
migrations.AddField(
model_name='comment',
name='needs_review',
field=models.BooleanField(default=False, verbose_name='Needs review'),
),
]
......@@ -99,6 +99,16 @@ class AbiGroup(ExtensibleModel):
class Meta:
verbose_name = _("Group (Abi)")
verbose_name_plural = _("Groups (Abi)")
permissions = [
("write_comment_for", _("Can write a comment for a person in this group")),
(
"write_comment_for_with_review",
_(
"Can write a comment for a person in this group"
" which has to be reviewed by the person"
),
),
]
class Profile(ExtensibleModel):
......@@ -295,8 +305,17 @@ class Comment(ExtensibleModel):
verbose_name=_("Author"),
related_name="written_comments",
)
text = models.TextField(blank=True, verbose_name=_("Comment"))
requested = models.BooleanField(default=False, verbose_name=_("Requested"))
text = models.TextField(
blank=True,
verbose_name=_("Comment"),
help_text=_("This text field supports multiple lines."),
)
needs_review = models.BooleanField(default=False, verbose_name=_("Needs review"))
reviewed = models.BooleanField(default=False, verbose_name=_("Reviewed"))
accepted = models.BooleanField(default=False, verbose_name=_("Accepted"))
def __str__(self):
return self.text
class Meta:
verbose_name = _("Comment")
......
from rules import add_perm, predicate
from aleksis.apps.abi.models import Quote
from aleksis.apps.abi.models import AbiGroup, Comment, Quote
from aleksis.core.util.predicates import (
has_any_object,
has_global_perm,
......@@ -93,25 +93,52 @@ add_perm("abi.export_rule", export_predicate)
rank_persons_predicate = has_person & has_abi_person & has_global_perm("abi.rank_persons")
add_perm("abi.rank_persons_rule", rank_persons_predicate)
# View own comments
view_own_comments_predicate = has_person & has_abi_person
add_perm("abi.view_own_comments_rule", view_own_comments_predicate)
# View all comments
view_all_comments_predicate = has_person & has_global_perm("abi.view_comment")
add_perm("abi.view_all_comments_rule", view_all_comments_predicate)
# Add comment
add_comment_predicate = view_own_comments_predicate & has_global_perm("abi.add_comment")
add_comment_predicate = (
has_person
& has_abi_person
& (
has_global_perm("abi.add_comment")
| has_any_object("abi.write_comment_for", AbiGroup)
| has_any_object("abi.write_comment_for_with_review", AbiGroup)
)
)
add_perm("abi.add_comment_rule", add_comment_predicate)
# View own comments
view_comments_predicate = add_comment_predicate | (
has_person & has_abi_person & has_any_object("abi.view_comment", Comment)
)
add_perm("abi.view_comments_rule", view_comments_predicate)
@predicate
def comment_is_for_person(obj, user):
return obj.person == user.person.abi_person
@predicate
def is_reviewable(obj, user):
return obj.needs_review and not obj.reviewed
review_comment_predicate = has_person & has_abi_person & comment_is_for_person & is_reviewable
add_perm("abi.review_comment_rule", review_comment_predicate)
# Edit comment
edit_comment_predicate = view_own_comments_predicate & has_global_perm("abi.change_comment")
edit_comment_predicate = view_comments_predicate & (
has_global_perm("abi.change_comment") | has_object_perm("abi.change_comment")
)
add_perm("abi.edit_comment_rule", edit_comment_predicate)
# Delete comment
delete_comment_predicate = view_own_comments_predicate & has_global_perm("abi.delete_comment")
delete_comment_predicate = view_comments_predicate & (
has_global_perm("abi.delete_comment") | has_object_perm("abi.delete_comment")
)
add_perm("abi.delete_comment_rule", delete_comment_predicate)
# Add quotes
create_quote_rule = has_person & has_global_perm("abi.create_quote")
add_perm("abi.create_quote_rule", create_quote_rule)
......@@ -157,5 +184,6 @@ add_perm(
| edit_profile_questions_predicate
| view_quotes_predicate
| suggest_quote_predicate
| view_comments_predicate
),
)
......@@ -105,8 +105,7 @@ class CommentTable(tables.Table):
author = tables.Column()
person = tables.Column()
text = tables.LinkColumn("edit_comment", args=[tables.A("id")])
requested = tables.Column()
text = tables.Column()
edit = tables.LinkColumn(
"edit_comment",
args=[tables.A("id")],
......@@ -142,9 +141,14 @@ class QuotesTable(tables.Table):
)
class ReceivedCommentTable(CommentTable):
class ReviewCommentTable(CommentTable):
class Meta:
exclude = ("person", "edit")
exclude = ("person", "edit", "delete")
review = tables.Column(accessor=tables.A("pk"), verbose_name=_("Review status"))
def render_review(self, value, record):
return render_to_string("abi/comment/review_column.html", dict(comment=record))
class WrittenCommentTable(CommentTable):
......
{# -*- engine:django -*- #}
{% extends "core/base.html" %}
{% load material_form i18n %}
{% load material_form i18n any_js %}
{% block browser_title %}{% blocktrans %}Create comment{% endblocktrans %}{% endblock %}
{% block page_title %}{% blocktrans %}Create comment{% endblocktrans %}{% endblock %}
{% block browser_title %}{% blocktrans %}Write a comment{% endblocktrans %}{% endblock %}
{% block page_title %}{% blocktrans %}Write a comment{% endblocktrans %}{% endblock %}
{% block extra_head %}
{{ form.media.css }}
{% include_css "select2-materialize" %}
{% endblock %}
{% block content %}
<form method="post">
{% csrf_token %}
{% form form=form %}{% endform %}
{% include "core/partials/save_button.html" %}
</form>
<form method="post">
{% csrf_token %}
{% form form=form %}{% endform %}
{% include "core/partials/save_button.html" %}
</form>
{% include_js "select2-materialize" %}
{{ form.media.js }}
{% endblock %}
{# -*- engine:django -*- #}
{% extends "core/base.html" %}
{% load material_form i18n %}
{% load material_form i18n any_js %}
{% block browser_title %}{% blocktrans %}Edit comment{% endblocktrans %}{% endblock %}
{% block page_title %}{% blocktrans %}Edit comment{% endblocktrans %}{% endblock %}
{% block extra_head %}
{{ form.media.css }}
{% include_css "select2-materialize" %}
{% endblock %}
{% block content %}
<form method="post">
{% csrf_token %}
{% form form=form %}{% endform %}
{% include "core/partials/save_button.html" %}
</form>
<form method="post">
{% csrf_token %}
{% form form=form %}{% endform %}
{% include "core/partials/save_button.html" %}
</form>
{% include_js "select2-materialize" %}
{{ form.media.js }}
{% endblock %}
......@@ -17,15 +17,19 @@
<div class="row">
<div class="col s12">
<ul class="tabs">
{% if tables.0.rows %}
<li class="tab col s3">
<a href="#received-comments">{% trans "Comments to review" %}</a>
</li>
{% endif %}
<li class="tab col s3">
<a href="#received-comments">Received comments</a>
</li>
<li class="tab col s3">
<a href="#written-comments">Written comments</a>
<a href="#written-comments">{% trans "Written comments" %}</a>
</li>
</ul>
</div>
<div id="received-comments" class="col s12">{% render_table tables.0 %}</div>
{% if tables.0.rows %}
<div id="received-comments" class="col s12">{% render_table tables.0 %}</div>
{% endif %}
<div id="written-comments" class="col s12">{% render_table tables.1 %}</div>
</div>
{% endblock %}
{% load i18n %}
{% if comment.reviewed %}
{% if comment.accepted %}
<i class="material-icons left green-text">check_circle</i>
{% trans "Use" %}
{% else %}
<i class="material-icons left red-text">check_circle</i>
{% trans "Don't use" %}
{% endif %}
{% else %}
<a class="btn waves-effect waves-light green" href="{% url "review_comment_yes" comment.pk %}">
<i class="material-icons left">check</i>
{% trans "Use" %}
</a>
<a class="btn waves-effect waves-light red" href="{% url "review_comment_no" comment.pk %}">
<i class="material-icons left">close</i>
{% trans "Don't use" %}
</a>
{% endif %}
\ No newline at end of file
......@@ -35,6 +35,18 @@ urlpatterns = [
path("comments/create/", views.CommentCreateView.as_view(), name="create_comment",),
path("comments/<int:pk>/edit/", views.CommentEditView.as_view(), name="edit_comment",),
path("comments/<int:pk>/delete/", views.CommentDeleteView.as_view(), name="delete_comment",),
path(
"comments/<int:pk>/review/yes/",
views.CommentReviewView.as_view(),
{"accept": True},
name="review_comment_yes",
),
path(
"comments/<int:pk>/review/no/",
views.CommentReviewView.as_view(),
{"accept": False},
name="review_comment_no",
),
path("profile/", views.ProfileView.as_view(), name="update_profile"),
path("profile/configure/", views.ConfigureProfileView.as_view(), name="configure_profile"),
path("quotes/my/", views.MyQuotesListView.as_view(), name="my_quotes"),
......
......@@ -3,11 +3,14 @@ from django.core.exceptions import PermissionDenied
from django.db.models import Count, OuterRef, Q, Subquery
from django.forms import modelformset_factory
from django.http import HttpResponse
from django.shortcuts import redirect
from django.urls import reverse, reverse_lazy
from django.utils.decorators import method_decorator
from django.utils.translation import gettext as _
from django.views import View
from django.views.decorators.cache import never_cache
from django.views.generic import FormView, TemplateView
from django.views.generic.detail import SingleObjectMixin
from csv_export.views import CSVExportView
from django_tables2 import MultiTableMixin, SingleTableView
......@@ -51,7 +54,7 @@ from .tables import (
MyQuotesTable,
QuotesTable,
RankingCategoryTable,
ReceivedCommentTable,
ReviewCommentTable,
WrittenCommentTable,
)
......@@ -236,18 +239,48 @@ class CommentListView(PermissionRequiredMixin, MultiTableMixin, SingleTableView)
"""Tables of all comments."""
model = Comment
permission_required = "abi.view_own_comments_rule"
permission_required = "abi.view_comments_rule"
template_name = "abi/comment/list.html"
tables = [ReceivedCommentTable, WrittenCommentTable]
tables = [ReviewCommentTable, WrittenCommentTable]
def get_tables_data(self):
qs = super().get_queryset()
return [
qs.filter(person=self.request.user.person.abi_person),
qs.filter(person=self.request.user.person.abi_person, needs_review=True),
qs.filter(author=self.request.user.person.abi_person),
]
@method_decorator(never_cache, name="dispatch")
class CommentReviewView(PermissionRequiredMixin, SingleObjectMixin, View):
model = Comment
permission_required = "abi.review_comment_rule"
def get(self, request, *args, **kwargs):
self.object = self.get_object()
if self.object.reviewed:
return redirect("comments")
if kwargs["accept"]:
self.object.accepted = True
messages.success(
request,
_("Ok, we will use this comment for your profile page in the abi magazine."),
)
else:
self.object.accepted = False
messages.success(
request,
_("Ok, we won't use this comment for your profile page in the abi magazine."),
)
self.object.reviewed = True
self.object.save()
return redirect("comments")
@method_decorator(never_cache, name="dispatch")
class CommentCreateView(PermissionRequiredMixin, AdvancedCreateView):
"""Create view for comments."""
......@@ -264,6 +297,15 @@ class CommentCreateView(PermissionRequiredMixin, AdvancedCreateView):
kwargs["person"] = self.request.user.person
return kwargs
def form_valid(self, form):
r = super().form_valid(form)
assign_perm("abi.view_comment", self.object.author.person.user, self.object)
assign_perm("abi.change_comment", self.object.author.person.user, self.object)
assign_perm("abi.delete_comment", self.object.author.person.user, self.object)
if self.object.needs_review:
assign_perm("abi.view_comment", self.object.person.person.user, self.object)
return r
@method_decorator(never_cache, name="dispatch")
class CommentEditView(PermissionRequiredMixin, AdvancedEditView):
......@@ -279,6 +321,7 @@ class CommentEditView(PermissionRequiredMixin, AdvancedEditView):
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["person"] = self.request.user.person
kwargs["edit"] = True
return kwargs
......@@ -289,7 +332,7 @@ class CommentDeleteView(PermissionRequiredMixin, RevisionMixin, AdvancedDeleteVi
model = Comment
permission_required = "abi.delete_comment_rule"
template_name = "core/pages/delete.html"
success_url = reverse_lazy("requested_comments")
success_url = reverse_lazy("comments")
success_message = _("The comment has been deleted.")
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment