Coverage for app/venv/lib/python3.14/site-packages/weblate/auth/views.py: 18%

143 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 

4from __future__ import annotations 

5 

6from typing import TYPE_CHECKING 

7 

8from django.core.exceptions import PermissionDenied 

9from django.forms import inlineformset_factory 

10from django.http import Http404, HttpResponseRedirect 

11from django.shortcuts import redirect 

12from django.urls import reverse 

13from django.utils.translation import gettext 

14from django.views.generic import DetailView, UpdateView 

15 

16from weblate.auth.forms import ProjectTeamForm, SitewideTeamForm 

17from weblate.auth.models import ( 

18 AutoGroup, 

19 Group, 

20 Invitation, 

21) 

22from weblate.trans.forms import UserAddTeamForm, UserManageForm 

23from weblate.trans.util import redirect_next, redirect_param 

24from weblate.utils import messages 

25from weblate.utils.views import get_paginator, show_form_errors 

26from weblate.wladmin.forms import ChangedCharField 

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 User, 

32 ) 

33 

34 

35class TeamUpdateView(UpdateView): 

36 model = Group 

37 template_name = "auth/team.html" 

38 request: AuthenticatedHttpRequest 

39 

40 auto_formset = inlineformset_factory( 

41 Group, 

42 AutoGroup, 

43 fields=("match",), 

44 extra=0, 

45 field_classes={"match": ChangedCharField}, 

46 ) 

47 

48 def get_form_class(self): 

49 if self.object.defining_project: 

50 return ProjectTeamForm 

51 return SitewideTeamForm 

52 

53 def get_form(self, form_class=None): 

54 if not self.request.user.has_perm("meta:team.edit", self.object): 

55 return None 

56 return super().get_form(form_class) 

57 

58 def get_form_kwargs(self): 

59 kwargs = super().get_form_kwargs() 

60 if self.object.defining_project: 

61 kwargs["project"] = self.object.defining_project 

62 return kwargs 

63 

64 def get_object(self, queryset=None): 

65 result = super().get_object(queryset=queryset) 

66 user = self.request.user 

67 

68 if ( 

69 not user.has_perm("meta:team.edit", result) 

70 and not user.has_perm("meta:team.users", result) 

71 and not user.groups.filter(pk=result.pk).exists() 

72 ): 

73 raise PermissionDenied 

74 

75 return result 

76 

77 def get_context_data(self, **kwargs): 

78 result = super().get_context_data(**kwargs) 

79 

80 if "auto_formset" not in result: 

81 result["auto_formset"] = self.auto_formset(instance=self.object) 

82 

83 if self.request.user.has_perm("meta:team.users", self.object): 

84 result["users"] = get_paginator( 

85 self.request, 

86 self.object.user_set.filter(is_active=True, is_bot=False).order(), 

87 ) 

88 result["add_user_form"] = UserAddTeamForm() 

89 result["admins"] = self.object.admins.all() 

90 

91 return result 

92 

93 def handle_add_user(self, request: AuthenticatedHttpRequest): 

94 form = UserAddTeamForm(request.POST) 

95 if form.is_valid(): 

96 if form.cleaned_data["make_admin"]: 

97 self.object.admins.add(form.cleaned_data["user"]) 

98 else: 

99 self.object.admins.remove(form.cleaned_data["user"]) 

100 form.cleaned_data["user"].add_team(request, self.object) 

101 else: 

102 show_form_errors(request, form) 

103 return HttpResponseRedirect(self.get_success_url()) 

104 

105 def handle_remove_user(self, request: AuthenticatedHttpRequest): 

106 form = UserManageForm(request.POST) 

107 if form.is_valid(): 

108 form.cleaned_data["user"].remove_team(request, self.object) 

109 else: 

110 show_form_errors(request, form) 

111 return HttpResponseRedirect(self.get_success_url()) 

112 

113 def handle_delete(self, request: AuthenticatedHttpRequest): 

114 if self.object.defining_project: 

115 fallback = ( 

116 reverse( 

117 "manage-access", 

118 kwargs={"project": self.object.defining_project.slug}, 

119 ) 

120 + "#teams" 

121 ) 

