Diverses améliorations

- ajout commande pour recharger l'ancienne base LDAP,
- améliorations dans les template,
- ajout des traductions,
- ajout de clé naturelles aux modèles,
- correction au caractère optionnel de certains champs.
This commit is contained in:
Benjamin Dauvergne 2014-10-30 17:51:04 +01:00
parent 9f2ad26f86
commit 4557734b9d
12 changed files with 787 additions and 48 deletions

View File

@ -32,4 +32,17 @@ class AccessForm(BaseForm):
class UserForm(BaseForm):
class Meta:
model = models.User
fields = ('uid', 'first_name', 'last_name', 'email')
fields = (
'uid',
'first_name',
'last_name',
'email',
'is_admin',
'direction',
'employee_type',
'postal_address',
'fax',
'mobile',
'phone'
)

View File

@ -0,0 +1,402 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-10-30 17:20+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: auth_frontends.py:9 dashboard.py:8
msgid "Pr@tic"
msgstr ""
#: models.py:20 models.py:92 tables.py:36 tables.py:49
msgid "identifier"
msgstr "identifiant"
#: models.py:24 models.py:212 models.py:279
msgid "collectivity"
msgstr "collectivité"
#: models.py:27
msgid "is admin"
msgstr "Administrateur?"
#: models.py:32 models.py:106 models.py:111
msgid "SIRH Code"
msgstr ""
#: models.py:37
msgid "direction"
msgstr ""
#: models.py:42
msgid "last connection duration"
msgstr "durée de la dernière connexion"
#: models.py:48
msgid "employee type"
msgstr "type de poste"
#: models.py:53 models.py:129
msgid "postal address"
msgstr "Adresse postale"
#: models.py:57 models.py:189
msgid "fax"
msgstr ""
#: models.py:62
msgid "mobile"
msgstr ""
#: models.py:67 models.py:184
msgid "phone"
msgstr "téléphone"
#: models.py:72
msgid "agent"
msgstr ""
#: models.py:73
msgid "agents"
msgstr ""
#: models.py:88 models.py:221 tables.py:10 tables.py:23
msgid "name"
msgstr "nom"
#: models.py:96
msgid "is superuser"
msgstr "Tous les utilisateurs sont des super-administrateurs?"
#: models.py:101
msgid "collectivity id"
msgstr "identifiant de la collectivité"
#: models.py:116
msgid "INSEE Code"
msgstr "code INSEE"
#: models.py:121
msgid "SIRET Code"
msgstr "code SIRET"
#: models.py:133
msgid "street number"
msgstr "numéro dans la rue"
#: models.py:138
msgid "street"
msgstr "rue"
#: models.py:143
msgid "postal code"
msgstr "code postal"
#: models.py:148
msgid "complementary address"
msgstr "adresse complémentaire"
#: models.py:153
msgid "address mention"
msgstr "adresse (mention complémentaire)"
#: models.py:158
msgid "arrondissement code"
msgstr "code d'arrondissement"
#: models.py:163
msgid "canton code"
msgstr "numéro de canton"
#: models.py:168
msgid "departement code"
msgstr "numéro de département"
#: models.py:173 models.py:178
msgid "distribution office"
msgstr "centre de distribution du courrier"
#: models.py:194
msgid "email"
msgstr "courriel"
#: models.py:199 models.py:233 models.py:281
msgid "URL"
msgstr ""
#: models.py:213
msgid "collectivities"
msgstr "collectivités"
#: models.py:229
msgid "is global"
msgstr "service global?"
#: models.py:235 models.py:284
msgid "SAML Metadata URL"
msgstr "URL des métadonnées SAML"
#: models.py:238 models.py:287
msgid "OAuth2 URL"
msgstr "URL du point d'accès OAuth2"
#: models.py:241 models.py:290
msgid "OAuth2 Key"
msgstr "Clé OAuth2"
#: models.py:254 models.py:276
msgid "service"
msgstr ""
#: models.py:255
msgid "services"
msgstr ""
#: models.py:308
msgid "There can be only one instance of a global service by collectivity"
msgstr ""
"Il ne peut y avoir qu'une seule instance d'un service global par collectivité"
#: models.py:315
msgid "Service URL field is required"
msgstr "L'URL de service est requise pour un service non global"
#: models.py:318 models.py:339
msgid "service instance"
msgstr "instance de service"
#: models.py:319
msgid "service instances"
msgstr "instances de service"
#: models.py:337
msgid "user"
msgstr "agent"
#: models.py:347
msgid "access"
msgstr "accréditation"
#: models.py:348
msgid "accesses"
msgstr "accréditations"
#: pratic_attribute_source.py:28
msgid "User domain"
msgstr ""
#: pratic_attribute_source.py:29
msgid "User identifier"
msgstr ""
#: tables.py:13 tables.py:26 tables.py:39 tables.py:52
msgid "delete"
msgstr "supprimer"
#: tables.py:61
msgid "Last name"
msgstr "Nom"
#: tables.py:63
msgid "First name"
msgstr "Prénom"
#: views.py:28 templates/authentic2_pratic/delete.html:27
msgid "Delete"
msgstr "Supprimer"
#: views.py:29
#, python-format
msgid "Do you really want to delete \"%s\" ?"
msgstr "Êtes-vous sûr de vouloir supprimer \"%s\" ?"
#: views.py:63 templates/authentic2_pratic/services.html:10
msgid "Add service"
msgstr "Ajouter un service"
#: views.py:65 views.py:91 views.py:141 views.py:168 views.py:207
msgid "Add"
msgstr "Ajouter"
#: views.py:70
msgid "Edit service"
msgstr "Éditer un service"
#: views.py:77
msgid "Delete service"
msgstr "Supprimer un service"
#: views.py:89 templates/authentic2_pratic/collectivities.html:10
msgid "Add collectivity"
msgstr "Ajouter une collectivité"
#: views.py:96
msgid "Edit collectivity"
msgstr "Éditer une collectivité"
#: views.py:104
msgid "Delete collectivity"
msgstr "Supprimer une collectivité"
#: views.py:139 templates/authentic2_pratic/users.html:10
msgid "Add agent"
msgstr "Ajouter un agent"
#: views.py:146
msgid "Edit agent"
msgstr "Éditer un agent"
#: views.py:154
msgid "Delete agent"
msgstr "Supprimer un agent"
#: views.py:166 views.py:205
msgid "Add service instance"
msgstr "Ajouter une instance de service"
#: views.py:174 views.py:213
msgid "Edit service instance"
msgstr "Éditer une instance de service"
#: views.py:182 views.py:221
msgid "Delete service instance"
msgstr "Supprimer une instance de service"
#: templates/authentic2_pratic/accesses.html:4
#: templates/authentic2_pratic/accesses.html:6
#: templates/authentic2_pratic/collectivity_edit.html:14
msgid "Accesses management"
msgstr "Gestion des accréditations"
#: templates/authentic2_pratic/accesses.html:10
msgid "Add access"
msgstr "Ajouter une accréditation"
#: templates/authentic2_pratic/accesses.html:14
#, python-format
msgid "%(count)s accesses"
msgid_plural "%(count)s access"
msgstr[0] "%(count)s accréditation"
msgstr[1] "%(count)s accréditations"
#: templates/authentic2_pratic/base.html:4
#: templates/authentic2_pratic/base.html:26
msgid "Management"
msgstr "Gestion"
#: templates/authentic2_pratic/collectivities.html:4
#: templates/authentic2_pratic/collectivities.html:6
#: templates/authentic2_pratic/collectivity_edit.html:8
msgid "Collectivities management"
msgstr "Gestion des collectivités"
#: templates/authentic2_pratic/collectivities.html:15
#: templates/authentic2_pratic/collectivities.html:18
msgid "Search"
msgstr "Recherche"
#: templates/authentic2_pratic/collectivities.html:21
#, python-format
msgid "%(count)s collectivities"
msgid_plural "%(count)s collectivity"
msgstr[0] "%(count)s collectivité"
msgstr[1] "%(count)s collectivités"
#: templates/authentic2_pratic/collectivity_edit.html:12
#: templates/authentic2_pratic/users.html:4
#: templates/authentic2_pratic/users.html:6
msgid "Agents management"
msgstr "Gestion des agents"
#: templates/authentic2_pratic/collectivity_edit.html:13
#: templates/authentic2_pratic/service_instances.html:4
#: templates/authentic2_pratic/service_instances.html:6
msgid "Service instances management"
msgstr "Gestion des instances de service"
#: templates/authentic2_pratic/collectivity_edit.html:24
#: templates/authentic2_pratic/form.html:31
msgid "Actions"
msgstr ""
#: templates/authentic2_pratic/collectivity_edit.html:48
#: templates/authentic2_pratic/form.html:26
msgid "Save"
msgstr "Sauvegarder"
#: templates/authentic2_pratic/collectivity_edit.html:49
#: templates/authentic2_pratic/delete.html:28
#: templates/authentic2_pratic/form.html:27
msgid "Cancel"
msgstr "Annuler"
#: templates/authentic2_pratic/delete.html:23
#, python-format
msgid "Do you really want to delete « %(object)s » ?"
msgstr "Êtez-vous sûr de vouloir supprimer « %(object)s » ?"
#: templates/authentic2_pratic/homepage.html:8
msgid "Welcome"
msgstr "Bievenue"
#: templates/authentic2_pratic/homepage.html:15
msgid "Password change"
msgstr "Changer de mot de passe"
#: templates/authentic2_pratic/homepage.html:19
msgid "Collectivities"
msgstr "Collectivités"
#: templates/authentic2_pratic/homepage.html:20
msgid "Services"
msgstr ""
#: templates/authentic2_pratic/service_instances.html:10
msgid "Add service-instance"
msgstr "Ajouter une instance de service"
#: templates/authentic2_pratic/service_instances.html:14
#, python-format
msgid "%(count)s services instances"
msgid_plural "%(count)s service instance"
msgstr[0] "%(count)s instance de service"
msgstr[1] "%(count)s instances de service"
#: templates/authentic2_pratic/services.html:4
#: templates/authentic2_pratic/services.html:6
msgid "Services management"
msgstr "Gestion des services"
#: templates/authentic2_pratic/services.html:14
#, python-format
msgid "%(count)s services"
msgid_plural "%(count)s service"
msgstr[0] "%(count)s service"
msgstr[1] "%(count)s services"
#: templates/authentic2_pratic/users.html:14
#, python-format
msgid "%(count)s agent"
msgid_plural "%(count)s agents"
msgstr[0] "%(count)s agent"
msgstr[1] "%(count)s agents"
#~ msgid "Add user"
#~ msgstr "Ajouter un agent"
#~ msgid "Edit user"
#~ msgstr "Éditer un agent"

