Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
  • 16 commits
  • 36 files changed
  • 0 commit comments
  • 4 contributors
Showing with 221 additions and 51 deletions.
  1. +6 −0 .coveragerc
  2. +3 −3 .travis.yml
  3. +1 −3 Makefile
  4. +6 −0 README.rst
  5. +4 −0 conftest.py
  6. +7 −0 pytest.ini
  7. +0 −3  test_project/requirements.txt
  8. 0  {userprofiles/tests → tests/test_accountverification}/__init__.py
  9. +16 −0 {userprofiles/contrib/accountverification/tests → tests/test_accountverification}/test_models.py
  10. +1 −1  {userprofiles/contrib/accountverification/tests → tests/test_accountverification}/test_views.py
  11. 0  {userprofiles → }/tests/test_auth_backends.py
  12. 0  {userprofiles/contrib/profiles/tests → tests/test_emailverification}/__init__.py
  13. +48 −0 tests/test_emailverification/test_models.py
  14. +24 −1 {userprofiles/contrib/emailverification/tests → tests/test_emailverification}/test_views.py
  15. 0  {userprofiles → }/tests/test_forms.py
  16. 0  {userprofiles/contrib/emailverification/tests → tests/test_profiles}/__init__.py
  17. +8 −0 {userprofiles/contrib/profiles/tests → tests/test_profiles}/test_views.py
  18. 0  {userprofiles/contrib/accountverification/tests → tests/test_project}/__init__.py
  19. 0  { → tests}/test_project/manage.py
  20. +5 −0 tests/test_project/requirements.txt
  21. +0 −4 { → tests}/test_project/settings.py
  22. 0  test_project/test_accounts/__init__.py → tests/test_project/templates/404.html
  23. 0  { → tests}/test_project/templates/base.html
  24. 0  {test_project → tests/test_project/test_accounts}/__init__.py
  25. 0  { → tests}/test_project/test_accounts/forms.py
  26. 0  { → tests}/test_project/test_accounts/models.py
  27. 0  { → tests}/test_project/urls.py
  28. 0  {userprofiles → }/tests/test_settings.py
  29. 0  {userprofiles → }/tests/test_utils.py
  30. 0  {userprofiles → }/tests/test_views.py
  31. +31 −0 tox.ini
  32. +16 −11 userprofiles/contrib/accountverification/models.py
  33. +2 −21 userprofiles/contrib/emailverification/forms.py
  34. +40 −3 userprofiles/contrib/emailverification/models.py
  35. +1 −0  userprofiles/contrib/emailverification/views.py
  36. +2 −1  userprofiles/settings.py
