This commit is contained in:
root 2018-03-11 23:28:44 +01:00
commit 7404de04e3
46 changed files with 1150 additions and 747 deletions

30
content/forms.py Normal file
View file

@ -0,0 +1,30 @@
from django import forms
from .models import Content, Category
class CreateContent(forms.ModelForm):
class Meta:
model = Content
fields = [
'name',
'category',
'file',
]
def __init__(self, school, *args, **kwargs):
super().__init__(*args, **kwargs)
self.instance.school = school
already_created = map(lambda x:x.category.pk, school.content_set.select_related('category'))
self.fields['category'].queryset = Category.objects.exclude(pk__in=already_created)
class ContentEdit(forms.ModelForm):
class Meta:
model = Content
fields = [
'name',
'file'
]

View file

@ -1,5 +1,6 @@
# Generated by Django 2.0.1 on 2018-02-28 18:43 # Generated by Django 2.0.1 on 2018-03-08 22:03
import content.models
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
@ -9,6 +10,7 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
('users', '0001_initial'),
] ]
operations = [ operations = [
@ -26,8 +28,9 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255, verbose_name='Nom du contenu')), ('name', models.CharField(max_length=255, verbose_name='Nom du contenu')),
('file', models.FileField(upload_to='', verbose_name='Fichier')), ('file', models.FileField(upload_to=content.models.get_upload_to, validators=[content.models.validate_file_extension], verbose_name='Fichier')),
('category', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='content.Category', verbose_name='Catégorie')), ('category', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='content.Category', verbose_name='Catégorie')),
('school_owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.School')),
], ],
), ),
] ]

View file

@ -0,0 +1,23 @@
# Generated by Django 2.0.1 on 2018-03-09 10:16
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('content', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='category',
name='description_short',
field=models.TextField(null=True, verbose_name='Description courte'),
),
migrations.AlterField(
model_name='category',
name='description',
field=models.TextField(default='', verbose_name='Description de la catégorie'),
),
]

View file

@ -1,22 +0,0 @@
# Generated by Django 2.0.1 on 2018-02-28 18:43
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('content', '0001_initial'),
('users', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='content',
name='school_owner',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.SchoolProfile'),
),
]

View file

@ -0,0 +1,19 @@
# Generated by Django 2.0.1 on 2018-03-09 11:33
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('content', '0002_auto_20180309_1116'),
]
operations = [
migrations.AlterField(
model_name='content',
name='school_owner',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='users.School'),
),
]

View file

@ -0,0 +1,19 @@
# Generated by Django 2.0.1 on 2018-03-09 11:35
import content.models
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('content', '0003_auto_20180309_1233'),
]
operations = [
migrations.AlterField(
model_name='content',
name='file',
field=models.FileField(upload_to=content.models.get_upload_to, verbose_name='Fichier'),
),
]

View file

@ -0,0 +1,18 @@
# Generated by Django 2.0.1 on 2018-03-09 11:55
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('content', '0004_auto_20180309_1235'),
]
operations = [
migrations.AlterField(
model_name='content',
name='file',
field=models.FileField(upload_to='', verbose_name='Fichier'),
),
]

View file

@ -0,0 +1,19 @@
# Generated by Django 2.0.1 on 2018-03-09 11:57
import content.models
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('content', '0005_auto_20180309_1255'),
]
operations = [
migrations.AlterField(
model_name='content',
name='file',
field=models.FileField(upload_to=content.models.get_upload_to, verbose_name='Fichier'),
),
]

View file

@ -0,0 +1,25 @@
# Generated by Django 2.0.1 on 2018-03-09 12:15
import content.models
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('content', '0006_auto_20180309_1257'),
]
operations = [
migrations.AlterField(
model_name='content',
name='file',
field=models.FileField(upload_to=content.models.get_upload_to, validators=[content.models.validate_file_extension], verbose_name='Fichier'),
),
migrations.AlterField(
model_name='content',
name='school_owner',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.School'),
),
]

View file

@ -1,9 +1,11 @@
from django.db import models import os
from django.urls import reverse
from django.contrib.auth.models import Group from django.db import models
from django.conf import settings from django.core.exceptions import ValidationError
from django.urls import reverse
from users.models import School
from users.models import SchoolProfile
class Category(models.Model): class Category(models.Model):
"""Une catégorie de contenu.""" """Une catégorie de contenu."""
@ -11,21 +13,40 @@ class Category(models.Model):
max_length=255, max_length=255,
verbose_name="Nom de la catégorie" verbose_name="Nom de la catégorie"
) )
description_short = models.TextField(
verbose_name="Description courte",
null=True,
)
description = models.TextField( description = models.TextField(
verbose_name="Descriton de la catégorie", verbose_name="Description de la catégorie",
default="" default=""
) )
image = models.ImageField( image = models.ImageField(
verbose_name="Illustration de la catégorie", verbose_name="Illustration de la catégorie",
null=True, null=True,
) )
def get_absolute_url(self): def get_absolute_url(self):
return reverse('content:category-list', kwargs={'pk':self.pk}) return reverse('content:category', kwargs={'pk': self.pk})
def __str__(self): def __str__(self):
return self.name return self.name
def get_upload_to(instance, filename):
extension = filename.split('.')[-1]
proper_school = ''.join(e for e in instance.school_owner.name if e.isalnum() and ord(e)<128)
proper_name = ''.join(e for e in instance.category.name if e.isalnum() and ord(e)<128)
return "static/media/"+proper_school+"/"+proper_name+'.'+extension
def validate_file_extension(value):
ext = os.path.splitext(value.name)[1] # [0] returns path+filename
valid_extensions = ['.mp4', '.avi', '.mov']
if not ext.lower() in valid_extensions:
raise ValidationError(u'Format non supporté : {}'.format(ext))
class Content(models.Model): class Content(models.Model):
"""Un contenu du site (vidéo).""" """Un contenu du site (vidéo)."""
name = models.CharField( name = models.CharField(
@ -33,7 +54,7 @@ class Content(models.Model):
verbose_name="Nom du contenu" verbose_name="Nom du contenu"
) )
school_owner = models.ForeignKey( school_owner = models.ForeignKey(
SchoolProfile, School,
on_delete=models.CASCADE, on_delete=models.CASCADE,
) )
category = models.ForeignKey( category = models.ForeignKey(
@ -43,11 +64,10 @@ class Content(models.Model):
null=True null=True
) )
file = models.FileField( file = models.FileField(
verbose_name="Fichier" verbose_name="Fichier",
validators=[validate_file_extension],
upload_to=get_upload_to
) )
def __str__(self): def __str__(self):
return self.name return self.name
def manager_right(self):
return 'users.manage_' + str(self.school_owner.group.pk)

View file

@ -5,15 +5,13 @@
</video> </video>
<div class="card-body"> <div class="card-body">
<h2 class="display-5">{{content.name}}</h2> <h2 class="display-5">{{content.name}}</h2>
<p class="lead">Contenu proposé par {{content.school_owner.group.name}}</p> <p class="lead">Catégorie : {{content.category.name}}</p>
{% if content.manager_right in perms %}
<div class="d-flex justify-content-between align-items-center"> <div class="d-flex justify-content-between align-items-center">
<div class="btn-group"> <div class="btn-group">
<a class="btn btn-sm btn-outline-primary"i href="{% url "content:content-edit" content.pk %}"><i class="fa fa-edit"></i> Éditer</a> <a class="btn btn-sm btn-outline-primary"i href="{% url "content:content-edit" content.pk %}"><i class="fa fa-edit"></i> Éditer</a>
<a class="btn btn-sm btn-outline-danger" href="{% url "content:content-delete" content.pk %}" ><i class="fa fa-trash"></i> Supprimer</a> <a class="btn btn-sm btn-outline-danger" href="{% url "content:content-delete" content.pk %}" ><i class="fa fa-trash"></i> Supprimer</a>
</div> </div>
</div> </div>
{% endif %}
</div> </div>
</div> </div>
</div> </div>

View file

@ -22,15 +22,13 @@ $('html, body').animate({scrollTop: $('#category-content').offset().top}, 800);
<div class="position-relative overflow-hidden p-3 p-md-5 text-center bg-light page-title"> <div class="position-relative overflow-hidden p-3 p-md-5 text-center bg-light page-title">
<div class="col-md-5 p-lg-5 mx-auto my-5 title-block"> <div class="col-md-5 p-lg-5 mx-auto my-5 title-block">
<h1 class="display-4 font-weight-normal">{{category.name}}</h1> <h1 class="display-4 font-weight-normal">{{category.name}}</h1>
<p class="lead font-weight-normal">{{category.description}}</p> <p class="lead font-weight-normal">{{category.description_short}}</p>
<a class="btn btn-outline-secondary smooth-scroll" href="#category-content">Aller voir !</a> <a class="btn btn-outline-secondary smooth-scroll" href="#category-content">Aller voir !</a>
</div> </div>
</div> </div>
<br /> <br />
<div class="row text-center" id="category-content"> <div id="category-content">
{% for content in contents %} {{category.description|safe}}
{% include "content/content.html" %}
{% endfor %}
</div> </div>
<br /> <br />
<br /> <br />