View File

@ -0,0 +1,242 @@
import logging
from optparse import make_option
from datetime import datetime
from itertools import chain
import ldif
from django.core.management.base import BaseCommand
from django.db import transaction
from django.utils.timezone import get_default_timezone, make_aware
from authentic2.hashers import olap_password_to_dj
from authentic2_pratic.models import (Service, Collectivity, ServiceInstance,
User, Access)
class PraticLDIFParser(ldif.LDIFParser):
def __init__(self, *args, **kwargs):
self.services = {}
self.service_instances = {}
self.accesses = []
self.collectivities = {}
self.users = []
self.log = logging.getLogger(__name__)
ldif.LDIFParser.__init__(self, *args, **kwargs)
def convert_to_utf8(self, entry):
for key in entry:
new_values = []
for value in entry[key]:
try:
value = unicode(value, 'utf-8')
except UnicodeDecodeError:
pass
new_values.append(value)
entry[key] = new_values
def handle(self, dn, entry):
objectclass = entry['objectClass']
self.convert_to_utf8(entry)
if 'cdg59serviceInstance' in objectclass:
self.handle_service_instance(dn, entry)
elif 'cdg59service' in objectclass:
self.handle_service(dn, entry)
elif 'cdg59collectivity' in objectclass:
self.handle_collectivity(dn, entry)
elif 'cdg59agent' in objectclass:
self.handle_user(dn, entry)
def resolve_attribute(self, entry, key):
return entry.get(key, [''])[0]
def resolve_mapping(self, mapping, entry):
d = {}
for key in mapping:
ldap_name = mapping[key]
if ldap_name in entry:
d[key] = entry[ldap_name][0]
return d
def handle_collectivity(self, dn, entry):
slug = entry['ou'][0]
name = entry.get('cn', ['slug'])[0]
mapping = {
'collectivity_id': 'cdg59collectivityId',
'sirh_code': 'cdg59collectivitySirhCode',
'sirh_label': 'cdg59collectivitySirhLabel',
'siret_code': 'cdg59siretCode',
'postal_address': 'postalAddress',
'street_number': 'cdg59streetNumber',
'street': 'street',
'postal_code': 'postalCode',
'address_complementary': 'cdg59addressCompl',
'address_mention': 'cdg59addressMention',
'arrondissement_code': 'cdg59arrondissementCode',
'canton_code': 'cdg59cantonCode',
'departement_code': 'cdg59departementCode',
'dist_office': 'cdg59distOffice',
'region_code': 'cdg59regionCode',
'phone': 'telephoneNumber',
'fax': 'facsimileTelephoneNumber',
'email': 'email',
'url': 'cdg59URL',
}
others = self.resolve_mapping(mapping, entry)
collectivity = Collectivity(name=name, slug=slug, **others)
collectivity.clean()
self.collectivities[dn] = collectivity
def handle_service(self, dn, entry):
# services
# dn: cdg59sid=agirhe,dc=pratic59,dc=fr
# cn: Agirhe
# cdg59isGlobal: TRUE
# objectClass: cdg59service
# cdg59metadataURL: https://agirhe.pratic59.fr/liberty/agirhe/saml/metadata.xml
# cdg59URL: https://agirhe.pratic59.fr/
# cdg59sid: agirhe
# structuralObjectClass: cdg59service
# entryUUID: c8a4a0fc-7813-102d-97eb-a7725ef3fb45
# creatorsName: uid=cdg59,ou=admin,dc=pratic59,dc=fr
# createTimestamp: 20090116122019Z
# entryCSN: 20090116122019.000000Z#000000#000#000000
# modifiersName: uid=cdg59,ou=admin,dc=pratic59,dc=fr
# modifyTimestamp: 20090116122019Z
slug = entry['cdg59sid'][0]
name = entry.get('cn', [slug])[0]
service_url = entry.get('cdg59URL', [''])[0]
metadata_url = entry.get('cdg59metadataURL', [''])[0]
is_global = entry.get('cdg59isGlobal', ['FALSE'])[0] == 'TRUE'
service = Service(
name=name,
slug=slug,
is_global=is_global,
service_url=service_url,
metadata_url=metadata_url)
service.clean()
self.services[slug] = service
def handle_service_instance(self, dn, entry):
collectivity_dn = dn.split(',', 1)[1]
collectivity = self.collectivities[collectivity_dn]
mapping = {
'slug': 'cdg59siid',
'service_url': 'cdg59URL',
'metadata_url': 'cdg59metadataURL',
}
others = self.resolve_mapping(mapping, entry)
def resolve():
service = self.services[entry['cdg59serviceType'][0]]
if not service.is_global and 'service_url' not in others:
others['service_url'] = 'http://missing-url-%s.com' % others['slug']
service_instance = ServiceInstance(
collectivity=collectivity,
service=service,
**others)
service_instance.clean()
return service_instance
self.service_instances[(collectivity.slug, others['slug'])] = resolve
def handle_user(self, dn, entry):
collectivity_dn = dn.split(',', 1)[1]
collectivity = self.collectivities[collectivity_dn]
is_active = entry.get('cdg59isDisabled', ['FALSE'])[0] == 'FALSE'
mapping = {
'first_name': 'givenName',
'last_name': 'sn',
'email': 'mail',
'uid': 'uid',
'sirh_code': 'cdg59agentSirhCode',
'direction': 'cdg59direction',
'employee_type': 'employeeType',
'postal_address': 'postalAddress',
'fax': 'facsimileTelephoneNumber',
'mobile': 'mobile',
'phone': 'telephoneNumber',
}
others = self.resolve_mapping(mapping, entry)
last_login_duration = int(entry.get('cdg59lastConnectionDuration', [0])[0])
is_admin = entry.get('cdg59isAdmin', ['FALSE'])[0] == 'TRUE'
user = User(
collectivity=collectivity,
is_active=is_active,
last_login_duration=last_login_duration,
is_admin=is_admin,
**others)
if 'cdg59lastConnectionTime' in entry:
last_login = datetime.fromtimestamp(int(entry['cdg59lastConnectionTime'][0]))
last_login = make_aware(last_login, get_default_timezone())
user.last_login = last_login
if 'userPassword' in entry:
password = olap_password_to_dj(entry['userPassword'][0])
user.password = password
user.clean()
self.users.append(user)
#accesses
for siid in entry.get('cdg59serviceAccesses', []):
def f():
try:
service_instance = self.service_instances[(collectivity.slug, siid)]
access = Access(
user=user,
service_instance=service_instance)
return access
except KeyError:
pass
self.accesses.append(f)
class Command(BaseCommand):
'''Load LDAP ldif file'''
can_import_django_settings = True
requires_model_validation = True
option_list = BaseCommand.option_list + (
make_option('--fake',
action='store_true',
help='file to store a JSON log of created users'),
)
args = '<ldif_file...>'
help = 'Load/update LDIF files as users'
def save_object(self, obj):
# set fk
if not obj:
return
for attr in ('collectivity', 'service', 'service_instance', 'user'):
if hasattr(obj, attr):
setattr(obj, attr, getattr(obj, attr))
try:
# link to existing object
already = obj.__class__.objects.get_by_natural_key(*obj.natural_key())
obj.pk = already.pk
except obj.__class__.DoesNotExist:
pass
obj.save()
@transaction.commit_on_success
def handle(self, *args, **options):
options['verbosity'] = int(options['verbosity'])
for arg in args:
f = file(arg)
parser = PraticLDIFParser(f)
parser.parse()
print 'Parsed:'
print '', '-', len(parser.collectivities), 'collectivities'
print '', '-', len(parser.services), 'services'
print '', '-', len(parser.users), 'users'
print '', '-', len(parser.service_instances), 'service instances'
print '', '-', len(parser.accesses), 'accesses'
for key in parser.service_instances:
parser.service_instances[key] = parser.service_instances[key]()
parser.accesses = [resolve() for resolve in parser.accesses]
for obj in chain(parser.collectivities.itervalues(),
parser.services.itervalues(),
parser.service_instances.itervalues(),
parser.users,
parser.accesses):
self.save_object(obj)
if options['fake']:
raise Exception('Fake execution')

