Browse Source

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

Fabs 10 years ago
parent
commit
eb7e0fdd6b
3 changed files with 70 additions and 115 deletions
  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)
-#==============================================================================