|
@@ -9,7 +9,7 @@ from django.db import models
|
|
|
from django.db.models import Q, Max
|
|
|
from django.db.models.signals import pre_save
|
|
|
from django.dispatch import receiver
|
|
|
-from django.contrib.auth.models import AbstractUser, Permission
|
|
|
+from django.contrib.auth.models import AbstractUser, Permission, UserManager
|
|
|
from django.contrib.contenttypes.models import ContentType
|
|
|
from django.conf import settings
|
|
|
from django.core.validators import RegexValidator
|
|
@@ -22,6 +22,19 @@ from coin.mixins import CoinLdapSyncMixin
|
|
|
from coin import utils
|
|
|
|
|
|
|
|
|
+
|
|
|
+class MemberManager(UserManager):
|
|
|
+ def manageable_by(self, user):
|
|
|
+ """" Renvoie la liste des members que l'utilisateur est autorisé à voir
|
|
|
+ dans l'interface d'administration.
|
|
|
+ """
|
|
|
+ if user.is_superuser:
|
|
|
+ return super(MemberManager, self).all()
|
|
|
+ else:
|
|
|
+ offers = Offer.objects.manageable_by(user)
|
|
|
+ return super(MemberManager, self).filter(offersubscription__offer__in=offers).distinct()
|
|
|
+
|
|
|
+
|
|
|
class Member(CoinLdapSyncMixin, AbstractUser):
|
|
|
|
|
|
# USERNAME_FIELD = 'login'
|
|
@@ -81,6 +94,8 @@ class Member(CoinLdapSyncMixin, AbstractUser):
|
|
|
default=True, verbose_name='relance de cotisation',
|
|
|
help_text='Précise si l\'utilisateur doit recevoir des mails de relance pour la cotisation. Certains membres n\'ont pas à recevoir de relance (prélèvement automatique, membres d\'honneurs, etc.)')
|
|
|
|
|
|
+ objects = MemberManager()
|
|
|
+
|
|
|
# Following fields are managed by the parent class AbstractUser :
|
|
|
# username, first_name, last_name, email
|
|
|
# However we hack the model to force theses fields to be required. (see
|
|
@@ -494,42 +509,6 @@ class LdapUser(ldapdb.models.Model):
|
|
|
# managed = False # Indique à Django de ne pas intégrer ce model en base
|
|
|
|
|
|
|
|
|
-class RowLevelPermission(Permission):
|
|
|
- offer = models.ForeignKey(
|
|
|
- 'offers.Offer', null=True, verbose_name="Offre",
|
|
|
- help_text="Offre dont l'utilisateur est autorisé à voir et modifier les membres et les abonnements.")
|
|
|
- description = models.TextField(blank=True)
|
|
|
-
|
|
|
- @classmethod
|
|
|
- def get_manageable_offers(cls, user):
|
|
|
- """" Renvoie la liste des offres dont l'utilisateur est autorisé à
|
|
|
- voir les membres et les abonnements dans l'interface d'administration.
|
|
|
- """
|
|
|
- # toutes les permissions appliquées à cet utilisateur
|
|
|
- # (liste de chaines de caractères)
|
|
|
- perms = user.get_all_permissions()
|
|
|
- allowedcodenames = [ s.split('offers.',1)[1] for s in perms if s.startswith('offers.')]
|
|
|
- # parmi toutes les RowLevelpermission, celles qui sont relatives à des OfferSubscription et qui sont dans allowedcodenames
|
|
|
- rowperms = cls.objects.filter(content_type=ContentType.objects.get_for_model(OfferSubscription), codename__in=allowedcodenames)
|
|
|
- # toutes les Offers pour lesquelles il existe une RowLevelpermission correspondante dans rowperms
|
|
|
- return Offer.objects.filter(rowlevelpermission__in=rowperms) # @JocelynD: un petit .distinct() ici non ?
|
|
|
-
|
|
|
- @classmethod
|
|
|
- def get_manageable_users(cls, user):
|
|
|
- """" Renvoie la liste des members que l'utilisateur est autorisé à voir
|
|
|
- dans l'interface d'administration.
|
|
|
- """
|
|
|
- if user.is_superuser:
|
|
|
- return Member.objects.all()
|
|
|
- else:
|
|
|
- offers = RowLevelPermission.get_manageable_offers(user)
|
|
|
- return Member.objects.filter(offersubscription__offer__in=offers).distinct()
|
|
|
-
|
|
|
- class Meta:
|
|
|
- verbose_name = 'permission fine'
|
|
|
- verbose_name_plural = 'permissions fines'
|
|
|
-
|
|
|
-
|
|
|
|
|
|
@receiver(pre_save, sender=Member)
|
|
|
def define_username(sender, instance, **kwargs):
|
|
@@ -550,3 +529,63 @@ def define_display_name(sender, instance, **kwargs):
|
|
|
if not instance.display_name:
|
|
|
instance.display_name = '%s %s' % (instance.first_name,
|
|
|
instance.last_name)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+class RowLevelPermission(Permission):
|
|
|
+ offer = models.ForeignKey(
|
|
|
+ 'offers.Offer', null=True, verbose_name="Offre",
|
|
|
+ help_text="Offre dont l'utilisateur est autorisé à voir et modifier les membres et les abonnements.")
|
|
|
+ description = models.TextField(blank=True)
|
|
|
+
|
|
|
+ class Meta:
|
|
|
+ verbose_name = 'permission fine'
|
|
|
+ verbose_name_plural = 'permissions fines'
|
|
|
+
|
|
|
+
|
|
|
+RowLevelPermission._meta.get_field('codename').blank = True
|
|
|
+RowLevelPermission._meta.get_field('codename').help_text = 'Laisser vide pour le générer automatiquement'
|
|
|
+RowLevelPermission._meta.get_field('content_type').help_text = "Garder 'abonnement' pour une utilisation normale"
|
|
|
+
|
|
|
+def get_automatic_codename(perm):
|
|
|
+ """
|
|
|
+ Calcule le codename automatiquement en fonction du name.
|
|
|
+ """
|
|
|
+ if perm.name:
|
|
|
+ codename = perm.name
|
|
|
+ else:
|
|
|
+ raise Exception('Il n\'y a pas sufissement d\'informations pour déterminer un codename automatiquement')
|
|
|
+
|
|
|
+ # Remplacer ou enlever les caractères non ascii
|
|
|
+ codename = unicodedata.normalize('NFD', codename)\
|
|
|
+ .encode('ascii', 'ignore')
|
|
|
+ # Enlever ponctuation (sauf _-.) et espace
|
|
|
+ punctuation = ('!"#$%&\'()*+,/:;<=>?@[\\]^`{|}~').encode('ascii')
|
|
|
+ codename = codename.translate(None, punctuation)
|
|
|
+ # En minuscule
|
|
|
+ codename = codename.lower()
|
|
|
+ # Maximum de 30 char
|
|
|
+ codename = codename[:30]
|
|
|
+ codename = codename.strip().replace(' ', '-')
|
|
|
+
|
|
|
+ # Recherche dans les membres existants un codename identique
|
|
|
+ perm = Permission.objects.filter(codename=codename)
|
|
|
+ base_codename = codename
|
|
|
+ incr = 2
|
|
|
+ # Tant qu'une permission est trouvée, incrémente un entier à la fin
|
|
|
+ while perm:
|
|
|
+ codename = base_codename + str(incr)
|
|
|
+ perm = Permission.objects.filter(codename=codename)
|
|
|
+ incr += 1
|
|
|
+
|
|
|
+ return codename
|
|
|
+
|
|
|
+
|
|
|
+@receiver(pre_save, sender=RowLevelPermission)
|
|
|
+def define_codename(sender, instance, **kwargs):
|
|
|
+ """
|
|
|
+ Lors de la sauvegarde d'une RowLevelPermission. Si le champ codename n'est pas définit,
|
|
|
+ le calcul automatiquement.
|
|
|
+ """
|
|
|
+ if not instance.codename:
|
|
|
+ instance.codename = get_automatic_codename(instance)
|