From 31e3dab4ec3ddad9d18e342df206ca7dcc925e8f Mon Sep 17 00:00:00 2001
From: kevinhowbrook <kbhowbrook@gmail.com>
Date: Wed, 10 Jul 2019 09:05:38 +0100
Subject: [PATCH] Allow users to change their first and last name

---
 CHANGELOG.txt                                 |  1 +
 docs/releases/2.7.rst                         |  1 +
 .../wagtailadmin/account/change_name.html     | 20 +++++++++++++++
 .../admin/tests/test_account_management.py    | 25 +++++++++++++++++++
 wagtail/admin/urls/__init__.py                |  1 +
 wagtail/admin/views/account.py                | 18 ++++++++++++-
 wagtail/admin/wagtail_hooks.py                | 11 +++++++-
 wagtail/users/forms.py                        |  9 +++++++
 8 files changed, 84 insertions(+), 2 deletions(-)
 create mode 100644 wagtail/admin/templates/wagtailadmin/account/change_name.html

diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 11c73eafaf..305e093734 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -12,6 +12,7 @@ Changelog
  * Remove need for Elasticsearch `update_all_types` workaround, upgrade minimum release to 6.4.0 or above (Jonathan Liuti)
  * Upgrade django-modelcluster to>=5.0 and upgrade django-taggit to >=1.0 for Django 3.0 support (Matt Westcott)
  * Revise tests to ensure all pass in Django 3.0 (Matt Westcott)
+ * Add ability for users to change their own name via the account settings page (Kevin Howbrook)
  * Fix: Added line breaks to long filenames on multiple image / document uploader (Kevin Howbrook)
  * Fix: Added https support for Scribd oEmbed provider (Rodrigo)
  * Fix: Changed StreamField group labels color so labels are visible (Catherine Farman)
diff --git a/docs/releases/2.7.rst b/docs/releases/2.7.rst
index 458a93419b..6087b2c1d6 100644
--- a/docs/releases/2.7.rst
+++ b/docs/releases/2.7.rst
@@ -27,6 +27,7 @@ Other features
  * Added Table Block caption for accessibility (Rahmi Pruitt)
  * Upgrade django-modelcluster to>=5.0 and upgrade django-taggit to >=1.0 for Django 3.0 support (Matt Westcott)
  * Revise tests to ensure all pass in Django 3.0 (Matt Westcott)
+ * Add ability for users to change their own name via the account settings page (Kevin Howbrook)
 
 
 
diff --git a/wagtail/admin/templates/wagtailadmin/account/change_name.html b/wagtail/admin/templates/wagtailadmin/account/change_name.html
new file mode 100644
index 0000000000..d34514136e
--- /dev/null
+++ b/wagtail/admin/templates/wagtailadmin/account/change_name.html
@@ -0,0 +1,20 @@
+{% extends "wagtailadmin/base.html" %}
+{% load i18n %}
+
+{% block titletag %}{% trans "Change name" %}{% endblock %}
+{% block content %}
+    {% trans "Change name" as change_str %}
+    {% include "wagtailadmin/shared/header.html" with title=change_str %}
+
+    <div class="nice-padding">
+        <form action="{% url 'wagtailadmin_account_change_name' %}" method="POST" novalidate>
+            {% csrf_token %}
+            <ul class="fields">
+                {% for field in form %}
+                    {% include "wagtailadmin/shared/field_as_li.html" with field=field %}
+                {% endfor %}
+            </ul>
+            <input type="submit" value="{% trans 'Change name' %}" class="button" />
+        </form>
+    </div>
+{% endblock %}
diff --git a/wagtail/admin/tests/test_account_management.py b/wagtail/admin/tests/test_account_management.py
index cda84aef15..f77ac59b6d 100644
--- a/wagtail/admin/tests/test_account_management.py
+++ b/wagtail/admin/tests/test_account_management.py
@@ -411,6 +411,31 @@ class TestAccountSection(TestCase, WagtailTestUtils):
         # Check that the current language is assumed as English
         self.assertEqual(profile.get_preferred_language(), "en")
 
+    def test_change_name(self):
+        """
+        This tests that the change name view responds with a change name page
+        """
+        # Get change name page
+        response = self.client.get(reverse('wagtailadmin_account_change_name'))
+
+        # Check that the user received a change name page
+        self.assertEqual(response.status_code, 200)
+        self.assertTemplateUsed(response, 'wagtailadmin/account/change_name.html')
+
+    def test_change_name_post(self):
+        post_data = {
+            'first_name': 'Fox',
+            'last_name': 'Mulder',
+        }
+        response = self.client.post(reverse('wagtailadmin_account_change_name'), post_data)
+
+        # Check that the user was redirected to the account page
+        self.assertRedirects(response, reverse('wagtailadmin_account'))
+
+        # Check that the name was changed
+        self.assertEqual(get_user_model().objects.get(pk=self.user.pk).first_name, post_data['first_name'])
+        self.assertEqual(get_user_model().objects.get(pk=self.user.pk).last_name, post_data['last_name'])
+
     @override_settings(WAGTAILADMIN_PERMITTED_LANGUAGES=[('en', 'English'), ('es', 'Spanish')])
     def test_available_admin_languages_with_permitted_languages(self):
         self.assertListEqual(get_available_admin_languages(), [('en', 'English'), ('es', 'Spanish')])