View File

@ -1,9 +1,11 @@
from django.db.models import (Model, TextField, CharField, EmailField,
URLField, BooleanField, IntegerField, ForeignKey, SlugField)
URLField, BooleanField, IntegerField, ForeignKey, SlugField, Manager)
from django.contrib.auth.models import User as AuthUser
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ValidationError
from authentic2 import managers
class User(AuthUser):
# givenName -> first_name
# sn -> last_name
@ -53,7 +55,8 @@ class User(AuthUser):
# facsimileTelephoneNumber
fax = CharField(
verbose_name=_('fax'),
max_length=32)
max_length=32,
blank=True)
# mobile
mobile = CharField(
verbose_name=_('mobile'),
@ -62,7 +65,11 @@ class User(AuthUser):
# telephoneNumber
phone = CharField(
verbose_name=_('phone'),
max_length=32)
max_length=32,
blank=True)
def __unicode__(self):
return self.get_full_name().strip() or self.uid
class Meta:
verbose_name = _('agent')
@ -88,6 +95,10 @@ class Collectivity(Model):
verbose_name=_('identifier'),
max_length=64,
unique=True)
is_superuser = BooleanField(
verbose_name=_('is superuser'),
default=False,
blank=True)
# cdg59collectivityId
collectivity_id = CharField(
verbose_name=_('collectivity id'),
@ -170,8 +181,6 @@ class Collectivity(Model):
verbose_name=_('distribution office'),
max_length=4,
blank=True)
# Contact
# telephoneNumber
phone = CharField(
@ -194,6 +203,11 @@ class Collectivity(Model):
max_length=128,
blank=True)
objects = managers.GetByNameManager()
def natural_key(self):
return (self.name,)
def __unicode__(self):
return self.name
@ -234,11 +248,28 @@ class Service(Model):
def __unicode__(self):
return self.name
objects = managers.GetByNameManager()
def natural_key(self):
return (self.name,)
class Meta:
verbose_name = _('service')
verbose_name_plural = _('services')
ordering = ('name',)
class ServiceInstanceManager(Manager):
def get_by_natural_key(self, slug, service_nk, collectivity_nk):
try:
service = Service.objects.get_by_natural_key(*service_nk)
except Service.DoesNotExist:
raise ServiceInstance.DoesNotExist
try:
collectivity = Collectivity.objects.get_by_natural_key(*collectivity_nk)
except Collectivity.DoesNotExist:
raise ServiceInstance.DoesNotExist
return self.get(slug=slug, service=service, collectivity=collectivity)
class ServiceInstance(Model):
# cdg59sid
slug = SlugField(
@ -266,6 +297,11 @@ class ServiceInstance(Model):
def __unicode__(self):
return unicode(self.service)
objects = ServiceInstanceManager()
def natural_key(self):
return (self.slug, self.service.natural_key(), self.collectivity.natural_key())
def clean(self):
if self.collectivity and self.service and self.service.is_global:
qs = ServiceInstance.objects.exclude(id=self.id)
@ -273,21 +309,45 @@ class ServiceInstance(Model):
service=self.service)
if qs.exists():
raise ValidationError(_('There can be only one instance of a global service by collectivity'))
if self.service and self.service.is_global:
self.service_url = self.service.service_url
self.metadata_url = self.service.metadata_url
self.oauth2_url = self.service.oauth2_url
self.oauth2_key = self.oauth2_key
if not self.service.is_global and not self.service_url:
import pdb
pdb.set_trace()
raise ValidationError(_('Service URL field is required'))
class Meta:
verbose_name = _('service instance')
verbose_name = _('service instances')
unique_together = (('slug', 'service', 'collectivity'),)
unique_together = (('slug', 'collectivity'),)
ordering = ('service__name', 'slug')
class AccessManager(Manager):
def get_by_natural_key(self, user_nk, service_instance_nk):
try:
user = User.objects.get_by_natural_key(*user_nk)
except User.DoesNotExist:
raise ServiceInstance.DoesNotExist
try:
service_instance = ServiceInstance.objects.get_by_natural_key(*service_instance_nk)
except ServiceInstance.DoesNotExist:
raise Access.DoesNotExist
return self.get(user=user, service_instance=service_instance)
class Access(Model):
user = ForeignKey('User',
verbose_name=_('user'))
service_instance = ForeignKey('ServiceInstance',
verbose_name=_('service instance'))
objects = AccessManager()
def natural_key(self):
return (self.user.natural_key(), self.service_instance.natural_key())
class Meta:
verbose_name = _('access')
verbose_name = _('accesses')

