• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rafalp / Misago / 9235638887

25 May 2024 12:58PM UTC coverage: 97.61% (-1.1%) from 98.716%
9235638887

Pull #1742

github

web-flow
Merge ef9c8656c into abad4f068
Pull Request #1742: Replace forum options with account settings

166 of 211 new or added lines in 20 files covered. (78.67%)

655 existing lines in 146 files now uncovered.

51625 of 52889 relevant lines covered (97.61%)

0.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.11
/misago/users/admin/forms/users.py
1
from typing import Type
1✔
2

3
from django import forms
1✔
4
from django.contrib.auth import get_user_model
1✔
5
from django.contrib.auth.password_validation import validate_password
1✔
6
from django.db.models import Q
1✔
7
from django.utils.translation import npgettext, pgettext, pgettext_lazy
1✔
8

9
from ....acl.models import Role
1✔
10
from ....admin.forms import IsoDateTimeField, YesNoSwitch
1✔
11
from ....notifications.threads import ThreadNotifications
1✔
12
from ....search.filter_queryset import filter_queryset
1✔
13
from ...enums import DefaultGroupId
1✔
14
from ...models import Group, Rank
1✔
15
from ...profilefields import profilefields
1✔
16
from ...utils import slugify_username
1✔
17
from ...validators import validate_email, validate_username
1✔
18

19
User = get_user_model()
1✔
20

21

22
class BaseUserForm(forms.ModelForm):
1✔
23
    groups_dict: dict[int, Group]
1✔
24

25
    username = forms.CharField(label=pgettext_lazy("admin user form", "Username"))
1✔
26
    title = forms.CharField(
1✔
27
        label=pgettext_lazy("admin user form", "Custom title"),
28
        required=False,
29
    )
30
    email = forms.EmailField(label=pgettext_lazy("admin user form", "E-mail address"))
1✔
31

32
    class Meta:
1✔
33
        model = User
1✔
34
        fields = ["username", "email", "title"]
1✔
35

36
    def __init__(self, *args, **kwargs):
1✔
37
        self.request = kwargs.pop("request")
1✔
38
        self.settings = self.request.settings
1✔
39

40
        super().__init__(*args, **kwargs)
1✔
41

42
    def clean_username(self):
1✔
43
        data = self.cleaned_data["username"]
1✔
44
        validate_username(self.settings, data, exclude=self.instance)
1✔
45
        return data
1✔
46

47
    def clean_email(self):
1✔
48
        data = self.cleaned_data["email"]
1✔
49
        validate_email(data, exclude=self.instance)
1✔
50
        return data
1✔
51

52
    def clean_new_password(self):
1✔
53
        data = self.cleaned_data["new_password"]
1✔
54
        if data:
1✔
55
            validate_password(data, user=self.instance)
1✔
56
        return data
1✔
57

58
    def clean_group(self):
1✔
59
        data = self.cleaned_data["group"]
1✔
60
        return self.groups_dict[data]
1✔
61

62
    def clean_secondary_groups(self):
1✔
63
        data = self.cleaned_data["secondary_groups"]
1✔
64
        return [self.groups_dict[item] for item in data]
1✔
65

66
    def clean_roles(self):
1✔
67
        data = self.cleaned_data["roles"]
1✔
68

69
        for role in data:
1✔
70
            if role.special_role == "authenticated":
1✔
71
                break
1✔
72
        else:
73
            message = pgettext(
1✔
74
                "admin user form", 'All registered members must have a "Member" role.'
75
            )
76
            raise forms.ValidationError(message)
1✔
77

78
        return data
1✔
79

80

81
class NewUserForm(BaseUserForm):
1✔
82
    new_password = forms.CharField(
1✔
83
        label=pgettext_lazy("admin user form", "Password"),
84
        strip=False,
85
        widget=forms.PasswordInput,
86
    )
87

88
    class Meta:
1✔
89
        model = User
1✔
90
        fields = ["username", "email", "title"]
1✔
91

