diff --git a/dav_base/config/modules.py b/dav_base/config/modules.py index ebba340..6c82e87 100644 --- a/dav_base/config/modules.py +++ b/dav_base/config/modules.py @@ -36,20 +36,22 @@ class ModuleMeta: return self._additional_apps @property - def url_conf_pattern(self): - url_pattern = '^' + def url_prefix(self): if self._url_prefix is None: - url_pattern += self._package_name + return self._package_name else: - url_pattern += self._url_prefix - url_pattern += '/' - url_conf = self._package_name + '.urls' - return django_conf_url(url_pattern, include(url_conf, self.url_namespace)) + return self._url_prefix @property def url_namespace(self): return self._package_name.replace('.', '_') + @property + def url_conf_pattern(self): + url_pattern = '^{}/'.format(self.url_prefix) + url_conf = self._package_name + '.urls' + return django_conf_url(url_pattern, include(url_conf, self.url_namespace)) + def _load_from_package(self): package_name = self._package_name json_text = pkg_resources.resource_string(package_name, self._json_file) diff --git a/dav_base/console_scripts/django_project_config/settings-dav_base.py b/dav_base/console_scripts/django_project_config/settings-dav_base.py index a86f7bc..be0fabe 100644 --- a/dav_base/console_scripts/django_project_config/settings-dav_base.py +++ b/dav_base/console_scripts/django_project_config/settings-dav_base.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- # E-Mails -EMAIL_SENDER = 'DAV heinzel ' +EMAIL_SENDER = 'DAV Touren & Kurse ' EMAIL_BASE_URL = 'http://localhost:8000' -EMAIL_SUBJECT_PREFIX = u'[DAV heinzel]' +EMAIL_SUBJECT_PREFIX = u'[DAV Touren & Kurse]' # The following settings are for the test suite. Do not change them. TEST_SETTING = 'do not change this value' diff --git a/dav_base/static/dav_base/css/local.css b/dav_base/static/dav_base/css/local.css index 90b1278..8f3980e 100644 --- a/dav_base/static/dav_base/css/local.css +++ b/dav_base/static/dav_base/css/local.css @@ -1,3 +1,22 @@ +/* + * Colors + * color text focus bg focus btn focus border focus active function sport level status + * orange #f07d00 #bd6200 #f07d00 #bd6200 #f07d00 #bd6200 #d77000 #572d00 #995000 primary ----- ----- ------ + * green #3c763d #2b542c #dff0d8 #c1e2b3 #58ab27 #43811e #4d9622 #182e0b #346417 success ----- ----- publis + * blue #31708f #245269 #d9edf7 #afd9ee #1d70b7 #16558b #1a63a1 #081f33 #11426c info -- ----- ----- draft + * yellow #8a6d3b #66512c #fcf8e3 #f7ecb5 #f9b000 #c68c00 #e09e00 #604400 #a27300 warning Klett ----- accept + * red #a94442 #843534 #f2dede #e4b9b9 #be1621 #901119 #a7131d #350609 #700d14 danger ----- ----- submit + * mandarin #b43a12 #902c0e #f0ded8 #e1bcb0 #e84e1b #b43a12 #aa3711 #3c1306 #78250c -------- ----- advan cancel + * lime #859201 #656e01 #eef0d8 #dde1b0 #bccf07 #859201 #7c8801 #2e3200 #4f5601 -------- Bergs ----- done - + * cyan #54bbd9 #469db5 #d8ebf0 #a7d5e1 #86cfe4 #54bbd9 #4cb8d7 #17343c #3a8ea5 -------- Ski - ----- ------ + * caramel #9c5b22 #78461a #f0e3d8 #e1c6b0 #ce792d #9c5b22 #935720 #3c230d #613915 -------- Wande ----- ------ + * purple #866dac #566088 #e1d8f0 #c2b0e1 #a694c2 #866dac #8067a8 #2f263c #5a4876 -------- MTB - ----- ------ + * plum #aa6c95 #784c69 #f0d4e7 #e6b0d4 #be91af #aa6c95 #a66691 #3c2635 #764867 -------- ----- famil ------ + * brown #??? #??? #??? #??? #925f36 #??? #??? #??? #??? -------- ----- ----- ------ + * black #??? #??? #??? #??? #??? #??? #??? #??? #??? -------- ----- ----- clear + * white #??? #??? #??? #??? #??? #??? #??? #??? #??? -------- ----- ----- ------ + */ + /* * General elements */ @@ -46,7 +65,7 @@ thead input { } #page-header h2 img { - margin-right: 1.2rem; + margin-right: .6rem; } #page-header h2 a { @@ -150,52 +169,362 @@ thead input { } /* - * We need more text colors (for event stati) + * Used to decolourize a link. */ -.text-dav-purple { - color: #866dac; +.text-default { + color: #333; } +a.text-default:hover, a.text-default:focus { + color: #333; +} + +/* + * We need more text colors (for event stati) + * TODO: we try to replace this with dedicated status elements + */ .text-dav-lime { color: #859201; } -.text-dav-cyan { - color: #54bbd9; -} -.text-dav-caramel { - color: #9c5b22; -} .text-dav-mandarin { color: #b43a12; } /* * We need more label colors (for event stati) + * TODO: we try to replace this with dedicated status elements */ -.label-black { - background-color: #333; -} -.label-dav-purple { - background-color: #a694c2; -} .label-dav-lime { background-color: #bccf07; } -.label-dav-cyan { - background-color: #86cfe4; -} -.label-dav-caramel { - background-color: #ce792d; -} .label-dav-mandarin { background-color: #e84e1b; } -.label-dav-brown { - background-color: #925f36; + +/* + * Elements for sport choices + */ +/* + * Bergsteigen: lime + */ +.btn-sport-B { + background-color: #bccf07; + border-color: #7c8801; + color: #fff; +} +.btn-sport-B.focus, .btn-sport-B:focus, .btn-sport-B:hover { + background-color: #859201; + border-color: #2e3200; + color: #fff; +} +/* + * Klettern: yellow + */ +.btn-sport-K { + background-color: #f9b000; + border-color: #e09e00; + color: #fff; +} +.btn-sport-K.focus, .btn-sport-K:focus, .btn-sport-K:hover { + background-color: #c68c00; + border-color: #604400; + color: #fff; +} +/* + * Mountainbike: purple + */ +.btn-sport-M { + background-color: #a694c2; + border-color: #8067a8; + color: #fff; +} +.btn-sport-M.focus, .btn-sport-M:focus, .btn-sport-M:hover { + background-color: #866dac; + border-color: #2f263c; + color: #fff; +} +/* + * Ski: cyan + */ +.btn-sport-S { + background-color: #86cfe4; + border-color: #4cb8d7; + color: #fff; +} +.btn-sport-S.focus, .btn-sport-S:focus, .btn-sport-S:hover { + background-color: #54bbd9; + border-color: #17343c; + color: #fff; +} +/* + * Wanderung: caramel + */ +.btn-sport-W { + background-color: #ce792d; + border-color: #935720; + color: #fff; +} +.btn-sport-W.focus, .btn-sport-W:focus, .btn-sport-W:hover { + background-color: #9c5b22; + border-color: #3c230d; + color: #fff; } /* - * We need more button colors (for event stati) + * Elements for level choices */ +/* + * advanced: mandarin + */ +.label-level-advanced { + background-color: #e84e1b; +} +.btn-level-advanced { + background-color: #e84e1b; + border-color: #aa3711; + color: #fff; +} +.btn-level-advanced.focus, .btn-level-advanced:focus, .btn-level-advanced:hover { + background-color: #b43a12; + border-color: #3c1306; + color: #fff; +} +/* + * family: plum + */ +.label-level-family { + background-color: #be91af; +} +.btn-level-family { + background-color: #be91af; + border-color: #a66691; + color: #fff; +} +.btn-level-family.focus, .btn-level-family:focus, .btn-level-family:hover { + background-color: #aa6c95; + border-color: #3c2635; + color: #fff; +} + +/* + * Coloured Elements (Buttons, Labels) for universal use + */ +.text-orange { + color: #f07d00; +} +.bg-orange { + color: #fff; + background-color: #f07d00; +} +.label-orange { + background-color: #f07d00; +} +.btn-orange { + background-color: #f07d00; + border-color: #d77000; + color: #fff; +} +.btn-orange.focus, .btn-orange:focus, .btn-orange:hover { + background-color: #bd6200; + border-color: #572d00; + color: #fff; +} +.text-green { + color: #3c763d; +} +.bg-green { + background-color: #dff0d8; +} +.label-green { + background-color: #58ab27; +} +.btn-green { + background-color: #58ab27; + border-color: #4d9622; + color: #fff; +} +.btn-green.focus, .btn-green:focus, .btn-green:hover { + background-color: #43811e; + border-color: #182e0b; + color: #fff; +} +.text-blue { + color: #31708f; +} +.bg-blue { + background-color: #d9edf7; +} +.label-blue { + background-color: #1d70b7; +} +.btn-blue { + background-color: #1d70b7; + border-color: #1a63a1; + color: #fff; +} +.btn-blue.focus, .btn-blue:focus, .btn-blue:hover { + background-color: #16558b; + border-color: #081f33; + color: #fff; +} +.text-yellow { + color: #8a6d3b; +} +.bg-yellow { + background-color: #fcf8e3; +} +.label-yellow { + background-color: #f9b000; +} +.btn-yellow { + background-color: #f9b000; + border-color: #e09e00; + color: #fff; +} +.btn-yellow.focus, .btn-yellow:focus, .btn-yellow:hover { + background-color: #c68c00; + border-color: #604400; + color: #fff; +} +.text-red { + color: #a94442; +} +.bg-red { + background-color: #f2dede; +} +.label-red { + background-color: #be1621; +} +.btn-red { + background-color: #be1621; + border-color: #a7131d; + color: #fff; +} +.btn-red.focus, .btn-red:focus, .btn-red:hover { + background-color: #901119; + border-color: #350609; + color: #fff; +} +.text-mandarin { + color: #b43a12; +} +.bg-mandarin { + background-color: #f0ded8; +} +.label-mandarin { + background-color: #e84e1b; +} +.btn-mandarin { + background-color: #e84e1b; + border-color: #aa3711; + color: #fff; +} +.btn-mandarin.focus, .btn-mandarin:focus, .btn-mandarin:hover { + background-color: #b43a12; + border-color: #3c1306; + color: #fff; +} +.text-lime { + color: #859201; +} +.bg-lime { + background-color: #eef0d8; +} +.label-lime { + background-color: #bccf07; +} +.btn-lime { + background-color: #bccf07; + border-color: #7c8801; + color: #fff; +} +.btn-lime.focus, .btn-lime:focus, .btn-lime:hover { + background-color: #859201; + border-color: #2e3200; + color: #fff; +} +.text-cyan { + color: #54bbd9; +} +.bg-cyan { + background-color: #d8ebf0; +} +.label-cyan { + background-color: #86cfe4; +} +.btn-cyan { + background-color: #86cfe4; + border-color: #4cb8d7; + color: #fff; +} +.btn-cyan.focus, .btn-cyan:focus, .btn-cyan:hover { + background-color: #54bbd9; + border-color: #17343c; + color: #fff; +} +.text-caramel { + color: #9c5b22; +} +.bg-caramel { + background-color: #f0e3d8; +} +.label-caramel { + background-color: #ce792d; +} +.btn-caramel { + background-color: #ce792d; + border-color: #935720; + color: #fff; +} +.btn-caramel.focus, .btn-caramel:focus, .btn-caramel:hover { + background-color: #9c5b22; + border-color: #3c230d; + color: #fff; +} +.text-purple { + color: #866dac; +} +.bg-purple { + background-color: #e1d8f0; +} +.label-purple { + background-color: #a694c2; +} +.btn-purple { + background-color: #a694c2; + border-color: #8067a8; + color: #fff; +} +.btn-purple.focus, .btn-purple:focus, .btn-purple:hover { + background-color: #866dac; + border-color: #2f263c; + color: #fff; +} +.text-plum { + color: #aa6c95; +} +.bg-plum { + background-color: #f0d4e7; +} +.label-plum { + background-color: #be91af; +} +.btn-plum { + background-color: #be91af; + border-color: #a66691; + color: #fff; +} +.btn-plum.focus, .btn-plum:focus, .btn-plum:hover { + background-color: #aa6c95; + border-color: #3c2635; + color: #fff; +} + +.text-black { + color: #333; +} +.label-black { + background-color: #333; +} .btn-black { background-color: #333; border-color: #ccc; @@ -206,53 +535,13 @@ thead input { border-color: #ccc; color: #fff; } -.btn-dav-purple { - background-color: #a694c2; - border-color: #8067a8; - color: #fff; +.btn-white { + background-color: #fff; + border-color: #ccc; + color: #333; } -.btn-dav-purple.focus, .btn-dav-purple:focus, .btn-dav-purple:hover { - background-color: #866dac; - border-color: #8067a8; - color: #fff; -} -.btn-dav-lime { - background-color: #bccf07; - border-color: #7c8801; - color: #fff; -} -.btn-dav-lime.focus, .btn-dav-lime:focus, .btn-dav-lime:hover { - background-color: #859201; - border-color: #7c8801; - color: #fff; -} -.btn-dav-cyan { - background-color: #86cfe4; - border-color: #4cb8d7; - color: #fff; -} -.btn-dav-cyan.focus, .btn-dav-cyan:focus, .btn-dav-cyan:hover { - background-color: #54bbd9; - border-color: #4cb8d7; - color: #fff; -} -.btn-dav-caramel { - background-color: #ce792d; - border-color: #935720; - color: #fff; -} -.btn-dav-caramel.focus, .btn-dav-caramel:focus, .btn-dav-caramel:hover { - background-color: #9c5b22; - border-color: #935720; - color: #fff; -} -.btn-dav-mandarin { - background-color: #e84e1b; - border-color: #aa3711; - color: #fff; -} -.btn-dav-mandarin.focus, .btn-dav-mandarin:focus, .btn-dav-mandarin:hover { - background-color: #b43a12; - border-color: #aa3711; - color: #fff; +.btn-white.focus, .btn-white:focus, .btn-white:hover { + background-color: #e6e6e6; + border-color: #8c8c8c; + color: #333; } diff --git a/dav_base/templates/dav_base/base.html b/dav_base/templates/dav_base/base.html index 058cc21..d26f8f2 100644 --- a/dav_base/templates/dav_base/base.html +++ b/dav_base/templates/dav_base/base.html @@ -31,9 +31,9 @@ diff --git a/dav_base/templates/dav_base/css_demo.html b/dav_base/templates/dav_base/css_demo.html index 59f8ecb..cb2d67a 100644 --- a/dav_base/templates/dav_base/css_demo.html +++ b/dav_base/templates/dav_base/css_demo.html @@ -5,8 +5,10 @@ {% block messages %}
- {% bootstrap_alert "This is a message." %} - {% bootstrap_alert "This is a message." %} + {% bootstrap_alert "This is a default (info) message." %} + {% bootstrap_alert "This is a success message." alert_type="success" %} + {% bootstrap_alert "This is a warning message." alert_type="warning" %} + {% bootstrap_alert "This is a error (danger) message." alert_type="danger" %}
{% endblock messages %} @@ -19,13 +21,14 @@
Header h6 Small Text
-