View file

@ -1,22 +1,17 @@
from django.urls import path from django.urls import path
from . import views
from .views import ( from .views import (
ContentCategoryList,
CreateCategory, CreateCategory,
ViewCategory,
DeleteCategory, DeleteCategory,
EditCategory, EditCategory,
CreateContent,
DeleteContent, DeleteContent,
EditContent,
) )
app_name = 'content' app_name = 'content'
urlpatterns = [ urlpatterns = [
path(
'category/<int:pk>/',
ContentCategoryList.as_view(),
name='category-list'
),
path( path(
'category/delete/<int:pk>', 'category/delete/<int:pk>',
DeleteCategory.as_view(), DeleteCategory.as_view(),
@ -28,13 +23,18 @@ urlpatterns = [
name='category-new' name='category-new'
), ),
path( path(
'category/edit/<int:pk>', 'category/<int:pk>',
ViewCategory.as_view(),
name='category',
),
path(
'category/<int:pk>/edit',
EditCategory.as_view(), EditCategory.as_view(),
name='category-edit', name='category-edit',
), ),
path( path(
'new', 'new/<int:school_pk>',
CreateContent.as_view(), views.create_content,
name='content-new', name='content-new',
), ),
path( path(
@ -44,7 +44,7 @@ urlpatterns = [
), ),
path( path(
'<int:pk>/edit', '<int:pk>/edit',
EditContent.as_view(), views.edit_content,
name="content-edit", name="content-edit",
), ),

View file

@ -1,31 +1,21 @@
from django.views import generic from django.views import generic
from django.urls import reverse, reverse_lazy from django.urls import reverse, reverse_lazy
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect, render
from django.contrib.auth.mixins import PermissionRequiredMixin, LoginRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin, LoginRequiredMixin
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib import messages from django.contrib import messages
from .models import Content, Category from .models import Content, Category
from users.models import School
from . import forms
from settings.models import SiteSettings from settings.models import SiteSettings
class ContentCategoryList(generic.ListView): class ViewCategory(generic.DetailView):
"""Affiche les contenus d'une catégorie.""" """Affiche une catégorie."""
model = Content model = Category
context_object_name = "contents"
template_name = "content/content_list.html" template_name = "content/content_list.html"
def get_queryset(self):
pk = self.kwargs['pk']
category = get_object_or_404(Category, pk=pk)
return Content.objects.filter(category=category)
def get_context_data(self, **kwargs):
context = super(generic.ListView, self).get_context_data(**kwargs)
pk = self.kwargs['pk']
category = get_object_or_404(Category, pk=pk)
context['category'] = category
return context
class CreateCategory(PermissionRequiredMixin, generic.CreateView): class CreateCategory(PermissionRequiredMixin, generic.CreateView):
"""Création de catégorie.""" """Création de catégorie."""
@ -60,39 +50,6 @@ class EditCategory(PermissionRequiredMixin, generic.UpdateView):
return context return context
class CreateContent(PermissionRequiredMixin, generic.CreateView):
"""Création de contenu."""
model = Content
fields = [
'name',
'category',
'file'
]
template_name = "edit.html"
extra_context = {
'title' : 'Envoi de contenu',
'validate' : 'Envoyer'
}
def has_permission(self):
return self.request.user.has_perm('users.manage_'+str(self.request.user.userprofile.school.group.pk))
def get_success_url(self):
return self.object.school_owner.get_absolute_url()
def form_valid(self, form):
form.instance.school_owner = self.request.user.userprofile.school
r = super().form_valid(form)
return r
def dispatch(self, request, *args, **kwargs):
settings,_ = SiteSettings.objects.get_or_create()
if not settings.allow_upload :
messages.error(request, "Le téléversement de contenu n'est pas autorisé actuellement.")
return redirect(reverse("home"))
return super().dispatch(request, *args, **kwargs)
class DeleteContent(PermissionRequiredMixin, generic.DeleteView): class DeleteContent(PermissionRequiredMixin, generic.DeleteView):
"""Suppression de contenu""" """Suppression de contenu"""
model = Content model = Content
@ -103,34 +60,53 @@ class DeleteContent(PermissionRequiredMixin, generic.DeleteView):
def has_permission(self): def has_permission(self):
school = get_object_or_404(Content, pk=self.kwargs['pk']).school_owner school = get_object_or_404(Content, pk=self.kwargs['pk']).school_owner
return self.request.user.has_perm('users.manage_'+str(school.group.pk)) return self.request.user.is_staff or self.request.user == school.admin
class EditContent(PermissionRequiredMixin, generic.UpdateView): def create_content(request, school_pk):
"""Édition d'un contenu""" settings,_ = SiteSettings.objects.get_or_create()
model = Content if not settings.allow_upload :
template_name = "edit.html" messages.error(request, "Le téléversement de contenu n'est pas autorisé actuellement.")
fields = [ return redirect(reverse("home"))
'name', school = get_object_or_404(School, pk=school_pk)
'category', can = request.user.is_staff or request.user == school.admin
'file' if not can:
] messages.error(request, 'Vous ne pouvez pas accéder à cette page')
template_name = "edit.html" return redirect(reverse('home'))
extra_context = {
'title' : 'Édition de contenu',
'validate' : 'Envoyer'
}
def get_success_url(self): content_form = forms.CreateContent(school, request.POST or None, request.FILES or None)
return self.object.school_owner.get_absolute_url() content_form.instance.school_owner = school
def has_permission(self): if content_form.is_valid():
school = get_object_or_404(Content, pk=self.kwargs['pk']).school_owner content_form.save()
return self.request.user.has_perm('users.manage_'+str(school.group.pk)) messages.success(request, "Contenu ajouté.")
return redirect(school.get_absolute_url())
return render(request, 'edit.html', {
'form' : content_form,
'title' : 'Ajout de contenu',
'validate' : 'Ajouter'
})
def dispatch(self, request, *args, **kwargs):
settings,_ = SiteSettings.objects.get_or_create() def edit_content(request, pk):
if not settings.allow_upload : settings,_ = SiteSettings.objects.get_or_create()
messages.error(request, "Le téléversement de contenu n'est pas autorisé actuellement.") if not settings.allow_upload :
return redirect(reverse("home")) messages.error(request, "Le téléversement de contenu n'est pas autorisé actuellement.")
return super().dispatch(request, *args, **kwargs) return redirect(reverse("home"))
content = get_object_or_404(Content, pk=pk)
school = content.school_owner
can = request.user.is_staff or request.user == school.admin
if not can:
messages.error(request, 'Vous ne pouvez pas accéder à cette page')
return redirect(reverse('home'))
content_form = forms.ContentEdit(request.POST or None, request.FILES or None, instance=content)
if content_form.is_valid():
content_form.save()
messages.success(request, "Contenu modifié.")
return redirect(school.get_absolute_url())
return render(request, 'edit.html', {
'form' : content_form,
'title' : 'Modifier un contenu',
'validate' : 'Modifier'
})

View file

@ -15,5 +15,5 @@ class SelectUserForm(forms.Form):
def populate(self): def populate(self):
admins,_ = Group.objects.get_or_create(name='admins') admins,_ = Group.objects.get_or_create(name='admins')
choices = [(u.pk, u.first_name + ' ' + u.last_name + '(' + u.username + ')') for u in User.objects.all()] choices = [(u.pk, u.first_name + ' ' + u.last_name + ' (' + u.username + ')') for u in User.objects.all()]
self.fields['pk'].choices = choices self.fields['pk'].choices = choices

View file

@ -1,4 +1,4 @@
# Generated by Django 2.0.1 on 2018-02-28 18:43 # Generated by Django 2.0.1 on 2018-03-08 22:03
from django.db import migrations, models from django.db import migrations, models
@ -17,6 +17,16 @@ class Migration(migrations.Migration):
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('allow_upload', models.BooleanField(default=False, verbose_name="Autoriser l'upload de vidéos.")), ('allow_upload', models.BooleanField(default=False, verbose_name="Autoriser l'upload de vidéos.")),
('home_message', models.TextField(default='', verbose_name="Message de la page d'accueil")), ('home_message', models.TextField(default='', verbose_name="Message de la page d'accueil")),
('site_logo', models.ImageField(blank=True, null=True, upload_to='', verbose_name='Logo du site')),
('event_poster', models.ImageField(blank=True, null=True, upload_to='', verbose_name="Affiche de l'événement")),
],
),
migrations.CreateModel(
name='StaticPage',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255, verbose_name='Titre de la catégorie')),
('text', models.TextField(verbose_name='Texte de la catégorie')),
], ],
), ),
] ]

