2 コミット

作成者 SHA1 メッセージ 日付
  Dirk Alders f31771b588 Password and User settings Form implemented 1ヶ月前
  Dirk Alders b63cfaa741 BugFix: Login with non existing user 1ヶ月前
6個のファイルの変更145行の追加51行の削除
  1. 70
    2
      forms.py
  2. 18
    0
      migrations/0004_userprofile_mail_pending.py
  3. 1
    0
      models.py
  4. 8
    1
      templates/users/profile.html
  5. 0
    6
      templates/users/profile_formdata.html
  6. 48
    42
      views.py

+ 70
- 2
forms.py ファイルの表示

@@ -1,7 +1,9 @@
1 1
 from django import forms
2 2
 from django.contrib.auth.models import User
3
-from django.contrib.auth.forms import UserCreationForm, UserChangeForm
4
-from .models import UserProfile
3
+from django.contrib.auth.forms import UserCreationForm, UserChangeForm, PasswordChangeForm
4
+from django.utils.translation import gettext as _
5
+from .models import UserProfile, get_userprofile
6
+from users import emails
5 7
 
6 8
 
7 9
 class UserRegistrationForm(UserCreationForm):
@@ -24,6 +26,72 @@ class UserProfileFormLanguageOnly(forms.ModelForm):
24 26
         fields = ['language_code']
25 27
 
26 28
 
29
+class UserPasswordChangeForm(PasswordChangeForm):
30
+    email = forms.EmailField()
31
+    first_name = forms.CharField(max_length=150)
32
+    last_name = forms.CharField(max_length=150)
33
+    field_order = ["email", "first_name", "last_name", "old_password", "new_password1", "new_password2"]
34
+
35
+    def __init__(self, request):
36
+        self.request = request
37
+        #
38
+        data = request.POST or {'email': request.user.email, 'first_name': request.user.first_name, 'last_name': request.user.last_name}
39
+        #
40
+        super().__init__(request.user, data)
41
+        self.fields['old_password'].widget.attrs.update({'autofocus': False})
42
+        self.fields['old_password'].required = False
43
+
44
+    def clean_old_password(self):
45
+        """
46
+        Validation of old_password field only, if set.
47
+        """
48
+        old_password = self.cleaned_data["old_password"]
49
+        if old_password and not self.user.check_password(old_password):
50
+            raise forms.ValidationError(
51
+                self.error_messages["password_incorrect"],
52
+                code="password_incorrect",
53
+            )
54
+        return old_password
55
+
56
+    def clean(self):
57
+        """
58
+        Validation of new_passwords only if old_password field is set.
59
+        """
60
+        clean_data = forms.Form.clean(self)
61
+        old_password = clean_data.get('old_password')
62
+        #
63
+        if old_password:
64
+            self.validate_passwords("new_password1", "new_password2")
65
+            self.validate_password_for_user(self.user, "new_password2")
66
+        return forms.Form.clean(self)
67
+
68
+    def save(self, commit=True):
69
+        changed = False
70
+        for key in self.fields:
71
+            new = self.data.get(key)
72
+            try:
73
+                old = getattr(self.user, key)
74
+            except AttributeError:
75
+                pass  # Is a password field
76
+            else:
77
+                if new != old:
78
+                    if key == 'email':
79
+                        emails.send_validation_mail(self.user, self.request)
80
+                        up = get_userprofile(self.user)
81
+                        up.mail_pending = new
82
+                        up.save()
83
+                    else:
84
+                        changed = True
85
+                        setattr(self.user, key, new)
86
+        if self.data.get('new_password1'):
87
+            changed = True
88
+            self.user.set_password(self.data.get('new_password1'))
89
+        #
90
+        if changed:
91
+            self.user.save()
92
+        return changed
93
+
94
+
27 95
 class UserActivationForm(UserChangeForm):
28 96
     password = None
29 97
 

+ 18
- 0
migrations/0004_userprofile_mail_pending.py ファイルの表示

@@ -0,0 +1,18 @@
1
+# Generated by Django 5.1.2 on 2024-10-27 17:50
2
+
3
+from django.db import migrations, models
4
+
5
+
6
+class Migration(migrations.Migration):
7
+
8
+    dependencies = [
9
+        ('users', '0003_userprofile_mail_validated'),
10
+    ]
11
+
12
+    operations = [
13
+        migrations.AddField(
14
+            model_name='userprofile',
15
+            name='mail_pending',
16
+            field=models.EmailField(max_length=254, null=True),
17
+        ),
18
+    ]

+ 1
- 0
models.py ファイルの表示

@@ -30,6 +30,7 @@ class UserProfile(models.Model):
30 30
     timezone = models.CharField(max_length=150, default='UTC', choices=[(t, t) for t in pytz.common_timezones])
31 31
     language_code = models.CharField(max_length=150, default='en', choices=settings.LANGUAGES)
32 32
     mail_validated = models.BooleanField(default=False)
33
+    mail_pending = models.EmailField(null=True)
33 34
 
34 35
     def export_key(self):
35 36
         return self.user.username

+ 8
- 1
templates/users/profile.html ファイルの表示

@@ -4,7 +4,14 @@
4 4
 {% block content %}
5 5
   <form action="" method="post">
6 6
     {% csrf_token %}
7
-    {% include 'users/profile_formdata.html' %}
7
+
8
+    {% get_current_language as LANGUAGE_CODE %}
9
+    <h1>{% trans "Language and Timezone" %}</h1>
10
+    {{ form_userprofile.as_p }}
11
+
12
+    <h1>{% trans "Userdata and Password" %}</h1>
13
+    {{ form_userchange.as_p }}
14
+
8 15
     <input type="submit" value="{% trans "Save" %}" class="button" />
9 16
   </form>
10 17
 

