Parcourir la source

Allow empty login at user creation (auto completed from name)
Allow empty password at user creation
Improve admin forms
Clean model code

Fabs il y a 10 ans
Parent
commit
eb7e0fdd6b
3 fichiers modifiés avec 70 ajouts et 115 suppressions
  1. 37 2
      coin/members/admin.py
  2. 27 37
      coin/members/forms.py
  3. 6 76
      coin/members/models.py

+ 37 - 2
coin/members/admin.py

@@ -31,9 +31,40 @@ class MemberAdmin(UserAdmin):
     form = MemberChangeForm
     add_form = MemberCreationForm
 
-    
+    fieldsets = (
+            ('Adhérent', {'fields':(
+                'status',
+                'type',   
+                ('first_name', 'last_name', 'organization_name'),
+                ('entry_date', 'resign_date'))}),
+            ('Coordonnées', {'fields':(
+                'email', 
+                ('home_phone_number', 'mobile_phone_number'),
+                'address',
+                ('postal_code', 'city', 'country'))}),
+            ('Authentification', {'fields':(
+                ('username','password'))}),
+            ('Permissions', {'fields':(
+                ('is_active', 'is_staff', 'is_superuser'))})
+            )
+
+    add_fieldsets = (
+            ('Adhérent', {'fields':(
+                'status',
+                'type',   
+                ('first_name', 'last_name', 'organization_name'),
+                ('entry_date', 'resign_date'))}),
+            ('Coordonnées', {'fields':(
+                'email', 
+                ('home_phone_number', 'mobile_phone_number'),
+                'address',
+                ('postal_code', 'city', 'country'))}),
+            ('Authentification', {'fields':(
+                ('username', 'password'),)}),
+            ('Permissions', {'fields':(
+                ('is_active', 'is_staff', 'is_superuser', 'date_joined'))})
+            )
 
-    
 
     radio_fields = {"type": admin.HORIZONTAL}
 
@@ -43,6 +74,10 @@ class MemberAdmin(UserAdmin):
     
     def get_readonly_fields(self, request, obj=None):
         if obj:
+            # Remove help_text for readonly field (can't do that in the Form 
+            # django seems to user help_text from model for readonly fields)
+            username_field = [f for f in obj._meta.fields if f.name == 'username']
+            username_field[0].help_text = ''
             return ['username',]
         else:
             return []

+ 27 - 37
coin/members/forms.py

@@ -1,32 +1,24 @@
 # -*- coding: utf-8 -*-
 from django import forms
-from django.contrib.auth.forms import ReadOnlyPasswordHashField
+from django.contrib.auth.forms import UserChangeForm, ReadOnlyPasswordHashField
 
 from coin.members.models import Member
 
 
 class MemberCreationForm(forms.ModelForm):
-    password = forms.CharField(label='Password', widget=forms.PasswordInput)
+    """
+    This form was inspired from django.contrib.auth.forms.UserCreationForm
+    and adapted to coin spcificities
+    """
+    username = forms.RegexField(required=False,
+        label="Nom d'utilisateur", max_length=30, regex=r"^[\w.@+-]+$",
+        help_text=u"Laisser vide pour le générer automatiquement à partir du "
+                  u"nom et du prénom")
+    password = forms.CharField(required=False, label='Mot de passe', widget=forms.PasswordInput)
 
     class Meta:
         model = Member
