mirror of
https://gitlab.crans.org/mediatek/med.git
synced 2024-11-27 00:47:10 +00:00
Initial commit, projet med
This commit is contained in:
parent
287d60db54
commit
9501a10eac
17
README.md
17
README.md
@ -11,7 +11,7 @@ Ce portail minimaliste permet aux utilisateurs de s'identifier. Leurs mac sont c
|
|||||||
|
|
||||||
## Installation des dépendances
|
## Installation des dépendances
|
||||||
|
|
||||||
L'installation comporte 2 parties : le serveur web où se trouve le depot portail_captif ainsi que toutes ses dépendances, et le serveur bdd (mysql ou pgsql). Ces 2 serveurs peuvent en réalité être la même machine, ou séparés (recommandé en production).
|
L'installation comporte 2 parties : le serveur web où se trouve le depot med ainsi que toutes ses dépendances, et le serveur bdd (mysql ou pgsql). Ces 2 serveurs peuvent en réalité être la même machine, ou séparés (recommandé en production).
|
||||||
Le serveur web sera nommé serveur A, le serveur bdd serveur B .
|
Le serveur web sera nommé serveur A, le serveur bdd serveur B .
|
||||||
|
|
||||||
### Prérequis sur le serveur A
|
### Prérequis sur le serveur A
|
||||||
@ -49,19 +49,19 @@ Sur le serveur B, installer mysql ou postgresql, dans la version jessie ou stret
|
|||||||
|
|
||||||
### Installation sur le serveur principal A
|
### Installation sur le serveur principal A
|
||||||
|
|
||||||
Cloner le dépot portail_captif à partir du gitlab, par exemple dans /var/www/portail_captif.
|
Cloner le dépot med à partir du gitlab, par exemple dans /var/www/med.
|
||||||
Ensuite, il faut créer le fichier settings_local.py dans le sous dossier portail_captif, un settings_local.example.py est présent. Les options sont commentées, et des options par défaut existent.
|
Ensuite, il faut créer le fichier settings_local.py dans le sous dossier med, un settings_local.example.py est présent. Les options sont commentées, et des options par défaut existent.
|
||||||
|
|
||||||
En particulier, il est nécessaire de générer un login/mdp admin pour le ldap et un login/mdp pour l'utilisateur sql (cf ci-dessous), à mettre dans settings_local.py
|
En particulier, il est nécessaire de générer un login/mdp admin pour le ldap et un login/mdp pour l'utilisateur sql (cf ci-dessous), à mettre dans settings_local.py
|
||||||
|
|
||||||
### Installation du serveur mysql/postgresql sur B
|
### Installation du serveur mysql/postgresql sur B
|
||||||
|
|
||||||
Sur le serveur mysql ou postgresl, il est nécessaire de créer une base de donnée portail_captif, ainsi qu'un user portail_captif et un mot de passe associé. Ne pas oublier de faire écouter le serveur mysql ou postgresql avec les acl nécessaire pour que A puisse l'utiliser.
|
Sur le serveur mysql ou postgresl, il est nécessaire de créer une base de donnée med, ainsi qu'un user med et un mot de passe associé. Ne pas oublier de faire écouter le serveur mysql ou postgresql avec les acl nécessaire pour que A puisse l'utiliser.
|
||||||
|
|
||||||
Voici les étapes à éxecuter pour mysql :
|
Voici les étapes à éxecuter pour mysql :
|
||||||
* CREATE DATABASE portail_captif;
|
* CREATE DATABASE med;
|
||||||
* CREATE USER 'newuser'@'localhost' IDENTIFIED BY 'password';
|
* CREATE USER 'newuser'@'localhost' IDENTIFIED BY 'password';
|
||||||
* GRANT ALL PRIVILEGES ON portail_captif.* TO 'newuser'@'localhost';
|
* GRANT ALL PRIVILEGES ON med.* TO 'newuser'@'localhost';
|
||||||
* FLUSH PRIVILEGES;
|
* FLUSH PRIVILEGES;
|
||||||
|
|
||||||
Si les serveurs A et B ne sont pas la même machine, il est nécessaire de remplacer localhost par l'ip avec laquelle A contacte B dans les commandes du dessus.
|
Si les serveurs A et B ne sont pas la même machine, il est nécessaire de remplacer localhost par l'ip avec laquelle A contacte B dans les commandes du dessus.
|
||||||
@ -71,7 +71,7 @@ Une fois ces commandes effectuées, ne pas oublier de vérifier que newuser et p
|
|||||||
|
|
||||||
Normalement à cette étape, le ldap et la bdd sql sont configurées correctement.
|
Normalement à cette étape, le ldap et la bdd sql sont configurées correctement.
|
||||||
|
|
||||||
Il faut alors lancer dans le dépot portail_captif '''python3 manage.py migrate''' qui va structurer initialement la base de données.
|
Il faut alors lancer dans le dépot med '''python3 manage.py migrate''' qui va structurer initialement la base de données.
|
||||||
Les migrations sont normalement comitées au fur et à mesure, néanmoins cette étape peut crasher, merci de reporter les bugs.
|
Les migrations sont normalement comitées au fur et à mesure, néanmoins cette étape peut crasher, merci de reporter les bugs.
|
||||||
|
|
||||||
## Démarer le site web
|
## Démarer le site web
|
||||||
@ -81,13 +81,12 @@ Pour apache2 :
|
|||||||
* apt install apache2
|
* apt install apache2
|
||||||
* apt install libapache2-mod-wsgi-py3 (pour le module wsgi)
|
* apt install libapache2-mod-wsgi-py3 (pour le module wsgi)
|
||||||
|
|
||||||
portail_captif/wsgi.py permet de fonctionner avec apache2 en production
|
med/wsgi.py permet de fonctionner avec apache2 en production
|
||||||
|
|
||||||
Pour nginx :
|
Pour nginx :
|
||||||
* apt install nginx
|
* apt install nginx
|
||||||
* apt install gunicorn3
|
* apt install gunicorn3
|
||||||
|
|
||||||
Utilisez alors un site nginx qui proxifie vers une socket gunicorn. Ensuite, utilisez les fichier portail_captif.service et portail_captif.socket avec systemd pour lancer le sous process gunicorn, présents dans portail_captif/ .
|
|
||||||
|
|
||||||
## Configuration avancée
|
## Configuration avancée
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "portail_captif.settings")
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "med.settings")
|
||||||
|
|
||||||
from django.core.management import execute_from_command_line
|
from django.core.management import execute_from_command_line
|
||||||
|
|
||||||
|
@ -24,9 +24,11 @@ from .settings import SITE_NAME
|
|||||||
|
|
||||||
def context_user(request):
|
def context_user(request):
|
||||||
user = request.user
|
user = request.user
|
||||||
is_admin = user.is_admin if hasattr(user,'is_admin') else False
|
is_perm = user.has_perms(['perm'])
|
||||||
|
is_bureau = user.has_perms(['bureau'])
|
||||||
return {
|
return {
|
||||||
'is_admin' : is_admin,
|
'is_perm' : is_perm,
|
||||||
|
'is_bureau': is_bureau,
|
||||||
'request_user': user,
|
'request_user': user,
|
||||||
'site_name': SITE_NAME,
|
'site_name': SITE_NAME,
|
||||||
}
|
}
|
@ -21,7 +21,7 @@
|
|||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Django settings for portail_captif project.
|
Django settings for med project.
|
||||||
|
|
||||||
Generated by 'django-admin startproject' using Django 1.8.13.
|
Generated by 'django-admin startproject' using Django 1.8.13.
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|||||||
# Auth definition
|
# Auth definition
|
||||||
|
|
||||||
PASSWORD_HASHERS = (
|
PASSWORD_HASHERS = (
|
||||||
'portail_captif.login.SSHAPasswordHasher',
|
'med.login.SSHAPasswordHasher',
|
||||||
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
|
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -65,7 +65,9 @@ INSTALLED_APPS = (
|
|||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
'bootstrap3',
|
'bootstrap3',
|
||||||
'users',
|
'users',
|
||||||
'portail_captif',
|
'med',
|
||||||
|
'media',
|
||||||
|
'search',
|
||||||
'reversion'
|
'reversion'
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -80,7 +82,7 @@ MIDDLEWARE_CLASSES = (
|
|||||||
'django.middleware.security.SecurityMiddleware',
|
'django.middleware.security.SecurityMiddleware',
|
||||||
)
|
)
|
||||||
|
|
||||||
ROOT_URLCONF = 'portail_captif.urls'
|
ROOT_URLCONF = 'med.urls'
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
@ -96,13 +98,13 @@ TEMPLATES = [
|
|||||||
'django.contrib.auth.context_processors.auth',
|
'django.contrib.auth.context_processors.auth',
|
||||||
'django.contrib.messages.context_processors.messages',
|
'django.contrib.messages.context_processors.messages',
|
||||||
'django.template.context_processors.request',
|
'django.template.context_processors.request',
|
||||||
'portail_captif.context_processors.context_user',
|
'med.context_processors.context_user',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
WSGI_APPLICATION = 'portail_captif.wsgi.application'
|
WSGI_APPLICATION = 'med.wsgi.application'
|
||||||
|
|
||||||
|
|
||||||
# Internationalization
|
# Internationalization
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "portail_captif/sidebar.html" %}
|
{% extends "med/sidebar.html" %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
se veut agnostique au réseau considéré, de manière à être installable en
|
se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
@ -29,7 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>Historique de {{ object }}</h2>
|
<h2>Historique de {{ object }}</h2>
|
||||||
{% include "portail_captif/aff_history.html" with reversions=reversions %}
|
{% include "med/aff_history.html" with reversions=reversions %}
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
38
med/templates/med/index.html
Normal file
38
med/templates/med/index.html
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{% extends "med/sidebar.html" %}
|
||||||
|
{% comment %}
|
||||||
|
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
quelques clics.
|
||||||
|
|
||||||
|
Copyright © 2017 Gabriel Détraz
|
||||||
|
Copyright © 2017 Goulven Kermarec
|
||||||
|
Copyright © 2017 Augustin Lemesle
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
|
||||||
|
{% block title %}Accueil{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>Bienvenue sur {{ site_name }} !</h1>
|
||||||
|
|
||||||
|
<h1>Liens utiles</h1>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -20,7 +20,7 @@
|
|||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
"""portail_captif URL Configuration
|
"""med URL Configuration
|
||||||
|
|
||||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||||
https://docs.djangoproject.com/en/1.8/topics/http/urls/
|
https://docs.djangoproject.com/en/1.8/topics/http/urls/
|
||||||
@ -46,4 +46,6 @@ urlpatterns = [
|
|||||||
url('^', include('django.contrib.auth.urls')),
|
url('^', include('django.contrib.auth.urls')),
|
||||||
url(r'^admin/', include(admin.site.urls)),
|
url(r'^admin/', include(admin.site.urls)),
|
||||||
url(r'^users/', include('users.urls', namespace='users')),
|
url(r'^users/', include('users.urls', namespace='users')),
|
||||||
|
url(r'^media/', include('media.urls', namespace='media')),
|
||||||
|
url(r'^search/', include('search.urls', namespace='search')),
|
||||||
]
|
]
|
@ -24,7 +24,7 @@ from django.shortcuts import render
|
|||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.template.context_processors import csrf
|
from django.template.context_processors import csrf
|
||||||
from django.template import Context, RequestContext, loader
|
from django.template import Context, RequestContext, loader
|
||||||
from portail_captif.settings import services_urls
|
from med.settings import services_urls
|
||||||
|
|
||||||
def form(ctx, template, request):
|
def form(ctx, template, request):
|
||||||
c = ctx
|
c = ctx
|
||||||
@ -40,4 +40,4 @@ def index(request):
|
|||||||
services[i][key] = s
|
services[i][key] = s
|
||||||
i = i + 1 if i < 2 else 0
|
i = i + 1 if i < 2 else 0
|
||||||
|
|
||||||
return form({'services_urls': services}, 'portail_captif/index.html', request)
|
return form({'services_urls': services}, 'med/index.html', request)
|
@ -21,7 +21,7 @@
|
|||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
WSGI config for portail_captif project.
|
WSGI config for med project.
|
||||||
|
|
||||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||||
|
|
||||||
@ -34,9 +34,8 @@ from django.core.wsgi import get_wsgi_application
|
|||||||
from os.path import dirname
|
from os.path import dirname
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
# On démarre le système du portail
|
|
||||||
|
|
||||||
sys.path.append(dirname(dirname(__file__)))
|
sys.path.append(dirname(dirname(__file__)))
|
||||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "portail_captif.settings")
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "med.settings")
|
||||||
|
|
||||||
application = get_wsgi_application()
|
application = get_wsgi_application()
|
0
media/__init__.py
Normal file
0
media/__init__.py
Normal file
20
media/admin.py
Normal file
20
media/admin.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
from reversion.admin import VersionAdmin
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.contrib.auth.models import Group
|
||||||
|
|
||||||
|
from .models import Auteur, Emprunt, Media
|
||||||
|
|
||||||
|
class AuteurAdmin(VersionAdmin):
|
||||||
|
list_display = ('nom',)
|
||||||
|
|
||||||
|
class MediaAdmin(VersionAdmin):
|
||||||
|
list_display = ('titre','cote')
|
||||||
|
|
||||||
|
class EmpruntAdmin(VersionAdmin):
|
||||||
|
list_display = ('media','user','date_emprunt', 'date_rendu', 'permanencier_emprunt', 'permanencier_rendu')
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.register(Auteur, AuteurAdmin)
|
||||||
|
admin.site.register(Media, MediaAdmin)
|
||||||
|
admin.site.register(Emprunt, EmpruntAdmin)
|
5
media/apps.py
Normal file
5
media/apps.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class MediaConfig(AppConfig):
|
||||||
|
name = 'media'
|
42
users/management/commands/stop_portail.py → media/forms.py
Executable file → Normal file
42
users/management/commands/stop_portail.py → media/forms.py
Executable file → Normal file
@ -1,3 +1,7 @@
|
|||||||
|
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
# se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
# quelques clics.
|
||||||
|
#
|
||||||
# Copyright © 2017 Gabriel Détraz
|
# Copyright © 2017 Gabriel Détraz
|
||||||
# Copyright © 2017 Goulven Kermarec
|
# Copyright © 2017 Goulven Kermarec
|
||||||
# Copyright © 2017 Augustin Lemesle
|
# Copyright © 2017 Augustin Lemesle
|
||||||
@ -15,27 +19,27 @@
|
|||||||
# You should have received a copy of the GNU General Public License along
|
# You should have received a copy of the GNU General Public License along
|
||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
#
|
|
||||||
# Ce script est appellé avant le démarage du portail, il insère les bonnes règles
|
|
||||||
# dans l'iptables et active le routage
|
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.forms import ModelForm, Form, ValidationError
|
||||||
|
from django import forms
|
||||||
|
from .models import Auteur, Media, Emprunt
|
||||||
from users.models import restore_iptables, create_ip_set, fill_ipset, disable_iptables, apply
|
|
||||||
from portail_captif.settings import AUTORIZED_INTERFACES
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
|
||||||
help = 'Mets en place iptables et le set ip au démarage'
|
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
|
||||||
# Destruction de l'iptables
|
|
||||||
disable_iptables()
|
|
||||||
# Desactivation du routage sur les bonnes if
|
|
||||||
for interface in AUTORIZED_INTERFACES:
|
|
||||||
apply(["sudo", "-n", "sysctl", "net.ipv6.conf.%s.forwarding=0" % interface])
|
|
||||||
apply(["sudo", "-n", "sysctl", "net.ipv4.conf.%s.forwarding=0" % interface])
|
|
||||||
|
|
||||||
|
class AuteurForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Auteur
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
class MediaForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Media
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
class EmpruntForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Emprunt
|
||||||
|
fields = ['media']
|
||||||
|
|
||||||
|
class EditEmpruntForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Emprunt
|
||||||
|
fields = ['media', 'permanencier_emprunt', 'permanencier_rendu', 'date_rendu']
|
62
media/migrations/0001_initial.py
Normal file
62
media/migrations/0001_initial.py
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.2 on 2017-06-28 10:35
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Auteur',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('nom', models.CharField(max_length=255)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Emprunt',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('date_emprunt', models.DateTimeField(help_text='%d/%m/%y %H:%M:%S')),
|
||||||
|
('date_rendu', models.DateTimeField(help_text='%d/%m/%y %H:%M:%S')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Media',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('titre', models.CharField(max_length=255)),
|
||||||
|
('cote', models.CharField(max_length=31)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='emprunt',
|
||||||
|
name='media',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='media.Media'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='emprunt',
|
||||||
|
name='permanencier_emprunt',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='user_permanencier_emprunt', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='emprunt',
|
||||||
|
name='permanencier_rendu',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='user_permanencier_rendu', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='emprunt',
|
||||||
|
name='user',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
]
|
22
media/migrations/0002_media_auteur.py
Normal file
22
media/migrations/0002_media_auteur.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.2 on 2017-06-29 12:38
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('media', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='media',
|
||||||
|
name='auteur',
|
||||||
|
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.PROTECT, to='media.Auteur'),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
]
|
27
media/migrations/0003_auto_20170629_1536.py
Normal file
27
media/migrations/0003_auto_20170629_1536.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.2 on 2017-06-29 13:36
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('media', '0002_media_auteur'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='emprunt',
|
||||||
|
name='date_rendu',
|
||||||
|
field=models.DateTimeField(blank=True, help_text='%d/%m/%y %H:%M:%S'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='emprunt',
|
||||||
|
name='permanencier_rendu',
|
||||||
|
field=models.ForeignKey(blank=True, on_delete=django.db.models.deletion.PROTECT, related_name='user_permanencier_rendu', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
]
|
27
media/migrations/0004_auto_20170629_1539.py
Normal file
27
media/migrations/0004_auto_20170629_1539.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.2 on 2017-06-29 13:39
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('media', '0003_auto_20170629_1536'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='emprunt',
|
||||||
|
name='date_rendu',
|
||||||
|
field=models.DateTimeField(blank=True, help_text='%d/%m/%y %H:%M:%S', null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='emprunt',
|
||||||
|
name='permanencier_rendu',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='user_permanencier_rendu', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
]
|
0
media/migrations/__init__.py
Normal file
0
media/migrations/__init__.py
Normal file
29
media/models.py
Normal file
29
media/models.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Auteur(models.Model):
|
||||||
|
nom = models.CharField(max_length=255)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.nom
|
||||||
|
|
||||||
|
class Media(models.Model):
|
||||||
|
titre = models.CharField(max_length=255)
|
||||||
|
cote = models.CharField(max_length=31)
|
||||||
|
auteur = models.ForeignKey('Auteur', on_delete=models.PROTECT)
|
||||||
|
# type = TODO
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.titre) + ' - ' + str(self.auteur)
|
||||||
|
|
||||||
|
class Emprunt(models.Model):
|
||||||
|
media = models.ForeignKey('Media', on_delete=models.PROTECT)
|
||||||
|
user = models.ForeignKey('users.User', on_delete=models.PROTECT)
|
||||||
|
date_emprunt = models.DateTimeField(help_text='%d/%m/%y %H:%M:%S')
|
||||||
|
date_rendu = models.DateTimeField(help_text='%d/%m/%y %H:%M:%S', blank=True, null=True)
|
||||||
|
permanencier_emprunt = models.ForeignKey('users.User', on_delete=models.PROTECT, related_name='user_permanencier_emprunt')
|
||||||
|
permanencier_rendu = models.ForeignKey('users.User', on_delete=models.PROTECT, related_name='user_permanencier_rendu', blank=True, null=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
40
media/templates/media/aff_auteurs.html
Normal file
40
media/templates/media/aff_auteurs.html
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{% comment %}
|
||||||
|
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
quelques clics.
|
||||||
|
|
||||||
|
Copyright © 2017 Gabriel Détraz
|
||||||
|
Copyright © 2017 Goulven Kermarec
|
||||||
|
Copyright © 2017 Augustin Lemesle
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Nom</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
{% for auteur in auteurs_list %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ auteur.nom }}</td>
|
||||||
|
<td>{% include 'buttons/edit.html' with href='media:edit-auteur' id=auteur.id %}
|
||||||
|
{% include 'buttons/suppr.html' with href='media:del-auteur' id=auteur.id %}
|
||||||
|
{% include 'buttons/history.html' with href='media:history' name='auteur' id=auteur.id %}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
66
media/templates/media/aff_emprunts.html
Normal file
66
media/templates/media/aff_emprunts.html
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
{% comment %}
|
||||||
|
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
quelques clics.
|
||||||
|
|
||||||
|
Copyright © 2017 Gabriel Détraz
|
||||||
|
Copyright © 2017 Goulven Kermarec
|
||||||
|
Copyright © 2017 Augustin Lemesle
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Media</th>
|
||||||
|
<th>Utilisateur</th>
|
||||||
|
<th>Date emprunt</th>
|
||||||
|
<th>Permanencier emprunt</th>
|
||||||
|
<th>Date rendu</th>
|
||||||
|
<th>Permanencier rendu</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
{% for emprunt in emprunts_list %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ emprunt.media }}</td>
|
||||||
|
<td>
|
||||||
|
<a href="{% url 'users:profil' userid=emprunt.user.id %}"><b>{{ emprunt.user }}</b></a>
|
||||||
|
</td>
|
||||||
|
<td>{{ emprunt.date_emprunt }}</td>
|
||||||
|
<td>{{ emprunt.permanencier_emprunt }}</td>
|
||||||
|
<td>{% if not emprunt.date_rendu %}<a class="btn btn-primary btn-sm" role="button" href="{% url 'media:retour-emprunt' emprunt.id %}"><i class="glyphicon glyphicon-ok"></i> Retour</a>{% else %}{{ emprunt.date_rendu }}{% endif %}</td>
|
||||||
|
<td>{{ emprunt.permanencier_rendu }}</td>
|
||||||
|
<td>
|
||||||
|
<div class="dropdown">
|
||||||
|
<button class="btn btn-default dropdown-toggle" type="button" id="editionemprunt" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||||
|
Modifier
|
||||||
|
<span class="caret"></span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu" aria-labelledby="editionemprunt">
|
||||||
|
{% if is_perm %}
|
||||||
|
<li><a href="{% url 'media:edit-emprunt' emprunt.id %}"><i class="glyphicon glyphicon-edit"></i> Editer</a></li>
|
||||||
|
{% endif %}
|
||||||
|
{% if is_bureau %}
|
||||||
|
<li><a href="{% url 'media:del-emprunt' emprunt.id %}"><i class="glyphicon glyphicon-trash"></i> Supprimer</a></li>
|
||||||
|
{% endif %}
|
||||||
|
<li><a href="{% url 'media:history' 'emprunt' emprunt.id %}"><i class="glyphicon glyphicon-time"></i> Historique</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
44
media/templates/media/aff_medias.html
Normal file
44
media/templates/media/aff_medias.html
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
{% comment %}
|
||||||
|
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
quelques clics.
|
||||||
|
|
||||||
|
Copyright © 2017 Gabriel Détraz
|
||||||
|
Copyright © 2017 Goulven Kermarec
|
||||||
|
Copyright © 2017 Augustin Lemesle
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Titre</th>
|
||||||
|
<th>Auteur</th>
|
||||||
|
<th>Cote</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
{% for media in medias_list %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ media.titre }}</td>
|
||||||
|
<td>{{ media.auteur }}</td>
|
||||||
|
<td>{{ media.cote }}</td>
|
||||||
|
<td>{% include 'buttons/edit.html' with href='media:edit-media' id=media.id %}
|
||||||
|
{% include 'buttons/suppr.html' with href='media:del-media' id=media.id %}
|
||||||
|
{% include 'buttons/history.html' with href='media:history' name='media' id=media.id %}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
40
media/templates/media/delete.html
Normal file
40
media/templates/media/delete.html
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{% extends "media/sidebar.html" %}
|
||||||
|
{% comment %}
|
||||||
|
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
quelques clics.
|
||||||
|
|
||||||
|
Copyright © 2017 Gabriel Détraz
|
||||||
|
Copyright © 2017 Goulven Kermarec
|
||||||
|
Copyright © 2017 Augustin Lemesle
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
|
||||||
|
{% block title %}Création et modification de media{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<form class="form" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<h4>Attention, voulez-vous vraiment supprimer cet objet {{ objet_name }} ( {{ objet }} ) ?</h4>
|
||||||
|
{% bootstrap_button "Confirmer" button_type="submit" icon="trash" %}
|
||||||
|
</form>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
{% endblock %}
|
39
media/templates/media/index_auteurs.html
Normal file
39
media/templates/media/index_auteurs.html
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{% extends "media/sidebar.html" %}
|
||||||
|
{% comment %}
|
||||||
|
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
quelques clics.
|
||||||
|
|
||||||
|
Copyright © 2017 Gabriel Détraz
|
||||||
|
Copyright © 2017 Goulven Kermarec
|
||||||
|
Copyright © 2017 Augustin Lemesle
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
|
||||||
|
{% block title %}Auteurs{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>Liste des auteurs</h2>
|
||||||
|
{% if is_perm %}
|
||||||
|
<a class="btn btn-primary btn-sm" role="button" href="{% url 'media:add-auteur' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un auteur</a>
|
||||||
|
{% endif %}
|
||||||
|
{% include "media/aff_auteurs.html" with auteurs_list=auteurs_list %}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
{% endblock %}
|
36
media/templates/media/index_emprunts.html
Normal file
36
media/templates/media/index_emprunts.html
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
{% extends "media/sidebar.html" %}
|
||||||
|
{% comment %}
|
||||||
|
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
quelques clics.
|
||||||
|
|
||||||
|
Copyright © 2017 Gabriel Détraz
|
||||||
|
Copyright © 2017 Goulven Kermarec
|
||||||
|
Copyright © 2017 Augustin Lemesle
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
|
||||||
|
{% block title %}Emprunts{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>Liste des emprunts</h2>
|
||||||
|
{% include "media/aff_emprunts.html" with emprunts_list=emprunts_list %}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
{% endblock %}
|
39
media/templates/media/index_medias.html
Normal file
39
media/templates/media/index_medias.html
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{% extends "media/sidebar.html" %}
|
||||||
|
{% comment %}
|
||||||
|
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
quelques clics.
|
||||||
|
|
||||||
|
Copyright © 2017 Gabriel Détraz
|
||||||
|
Copyright © 2017 Goulven Kermarec
|
||||||
|
Copyright © 2017 Augustin Lemesle
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
|
||||||
|
{% block title %}Media{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>Liste des medias</h2>
|
||||||
|
{% if is_perm %}
|
||||||
|
<a class="btn btn-primary btn-sm" role="button" href="{% url 'media:add-media' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un media</a>
|
||||||
|
{% endif %}
|
||||||
|
{% include "media/aff_medias.html" with medias_list=medias_list %}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
{% endblock %}
|
42
media/templates/media/media.html
Normal file
42
media/templates/media/media.html
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
{% extends "media/sidebar.html" %}
|
||||||
|
{% comment %}
|
||||||
|
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
quelques clics.
|
||||||
|
|
||||||
|
Copyright © 2017 Gabriel Détraz
|
||||||
|
Copyright © 2017 Goulven Kermarec
|
||||||
|
Copyright © 2017 Augustin Lemesle
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
|
||||||
|
{% block title %}Création et modification de media{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% bootstrap_form_errors mediaform %}
|
||||||
|
|
||||||
|
|
||||||
|
<form class="form" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% bootstrap_form mediaform %}
|
||||||
|
{% bootstrap_button "Créer ou modifier" button_type="submit" icon="star" %}
|
||||||
|
</form>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
{% endblock %}
|
41
media/templates/media/sidebar.html
Normal file
41
media/templates/media/sidebar.html
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% comment %}
|
||||||
|
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
quelques clics.
|
||||||
|
|
||||||
|
Copyright © 2017 Gabriel Détraz
|
||||||
|
Copyright © 2017 Goulven Kermarec
|
||||||
|
Copyright © 2017 Augustin Lemesle
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block sidebar %}
|
||||||
|
<a class="list-group-item list-group-item-info" href="{% url "media:index" %}">
|
||||||
|
<i class="glyphicon glyphicon-list"></i>
|
||||||
|
Emprunts
|
||||||
|
</a>
|
||||||
|
<a class="list-group-item list-group-item-info" href="{% url "media:index-auteurs" %}">
|
||||||
|
<i class="glyphicon glyphicon-list"></i>
|
||||||
|
Auteurs
|
||||||
|
</a>
|
||||||
|
<a class="list-group-item list-group-item-info" href="{% url "media:index-medias" %}">
|
||||||
|
<i class="glyphicon glyphicon-list"></i>
|
||||||
|
Medias
|
||||||
|
</a>
|
||||||
|
{% endblock %}
|
||||||
|
|
3
media/tests.py
Normal file
3
media/tests.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
45
media/urls.py
Normal file
45
media/urls.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
# se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
# quelques clics.
|
||||||
|
#
|
||||||
|
# Copyright © 2017 Gabriel Détraz
|
||||||
|
# Copyright © 2017 Goulven Kermarec
|
||||||
|
# Copyright © 2017 Augustin Lemesle
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
from django.conf.urls import url
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
url(r'^add_auteur/$', views.add_auteur, name='add-auteur'),
|
||||||
|
url(r'^edit_auteur/(?P<auteurid>[0-9]+)$', views.edit_auteur, name='edit-auteur'),
|
||||||
|
url(r'^del_auteur/(?P<auteurid>[0-9]+)$', views.del_auteur, name='del-auteur'),
|
||||||
|
url(r'^index_auteurs/$', views.index_auteurs, name='index-auteurs'),
|
||||||
|
url(r'^history/(?P<object>auteur)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||||
|
url(r'^add_media/$', views.add_media, name='add-media'),
|
||||||
|
url(r'^edit_media/(?P<mediaid>[0-9]+)$', views.edit_media, name='edit-media'),
|
||||||
|
url(r'^del_media/(?P<mediaid>[0-9]+)$', views.del_media, name='del-media'),
|
||||||
|
url(r'^index_medias/$', views.index_medias, name='index-medias'),
|
||||||
|
url(r'^history/(?P<object>media)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||||
|
url(r'^add_emprunt/(?P<userid>[0-9]+)$', views.add_emprunt, name='add-emprunt'),
|
||||||
|
url(r'^retour_emprunt/(?P<empruntid>[0-9]+)$', views.retour_emprunt, name='retour-emprunt'),
|
||||||
|
url(r'^edit_emprunt/(?P<empruntid>[0-9]+)$', views.edit_emprunt, name='edit-emprunt'),
|
||||||
|
url(r'^del_emprunt/(?P<empruntid>[0-9]+)$', views.del_emprunt, name='del-emprunt'),
|
||||||
|
url(r'^index_emprunts/$', views.index, name='index'),
|
||||||
|
url(r'^history/(?P<object>emprunt)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||||
|
url(r'^$', views.index, name='index'),
|
||||||
|
]
|
252
media/views.py
Normal file
252
media/views.py
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
from django.shortcuts import render, redirect
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
from django.template.context_processors import csrf
|
||||||
|
from django.template import Context, RequestContext, loader
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.contrib.auth.decorators import login_required, permission_required
|
||||||
|
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||||
|
from django.contrib.auth import authenticate, login
|
||||||
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
from django.utils import timezone
|
||||||
|
from .forms import AuteurForm, MediaForm, EmpruntForm, EditEmpruntForm
|
||||||
|
from .models import Auteur, Media, Emprunt
|
||||||
|
from users.models import User
|
||||||
|
from django.db import transaction
|
||||||
|
from reversion import revisions as reversion
|
||||||
|
from reversion.models import Version
|
||||||
|
|
||||||
|
from med.settings import PAGINATION_NUMBER as pagination_number
|
||||||
|
|
||||||
|
def form(ctx, template, request):
|
||||||
|
c = ctx
|
||||||
|
c.update(csrf(request))
|
||||||
|
return render(request, template, c)
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@permission_required('perm')
|
||||||
|
def add_auteur(request):
|
||||||
|
auteur = AuteurForm(request.POST or None)
|
||||||
|
if auteur.is_valid():
|
||||||
|
with transaction.atomic(), reversion.create_revision():
|
||||||
|
auteur.save()
|
||||||
|
reversion.set_user(request.user)
|
||||||
|
reversion.set_comment("Création")
|
||||||
|
messages.success(request, "L'auteur a été ajouté")
|
||||||
|
return redirect("/media/index_auteurs/")
|
||||||
|
return form({'mediaform': auteur}, 'media/media.html', request)
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@permission_required('perm')
|
||||||
|
def edit_auteur(request, auteurid):
|
||||||
|
try:
|
||||||
|
auteur_instance = Auteur.objects.get(pk=auteurid)
|
||||||
|
except Auteur.DoesNotExist:
|
||||||
|
messages.error(request, u"Entrée inexistante" )
|
||||||
|
return redirect("/media/index_auteurs/")
|
||||||
|
auteur = AuteurForm(request.POST or None, instance=auteur_instance)
|
||||||
|
if auteur.is_valid():
|
||||||
|
with transaction.atomic(), reversion.create_revision():
|
||||||
|
auteur.save()
|
||||||
|
reversion.set_user(request.user)
|
||||||
|
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in auteur.changed_data))
|
||||||
|
messages.success(request, "Auteur modifié")
|
||||||
|
return redirect("/media/index_auteurs/")
|
||||||
|
return form({'mediaform': auteur}, 'media/media.html', request)
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@permission_required('perm')
|
||||||
|
def del_auteur(request, auteurid):
|
||||||
|
try:
|
||||||
|
auteur_instance = Auteur.objects.get(pk=auteurid)
|
||||||
|
except Auteur.DoesNotExist:
|
||||||
|
messages.error(request, u"Entrée inexistante" )
|
||||||
|
return redirect("/media/index_auteurs/")
|
||||||
|
if request.method == "POST":
|
||||||
|
with transaction.atomic(), reversion.create_revision():
|
||||||
|
auteur_instance.delete()
|
||||||
|
reversion.set_user(request.user)
|
||||||
|
messages.success(request, "L'auteur a été détruit")
|
||||||
|
return redirect("/media/index_auteurs")
|
||||||
|
return form({'objet': auteur_instance, 'objet_name': 'auteur'}, 'media/delete.html', request)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@permission_required('perm')
|
||||||
|
def add_media(request):
|
||||||
|
media = MediaForm(request.POST or None)
|
||||||
|
if media.is_valid():
|
||||||
|
with transaction.atomic(), reversion.create_revision():
|
||||||
|
media.save()
|
||||||
|
reversion.set_user(request.user)
|
||||||
|
reversion.set_comment("Création")
|
||||||
|
messages.success(request, "Le media a été ajouté")
|
||||||
|
return redirect("/media/index_medias/")
|
||||||
|
return form({'mediaform': media}, 'media/media.html', request)
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@permission_required('perm')
|
||||||
|
def edit_media(request, mediaid):
|
||||||
|
try:
|
||||||
|
media_instance = Media.objects.get(pk=mediaid)
|
||||||
|
except Media.DoesNotExist:
|
||||||
|
messages.error(request, u"Entrée inexistante" )
|
||||||
|
return redirect("/media/index_medias/")
|
||||||
|
media = MediaForm(request.POST or None, instance=media_instance)
|
||||||
|
if media.is_valid():
|
||||||
|
with transaction.atomic(), reversion.create_revision():
|
||||||
|
media.save()
|
||||||
|
reversion.set_user(request.user)
|
||||||
|
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in media.changed_data))
|
||||||
|
messages.success(request, "Media modifié")
|
||||||
|
return redirect("/media/index_medias/")
|
||||||
|
return form({'mediaform': media}, 'media/media.html', request)
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@permission_required('perm')
|
||||||
|
def del_media(request, mediaid):
|
||||||
|
try:
|
||||||
|
media_instance = Media.objects.get(pk=mediaid)
|
||||||
|
except Media.DoesNotExist:
|
||||||
|
messages.error(request, u"Entrée inexistante" )
|
||||||
|
return redirect("/media/index_medias/")
|
||||||
|
if request.method == "POST":
|
||||||
|
with transaction.atomic(), reversion.create_revision():
|
||||||
|
media_instance.delete()
|
||||||
|
reversion.set_user(request.user)
|
||||||
|
messages.success(request, "Le media a été détruit")
|
||||||
|
return redirect("/media/index_medias")
|
||||||
|
return form({'objet': media_instance, 'objet_name': 'media'}, 'media/delete.html', request)
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@permission_required('perm')
|
||||||
|
def add_emprunt(request, userid):
|
||||||
|
try:
|
||||||
|
user = User.objects.get(pk=userid)
|
||||||
|
except User.DoesNotExist:
|
||||||
|
messages.error(request, u"Entrée inexistante" )
|
||||||
|
return redirect("/media/index_emprunts/")
|
||||||
|
emprunts_en_cours = Emprunt.objects.filter(date_rendu=None, user=user).count()
|
||||||
|
if emprunts_en_cours >= user.maxemprunt:
|
||||||
|
messages.error(request, "Maximum d'emprunts atteint de l'user %s" % user.maxemprunt)
|
||||||
|
return redirect("/media/index_emprunts/")
|
||||||
|
emprunt = EmpruntForm(request.POST or None)
|
||||||
|
if emprunt.is_valid():
|
||||||
|
emprunt = emprunt.save(commit=False)
|
||||||
|
emprunt.user = user
|
||||||
|
emprunt.permanencier_emprunt = request.user
|
||||||
|
emprunt.date_emprunt = timezone.now()
|
||||||
|
with transaction.atomic(), reversion.create_revision():
|
||||||
|
emprunt.save()
|
||||||
|
reversion.set_user(request.user)
|
||||||
|
reversion.set_comment("Création")
|
||||||
|
messages.success(request, "Le emprunt a été ajouté")
|
||||||
|
return redirect("/media/index_emprunts/")
|
||||||
|
return form({'mediaform': emprunt}, 'media/media.html', request)
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@permission_required('perm')
|
||||||
|
def edit_emprunt(request, empruntid):
|
||||||
|
try:
|
||||||
|
emprunt_instance = Emprunt.objects.get(pk=empruntid)
|
||||||
|
except Emprunt.DoesNotExist:
|
||||||
|
messages.error(request, u"Entrée inexistante" )
|
||||||
|
return redirect("/media/index_emprunts/")
|
||||||
|
emprunt = EditEmpruntForm(request.POST or None, instance=emprunt_instance)
|
||||||
|
if emprunt.is_valid():
|
||||||
|
with transaction.atomic(), reversion.create_revision():
|
||||||
|
emprunt.save()
|
||||||
|
reversion.set_user(request.user)
|
||||||
|
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in emprunt.changed_data))
|
||||||
|
messages.success(request, "Emprunt modifié")
|
||||||
|
return redirect("/media/index_emprunts/")
|
||||||
|
return form({'mediaform': emprunt}, 'media/media.html', request)
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@permission_required('bureau')
|
||||||
|
def retour_emprunt(request, empruntid):
|
||||||
|
try:
|
||||||
|
emprunt_instance = Emprunt.objects.get(pk=empruntid)
|
||||||
|
except Emprunt.DoesNotExist:
|
||||||
|
messages.error(request, u"Entrée inexistante" )
|
||||||
|
return redirect("/media/index_emprunts/")
|
||||||
|
with transaction.atomic(), reversion.create_revision():
|
||||||
|
emprunt_instance.permanencier_rendu = request.user
|
||||||
|
emprunt_instance.date_rendu = timezone.now()
|
||||||
|
emprunt_instance.save()
|
||||||
|
reversion.set_user(request.user)
|
||||||
|
messages.success(request, "Retour enregistré")
|
||||||
|
return redirect("/media/index_emprunts/")
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@permission_required('bureau')
|
||||||
|
def del_emprunt(request, empruntid):
|
||||||
|
try:
|
||||||
|
emprunt_instance = Emprunt.objects.get(pk=empruntid)
|
||||||
|
except Emprunt.DoesNotExist:
|
||||||
|
messages.error(request, u"Entrée inexistante" )
|
||||||
|
return redirect("/media/index_emprunts/")
|
||||||
|
if request.method == "POST":
|
||||||
|
with transaction.atomic(), reversion.create_revision():
|
||||||
|
emprunt_instance.delete()
|
||||||
|
reversion.set_user(request.user)
|
||||||
|
messages.success(request, "L'emprunt a été détruit")
|
||||||
|
return redirect("/media/index_emprunts")
|
||||||
|
return form({'objet': emprunt_instance, 'objet_name': 'emprunt'}, 'media/delete.html', request)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def index_auteurs(request):
|
||||||
|
auteurs_list = Auteur.objects.all()
|
||||||
|
return render(request, 'media/index_auteurs.html', {'auteurs_list':auteurs_list})
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def index_medias(request):
|
||||||
|
medias_list = Media.objects.all()
|
||||||
|
return render(request, 'media/index_medias.html', {'medias_list':medias_list})
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def index(request):
|
||||||
|
if request.user.has_perms(['perm']):
|
||||||
|
emprunts_list = Emprunt.objects.all()
|
||||||
|
else:
|
||||||
|
emprunts_list = Emprunt.objects.filter(user=request.user)
|
||||||
|
return render(request, 'media/index_emprunts.html', {'emprunts_list':emprunts_list})
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def history(request, object, id):
|
||||||
|
if object == 'auteur':
|
||||||
|
try:
|
||||||
|
object_instance = Auteur.objects.get(pk=id)
|
||||||
|
except Auteur.DoesNotExist:
|
||||||
|
messages.error(request, "Auteur inexistant")
|
||||||
|
return redirect("/media/index_auteurs")
|
||||||
|
elif object == 'media':
|
||||||
|
try:
|
||||||
|
object_instance = Media.objects.get(pk=id)
|
||||||
|
except Media.DoesNotExist:
|
||||||
|
messages.error(request, "Media inexistant")
|
||||||
|
return redirect("/media/index_medias")
|
||||||
|
elif object == 'emprunt':
|
||||||
|
try:
|
||||||
|
object_instance = Emprunt.objects.get(pk=id)
|
||||||
|
except Emprunt.DoesNotExist:
|
||||||
|
messages.error(request, "Emprunt inexistant")
|
||||||
|
return redirect("/media/index_emprunts")
|
||||||
|
reversions = Version.objects.get_for_object(object_instance)
|
||||||
|
paginator = Paginator(reversions, pagination_number)
|
||||||
|
page = request.GET.get('page')
|
||||||
|
try:
|
||||||
|
reversions = paginator.page(page)
|
||||||
|
except PageNotAnInteger:
|
||||||
|
# If page is not an integer, deliver first page.
|
||||||
|
reversions = paginator.page(1)
|
||||||
|
except EmptyPage:
|
||||||
|
# If page is out of range (e.g. 9999), deliver last page of results.
|
||||||
|
reversions = paginator.page(paginator.num_pages)
|
||||||
|
return render(request, 'med/history.html', {'reversions': reversions, 'object': object_instance})
|
||||||
|
|
@ -1,26 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=Crans Portail Captif
|
|
||||||
Requires=nginx.service
|
|
||||||
Requires=portail_captif.socket
|
|
||||||
After=nginx.service
|
|
||||||
After=network-online.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=forking
|
|
||||||
User=root
|
|
||||||
Group=root
|
|
||||||
PIDFile=/run/portail_captif.pid
|
|
||||||
WorkingDirectory=/var/www/portail_captif/
|
|
||||||
ExecStartPre=/usr/bin/python3 /var/www/portail_captif/manage.py start_portail
|
|
||||||
ExecStart=/usr/bin/gunicorn3 portail_captif.wsgi:application --pid=/run/portail_captif.pid --name www-data --user www-data --group www-data --daemon --log-file /var/log/gunicorn/portail_captif.log --log-level=info --bind=unix:///tmp/gunicorn-portail_captif.sock --workers=1
|
|
||||||
ExecReload=/bin/kill -s HUP $MAINPID
|
|
||||||
ExecStop=/bin/kill -s TERM $MAINPID
|
|
||||||
ExecStopPost=/usr/bin/python3 /var/www/portail_captif/manage.py stop_portail
|
|
||||||
Restart=on-failure
|
|
||||||
RestartSec=65
|
|
||||||
StartLimitInterval=60
|
|
||||||
StartLimitBurst=2
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
Also=portail_captif.socket
|
|
@ -1,8 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=Socket Portail Captif
|
|
||||||
|
|
||||||
[Socket]
|
|
||||||
ListenStream=/tmp/gunicorn-portail_captif.sock
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=sockets.target
|
|
@ -1,92 +0,0 @@
|
|||||||
{% extends "portail_captif/sidebar.html" %}
|
|
||||||
{% comment %}
|
|
||||||
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
|
||||||
se veut agnostique au réseau considéré, de manière à être installable en
|
|
||||||
quelques clics.
|
|
||||||
|
|
||||||
Copyright © 2017 Gabriel Détraz
|
|
||||||
Copyright © 2017 Goulven Kermarec
|
|
||||||
Copyright © 2017 Augustin Lemesle
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
{% endcomment %}
|
|
||||||
|
|
||||||
{% load bootstrap3 %}
|
|
||||||
{% load staticfiles %}
|
|
||||||
|
|
||||||
{% block title %}Accueil{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<h1>Bienvenue sur {{ site_name }} !</h1>
|
|
||||||
|
|
||||||
<h4>Bienvenue à toi ! Ce portail mis en place par le Cr@ns te permet d'accéder gratuitement à Internet sur le campus durant ton passage</h4>
|
|
||||||
|
|
||||||
<center>
|
|
||||||
<div class="thumbnail">
|
|
||||||
<img src="/static/logo/logo.png" alt="logo">
|
|
||||||
</div>
|
|
||||||
</center>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-6 col-md-6">
|
|
||||||
<div class="col-12">
|
|
||||||
<div class="thumbnail">
|
|
||||||
<div class="caption">
|
|
||||||
<h3>Inscription</h3>
|
|
||||||
<p>Si vous n'avez pas de compte, inscrivez-vous pour bénéficier d'un accès à internet !</p>
|
|
||||||
<p><a href="{% url 'users:new-user' %}" class="btn btn-primary" role="button">Inscription</a></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-6 col-md-6">
|
|
||||||
<div class="col-12">
|
|
||||||
<div class="thumbnail">
|
|
||||||
<div class="caption">
|
|
||||||
<h3>Indentification</h3>
|
|
||||||
<p>Si avez déjà un compte, identifiez-vous pour accèder à Internet</p>
|
|
||||||
<p><a href="{% url 'login' %}" class="btn btn-primary" role="button">Identification</a></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h1>Liens utiles</h1>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
{% for col in services_urls %}
|
|
||||||
<div class="col-sm-6 col-md-6">
|
|
||||||
{% for key, s in col.items %}
|
|
||||||
<div class="col-12">
|
|
||||||
<div class="thumbnail">
|
|
||||||
<img src="{% static "logo/"|add:s.logo %}" alt="{{ key }}">
|
|
||||||
<div class="caption">
|
|
||||||
<h3>{{ key }}</h3>
|
|
||||||
<p>{{ s.description }}</p>
|
|
||||||
<p><a href="{{ s.url }}" class="btn btn-primary" role="button">Accéder</a></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p>Compte tenu des obligations de traçabilité des connexions à internet, vous vous engagez à fournir des informations exactes lors de votre inscription conformément à l'article 441-1 du nouveau code pénal.
|
|
||||||
Vous êtes seul responsable de l'utilisation du service, conformément aux articles L32 et L33 du code des postes et télécomunications éléctroniques.</p>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
|
|
0
search/__init__.py
Normal file
0
search/__init__.py
Normal file
3
search/admin.py
Normal file
3
search/admin.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
5
search/apps.py
Normal file
5
search/apps.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class SearchConfig(AppConfig):
|
||||||
|
name = 'search'
|
49
search/forms.py
Normal file
49
search/forms.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
# se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
# quelques clics.
|
||||||
|
#
|
||||||
|
# Copyright © 2017 Gabriel Détraz
|
||||||
|
# Copyright © 2017 Goulven Kermarec
|
||||||
|
# Copyright © 2017 Augustin Lemesle
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from django import forms
|
||||||
|
from django.forms import Form
|
||||||
|
from django.forms import ModelForm
|
||||||
|
|
||||||
|
CHOICES = (
|
||||||
|
('0', 'Actifs'),
|
||||||
|
('1', 'Désactivés'),
|
||||||
|
('2', 'Archivés'),
|
||||||
|
)
|
||||||
|
|
||||||
|
CHOICES2 = (
|
||||||
|
('0', 'Utilisateurs'),
|
||||||
|
('1', 'Media'),
|
||||||
|
('2', 'Emprunts'),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SearchForm(Form):
|
||||||
|
search_field = forms.CharField(label = 'Search', max_length = 100)
|
||||||
|
|
||||||
|
class SearchFormPlus(Form):
|
||||||
|
search_field = forms.CharField(label = 'Search', max_length = 100, required=False)
|
||||||
|
filtre = forms.MultipleChoiceField(label="Filtre utilisateurs", required=False, widget =forms.CheckboxSelectMultiple,choices=CHOICES)
|
||||||
|
affichage = forms.MultipleChoiceField(label="Filtre affichage", required=False, widget =forms.CheckboxSelectMultiple,choices=CHOICES2)
|
||||||
|
date_deb = forms.DateField(required=False, label="Date de début", help_text='DD/MM/YYYY', input_formats=['%d/%m/%Y'])
|
||||||
|
date_fin = forms.DateField(required=False, help_text='DD/MM/YYYY', input_formats=['%d/%m/%Y'], label="Date de fin")
|
0
search/migrations/__init__.py
Normal file
0
search/migrations/__init__.py
Normal file
3
search/models.py
Normal file
3
search/models.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
50
search/templates/search/index.html
Normal file
50
search/templates/search/index.html
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
{% extends "search/sidebar.html" %}
|
||||||
|
{% comment %}
|
||||||
|
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
quelques clics.
|
||||||
|
|
||||||
|
Copyright © 2017 Gabriel Détraz
|
||||||
|
Copyright © 2017 Goulven Kermarec
|
||||||
|
Copyright © 2017 Augustin Lemesle
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
|
||||||
|
{% block title %}Résultats de la recherche{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% if users_list %}
|
||||||
|
<h2>Résultats dans les utilisateurs</h2>
|
||||||
|
{% include "users/aff_users.html" with users_list=users_list %}
|
||||||
|
{% endif%}
|
||||||
|
{% if emprunts_list %}
|
||||||
|
<h2>Résultats dans les emprunt : </h2>
|
||||||
|
{% include "media/aff_emprunts.html" with emprunts_list=emprunts_list %}
|
||||||
|
{% endif %}
|
||||||
|
{% if medias_list %}
|
||||||
|
<h2>Résultats dans les media : </h2>
|
||||||
|
{% include "media/aff_medias.html" with medias_list=medias_list %}
|
||||||
|
{% endif %}
|
||||||
|
{% if not users_list and not emprunts_list and not medias_list %}
|
||||||
|
<h3>Aucun résultat</h3>
|
||||||
|
{% endif %}
|
||||||
|
<h6>(Seulement les {{ max_result }} premiers résultats sont affichés dans chaque catégorie)</h6>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
{% endblock %}
|
43
search/templates/search/search.html
Normal file
43
search/templates/search/search.html
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{% extends "search/sidebar.html" %}
|
||||||
|
{% comment %}
|
||||||
|
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
quelques clics.
|
||||||
|
|
||||||
|
Copyright © 2017 Gabriel Détraz
|
||||||
|
Copyright © 2017 Goulven Kermarec
|
||||||
|
Copyright © 2017 Augustin Lemesle
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
|
||||||
|
{% block title %}Recherche{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% bootstrap_form_errors searchform %}
|
||||||
|
|
||||||
|
<form class="form" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% bootstrap_form searchform %}
|
||||||
|
{% bootstrap_button "Search" button_type="submit" icon="search" %}
|
||||||
|
</form>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
{% endblock %}
|
36
search/templates/search/sidebar.html
Normal file
36
search/templates/search/sidebar.html
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% comment %}
|
||||||
|
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
quelques clics.
|
||||||
|
|
||||||
|
Copyright © 2017 Gabriel Détraz
|
||||||
|
Copyright © 2017 Goulven Kermarec
|
||||||
|
Copyright © 2017 Augustin Lemesle
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block sidebar %}
|
||||||
|
<a class="list-group-item list-group-item-warning" href="{% url "search:search" %}">
|
||||||
|
<i class="glyphicon glyphicon-search"></i>
|
||||||
|
Recherche simple
|
||||||
|
</a>
|
||||||
|
<a class="list-group-item list-group-item-warning" href="{% url "search:searchp" %}">
|
||||||
|
<i class="glyphicon glyphicon-zoom-in"></i>
|
||||||
|
Recherche avancée
|
||||||
|
</a>
|
||||||
|
{% endblock %}
|
3
search/tests.py
Normal file
3
search/tests.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
30
search/urls.py
Normal file
30
search/urls.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
# se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
# quelques clics.
|
||||||
|
#
|
||||||
|
# Copyright © 2017 Gabriel Détraz
|
||||||
|
# Copyright © 2017 Goulven Kermarec
|
||||||
|
# Copyright © 2017 Augustin Lemesle
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
from django.conf.urls import url
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
url(r'^$', views.search, name='search'),
|
||||||
|
url(r'^avance/$', views.searchp, name='searchp'),
|
||||||
|
]
|
109
search/views.py
Normal file
109
search/views.py
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
# se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
# quelques clics.
|
||||||
|
#
|
||||||
|
# Copyright © 2017 Gabriel Détraz
|
||||||
|
# Copyright © 2017 Goulven Kermarec
|
||||||
|
# Copyright © 2017 Augustin Lemesle
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
# App de recherche pour re2o
|
||||||
|
# Augustin lemesle, Gabriel Détraz, Goulven Kermarec
|
||||||
|
# Gplv2
|
||||||
|
from django.shortcuts import render
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
from django.template.context_processors import csrf
|
||||||
|
from django.template import Context, RequestContext, loader
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
|
||||||
|
from django.db.models import Q
|
||||||
|
from users.models import User
|
||||||
|
from search.forms import SearchForm, SearchFormPlus
|
||||||
|
|
||||||
|
from media.models import Media, Emprunt
|
||||||
|
|
||||||
|
def form(ctx, template, request):
|
||||||
|
c = ctx
|
||||||
|
c.update(csrf(request))
|
||||||
|
return render(request, template, c)
|
||||||
|
|
||||||
|
def search_result(search, type, request):
|
||||||
|
date_deb = None
|
||||||
|
date_fin = None
|
||||||
|
states=[]
|
||||||
|
aff=[]
|
||||||
|
if(type):
|
||||||
|
aff = search.cleaned_data['affichage']
|
||||||
|
states = search.cleaned_data['filtre']
|
||||||
|
date_deb = search.cleaned_data['date_deb']
|
||||||
|
date_fin = search.cleaned_data['date_fin']
|
||||||
|
date_query = Q()
|
||||||
|
if aff==[]:
|
||||||
|
aff = ['0','1','2']
|
||||||
|
if date_deb != None:
|
||||||
|
date_query = date_query & Q(date_emprunt__gte=date_deb)
|
||||||
|
if date_fin != None:
|
||||||
|
date_query = date_query & Q(date_emprunt__lte=date_fin)
|
||||||
|
search = search.cleaned_data['search_field']
|
||||||
|
query1 = Q()
|
||||||
|
for s in states:
|
||||||
|
query1 = query1 | Q(state = s)
|
||||||
|
|
||||||
|
connexion = []
|
||||||
|
|
||||||
|
recherche = {'users_list': None, 'emprunts_list' : None, 'medias_list' : None}
|
||||||
|
|
||||||
|
if request.user.has_perms(('perm',)):
|
||||||
|
query = Q(user__pseudo__icontains = search) | Q(user__name__icontains = search) | Q(user__surname__icontains = search)
|
||||||
|
else:
|
||||||
|
query = (Q(user__pseudo__icontains = search) | Q(user__name__icontains = search) | Q(user__surname__icontains = search)) & Q(user = request.user)
|
||||||
|
|
||||||
|
|
||||||
|
for i in aff:
|
||||||
|
if i == '0':
|
||||||
|
query_user_list = Q(pseudo__icontains = search) | Q(name__icontains = search) | Q(surname__icontains = search) & query1
|
||||||
|
if request.user.has_perms(('perm',)):
|
||||||
|
recherche['users_list'] = User.objects.filter(query_user_list).order_by('state', 'surname')
|
||||||
|
else :
|
||||||
|
recherche['users_list'] = User.objects.filter(query_user_list & Q(id=request.user.id)).order_by('state', 'surname')
|
||||||
|
if i == '1':
|
||||||
|
recherche['emprunts_list'] = Emprunt.objects.filter(query & date_query).order_by('date_emprunt').reverse()
|
||||||
|
if i == '2':
|
||||||
|
recherche['medias_list'] = Media.objects.filter(Q(auteur__nom__icontains = search) | Q(titre__icontains = search))
|
||||||
|
search_display_page = 5
|
||||||
|
|
||||||
|
for r in recherche:
|
||||||
|
if recherche[r] != None:
|
||||||
|
recherche[r] = recherche[r][:search_display_page]
|
||||||
|
|
||||||
|
recherche.update({'max_result': search_display_page})
|
||||||
|
|
||||||
|
return recherche
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def search(request):
|
||||||
|
search = SearchForm(request.POST or None)
|
||||||
|
if search.is_valid():
|
||||||
|
return form(search_result(search, False, request), 'search/index.html',request)
|
||||||
|
return form({'searchform' : search}, 'search/search.html', request)
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def searchp(request):
|
||||||
|
search = SearchFormPlus(request.POST or None)
|
||||||
|
if search.is_valid():
|
||||||
|
return form(search_result(search, True, request), 'search/index.html',request)
|
||||||
|
return form({'searchform' : search}, 'search/search.html', request)
|
||||||
|
|
@ -54,10 +54,23 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
<div class="collapse navbar-collapse" id="myNavbar">
|
<div class="collapse navbar-collapse" id="myNavbar">
|
||||||
<ul class="nav navbar-nav">
|
<ul class="nav navbar-nav">
|
||||||
<li><a href="{% url "users:mon-profil" %}">Mon profil</a></li>
|
<li><a href="{% url "users:mon-profil" %}">Mon profil</a></li>
|
||||||
{% if is_admin %}
|
{% if is_perm %}
|
||||||
<li><a href="{% url "users:index" %}">Utilisateurs</a></li>
|
<li><a href="{% url "users:index" %}">Utilisateurs</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<li><a href="{% url "media:index" %}">Media</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<div class="col-sm-3 col-md-3 navbar-right">
|
||||||
|
<form action="{% url "search:search"%}" method="POST" class="navbar-form" role="search">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control" placeholder="Search" name="search_field" id="search-term">
|
||||||
|
<div class="input-group-btn">
|
||||||
|
<button class="btn btn-default" type="submit"><i class="glyphicon glyphicon-search"></i></button>
|
||||||
|
<a href="{% url "search:searchp" %}" class="btn btn-default" role="button"><i class="glyphicon glyphicon-plus"></i></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
<ul class="nav navbar-nav navbar-right">
|
<ul class="nav navbar-nav navbar-right">
|
||||||
<li>
|
<li>
|
||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
|
@ -25,7 +25,7 @@ from django.contrib.auth.models import Group
|
|||||||
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
||||||
from reversion.admin import VersionAdmin
|
from reversion.admin import VersionAdmin
|
||||||
|
|
||||||
from .models import User, Machine, Request
|
from .models import User, Right, ListRight, Request
|
||||||
from .forms import UserChangeForm, UserCreationForm
|
from .forms import UserChangeForm, UserCreationForm
|
||||||
|
|
||||||
|
|
||||||
@ -42,8 +42,11 @@ class UserAdmin(admin.ModelAdmin):
|
|||||||
class RequestAdmin(admin.ModelAdmin):
|
class RequestAdmin(admin.ModelAdmin):
|
||||||
list_display = ('user', 'type', 'created_at', 'expires_at')
|
list_display = ('user', 'type', 'created_at', 'expires_at')
|
||||||
|
|
||||||
class MachineAdmin(VersionAdmin):
|
class RightAdmin(VersionAdmin):
|
||||||
list_display = ('mac_address','proprio')
|
list_display = ('user', 'right')
|
||||||
|
|
||||||
|
class ListRightAdmin(VersionAdmin):
|
||||||
|
list_display = ('listright',)
|
||||||
|
|
||||||
class UserAdmin(VersionAdmin, BaseUserAdmin):
|
class UserAdmin(VersionAdmin, BaseUserAdmin):
|
||||||
# The forms to add and change user instances
|
# The forms to add and change user instances
|
||||||
@ -72,9 +75,10 @@ class UserAdmin(VersionAdmin, BaseUserAdmin):
|
|||||||
ordering = ('pseudo',)
|
ordering = ('pseudo',)
|
||||||
filter_horizontal = ()
|
filter_horizontal = ()
|
||||||
|
|
||||||
admin.site.register(Machine, MachineAdmin)
|
|
||||||
admin.site.register(User, UserAdmin)
|
admin.site.register(User, UserAdmin)
|
||||||
admin.site.register(Request, RequestAdmin)
|
admin.site.register(Request, RequestAdmin)
|
||||||
|
admin.site.register(ListRight, ListRightAdmin)
|
||||||
|
admin.site.register(Right, RightAdmin)
|
||||||
# Now register the new UserAdmin...
|
# Now register the new UserAdmin...
|
||||||
admin.site.unregister(User)
|
admin.site.unregister(User)
|
||||||
admin.site.register(User, UserAdmin)
|
admin.site.register(User, UserAdmin)
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
# Copyright © 2017 Gabriel Détraz
|
|
||||||
# Copyright © 2017 Goulven Kermarec
|
|
||||||
# Copyright © 2017 Augustin Lemesle
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License along
|
|
||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
#
|
|
||||||
# Ce script est appellé avant le démarage du portail, il insère les bonnes règles
|
|
||||||
# dans l'iptables et active le routage
|
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
|
||||||
|
|
||||||
|
|
||||||
from users.models import restore_iptables, create_ip_set, fill_ipset, apply
|
|
||||||
from portail_captif.settings import AUTORIZED_INTERFACES
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
|
||||||
help = 'Mets en place iptables et le set ip au démarage'
|
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
|
||||||
# Creation de l'ipset
|
|
||||||
create_ip_set()
|
|
||||||
# Remplissage avec les macs autorisées
|
|
||||||
fill_ipset()
|
|
||||||
# Restauration de l'iptables
|
|
||||||
restore_iptables()
|
|
||||||
# Activation du routage sur les bonnes if
|
|
||||||
for interface in AUTORIZED_INTERFACES:
|
|
||||||
apply(["sudo", "-n", "sysctl", "net.ipv6.conf.%s.forwarding=1" % interface])
|
|
||||||
apply(["sudo", "-n", "sysctl", "net.ipv4.conf.%s.forwarding=1" % interface])
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,13 +1,16 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.2 on 2017-06-28 10:30
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -15,16 +18,20 @@ class Migration(migrations.Migration):
|
|||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='User',
|
name='User',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(serialize=False, verbose_name='ID', primary_key=True, auto_created=True)),
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('password', models.CharField(max_length=128, verbose_name='password')),
|
('password', models.CharField(max_length=128, verbose_name='password')),
|
||||||
('last_login', models.DateTimeField(blank=True, verbose_name='last login', null=True)),
|
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
||||||
('name', models.CharField(max_length=255)),
|
('name', models.CharField(max_length=255)),
|
||||||
('surname', models.CharField(max_length=255)),
|
('surname', models.CharField(max_length=255)),
|
||||||
('email', models.EmailField(max_length=254)),
|
('email', models.EmailField(blank=True, max_length=254, null=True)),
|
||||||
('state', models.IntegerField(default=0, choices=[(0, 'STATE_ACTIVE'), (1, 'STATE_DISABLED'), (2, 'STATE_ARCHIVE')])),
|
('telephone', models.CharField(blank=True, max_length=15, null=True)),
|
||||||
('pseudo', models.CharField(max_length=32, unique=True)),
|
('adresse', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
|
('maxemprunt', models.IntegerField(default=5)),
|
||||||
|
('state', models.IntegerField(choices=[(0, 'STATE_ACTIVE'), (1, 'STATE_DISABLED'), (2, 'STATE_ARCHIVE')], default=0)),
|
||||||
|
('pseudo', models.CharField(help_text='Doit contenir uniquement des lettres, chiffres, ou tirets. ', max_length=32, unique=True)),
|
||||||
|
('comment', models.CharField(blank=True, help_text='Commentaire, promo', max_length=255)),
|
||||||
('registered', models.DateTimeField(auto_now_add=True)),
|
('registered', models.DateTimeField(auto_now_add=True)),
|
||||||
('admin', models.BooleanField(default=False)),
|
('right', models.IntegerField(choices=[(0, 'BASIC'), (1, 'PERM'), (2, 'BUREAU')], default=0)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'abstract': False,
|
'abstract': False,
|
||||||
@ -33,8 +40,8 @@ class Migration(migrations.Migration):
|
|||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Request',
|
name='Request',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(serialize=False, verbose_name='ID', primary_key=True, auto_created=True)),
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('type', models.CharField(max_length=2, choices=[('PW', 'Mot de passe'), ('EM', 'Email')])),
|
('type', models.CharField(choices=[('PW', 'Mot de passe'), ('EM', 'Email')], max_length=2)),
|
||||||
('token', models.CharField(max_length=32)),
|
('token', models.CharField(max_length=32)),
|
||||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
('expires_at', models.DateTimeField()),
|
('expires_at', models.DateTimeField()),
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('users', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='user',
|
|
||||||
name='comment',
|
|
||||||
field=models.CharField(blank=True, help_text='Commentaire, promo', max_length=255),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='user',
|
|
||||||
name='pseudo',
|
|
||||||
field=models.CharField(unique=True, help_text='Doit contenir uniquement des lettres, chiffres, ou tirets. ', max_length=32),
|
|
||||||
),
|
|
||||||
]
|
|
25
users/migrations/0002_auto_20170629_1438.py
Normal file
25
users/migrations/0002_auto_20170629_1438.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.2 on 2017-06-29 12:38
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='user',
|
||||||
|
name='maxemprunt',
|
||||||
|
field=models.IntegerField(default=5, help_text="Maximum d'emprunts autorisés"),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='user',
|
||||||
|
name='right',
|
||||||
|
field=models.IntegerField(choices=[(0, 'BASIC'), (1, 'PERM'), (2, 'BUREAU')], default=0, help_text='Droits accordés'),
|
||||||
|
),
|
||||||
|
]
|
45
users/migrations/0003_auto_20170629_2156.py
Normal file
45
users/migrations/0003_auto_20170629_2156.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.2 on 2017-06-29 19:56
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0002_auto_20170629_1438'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ListRight',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('listright', models.CharField(max_length=255, unique=True)),
|
||||||
|
('details', models.CharField(blank=True, help_text='Description', max_length=255)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Right',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('right', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='users.ListRight')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='user',
|
||||||
|
name='right',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='right',
|
||||||
|
name='user',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='right',
|
||||||
|
unique_together=set([('user', 'right')]),
|
||||||
|
),
|
||||||
|
]
|
@ -1,25 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
import macaddress.fields
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('users', '0002_auto_20170610_1550'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Machines',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
|
|
||||||
('mac_address', macaddress.fields.MACAddressField(unique=True, max_length=17, integer=False)),
|
|
||||||
('proprio', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=django.db.models.deletion.PROTECT)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,32 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
import macaddress.fields
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('users', '0003_machines'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Machine',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
|
|
||||||
('mac_address', macaddress.fields.MACAddressField(max_length=17, unique=True, integer=False)),
|
|
||||||
('proprio', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=django.db.models.deletion.PROTECT)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='machines',
|
|
||||||
name='proprio',
|
|
||||||
),
|
|
||||||
migrations.DeleteModel(
|
|
||||||
name='Machines',
|
|
||||||
),
|
|
||||||
]
|
|
258
users/models.py
258
users/models.py
@ -28,9 +28,8 @@ from django.db.models.signals import post_save, post_delete
|
|||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
|
|
||||||
from macaddress.fields import MACAddressField
|
|
||||||
|
|
||||||
from portail_captif.settings import GENERIC_IPSET_COMMAND, IPSET_NAME, REQ_EXPIRE_HRS,FORBIDEN_INTERFACES, SERVER_SELF_IP, AUTORIZED_INTERFACES, PORTAIL_ACTIVE
|
from med.settings import MAX_EMPRUNT, REQ_EXPIRE_HRS
|
||||||
import re, uuid
|
import re, uuid
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
@ -38,116 +37,6 @@ from django.utils import timezone
|
|||||||
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
|
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
def apply(cmd):
|
|
||||||
return subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
|
||||||
|
|
||||||
def mac_from_ip(ip):
|
|
||||||
cmd = ['/usr/sbin/arp','-na',ip]
|
|
||||||
p = apply(cmd)
|
|
||||||
output, errors = p.communicate()
|
|
||||||
if output is not None :
|
|
||||||
mac_addr = output.decode().split()[3]
|
|
||||||
return str(mac_addr)
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def create_ip_set():
|
|
||||||
command_to_execute = ["sudo", "-n"] + GENERIC_IPSET_COMMAND.split() + ["create",IPSET_NAME,"hash:mac","hashsize","1024","maxelem","65536"]
|
|
||||||
apply(command_to_execute)
|
|
||||||
command_to_execute = ["sudo", "-n"] + GENERIC_IPSET_COMMAND.split() + ["flush",IPSET_NAME]
|
|
||||||
apply(command_to_execute)
|
|
||||||
return
|
|
||||||
|
|
||||||
def fill_ipset():
|
|
||||||
all_machines = Machine.objects.filter(proprio__in=User.objects.filter(state=User.STATE_ACTIVE))
|
|
||||||
ipset = "%s\nCOMMIT\n" % '\n'.join(["add %s %s" % (IPSET_NAME, str(machine.mac_address)) for machine in all_machines])
|
|
||||||
command_to_execute = ["sudo", "-n"] + GENERIC_IPSET_COMMAND.split() + ["restore"]
|
|
||||||
process = apply(command_to_execute)
|
|
||||||
process.communicate(input=ipset.encode('utf-8'))
|
|
||||||
return
|
|
||||||
|
|
||||||
class iptables:
|
|
||||||
def __init__(self):
|
|
||||||
self.nat = "\n*nat"
|
|
||||||
self.mangle = "\n*mangle"
|
|
||||||
self.filter = "\n*filter"
|
|
||||||
|
|
||||||
def commit(self, chain):
|
|
||||||
self.add(chain, "COMMIT\n")
|
|
||||||
|
|
||||||
def add(self, chain, value):
|
|
||||||
setattr(self, chain, getattr(self, chain) + "\n" + value)
|
|
||||||
|
|
||||||
def init_filter(self, subchain, decision="ACCEPT"):
|
|
||||||
self.add("filter", ":" + subchain + " " + decision)
|
|
||||||
|
|
||||||
def init_nat(self, subchain, decision="ACCEPT"):
|
|
||||||
self.add("nat", ":" + subchain + " " + decision)
|
|
||||||
|
|
||||||
def init_mangle(self, subchain, decision="ACCEPT"):
|
|
||||||
self.add("mangle", ":" + subchain + " " + decision)
|
|
||||||
|
|
||||||
def jump(self, chain, subchainA, subchainB):
|
|
||||||
self.add(chain, "-A " + subchainA + " -j " + subchainB)
|
|
||||||
|
|
||||||
def gen_filter(ipt):
|
|
||||||
ipt.init_filter("INPUT")
|
|
||||||
ipt.init_filter("FORWARD")
|
|
||||||
ipt.init_filter("OUTPUT")
|
|
||||||
for interface in FORBIDEN_INTERFACES:
|
|
||||||
ipt.add("filter", "-A FORWARD -o %s -j REJECT --reject-with icmp-port-unreachable" % interface)
|
|
||||||
ipt.commit("filter")
|
|
||||||
return ipt
|
|
||||||
|
|
||||||
def gen_nat(ipt, nat_active=True):
|
|
||||||
ipt.init_nat("PREROUTING")
|
|
||||||
ipt.init_nat("INPUT")
|
|
||||||
ipt.init_nat("OUTPUT")
|
|
||||||
ipt.init_nat("POSTROUTING")
|
|
||||||
if nat_active:
|
|
||||||
ipt.init_nat("CAPTIF", decision="-")
|
|
||||||
ipt.jump("nat", "PREROUTING", "CAPTIF")
|
|
||||||
ipt.jump("nat", "POSTROUTING", "MASQUERADE")
|
|
||||||
if PORTAIL_ACTIVE:
|
|
||||||
ipt.add("nat", "-A CAPTIF -m set ! --match-set %s src -j DNAT --to-destination %s" % (IPSET_NAME, SERVER_SELF_IP))
|
|
||||||
ipt.jump("nat", "CAPTIF", "RETURN")
|
|
||||||
ipt.commit("nat")
|
|
||||||
return ipt
|
|
||||||
|
|
||||||
def gen_mangle(ipt):
|
|
||||||
ipt.init_mangle("PREROUTING")
|
|
||||||
ipt.init_mangle("INPUT")
|
|
||||||
ipt.init_mangle("FORWARD")
|
|
||||||
ipt.init_mangle("OUTPUT")
|
|
||||||
ipt.init_mangle("POSTROUTING")
|
|
||||||
for interface in AUTORIZED_INTERFACES:
|
|
||||||
ipt.add("mangle", """-A PREROUTING -i %s -m state --state NEW -j LOG --log-prefix "LOG_ALL " """ % interface)
|
|
||||||
ipt.commit("mangle")
|
|
||||||
return ipt
|
|
||||||
|
|
||||||
def restore_iptables():
|
|
||||||
""" Restrore l'iptables pour la création du portail. Est appellé par root au démarage du serveur"""
|
|
||||||
ipt = iptables()
|
|
||||||
gen_mangle(ipt)
|
|
||||||
gen_nat(ipt)
|
|
||||||
gen_filter(ipt)
|
|
||||||
global_chain = ipt.nat + ipt.filter + ipt.mangle
|
|
||||||
command_to_execute = ["sudo","-n","/sbin/iptables-restore"]
|
|
||||||
process = apply(command_to_execute)
|
|
||||||
process.communicate(input=global_chain.encode('utf-8'))
|
|
||||||
return
|
|
||||||
|
|
||||||
def disable_iptables():
|
|
||||||
""" Insère une iptables minimaliste sans nat"""
|
|
||||||
ipt = iptables()
|
|
||||||
gen_mangle(ipt)
|
|
||||||
gen_filter(ipt)
|
|
||||||
gen_nat(ipt, nat_active=False)
|
|
||||||
global_chain = ipt.nat + ipt.filter + ipt.mangle
|
|
||||||
command_to_execute = ["sudo","-n","/sbin/iptables-restore"]
|
|
||||||
process = apply(command_to_execute)
|
|
||||||
process.communicate(input=global_chain.encode('utf-8'))
|
|
||||||
return
|
|
||||||
|
|
||||||
class UserManager(BaseUserManager):
|
class UserManager(BaseUserManager):
|
||||||
def _create_user(self, pseudo, name, surname, email, password=None, su=False):
|
def _create_user(self, pseudo, name, surname, email, password=None, su=False):
|
||||||
@ -193,15 +82,17 @@ class User(AbstractBaseUser):
|
|||||||
(2, 'STATE_ARCHIVE'),
|
(2, 'STATE_ARCHIVE'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255)
|
||||||
surname = models.CharField(max_length=255)
|
surname = models.CharField(max_length=255)
|
||||||
email = models.EmailField()
|
email = models.EmailField(null=True, blank=True)
|
||||||
|
telephone = models.CharField(max_length=15, null=True, blank=True)
|
||||||
|
adresse = models.CharField(max_length=255, null=True, blank=True)
|
||||||
|
maxemprunt = models.IntegerField(default=MAX_EMPRUNT, help_text="Maximum d'emprunts autorisés")
|
||||||
state = models.IntegerField(choices=STATES, default=STATE_ACTIVE)
|
state = models.IntegerField(choices=STATES, default=STATE_ACTIVE)
|
||||||
pseudo = models.CharField(max_length=32, unique=True, help_text="Doit contenir uniquement des lettres, chiffres, ou tirets. ")
|
pseudo = models.CharField(max_length=32, unique=True, help_text="Doit contenir uniquement des lettres, chiffres, ou tirets. ")
|
||||||
comment = models.CharField(help_text="Commentaire, promo", max_length=255, blank=True)
|
comment = models.CharField(help_text="Commentaire, promo", max_length=255, blank=True)
|
||||||
registered = models.DateTimeField(auto_now_add=True)
|
registered = models.DateTimeField(auto_now_add=True)
|
||||||
admin = models.BooleanField(default=False)
|
|
||||||
|
|
||||||
USERNAME_FIELD = 'pseudo'
|
USERNAME_FIELD = 'pseudo'
|
||||||
REQUIRED_FIELDS = ['name', 'surname', 'email']
|
REQUIRED_FIELDS = ['name', 'surname', 'email']
|
||||||
@ -218,7 +109,11 @@ class User(AbstractBaseUser):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def is_admin(self):
|
def is_admin(self):
|
||||||
return self.admin
|
try:
|
||||||
|
Right.objects.get(user=self, right__listright='admin')
|
||||||
|
except Right.DoesNotExist:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
@is_admin.setter
|
@is_admin.setter
|
||||||
def is_admin(self, value):
|
def is_admin(self, value):
|
||||||
@ -229,9 +124,11 @@ class User(AbstractBaseUser):
|
|||||||
|
|
||||||
def has_perms(self, perms, obj=None):
|
def has_perms(self, perms, obj=None):
|
||||||
for perm in perms:
|
for perm in perms:
|
||||||
if perm=="admin":
|
try:
|
||||||
return self.is_admin
|
Right.objects.get(user=self, right__listright=perm)
|
||||||
return False
|
return True
|
||||||
|
except Right.DoesNotExist:
|
||||||
|
return False
|
||||||
|
|
||||||
def get_full_name(self):
|
def get_full_name(self):
|
||||||
return '%s %s' % (self.name, self.surname)
|
return '%s %s' % (self.name, self.surname)
|
||||||
@ -242,48 +139,33 @@ class User(AbstractBaseUser):
|
|||||||
def has_perm(self, perm, obj=None):
|
def has_perm(self, perm, obj=None):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def has_right(self, right):
|
||||||
|
return Right.objects.filter(user=self).filter(right=ListRight.objects.get(listright=right)).exists()
|
||||||
|
|
||||||
def has_module_perms(self, app_label):
|
def has_module_perms(self, app_label):
|
||||||
# Simplest version again
|
# Simplest version again
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def get_admin_right(self):
|
||||||
|
admin, created = ListRight.objects.get_or_create(listright="admin")
|
||||||
|
return admin
|
||||||
|
|
||||||
def make_admin(self):
|
def make_admin(self):
|
||||||
""" Make User admin """
|
""" Make User admin """
|
||||||
self.admin = True
|
user_admin_right = Right(user=self, right=self.get_admin_right())
|
||||||
self.save()
|
user_admin_right.save()
|
||||||
|
|
||||||
def un_admin(self):
|
def un_admin(self):
|
||||||
self.admin = False
|
try:
|
||||||
self.save()
|
user_right = Right.objects.get(user=self,right=self.get_admin_right())
|
||||||
|
except Right.DoesNotExist:
|
||||||
def machines(self):
|
return
|
||||||
return Machine.objects.filter(proprio=self)
|
user_right.delete()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name + " " + self.surname
|
return self.pseudo
|
||||||
|
|
||||||
|
|
||||||
class Machine(models.Model):
|
|
||||||
proprio = models.ForeignKey('User', on_delete=models.PROTECT)
|
|
||||||
mac_address = MACAddressField(integer=False, unique=True)
|
|
||||||
|
|
||||||
def add_to_set(self):
|
|
||||||
command_to_execute = ["sudo", "-n"] + GENERIC_IPSET_COMMAND.split() + ["add",IPSET_NAME,str(self.mac_address)]
|
|
||||||
apply(command_to_execute)
|
|
||||||
|
|
||||||
def del_to_set(self):
|
|
||||||
command_to_execute = ["sudo", "-n"] + GENERIC_IPSET_COMMAND.split() + ["del",IPSET_NAME,str(self.mac_address)]
|
|
||||||
apply(command_to_execute)
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Machine)
|
|
||||||
def machine_post_save(sender, **kwargs):
|
|
||||||
machine = kwargs['instance']
|
|
||||||
machine.add_to_set()
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=Machine)
|
|
||||||
def machine_post_delete(sender, **kwargs):
|
|
||||||
machine = kwargs['instance']
|
|
||||||
machine.del_to_set()
|
|
||||||
|
|
||||||
class Request(models.Model):
|
class Request(models.Model):
|
||||||
PASSWD = 'PW'
|
PASSWD = 'PW'
|
||||||
EMAIL = 'EM'
|
EMAIL = 'EM'
|
||||||
@ -305,6 +187,27 @@ class Request(models.Model):
|
|||||||
self.token = str(uuid.uuid4()).replace('-', '') # remove hyphens
|
self.token = str(uuid.uuid4()).replace('-', '') # remove hyphens
|
||||||
super(Request, self).save()
|
super(Request, self).save()
|
||||||
|
|
||||||
|
class Right(models.Model):
|
||||||
|
PRETTY_NAME = "Droits affectés à des users"
|
||||||
|
|
||||||
|
user = models.ForeignKey('User', on_delete=models.PROTECT)
|
||||||
|
right = models.ForeignKey('ListRight', on_delete=models.PROTECT)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = ("user", "right")
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.user) + " - " + str(self.right)
|
||||||
|
|
||||||
|
class ListRight(models.Model):
|
||||||
|
PRETTY_NAME = "Liste des droits existants"
|
||||||
|
|
||||||
|
listright = models.CharField(max_length=255, unique=True)
|
||||||
|
details = models.CharField(help_text="Description", max_length=255, blank=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.listright
|
||||||
|
|
||||||
class BaseInfoForm(ModelForm):
|
class BaseInfoForm(ModelForm):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(BaseInfoForm, self).__init__(*args, **kwargs)
|
super(BaseInfoForm, self).__init__(*args, **kwargs)
|
||||||
@ -319,33 +222,22 @@ class BaseInfoForm(ModelForm):
|
|||||||
'pseudo',
|
'pseudo',
|
||||||
'surname',
|
'surname',
|
||||||
'email',
|
'email',
|
||||||
]
|
'telephone',
|
||||||
|
'adresse',
|
||||||
class EditInfoForm(BaseInfoForm):
|
|
||||||
class Meta(BaseInfoForm.Meta):
|
|
||||||
fields = [
|
|
||||||
'name',
|
|
||||||
'pseudo',
|
|
||||||
'surname',
|
|
||||||
'comment',
|
|
||||||
'email',
|
|
||||||
'admin',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
class InfoForm(BaseInfoForm):
|
class InfoForm(BaseInfoForm):
|
||||||
class Meta(BaseInfoForm.Meta):
|
class Meta(BaseInfoForm.Meta):
|
||||||
fields = [
|
fields = [
|
||||||
'name',
|
'name',
|
||||||
'pseudo',
|
'pseudo',
|
||||||
'comment',
|
|
||||||
'surname',
|
'surname',
|
||||||
'email',
|
'email',
|
||||||
'admin',
|
'telephone',
|
||||||
|
'adresse',
|
||||||
|
'maxemprunt',
|
||||||
]
|
]
|
||||||
|
|
||||||
class UserForm(EditInfoForm):
|
|
||||||
class Meta(EditInfoForm.Meta):
|
|
||||||
fields = '__all__'
|
|
||||||
|
|
||||||
class PasswordForm(ModelForm):
|
class PasswordForm(ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -357,7 +249,35 @@ class StateForm(ModelForm):
|
|||||||
model = User
|
model = User
|
||||||
fields = ['state']
|
fields = ['state']
|
||||||
|
|
||||||
class MachineForm(ModelForm):
|
class RightForm(ModelForm):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(RightForm, self).__init__(*args, **kwargs)
|
||||||
|
self.fields['right'].label = 'Droit'
|
||||||
|
self.fields['right'].empty_label = "Choisir un nouveau droit"
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Machine
|
model = Right
|
||||||
exclude = '__all__'
|
fields = ['right']
|
||||||
|
|
||||||
|
|
||||||
|
class DelRightForm(Form):
|
||||||
|
rights = forms.ModelMultipleChoiceField(queryset=Right.objects.all(), label="Droits actuels", widget=forms.CheckboxSelectMultiple)
|
||||||
|
|
||||||
|
|
||||||
|
class ListRightForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = ListRight
|
||||||
|
fields = ['listright', 'details']
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(ListRightForm, self).__init__(*args, **kwargs)
|
||||||
|
self.fields['listright'].label = 'Nom du droit/groupe'
|
||||||
|
|
||||||
|
class NewListRightForm(ListRightForm):
|
||||||
|
class Meta(ListRightForm.Meta):
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
class DelListRightForm(Form):
|
||||||
|
listrights = forms.ModelMultipleChoiceField(queryset=ListRight.objects.all(), label="Droits actuels", widget=forms.CheckboxSelectMultiple)
|
||||||
|
|
||||||
|
|
||||||
|
44
users/templates/users/aff_listright.html
Normal file
44
users/templates/users/aff_listright.html
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
{% comment %}
|
||||||
|
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
quelques clics.
|
||||||
|
|
||||||
|
Copyright © 2017 Gabriel Détraz
|
||||||
|
Copyright © 2017 Goulven Kermarec
|
||||||
|
Copyright © 2017 Augustin Lemesle
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Droit</th>
|
||||||
|
<th>Details</th>
|
||||||
|
<th></th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
{% for listright in listright_list %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ listright.listright }}</td>
|
||||||
|
<td>{{ listright.details }}</td>
|
||||||
|
<td class="text-right">
|
||||||
|
{% include 'buttons/edit.html' with href='users:edit-listright' id=listright.id %}
|
||||||
|
{% include 'buttons/history.html' with href='users:history' name='listright' id=listright.id %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
<th>Nom</th>
|
<th>Nom</th>
|
||||||
<th>Pseudo</th>
|
<th>Pseudo</th>
|
||||||
<th>Mail</th>
|
<th>Mail</th>
|
||||||
<th>Machines</th>
|
<th>Max emprunts</th>
|
||||||
<th>Profil</th>
|
<th>Profil</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@ -43,13 +43,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
<td>{{ user.surname }}</td>
|
<td>{{ user.surname }}</td>
|
||||||
<td>{{ user.pseudo }}</td>
|
<td>{{ user.pseudo }}</td>
|
||||||
<td>{{ user.email }}</td>
|
<td>{{ user.email }}</td>
|
||||||
<td><table class="table table-striped">
|
<td>{{ user.maxemprunt }}</td>
|
||||||
{% for machine in user.machines %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ machine.mac_address }}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
</td>
|
</td>
|
||||||
<td><a href="{% url "users:profil" user.id%}" class="btn btn-primary btn-sm" role="button"><i class="glyphicon glyphicon-user"></i></a>
|
<td><a href="{% url "users:profil" user.id%}" class="btn btn-primary btn-sm" role="button"><i class="glyphicon glyphicon-user"></i></a>
|
||||||
</td>
|
</td>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "users/sidebar.html" %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
se veut agnostique au réseau considéré, de manière à être installable en
|
se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
38
users/templates/users/index_listright.html
Normal file
38
users/templates/users/index_listright.html
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{% extends "users/sidebar.html" %}
|
||||||
|
{% comment %}
|
||||||
|
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
quelques clics.
|
||||||
|
|
||||||
|
Copyright © 2017 Gabriel Détraz
|
||||||
|
Copyright © 2017 Goulven Kermarec
|
||||||
|
Copyright © 2017 Augustin Lemesle
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
|
||||||
|
{% block title %}Utilisateurs{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>Liste des droits</h2>
|
||||||
|
<a class="btn btn-primary btn-sm" role="button" href="{% url 'users:add-listright' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un droit ou groupe</a>
|
||||||
|
<a class="btn btn-danger btn-sm" role="button" href="{% url 'users:del-listright' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un ou plusieurs droits/groupes</a>
|
||||||
|
{% include "users/aff_listright.html" with listright_list=listright_list %}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
{% endblock %}
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "users/sidebar.html" %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
se veut agnostique au réseau considéré, de manière à être installable en
|
se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
@ -38,11 +38,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
<i class="glyphicon glyphicon-lock"></i>
|
<i class="glyphicon glyphicon-lock"></i>
|
||||||
Changer le mot de passe
|
Changer le mot de passe
|
||||||
</a>
|
</a>
|
||||||
{% if is_admin %}
|
{% if is_bureau %}
|
||||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'users:state' user.id %}">
|
<a class="btn btn-primary btn-sm" role="button" href="{% url 'users:state' user.id %}">
|
||||||
<i class="glyphicon glyphicon-flash"></i>
|
<i class="glyphicon glyphicon-flash"></i>
|
||||||
Changer le statut
|
Changer le statut
|
||||||
</a>
|
</a>
|
||||||
|
<a class="btn btn-primary btn-sm" role="button" href="{% url 'users:add-right' user.id %}">
|
||||||
|
<i class="glyphicon glyphicon-ok"></i>
|
||||||
|
Ajouter un droit
|
||||||
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a class="btn btn-info btn-sm" role="button" href="{% url 'users:history' 'user' user.id %}">
|
<a class="btn btn-info btn-sm" role="button" href="{% url 'users:history' 'user' user.id %}">
|
||||||
<i class="glyphicon glyphicon-time"></i>
|
<i class="glyphicon glyphicon-time"></i>
|
||||||
@ -71,7 +75,24 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
<th>Date d'inscription</th>
|
<th>Date d'inscription</th>
|
||||||
<td>{{ user.registered }}</td>
|
<td>{{ user.registered }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<th>Statut</th>
|
<tr>
|
||||||
|
<th>Adresse</th>
|
||||||
|
<td>{{ user.adresse }}</td>
|
||||||
|
<th>Telephone</th>
|
||||||
|
<td>{{ user.telephone }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Emprunts maximums autorisés</th>
|
||||||
|
<td>{{ user.maxemprunt }}</td>
|
||||||
|
<th>Droits</th>
|
||||||
|
{% if list_droits %}
|
||||||
|
<td>{% for droit in list_droits %}{{ droit.right }}{% if list_droits|length != forloop.counter %} - {% endif %} {% endfor %}</td>
|
||||||
|
{% else %}
|
||||||
|
<td>Aucun</td>
|
||||||
|
{% endif %}
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Statut</th>
|
||||||
{% if user.state == 0 %}
|
{% if user.state == 0 %}
|
||||||
<td><font color="green">Actif</font></td>
|
<td><font color="green">Actif</font></td>
|
||||||
{% elif user.state == 1 %}
|
{% elif user.state == 1 %}
|
||||||
@ -83,28 +104,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
<td>{{ user.last_login }}</td>
|
<td>{{ user.last_login }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<h2>Machines enregistrées</h2>
|
<h2>Emprunts</h2>
|
||||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'users:capture' %}">
|
<h4><a class="btn btn-primary btn-sm" role="button" href="{% url 'media:add-emprunt' user.id %}"><i class="glyphicon glyphicon-flag"></i> Ajouter</a></h4>
|
||||||
<i class="glyphicon glyphicon-flash"></i>
|
{% if emprunts_list %}
|
||||||
Enregistrer la machine utilisée actuellement
|
{% include "media/aff_emprunts.html" with emprunts_list=emprunts_list %}
|
||||||
</a>
|
{% else %}
|
||||||
<table class="table table-striped">
|
<p>Aucun emprunt</p>
|
||||||
<thead>
|
{% endif %}
|
||||||
<tr>
|
|
||||||
<th>Adresse mac</th>
|
|
||||||
<th>Historique</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
{% for machine in machines_list %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ machine.mac_address }}</td>
|
|
||||||
<td><a class="btn btn-info btn-sm" role="button" href="{% url 'users:history' 'machines' machine.id %}">
|
|
||||||
<i class="glyphicon glyphicon-time"></i>
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
|
48
users/templates/users/sidebar.html
Normal file
48
users/templates/users/sidebar.html
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% comment %}
|
||||||
|
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
quelques clics.
|
||||||
|
|
||||||
|
Copyright © 2017 Gabriel Détraz
|
||||||
|
Copyright © 2017 Goulven Kermarec
|
||||||
|
Copyright © 2017 Augustin Lemesle
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block sidebar %}
|
||||||
|
{% if is_perm %}
|
||||||
|
<a class="list-group-item list-group-item-success" href="{% url "users:new-user" %}">
|
||||||
|
<i class="glyphicon glyphicon-plus"></i>
|
||||||
|
Créer un adhérent
|
||||||
|
</a>
|
||||||
|
<a class="list-group-item list-group-item-info" href="{% url "users:index" %}">
|
||||||
|
<i class="glyphicon glyphicon-list"></i>
|
||||||
|
Adhérents
|
||||||
|
</a>
|
||||||
|
<a class="list-group-item list-group-item-info" href="{% url "users:index-listright" %}">
|
||||||
|
<i class="glyphicon glyphicon-list"></i>
|
||||||
|
Droits
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if is_bureau %}
|
||||||
|
<a class="list-group-item list-group-item-danger" href="{% url "users:del-right" %}">
|
||||||
|
<i class="glyphicon glyphicon-trash"></i>
|
||||||
|
Retirer un droit
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "users/sidebar.html" %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
se veut agnostique au réseau considéré, de manière à être installable en
|
se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
@ -27,16 +27,21 @@ from . import views
|
|||||||
app_name = 'users'
|
app_name = 'users'
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^new_user/$', views.new_user, name='new-user'),
|
url(r'^new_user/$', views.new_user, name='new-user'),
|
||||||
url(r'^capture/$', views.capture, name='capture'),
|
|
||||||
url(r'^edit_info/(?P<userid>[0-9]+)$', views.edit_info, name='edit-info'),
|
url(r'^edit_info/(?P<userid>[0-9]+)$', views.edit_info, name='edit-info'),
|
||||||
url(r'^state/(?P<userid>[0-9]+)$', views.state, name='state'),
|
url(r'^state/(?P<userid>[0-9]+)$', views.state, name='state'),
|
||||||
url(r'^password/(?P<userid>[0-9]+)$', views.password, name='password'),
|
url(r'^password/(?P<userid>[0-9]+)$', views.password, name='password'),
|
||||||
url(r'^profil/(?P<userid>[0-9]+)$', views.profil, name='profil'),
|
url(r'^profil/(?P<userid>[0-9]+)$', views.profil, name='profil'),
|
||||||
url(r'^mon_profil/$', views.mon_profil, name='mon-profil'),
|
url(r'^mon_profil/$', views.mon_profil, name='mon-profil'),
|
||||||
|
url(r'^add_listright/$', views.add_listright, name='add-listright'),
|
||||||
|
url(r'^edit_listright/(?P<listrightid>[0-9]+)$', views.edit_listright, name='edit-listright'),
|
||||||
|
url(r'^del_listright/$', views.del_listright, name='del-listright'),
|
||||||
|
url(r'^index_listright/$', views.index_listright, name='index-listright'),
|
||||||
|
url(r'^add_right/(?P<userid>[0-9]+)$', views.add_right, name='add-right'),
|
||||||
|
url(r'^del_right/$', views.del_right, name='del-right'),
|
||||||
url(r'^process/(?P<token>[a-z0-9]{32})/$', views.process, name='process'),
|
url(r'^process/(?P<token>[a-z0-9]{32})/$', views.process, name='process'),
|
||||||
url(r'^reset_password/$', views.reset_password, name='reset-password'),
|
url(r'^reset_password/$', views.reset_password, name='reset-password'),
|
||||||
url(r'^history/(?P<object>user)/(?P<id>[0-9]+)$', views.history, name='history'),
|
url(r'^history/(?P<object>user)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||||
url(r'^history/(?P<object>machines)/(?P<id>[0-9]+)$', views.history, name='history'),
|
url(r'^history/(?P<object>listright)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||||
url(r'^$', views.index, name='index'),
|
url(r'^$', views.index, name='index'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
198
users/views.py
198
users/views.py
@ -20,7 +20,7 @@
|
|||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
# App de gestion des users pour portail_captif
|
# App de gestion des users pour med
|
||||||
# Goulven Kermarec, Gabriel Détraz, Lemesle Augustin
|
# Goulven Kermarec, Gabriel Détraz, Lemesle Augustin
|
||||||
# Gplv2
|
# Gplv2
|
||||||
from django.shortcuts import get_object_or_404, render, redirect
|
from django.shortcuts import get_object_or_404, render, redirect
|
||||||
@ -39,13 +39,12 @@ from django.db import transaction
|
|||||||
|
|
||||||
from reversion.models import Version
|
from reversion.models import Version
|
||||||
from reversion import revisions as reversion
|
from reversion import revisions as reversion
|
||||||
from users.models import User, MachineForm, Request
|
from users.models import User, Request, ListRight, Right, DelListRightForm, NewListRightForm, ListRightForm, RightForm, DelRightForm
|
||||||
from users.models import EditInfoForm, InfoForm, BaseInfoForm, Machine, StateForm, mac_from_ip
|
from users.models import InfoForm, BaseInfoForm, StateForm
|
||||||
from users.forms import PassForm, ResetPasswordForm
|
from users.forms import PassForm, ResetPasswordForm
|
||||||
import ipaddress
|
from media.models import Emprunt
|
||||||
import subprocess
|
|
||||||
|
|
||||||
from portail_captif.settings import REQ_EXPIRE_STR, EMAIL_FROM, ASSO_NAME, ASSO_EMAIL, SITE_NAME, CAPTIVE_IP_RANGE, CAPTIVE_WIFI, PAGINATION_NUMBER
|
from med.settings import REQ_EXPIRE_STR, EMAIL_FROM, ASSO_NAME, ASSO_EMAIL, SITE_NAME, PAGINATION_NUMBER
|
||||||
|
|
||||||
|
|
||||||
def form(ctx, template, request):
|
def form(ctx, template, request):
|
||||||
@ -85,6 +84,8 @@ def reset_passwd_mail(req, request):
|
|||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@permission_required('perm')
|
||||||
def new_user(request):
|
def new_user(request):
|
||||||
""" Vue de création d'un nouvel utilisateur, envoie un mail pour le mot de passe"""
|
""" Vue de création d'un nouvel utilisateur, envoie un mail pour le mot de passe"""
|
||||||
user = BaseInfoForm(request.POST or None)
|
user = BaseInfoForm(request.POST or None)
|
||||||
@ -111,10 +112,10 @@ def edit_info(request, userid):
|
|||||||
except User.DoesNotExist:
|
except User.DoesNotExist:
|
||||||
messages.error(request, "Utilisateur inexistant")
|
messages.error(request, "Utilisateur inexistant")
|
||||||
return redirect("/users/")
|
return redirect("/users/")
|
||||||
if not request.user.is_admin and user != request.user:
|
if not request.user.has_perms(('perm',)) and user != request.user:
|
||||||
messages.error(request, "Vous ne pouvez pas modifier un autre user que vous sans droit admin")
|
messages.error(request, "Vous ne pouvez pas modifier un autre user que vous sans droit admin")
|
||||||
return redirect("/users/profil/" + str(request.user.id))
|
return redirect("/users/profil/" + str(request.user.id))
|
||||||
if not request.user.is_admin:
|
if not request.user.has_perms(('perm',)):
|
||||||
user = BaseInfoForm(request.POST or None, instance=user)
|
user = BaseInfoForm(request.POST or None, instance=user)
|
||||||
else:
|
else:
|
||||||
user = InfoForm(request.POST or None, instance=user)
|
user = InfoForm(request.POST or None, instance=user)
|
||||||
@ -128,7 +129,7 @@ def edit_info(request, userid):
|
|||||||
return form({'userform': user}, 'users/user.html', request)
|
return form({'userform': user}, 'users/user.html', request)
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('admin')
|
@permission_required('bureau')
|
||||||
def state(request, userid):
|
def state(request, userid):
|
||||||
""" Changer l'etat actif/desactivé/archivé d'un user, need droit bureau """
|
""" Changer l'etat actif/desactivé/archivé d'un user, need droit bureau """
|
||||||
try:
|
try:
|
||||||
@ -156,7 +157,7 @@ def password(request, userid):
|
|||||||
except User.DoesNotExist:
|
except User.DoesNotExist:
|
||||||
messages.error(request, "Utilisateur inexistant")
|
messages.error(request, "Utilisateur inexistant")
|
||||||
return redirect("/users/")
|
return redirect("/users/")
|
||||||
if not request.user.is_admin and user != request.user:
|
if not request.user.has_perms(('perm',)) and user != request.user:
|
||||||
messages.error(request, "Vous ne pouvez pas modifier un autre user que vous sans droit admin")
|
messages.error(request, "Vous ne pouvez pas modifier un autre user que vous sans droit admin")
|
||||||
return redirect("/users/profil/" + str(request.user.id))
|
return redirect("/users/profil/" + str(request.user.id))
|
||||||
u_form = PassForm(request.POST or None)
|
u_form = PassForm(request.POST or None)
|
||||||
@ -165,7 +166,108 @@ def password(request, userid):
|
|||||||
return form({'userform': u_form}, 'users/user.html', request)
|
return form({'userform': u_form}, 'users/user.html', request)
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('admin')
|
@permission_required('bureau')
|
||||||
|
def add_listright(request):
|
||||||
|
""" Ajouter un droit/groupe, nécessite droit bureau.
|
||||||
|
Obligation de fournir un gid pour la synchro ldap, unique """
|
||||||
|
listright = NewListRightForm(request.POST or None)
|
||||||
|
if listright.is_valid():
|
||||||
|
with transaction.atomic(), reversion.create_revision():
|
||||||
|
listright.save()
|
||||||
|
reversion.set_user(request.user)
|
||||||
|
reversion.set_comment("Création")
|
||||||
|
messages.success(request, "Le droit/groupe a été ajouté")
|
||||||
|
return redirect("/users/index_listright/")
|
||||||
|
return form({'userform': listright}, 'users/user.html', request)
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@permission_required('bureau')
|
||||||
|
def edit_listright(request, listrightid):
|
||||||
|
""" Editer un groupe/droit, necessite droit bureau, à partir du listright id """
|
||||||
|
try:
|
||||||
|
listright_instance = ListRight.objects.get(pk=listrightid)
|
||||||
|
except ListRight.DoesNotExist:
|
||||||
|
messages.error(request, u"Entrée inexistante" )
|
||||||
|
return redirect("/users/")
|
||||||
|
listright = ListRightForm(request.POST or None, instance=listright_instance)
|
||||||
|
if listright.is_valid():
|
||||||
|
with transaction.atomic(), reversion.create_revision():
|
||||||
|
listright.save()
|
||||||
|
reversion.set_user(request.user)
|
||||||
|
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in listright.changed_data))
|
||||||
|
messages.success(request, "Droit modifié")
|
||||||
|
return redirect("/users/index_listright/")
|
||||||
|
return form({'userform': listright}, 'users/user.html', request)
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@permission_required('bureau')
|
||||||
|
def del_listright(request):
|
||||||
|
""" Supprimer un ou plusieurs groupe, possible si il est vide, need droit bureau """
|
||||||
|
listright = DelListRightForm(request.POST or None)
|
||||||
|
if listright.is_valid():
|
||||||
|
listright_dels = listright.cleaned_data['listrights']
|
||||||
|
for listright_del in listright_dels:
|
||||||
|
try:
|
||||||
|
with transaction.atomic(), reversion.create_revision():
|
||||||
|
listright_del.delete()
|
||||||
|
reversion.set_comment("Destruction")
|
||||||
|
messages.success(request, "Le droit/groupe a été supprimé")
|
||||||
|
except ProtectedError:
|
||||||
|
messages.error(
|
||||||
|
request,
|
||||||
|
"L'établissement %s est affecté à au moins un user, \
|
||||||
|
vous ne pouvez pas le supprimer" % listright_del)
|
||||||
|
return redirect("/users/index_listright/")
|
||||||
|
return form({'userform': listright}, 'users/user.html', request)
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@permission_required('bureau')
|
||||||
|
def add_right(request, userid):
|
||||||
|
""" Ajout d'un droit à un user, need droit bureau """
|
||||||
|
try:
|
||||||
|
user = User.objects.get(pk=userid)
|
||||||
|
except User.DoesNotExist:
|
||||||
|
messages.error(request, "Utilisateur inexistant")
|
||||||
|
return redirect("/users/")
|
||||||
|
right = RightForm(request.POST or None)
|
||||||
|
if right.is_valid():
|
||||||
|
right = right.save(commit=False)
|
||||||
|
right.user = user
|
||||||
|
try:
|
||||||
|
with transaction.atomic(), reversion.create_revision():
|
||||||
|
reversion.set_user(request.user)
|
||||||
|
reversion.set_comment("Ajout du droit %s" % right.right)
|
||||||
|
right.save()
|
||||||
|
messages.success(request, "Droit ajouté")
|
||||||
|
except IntegrityError:
|
||||||
|
pass
|
||||||
|
return redirect("/users/profil/" + userid)
|
||||||
|
return form({'userform': right}, 'users/user.html', request)
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@permission_required('bureau')
|
||||||
|
def del_right(request):
|
||||||
|
""" Supprimer un droit à un user, need droit bureau """
|
||||||
|
user_right_list = DelRightForm(request.POST or None)
|
||||||
|
if user_right_list.is_valid():
|
||||||
|
right_del = user_right_list.cleaned_data['rights']
|
||||||
|
with transaction.atomic(), reversion.create_revision():
|
||||||
|
reversion.set_user(request.user)
|
||||||
|
reversion.set_comment("Retrait des droit %s" % ','.join(str(deleted_right) for deleted_right in right_del))
|
||||||
|
right_del.delete()
|
||||||
|
messages.success(request, "Droit retiré avec succès")
|
||||||
|
return redirect("/users/")
|
||||||
|
return form({'userform': user_right_list}, 'users/user.html', request)
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@permission_required('perm')
|
||||||
|
def index_listright(request):
|
||||||
|
""" Affiche l'ensemble des droits , need droit perm """
|
||||||
|
listright_list = ListRight.objects.order_by('listright')
|
||||||
|
return render(request, 'users/index_listright.html', {'listright_list':listright_list})
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@permission_required('perm')
|
||||||
def index(request):
|
def index(request):
|
||||||
""" Affiche l'ensemble des users, need droit admin """
|
""" Affiche l'ensemble des users, need droit admin """
|
||||||
users_list = User.objects.order_by('state', 'name')
|
users_list = User.objects.order_by('state', 'name')
|
||||||
@ -191,18 +293,15 @@ def history(request, object, id):
|
|||||||
except User.DoesNotExist:
|
except User.DoesNotExist:
|
||||||
messages.error(request, "Utilisateur inexistant")
|
messages.error(request, "Utilisateur inexistant")
|
||||||
return redirect("/users/")
|
return redirect("/users/")
|
||||||
if not request.user.is_admin and object_instance != request.user:
|
if not request.user.has_perms(('perm',)) and object_instance != request.user:
|
||||||
messages.error(request, "Vous ne pouvez pas afficher l'historique d'un autre user que vous sans droit admin")
|
messages.error(request, "Vous ne pouvez pas afficher l'historique d'un autre user que vous sans droit admin")
|
||||||
return redirect("/users/profil/" + str(request.user.id))
|
return redirect("/users/profil/" + str(request.user.id))
|
||||||
elif object == 'machines':
|
elif object == 'listright' and request.user.has_perms(('perm',)):
|
||||||
try:
|
try:
|
||||||
object_instance = Machine.objects.get(pk=id)
|
object_instance = ListRight.objects.get(pk=id)
|
||||||
except User.DoesNotExist:
|
except ListRight.DoesNotExist:
|
||||||
messages.error(request, "Machine inexistante")
|
messages.error(request, "Droit inexistant")
|
||||||
return redirect("/users/")
|
return redirect("/users/")
|
||||||
if not request.user.is_admin and object_instance.proprio != request.user:
|
|
||||||
messages.error(request, "Vous ne pouvez pas afficher l'historique d'un autre user que vous sans droit admin")
|
|
||||||
return redirect("/users/profil/" + str(request.user.id))
|
|
||||||
else:
|
else:
|
||||||
messages.error(request, "Objet inconnu")
|
messages.error(request, "Objet inconnu")
|
||||||
return redirect("/users/")
|
return redirect("/users/")
|
||||||
@ -217,7 +316,7 @@ def history(request, object, id):
|
|||||||
except EmptyPage:
|
except EmptyPage:
|
||||||
# If page is out of range (e.g. 9999), deliver last page of results.
|
# If page is out of range (e.g. 9999), deliver last page of results.
|
||||||
reversions = paginator.page(paginator.num_pages)
|
reversions = paginator.page(paginator.num_pages)
|
||||||
return render(request, 'portail_captif/history.html', {'reversions': reversions, 'object': object_instance})
|
return render(request, 'med/history.html', {'reversions': reversions, 'object': object_instance})
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def mon_profil(request):
|
def mon_profil(request):
|
||||||
@ -230,70 +329,21 @@ def profil(request, userid):
|
|||||||
except User.DoesNotExist:
|
except User.DoesNotExist:
|
||||||
messages.error(request, "Utilisateur inexistant")
|
messages.error(request, "Utilisateur inexistant")
|
||||||
return redirect("/users/")
|
return redirect("/users/")
|
||||||
machines_list = Machine.objects.filter(proprio=users)
|
if not request.user.has_perms(('perm',)) and users != request.user:
|
||||||
if not request.user.is_admin and users != request.user:
|
|
||||||
messages.error(request, "Vous ne pouvez pas afficher un autre user que vous sans droit admin")
|
messages.error(request, "Vous ne pouvez pas afficher un autre user que vous sans droit admin")
|
||||||
return redirect("/users/profil/" + str(request.user.id))
|
return redirect("/users/profil/" + str(request.user.id))
|
||||||
|
emprunts_list = Emprunt.objects.filter(user=users)
|
||||||
|
list_droits = Right.objects.filter(user=users)
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
'users/profil.html',
|
'users/profil.html',
|
||||||
{
|
{
|
||||||
'user': users,
|
'user': users,
|
||||||
'machines_list': machines_list,
|
'emprunts_list': emprunts_list,
|
||||||
|
'list_droits': list_droits,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_ip(request):
|
|
||||||
"""Returns the IP of the request, accounting for the possibility of being
|
|
||||||
behind a proxy.
|
|
||||||
"""
|
|
||||||
ip = request.META.get("HTTP_X_FORWARDED_FOR", None)
|
|
||||||
if ip:
|
|
||||||
# X_FORWARDED_FOR returns client1, proxy1, proxy2,...
|
|
||||||
ip = ip.split(", ")[0]
|
|
||||||
else:
|
|
||||||
ip = request.META.get("REMOTE_ADDR", "")
|
|
||||||
return ip
|
|
||||||
|
|
||||||
def capture_mac(request, users, verbose=True):
|
|
||||||
remote_ip = get_ip(request)
|
|
||||||
if ipaddress.ip_address(remote_ip) in ipaddress.ip_network(CAPTIVE_IP_RANGE):
|
|
||||||
mac_addr = mac_from_ip(remote_ip)
|
|
||||||
if mac_addr:
|
|
||||||
machine = Machine()
|
|
||||||
machine.proprio = users
|
|
||||||
machine.mac_address = str(mac_addr)
|
|
||||||
try:
|
|
||||||
with transaction.atomic(), reversion.create_revision():
|
|
||||||
machine.save()
|
|
||||||
reversion.set_comment("Enregistrement de la machine")
|
|
||||||
except:
|
|
||||||
if verbose:
|
|
||||||
messages.error(request, "Assurez-vous que la machine n'est pas déjà enregistrée")
|
|
||||||
else:
|
|
||||||
if verbose:
|
|
||||||
messages.error(request, "Impossible d'enregistrer la machine")
|
|
||||||
else:
|
|
||||||
if verbose:
|
|
||||||
messages.error(request, "Merci de vous connecter sur le réseau du portail captif pour capturer la machine (WiFi %s)" % CAPTIVE_WIFI)
|
|
||||||
|
|
||||||
def capture_mac_afterlogin(sender, user, request, **kwargs):
|
|
||||||
capture_mac(request, user, verbose=False)
|
|
||||||
|
|
||||||
# On récupère la mac après le login
|
|
||||||
user_logged_in.connect(capture_mac_afterlogin)
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def capture(request):
|
|
||||||
userid = str(request.user.id)
|
|
||||||
try:
|
|
||||||
users = User.objects.get(pk=userid)
|
|
||||||
except User.DoesNotExist:
|
|
||||||
messages.error(request, "Utilisateur inexistant")
|
|
||||||
return redirect("/users/")
|
|
||||||
capture_mac(request, users)
|
|
||||||
return redirect("/users/profil/" + str(users.id))
|
|
||||||
|
|
||||||
def reset_password(request):
|
def reset_password(request):
|
||||||
userform = ResetPasswordForm(request.POST or None)
|
userform = ResetPasswordForm(request.POST or None)
|
||||||
if userform.is_valid():
|
if userform.is_valid():
|
||||||
|
Loading…
Reference in New Issue
Block a user