+ 0
- 6
templates/users/profile_formdata.html ファイルの表示

@@ -1,6 +0,0 @@
1
-{% load i18n %}
2
-
3
-{% get_current_language as LANGUAGE_CODE %}
4
-  <h1>{% trans "Language and Timezone" %}</h1>
5
-  {{ form_userprofile.as_p }}
6
-  </select>

+ 48
- 42
views.py ファイルの表示

@@ -11,7 +11,7 @@ from django.contrib.auth.models import User
11 11
 from django.utils.encoding import force_str
12 12
 from django.utils.http import urlsafe_base64_decode
13 13
 from django.utils.translation import gettext as _
14
-from .forms import UserRegistrationForm, UserProfileForm, UserActivationForm
14
+from .forms import UserRegistrationForm, UserProfileForm, UserActivationForm, UserPasswordChangeForm
15 15
 import logging
16 16
 from .models import get_userprofile
17 17
 from themes import Context
@@ -28,37 +28,28 @@ def password_recovery(request):
28 28
     return redirect(request.GET.get('next') or '/')
29 29
 
30 30
 
31
-def profile_post_actions(request, context):
32
-    if request.POST:
33
-        form = context.get('form_userprofile')
34
-        if form.is_valid():
35
-            form.save()
36
-        return redirect(request.GET.get('next') or '/')
37
-
38
-
39
-def profile_pre_actions(request, context, form_to_be_used=UserProfileForm):
40
-    profile = get_userprofile(request.user)
41
-    if request.POST:
42
-        form = form_to_be_used(request.POST, instance=profile)
43
-    else:
44
-        form = form_to_be_used(instance=profile)
45
-    context['form_userprofile'] = form
46
-
47
-
48 31
 @login_required
49 32
 def profile(request):
50 33
     context = Context(request)      # needs to be executed first because of time mesurement
51
-    profile_pre_actions(request, context)
52
-    response = profile_post_actions(request, context)
53
-    if response is not None:
54
-        return response
34
+    profile = get_userprofile(request.user)
35
+    if request.POST:
36
+        form_userprofile = UserProfileForm(request.POST, instance=profile)
37
+        form_userchange = UserPasswordChangeForm(request)
38
+        if form_userprofile.is_valid() and form_userchange.is_valid():
39
+            form_userprofile.save()
40
+            form_userchange.save()
41
+            return redirect(request.GET.get('next') or '/')
55 42
     else:
56
-        context_adaption(
57
-            context,
58
-            request,
59
-            _('Profile for %(username)s') % {'username': request.user.username},
60
-        )
61
-        return render(request, 'users/profile.html', context=context)
43
+        form_userprofile = UserProfileForm(instance=profile)
44
+        form_userchange = UserPasswordChangeForm(request)
45
+    context_adaption(
46
+        context,
47
+        request,
48
+        _('Profile for %(username)s') % {'username': request.user.username},
49
+        form_userprofile=form_userprofile,
50
+        form_userchange=form_userchange,
51
+    )
52
+    return render(request, 'users/profile.html', context=context)
62 53
 
63 54
 
64 55
 def register(request):
@@ -108,8 +99,13 @@ def login(request):
108 99
             return redirect(request.GET.get('next') or '/')
109 100
         else:
110 101
             username = form.cleaned_data.get('username')
111
-            user = User.objects.get(username=username)
112
-            if user.is_active:
102
+            try:
103
+                user = User.objects.get(username=username)
104
+            except User.DoesNotExist:
105
+                is_active = True
106
+            else:
107
+                is_active = user.is_active
108
+            if is_active:
113 109
                 if parameter.get(parameter.USERS_SELF_REGISTRATION):
114 110
                     messages.error(request, _('Login failed! You can do a password recorvery <a href="%(url_recover)s">here</a> or you can register <a href="%(url_register)s">here</a>.') %
115 111
                                    {'url_register': users.url_register(request), 'url_recover': users.url_password_recovery(request)})
@@ -153,20 +149,30 @@ def validate(request, uidb64, token):
153 149
             myuser = None
154 150
 
155 151
     if myuser is not None and generate_token.check_token(myuser, token):
156
-        # Store mail validation to user profile
157
-        profile = get_userprofile(myuser)
158
-        profile.mail_validated = True
159
-        profile.save()
160
-        if not parameter.get(parameter.USERS_ADMIN_ACTIVATION):
161
-            # Activate user
162
-            myuser.is_active = True
152
+        up = get_userprofile(myuser)
153
+        if up.mail_pending:
154
+            # change of email-address
155
+            myuser.email = up.mail_pending
163 156
             myuser.save()
164
-            messages.success(request, _("Your Account has been activated."))
165
-            return redirect('users-login')
166
-        else:
167
-            emails.send_activation_mail(myuser, request)
168
-            messages.success(request, _("Your Email has been validated. Wait for the administrator to activate your account"))
157
+            up.mail_pending = None
158
+            up.save()
159
+            messages.success(request, _("Your new email address is now active."))
169 160
             return redirect("/")
161
+        else:
162
+            # Store mail validation to user profile
163
+            profile = get_userprofile(myuser)
164
+            profile.mail_validated = True
165
+            profile.save()
166
+            if not parameter.get(parameter.USERS_ADMIN_ACTIVATION):
167
+                # Activate user
168
+                myuser.is_active = True
169
+                myuser.save()
170
+                messages.success(request, _("Your Account has been activated."))
171
+                return redirect('users-login')
172
+            else:
173
+                emails.send_activation_mail(myuser, request)
174
+                messages.success(request, _("Your Email has been validated. Wait for the administrator to activate your account"))
175
+                return redirect("/")
170 176
     else:
171 177
         context_adaption(
172 178
             context,

読み込み中…
キャンセル
保存