Coverage for app/venv/lib/python3.14/site-packages/weblate/auth/forms.py: 36%
104 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 07:15 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 07:15 +0000
1# Copyright © Michal Čihař <michal@weblate.org>
2#
3# SPDX-License-Identifier: GPL-3.0-or-later
5from __future__ import annotations
7from typing import TYPE_CHECKING
9from crispy_forms.helper import FormHelper
10from django import forms
11from django.core.exceptions import ValidationError
12from django.utils.translation import gettext
14from weblate.accounts.forms import UniqueEmailMixin
15from weblate.accounts.models import AuditLog
16from weblate.auth.data import GLOBAL_PERM_NAMES, SELECTION_MANUAL
17from weblate.auth.models import (
18 Group,
19 Invitation,
20 Role,
21 User,
22)
23from weblate.trans.actions import ActionEvents
24from weblate.trans.models import Change
25from weblate.utils import messages
26from weblate.utils.forms import UserField, WeblateDateInput
28if TYPE_CHECKING: 28 ↛ 29line 28 didn't jump to line 29 because the condition on line 28 was never true
29 from weblate.auth.models import (
30 AuthenticatedHttpRequest,
31 )
34class InviteUserForm(forms.ModelForm):
35 class Meta:
36 model = Invitation
37 fields = ("user", "group")
38 field_classes = { # noqa: RUF012
39 "user": UserField
40 }
42 def __init__(
43 self,
44 data=None,
45 files=None,
46 project=None,
47 **kwargs,
48 ) -> None:
49 self.project = project
50 super().__init__(data=data, files=files, **kwargs)
51 if project:
52 self.fields["group"].queryset = project.group_set.all()
53 else:
54 self.fields["group"].queryset = Group.objects.filter(defining_project=None)
55 for field in ("user", "email"):
56 if field in self.fields:
57 self.fields[field].required = True
59 def save(self, request: AuthenticatedHttpRequest, commit: bool = True) -> None:
60 self.instance.author = author = request.user
61 # Migrate to user if e-mail matches
62 if self.instance.email:
63 try:
64 self.instance.user = (
65 User.objects.filter(
66 social_auth__verifiedemail__email=self.instance.email
67 )
68 .distinct()
69 .get()
70 )
71 except (User.DoesNotExist, User.MultipleObjectsReturned):
72 pass
73 else:
74 self.instance.email = ""
75 super().save(commit=commit)
76 if commit:
77 if self.instance.user:
78 details = {"username": self.instance.user.username}
79 else:
80 details = {"email": self.instance.email}
81 Change.objects.create(
82 project=self.project, # Might be None
83 action=ActionEvents.INVITE_USER,
84 user=author,
85 details=details,
86 )
87 if self.instance.user:
88 details = {"username": request.user.username}
89 if self.project:
90 details["project"] = self.project.name
91 details["method"] = "project"
92 AuditLog.objects.create(
93 user=self.instance.user,
94 request=request,
95 activity="invited",
96 **details,
97 )
98 self.instance.send_email()
99 messages.success(request, gettext("User invitation e-mail was sent."))
102class InviteEmailForm(InviteUserForm, UniqueEmailMixin):
103 class Meta:
104 model = Invitation
105 fields = ("email", "username", "full_name", "group")
108class AdminInviteUserForm(InviteUserForm):
109 class Meta:
110 model = Invitation
111 fields = ("email", "username", "full_name", "group", "is_superuser")
114class UserEditForm(forms.ModelForm):
115 class Meta:
116 model = User
117 fields = (
118 "username",
119 "full_name",
120 "email",
121 "is_superuser",
122 "is_active",
123 "date_expires",
124 )
125 widgets = { # noqa: RUF012
126 "date_expires": WeblateDateInput(),
127 }
130class BaseTeamForm(forms.ModelForm):
131 class Meta:
132 model = Group
133 fields = (
134 "name",
135 "roles",
136 "language_selection",
137 "languages",
138 "components",
139 "enforced_2fa",
140 )
142 internal_fields = (
143 "name",
144 "project_selection",
145 "language_selection",
146 )
148 def __init__(self, *args, **kwargs) -> None:
149 super().__init__(*args, **kwargs)
150 self.helper = FormHelper(self)
151 self.helper.form_tag = False
153 def clean(self) -> None:
154 super().clean()
155 if self.instance.internal:
156 for field in self.internal_fields:
157 if field in self.cleaned_data and self.cleaned_data[field] != getattr(
158 self.instance, field
159 ):
160 raise ValidationError(
161 {field: gettext("Cannot change this on a built-in team.")}
162 )
164 def save(self, commit=True, project=None):
165 if not commit:
166 return super().save(commit=commit)
167 if project:
168 self.instance.defining_project = project
169 self.instance.project_selection = SELECTION_MANUAL
171 self.instance.save()
173 # Save languages only for manual selection, otherwise
174 # it would override logic from Group.save()
175 if self.instance.language_selection != SELECTION_MANUAL:
176 self.cleaned_data.pop("languages", None)
177 if self.instance.project_selection != SELECTION_MANUAL:
178 self.cleaned_data.pop("projects", None)
179 self._save_m2m()
180 if project:
181 self.instance.projects.add(project)
182 return self.instance
185class ProjectTeamForm(BaseTeamForm):
186 def __init__(self, project, *args, **kwargs) -> None:
187 super().__init__(*args, **kwargs)
188 self.fields["components"].queryset = project.component_set.order()
189 # Exclude site-wide permissions here
190 self.fields["roles"].queryset = Role.objects.exclude(
191 permissions__codename__in=GLOBAL_PERM_NAMES
192 )
195class SitewideTeamForm(BaseTeamForm):
196 class Meta:
197 model = Group
198 fields = (
199 "name",
200 "roles",
201 "project_selection",
202 "projects",
203 "componentlists",
204 "language_selection",
205 "languages",
206 "enforced_2fa",
207 )