Ajout de groupes et raffinement des permissions

This commit is contained in:
Jean-Marie Favreau 2024-04-01 11:51:00 +02:00
parent 0b67454824
commit 7b51236f06
12 changed files with 161 additions and 18 deletions

View File

@ -516,7 +516,7 @@ msgstr ""
#: agenda_culturel/views.py:350
msgid "Your message has been sent successfully."
msgstr "L'événement a été supprimé avec succès"
msgstr "Votre message a été envoyé avec succès."
#: agenda_culturel/views.py:358
msgid "The contact message properties has been successfully modified."

View File

@ -0,0 +1,29 @@
# Generated by Django 4.2.7 on 2024-03-31 16:15
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('agenda_culturel', '0036_auto_20240331_1421'),
]
operations = [
migrations.AlterModelOptions(
name='batchimportation',
options={'permissions': [('run_batchimportation', 'Can run a batch importation')]},
),
migrations.AlterModelOptions(
name='categorisationrule',
options={'permissions': [('apply_categorisationrules', 'Apply a categorisation rule')]},
),
migrations.AlterModelOptions(
name='event',
options={'permissions': [('set_duplicated_event', 'Can set an event as duplicated')], 'verbose_name': 'Event', 'verbose_name_plural': 'Events'},
),
migrations.AlterModelOptions(
name='recurrentimport',
options={'permissions': [('run_recurrentimport', 'Can run a recurrent import')]},
),
]

View File

@ -0,0 +1,45 @@
# Generated by Django 4.2.7 on 2024-03-31 16:15
from django.db import migrations
from django.contrib.auth.management import create_permissions
from django.contrib.auth.models import Group, Permission
def update_groups_permissions(apps, schema_editor):
# first add a missing role
user_roles = ["Moderator"]
for name in user_roles:
Group.objects.create(name=name)
all_perms = Permission.objects.all()
# set permissions for moderators
moderator_perms = [i for i in all_perms if i.content_type.app_label == 'agenda_culturel' and i.content_type.model in ['event', 'duplicatedevents']]
Group.objects.get(name="Moderator").permissions.add(*moderator_perms)
read_mod_perms = [i for i in moderator_perms if i.codename.startswith('view_')]
# set permissions for automation managers
automanager_perms = [i for i in all_perms if i.content_type.app_label == 'agenda_culturel' and i.content_type.model in ['batchimportation', 'recurrentimport', 'categorisationrule']]
Group.objects.get(name="Automation Manager").permissions.add(*automanager_perms)
Group.objects.get(name="Automation Manager").permissions.add(*read_mod_perms)
# set permissions for receptionists
receptionist_perms = [i for i in all_perms if i.content_type.app_label == 'agenda_culturel' and i.content_type.model in ['contactmessage']]
Group.objects.get(name="Receptionist").permissions.add(*receptionist_perms)
Group.objects.get(name="Receptionist").permissions.add(*read_mod_perms)
class Migration(migrations.Migration):
dependencies = [
('agenda_culturel', '0037_alter_batchimportation_options_and_more'),
]
operations = [
migrations.RunPython(update_groups_permissions),
]

View File

@ -237,6 +237,7 @@ class Event(models.Model):
class Meta:
verbose_name = _('Event')
verbose_name_plural = _('Events')
permissions = [("set_duplicated_event", "Can set an event as duplicated")]
def get_all_tags():
try:
@ -737,6 +738,10 @@ class ContactMessage(models.Model):
class RecurrentImport(models.Model):
class Meta:
permissions = [("run_recurrentimport", "Can run a recurrent import")]
class PROCESSOR(models.TextChoices):
ICAL = "ical", _("ical")
ICALNOBUSY = "icalnobusy", _("ical no busy")
@ -781,6 +786,9 @@ class BatchImportation(models.Model):
SUCCESS = "success", _("Success")
FAILED = "failed", _("Failed")
class Meta:
permissions = [("run_batchimportation", "Can run a batch importation")]
created_date = models.DateTimeField(auto_now_add=True)
@ -810,6 +818,10 @@ class CategorisationRule(models.Model):
title_contains = models.CharField(verbose_name=_('Contained in the title'), help_text=_('Text contained in the event title'), max_length=512, blank=True, null=True)
title_exact = models.BooleanField(verbose_name=_('Exact title extract'), help_text=_("If checked, the extract will be searched for in the title using the exact form (capitals, accents)."), default=False)
class Meta:
permissions = [("apply_categorisationrules", "Apply a categorisation rule")]
# on applique toutes les règles, de la première à la dernière
def apply_rules(event):
rules = CategorisationRule.objects.all().order_by("weight", "pk")