Hallo

+

This is a jumbotron with h1 header

{% lorem %}

+

This is a well with h2 header

{% lorem %}

@@ -33,24 +36,79 @@ {% lorem %}

+
+

Regular Bootstrap Buttons

btn-default btn-primary btn-success btn-info btn-warning btn-danger + +

Additional defined Buttons

+

+btn-white +btn-orange +btn-green +btn-blue +btn-yellow +btn-red +

+

+btn-black +btn-mandarin +btn-lime +btn-cyan +btn-caramel +btn-plum +btn-purple +


-

Text-Muted {% lorem %}

-

Text-Primary {% lorem %}

-

Text-Success {% lorem %}

-

Text-Info {% lorem %}

-

Text-Warning {% lorem %}

-

Text-Danger {% lorem %}

+

Regular Bootstrap Text

+

text-muted {% lorem %}

+

text-primary {% lorem %}

+

text-success {% lorem %}

+

text-info {% lorem %}

+

text-warning {% lorem %}

+

text-danger {% lorem %}

+

Additional defined Text

+

text-default {% lorem %}

+

text-orange {% lorem %}

+

text-green {% lorem %}

+

text-blue {% lorem %}

+

text-yellow {% lorem %}

+

text-red {% lorem %}

+

text-black {% lorem %}

