Merge pull request 'Update production branch with latest stuff' (#66) from master into production
All checks were successful
buildbot/django-dav-events--test Build done.

Reviewed-on: #66
This commit was merged in pull request #66.
This commit is contained in:
2023-05-08 12:48:23 +02:00
24 changed files with 787 additions and 147 deletions

View File

@@ -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)

View File

@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
# E-Mails
EMAIL_SENDER = 'DAV heinzel <heinzel@alpenverein-karlsruhe.de>'
EMAIL_SENDER = 'DAV Touren & Kurse <tourenportal@alpenverein-karlsruhe.de>'
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'

View File

@@ -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;
}

View File

@@ -31,9 +31,9 @@
<div id="page">
<div id="page-header">
<h2>
<a href="{% url 'root' %}">
<img width="217" height="30" src="{% static 'dav_base/img/brand.png' %}" />{% block project-name %}{% include_if_exist 'project_name.html' %}{% endblock project-name %}
</a>
<a href="https://alpenverein-karlsruhe.de" target="_blank"><img
width="217" height="30" src="{% static 'dav_base/img/brand.png' %}" /></a>
<a href="{% url 'root' %}">{% block project-name %}{% include_if_exist 'project_name.html' %}{% endblock project-name %}</a>
</h2>
<div id="login-widget">{% include_if_exist 'dav_auth/includes/login_widget.html' %}</div>
</div>
@@ -59,9 +59,9 @@
</div>
<div id="page-footer">
<div class="signum">{% block signum %}<a href="mailto:heinzel@alpenverein-karlsruhe.de">heinzel@alpenverein-karlsruhe.de</a>{% endblock signum %}</div>
<a href="http://alpenverein-karlsruhe.de" target="_blank">&copy; Sektion Karlsruhe im Deutschen Alpenverein (DAV) e.V.</a> &ensp;&bull;&ensp;
<a href="http://alpenverein-karlsruhe.de/impressum">{% trans 'Impressum' %}</a>
<div class="signum">{% block signum %}<a href="mailto:tourenportal@alpenverein-karlsruhe.de">tourenportal@alpenverein-karlsruhe.de</a>{% endblock signum %}</div>
<a href="https://alpenverein-karlsruhe.de" target="_blank">&copy; Sektion Karlsruhe im Deutschen Alpenverein (DAV) e.V.</a> &ensp;&bull;&ensp;
<a href="https://alpenverein-karlsruhe.de/impressum">{% trans 'Impressum' %}</a>
</div>
</div>
</body>

View File

