feat(instance): Ajoute la possibilité de téléverser une charte d'utilisation en pdf
Parent
98058c931d
révision
d67538631a
|
@ -0,0 +1,6 @@
|
|||
from .models import TermsOfUse
|
||||
|
||||
|
||||
def terms(request):
|
||||
obj = TermsOfUse.objects.order_by('pk').last()
|
||||
return {'terms': {'file': obj}}
|
|
@ -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__'
|
||||
|
|
|
@ -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"},
|
||||
),
|
||||
]
|
|
@ -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",
|
||||
)
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -13,5 +13,6 @@ urlpatterns = (
|
|||
path(
|
||||
'instance-category/',
|
||||
crud_include(views, 'InstanceCategory', 'instance_category'),
|
||||
)
|
||||
),
|
||||
path('charte/', views.TermsOfUseCreate.as_view(), name='terms_of_use'),
|
||||
)
|
||||
|
|
|
@ -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
|
|
@ -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."
|
||||
)
|
||||
|
|
|
@ -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',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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
|
||||
========================================
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@ tablib >=3.0,<3.1 # https://github.com/kennethreitz/tablib
|
|||
requests
|
||||
# Version comparison
|
||||
packaging
|
||||
# File type identification
|
||||
python-magic
|
||||
|
||||
# Django
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
Chargement…
Référencer dans un nouveau ticket