[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