View file

@ -1,28 +0,0 @@
# Generated by Django 2.0.1 on 2018-03-01 10:47
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('settings', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='sitesettings',
name='event_poster',
field=models.ImageField(null=True, upload_to='', verbose_name="Affiche de l'événement"),
),
migrations.AddField(
model_name='sitesettings',
name='min_number_of_categories',
field=models.PositiveIntegerField(default=0, verbose_name='Nombre minimal de catégories dans laquelle participer'),
),
migrations.AddField(
model_name='sitesettings',
name='site_logo',
field=models.ImageField(null=True, upload_to='', verbose_name='Logo du site'),
),
]

View file

@ -1,23 +0,0 @@
# Generated by Django 2.0.1 on 2018-03-01 11:09
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('settings', '0002_auto_20180301_1047'),
]
operations = [
migrations.AlterField(
model_name='sitesettings',
name='event_poster',
field=models.ImageField(blank=True, null=True, upload_to='', verbose_name="Affiche de l'événement"),
),
migrations.AlterField(
model_name='sitesettings',
name='site_logo',
field=models.ImageField(blank=True, null=True, upload_to='', verbose_name='Logo du site'),
),
]

View file

@ -1,7 +1,5 @@
from django.db import models from django.db import models
from .aes_field import AESEncryptedField
class SiteSettings(models.Model): class SiteSettings(models.Model):
PRETTY_NAME = "Réglages du site" PRETTY_NAME = "Réglages du site"
@ -23,11 +21,17 @@ class SiteSettings(models.Model):
null=True, null=True,
blank=True blank=True
) )
min_number_of_categories = models.PositiveIntegerField(
verbose_name="Nombre minimal de catégories dans laquelle participer",
default=0,
)
@classmethod @classmethod
def get_settings(cls): def get_settings(cls):
return cls.objects.get_or_create()[0] return cls.objects.get_or_create()[0]
class StaticPage(models.Model):
name = models.CharField(
max_length=255,
verbose_name="Titre de la catégorie",
)
text = models.TextField(
verbose_name="Texte de la catégorie"
)

View file

@ -24,7 +24,7 @@
<td>{{admin.last_name}}</td> <td>{{admin.last_name}}</td>
<td>{{admin.username}}</td> <td>{{admin.username}}</td>
<td> <td>
<a class="btn btn-outline-danger btn-sm" href="{% url 'settings:degrade-user' admin.pk %}"> <a class="btn btn-outline-danger btn-sm" href="{% url 'settings:degrade-user' admin.pk %}">
<i class="fa fa-trash"></i> <i class="fa fa-trash"></i>
Enlever le privilège Administrateur Enlever le privilège Administrateur
</a> </a>
@ -65,6 +65,33 @@
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
<h2>Pages statiques</h2>
<a class="btn btn-success btn-sm" role="button" href="{% url 'settings:staticpage-new' %}">
<i class="fas fa-plus"></i>
Créer une nouvelle page statique
</a>
<br/>
<br/>
<table class="table table-striped">
<tr>
<th>Nom</th>
<th></th>
</tr>
{% for p in static_pages %}
<tr>
<td><a href="{% url 'settings:staticpage' p.pk %}">{{p.name}}</a></td>
<td><a class="btn btn-outline-primary btn-sm" href="{% url 'settings:staticpage-edit' p.pk %}">
<i class="fas fa-edit"></i>
Éditer
</a>
<a class="btn btn-outline-danger btn-sm" title="Supprimer" href="{% url 'settings:staticpage-delete' p.pk %}">
<i class="fas fa-trash-alt"></i>
Supprimer
</a>
</td>
</tr>
{% endfor %}
</table>
<h2>Écoles</h2> <h2>Écoles</h2>
<a class="btn btn-success btn-sm" role="button" href="{% url 'users:new-school' %}"> <a class="btn btn-success btn-sm" role="button" href="{% url 'users:new-school' %}">
@ -76,30 +103,52 @@
<table class="table table-striped"> <table class="table table-striped">
<tr> <tr>
<th>Nom</th> <th>Nom</th>
<th>Nombre de membres</th>
<th>Nombre de contenus</th> <th>Nombre de contenus</th>
<th>Nombre de catégories</th> <th>Nombre de catégories</th>
<th>Numéro de téléphone</th> <th>Numéro de téléphone</th>
<th>Inscription</th>
<th>Administrateur</th>
<th></th> <th></th>
{% for school in schools %} {% for school in schools %}
<tr> <tr>
<th><a href="{{school.get_absolute_url}}">{{school.group.name}}</a></th> <th><a href="{{school.get_absolute_url}}">{{school.name}}</a></th>
<td>{{school.group.user_set.count}}</td>
<td>{{school.content_set.count}}</td> <td>{{school.content_set.count}}</td>
<td>{{school.number_of_categories}}</td> <td>{{school.number_of_categories}}</td>
<td>{{school.phone}}</td> <td>{{school.phone}}</td>
<td><a class="btn btn-outline-primary btn-sm" href="{% url "users:edit-school-name" pk=school.group.pk%}"> <td>
<i class="fas fa-edit"></i> {% if school.validated %}
Éditer <span class="badge badge-success">
</a> <i class="fa fa-check"></i>
<a class="btn btn-outline-danger btn-sm" title="Supprimer" href=""> Inscription validée
<i class="fas fa-trash-alt"></i> </span>
Supprimer {% else %}
</a> <span class="badge badge-danger">
<i class="fa fa-exclamation"></i>
Inscription non validée
</span>
{% endif %}
</td>
<td>
{% if school.admin %}
{{school.admin.first_name}} {{school.admin.last_name}} ({{school.admin.username}})
{% else %}
Non défini
{% endif %}
</td>
<td>
<a href="{% url 'users:edit-admin' school.pk %}" class="btn btn-outline-primary btn-sm">
<i class="fa fa-edit"></i>
Définir
</a>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
<h2>Utilisateurs</h2>
<a class="btn btn-success btn-lg" href="{% url 'users:new-user' %}">
<i class="fa fa-plus"></i>
Ajouter un utilisateur
</a>
<h2>Réglages</h2> <h2>Réglages</h2>
<a class="btn btn-primary btn-sm" href="{% url 'settings:site-settings' %}"> <a class="btn btn-primary btn-sm" href="{% url 'settings:site-settings' %}">
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
@ -142,9 +191,5 @@
{% endif %} {% endif %}
</td> </td>
</tr> </tr>
<tr>
<th>Nombre minimal de catégories</th>
<td>{{ site_settings.min_number_of_categories }}</td>
</tr>
</table> </table>
{% endblock %} {% endblock %}

View file

@ -0,0 +1,5 @@
{% extends "base.html" %}
{% block content %}
<h1>{{object.name}}</h1>
{{object.text|safe}}
{% endblock %}

View file

@ -1,5 +1,5 @@
from django import template from django import template
from settings.models import SiteSettings from settings.models import SiteSettings, StaticPage
register = template.Library() register = template.Library()
@ -9,7 +9,18 @@ def load_site_settings(parser, token):
return LoadSiteSettingsNode() return LoadSiteSettingsNode()
@register.tag('load_static_pages')
def load_static_pages(parser, token):
return LoadStaticPagesNode()
class LoadSiteSettingsNode(template.Node): class LoadSiteSettingsNode(template.Node):
def render(self, context): def render(self, context):
context['site_settings'] = SiteSettings.get_settings() context['site_settings'] = SiteSettings.get_settings()
return '' return ''
class LoadStaticPagesNode(template.Node):
def render(self, context):
context['static_pages'] = StaticPage.objects.all()
return ''

View file

@ -1,5 +1,5 @@
from django.urls import path from django.urls import path
from .views import SettingsView, EditSiteSettingsView, degrade_user, promote_user from .views import SettingsView, EditSiteSettingsView, degrade_user, promote_user, CreateStaticPageView, StaticPageView, DeleteStaticPageView, EditStaticPageView
app_name = 'settings' app_name = 'settings'
urlpatterns = [ urlpatterns = [
@ -22,6 +22,25 @@ urlpatterns = [
'promote_user', 'promote_user',
promote_user, promote_user,
name='promote-user', name='promote-user',
),
path(
'static_page/new',
CreateStaticPageView.as_view(),
name='staticpage-new'
),
path(
'static_page/<int:pk>',
StaticPageView.as_view(),
name='staticpage'
),
path(
'static_page/<int:pk>/delete',
DeleteStaticPageView.as_view(),
name='staticpage-delete'
),
path(
'static_page/<int:pk>/edit',
EditStaticPageView.as_view(),
name='staticpage-edit'
) )
] ]