92
    def clean_group(self):
1✔
93
        data = self.cleaned_data["group"]
1✔
94
        group = self.groups_dict[data]
1✔
95

96
        if not self.request.user.is_misago_root and data == DefaultGroupId.ADMINS:
1✔
97
            raise forms.ValidationError(
1✔
98
                pgettext(
99
                    "admin user form",
100
                    "You must be a root administrator to set this user's main group to the %(group)s.",
101
                )
102
                % {"group": group}
103
            )
104

105
        return group
1✔
106

107
    def clean_secondary_groups(self):
1✔
108
        data = self.cleaned_data["secondary_groups"]
1✔
109

110
        if not self.request.user.is_misago_root and DefaultGroupId.ADMINS in data:
1✔
111
            admins_group = self.groups_dict[DefaultGroupId.ADMINS]
1✔
112
            raise forms.ValidationError(
1✔
113
                pgettext(
114
                    "admin user form",
115
                    "You must be a root administrator to add this user to the %(group)s group.",
116
                )
117
                % {"group": admins_group}
118
            )
119

120
        return [self.groups_dict[item] for item in data]
1✔
121

122

123
class EditUserForm(BaseUserForm):
1✔
124
    new_password = forms.CharField(
1✔
125
        label=pgettext_lazy("admin user form", "New password"),
126
        strip=False,
127
        widget=forms.PasswordInput,
128
        required=False,
129
    )
130

131
    is_misago_root = YesNoSwitch(
1✔
132
        label=pgettext_lazy("admin user form", "Is root administrator"),
133
        help_text=pgettext_lazy(
134
            "admin user form",
135
            "Root administrators can sign in to the Misago Admin Control Panel and change other users' admin status. You need to be a root administrator to change this field.",
136
        ),
137
        disabled=True,
138
    )
139

140
    is_active = YesNoSwitch(
1✔
141
        label=pgettext_lazy("admin user form", "Is active"),
142
        help_text=pgettext_lazy(
143
            "admin user form",
144
            "Designates whether this user should be treated as active. Turning this off is non-destructible way to remove user accounts.",
145
        ),
146
    )
147
    is_active_staff_message = forms.CharField(
1✔
148
        label=pgettext_lazy("admin user form", "Staff message"),
149
        help_text=pgettext_lazy(
150
            "admin user form",
151
            "Optional message for forum team members explaining why user's account has been disabled.",
152
        ),
153
        widget=forms.Textarea(attrs={"rows": 3}),
154
        required=False,
155
    )
156

157
    is_avatar_locked = YesNoSwitch(
1✔
158
        label=pgettext_lazy("admin user form", "Lock avatar changes"),
159
        help_text=pgettext_lazy(
160
            "admin user form",
161
            "Setting this to yes will stop user from changing their avatar, and will reset their avatar to procedurally generated one.",
162
        ),
163
    )
164
    avatar_lock_user_message = forms.CharField(
1✔
165
        label=pgettext_lazy("admin user form", "User message"),
166
        help_text=pgettext_lazy(
167
            "admin user form",
168
            "Optional message for user explaining why they are banned form changing avatar.",
169
        ),
170
        widget=forms.Textarea(attrs={"rows": 3}),
171
        required=False,
172
    )
173
    avatar_lock_staff_message = forms.CharField(
1✔
174
        label=pgettext_lazy("admin user form", "Staff message"),
175
        help_text=pgettext_lazy(
176
            "admin user form",
177
            "Optional message for forum team members explaining why user is banned form changing avatar.",
178
        ),
179
        widget=forms.Textarea(attrs={"rows": 3}),
180
        required=False,
181
    )
182

183
    signature = forms.CharField(
1✔
184
        label=pgettext_lazy("admin user form", "Signature contents"),
185
        widget=forms.Textarea(attrs={"rows": 3}),
186
        required=False,
187
    )
