From 03c677840ac5c08b9f7231c26aa137d858e486cb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= <fpeters@entrouvert.com>
Date: Sat, 5 Nov 2022 12:45:32 +0100
Subject: [PATCH] general: add a new "language" attribute kind (#71033)

---
 src/authentic2/attribute_kinds.py | 27 +++++++++++++++++++++++++++
 src/authentic2/forms/profile.py   |  7 +++++++
 tests/test_profile.py             |  8 ++++++++
 3 files changed, 42 insertions(+)

diff --git a/src/authentic2/attribute_kinds.py b/src/authentic2/attribute_kinds.py
index 74e379e6d..14ede4284 100644
--- a/src/authentic2/attribute_kinds.py
+++ b/src/authentic2/attribute_kinds.py
@@ -33,6 +33,7 @@ from django.core.validators import RegexValidator
 from django.urls import reverse, reverse_lazy
 from django.utils import html
 from django.utils.functional import keep_lazy
+from django.utils.translation import get_supported_language_variant
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import pgettext_lazy
 from gadjo.templatetags.gadjo import xstatic
@@ -286,6 +287,21 @@ class Base64ImageField(serializers.ImageField):
         return super().to_internal_value(data)
 
 
+def language_choices():
+    return settings.LANGUAGES
+
+
+def language_default():
+    return get_supported_language_variant(settings.LANGUAGE_CODE)
+
+
+def language_html_value(attribute, value):
+    for key, label in language_choices():
+        if value == key:
+            return label
+    return ''
+
+
 DEFAULT_ALLOW_BLANK = True
 DEFAULT_MAX_LENGTH = 256
 
@@ -371,6 +387,17 @@ DEFAULT_ATTRIBUTE_KINDS = [
         'attributes_ng_serialize': profile_attributes_ng_serialize,
         'csv_importable': False,
     },
+    {
+        'label': _('language'),
+        'name': 'language',
+        'default': language_default,
+        'field_class': forms.ChoiceField,
+        'html_value': language_html_value,
+        'kwargs': {
+            'choices': language_choices,
+            'widget': forms.Select,
+        },
+    },
 ]
 
 
diff --git a/src/authentic2/forms/profile.py b/src/authentic2/forms/profile.py
index fcec00e8d..d2dde102d 100644
--- a/src/authentic2/forms/profile.py
+++ b/src/authentic2/forms/profile.py
@@ -68,6 +68,13 @@ class BaseUserForm(LockedFieldFormMixin, forms.ModelForm):
         self.atvs = []
         self.locked_fields = set()
         if instance:
+            for attribute in self.attributes:
+                kind = attribute.get_kind()
+                if kind.get('default'):
+                    if attribute.name in self.declared_fields:
+                        initial[attribute.name] = (
+                            kind['default']() if callable(kind['default']) else kind['default']
+                        )
             self.atvs = models.AttributeValue.objects.select_related('attribute').with_owner(instance)
             for atv in self.atvs:
                 name = atv.attribute.name
diff --git a/tests/test_profile.py b/tests/test_profile.py
index 8e0179cad..594bc148b 100644
--- a/tests/test_profile.py
+++ b/tests/test_profile.py
@@ -41,18 +41,25 @@ def test_account_edit_view(app, simple_user):
     agreement = Attribute.objects.create(
         name='agreement', label='agreement', kind='boolean', user_visible=True, user_editable=True
     )
+    language = Attribute.objects.create(
+        name='language', label='language', kind='language', user_visible=True, user_editable=True
+    )
 
     resp = old_resp = app.get(url, status=200)
     resp.form['phone'] = '1234'
     assert resp.form['phone'].attrs['type'] == 'tel'
     resp.form['title'] = 'Mrs'
     resp.form['agreement'] = False
+    assert resp.form['language'].tag == 'select'
+    assert resp.form['language'].value == 'en'
+    resp.form['language'] = 'fr'
     resp = resp.form.submit()
     # verify that missing next_url in POST is ok
     assert resp['Location'].endswith(reverse('account_management'))
     assert phone.get_value(simple_user) == '1234'
     assert title.get_value(simple_user) == 'Mrs'
     assert agreement.get_value(simple_user) is False
+    assert language.get_value(simple_user) == 'fr'
 
     resp = resp.follow()
     profile = [
@@ -65,6 +72,7 @@ def test_account_edit_view(app, simple_user):
         ('Email address', 'user@example.net'),
         ('Phone', '1234'),
         ('Title', 'Mrs'),
+        ('Language', 'French'),
     ]
 
     resp = app.get(url, status=200)
-- 
2.38.1