+

text-mandarin {% lorem %}

+

text-lime {% lorem %}

+

text-cyan {% lorem %}

+

text-caramel {% lorem %}

+

text-plum {% lorem %}

+

text-purple {% lorem %}

+
+ + +

Regular Bootstrap Backgrounds

+

bg-primary {% lorem %}

bg-success {% lorem %}

bg-info {% lorem %}

bg-warning {% lorem %}

bg-danger {% lorem %}

+ +

Additional defined Backgrounds

+

bg-orange {% lorem %}

+

bg-green {% lorem %}

+

bg-blue {% lorem %}

+

bg-yellow {% lorem %}

+

bg-red {% lorem %}

+

bg-mandarin {% lorem %}

+

bg-lime {% lorem %}

+

bg-cyan {% lorem %}

+

bg-caramel {% lorem %}

+

bg-plum {% lorem %}

+

bg-purple {% lorem %}

+
{% endblock page-container-fluid %} diff --git a/dav_base/tests/test_templates.py b/dav_base/tests/test_templates.py index f5fb812..b61e7ee 100644 --- a/dav_base/tests/test_templates.py +++ b/dav_base/tests/test_templates.py @@ -45,9 +45,9 @@ class TemplatesTestCase(SimpleTestCase): html = """ """ diff --git a/dav_events/choices.py b/dav_events/choices.py index 8167b8b..6388955 100644 --- a/dav_events/choices.py +++ b/dav_events/choices.py @@ -71,6 +71,7 @@ DEADLINE_CHOICES = ChoiceSet([ LEVEL_CHOICES = ChoiceSet([ ('beginner', _(u'Anfänger')), ('advanced', _(u'Fortgeschrittene')), + ('family', _(u'Familien')), ]) MEALS_CHOICES = ChoiceSet([ diff --git a/dav_events/forms/events.py b/dav_events/forms/events.py index af144f1..2b83268 100644 --- a/dav_events/forms/events.py +++ b/dav_events/forms/events.py @@ -227,7 +227,7 @@ class ModeForm(EventCreateForm): ) level = forms.ChoiceField(choices=choices.LEVEL_CHOICES, - label=_(u'Schwierigkeitsnivau'), + label=_(u'Schwierigkeitsnivau / Familien'), widget=forms.RadioSelect()) first_day = forms.DateField(required=True, @@ -970,28 +970,38 @@ class DescriptionForm(EventCreateForm): value = u'' if mode == 'training': - if level == 'beginner': - value += u'%s ' % ugettext(u'Grundkurs') + # Titelprefix für Kurse + if level == 'family': + value += u'%s' % ugettext(u'Familienkurs') + elif level == 'beginner': + value += u'%s' % ugettext(u'Grundkurs') else: - value += u'%s ' % ugettext(u'Aufbaukurs') + value += u'%s' % ugettext(u'Aufbaukurs') if sport == 'B': - value += u'%s' % ugettext(u'Alpin') + value += u' %s' % ugettext(u'Alpin') elif sport == 'K': if terrain == 'gym': - value += ugettext(u'Indoorklettern') + value += u' %s' % ugettext(u'Indoorklettern') elif terrain == 'crag': - value += ugettext(u'Fels') + value += u' %s' % ugettext(u'Fels') elif terrain == 'alpine': - value += ugettext(u'Alpinklettern') + value += u' %s' % ugettext(u'Alpinklettern') value += u': ...' - elif sport == 'W' and not last_day: - value += u'%s ...' % ugettext(u'Tageswanderung') + elif sport == 'W': + # Titelprefix für Wanderungen + if level == 'family': + value += u'%s ...' % ugettext(u'Familienwanderung') + elif not last_day: + value += u'%s ...' % ugettext(u'Tageswanderung') + elif level == 'family': + # Titelprefix für sonstige Familientouren + value += u'%s: ...' % ugettext(u'Familientour') if app_config.settings.forms_development_init: if not value: - value = u'%s' % choices.SPORT_CHOICES.get_label(sport) + value += u'%s' % choices.SPORT_CHOICES.get_label(sport) return value diff --git a/dav_events/migrations/0043_alter_event_level.py b/dav_events/migrations/0043_alter_event_level.py new file mode 100644 index 0000000..60d0f76 --- /dev/null +++ b/dav_events/migrations/0043_alter_event_level.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.13 on 2023-02-26 22:19 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dav_events', '0042_auto_20220607_1345'), + ] + + operations = [ + migrations.AlterField( + model_name='event', + name='level', + field=models.CharField(choices=[('beginner', 'Anfänger'), ('advanced', 'Fortgeschrittene'), ('family', 'Familien')], max_length=25, verbose_name='Schwierigkeitsnivau'), + ), + ] diff --git a/dav_events/templates/dav_events/event_detail.html b/dav_events/templates/dav_events/event_detail.html index b2865d7..e2515a2 100644 --- a/dav_events/templates/dav_events/event_detail.html +++ b/dav_events/templates/dav_events/event_detail.html @@ -147,7 +147,7 @@ {% endif %} {% if has_permission_realize and is_started and not is_canceled %} - {% if is_realized %} {% bootstrap_icon 'check' %}  @@ -158,7 +158,7 @@ {% endif %} {% if has_permission_cancel and is_submitted and not is_realized %} - {% if is_canceled %} {% bootstrap_icon 'check' %}  diff --git a/dav_events/templates/dav_events/home.html b/dav_events/templates/dav_events/home.html index ce39d20..9e7809b 100644 --- a/dav_events/templates/dav_events/home.html +++ b/dav_events/templates/dav_events/home.html @@ -3,6 +3,6 @@ {% load i18n %} {% block page-container-fluid %} -

{% trans 'Für Tourenleiter' %}

+

{% trans 'Für Tourenleiter*innen' %}

{% include './includes/home_tiles.html' %} {% endblock page-container-fluid %} diff --git a/dav_events/tests/generic.py b/dav_events/tests/generic.py index f12a516..d10b92e 100644 --- a/dav_events/tests/generic.py +++ b/dav_events/tests/generic.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals import datetime import json +import logging import os from django.apps import apps from django.contrib.auth import get_user_model @@ -12,21 +13,30 @@ from ..models.eventstatus import EventStatus class RoleMixin: - def create_user_for_role(self, role_name, password, first_name, last_name): - group = Group(name=role_name) - group.save() + def create_user_for_role(self, role_name, first_name, last_name, password='password'): + app_config = apps.get_app_config('dav_events') + var_name = 'groups_{}'.format(role_name) + if hasattr(app_config.settings, var_name): + # There are groups configured for this role. + # Use the first group name of the configured groups. + group_name = getattr(app_config.settings, var_name)[0] + else: + # There are no groups configured for this role. + # Configure one. + group_name = role_name + setting_name = 'groups_{}'.format(role_name) + setattr(app_config.settings, setting_name, [group_name]) + + # Make sure the configured group exists. + group = Group.objects.get_or_create(name=group_name)[0] user_model = get_user_model() - email = '{}@localhost'.format(role_name) - user_name = email + number = user_model.objects.all().count() + 1 + user_name = 'user{}-{}@localhost'.format(number, role_name) user = user_model.objects.create_user(username=user_name, password=password, email=user_name, first_name=first_name, last_name=last_name) user.groups.add(group) - setting_name = 'groups_{}'.format(role_name) - app_config = apps.get_app_config('dav_events') - setattr(app_config.settings, setting_name, [role_name]) - return user diff --git a/dav_events/tests/test_emails.py b/dav_events/tests/test_emails.py index f17fb42..91e9200 100644 --- a/dav_events/tests/test_emails.py +++ b/dav_events/tests/test_emails.py @@ -176,11 +176,11 @@ class EmailTestCase(EmailTestMixin, RoleMixin, EventMixin, TestCase): self.event = self.create_event_by_model(event_data) self.trainer = self.event.owner - self.manager_super = self.create_user_for_role('manager_super', 'password', 'Touren', 'Referent') - self.manager_w = self.create_user_for_role('manager_w', 'password', 'Bereichsleiter', 'Wandern') - self.manager_s = self.create_user_for_role('manager_s', 'password', 'Bereichsleiter', 'Ski') - self.publisher_web = self.create_user_for_role('publisher_web', 'password', 'Joomla', 'Redakteur') - self.publisher_facebook = self.create_user_for_role('publisher_facebook', 'password', 'Facebook', 'Redakteur') + self.manager_super = self.create_user_for_role('manager_super', 'Touren', 'Referent') + self.manager_w = self.create_user_for_role('manager_w', 'Bereichsleiter', 'Wandern') + self.manager_s = self.create_user_for_role('manager_s', 'Bereichsleiter', 'Ski') + self.publisher_web = self.create_user_for_role('publisher_web', 'Joomla', 'Redakteur') + self.publisher_facebook = self.create_user_for_role('publisher_facebook', 'Facebook', 'Redakteur') model = get_user_model() self.recipient = model.objects.create_user(username='recipient@example.com', diff --git a/dav_events/tests/test_roles.py b/dav_events/tests/test_roles.py new file mode 100644 index 0000000..321fa41 --- /dev/null +++ b/dav_events/tests/test_roles.py @@ -0,0 +1,128 @@ +from django.apps import apps +from django.contrib.auth import get_user_model +from django.contrib.auth.models import Group +from django.test import TestCase + +from ..roles import get_system_user, get_ghost_user, get_group_members +from ..roles import get_group_names_by_role, get_users_by_role, has_role + +from .generic import RoleMixin + + +class RolesTestCase(RoleMixin, TestCase): + def test_get_system_user(self): + u = get_system_user() + self.assertIsInstance(u, get_user_model()) + self.assertEqual(u.username, '-system-') + self.assertIsNotNone(u.pk) + + def test_get_ghost_user(self): + u = get_ghost_user() + self.assertIsInstance(u, get_user_model()) + self.assertEqual(u.username, '-deleted-') + self.assertIsNotNone(u.pk) + + def test_group_members(self): + # Create some groups + g1 = Group(name='gruppe1') + g1.save() + g2 = Group(name='gruppe2') + g2.save() + + # Create some users and add them to groups + user_model = get_user_model() + u1 = user_model(username='user1') + u1.save() + g1.user_set.add(u1) + u2 = user_model(username='user2') + u2.save() + g1.user_set.add(u2) + u3 = user_model(username='user3') + u3.save() + g2.user_set.add(u3) + + # Create a user, that will belong to no group + u4 = user_model(username='user4') + u4.save() + + # Check results of get_group_members + self.assertSequenceEqual(get_group_members('gruppe1'), [u1, u2]) + self.assertSequenceEqual(get_group_members('gruppe2'), [u3]) + with self.assertRaises(Group.DoesNotExist): + get_group_members('gruppe3') + self.assertSequenceEqual(get_group_members('gruppe3', ignore_missing=True), []) + + def test_get_group_names_by_role_default_config(self): + test_data = [ + ('publisher_print', ['Redaktion_KA-Alpin']), + ('publisher_web', ['Redaktion_Joomla']), + ('publisher_facebook', ['Redaktion_Facebook']), + ('manager_w', ['Bereichsleiter_Wandern', 'Tourenreferenten']), + ('manager_s', ['Bereichsleiter_Ski', 'Tourenreferenten']), + ('manager_m', ['Bereichsleiter_MTB', 'Tourenreferenten']), + ('manager_k', ['Bereichsleiter_Klettern', 'Tourenreferenten']), + ('manager_b', ['Bereichsleiter_Bergsteigen', 'Tourenreferenten']), + ('manager_super', ['Tourenreferenten']), + ('publisher', ['Redaktion_KA-Alpin', 'Redaktion_Joomla', 'Redaktion_Facebook']), + ('manager', ['Bereichsleiter_Wandern', 'Bereichsleiter_Ski', 'Bereichsleiter_MTB', + 'Bereichsleiter_Klettern', 'Bereichsleiter_Bergsteigen', 'Tourenreferenten']), + ('office', ['Geschaeftsstelle']), + ] + for role, expected_group_names in test_data: + group_names = get_group_names_by_role(role) + self.assertEqual(len(group_names), len(expected_group_names)) + for name in group_names: + self.assertIn(name, expected_group_names) + + def get_group_names_by_role_adhoc_config(self): + role_name = 'manager_w' + super_role_name = 'manager_super' + role_groups = ['group1', 'group2'] + super_role_groups = ['group3', 'group4'] + + role_setting = 'groups_{}'.format(role_name) + super_role_setting = 'groups_{}'.format(super_role_name) + + app_config = apps.get_app_config('dav_events') + + buf_role_setting = getattr(app_config.settings, role_setting) + buf_super_role_setting = getattr(app_config.settings, super_role_setting) + + setattr(app_config.settings, role_setting, role_groups) + setattr(app_config.settings, super_role_setting, super_role_groups) + + expected_groups = role_groups + super_role_groups + self.assertSequenceEqual(get_group_names_by_role(role_name), expected_groups) + + setattr(app_config.settings, role_setting, buf_role_setting) + setattr(app_config.settings, super_role_setting, buf_super_role_setting) + + def test_get_group_names_by_role_notexist(self): + self.assertSequenceEqual(get_group_names_by_role('not_existing_test_role'), []) + + def test_get_users_by_role(self): + u1 = self.create_user_for_role('manager_super', 'Touren', 'Referent') + u2 = self.create_user_for_role('manager_super', 'Praktikant', 'Referent') + u3 = self.create_user_for_role('manager_w', 'Bereichsleiter', 'Wandern') + u4 = self.create_user_for_role('manager_w', 'Praktikant', 'Wandern') + u5 = self.create_user_for_role('manager_s', 'Bereichsleiter', 'Ski') + users = get_users_by_role('manager_w') + self.assertSequenceEqual(users, [u1, u2, u3, u4]) + + def test_has_role(self): + u1 = self.create_user_for_role('test_role1', 'User1', 'Test') + u2 = self.create_user_for_role('test_role1', 'User2', 'Test') + u3 = self.create_user_for_role('test_role2', 'User3', 'Test') + u4 = self.create_user_for_role('test_role2', 'User4', 'Test') + + g1 = u1.groups.first() + g1.user_set.add(u4) + + self.assertTrue(has_role(u1, 'test_role1')) + self.assertFalse(has_role(u1, 'test_role2')) + self.assertTrue(has_role(u2, 'test_role1')) + self.assertFalse(has_role(u2, 'test_role2')) + self.assertFalse(has_role(u3, 'test_role1')) + self.assertTrue(has_role(u3, 'test_role2')) + self.assertTrue(has_role(u4, 'test_role1')) + self.assertTrue(has_role(u4, 'test_role2')) \ No newline at end of file diff --git a/dav_events/tests/test_screenshots.py b/dav_events/tests/test_screenshots.py index a211883..78de5aa 100644 --- a/dav_events/tests/test_screenshots.py +++ b/dav_events/tests/test_screenshots.py @@ -658,20 +658,24 @@ class TestCase(SeleniumAuthMixin, RoleMixin, ScreenshotTestCase): def setUp(self): super(TestCase, self).setUp() - - password = TEST_PASSWORD - self.manager_super = self.create_user_for_role('manager_super', password, - 'Manager Super', 'Tourenreferent') - self.manager_w = self.create_user_for_role('manager_w', password, - 'Manager W', 'BereichsleiterWandern') - self.manager_s = self.create_user_for_role('manager_s', password, - 'Manager S', 'BereichsleiterSki') - self.publisher_print = self.create_user_for_role('publisher_print', password, - 'Publisher Print', 'RedaktionPrint') - self.publisher_web = self.create_user_for_role('publisher_web', password, - 'Publisher Web', 'RedaktionWeb') - self.publisher_facebook = self.create_user_for_role('publisher_facebook', password, - 'Publisher Facebook', 'RedaktionFacebook') + self.manager_super = self.create_user_for_role('manager_super', + 'Manager Super', 'Tourenreferent', + password=TEST_PASSWORD) + self.manager_w = self.create_user_for_role('manager_w', + 'Manager W', 'BereichsleiterWandern', + password=TEST_PASSWORD) + self.manager_s = self.create_user_for_role('manager_s', + 'Manager S', 'BereichsleiterSki', + password=TEST_PASSWORD) + self.publisher_print = self.create_user_for_role('publisher_print', + 'Publisher Print', 'RedaktionPrint', + password=TEST_PASSWORD) + self.publisher_web = self.create_user_for_role('publisher_web', + 'Publisher Web', 'RedaktionWeb', + password=TEST_PASSWORD) + self.publisher_facebook = self.create_user_for_role('publisher_facebook', + 'Publisher Facebook', 'RedaktionFacebook', + password=TEST_PASSWORD) def test_screenshots(self): # self.quit_selenium = False diff --git a/dav_events/tests/test_urls.py b/dav_events/tests/test_urls.py index c626b84..df70ec7 100644 --- a/dav_events/tests/test_urls.py +++ b/dav_events/tests/test_urls.py @@ -1,8 +1,10 @@ +from django.conf import settings + from dav_base.tests.generic import Url, UrlsTestCase from .. import views -url_prefix = 'events' +url_prefix = settings.MODULE_CONFIG.modules['dav_events'].url_prefix class TestCase(UrlsTestCase): diff --git a/dav_events/views/events.py b/dav_events/views/events.py index 40f3f37..823cdd3 100644 --- a/dav_events/views/events.py +++ b/dav_events/views/events.py @@ -713,7 +713,7 @@ class EventCreateView(EventPermissionMixin, generic.FormView): else: event.editor = self.request.user - # Check for double submission (seems to happens accidentally if smartphone user reload the submit page) + # Check for double submission (seems to happen accidentally if smartphone user reload the submit page) possible_doublets = models.Event.objects.filter(owner=event.owner, title=event.title, first_day=event.first_day) diff --git a/dav_registration/static/dav_registration/img/sport_icons/F.png b/dav_registration/static/dav_registration/img/sport_icons/F.png new file mode 100644 index 0000000..f388388 Binary files /dev/null and b/dav_registration/static/dav_registration/img/sport_icons/F.png differ diff --git a/dav_registration/static/dav_registration/img/sport_icons/outdooractive/Noun_Project_Original.png b/dav_registration/static/dav_registration/img/sport_icons/outdooractive/Noun_Project_Original.png new file mode 100644 index 0000000..6303865 Binary files /dev/null and b/dav_registration/static/dav_registration/img/sport_icons/outdooractive/Noun_Project_Original.png differ diff --git a/dav_registration/templates/dav_registration/event_list.html b/dav_registration/templates/dav_registration/event_list.html index 4a69056..216e170 100644 --- a/dav_registration/templates/dav_registration/event_list.html +++ b/dav_registration/templates/dav_registration/event_list.html @@ -5,16 +5,29 @@ {% block page-container-fluid %}
+
+ + + + + + + +

{% trans 'Touren & Kurse' %}

+ + {% for event in event_list %} + +
@@ -56,6 +70,9 @@ {{ event.get_number }} - {{ event.title }} + {% if event.level == 'family' %} +  für Familien + {% endif %}

{% if event.is_canceled %}{% endif %} {{ event.get_formated_date }} @@ -110,6 +127,37 @@

diff --git a/dav_registration/templates/dav_registration/home.html b/dav_registration/templates/dav_registration/home.html new file mode 100644 index 0000000..efdac5c --- /dev/null +++ b/dav_registration/templates/dav_registration/home.html @@ -0,0 +1,8 @@ +{% extends "dav_registration/base.html" %} +{% load bootstrap3 %} +{% load i18n %} + +{% block page-container-fluid %} +

{% trans 'Für Teilnehmer*innen' %}

+{% include './includes/home_tiles.html' %} +{% endblock page-container-fluid %} diff --git a/dav_registration/templates/dav_registration/includes/home_tiles.html b/dav_registration/templates/dav_registration/includes/home_tiles.html new file mode 100644 index 0000000..247b82e --- /dev/null +++ b/dav_registration/templates/dav_registration/includes/home_tiles.html @@ -0,0 +1,17 @@ +{% load bootstrap3 %} +{% load i18n %} + +
diff --git a/dav_registration/urls.py b/dav_registration/urls.py index c0585d8..3051da5 100644 --- a/dav_registration/urls.py +++ b/dav_registration/urls.py @@ -6,6 +6,7 @@ app_name = 'dav_registration' urlpatterns = [ url(r'^$', views.RootView.as_view(), name='root'), + url(r'^home$', views.HomeView.as_view(), name='home'), url(r'^finished', views.RegistrationSuccessView.as_view(), name='registered'), url(r'^event/(?P\d+)/registration', views.RegistrationView.as_view(), name='register'), url(r'^event/(?P\d+)/', views.EventDetailView.as_view(), name='event'), diff --git a/dav_registration/views.py b/dav_registration/views.py index d8efc02..f2b39d0 100644 --- a/dav_registration/views.py +++ b/dav_registration/views.py @@ -8,6 +8,7 @@ from django.urls import reverse_lazy from django.utils.translation import ugettext as _ from django.views import generic +from dav_events.choices import SPORT_CHOICES, LEVEL_CHOICES from dav_events.models.event import Event from .forms import RegistrationForm @@ -26,6 +27,10 @@ class RootView(generic.RedirectView): return super().get(request, *args, **kwargs) +class HomeView(generic.TemplateView): + template_name = 'dav_registration/home.html' + + class EventListView(generic.ListView): model = Event template_name = 'dav_registration/event_list.html' @@ -44,6 +49,25 @@ class EventListView(generic.ListView): return qs + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + if hasattr(self, 'init_sport_filter'): + context['init_sport_filter'] = self.init_sport_filter + if hasattr(self, 'init_level_filter'): + context['init_level_filter'] = self.init_level_filter + return context + + def get(self, request, *args, **kwargs): + if 'sport' in request.GET: + sport = request.GET['sport'] + if (sport, 'Bogus') in SPORT_CHOICES: + self.init_sport_filter = sport + if 'level' in request.GET: + level = request.GET['level'] + if (level, 'Bogus') in LEVEL_CHOICES: + self.init_level_filter = level + return super().get(request, *args, **kwargs) + class EventDetailView(generic.DetailView): model = Event