View File

@ -27,12 +27,14 @@
<li>{{ e.start_day }}{% if e.start_time %} à {{ e.start_time }}{% endif %}&nbsp;: <a href="{{ e.get_absolute_url }}">{{ e.title }}</a> créé le {{ e.created_date }}</li>
{% endfor %}
</ul>
{% if perms.agenda_culturel.change_duplicatedevents %}
<footer class="infos-and-buttons">
<div class="infos"></div>
<div class="buttons">
<a role="button" href="{% url 'fix_duplicate' obj.pk %}">Corriger {% picto_from_name "tool" %}</a>
</div>
</footer>
{% endif %}
</article>
{% endwith %}
{% endfor %}

View File

@ -46,10 +46,16 @@
</a>
</li>
<li>
{% if user.is_authenticated %}
{% if perms.agenda_culturel.change_event %}
{% show_badges_events "bottom" %}
{% endif %}
{% if perms.agenda_culturel.change_duplicatedevents %}
{% show_badge_duplicated "bottom" %}
{% endif %}
{% if perms.agenda_culturel.view_contactmessage %}
{% show_badge_contactmessages "bottom" %}
{% endif %}
{% if user.is_authenticated %}
{{ user.username }} @
{% endif %}
<a href="{% url 'home' %}" aria-label="Retour accueil">Pommes de lune</a>

View File

@ -9,31 +9,48 @@
<h3>Événements</h3>
<nav>
<ul>
{% if perms.agenda_culturel.change_event %}
<li><a {% if current == "moderation" %}class="selected" {% endif %}href="{% url 'moderation' %}">Derniers événements soumis</a>{% show_badges_events "left" %}</li>
{% endif %}
{% if perms.agenda_culturel.change_duplicatedevents %}
<li><a {% if current == "duplicates" %}class="selected" {% endif %}href="{% url 'duplicates' %}">Gestion des doublons</a>{% show_badge_duplicated "left" %}</li>
{% endif %}
<li><a {% if current == "tags" %}class="selected" {% endif %}href="{% url 'view_all_tags' %}">Consulter les étiquettes</a></li>
</ul>
</nav>
{% if perms.agenda_culturel.view_batchimportation or perms.agenda_culturel.view_recurrentimport or perms.agenda_culturel.view_categorisationrule%}
<h3>Traitements automatiques</h3>
<nav>
<ul>
{% if perms.agenda_culturel.view_batchimportation %}
<li><a {% if current == "imports" %}class="selected" {% endif %}href="{% url 'imports' %}">Historiques des importations</a></li>
{% endif %}
{% if perms.agenda_culturel.view_recurrentimport %}
<li><a {% if current == "rimports" %}class="selected" {% endif %}href="{% url 'recurrent_imports' %}">Importations récurrentes</a></li>
{% endif %}
{% if perms.agenda_culturel.view_categorisationrule %}
<li><a {% if current == "catrules" %}class="selected" {% endif %}href="{% url 'categorisation_rules' %}">Règles de catégorisation</a></li>
{% endif %}
</ul>
</nav>
{% endif %}
{% if perms.agenda_culturel.view_contactmessage %}
<h3>Messages</h3>
<nav>
<ul>
<li><a {% if current == "contactmessages" %}class="selected" {% endif %}href="{% url 'contactmessages' %}">Messages de contact</a>{% show_badge_contactmessages "left" %}</li>
</ul>
</nav>
{% endif %}
{% if user.is_staff %}
<h3>Configuration interne</h3>
<nav>
<ul>
<li><a href="{% url 'admin:index' %}">Administration de django</a></li>
</ul>
</nav>
{% endif %}
</article>
<article>
<p>Vous êtes connecté(e) en tant que {{ user }}.</p>

