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

1# Copyright © Michal Čihař <michal@weblate.org> 

2# 

3# SPDX-License-Identifier: GPL-3.0-or-later 

4 

5from __future__ import annotations 

6 

7from typing import TYPE_CHECKING 

8 

9from crispy_forms.helper import FormHelper 

10from django import forms 

11from django.core.exceptions import ValidationError 

12from django.utils.translation import gettext 

13 

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 

27 

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 ) 

32 

33 

34class InviteUserForm(forms.ModelForm): 

35 class Meta: 

36 model = Invitation 

37 fields = ("user", "group") 

38 field_classes = { # noqa: RUF012 

39 "user": UserField 

40 } 

41 

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 

58 

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.")) 

100 

101 

102class InviteEmailForm(InviteUserForm, UniqueEmailMixin): 

103 class Meta: 

104 model = Invitation 

105 fields = ("email", "username", "full_name", "group") 

106 

107 

108class AdminInviteUserForm(InviteUserForm): 

109 class Meta: 

110 model = Invitation 

111 fields = ("email", "username", "full_name", "group", "is_superuser") 

112 

113 

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 } 

128 

129 

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 ) 

141 

142 internal_fields = ( 

143 "name", 

144 "project_selection", 

145 "language_selection", 

146 ) 

147 

148 def __init__(self, *args, **kwargs) -> None: 

149 super().__init__(*args, **kwargs) 

150 self.helper = FormHelper(self) 

151 self.helper.form_tag = False 

152 

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 ) 

163 

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 

170 

171 self.instance.save() 

172 

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 

183 

184 

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 ) 

193 

194 

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 )