-        # fields = ('email', 'first_name', 'last_name')
-        fieldsets = (
-                ('Adhérent', {'fields':(
-                    'status',
-                    'type',   
-                    ('first_name', 'last_name', 'organization_name'),
-                    ('entry_date', 'resign_date'))}),
-                ('Coordonnées', {'fields':(
-                    'email', 
-                    ('home_phone_number', 'mobile_phone_number'),
-                    'address',
-                    ('postal_code', 'city', 'country'))}),
-                ('Authentification', {'fields':(
-                    ('username', 'password'),)}),
-                ('Permissions', {'fields':(
-                    ('is_active', 'is_staff', 'is_superuser', 'date_joined'))})
-                )
+        fields = '__all__'
 
     def save(self, commit=True):
         """
@@ -36,35 +28,33 @@ class MemberCreationForm(forms.ModelForm):
         member.set_password(self.cleaned_data["password"])
         if commit:
             member.member()
-            # member.set_ldap_password(self.cleaned_data["password"])
         return member
 
 
+
 class MemberChangeForm(forms.ModelForm):
+    """
+    This form was inspired from django.contrib.auth.forms.UserChangeForm
+    and adapted to coin spcificities
+    """
     password = ReadOnlyPasswordHashField()
 
     class Meta:
         model = Member
-        # fields = ('password', 'first_name', 'last_name', 'is_active', 'is_staff')
-        fieldsets = (
-                ('Adhérent', {'fields':(
-                    'status',
-                    'type',   
-                    ('first_name', 'last_name', 'organization_name'),
-                    ('entry_date', 'resign_date'))}),
-                ('Coordonnées', {'fields':(
-                    'email', 
-                    ('home_phone_number', 'mobile_phone_number'),
-                    'address',
-                    ('postal_code', 'city', 'country'))}),
-                ('Authentification', {'fields':(
-                    ('username','password'))}),
-                ('Permissions', {'fields':(
-                    ('is_active', 'is_staff', 'is_superuser'))})
-                )
+        fields = '__all__'
+
+    def __init__(self, *args, **kwargs):
+        super(MemberChangeForm, self).__init__(*args, **kwargs)
+        f = self.fields.get('user_permissions', None)
+        if f is not None:
+            f.queryset = f.queryset.select_related('content_type')
 
     def clean_password(self):
         # Regardless of what the user provides, return the initial value.
         # This is done here, rather than on the field, because the
         # field does not have access to the initial value
         return self.initial["password"]
+
+    def clean_username(self):
+        # idem clean_password
+        return self.initial["username"]

+ 6 - 76
coin/members/models.py

@@ -32,26 +32,14 @@ class Member(CoinLdapSyncMixin, AbstractUser):
         ('pending', "Demande d'adhésion"),
     )
 
-    # objects = CoinUserManager()
-    # is_active = models.BooleanField(default=True)
-    # is_staff = models.BooleanField(default=False, verbose_name='Administrateur COIN',
-    #                                 help_text='TODO')
-
     status = models.CharField(max_length=50, choices=MEMBER_STATUS_CHOICES,
                               default='pending')
     type = models.CharField(max_length=20, choices=MEMBER_TYPE_CHOICES,
                             default='natural_person')
-    # first_name = models.CharField(max_length=200, verbose_name=u'Prénom')
-    # last_name = models.CharField(max_length=200, verbose_name=u'Nom')
-    # login = models.CharField(max_length=200, unique=True, null=True,
-    #                            blank=True,
-    #                            verbose_name='login',
-    #                            help_text='Clé avec le LDAP<br />Laisser vide pour '
-    #                            'le générer automatiquement')
+
     organization_name = models.CharField(max_length=200, blank=True,
                                          verbose_name='Nom de l\'organisme',
                                          help_text='Pour une personne morale')
-    # email = models.EmailField(max_length=254, verbose_name=u'Courriel')
     home_phone_number = models.CharField(max_length=25, blank=True,
                                          verbose_name=u'Téléphone fixe')
     mobile_phone_number = models.CharField(max_length=25, blank=True,
@@ -76,6 +64,9 @@ class Member(CoinLdapSyncMixin, AbstractUser):
                                    verbose_name='Date de départ de '
                                    'l\'association')
 
+    # Following fields are managed by the parent class AbstractUser :
+    # username, first_name, last_name, email
+
     # This property is used to change password in LDAP. Used in sync_to_ldap.
     # Should not be defined manually. Prefer use set_password method that hash
     # passwords for both ldap and local db
@@ -118,15 +109,6 @@ class Member(CoinLdapSyncMixin, AbstractUser):
         """
         super(Member, self).set_password(new_password, *args, **kwargs)
         self._password_ldap = utils.ldap_hash(new_password)