View File

@ -37,7 +37,7 @@
{% if event.description_hl %}{{ event.description_hl | safe }} [...]{% else %}{% if event.description %}{{ event.description |truncatewords:60 }}{% else %}<em>pas de description</em>{% endif %}{% endif %}
</div>
{% if user.is_authenticated %}
{% if perms.agenda_culturel.change_event %}
<footer>
<div class="buttons">
{% include "agenda_culturel/edit-buttons-inc.html" with event=event %}

View File

@ -77,7 +77,7 @@
</p>
{% endif %}
</div>
{% if user.is_authenticated %}
{% if perms.agenda_culturel.change_event %}
<div class="buttons">
{% include "agenda_culturel/edit-buttons-inc.html" with event=event %}
<a href="{{ event.get_absolute_url }}" role="button">Voir l'événement <svg width="1em" height="1em" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">

View File

@ -56,7 +56,7 @@
<p><em>À notre connaissance, cet événement n'est pas référencé autre part sur internet.</em></p>
{% endif %}
</div>
{% if user.is_authenticated %}
{% if perms.agenda_culturel.change_event %}
<div class="buttons">
{% include "agenda_culturel/edit-buttons-inc.html" with event=event %}
<a href="{{ event.get_absolute_url }}" role="button">Voir l'événement <svg width="1em" height="1em" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">

View File

@ -67,7 +67,7 @@
{% endif %}
</p>
</div>
{% if user.is_authenticated %}
{% if perms.agenda_culturel.change_event %}
<div class="buttons">
{% include "agenda_culturel/edit-buttons-inc.html" with event=event %}
</div>

View File