188
    is_signature_locked = YesNoSwitch(
1✔
189
        label=pgettext_lazy("admin user form", "Lock signature changes"),
190
        help_text=pgettext_lazy(
191
            "admin user form",
192
            "Setting this to yes will stop user from making changes to his/her signature.",
193
        ),
194
    )
195
    signature_lock_user_message = forms.CharField(
1✔
196
        label=pgettext_lazy("admin user form", "User message"),
197
        help_text=pgettext_lazy(
198
            "admin user form",
199
            "Optional message to user explaining why his/hers signature is locked.",
200
        ),
201
        widget=forms.Textarea(attrs={"rows": 3}),
202
        required=False,
203
    )
204
    signature_lock_staff_message = forms.CharField(
1✔
205
        label=pgettext_lazy("admin user form", "Staff message"),
206
        help_text=pgettext_lazy(
207
            "admin user form",
208
            "Optional message to team members explaining why user signature is locked.",
209
        ),
210
        widget=forms.Textarea(attrs={"rows": 3}),
211
        required=False,
212
    )
213

214
    is_hiding_presence = YesNoSwitch(
1✔
215
        label=pgettext_lazy("admin user form", "Hides presence")
216
    )
217

218
    limits_private_thread_invites_to = forms.TypedChoiceField(
1✔
219
        label=pgettext_lazy("admin user form", "Who can add user to private threads"),
220
        coerce=int,
221
        choices=User.LIMIT_INVITES_TO_CHOICES,
222
    )
223

224
    subscribe_to_started_threads = forms.TypedChoiceField(
1✔
225
        label=pgettext_lazy("admin user form", "Started threads"),
226
        coerce=int,
227
        choices=User.SUBSCRIPTION_CHOICES,
228
    )
229
    subscribe_to_replied_threads = forms.TypedChoiceField(
1✔
230
        label=pgettext_lazy("admin user form", "Replied threads"),
231
        coerce=int,
232
        choices=User.SUBSCRIPTION_CHOICES,
233
    )
234

235
    watch_started_threads = forms.TypedChoiceField(
1✔
236
        label=pgettext_lazy(
237
            "admin user form",
238
            "Automatically watch threads that the user has started",
239
        ),
240
        coerce=int,
241
        choices=ThreadNotifications.choices,
242
    )
243
    watch_replied_threads = forms.TypedChoiceField(
1✔
244
        label=pgettext_lazy(
245
            "admin user form",
246
            "Automatically watch threads that the user has replied to",
247
        ),
248
        coerce=int,
249
        choices=ThreadNotifications.choices,
250
    )
251
    watch_new_private_threads_by_followed = forms.TypedChoiceField(
1✔
252
        label=pgettext_lazy(
253
            "admin user form",
254
            "Automatically watch private threads that the user was invited to by users they are following",
255
        ),
256
        coerce=int,
257
        choices=ThreadNotifications.choices,
258
    )
259
    watch_new_private_threads_by_other_users = forms.TypedChoiceField(
1✔
260
        label=pgettext_lazy(
261
            "admin user form",
262
            "Automatically watch private threads that the user was invited to by other users",
263
        ),
264
        coerce=int,
265
        choices=ThreadNotifications.choices,
266
    )
267
    notify_new_private_threads_by_followed = forms.TypedChoiceField(
1✔
268
        label=pgettext_lazy(
269
            "admin user form",
270
            "Notify about new private thread invitations from users this user is following",
271
        ),
272
        coerce=int,
273
        choices=ThreadNotifications.choices,
274
    )
275
    notify_new_private_threads_by_other_users = forms.TypedChoiceField(
1✔
276
        label=pgettext_lazy(
277
            "admin user form",
278
            "Notify about new private thread invitations from other users",
279
        ),
280
        coerce=int,
281
        choices=ThreadNotifications.choices,
282
    )
283

284
    class Meta:
1✔
285
        model = User
