From 5dc2192c8806837b6ae1d9c7627228e7b5d9dd92 Mon Sep 17 00:00:00 2001
From: Lucas Brandstaetter <lucas@brandstaetter.tech>
Date: Sun, 15 Dec 2024 04:40:32 +0100
Subject: [PATCH] Add name property to PlatformUser

- Add a name property to PlatformUser that returns the display name with
  the username in angle brackets if the user has a first or last name set.
- Update get_display_name to render if first_name or last_name is set.
  This is in preparation for #636
---
 src/core/models/users.py                    | 27 +++++++++++++++++-
 src/core/tests/users.py                     | 31 +++++++++++++++++++++
 src/plainui/jinja2/plainui/user.html.j2     |  2 +-
 src/plainui/locale/de/LC_MESSAGES/django.po |  5 ++--
 src/plainui/locale/en/LC_MESSAGES/django.po |  5 ++--
 5 files changed, 64 insertions(+), 6 deletions(-)

diff --git a/src/core/models/users.py b/src/core/models/users.py
index 134d8cc41..07d73c732 100644
--- a/src/core/models/users.py
+++ b/src/core/models/users.py
@@ -1,5 +1,6 @@
 import logging
 import re
+from contextlib import suppress
 from pathlib import Path
 from random import choices
 from string import ascii_lowercase, digits
@@ -243,8 +244,30 @@ class PlatformUser(AbstractUser):
     def is_person(self):
         return self.user_type in self.PERSON_TYPES
 
+    @cached_property
+    def name(self) -> str:
+        """
+        Shows the display name with username if they differ.
+
+        This is used to make the name available in a way matching other models (e.g. for invitations).
+
+        ReturnsL
+            str: The users display name.
+        """
+        name = self.display_name
+        if self.first_name or self.last_name:
+            name += f' <{self.username}>'
+        return name
+
     def get_display_name(self):
-        result = self.username if self.user_type != self.Type.SPEAKER else (self.first_name + ' ' + self.last_name).strip()
+        result = ''
+        if self.first_name:
+            result += self.first_name
+        if self.last_name:
+            result += f' {self.last_name}'
+        result = result.strip()
+        if not result:
+            result = self.username
         if self.pronouns:
             result += f' ({self.pronouns})'
         return result
@@ -344,6 +367,8 @@ class PlatformUser(AbstractUser):
 
         # update the display name
         self.display_name = self.get_display_name()
+        with suppress(AttributeError):
+            del self.name  # delete the cached property to force a refresh
 
         return super().save(*args, update_fields=update_fields, **kwargs)
 
diff --git a/src/core/tests/users.py b/src/core/tests/users.py
index 8f7d86b22..60f308c29 100644
--- a/src/core/tests/users.py
+++ b/src/core/tests/users.py
@@ -163,3 +163,34 @@ class UserRegistrationTests(TestCase):
         self.assertEqual(response.status_code, 302)
         user = PlatformUser.objects.get(username=username)
         self.assertTrue(len(user.slug) > 0)
+
+
+class UserNameTests(TestCase):
+    def setUp(self) -> None:
+        self.user = PlatformUser(username='bernd')
+        self.user.save()
+
+    def test_name_output(self):
+        self.assertEqual(self.user.display_name, 'bernd')
+        self.assertEqual(self.user.name, 'bernd')
+
+        self.user.first_name = 'Bernd'
+        self.user.save()
+        self.user.refresh_from_db()
+
+        self.assertEqual(self.user.display_name, 'Bernd')
+        self.assertEqual(self.user.name, 'Bernd <bernd>')
+
+        self.user.last_name = 'Brot'
+        self.user.save()
+        self.user.refresh_from_db()
+
+        self.assertEqual(self.user.display_name, 'Bernd Brot')
+        self.assertEqual(self.user.name, 'Bernd Brot <bernd>')
+
+        self.user.pronouns = 'das'
+        self.user.save()
+        self.user.refresh_from_db()
+
+        self.assertEqual(self.user.display_name, 'Bernd Brot (das)')
+        self.assertEqual(self.user.name, 'Bernd Brot (das) <bernd>')
diff --git a/src/plainui/jinja2/plainui/user.html.j2 b/src/plainui/jinja2/plainui/user.html.j2
index 6f8f97ea3..32c954191 100644
--- a/src/plainui/jinja2/plainui/user.html.j2
+++ b/src/plainui/jinja2/plainui/user.html.j2
@@ -14,7 +14,7 @@
 {% endblock title %}
 {% block content %}
 
-  {{ navMacro.top_nav(_("User Profile") ) }}
+  {{ navMacro.top_nav(_("User Profile %(username)s", username=display_user.username) ) }}
 
   <div class="hub-vlayout">
     <div class="hub-row">
diff --git a/src/plainui/locale/de/LC_MESSAGES/django.po b/src/plainui/locale/de/LC_MESSAGES/django.po
index 4d7fa3828..2d02369df 100644
--- a/src/plainui/locale/de/LC_MESSAGES/django.po
+++ b/src/plainui/locale/de/LC_MESSAGES/django.po
@@ -1087,5 +1087,6 @@ msgstr "Kommende Events"
 msgid "%(conf)s - User %(name)s"
 msgstr ""
 
-msgid "User Profile"
-msgstr "Profil"
+#, python-format
+msgid "User Profile %(username)s"
+msgstr "Profil: %(username)s"
diff --git a/src/plainui/locale/en/LC_MESSAGES/django.po b/src/plainui/locale/en/LC_MESSAGES/django.po
index 9471456f3..3e63a2246 100644
--- a/src/plainui/locale/en/LC_MESSAGES/django.po
+++ b/src/plainui/locale/en/LC_MESSAGES/django.po
@@ -1087,5 +1087,6 @@ msgstr ""
 msgid "%(conf)s - User %(name)s"
 msgstr ""
 
-msgid "User Profile"
-msgstr ""
+#, python-format
+msgid "User Profile %(username)s"
+msgstr "User Profile %(username)s"
-- 
GitLab