View File

@ -1,4 +1,5 @@
from django.utils.translation import ugettext_lazy as _
from django.utils.safestring import mark_safe
import django_tables2 as tables
@ -10,7 +11,7 @@ class ServiceTable(tables.Table):
verbose_name=_('name'))
delete = tables.TemplateColumn(
'{% load i18n %}<a rel="popup" href="{% url "a2-pratic-service-delete" pk=record.pk %}">{% trans "Delete" %}</a>',
verbose_name=_('delete'))
verbose_name=mark_safe('&nbsp;'))
class Meta:
model = models.Service
@ -23,7 +24,7 @@ class CollectivityTable(tables.Table):
verbose_name=_('name'))
delete = tables.TemplateColumn(
'{% load i18n %}<a rel="popup" href="{% url "a2-pratic-collectivity-delete" collectivity_pk=record.pk %}">{% trans "Delete" %}</a>',
verbose_name=_('delete'))
verbose_name=mark_safe('&nbsp;'))
class Meta:
model = models.Collectivity
@ -36,7 +37,7 @@ class UserTable(tables.Table):
verbose_name=_('identifier'))
delete = tables.TemplateColumn(
'{% load i18n %}<a rel="popup" href="{% url "a2-pratic-user-delete" collectivity_pk=record.collectivity.pk pk=record.pk %}">{% trans "Delete" %}</a>',
verbose_name=_('delete'))
verbose_name=mark_safe('&nbsp;'))
class Meta:
model = models.User
@ -49,18 +50,18 @@ class ServiceInstanceTable(tables.Table):
verbose_name=_('identifier'))
delete = tables.TemplateColumn(
'{% load i18n %}<a rel="popup" href="{% url "a2-pratic-service-instance-delete" collectivity_pk=record.collectivity.pk pk=record.pk %}">{% trans "Delete" %}</a>',
verbose_name=_('delete'))
verbose_name=mark_safe('&nbsp;'))
class Meta:
model = models.User
attrs = {'class': 'main', 'id': 'users-table'}
fields = ('slug', 'service_url')
fields = ('service', 'slug', 'service_url')
class AccessTable(tables.Table):
last_name = tables.TemplateColumn('{{ record.user.last_name}}',
verbose_name=_('Last name'))
verbose_name=_('Last name'), order_by=('user__last_name',))
first_name = tables.TemplateColumn('{{ record.user.first_name }}',
verbose_name=_('First name'))
verbose_name=_('First name'), order_by=('user__first_name',))
class Meta:
model = models.Access