View file

@ -1,14 +1,14 @@
from django.views.generic import TemplateView, UpdateView from django.views.generic import TemplateView, UpdateView, CreateView, DetailView, DeleteView
from django.urls import reverse_lazy, reverse from django.urls import reverse_lazy, reverse
from django.contrib.auth.mixins import PermissionRequiredMixin, LoginRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin, LoginRequiredMixin
from django.contrib.auth.decorators import permission_required from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.models import Group, User from django.contrib.auth.models import Group, User
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.contrib import messages from django.contrib import messages
from content.models import Category from content.models import Category
from users.models import SchoolProfile from users.models import School
from .models import SiteSettings from .models import SiteSettings, StaticPage
from .forms import SelectUserForm from .forms import SelectUserForm
@ -20,11 +20,13 @@ class SettingsView(LoginRequiredMixin, PermissionRequiredMixin, TemplateView):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['categories'] = Category.objects.all() context['categories'] = Category.objects.all()
context['site_settings'], _ = SiteSettings.objects.get_or_create() context['site_settings'], _ = SiteSettings.objects.get_or_create()
context['schools'] = SchoolProfile.objects.all() context['schools'] = School.objects.all()
context['settings'] = True context['settings'] = True
context['administrators'] = Group.objects.get(name='admins').user_set.all() context['administrators'] = User.objects.filter(is_staff=True)
context['static_pages'] = StaticPage.objects.all()
return context return context
class EditSiteSettingsView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): class EditSiteSettingsView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
template_name = "edit.html" template_name = "edit.html"
model = SiteSettings model = SiteSettings
@ -42,24 +44,82 @@ class EditSiteSettingsView(LoginRequiredMixin, PermissionRequiredMixin, UpdateVi
return context return context
@permission_required('auth.change_user') class CreateStaticPageView(LoginRequiredMixin, CreateView):
template_name = "edit.html"
model = StaticPage
fields = '__all__'
success_url = reverse_lazy('settings:index')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["title"] = "Création de page statique"
context["validate"] = "Créer"
return context
@classmethod
def as_view(self, *args, **kwargs):
view = super().as_view(*args, **kwargs)
return staff_member_required(view)
class StaticPageView(DetailView):
template_name = "settings/static_page.html"
model = StaticPage
fields = '__all__'
@classmethod
def as_view(self, *args, **kwargs):
view = super().as_view(*args, **kwargs)
return staff_member_required(view)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['page'] = self.object
return context
class DeleteStaticPageView(DeleteView):
template_name = "confirm_delete.html"
model = StaticPage
success_url = reverse_lazy('settings:index')
@classmethod
def as_view(self, *args, **kwargs):
view = super().as_view(*args, **kwargs)
return staff_member_required(view)
class EditStaticPageView(UpdateView):
template_name = "edit.html"
model = StaticPage
success_url = reverse_lazy('settings:index')
fields = '__all__'
@classmethod
def as_view(self, *args, **kwargs):
view = super().as_view(*args, **kwargs)
return staff_member_required(view)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["title"] = "Édition de page statique"
context["validate"] = "Éditer"
return context
@staff_member_required
def degrade_user(request, pk): def degrade_user(request, pk):
user = get_object_or_404(User, pk=pk) user = get_object_or_404(User, pk=pk)
admins,_ = Group.objects.get_or_create(name='admins') user.is_staff = False
user.groups.remove(admins)
user.save() user.save()
messages.success(request, user.username + ' a été enlevé des administrateurs du site') messages.success(request, user.username + ' a été enlevé des administrateurs du site')
return redirect(reverse('settings:index')) return redirect(reverse('settings:index'))
@permission_required('auth.change_user') @staff_member_required
def promote_user(request): def promote_user(request):
user_form = SelectUserForm(request.POST or None) user_form = SelectUserForm(request.POST or None)
user_form.populate() user_form.populate()
if user_form.is_valid(): if user_form.is_valid():
user=user_form.get_user() user=user_form.get_user()
admins,_ = Group.objects.get_or_create(name='admins') user.is_staff = True
user.groups.add(admins)
user.save() user.save()
messages.success(request, user.username + ' a été ajouté des administrateurs du site') messages.success(request, user.username + ' a été ajouté des administrateurs du site')
return redirect(reverse('settings:index')) return redirect(reverse('settings:index'))

View file

@ -1,16 +1,18 @@
"""
WSGI config for site_tps project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/
"""
import os import os
import sys
VIRTUALENV_LOC = '/var/www/site_tps/env_site'
# Activation de l'environnement virtuel
activate_env=os.path.join(VIRTUALENV_LOC, 'bin/activate_this.py')
exec(compile(open(activate_env, "rb").read(), activate_env, 'exec'), {'__file__':activate_env})
# Ajout du répertoire du site au PATH
sys.path.append('/var/www/site_tps')
sys.path.append('/var/www/site_tps/site_tps')
# Les trucs par défaut de Django
from django.core.wsgi import get_wsgi_application from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "site_tps.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "site_tps.settings")
application = get_wsgi_application() application = get_wsgi_application()

Binary file not shown.

BIN
static/Western Dead.ttf Normal file

Binary file not shown.

View file

@ -28,11 +28,12 @@ body {
@font-face { @font-face {
font-family: 'Valeria'; font-family: 'Valeria';
font-style: normal; font-style: normal;
src: url("/static/ValeriaBoldGrunge.ttf"); src: url("/static/Western Dead.ttf");
} }
h1.site-title h1.site-title
{ {
font-family: 'Valeria'; font-family: 'Valeria';
font-size: 5rem;
} }
{% block style %}{% endblock %} {% block style %}{% endblock %}
</style> </style>

View file

@ -1,6 +1,6 @@
{% extends 'base.html'%} {% extends 'base.html'%}
{% block content %} {% block content %}
<h1 class="site-title">IL ETAIT UNE FOIS DANS L'EST</h1> <h1 class="site-title text-center">IL ETAIT UNE FOIS DANS L'EST</h1>
<br/> <br/>
<br/> <br/>
<div class="row"> <div class="row">

View file

@ -2,6 +2,7 @@
{% load load_settings %} {% load load_settings %}
{% load_categories %} {% load_categories %}
{% load_site_settings %} {% load_site_settings %}
{% load_static_pages %}
<nav class="navbar navbar-expand-lg navbar-light bg-light"> <nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="{% url "home"%}"> <a class="navbar-brand" href="{% url "home"%}">
{% if site_settings.site_logo %} {% if site_settings.site_logo %}
@ -17,14 +18,20 @@
{% for c in categories %} {% for c in categories %}
<li class="nav-item <li class="nav-item
{% if category.pk == c.pk %}active{%endif%}"> {% if category.pk == c.pk %}active{%endif%}">
<a class="nav-link" href="{% url 'content:category-list' c.pk %}">{{c.name}} <a class="nav-link" href="{% url 'content:category' c.pk %}">{{c.name}}
</a></li>
{% endfor %}
{% for p in static_pages %}
<li class="nav-item
{% if page.pk == p.pk %}active{%endif%}">
<a class="nav-link" href="{% url 'settings:staticpage' p.pk %}">{{p.name}}
</a></li> </a></li>
{% endfor %} {% endfor %}
</ul> </ul>
<ul class="navbar-nav ml-auto"> <ul class="navbar-nav ml-auto">
{% if request.user.userprofile.school %} {% if request.user.school %}
<li class="nav-item {% if school %}active{% endif %}"><a class="nav-link" href="{% url 'users:school' request.user.userprofile.school.group.pk %}"><i class="fas fa-graduation-cap"></i> Mon école</a></li> <li class="nav-item {% if school %}active{% endif %}"><a class="nav-link" href="{% url 'users:school' request.user.school.pk %}"><i class="fas fa-graduation-cap"></i> Mon école</a></li>
{% endif %} {% endif %}
<li class="nav-item {% if settings %}active{% endif %}"><a class="nav-link" href="{% url 'settings:index' %}"><i class="fas fa-cogs"></i> Administration</a></li> <li class="nav-item {% if settings %}active{% endif %}"><a class="nav-link" href="{% url 'settings:index' %}"><i class="fas fa-cogs"></i> Administration</a></li>
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}
@ -33,16 +40,15 @@
{{request.user}} {{request.user}}
</a> </a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown"> <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="{% url 'users:profile' request.user.pk%}"><i class="fa fa-user"></i> Accéder à mon profil</a>
<a class="dropdown-item" href="{% url 'users:logout' %}"><i class="fa fa-sign-out-alt"></i> Se déconnecter</a> <a class="dropdown-item" href="{% url 'users:logout' %}"><i class="fa fa-sign-out-alt"></i> Se déconnecter</a>
</div> </div>
</li> </li>
{% else %} {% else %}
<li class="nav-item {% if active == 4 %}active{% endif %}"> <li class="nav-item {% if active == 4 %}active{% endif %}">
<a class="nav-link" href="{% url 'users:login' %}">Connexion<span class="sr-only">(current)</span></a> <a class="nav-link" href="{% url 'users:login' %}">Connexion<span class="sr-only">(current)</span></a>
</li> </li>
<li class="nav-item"> <li>
<a class="nav-link" href="{% url 'users:new-user' %}">Inscription<span class="sr-only">(current)</span></a> <a class="nav-link" href="{% url "users:password-reset" %}">(Mot de passe oublié)</a>
</li> </li>
{% endif %} {% endif %}

View file

@ -1,44 +1,6 @@
from django.contrib import admin from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin from .models import School
from django.contrib.auth.admin import GroupAdmin as BaseGroupAdmin
from django.contrib.auth.models import User, Group
from .models import UserProfile, SchoolProfile class SchoolAdmin(admin.ModelAdmin):
pass
# Define an inline admin descriptor for Employee model admin.site.register(School, SchoolAdmin)
# which acts a bit like a singleton
class UserInline(admin.StackedInline):
model = UserProfile
can_delete = False
verbose_name_plural = 'user profiles'
# Define a new User admin
class UserAdmin(BaseUserAdmin):
inlines = (UserInline, )
# Define an inline admin descriptor for Employee model
# which acts a bit like a singleton
class SchoolInline(admin.StackedInline):
model = SchoolProfile
can_delete = False
verbose_name_plural = 'schools'
fk_name = 'admins'
# Define a new User admin
class GroupAdmin(BaseGroupAdmin):
inlines = (SchoolInline, )
# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
admin.site.unregister(Group)
admin.site.register(Group, GroupAdmin)

72
users/forms.py Normal file
View file

@ -0,0 +1,72 @@
from django import forms
from django.contrib.auth.models import User
from .models import School
class CreateSchool(forms.ModelForm):
class Meta:
model = School
fields = ['name', 'admin']
class EditName(forms.ModelForm):
class Meta:
model = School
fields = ['name']
class CreateUser(forms.ModelForm):
class Meta:
model = User
fields = [
'username',
'first_name',
'last_name',
'email',
'groups',
'password',
]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['password'].widget = forms.PasswordInput()
class EditPhone(forms.ModelForm):
class Meta:
model = School
fields = ['phone']
class EditLogo(forms.ModelForm):
class Meta:
model = School
fields = ['logo']
class EditMail(forms.ModelForm):
class Meta:
model = User
fields = ['email']
class EditJury1(forms.ModelForm):
class Meta:
model = School
fields = [
'first_name_j1',
'last_name_j1',
'phone_j1',
'mail_j1'
]
class EditJury2(forms.ModelForm):
class Meta:
model = School
fields = [
'first_name_j2',
'last_name_j2',
'phone_j2',
'mail_j2'
]

View file

@ -1,8 +1,10 @@
# Generated by Django 2.0.1 on 2018-02-28 18:43 # Generated by Django 2.0.1 on 2018-03-08 22:03
from django.conf import settings from django.conf import settings
import django.core.validators
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
import users.models
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -10,24 +12,25 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
('auth', '0009_alter_user_last_name_max_length'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
] ]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='SchoolProfile', name='School',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('group', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='students', to='auth.Group')), ('phone', models.CharField(help_text='Visible uniquement des administrateurs', max_length=10, null=True, validators=[django.core.validators.RegexValidator('^[0-9]{10}$', 'Veuillez entrer un numéro à 10 chiffres.')], verbose_name='Numéro de téléphone pour contacter le responsable des productions')),
], ('logo', models.ImageField(null=True, upload_to=users.models.get_upload_to, verbose_name="Logo à utiliser pour représenter l'école")),
), ('first_name_j1', models.CharField(max_length=255, verbose_name='Prénom juré n°1')),
migrations.CreateModel( ('last_name_j1', models.CharField(max_length=255, verbose_name='Nom juré n°1')),
name='UserProfile', ('phone_j1', models.CharField(help_text='Visible uniquement des administrateurs', max_length=10, null=True, validators=[django.core.validators.RegexValidator('^[0-9]{10}$', 'Veuillez entrer un numéro à 10 chiffres.')], verbose_name='Numéro de téléphone juré n°1')),
fields=[ ('mail_j1', models.EmailField(max_length=254, verbose_name='Email juré n°1')),
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('first_name_j2', models.CharField(max_length=255, verbose_name='Prénom juré n°2')),
('school', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='users.SchoolProfile')), ('last_name_j2', models.CharField(max_length=255, verbose_name='Nom juré n°2')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ('phone_j2', models.CharField(help_text='Visible uniquement des administrateurs', max_length=10, null=True, validators=[django.core.validators.RegexValidator('^[0-9]{10}$', 'Veuillez entrer un numéro à 10 chiffres.')], verbose_name='Numéro de téléphone juré n°2')),
('mail_j2', models.EmailField(max_length=254, verbose_name='Email juré n°2')),
('admin', models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name="Administrateur de l'école")),
], ],
), ),
] ]

