[PATCH 2/5] Allow changing primary email address for accounts
Bernhard Reutner-Fischer
rep.dot.nop at gmail.com
Wed Nov 11 07:53:10 AEDT 2015
Signed-off-by: Bernhard Reutner-Fischer <rep.dot.nop at gmail.com>
---
docs/TODO | 1 -
patchwork/forms.py | 14 +++++++
patchwork/models.py | 3 ++
patchwork/templates/patchwork/profile.html | 18 ++++++++-
patchwork/templates/patchwork/user-data.html | 34 ++++++++++++++++
patchwork/tests/test_user.py | 60 ++++++++++++++++++++++++++++
patchwork/urls.py | 1 +
patchwork/views/user.py | 42 ++++++++++++++++++-
8 files changed, 170 insertions(+), 3 deletions(-)
create mode 100644 patchwork/templates/patchwork/user-data.html
diff --git a/docs/TODO b/docs/TODO
index 17d8cd1..b020afd 100644
--- a/docs/TODO
+++ b/docs/TODO
@@ -13,4 +13,3 @@
* pwclient: integrate hash parser
* pwclient: add example scripts (eg, git post-commit hook) to online help
* pwclient: case-insensitive searches for author and project name
-* changing primary email addresses for accounts
diff --git a/patchwork/forms.py b/patchwork/forms.py
index 0327958..76b8f2d 100644
--- a/patchwork/forms.py
+++ b/patchwork/forms.py
@@ -53,6 +53,20 @@ class RegistrationForm(forms.Form):
def clean(self):
return self.cleaned_data
+class UpdateUserForm(forms.ModelForm):
+ class Meta:
+ model = User
+ fields = ['first_name', 'last_name', 'email']
+
+ # Can't easily reuse (parts of) RegistrationForm with current django :(
+ def clean_email(self):
+ value = self.cleaned_data['email']
+ try:
+ user = User.objects.get(email__iexact = value)
+ except User.DoesNotExist:
+ return self.cleaned_data['email']
+ raise forms.ValidationError('This email address is already in use\n')
+
class LoginForm(forms.Form):
username = forms.CharField(max_length = 30)
password = forms.CharField(widget = forms.PasswordInput)
diff --git a/patchwork/models.py b/patchwork/models.py
index a2b9498..d8690b7 100644
--- a/patchwork/models.py
+++ b/patchwork/models.py
@@ -558,6 +558,9 @@ class EmailConfirmation(models.Model):
date = models.DateTimeField(default=datetime.datetime.now)
active = models.BooleanField(default=True)
+ def __unicode__(self):
+ return u'%s, %s: %s' % (self.user.username, self.type, self.email)
+
def deactivate(self):
self.active = False
self.save()
diff --git a/patchwork/templates/patchwork/profile.html b/patchwork/templates/patchwork/profile.html
index 116d6d6..133a5a1 100644
--- a/patchwork/templates/patchwork/profile.html
+++ b/patchwork/templates/patchwork/profile.html
@@ -116,8 +116,24 @@ address.</p>
<div class="box">
-<h2>Settings</h2>
+<h2>User data</h2>
+<form action="{% url 'patchwork.views.user.userdata' user_id=user.id %}"
+ method="post">
+ {% csrf_token %}
+ <table class="form">
+ {{ userform }}
+ <tr>
+ <td/>
+ <td>
+ <input type="submit" value="Apply"/>
+ </td>
+ </tr>
+ </table>
+</form>
+</div>
+<div class="box">
+<h2>Settings</h2>
<form method="post">
{% csrf_token %}
<table class="form">
diff --git a/patchwork/templates/patchwork/user-data.html b/patchwork/templates/patchwork/user-data.html
new file mode 100644
index 0000000..a1d0e05
--- /dev/null
+++ b/patchwork/templates/patchwork/user-data.html
@@ -0,0 +1,34 @@
+{% extends "base.html" %}
+
+{% block title %}User Profile: {{ user.username }}{% endblock %}
+{% block heading %}User Profile: {{ user.username }}{% endblock %}
+
+{% block body %}
+
+<div class="leftcol">
+</div>
+
+<div class="rightcol">
+
+<div class="box">
+<h2>User data</h2>
+<form action="{% url 'patchwork.views.user.userdata' user_id=user.id %}"
+ method="post">
+ {% csrf_token %}
+ <table class="form">
+ {{ userform }}
+ <tr>
+ <td/>
+ <td>
+ <input type="submit" value="Apply"/>
+ </td>
+ </tr>
+ </table>
+</form>
+</div>
+
+</div>
+
+<p style="clear: both"></p>
+
+{% endblock %}
diff --git a/patchwork/tests/test_user.py b/patchwork/tests/test_user.py
index ab46126..9bc1214 100644
--- a/patchwork/tests/test_user.py
+++ b/patchwork/tests/test_user.py
@@ -176,6 +176,66 @@ class UserProfileTest(TestCase):
user_profile = UserProfile.objects.get(user=self.user.user.id)
self.assertEquals(user_profile.patches_per_page, old_ppp)
+ def testUserProfileUserData(self):
+ user = User.objects.get(id=self.user.user.id)
+ old_fn = user.first_name
+ old_ln = user.last_name
+ old_email = user.email
+ new_fn = u'new_firstname'
+ new_ln = u'new_lastname'
+ new_email = u'new-' + old_email
+
+ # setup appropriate EmailConfirmation for registration + userperson
+ conf = EmailConfirmation(type='registration',
+ email=user.email,
+ user=user)
+ conf.save()
+ self.client.get(_confirmation_url(conf))
+ self.client.post('/user/link/', {'email': self.user.secondary_email})
+ conf = EmailConfirmation.objects.get(email=self.user.secondary_email)
+ self.assertEqual(conf.type, 'userperson')
+ self.client.get(_confirmation_url(conf))
+ confs = EmailConfirmation.objects.filter(user_id=user.id) \
+ .values_list('email', flat=True)
+ self.assertEqual(confs.count(), 2)
+ self.assertIn(self.user.email, confs)
+ self.assertIn(self.user.secondary_email, confs)
+
+ response = self.client.post('/user/userdata/%d/' % (user.id), {
+ 'email': new_email,
+ 'first_name': new_fn,
+ 'last_name': new_ln})
+
+ user = User.objects.get(id=user.id)
+ self.assertRedirects(response, '/user/')
+ self.assertEqual(user.email, new_email)
+ self.assertEqual(user.first_name, new_fn)
+ self.assertEqual(user.last_name, new_ln)
+ confs = EmailConfirmation.objects.filter(user_id=user.id) \
+ .values_list('email', flat=True)
+ self.assertEqual(confs.count(), 2)
+ self.assertIn(new_email, confs)
+ self.assertIn(self.user.secondary_email, confs)
+
+ # Now set the formerly secondary addr as the new primary.
+ new_email = self.user.secondary_email
+ response = self.client.post('/user/userdata/%d/' % (user.id), {
+ 'email': new_email,
+ 'first_name': new_fn,
+ 'last_name': new_ln})
+
+ user = User.objects.get(id=user.id)
+ self.assertRedirects(response, '/user/')
+ self.assertEqual(user.email, new_email)
+ self.assertEqual(user.first_name, new_fn)
+ self.assertEqual(user.last_name, new_ln)
+ # The secondary addr should now be registration (not userperson)
+ # and should be the only confirmation -- the userperson should
+ # have been deleted
+ conf = EmailConfirmation.objects.get(user_id=user.id)
+ self.assertEqual(conf.email, new_email)
+ self.assertEqual(conf.type, 'registration')
+
class UserPasswordChangeTest(TestCase):
user = None
diff --git a/patchwork/urls.py b/patchwork/urls.py
index d9da7db..561cf98 100644
--- a/patchwork/urls.py
+++ b/patchwork/urls.py
@@ -46,6 +46,7 @@ urlpatterns = patterns('',
(r'^user/link/$', 'patchwork.views.user.link'),
(r'^user/unlink/(?P<person_id>[^/]+)/$', 'patchwork.views.user.unlink'),
+ (r'^user/userdata/(?P<user_id>[^/]+)/$', 'patchwork.views.user.userdata'),
# password change
url(r'^user/password-change/$', auth_views.password_change,
diff --git a/patchwork/views/user.py b/patchwork/views/user.py
index 126ecc9..533a02a 100644
--- a/patchwork/views/user.py
+++ b/patchwork/views/user.py
@@ -27,7 +27,7 @@ from django.http import HttpResponseRedirect
from patchwork.models import Project, Bundle, Person, EmailConfirmation, \
State, EmailOptout
from patchwork.forms import UserProfileForm, UserPersonLinkForm, \
- RegistrationForm
+ RegistrationForm, UpdateUserForm
from patchwork.filters import DelegateFilter
from patchwork.views import generic_list
from django.template.loader import render_to_string
@@ -99,14 +99,20 @@ def profile(request):
if request.method == 'POST':
form = UserProfileForm(instance = request.user.profile,
data = request.POST)
+ userform = UpdateUserForm(instance = request.user,
+ data = request.POST)
if form.is_valid():
form.save()
+ if userform.is_valid():
+ userform.save()
else:
form = UserProfileForm(instance = request.user.profile)
+ userform = UpdateUserForm(instance = request.user)
context.project = request.user.profile.primary_project
context['bundles'] = Bundle.objects.filter(owner = request.user)
context['profileform'] = form
+ context['userform'] = userform
optout_query = '%s.%s IN (SELECT %s FROM %s)' % (
Person._meta.db_table,
@@ -178,6 +184,40 @@ def unlink(request, person_id):
url = django.core.urlresolvers.reverse('patchwork.views.user.profile')
return HttpResponseRedirect(url)
+ at login_required
+def userdata(request, user_id):
+ if request.method == 'POST':
+ user = request.user
+ old_email = user.email
+ form = UpdateUserForm(request.POST, instance = user)
+ if form.is_valid():
+ email = form.clean_email()
+ form.save()
+ conf = EmailConfirmation.objects.get(user_id = user_id,
+ type = 'registration',
+ email = old_email)
+ dups = EmailConfirmation.objects.filter(user_id = user_id,
+ email = email)
+ dups.delete() # maybe just if dups.exists() but another roundtrip..
+ conf.email = email
+ conf.key = ''
+ conf.save()
+ person = get_object_or_404(Person, user_id = user_id,
+ email__iexact = old_email)
+ dups = Person.objects.filter(user_id = user_id,
+ email__iexact = email).count()
+ if dups:
+ person.delete()
+ else:
+ person.email = email
+ person.save()
+ else:
+ context = PatchworkRequestContext(request)
+ context['userform'] = form
+ return render_to_response('patchwork/user-data.html', context)
+
+ url = django.core.urlresolvers.reverse('patchwork.views.user.profile')
+ return HttpResponseRedirect(url)
@login_required
def todo_lists(request):
--
2.6.2
More information about the Patchwork
mailing list