From 84764ce51b9e9a141a150c1c6c3beb9b66ce2351 Mon Sep 17 00:00:00 2001 From: grisel-davy Date: Thu, 9 Aug 2018 23:43:35 +0200 Subject: [PATCH 1/6] verification des extension mail externe et lower des adresses mail pour mieux verifier les doublons --- users/forms.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/users/forms.py b/users/forms.py index 49135266..97fb3fc3 100644 --- a/users/forms.py +++ b/users/forms.py @@ -138,6 +138,12 @@ class UserCreationForm(FormRevMixin, forms.ModelForm): prefix = kwargs.pop('prefix', self.Meta.model.__name__) super(UserCreationForm, self).__init__(*args, prefix=prefix, **kwargs) + def clean_email(self): + if not OptionalUser.objects.first().local_email_domain in self.cleaned_data.get('email'): + return self.cleaned_data.get('email').lower() + else: + raise forms.ValidationError("You can't use an internal address as your external address.") + class Meta: model = Adherent fields = ('pseudo', 'surname', 'email') @@ -308,6 +314,12 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): self.fields['room'].empty_label = "Pas de chambre" self.fields['school'].empty_label = "Séléctionner un établissement" + def clean_email(self): + if not OptionalUser.objects.first().local_email_domain in self.cleaned_data.get('email'): + return self.cleaned_data.get('email').lower() + else: + raise forms.ValidationError("Vous ne pouvez pas utiliser une addresse {}".format(OptionalUser.objects.first().local_email_domain)) + class Meta: model = Adherent fields = [ @@ -323,6 +335,7 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): 'gpg_fingerprint' ] + def clean_telephone(self): """Verifie que le tel est présent si 'option est validée dans preferences""" @@ -607,6 +620,9 @@ class EMailAddressForm(FormRevMixin, ModelForm): super(EMailAddressForm, self).__init__(*args, prefix=prefix, **kwargs) self.fields['local_part'].label = "Local part of the email" self.fields['local_part'].help_text = "Can't contain @" + + def clean_local_part(self): + return self.cleaned_data.get('local_part').lower() class Meta: model = EMailAddress @@ -631,6 +647,12 @@ class EmailSettingsForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): "Enable the use of the local email account" ) + def clean_email(self): + if not OptionalUser.objects.first().local_email_domain in self.cleaned_data.get('email'): + return self.cleaned_data.get('email').lower() + else: + raise forms.ValidationError("Vous ne pouvez pas utiliser une addresse {}".format(OptionalUser.objects.first().local_email_domain)) + class Meta: model = User fields = ['email', 'local_email_redirect', 'local_email_enabled'] From e70b063a3dada89baa85e217cd18767f3e44a2bb Mon Sep 17 00:00:00 2001 From: Charlie Jacomme Date: Fri, 10 Aug 2018 19:27:10 +0200 Subject: [PATCH 2/6] fix mail problems: check pseudo taken and lower) --- users/models.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/users/models.py b/users/models.py index 01de431c..121d83e2 100755 --- a/users/models.py +++ b/users/models.py @@ -103,6 +103,13 @@ def linux_user_validator(login): params={'label': login}, ) +def pseudo_taken(login): + """ Retourne une erreur de validation si le login ne respecte + pas les contraintes unix (maj, min, chiffres ou tiret)""" + if (EMailAddress.objects + .filter(local_part=login.lower())): + raise forms.ValidationError('Pseudo is already taken') + def get_fresh_user_uid(): """ Renvoie le plus petit uid non pris. Fonction très paresseuse """ @@ -193,7 +200,7 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, max_length=32, unique=True, help_text="Doit contenir uniquement des lettres, chiffres, ou tirets", - validators=[linux_user_validator] + validators=[linux_user_validator, pseudo_taken] ) email = models.EmailField() local_email_redirect = models.BooleanField( @@ -966,8 +973,8 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, """Check if this pseudo is already used by any mailalias. Better than raising an error in post-save and catching it""" if (EMailAddress.objects - .filter(local_part=self.pseudo) - .exclude(user=self)): + .filter(local_part=self.pseudo.lower()).exclude(user=self) + ): raise ValidationError("This pseudo is already in use.") def __str__(self): @@ -1106,7 +1113,7 @@ def user_post_save(**kwargs): Synchronise le ldap""" is_created = kwargs['created'] user = kwargs['instance'] - EMailAddress.objects.get_or_create(local_part=user.pseudo, user=user) + EMailAddress.objects.get_or_create(local_part=user.pseudo.lower(), user=user) if is_created: user.notif_inscription() user.state_sync() @@ -1803,6 +1810,7 @@ class EMailAddress(RevMixin, AclMixin, models.Model): "local email account") def clean(self, *args, **kwargs): + self.local_part = self.local_part.lower() if "@" in self.local_part: raise ValidationError("The local part cannot contain a @") super(EMailAddress, self).clean(*args, **kwargs) From ff2a6c27221ad853b6a022157812bd79aba00925 Mon Sep 17 00:00:00 2001 From: Charlie Jacomme Date: Sat, 11 Aug 2018 00:13:27 +0200 Subject: [PATCH 3/6] many fix mail --- users/models.py | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/users/models.py b/users/models.py index 121d83e2..ff596424 100755 --- a/users/models.py +++ b/users/models.py @@ -53,6 +53,7 @@ import sys from django.db import models from django.db.models import Q from django import forms +from django.forms import ValidationError from django.db.models.signals import post_save, post_delete, m2m_changed from django.dispatch import receiver from django.utils.functional import cached_property @@ -103,14 +104,6 @@ def linux_user_validator(login): params={'label': login}, ) -def pseudo_taken(login): - """ Retourne une erreur de validation si le login ne respecte - pas les contraintes unix (maj, min, chiffres ou tiret)""" - if (EMailAddress.objects - .filter(local_part=login.lower())): - raise forms.ValidationError('Pseudo is already taken') - - def get_fresh_user_uid(): """ Renvoie le plus petit uid non pris. Fonction très paresseuse """ uids = list(range( @@ -200,9 +193,9 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, max_length=32, unique=True, help_text="Doit contenir uniquement des lettres, chiffres, ou tirets", - validators=[linux_user_validator, pseudo_taken] + validators=[linux_user_validator] ) - email = models.EmailField() + email = models.EmailField(unique=True) local_email_redirect = models.BooleanField( default=False, help_text="Whether or not to redirect the local email messages to the main email." @@ -293,7 +286,7 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, if not OptionalUser.get_cached_value('local_email_accounts_enabled') or not self.local_email_enabled or self.local_email_redirect: return str(self.email) else: - return str(self.emailaddress_set.get(local_part=self.pseudo)) + return str(self.emailaddress_set.get(local_part=self.pseudo.lower())) @cached_property def class_name(self): @@ -973,7 +966,7 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, """Check if this pseudo is already used by any mailalias. Better than raising an error in post-save and catching it""" if (EMailAddress.objects - .filter(local_part=self.pseudo.lower()).exclude(user=self) + .filter(local_part=self.pseudo.lower()).exclude(user_id=self.id) ): raise ValidationError("This pseudo is already in use.") @@ -1775,7 +1768,7 @@ class EMailAddress(RevMixin, AclMixin, models.Model): a message and a boolean which is True if the user can delete the local email account. """ - if self.local_part == self.user.pseudo: + if self.local_part == self.user.pseudo.lower(): return False, ("You cannot delete a local email account whose " "local part is the same as the username.") if user_request.has_perm('users.delete_emailaddress'): @@ -1797,7 +1790,7 @@ class EMailAddress(RevMixin, AclMixin, models.Model): a message and a boolean which is True if the user can edit the local email account. """ - if self.local_part == self.user.pseudo: + if self.local_part == self.user.pseudo.lower(): return False, ("You cannot edit a local email account whose " "local part is the same as the username.") if user_request.has_perm('users.change_emailaddress'): From cef93af15a5c45b1a5ea4afcb566d67c59dc2dce Mon Sep 17 00:00:00 2001 From: Charlie Jacomme Date: Sat, 11 Aug 2018 04:32:50 +0200 Subject: [PATCH 4/6] last email migration --- users/migrations/0076_auto_20180811_0425.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 users/migrations/0076_auto_20180811_0425.py diff --git a/users/migrations/0076_auto_20180811_0425.py b/users/migrations/0076_auto_20180811_0425.py new file mode 100644 index 00000000..0b53af9a --- /dev/null +++ b/users/migrations/0076_auto_20180811_0425.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-08-11 02:25 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0075_auto_20180811_0420'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='email', + field=models.EmailField(max_length=254, unique=True), + ), + ] From 680b8a7ec73b81be0730c05dff15d2a664b4fa79 Mon Sep 17 00:00:00 2001 From: Hugo LEVY-FALK Date: Tue, 14 Aug 2018 12:21:58 +0200 Subject: [PATCH 5/6] Rend l'affichage des mails locaux plus intuitif --- CHANGELOG.md | 8 ++++++ static/js/email_address.js | 17 ++++++++++++ users/forms.py | 13 +++------ users/migrations/0074_auto_20180814_1059.py | 30 +++++++++++++++++++++ users/migrations/0076_auto_20180811_0425.py | 20 -------------- users/models.py | 13 +++++---- users/templates/users/profil.html | 28 ++++++++++--------- users/templates/users/user.html | 3 +++ users/views.py | 6 +++-- 9 files changed, 88 insertions(+), 50 deletions(-) create mode 100644 static/js/email_address.js create mode 100644 users/migrations/0074_auto_20180814_1059.py delete mode 100644 users/migrations/0076_auto_20180811_0425.py diff --git a/CHANGELOG.md b/CHANGELOG.md index f390ba09..5ec2da1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -128,3 +128,11 @@ You need to ensure that your database character set is utf-8. ```sql ALTER DATABASE re2o CHARACTER SET utf8; ``` + +## MR 247: Fix des comptes mails + +Fix several issues with email accounts, you need to collect the static files. + +```bash +./manage.py collectstatic +``` diff --git a/static/js/email_address.js b/static/js/email_address.js new file mode 100644 index 00000000..10c1f544 --- /dev/null +++ b/static/js/email_address.js @@ -0,0 +1,17 @@ +/** To enable the redirection has no meaning if the local email adress is not + * enabled. Thus this function enable the checkbox if needed and changes its + * state. + */ +function enable_redirection_chkbox() { + var redirect = document.getElementById('id_User-local_email_redirect'); + var enabled = document.getElementById('id_User-local_email_enabled').checked; + if(!enabled) + { + redirect.checked = false; + } + redirect.disabled = !enabled; +} + +var enabled_chkbox = document.getElementById('id_User-local_email_enabled'); +enabled_chkbox.onclick = enable_redirection_chkbox; +enable_redirection_chkbox(); diff --git a/users/forms.py b/users/forms.py index 97fb3fc3..dac86d71 100644 --- a/users/forms.py +++ b/users/forms.py @@ -620,7 +620,7 @@ class EMailAddressForm(FormRevMixin, ModelForm): super(EMailAddressForm, self).__init__(*args, prefix=prefix, **kwargs) self.fields['local_part'].label = "Local part of the email" self.fields['local_part'].help_text = "Can't contain @" - + def clean_local_part(self): return self.cleaned_data.get('local_part').lower() @@ -634,18 +634,11 @@ class EmailSettingsForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) super(EmailSettingsForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['email'].label = "Contact email address" + self.fields['email'].label = "Main email address" if 'local_email_redirect' in self.fields: self.fields['local_email_redirect'].label = "Redirect local emails" - self.fields['local_email_redirect'].help_text = ( - "Enable the automated redirection of the local email address " - "to the contact email address" - ) if 'local_email_enabled' in self.fields: self.fields['local_email_enabled'].label = "Use local emails" - self.fields['local_email_enabled'].help_text = ( - "Enable the use of the local email account" - ) def clean_email(self): if not OptionalUser.objects.first().local_email_domain in self.cleaned_data.get('email'): @@ -655,4 +648,4 @@ class EmailSettingsForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): class Meta: model = User - fields = ['email', 'local_email_redirect', 'local_email_enabled'] + fields = ['email','local_email_enabled', 'local_email_redirect'] diff --git a/users/migrations/0074_auto_20180814_1059.py b/users/migrations/0074_auto_20180814_1059.py new file mode 100644 index 00000000..ced792f4 --- /dev/null +++ b/users/migrations/0074_auto_20180814_1059.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-08-14 08:59 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0073_auto_20180629_1614'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='email', + field=models.EmailField(blank=True, help_text='External email address allowing us to contact you.', max_length=254), + ), + migrations.AlterField( + model_name='user', + name='local_email_enabled', + field=models.BooleanField(default=False, help_text='Enable the local email account.'), + ), + migrations.AlterField( + model_name='user', + name='local_email_redirect', + field=models.BooleanField(default=False, help_text='Enable redirection of the local email messages to the main email.'), + ), + ] diff --git a/users/migrations/0076_auto_20180811_0425.py b/users/migrations/0076_auto_20180811_0425.py deleted file mode 100644 index 0b53af9a..00000000 --- a/users/migrations/0076_auto_20180811_0425.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2018-08-11 02:25 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('users', '0075_auto_20180811_0420'), - ] - - operations = [ - migrations.AlterField( - model_name='user', - name='email', - field=models.EmailField(max_length=254, unique=True), - ), - ] diff --git a/users/models.py b/users/models.py index ff596424..7331a460 100755 --- a/users/models.py +++ b/users/models.py @@ -195,14 +195,17 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, help_text="Doit contenir uniquement des lettres, chiffres, ou tirets", validators=[linux_user_validator] ) - email = models.EmailField(unique=True) + email = models.EmailField( + blank=True, + help_text="External email address allowing us to contact you." + ) local_email_redirect = models.BooleanField( default=False, - help_text="Whether or not to redirect the local email messages to the main email." + help_text="Enable redirection of the local email messages to the main email." ) local_email_enabled = models.BooleanField( default=False, - help_text="Wether or not to enable the local email account." + help_text="Enable the local email account." ) school = models.ForeignKey( 'School', @@ -565,7 +568,7 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, # depending on the length, we need to remove or not a $ if len(self.password)==41: user_ldap.user_password = self.password - else: + else: user_ldap.user_password = self.password[:7] + self.password[8:] user_ldap.sambat_nt_password = self.pwd_ntlm.upper() @@ -1771,7 +1774,7 @@ class EMailAddress(RevMixin, AclMixin, models.Model): if self.local_part == self.user.pseudo.lower(): return False, ("You cannot delete a local email account whose " "local part is the same as the username.") - if user_request.has_perm('users.delete_emailaddress'): + if user_request.has_perm('users.delete_emailaddress'): return True, None if not OptionalUser.get_cached_value('local_email_accounts_enabled'): return False, "The local email accounts are not enabled." diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index c2053612..f573941b 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -27,15 +27,16 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load acl %} {% load logs_extra %} {% load design %} +{% load i18n %} {% block title %}Profil{% endblock %} {% block content %}
{% if user == users %} -

Welcome {{ users.name }} {{ users.surname }}

+

Welcome {{ users.name }} {{ users.surname }}

{% else %} -

Profil de {{ users.name }} {{ users.surname }}

+

Profil de {{ users.name }} {{ users.surname }}

{% endif %}
@@ -74,7 +75,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
- {{ users.solde }} + {{ users.solde }}
diff --git a/users/templates/users/user.html b/users/templates/users/user.html index edd57ea1..5e4048a3 100644 --- a/users/templates/users/user.html +++ b/users/templates/users/user.html @@ -36,6 +36,9 @@ with this program; if not, write to the Free Software Foundation, Inc., {% massive_bootstrap_form userform 'room,school,administrators,members' %} {% bootstrap_button action_name button_type="submit" icon="star" %} +{% if load_js_file %} + +{% endif %}
{% if showCGU %}

En cliquant sur Créer ou modifier, l'utilisateur s'engage à respecter les règles d'utilisation du réseau.

diff --git a/users/views.py b/users/views.py index ca7438df..5279dbb0 100644 --- a/users/views.py +++ b/users/views.py @@ -541,7 +541,8 @@ def edit_emailaddress(request, emailaddress_instance, **_kwargs): return form( {'userform': emailaddress, 'showCGU': False, - 'action_name': 'Edit a local email account'}, + 'action_name': 'Edit a local email account', + }, 'users/user.html', request ) @@ -585,6 +586,7 @@ def edit_email_settings(request, user_instance, **_kwargs): return form( {'userform': email_settings, 'showCGU': False, + 'load_js_file': '/static/js/email_address.js', 'action_name': 'Edit the email settings'}, 'users/user.html', request @@ -1017,7 +1019,7 @@ def profil(request, users, **_kwargs): 'emailaddress_list': users.email_address, 'local_email_accounts_enabled': ( OptionalUser.objects.first().local_email_accounts_enabled - ) + ) } ) From 49585cade184655150eb92326f130eaea67196e7 Mon Sep 17 00:00:00 2001 From: Hugo LEVY-FALK Date: Tue, 14 Aug 2018 13:09:13 +0200 Subject: [PATCH 6/6] =?UTF-8?q?Ajoute=20la=20possibilit=C3=A9=20d'utiliser?= =?UTF-8?q?=20uniquement=20le=20compte=20mail=20local.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- users/migrations/0074_auto_20180814_1059.py | 2 +- users/models.py | 41 +++++++++++++++------ 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/users/migrations/0074_auto_20180814_1059.py b/users/migrations/0074_auto_20180814_1059.py index ced792f4..e3e8527f 100644 --- a/users/migrations/0074_auto_20180814_1059.py +++ b/users/migrations/0074_auto_20180814_1059.py @@ -15,7 +15,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='user', name='email', - field=models.EmailField(blank=True, help_text='External email address allowing us to contact you.', max_length=254), + field=models.EmailField(blank=True, null=True, help_text='External email address allowing us to contact you.', max_length=254), ), migrations.AlterField( model_name='user', diff --git a/users/models.py b/users/models.py index 7331a460..0fe2cd62 100755 --- a/users/models.py +++ b/users/models.py @@ -104,6 +104,7 @@ def linux_user_validator(login): params={'label': login}, ) + def get_fresh_user_uid(): """ Renvoie le plus petit uid non pris. Fonction très paresseuse """ uids = list(range( @@ -131,6 +132,7 @@ def get_fresh_gid(): class UserManager(BaseUserManager): """User manager basique de django""" + def _create_user( self, pseudo, @@ -197,6 +199,7 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, ) email = models.EmailField( blank=True, + null=True, help_text="External email address allowing us to contact you." ) local_email_redirect = models.BooleanField( @@ -563,13 +566,15 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, user_ldap.gid = LDAP['user_gid'] if '{SSHA}' in self.password or '{SMD5}' in self.password: # We remove the extra $ added at import from ldap - user_ldap.user_password = self.password[:6] + self.password[7:] + user_ldap.user_password = self.password[:6] + \ + self.password[7:] elif '{crypt}' in self.password: # depending on the length, we need to remove or not a $ - if len(self.password)==41: + if len(self.password) == 41: user_ldap.user_password = self.password else: - user_ldap.user_password = self.password[:7] + self.password[8:] + user_ldap.user_password = self.password[:7] + \ + self.password[8:] user_ldap.sambat_nt_password = self.pwd_ntlm.upper() if self.get_shell: @@ -614,7 +619,7 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, send_mail( 'Bienvenue au %(name)s / Welcome to %(name)s' % { 'name': AssoOption.get_cached_value('name') - }, + }, '', GeneralOption.get_cached_value('email_from'), [self.email], @@ -657,8 +662,8 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, une machine inconnue sur le compte de l'user""" all_interfaces = self.user_interfaces(active=False) if all_interfaces.count() > OptionalMachine.get_cached_value( - 'max_lambdauser_interfaces' - ): + 'max_lambdauser_interfaces' + ): return False, "Maximum de machines enregistrees atteinte" if not nas_type: return False, "Re2o ne sait pas à quel machinetype affecter cette\ @@ -961,7 +966,7 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, 'force': self.can_change_force, 'selfpasswd': self.check_selfpasswd, 'local_email_redirect': self.can_change_local_email_redirect, - 'local_email_enabled' : self.can_change_local_email_enabled, + 'local_email_enabled': self.can_change_local_email_enabled, } self.__original_state = self.state @@ -969,9 +974,22 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, """Check if this pseudo is already used by any mailalias. Better than raising an error in post-save and catching it""" if (EMailAddress.objects - .filter(local_part=self.pseudo.lower()).exclude(user_id=self.id) + .filter(local_part=self.pseudo.lower()).exclude(user_id=self.id) ): raise ValidationError("This pseudo is already in use.") + if not self.local_email_enabled and not self.email: + raise ValidationError( + {'email': ( + 'There is neither a local email address nor an external' + ' email address for this user.' + ), } + ) + if self.local_email_redirect and not self.email: + raise ValidationError( + {'local_email_redirect': ( + 'You cannot redirect your local email if no external email ' + 'has been set.'), } + ) def __str__(self): return self.pseudo @@ -1109,7 +1127,8 @@ def user_post_save(**kwargs): Synchronise le ldap""" is_created = kwargs['created'] user = kwargs['instance'] - EMailAddress.objects.get_or_create(local_part=user.pseudo.lower(), user=user) + EMailAddress.objects.get_or_create( + local_part=user.pseudo.lower(), user=user) if is_created: user.notif_inscription() user.state_sync() @@ -1132,6 +1151,7 @@ def user_group_relation_changed(**kwargs): mac_refresh=False, group_refresh=True) + @receiver(post_delete, sender=Adherent) @receiver(post_delete, sender=Club) @receiver(post_delete, sender=User) @@ -1520,7 +1540,7 @@ class Request(models.Model): hours=GeneralOption.get_cached_value( 'req_expire_hrs' ) - )) + )) if not self.token: self.token = str(uuid.uuid4()).replace('-', '') # remove hyphens super(Request, self).save() @@ -1810,4 +1830,3 @@ class EMailAddress(RevMixin, AclMixin, models.Model): if "@" in self.local_part: raise ValidationError("The local part cannot contain a @") super(EMailAddress, self).clean(*args, **kwargs) -