diff --git a/wagtail/admin/urls/__init__.py b/wagtail/admin/urls/__init__.py
index 4a46288fce..3f0858c1d1 100644
--- a/wagtail/admin/urls/__init__.py
+++ b/wagtail/admin/urls/__init__.py
@@ -46,6 +46,7 @@ urlpatterns = [
     url(r'^account/$', account.account, name='wagtailadmin_account'),
     url(r'^account/change_password/$', account.change_password, name='wagtailadmin_account_change_password'),
     url(r'^account/change_email/$', account.change_email, name='wagtailadmin_account_change_email'),
+    url(r'^account/change_name/$', account.change_name, name='wagtailadmin_account_change_name'),
     url(
         r'^account/notification_preferences/$',
         account.notification_preferences,
diff --git a/wagtail/admin/views/account.py b/wagtail/admin/views/account.py
index d04b813b64..5bcfc36a23 100644
--- a/wagtail/admin/views/account.py
+++ b/wagtail/admin/views/account.py
@@ -12,7 +12,7 @@ from django.utils.translation import activate
 from wagtail.admin.forms.auth import LoginForm, PasswordResetForm
 from wagtail.core import hooks
 from wagtail.users.forms import (
-    AvatarPreferencesForm, CurrentTimeZoneForm, EmailForm, NotificationPreferencesForm, PreferredLanguageForm)
+    AvatarPreferencesForm, CurrentTimeZoneForm, EmailForm, NameForm, NotificationPreferencesForm, PreferredLanguageForm)
 from wagtail.users.models import UserProfile
 from wagtail.utils.loading import get_custom_form
 
@@ -95,6 +95,22 @@ def change_email(request):
     })
 
 
+def change_name(request):
+    if request.method == 'POST':
+        form = NameForm(request.POST, instance=request.user)
+
+        if form.is_valid():
+            form.save()
+            messages.success(request, _("Your name has been changed successfully!"))
+            return redirect('wagtailadmin_account')
+    else:
+        form = NameForm(instance=request.user)
+
+    return render(request, 'wagtailadmin/account/change_name.html', {
+        'form': form,
+    })
+
+
 class PasswordResetEnabledViewMixin:
     """
     Class based view mixin that disables the view if password reset is disabled by one of the following settings:
diff --git a/wagtail/admin/wagtail_hooks.py b/wagtail/admin/wagtail_hooks.py
index 1aca554364..81abec0aad 100644
--- a/wagtail/admin/wagtail_hooks.py
+++ b/wagtail/admin/wagtail_hooks.py
@@ -203,7 +203,7 @@ def register_account_set_profile_picture(request):
     return {
         'url': reverse('wagtailadmin_account_change_avatar'),
         'label': _('Set profile picture'),
-        'help_text': _("Change your profile picture")
+        'help_text': _("Change your profile picture.")
     }
 
 
@@ -257,6 +257,15 @@ def register_account_current_time_zone(request):
         }
 
 
+@hooks.register('register_account_menu_item')
+def register_account_change_name(request):
+    return {
+        'url': reverse('wagtailadmin_account_change_name'),
+        'label': _('Change name'),
+        'help_text': _('Change your first and last name on your account.'),
+    }
+
+
 @hooks.register('register_rich_text_features')
 def register_core_features(features):
     # Hallo.js
diff --git a/wagtail/users/forms.py b/wagtail/users/forms.py
index 653ba2a295..fe6eeca98f 100644
--- a/wagtail/users/forms.py
+++ b/wagtail/users/forms.py
@@ -409,6 +409,15 @@ class EmailForm(forms.ModelForm):
         fields = ("email",)
 
 
+class NameForm(forms.ModelForm):
+    first_name = forms.CharField(required=True, label=_('First Name'))
+    last_name = forms.CharField(required=True, label=_('Last Name'))
+
+    class Meta:
+        model = User
+        fields = ("first_name", "last_name",)
+
+
 def _get_time_zone_choices():
     time_zones = [(tz, str(l18n.tz_fullnames.get(tz, tz)))
                   for tz in get_available_admin_time_zones()]