Merge branch 'master' of https://gitlab.rezometz.org/klafyvel/site_tps
This commit is contained in:
commit
7404de04e3
46 changed files with 1150 additions and 747 deletions
30
content/forms.py
Normal file
30
content/forms.py
Normal 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'
|
||||
]
|
||||
|
|
@ -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
|
||||
import django.db.models.deletion
|
||||
|
||||
|
@ -9,6 +10,7 @@ class Migration(migrations.Migration):
|
|||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('users', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
@ -26,8 +28,9 @@ class Migration(migrations.Migration):
|
|||
fields=[
|
||||
('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')),
|
||||
('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')),
|
||||
('school_owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.School')),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
|
23
content/migrations/0002_auto_20180309_1116.py
Normal file
23
content/migrations/0002_auto_20180309_1116.py
Normal 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'),
|
||||
),
|
||||
]
|
|
@ -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'),
|
||||
),
|
||||
]
|
19
content/migrations/0003_auto_20180309_1233.py
Normal file
19
content/migrations/0003_auto_20180309_1233.py
Normal 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'),
|
||||
),
|
||||
]
|
19
content/migrations/0004_auto_20180309_1235.py
Normal file
19
content/migrations/0004_auto_20180309_1235.py
Normal 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'),
|
||||
),
|
||||
]
|
18
content/migrations/0005_auto_20180309_1255.py
Normal file
18
content/migrations/0005_auto_20180309_1255.py
Normal 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'),
|
||||
),
|
||||
]
|
19
content/migrations/0006_auto_20180309_1257.py
Normal file
19
content/migrations/0006_auto_20180309_1257.py
Normal 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'),
|
||||
),
|
||||
]
|
25
content/migrations/0007_auto_20180309_1315.py
Normal file
25
content/migrations/0007_auto_20180309_1315.py
Normal 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'),
|
||||
),
|
||||
]
|
|
@ -1,9 +1,11 @@
|
|||
from django.db import models
|
||||
from django.urls import reverse
|
||||
from django.contrib.auth.models import Group
|
||||
from django.conf import settings
|
||||
import os
|
||||
|
||||
from django.db import models
|
||||
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):
|
||||
"""Une catégorie de contenu."""
|
||||
|
@ -11,21 +13,40 @@ class Category(models.Model):
|
|||
max_length=255,
|
||||
verbose_name="Nom de la catégorie"
|
||||
)
|
||||
description_short = models.TextField(
|
||||
verbose_name="Description courte",
|
||||
null=True,
|
||||
)
|
||||
description = models.TextField(
|
||||
verbose_name="Descriton de la catégorie",
|
||||
verbose_name="Description de la catégorie",
|
||||
default=""
|
||||
)
|
||||
image = models.ImageField(
|
||||
verbose_name="Illustration de la catégorie",
|
||||
null=True,
|
||||
)
|
||||
|
||||
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):
|
||||
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):
|
||||
"""Un contenu du site (vidéo)."""
|
||||
name = models.CharField(
|
||||
|
@ -33,7 +54,7 @@ class Content(models.Model):
|
|||
verbose_name="Nom du contenu"
|
||||
)
|
||||
school_owner = models.ForeignKey(
|
||||
SchoolProfile,
|
||||
School,
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
category = models.ForeignKey(
|
||||
|
@ -43,11 +64,10 @@ class Content(models.Model):
|
|||
null=True
|
||||
)
|
||||
file = models.FileField(
|
||||
verbose_name="Fichier"
|
||||
verbose_name="Fichier",
|
||||
validators=[validate_file_extension],
|
||||
upload_to=get_upload_to
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def manager_right(self):
|
||||
return 'users.manage_' + str(self.school_owner.group.pk)
|
||||
|
|
|
@ -5,15 +5,13 @@
|
|||
</video>
|
||||
<div class="card-body">
|
||||
<h2 class="display-5">{{content.name}}</h2>
|
||||
<p class="lead">Contenu proposé par {{content.school_owner.group.name}}</p>
|
||||
{% if content.manager_right in perms %}
|
||||
<p class="lead">Catégorie : {{content.category.name}}</p>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<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-danger" href="{% url "content:content-delete" content.pk %}" ><i class="fa fa-trash"></i> Supprimer</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -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="col-md-5 p-lg-5 mx-auto my-5 title-block">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="row text-center" id="category-content">
|
||||
{% for content in contents %}
|
||||
{% include "content/content.html" %}
|
||||
{% endfor %}
|
||||
<div id="category-content">
|
||||
{{category.description|safe}}
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -1,22 +1,17 @@
|
|||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
from .views import (
|
||||
ContentCategoryList,
|
||||
CreateCategory,
|
||||
ViewCategory,
|
||||
DeleteCategory,
|
||||
EditCategory,
|
||||
CreateContent,
|
||||
DeleteContent,
|
||||
EditContent,
|
||||
)
|
||||
|
||||
app_name = 'content'
|
||||
urlpatterns = [
|
||||
path(
|
||||
'category/<int:pk>/',
|
||||
ContentCategoryList.as_view(),
|
||||
name='category-list'
|
||||
),
|
||||
path(
|
||||
'category/delete/<int:pk>',
|
||||
DeleteCategory.as_view(),
|
||||
|
@ -28,13 +23,18 @@ urlpatterns = [
|
|||
name='category-new'
|
||||
),
|
||||
path(
|
||||
'category/edit/<int:pk>',
|
||||
'category/<int:pk>',
|
||||
ViewCategory.as_view(),
|
||||
name='category',
|
||||
),
|
||||
path(
|
||||
'category/<int:pk>/edit',
|
||||
EditCategory.as_view(),
|
||||
name='category-edit',
|
||||
),
|
||||
path(
|
||||
'new',
|
||||
CreateContent.as_view(),
|
||||
'new/<int:school_pk>',
|
||||
views.create_content,
|
||||
name='content-new',
|
||||
),
|
||||
path(
|
||||
|
@ -44,7 +44,7 @@ urlpatterns = [
|
|||
),
|
||||
path(
|
||||
'<int:pk>/edit',
|
||||
EditContent.as_view(),
|
||||
views.edit_content,
|
||||
name="content-edit",
|
||||
),
|
||||
|
||||
|
|
128
content/views.py
128
content/views.py
|
@ -1,31 +1,21 @@
|
|||
from django.views import generic
|
||||
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.admin.views.decorators import staff_member_required
|
||||
from django.contrib import messages
|
||||
|
||||
from .models import Content, Category
|
||||
from users.models import School
|
||||
from . import forms
|
||||
from settings.models import SiteSettings
|
||||
|
||||
|
||||
class ContentCategoryList(generic.ListView):
|
||||
"""Affiche les contenus d'une catégorie."""
|
||||
model = Content
|
||||
context_object_name = "contents"
|
||||
class ViewCategory(generic.DetailView):
|
||||
"""Affiche une catégorie."""
|
||||
model = Category
|
||||
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):
|
||||
"""Création de catégorie."""
|
||||
|
@ -60,39 +50,6 @@ class EditCategory(PermissionRequiredMixin, generic.UpdateView):
|
|||
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):
|
||||
"""Suppression de contenu"""
|
||||
model = Content
|
||||
|
@ -103,34 +60,53 @@ class DeleteContent(PermissionRequiredMixin, generic.DeleteView):
|
|||
|
||||
def has_permission(self):
|
||||
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):
|
||||
"""Édition d'un contenu"""
|
||||
model = Content
|
||||
template_name = "edit.html"
|
||||
fields = [
|
||||
'name',
|
||||
'category',
|
||||
'file'
|
||||
]
|
||||
template_name = "edit.html"
|
||||
extra_context = {
|
||||
'title' : 'Édition de contenu',
|
||||
'validate' : 'Envoyer'
|
||||
}
|
||||
def create_content(request, school_pk):
|
||||
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"))
|
||||
school = get_object_or_404(School, pk=school_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'))
|
||||
|
||||
def get_success_url(self):
|
||||
return self.object.school_owner.get_absolute_url()
|
||||
content_form = forms.CreateContent(school, request.POST or None, request.FILES or None)
|
||||
content_form.instance.school_owner = school
|
||||
|
||||
def has_permission(self):
|
||||
school = get_object_or_404(Content, pk=self.kwargs['pk']).school_owner
|
||||
return self.request.user.has_perm('users.manage_'+str(school.group.pk))
|
||||
if content_form.is_valid():
|
||||
content_form.save()
|
||||
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()
|
||||
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)
|
||||
|
||||
def edit_content(request, pk):
|
||||
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"))
|
||||
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'
|
||||
})
|
||||
|
|
|
@ -15,5 +15,5 @@ class SelectUserForm(forms.Form):
|
|||
|
||||
def populate(self):
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -17,6 +17,16 @@ class Migration(migrations.Migration):
|
|||
('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.")),
|
||||
('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')),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
|
|
@ -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'),
|
||||
),
|
||||
]
|
|
@ -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'),
|
||||
),
|
||||
]
|
|
@ -1,7 +1,5 @@
|
|||
from django.db import models
|
||||
|
||||
from .aes_field import AESEncryptedField
|
||||
|
||||
|
||||
class SiteSettings(models.Model):
|
||||
PRETTY_NAME = "Réglages du site"
|
||||
|
@ -23,11 +21,17 @@ class SiteSettings(models.Model):
|
|||
null=True,
|
||||
blank=True
|
||||
)
|
||||
min_number_of_categories = models.PositiveIntegerField(
|
||||
verbose_name="Nombre minimal de catégories dans laquelle participer",
|
||||
default=0,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_settings(cls):
|
||||
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"
|
||||
)
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
<td>{{admin.last_name}}</td>
|
||||
<td>{{admin.username}}</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>
|
||||
Enlever le privilège Administrateur
|
||||
</a>
|
||||
|
@ -65,6 +65,33 @@
|
|||
</tr>
|
||||
{% endfor %}
|
||||
</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>
|
||||
<a class="btn btn-success btn-sm" role="button" href="{% url 'users:new-school' %}">
|
||||
|
@ -76,30 +103,52 @@
|
|||
<table class="table table-striped">
|
||||
<tr>
|
||||
<th>Nom</th>
|
||||
<th>Nombre de membres</th>
|
||||
<th>Nombre de contenus</th>
|
||||
<th>Nombre de catégories</th>
|
||||
<th>Numéro de téléphone</th>
|
||||
<th>Inscription</th>
|
||||
<th>Administrateur</th>
|
||||
<th></th>
|
||||
{% for school in schools %}
|
||||
<tr>
|
||||
<th><a href="{{school.get_absolute_url}}">{{school.group.name}}</a></th>
|
||||
<td>{{school.group.user_set.count}}</td>
|
||||
<th><a href="{{school.get_absolute_url}}">{{school.name}}</a></th>
|
||||
<td>{{school.content_set.count}}</td>
|
||||
<td>{{school.number_of_categories}}</td>
|
||||
<td>{{school.phone}}</td>
|
||||
<td><a class="btn btn-outline-primary btn-sm" href="{% url "users:edit-school-name" pk=school.group.pk%}">
|
||||
<i class="fas fa-edit"></i>
|
||||
Éditer
|
||||
</a>
|
||||
<a class="btn btn-outline-danger btn-sm" title="Supprimer" href="">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
Supprimer
|
||||
</a>
|
||||
<td>
|
||||
{% if school.validated %}
|
||||
<span class="badge badge-success">
|
||||
<i class="fa fa-check"></i>
|
||||
Inscription validée
|
||||
</span>
|
||||
{% else %}
|
||||
<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>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</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>
|
||||
<a class="btn btn-primary btn-sm" href="{% url 'settings:site-settings' %}">
|
||||
<i class="fas fa-edit"></i>
|
||||
|
@ -142,9 +191,5 @@
|
|||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Nombre minimal de catégories</th>
|
||||
<td>{{ site_settings.min_number_of_categories }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
{% endblock %}
|
||||
|
|
5
settings/templates/settings/static_page.html
Normal file
5
settings/templates/settings/static_page.html
Normal file
|
@ -0,0 +1,5 @@
|
|||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<h1>{{object.name}}</h1>
|
||||
{{object.text|safe}}
|
||||
{% endblock %}
|
|
@ -1,5 +1,5 @@
|
|||
from django import template
|
||||
from settings.models import SiteSettings
|
||||
from settings.models import SiteSettings, StaticPage
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
@ -9,7 +9,18 @@ def load_site_settings(parser, token):
|
|||
return LoadSiteSettingsNode()
|
||||
|
||||
|
||||
@register.tag('load_static_pages')
|
||||
def load_static_pages(parser, token):
|
||||
return LoadStaticPagesNode()
|
||||
|
||||
|
||||
class LoadSiteSettingsNode(template.Node):
|
||||
def render(self, context):
|
||||
context['site_settings'] = SiteSettings.get_settings()
|
||||
return ''
|
||||
|
||||
|
||||
class LoadStaticPagesNode(template.Node):
|
||||
def render(self, context):
|
||||
context['static_pages'] = StaticPage.objects.all()
|
||||
return ''
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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'
|
||||
urlpatterns = [
|
||||
|
@ -22,6 +22,25 @@ urlpatterns = [
|
|||
'promote_user',
|
||||
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'
|
||||
)
|
||||
|
||||
]
|
||||
|
|
|
@ -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.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.shortcuts import get_object_or_404, redirect, render
|
||||
from django.contrib import messages
|
||||
|
||||
from content.models import Category
|
||||
from users.models import SchoolProfile
|
||||
from .models import SiteSettings
|
||||
from users.models import School
|
||||
from .models import SiteSettings, StaticPage
|
||||
from .forms import SelectUserForm
|
||||
|
||||
|
||||
|
@ -20,11 +20,13 @@ class SettingsView(LoginRequiredMixin, PermissionRequiredMixin, TemplateView):
|
|||
context = super().get_context_data(**kwargs)
|
||||
context['categories'] = Category.objects.all()
|
||||
context['site_settings'], _ = SiteSettings.objects.get_or_create()
|
||||
context['schools'] = SchoolProfile.objects.all()
|
||||
context['schools'] = School.objects.all()
|
||||
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
|
||||
|
||||
|
||||
class EditSiteSettingsView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
||||
template_name = "edit.html"
|
||||
model = SiteSettings
|
||||
|
@ -42,24 +44,82 @@ class EditSiteSettingsView(LoginRequiredMixin, PermissionRequiredMixin, UpdateVi
|
|||
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):
|
||||
user = get_object_or_404(User, pk=pk)
|
||||
admins,_ = Group.objects.get_or_create(name='admins')
|
||||
user.groups.remove(admins)
|
||||
user.is_staff = False
|
||||
user.save()
|
||||
messages.success(request, user.username + ' a été enlevé des administrateurs du site')
|
||||
return redirect(reverse('settings:index'))
|
||||
|
||||
|
||||
@permission_required('auth.change_user')
|
||||
@staff_member_required
|
||||
def promote_user(request):
|
||||
user_form = SelectUserForm(request.POST or None)
|
||||
user_form.populate()
|
||||
if user_form.is_valid():
|
||||
user=user_form.get_user()
|
||||
admins,_ = Group.objects.get_or_create(name='admins')
|
||||
user.groups.add(admins)
|
||||
user.is_staff = True
|
||||
user.save()
|
||||
messages.success(request, user.username + ' a été ajouté des administrateurs du site')
|
||||
return redirect(reverse('settings:index'))
|
||||
|
|
|
@ -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 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
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "site_tps.settings")
|
||||
|
||||
application = get_wsgi_application()
|
||||
|
|
Binary file not shown.
BIN
static/Western Dead.ttf
Normal file
BIN
static/Western Dead.ttf
Normal file
Binary file not shown.
|
@ -28,11 +28,12 @@ body {
|
|||
@font-face {
|
||||
font-family: 'Valeria';
|
||||
font-style: normal;
|
||||
src: url("/static/ValeriaBoldGrunge.ttf");
|
||||
src: url("/static/Western Dead.ttf");
|
||||
}
|
||||
h1.site-title
|
||||
{
|
||||
font-family: 'Valeria';
|
||||
font-size: 5rem;
|
||||
}
|
||||
{% block style %}{% endblock %}
|
||||
</style>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{% extends 'base.html'%}
|
||||
{% 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/>
|
||||
<div class="row">
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
{% load load_settings %}
|
||||
{% load_categories %}
|
||||
{% load_site_settings %}
|
||||
{% load_static_pages %}
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<a class="navbar-brand" href="{% url "home"%}">
|
||||
{% if site_settings.site_logo %}
|
||||
|
@ -17,14 +18,20 @@
|
|||
{% for c in categories %}
|
||||
<li class="nav-item
|
||||
{% 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>
|
||||
{% endfor %}
|
||||
|
||||
</ul>
|
||||
<ul class="navbar-nav ml-auto">
|
||||
{% if request.user.userprofile.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>
|
||||
{% if request.user.school %}
|
||||
<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 %}
|
||||
<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 %}
|
||||
|
@ -33,16 +40,15 @@
|
|||
{{request.user}}
|
||||
</a>
|
||||
<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>
|
||||
</div>
|
||||
</li>
|
||||
{% else %}
|
||||
<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 class="nav-item">
|
||||
<a class="nav-link" href="{% url 'users:new-user' %}">Inscription<span class="sr-only">(current)</span></a>
|
||||
<li>
|
||||
<a class="nav-link" href="{% url "users:password-reset" %}">(Mot de passe oublié)</a>
|
||||
</li>
|
||||
|
||||
{% endif %}
|
||||
|
|
|
@ -1,44 +1,6 @@
|
|||
from django.contrib import admin
|
||||
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
||||
from django.contrib.auth.admin import GroupAdmin as BaseGroupAdmin
|
||||
from django.contrib.auth.models import User, Group
|
||||
from .models import School
|
||||
|
||||
from .models import UserProfile, SchoolProfile
|
||||
|
||||
# Define an inline admin descriptor for Employee model
|
||||
# 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)
|
||||
class SchoolAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
admin.site.register(School, SchoolAdmin)
|
||||
|
|
72
users/forms.py
Normal file
72
users/forms.py
Normal 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'
|
||||
]
|
|
@ -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
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import users.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
@ -10,24 +12,25 @@ class Migration(migrations.Migration):
|
|||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('auth', '0009_alter_user_last_name_max_length'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='SchoolProfile',
|
||||
name='School',
|
||||
fields=[
|
||||
('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')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='UserProfile',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('school', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='users.SchoolProfile')),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
('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')),
|
||||
('last_name_j1', models.CharField(max_length=255, verbose_name='Nom juré n°1')),
|
||||
('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')),
|
||||
('mail_j1', models.EmailField(max_length=254, 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(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")),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
|
18
users/migrations/0002_school_validated.py
Normal file
18
users/migrations/0002_school_validated.py
Normal 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é.'),
|
||||
),
|
||||
]
|
|
@ -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'),
|
||||
),
|
||||
]
|
|
@ -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'),
|
||||
),
|
||||
]
|
18
users/migrations/0003_school_name.py
Normal file
18
users/migrations/0003_school_name.py
Normal 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"),
|
||||
),
|
||||
]
|
|
@ -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'),
|
||||
),
|
||||
]
|
|
@ -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'),
|
||||
),
|
||||
]
|
|
@ -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'),
|
||||
),
|
||||
]
|
129
users/models.py
129
users/models.py
|
@ -7,18 +7,22 @@ from django.dispatch import receiver
|
|||
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."""
|
||||
group = models.OneToOneField(
|
||||
Group,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="school",
|
||||
name = models.CharField(
|
||||
verbose_name="Nom de l'école",
|
||||
max_length=255,
|
||||
default=""
|
||||
)
|
||||
admins = models.OneToOneField(
|
||||
Group,
|
||||
admin = models.OneToOneField(
|
||||
User,
|
||||
verbose_name="Administrateur de l'école",
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name="admin_of",
|
||||
null=True
|
||||
)
|
||||
phone = models.CharField(
|
||||
max_length=10,
|
||||
|
@ -31,65 +35,64 @@ class SchoolProfile(models.Model):
|
|||
"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):
|
||||
return self.group.name
|
||||
return self.name
|
||||
|
||||
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):
|
||||
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)
|
||||
|
||||
|
|
14
users/templates/users/password_reset_mail.html
Normal file
14
users/templates/users/password_reset_mail.html
Normal 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 %}
|
|
@ -2,61 +2,163 @@
|
|||
{% load bootstrap4 %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{{object.name}}</h1>
|
||||
{% if manager_right in perms %}
|
||||
Numéro de téléphone :
|
||||
{% if object.school.phone %}{{object.school.phone}}
|
||||
<a class="btn btn-primary btn-sm" href="{% url 'users:edit-school-name' object.pk %}">
|
||||
{% if school.validated %}
|
||||
<span class="badge badge-success">
|
||||
<i class="fa fa-check"></i>
|
||||
Inscription validée
|
||||
</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>
|
||||
Éditer
|
||||
</a>
|
||||
{%else%}
|
||||
Non indiqué{%endif%}<br/>
|
||||
{%endif%}
|
||||
{% if manager_right in perms %}
|
||||
<h2>Membres</h2>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<th>Nom</th>
|
||||
<th>Prénom</th>
|
||||
<th>Pseudo</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>
|
||||
<tr>
|
||||
<th>
|
||||
Numéro de téléphone
|
||||
</th>
|
||||
<td>
|
||||
{% if member in manager_group.user_set.all %}
|
||||
<a class="btn btn-outline-danger btn-sm" href="{% url 'users:degrade-user' object.pk member.pk %}">
|
||||
<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 %}
|
||||
{% if school.phone %}{{school.phone}}{%else%}
|
||||
Non indiqué{%endif%}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<td>
|
||||
<a class="btn btn-primary btn-sm" href="{% url 'users:edit-school-phone' school.pk %}">
|
||||
<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>
|
||||
{% 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>
|
||||
{% if manager_right in perms %}
|
||||
<a class="btn btn-success btn-sm" href="{% url 'content:content-new' %}">
|
||||
<a class="btn btn-success btn-sm" href="{% url 'content:content-new' school.pk %}">
|
||||
<i class="fa fa-plus"></i>
|
||||
Ajouter un contenu
|
||||
</a>
|
||||
{% endif %}
|
||||
<br />
|
||||
<br />
|
||||
<div class="row">
|
||||
{% for content in contents %}
|
||||
{% for content in school.content_set.all %}
|
||||
{% include "content/content.html" %}
|
||||
{%endfor%}
|
||||
</div>
|
||||
|
|
14
users/templates/users/welcome_user.txt
Normal file
14
users/templates/users/welcome_user.txt
Normal 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.
|
110
users/urls.py
110
users/urls.py
|
@ -1,85 +1,93 @@
|
|||
from django.urls import path
|
||||
from .views import (
|
||||
CreateUser,
|
||||
CreateUserProfile,
|
||||
CreateSchool,
|
||||
EditSchoolName,
|
||||
EditSchoolPhone,
|
||||
DeleteSchool,
|
||||
Login,
|
||||
Logout,
|
||||
PasswordChange,
|
||||
Profile,
|
||||
School,
|
||||
promote_user,
|
||||
degrade_user
|
||||
)
|
||||
from django.urls import reverse_lazy
|
||||
from django.contrib.auth import views as auth_views
|
||||
from . import views
|
||||
|
||||
app_name = 'users'
|
||||
urlpatterns = [
|
||||
path(
|
||||
'user/new',
|
||||
CreateUser.as_view(),
|
||||
'new',
|
||||
views.create_user,
|
||||
name='new-user'
|
||||
),
|
||||
path(
|
||||
'login',
|
||||
Login.as_view(),
|
||||
auth_views.LoginView.as_view(template_name="edit.html"),
|
||||
name='login'
|
||||
),
|
||||
path(
|
||||
'logout',
|
||||
Logout.as_view(),
|
||||
name='logout',
|
||||
auth_views.LogoutView.as_view(),
|
||||
name='logout'
|
||||
),
|
||||
path(
|
||||
'change_password',
|
||||
PasswordChange.as_view(),
|
||||
name='change-password'
|
||||
'password_change',
|
||||
auth_views.PasswordChangeView.as_view(template_name="edit.html"),
|
||||
name='password-change'
|
||||
),
|
||||
path(
|
||||
'user/<int:pk>/set_school',
|
||||
CreateUserProfile.as_view(),
|
||||
name='create-userprofile'
|
||||
'password_change/done',
|
||||
views.PasswordChangeDoneView.as_view(),
|
||||
name='password-change-done'
|
||||
),
|
||||
path(
|
||||
'user/<int:pk>',
|
||||
Profile.as_view(),
|
||||
name='profile',
|
||||
'reset',
|
||||
views.PasswordResetView.as_view(),
|
||||
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(
|
||||
'school/new',
|
||||
CreateSchool.as_view(),
|
||||
views.create_school,
|
||||
name='new-school'
|
||||
),
|
||||
path(
|
||||
'school/<int:pk>',
|
||||
School.as_view(),
|
||||
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'
|
||||
views.school,
|
||||
name='school',
|
||||
),
|
||||
path(
|
||||
'school/<int:pk>/edit_phone',
|
||||
EditSchoolPhone.as_view(),
|
||||
views.edit_phone,
|
||||
name='edit-school-phone'
|
||||
),
|
||||
path(
|
||||
'school/<int:pk>/delete',
|
||||
DeleteSchool.as_view(),
|
||||
name='delete-school'
|
||||
'school/<int:pk>/edit_name',
|
||||
views.edit_name,
|
||||
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',
|
||||
)
|
||||
]
|
||||
|
|
450
users/views.py
450
users/views.py
|
@ -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.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 content.models import Content
|
||||
from settings.forms import SelectUserForm
|
||||
from content.models import Category
|
||||
|
||||
from .models import School
|
||||
from . import forms
|
||||
|
||||
|
||||
class CreateUser(CreateView):
|
||||
model = User
|
||||
fields = [
|
||||
'first_name',
|
||||
'last_name',
|
||||
'email',
|
||||
'username',
|
||||
'password',
|
||||
]
|
||||
template_name = 'edit.html'
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse(
|
||||
'users:create-userprofile',
|
||||
kwargs={'pk': self.object.pk}
|
||||
@staff_member_required
|
||||
def create_user(request):
|
||||
user_form = forms.CreateUser(request.POST or None)
|
||||
if user_form.is_valid():
|
||||
u = user_form.save()
|
||||
send_mail(
|
||||
"Bienvenue sur Il était une fois dans l'Est.",
|
||||
render_to_string("users/welcome_user.txt", {'id':u.username}),
|
||||
"noreply.festart@rezometz.org",
|
||||
[u.email],
|
||||
fail_silently=False
|
||||
)
|
||||
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):
|
||||
r = super().form_valid(form)
|
||||
self.object.set_password(form.cleaned_data['password'])
|
||||
self.object.save()
|
||||
@staff_member_required
|
||||
def create_school(request):
|
||||
school_form = forms.CreateSchool(request.POST or None)
|
||||
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
|
||||
|
||||
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')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['title'] = "Choix de l'école"
|
||||
context['validate'] = "Choisir"
|
||||
return context
|
||||
email_template_name = "users/password_reset_mail.html"
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.user = get_object_or_404(User, pk=self.kwargs['pk'])
|
||||
return super(CreateUserProfile, self).form_valid(form)
|
||||
messages.success(self.request, "Un mail pour le changement de mot de passe a été envoyé.")
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class CreateSchool(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
||||
permission_required = 'users.add_schoolprofile'
|
||||
model = Group
|
||||
fields = ['name']
|
||||
template_name = 'edit.html'
|
||||
success_url = reverse_lazy('settings:index')
|
||||
class PasswordResetCompleteView(auth_views.PasswordResetCompleteView):
|
||||
template_name = "home.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['title'] = "Création de l'école"
|
||||
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()
|
||||
def dispatch(self, *args, **kwargs):
|
||||
r = super().dispatch(*args, **kwargs)
|
||||
messages.success(self.request, "Votre mot de passe a été réinitialisé.")
|
||||
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')
|
||||
|
|
Loading…
Reference in a new issue