feat(instance): Permettre aux utilisateurs de se débarasser d'un message d'instance pour toute la session
Parent
8d63a65a4c
révision
1553839ad1
|
@ -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();
|
||||
});
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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',
|
||||
)
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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">
|
||||
×
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Chargement…
Référencer dans un nouveau ticket