diff --git a/docker/development/docker.env.example b/docker/development/docker.env.example new file mode 100644 index 0000000..1e1131d --- /dev/null +++ b/docker/development/docker.env.example @@ -0,0 +1,33 @@ +DJANGO_ALLOWED_HOST='*' +DJANGO_BASE_URL='localhost:8000' +DJANGO_PROTOCOL='http' + +EMAIL_HOST='example.com' +EMAIL_HOST_USER='foo@example.com' +EMAIL_HOST_PASSWORD='password' +EMAIL_SENDING_ADDRESS='foo@example.com' +EMAIL_SENDING_NAME='My association' + +DOMAIN='example.com' + +DJANGO_DEPLOY=1 +DJANGO_DEBUG=1 + +DJANGO_DATABASE_NAME='kompass' +DJANGO_DATABASE_USER='kompass' +DJANGO_DATABASE_PASSWORD='foobar' +DJANGO_DATABASE_HOST='db' +DJANGO_DATABASE_PORT=3306 + +MYSQL_ROOT_PASSWORD='secret' +MYSQL_PASSWORD='foobar' +MYSQL_USER='kompass' +MYSQL_DATABASE='kompass' + +DJANGO_SETTINGS_MODULE='jdav_web.settings' +DJANGO_STATIC_ROOT='/var/www/jdav_web/assets' + +MEMCACHED_URL='cache:11211' +BROKER_URL='redis://redis:6379/0' + +SEND_FROM_ASSOCIATION_EMAIL=0 diff --git a/jdav_web/jdav_web/settings/components/base.py b/jdav_web/jdav_web/settings/components/base.py index 2717694..bbe5684 100644 --- a/jdav_web/jdav_web/settings/components/base.py +++ b/jdav_web/jdav_web/settings/components/base.py @@ -191,3 +191,6 @@ MARKDOWNIFY = { } } } + +# allowed characters in names appearing in urls on the website +STARTPAGE_URL_NAME_PATTERN = "[\w\-: *]" diff --git a/jdav_web/jdav_web/settings/components/texts.py b/jdav_web/jdav_web/settings/components/texts.py index 68042f8..c2ad0ef 100644 --- a/jdav_web/jdav_web/settings/components/texts.py +++ b/jdav_web/jdav_web/settings/components/texts.py @@ -35,7 +35,7 @@ Bitte kontaktiere die Gruppenleitung ({contact_email}) für alle weiteren Abspra Wenn du nach der Schnupperstunde beschließt der Gruppe beizutreten, benötigen wir noch ein paar Informationen und deine Anmeldebestätigung von dir. Die lädst du herunter -(siehe %(REGISTRATION_FORM_DOWNLOAD_LINK)s), lässt sie von deinen Eltern ausfüllen, unterschreiben +(siehe %(REGISTRATION_FORM_DOWNLOAD_LINK)s ), lässt sie von deinen Eltern ausfüllen, unterschreiben und lädst ein Foto davon in unserem Anmeldeformular hoch. Das kannst du alles über folgenden Link erledigen: {link} diff --git a/jdav_web/jdav_web/settings/local.py b/jdav_web/jdav_web/settings/local.py index c2c96c9..d2bd9eb 100644 --- a/jdav_web/jdav_web/settings/local.py +++ b/jdav_web/jdav_web/settings/local.py @@ -45,9 +45,9 @@ SEND_FROM_ASSOCIATION_EMAIL = os.environ.get('SEND_FROM_ASSOCIATION_EMAIL', '0') ALLOWANCE_PER_DAY = 22 MAX_NIGHT_COST = 11 -CLOUD_LINK = 'https://nc.cloud-jdav-hd.de' -DAV_360_LINK = 'https://dav360.de' -WIKI_LINK = 'https://davbgs.sharepoint.com/sites/S-114-O-JDAV-Jugendreferat' +CLOUD_LINK = os.environ.get('CLOUD_LINK', 'https://startpage.com') +DAV_360_LINK = os.environ.get('DAV_360_LINK', 'https://dav360.de') +WIKI_LINK = os.environ.get('WIKI_LINK', 'https://wikipedia.org') DOCS_LINK = os.environ.get('DOCS_LINK', 'https://jdav-hd.de/static/docs/') # Admin setup @@ -65,7 +65,7 @@ MAX_REMINDER_COUNT = 3 TEST_MAIL = "post@flavigny.de" -REGISTRATION_FORM_DOWNLOAD_LINK = 'https://nc.cloud-jdav-hd.de' +REGISTRATION_FORM_DOWNLOAD_LINK = os.environ.get('REGISTRATION_FORM_DOWNLOAD_LINK', 'https://startpage.com') DOMAIN = os.environ.get('DOMAIN', 'example.com') diff --git a/jdav_web/mailer/migrations/0008_alter_emailaddress_name.py b/jdav_web/mailer/migrations/0008_alter_emailaddress_name.py new file mode 100644 index 0000000..952bacd --- /dev/null +++ b/jdav_web/mailer/migrations/0008_alter_emailaddress_name.py @@ -0,0 +1,19 @@ +# Generated by Django 4.0.1 on 2024-12-03 23:19 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mailer', '0007_emailaddress_internal_only'), + ] + + operations = [ + migrations.AlterField( + model_name='emailaddress', + name='name', + field=models.CharField(max_length=50, unique=True, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z._-]*$', 'Only alphanumeric characters, ., - and _ are allowed')], verbose_name='name'), + ), + ] diff --git a/jdav_web/mailer/models.py b/jdav_web/mailer/models.py index bed3877..685be2f 100644 --- a/jdav_web/mailer/models.py +++ b/jdav_web/mailer/models.py @@ -22,7 +22,8 @@ alphanumeric = RegexValidator(r'^[0-9a-zA-Z._-]*$', class EmailAddress(models.Model): """Represents an email address, that is forwarded to specific members""" - name = models.CharField(_('name'), max_length=50, validators=[alphanumeric]) + name = models.CharField(_('name'), max_length=50, validators=[alphanumeric], + unique=True) to_members = models.ManyToManyField('members.Member', verbose_name=_('Forward to participants'), blank=True) @@ -63,6 +64,7 @@ class EmailAddressForm(forms.ModelForm): exclude = [] def clean(self): + super(EmailAddressForm, self).clean() group = self.cleaned_data.get('to_groups') members = self.cleaned_data.get('to_members') if not group and not members: diff --git a/jdav_web/members/admin.py b/jdav_web/members/admin.py index d4ca8c8..65fa705 100644 --- a/jdav_web/members/admin.py +++ b/jdav_web/members/admin.py @@ -15,6 +15,7 @@ from django.template.loader import get_template from django.urls import path, reverse from django.http import HttpResponse, HttpResponseRedirect from wsgiref.util import FileWrapper +from django.utils import timezone from django import forms from django.contrib import admin, messages from django.contrib.admin import DateFieldListFilter @@ -406,7 +407,7 @@ class MemberUnconfirmedAdmin(CommonAdminMixin, admin.ModelAdmin): 'comments', 'legal_guardians', 'dav_badge_no', - 'active', 'echoed', + 'echoed', 'user', ] } @@ -439,7 +440,7 @@ class MemberUnconfirmedAdmin(CommonAdminMixin, admin.ModelAdmin): search_fields = ('prename', 'lastname', 'email') list_filter = ('group', 'confirmed_mail', 'confirmed_alternative_mail') readonly_fields = ['confirmed_mail', 'confirmed_alternative_mail', - 'good_conduct_certificate_valid'] + 'good_conduct_certificate_valid', 'echoed'] actions = ['request_mail_confirmation', 'confirm', 'demote_to_waiter_action'] inlines = [EmergencyContactInline] change_form_template = "members/change_member_unconfirmed.html" @@ -453,6 +454,7 @@ class MemberUnconfirmedAdmin(CommonAdminMixin, admin.ModelAdmin): field_change_permissions = { 'user': 'members.may_set_auth_user', + 'group': 'members.may_change_member_group', 'good_conduct_certificate_presented_date': 'members.may_change_organizationals', 'has_key': 'members.may_change_organizationals', 'has_free_ticket_gym': 'members.may_change_organizationals', @@ -541,17 +543,8 @@ class MemberUnconfirmedAdmin(CommonAdminMixin, admin.ModelAdmin): def demote_to_waiter(self, request, queryset): for member in queryset: - waiter = MemberWaitingList(prename=member.prename, - lastname=member.lastname, - email=member.email, - birth_date=member.birth_date, - gender=member.gender, - comments=member.comments, - confirmed_mail=member.confirmed_mail, - confirm_mail_key=member.confirm_mail_key) - waiter.save() - member.delete() - messages.success(request, _("Successfully demoted %(name)s to waiter.") % {'name': waiter.name}) + member.demote_to_waiter() + messages.success(request, _("Successfully demoted %(name)s to waiter.") % {'name': member.name}) def response_change(self, request, member): if "_confirm" in request.POST: @@ -580,14 +573,28 @@ class InvitationToGroupAdmin(admin.TabularInline): return False +class InvitedToGroupFilter(admin.SimpleListFilter): + title = _('Pending group invitation for group') + parameter_name = 'pending_group_invitation' + + def lookups(self, request, model_admin): + return [(g.pk, g.name) for g in Group.objects.all()] + + def queryset(self, request, queryset): + pk = self.value() + if not pk: + return queryset + return queryset.filter(invitationtogroup__group__pk=pk, invitationtogroup__rejected=False, + invitationtogroup__date__gt=(timezone.now() - timezone.timedelta(days=30)).date()).distinct() + + class MemberWaitingListAdmin(CommonAdminMixin, admin.ModelAdmin): fields = ['prename', 'lastname', 'email', 'birth_date', 'gender', 'application_text', - 'application_date', 'comments', - 'sent_reminders'] - list_display = ('name', 'birth_date', 'age', 'application_date', 'confirmed_mail', - 'waiting_confirmed', 'sent_reminders') + 'application_date', 'comments', 'sent_reminders'] + list_display = ('name', 'birth_date', 'age', 'gender', 'application_date', 'latest_group_invitation', + 'confirmed_mail', 'waiting_confirmed', 'sent_reminders') search_fields = ('prename', 'lastname', 'email') - list_filter = ('confirmed_mail',) + list_filter = ['confirmed_mail', 'gender', InvitedToGroupFilter] actions = ['ask_for_registration', 'ask_for_wait_confirmation'] inlines = [InvitationToGroupAdmin] readonly_fields= ['application_date', 'sent_reminders'] @@ -662,6 +669,10 @@ class MemberWaitingListAdmin(CommonAdminMixin, admin.ModelAdmin): ] return custom_urls + urls + def get_queryset(self, request): + queryset = super().get_queryset(request) + return queryset.prefetch_related('invitationtogroup_set') + def invite_view(self, request, object_id): waiter = MemberWaitingList.objects.get(pk=object_id) @@ -704,6 +715,10 @@ class RegistrationPasswordInline(admin.TabularInline): class GroupAdminForm(forms.ModelForm): + name = forms.RegexField(regex=r'^{pattern}+$'.format(pattern=settings.STARTPAGE_URL_NAME_PATTERN), + label=_('name'), + error_messages={'invalid': _('The group name may only consist of letters, numerals, _, -, :, * and spaces.')}) + class Meta: model = Freizeit exclude = ['add_member'] diff --git a/jdav_web/members/locale/de/LC_MESSAGES/django.po b/jdav_web/members/locale/de/LC_MESSAGES/django.po index af1e6d9..15d2225 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: 2024-12-03 00:26+0100\n" +"POT-Creation-Date: 2024-12-14 16:49+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,186 +18,190 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: members/admin.py:126 members/models.py:391 +#: members/admin.py:127 members/models.py:404 msgid "Registration complete" msgstr "Anmeldung vollständig" -#: members/admin.py:132 +#: members/admin.py:133 msgid "True" msgstr "Ja" -#: members/admin.py:133 +#: members/admin.py:134 msgid "False" msgstr "Nein" -#: members/admin.py:134 +#: members/admin.py:135 msgid "All" msgstr "Alle" -#: members/admin.py:184 members/admin.py:413 +#: members/admin.py:185 members/admin.py:415 msgid "Contact information" msgstr "Kontaktinformationen" -#: members/admin.py:189 members/admin.py:418 +#: members/admin.py:190 members/admin.py:420 msgid "Skills" msgstr "Fähigkeiten" -#: members/admin.py:194 members/admin.py:423 +#: members/admin.py:195 members/admin.py:425 msgid "Others" msgstr "Sonstiges" -#: members/admin.py:200 members/admin.py:428 +#: members/admin.py:201 members/admin.py:430 msgid "Organizational" msgstr "Organisatorisches" -#: members/admin.py:281 +#: members/admin.py:283 msgid "Compose new mail to selected members" msgstr "Neue Nachricht an ausgewählte Teilnehmer*innen verfassen" -#: members/admin.py:287 +#: members/admin.py:289 msgid "Echo required" msgstr "Rückmeldung erforderlich" -#: members/admin.py:289 +#: members/admin.py:291 msgid "Successfully requested echo from selected members." msgstr "" "Rückmeldungsaufforderung erfolgreich an ausgewählte Teilnehmer*innen " "verschickt." -#: members/admin.py:290 +#: members/admin.py:292 msgid "Request echo from selected members" msgstr "Rückmeldungsaufforderung an ausgewählte Teilnehmer*innen verschicken" -#: members/admin.py:299 +#: members/admin.py:301 #, python-format msgid "%(name)s does not have a DAV360 email address or is already registered." msgstr "%(name)s hat keine DAV360 E-Mail Adresse oder ist bereits registriert." -#: members/admin.py:301 +#: members/admin.py:303 #, python-format msgid "Successfully invited %(name)s as user." msgstr "Erfolgreich %(name)s aufgefordert Zugangsdaten zu wählen." -#: members/admin.py:303 +#: members/admin.py:305 msgid "Successfully invited selected members to join as users." msgstr "" "Erfolgreich ausgewählte Teilnehmer*innen aufgefordert Zugangsdaten zu wählen." -#: members/admin.py:305 +#: members/admin.py:307 msgid "Some members have been invited, others could not be invited." msgstr "" "Manche Teilnehmer*innen wurden eingeladen, andere konnten nicht eingeladen " "werden." -#: members/admin.py:312 members/admin.py:329 +#: members/admin.py:314 members/admin.py:331 msgid "Permission denied." msgstr "Fehlende Berechtigungen." -#: members/admin.py:319 members/admin.py:353 +#: members/admin.py:321 members/admin.py:355 #: members/templates/admin/invite_as_user.html:21 msgid "Invite as user" msgstr "Kompass Zugangsdaten wählen lassen" -#: members/admin.py:324 +#: members/admin.py:326 msgid "Invite selected members to join Kompass as users." msgstr "Ausgewählte Teilnehmer*innen Kompass Zugangsdaten wählen lassen." -#: members/admin.py:335 +#: members/admin.py:337 msgid "Member not found." msgstr "Teilnehmer*in nicht gefunden." -#: members/admin.py:339 +#: members/admin.py:341 #, python-format msgid "%(name)s already has login data." msgstr "%(name)s hat schon Zugangsdaten." -#: members/admin.py:344 +#: members/admin.py:346 #, python-format msgid "The configured email address for %(name)s is not an internal one." msgstr "Die für %(name)s eingestellte E-Mail Adresse ist keine DAV360 Adresse." -#: members/admin.py:358 +#: members/admin.py:360 #, python-format msgid "%(name)s already has a pending invitation as user." msgstr "" "%(name)s hat bereits eine ausstehende Aufforderung Zugangsdaten zu wählen." -#: members/admin.py:376 +#: members/admin.py:378 msgid "activity" msgstr "Aktivität" -#: members/admin.py:386 members/models.py:55 members/models.py:1506 +#: members/admin.py:388 members/models.py:56 members/models.py:1527 msgid "Name" msgstr "Name" -#: members/admin.py:476 +#: members/admin.py:478 msgid "Successfully requested mail confirmation from selected registrations." msgstr "Aufforderung zur Bestätigung der Email Adresse versendet." -#: members/admin.py:477 +#: members/admin.py:479 msgid "Request mail confirmation from selected registrations" msgstr "Aufforderung zur Bestätigung der Email Adresse versenden" -#: members/admin.py:484 members/admin.py:558 +#: members/admin.py:486 members/admin.py:560 #, python-format msgid "Successfully confirmed %(name)s." msgstr "Registrierung von %(name)s erfolgreich bestätigt." -#: members/admin.py:488 members/admin.py:561 +#: members/admin.py:490 members/admin.py:563 #, 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:493 +#: members/admin.py:495 msgid "Successfully confirmed multiple registrations." msgstr "Erfolgreich mehrere Registrierungen bestätigt." -#: members/admin.py:495 +#: members/admin.py:497 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:496 +#: members/admin.py:498 msgid "Confirm selected registrations" msgstr "Ausgewählte Registrierungen bestätigen" -#: members/admin.py:519 +#: members/admin.py:521 msgid "Demote selected registrations to waiters." msgstr "Ausgewählte Registrierungen zurück auf die Warteliste setzen." -#: members/admin.py:535 +#: members/admin.py:537 msgid "Demote member to waiter" msgstr "Ausgewählte Registrierung zurück auf die Warteliste setzen." -#: members/admin.py:553 +#: members/admin.py:555 #, python-format msgid "Successfully demoted %(name)s to waiter." msgstr "%(name)s zurück auf die Warteliste gesetzt." -#: members/admin.py:568 members/models.py:398 members/models.py:764 -#: members/models.py:1251 +#: members/admin.py:570 members/models.py:411 members/models.py:777 +#: members/models.py:1272 msgid "Group" msgstr "Gruppe" -#: members/admin.py:602 +#: members/admin.py:585 +msgid "Pending group invitation for group" +msgstr "Ausstehende Gruppeneinladung für Gruppe" + +#: members/admin.py:618 #, python-format msgid "Successfully asked %(name)s to confirm their waiting status." msgstr "Erfolgreich %(name)s aufgefordert den Wartelistenplatz zu bestätigen." -#: members/admin.py:603 +#: members/admin.py:619 msgid "Ask selected waiters to confirm their waiting status" msgstr "Wartende auffordern den Wartelistenplatz zu bestätigen" -#: members/admin.py:612 members/admin.py:672 +#: members/admin.py:628 members/admin.py:688 msgid "" "An error occurred while trying to invite said members. Please try again." msgstr "" "Beim Einladen dieser Personen ist ein Fehler aufgetreten. Bitte versuche es " "nochmal. " -#: members/admin.py:616 members/admin.py:677 +#: members/admin.py:632 members/admin.py:693 msgid "" "The selected group does not have a contact email. Please first set a contact " "email and then try again." @@ -205,32 +209,43 @@ msgstr "" "Die ausgewählte Gruppe hat keine Kontakt E-Mail Adresse. Bitte stelle eine " "Kontakt E-Mail Adresse ein und versuche es erneut." -#: members/admin.py:624 members/admin.py:684 +#: members/admin.py:640 members/admin.py:700 #, python-format msgid "Successfully invited %(name)s to %(group)s." msgstr "Erfolgreich %(name)s zu Gruppe %(group)s eingeladen." -#: members/admin.py:628 members/admin.py:690 +#: members/admin.py:644 members/admin.py:706 msgid "Select group for invitation" msgstr "Wähle Gruppe für Einladung aus" -#: members/admin.py:635 +#: members/admin.py:651 msgid "Offer waiter a place in a group." msgstr "Personen auf der Warteliste einen Gruppenplatz anbieten." -#: members/admin.py:733 +#: members/admin.py:723 members/models.py:72 +msgid "name" +msgstr "Name" + +#: members/admin.py:724 +msgid "" +"The group name may only consist of letters, numerals, _, -, :, * and spaces." +msgstr "" +"Der Gruppenname darf nur aus Buchstaben, Zahlen, _, -, :, * oder Leerzeichen " +"bestehen." + +#: members/admin.py:753 msgid "Difficulty" msgstr "Schwierigkeit" -#: members/admin.py:736 +#: members/admin.py:756 msgid "Tour type" msgstr "Art der Tour" -#: members/admin.py:739 members/models.py:982 +#: members/admin.py:759 members/models.py:1003 msgid "Means of transportation" msgstr "Verkehrsmittel" -#: members/admin.py:765 +#: members/admin.py:785 msgid "" "Please list here all expenses in relation with this excursion and upload " "relevant bills. These have to be permanently stored for the application of " @@ -243,7 +258,7 @@ msgstr "" "einzelnen Posten wird dabei auf der LJP-Kostenübersicht angezeigt (sinnvoll " "wären z.B. Anreise, Verpflegung, Material etc.)." -#: members/admin.py:783 +#: members/admin.py:803 msgid "" "Here you can work on a seminar report for applying for financial " "contributions from Landesjugendplan (LJP). More information on creating a " @@ -256,7 +271,7 @@ msgstr "" "wahlweise nur TN-Liste und Kostenübersicht kannst du anschließend " "herunterladen." -#: members/admin.py:791 +#: members/admin.py:811 msgid "" "Please list all participants (also youth leaders) of this excursion. Here " "you can still make changes just before departure and hence generate the " @@ -267,34 +282,34 @@ msgstr "" "jederzeit die aktuelle Teilnehmer*innenliste für die Krisenintervention " "generieren." -#: members/admin.py:837 +#: members/admin.py:857 #, python-format msgid "You are not allowed to view all members on note list %(name)s." msgstr "" "Du hast nicht die nötigen Rechte um alle Teilnehmer*innen der Notizliste " "%(name)s anzusehen." -#: members/admin.py:847 +#: members/admin.py:867 msgid "Generate PDF summary" msgstr "Übersicht erstellen" -#: members/admin.py:851 +#: members/admin.py:871 msgid "Full report" msgstr "Vollständiger Seminarbericht" -#: members/admin.py:852 +#: members/admin.py:872 msgid "Costs and participants only" msgstr "Nur Kosten und Teilnehmende" -#: members/admin.py:853 +#: members/admin.py:873 msgid "Mode" msgstr "Modus" -#: members/admin.py:854 +#: members/admin.py:874 msgid "Prepend V32" msgstr "V32 Formblatt einfügen" -#: members/admin.py:870 +#: members/admin.py:890 msgid "" "General information on your excursion. These are partly relevant for the " "amount of financial compensation (means of transport, travel distance, etc.)." @@ -303,48 +318,48 @@ msgstr "" "teilweise relevant für die Zuschüsse aus dem Jugendetat (Verkehrsmittel, " "Fahrstrecke in km)." -#: members/admin.py:900 +#: members/admin.py:920 #, python-format msgid "You are not allowed to view all members on excursion %(name)s." msgstr "" "Du hast nicht die nötigen Rechte um alle Teilnehmer*innen der Ausfahrt " "%(name)s anzusehen." -#: members/admin.py:908 +#: members/admin.py:928 msgid "Generate crisis intervention list" msgstr "Kriseninterventionsliste erstellen" -#: members/admin.py:916 +#: members/admin.py:936 msgid "Generate overview" msgstr "Hinweise für Jugendleiter erstellen" -#: members/admin.py:920 members/admin.py:952 +#: members/admin.py:940 members/admin.py:972 #: members/templates/admin/generate_seminar_report.html:21 msgid "Generate seminar report" msgstr "Landesjugendplan Antrag erstellen" -#: members/admin.py:933 +#: members/admin.py:953 msgid "Please select a mode." msgstr "Bitte wähle einen Modus aus." -#: members/admin.py:938 +#: members/admin.py:958 msgid "" "Full mode is only available, if the seminar report section is filled out." msgstr "" "Der vollständiger Modus ist nur verfügbar, wenn der Seminarbericht " "ausgefüllt ist. " -#: members/admin.py:964 +#: members/admin.py:984 msgid "Generate SJR application" msgstr "SJR Antrag erstellen" -#: members/admin.py:968 +#: members/admin.py:988 msgid "No statement found. Please add a statement and then retry." msgstr "" "Keine Abrechnung angelegt. Bitte lege eine Abrechnung and und versuche es " "erneut." -#: members/admin.py:972 +#: members/admin.py:992 msgid "" "Successfully submited statement. The finance department will notify you as " "soon as possible." @@ -352,7 +367,7 @@ msgstr "" "Abrechnung erfolgreich eingericht. Die Finanzabteilung wird sich bei dir so " "schnell wie möglich melden." -#: members/admin.py:975 +#: members/admin.py:995 #: members/templates/admin/freizeit_finance_overview.html:21 msgid "Finance overview" msgstr "Kostenübersicht" @@ -361,580 +376,580 @@ msgstr "Kostenübersicht" msgid "member administration" msgstr "Teilnehmer*innenverwaltung" -#: members/models.py:41 +#: members/models.py:42 msgid "Monday" msgstr "Montag" -#: members/models.py:42 +#: members/models.py:43 msgid "Tuesday" msgstr "Dienstag" -#: members/models.py:43 +#: members/models.py:44 msgid "Wednesday" msgstr "Mittwoch" -#: members/models.py:44 +#: members/models.py:45 msgid "Thursday" msgstr "Donnerstag" -#: members/models.py:45 +#: members/models.py:46 msgid "Friday" msgstr "Freitag" -#: members/models.py:46 +#: members/models.py:47 msgid "Saturday" msgstr "Samstag" -#: members/models.py:47 +#: members/models.py:48 msgid "Sunday" msgstr "Sonntag" -#: members/models.py:56 members/models.py:968 +#: members/models.py:57 members/models.py:989 msgid "Description" msgstr "Beschreibung" -#: members/models.py:62 members/models.py:960 +#: members/models.py:63 members/models.py:981 #: members/templates/members/change_member.html:18 msgid "Activity" msgstr "Aktivität" -#: members/models.py:63 +#: members/models.py:64 msgid "Activities" msgstr "Aktivitäten" -#: members/models.py:71 -msgid "name" -msgstr "Name" - -#: members/models.py:72 +#: members/models.py:73 msgid "description" msgstr "Beschreibung" -#: members/models.py:73 +#: members/models.py:74 msgid "show on website" msgstr "Auf der Webseite anzeigen" -#: members/models.py:74 +#: members/models.py:75 msgid "lowest year" msgstr "Ab Jahrgang" -#: members/models.py:75 +#: members/models.py:76 msgid "highest year" msgstr "Bis Jahrgang" -#: members/models.py:76 +#: members/models.py:77 msgid "youth leaders" msgstr "Jugendleiter" -#: members/models.py:78 +#: members/models.py:79 msgid "week day" msgstr "Wochentag" -#: members/models.py:79 members/models.py:1333 +#: members/models.py:80 members/models.py:1354 msgid "Starting time" msgstr "Zeitpunkt" -#: members/models.py:80 +#: members/models.py:81 msgid "Ending time" msgstr "Endzeitpunkt" -#: members/models.py:82 +#: members/models.py:83 msgid "Contact email" msgstr "Kontakt Email" -#: members/models.py:92 members/models.py:259 +#: members/models.py:93 members/models.py:272 msgid "group" msgstr "Gruppe" -#: members/models.py:93 +#: members/models.py:94 msgid "groups" msgstr "Gruppen" -#: members/models.py:109 +#: members/models.py:110 msgid "prename" msgstr "Vorname" -#: members/models.py:110 +#: members/models.py:111 msgid "last name" msgstr "Nachname" -#: members/models.py:113 +#: members/models.py:114 msgid "Email confirmed" msgstr "Emailadresse bestätigt" -#: members/models.py:150 -msgid "Email confirmation needed" -msgstr "Email Bestätigung erforderlich" - -#: members/models.py:190 members/models.py:233 +#: members/models.py:132 members/models.py:203 members/models.py:246 msgid "phone number" msgstr "Telefonnummer (mobil)" -#: members/models.py:200 +#: members/models.py:163 +msgid "Email confirmation needed" +msgstr "Email Bestätigung erforderlich" + +#: members/models.py:213 msgid "birth date" msgstr "Geburtsdatum" -#: members/models.py:205 +#: members/models.py:218 msgid "Gender" msgstr "Gender" -#: members/models.py:206 +#: members/models.py:219 msgid "comments" msgstr "Kommentare" -#: members/models.py:230 +#: members/models.py:243 msgid "Alternative email confirmed" msgstr "Alternative E-Mail Adresse bestätigt" -#: members/models.py:234 +#: members/models.py:247 msgid "street and house number" msgstr "Straße und Hausnummer" -#: members/models.py:235 +#: members/models.py:248 msgid "Postcode" msgstr "PLZ" -#: members/models.py:237 +#: members/models.py:250 msgid "town" msgstr "Stadt" -#: members/models.py:238 +#: members/models.py:251 msgid "Address extra" msgstr "Adress-Zusatz" -#: members/models.py:239 +#: members/models.py:252 msgid "Country" msgstr "Land" -#: members/models.py:241 +#: members/models.py:254 msgid "Good conduct certificate presented on" msgstr "Führungszeugnis vorgelegt am" -#: members/models.py:242 +#: members/models.py:255 msgid "Joined on" msgstr "Eintritt" -#: members/models.py:243 +#: members/models.py:256 msgid "Left on" msgstr "Austritt" -#: members/models.py:244 +#: members/models.py:257 msgid "Has key" msgstr "Hat Jugendraumschlüssel" -#: members/models.py:245 +#: members/models.py:258 msgid "Has a free ticket for the climbing gym" msgstr "Hat Freikarte für Kletterhalle" -#: members/models.py:246 +#: members/models.py:259 msgid "DAV badge number" msgstr "DAV Mitgliedsnummer" -#: members/models.py:247 +#: members/models.py:260 msgid "Knows how to swim" msgstr "Kann schwimmen" -#: members/models.py:248 +#: members/models.py:261 msgid "Climbing badge" msgstr "Kletterschein" -#: members/models.py:249 +#: members/models.py:262 msgid "Alpine experience" msgstr "Alpine Erfahrung" -#: members/models.py:250 +#: members/models.py:263 msgid "Allergies" msgstr "Allergieen" -#: members/models.py:251 +#: members/models.py:264 msgid "Medication" msgstr "Medikamente" -#: members/models.py:252 +#: members/models.py:265 msgid "Tetanus vaccination" msgstr "Tetanusimpfung" -#: members/models.py:253 +#: members/models.py:266 msgid "Photos may be taken" msgstr "Fotoerlaubnis" -#: members/models.py:254 +#: members/models.py:267 msgid "Legal guardians" msgstr "Erziehungsberechtigte" -#: members/models.py:256 +#: members/models.py:269 msgid "May cancel a group appointment independently" msgstr "Darf sich allein von der Gruppenstunde abmelden" -#: members/models.py:263 +#: members/models.py:276 msgid "receives newsletter" msgstr "Erhält den Newsletter" -#: members/models.py:267 +#: members/models.py:280 msgid "created" msgstr "erstellt" -#: members/models.py:268 +#: members/models.py:281 msgid "Active" msgstr "Aktiv" -#: members/models.py:269 +#: members/models.py:282 msgid "registration form" msgstr "Anmeldeformular" -#: members/models.py:277 +#: members/models.py:290 msgid "image" msgstr "Bild" -#: members/models.py:286 +#: members/models.py:299 msgid "Echoed" msgstr "Rückgemeldet" -#: members/models.py:287 +#: members/models.py:300 msgid "Confirmed" msgstr "Bestätigt" -#: members/models.py:289 +#: members/models.py:302 msgid "Login data" msgstr "Zugangsdaten" -#: members/models.py:319 +#: members/models.py:332 msgid "Good conduct certificate valid" msgstr "Führungszeugnis gültig" -#: members/models.py:401 +#: members/models.py:414 msgid "member" msgstr "Teilnehmer*in" -#: members/models.py:402 +#: members/models.py:415 msgid "members" msgstr "Teilnehmer*innen" -#: members/models.py:471 +#: members/models.py:484 #, python-format msgid "New unconfirmed registration for group %(group)s" msgstr "Neue unbestätigte Registrierung für Gruppe %(group)s" -#: members/models.py:697 +#: members/models.py:710 msgid "Set login data for Kompass" msgstr "Zugangsdaten für Kompass wählen" -#: members/models.py:715 members/models.py:916 members/models.py:927 -#: members/models.py:1282 members/models.py:1289 +#: members/models.py:728 members/models.py:937 members/models.py:948 +#: members/models.py:1303 members/models.py:1310 msgid "Member" msgstr "Teilnehmer*in" -#: members/models.py:722 +#: members/models.py:735 msgid "Emergency contact" msgstr "Notfallkontakt" -#: members/models.py:723 +#: members/models.py:736 msgid "Emergency contacts" msgstr "Notfallkontakte" -#: members/models.py:743 +#: members/models.py:756 msgid "Unconfirmed registration" msgstr "Unbestätigte Registrierung" -#: members/models.py:744 +#: members/models.py:757 msgid "Unconfirmed registrations" msgstr "Unbestätigte Registrierungen" -#: members/models.py:763 members/models.py:808 +#: members/models.py:776 members/models.py:821 msgid "Waiter" msgstr "Wartende Person" -#: members/models.py:765 +#: members/models.py:778 msgid "Invitation date" msgstr "Einladungsdatum" -#: members/models.py:766 members/templates/members/reject_success.html:6 +#: members/models.py:779 members/templates/members/reject_success.html:6 #: members/templates/members/reject_success.html:11 msgid "Invitation rejected" msgstr "Einladung abgelehnt" -#: members/models.py:770 +#: members/models.py:783 msgid "Invitation to group" msgstr "Gruppeneinladung" -#: members/models.py:771 +#: members/models.py:784 msgid "Invitations to groups" msgstr "Gruppeneinladungen" -#: members/models.py:778 +#: members/models.py:791 msgid "Rejected" msgstr "Abgelehnt" -#: members/models.py:780 +#: members/models.py:793 msgid "Expired" msgstr "Abgelaufen" -#: members/models.py:782 +#: members/models.py:795 msgid "Undecided" msgstr "Ausstehend" -#: members/models.py:783 +#: members/models.py:796 msgid "Status" msgstr "Status" -#: members/models.py:794 +#: members/models.py:807 msgid "Do you want to tell us something else?" msgstr "Möchtest du uns noch etwas mitteilen?" -#: members/models.py:795 +#: members/models.py:808 msgid "application date" msgstr "Bewerbungsdatum" -#: members/models.py:797 +#: members/models.py:810 msgid "Last wait confirmation" msgstr "Letzte Wartebestätigung" -#: members/models.py:801 +#: members/models.py:814 msgid "Last reminder" msgstr "Letzte Erinnerung" -#: members/models.py:802 +#: members/models.py:815 msgid "Missed reminders" msgstr "Verpasste Erinnerungen" -#: members/models.py:809 +#: members/models.py:822 msgid "Waiters" msgstr "Warteliste" -#: members/models.py:833 +#: members/models.py:837 +msgid "Latest group invitation" +msgstr "Letzte Gruppeneinladung" + +#: members/models.py:854 msgid "Waiting status confirmed" msgstr "Wartelistenplatz bestätigt" -#: members/models.py:840 +#: members/models.py:861 msgid "Waiting confirmation needed" msgstr "Wartelistenplatzbestätigung erforderlich" -#: members/models.py:895 +#: members/models.py:916 msgid "Invitation to trial group meeting" msgstr "Einladung zu Schnupperstunde" -#: members/models.py:907 +#: members/models.py:928 msgid "Unregistered from waiting list" msgstr "Von der Warteliste abgemeldet" -#: members/models.py:921 +#: members/models.py:942 msgid "Comment" msgstr "Kommentar" -#: members/models.py:928 members/models.py:1290 +#: members/models.py:949 members/models.py:1311 msgid "Members" msgstr "Teilnehmer*innen" -#: members/models.py:962 +#: members/models.py:983 msgid "Place" msgstr "Stützpunkt / Ort" -#: members/models.py:963 +#: members/models.py:984 msgid "Destination (optional)" msgstr "ggf. Ziel" -#: members/models.py:965 +#: members/models.py:986 msgid "e.g. a peak" msgstr "z.B. ein Gipfel" -#: members/models.py:966 +#: members/models.py:987 msgid "Begin" msgstr "Anfang" -#: members/models.py:967 +#: members/models.py:988 msgid "End (optional)" msgstr "Ende" -#: members/models.py:970 +#: members/models.py:991 msgid "Groups" msgstr "Gruppen" -#: members/models.py:983 +#: members/models.py:1004 msgid "Kilometers traveled" msgstr "Fahrstrecke in Kilometer" -#: members/models.py:986 +#: members/models.py:1007 msgid "Categories" msgstr "Kategorien" -#: members/models.py:987 +#: members/models.py:1008 msgid "easy" msgstr "leicht" -#: members/models.py:987 +#: members/models.py:1008 msgid "medium" msgstr "mittel" -#: members/models.py:987 +#: members/models.py:1008 msgid "hard" msgstr "schwer" -#: members/models.py:997 members/models.py:1313 +#: members/models.py:1018 members/models.py:1334 #: members/templates/admin/freizeit_finance_overview.html:26 msgid "Excursion" msgstr "Ausfahrt" -#: members/models.py:998 +#: members/models.py:1019 msgid "Excursions" msgstr "Ausfahrten" -#: members/models.py:1228 members/models.py:1304 members/models.py:1520 +#: members/models.py:1249 members/models.py:1325 members/models.py:1541 msgid "Title" msgstr "Titel" -#: members/models.py:1229 members/models.py:1247 members/models.py:1521 +#: members/models.py:1250 members/models.py:1268 members/models.py:1542 msgid "Date" msgstr "Datum" -#: members/models.py:1248 +#: members/models.py:1269 msgid "Location" msgstr "Ort" -#: members/models.py:1249 +#: members/models.py:1270 msgid "Topic" msgstr "Thema" -#: members/models.py:1273 +#: members/models.py:1294 msgid "Jugendleiter" msgstr "Jugendleiter" -#: members/models.py:1276 +#: members/models.py:1297 msgid "Klettertreff" msgstr "Klettertreff" -#: members/models.py:1277 +#: members/models.py:1298 msgid "Klettertreffs" msgstr "Klettertreffs" -#: members/models.py:1295 +#: members/models.py:1316 msgid "Password" msgstr "Passwort" -#: members/models.py:1298 +#: members/models.py:1319 msgid "registration password" msgstr "Registrierungspassort" -#: members/models.py:1299 +#: members/models.py:1320 msgid "registration passwords" msgstr "Registrierungspasswörter" -#: members/models.py:1306 +#: members/models.py:1327 msgid "Alpinistic goals" msgstr "Alpintechnische Ziele" -#: members/models.py:1307 +#: members/models.py:1328 msgid "Pedagogic goals" msgstr "Pädagogische Ziele" -#: members/models.py:1308 +#: members/models.py:1329 msgid "Content and methods" msgstr "Inhalte und Methoden" -#: members/models.py:1309 +#: members/models.py:1330 msgid "Evaluation" msgstr "Wertung" -#: members/models.py:1310 +#: members/models.py:1331 msgid "Experiences and possible improvements" msgstr "Erfahrungen und Verbesserungsvorschläge" -#: members/models.py:1319 members/models.py:1340 +#: members/models.py:1340 members/models.py:1361 msgid "LJP Proposal" msgstr "Seminarbericht" -#: members/models.py:1320 +#: members/models.py:1341 msgid "LJP Proposals" msgstr "Seminarberichte" -#: members/models.py:1334 +#: members/models.py:1355 msgid "Duration in hours" msgstr "Dauer in Stunden" -#: members/models.py:1337 +#: members/models.py:1358 msgid "Activity and method" msgstr "Art der Aktion inkl. Methode" -#: members/models.py:1345 +#: members/models.py:1366 msgid "Intervention" msgstr "Aktion" -#: members/models.py:1346 +#: members/models.py:1367 msgid "Interventions" msgstr "Aktionen" -#: members/models.py:1448 members/models.py:1478 +#: members/models.py:1469 members/models.py:1499 msgid "May list members" msgstr "Darf folgende Teilnehmer*innen listen" -#: members/models.py:1450 members/models.py:1480 +#: members/models.py:1471 members/models.py:1501 msgid "May view members" msgstr "Darf folgende Teilnehmer*innen anzeigen" -#: members/models.py:1452 members/models.py:1482 +#: members/models.py:1473 members/models.py:1503 msgid "May change members" msgstr "Darf folgende Teilnehmer*innen ändern" -#: members/models.py:1454 members/models.py:1484 +#: members/models.py:1475 members/models.py:1505 msgid "May delete members" msgstr "Darf folgende Teilnehmer*innen löschen" -#: members/models.py:1458 members/models.py:1488 +#: members/models.py:1479 members/models.py:1509 msgid "May list members of groups" msgstr "Darf Teilnehmer*innen folgender Gruppen listen" -#: members/models.py:1460 members/models.py:1490 +#: members/models.py:1481 members/models.py:1511 msgid "May view members of groups" msgstr "Darf Teilnehmer*innen folgender Gruppen anzeigen" -#: members/models.py:1462 members/models.py:1492 +#: members/models.py:1483 members/models.py:1513 msgid "May change members of groups" msgstr "Darf Teilnehmer*innen folgender Gruppen ändern" -#: members/models.py:1464 members/models.py:1494 +#: members/models.py:1485 members/models.py:1515 msgid "May delete members of groups" msgstr "Darf Teilnehmer*innen folgender Gruppen löschen" -#: members/models.py:1467 members/models.py:1468 members/models.py:1471 +#: members/models.py:1488 members/models.py:1489 members/models.py:1492 msgid "Permissions" msgstr "Berechtigungen" -#: members/models.py:1497 members/models.py:1498 members/models.py:1501 +#: members/models.py:1518 members/models.py:1519 members/models.py:1522 msgid "Group permissions" msgstr "Gruppenberechtigungen" -#: members/models.py:1507 +#: members/models.py:1528 msgid "Permission needed" msgstr "Freigabe erforderlich" -#: members/models.py:1510 +#: members/models.py:1531 msgid "Training category" msgstr "Fortbildungstyp" -#: members/models.py:1511 +#: members/models.py:1532 msgid "Training categories" msgstr "Fortbildungstypen" -#: members/models.py:1522 +#: members/models.py:1543 msgid "Category" msgstr "Kategorien" -#: members/models.py:1523 +#: members/models.py:1544 msgid "Comments" msgstr "Kommentar" -#: members/models.py:1524 +#: members/models.py:1545 msgid "Participated" msgstr "Teilgenommmen" -#: members/models.py:1525 +#: members/models.py:1546 msgid "Passed" msgstr "Bestanden" -#: members/models.py:1528 +#: members/models.py:1549 msgid "Training" msgstr "Fortbildung" -#: members/models.py:1529 +#: members/models.py:1550 msgid "Trainings" msgstr "Fortbildungen" diff --git a/jdav_web/members/migrations/0031_member_waitinglist_application_date.py b/jdav_web/members/migrations/0031_member_waitinglist_application_date.py new file mode 100644 index 0000000..76e72f7 --- /dev/null +++ b/jdav_web/members/migrations/0031_member_waitinglist_application_date.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.1 on 2024-12-15 18:24 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('members', '0030_alter_member_options'), + ] + + operations = [ + migrations.AddField( + model_name='member', + name='waitinglist_application_date', + field=models.DateTimeField(blank=True, help_text='If the person registered from the waitinglist, this is their application date.', null=True, verbose_name='waitinglist application date'), + ), + ] diff --git a/jdav_web/members/models.py b/jdav_web/members/models.py index 9f11fd3..273b92e 100644 --- a/jdav_web/members/models.py +++ b/jdav_web/members/models.py @@ -301,6 +301,9 @@ class Member(Person): user = models.OneToOneField(User, blank=True, null=True, on_delete=models.SET_NULL, verbose_name=_('Login data')) invite_as_user_key = models.CharField(max_length=32, default="") + waitinglist_application_date = models.DateTimeField(verbose_name=_('waitinglist application date'), + null=True, blank=True, + help_text=_('If the person registered from the waitinglist, this is their application date.')) objects = MemberManager() @@ -452,6 +455,9 @@ class Member(Person): if self.email == waiter.email: self.confirmed_mail = waiter.confirmed_mail self.confirm_mail_key = waiter.confirm_mail_key + # store waitinglist application date in member, this will be used + # if the member is later demoted to waiter again + self.waitinglist_application_date = waiter.application_date if self.alternative_email: self.confirmed_alternative_mail = False self.save() @@ -465,8 +471,7 @@ class Member(Person): def registration_ready(self): """Returns if the member is currently unconfirmed and all email addresses are confirmed.""" - return not self.confirmed and self.confirmed_alternative_mail and self.confirmed_mail and\ - all([emc.confirmed_mail for emc in self.emergencycontact_set.all()]) + return not self.confirmed and self.confirmed_alternative_mail and self.confirmed_mail def confirm_mail(self, key): ret = super().confirm_mail(key) @@ -720,6 +725,23 @@ class Member(Person): """Returns a queryset of freizeiten that this member is a youth leader of.""" return Freizeit.objects.filter(jugendleiter__pk=self.pk)[:limit] + def demote_to_waiter(self): + """Demote this member to a waiter by creating a waiter from the data and removing + this member.""" + waiter = MemberWaitingList(prename=self.prename, + lastname=self.lastname, + email=self.email, + birth_date=self.birth_date, + gender=self.gender, + comments=self.comments, + confirmed_mail=self.confirmed_mail, + confirm_mail_key=self.confirm_mail_key) + # if this member was created from the waitinglist, keep the original application date + if self.waitinglist_application_date: + waiter.application_date = self.waitinglist_application_date + waiter.save() + self.delete() + class EmergencyContact(ContactWithPhoneNumber): """ @@ -828,6 +850,14 @@ class MemberWaitingList(Person): 'delete_obj': has_global_perm('members.delete_global_memberwaitinglist'), } + def latest_group_invitation(self): + gi = self.invitationtogroup_set.order_by('-pk').first() + if gi: + return "{group}: {status}".format(group=gi.group.name, status=gi.status()) + else: + return "-" + latest_group_invitation.short_description = _('Latest group invitation') + @property def waiting_confirmation_needed(self): """Returns if person should be asked to confirm waiting status.""" diff --git a/jdav_web/startpage/admin.py b/jdav_web/startpage/admin.py index c1dcd06..724f17d 100644 --- a/jdav_web/startpage/admin.py +++ b/jdav_web/startpage/admin.py @@ -1,4 +1,7 @@ from django.contrib import admin +from django.conf import settings +from django import forms +from django.utils.translation import gettext_lazy as _ from .models import Post, Image, Section, MemberOnPost @@ -13,14 +16,27 @@ class MemberOnPostInline(admin.TabularInline): extra = 0 +class PostForm(forms.ModelForm): + urlname = forms.RegexField(regex=r'^{pattern}+$'.format(pattern=settings.STARTPAGE_URL_NAME_PATTERN), + label=_('URL'), + error_messages={'invalid': _('The url may only consist of letters, numerals, _, -, :, * and spaces.')}) + + @admin.register(Post) class PostAdmin(admin.ModelAdmin): inlines = [ImageInline, MemberOnPostInline] list_display = ['title', 'date', 'section', 'absolute_urlname'] list_filter = ['section'] search_fields = ['title'] + form = PostForm + +class SectionForm(forms.ModelForm): + urlname = forms.RegexField(regex=r'^{pattern}+$'.format(pattern=settings.STARTPAGE_URL_NAME_PATTERN), + label=_('URL'), + error_messages={'invalid': _('The url may only consist of letters, numerals, _, -, :, * and spaces.')}) @admin.register(Section) class SectionAdmin(admin.ModelAdmin): list_display = ['title', 'absolute_urlname'] + form = SectionForm diff --git a/jdav_web/startpage/locale/de/LC_MESSAGES/django.po b/jdav_web/startpage/locale/de/LC_MESSAGES/django.po index a02680c..4705c40 100644 --- a/jdav_web/startpage/locale/de/LC_MESSAGES/django.po +++ b/jdav_web/startpage/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: 2024-12-01 16:23+0100\n" +"POT-Creation-Date: 2024-12-04 00:04+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,14 +18,21 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" +#: startpage/admin.py:21 startpage/admin.py:36 startpage/models.py:18 +#: startpage/models.py:40 +msgid "URL" +msgstr "URL" + +#: startpage/admin.py:22 startpage/admin.py:37 +msgid "The url may only consist of letters, numerals, _, -, :, * and spaces." +msgstr "" +"Die URL darf nur aus Buchstaben, Zahlen, _, -, :, * oder Leerzeichen " +"bestehen." + #: startpage/models.py:17 startpage/models.py:39 msgid "Title" msgstr "Titel" -#: startpage/models.py:18 startpage/models.py:40 -msgid "URL" -msgstr "URL" - #: startpage/models.py:19 startpage/models.py:42 msgid "website text" msgstr "Webseitentext" diff --git a/jdav_web/startpage/urls.py b/jdav_web/startpage/urls.py index de98543..417f6fc 100644 --- a/jdav_web/startpage/urls.py +++ b/jdav_web/startpage/urls.py @@ -1,3 +1,4 @@ +from django.conf import settings from django.urls import re_path from . import views @@ -10,7 +11,10 @@ urlpatterns = [ re_path(r'^berichte/?$', views.berichte, name='berichte'), re_path(r'^gruppen/?$', views.static_view('startpage/gruppen.html'), name='gruppen'), re_path(r'^gruppen/faq/?$', views.static_view('startpage/gruppen/faq.html'), name='faq'), - re_path(r'^gruppen/(?P[\w\-:]+)/?$', views.gruppe_detail, name='gruppe_detail'), - re_path(r'^(?P[\w\-:]+)/(?P[\w\-:]+)/?$', views.post, name='post'), - re_path(r'^(?P[\w\-:]+)/?$', views.section, name='section'), + re_path(r'^gruppen/(?P{pattern}+)/?$'.format(pattern=settings.STARTPAGE_URL_NAME_PATTERN), + views.gruppe_detail, name='gruppe_detail'), + re_path(r'^(?P{pattern}+)/(?P{pattern}+)/?$'.format(pattern=settings.STARTPAGE_URL_NAME_PATTERN), + views.post, name='post'), + re_path(r'^(?P{pattern}+)/?$'.format(pattern=settings.STARTPAGE_URL_NAME_PATTERN), + views.section, name='section'), ] diff --git a/jdav_web/templates/admin/members/app_index.html b/jdav_web/templates/admin/members/app_index.html index 9773569..dacacea 100644 --- a/jdav_web/templates/admin/members/app_index.html +++ b/jdav_web/templates/admin/members/app_index.html @@ -45,20 +45,28 @@ Hier siehst du alle von dir geleiteten Ausfahrten und für dich sichtbare Teilne -{% if perms.members.may_manage_waiting_list %} +{% if perms.members.view_memberunconfirmedproxy %}

Neue Mitglieder

-Hier werden neue Mitglieder verwaltet. Um ein neues Mitglied anzulegen, muss sich die Person +{% if perms.member.may_manage_waiting_list %} +Um ein neues Mitglied anzulegen, muss sich die Person anmelden. Daraufhin landet sie auf der Warteliste. Eine Person auf der Warteliste kannst du dann zu einer Schnupperstunde einer gewählten Gruppe einladen. Diese Einladung enthält einen Registrierungslink zu einem Formular in dem die Person alle ihre Stammdaten eingbit. Diese Daten landen dann unter Unbestätigte Registrierungen. +{% else %} +Ob über die Warteliste oder über ein Registrierungspasswort, +liegt eine neue Registrierung für eine von dir geleitete Jugendgruppe vor, kannst du die hier einsehen +und die Daten prüfen. Falls die Daten vollständig sind, bestätige die Registrierung um die Person in deine +Jugendgruppe aufzunehmen. +{% endif %}

+ {% if perms.member.may_manage_waiting_list %} + {% endif %}
Warteliste @@ -66,6 +74,7 @@ Stammdaten eingbit. Diese Daten landen dann unter
Unbestätigte Registrierungen