feat(instance): Permettre aux utilisateurs de se débarasser d'un message d'instance pour toute la session

pull/225/head
Zoé Martin 2022-04-08 16:32:47 +02:00
Parent 8d63a65a4c
révision 1553839ad1
9 fichiers modifiés avec 80 ajouts et 29 suppressions

Voir le fichier

@ -13,6 +13,21 @@ window.jQuery = window.$ = $; // eslint-disable-line no-multi-assign
$(() => {
$('.no-js').removeClass('no-js');
// remember when the user dismisses an instance message
$(document).ready(function () {
$('.instance-message').on('closed.bs.alert', function () {
$.ajax({
type: "POST",
url: `/instance/instance-message/${this.dataset.id}/dismiss`,
data: {
csrfmiddlewaretoken: $('input[name=csrfmiddlewaretoken]').val()
},
dataType: 'json',
});
})
});
// Make custom file input dynamic
bsCustomFileInput.init();
});

Voir le fichier

@ -120,7 +120,9 @@ class User(AbstractBaseUser, PermissionsMixin):
@property
def is_manager(self):
return any([e.role.manage_association for e in self.engagement_set.all()])
return any(
[e.role.manage_association for e in self.engagement_set.all()]
)
@property
def pseudo_rand_color(self):

Voir le fichier

@ -180,16 +180,13 @@ class InstanceMessage(models.Model):
verbose_name = "message d'instance"
verbose_name_plural = "messages d'instance"
class TargetType(models.TextChoices):
EVERYONE = 'ALL', 'Tout le monde'
MANAGERS = 'MNG', 'Les managers d\'asso'
def default_expiration_date():
return datetime.today() + timedelta(days=1)
content = models.TextField("contenu")
author = models.ForeignKey(
'accounts.User',
@ -201,14 +198,14 @@ class InstanceMessage(models.Model):
broadcast_date = models.DateTimeField(
"date de diffusion",
default=datetime.today,
validators=[validate_after2k, validate_before1y]
validators=[validate_after2k, validate_before1y],
)
expiration_date = models.DateTimeField(
"date d'expiration",
null=True,
default=default_expiration_date,
validators=[validate_after2k, validate_before1y]
validators=[validate_after2k, validate_before1y],
)
target = models.CharField(
@ -218,16 +215,13 @@ class InstanceMessage(models.Model):
default=TargetType.EVERYONE,
)
@property
def name(self):
return self.content[0:20] + ""
def __str__(self):
return self.name
def is_target(self, user):
if self.target == self.TargetType.EVERYONE:
return True
@ -235,11 +229,9 @@ class InstanceMessage(models.Model):
if self.target == self.TargetType.MANAGERS:
return user.is_authenticated and user.is_manager
def is_on_time(self):
now = datetime.now(timezone.utc)
return now >= self.broadcast_date and now < self.expiration_date
def should_be_displayed(self, user):
return self.is_on_time() and self.is_target(user)

Voir le fichier

@ -67,4 +67,11 @@ class InstanceMessageIndexTable(tables.Table):
class Meta:
model = models.InstanceMessage
fields = ('name', 'target', 'broadcast_date', 'expiration_date', 'author', 'actions')
fields = (
'name',
'target',
'broadcast_date',
'expiration_date',
'author',
'actions',
)

Voir le fichier

@ -14,6 +14,11 @@ urlpatterns = (
'instance-message/',
crud_include(views, 'InstanceMessage', 'instance_message')
),
path(
'instance-message/<int:id>/dismiss',
views.InstanceMessageDismiss.as_view(),
name='instance_message_dismiss',
),
path(
'instance-category/',
crud_include(views, 'InstanceCategory', 'instance_category'),

Voir le fichier

@ -1,7 +1,10 @@
from django.http import HttpResponseBadRequest, JsonResponse
from django.urls import reverse, reverse_lazy
from django.views import View
from cruditor import views
from benevalibre.utils import utils
from benevalibre.utils.views import StaffCruditorPageMixin
from . import forms, models, tables
@ -190,10 +193,25 @@ class InstanceMessageUpdate(
form_class = forms.InstanceMessageUpdateForm
def get_delete_url(self):
return reverse('instance:instance_message:delete', args=(self.object.pk,))
return reverse(
'instance:instance_message:delete', args=(self.object.pk,)
)
class InstanceMessageDelete(
InstanceMessageViewMixin, views.CruditorDeleteView
):
pass
class InstanceMessageDismiss(View):
def post(self, request, *args, **kwargs):
if not request.is_ajax():
return HttpResponseBadRequest()
dismissed = utils.get_dismissed_instance_messages(request)
utils.set_dismissed_instance_messages(
request, dismissed + [kwargs['id']]
)
return JsonResponse({'dismissed': True})

Voir le fichier

@ -1,7 +1,8 @@
{% if messages or page.instancemessages %}
<div class="app-messages" aria-live="polite" aria-atomic="true">
{% csrf_token %}
{% for message in page.instancemessages %}
<div class="alert alert-dismissible alert-info mb-0 border-top-0 rounded-0">
<div class="instance-message alert alert-dismissible alert-info mb-0 border-top-0 rounded-0" data-id="{{ message.id }}">
{{ message.content }}
<button type="button" class="close" data-dismiss="alert" aria-label="Fermer">
&times;

Voir le fichier

@ -7,3 +7,14 @@ def get_instance_name(request):
return instance_name.encode().decode('idna')
except Exception:
return instance_name
def get_dismissed_instance_messages(request):
try:
return request.session['dismissed-instance-messages']
except KeyError:
return []
def set_dismissed_instance_messages(request, dismissed):
request.session['dismissed-instance-messages'] = dismissed

Voir le fichier

@ -10,10 +10,22 @@ from django.template.loader import render_to_string
from benevalibre.accounts.models import User
from benevalibre.instance.models import InstanceMessage
from . import utils
from .html import fa_icon
class PageMixin:
class BasePage:
def get_instance_messages(self):
return filter(
lambda m: m.should_be_displayed(self.request.user),
InstanceMessage.objects.exclude(
id__in=utils.get_dismissed_instance_messages(self.request)
)
or [],
)
class PageMixin(BasePage):
"""
Mixin to apply to the views which render a page. It adds to the context a
`page` object which ease the attributes definition and access.
@ -57,12 +69,6 @@ class PageMixin:
"""
return []
def get_instance_messages(self):
return filter(
lambda m: m.should_be_displayed(self.request.user),
InstanceMessage.objects.all() or []
)
def get_page_context(self):
"""Provide the context to by used for this page."""
return {
@ -79,7 +85,7 @@ class PageMixin:
return context
class CruditorPageMixin:
class CruditorPageMixin(BasePage):
"""
Mixin to apply to the views based on `django-cruditor` - and which inherit
from `cruditor.mixins.CruditorView`. It customizes and extends the default
@ -113,12 +119,6 @@ class CruditorPageMixin:
cruditor['header_title'] = self.get_header_title() or cruditor['title']
return cruditor
def get_instance_messages(self):
return filter(
lambda m: m.should_be_displayed(self.request.user),
InstanceMessage.objects.all() or []
)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# Use the same values as cruditor for the page context.
@ -127,7 +127,7 @@ class CruditorPageMixin:
**{
'tablebuttons': self.get_tablebuttons(),
'instancemessages': self.get_instance_messages(),
}
},
}
return context