1✔
286
        fields = [
1✔
287
            "username",
288
            "email",
289
            "title",
290
            "is_misago_root",
291
            "is_active",
292
            "is_active_staff_message",
293
            "is_avatar_locked",
294
            "avatar_lock_user_message",
295
            "avatar_lock_staff_message",
296
            "signature",
297
            "is_signature_locked",
298
            "is_hiding_presence",
299
            "limits_private_thread_invites_to",
300
            "signature_lock_user_message",
301
            "signature_lock_staff_message",
302
            "subscribe_to_started_threads",
303
            "subscribe_to_replied_threads",
304
            "watch_started_threads",
305
            "watch_replied_threads",
306
            "watch_new_private_threads_by_followed",
307
            "watch_new_private_threads_by_other_users",
308
            "notify_new_private_threads_by_followed",
309
            "notify_new_private_threads_by_other_users",
310
        ]
311

312
    def __init__(self, *args, **kwargs):
1✔
313
        super().__init__(*args, **kwargs)
1✔
314

315
        request_user = kwargs["request"].user
1✔
316

317
        if request_user.is_misago_root and request_user != self.instance:
1✔
318
            self.fields["is_misago_root"].disabled = False
1✔
319

320
        if self.instance.is_misago_admin and not (
1✔
321
            request_user.is_misago_root or request_user == self.instance
322
        ):
323
            self.credentials_require_root = True
1✔
324
            self.fields["new_password"].disabled = True
1✔
325
            self.fields["email"].disabled = True
1✔
326
        else:
327
            self.credentials_require_root = False
1✔
328

329
        if (
1✔
330
            self.instance.is_deleting_account
331
            or request_user == self.instance
332
            or (self.instance.is_misago_admin and not request_user.is_misago_root)
333
        ):
334
            self.fields["is_active"].disabled = True
1✔
335
            self.fields["is_active_staff_message"].disabled = True
1✔
336

337
        profilefields.add_fields_to_admin_form(self.request, self.instance, self)
1✔
338

339
    def get_profile_fields_groups(self):
1✔
340
        profile_fields_groups = []
1✔
341
        for group in self._profile_fields_groups:
1✔
342
            fields_group = {"name": group["name"], "fields": []}
1✔
343

344
            for fieldname in group["fields"]:
1✔
345
                fields_group["fields"].append(self[fieldname])
1✔
346

347
            profile_fields_groups.append(fields_group)
1✔
348
        return profile_fields_groups
1✔
349

350
    def clean_group(self):
1✔
351
        group = super().clean_group()
1✔
352

353
        if self.request.user.is_misago_root:
1✔
354
            return group
1✔
355

356
        if group.id != self.instance.group.id:
1✔
357
            if self.instance.group.id == DefaultGroupId.ADMINS:
1✔
358
                raise forms.ValidationError(
1✔
359
                    pgettext(
360
                        "admin user form",
361
                        "You must be a root administrator to change this user's main group from the %(group)s.",
362
                    )
363
                    % {"group": self.instance.group}
364
                )
365

366
            if group.id == DefaultGroupId.ADMINS:
1✔
367
                raise forms.ValidationError(
1✔
368
                    pgettext(
369
                        "admin user form",
370
                        "You must be a root administrator to change this user's main group to the %(group)s.",
371
                    )
372
                    % {"group": group}
373
                )
374

375
        return group
1✔
376

377
    def clean_secondary_groups(self):
1✔
378
        secondary_groups = super().clean_secondary_groups()
1✔
379

380
        if self.request.user.is_misago_root:
1✔
381
            return secondary_groups
1✔
382

383
        admins_id = DefaultGroupId.ADMINS.value
1✔
384
        initial_secondary_groups_ids = [
1✔
385
            group_id
386
            for group_id in self.instance.groups_ids
387
            if group_id != self.instance.group_id
388
        ]
389
        updated_secondary_groups_ids = [group.id for group in secondary_groups]
1✔
390

391
        if (
1✔
392
            admins_id in initial_secondary_groups_ids
393
            and admins_id not in updated_secondary_groups_ids
394
        ):
