|
@@ -7,6 +7,8 @@ import datetime
|
|
|
from django.db import models
|
|
|
from django.db.models import Q
|
|
|
from django.db.models.signals import pre_save
|
|
|
+from django.utils.translation import ugettext_lazy as _
|
|
|
+from django.utils.text import capfirst
|
|
|
from django.dispatch import receiver
|
|
|
from django.contrib.auth.models import AbstractUser
|
|
|
from django.conf import settings
|
|
@@ -25,51 +27,51 @@ class Member(CoinLdapSyncMixin, AbstractUser):
|
|
|
REQUIRED_FIELDS = ['first_name', 'last_name', 'email', ]
|
|
|
|
|
|
MEMBER_TYPE_CHOICES = (
|
|
|
- ('natural_person', 'Personne physique'),
|
|
|
- ('legal_entity', 'Personne morale'),
|
|
|
+ ('natural_person', _("Individual")),
|
|
|
+ ('legal_entity', _("Organisation")),
|
|
|
)
|
|
|
MEMBER_STATUS_CHOICES = (
|
|
|
- ('member', 'Adhérent'),
|
|
|
- ('not_member', 'Non adhérent'),
|
|
|
- ('pending', "Demande d'adhésion"),
|
|
|
+ ('member', _("Member")),
|
|
|
+ ('not_member', _("Not a member")),
|
|
|
+ ('pending', _("Pending membership")),
|
|
|
)
|
|
|
|
|
|
status = models.CharField(max_length=50, choices=MEMBER_STATUS_CHOICES,
|
|
|
- default='member', verbose_name='statut')
|
|
|
+ default='member', verbose_name=_('status'))
|
|
|
type = models.CharField(max_length=20, choices=MEMBER_TYPE_CHOICES,
|
|
|
- default='natural_person', verbose_name='type')
|
|
|
+ default='natural_person', verbose_name=_("type"))
|
|
|
|
|
|
nickname = models.CharField(max_length=64, blank=True,
|
|
|
- verbose_name="nom d'usage",
|
|
|
- help_text='Pseudonyme, …')
|
|
|
+ verbose_name=_("nickname"),
|
|
|
+ help_text=_("Pseudonym, …"))
|
|
|
organization_name = models.CharField(max_length=200, blank=True,
|
|
|
- verbose_name="nom de l'organisme",
|
|
|
- help_text='Pour une personne morale')
|
|
|
+ verbose_name=_("organisation name"),
|
|
|
+ help_text=_("For organisations"))
|
|
|
home_phone_number = models.CharField(max_length=25, blank=True,
|
|
|
- verbose_name='téléphone fixe')
|
|
|
+ verbose_name=_("home phone number"))
|
|
|
mobile_phone_number = models.CharField(max_length=25, blank=True,
|
|
|
- verbose_name='téléphone mobile')
|
|
|
+ verbose_name=_("mobile phone number"))
|
|
|
# TODO: use a django module that provides an address model? (would
|
|
|
# support more countries and address types)
|
|
|
address = models.TextField(
|
|
|
- verbose_name='adresse postale', blank=True, null=True)
|
|
|
+ verbose_name=_("physical address"), blank=True, null=True)
|
|
|
+ validator = RegexValidator(regex=r'^\d{5}$',
|
|
|
+ message=_("Invalid postal code."))
|
|
|
postal_code = models.CharField(max_length=5, blank=True, null=True,
|
|
|
- validators=[RegexValidator(regex=r'^\d{5}$',
|
|
|
- message='Code postal non valide.')],
|
|
|
- verbose_name='code postal')
|
|
|
+ validators=[validator],
|
|
|
+ verbose_name=_("postal code"))
|
|
|
city = models.CharField(max_length=200, blank=True, null=True,
|
|
|
- verbose_name='commune')
|
|
|
+ verbose_name=_("city"))
|
|
|
country = models.CharField(max_length=200, blank=True, null=True,
|
|
|
default='France',
|
|
|
- verbose_name='pays')
|
|
|
+ verbose_name=_("country"))
|
|
|
resign_date = models.DateField(null=True, blank=True,
|
|
|
- verbose_name="date de départ de "
|
|
|
- "l'association",
|
|
|
- help_text="En cas de départ prématuré")
|
|
|
- comments = models.TextField(blank=True, verbose_name='commentaires',
|
|
|
- help_text="Commentaires libres (informations"
|
|
|
- " spécifiques concernant l'adhésion,"
|
|
|
- " raison du départ, etc)")
|
|
|
+ verbose_name=_("resign date of membership"),
|
|
|
+ help_text=_("In case of premature leaving"))
|
|
|
+ comments = models.TextField(blank=True, verbose_name=_("comments"),
|
|
|
+ help_text=_("Free-form comments (specific"
|
|
|
+ " membership information, reason"
|
|
|
+ " for leaving, etc)"))
|
|
|
|
|
|
# Following fields are managed by the parent class AbstractUser :
|
|
|
# username, first_name, last_name, email
|
|
@@ -82,22 +84,26 @@ class Member(CoinLdapSyncMixin, AbstractUser):
|
|
|
_password_ldap = None
|
|
|
|
|
|
def clean(self):
|
|
|
+ from django.utils.translation import ugettext as _
|
|
|
if self.type == 'legal_entity':
|
|
|
if not self.organization_name:
|
|
|
- raise ValidationError("Le nom de l'organisme est obligatoire "
|
|
|
- "pour une personne morale")
|
|
|
+ raise ValidationError(_("An organisation name is mandatory for"
|
|
|
+ " organisations."))
|
|
|
elif self.type == 'natural_person':
|
|
|
if not (self.first_name and self.last_name):
|
|
|
- raise ValidationError("Le nom et prénom sont obligatoires "
|
|
|
- "pour une personne physique")
|
|
|
+ raise ValidationError(_("First name and last name are mandatory"
|
|
|
+ " for individuals."))
|
|
|
|
|
|
def __unicode__(self):
|
|
|
+ from django.utils.translation import ugettext as _
|
|
|
if self.type == 'legal_entity':
|
|
|
return self.organization_name
|
|
|
elif self.nickname:
|
|
|
return self.nickname
|
|
|
else:
|
|
|
- return self.first_name + ' ' + self.last_name
|
|
|
+ return _("{first_name} {last_name}").format(
|
|
|
+ first_name=self.first_name,
|
|
|
+ last_name=self.last_name)
|
|
|
|
|
|
def get_full_name(self):
|
|
|
return str(self)
|
|
@@ -110,7 +116,7 @@ class Member(CoinLdapSyncMixin, AbstractUser):
|
|
|
x = self.membership_fees.order_by('-end_date')
|
|
|
if x:
|
|
|
return self.membership_fees.order_by('-end_date')[0].end_date
|
|
|
- end_date_of_membership.short_description = "Date de fin d'adhésion"
|
|
|
+ end_date_of_membership.short_description = _("End date of membership")
|
|
|
|
|
|
def is_paid_up(self):
|
|
|
"""
|
|
@@ -167,7 +173,7 @@ class Member(CoinLdapSyncMixin, AbstractUser):
|
|
|
"""
|
|
|
Update LDAP data when a member is saved
|
|
|
"""
|
|
|
-
|
|
|
+ from django.utils.translation import ugettext as _
|
|
|
# Do not perform LDAP query if no usefull fields to update are specified
|
|
|
# in update_fields
|
|
|
# Ex : at login, last_login field is updated by django auth module.
|
|
@@ -175,8 +181,8 @@ class Member(CoinLdapSyncMixin, AbstractUser):
|
|
|
return
|
|
|
|
|
|
# Fail if no username specified
|
|
|
- assert self.username, ('Can\'t sync with LDAP because missing username '
|
|
|
- 'value for the Member : %s' % self)
|
|
|
+ assert self.username, _("Can't sync with LDAP because missing username "
|
|
|
+ "value for Member <{name}>").format(name=str(self))
|
|
|
|
|
|
# If try to sync a superuser in creation mode
|
|
|
# Try to retrieve the user in ldap. If exists, switch to update mode
|
|
@@ -231,8 +237,9 @@ class Member(CoinLdapSyncMixin, AbstractUser):
|
|
|
"""
|
|
|
Delete member from the LDAP
|
|
|
"""
|
|
|
- assert self.username, ('Can\'t delete from LDAP because missing '
|
|
|
- 'username value for the Member : %s' % self)
|
|
|
+ from django.utils.translation import ugettext as _
|
|
|
+ assert self.username, _("Can't delete from LDAP because missing username "
|
|
|
+ "value for Member <{name}>").format(name=str(self))
|
|
|
|
|
|
# Delete user from LDAP
|
|
|
ldap_user = LdapUser.objects.get(pk=self.username)
|
|
@@ -256,7 +263,8 @@ class Member(CoinLdapSyncMixin, AbstractUser):
|
|
|
context={'member': self, 'branding':ISPInfo.objects.first()})
|
|
|
|
|
|
class Meta:
|
|
|
- verbose_name = 'membre'
|
|
|
+ verbose_name = _('member')
|
|
|
+ verbose_name_plural = _('members')
|
|
|
|
|
|
# Hack to force email to be required by Member model
|
|
|
Member._meta.get_field('email')._unique = True
|
|
@@ -289,7 +297,8 @@ def get_automatic_username(member):
|
|
|
# Concaténer avec nom de famille
|
|
|
username = ('%s%s' % (first_name_letters, member.last_name))
|
|
|
else:
|
|
|
- raise Exception('Il n\'y a pas sufissement d\'informations pour déterminer un login automatiquement')
|
|
|
+ raise Exception(_("Not enough information provided to generate "
|
|
|
+ "a login for the member."))
|
|
|
|
|
|
# Remplacer ou enlever les caractères non ascii
|
|
|
username = unicodedata.normalize('NFD', username)\
|
|
@@ -321,12 +330,12 @@ def get_automatic_username(member):
|
|
|
|
|
|
class CryptoKey(CoinLdapSyncMixin, models.Model):
|
|
|
|
|
|
- KEY_TYPE_CHOICES = (('RSA', 'RSA'), ('GPG', 'GPG'))
|
|
|
+ KEY_TYPE_CHOICES = (('RSA', _('RSA')), ('GPG', _('GPG')))
|
|
|
|
|
|
type = models.CharField(max_length=3, choices=KEY_TYPE_CHOICES,
|
|
|
- verbose_name='type')
|
|
|
- key = models.TextField(verbose_name='clé')
|
|
|
- member = models.ForeignKey('Member', verbose_name='membre')
|
|
|
+ verbose_name=_('type'))
|
|
|
+ key = models.TextField(verbose_name=_('key'))
|
|
|
+ member = models.ForeignKey('Member', verbose_name=_('member'))
|
|
|
|
|
|
def sync_to_ldap(self, creation, *args, **kwargs):
|
|
|
"""Simply tell the member object to resync all its SSH keys to LDAP"""
|
|
@@ -336,44 +345,50 @@ class CryptoKey(CoinLdapSyncMixin, models.Model):
|
|
|
self.member.sync_ssh_keys()
|
|
|
|
|
|
def __unicode__(self):
|
|
|
- return 'Clé %s de %s' % (self.type, self.member)
|
|
|
+ from django.utils.translation import ugettext as _
|
|
|
+ readable_type = capfirst(self.get_type_display())
|
|
|
+ return _('{type} key of {user}').format(type=readable_type,
|
|
|
+ user=self.member)
|
|
|
|
|
|
class Meta:
|
|
|
- verbose_name = 'clé'
|
|
|
+ verbose_name = _('key')
|
|
|
+ verbose_name_plural = _('keys')
|
|
|
|
|
|
|
|
|
class MembershipFee(models.Model):
|
|
|
PAYMENT_METHOD_CHOICES = (
|
|
|
- ('cash', 'Espèces'),
|
|
|
- ('check', 'Chèque'),
|
|
|
- ('transfer', 'Virement'),
|
|
|
- ('other', 'Autre')
|
|
|
+ ('cash', _('Cash')),
|
|
|
+ ('check', _('Check')),
|
|
|
+ ('transfer', _('Transfer')),
|
|
|
+ ('other', _('Other'))
|
|
|
)
|
|
|
|
|
|
member = models.ForeignKey('Member', related_name='membership_fees',
|
|
|
- verbose_name='membre')
|
|
|
+ verbose_name=_('member'))
|
|
|
amount = models.DecimalField(null=False, max_digits=5, decimal_places=2,
|
|
|
default=settings.MEMBER_DEFAULT_COTISATION,
|
|
|
- verbose_name='montant', help_text='en €')
|
|
|
+ verbose_name=_('amount'),
|
|
|
+ help_text=_('in €'))
|
|
|
start_date = models.DateField(
|
|
|
null=False,
|
|
|
blank=False,
|
|
|
- verbose_name='date de début de cotisation')
|
|
|
+ verbose_name=_('start date of membership fee'))
|
|
|
end_date = models.DateField(
|
|
|
null=False,
|
|
|
blank=True,
|
|
|
- verbose_name='date de fin de cotisation',
|
|
|
- help_text='par défaut, la cotisation dure un an')
|
|
|
+ verbose_name=_('end date of membership fee'),
|
|
|
+ help_text=_('By default, a membership fee covers one year'))
|
|
|
|
|
|
payment_method = models.CharField(max_length=100, null=True, blank=True,
|
|
|
choices=PAYMENT_METHOD_CHOICES,
|
|
|
- verbose_name='moyen de paiement')
|
|
|
+ verbose_name=_('payment method'))
|
|
|
reference = models.CharField(max_length=125, null=True, blank=True,
|
|
|
- verbose_name='référence du paiement',
|
|
|
- help_text='numéro de chèque, '
|
|
|
- 'référence de virement, commentaire...')
|
|
|
+ verbose_name=_('payment reference'),
|
|
|
+ help_text=_('Check number, '
|
|
|
+ 'reference of bank transfer, '
|
|
|
+ 'comment…'))
|
|
|
payment_date = models.DateField(null=True, blank=True,
|
|
|
- verbose_name='date du paiement')
|
|
|
+ verbose_name=_('date of payment'))
|
|
|
|
|
|
def clean(self):
|
|
|
if self.end_date is None:
|
|
@@ -383,7 +398,8 @@ class MembershipFee(models.Model):
|
|
|
return '%s - %s - %i€' % (self.member, self.start_date, self.amount)
|
|
|
|
|
|
class Meta:
|
|
|
- verbose_name = 'cotisation'
|
|
|
+ verbose_name = _('membership fee')
|
|
|
+ verbose_name_plural = _('membership fees')
|
|
|
|
|
|
|
|
|
class LdapUser(ldapdb.models.Model):
|