View file

@ -0,0 +1,18 @@
# Generated by Django 2.0.1 on 2018-03-09 08:55
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='school',
name='validated',
field=models.BooleanField(default=False, verbose_name='Inscription validé.'),
),
]

View file

@ -1,20 +0,0 @@
# Generated by Django 2.0.1 on 2018-03-01 07:16
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('auth', '0009_alter_user_last_name_max_length'),
('users', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='schoolprofile',
name='admins',
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='admin_of', to='auth.Group'),
),
]

View file

@ -1,19 +0,0 @@
# Generated by Django 2.0.1 on 2018-03-01 09:33
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('users', '0002_schoolprofile_admins'),
]
operations = [
migrations.AlterField(
model_name='schoolprofile',
name='group',
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='school', to='auth.Group'),
),
]

View file

@ -0,0 +1,18 @@
# Generated by Django 2.0.1 on 2018-03-09 09:07
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0002_school_validated'),
]
operations = [
migrations.AddField(
model_name='school',
name='name',
field=models.CharField(default='', max_length=255, verbose_name="Nom de l'école"),
),
]

View file

@ -1,19 +0,0 @@
# Generated by Django 2.0.1 on 2018-03-01 09:52
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('users', '0003_auto_20180301_0933'),
]
operations = [
migrations.AlterField(
model_name='schoolprofile',
name='group',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='school', to='auth.Group'),
),
]

View file

@ -1,19 +0,0 @@
# Generated by Django 2.0.1 on 2018-03-01 10:29
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('users', '0004_auto_20180301_0952'),
]
operations = [
migrations.AlterField(
model_name='userprofile',
name='school',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='users.SchoolProfile'),
),
]

View file

@ -1,18 +0,0 @@
# Generated by Django 2.0.1 on 2018-03-01 23:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0005_auto_20180301_1029'),
]
operations = [
migrations.AddField(
model_name='schoolprofile',
name='phone',
field=models.CharField(help_text='Visible uniquement des administrateurs', max_length=10, null=True, verbose_name='Numéro de téléphone pour contacter le responsable des productions'),
),
]

View file

