Commit 49afe7ae authored by Daniel STAN's avatar Daniel STAN

Merge branch 'master' into i18n

Conflicts:
	impressions/templates/impressions/imprimante.html
parents 2f09d1e4 c980027b
......@@ -16,7 +16,7 @@ class LinkAccount(Form):
wiki_name = CharField(label=_(u"Nom d'utilisateur Wiki"))
password = CharField(label=_(u"Mot de passe associé"), widget=PasswordInput)
check_url = 'https://wiki.crans.org/?action=checkpassword'
check_url = 'https://wiki.crans.org/API?action=checkpassword'
def clean(self):
""" Teste la validité du couple login/mdp """
......
......@@ -13,9 +13,9 @@ from forms import LinkAccount, CreateAccount, Form
from intranet import settings
if settings.DEBUG:
from models_ldap import WikiName
else:
from models_test import WikiName
else:
from models_ldap import WikiName
class Create(FormView):
......
......@@ -10,7 +10,7 @@ def mozilla(request):
emailaddress = request.GET.get("emailaddress", '%EMAILLOCALPART%')
username = emailaddress.split(u'@', 1)[0]
conn = conn_pool.get_conn(request.user)
ret = conn.search(u"(|(mail=%(p)s)(canonicalAlias=%(p)s)(mailAlias=%(p)s))" % {'p': emailaddress})
ret = conn.search(u"(|(mail=%(p)s)(canonicalAlias=%(p)s))" % {'p': emailaddress})
if ret:
username = unicode(ret[0]['uid'][0])
return django.shortcuts.render(request,
......
......@@ -32,3 +32,19 @@ class ImprimeurAddForm(forms.Form):
login = new_imprimeur.lower().strip(' ')
return login
class ResponsableEditForm(forms.Form):
new_responsable = forms.CharField(
required=True,
label='Modifier le responsable',
help_text=('Entrer le login crans de la personne.\n'
'Usuellement le nom (sans accents) '
'ou la première lettre du prénom suivi du nom'
)
)
def clean_new_responsable(self):
"""Passe le login du responsable en minuscule et retire les espaces"""
# Apparement cette méthode est appelé par Django au moment du clean
new_responsable = self.cleaned_data['new_responsable']
login = new_responsable.lower().strip(' ')
return login
......@@ -11,7 +11,8 @@
<footer>
<div class="row">
<div class="six columns">
<a href="{% url 'club:index' %}" class="button-cancel">Retour à la liste des clubs</a>
<a href="{% if not cablage %}{% url 'club:index' %}
{% else %} {% url 'club:index' cid %}{% endif %}" class="button-cancel">Retour</a>
<input class="button-add" type="submit" value="Ajouter cet imprimeur">
</div>
</div>
......
......@@ -11,7 +11,8 @@
<footer>
<div class="row">
<div class="six columns">
<a href="{% url 'club:index' %}" class="button-cancel">Retour à la liste des clubs</a>
<a href="{% if not cablage %}{% url 'club:index' %}
{% else %} {% url 'club:index' cid %}{% endif %}" class="button-cancel">Retour</a>
<input class="button-del" type="submit" value="Retirer ces imprimeurs">
</div>
</div>
......
......@@ -3,7 +3,9 @@
{% block h1 %} Gestion des comptes {% endblock %}
{% block content %}
<div class="row">
{% for club in clubs %}
<div class="six columns">
<h2>{{ club.club_name }}</h2>
Liste des imprimeurs:
<ul>
......@@ -13,12 +15,38 @@
<span class="bad-thing">Vous n'avez actuellement aucun imprimeurs pour ce club !</span>
{% endfor %}
</ul>
<a href="{% url 'club:imprimeur_add' cid=club.cid %}"><button class="button-add">Ajouter un imprimeur</button></a>
Responsable : {{club.responsable}}
<footer>
<a href="
{% if not cablage %}
{% url 'club:imprimeur_add' cid=club.cid %}
{% else %}
{% url 'club:imprimeur_add' cid=club.cid cablage='cablage'%}
{% endif %}
"><button class="button-add">Ajouter un imprimeur</button></a>
{% if club.imprimeurs %}
<a href="{% url 'club:imprimeur_del' cid=club.cid %}"><button class="button-del">Retirer un imprimeur</button></a>
<a href="
{% if not cablage %}
{% url 'club:imprimeur_del' cid=club.cid %}
{% else %}
{% url 'club:imprimeur_del' cid=club.cid cablage='cablage'%}
{% endif %}
"><button class="button-del">Retirer un imprimeur</button></a>
{% endif %}
{% if perms.aut.crans_cableur %}
<a href="
{% if not cablage %}
{% url 'club:responsable_edit' cid=club.cid %}
{% else %}
{% url 'club:responsable_edit' cid=club.cid cablage='cablage'%}
{% endif %}
"><button class="button-add">Modifier le responsable</button></a>
{% endif %}
</footer>
</div>
{% empty %}
Vous n'êtes gestionnaire d'aucun club.
{% endfor %}
</div>
{% endblock %}
{% extends "template.html" %}
{% block title %} Club - Modification du responsable {% endblock %}
{% block h1 %} Modification du responsable {% endblock %}
{% block content %}
<div class="error-container">
{{ form.non_field_errors }}
</div>
<form class="form-full-width" method="post">{% csrf_token %}
{{ form.as_p }}
<footer>
<div class="row">
<div class="six columns">
<a href="{% if not cablage %}{% url 'club:index' %}
{% else %} {% url 'club:index' cid %}{% endif %}" class="button-cancel">Retour</a>
<input class="button-add" type="submit" value="Modifier le responsable">
</div>
</div>
</footer>
</form>
{% endblock %}
......@@ -4,6 +4,11 @@ import views
urlpatterns = [
url('^$', views.IndexView.as_view(), name='index'),
url('^liste/(?P<cid>[0-9]+)/$', views.IndexView.as_view(), name='index'),
url('^(?P<cid>[0-9]+)/imprimeur/ajouter$', views.ImprimeurAddView.as_view(), name='imprimeur_add'),
url('^(?P<cid>[0-9]+)/imprimeur/retirer$', views.ImprimeurDelView.as_view(), name='imprimeur_del'),
url('^(?P<cid>[0-9]+)/responsable/edit$', views.ResponsableEditView.as_view(), name='responsable_edit'),
url('^(?P<cid>[0-9]+)/imprimeur/ajouter/(?P<cablage>cablage)$', views.ImprimeurAddView.as_view(), name='imprimeur_add'),
url('^(?P<cid>[0-9]+)/imprimeur/retirer/(?P<cablage>cablage)$', views.ImprimeurDelView.as_view(), name='imprimeur_del'),
url('^(?P<cid>[0-9]+)/responsable/edit/(?P<cablage>cablage)$', views.ResponsableEditView.as_view(), name='responsable_edit'),
]
......@@ -8,7 +8,7 @@ from django.shortcuts import redirect
from django.forms.models import formset_factory
from django.contrib import messages
from forms import ImprimeurAddForm, ImprimeurDelForm
from forms import ImprimeurAddForm, ImprimeurDelForm, ResponsableEditForm
from tools import get_clubs_for_user, get_imprimeurs_for_club
from intranet import conn_pool
......@@ -16,10 +16,16 @@ from intranet import conn_pool
class IndexView(TemplateView):
template_name = 'club/index.html'
def get_context_data(self, *args, **kwargs):
def get_context_data(self, cid = None, *args, **kwargs):
context = super(IndexView, self).get_context_data(*args, **kwargs)
clubs = []
for (club_name, club) in get_clubs_for_user(self.request.user):
if cid and self.request.user.has_perm('auth.crans_cableur'):
club = conn_pool.get_conn(self.request.user).search(u'cid=%s' % cid)[0]
user_clubs = [(unicode(club['nom'][0]),club)]
context.update({'cablage' : True})
else:
user_clubs = get_clubs_for_user(self.request.user)
for (club_name, club) in user_clubs:
imprimeurs = get_imprimeurs_for_club(club)
club_dict = {
'club_name': club_name,
......@@ -33,6 +39,10 @@ class IndexView(TemplateView):
club_dict.update({
'imprimeurs': [],
})
responsable = club['responsable'][0].get_respo()
club_dict.update({
'responsable': unicode(responsable['prenom'][0]) +u' ' + unicode(responsable['nom'][0])
})
clubs.append(club_dict)
context.update({'clubs': clubs})
return context
......@@ -41,21 +51,31 @@ class IndexView(TemplateView):
def dispatch(self, *args, **kwargs):
return super(IndexView, self).dispatch(*args, **kwargs)
class ImprimeurView(FormView):
class EditClubView(FormView):
success_url = reverse_lazy('club:index')
def get_context_data(self, *args, **kwargs):
context = super(EditClubView, self).get_context_data(*args, **kwargs)
if 'cablage' in self.kwargs:
context.update({'cablage':True,'cid':self.cid})
return context
@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
self.cid = int(self.kwargs['cid'])
for club_name, club in get_clubs_for_user(self.request.user):
if int(club['cid'][0]) == self.cid:
break
else: # Ce else est pour le for et non pour le if
messages.error(self.request, "Vous n'avez pas le droit de modifier ce club !")
return redirect('club:index')
return super(ImprimeurView, self).dispatch(*args, **kwargs)
class ImprimeurAddView(ImprimeurView):
if 'cablage' in self.kwargs:
self.success_url = reverse_lazy('club:index', args=[(self.cid)])
if not self.request.user.has_perm('auth.crans_cableur'):
for club_name, club in get_clubs_for_user(self.request.user):
if int(club['cid'][0]) == self.cid:
break
else: # Ce else est pour le for et non pour le if
messages.error(self.request, "Vous n'avez pas le droit de modifier ce club !")
return redirect('club:index')
return super(EditClubView, self).dispatch(*args, **kwargs)
class ImprimeurAddView(EditClubView):
template_name = 'club/imprimeur_add.html'
form_class = ImprimeurAddForm
......@@ -78,7 +98,7 @@ class ImprimeurAddView(ImprimeurView):
return redirect(self.request.path)
return super(ImprimeurAddView, self).form_valid(form)
class ImprimeurDelView(ImprimeurView):
class ImprimeurDelView(EditClubView):
template_name = 'club/imprimeur_del.html'
form_class = ImprimeurDelForm
......@@ -108,3 +128,26 @@ class ImprimeurDelView(ImprimeurView):
club.save()
return super(ImprimeurDelView, self).form_valid(form)
class ResponsableEditView(EditClubView):
template_name = 'club/responsable_edit.html'
form_class = ResponsableEditForm
def form_valid(self, form):
ldap_user = conn_pool.get_conn(self.request.user)
club = ldap_user.search(u'cid=%s' % self.cid, mode='w')[0]
with club as club:
new_responsable = form.cleaned_data['new_responsable']
try:
new_responsable_ldap = ldap_user.search(u'uid=%s' % new_responsable)[0]
except IndexError:
messages.error(self.request, "Ce login n'est pas valide")
return redirect(self.request.path)
try:
club['responsable'] = unicode(new_responsable_ldap['aid'][0])
club.history_gen()
club.save()
except ValueError as e:
messages.error(self.request, e)
return redirect(self.request.path)
return super(ResponsableEditView, self).form_valid(form)
......@@ -88,6 +88,11 @@
</div>
{% endif %}
</div>
<div class="row">
<div class="four columns">
<a class="button" href="{% url 'club:index' luser.cid.0 %}">Gérer imprimeurs et responsable</a>
</div>
</div>
<h2>Mail</h2>
......
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from django import template
register = template.Library()
CSS_CLASSES = ["overflow", "alert", "warning", "ok", "good"]
def _progress_class_generic(classes, value, arg=100):
"""Determines appropriate css class given a progress bar value and a maximum"""
try:
arg = float(arg)
value = float(value)
except ValueError:
return "overflow"
res = value/arg
if (res < 0) or (res > 1):
return classes[0]
elif res <= 0.25:
return classes[1]
elif res <= 0.5:
return classes[2]
elif res <= 0.75:
return classes[3]
else:
return classes[4]
def progress_class(value, arg=100):
return _progress_class_generic(CSS_CLASSES, value, arg)
def progress_class_reverse(value, arg=100):
return _progress_class_generic(CSS_CLASSES[:1] + CSS_CLASSES[:0:-1], value, arg)
register.filter('progress_class', progress_class)
register.filter('progress_class_reverse', progress_class_reverse)
# -*- coding: utf-8 -*-
from django.core.management.base import BaseCommand
from models import Code
from digicode.models import Code
class Command(BaseCommand):
help = u"Efface les codes périmés"
......
......@@ -167,6 +167,67 @@ Il existe 4 classe de bouton déjà stylisés pour les `<bouton>` ou les `<input
* La classe `button-del` qui indique que l'appuie sur le bouton va entraîner une suppression.
* La classe `button-cancel` qui indique que l'appuie sur le bouton va annuler les changements.
### Barres de progression ###
Le CSS intégre des barres de progressions qui permettent de mettre en valeur
les données de taux d'utilisation.
#### Classes ####
Pour utiliser ces barres il suffit de créer un div containeur de classe
`progress-container` ainsi qu'un div pour la barre à afficher de classe
`progress-bar` et d'une classe de statut parmis:
* `overflow`
* `alert`
* `warning`
* `ok`
* `good`
```html
<div class="progress-container">
<div class="progress-bar good">
Bravo tu n'as pas dépassé ton quota !
</div>
</div>
```
Note: Comme souvent on ne connais pas la largeur à l'avance
on peut s'autoriser **exceptionnellement** à utiliser l'attribut
`style` dans le code html:
```html
<div class="progress-bar good" style="width: {{ ma_largeur }}%;">
Tu utilises {{ mon_quota }}% de ton quota
<div>
```
#### Raccourcis ####
En pratique il est utile de pouvoir adapter la classe de statut de la
barre de progression en fonction du pourcentage afficher.
Afin d'éviter la duplication de code il existe des filtres dans l'intranet
pour pouvoir choisir la classe de statut automatiquement.
Il faut d'abord pour cela charger les filtres supplémentaire de l'intranet
à l'aide de la balise: `{% load custom_filters %}`.
Puis l'on peut utiliser un des deux filtres disponibles:
* `progress_class`:`max`, qui vaut `good` à `max` et `alert` à 0.
* `progress_class_reverse`:`max`, qui vaut `alert` à `max` et `good` à 0.
`max` est le maximum que notre valeur filtrée peut atteindre.
En cas de dépassement nos filtres vaudront `overflow`.
Si `max` n'est pas précisé la valeur 100 sera utilisé.
Exemple d'utilisation des barres de progression (extrait d'impression)
```html
{% load custom_filters %}
<div class="progress-container">
<div class="progress-bar {{ tray.count|progress_class:tray.max_capacity }}"
style="width:{% widthratio tray.count tray.max_capacity 100 %}%;">
{{ tray.count }}/{{ tray.max_capacity }}
</div>
</div>
```
Dans les cas où la valeur calculée n'est pas un pourcentage, on peut faire une
mise à l'échelle à l'aide de la balise Django [widthratio](https://docs.djangoproject.com/fr/1.7/ref/templates/builtins/#widthratio)
Cas particulier des formulaires
-------------------------------
......
from django.core.management.base import BaseCommand, CommandError
from impressions.models import Jobs
from urllib2 import URLError
class Command(BaseCommand):
help = 'Rafrachit les jobs de l\' imprimante'
......
......@@ -5,6 +5,8 @@
{% load staticfiles %}
{% block content %}
{% load custom_filters %}
<h2>{{ printer.name }}</h2>
<div class="row">
<div class="six columns">
......@@ -13,7 +15,7 @@
</div>
<div class="six columns">
<label>{% trans "Status :" %}</label>
{{ printer.status }}
<span class="{% if printer.status == 5 %}bad-thing{% endif %}">{{ printer.get_status_display }}</span>
</div>
</div>
......@@ -23,7 +25,11 @@
<div class="three columns">
<h5>{{ tray.name }}</h5>
<label>{% trans "Nombre de page :" %}</label>
{{ tray.count }}/{{ tray.max_capacity }}
<div class="progress-container">
<div class="progress-bar {{ tray.count|progress_class:tray.max_capacity }}" style=" width:{% widthratio tray.count tray.max_capacity 100 %}%;">
{{ tray.count }}/{{ tray.max_capacity }}
</div>
</div>
<label>{% trans "Format du papier :" %}</label>
{{ tray.dimx }}x{{ tray.dimy }}mm
</div>
......@@ -40,7 +46,11 @@
<div class="three columns">
<h5>{{ supply.name }}</h5>
<label>{% trans "Pourcentage :" %}</label>
{{ supply.percent }}
<div class="progress-container">
<div class="progress-bar {{ supply.percent|progress_class }}" style=" width:{{ supply.percent }}%;">
{{ supply.percent }}
</div>
</div>
</div>
{% if forloop.counter|divisibleby:"4" and not forloop.first and not forloop.last %}
</div>
......
......@@ -55,7 +55,6 @@ class MainPrinterView(DetailView):
template_name = 'impressions/imprimante.html'
@method_decorator(login_required)
@method_decorator(permission_required('printer.can_view'))
def dispatch(self, *args, **kwargs):
return super(DetailView, self).dispatch(*args, **kwargs)
......
......@@ -25,7 +25,7 @@ import ldap
import settings
from ldap import SERVER_DOWN
import lc_ldap.shortcuts as shortcuts
from lc_ldap.shortcuts import lc_ldap_admin as make_admin_conn
CONNS = {}
OBJECTS = {}
......@@ -35,12 +35,15 @@ LDAP_USER_LAST_UPDATE = {}
LDAP_USER_TIMEOUT = 10
def get_conn(user, force_new=False):
if settings.BASE_LDAP_TEST:
make_admin_conn = shortcuts.lc_ldap_test
else:
make_admin_conn = shortcuts.lc_ldap_admin
if force_new or not user.username in CONNS.keys():
CONNS[user.username]= make_admin_conn(user=user.username)
else:
# On vérifie que la connection n'est pas morte
try:
_ = CONNS[user.username].whoami_s()
except SERVER_DOWN:
CONNS[user.username] = make_admin_conn(user=user.username)
return CONNS[user.username]
def get_user(user, mode='ro', refresh=False):
......
......@@ -124,7 +124,7 @@ class LDAPUserBackend(ModelBackend):
supports_anonymous_user = False
@sensitive_variables('password')
def authenticate(self, username=None, password=None):
def authenticate(self, username=None, password=None, **kwargs):
"""Authentifie l'utilisateur sur la base LDAP. Crée un
utilisateur django s'il n'existe pas encore."""
......
......@@ -147,6 +147,7 @@ MIDDLEWARE_CLASSES = (
# 'django.middleware.transaction.TransactionMiddleware', Est retiré dans Django 1.8
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.middleware.locale.LocaleMiddleware',
......@@ -406,10 +407,13 @@ INSTALLED_APPS = (
'django.contrib.staticfiles',
#'south',
'reversion',
#'custom_shortcuts'
'custom_shortcuts',
'printer',
) + tuple( app.get('module', app['name']) for app in INTRANET_APPS )
if CAS_ENABLED:
INSTALLED_APPS = INSTALLED_APPS + ('django_cas_ng',)
# Config de l'app d'impressions
MAX_PRINTFILE_SIZE=25 * 1024 * 1024 # 25Mio
......
......@@ -12,3 +12,27 @@ class PagePersoForm(forms.ModelForm):
model = PagePerso
fields = ['nom_site', 'slogan', 'logo']
class SqlPasswordForm(forms.Form):
password = forms.CharField(
label="mot de passe",
max_length=256,
widget=forms.PasswordInput,
required=True
)
password_confirmation = forms.CharField(
label="confirmation",
max_length=256,
widget=forms.PasswordInput,
required=True
)
def clean_password(self):
if len(self.cleaned_data["password"]) < 8:
raise forms.ValidationError("Le mot de passe doit faire au moins 8 caractères.")
return self.cleaned_data["password"]
def clean(self):
if "password" in self.cleaned_data:
if self.cleaned_data["password"] != self.cleaned_data.get("password_confirmation"):
raise forms.ValidationError("Les mots de passe ne sont pas identiques.")
......@@ -2,6 +2,9 @@
{% block title %} Ma page Perso {% endblock %}
{% block h1 %} Ma page perso {% endblock %}
{% block content %}
<h2>Référencement</h2>
Cette interface permet de référencer sa page perso sur <a href="https://wiki.crans.org/PagesPerso"a>Wiki/PagesPerso</a><br/>
Pour plus d'informations, rendez-vous sur le <a href="https://wiki.crans.org/HowToEspacePerso"a>Wiki</a>
<form class="form-full-width" method="post"> {% csrf_token %}
<div class="error-container">
{{ form.non_field_errors }}
......@@ -34,13 +37,25 @@
{% if deref %}
<h2>Déréférencement</h2>
<form action="{% url 'pageperso:deref' %}">
<footer>
<input type="submit" value="Déréférencer ma page perso" class="button-del">
</footer>
<input type="submit" value="Déréférencer ma page perso" class="button-del">
</form>
{% endif %}
Cette interface permet de référencer sa page perso sur <a href="https://wiki.crans.org/PagesPerso"a>Wiki/PagesPerso</a><br/>
Pour plus d'informations, rendez-vous sur le <a href="https://wiki.crans.org/HowToEspacePerso"a>Wiki</a>
<h2>Base de données</h2>
{% if not mysql_error %}
{% if mysql_exists %}
Vous disposez d'une base de données mysql accessible depuis zamok.<br/>
Nom d'utilisateur "{{user.username}}", nom de la base, "{{user.username}}", adresse "localhost".<br/>
Vous pouvez vous y connecter en ligne de commande depuis zamok en faisant "mysql -u {{user.username}} -p {{user.username}}" puis en entrant votre mot de passe de base de donnée.<br/>
Vous pouvez également utiliser l'interface web <a href="https://perso.crans.org/phpmyadmin/">phpmyadmin</a></br/>
<a href="{% url "pageperso:reset_db_password" %}" class="button">Réinitialiser le mot de passe de ma base de données</a>
<a href="{% url "pageperso:delete_database" %}" class="button-del">Supprimer ma base de données</a>
{% else %}
Vous ne disposez pas encore d'une base de données mysql accessible depuis zamok.<br/>
<a href="{% url "pageperso:create_database"%}" class="button">Créer une base de données</a>
{% endif %}
{% else %}
Impossible d'accéder aux information de connexion mysql
{% endif %}
{% endblock %}
......
{% extends "template.html" %}
{% block title %} Ma page Perso {% endblock %}
{% block h1 %} Ma page perso {% endblock %}
{% block content %}
<script>
function generatePassword(){
var password = Math.random().toString(36).slice(-8);
password = password + Math.random().toString(36).slice(-8);
form = document.forms['form'];
form.elements["password"].value = password;
form.elements["password_confirmation"].value = password;
form.elements["generated"].value = password;
return false;
}
</script>
<h2>Création de la base de données</h2>
Merci de choisir un mot de passe pour votre base de données.<br/>
Il est fortement recommandé d'utiliser un mot de passe différent de celui de votre compte Crans.
<form id="form" class="form" method="post"> {% csrf_token %}
<table>
{{form}}
<tr><th>Générer un mot de passe