395
            raise forms.ValidationError(
1✔
396
                pgettext(
397
                    "admin user form",
398
                    "You must be a root administrator to remove this user from the %(group)s group.",
399
                )
400
                % {"group": self.groups_dict[admins_id]}
401
            )
402

403
        if (
1✔
404
            admins_id not in initial_secondary_groups_ids
405
            and admins_id in updated_secondary_groups_ids
406
        ):
407
            raise forms.ValidationError(
1✔
408
                pgettext(
409
                    "admin user form",
410
                    "You must be a root administrator to add this user to the %(group)s group.",
411
                )
412
                % {"group": self.groups_dict[admins_id]}
413
            )
414

415
        return secondary_groups
1✔
416

417
    def clean_signature(self):
1✔
418
        data = self.cleaned_data["signature"]
1✔
419

420
        length_limit = self.settings.signature_length_max
1✔
421
        if len(data) > length_limit:
1✔
UNCOV
422
            message = npgettext(
×
423
                "signature length validator",
424
                "Signature can't be longer than %(limit)s character.",
425
                "Signature can't be longer than %(limit)s characters.",
426
                length_limit,
427
            )
UNCOV
428
            raise forms.ValidationError(message % {"limit": length_limit})
×
429

430
        return data
1✔
431

432
    def clean(self):
1✔
433
        data = super().clean()
1✔
434
        return profilefields.clean_form(self.request, self.instance, self, data)
1✔
435

436

437
def user_form_factory(base_form_type: Type[BaseUserForm], instance: User):
1✔
438
    groups = list(Group.objects.all())
1✔
439
    groups_data = {group.id: group for group in groups}
1✔
440

441
    groups_choices = []
1✔
442
    for group in groups:
1✔
443
        if group.id == DefaultGroupId.ADMINS:
1✔
444
            groups_choices.append((group.id, f"{group}*"))
1✔
445
        else:
446
            groups_choices.append((group.id, str(group)))
1✔
447

448
    if instance.group_id:
1✔
449
        instance.group = groups_data[instance.group_id]
1✔
450

451
    secondary_groups_initial = instance.groups_ids[:]
1✔
452
    if instance.group_id in secondary_groups_initial:
1✔
453
        secondary_groups_initial.remove(instance.group_id)
1✔
454

455
    form_attrs = {
1✔
456
        "groups_dict": groups_data,
457
        "group": forms.TypedChoiceField(
458
            label=pgettext_lazy("admin user form", "Main group"),
459
            help_text=pgettext_lazy(
460
                "admin user form",
461
                "An asterisk (*) after the group's name signifies the administrators group.",
462
            ),
463
            coerce=int,
464
            choices=groups_choices,
465
            initial=instance.group_id,
466
        ),
467
        "secondary_groups": forms.TypedMultipleChoiceField(
468
            label=pgettext_lazy("admin user form", "Secondary groups"),
469
            help_text=pgettext_lazy(
470
                "admin user form",
471
                "Optional, other groups this user should be a member of. An asterisk (*) after the group's name signifies the administrators group.",
472
            ),
473
            coerce=int,
474
            choices=groups_choices,
475
            initial=secondary_groups_initial,
476
            widget=forms.CheckboxSelectMultiple,
477
            required=False,
478
        ),
479
        "rank": forms.ModelChoiceField(
480
            label=pgettext_lazy("admin user form", "Rank"),
481
            help_text=pgettext_lazy(
482
                "admin user form",
483
                "Ranks are used to group and distinguish users. They are also used to add permissions to groups of users.",
484
            ),
485
            queryset=Rank.objects.order_by("name"),
486
            initial=instance.rank,
487
        ),
488
    }
489

490
    roles = Role.objects.order_by("name")
1✔
491

492
    form_attrs["roles"] = forms.ModelMultipleChoiceField(
1✔
493
        label=pgettext_lazy("admin user form", "Roles"),
494
        help_text=pgettext_lazy(
495
            "admin user form",
496
            'Individual roles of this user. All users must have a "Member" role.',
497
        ),
498
        queryset=roles,
499
        initial=instance.roles.all() if instance.pk else None,
500
        widget=forms.CheckboxSelectMultiple,
501
    )
