Skip to content

Commit

Permalink
Merge 8cd3e7a into 669747b
Browse files Browse the repository at this point in the history
  • Loading branch information
imposeren committed Aug 14, 2015
2 parents 669747b + 8cd3e7a commit 426adef
Show file tree
Hide file tree
Showing 5 changed files with 183 additions and 3 deletions.
56 changes: 55 additions & 1 deletion docs/source/signup_tweaks.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ to take some additional steps:
* You're done. Upon registration user will be notified he needs to confirm his email address.

An email with account activation link will be sent by **django-sitemessage**.
An email with account activation link will be sent by **django-sitemessage** or by **django.core.mail.send_mail** when django-sitemessage is not available


.. note::
Expand All @@ -55,3 +55,57 @@ to take some additional steps:

See :ref:`Preferences <email-prefs>` chapter.



Sending confirmation email for changed emails
---------------------------------------------

View for email confirmation can also be used to confirm email changes. Django-sitegate does not provide
means to actually initiate email change so it's up to you to actually validate new emails, to generate required ``EmailConfirmation`` instance, tpgenerate activation url, to send email with url.

Example of generating url for included confirmation view:

.. code-block:: python
from sitegate.models import EmailConfirmation
from sitegate.settings import SIGNUP_VERIFY_EMAIL_VIEW_NAME
from django.contrib.auth import get_user_model
some_user = get_user_model().objects.all().first()
code = EmailConfirmation.add(user=some_user, new_email='other_email@domain.com')
email_confirmation_url = request.build_absolute_uri(reverse(SIGNUP_VERIFY_EMAIL_VIEW_NAME, args=(code.code,)))
# now you can generate some email with this url and sendit to new email of user.
.. warning::

This approach does not send anything to old email! It only ensures that new email is valid.
So if someone will gain access to authorised user session on site then he or she will be able to change
email without access to old mailbox.


If **settings.SIGNUP_VERIFY_EMAIL_ALLOW_DUPLICATES** is ``False`` (the default) then confirmation view will fail
if user tries to change email to email of exising user. But it's better to perform this check in your email change form.
Current confirmation view provides same messages for registration confirmation and email change confirmation. You can create your own confirmation view, example:

.. code-block:: python
def custom_verify_email(request, code, redirect_to=None):
success = False
valid_code = EmailConfirmation.is_valid(code)
if valid_code and valid_code.user.is_active: # also verify that user is already activated
valid_code.activate()
success = True
if success:
messages.success(request, _("Change of email confirmed"), 'success')
else:
messages.error(request, SIGNUP_VERIFY_EMAIL_ERROR_TEXT, 'danger error')
if redirect_to is None:
redirect_to = '/'
return redirect(redirect_to)
19 changes: 19 additions & 0 deletions sitegate/migrations/0002_emailconfirmation_new_email.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


class Migration(migrations.Migration):

dependencies = [
('sitegate', '0001_initial'),
]

operations = [
migrations.AddField(
model_name='emailconfirmation',
name='new_email',
field=models.EmailField(default=None, max_length=254, null=True, verbose_name='email address', blank=True),
),
]
23 changes: 21 additions & 2 deletions sitegate/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import python_2_unicode_compatible

from .settings import SIGNUP_VERIFY_EMAIL_ALLOW_DUPLICATES
from etc.models import InheritedModel


Expand Down Expand Up @@ -106,6 +107,7 @@ def accept(cls, code, acceptor):
class EmailConfirmation(InheritedModel, ModelWithCode):

user = models.ForeignKey(USER_MODEL, verbose_name=_('User'))
new_email = models.EmailField(_('email address'), blank=True, null=True, default=None)

class Meta(object):
verbose_name = _('Activation code')
Expand All @@ -116,8 +118,8 @@ class Fields(object):
expired = {'help_text': _('Expired codes couldn\'t be used for repeated account activations.')}

@classmethod
def add(cls, user):
new_code = cls(user=user)
def add(cls, user, new_email=None):
new_code = cls(user=user, new_email=new_email)
new_code.save(force_insert=True)
return new_code

Expand All @@ -127,5 +129,22 @@ def activate(self):
self.save()

user = self.user
if self.new_email is not None:
user.email = self.new_email
user.is_active = True
user.save()

@classmethod
def is_valid(cls, code):
code = super(EmailConfirmation, cls).is_valid(code)
if not code or code.new_email is None:
return code

if not SIGNUP_VERIFY_EMAIL_ALLOW_DUPLICATES:
code = code
if USER_MODEL.objects.filter(email=code.new_email).exclude(pk=code.user_id).exists():
return False
else:
return code
else:
return code
1 change: 1 addition & 0 deletions sitegate/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
_('Unable to verify an e-mail. User account was not activated.'))

SIGNUP_VERIFY_EMAIL_VIEW_NAME = getattr(settings, 'SITEGATE_SIGNUP_VERIFY_EMAIL_VIEW_NAME', 'verify_email')
SIGNUP_VERIFY_EMAIL_ALLOW_DUPLICATES = getattr(settings, 'SIGNUP_VERIFY_EMAIL_ALLOW_DUPLICATES', False)

try:
from siteprefs.toolbox import patch_locals, register_prefs, pref, pref_group
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# -*- coding: utf-8 -*-
from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models


class Migration(SchemaMigration):

def forwards(self, orm):
# Adding field 'EmailConfirmation.new_email'
db.add_column(u'sitegate_emailconfirmation', 'new_email',
self.gf('django.db.models.fields.EmailField')(default=None, max_length=75, null=True, blank=True),
keep_default=False)


def backwards(self, orm):
# Deleting field 'EmailConfirmation.new_email'
db.delete_column(u'sitegate_emailconfirmation', 'new_email')


models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
u'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
u'sitegate.blacklisteddomain': {
'Meta': {'object_name': 'BlacklistedDomain'},
'domain': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '253'}),
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
u'sitegate.emailconfirmation': {
'Meta': {'object_name': 'EmailConfirmation'},
'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
'expired': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'new_email': ('django.db.models.fields.EmailField', [], {'default': 'None', 'max_length': '75', 'null': 'True', 'blank': 'True'}),
'time_accepted': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'time_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
},
u'sitegate.invitationcode': {
'Meta': {'object_name': 'InvitationCode'},
'acceptor': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'acceptors'", 'null': 'True', 'to': u"orm['auth.User']"}),
'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
'creator': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'creators'", 'to': u"orm['auth.User']"}),
'expired': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'time_accepted': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'time_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
}
}

complete_apps = ['sitegate']

0 comments on commit 426adef

Please sign in to comment.