feat(instance): Ajoute la possibilité de téléverser une charte d'utilisation en pdf

pull/214/head
François Poulain 2021-09-07 16:12:57 +02:00
Parent 98058c931d
révision d67538631a
13 fichiers modifiés avec 161 ajouts et 2 suppressions

Voir le fichier

@ -0,0 +1,6 @@
from .models import TermsOfUse
def terms(request):
obj = TermsOfUse.objects.order_by('pk').last()
return {'terms': {'file': obj}}

Voir le fichier

@ -43,3 +43,9 @@ class InstanceCategoryCreateForm(CustomTapeformMixin, forms.ModelForm):
class InstanceCategoryUpdateForm(InstanceCategoryCreateForm):
pass
class TermsOfUseCreateForm(CustomTapeformMixin, forms.ModelForm):
class Meta:
model = models.TermsOfUse
fields = '__all__'

Voir le fichier

@ -0,0 +1,22 @@
# Generated by Django 2.2.24 on 2021-09-02 15:03
import benevalibre.instance.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('instance', '0005_defaultrole_verbose_name'),
]
operations = [
migrations.CreateModel(
name='TermsOfUse',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('file', models.FileField(help_text='Format attendu : pdf', upload_to='terms', validators=[benevalibre.instance.validators.validate_is_pdf], verbose_name='Charte')),
],
options={'verbose_name': "charte d'utilisation", 'verbose_name_plural': "chartes d'utilisation"},
),
]

Voir le fichier

@ -6,6 +6,8 @@ import reversion
from benevalibre.utils.mixins import HTMLDocString
from benevalibre.utils.models import AbstractRole, AbstractTaxonomy
from .validators import validate_is_pdf
@reversion.register()
class InstanceCategory(HTMLDocString, AbstractTaxonomy):
@ -141,3 +143,25 @@ class DefaultRole(HTMLDocString, AbstractRole):
"Vous devez en disposer d'un pour permettre l'inscription d'un "
"bénévole."
)
class TermsOfUse(models.Model):
"""
La charte d'utilisation de la plateforme.
Une fois définie elle apparait à tous les utilisateurs via le menu
principal de la plateforme. Le format attendu est un pdf.
Pour mettre à jour la charte il suffit de téléverser un nouveau document.
"""
class Meta:
verbose_name = "charte d'utilisation"
verbose_name_plural = "chartes d'utilisation"
file = models.FileField(
'Charte',
upload_to='terms',
validators=[validate_is_pdf],
help_text="Format attendu : pdf",
)

Voir le fichier

@ -1,5 +1,7 @@
from io import BytesIO
from django.contrib.messages import get_messages
from django.urls import reverse_lazy
from django.urls import reverse, reverse_lazy
import pytest
from bs4 import BeautifulSoup as bs
@ -255,3 +257,45 @@ class TestDefaultLevel(StaffOnlyViewMixin):
messages = [m.message for m in get_messages(response.wsgi_request)]
assert len(messages) == 1
assert "« niveau de lutte » a été supprimé" in messages[0]
@pytest.mark.django_db
class TestTermsOfUsePage:
def test_terms_page_redirects_not_logged_users(self, client):
response = client.get(reverse('instance:terms_of_use'))
assert response.status_code == 302
assert response.url == "/account/login/?next=/instance/charte/"
def test_terms_page_displays_403_to_non_superusers(self, client, user):
client.force_login(user)
response = client.get(reverse('instance:terms_of_use'))
assert response.status_code == 403
def test_superusers_access_terms_page(self, client, superuser):
client.force_login(superuser)
response = client.get(reverse('instance:terms_of_use'))
assert "Téléverser une nouvelle charte" in response.content.decode()
@pytest.mark.parametrize(
'content, name, expected',
(
('test', 'test.txt', 200),
('%PDF-1.5', 'test.txt', 200),
('test', 'test.pdf', 200),
('%PDF-1.5', 'test.pdf', 302),
),
)
def test_terms_of_use_validation(
self, client, superuser, content, name, expected
):
client.force_login(superuser)
_file = BytesIO(content.encode())
_file.name = name
response = client.post(
reverse('instance:terms_of_use'), data={'file': _file}
)
assert response.status_code == expected
if expected == 302:
messages = [m.message for m in get_messages(response.wsgi_request)]
assert len(messages) == 1
assert "est désormais en ligne." in messages[0]

