555 lignes
18 KiB
Python
555 lignes
18 KiB
Python
from django.conf import settings
|
|
from django.contrib import messages
|
|
from django.contrib.messages.views import SuccessMessageMixin
|
|
from django.core.exceptions import ValidationError
|
|
from django.http import Http404
|
|
from django.shortcuts import get_object_or_404, redirect
|
|
from django.urls import reverse, reverse_lazy
|
|
from django.utils.text import slugify
|
|
from django.views.generic import FormView, TemplateView
|
|
|
|
from cruditor.views import (
|
|
CruditorAddView,
|
|
CruditorChangeView,
|
|
CruditorDeleteView,
|
|
CruditorListView,
|
|
)
|
|
from django_tables2.export.export import TableExport
|
|
from django_tables2.export.views import ExportMixin
|
|
|
|
from benevalibre.association import models as asso_models
|
|
from benevalibre.association.views import (
|
|
AssociationManageMenu,
|
|
AssociationRelatedFormMixin,
|
|
AssociationRelatedMixin,
|
|
)
|
|
from benevalibre.benevalo.models import Benevalo
|
|
from benevalibre.utils.html import fa_icon
|
|
from benevalibre.utils.views import CruditorPageMixin, PageMixin
|
|
|
|
from . import forms, tables
|
|
|
|
|
|
class ExportTable(ExportMixin):
|
|
def get_export_filename(self, export_format):
|
|
return "{}.{}".format(slugify(self.get_title()), export_format)
|
|
|
|
def create_export(self, export_format):
|
|
"""
|
|
We overload create_export because of a needed ajustement
|
|
"""
|
|
exporter = TableExport(
|
|
export_format=export_format,
|
|
table=self.table_export_class(
|
|
self.get_queryset(), **self.get_table_kwargs()
|
|
),
|
|
exclude_columns=self.exclude_columns,
|
|
)
|
|
return exporter.response(
|
|
filename=self.get_export_filename(export_format)
|
|
)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['export'] = [
|
|
['table', 'Format tableur', 'ods'],
|
|
['align-left', 'Format csv', 'csv'],
|
|
['code', 'Format json', 'json'],
|
|
]
|
|
return context
|
|
|
|
|
|
class Home(PageMixin, TemplateView):
|
|
template_name = 'base/home.html'
|
|
page_title = "Accueil"
|
|
|
|
def get(self, *args, **kwargs):
|
|
# Redirect to the board if the user is logged in
|
|
if not self.request.user.is_anonymous:
|
|
return redirect('base:board')
|
|
return super().get(*args, **kwargs)
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# Board
|
|
# ----------------------------------------------------------------------------
|
|
|
|
|
|
class AssociationEngage(
|
|
AssociationRelatedMixin,
|
|
AssociationManageMenu,
|
|
CruditorPageMixin,
|
|
SuccessMessageMixin,
|
|
FormView,
|
|
):
|
|
"""
|
|
Pour s'engager comme bénévole auprès d'une association, il suffit de
|
|
choisir l'association concernée et de clicker sur :guilabel:`Rejoindre`.
|
|
|
|
* Si l'association modère l'engagement bénévole, les modérateurs seront
|
|
informés afin de donner suite.
|
|
* Autrement, la demande prend effet tout de suite.
|
|
|
|
Vous deviendrez ainsi bénévole pour l'association choisie. Celle-ci
|
|
apparaîtra dans votre tableau de bord et vous pourrez saisir des actions
|
|
de bénévolat pour l'association concernée.
|
|
|
|
Vous êtes ensuite orienté vers votre tableau de bord.
|
|
"""
|
|
|
|
http_method_names = ['get', 'post']
|
|
success_url = reverse_lazy('base:home')
|
|
unsuccess_url = reverse_lazy('association:index')
|
|
form_class = forms.AssociationEngageForm
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
return redirect(self.unsuccess_url)
|
|
|
|
def form_valid(self, form):
|
|
try:
|
|
"""We try the setup the requestor with the local benevole role"""
|
|
role = self.association.role_set.get_benevole()
|
|
self.association.engagement_set.create(
|
|
user=self.request.user,
|
|
role=role,
|
|
is_active=not self.association.moderate_engagement,
|
|
)
|
|
if self.association.moderate_engagement:
|
|
# FIXME: notifier les modérat⋅eurs⋅rices
|
|
self.success_message = (
|
|
"Votre demande a bien été reçue et soumise à modération. "
|
|
"Les modérat⋅eurs⋅rices de l'association donneront suite "
|
|
"dès que possible."
|
|
)
|
|
else:
|
|
self.success_message = "Votre demande a été acceptée."
|
|
except ValidationError as e:
|
|
form.add_error(None, e)
|
|
for k, v in form.errors.items():
|
|
messages.error(self.request, v)
|
|
return redirect(self.unsuccess_url)
|
|
return super().form_valid(form)
|
|
|
|
|
|
class EngagementModerationMixin:
|
|
"""
|
|
Un⋅e bénévole privilégié⋅e d'une association peut modérer les demandes de
|
|
d'engagement bénévole via son tableau de bord.
|
|
|
|
Pour donner suite à la modération, il suffit de suivre le lien
|
|
:guilabel:`Donner suite` qui vous amène à éditer la demande. Par défaut, la
|
|
demande d'engagement cible le rôle « bénévole », mais vous pouvez à ce
|
|
stade modifier si nécessaire le rôle du bénévole et/ou valider la demande
|
|
en validant le formulaire. Sinon vous pouvez rejeter l'enregistrement en
|
|
effaçant la demande d'engagement.
|
|
"""
|
|
|
|
def get_engagement_moderation_queryset(self):
|
|
return (
|
|
asso_models.Engagement.objects.manageable_by(self.request.user)
|
|
.inactive()
|
|
.distinct()
|
|
)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
qs = self.get_engagement_moderation_queryset()
|
|
if qs.exists():
|
|
context[
|
|
'engagement_moderation_table'
|
|
] = tables.EngagementModerationTable(qs)
|
|
return context
|
|
|
|
|
|
class BenevaloModerationMixin:
|
|
"""
|
|
Un⋅e bénévole privilégié⋅e d'une association peut modérer les demandes de
|
|
saisie d'actions de bénévolat via son tableau de bord.
|
|
|
|
Pour donner suite à la modération, il suffit de suivre le lien
|
|
:guilabel:`Donner suite` qui vous amène à éditer la saisie. Vous pouvez
|
|
alors corriger et/ou valider la saisie en validant le formulaire, sinon
|
|
rejeter l'enregistrement en effaçant la saisie.
|
|
"""
|
|
|
|
def get_benevalo_moderation_queryset(self):
|
|
return (
|
|
Benevalo.objects.manageable_by(self.request.user)
|
|
.inactive()
|
|
.distinct()
|
|
)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
qs = self.get_benevalo_moderation_queryset()
|
|
if qs.exists():
|
|
context[
|
|
'benevalo_moderation_table'
|
|
] = tables.BenevaloModerationTable(qs)
|
|
return context
|
|
|
|
|
|
class AssociationModerationMixin:
|
|
"""
|
|
Suivant la même logique que la :doc:`modération
|
|
<../utilisation/moderer_association>` des engagements bénévoles et la
|
|
modération des actions de bénévolat, l'administrateur de **Bénévalibre**
|
|
peut modérer les demandes de création d'association via son tableau de
|
|
bord.
|
|
|
|
Pour donner suite à la modération, il suffit de suivre le lien qui vous
|
|
amène à éditer l'association. Vous pouvez alors corriger et/ou valider
|
|
l'enregistrement de l'association en validant le formulaire, sinon
|
|
rejeter l'enregistrement en effaçant l'association.
|
|
"""
|
|
|
|
def get_association_moderation_queryset(self):
|
|
return asso_models.Association.objects.inactive().distinct()
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
if self.request.user.is_superuser:
|
|
qs = self.get_association_moderation_queryset()
|
|
if qs.exists():
|
|
context[
|
|
'association_moderation_table'
|
|
] = tables.AssociationModerationTable(qs)
|
|
return context
|
|
|
|
|
|
class EngagementIndex(
|
|
CruditorPageMixin,
|
|
AssociationModerationMixin,
|
|
BenevaloModerationMixin,
|
|
EngagementModerationMixin,
|
|
CruditorListView,
|
|
):
|
|
"""
|
|
Votre tableau de bord liste vos engagements associatifs. C'est à partir
|
|
d'ici que vous pouvez lister ou saisir des actions de bénévolat pour
|
|
chaque association.
|
|
|
|
Il est également possible à partir de cette page d’:doc:`enregistrer une
|
|
association <enregistrer_association>`.
|
|
|
|
En fonction de vôtre rôle dans l'association, il est possible également que
|
|
des éléments en instance de modération vous soient présentés. Voyez la
|
|
section « :doc:`moderer_association` » pour plus de détails.
|
|
"""
|
|
|
|
model = asso_models.Engagement
|
|
table_class = tables.MyEngagementTable
|
|
page_title = "Mes engagements associatifs"
|
|
create_titlebutton_label = "Enregistrer une association"
|
|
create_url = reverse_lazy('association:create')
|
|
|
|
def add_create_titlebutton(self):
|
|
return settings.OPEN_REGISTRATION
|
|
|
|
def get_queryset(self):
|
|
return self.model.objects.for_user(self.request.user)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['hide_doc'] = True
|
|
return context
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# Engagement listing
|
|
# ----------------------------------------------------------------------------
|
|
|
|
|
|
class AssociatedEngagementReadOnlyIndex(
|
|
AssociationRelatedMixin,
|
|
AssociationManageMenu,
|
|
CruditorPageMixin,
|
|
CruditorListView,
|
|
):
|
|
model = asso_models.Engagement
|
|
table_class = tables.EngagementReadOnlyIndexTable
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
if not self.association.can_list_users(self.request.user):
|
|
raise Http404
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
def get_queryset(self):
|
|
return self.association.engagement_set.active()
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# Benevalo management
|
|
# ----------------------------------------------------------------------------
|
|
|
|
|
|
class AssociatedFormMixin:
|
|
def get_form_kwargs(self):
|
|
kwargs = super().get_form_kwargs()
|
|
kwargs['association'] = self.association
|
|
return kwargs
|
|
|
|
|
|
class AssociatedBenevaloMixin(AssociationRelatedMixin, AssociationManageMenu):
|
|
model = Benevalo
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
if not self.association.can_manage_benevalos(self.request.user):
|
|
raise Http404
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
def get_queryset(self):
|
|
return self.association.benevalo_set.all()
|
|
|
|
def get_success_url(self):
|
|
return reverse(
|
|
'base:association:benevalo:index', args=[self.association.id]
|
|
)
|
|
|
|
|
|
class AssociatedBenevaloModerationMixin(BenevaloModerationMixin):
|
|
def get_benevalo_moderation_queryset(self):
|
|
return (
|
|
self.association.benevalo_set.manageable_by(self.request.user)
|
|
.inactive()
|
|
.distinct()
|
|
)
|
|
|
|
|
|
class AssociatedBenevaloIndex(
|
|
AssociatedBenevaloMixin,
|
|
ExportTable,
|
|
AssociatedBenevaloModerationMixin,
|
|
CruditorPageMixin,
|
|
CruditorListView,
|
|
):
|
|
model = Benevalo
|
|
table_class = tables.AssociatedBenevaloTable
|
|
table_export_class = tables.BenevaloExportTable
|
|
page_title = "Tout le bénévolat"
|
|
|
|
def get_create_url(self):
|
|
return reverse(
|
|
'base:association:benevalo:create', args=[self.association.id]
|
|
)
|
|
|
|
def get_create_titlebutton_label(self):
|
|
return fa_icon(
|
|
'plus-circle',
|
|
"Saisir une action de bénévolat pour {}".format(self.association),
|
|
)
|
|
|
|
|
|
class AssociatedBenevaloCreate(
|
|
AssociatedBenevaloMixin,
|
|
AssociationRelatedFormMixin,
|
|
AssociatedFormMixin,
|
|
CruditorPageMixin,
|
|
CruditorAddView,
|
|
):
|
|
form_class = forms.AssociatedBenevaloCreateForm
|
|
|
|
|
|
class AssociatedBenevaloUpdate(
|
|
AssociatedBenevaloMixin,
|
|
AssociatedFormMixin,
|
|
CruditorPageMixin,
|
|
CruditorChangeView,
|
|
):
|
|
form_class = forms.AssociatedBenevaloUpdateForm
|
|
|
|
def form_valid(self, form):
|
|
self.object = form.save()
|
|
if not self.object.is_active:
|
|
self.object.is_active = True
|
|
# FIXME: notifier le bénévole
|
|
return super().form_valid(form)
|
|
|
|
def get_delete_url(self):
|
|
return reverse(
|
|
'base:association:benevalo:delete',
|
|
args=(self.association.id, self.object.pk),
|
|
)
|
|
|
|
|
|
class AssociatedBenevaloDelete(
|
|
AssociatedBenevaloMixin, CruditorPageMixin, CruditorDeleteView
|
|
):
|
|
pass
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# EngagedBenevalo management
|
|
# ----------------------------------------------------------------------------
|
|
|
|
|
|
class AssociatedEngagedBenevaloModerationMixin(BenevaloModerationMixin):
|
|
def get_benevalo_moderation_queryset(self):
|
|
return (
|
|
self.association.benevalo_set.by_user(self.engagement.user)
|
|
.manageable_by(self.request.user)
|
|
.inactive()
|
|
.distinct()
|
|
)
|
|
|
|
|
|
class AssociatedEngagedBenevaloIndex(
|
|
AssociationRelatedMixin,
|
|
AssociationManageMenu,
|
|
AssociatedEngagedBenevaloModerationMixin,
|
|
ExportTable,
|
|
CruditorPageMixin,
|
|
CruditorListView,
|
|
):
|
|
model = Benevalo
|
|
table_class = tables.AssociatedEngagedBenevaloTable
|
|
table_export_class = tables.BenevaloExportTable
|
|
page_title = "Bénévolat"
|
|
|
|
def setup(self, request, *args, **kwargs):
|
|
super().setup(request, *args, **kwargs)
|
|
self.engagement = self.get_engagement()
|
|
|
|
def get_engagement(self):
|
|
return get_object_or_404(
|
|
asso_models.Engagement.objects.viewable_by(
|
|
self.request.user
|
|
).distinct(),
|
|
pk=self.kwargs['engagement'],
|
|
)
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
if not self.association.can_manage_benevalos(
|
|
self.request.user
|
|
) or not self.association.can_self_list_benevalo(self.engagement.user):
|
|
raise Http404
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
def get_title(self):
|
|
return "{} de {}".format(super().get_title(), self.engagement.user)
|
|
|
|
def add_create_titlebutton(self):
|
|
return self.association.is_engaged(self.engagement.user)
|
|
|
|
def get_create_url(self):
|
|
return reverse(
|
|
'base:association:benevalo:create', args=[self.association.id]
|
|
)
|
|
|
|
def get_create_titlebutton_label(self):
|
|
return fa_icon(
|
|
'plus-circle',
|
|
"Saisir une action de bénévolat de {} pour {}".format(
|
|
self.engagement.user, self.association
|
|
),
|
|
)
|
|
|
|
def get_queryset(self):
|
|
return self.association.benevalo_set.by_user(self.engagement.user)
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# Benevalo self management
|
|
# ----------------------------------------------------------------------------
|
|
|
|
|
|
class AssociatedSelfBenevaloMixin(AssociationRelatedMixin):
|
|
model = Benevalo
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
if not self.association.can_self_create_benevalo(self.request.user):
|
|
raise Http404
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
def get_queryset(self):
|
|
return self.association.benevalo_set.by_user(self.request.user)
|
|
|
|
def get_success_url(self):
|
|
return reverse(
|
|
'base:association:self-benevalo:index', args=[self.association.id]
|
|
)
|
|
|
|
|
|
class AssociatedSelfBenevaloIndex(
|
|
AssociationRelatedMixin,
|
|
AssociationManageMenu,
|
|
AssociatedBenevaloModerationMixin,
|
|
ExportTable,
|
|
CruditorPageMixin,
|
|
CruditorListView,
|
|
):
|
|
model = Benevalo
|
|
table_class = tables.AssociatedEngagedSelfBenevaloTable
|
|
table_export_class = tables.BenevaloExportTable
|
|
page_title = "Mon bénévolat"
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
if not self.association.can_self_list_benevalo(self.request.user):
|
|
raise Http404
|
|
return super().dispatch(request, *args, **kwargs)
|
|
|
|
def add_create_titlebutton(self):
|
|
return self.association.can_self_create_benevalo(self.request.user)
|
|
|
|
def get_create_url(self):
|
|
return reverse(
|
|
'base:association:self-benevalo:create', args=[self.association.id]
|
|
)
|
|
|
|
def get_create_titlebutton_label(self):
|
|
return fa_icon(
|
|
'plus-circle',
|
|
"Saisir une action de bénévolat pour {}".format(self.association),
|
|
)
|
|
|
|
def get_queryset(self):
|
|
return self.association.benevalo_set.by_user(self.request.user)
|
|
|
|
|
|
class AssociatedSelfBenevaloCreate(
|
|
AssociatedSelfBenevaloMixin,
|
|
AssociatedFormMixin,
|
|
CruditorPageMixin,
|
|
CruditorAddView,
|
|
):
|
|
form_class = forms.AssociatedEngagedBenevaloCreateForm
|
|
|
|
def form_valid(self, form):
|
|
self.object = form.save(commit=False)
|
|
self.object.association = self.association
|
|
self.object.user = self.request.user
|
|
|
|
if self.object.association.need_moderation(self.request.user):
|
|
self.object.is_active = False
|
|
self.success_message = (
|
|
"Votre saisie a bien été reçue et soumise à modération. "
|
|
"Les Animat⋅eurs⋅rices de l'association donneront suite "
|
|
"dès que possible."
|
|
)
|
|
# FIXME: notifier les anims
|
|
|
|
self.object.save()
|
|
|
|
return super().form_valid(form)
|
|
|
|
|
|
class AssociatedSelfBenevaloUpdate(
|
|
AssociatedSelfBenevaloMixin,
|
|
AssociatedFormMixin,
|
|
CruditorPageMixin,
|
|
CruditorChangeView,
|
|
):
|
|
form_class = forms.AssociatedEngagedBenevaloUpdateForm
|
|
|
|
def get_delete_url(self):
|
|
return reverse(
|
|
'base:association:self-benevalo:delete',
|
|
args=(self.association.id, self.object.pk),
|
|
)
|
|
|
|
|
|
class AssociatedSelfBenevaloDelete(
|
|
AssociatedSelfBenevaloMixin, CruditorPageMixin, CruditorDeleteView
|
|
):
|
|
pass
|