@@ -5,8 +5,10 @@
{% block messages %}
<div class="container-fluid">
{% 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" %}
</div>
{% endblock messages %}
@@ -19,13 +21,14 @@
<h6>Header h6 <small>Small Text</small></h6>
<div class="jumbotron">
<h1>Hallo</h1>
<h1>This is a jumbotron with h1 header</h1>
<p>
{% lorem %}
</p>
</div>
<div class="well">
<h2>This is a well with h2 header</h2>
<p class="lead">
{% lorem %}
</p>
@@ -33,24 +36,79 @@
{% lorem %}
</p>
</div>
<hr />
<h3>Regular Bootstrap Buttons</h3>
<a class="btn btn-default" href="#">btn-default</a>
<a class="btn btn-primary" href="#">btn-primary</a>
<a class="btn btn-success" href="#">btn-success</a>
<a class="btn btn-info" href="#">btn-info</a>
<a class="btn btn-warning" href="#">btn-warning</a>
<a class="btn btn-danger" href="#">btn-danger</a>
<h3>Additional defined Buttons</h3>
<p>
<a class="btn btn-white" href="#">btn-white</a>
<a class="btn btn-orange" href="#">btn-orange</a>
<a class="btn btn-green" href="#">btn-green</a>
<a class="btn btn-blue" href="#">btn-blue</a>
<a class="btn btn-yellow" href="#">btn-yellow</a>
<a class="btn btn-red" href="#">btn-red</a>
</p>
<p>
<a class="btn btn-black" href="#">btn-black</a>
<a class="btn btn-mandarin" href="#">btn-mandarin</a>
<a class="btn btn-lime" href="#">btn-lime</a>
<a class="btn btn-cyan" href="#">btn-cyan</a>
<a class="btn btn-caramel" href="#">btn-caramel</a>
<a class="btn btn-plum" href="#">btn-plum</a>
<a class="btn btn-purple" href="#">btn-purple</a>
</p>
<hr />
<p class="text-muted"><span class="label label-default">Text-Muted</span> {% lorem %}</p>
<p class="text-primary"><span class="label label-primary">Text-Primary</span> {% lorem %}</p>
<p class="text-success"><span class="label label-success">Text-Success</span> {% lorem %}</p>
<p class="text-info"><span class="label label-info">Text-Info</span> {% lorem %}</p>
<p class="text-warning"><span class="label label-warning">Text-Warning</span> {% lorem %}</p>
<p class="text-danger"><span class="label label-danger">Text-Danger</span> {% lorem %}</p>
<h3>Regular Bootstrap Text</h3>
<p class="text-muted"><span class="label label-default">text-muted</span> {% lorem %}</p>
<p class="text-primary"><span class="label label-primary">text-primary</span> {% lorem %}</p>
<p class="text-success"><span class="label label-success">text-success</span> {% lorem %}</p>
<p class="text-info"><span class="label label-info">text-info</span> {% lorem %}</p>
<p class="text-warning"><span class="label label-warning">text-warning</span> {% lorem %}</p>
<p class="text-danger"><span class="label label-danger">text-danger</span> {% lorem %}</p>
<h3>Additional defined Text</h3>
<p class="text-default"><span class="label label-default">text-default</span> {% lorem %}</p>
<p class="text-orange"><span class="label label-orange">text-orange</span> {% lorem %}</p>
<p class="text-green"><span class="label label-green">text-green</span> {% lorem %}</p>
<p class="text-blue"><span class="label label-blue">text-blue</span> {% lorem %}</p>
<p class="text-yellow"><span class="label label-yellow">text-yellow</span> {% lorem %}</p>
<p class="text-red"><span class="label label-red">text-red</span> {% lorem %}</p>
<p class="text-black"><span class="label label-black">text-black</span> {% lorem %}</p>
<p class="text-mandarin"><span class="label label-mandarin">text-mandarin</span> {% lorem %}</p>
<p class="text-lime"><span class="label label-lime">text-lime</span> {% lorem %}</p>
<p class="text-cyan"><span class="label label-cyan">text-cyan</span> {% lorem %}</p>
<p class="text-caramel"><span class="label label-caramel">text-caramel</span> {% lorem %}</p>
<p class="text-plum"><span class="label label-plum">text-plum</span> {% lorem %}</p>
<p class="text-purple"><span class="label label-purple">text-purple</span> {% lorem %}</p>
<hr />
<h3>Regular Bootstrap Backgrounds</h3>
<p class="bg-primary"><span class="label label-primary">bg-primary</span> {% lorem %} </p>
<p class="bg-success"><span class="label label-success">bg-success</span> {% lorem %} </p>
<p class="bg-info"><span class="label label-info">bg-info</span> {% lorem %} </p>
<p class="bg-warning"><span class="label label-warning">bg-warning</span> {% lorem %} </p>
<p class="bg-danger"><span class="label label-danger">bg-danger</span> {% lorem %} </p>
<h3>Additional defined Backgrounds</h3>
<p class="bg-orange"><span class="label label-orange">bg-orange</span> {% lorem %} </p>
<p class="bg-green"><span class="label label-green">bg-green</span> {% lorem %} </p>
<p class="bg-blue"><span class="label label-blue">bg-blue</span> {% lorem %} </p>
<p class="bg-yellow"><span class="label label-yellow">bg-yellow</span> {% lorem %} </p>
<p class="bg-red"><span class="label label-red">bg-red</span> {% lorem %} </p>
<p class="bg-mandarin"><span class="label label-mandarin">bg-mandarin</span> {% lorem %} </p>
<p class="bg-lime"><span class="label label-lime">bg-lime</span> {% lorem %} </p>
<p class="bg-cyan"><span class="label label-cyan">bg-cyan</span> {% lorem %} </p>
<p class="bg-caramel"><span class="label label-caramel">bg-caramel</span> {% lorem %} </p>
<p class="bg-plum"><span class="label label-plum">bg-plum</span> {% lorem %} </p>
<p class="bg-purple"><span class="label label-purple">bg-purple</span> {% lorem %} </p>
<hr />
{% endblock page-container-fluid %}

View File

@@ -45,9 +45,9 @@ class TemplatesTestCase(SimpleTestCase):
html = """
<div id="page-footer">
<div class="signum"><a href="mailto:heinzel@alpenverein-karlsruhe.de">heinzel@alpenverein-karlsruhe.de</a></div>
<a href="http://alpenverein-karlsruhe.de" target="_blank">&copy; Sektion Karlsruhe im Deutschen Alpenverein (DAV) e.V.</a> &ensp;&bull;&ensp;
<a href="http://alpenverein-karlsruhe.de/impressum">Impressum</a>
<div class="signum"><a href="mailto:tourenportal@alpenverein-karlsruhe.de">tourenportal@alpenverein-karlsruhe.de</a></div>
<a href="https://alpenverein-karlsruhe.de" target="_blank">&copy; Sektion Karlsruhe im Deutschen Alpenverein (DAV) e.V.</a> &ensp;&bull;&ensp;
<a href="https://alpenverein-karlsruhe.de/impressum">Impressum</a>
</div>
"""

View File

@@ -71,6 +71,7 @@ DEADLINE_CHOICES = ChoiceSet([
LEVEL_CHOICES = ChoiceSet([
('beginner', _(u'Anfänger')),
('advanced', _(u'Fortgeschrittene')),
('family', _(u'Familien')),
])
MEALS_CHOICES = ChoiceSet([

View File

@@ -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

View File

@@ -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'),
),
]

View File

@@ -147,7 +147,7 @@
</a>
{% endif %}
{% if has_permission_realize and is_started and not is_canceled %}
<a id="btn-realize" class="btn btn-sm {% if not is_realized %}btn-dav-lime{% else %}btn-default disabled{% endif %}"
<a id="btn-realize" class="btn btn-sm {% if not is_realized %}btn-lime{% else %}btn-default disabled{% endif %}"
href="{% url 'dav_events:updatestatus' event.pk 'realized' %}">
{% if is_realized %}
{% bootstrap_icon 'check' %}&thinsp;
@@ -158,7 +158,7 @@
</a>
{% endif %}
{% if has_permission_cancel and is_submitted and not is_realized %}
<a id="btn-cancel" class="btn btn-sm {% if not is_canceled %}btn-dav-mandarin{% else %}btn-default disabled{% endif %}"
<a id="btn-cancel" class="btn btn-sm {% if not is_canceled %}btn-mandarin{% else %}btn-default disabled{% endif %}"
href="{% url 'dav_events:updatestatus' event.pk 'canceled' %}">
{% if is_canceled %}
{% bootstrap_icon 'check' %}&thinsp;

View File

@@ -3,6 +3,6 @@
{% load i18n %}
{% block page-container-fluid %}
<h3 class="top-most">{% trans 'Für Tourenleiter' %}</h3>
<h3 class="top-most">{% trans 'Für Tourenleiter*innen' %}</h3>
{% include './includes/home_tiles.html' %}
{% endblock page-container-fluid %}

View File

@@ -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

View File

@@ -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',

View File

@@ -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'))

View File

@@ -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

View File

@@ -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):

View File

@@ -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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -5,16 +5,29 @@
{% block page-container-fluid %}
<div>
<div style="float: right;">
<button id="btn-filter-All" type="button" class="btn btn-xs btn-green">Alle Touren</button>
<button id="btn-filter-B" type="button" class="btn btn-xs btn-sport-B btn-white">Bergsteigen</button>
<button id="btn-filter-family" type="button" class="btn btn-xs btn-level-family btn-white">Familien</button>
<button id="btn-filter-K" type="button" class="btn btn-xs btn-sport-K btn-white">Klettern</button>
<button id="btn-filter-M" type="button" class="btn btn-xs btn-sport-M btn-white">Mountainbike</button>
<button id="btn-filter-S" type="button" class="btn btn-xs btn-sport-S btn-white">Ski</button>
<button id="btn-filter-W" type="button" class="btn btn-xs btn-sport-W btn-white">Wandern</button>
</div>
<h3 class="top-most">{% trans 'Touren & Kurse' %}</h3>
<table id="objects_table" class="table table-bordered table-hover">
<thead>
<tr>
<th class="hidden">Sport</th>
<th class="hidden">Level</th>
<th><input type="text" id="searchfield" placeholder="{% trans 'Volltextsuche' %}"></th>
</tr>
</thead>
<tbody>
{% for event in event_list %}
<tr>
<td class="hidden">{{ event.sport }}</td>
<td class="hidden">{{ event.level }}</td>
<td>
<div class="pull-right" style="margin-left: 2em;">
<a role="button" id="controlChevronCollapseDetails{{ event.id }}" data-toggle="collapse"
@@ -45,6 +58,7 @@
<img src="{% static icon %}" width="48px"
alt="{{ event.get_sport_display }}" title="{{ event.get_sport_display }}">
<span class="hidden">{{ event.get_sport_display }}</span>
<span class="hidden">{{ event.get_level_display }}</span>
{% endwith %}
{% endwith %}
</div>
@@ -56,6 +70,9 @@
{{ event.get_number }} - {{ event.title }}
</a>
</span></strong>
{% if event.level == 'family' %}
&emsp;<span class="label label-level-family">für Familien</span>
{% endif %}
<p>
{% if event.is_canceled %}<del>{% endif %}
{{ event.get_formated_date }}
@@ -110,6 +127,37 @@
</tbody>
</table>
<script type="text/javascript">
function filter_table(table, sport, level) {
const sport_choices = ["B", "K", "M", "S", "W"];
const level_choices = ["beginner", "advanced", "family"];
if(sport != "*")
$("#btn-filter-" + sport).removeClass("btn-white");
if(level != "*")
$("#btn-filter-" + level).removeClass("btn-white");
if(sport == "*" && level == "*")
$("#btn-filter-All").removeClass("btn-white");
else
$("#btn-filter-All").addClass("btn-white");
for(let i in sport_choices) {
if(sport != sport_choices[i])
$("#btn-filter-" + sport_choices[i]).addClass("btn-white");
}
for(let i in level_choices) {
if(level != level_choices[i])
$("#btn-filter-" + level_choices[i]).addClass("btn-white");
}
if(sport == "*")
sport = ""
table.column(0).search(sport).draw();
if(level == "*")
level = ""
table.column(1).search(level).draw();
};
$(document).ready( function () {
var table = $("#objects_table").DataTable( {
ordering: false,
@@ -124,12 +172,32 @@
zeroRecords: "{% trans 'Keine passenden Einträge.' %}",
}
} );
$("#btn-filter-All").on("click", function() {
filter_table(table, "*", "*");
} );
$("#btn-filter-B").on("click", function() {
filter_table(table, "B", "*")
} );
$("#btn-filter-K").on("click", function() {
filter_table(table, "K", "*")
} );
$("#btn-filter-M").on("click", function() {
filter_table(table, "M", "*")
} );
$("#btn-filter-S").on("click", function() {
filter_table(table, "S", "*")
} );
$("#btn-filter-W").on("click", function() {
filter_table(table, "W", "*")
} );
$("#btn-filter-family").on("click", function() {
filter_table(table, "*", "family")
} );
$("#searchfield").on( "keyup change", function() {
table
.search( this.value )
.draw();
table.column(2).search( this.value ).draw();
} );
$("#objects_table_filter").hide();
filter_table(table, "{{ init_sport_filter|default:'*' }}", "{{ init_level_filter|default:'*' }}");
} );
</script>
</div>

View File

@@ -0,0 +1,8 @@
{% extends "dav_registration/base.html" %}
{% load bootstrap3 %}
{% load i18n %}
{% block page-container-fluid %}
<h3 class="top-most">{% trans 'Für Teilnehmer*innen' %}</h3>
{% include './includes/home_tiles.html' %}
{% endblock page-container-fluid %}

View File

@@ -0,0 +1,17 @@
{% load bootstrap3 %}
{% load i18n %}
<div class="row">
<div class="col-sm-12">
<div class="well">
<p class="lead">Bei Veranstaltungen anmelden...</p>
<p>
Hier siehst du welche Touren und Kurse angeboten werden und
kannst dich dafür anmelden.
</p>
<p>
<a class="btn btn-primary" href="{% url 'dav_registration:root' %}">Zur Veranstaltungsliste</a>
</p>
</div>
</div>
</div>

View File

@@ -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<pk>\d+)/registration', views.RegistrationView.as_view(), name='register'),
url(r'^event/(?P<pk>\d+)/', views.EventDetailView.as_view(), name='event'),

View File

@@ -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