Voir le fichier

@ -13,5 +13,6 @@ urlpatterns = (
path(
'instance-category/',
crud_include(views, 'InstanceCategory', 'instance_category'),
)
),
path('charte/', views.TermsOfUseCreate.as_view(), name='terms_of_use'),
)

Voir le fichier

@ -0,0 +1,20 @@
import os
from django.core.exceptions import ValidationError
import magic
def validate_is_pdf(file):
validation_error = ValidationError(
'Veuillez sélectionner un fichier au format PDF.'
)
valid_mime_types = ['application/pdf']
file_mime_type = magic.from_buffer(file.read(1024), mime=True)
if file_mime_type not in valid_mime_types:
raise validation_error
valid_file_extensions = ['.pdf']
_, ext = os.path.splitext(file.name)
if ext.lower() not in valid_file_extensions:
raise validation_error

Voir le fichier

@ -138,3 +138,20 @@ class DefaultCategoryDelete(
DefaultCategoryViewMixin, views.CruditorDeleteView
):
pass
# CHART OF USE DEFINITION
# ------------------------------------------------------------------------------
class TermsOfUseCreate(
StaffCruditorPageMixin,
views.CruditorAddView,
):
object = None
success_url = reverse_lazy('base:home')
title = "Téléverser une nouvelle charte d'utilisation"
form_class = forms.TermsOfUseCreateForm
success_message = (
"La nouvelle charte d'utilisation est désormais en ligne."
)

Voir le fichier

@ -222,6 +222,7 @@ TEMPLATES = [
'django.contrib.messages.context_processors.messages',
'benevalibre.base.context_processors.project',
'benevalibre.base.context_processors.instance_name',
'benevalibre.instance.context_processors.terms',
],
},
}

Voir le fichier

@ -15,6 +15,13 @@
<i class="fa fa-university fa-fw mr-2 d-lg-none" aria-hidden="true"></i>Liste des associations
</a>
</li>
{% if terms.file %}
<li class="nav-item">
<a class="nav-link" href="{{ terms.file.url }}">
<i class="fa fa-file-text fa-fw mr-2 d-lg-none" aria-hidden="true"></i>Charte d'utilisation
</a>
</li>
{% endif %}
{% if export %}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" aria-haspopup="true" aria-expanded="false" data-toggle="dropdown" role="button">

Voir le fichier

@ -25,6 +25,9 @@
<a class="dropdown-item" href="{% url "instance:instance_category:index" %}">
<i class="fa fa-tag fa-fw mr-2" aria-hidden="true"></i>Catégories d'instance
</a>
<a class="dropdown-item" href="{% url "instance:terms_of_use" %}">
<i class="fa fa-file-text fa-fw mr-2" aria-hidden="true"></i>Charte d'utilisation
</a>
</div>
</li>
{% endif %}

Voir le fichier

@ -36,6 +36,7 @@ Une fois connecté avec le compte administrateur :
l'instance.
* Vous avez accès à une page dédiée aux statistiques globales de l'application,
accessible via le menu administrateur spécifique susmentionné.
* Vous avez accès à la définition d'une charte d'utilisation.
Gestion des rôles par défaut
@ -66,6 +67,11 @@ Statistiques globales de l'application
.. autoclass:: benevalibre.base.views.AdminOnlyStatsPage
Charte d'utilisation
^^^^^^^^^^^^^^^^^^^^
.. autoclass:: benevalibre.instance.models.TermsOfUse
Tableau de bord du compte administrateur
========================================

Voir le fichier

@ -6,6 +6,8 @@ tablib >=3.0,<3.1 # https://github.com/kennethreitz/tablib
requests
# Version comparison
packaging
# File type identification
python-magic
# Django
# ------------------------------------------------------------------------------