@ -7,18 +7,22 @@ from django.dispatch import receiver
from django.core import validators from django.core import validators
class SchoolProfile(models.Model): def get_upload_to(instance, filename):
return "static/media/"+instance.name+"/"+filename
class School(models.Model):
"""Ajoute un champ pour distinguer les groupes écoles des autres.""" """Ajoute un champ pour distinguer les groupes écoles des autres."""
group = models.OneToOneField( name = models.CharField(
Group, verbose_name="Nom de l'école",
on_delete=models.CASCADE, max_length=255,
related_name="school", default=""
) )
admins = models.OneToOneField( admin = models.OneToOneField(
Group, User,
verbose_name="Administrateur de l'école",
null=True,
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
related_name="admin_of",
null=True
) )
phone = models.CharField( phone = models.CharField(
max_length=10, max_length=10,
@ -31,65 +35,64 @@ class SchoolProfile(models.Model):
"Veuillez entrer un numéro à 10 chiffres."), "Veuillez entrer un numéro à 10 chiffres."),
] ]
) )
logo = models.ImageField(
upload_to=get_upload_to,
verbose_name="Logo à utiliser pour représenter l'école",
null=True,
blank=False,
)
validated = models.BooleanField(
verbose_name="Inscription validé.",
default=False
)
first_name_j1 = models.CharField(
max_length=255,
verbose_name="Prénom juré n°1"
)
last_name_j1 = models.CharField(
max_length=255,
verbose_name="Nom juré n°1"
)
phone_j1 = models.CharField(
max_length=10,
help_text="Visible uniquement des administrateurs",
verbose_name="Numéro de téléphone juré n°1",
blank=False,
null=True,
validators=[
validators.RegexValidator('^[0-9]{10}$',
"Veuillez entrer un numéro à 10 chiffres."),
]
)
mail_j1 = models.EmailField(verbose_name="Email juré n°1")
first_name_j2 = models.CharField(
max_length=255,
verbose_name="Prénom juré n°2"
)
last_name_j2 = models.CharField(
max_length=255,
verbose_name="Nom juré n°2"
)
phone_j2 = models.CharField(
max_length=10,
help_text="Visible uniquement des administrateurs",
verbose_name="Numéro de téléphone juré n°2",
blank=False,
null=True,
validators=[
validators.RegexValidator('^[0-9]{10}$',
"Veuillez entrer un numéro à 10 chiffres."),
]
)
mail_j2 = models.EmailField(verbose_name="Email juré n°2")
def __str__(self): def __str__(self):
return self.group.name return self.name
def get_absolute_url(self): def get_absolute_url(self):
return reverse("users:school", kwargs={'pk':self.group.pk}) return reverse("users:school", kwargs={'pk':self.pk})
def number_of_categories(self): def number_of_categories(self):
return self.content_set.values('category').distinct().count() return self.content_set.values('category').distinct().count()
def save(self, *args, **kwargs):
viewing_right, _ = Permission.objects.get_or_create(
codename='view_' + str(self.group.pk),
name='Peut voir ' + str(self.group.pk),
content_type=ContentType.objects.get_for_model(SchoolProfile)
)
if viewing_right not in self.group.permissions.all():
self.group.permissions.add(viewing_right)
self.group.save()
admins,_ = Group.objects.get_or_create(name='admins')
admins.permissions.add(viewing_right)
super().save(*args, **kwargs)
@receiver(post_save, sender=SchoolProfile)
def update_permissions_school(sender, instance, **kwargs):
instance.admins,admin_created = Group.objects.get_or_create(name=str(instance.group.pk)+'_admins')
admin_right,_ = Permission.objects.get_or_create(
codename='manage_' + str(instance.group.pk),
name="Administrateur de l'école " + str(instance.group.pk),
content_type=ContentType.objects.get_for_model(SchoolProfile)
)
admins,_ = Group.objects.get_or_create(name='admins')
admins.permissions.add(admin_right)
if admin_created:
instance.save(update_fields=['admins'])
instance.admins.permissions.add(admin_right)
instance.admins.save()
class UserProfile(models.Model):
"""Profil d'un utilisateur"""
school = models.ForeignKey(SchoolProfile, on_delete=models.SET_NULL, null=True)
user = models.OneToOneField(User, on_delete=models.CASCADE)
@receiver(post_save, sender=UserProfile)
def update_groups(sender, instance, **kwargs):
instance.user.groups.add(instance.school.group)
@receiver(post_save, sender=User)
def update_permission_user(sender, instance, **kwargs):
perm,_ = Permission.objects.get_or_create(
codename='manage_'+str(instance.pk),
name='Peut administrer ' + instance.username,
content_type=ContentType.objects.get_for_model(User)
)
instance.user_permissions.add(perm)
admins,_ = Group.objects.get_or_create(name='admins')
admins.permissions.add(perm)

View file

@ -0,0 +1,14 @@
{% load i18n %}{% autoescape off %}
{% blocktrans %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}.{% endblocktrans %}
{% trans "Please go to the following page and choose a new password:" %}
{% block reset_link %}
{{ protocol }}://festart.rezometz.org{% url 'users:password-reset-confirm' uidb64=uid token=token %}
{% endblock %}
{% trans "Your username, in case you've forgotten:" %} {{ user.get_username }}
{% trans "Thanks for using our site!" %}
{% blocktrans %}The {{ site_name }} team{% endblocktrans %}
{% endautoescape %}

View file

@ -2,61 +2,163 @@
{% load bootstrap4 %} {% load bootstrap4 %}
{% block content %} {% block content %}
<h1>{{object.name}}</h1> {% if school.validated %}
{% if manager_right in perms %} <span class="badge badge-success">
Numéro de téléphone : <i class="fa fa-check"></i>
{% if object.school.phone %}{{object.school.phone}} Inscription validée
<a class="btn btn-primary btn-sm" href="{% url 'users:edit-school-name' object.pk %}"> </span>
{% endif %}
<h1>
{{school.name}}
</h1>
<a class="btn btn-primary btn-sm" href="{% url 'users:password-change' %}">
<i class="fa fa-edit"></i>
Changer mon mot de passe
</a>
<a class="btn btn-primary btn-sm" href="{% url 'users:edit-school-name' school.pk %}">
<i class="fa fa-edit"></i> <i class="fa fa-edit"></i>
Éditer Éditer
</a> </a>
{%else%}
Non indiqué{%endif%}<br/>
{%endif%}
{% if manager_right in perms %}
<h2>Membres</h2>
<table class="table table-striped"> <table class="table table-striped">
<thead> <tr>
<th>Nom</th> <th>
<th>Prénom</th> Numéro de téléphone
<th>Pseudo</th> </th>
<th>Administrer</th>
</thead>
<tbody>
{% for member in members %}
<tr>
<td>{{member.last_name}}</td>
<td>{{member.first_name}}</td>
<td>{{member.username}}</td>
<td> <td>
{% if member in manager_group.user_set.all %} {% if school.phone %}{{school.phone}}{%else%}
<a class="btn btn-outline-danger btn-sm" href="{% url 'users:degrade-user' object.pk member.pk %}"> Non indiqué{%endif%}
<i class="fa fa-trash"></i>
Enlever le privilège Administrateur
</a>
{% else %}
<a class="btn btn-outline-warning btn-sm" href="{% url 'users:promote-user' object.pk member.pk %}">
<i class="fa fa-star"></i>
Promouvoir administrateur
</a>
{% endif %}
</td> </td>
</tr> <td>
{% endfor %} <a class="btn btn-primary btn-sm" href="{% url 'users:edit-school-phone' school.pk %}">
</tbody> <i class="fa fa-edit"></i>
Éditer
</a>
</td>
</tr>
<tr>
<th>
Email
</th>
<td>
{{school.admin.email}}
</td>
<td>
<a class="btn btn-primary btn-sm" href="{% url 'users:edit-school-mail' school.pk %}">
<i class="fa fa-edit"></i>
Éditer
</a>
</td>
</tr>
<tr>
<th>
Juré n°1
</th>
<td>
<table class="table table-striped">
<tr>
<th>
Nom
</th>
<td>
{{school.last_name_j1}}
</td>
</tr>
<tr>
<th>
Prénom
</th>
<td>
{{school.first_name_j1}}
</td>
</tr>
<tr>
<th>
Email
</th>
<td>
{{school.mail_j1}}
</td>
</tr>
<tr>
<th>
N° de téléphone
</th>
<td>
{{school.phone_j1}}
</td>
</tr>
</table>
</td>
<td>
<a class="btn btn-primary btn-sm" href="{% url 'users:edit-jury-1' school.pk %}">
<i class="fa fa-edit"></i>
Éditer
</a>
</td>
</tr>
<tr>
<th>
Juré n°2
</th>
<td>
<table class="table table-striped">
<tr>
<th>
Nom
</th>
<td>
{{school.last_name_j2}}
</td>
</tr>
<tr>
<th>
Prénom
</th>
<td>
{{school.first_name_j2}}
</td>
</tr>
<tr>
<th>
Email
</th>
<td>
{{school.mail_j2}}
</td>
</tr>
<tr>
<th>
N° de téléphone
</th>
<td>
{{school.phone_j2}}
</td>
</tr>
</table>
</td>
<td>
<a class="btn btn-primary btn-sm" href="{% url 'users:edit-jury-2' school.pk %}">
<i class="fa fa-edit"></i>
Éditer
</a>
</td>
</tr>
</table> </table>
{% endif %}
<a class="btn btn-success btn-lg" href="{% url 'users:validate' school.pk %}">
<i class="fa fa-check"></i>
Valider mon inscription
</a>
<h2>Contenus</h2> <h2>Contenus</h2>
{% if manager_right in perms %} <a class="btn btn-success btn-sm" href="{% url 'content:content-new' school.pk %}">
<a class="btn btn-success btn-sm" href="{% url 'content:content-new' %}">
<i class="fa fa-plus"></i> <i class="fa fa-plus"></i>
Ajouter un contenu Ajouter un contenu
</a> </a>
{% endif %}
<br /> <br />
<br /> <br />
<div class="row"> <div class="row">
{% for content in contents %} {% for content in school.content_set.all %}
{% include "content/content.html" %} {% include "content/content.html" %}
{%endfor%} {%endfor%}
</div> </div>

View file

