diff --git a/jdav_web/members/admin.py b/jdav_web/members/admin.py index e2d6d22..8b32678 100644 --- a/jdav_web/members/admin.py +++ b/jdav_web/members/admin.py @@ -87,9 +87,18 @@ class MemberAdmin(admin.ModelAdmin): #ordering = ('activity_score',) actions = ['send_mail_to', 'request_echo'] + def get_fields(self, request, obj=None): + if request.user.has_perm('members.may_set_auth_user'): + if 'user' not in self.fields: + self.fields.append('user') + else: + if 'user' in self.fields: + self.fields.remove('user') + return super(MemberAdmin, self).get_fields(request, obj) + def get_queryset(self, request): queryset = super().get_queryset(request) - return annotate_activity_score(queryset.filter(confirmed=True)) + return annotate_activity_score(queryset) def change_view(self, request, object_id, form_url="", extra_context=None): extra_context = extra_context or {} @@ -167,7 +176,13 @@ class MemberUnconfirmedAdmin(admin.ModelAdmin): def get_queryset(self, request): queryset = super().get_queryset(request) - return queryset.filter(confirmed=False) + if request.user.has_perm('members.may_manage_all_registrations'): + return queryset + if request.user.member is None: + return MemberUnconfirmedProxy.objects.none() + groups = request.user.member.leited_groups.all() + # this is magic (the first part, group is a manytomanyfield) but seems to work + return queryset.filter(group__in=groups).distinct() def request_mail_confirmation(self, request, queryset): for member in queryset: @@ -209,8 +224,20 @@ class RegistrationPasswordInline(admin.TabularInline): extra = 0 +class GroupAdminForm(forms.ModelForm): + class Meta: + model = Freizeit + exclude = ['add_member'] + + + def __init__(self, *args, **kwargs): + super(GroupAdminForm, self).__init__(*args, **kwargs) + self.fields['leiters'].queryset = Member.objects.filter(group__name='Jugendleiter') + + class GroupAdmin(admin.ModelAdmin): - fields = ['name', 'year_from', 'year_to'] + fields = ['name', 'year_from', 'year_to', 'leiters'] + form = GroupAdminForm list_display = ('name', 'year_from', 'year_to') inlines = [RegistrationPasswordInline] diff --git a/jdav_web/members/locale/de/LC_MESSAGES/django.po b/jdav_web/members/locale/de/LC_MESSAGES/django.po index b572d75..edcdcf1 100644 --- a/jdav_web/members/locale/de/LC_MESSAGES/django.po +++ b/jdav_web/members/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-10-03 18:21+0200\n" +"POT-Creation-Date: 2022-10-03 23:57+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,7 +18,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: members/admin.py:33 members/models.py:80 +#: members/admin.py:33 members/models.py:88 msgid "Registration complete" msgstr "Anmeldung vollständig" @@ -34,308 +34,312 @@ msgstr "Nein" msgid "All" msgstr "Alle" -#: members/admin.py:108 +#: members/admin.py:117 msgid "Compose new mail to selected members" msgstr "Neue Nachricht an ausgewählte Teilnehmer verfassen" -#: members/admin.py:133 +#: members/admin.py:142 msgid "Successfully requested echo from selected members." msgstr "" "Rückmeldungsaufforderung erfolgreich an ausgewählte Teilnehmer verschickt." -#: members/admin.py:134 +#: members/admin.py:143 msgid "Request echo from selected members" msgstr "Rückmeldungsaufforderung an ausgewählte Teilnehmer verschicken" -#: members/admin.py:151 +#: members/admin.py:160 msgid "activity" msgstr "Aktivität" -#: members/admin.py:175 +#: members/admin.py:190 msgid "Successfully requested mail confirmation from selected registrations." msgstr "Aufforderung zur Bestätigung der Email Adresse versendet" -#: members/admin.py:176 +#: members/admin.py:191 msgid "Request mail confirmation from selected registrations" msgstr "Aufforderung zur Bestätigung der Email Adresse versenden" -#: members/admin.py:183 members/admin.py:200 +#: members/admin.py:198 members/admin.py:215 #, python-format msgid "Successfully confirmed %(name)s." msgstr "Registrierung von %(name)s erfolgreich bestätigt." -#: members/admin.py:187 members/admin.py:203 +#: members/admin.py:202 members/admin.py:218 #, python-format msgid "Can't confirm. %(name)s has unconfirmed email addresses." msgstr "Bestätigung nicht möglich. %(name)s hat unbestätigte Emailadressen." -#: members/admin.py:192 +#: members/admin.py:207 msgid "Successfully confirmed multiple registrations." msgstr "Erfolgreich mehrere Registrierungen bestätigt." -#: members/admin.py:194 +#: members/admin.py:209 msgid "" "Failed to confirm some registrations because of unconfirmed email addresses." msgstr "" "Einige Bestätigungen fehlgeschlagen, weil Emailadressen noch nicht bestätigt " "sind." -#: members/admin.py:195 +#: members/admin.py:210 msgid "Confirm selected registrations" msgstr "Ausgewählte Registrierungen bestätigen" -#: members/admin.py:225 +#: members/admin.py:252 msgid "Difficulty" msgstr "Schwierigkeit" -#: members/admin.py:228 members/admin.py:231 +#: members/admin.py:255 members/admin.py:258 msgid "Tour type" msgstr "Art der Tour" -#: members/admin.py:529 +#: members/admin.py:556 msgid "Convert to PDF" msgstr "Kriseninterventionsliste erstellen" -#: members/admin.py:638 +#: members/admin.py:665 msgid "Generate overview" msgstr "Hinweise für Jugendleiter erstellen" -#: members/admin.py:735 +#: members/admin.py:762 msgid "Generate list for LJP" msgstr "LJP Liste erstellen" -#: members/apps.py:7 members/models.py:229 +#: members/apps.py:7 members/models.py:240 msgid "members" msgstr "Teilnehmer" -#: members/models.py:26 +#: members/models.py:27 msgid "Name" msgstr "Name" -#: members/models.py:27 +#: members/models.py:28 msgid "Description" msgstr "Beschreibung" -#: members/models.py:33 members/models.py:266 members/models.py:345 +#: members/models.py:34 members/models.py:285 members/models.py:364 #: members/templates/members/change_member.html:17 msgid "Activity" msgstr "Aktivität" -#: members/models.py:34 +#: members/models.py:35 msgid "Activities" msgstr "Aktivitäten" -#: members/models.py:42 +#: members/models.py:43 msgid "name" msgstr "Name" -#: members/models.py:43 +#: members/models.py:44 msgid "lowest year" msgstr "Ab Jahrgang" -#: members/models.py:44 +#: members/models.py:45 msgid "highest year" msgstr "Bis Jahrgang" -#: members/models.py:51 members/models.py:73 +#: members/models.py:46 +msgid "youth leaders" +msgstr "Jugendleiter" + +#: members/models.py:54 members/models.py:81 msgid "group" msgstr "Gruppe" -#: members/models.py:52 +#: members/models.py:55 msgid "groups" msgstr "Gruppen" -#: members/models.py:60 +#: members/models.py:68 msgid "prename" msgstr "Vorname" -#: members/models.py:61 +#: members/models.py:69 msgid "last name" msgstr "Nachname" -#: members/models.py:62 +#: members/models.py:70 msgid "street" msgstr "Straße" -#: members/models.py:63 +#: members/models.py:71 msgid "Postcode" msgstr "PLZ" -#: members/models.py:65 +#: members/models.py:73 msgid "town" msgstr "Stadt" -#: members/models.py:66 +#: members/models.py:74 msgid "phone number" msgstr "Telefonnummer" -#: members/models.py:67 +#: members/models.py:75 msgid "parents phone number" msgstr "Telefonnummer der Eltern" -#: members/models.py:70 +#: members/models.py:78 msgid "Parents' Email" msgstr "Email der Eltern" -#: members/models.py:71 +#: members/models.py:79 msgid "Also send mails to parents" msgstr "Emails auch an Eltern schicken" -#: members/models.py:72 +#: members/models.py:80 msgid "birth date" msgstr "Geburtsdatum" -#: members/models.py:74 +#: members/models.py:82 msgid "receives newsletter" msgstr "Erhält den Newsletter" -#: members/models.py:78 +#: members/models.py:86 msgid "comments" msgstr "Kommentare" -#: members/models.py:79 +#: members/models.py:87 msgid "created" msgstr "erstellt" -#: members/models.py:81 +#: members/models.py:89 msgid "Active" msgstr "Aktiv" -#: members/models.py:82 +#: members/models.py:90 msgid "Not waiting" msgstr "NICHT Warteliste" -#: members/models.py:83 +#: members/models.py:91 msgid "registration form" msgstr "Anmeldeformular" -#: members/models.py:93 +#: members/models.py:101 msgid "Echoed" msgstr "Rückgemeldet" -#: members/models.py:94 +#: members/models.py:102 msgid "Confirmed" msgstr "Bestätigt" -#: members/models.py:95 +#: members/models.py:103 msgid "Email confirmed" msgstr "Emailadresse bestätigt" -#: members/models.py:96 +#: members/models.py:104 msgid "Parents email confirmed" msgstr "Emailadresse der Eltern bestätigt" -#: members/models.py:126 members/models.py:136 +#: members/models.py:137 members/models.py:147 msgid "Email confirmation" msgstr "Email Bestätigung" -#: members/models.py:225 members/models.py:427 +#: members/models.py:236 members/models.py:446 msgid "Group" msgstr "Gruppe" -#: members/models.py:228 +#: members/models.py:239 msgid "member" msgstr "Teilnehmer" -#: members/models.py:252 +#: members/models.py:270 msgid "Unconfirmed registration" msgstr "Unbestätigte Registrierung" -#: members/models.py:253 +#: members/models.py:271 msgid "Unconfirmed registrations" msgstr "Unbestätigte Registrierungen" -#: members/models.py:268 members/models.py:347 +#: members/models.py:287 members/models.py:366 msgid "Place" msgstr "Ort" -#: members/models.py:269 members/models.py:348 +#: members/models.py:288 members/models.py:367 msgid "Destination (optional)" msgstr "Ziel (optional)" -#: members/models.py:271 members/models.py:350 members/models.py:405 -#: members/models.py:423 +#: members/models.py:290 members/models.py:369 members/models.py:424 +#: members/models.py:442 msgid "Date" msgstr "Datum" -#: members/models.py:272 members/models.py:351 +#: members/models.py:291 members/models.py:370 msgid "End (optional)" msgstr "Ende" -#: members/models.py:274 members/models.py:353 +#: members/models.py:293 members/models.py:372 msgid "Groups" msgstr "Gruppen" -#: members/models.py:282 members/models.py:366 +#: members/models.py:301 members/models.py:385 msgid "Categories" msgstr "Kategorien" -#: members/models.py:283 members/models.py:367 +#: members/models.py:302 members/models.py:386 msgid "easy" msgstr "leicht" -#: members/models.py:283 members/models.py:367 +#: members/models.py:302 members/models.py:386 msgid "medium" msgstr "mittel" -#: members/models.py:283 members/models.py:367 +#: members/models.py:302 members/models.py:386 msgid "hard" msgstr "schwer" -#: members/models.py:292 +#: members/models.py:311 msgid "Memberlist" msgstr "Teilnehmerliste" -#: members/models.py:293 +#: members/models.py:312 msgid "Memberlists" msgstr "Teilnehmerlisten" -#: members/models.py:311 members/models.py:319 members/models.py:327 -#: members/models.py:338 members/models.py:458 members/models.py:465 +#: members/models.py:330 members/models.py:338 members/models.py:346 +#: members/models.py:357 members/models.py:477 members/models.py:484 msgid "Member" msgstr "Teilnehmer" -#: members/models.py:313 members/models.py:332 +#: members/models.py:332 members/models.py:351 msgid "Comment" msgstr "Kommentar" -#: members/models.py:320 members/models.py:339 members/models.py:466 +#: members/models.py:339 members/models.py:358 members/models.py:485 msgid "Members" msgstr "Teilnehmer" -#: members/models.py:404 +#: members/models.py:423 msgid "Title" msgstr "Titel" -#: members/models.py:424 +#: members/models.py:443 msgid "Location" msgstr "Ort" -#: members/models.py:425 +#: members/models.py:444 msgid "Topic" msgstr "Thema" -#: members/models.py:449 +#: members/models.py:468 msgid "Jugendleiter" msgstr "Jugendleiter" -#: members/models.py:452 +#: members/models.py:471 msgid "Klettertreff" msgstr "Klettertreff" -#: members/models.py:453 +#: members/models.py:472 msgid "Klettertreffs" msgstr "Klettertreffs" -#: members/models.py:471 +#: members/models.py:490 msgid "Password" msgstr "Passwort" -#: members/models.py:474 +#: members/models.py:493 msgid "registration password" msgstr "Registrierungspassort" -#: members/models.py:475 +#: members/models.py:494 msgid "registration passwords" msgstr "Registrierungspasswörter" diff --git a/jdav_web/members/models.py b/jdav_web/members/models.py index dae9798..a74bbc7 100644 --- a/jdav_web/members/models.py +++ b/jdav_web/members/models.py @@ -11,6 +11,7 @@ from django.contrib.contenttypes.models import ContentType from utils import RestrictedFileField import os from mailer.mailutils import send as send_mail, mail_root, get_mail_confirmation_link +from django.contrib.auth.models import User from dateutil.relativedelta import relativedelta @@ -43,7 +44,7 @@ class Group(models.Model): year_from = models.IntegerField(verbose_name=_('lowest year'), default=2010) year_to = models.IntegerField(verbose_name=_('highest year'), default=2011) leiters = models.ManyToManyField('members.Member', verbose_name=_('youth leaders'), - related_name='leiters', blank=True) + related_name='leited_groups', blank=True) def __str__(self): """String representation""" @@ -54,6 +55,11 @@ class Group(models.Model): verbose_name_plural = _('groups') +class MemberManager(models.Manager): + def get_queryset(self): + return super().get_queryset().filter(confirmed=True) + + class Member(models.Model): """ Represents a member of the association @@ -98,6 +104,9 @@ class Member(models.Model): confirmed_mail_parents = models.BooleanField(default=True, verbose_name=_('Parents email confirmed')) confirm_mail_key = models.CharField(max_length=32, default="") confirm_mail_parents_key = models.CharField(max_length=32, default="") + user = models.OneToOneField(User, blank=True, null=True, on_delete=models.SET_NULL) + + objects = MemberManager() def __str__(self): """String representation""" @@ -229,7 +238,8 @@ class Member(models.Model): class Meta: verbose_name = _('member') verbose_name_plural = _('members') - permissions = (('may_see_qualities', 'Is allowed to see the quality overview'),) + permissions = (('may_see_qualities', 'Is allowed to see the quality overview'), + ('may_set_auth_user', 'Is allowed to set auth user member connections.')) def get_skills(self): # get skills by summing up all the activities taken part in @@ -246,13 +256,20 @@ class Member(models.Model): return Freizeit.objects.filter(membersonlist__member=self) +class MemberUnconfirmedManager(models.Manager): + def get_queryset(self): + return super().get_queryset().filter(confirmed=False) + + class MemberUnconfirmedProxy(Member): """Proxy to show unconfirmed members seperately in admin""" + objects = MemberUnconfirmedManager() class Meta: proxy = True verbose_name = _('Unconfirmed registration') verbose_name_plural = _('Unconfirmed registrations') + permissions = (('may_manage_all_registrations', 'Can view and manage all unconfirmed registrations.'),) def __str__(self): """String representation"""