View
6 .coveragerc
@@ -0,0 +1,6 @@
+[run]
+branch = True
+omit = *test*
+
+[html]
+directory = tests_report/
View
6 .travis.yml
@@ -4,11 +4,11 @@ python:
- "2.7"
env:
- DJANGO=1.4.5
- - DJANGO=1.5
+ - DJANGO=1.5.1
install:
- pip install -q Django==$DJANGO --use-mirrors
- - pip install -q -r test_project/requirements.txt --use-mirrors
+ - pip install -q -r tests/test_project/requirements.txt --use-mirrors
- pip install -q -e . --use-mirrors
script:
- - make test
+ - py.test
View
4 Makefile
@@ -1,4 +1,2 @@
test:
- flake8 userprofiles --ignore=E501,E128
- coverage run --branch --source=userprofiles `which django-admin.py` test --settings=test_project.settings userprofiles
- coverage report --show-missing --omit=*test*
+ py.test
View
6 README.rst
@@ -32,6 +32,12 @@ Registration settings
`userprofiles.contrib.accountverification` to your `INSTALLED_APPS` in
order to enable the verification.
+`USERPROFILES_ACCOUNT_VERIFICATION_SEND_EMAIL_ON_CREATE`
+ Automatically send an email with the activation link after the registration
+ is done. If disabled, you have to use the `send_activation_email()` method
+ on the AccountVerification instance (which is created automatically when the
+ user is created), to send the activation email.
+
`USERPROFILES_ACCOUNT_VERIFICATION_DAYS`
Defines the amount of days a user has to activate his account. Defaults to
7.
View
4 conftest.py
@@ -0,0 +1,4 @@
+import os
+import sys
+
+sys.path.append(os.path.join(os.path.dirname(__file__), 'tests'))
View
7 pytest.ini
@@ -0,0 +1,7 @@
+[pytest]
+addopts = -v --tb=short --pep8 --flakes --cov=userprofiles/ --cov-report=term-missing --cov-report=html userprofiles/ tests/
+norecursedirs = test_project
+pep8ignore =
+ *.py E501 E128
+
+DJANGO_SETTINGS_MODULE = test_project.settings
View
3  test_project/requirements.txt
@@ -1,3 +0,0 @@
-django-discover-runner
-flake8
-coverage
View
0  userprofiles/tests/__init__.py → tests/test_accountverification/__init__.py
File renamed without changes
View
16 .../contrib/accountverification/tests/test_models.py → tests/test_accountverification/test_models.py
@@ -1,7 +1,9 @@
from datetime import timedelta
from django.contrib.auth.models import User
+from django.core import mail
from django.test import TestCase
+from django.test.utils import override_settings
from userprofiles.contrib.accountverification.models import AccountVerification
from userprofiles.settings import up_settings
@@ -59,3 +61,17 @@ def test_delete_expired_users(self):
user.save()
AccountVerification.objects.delete_expired_users()
self.assertFalse(User.objects.filter(pk=user.pk).exists())
+
+ def test_create_inactive_user_auto_email(self):
+ AccountVerification.objects.create_inactive_user(
+ self.data['username'], self.data['password'], self.data['email'])
+ self.assertEqual(len(mail.outbox), 1)
+
+ @override_settings(USERPROFILES_ACCOUNT_VERIFICATION_SEND_EMAIL_ON_CREATE=False)
+ def test_create_inactive_user_manual_email(self):
+ user = AccountVerification.objects.create_inactive_user(
+ self.data['username'], self.data['password'], self.data['email'])
+ self.assertEqual(len(mail.outbox), 0)
+
+ user.accountverification_set.get().send_activation_email()
+ self.assertEqual(len(mail.outbox), 1)
View
2  ...s/contrib/accountverification/tests/test_views.py → tests/test_accountverification/test_views.py
@@ -8,7 +8,7 @@
from userprofiles.contrib.accountverification.models import AccountVerification
-@override_settings(USE_ACCOUNT_VERIFICATION=True)
+@override_settings(USERPROFILES_USE_ACCOUNT_VERIFICATION=True)
class ViewTests(TestCase):
def setUp(self):
self.data = {
View
0  userprofiles/tests/test_auth_backends.py → tests/test_auth_backends.py
File renamed without changes
View
0  userprofiles/contrib/profiles/tests/__init__.py → tests/test_emailverification/__init__.py
File renamed without changes
View
48 tests/test_emailverification/test_models.py
@@ -0,0 +1,48 @@
+from django.core import mail
+from django.test import TestCase
+from django.contrib.auth import models as auth_models
+
+from userprofiles.contrib.emailverification import models
+
+
+class ModelTests(TestCase):
+ def setUp(self):
+ self.user = auth_models.User.objects.create(
+ username="test", password="test", email="old@test.com")
+
+ def tearDown(self):
+ models.EmailVerification.objects.all().delete()
+ self.user.delete()
+
+ def test_unicode(self):
+ """Unicode rendering should contain the user, the old email and the new email"""
+ rendering = unicode(models.EmailVerification(user=self.user, new_email="new@email.com", old_email="old@email.com"))
+ self.assertEquals(rendering, "test - old@email.com/new@email.com")
+
+ def test_get_pending(self):
+ """Tests that get_pending really only returns pending items."""
+ models.EmailVerification.objects.create_new_verification(
+ self.user, 'new@email.com')
+ pending = models.EmailVerification.objects.create_new_verification(
+ self.user, 'newer@email.com')
+ self.assertEquals(
+ list(models.EmailVerification.objects.get_pending(self.user)),
+ [pending])
+
+ def test_create_sends_mail(self):
+ """Tests that the create manager method sends an activation email."""
+ models.EmailVerification.objects.create_new_verification(
+ self.user, "test@test.com")
+ self.assertEquals(len(mail.outbox), 1)
+ self.assertEquals(mail.outbox[0].to, ["test@test.com"])
+
+ def test_old_expired(self):
+ """Tests that the creation of a new verification expired pending old
+ ones."""
+ first = models.EmailVerification.objects.create_new_verification(
+ self.user, "test@test.com")
+ second = models.EmailVerification.objects.create_new_verification(
+ self.user, "test@test.com")
+
+ self.assertTrue(models.EmailVerification.objects.get(pk=first.pk).is_expired)
+ self.assertFalse(models.EmailVerification.objects.get(pk=second.pk).is_expired)
View
25 ...les/contrib/emailverification/tests/test_views.py → tests/test_emailverification/test_views.py
@@ -9,7 +9,6 @@
@override_settings(USERPROFILES_USE_PROFILE=False, USERPROFILES_USE_EMAIL_VERIFICATION=True)
class ViewTests(TestCase):
-
def setUp(self):
self.data = {
'username': 'newuser',
@@ -87,3 +86,27 @@ def test_email_change_approve_not_exists(self):
self.assertEqual(response.status_code, 302)
self.assertEqual(User.objects.get(pk=self.user.pk).email, self.user.email)
+
+ def test_email_change_twice(self):
+ self.client.login(username=self.data['username'], password=self.data['password'])
+
+ self.client.post(reverse('userprofiles_email_change'),
+ {'new_email': 'test@example.com'}, follow=True)
+ self.assertEqual(EmailVerification.objects.filter(user=self.user,
+ is_expired=True).count(), 0) # No expired requests
+ self.assertEqual(EmailVerification.objects.filter(user=self.user,
+ is_expired=False).count(), 1) # One active request
+
+ self.client.post(reverse('userprofiles_email_change'),
+ {'new_email': 'test2@example.com'}, follow=True)
+ self.assertEqual(EmailVerification.objects.filter(user=self.user,
+ is_expired=True).count(), 1) # One expired request
+ self.assertEqual(EmailVerification.objects.filter(user=self.user,
+ is_expired=False).count(), 1) # One active request
+
+ self.client.post(reverse('userprofiles_email_change'),
+ {'new_email': 'test3@example.com'}, follow=True)
+ self.assertEqual(EmailVerification.objects.filter(user=self.user,
+ is_expired=True).count(), 2) # Two expired requests
+ self.assertEqual(EmailVerification.objects.filter(user=self.user,
+ is_expired=False).count(), 1) # One active request
View
0  userprofiles/tests/test_forms.py → tests/test_forms.py
File renamed without changes
View
0  ...files/contrib/emailverification/tests/__init__.py → tests/test_profiles/__init__.py
File renamed without changes
View
8 userprofiles/contrib/profiles/tests/test_views.py → tests/test_profiles/test_views.py
@@ -3,6 +3,9 @@
from django.test import TestCase
from django.test.utils import override_settings
+from userprofiles.contrib.profiles.forms import ProfileForm
+from userprofiles.utils import get_profile_model
+
from test_project.test_accounts.models import Profile
@@ -22,6 +25,11 @@ def setUp(self):
self.data['password'])
Profile(user=self.user).save()
+ # Because of the settings change, the cached model in the ModelForm
+ # is None, this only happens because the AUTH_PROFILE_MODULE is None
+ # when starting the tests. We overwrite this attribute.
+ ProfileForm._meta.model = get_profile_model()
+
def tearDown(self):
User.objects.get(username=self.data['username']).delete()
View
0  ...les/contrib/accountverification/tests/__init__.py → tests/test_project/__init__.py
File renamed without changes
View
0  test_project/manage.py → tests/test_project/manage.py
File renamed without changes
View
5 tests/test_project/requirements.txt
@@ -0,0 +1,5 @@
+pytest==2.3.4
+pytest-cov==1.6
+pytest-django==2.3.0
+pytest-flakes==0.1
+pytest-pep8==1.0.4
View
4 test_project/settings.py → tests/test_project/settings.py
@@ -1,9 +1,5 @@
import os
-TEST_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__),
- '..', 'userprofiles', 'tests'))
-
-TEST_RUNNER = 'discover_runner.DiscoverRunner'
DATABASES = {
'default': {
View
0  test_project/test_accounts/__init__.py → tests/test_project/templates/404.html
File renamed without changes
View
0  test_project/templates/base.html → tests/test_project/templates/base.html
File renamed without changes
View
0  test_project/__init__.py → tests/test_project/test_accounts/__init__.py
File renamed without changes
View
0  test_project/test_accounts/forms.py → tests/test_project/test_accounts/forms.py
File renamed without changes
View
0  test_project/test_accounts/models.py → tests/test_project/test_accounts/models.py
File renamed without changes
View
0  test_project/urls.py → tests/test_project/urls.py
File renamed without changes
View
0  userprofiles/tests/test_settings.py → tests/test_settings.py
File renamed without changes
View
0  userprofiles/tests/test_utils.py → tests/test_utils.py
File renamed without changes
View
0  userprofiles/tests/test_views.py → tests/test_views.py
File renamed without changes
View
31 tox.ini
@@ -0,0 +1,31 @@
+[tox]
+envlist =
+ django1.4-py27, django1.5-py27, django1.6-py27
+
+indexserver =
+ default = https://simple.crate.io
+downloadcache = ~/.pip/cache/
+
+[testenv]
+deps =
+ -r{toxinidir}/tests/test_project/requirements.txt
+commands =
+ py.test
+
+[testenv:django1.4-py27]
+basepython = python2.7
+deps =
+ Django==1.4.5
+ {[testenv]deps}
+
+[testenv:django1.5-py27]
+basepython = python2.7
+deps =
+ Django==1.5.1
+ {[testenv]deps}
+
+[testenv:django1.6-py27]
+basepython = python2.7
+deps =
+ https://github.com/django/django/zipball/master
+ {[testenv]deps}
View
27 userprofiles/contrib/accountverification/models.py
@@ -45,18 +45,9 @@ def create_inactive_user(self, username, password, email):
new_user.save()
account_verification = self.create_verification(new_user)
- current_site = Site.objects.get_current()
-
- subject = ''.join(render_to_string(
- 'userprofiles/mails/activation_email_subject.html',
- {'site': current_site}).splitlines())
- message = render_to_string('userprofiles/mails/activation_email.html', {
- 'activation_key': account_verification.activation_key,
- 'expiration_days': up_settings.ACCOUNT_VERIFICATION_DAYS,
- 'site': current_site})
-
- send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [new_user.email])
+ if up_settings.ACCOUNT_VERIFICATION_SEND_EMAIL_ON_CREATE:
+ account_verification.send_activation_email()
return new_user
@@ -84,6 +75,20 @@ class AccountVerification(models.Model):
def __unicode__(self):
return u'Account verification: %s' % self.user
+ def send_activation_email(self):
+ current_site = Site.objects.get_current()
+
+ subject = ''.join(render_to_string(
+ 'userprofiles/mails/activation_email_subject.html',
+ {'site': current_site}).splitlines())
+
+ message = render_to_string('userprofiles/mails/activation_email.html', {
+ 'activation_key': self.activation_key,
+ 'expiration_days': up_settings.ACCOUNT_VERIFICATION_DAYS,
+ 'site': current_site})
+
+ send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [self.user.email])
+
def activation_key_expired(self):
expiration_date = timedelta(days=up_settings.ACCOUNT_VERIFICATION_DAYS)
return (self.activation_key == self.ACTIVATED
View
23 userprofiles/contrib/emailverification/forms.py
@@ -1,10 +1,6 @@
# -*- coding: utf-8 -*-
from django import forms
-from django.conf import settings
from django.contrib.auth.models import User
-from django.contrib.sites.models import Site
-from django.core.mail import send_mail
-from django.template.loader import render_to_string
from django.utils.translation import ugettext_lazy as _
from userprofiles.contrib.emailverification.models import EmailVerification
@@ -26,20 +22,5 @@ def clean_new_email(self):
return new_email
def save(self, user):
- verification = EmailVerification.objects.create(user=user,
- old_email=user.email, new_email=self.cleaned_data['new_email'])
-
- context = {
- 'user': user,
- 'verification': verification,
- 'site': Site.objects.get_current(),
- }
-
- subject = ''.join(render_to_string(
- 'userprofiles/mails/emailverification_subject.html', context).splitlines())
- body = render_to_string('userprofiles/mails/emailverification.html', context)
-
- send_mail(subject, body, settings.DEFAULT_FROM_EMAIL,
- [self.cleaned_data['new_email']])
-
- return verification
+ return EmailVerification.objects.create_new_verification(
+ user, self.cleaned_data['new_email'])
View
43 userprofiles/contrib/emailverification/models.py
@@ -2,11 +2,16 @@
from datetime import timedelta
import uuid
+from django.conf import settings
from django.contrib.auth.models import User
+from django.contrib.sites.models import Site
+from django.core.mail import send_mail
from django.db import models
+from django.template.loader import render_to_string
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
+
from userprofiles.settings import up_settings
@@ -18,6 +23,35 @@ def generate_confirm_expire_date():
return timezone.now() + timedelta(days=up_settings.EMAIL_VERIFICATION_DAYS)
+class EmailVerificationManager(models.Manager):
+
+ def create_new_verification(self, user, new_email):
+ """Creates a new verification and sends the respective e-mail."""
+
+ verification = self.create(user=user, old_email=user.email,
+ new_email=new_email)
+
+ context = {
+ 'user': user,
+ 'verification': verification,
+ 'site': Site.objects.get_current(),
+ }
+
+ subject = ''.join(render_to_string(
+ 'userprofiles/mails/emailverification_subject.html', context).splitlines())
+ body = render_to_string('userprofiles/mails/emailverification.html', context)
+
+ send_mail(subject, body, settings.DEFAULT_FROM_EMAIL, [new_email])
+
+ return verification
+
+ def get_pending(self, user):
+ """Returns a queryset for all the still pending verifications of the
+ given user. This should return either one or no element."""
+ return self.get_query_set().filter(
+ user=user, is_approved=False, is_expired=False)
+
+
class EmailVerification(models.Model):
user = models.ForeignKey(User, verbose_name=_('User'), blank=False)
old_email = models.EmailField(_('Old e-mail address'))
@@ -32,19 +66,22 @@ class EmailVerification(models.Model):
expiration_date = models.DateTimeField(_('Expiration date'),
default=generate_confirm_expire_date)
+ objects = EmailVerificationManager()
+
def __unicode__(self):
return '%s - %s/%s' % (self.user, self.old_email, self.new_email)
def save(self, *args, **kwargs):
- if self.is_approved:
- EmailVerification.objects.filter(
- user=self.user, is_approved=False).update(is_expired=True)
+ EmailVerification.objects.exclude(pk=self.pk).filter(user=self.user,
+ is_approved=False).update(is_expired=True)
+ if self.is_approved:
self.is_expired = True
if self.user.email == self.old_email:
self.user.email = self.new_email
self.user.save()
+
return super(EmailVerification, self).save(*args, **kwargs)
class Meta:
View
1  userprofiles/contrib/emailverification/views.py
@@ -50,6 +50,7 @@ def get_redirect_url(self, token, code):
except EmailVerification.DoesNotExist:
messages.error(self.request,
_(u'Unable to change e-mail address. Confirmation link is invalid.'))
+ return reverse('userprofiles_email_change')
return reverse(up_settings.EMAIL_VERIFICATION_DONE_URL)
View
3  userprofiles/settings.py
@@ -30,6 +30,7 @@ def __getattr__(self, key):
USE_ACCOUNT_VERIFICATION=False,
ACCOUNT_VERIFICATION_DAYS=7,
+ ACCOUNT_VERIFICATION_SEND_EMAIL_ON_CREATE=True,
USE_EMAIL_VERIFICATION=False,
EMAIL_VERIFICATION_DAYS=2,
@@ -73,7 +74,7 @@ def validate_settings():
'USERPROFILES_PROFILE_ALLOW_EMAIL_CHANGE cannot be activated '
'when USERPROFILES_USE_EMAIL_VERIFICATION is activated.')
- if ('test' not in sys.argv and not up_settings.USE_EMAIL_VERIFICATION and
+ if ('test' not in sys.argv and 'py.test' not in sys.argv[0] and not up_settings.USE_EMAIL_VERIFICATION and
'userprofiles.contrib.emailverification' in settings.INSTALLED_APPS):
raise ImproperlyConfigured('You need to set USERPROFILES_USE_EMAIL_VERIFICATION '
'to use `userprofiles.contrib.emailverification`')

No commit comments for this range

Something went wrong with that request. Please try again.