122 elif request.user.is_superuser: 

123 fallback = reverse("manage-teams") 

124 else: 

125 fallback = reverse("manage_access") + "#teams" 

126 if self.object.internal and not self.object.defining_project: 

127 messages.error(request, gettext("Cannot remove built-in team!")) 

128 else: 

129 self.object.delete() 

130 return redirect_next(request.POST.get("next"), fallback) 

131 

132 def post(self, request: AuthenticatedHttpRequest, **kwargs): 

133 self.object = self.get_object() 

134 if self.request.user.has_perm("meta:team.users", self.object): 

135 if "add_user" in request.POST: 

136 return self.handle_add_user(request) 

137 if "remove_user" in request.POST: 

138 return self.handle_remove_user(request) 

139 

140 form = self.get_form() 

141 if form is None: 

142 raise PermissionDenied 

143 

144 if "delete" in request.POST: 

145 return self.handle_delete(request) 

146 

147 formset = self.auto_formset(instance=self.object, data=request.POST) 

148 if form.is_valid() and formset.is_valid(): 

149 formset.save() 

150 return self.form_valid(form) 

151 return self.form_invalid(form, formset) 

152 

153 def form_invalid(self, form, formset): 

154 """If the form is invalid, render the invalid form.""" 

155 return self.render_to_response( 

156 self.get_context_data(form=form, auto_formset=formset) 

157 ) 

158 

159 

160class InvitationView(DetailView): 

161 model = Invitation 

162 

163 def get(self, request: AuthenticatedHttpRequest, *args, **kwargs): 

164 self.object = self.get_object() 

165 if request.user.is_authenticated and self.object.user != request.user: 

166 # Invitation not for this user (either is for email and user is None or different user) 

167 messages.error( 

168 request, 

169 gettext( 

170 "This invitation can be accepted only by the e-mail address chosen by the inviter; it can't be used by your account." 

171 ), 

172 ) 

173 return redirect_param("profile", "#account") 

174 if not self.object.user: 

175 # When inviting new user go through registration 

176 request.session["invitation_link"] = str(self.object.pk) 

177 return redirect("register") 

178 context = self.get_context_data(object=self.object) 

179 return self.render_to_response(context) 

180 

181 def post(self, request: AuthenticatedHttpRequest, **kwargs): 

182 self.object = invitation = self.get_object() 

183 user = request.user 

184 

185 # Handle admin actions first 

186 action = request.POST.get("action", "") 

187 if action in {"resend", "remove"}: 

188 project = invitation.group.defining_project 

189 # Permission check 

190 if not user.has_perm( 

191 "project.permissions" if project else "user.edit", project 

192 ): 

193 raise PermissionDenied 

194 

195 # Perform admin action 

196 if action == "resend": 

197 invitation.send_email() 

198 messages.success(request, gettext("User invitation e-mail was sent.")) 

199 else: 

200 invitation.delete() 

201 messages.success(request, gettext("User invitation was removed.")) 

202 

203 # Redirect 

204 if project: 

205 return redirect("manage-access", project=project.slug) 

206 return redirect("manage-users") 

207 

208 # Check if invitation can be accepted 

209 if not invitation.user: 

210 # This should go via registration path 

211 raise Http404 

212 if not user.is_authenticated: 

213 raise PermissionDenied 

214 if invitation.user != user: 

215 raise Http404 

216 

217 # Accept invitation 

218 invitation.accept(request, user) 

219 

220 if invitation.group.defining_project: 

221 return redirect(invitation.group.defining_project) 

222 return redirect("home") 

223 

224 

225def accept_invitation( 

226 request: AuthenticatedHttpRequest, invitation: Invitation, user: User | None 

227) -> None: 

228 if user is None: 

229 user = invitation.user 

230 if user is None: 

231 raise Http404 

232 

233 # Add user to invited group 

234 user.add_team(request, invitation.group) 

235 # Let him watch the project 

236 if invitation.group.defining_project: 

237 user.profile.watched.add(invitation.group.defining_project) 

238 

239 messages.success( 

240 request, gettext("Accepted invitation to the %s team.") % invitation.group 

241 ) 

242 invitation.delete()