@ -0,0 +1,14 @@
Bienvenue sur le site d'Il était une fois dans l'Est.
Vous recevez cet email car vous avez été désigné responsable des productions
vidéos pour votre école.
Votre identifiant est : {{id}}
Rendez-vous ici : http://festart.rezometz.org/users/reset pour réinitialiser
votre mot de passe (en utilisant cette adresse email).
Vous pouvez retrouver l'ensemble des informations sur l'évènement ici :
http://festart.rezometz.org
L'équipe de Il était une fois dans l'Est.

View file

@ -1,85 +1,93 @@
from django.urls import path from django.urls import path
from .views import ( from django.urls import reverse_lazy
CreateUser, from django.contrib.auth import views as auth_views
CreateUserProfile, from . import views
CreateSchool,
EditSchoolName,
EditSchoolPhone,
DeleteSchool,
Login,
Logout,
PasswordChange,
Profile,
School,
promote_user,
degrade_user
)
app_name = 'users' app_name = 'users'
urlpatterns = [ urlpatterns = [
path( path(
'user/new', 'new',
CreateUser.as_view(), views.create_user,
name='new-user' name='new-user'
), ),
path( path(
'login', 'login',
Login.as_view(), auth_views.LoginView.as_view(template_name="edit.html"),
name='login' name='login'
), ),
path( path(
'logout', 'logout',
Logout.as_view(), auth_views.LogoutView.as_view(),
name='logout', name='logout'
), ),
path( path(
'change_password', 'password_change',
PasswordChange.as_view(), auth_views.PasswordChangeView.as_view(template_name="edit.html"),
name='change-password' name='password-change'
), ),
path( path(
'user/<int:pk>/set_school', 'password_change/done',
CreateUserProfile.as_view(), views.PasswordChangeDoneView.as_view(),
name='create-userprofile' name='password-change-done'
), ),
path( path(
'user/<int:pk>', 'reset',
Profile.as_view(), views.PasswordResetView.as_view(),
name='profile', name='password-reset'
),
path(
'reset/<uidb64>/<token>/',
auth_views.PasswordResetConfirmView.as_view(template_name="edit.html", success_url=reverse_lazy('users:password-reset-done')),
name='password-reset-confirm'
),
path(
'reset/done/',
views.PasswordResetCompleteView.as_view(),
name='password-reset-done'
), ),
path( path(
'school/new', 'school/new',
CreateSchool.as_view(), views.create_school,
name='new-school' name='new-school'
), ),
path( path(
'school/<int:pk>', 'school/<int:pk>',
School.as_view(), views.school,
name='school' name='school',
),
path(
'school/<int:school_pk>/degrade/<int:user_pk>',
degrade_user,
name='degrade-user'
),
path(
'school/<int:school_pk>/promote/<int:user_pk>',
promote_user,
name='promote-user'
),
path(
'school/<int:pk>/edit_name',
EditSchoolName.as_view(),
name='edit-school-name'
), ),
path( path(
'school/<int:pk>/edit_phone', 'school/<int:pk>/edit_phone',
EditSchoolPhone.as_view(), views.edit_phone,
name='edit-school-phone' name='edit-school-phone'
), ),
path( path(
'school/<int:pk>/delete', 'school/<int:pk>/edit_name',
DeleteSchool.as_view(), views.edit_name,
name='delete-school' name='edit-school-name'
), ),
path(
'school/<int:pk>/edit_mail',
views.edit_mail,
name='edit-school-mail'
),
path(
'school/<int:pk>/jury_1',
views.edit_jury_1,
name='edit-jury-1'
),
path(
'school/<int:pk>/jury_2',
views.edit_jury_2,
name='edit-jury-2'
),
path(
'school/<int:pk>/edit_admin',
views.edit_admin,
name='edit-admin'
),
path(
'school/<int:pk>/validate',
views.validate,
name='validate',
)
] ]

View file

