Skip to content

Commit

Permalink
Sync Profile and EmailAddress models
Browse files Browse the repository at this point in the history
Changing the email address of a user changes his primary address and syncs with EmailAddress model and vice versa, changing primary email address of a user from the admin updates his main email field.

Closes #187
  • Loading branch information
nemesifier committed Feb 6, 2015
1 parent b2ed72a commit 4116e7d
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 113 deletions.
8 changes: 2 additions & 6 deletions nodeshot/community/profiles/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,6 @@ class UserAdmin(BaseUserAdmin):
ordering = ['-is_staff', '-date_joined']
search_fields = ('email', 'username', 'first_name', 'last_name')
list_filter = ('is_active', 'is_staff', 'is_superuser')

if EMAIL_CONFIRMATION:
readonly_fields = ['email']

form = UserChangeForm
add_form = UserCreationForm
change_password_form = AdminPasswordChangeForm
Expand Down Expand Up @@ -113,10 +109,10 @@ class EmailAddressAdmin(admin.ModelAdmin):
search_fields = ('email', 'user__username')
list_select_related = True
list_display = ('__unicode__', 'verified', 'primary', 'user')

class EmailConfirmationAdmin(admin.ModelAdmin):
list_display = ('__unicode__', 'key_expired')

admin.site.register((EmailAddress,), EmailAddressAdmin)
admin.site.register((EmailConfirmation,), EmailConfirmationAdmin)

Expand Down
22 changes: 19 additions & 3 deletions nodeshot/community/profiles/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@
from .profile import Profile
from .social_link import SocialLink
from .password_reset import PasswordReset
from .emailconfirmation import *

__all__ = ['Profile', 'SocialLink', 'PasswordReset']
from .emailconfirmation import EmailAddress, EmailConfirmation

__all__ = [
'Profile',
'SocialLink',
'PasswordReset',
'EmailAddress',
'EmailConfirmation'
]

# ------ SIGNALS ------ #

Expand Down Expand Up @@ -40,6 +45,17 @@ def new_user(sender, **kwargs):
if EMAIL_CONFIRMATION:
from ..signals import email_confirmed

@receiver(post_save, sender=Profile)
def add_mail_to_user(sender, **kwargs):
"""
Adds email to DB and sends a confirmation mail if PROFILE_EMAL_CONFIRMATION is True
"""
user = kwargs['instance']
# sync with EmailAddress model
if user.email and user.email_set.filter(email=user.email).count() < 1:
user.email_set.add_email(user, email=user.email)
user.email_set.last().set_as_primary()

@receiver(email_confirmed, sender=EmailConfirmation)
def activate_user(sender, email_address, **kwargs):
"""
Expand Down
28 changes: 18 additions & 10 deletions nodeshot/community/profiles/models/emailconfirmation.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,7 @@ def get_users_for(self, email):
"""
# this is a list rather than a generator because we probably want to
# do a len() on it right away
return [address.user for address in EmailAddress.objects.filter(
verified=True, email=email)]
return [address.user for address in EmailAddress.objects.filter(verified=True, email=email)]


class EmailAddress(models.Model):
Expand All @@ -65,24 +64,33 @@ class EmailAddress(models.Model):
primary = models.BooleanField(default=False)

objects = EmailAddressManager()

def clean(self):
try:
if self.pk and not self.primary and EmailAddress.objects.filter(user=self.user, primary=True).first().pk == self.pk:
if self.pk and not self.primary and self.user.email_set.get_primary().pk == self.pk:
raise ValidationError(_("You must have at least one primary email address."))
except IndexError:
pass

def save(self, *args, **kwargs):
# if this is not the only primary email address for this user
if self.primary and EmailAddress.objects.filter(user=self.user, primary=True).exclude(pk=self.pk).count() > 0:
set_as_primary = False
# if primary is True and this is not the only primary email address for this user
if self.primary and self.user.email_set.filter(primary=True).exclude(pk=self.pk).count() > 0:
set_as_primary = True
# if primary is not true but no primary email addresses for this user
if not self.primary and self.user.email_set.filter(primary=True).count() < 1:
set_as_primary = True
# if adding a new primary address from the admin
if not self.pk and self.primary:
set_as_primary = True
super(EmailAddress, self).save(*args, **kwargs)
if set_as_primary:
# ensure there's only 1 primary address
self.set_as_primary()
super(EmailAddress, self).save(*args, **kwargs)

def set_as_primary(self):
old_primary = EmailAddress.objects.get_primary(self.user)
if old_primary:
old_primaries = self.user.email_set.filter(primary=True).exclude(pk=self.pk)
for old_primary in old_primaries:
old_primary.primary = False
old_primary.save()
self.primary = True
Expand Down
6 changes: 2 additions & 4 deletions nodeshot/community/profiles/models/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,13 @@ def email_user(self, subject, message, from_email=None):
"""
send_mail(subject, message, from_email, [self.email])

def add_email(self):
def needs_confirmation(self):
"""
Add email to DB and sends a confirmation mail if PROFILE_EMAL_CONFIRMATION is True
set is_active to False if needs email confirmation
"""
if EMAIL_CONFIRMATION:
from . import EmailAddress
self.is_active = False
self.save()
EmailAddress.objects.add_email(self, self.email)
return True
else:
return False
Expand Down
16 changes: 8 additions & 8 deletions nodeshot/community/profiles/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
class LoginSerializer(serializers.Serializer):
username = serializers.CharField(max_length=User._meta.get_field('username').max_length)
password = serializers.CharField(max_length=PASSWORD_MAX_LENGTH)
remember = serializers.BooleanField(default=True, help_text = _("If checked you will stay logged in for 3 weeks"))
remember = serializers.BooleanField(default=True, help_text=_("If checked you will stay logged in for 3 weeks"))

def user_credentials(self, attrs):
"""
Expand Down Expand Up @@ -129,11 +129,11 @@ class Meta:

fields += ['social_links_url', 'social_links']

read_only_fields = (
read_only_fields = [
'username',
'date_joined',
'last_login'
)
]


class ProfileOwnSerializer(ProfileSerializer):
Expand Down Expand Up @@ -242,11 +242,11 @@ class ChangePasswordSerializer(serializers.Serializer):
required=False # optional because users subscribed from social network won't have a password set
)
password1 = serializers.CharField(
help_text = _('New Password'),
help_text=_('New Password'),
max_length=PASSWORD_MAX_LENGTH
)
password2 = serializers.CharField(
help_text = _('New Password (confirmation)'),
help_text=_('New Password (confirmation)'),
max_length=PASSWORD_MAX_LENGTH
)

Expand Down Expand Up @@ -281,7 +281,7 @@ def restore_object(self, attrs, instance=None):


class ResetPasswordSerializer(serializers.Serializer):
email = serializers.EmailField(required = True)
email = serializers.EmailField(required=True)

def validate_email(self, attrs, source):
""" ensure email is in the database """
Expand All @@ -305,11 +305,11 @@ def restore_object(self, attrs, instance=None):

class ResetPasswordKeySerializer(serializers.Serializer):
password1 = serializers.CharField(
help_text = _('New Password'),
help_text=_('New Password'),
max_length=PASSWORD_MAX_LENGTH
)
password2 = serializers.CharField(
help_text = _('New Password (confirmation)'),
help_text=_('New Password (confirmation)'),
max_length=PASSWORD_MAX_LENGTH
)

Expand Down

0 comments on commit 4116e7d

Please sign in to comment.