-
-
-    # def set_ldap_password(self, new_password):
-    #     """
-    #     Change password in LDAP
-    #     """
-    #     ldap_user = LdapUser.objects.get(pk=self.username)
-    #     ldap_user.password = new_password
-    #     ldap_user.save()
         
     def get_active_subscriptions(self, date=datetime.date.today()):
         """
@@ -180,9 +162,9 @@ class Member(CoinLdapSyncMixin, AbstractUser):
         ldap_user.last_name = self.last_name
         ldap_user.first_name = self.first_name
         
-        #If a password is definied in _password_ldap, change it in LDAP
+        # If a password is definied in _password_ldap, change it in LDAP
         if self._password_ldap:
-            #Make sure password is hashed
+            # Make sure password is hashed
             ldap_user.password = utils.ldap_hash(self._password_ldap)
 
         ldap_user.save()
@@ -207,16 +189,6 @@ class Member(CoinLdapSyncMixin, AbstractUser):
             ldap_group.members.remove(self.username)
             ldap_group.save()
 
-    # def has_perm(self, perm, obj=None):
-    #     "Does the user have a specific permission?"
-    #     # Simplest possible answer: Yes, always
-    #     return True
-
-    # def has_module_perms(self, app_label):
-    #     "Does the user have permissions to view the app `app_label`?"
-    #     # Simplest possible answer: Yes, always
-    #     return True
-
     class Meta:
         verbose_name = 'membre'
 
@@ -319,17 +291,6 @@ def define_username(sender, instance, **kwargs):
     if not instance.username and not instance.pk:
         instance.username = instance.get_automatic_username()
 
-
-# @receiver(pre_save, sender=LdapUser)
-# def change_password(sender, instance, **kwargs):
-#     """
-#     Lors de la sauvegarde d'un utilisateur Ldap, cette fonction est exécutée
-#     avant la sauvegarde pour chiffrer le mot de passe s'il est définit
-#     et s'il n'est pas déjà chiffré
-#     """
-#     instance.password = utils.ldap_hash(instance.password)
-
-
 @receiver(pre_save, sender=LdapUser)
 def define_display_name(sender, instance, **kwargs):
     """
@@ -339,34 +300,3 @@ def define_display_name(sender, instance, **kwargs):
     if not instance.display_name:
         instance.display_name = '%s %s' % (instance.first_name,
                                            instance.last_name)
-
-
-# @receiver(user_logged_in)
-# def define_member_user(sender, request, user, **kwargs):
-#     """
-#     Lorsqu'un utilisateur se connect avec succes, fait le lien entre le membre
-#     et l'utilisateur en définissant le champ user du model membre ayant le
-#     ldap_cn utilisé pour la connexion
-#     """
-#     try:
-#         member = Member.objects.get(ldap_cn=user.username)
-#         if not member.user:
-#             member.user = user
-#             member.save()
-#         elif member.user.username != user.username:
-#             raise Exception('Un membre avec cet ldap_cn existe en base de '
-#                             'donnée mais l\'utilisateur auquel il est rattaché '
-#                             'ne correspond pas.')
-#     except Member.DoesNotExist:
-#         if not user.is_superuser:
-#             raise
-
-
-#==============================================================================
-# @receiver(pre_save, sender = LdapUser)
-# def ssha_password(sender, **kwargs):
-#     if not kwargs['instance'].password.startswith('{SSHA}'):
-#         salt = os.urandom(8).encode('hex')
-#         kwargs['instance'].password = '{SSHA}' + base64.b64encode(
-    #         hashlib.sha1(obj.password + salt).digest() + salt)
-#==============================================================================