View File

@ -11,6 +11,13 @@
{% endblock %}
{% block sidebar %}
<div>
<h3>{% trans "Search" %}</h3>
<div id="search-form">
<input id="search-input" type="search" value="{{ request.GET.search }}">
<button>{% trans "Search" %}</button>
</div>
</div>
<p>{% blocktrans count count=object_list.count %}{{ count }} collectivities{% plural %}{{ count }} collectivity{% endblocktrans %}</p>
{% endblock %}

View File

@ -9,7 +9,7 @@
{% endblock %}
{% block sidebar %}
<p><a href="users/">{% trans "Users management" %}</a></p>
<p><a href="users/">{% trans "Agents management" %}</a></p>
<p><a href="services/">{% trans "Service instances management" %}</a></p>
<p><a href="accesses/">{% trans "Accesses management" %}</a></p>
{% endblock %}

View File

@ -9,18 +9,6 @@
{% if title %}
<div id="appbar"><h2>{{ title }}</h2></div>
{% endif %}
{% if other_actions %}
<div class="other_actions">
<strong>{% trans "Actions" %}</strong>
<form method="post">
{% for action in other_actions %}
<input type="submit" name="{{ action.name }}" value="{{ action.title }}"
{% if action.confirm %}data-confirm="{{ action.confirm }}"{% endif %}
/>
{% endfor %}
</form>
</div>
{% endif %}
<form method="post">
<div class="form-inner-container">
{% if messages %}
@ -38,6 +26,18 @@
<button>{% if action %}{{ action }}{% else %}{% trans "Save" %}{% endif %}</button>
<a class="cancel" href="..">{% trans "Cancel" %}</a>
</div>
{% if other_actions %}
<div class="other_actions">
<strong>{% trans "Actions" %}</strong>
<form method="post">
{% for action in other_actions %}
<input type="submit" name="{{ action.name }}" value="{{ action.title }}"
{% if action.confirm %}data-confirm="{{ action.confirm }}"{% endif %}
/>
{% endfor %}
</form>
</div>
{% endif %}
</div>
</form>
</div>