@ -1,220 +1,266 @@
from django.contrib.auth.models import User, Group
from django.contrib.auth.mixins import PermissionRequiredMixin, LoginRequiredMixin
from django.views.generic import CreateView, UpdateView, DeleteView, DetailView
from django.contrib.auth.views import LoginView, LogoutView, PasswordChangeView, login_required
from django.contrib.auth.hashers import make_password
from django.contrib.messages.views import SuccessMessageMixin
from django.contrib import messages from django.contrib import messages
from django.urls import reverse, reverse_lazy from django.urls import reverse, reverse_lazy
from django.shortcuts import get_object_or_404, redirect from django.core.mail import send_mail
from django.shortcuts import get_object_or_404, redirect, render
from django.contrib.admin.views.decorators import staff_member_required
from django.template.loader import render_to_string
from django.contrib.auth import views as auth_views
from .models import UserProfile, SchoolProfile from settings.forms import SelectUserForm
from content.models import Content from content.models import Category
from .models import School
from . import forms
class CreateUser(CreateView): @staff_member_required
model = User def create_user(request):
fields = [ user_form = forms.CreateUser(request.POST or None)
'first_name', if user_form.is_valid():
'last_name', u = user_form.save()
'email', send_mail(
'username', "Bienvenue sur Il était une fois dans l'Est.",
'password', render_to_string("users/welcome_user.txt", {'id':u.username}),
] "noreply.festart@rezometz.org",
template_name = 'edit.html' [u.email],
fail_silently=False
def get_success_url(self):
return reverse(
'users:create-userprofile',
kwargs={'pk': self.object.pk}
) )
messages.success(request, "L'utilisateur {} {} a bien été créé un mail lui a été envoyé pour réinitialiser son mot de passe.".format(
u.first_name, u.last_name))
return redirect(reverse('settings:index'))
return render(request, 'edit.html', {
'form': user_form,
'title': "Création d'un utilisateur",
'validate': "Créer"
})
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = "Inscription"
context['validate'] = "S'inscrire"
return context
def form_valid(self, form): @staff_member_required
r = super().form_valid(form) def create_school(request):
self.object.set_password(form.cleaned_data['password']) school_form = forms.CreateSchool(request.POST or None)
self.object.save() if school_form.is_valid():
s = school_form.save()
messages.success(
request, "L'école {} a bien été créée.".format(s.name))
return redirect(reverse('settings:index'))
return render(request, 'edit.html', {
'form': school_form,
'title': "Création d'une école",
'validate': "Créer"
})
def school(request, pk):
school = get_object_or_404(School, pk=pk)
can = request.user.is_staff or request.user == school.admin
if not can:
messages.error(request, 'Vous ne pouvez pas accéder à cette page')
return redirect(reverse('home'))
return render(request, 'users/school.html', {'school': school})
def edit_phone(request, pk):
school = get_object_or_404(School, pk=pk)
can = request.user.is_staff or request.user == school.admin
if not can:
messages.error(request, 'Vous ne pouvez pas accéder à cette page')
return redirect(reverse('home'))
school_form = forms.EditPhone(request.POST or None, instance=school)
if school_form.is_valid():
s = school_form.save()
messages.success(
request, "L'école {} a bien été modifiée.".format(s.name))
return redirect(s.get_absolute_url())
return render(request, 'edit.html', {
'form': school_form,
'title': "Édition du numéro de téléphone",
'validate': "Modifier"
})
def edit_logo(request, pk):
school = get_object_or_404(School, pk=pk)
can = request.user.is_staff or request.user == school.admin
if not can:
messages.error(request, 'Vous ne pouvez pas accéder à cette page')
return redirect(reverse('home'))
school_form = forms.EditLogo(request.POST or None, instance=school)
if school_form.is_valid():
s = school_form.save()
messages.success(
request, "L'école {} a bien été modifiée.".format(s.name))
return redirect(s.get_absolute_url())
return render(request, 'edit.html', {
'form': school_form,
'title': "Édition du logo",
'validate': "Modifier"
})
def edit_mail(request, pk):
school = get_object_or_404(School, pk=pk)
can = request.user.is_staff or request.user == school.admin
if not can:
messages.error(request, 'Vous ne pouvez pas accéder à cette page')
return redirect(reverse('home'))
user_form = forms.EditMail(request.POST or None, instance=school.admin)
if user_form.is_valid():
s = user_form.save()
messages.success(
request, "L'école {} a bien été modifiée.".format(s.school.name))
return redirect(s.school.get_absolute_url())
return render(request, 'edit.html', {
'form': user_form,
'title': "Édition du numéro du mail",
'validate': "Modifier"
})
def edit_jury_1(request, pk):
school = get_object_or_404(School, pk=pk)
can = request.user.is_staff or request.user == school.admin
if not can:
messages.error(request, 'Vous ne pouvez pas accéder à cette page')
return redirect(reverse('home'))
school_form = forms.EditJury1(request.POST or None, instance=school)
if school_form.is_valid():
s = school_form.save()
messages.success(
request, "L'école {} a bien été modifiée.".format(s.name))
return redirect(s.get_absolute_url())
return render(request, 'edit.html', {
'form': school_form,
'title': "Édition du jury 1",
'validate': "Modifier"
})
def edit_jury_2(request, pk):
school = get_object_or_404(School, pk=pk)
can = request.user.is_staff or request.user == school.admin
if not can:
messages.error(request, 'Vous ne pouvez pas accéder à cette page')
return redirect(reverse('home'))
school_form = forms.EditJury2(request.POST or None, instance=school)
if school_form.is_valid():
s = school_form.save()
messages.success(
request, "L'école {} a bien été modifiée.".format(s.name))
return redirect(s.get_absolute_url())
return render(request, 'edit.html', {
'form': school_form,
'title': "Édition du jury 2",
'validate': "Modifier"
})
def edit_name(request, pk):
school = get_object_or_404(School, pk=pk)
can = request.user.is_staff or request.user == school.admin
if not can:
messages.error(request, 'Vous ne pouvez pas accéder à cette page')
return redirect(reverse('home'))
school_form = forms.EditName(request.POST or None, instance=school)
if school_form.is_valid():
s = school_form.save()
messages.success(
request, "L'école {} a bien été modifiée.".format(s.name))
return redirect(s.get_absolute_url())
return render(request, 'edit.html', {
'form': school_form,
'title': "Édition du nom",
'validate': "Modifier"
})
@staff_member_required
def edit_admin(request, pk):
school = get_object_or_404(School, pk=pk)
user_form = SelectUserForm(request.POST or None)
user_form.populate()
if user_form.is_valid():
user = user_form.get_user()
school.admin = user
school.save()
user.save()
messages.success(request, user.username +
' a été nommé admin de ' + school.name)
return redirect(reverse('settings:index'))
return render(request, 'edit.html', {
'form': user_form,
'title': "Définir l'administrateur de {}".format(school.name),
'validate': 'Ajouter'
})
def validate(request, pk):
school = get_object_or_404(School, pk=pk)
can = request.user.is_staff or request.user == school.admin
if not can:
messages.error(request, 'Vous ne pouvez pas accéder à cette page')
return redirect(reverse('home'))
jury_1_ok = any([
school.first_name_j1,
school.last_name_j1,
school.phone_j1,
school.mail_j1
])
jury_2_ok = any([
school.first_name_j2,
school.last_name_j2,
school.phone_j2,
school.mail_j2
])
logo_ok = school.logo
phone_ok = school.phone
mail_ok = school.admin.email
if not jury_1_ok:
messages.warning(request, 'Pas de jury n°1 défini.')
if not jury_2_ok:
messages.warning(request, 'Pas de jury n°2 défini.')
if not logo_ok:
messages.warning(request, 'Pas de logo défini.')
if not phone_ok:
messages.error(request, 'Pas de téléphone défini.')
if not mail_ok:
messages.error(request, 'Pas de mail défini.')
for category in Category.objects.all():
if not category.content_set.filter(school_owner=school):
messages.warning(
request, 'Pas de contenu dans la catégorie {}.'.format(category.name))
if phone_ok and mail_ok:
school.validated = True
school.save()
messages.success(request, 'Inscription validée.')
return redirect(school.get_absolute_url())
class PasswordChangeDoneView(auth_views.PasswordChangeDoneView):
template_name = "home.html"
def dispatch(self, *args, **kwargs):
r = super().dispatch(*args, **kwargs)
messages.success(self.request, "Le mot de passe a été changé.")
return r return r
class Profile(LoginRequiredMixin, UpdateView):
model = User
template_name = 'users/profile.html'
fields = [
'username',
'first_name',
'last_name',
'email'
]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = self.object.username
context['validate'] = "Modifier"
return context
def get_success_url(self):
return reverse(
'users:profile',
kwargs={'pk': self.object.pk}
)
class CreateUserProfile(CreateView):
model = UserProfile
fields = ['school']
template_name = 'edit.html'
class PasswordResetView(auth_views.PasswordResetView):
template_name = "edit.html"
success_url = reverse_lazy('home') success_url = reverse_lazy('home')
email_template_name = "users/password_reset_mail.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = "Choix de l'école"
context['validate'] = "Choisir"
return context
def form_valid(self, form): def form_valid(self, form):
form.instance.user = get_object_or_404(User, pk=self.kwargs['pk']) messages.success(self.request, "Un mail pour le changement de mot de passe a été envoyé.")
return super(CreateUserProfile, self).form_valid(form) return super().form_valid(form)
class CreateSchool(LoginRequiredMixin, PermissionRequiredMixin, CreateView): class PasswordResetCompleteView(auth_views.PasswordResetCompleteView):
permission_required = 'users.add_schoolprofile' template_name = "home.html"
model = Group
fields = ['name']
template_name = 'edit.html'
success_url = reverse_lazy('settings:index')
def get_context_data(self, **kwargs): def dispatch(self, *args, **kwargs):
context = super().get_context_data(**kwargs) r = super().dispatch(*args, **kwargs)
context['title'] = "Création de l'école" messages.success(self.request, "Votre mot de passe a été réinitialisé.")
context['validate'] = "Créer"
return context
def form_valid(self, form):
response = super(CreateSchool, self).form_valid(form)
profile = SchoolProfile()
profile.group = form.instance
profile.save()
return response
class EditSchoolName(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
model = Group
fields = ['name']
template_name = 'edit.html'
queryset = Group.objects.filter(school__isnull=False)
def get_success_url(self):
return reverse('users:edit-school-phone', kwargs={'pk':self.object.school.pk})
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = "Édition de l'école"
context['validate'] = "Modifier"
return context
def has_permission(self):
return self.request.user.has_perm('users.manage_'+str(self.kwargs['pk']))
def form_valid(self, *args, **kwargs):
r = super().form_valid(*args, **kwargs)
self.object.school.save()
return r return r
class EditSchoolPhone(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
model = SchoolProfile
fields = ['phone']
template_name = 'edit.html'
def get_success_url(self):
return reverse('users:school', kwargs={'pk':self.object.group.pk})
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = "Édition de l'école"
context['validate'] = "Modifier"
return context
def has_permission(self):
return self.request.user.has_perm('users.manage_'+str(self.kwargs['pk']))
class DeleteSchool(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
model = Group
permission_required = 'users.delete_schoolprofile'
queryset = Group.objects.filter(school__isnull=False)
class School(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
model = Group
template_name = "users/school.html"
queryset = Group.objects.filter(school__isnull=False)
def get_context_data(self, **kwargs):
context = super().get_context_data()
context['contents'] = Content.objects.filter(school_owner=self.object.school)
context['school'] = True
context['members'] = User.objects.filter(userprofile__school=self.object.school)
context['manager_right'] = 'users.manage_' + str(self.object.pk)
context['manager_group'],_ = Group.objects.get_or_create(name=str(self.object.pk)+'_admins')
return context
def has_permission(self):
return self.request.user.has_perm('users.view_'+str(self.kwargs['pk']))
class Logout(SuccessMessageMixin, LogoutView):
success_message = "Vous vous êtes bien déconnecté."
class Login(SuccessMessageMixin, LoginView):
template_name = "edit.html"
success_message = "Bienvenue !"
extra_context = {
'title' : "Connexion",
'validate' : "Se connecter",
}
class PasswordChange(SuccessMessageMixin, PasswordChangeView):
template_name = "edit.html"
success_url = reverse_lazy("home")
success_message = "Le mot de passe a été changé."
extra_context = {
'title' : "Changer le mot de passe",
'validate' : "Changer",
}
@login_required
def promote_user(request, school_pk, user_pk):
school = get_object_or_404(Group, pk=school_pk)
user = get_object_or_404(User, pk=user_pk)
if request.user.has_perm('manage_'+str(school.pk)):
admins,_ = Group.objects.get_or_create(name=str(school.pk)+'_admins')
user.groups.add(admins)
user.save()
messages.success(request, user.username + ' a été ajouté aux administrateurs de ' + school.name)
return redirect(reverse('users:school', kwargs={'pk':school.pk}))
messages.error(request, "Vous n'aves pas ce droit.")
return redirect('home')
@login_required
def degrade_user(request, school_pk, user_pk):
school = get_object_or_404(Group, pk=school_pk)
user = get_object_or_404(User, pk=user_pk)
if request.user.has_perm('manage_'+str(school.pk)):
admins,_ = Group.objects.get_or_create(name=str(school.pk)+'_admins')
user.groups.remove(admins)
user.save()
messages.success(request, user.username + ' a été enlevé des administrateurs de ' + school.name)
return redirect(reverse('users:school', kwargs={'pk':school.pk}))
messages.error(request, "Vous n'aves pas ce droit.")
return redirect('home')