@ -1,7 +1,7 @@
from django.shortcuts import render, get_object_or_404
from django.views.generic import ListView, DetailView, FormView
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin, PermissionRequiredMixin
from django.http import QueryDict
from django import forms
from django.contrib.postgres.search import SearchQuery, SearchHeadline
@ -24,7 +24,7 @@ from django.utils.translation import gettext_lazy as _
from django.utils.translation import activate, get_language_info
import django_filters
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib import messages
from django.contrib.messages.views import SuccessMessageMixin
@ -208,6 +208,7 @@ def tag_list(request):
class StaticContentCreateView(LoginRequiredMixin, CreateView):
model = StaticContent
fields = ['text']
permission_required = ("agenda_culturel.add_staticcontent")
def form_valid(self, form):
form.instance.name = self.request.GET["name"]
@ -215,14 +216,16 @@ class StaticContentCreateView(LoginRequiredMixin, CreateView):
return super().form_valid(form)
class StaticContentUpdateView(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
class StaticContentUpdateView(SuccessMessageMixin, PermissionRequiredMixin, LoginRequiredMixin, UpdateView):
model = StaticContent
permission_required = ("agenda_culturel.change_staticcontent")
fields = ['text']
success_message = _('The static content has been successfully updated.')
class EventUpdateView(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
class EventUpdateView(SuccessMessageMixin, PermissionRequiredMixin, LoginRequiredMixin, UpdateView):
model = Event
permission_required = ("agenda_culturel.changes_event")
form_class = EventForm
success_message = _('The event has been successfully modified.')
@ -232,8 +235,9 @@ class EventUpdateView(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
return kwargs
class EventDeleteView(SuccessMessageMixin, LoginRequiredMixin, DeleteView):
class EventDeleteView(SuccessMessageMixin, PermissionRequiredMixin, LoginRequiredMixin, DeleteView):
model = Event
permission_required = ("agenda_culturel.delete_event")
success_url = reverse_lazy('moderation')
success_message = _('The event has been successfully deleted.')
@ -256,6 +260,7 @@ class EventDetailView(UserPassesTestMixin, DetailView):
@login_required(login_url="/accounts/login/")
@permission_required('agenda_culturel.change_event')
def change_status_event(request, pk, status):
event = get_object_or_404(Event, pk=pk)
@ -368,8 +373,9 @@ class ContactMessageCreateView(SuccessMessageMixin, CreateView):
success_message = _('Your message has been sent successfully.')
class ContactMessageUpdateView(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
class ContactMessageUpdateView(SuccessMessageMixin, PermissionRequiredMixin, LoginRequiredMixin, UpdateView):
model = ContactMessage
permission_required = ("agenda_culturel.change_contactmessage")
template_name = "agenda_culturel/contactmessage_moderation_form.html"
fields = ('closed', 'comments')
@ -396,6 +402,7 @@ class ContactMessagesFilterAdmin(django_filters.FilterSet):
@login_required(login_url="/accounts/login/")
@permission_required('agenda_culturel.view_event')
def moderation(request):
filter = EventFilterAdmin(request.GET, queryset=Event.objects.all().order_by("-created_date"))
paginator = Paginator(filter.qs, 10)
@ -411,6 +418,7 @@ def moderation(request):
return render(request, 'agenda_culturel/moderation.html', {'filter': filter, 'paginator_filter': response} )
@login_required(login_url="/accounts/login/")
@permission_required('agenda_culturel.view_contactmessage')
def contactmessages(request):
filter = ContactMessagesFilterAdmin(request.GET, queryset=ContactMessage.objects.all().order_by("-date"))
paginator = Paginator(filter.qs, 10)
@ -518,6 +526,7 @@ def event_search_full(request):
#########################
@login_required(login_url="/accounts/login/")
@permission_required('agenda_culturel.view_batchimportation')
def imports(request):
paginator = Paginator(BatchImportation.objects.all().order_by("-created_date"), 10)
page = request.GET.get('page')
@ -533,6 +542,8 @@ def imports(request):
@login_required(login_url="/accounts/login/")
@permission_required('agenda_culturel.add_batchimportation')
@permission_required('agenda_culturel.run_batchimportation')
def add_import(request):
form = BatchImportationForm()
@ -550,6 +561,8 @@ def add_import(request):
@login_required(login_url="/accounts/login/")
@permission_required('agenda_culturel.view_batchimportation')
@permission_required('agenda_culturel.run_batchimportation')
def cancel_import(request, pk):
import_process = get_object_or_404(BatchImportation, pk=pk)
@ -570,6 +583,7 @@ def cancel_import(request, pk):
#########################
@login_required(login_url="/accounts/login/")
@permission_required('agenda_culturel.view_recurrentimport')
def recurrent_imports(request):
paginator = Paginator(RecurrentImport.objects.all().order_by("-pk"), 10)
page = request.GET.get('page')
@ -584,25 +598,30 @@ def recurrent_imports(request):
return render(request, 'agenda_culturel/rimports.html', {'paginator_filter': response} )
class RecurrentImportCreateView(LoginRequiredMixin, CreateView):
class RecurrentImportCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
model = RecurrentImport
permission_required = ("agenda_culturel.create_recurrentimport")
success_url = reverse_lazy('recurrent_imports')
form_class = RecurrentImportForm
class RecurrentImportUpdateView(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
class RecurrentImportUpdateView(SuccessMessageMixin, PermissionRequiredMixin, LoginRequiredMixin, UpdateView):
model = RecurrentImport
permission_required = ("agenda_culturel.change_recurrentimport")
form_class = RecurrentImportForm
success_message = _('The recurrent import has been successfully modified.')
class RecurrentImportDeleteView(SuccessMessageMixin, LoginRequiredMixin, DeleteView):
class RecurrentImportDeleteView(SuccessMessageMixin, PermissionRequiredMixin, LoginRequiredMixin, DeleteView):
model = RecurrentImport
permission_required = ("agenda_culturel.delete_recurrentimport")
success_url = reverse_lazy('recurrent_imports')
success_message = _('The recurrent import has been successfully deleted.')
@login_required(login_url="/accounts/login/")
@permission_required('agenda_culturel.view_recurrentimport')
@permission_required('agenda_culturel.view_batchimportation')
def view_rimport(request, pk):
obj = get_object_or_404(RecurrentImport, pk=pk)
paginator = Paginator(BatchImportation.objects.filter(recurrentImport=pk).order_by("-created_date"), 10)
@ -620,6 +639,8 @@ def view_rimport(request, pk):
@login_required(login_url="/accounts/login/")
@permission_required('agenda_culturel.view_recurrentimport')
@permission_required('agenda_culturel.run_recurrentimport')
def run_rimport(request, pk):
rimport = get_object_or_404(RecurrentImport, pk=pk)
@ -644,6 +665,8 @@ class DuplicatedEventsDetailView(LoginRequiredMixin, DetailView):
@login_required(login_url="/accounts/login/")
@permission_required('agenda_culturel.change_event')
@permission_required('agenda_culturel.change_duplicatedevents')
def merge_duplicate(request, pk):
edup = get_object_or_404(DuplicatedEvents, pk=pk)
form = MergeDuplicates(duplicates=edup)
@ -694,6 +717,8 @@ def merge_duplicate(request, pk):
@login_required(login_url="/accounts/login/")
@permission_required('agenda_culturel.change_event')
@permission_required('agenda_culturel.change_duplicatedevents')
def fix_duplicate(request, pk):
edup = get_object_or_404(DuplicatedEvents, pk=pk)
@ -760,6 +785,7 @@ class DuplicatedEventsUpdateView(LoginRequiredMixin, UpdateView):
@login_required(login_url="/accounts/login/")
@permission_required('agenda_culturel.view_duplicatedevents')
def duplicates(request):
paginator = Paginator(DuplicatedEvents.objects.all(), 10)
page = request.GET.get('page')
@ -803,6 +829,7 @@ def set_duplicate(request, year, month, day, pk):
#########################
@login_required(login_url="/accounts/login/")
@permission_required('agenda_culturel.view_categorisationrule')
def categorisation_rules(request):
paginator = Paginator(CategorisationRule.objects.all().order_by("weight", "pk"), 10)
page = request.GET.get('page')
@ -816,25 +843,30 @@ def categorisation_rules(request):
return render(request, 'agenda_culturel/categorisation_rules.html', {'paginator_filter': response} )
class CategorisationRuleCreateView(LoginRequiredMixin, CreateView):
class CategorisationRuleCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
model = CategorisationRule
permission_required = ("agenda_culturel.add_categorisationrule")
success_url = reverse_lazy('categorisation_rules')
form_class = CategorisationRuleImportForm
class CategorisationRuleUpdateView(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
class CategorisationRuleUpdateView(SuccessMessageMixin, PermissionRequiredMixin, LoginRequiredMixin, UpdateView):
model = CategorisationRule
permission_required = ("agenda_culturel.change_categorisationrule")
form_class = CategorisationRuleImportForm
success_url = reverse_lazy('categorisation_rules')
success_message = _('The categorisation rule has been successfully modified.')
class CategorisationRuleDeleteView(SuccessMessageMixin, LoginRequiredMixin, DeleteView):
class CategorisationRuleDeleteView(SuccessMessageMixin, PermissionRequiredMixin, LoginRequiredMixin, DeleteView):
model = CategorisationRule
permission_required = ("agenda_culturel.delete_categorisationrule")
success_url = reverse_lazy('categorisation_rules')
success_message = _('The categorisation rule has been successfully deleted.')
@login_required(login_url="/accounts/login/")
@permission_required('agenda_culturel.change_event')
@permission_required('agenda_culturel.apply_categorisationrule')
def apply_categorisation_rules(request):
nb = 0