502

503
    return type("UserForm", (base_form_type,), form_attrs)
1✔
504

505

506
class BaseFilterUsersForm(forms.Form):
1✔
507
    username = forms.CharField(
1✔
508
        label=pgettext_lazy("admin users filter form", "Username"),
509
        required=False,
510
    )
511
    email = forms.CharField(
1✔
512
        label=pgettext_lazy("admin users filter form", "E-mail"),
513
        required=False,
514
    )
515
    profilefields = forms.CharField(
1✔
516
        label=pgettext_lazy("admin users filter form", "Profile fields contain"),
517
        required=False,
518
    )
519
    is_inactive = forms.BooleanField(
1✔
520
        label=pgettext_lazy("admin users filter form", "Requires activation")
521
    )
522
    is_deactivated = forms.BooleanField(
1✔
523
        label=pgettext_lazy("admin users filter form", "Account deactivated")
524
    )
525
    is_admin = forms.BooleanField(
1✔
526
        label=pgettext_lazy("admin users filter form", "Administrator")
527
    )
528
    is_deleting_account = forms.BooleanField(
1✔
529
        label=pgettext_lazy("admin users filter form", "Deletes their account")
530
    )
531

532
    def filter_queryset(self, criteria, queryset):
1✔
533
        if criteria.get("username"):
1✔
534
            queryset = filter_queryset(
1✔
535
                queryset, "slug", slugify_username(criteria["username"])
536
            )
537

538
        if criteria.get("email"):
1✔
539
            queryset = filter_queryset(
1✔
540
                queryset, "email", criteria["email"], case_sensitive=False
541
            )
542

543
        if criteria.get("main_group"):
1✔
544
            queryset = queryset.filter(group_id=criteria["main_group"])
1✔
545

546
        if criteria.get("group"):
1✔
547
            queryset = queryset.filter(groups_ids__contains=[criteria["group"]])
1✔
548

549
        if criteria.get("rank"):
1✔
550
            queryset = queryset.filter(rank_id=criteria["rank"])
1✔
551

552
        if criteria.get("role"):
1✔
553
            queryset = queryset.filter(roles__id=criteria["role"])
1✔
554

555
        if criteria.get("is_inactive"):
1✔
556
            queryset = queryset.filter(requires_activation__gt=0)
1✔
557

558
        if criteria.get("is_deactivated"):
1✔
559
            queryset = queryset.filter(is_active=False)
1✔
560

561
        if criteria.get("is_admin"):
1✔
562
            queryset = queryset.filter(
1✔
563
                Q(is_misago_root=True) | Q(groups_ids__contains=[DefaultGroupId.ADMINS])
564
            )
565

566
        if criteria.get("is_deleting_account"):
1✔
567
            queryset = queryset.filter(is_deleting_account=True)
1✔
568

569
        if criteria.get("profilefields", "").strip():
1✔
570
            queryset = profilefields.search_users(
1✔
571
                criteria.get("profilefields").strip(), queryset
572
            )
573

574
        return queryset
1✔
575

576

577
def create_filter_users_form():
1✔
578
    """
579
    Factory that uses cache for ranks and roles,
580
    and makes those ranks and roles typed choice fields that play nice
581
    with passing values via GET
582
    """
583
    groups_choices = [
1✔
584
        ("", pgettext_lazy("admin users group filter choice", "All groups"))
585
    ]
586
    for group in Group.objects.order_by("name").iterator(chunk_size=50):
1✔
587
        groups_choices.append((group.pk, str(group)))
1✔
588

589
    ranks_choices = [("", pgettext_lazy("admin users rank filter choice", "All ranks"))]
1✔
590
    for rank in Rank.objects.order_by("name").iterator(chunk_size=50):
1✔
591
        ranks_choices.append((rank.pk, str(rank)))
1✔
592

