248 lignes
8.1 KiB
Python
248 lignes
8.1 KiB
Python
import logging
|
|
import traceback
|
|
|
|
from django.contrib import messages
|
|
from django.contrib.auth.views import redirect_to_login
|
|
from django.core.exceptions import PermissionDenied
|
|
from django.db.models import Q
|
|
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 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.
|
|
"""
|
|
|
|
#: The title of the page
|
|
title = None
|
|
|
|
def get_title(self):
|
|
"""Return the page's title defined by `title`."""
|
|
if not self.title:
|
|
raise ValueError("page's title property can not be empty")
|
|
return self.title
|
|
|
|
def get_header_title(self):
|
|
"""
|
|
Return the page's title to show in the header, either defined by
|
|
`header_title` or by the page's title otherwise. Note that
|
|
`header_title` can be empty to not show it on the page.
|
|
"""
|
|
return getattr(self, 'header_title', self.get_title())
|
|
|
|
def get_titlebuttons(self):
|
|
"""
|
|
Return an optional list of buttons to display in the content
|
|
header, next to the title.
|
|
|
|
A button is defined by a dict with the following keys:
|
|
|
|
* label: The text to be displayed in the button
|
|
* url: Value of the `href` attribute of the link (optional)
|
|
* button_class: The main CSS class of the button to use (optional)
|
|
* extra_classes: Any extra CSS classes that should be added (optional)
|
|
|
|
If 'url' is not defined, the button will be rendered as a `<button>`
|
|
element instead of `<a>`. Alternatively, the button can be a dropdown
|
|
for toggling a list of links. Instead of 'url', this requires 'submenu'
|
|
which must be list of dict with 'label' and optionally 'extra_classes'
|
|
keys. The main CSS class of the dropdown menu can be changed in
|
|
'submenu_class'.
|
|
"""
|
|
return []
|
|
|
|
def get_page_context(self):
|
|
"""Provide the context to by used for this page."""
|
|
return {
|
|
'title': self.get_title(),
|
|
'header_title': self.get_header_title(),
|
|
'titlebuttons': self.get_titlebuttons(),
|
|
'instancemessages': self.get_instance_messages(),
|
|
}
|
|
|
|
def get_context_data(self, **kwargs):
|
|
"""Add a `page` object to the template context."""
|
|
context = super().get_context_data(**kwargs)
|
|
context['page'] = self.get_page_context()
|
|
return context
|
|
|
|
|
|
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
|
|
behaviour, and adds to the context a `page` object as `PageMixin`.
|
|
"""
|
|
|
|
staff_required = False
|
|
|
|
#: The URL to the create view.
|
|
create_url = None
|
|
|
|
#: The label of the create button displayed in the title row of the page.
|
|
create_tablebutton_label = "Ajouter"
|
|
|
|
def handle_not_logged_in(self, request, *args, **kwargs):
|
|
# Deny the access if the user is already logged in instead.
|
|
if self.request.user.is_authenticated:
|
|
raise PermissionDenied
|
|
return redirect_to_login(request.get_full_path())
|
|
|
|
def get_queryset(self, *args, **kwargs):
|
|
qs = super().get_queryset(*args, **kwargs)
|
|
# Restrict the queryset for the user if available.
|
|
if hasattr(qs, 'viewable_by'):
|
|
return qs.viewable_by(self.request.user).distinct()
|
|
return qs
|
|
|
|
def get_cruditor_context(self, *args, **kwargs):
|
|
cruditor = super().get_cruditor_context(*args, **kwargs)
|
|
# Set the header_title as PageMixin but prevent an empty one.
|
|
cruditor['header_title'] = self.get_header_title() or cruditor['title']
|
|
return cruditor
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
# Use the same values as cruditor for the page context.
|
|
context['page'] = {
|
|
**context['cruditor'],
|
|
**{
|
|
'tablebuttons': self.get_tablebuttons(),
|
|
'instancemessages': self.get_instance_messages(),
|
|
},
|
|
}
|
|
return context
|
|
|
|
def get_title(self):
|
|
# First try to return the title property in case of the inherited view
|
|
# do not take it into account (e.g. CruditorAddView).
|
|
return getattr(self, 'title', None) or super().get_title()
|
|
|
|
def get_header_title(self):
|
|
"""
|
|
Return the page's title to show in the header optionnaly defined by
|
|
`header_title`. Note that the page's title will be used if empty.
|
|
"""
|
|
return getattr(self, 'header_title', None)
|
|
|
|
def get_titlebuttons(self):
|
|
return []
|
|
|
|
def get_tablebuttons(self):
|
|
"""Add a Create button if requested and the URL is defined."""
|
|
create_url = (
|
|
self.get_create_url() if self.add_create_tablebutton() else None
|
|
)
|
|
if create_url is not None:
|
|
return [
|
|
{
|
|
'url': create_url,
|
|
'label': self.get_create_tablebutton_label(),
|
|
'button_class': 'btn-secondary',
|
|
}
|
|
]
|
|
return []
|
|
|
|
def get_create_url(self):
|
|
"""Return the URL to the create view used by the Create button."""
|
|
return self.create_url
|
|
|
|
def get_create_tablebutton_label(self):
|
|
"""
|
|
Return the label of the Create button, composed by an icon and
|
|
`create_tablebutton_label` by default.
|
|
"""
|
|
return fa_icon('plus-circle', self.create_tablebutton_label)
|
|
|
|
def add_create_tablebutton(self):
|
|
"""
|
|
Define whether a Create button should be appended in the title row of
|
|
the page.
|
|
"""
|
|
return True
|
|
|
|
|
|
class StaffCruditorPageMixin(CruditorPageMixin):
|
|
"""
|
|
Restrict the view to super-user only.
|
|
"""
|
|
|
|
staff_required = True
|
|
|
|
|
|
class EmailNotifySuccessMixin:
|
|
notify_condition = True
|
|
|
|
def get_notify_condition(self):
|
|
return self.notify_condition
|
|
|
|
notify_users_query = Q(pk=None)
|
|
|
|
def get_notify_users_query(self):
|
|
return Q(is_active=True) & self.notify_users_query
|
|
|
|
notify_template = None
|
|
|
|
def get_notify_template(self):
|
|
return self.notify_template
|
|
|
|
notify_subject_template = None
|
|
|
|
def get_notify_subject_template(self):
|
|
return self.notify_subject_template
|
|
|
|
def render_subject(self, user):
|
|
context = {
|
|
'user': user,
|
|
'request': self.request,
|
|
'object': self.object,
|
|
}
|
|
subject = render_to_string(self.get_notify_subject_template(), context)
|
|
# Email subject *must not* contain newlines
|
|
return ''.join(subject.splitlines())
|
|
|
|
def render_message(self, user):
|
|
context = {
|
|
'user': user,
|
|
'request': self.request,
|
|
'object': self.object,
|
|
}
|
|
return render_to_string(self.get_notify_template(), context)
|
|
|
|
def form_valid(self, form):
|
|
r = super().form_valid(form)
|
|
try:
|
|
if self.get_notify_condition():
|
|
for u in User.objects.filter(self.get_notify_users_query()):
|
|
u.email_user(
|
|
self.render_subject(u), self.render_message(u)
|
|
)
|
|
except Exception:
|
|
messages.warning(
|
|
self.request,
|
|
'Une erreur est survenue : votre demande est traitée, '
|
|
'mais il est impossible de notifier par courriel '
|
|
'les comptes concernés.',
|
|
)
|
|
logger = logging.getLogger('benevalibre')
|
|
logger.warning(traceback.format_exc())
|
|
return r
|