View File

@ -1,17 +1,17 @@
{% extends "authentic2_pratic/collectivity_sidebar.html" %}
{% load i18n staticfiles django_tables2 %}
{% block page-title %}{{ block.super }} - {% trans "Users management" %}{% endblock %}
{% block page-title %}{{ block.super }} - {% trans "Agents management" %}{% endblock %}
{% block page_title %}{{ block.super }}{% trans "Users management" %}{% endblock %}
{% block page_title %}{{ block.super }}{% trans "Agents management" %}{% endblock %}
{% block appbar %}
{{ block.super }}
<a rel="popup" href="{% url "a2-pratic-user-add" collectivity_pk=collectivity.pk %}" id="add-user-btn">{% trans "Add user" %}</a>
<a rel="popup" href="{% url "a2-pratic-user-add" collectivity_pk=collectivity.pk %}" id="add-user-btn">{% trans "Add agent" %}</a>
{% endblock %}
{% block sidebar %}
<p>{% blocktrans count count=object_list.count %}{{ count }} users{% plural %}{{ count }} user{% endblocktrans %}</p>
<p>{% blocktrans count count=object_list.count %}{{ count }} agent{% plural %}{{ count }} agents{% endblocktrans %}</p>
{% endblock %}
{% block main %}

View File

@ -4,6 +4,7 @@ from django.utils.translation import ugettext_lazy as _
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.contrib import messages
from django.db.models.query import Q
from django.views.generic import (TemplateView, FormView, UpdateView,
CreateView, DeleteView, View, ListView)
@ -20,16 +21,21 @@ def is_pratic_admin(user):
return getattr(user, 'is_admin', False)
pratic_admin = user_passes_test(is_pratic_admin)
class DeleteActionMixin(object):
def get_other_actions(self):
yield Action('delete',
_('Delete'),
_(u'Do you really want to delete "%s" ?') % self.object)
class SearchMixin(object):
search_filter = []
def get_queryset(self):
qs = super(SearchMixin, self).get_queryset()
search = self.request.GET.get('search')
if search:
filters = []
for name in self.search_filter:
filters.append(Q(**{'%s__contains' % name: search}))
filters = reduce(Q.__or__, filters)
qs = qs.filter(filters)
return qs
def action_delete(self, request, *args, **kwargs):
self.object.delete()
return HttpResponseRedirect('.')
class HomepageView(TemplateView):
template_name = 'authentic2_pratic/homepage.html'
@ -48,7 +54,7 @@ class ServiceAddView(TitleMixin, ActionMixin, AjaxFormViewMixin, CreateView):
template_name = 'authentic2_pratic/form.html'
action = _('Add')
class ServiceView(DeleteActionMixin, TitleMixin, OtherActionsMixin,
class ServiceView(TitleMixin, OtherActionsMixin,
AjaxFormViewMixin, UpdateView):
model = models.Service
title = _('Edit service')
@ -62,7 +68,8 @@ class ServiceDeleteView(TitleMixin, AjaxFormViewMixin, DeleteView):
success_url = 'a2-pratic-services'
# Collectivities
class CollectivitiesView(SingleTableView):
class CollectivitiesView(SearchMixin, SingleTableView):
search_filter = ('name', 'slug', 'sirh_label', 'postal_code')
template_name = 'authentic2_pratic/collectivities.html'
model = models.Collectivity
table_class = tables.CollectivityTable
@ -73,7 +80,7 @@ class CollectivityAddView(TitleMixin, ActionMixin, AjaxFormViewMixin, CreateView
template_name = 'authentic2_pratic/form.html'
action = _('Add')
class CollectivityView(DeleteActionMixin, TitleMixin, OtherActionsMixin,
class CollectivityView(TitleMixin, OtherActionsMixin,
AjaxFormViewMixin, UpdateView):
model = models.Collectivity
title = _('Edit collectivity')
@ -119,22 +126,29 @@ class UsersView(CollectivityChildMixin, SingleTableView):
class UserAddView(CollectivityChildMixin, TitleMixin, ActionMixin,
AjaxFormViewMixin, CreateView):
model = models.User
title = _('Add user')
title = _('Add agent')
template_name = 'authentic2_pratic/form.html'
action = _('Add')
form_class = forms.UserForm
class UserView(CollectivityChildMixin, UserEditView):
model = models.User
title = _('Edit user')
title = _('Edit agent')
template_name = 'authentic2_pratic/form.html'
form_class = forms.UserForm
def get_other_actions(self):
yield Action('password_reset', _('Reset password'))
if self.object.is_active:
yield Action('deactivate', _('Deactivate'))
else:
yield Action('activate', _('Activate'))
class UserDeleteView(CollectivityChildMixin, TitleMixin, AjaxFormViewMixin,
DeleteView):
model = models.Service
model = models.User
template_name = 'authentic2_pratic/delete.html'
title = _('Delete user')
title = _('Delete agent')
success_url = 'a2-pratic-users'
# service instances
@ -151,7 +165,7 @@ class ServiceInstanceAddView(CollectivityChildMixin, TitleMixin, ActionMixin,
action = _('Add')
form_class = forms.ServiceInstanceForm
class ServiceInstanceView(CollectivityChildMixin, DeleteActionMixin, TitleMixin,
class ServiceInstanceView(CollectivityChildMixin, TitleMixin,
OtherActionsMixin, AjaxFormViewMixin, UpdateView):
model = models.ServiceInstance
title = _('Edit service instance')
@ -190,7 +204,7 @@ class AccessAddView(AccessMixin, CollectivityMixin, TitleMixin, ActionMixin,
action = _('Add')
form_class = forms.AccessForm
class AccessView(AccessMixin, CollectivityMixin, DeleteActionMixin, TitleMixin,
class AccessView(AccessMixin, CollectivityMixin, TitleMixin,
OtherActionsMixin, AjaxFormViewMixin, UpdateView):
model = models.Access
title = _('Edit service instance')