593
    roles_choices = [("", pgettext_lazy("admin users role filter choice", "All roles"))]
1✔
594
    for role in Role.objects.order_by("name").iterator(chunk_size=50):
1✔
595
        roles_choices.append((role.pk, str(role)))
1✔
596

597
    extra_fields = {
1✔
598
        "main_group": forms.TypedChoiceField(
599
            label=pgettext_lazy("admin users filter form", "Main group"),
600
            coerce=int,
601
            required=False,
602
            choices=groups_choices,
603
        ),
604
        "group": forms.TypedChoiceField(
605
            label=pgettext_lazy("admin users filter form", "Has group"),
606
            coerce=int,
607
            required=False,
608
            choices=groups_choices,
609
        ),
610
        "rank": forms.TypedChoiceField(
611
            label=pgettext_lazy("admin users filter form", "Has rank"),
612
            coerce=int,
613
            required=False,
614
            choices=ranks_choices,
615
        ),
616
        "role": forms.TypedChoiceField(
617
            label=pgettext_lazy("admin users filter form", "Has role"),
618
            coerce=int,
619
            required=False,
620
            choices=roles_choices,
621
        ),
622
    }
623

624
    return type("FilterUsersForm", (BaseFilterUsersForm,), extra_fields)
1✔
625

626

627
class BanUsersForm(forms.Form):
1✔
628
    ban_type = forms.MultipleChoiceField(
1✔
629
        label=pgettext_lazy("admin ban users form", "Values to ban"),
630
        widget=forms.CheckboxSelectMultiple,
631
        choices=[],
632
    )
633
    user_message = forms.CharField(
1✔
634
        label=pgettext_lazy("admin ban users form", "User message"),
635
        required=False,
636
        max_length=1000,
637
        help_text=pgettext_lazy(
638
            "admin ban users form",
639
            "Optional message displayed to users instead of default one.",
640
        ),
641
        widget=forms.Textarea(attrs={"rows": 3}),
642
        error_messages={
643
            "max_length": pgettext_lazy(
644
                "admin ban users form", "Message can't be longer than 1000 characters."
645
            )
646
        },
647
    )
648
    staff_message = forms.CharField(
1✔
649
        label=pgettext_lazy("admin ban users form", "Team message"),
650
        required=False,
651
        max_length=1000,
652
        help_text=pgettext_lazy(
653
            "admin ban users form",
654
            "Optional ban message for moderators and administrators.",
655
        ),
656
        widget=forms.Textarea(attrs={"rows": 3}),
657
        error_messages={
658
            "max_length": pgettext_lazy(
659
                "admin ban users form", "Message can't be longer than 1000 characters."
660
            )
661
        },
662
    )
663
    expires_on = IsoDateTimeField(
1✔
664
        label=pgettext_lazy("admin ban users form", "Expiration date"), required=False
665
    )
666

667
    def __init__(self, *args, **kwargs):
1✔
668
        users = kwargs.pop("users")
1✔
669

670
        super().__init__(*args, **kwargs)
1✔
671

672
        self.fields["ban_type"].choices = [
1✔
673
            ("usernames", pgettext_lazy("admin ban users form", "Usernames")),
674
            ("emails", pgettext_lazy("admin ban users form", "E-mails")),
675
            ("domains", pgettext_lazy("admin ban users form", "E-mail domains")),
676
        ]
677

678
        enable_ip_bans = list(filter(None, [u.joined_from_ip for u in users]))
1✔
679
        if enable_ip_bans:
1✔
680
            self.fields["ban_type"].choices += [
1✔
681
                ("ip", pgettext_lazy("admin ban users form", "IP addresses")),
682
                (
683
                    "ip_first",
684
                    pgettext_lazy(
685
                        "admin ban users form", "First segment of IP addresses"
686
                    ),
687
                ),
688
                (
689
                    "ip_two",
690
                    pgettext_lazy(
691
                        "admin ban users form", "First two segments of IP addresses"
692
                    ),
693
                ),
694
            ]
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc