Merge pull request 'update production branch' (#57) from master into production
All checks were successful
buildbot/tox Build done.
All checks were successful
buildbot/tox Build done.
Reviewed-on: #57
This commit was merged in pull request #57.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,6 +4,7 @@
|
|||||||
/env/
|
/env/
|
||||||
/tmp/
|
/tmp/
|
||||||
|
|
||||||
|
/.eggs/
|
||||||
*.pyc
|
*.pyc
|
||||||
*.mo
|
*.mo
|
||||||
/.coverage
|
/.coverage
|
||||||
|
|||||||
13
.pylintrc
13
.pylintrc
@@ -10,7 +10,8 @@ disable=missing-docstring,
|
|||||||
missing-module-docstring,
|
missing-module-docstring,
|
||||||
missing-class-docstring,
|
missing-class-docstring,
|
||||||
missing-function-docstring,
|
missing-function-docstring,
|
||||||
useless-object-inheritance,
|
consider-using-f-string,
|
||||||
|
duplicate-code,
|
||||||
|
|
||||||
[BASIC]
|
[BASIC]
|
||||||
|
|
||||||
@@ -18,10 +19,20 @@ good-names=_,
|
|||||||
c,
|
c,
|
||||||
d,
|
d,
|
||||||
e,
|
e,
|
||||||
|
f,
|
||||||
i,
|
i,
|
||||||
j,
|
j,
|
||||||
k,
|
k,
|
||||||
|
n,
|
||||||
|
r,
|
||||||
|
s,
|
||||||
|
t,
|
||||||
|
pk,
|
||||||
|
|
||||||
[FORMAT]
|
[FORMAT]
|
||||||
|
|
||||||
max-line-length=120
|
max-line-length=120
|
||||||
|
|
||||||
|
[SIMILARITIES]
|
||||||
|
|
||||||
|
ignore-comments=no
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
REQUIREMENTS
|
REQUIREMENTS
|
||||||
============
|
============
|
||||||
- Python 2.7
|
- Python 3
|
||||||
- Python package virtualenv (in most cases this is distributed or installed together with python)
|
- Python package virtualenv (in most cases this is distributed or installed together with python)
|
||||||
- Django (will be installed automatically)
|
- Django (will be installed automatically)
|
||||||
- Several additional django related python packages (will be installed automatically)
|
- Several additional django related python packages (will be installed automatically)
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
import argparse
|
import argparse
|
||||||
import coverage
|
|
||||||
import datetime
|
import datetime
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
import coverage
|
||||||
from selenium import webdriver
|
from selenium import webdriver
|
||||||
from selenium.common.exceptions import WebDriverException
|
from selenium.common.exceptions import WebDriverException
|
||||||
|
|
||||||
|
|
||||||
class Command(object):
|
class Command: # pylint: disable=too-few-public-methods
|
||||||
default_browser = 'firefox'
|
default_browser = 'firefox'
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
default_app_config = 'dav_auth.apps.AppConfig'
|
default_app_config = 'dav_auth.apps.AppConfig' # pylint: disable=invalid-name
|
||||||
|
|||||||
@@ -8,5 +8,5 @@ DEFAULT_SETTINGS = (
|
|||||||
|
|
||||||
class AppConfig(_AppConfig):
|
class AppConfig(_AppConfig):
|
||||||
name = 'dav_auth'
|
name = 'dav_auth'
|
||||||
verbose_name = u'DAV Benutzerverwaltung'
|
verbose_name = 'DAV Benutzerverwaltung'
|
||||||
default_settings = DEFAULT_SETTINGS
|
default_settings = DEFAULT_SETTINGS
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
from dav_base.emails import AbstractMail
|
from dav_base.emails import AbstractMail
|
||||||
|
|
||||||
|
|
||||||
class PasswordSetEmail(AbstractMail):
|
class PasswordSetEmail(AbstractMail): # pylint: disable=too-few-public-methods
|
||||||
_subject = u'Zugangsdaten'
|
_subject = 'Zugangsdaten'
|
||||||
_template_name = 'dav_auth/emails/password_set.txt'
|
_template_name = 'dav_auth/emails/password_set.txt'
|
||||||
|
|
||||||
def __init__(self, user, password):
|
def __init__(self, user, password):
|
||||||
@@ -11,12 +11,12 @@ class PasswordSetEmail(AbstractMail):
|
|||||||
self._password = password
|
self._password = password
|
||||||
|
|
||||||
def _get_recipients(self):
|
def _get_recipients(self):
|
||||||
r = u'"{fullname}" <{email}>'.format(fullname=self._user.get_full_name(),
|
r = '"{fullname}" <{email}>'.format(fullname=self._user.get_full_name(),
|
||||||
email=self._user.email)
|
email=self._user.email)
|
||||||
return [r]
|
return [r]
|
||||||
|
|
||||||
def _get_context_data(self, extra_context=None):
|
def _get_context_data(self, extra_context=None):
|
||||||
context = super(PasswordSetEmail, self)._get_context_data(extra_context=extra_context)
|
context = super()._get_context_data(extra_context=extra_context)
|
||||||
context.update({
|
context.update({
|
||||||
'fullname': self._user.get_full_name(),
|
'fullname': self._user.get_full_name(),
|
||||||
'username': self._user.username,
|
'username': self._user.username,
|
||||||
|
|||||||
@@ -9,14 +9,13 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class LoginForm(auth_forms.AuthenticationForm):
|
class LoginForm(auth_forms.AuthenticationForm):
|
||||||
username = auth_forms.UsernameField(
|
username = auth_forms.UsernameField(
|
||||||
max_length=254,
|
label=_('E-Mail-Adresse'),
|
||||||
label=_(u'E-Mail-Adresse'),
|
|
||||||
widget=forms.TextInput(attrs={'autofocus': True}),
|
widget=forms.TextInput(attrs={'autofocus': True}),
|
||||||
)
|
)
|
||||||
|
|
||||||
error_messages = {
|
error_messages = {
|
||||||
'invalid_login': _(u'Benutzername oder Passwort falsch.'),
|
'invalid_login': _('Benutzername oder Passwort falsch.'),
|
||||||
'inactive': _("This account is inactive."),
|
'inactive': _('This account is inactive.'),
|
||||||
}
|
}
|
||||||
|
|
||||||
def clean_username(self):
|
def clean_username(self):
|
||||||
@@ -25,18 +24,18 @@ class LoginForm(auth_forms.AuthenticationForm):
|
|||||||
|
|
||||||
|
|
||||||
class SetPasswordForm(forms.Form):
|
class SetPasswordForm(forms.Form):
|
||||||
new_password = forms.CharField(label=_(u'Neues Passwort'),
|
new_password = forms.CharField(label=_('Neues Passwort'),
|
||||||
widget=forms.PasswordInput)
|
widget=forms.PasswordInput)
|
||||||
new_password_repeat = forms.CharField(label=_(u'Neues Passwort wiederholen'),
|
new_password_repeat = forms.CharField(label=_('Neues Passwort wiederholen'),
|
||||||
widget=forms.PasswordInput)
|
widget=forms.PasswordInput)
|
||||||
send_password_mail = forms.BooleanField(required=False,
|
send_password_mail = forms.BooleanField(required=False,
|
||||||
initial=False,
|
initial=False,
|
||||||
label=_(u'Neues Passwort per E-Mail zusenden'),
|
label=_('Neues Passwort per E-Mail zusenden'),
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, user, *args, **kwargs):
|
def __init__(self, user, *args, **kwargs):
|
||||||
self.user = user
|
self.user = user
|
||||||
super(SetPasswordForm, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def clean_new_password(self):
|
def clean_new_password(self):
|
||||||
password = self.cleaned_data.get('new_password')
|
password = self.cleaned_data.get('new_password')
|
||||||
@@ -49,7 +48,7 @@ class SetPasswordForm(forms.Form):
|
|||||||
if password1 and password2:
|
if password1 and password2:
|
||||||
if password1 != password2:
|
if password1 != password2:
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
ugettext(u'Passwörter stimmen nicht überein'),
|
ugettext('Passwörter stimmen nicht überein'),
|
||||||
code='password_mismatch',
|
code='password_mismatch',
|
||||||
)
|
)
|
||||||
return password2
|
return password2
|
||||||
@@ -64,8 +63,7 @@ class SetPasswordForm(forms.Form):
|
|||||||
|
|
||||||
class CreateAndSendPasswordForm(forms.Form):
|
class CreateAndSendPasswordForm(forms.Form):
|
||||||
username = auth_forms.UsernameField(
|
username = auth_forms.UsernameField(
|
||||||
max_length=254,
|
label=_('E-Mail-Adresse'),
|
||||||
label=_(u'E-Mail-Adresse'),
|
|
||||||
widget=forms.TextInput(attrs={'autofocus': True}),
|
widget=forms.TextInput(attrs={'autofocus': True}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from selenium.webdriver.common.by import By
|
|||||||
from selenium.webdriver.common.keys import Keys
|
from selenium.webdriver.common.keys import Keys
|
||||||
|
|
||||||
|
|
||||||
class SeleniumAuthMixin(object):
|
class SeleniumAuthMixin:
|
||||||
def login(self, driver, username, password):
|
def login(self, driver, username, password):
|
||||||
driver.get(self.complete_url(reverse('dav_auth:login')))
|
driver.get(self.complete_url(reverse('dav_auth:login')))
|
||||||
username_field = self.wait_on_presence(driver, (By.ID, 'id_username'))
|
username_field = self.wait_on_presence(driver, (By.ID, 'id_username'))
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ from ..emails import PasswordSetEmail
|
|||||||
|
|
||||||
|
|
||||||
TEST_USERNAME = 'user'
|
TEST_USERNAME = 'user'
|
||||||
TEST_PASSWORD = u'me||ön 21ABll'
|
TEST_PASSWORD = 'me||ön 21ABll'
|
||||||
TEST_EMAIL = 'root@localhost'
|
TEST_EMAIL = 'root@localhost'
|
||||||
|
|
||||||
PASSWORD_EMAIL_TEMPLATE = u"""Hallo {fullname},
|
PASSWORD_EMAIL_TEMPLATE = """Hallo {fullname},
|
||||||
|
|
||||||
Benutzername: {username}
|
Benutzername: {username}
|
||||||
Passwort: {password}
|
Passwort: {password}
|
||||||
@@ -23,7 +23,7 @@ URL: {base_url}/
|
|||||||
|
|
||||||
class EmailTestCase(EmailTestMixin, TestCase):
|
class EmailTestCase(EmailTestMixin, TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(EmailTestCase, self).setUp()
|
super().setUp()
|
||||||
model = get_user_model()
|
model = get_user_model()
|
||||||
self.user = model.objects.create_user(username=TEST_USERNAME, password=TEST_PASSWORD, email=TEST_EMAIL)
|
self.user = model.objects.create_user(username=TEST_USERNAME, password=TEST_PASSWORD, email=TEST_EMAIL)
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ class EmailTestCase(EmailTestMixin, TestCase):
|
|||||||
|
|
||||||
self.assertSender(mail)
|
self.assertSender(mail)
|
||||||
self.assertRecipients(mail, [self.user])
|
self.assertRecipients(mail, [self.user])
|
||||||
self.assertSubject(mail, u'Zugangsdaten')
|
self.assertSubject(mail, 'Zugangsdaten')
|
||||||
|
|
||||||
expected_body = PASSWORD_EMAIL_TEMPLATE.format(
|
expected_body = PASSWORD_EMAIL_TEMPLATE.format(
|
||||||
fullname=self.user.get_full_name(),
|
fullname=self.user.get_full_name(),
|
||||||
|
|||||||
@@ -9,9 +9,8 @@ from dav_base.tests.generic import FormDataSet, FormsTestCase
|
|||||||
from ..forms import LoginForm, SetPasswordForm, CreateAndSendPasswordForm
|
from ..forms import LoginForm, SetPasswordForm, CreateAndSendPasswordForm
|
||||||
|
|
||||||
TEST_USERNAME = 'root@localhost'
|
TEST_USERNAME = 'root@localhost'
|
||||||
TEST_PASSWORD = u'me||ön 21ABll'
|
TEST_PASSWORD = 'me||ön 21ABll'
|
||||||
TEST_EMAIL = TEST_USERNAME
|
TEST_EMAIL = TEST_USERNAME
|
||||||
USERNAME_MAX_LENGTH = 254
|
|
||||||
|
|
||||||
|
|
||||||
class LoginFormTestCase(FormsTestCase):
|
class LoginFormTestCase(FormsTestCase):
|
||||||
@@ -24,15 +23,10 @@ class LoginFormTestCase(FormsTestCase):
|
|||||||
model = get_user_model()
|
model = get_user_model()
|
||||||
self.user = model.objects.create_user(username=TEST_USERNAME, password=TEST_PASSWORD, email=TEST_EMAIL)
|
self.user = model.objects.create_user(username=TEST_USERNAME, password=TEST_PASSWORD, email=TEST_EMAIL)
|
||||||
|
|
||||||
def test_length(self):
|
|
||||||
form = self.form_class()
|
|
||||||
self.assertEqual(form.fields['username'].max_length, USERNAME_MAX_LENGTH)
|
|
||||||
self.assertEqual(form.fields['password'].max_length, None)
|
|
||||||
|
|
||||||
def test_labels(self):
|
def test_labels(self):
|
||||||
form = self.form_class()
|
form = self.form_class()
|
||||||
self.assertEqual(form.fields['username'].label, ugettext(u'E-Mail-Adresse'))
|
self.assertEqual(form.fields['username'].label, ugettext('E-Mail-Adresse'))
|
||||||
self.assertEqual(form.fields['password'].label, ugettext(u'Password'))
|
self.assertEqual(form.fields['password'].label, ugettext('Password'))
|
||||||
|
|
||||||
def test_required(self):
|
def test_required(self):
|
||||||
form = self.form_class()
|
form = self.form_class()
|
||||||
@@ -44,35 +38,28 @@ class LoginFormTestCase(FormsTestCase):
|
|||||||
FormDataSet({'username': '', 'password': self.test_password},
|
FormDataSet({'username': '', 'password': self.test_password},
|
||||||
expected_errors=[('username', 'required')]),
|
expected_errors=[('username', 'required')]),
|
||||||
]
|
]
|
||||||
super(LoginFormTestCase, self).test_invalid_data(data_sets=data_sets)
|
super().test_invalid_data(data_sets=data_sets)
|
||||||
|
|
||||||
def test_password_empty(self):
|
def test_password_empty(self):
|
||||||
data_sets = [
|
data_sets = [
|
||||||
FormDataSet({'username': self.test_username, 'password': ''},
|
FormDataSet({'username': self.test_username, 'password': ''},
|
||||||
expected_errors=[('password', 'required')]),
|
expected_errors=[('password', 'required')]),
|
||||||
]
|
]
|
||||||
super(LoginFormTestCase, self).test_invalid_data(data_sets=data_sets)
|
super().test_invalid_data(data_sets=data_sets)
|
||||||
|
|
||||||
def test_username_too_long(self):
|
|
||||||
data_sets = [
|
|
||||||
FormDataSet({'username': 'u' * USERNAME_MAX_LENGTH + 'u', 'password': self.test_password},
|
|
||||||
expected_errors=[('username', 'max_length')]),
|
|
||||||
]
|
|
||||||
super(LoginFormTestCase, self).test_invalid_data(data_sets=data_sets)
|
|
||||||
|
|
||||||
def test_invalid_username(self):
|
def test_invalid_username(self):
|
||||||
data_sets = [
|
data_sets = [
|
||||||
FormDataSet({'username': self.test_username[::-1], 'password': self.test_password},
|
FormDataSet({'username': self.test_username[::-1], 'password': self.test_password},
|
||||||
expected_errors=[('__all__', 'invalid_login')]),
|
expected_errors=[('__all__', 'invalid_login')]),
|
||||||
]
|
]
|
||||||
super(LoginFormTestCase, self).test_invalid_data(data_sets=data_sets)
|
super().test_invalid_data(data_sets=data_sets)
|
||||||
|
|
||||||
def test_invalid_password(self):
|
def test_invalid_password(self):
|
||||||
data_sets = [
|
data_sets = [
|
||||||
FormDataSet({'username': self.test_username, 'password': self.test_password[::-1]},
|
FormDataSet({'username': self.test_username, 'password': self.test_password[::-1]},
|
||||||
expected_errors=[('__all__', 'invalid_login')]),
|
expected_errors=[('__all__', 'invalid_login')]),
|
||||||
]
|
]
|
||||||
super(LoginFormTestCase, self).test_invalid_data(data_sets=data_sets)
|
super().test_invalid_data(data_sets=data_sets)
|
||||||
|
|
||||||
def test_inactive_user(self):
|
def test_inactive_user(self):
|
||||||
self.user.is_active = False
|
self.user.is_active = False
|
||||||
@@ -81,13 +68,13 @@ class LoginFormTestCase(FormsTestCase):
|
|||||||
FormDataSet({'username': self.test_username, 'password': self.test_password},
|
FormDataSet({'username': self.test_username, 'password': self.test_password},
|
||||||
expected_errors=[('__all__', 'invalid_login')]),
|
expected_errors=[('__all__', 'invalid_login')]),
|
||||||
]
|
]
|
||||||
super(LoginFormTestCase, self).test_invalid_data(data_sets=data_sets)
|
super().test_invalid_data(data_sets=data_sets)
|
||||||
|
|
||||||
def test_valid_credentials(self):
|
def test_valid_credentials(self):
|
||||||
data_sets = [
|
data_sets = [
|
||||||
FormDataSet({'username': self.test_username, 'password': self.test_password}),
|
FormDataSet({'username': self.test_username, 'password': self.test_password}),
|
||||||
]
|
]
|
||||||
super(LoginFormTestCase, self).test_valid_data(data_sets=data_sets)
|
super().test_valid_data(data_sets=data_sets)
|
||||||
|
|
||||||
|
|
||||||
class SetPasswordFormTestCase(FormsTestCase):
|
class SetPasswordFormTestCase(FormsTestCase):
|
||||||
@@ -111,56 +98,56 @@ class SetPasswordFormTestCase(FormsTestCase):
|
|||||||
FormDataSet({'new_password': 'mellonAB12+-', 'new_password_repeat': 'mellonAB13+-'},
|
FormDataSet({'new_password': 'mellonAB12+-', 'new_password_repeat': 'mellonAB13+-'},
|
||||||
[('new_password_repeat', 'password_mismatch')]),
|
[('new_password_repeat', 'password_mismatch')]),
|
||||||
]
|
]
|
||||||
super(SetPasswordFormTestCase, self).test_invalid_data(data_sets=data_sets, form_kwargs={'user': self.user})
|
super().test_invalid_data(data_sets=data_sets, form_kwargs={'user': self.user})
|
||||||
|
|
||||||
def test_empty(self):
|
def test_empty(self):
|
||||||
data_sets = [
|
data_sets = [
|
||||||
FormDataSet({'new_password': '', 'new_password_repeat': ''},
|
FormDataSet({'new_password': '', 'new_password_repeat': ''},
|
||||||
[('new_password', 'required')]),
|
[('new_password', 'required')]),
|
||||||
]
|
]
|
||||||
super(SetPasswordFormTestCase, self).test_invalid_data(data_sets=data_sets, form_kwargs={'user': self.user})
|
super().test_invalid_data(data_sets=data_sets, form_kwargs={'user': self.user})
|
||||||
|
|
||||||
def test_too_short(self):
|
def test_too_short(self):
|
||||||
data_sets = [
|
data_sets = [
|
||||||
FormDataSet({'new_password': 'mellon', 'new_password_repeat': 'mellon'},
|
FormDataSet({'new_password': 'mellon', 'new_password_repeat': 'mellon'},
|
||||||
[('new_password', 'password_too_short')]),
|
[('new_password', 'password_too_short')]),
|
||||||
]
|
]
|
||||||
super(SetPasswordFormTestCase, self).test_invalid_data(data_sets=data_sets, form_kwargs={'user': self.user})
|
super().test_invalid_data(data_sets=data_sets, form_kwargs={'user': self.user})
|
||||||
|
|
||||||
def test_entirely_numeric(self):
|
def test_entirely_numeric(self):
|
||||||
data_sets = [
|
data_sets = [
|
||||||
FormDataSet({'new_password': '1357924680', 'new_password_repeat': '1357924680'},
|
FormDataSet({'new_password': '1357924680', 'new_password_repeat': '1357924680'},
|
||||||
[('new_password', 'password_entirely_numeric')]),
|
[('new_password', 'password_entirely_numeric')]),
|
||||||
]
|
]
|
||||||
super(SetPasswordFormTestCase, self).test_invalid_data(data_sets=data_sets, form_kwargs={'user': self.user})
|
super().test_invalid_data(data_sets=data_sets, form_kwargs={'user': self.user})
|
||||||
|
|
||||||
def test_too_similar(self):
|
def test_too_similar(self):
|
||||||
data_sets = [
|
data_sets = [
|
||||||
FormDataSet({'new_password': self.test_username, 'new_password_repeat': self.test_username},
|
FormDataSet({'new_password': self.test_username, 'new_password_repeat': self.test_username},
|
||||||
[('new_password', 'password_too_similar')]),
|
[('new_password', 'password_too_similar')]),
|
||||||
]
|
]
|
||||||
super(SetPasswordFormTestCase, self).test_invalid_data(data_sets=data_sets, form_kwargs={'user': self.user})
|
super().test_invalid_data(data_sets=data_sets, form_kwargs={'user': self.user})
|
||||||
|
|
||||||
def test_too_common(self):
|
def test_too_common(self):
|
||||||
data_sets = [
|
data_sets = [
|
||||||
FormDataSet({'new_password': 'password', 'new_password_repeat': 'password'},
|
FormDataSet({'new_password': 'password', 'new_password_repeat': 'password'},
|
||||||
[('new_password', 'password_too_common')]),
|
[('new_password', 'password_too_common')]),
|
||||||
]
|
]
|
||||||
super(SetPasswordFormTestCase, self).test_invalid_data(data_sets=data_sets, form_kwargs={'user': self.user})
|
super().test_invalid_data(data_sets=data_sets, form_kwargs={'user': self.user})
|
||||||
|
|
||||||
def test_valid(self):
|
def test_valid(self):
|
||||||
data_sets = [
|
data_sets = [
|
||||||
FormDataSet({'new_password': 'mellonAB12+-', 'new_password_repeat': 'mellonAB12+-'}),
|
FormDataSet({'new_password': 'mellonAB12+-', 'new_password_repeat': 'mellonAB12+-'}),
|
||||||
FormDataSet({'new_password': 'mellonAB12+-', 'new_password_repeat': 'mellonAB12+-',
|
FormDataSet({'new_password': 'mellonAB12+-', 'new_password_repeat': 'mellonAB12+-',
|
||||||
'send_password_mail': True}),
|
'send_password_mail': True}),
|
||||||
FormDataSet({'new_password': u'"ä§ MellonAB12+-', 'new_password_repeat': u'"ä§ MellonAB12+-'}),
|
FormDataSet({'new_password': '"ä§ MellonAB12+-', 'new_password_repeat': '"ä§ MellonAB12+-'}),
|
||||||
FormDataSet({'new_password': 'mellon12' * 128, 'new_password_repeat': 'mellon12' * 128}),
|
FormDataSet({'new_password': 'mellon12' * 128, 'new_password_repeat': 'mellon12' * 128}),
|
||||||
]
|
]
|
||||||
super(SetPasswordFormTestCase, self).test_valid_data(data_sets=data_sets, form_kwargs={'user': self.user})
|
super().test_valid_data(data_sets=data_sets, form_kwargs={'user': self.user})
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
new_passwords = [
|
new_passwords = [
|
||||||
u'"ä§ Mellon12'
|
'"ä§ Mellon12'
|
||||||
'mellon12' * 128,
|
'mellon12' * 128,
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -175,7 +162,7 @@ class SetPasswordFormTestCase(FormsTestCase):
|
|||||||
@skip('Function is implemented in SetPasswordView instead of SetPasswordForm')
|
@skip('Function is implemented in SetPasswordView instead of SetPasswordForm')
|
||||||
def test_save_with_mail(self): # pragma: no cover
|
def test_save_with_mail(self): # pragma: no cover
|
||||||
new_passwords = [
|
new_passwords = [
|
||||||
u'"ä§ Mellon12'
|
'"ä§ Mellon12'
|
||||||
'mellon12' * 128,
|
'mellon12' * 128,
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -202,16 +189,11 @@ class CreateAndSendPasswordFormTestCase(FormsTestCase):
|
|||||||
)
|
)
|
||||||
invalid_data_sets = (
|
invalid_data_sets = (
|
||||||
FormDataSet({'username': ''}, expected_errors=[('username', 'required')]),
|
FormDataSet({'username': ''}, expected_errors=[('username', 'required')]),
|
||||||
FormDataSet({'username': 'u' * USERNAME_MAX_LENGTH + 'u'}, expected_errors=[('username', 'max_length')]),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_length(self):
|
|
||||||
form = self.form_class()
|
|
||||||
self.assertEqual(form.fields['username'].max_length, USERNAME_MAX_LENGTH)
|
|
||||||
|
|
||||||
def test_labels(self):
|
def test_labels(self):
|
||||||
form = self.form_class()
|
form = self.form_class()
|
||||||
self.assertEqual(form.fields['username'].label, ugettext(u'E-Mail-Adresse'))
|
self.assertEqual(form.fields['username'].label, ugettext('E-Mail-Adresse'))
|
||||||
|
|
||||||
def test_required(self):
|
def test_required(self):
|
||||||
form = self.form_class()
|
form = self.form_class()
|
||||||
|
|||||||
32
dav_auth/tests/test_models.py
Normal file
32
dav_auth/tests/test_models.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from unittest import skip
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.test import TestCase, Client
|
||||||
|
|
||||||
|
|
||||||
|
class ModelsTestCase(TestCase):
|
||||||
|
@skip('I do not know, why the user.save() does not raise an exception')
|
||||||
|
def test_username_length(self):
|
||||||
|
max_length = 150 # Hard coded in django.contrib.auth.models.AbstractUser
|
||||||
|
username_ok = 'u' * max_length
|
||||||
|
username_too_long = username_ok + 'u'
|
||||||
|
|
||||||
|
user_model = get_user_model()
|
||||||
|
user = user_model(username=username_too_long,
|
||||||
|
first_name='A',
|
||||||
|
last_name='User',
|
||||||
|
email='a.user@example.com')
|
||||||
|
user.save()
|
||||||
|
|
||||||
|
def test_last_login(self):
|
||||||
|
user_model = get_user_model()
|
||||||
|
user = user_model(username='auser',
|
||||||
|
first_name='A',
|
||||||
|
last_name='User',
|
||||||
|
email='a.user@example.com')
|
||||||
|
user.save()
|
||||||
|
self.assertEqual(user.last_login, None)
|
||||||
|
|
||||||
|
c = Client()
|
||||||
|
c.force_login(user)
|
||||||
|
self.assertNotEqual(user.last_login, None)
|
||||||
@@ -9,7 +9,7 @@ from selenium.webdriver.common.keys import Keys
|
|||||||
from dav_base.tests.generic import ScreenshotTestCase
|
from dav_base.tests.generic import ScreenshotTestCase
|
||||||
|
|
||||||
TEST_USERNAME = 'root@localhost'
|
TEST_USERNAME = 'root@localhost'
|
||||||
TEST_PASSWORD = u'me||ön 21ABll'
|
TEST_PASSWORD = 'me||ön 21ABll'
|
||||||
TEST_EMAIL = TEST_USERNAME
|
TEST_EMAIL = TEST_USERNAME
|
||||||
|
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ class TestCase(ScreenshotTestCase):
|
|||||||
screenshot_prefix = 'dav_auth-'
|
screenshot_prefix = 'dav_auth-'
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestCase, self).setUp()
|
super().setUp()
|
||||||
# Need a test user
|
# Need a test user
|
||||||
self.test_username = TEST_USERNAME
|
self.test_username = TEST_USERNAME
|
||||||
self.test_password = TEST_PASSWORD
|
self.test_password = TEST_PASSWORD
|
||||||
@@ -105,7 +105,7 @@ class TestCase(ScreenshotTestCase):
|
|||||||
|
|
||||||
# Click on 'set password' -> save set password page
|
# Click on 'set password' -> save set password page
|
||||||
user_menu = c.find_element_by_css_selector('#login-widget ul')
|
user_menu = c.find_element_by_css_selector('#login-widget ul')
|
||||||
link = user_menu.find_element_by_partial_link_text(ugettext(u'Passwort ändern'))
|
link = user_menu.find_element_by_partial_link_text(ugettext('Passwort ändern'))
|
||||||
link.click()
|
link.click()
|
||||||
password_field = self.wait_on_presence(c, (By.ID, 'id_new_password'))
|
password_field = self.wait_on_presence(c, (By.ID, 'id_new_password'))
|
||||||
self.save_screenshot('empty_set_password_form', sequence=sequence_name)
|
self.save_screenshot('empty_set_password_form', sequence=sequence_name)
|
||||||
@@ -189,7 +189,7 @@ class TestCase(ScreenshotTestCase):
|
|||||||
dropdown_button = self.wait_on_presence(c, (By.ID, 'user_dropdown_button'))
|
dropdown_button = self.wait_on_presence(c, (By.ID, 'user_dropdown_button'))
|
||||||
dropdown_button.click()
|
dropdown_button.click()
|
||||||
user_menu = c.find_element_by_css_selector('#login-widget ul')
|
user_menu = c.find_element_by_css_selector('#login-widget ul')
|
||||||
link = user_menu.find_element_by_partial_link_text(ugettext(u'Logout'))
|
link = user_menu.find_element_by_partial_link_text(ugettext('Logout'))
|
||||||
link.click()
|
link.click()
|
||||||
self.wait_until_stale(c, user_menu)
|
self.wait_until_stale(c, user_menu)
|
||||||
self.save_screenshot('logout_succeed', sequence=sequence_name)
|
self.save_screenshot('logout_succeed', sequence=sequence_name)
|
||||||
@@ -200,7 +200,7 @@ class TestCase(ScreenshotTestCase):
|
|||||||
self.wait_on_presence(c, (By.ID, 'id_username'))
|
self.wait_on_presence(c, (By.ID, 'id_username'))
|
||||||
|
|
||||||
# Locate password recreate link, click it -> save password recreate form
|
# Locate password recreate link, click it -> save password recreate form
|
||||||
link = c.find_element_by_partial_link_text(ugettext(u'Passwort vergessen'))
|
link = c.find_element_by_partial_link_text(ugettext('Passwort vergessen'))
|
||||||
link.click()
|
link.click()
|
||||||
username_field = self.wait_on_presence(c, (By.ID, 'id_username'))
|
username_field = self.wait_on_presence(c, (By.ID, 'id_username'))
|
||||||
self.save_screenshot('empty_recreate_password_form', sequence=sequence_name)
|
self.save_screenshot('empty_recreate_password_form', sequence=sequence_name)
|
||||||
@@ -212,7 +212,7 @@ class TestCase(ScreenshotTestCase):
|
|||||||
self.save_screenshot('recreate_password_invalid_user', sequence=sequence_name)
|
self.save_screenshot('recreate_password_invalid_user', sequence=sequence_name)
|
||||||
|
|
||||||
# Locate password recreate link, click it
|
# Locate password recreate link, click it
|
||||||
link = c.find_element_by_partial_link_text(ugettext(u'Passwort vergessen'))
|
link = c.find_element_by_partial_link_text(ugettext('Passwort vergessen'))
|
||||||
link.click()
|
link.click()
|
||||||
username_field = self.wait_on_presence(c, (By.ID, 'id_username'))
|
username_field = self.wait_on_presence(c, (By.ID, 'id_username'))
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class TemplatesTestCase(SimpleTestCase):
|
|||||||
@tag('browser')
|
@tag('browser')
|
||||||
class TestCase(SeleniumAuthMixin, SeleniumTestCase):
|
class TestCase(SeleniumAuthMixin, SeleniumTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestCase, self).setUp()
|
super().setUp()
|
||||||
# Need a test user
|
# Need a test user
|
||||||
self.test_username = TEST_USERNAME
|
self.test_username = TEST_USERNAME
|
||||||
self.test_password = TEST_PASSWORD
|
self.test_password = TEST_PASSWORD
|
||||||
@@ -40,7 +40,7 @@ class TestCase(SeleniumAuthMixin, SeleniumTestCase):
|
|||||||
c = self.selenium
|
c = self.selenium
|
||||||
c.get(self.complete_url('/'))
|
c.get(self.complete_url('/'))
|
||||||
try:
|
try:
|
||||||
link = c.find_element_by_css_selector('#login-widget a')
|
c.find_element_by_css_selector('#login-widget a')
|
||||||
except NoSuchElementException as e: # pragma: no cover
|
except NoSuchElementException as e: # pragma: no cover
|
||||||
self.fail(str(e))
|
self.fail(str(e))
|
||||||
|
|
||||||
@@ -65,4 +65,3 @@ class TestCase(SeleniumAuthMixin, SeleniumTestCase):
|
|||||||
self.assertEqual(field.get_attribute('required'), 'true')
|
self.assertEqual(field.get_attribute('required'), 'true')
|
||||||
field = c.find_element_by_id('id_send_password_mail')
|
field = c.find_element_by_id('id_send_password_mail')
|
||||||
self.assertEqual(field.get_attribute('required'), None)
|
self.assertEqual(field.get_attribute('required'), None)
|
||||||
|
|
||||||
|
|||||||
@@ -8,19 +8,19 @@ from ..validators import PasswordScoreValidator, CustomWordlistPasswordValidator
|
|||||||
class PasswordScoreValidatorTestCase(SimpleTestCase):
|
class PasswordScoreValidatorTestCase(SimpleTestCase):
|
||||||
def test_too_little_score(self):
|
def test_too_little_score(self):
|
||||||
passwords = [
|
passwords = [
|
||||||
u'', # score = 0
|
'', # score = 0
|
||||||
u'abcdefghijklmnopq', # score = 17
|
'abcdefghijklmnopq', # score = 17
|
||||||
u'Abcdefghijklmnop', # score = 16 + 1
|
'Abcdefghijklmnop', # score = 16 + 1
|
||||||
u'ABcdefghijklmno', # score = 15 + 2
|
'ABcdefghijklmno', # score = 15 + 2
|
||||||
u'AB1defghijklmn', # score = 14 + 2 + 1
|
'AB1defghijklmn', # score = 14 + 2 + 1
|
||||||
u'AB12efghijklm', # score = 13 + 2 + 2
|
'AB12efghijklm', # score = 13 + 2 + 2
|
||||||
u'AB12+fghijkl', # score = 12 + 2 + 2 + 1
|
'AB12+fghijkl', # score = 12 + 2 + 2 + 1
|
||||||
u'AB12+-ghijk', # score = 11 + 2 + 2 + 2
|
'AB12+-ghijk', # score = 11 + 2 + 2 + 2
|
||||||
u'AB12+-*hij', # score = 10 + 2 + 2 + 3
|
'AB12+-*hij', # score = 10 + 2 + 2 + 3
|
||||||
u'AB12+-*hi/', # score = 10 + 2 + 2 + 3
|
'AB12+-*hi/', # score = 10 + 2 + 2 + 3
|
||||||
u'AB12+-*hi3', # score = 10 + 2 + 2 + 3
|
'AB12+-*hi3', # score = 10 + 2 + 2 + 3
|
||||||
u'AB12+-*hiC', # score = 10 + 2 + 2 + 3
|
'AB12+-*hiC', # score = 10 + 2 + 2 + 3
|
||||||
u'abcbbbgbbjklmnopqr', # score = 17
|
'abcbbbgbbjklmnopqr', # score = 17
|
||||||
]
|
]
|
||||||
|
|
||||||
validator = PasswordScoreValidator(min_classes=0)
|
validator = PasswordScoreValidator(min_classes=0)
|
||||||
@@ -31,19 +31,19 @@ class PasswordScoreValidatorTestCase(SimpleTestCase):
|
|||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
self.assertEqual('too_little_score', e.code)
|
self.assertEqual('too_little_score', e.code)
|
||||||
else:
|
else:
|
||||||
self.fail(u'%s: no validation error was raised' % password)
|
self.fail('%s: no validation error was raised' % password)
|
||||||
|
|
||||||
def test_too_few_classes(self):
|
def test_too_few_classes(self):
|
||||||
passwords = [
|
passwords = [
|
||||||
u'', # classes = 0
|
'', # classes = 0
|
||||||
u'abcdefgh', # classes = 1
|
'abcdefgh', # classes = 1
|
||||||
u'abcdef+-', # classes = 2
|
'abcdef+-', # classes = 2
|
||||||
u'abcd12gh', # classes = 2
|
'abcd12gh', # classes = 2
|
||||||
u'abcd12-+', # classes = 3
|
'abcd12-+', # classes = 3
|
||||||
u'abCDefgh', # classes = 2
|
'abCDefgh', # classes = 2
|
||||||
u'abCDef+-', # classes = 3
|
'abCDef+-', # classes = 3
|
||||||
u'abCD12gh', # classes = 3
|
'abCD12gh', # classes = 3
|
||||||
u'ABCD12-+', # classes = 3
|
'ABCD12-+', # classes = 3
|
||||||
]
|
]
|
||||||
|
|
||||||
validator = PasswordScoreValidator(min_score=0, min_classes=4)
|
validator = PasswordScoreValidator(min_score=0, min_classes=4)
|
||||||
@@ -54,20 +54,20 @@ class PasswordScoreValidatorTestCase(SimpleTestCase):
|
|||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
self.assertEqual('too_few_classes', e.code)
|
self.assertEqual('too_few_classes', e.code)
|
||||||
else:
|
else:
|
||||||
self.fail(u'%s: no validation error was raised' % password)
|
self.fail('%s: no validation error was raised' % password)
|
||||||
|
|
||||||
def test_valid(self):
|
def test_valid(self):
|
||||||
passwords = [
|
passwords = [
|
||||||
u'Abcdefghijklmnopq',
|
'Abcdefghijklmnopq',
|
||||||
u'ABcdefghijklmnop',
|
'ABcdefghijklmnop',
|
||||||
u'AB1defghijklmno',
|
'AB1defghijklmno',
|
||||||
u'AB12efghijklmn',
|
'AB12efghijklmn',
|
||||||
u'AB12+fghijklm',
|
'AB12+fghijklm',
|
||||||
u'AB12+-ghijkl',
|
'AB12+-ghijkl',
|
||||||
u'AB12+-*hijk',
|
'AB12+-*hijk',
|
||||||
u'ab1defghijklmnopq',
|
'ab1defghijklmnopq',
|
||||||
u'abcd+fghijklmnopq',
|
'abcd+fghijklmnopq',
|
||||||
u'AB1CDEFGHIJKLMNOPQ',
|
'AB1CDEFGHIJKLMNOPQ',
|
||||||
]
|
]
|
||||||
|
|
||||||
validator = PasswordScoreValidator()
|
validator = PasswordScoreValidator()
|
||||||
@@ -82,23 +82,23 @@ class PasswordScoreValidatorTestCase(SimpleTestCase):
|
|||||||
class CustomWordlistPasswordValidatorTestCase(SimpleTestCase):
|
class CustomWordlistPasswordValidatorTestCase(SimpleTestCase):
|
||||||
def test_invalid(self):
|
def test_invalid(self):
|
||||||
invalid_passwords = [
|
invalid_passwords = [
|
||||||
(u'passwort', [
|
('passwort', [
|
||||||
u'Das Passwort darf nicht die Zeichenfolge \'passwort\' enthalten.',
|
'Das Passwort darf nicht die Zeichenfolge \'passwort\' enthalten.',
|
||||||
]),
|
]),
|
||||||
(u'abcdDaVefgh', [
|
('abcdDaVefgh', [
|
||||||
u'Das Passwort darf nicht die Zeichenfolge \'dav\' enthalten.',
|
'Das Passwort darf nicht die Zeichenfolge \'dav\' enthalten.',
|
||||||
]),
|
]),
|
||||||
(u'abcdsektIonefgh', [
|
('abcdsektIonefgh', [
|
||||||
u'Das Passwort darf nicht die Zeichenfolge \'sektion\' enthalten.',
|
'Das Passwort darf nicht die Zeichenfolge \'sektion\' enthalten.',
|
||||||
]),
|
]),
|
||||||
(u'alpen12verein34KArlsruhE berge', [
|
('alpen12verein34KArlsruhE berge', [
|
||||||
u'Das Passwort darf nicht die Zeichenfolge \'karlsruhe\' enthalten.',
|
'Das Passwort darf nicht die Zeichenfolge \'karlsruhe\' enthalten.',
|
||||||
u'Das Passwort darf nicht die Zeichenfolge \'berge\' enthalten.',
|
'Das Passwort darf nicht die Zeichenfolge \'berge\' enthalten.',
|
||||||
]),
|
]),
|
||||||
(u'heinzel@alpenverein-karlsruhe.de', [
|
('heinzel@alpenverein-karlsruhe.de', [
|
||||||
u'Das Passwort darf nicht die Zeichenfolge \'heinzel\' enthalten.',
|
'Das Passwort darf nicht die Zeichenfolge \'heinzel\' enthalten.',
|
||||||
u'Das Passwort darf nicht die Zeichenfolge \'alpenverein\' enthalten.',
|
'Das Passwort darf nicht die Zeichenfolge \'alpenverein\' enthalten.',
|
||||||
u'Das Passwort darf nicht die Zeichenfolge \'karlsruhe\' enthalten.',
|
'Das Passwort darf nicht die Zeichenfolge \'karlsruhe\' enthalten.',
|
||||||
]),
|
]),
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -114,13 +114,13 @@ class CustomWordlistPasswordValidatorTestCase(SimpleTestCase):
|
|||||||
for error in errors:
|
for error in errors:
|
||||||
self.assertIn(error, expected_errors)
|
self.assertIn(error, expected_errors)
|
||||||
else:
|
else:
|
||||||
self.fail(u'%s: no validation error was raised' % password)
|
self.fail('%s: no validation error was raised' % password)
|
||||||
|
|
||||||
def test_valid(self):
|
def test_valid(self):
|
||||||
passwords = [
|
passwords = [
|
||||||
u'',
|
'',
|
||||||
u'password',
|
'password',
|
||||||
u'münchen',
|
'münchen',
|
||||||
]
|
]
|
||||||
|
|
||||||
validator = CustomWordlistPasswordValidator()
|
validator = CustomWordlistPasswordValidator()
|
||||||
@@ -134,72 +134,72 @@ class CustomWordlistPasswordValidatorTestCase(SimpleTestCase):
|
|||||||
|
|
||||||
class CharacterClassPasswordValidatorTestCase(SimpleTestCase):
|
class CharacterClassPasswordValidatorTestCase(SimpleTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(CharacterClassPasswordValidatorTestCase, self).setUp()
|
super().setUp()
|
||||||
self.validator = CharacterClassPasswordValidator()
|
self.validator = CharacterClassPasswordValidator()
|
||||||
|
|
||||||
def test_invalid(self):
|
def test_invalid(self):
|
||||||
invalid_passwords = [
|
invalid_passwords = [
|
||||||
(u'', [
|
('', [
|
||||||
u'Das Passwort muss mindestens 2 Kleinbuchstaben enthalten.',
|
'Das Passwort muss mindestens 2 Kleinbuchstaben enthalten.',
|
||||||
u'Das Passwort muss mindestens 2 Großbuchstaben enthalten.',
|
'Das Passwort muss mindestens 2 Großbuchstaben enthalten.',
|
||||||
u'Das Passwort muss mindestens 2 Ziffern enthalten.',
|
'Das Passwort muss mindestens 2 Ziffern enthalten.',
|
||||||
u'Das Passwort muss mindestens 2 Sonderzeichen enthalten.',
|
'Das Passwort muss mindestens 2 Sonderzeichen enthalten.',
|
||||||
]),
|
]),
|
||||||
(u'A+-', [
|
('A+-', [
|
||||||
u'Das Passwort muss mindestens 2 Kleinbuchstaben enthalten.',
|
'Das Passwort muss mindestens 2 Kleinbuchstaben enthalten.',
|
||||||
u'Das Passwort muss mindestens 2 Großbuchstaben enthalten.',
|
'Das Passwort muss mindestens 2 Großbuchstaben enthalten.',
|
||||||
u'Das Passwort muss mindestens 2 Ziffern enthalten.',
|
'Das Passwort muss mindestens 2 Ziffern enthalten.',
|
||||||
]),
|
]),
|
||||||
(u'1234567890*', [
|
('1234567890*', [
|
||||||
u'Das Passwort muss mindestens 2 Kleinbuchstaben enthalten.',
|
'Das Passwort muss mindestens 2 Kleinbuchstaben enthalten.',
|
||||||
u'Das Passwort muss mindestens 2 Großbuchstaben enthalten.',
|
'Das Passwort muss mindestens 2 Großbuchstaben enthalten.',
|
||||||
u'Das Passwort muss mindestens 2 Sonderzeichen enthalten.',
|
'Das Passwort muss mindestens 2 Sonderzeichen enthalten.',
|
||||||
]),
|
]),
|
||||||
(u'34*/()', [
|
('34*/()', [
|
||||||
u'Das Passwort muss mindestens 2 Kleinbuchstaben enthalten.',
|
'Das Passwort muss mindestens 2 Kleinbuchstaben enthalten.',
|
||||||
u'Das Passwort muss mindestens 2 Großbuchstaben enthalten.',
|
'Das Passwort muss mindestens 2 Großbuchstaben enthalten.',
|
||||||
]),
|
]),
|
||||||
(u'AA', [
|
('AA', [
|
||||||
u'Das Passwort muss mindestens 2 Kleinbuchstaben enthalten.',
|
'Das Passwort muss mindestens 2 Kleinbuchstaben enthalten.',
|
||||||
u'Das Passwort muss mindestens 2 Ziffern enthalten.',
|
'Das Passwort muss mindestens 2 Ziffern enthalten.',
|
||||||
u'Das Passwort muss mindestens 2 Sonderzeichen enthalten.',
|
'Das Passwort muss mindestens 2 Sonderzeichen enthalten.',
|
||||||
]),
|
]),
|
||||||
(u'CD0.,', [
|
('CD0.,', [
|
||||||
u'Das Passwort muss mindestens 2 Kleinbuchstaben enthalten.',
|
'Das Passwort muss mindestens 2 Kleinbuchstaben enthalten.',
|
||||||
u'Das Passwort muss mindestens 2 Ziffern enthalten.',
|
'Das Passwort muss mindestens 2 Ziffern enthalten.',
|
||||||
]),
|
]),
|
||||||
(u'EF56', [
|
('EF56', [
|
||||||
u'Das Passwort muss mindestens 2 Kleinbuchstaben enthalten.',
|
'Das Passwort muss mindestens 2 Kleinbuchstaben enthalten.',
|
||||||
u'Das Passwort muss mindestens 2 Sonderzeichen enthalten.',
|
'Das Passwort muss mindestens 2 Sonderzeichen enthalten.',
|
||||||
]),
|
]),
|
||||||
(u'8GH?!8', [
|
('8GH?!8', [
|
||||||
u'Das Passwort muss mindestens 2 Kleinbuchstaben enthalten.',
|
'Das Passwort muss mindestens 2 Kleinbuchstaben enthalten.',
|
||||||
]),
|
]),
|
||||||
(u'bbX', [
|
('bbX', [
|
||||||
u'Das Passwort muss mindestens 2 Großbuchstaben enthalten.',
|
'Das Passwort muss mindestens 2 Großbuchstaben enthalten.',
|
||||||
u'Das Passwort muss mindestens 2 Ziffern enthalten.',
|
'Das Passwort muss mindestens 2 Ziffern enthalten.',
|
||||||
u'Das Passwort muss mindestens 2 Sonderzeichen enthalten.',
|
'Das Passwort muss mindestens 2 Sonderzeichen enthalten.',
|
||||||
]),
|
]),
|
||||||
(u'$cd%', [
|
('$cd%', [
|
||||||
u'Das Passwort muss mindestens 2 Großbuchstaben enthalten.',
|
'Das Passwort muss mindestens 2 Großbuchstaben enthalten.',
|
||||||
u'Das Passwort muss mindestens 2 Ziffern enthalten.',
|
'Das Passwort muss mindestens 2 Ziffern enthalten.',
|
||||||
]),
|
]),
|
||||||
(u'ef90', [
|
('ef90', [
|
||||||
u'Das Passwort muss mindestens 2 Großbuchstaben enthalten.',
|
'Das Passwort muss mindestens 2 Großbuchstaben enthalten.',
|
||||||
u'Das Passwort muss mindestens 2 Sonderzeichen enthalten.',
|
'Das Passwort muss mindestens 2 Sonderzeichen enthalten.',
|
||||||
]),
|
]),
|
||||||
(u'1g=h3~', [
|
('1g=h3~', [
|
||||||
u'Das Passwort muss mindestens 2 Großbuchstaben enthalten.',
|
'Das Passwort muss mindestens 2 Großbuchstaben enthalten.',
|
||||||
]),
|
]),
|
||||||
(u'Gi&jH', [
|
('Gi&jH', [
|
||||||
u'Das Passwort muss mindestens 2 Ziffern enthalten.',
|
'Das Passwort muss mindestens 2 Ziffern enthalten.',
|
||||||
u'Das Passwort muss mindestens 2 Sonderzeichen enthalten.',
|
'Das Passwort muss mindestens 2 Sonderzeichen enthalten.',
|
||||||
]),
|
]),
|
||||||
(u'IkK:i;', [
|
('IkK:i;', [
|
||||||
u'Das Passwort muss mindestens 2 Ziffern enthalten.',
|
'Das Passwort muss mindestens 2 Ziffern enthalten.',
|
||||||
]),
|
]),
|
||||||
(u'mKn4L8', [
|
('mKn4L8', [
|
||||||
u'Das Passwort muss mindestens 2 Sonderzeichen enthalten.',
|
'Das Passwort muss mindestens 2 Sonderzeichen enthalten.',
|
||||||
]),
|
]),
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -215,10 +215,10 @@ class CharacterClassPasswordValidatorTestCase(SimpleTestCase):
|
|||||||
for error in errors:
|
for error in errors:
|
||||||
self.assertIn(error, expected_errors)
|
self.assertIn(error, expected_errors)
|
||||||
else:
|
else:
|
||||||
self.fail(u'%s: no validation error was raised' % password)
|
self.fail('%s: no validation error was raised' % password)
|
||||||
|
|
||||||
def test_valid(self):
|
def test_valid(self):
|
||||||
valid_passwords = [u'abCD12+-']
|
valid_passwords = ['abCD12+-']
|
||||||
validator = self.validator
|
validator = self.validator
|
||||||
for password in valid_passwords:
|
for password in valid_passwords:
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from django.urls import reverse
|
|||||||
from ..forms import LoginForm, SetPasswordForm, CreateAndSendPasswordForm
|
from ..forms import LoginForm, SetPasswordForm, CreateAndSendPasswordForm
|
||||||
|
|
||||||
TEST_USERNAME = 'root@localhost'
|
TEST_USERNAME = 'root@localhost'
|
||||||
TEST_PASSWORD = u'me||ön 21ABll'
|
TEST_PASSWORD = 'me||ön 21ABll'
|
||||||
TEST_EMAIL = TEST_USERNAME
|
TEST_EMAIL = TEST_USERNAME
|
||||||
|
|
||||||
|
|
||||||
@@ -30,12 +30,12 @@ class ViewsTestCase(TestCase):
|
|||||||
cls.recreate_password_url = reverse('dav_auth:recreate_password')
|
cls.recreate_password_url = reverse('dav_auth:recreate_password')
|
||||||
|
|
||||||
# Some messages
|
# Some messages
|
||||||
cls.wrong_credentials_message = ugettext(u'Benutzername oder Passwort falsch.')
|
cls.wrong_credentials_message = ugettext('Benutzername oder Passwort falsch.')
|
||||||
cls.logout_message = ugettext(u'Benutzer abgemeldet.')
|
cls.logout_message = ugettext('Benutzer abgemeldet.')
|
||||||
cls.set_password_message = ugettext(u'Passwort gespeichert.')
|
cls.set_password_message = ugettext('Passwort gespeichert.')
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestCase, self).setUp()
|
super().setUp()
|
||||||
# Need a test user
|
# Need a test user
|
||||||
self.test_username = TEST_USERNAME
|
self.test_username = TEST_USERNAME
|
||||||
self.test_password = TEST_PASSWORD
|
self.test_password = TEST_PASSWORD
|
||||||
@@ -74,7 +74,7 @@ class ViewsTestCase(TestCase):
|
|||||||
|
|
||||||
def test_integrated_login_succeed(self):
|
def test_integrated_login_succeed(self):
|
||||||
username = self.user.username
|
username = self.user.username
|
||||||
message = ugettext(u'Benutzer angemeldet: %(username)s') % {'username': username}
|
message = ugettext('Benutzer angemeldet: %(username)s') % {'username': username}
|
||||||
|
|
||||||
response = self.client.post(self.login_url, {'username': username, 'password': self.test_password})
|
response = self.client.post(self.login_url, {'username': username, 'password': self.test_password})
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
@@ -166,7 +166,7 @@ class ViewsTestCase(TestCase):
|
|||||||
'send_password_mail': True})
|
'send_password_mail': True})
|
||||||
self.assertEqual(len(django_mail.outbox), 1)
|
self.assertEqual(len(django_mail.outbox), 1)
|
||||||
mail = django_mail.outbox[0]
|
mail = django_mail.outbox[0]
|
||||||
recipient = u'"%s" <%s>' % (self.user.get_full_name(), self.user.email)
|
recipient = '"%s" <%s>' % (self.user.get_full_name(), self.user.email)
|
||||||
recipients = mail.recipients()
|
recipients = mail.recipients()
|
||||||
self.assertIn(recipient, recipients)
|
self.assertIn(recipient, recipients)
|
||||||
self.assertEqual(len(recipients), 1)
|
self.assertEqual(len(recipients), 1)
|
||||||
@@ -202,7 +202,7 @@ class ViewsTestCase(TestCase):
|
|||||||
|
|
||||||
self.assertEqual(len(django_mail.outbox), 1)
|
self.assertEqual(len(django_mail.outbox), 1)
|
||||||
mail = django_mail.outbox[0]
|
mail = django_mail.outbox[0]
|
||||||
recipient = u'"%s" <%s>' % (self.user.get_full_name(), self.user.email)
|
recipient = '"%s" <%s>' % (self.user.get_full_name(), self.user.email)
|
||||||
recipients = mail.recipients()
|
recipients = mail.recipients()
|
||||||
self.assertIn(recipient, recipients)
|
self.assertIn(recipient, recipients)
|
||||||
self.assertEqual(len(recipients), 1)
|
self.assertEqual(len(recipients), 1)
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ from django.conf.urls import url
|
|||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
|
app_name = 'dav_auth'
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^login$', views.LoginView.as_view(), name='login'),
|
url(r'^login$', views.LoginView.as_view(), name='login'),
|
||||||
url(r'^logout$', views.LogoutView.as_view(), name='logout'),
|
url(r'^logout$', views.LogoutView.as_view(), name='logout'),
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from django.core.exceptions import ValidationError
|
|||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
|
|
||||||
class PasswordScoreValidator(object):
|
class PasswordScoreValidator:
|
||||||
def _get_score(self, password, user=None):
|
def _get_score(self, password, user=None):
|
||||||
score = 0
|
score = 0
|
||||||
char_counters = {}
|
char_counters = {}
|
||||||
@@ -56,67 +56,67 @@ class PasswordScoreValidator(object):
|
|||||||
score, used_classes = self._get_score(password, user=user)
|
score, used_classes = self._get_score(password, user=user)
|
||||||
|
|
||||||
if used_classes < self.min_classes:
|
if used_classes < self.min_classes:
|
||||||
raise ValidationError(_(u'Das Passwort muss Zeichen aus mindestens %(min_classes)d'
|
raise ValidationError(_('Das Passwort muss Zeichen aus mindestens %(min_classes)d'
|
||||||
u' verschiedenen Arten von Zeichen bestehen'
|
' verschiedenen Arten von Zeichen bestehen'
|
||||||
u' (d.h. Kleinbuchstaben, Großbuchstaben, Ziffern und Sonderzeichen).'),
|
' (d.h. Kleinbuchstaben, Großbuchstaben, Ziffern und Sonderzeichen).'),
|
||||||
code='too_few_classes',
|
code='too_few_classes',
|
||||||
params={'min_classes': self.min_classes})
|
params={'min_classes': self.min_classes})
|
||||||
|
|
||||||
if score < self.min_score:
|
if score < self.min_score:
|
||||||
raise ValidationError(_(u'Dieses Passwort ist zu einfach. Benutze mehr Zeichen'
|
raise ValidationError(_('Dieses Passwort ist zu einfach. Benutze mehr Zeichen'
|
||||||
u' und gegebenenfalls auch Großbuchstaben, Ziffern und Sonderzeichen.'),
|
' und gegebenenfalls auch Großbuchstaben, Ziffern und Sonderzeichen.'),
|
||||||
code='too_little_score')
|
code='too_little_score')
|
||||||
|
|
||||||
return score
|
return score
|
||||||
|
|
||||||
def get_help_text(self):
|
def get_help_text(self):
|
||||||
text = u'%s\n%s' % (
|
text = '%s\n%s' % (
|
||||||
_(u'The password must get a minimum score of %d points.') % self.min_score,
|
_('The password must get a minimum score of %d points.') % self.min_score,
|
||||||
_(u'For each character the score is increased by 1 point.'),
|
_('For each character the score is increased by 1 point.'),
|
||||||
)
|
)
|
||||||
if self.lcredit > 0:
|
if self.lcredit > 0:
|
||||||
text += '\n'
|
text += '\n'
|
||||||
text += _(u'The first %d lower characters increase the score by 1 point each.') % self.lcredit
|
text += _('The first %d lower characters increase the score by 1 point each.') % self.lcredit
|
||||||
if self.ucredit > 0:
|
if self.ucredit > 0:
|
||||||
text += '\n'
|
text += '\n'
|
||||||
text += _(u'The first %d upper characters increase the score by 1 point each.') % self.ucredit
|
text += _('The first %d upper characters increase the score by 1 point each.') % self.ucredit
|
||||||
if self.dcredit > 0:
|
if self.dcredit > 0:
|
||||||
text += '\n'
|
text += '\n'
|
||||||
text += _(u'The first %d digits increase the score by 1 point each.') % self.dcredit
|
text += _('The first %d digits increase the score by 1 point each.') % self.dcredit
|
||||||
if self.ocredit > 0:
|
if self.ocredit > 0:
|
||||||
text += '\n'
|
text += '\n'
|
||||||
text += _(u'The first %d non alpha numeric characters'
|
text += _('The first %d non alpha numeric characters'
|
||||||
u' increase the score by 1 point each.') % self.ocredit
|
' increase the score by 1 point each.') % self.ocredit
|
||||||
if self.max_repeat > 0:
|
if self.max_repeat > 0:
|
||||||
text += '\n'
|
text += '\n'
|
||||||
text += _(u'If a particular character is used more than %d times,'
|
text += _('If a particular character is used more than %d times,'
|
||||||
u' it will not increase the score anymore.') % self.max_repeat
|
' it will not increase the score anymore.') % self.max_repeat
|
||||||
if self.min_classes > 0:
|
if self.min_classes > 0:
|
||||||
text += '\n'
|
text += '\n'
|
||||||
text += _(u'Also the password must contain characters from %d different character classes'
|
text += _('Also the password must contain characters from %d different character classes'
|
||||||
u' (i.e. lower, upper, digits, others).') % self.min_classes
|
' (i.e. lower, upper, digits, others).') % self.min_classes
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
class CustomWordlistPasswordValidator(object):
|
class CustomWordlistPasswordValidator:
|
||||||
context = 'the Sektion Karlsruhe'
|
context = 'the Sektion Karlsruhe'
|
||||||
words = (
|
words = (
|
||||||
u'dav',
|
'dav',
|
||||||
u'berge',
|
'berge',
|
||||||
u'sektion',
|
'sektion',
|
||||||
u'karlsruhe',
|
'karlsruhe',
|
||||||
u'alpenverein',
|
'alpenverein',
|
||||||
u'heinzel',
|
'heinzel',
|
||||||
u'passwort',
|
'passwort',
|
||||||
)
|
)
|
||||||
|
|
||||||
def validate(self, password, user=None):
|
def validate(self, password, user=None): # pylint: disable=unused-argument
|
||||||
errors = []
|
errors = []
|
||||||
|
|
||||||
lower_pw = password.lower()
|
lower_pw = password.lower()
|
||||||
for word in self.words:
|
for word in self.words:
|
||||||
if word in lower_pw:
|
if word in lower_pw:
|
||||||
error = ValidationError(_(u'Das Passwort darf nicht die Zeichenfolge \'%(word)s\' enthalten.'),
|
error = ValidationError(_('Das Passwort darf nicht die Zeichenfolge \'%(word)s\' enthalten.'),
|
||||||
code='forbidden_word',
|
code='forbidden_word',
|
||||||
params={'word': word})
|
params={'word': word})
|
||||||
errors.append(error)
|
errors.append(error)
|
||||||
@@ -125,14 +125,14 @@ class CustomWordlistPasswordValidator(object):
|
|||||||
raise ValidationError(errors)
|
raise ValidationError(errors)
|
||||||
|
|
||||||
def get_help_text(self):
|
def get_help_text(self):
|
||||||
text = _(u'The password must not contain some specific words,'
|
text = _('The password must not contain some specific words,'
|
||||||
u' that are common in context with %(context)s.') % {'context': self.context}
|
' that are common in context with %(context)s.') % {'context': self.context}
|
||||||
text += u'\n'
|
text += '\n'
|
||||||
text += _(u'All words are matched case insensitive.')
|
text += _('All words are matched case insensitive.')
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
class CharacterClassPasswordValidator(object):
|
class CharacterClassPasswordValidator:
|
||||||
def _is_enough_lower(self, password):
|
def _is_enough_lower(self, password):
|
||||||
lower = re.sub(r'[^a-z]', '', password)
|
lower = re.sub(r'[^a-z]', '', password)
|
||||||
if len(lower) < self.minimum_lower:
|
if len(lower) < self.minimum_lower:
|
||||||
@@ -163,26 +163,26 @@ class CharacterClassPasswordValidator(object):
|
|||||||
self.minimum_digits = minimum_digits
|
self.minimum_digits = minimum_digits
|
||||||
self.minimum_others = minimum_others
|
self.minimum_others = minimum_others
|
||||||
|
|
||||||
def validate(self, password, user=None):
|
def validate(self, password, user=None): # pylint: disable=unused-argument
|
||||||
errors = []
|
errors = []
|
||||||
if not self._is_enough_lower(password):
|
if not self._is_enough_lower(password):
|
||||||
error = ValidationError(_(u'Das Passwort muss mindestens %(min_lower)d Kleinbuchstaben enthalten.'),
|
error = ValidationError(_('Das Passwort muss mindestens %(min_lower)d Kleinbuchstaben enthalten.'),
|
||||||
code='too_few_lower_characters',
|
code='too_few_lower_characters',
|
||||||
params={'min_lower': self.minimum_lower})
|
params={'min_lower': self.minimum_lower})
|
||||||
errors.append(error)
|
errors.append(error)
|
||||||
if not self._is_enough_upper(password):
|
if not self._is_enough_upper(password):
|
||||||
error = ValidationError(_(u'Das Passwort muss mindestens %(min_upper)d Großbuchstaben enthalten.'),
|
error = ValidationError(_('Das Passwort muss mindestens %(min_upper)d Großbuchstaben enthalten.'),
|
||||||
code='too_few_upper_characters',
|
code='too_few_upper_characters',
|
||||||
params={'min_upper': self.minimum_upper})
|
params={'min_upper': self.minimum_upper})
|
||||||
errors.append(error)
|
errors.append(error)
|
||||||
if not self._is_enough_digits(password):
|
if not self._is_enough_digits(password):
|
||||||
error = ValidationError(_(u'Das Passwort muss mindestens %(min_digits)d Ziffern enthalten.'),
|
error = ValidationError(_('Das Passwort muss mindestens %(min_digits)d Ziffern enthalten.'),
|
||||||
code='too_few_digits',
|
code='too_few_digits',
|
||||||
params={'min_digits': self.minimum_digits})
|
params={'min_digits': self.minimum_digits})
|
||||||
errors.append(error)
|
errors.append(error)
|
||||||
if not self._is_enough_others(password):
|
if not self._is_enough_others(password):
|
||||||
error = ValidationError(_(u'Das Passwort muss mindestens %(min_others)d'
|
error = ValidationError(_('Das Passwort muss mindestens %(min_others)d'
|
||||||
u' Sonderzeichen enthalten.'),
|
' Sonderzeichen enthalten.'),
|
||||||
code='too_few_other_characters',
|
code='too_few_other_characters',
|
||||||
params={'min_others': self.minimum_others})
|
params={'min_others': self.minimum_others})
|
||||||
errors.append(error)
|
errors.append(error)
|
||||||
@@ -191,7 +191,7 @@ class CharacterClassPasswordValidator(object):
|
|||||||
raise ValidationError(errors)
|
raise ValidationError(errors)
|
||||||
|
|
||||||
def get_help_text(self):
|
def get_help_text(self):
|
||||||
text = u'%s %s %s %s' % (
|
text = '%s %s %s %s' % (
|
||||||
_('The password must contain at least %d characters from a-z.') % self.minimum_lower,
|
_('The password must contain at least %d characters from a-z.') % self.minimum_lower,
|
||||||
_('The password must contain at least %d characters from A-Z.') % self.minimum_upper,
|
_('The password must contain at least %d characters from A-Z.') % self.minimum_upper,
|
||||||
_('The password must contain at least %d digits from 0-9.') % self.minimum_digits,
|
_('The password must contain at least %d digits from 0-9.') % self.minimum_digits,
|
||||||
|
|||||||
@@ -24,39 +24,39 @@ class LoginView(auth_views.LoginView):
|
|||||||
template_name = 'dav_auth/forms/login.html'
|
template_name = 'dav_auth/forms/login.html'
|
||||||
|
|
||||||
def get_redirect_url(self):
|
def get_redirect_url(self):
|
||||||
url = super(LoginView, self).get_redirect_url()
|
url = super().get_redirect_url()
|
||||||
if not url and app_config.settings.login_redirect_url:
|
if not url and app_config.settings.login_redirect_url:
|
||||||
url = resolve_url(app_config.settings.login_redirect_url)
|
url = resolve_url(app_config.settings.login_redirect_url)
|
||||||
return url
|
return url
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
r = super(LoginView, self).form_valid(form)
|
r = super().form_valid(form)
|
||||||
messages.success(self.request, _(u'Benutzer angemeldet: %(username)s') % {'username': form.get_user()})
|
messages.success(self.request, _('Benutzer angemeldet: %(username)s') % {'username': form.get_user()})
|
||||||
try:
|
try:
|
||||||
validate_password(form.cleaned_data['password'])
|
validate_password(form.cleaned_data['password'])
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
logger.warning(u'Weak password (%d): %s', self.request.user.pk, e)
|
logger.warning('Weak password (%d): %s', self.request.user.pk, e)
|
||||||
message = u'<br />\n<p>\n'
|
message = '<br />\n<p>\n'
|
||||||
message += u'Dein Passwort entspricht nicht mehr den aktuellen Passwortrichtlinien.<br />\n'
|
message += 'Dein Passwort entspricht nicht mehr den aktuellen Passwortrichtlinien.<br />\n'
|
||||||
message += u'Bitte hilf uns die Daten deiner Teilnehmer zu schützen und ändere dein Passwort.<br />\n'
|
message += 'Bitte hilf uns die Daten deiner Teilnehmer zu schützen und ändere dein Passwort.<br />\n'
|
||||||
message += u'</p>\n'
|
message += '</p>\n'
|
||||||
message += u'<p>\n'
|
message += '<p>\n'
|
||||||
message += u'<a href="%(href)s">Passwort ändern</a>\n' % {'href': reverse('dav_auth:set_password')}
|
message += '<a href="%(href)s">Passwort ändern</a>\n' % {'href': reverse('dav_auth:set_password')}
|
||||||
message += u'</p>\n<br />\n'
|
message += '</p>\n<br />\n'
|
||||||
messages.warning(self.request, mark_safe(message))
|
messages.warning(self.request, mark_safe(message))
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
class LogoutView(auth_views.LogoutView):
|
class LogoutView(auth_views.LogoutView):
|
||||||
def get_next_page(self):
|
def get_next_page(self):
|
||||||
url = super(LogoutView, self).get_next_page()
|
url = super().get_next_page()
|
||||||
if not url and app_config.settings.logout_redirect_url:
|
if not url and app_config.settings.logout_redirect_url:
|
||||||
url = resolve_url(app_config.settings.logout_redirect_url)
|
url = resolve_url(app_config.settings.logout_redirect_url)
|
||||||
return url
|
return url
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
r = super(LogoutView, self).dispatch(request, *args, **kwargs)
|
r = super().dispatch(request, *args, **kwargs)
|
||||||
messages.success(self.request, _(u'Benutzer abgemeldet.'))
|
messages.success(self.request, _('Benutzer abgemeldet.'))
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
@@ -68,8 +68,8 @@ class SetPasswordView(auth_views.PasswordChangeView):
|
|||||||
return resolve_url(app_config.settings.login_redirect_url)
|
return resolve_url(app_config.settings.login_redirect_url)
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
r = super(SetPasswordView, self).form_valid(form)
|
r = super().form_valid(form)
|
||||||
messages.success(self.request, _(u'Passwort gespeichert.'))
|
messages.success(self.request, _('Passwort gespeichert.'))
|
||||||
if form.cleaned_data.get('send_password_mail', False):
|
if form.cleaned_data.get('send_password_mail', False):
|
||||||
email = emails.PasswordSetEmail(self.request.user, form.cleaned_data['new_password'])
|
email = emails.PasswordSetEmail(self.request.user, form.cleaned_data['new_password'])
|
||||||
email.send()
|
email.send()
|
||||||
@@ -91,14 +91,14 @@ class CreateAndSendPasswordView(generic.FormView):
|
|||||||
user.save()
|
user.save()
|
||||||
email = emails.PasswordSetEmail(user, random_password)
|
email = emails.PasswordSetEmail(user, random_password)
|
||||||
email.send()
|
email.send()
|
||||||
messages.success(self.request, _(u'Neues Passwort versendet.'))
|
messages.success(self.request, _('Neues Passwort versendet.'))
|
||||||
logger.info('Password recreated for user \'%s\'', username)
|
logger.info('Password recreated for user \'%s\'', username)
|
||||||
except user_model.DoesNotExist:
|
except user_model.DoesNotExist:
|
||||||
logger.warning('Password recreated for unknown user \'%s\'', username)
|
logger.warning('Password recreated for unknown user \'%s\'', username)
|
||||||
|
|
||||||
return super(CreateAndSendPasswordView, self).form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
if request.user.is_authenticated:
|
if request.user.is_authenticated:
|
||||||
return HttpResponseRedirect(reverse('dav_auth:set_password'))
|
return HttpResponseRedirect(reverse('dav_auth:set_password'))
|
||||||
return super(CreateAndSendPasswordView, self).get(request, *args, **kwargs)
|
return super().get(request, *args, **kwargs)
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
default_app_config = 'dav_base.apps.AppConfig'
|
default_app_config = 'dav_base.apps.AppConfig' # pylint: disable=invalid-name
|
||||||
|
|||||||
@@ -9,5 +9,5 @@ DEFAULT_SETTINGS = (
|
|||||||
|
|
||||||
class AppConfig(_AppConfig):
|
class AppConfig(_AppConfig):
|
||||||
name = 'dav_base'
|
name = 'dav_base'
|
||||||
verbose_name = u'DAV Base App'
|
verbose_name = 'DAV Base App'
|
||||||
default_settings = DEFAULT_SETTINGS
|
default_settings = DEFAULT_SETTINGS
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from django.core.exceptions import ImproperlyConfigured
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class DefaultSetting(object):
|
class DefaultSetting: # pylint: disable=too-few-public-methods
|
||||||
def __init__(self, name, value, key_name=None, validator=None):
|
def __init__(self, name, value, key_name=None, validator=None):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.value = value
|
self.value = value
|
||||||
@@ -27,7 +27,7 @@ class DefaultSetting(object):
|
|||||||
raise ImproperlyConfigured('Does not match /{re}/'.format(re=self.validator))
|
raise ImproperlyConfigured('Does not match /{re}/'.format(re=self.validator))
|
||||||
|
|
||||||
|
|
||||||
class AppSettings(object):
|
class AppSettings: # pylint: disable=too-few-public-methods
|
||||||
def __init__(self, app_name, defaults):
|
def __init__(self, app_name, defaults):
|
||||||
settings_name = 'main.settings-' + app_name
|
settings_name = 'main.settings-' + app_name
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ class AppSettings(object):
|
|||||||
msg = 'Invalid value of {key} in {module}: {cause}'.format(key=default.key_name,
|
msg = 'Invalid value of {key} in {module}: {cause}'.format(key=default.key_name,
|
||||||
module=settings_name,
|
module=settings_name,
|
||||||
cause=e)
|
cause=e)
|
||||||
raise ImproperlyConfigured(msg)
|
raise ImproperlyConfigured(msg) from e
|
||||||
setattr(self, default.name, value)
|
setattr(self, default.name, value)
|
||||||
elif isinstance(default.value, ImproperlyConfigured):
|
elif isinstance(default.value, ImproperlyConfigured):
|
||||||
raise default.value
|
raise default.value
|
||||||
@@ -59,7 +59,7 @@ class AppSettings(object):
|
|||||||
msg = '{key} must be defined in {module}'.format(key=default.key_name,
|
msg = '{key} must be defined in {module}'.format(key=default.key_name,
|
||||||
module=settings_name)
|
module=settings_name)
|
||||||
raise default.value(msg)
|
raise default.value(msg)
|
||||||
else:
|
|
||||||
setattr(self, default.name, default.value)
|
setattr(self, default.name, default.value)
|
||||||
|
|
||||||
|
|
||||||
@@ -67,5 +67,5 @@ class AppConfig(_AppConfig):
|
|||||||
default_settings = ()
|
default_settings = ()
|
||||||
|
|
||||||
def __init__(self, app_name, app_module):
|
def __init__(self, app_name, app_module):
|
||||||
super(AppConfig, self).__init__(app_name, app_module)
|
super().__init__(app_name, app_module)
|
||||||
self.settings = AppSettings(app_name, self.default_settings)
|
self.settings = AppSettings(app_name, self.default_settings)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class ModuleConfigError(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ModuleMeta(object):
|
class ModuleMeta:
|
||||||
_json_file = 'module.json'
|
_json_file = 'module.json'
|
||||||
_root_url_name = 'root'
|
_root_url_name = 'root'
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ class ModuleMeta(object):
|
|||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
class ModuleConfig(object):
|
class ModuleConfig:
|
||||||
_lazy_load = True
|
_lazy_load = True
|
||||||
|
|
||||||
def __init__(self, config_file_path=None, django_base_dir=None):
|
def __init__(self, config_file_path=None, django_base_dir=None):
|
||||||
@@ -83,7 +83,7 @@ class ModuleConfig(object):
|
|||||||
config_file_path = os.path.join(django_base_dir, DJANGO_MAIN_MODULE, MODULE_CONFIG_FILE_NAME)
|
config_file_path = os.path.join(django_base_dir, DJANGO_MAIN_MODULE, MODULE_CONFIG_FILE_NAME)
|
||||||
self._config_file_path = config_file_path
|
self._config_file_path = config_file_path
|
||||||
|
|
||||||
self._modules = dict()
|
self._modules = {}
|
||||||
|
|
||||||
self._loaded = False
|
self._loaded = False
|
||||||
if not self._lazy_load:
|
if not self._lazy_load:
|
||||||
@@ -96,13 +96,13 @@ class ModuleConfig(object):
|
|||||||
def _load(self):
|
def _load(self):
|
||||||
path = self._config_file_path
|
path = self._config_file_path
|
||||||
|
|
||||||
self._modules = dict()
|
self._modules = {}
|
||||||
|
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
with open(path, 'r') as f:
|
with open(path, 'r', encoding='ascii') as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
else:
|
else:
|
||||||
data = dict()
|
data = {}
|
||||||
|
|
||||||
if 'modules' in data:
|
if 'modules' in data:
|
||||||
for meta_dict in data['modules']:
|
for meta_dict in data['modules']:
|
||||||
@@ -131,5 +131,5 @@ class ModuleConfig(object):
|
|||||||
for meta_obj in self._modules.values():
|
for meta_obj in self._modules.values():
|
||||||
data['modules'].append(meta_obj.dump_as_dict())
|
data['modules'].append(meta_obj.dump_as_dict())
|
||||||
|
|
||||||
with open(path, 'w') as f:
|
with open(path, 'w', encoding='ascii') as f:
|
||||||
json.dump(data, f, indent=4)
|
json.dump(data, f, indent=4)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
import pkg_resources
|
|
||||||
import posix
|
import posix
|
||||||
import sys
|
import sys
|
||||||
|
import pkg_resources
|
||||||
from django.core.management import execute_from_command_line
|
from django.core.management import execute_from_command_line
|
||||||
|
|
||||||
from dav_base.config.modules import DJANGO_MAIN_MODULE, ModuleConfig
|
from dav_base.config.modules import DJANGO_MAIN_MODULE, ModuleConfig
|
||||||
@@ -10,7 +10,7 @@ from dav_base.config.modules import DJANGO_MAIN_MODULE, ModuleConfig
|
|||||||
VERSION = '0.1'
|
VERSION = '0.1'
|
||||||
|
|
||||||
|
|
||||||
class AdminCommand(object):
|
class AdminCommand: # pylint: disable=too-few-public-methods
|
||||||
def _setup_argparser(self):
|
def _setup_argparser(self):
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'description': 'Tool to manage the DAV django project installation.',
|
'description': 'Tool to manage the DAV django project installation.',
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
# Additional settings for django-dav
|
# Additional settings for django-dav
|
||||||
#
|
#
|
||||||
|
|
||||||
BASE_VAR_DIR = os.path.join(BASE_DIR, 'var')
|
BASE_VAR_DIR = BASE_DIR / 'var'
|
||||||
BASE_SHARE_DIR = os.path.join(BASE_DIR, 'common')
|
BASE_SHARE_DIR = BASE_DIR / 'common'
|
||||||
|
|
||||||
# Get modules config
|
# Get modules config
|
||||||
from dav_base.config.modules import ModuleConfig
|
from dav_base.config.modules import ModuleConfig
|
||||||
@@ -14,7 +14,7 @@ INSTALLED_APPS += [
|
|||||||
'bootstrap3',
|
'bootstrap3',
|
||||||
'datetimewidget',
|
'datetimewidget',
|
||||||
'django_countries',
|
'django_countries',
|
||||||
'django_extensions',
|
# 'django_extensions',
|
||||||
# Our main app
|
# Our main app
|
||||||
'dav_base',
|
'dav_base',
|
||||||
]
|
]
|
||||||
@@ -45,11 +45,11 @@ TEMPLATES += [
|
|||||||
|
|
||||||
# Add our local templates directory to the template engine configurations.
|
# Add our local templates directory to the template engine configurations.
|
||||||
for config in TEMPLATES:
|
for config in TEMPLATES:
|
||||||
config['DIRS'].append(os.path.join(BASE_SHARE_DIR, 'templates'))
|
config['DIRS'].append(BASE_SHARE_DIR / 'templates')
|
||||||
|
|
||||||
DATABASES['default'] = {
|
DATABASES['default'] = {
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
'NAME': os.path.join(BASE_VAR_DIR, 'db', 'devel.sqlite3'),
|
'NAME': BASE_VAR_DIR / 'db' / 'devel.sqlite3',
|
||||||
}
|
}
|
||||||
|
|
||||||
AUTH_PASSWORD_VALIDATORS = [
|
AUTH_PASSWORD_VALIDATORS = [
|
||||||
@@ -76,7 +76,7 @@ AUTH_PASSWORD_VALIDATORS = [
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
STATIC_ROOT = os.path.join(BASE_VAR_DIR, 'www', 'static')
|
STATIC_ROOT = BASE_VAR_DIR / 'www' / 'static'
|
||||||
|
|
||||||
LANGUAGE_CODE = 'de'
|
LANGUAGE_CODE = 'de'
|
||||||
TIME_ZONE = 'Europe/Berlin'
|
TIME_ZONE = 'Europe/Berlin'
|
||||||
@@ -130,20 +130,20 @@ LOGGING = {
|
|||||||
'default_log': {
|
'default_log': {
|
||||||
'level': 'INFO',
|
'level': 'INFO',
|
||||||
'class': 'logging.FileHandler',
|
'class': 'logging.FileHandler',
|
||||||
'filename': os.path.join(BASE_VAR_DIR, 'log', 'default.log'),
|
'filename': BASE_VAR_DIR / 'log' / 'default.log',
|
||||||
'formatter': 'default',
|
'formatter': 'default',
|
||||||
},
|
},
|
||||||
'error_log': {
|
'error_log': {
|
||||||
'level': 'ERROR',
|
'level': 'ERROR',
|
||||||
'class': 'logging.FileHandler',
|
'class': 'logging.FileHandler',
|
||||||
'filename': os.path.join(BASE_VAR_DIR, 'log', 'error.log'),
|
'filename': BASE_VAR_DIR / 'log' / 'error.log',
|
||||||
'formatter': 'default',
|
'formatter': 'default',
|
||||||
},
|
},
|
||||||
'debug_log': {
|
'debug_log': {
|
||||||
'level': 'DEBUG',
|
'level': 'DEBUG',
|
||||||
'filters': ['require_debug_true'],
|
'filters': ['require_debug_true'],
|
||||||
'class': 'logging.FileHandler',
|
'class': 'logging.FileHandler',
|
||||||
'filename': os.path.join(BASE_VAR_DIR, 'log', 'debug.log'),
|
'filename': BASE_VAR_DIR / 'log' / 'debug.log',
|
||||||
'formatter': 'verbose',
|
'formatter': 'verbose',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ app_config = apps.get_containing_app_config(__package__)
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class AbstractMail(object):
|
class AbstractMail: # pylint: disable=too-few-public-methods
|
||||||
_subject = u''
|
_subject = ''
|
||||||
_template_name = None
|
_template_name = None
|
||||||
|
|
||||||
def _get_sender(self):
|
def _get_sender(self):
|
||||||
@@ -20,7 +20,7 @@ class AbstractMail(object):
|
|||||||
if subject_fmt is None:
|
if subject_fmt is None:
|
||||||
subject_fmt = self._subject
|
subject_fmt = self._subject
|
||||||
if app_config.settings.email_subject_prefix:
|
if app_config.settings.email_subject_prefix:
|
||||||
subject_fmt = u'%s %s' % (app_config.settings.email_subject_prefix, subject_fmt)
|
subject_fmt = '%s %s' % (app_config.settings.email_subject_prefix, subject_fmt)
|
||||||
subject = subject_fmt.format(**kwargs)
|
subject = subject_fmt.format(**kwargs)
|
||||||
return subject
|
return subject
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ class AbstractMail(object):
|
|||||||
|
|
||||||
email = EmailMessage(subject=subject, body=body, from_email=sender, to=recipients, reply_to=reply_to)
|
email = EmailMessage(subject=subject, body=body, from_email=sender, to=recipients, reply_to=reply_to)
|
||||||
if fail_silently:
|
if fail_silently:
|
||||||
logger.info(u'Fake sending %s to %s', self.__class__.__name__, recipients)
|
logger.info('Fake sending %s to %s', self.__class__.__name__, recipients)
|
||||||
else:
|
else:
|
||||||
logger.info(u'Send %s to %s', self.__class__.__name__, recipients)
|
logger.info('Send %s to %s', self.__class__.__name__, recipients)
|
||||||
email.send(fail_silently=fail_silently)
|
email.send(fail_silently=fail_silently)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{# This template is used by software tests #}{% load dav_base %}
|
{# This template is used by software tests #}{% load dav_base %}
|
||||||
--{% include_if_exist './includes/include_missing.html' %}--
|
--{% include_if_exist './includes/include_missing3.html' %}--
|
||||||
--{% include_if_exist './includes/include_missing.html' default 'dav_base/tests/includes/include_default.html' %}--
|
--{% include_if_exist './includes/include_missing4.html' default 'dav_base/tests/includes/include_default.html' %}--
|
||||||
--{% include_if_exist './includes/include_optional.html' %}--
|
--{% include_if_exist './includes/include_optional.html' %}--
|
||||||
--{% include_if_exist './includes/include_optional.html' default 'dav_base/tests/includes/include_default.html' %}--
|
--{% include_if_exist './includes/include_optional.html' default 'dav_base/tests/includes/include_default.html' %}--
|
||||||
--{% include_if_exist './includes/include_with_missing_include.html' %}--
|
--{% include_if_exist './includes/include_with_missing_include.html' %}--
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
{# This template is used by software tests #}{% load dav_base %}
|
{# This template is used by software tests #}{% load dav_base %}
|
||||||
--{% include_if_exist './includes/include_missing.html' default './includes/include_missing.html' %}--
|
--{% include_if_exist './includes/include_missing1.html' default './includes/include_missing2.html' %}--
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
{# This template is used by software tests #}{% load dav_base %}
|
{# This template is used by software tests #}{% load dav_base %}
|
||||||
--{% include './include_missing.html' %}--
|
--{% include './include_missing5.html' %}--
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ def do_include_if_exist(parser, token):
|
|||||||
"""
|
"""
|
||||||
bits = token.split_contents()
|
bits = token.split_contents()
|
||||||
if len(bits) < 2:
|
if len(bits) < 2:
|
||||||
raise template.TemplateSyntaxError("%r tag takes at least two arguments:"
|
raise template.TemplateSyntaxError("%r tag takes at least one argument:"
|
||||||
" the name of the template to be included" % bits[0])
|
" the name of the template to be included" % bits[0])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -27,6 +27,10 @@ def do_include_if_exist(parser, token):
|
|||||||
token = template.base.Token(token.token_type, ' '.join(bits))
|
token = template.base.Token(token.token_type, ' '.join(bits))
|
||||||
token2 = template.base.Token(token.token_type, bits[0] + ' ' + default_template + ' '.join(bits[2:]))
|
token2 = template.base.Token(token.token_type, bits[0] + ' ' + default_template + ' '.join(bits[2:]))
|
||||||
default_node = template.loader_tags.do_include(parser, token2)
|
default_node = template.loader_tags.do_include(parser, token2)
|
||||||
|
# TODO: I belive, this ist not the correct way to do things.
|
||||||
|
# But without setting default_node.origin here the following AttributeError will be risen within the tests:
|
||||||
|
# AttributeError: 'IncludeNode' object has no attribute 'origin'
|
||||||
|
default_node.origin = template.Origin(name='<unknown_source>', template_name=None)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
default_node = template.defaulttags.CommentNode()
|
default_node = template.defaulttags.CommentNode()
|
||||||
except IndexError:
|
except IndexError:
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import os
|
import os
|
||||||
|
from urllib.parse import quote
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.contrib.auth.models import AbstractUser
|
from django.contrib.auth.models import AbstractUser
|
||||||
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
|
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
|
||||||
@@ -9,11 +10,10 @@ from django.test import SimpleTestCase, TestCase, tag
|
|||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from selenium import webdriver
|
from selenium import webdriver
|
||||||
from selenium.webdriver.support.ui import WebDriverWait
|
from selenium.webdriver.support.ui import WebDriverWait
|
||||||
from selenium.webdriver.support import expected_conditions as EC
|
from selenium.webdriver.support import expected_conditions as ExpectedConditions
|
||||||
from six.moves.urllib.parse import quote
|
|
||||||
|
|
||||||
|
|
||||||
class AppSetting(object):
|
class AppSetting: # pylint: disable=too-few-public-methods
|
||||||
def __init__(self, name, of=None):
|
def __init__(self, name, of=None):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.of = of
|
self.of = of
|
||||||
@@ -24,7 +24,7 @@ class AppsTestCase(SimpleTestCase):
|
|||||||
settings = ()
|
settings = ()
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(AppsTestCase, self).setUp()
|
super().setUp()
|
||||||
if self.app_config:
|
if self.app_config:
|
||||||
self.configured_settings = self.app_config.settings
|
self.configured_settings = self.app_config.settings
|
||||||
else:
|
else:
|
||||||
@@ -41,7 +41,7 @@ class AppsTestCase(SimpleTestCase):
|
|||||||
self.assertIsInstance(value, of)
|
self.assertIsInstance(value, of)
|
||||||
|
|
||||||
|
|
||||||
class EmailTestMixin(object):
|
class EmailTestMixin:
|
||||||
email_sender = 'Automatic Software Test <root@localhost>'
|
email_sender = 'Automatic Software Test <root@localhost>'
|
||||||
email_base_url = 'http://localhost'
|
email_base_url = 'http://localhost'
|
||||||
email_subject_prefix = '[Test]'
|
email_subject_prefix = '[Test]'
|
||||||
@@ -55,29 +55,29 @@ class EmailTestMixin(object):
|
|||||||
|
|
||||||
return mails
|
return mails
|
||||||
|
|
||||||
def assertSender(self, mail):
|
def assertSender(self, mail): # pylint: disable=invalid-name
|
||||||
self.assertEqual(mail.from_email, self.email_sender)
|
self.assertEqual(mail.from_email, self.email_sender)
|
||||||
|
|
||||||
def assertReplyTo(self, mail, addresses):
|
def assertReplyTo(self, mail, addresses): # pylint: disable=invalid-name
|
||||||
self.assertEqual(len(mail.reply_to), len(addresses))
|
self.assertEqual(len(mail.reply_to), len(addresses))
|
||||||
for expected_address in addresses:
|
for expected_address in addresses:
|
||||||
if isinstance(expected_address, AbstractUser):
|
if isinstance(expected_address, AbstractUser):
|
||||||
expected_address = u'"%s" <%s>' % (expected_address.get_full_name(), expected_address.email)
|
expected_address = '"%s" <%s>' % (expected_address.get_full_name(), expected_address.email)
|
||||||
self.assertIn(expected_address, mail.reply_to)
|
self.assertIn(expected_address, mail.reply_to)
|
||||||
|
|
||||||
def assertRecipients(self, mail, recipients):
|
def assertRecipients(self, mail, recipients): # pylint: disable=invalid-name
|
||||||
self.assertEqual(len(mail.recipients()), len(recipients))
|
self.assertEqual(len(mail.recipients()), len(recipients))
|
||||||
for expected_recipient in recipients:
|
for expected_recipient in recipients:
|
||||||
if isinstance(expected_recipient, AbstractUser):
|
if isinstance(expected_recipient, AbstractUser):
|
||||||
expected_recipient = u'"%s" <%s>' % (expected_recipient.get_full_name(), expected_recipient.email)
|
expected_recipient = '"%s" <%s>' % (expected_recipient.get_full_name(), expected_recipient.email)
|
||||||
recipients = mail.recipients()
|
recipients = mail.recipients()
|
||||||
self.assertIn(expected_recipient, recipients)
|
self.assertIn(expected_recipient, recipients)
|
||||||
|
|
||||||
def assertSubject(self, mail, subject):
|
def assertSubject(self, mail, subject): # pylint: disable=invalid-name
|
||||||
expected_subject = u'{} {}'.format(self.email_subject_prefix, subject)
|
expected_subject = '{} {}'.format(self.email_subject_prefix, subject)
|
||||||
self.assertEqual(mail.subject, expected_subject)
|
self.assertEqual(mail.subject, expected_subject)
|
||||||
|
|
||||||
def assertBody(self, mail, body):
|
def assertBody(self, mail, body): # pylint: disable=invalid-name
|
||||||
expected_lines = body.splitlines()
|
expected_lines = body.splitlines()
|
||||||
lines = mail.body.splitlines()
|
lines = mail.body.splitlines()
|
||||||
i = 0
|
i = 0
|
||||||
@@ -93,14 +93,14 @@ class EmailTestMixin(object):
|
|||||||
self.fail('line %d: %s' % (i, e))
|
self.fail('line %d: %s' % (i, e))
|
||||||
self.assertEqual(mail.body, body)
|
self.assertEqual(mail.body, body)
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self): # pylint: disable=invalid-name
|
||||||
app_config = apps.get_app_config('dav_base')
|
app_config = apps.get_app_config('dav_base')
|
||||||
app_config.settings.email_sender = self.email_sender
|
app_config.settings.email_sender = self.email_sender
|
||||||
app_config.settings.email_base_url = self.email_base_url
|
app_config.settings.email_base_url = self.email_base_url
|
||||||
app_config.settings.email_subject_prefix = self.email_subject_prefix
|
app_config.settings.email_subject_prefix = self.email_subject_prefix
|
||||||
|
|
||||||
|
|
||||||
class FormDataSet(object):
|
class FormDataSet: # pylint: disable=too-few-public-methods
|
||||||
def __init__(self, data, expected_errors=None, form_kwargs=None):
|
def __init__(self, data, expected_errors=None, form_kwargs=None):
|
||||||
self.data = data
|
self.data = data
|
||||||
self.expected_errors = expected_errors
|
self.expected_errors = expected_errors
|
||||||
@@ -116,44 +116,46 @@ class FormsTestCase(TestCase):
|
|||||||
if form_class is None:
|
if form_class is None:
|
||||||
form_class = self.form_class
|
form_class = self.form_class
|
||||||
if form_class is None:
|
if form_class is None:
|
||||||
return True
|
return
|
||||||
|
|
||||||
if data_sets is None:
|
if data_sets is None:
|
||||||
data_sets = self.valid_data_sets
|
data_sets = self.valid_data_sets
|
||||||
|
|
||||||
|
given_form_kwargs = form_kwargs
|
||||||
for data_set in data_sets:
|
for data_set in data_sets:
|
||||||
fk = {}
|
form_kwargs = {}
|
||||||
if form_kwargs is not None:
|
if given_form_kwargs is not None:
|
||||||
fk.update(form_kwargs)
|
form_kwargs.update(given_form_kwargs)
|
||||||
if data_set.form_kwargs is not None:
|
if data_set.form_kwargs is not None:
|
||||||
fk.update(data_set.form_kwargs)
|
form_kwargs.update(data_set.form_kwargs)
|
||||||
fk['data'] = data_set.data
|
form_kwargs['data'] = data_set.data
|
||||||
form = form_class(**fk)
|
form = form_class(**form_kwargs)
|
||||||
if not form.is_valid():
|
if not form.is_valid():
|
||||||
errors = []
|
errors = []
|
||||||
for key in form.errors.as_data():
|
for key in form.errors.as_data():
|
||||||
for ve in form.errors[key].as_data():
|
for e in form.errors[key].as_data():
|
||||||
errors.append(u'%s (%s)' % (ve.code, ve.message))
|
errors.append('%s (%s)' % (e.code, e.message))
|
||||||
self.fail(u'Invalid form data \'%s\': %s' % (data_set.data, errors))
|
self.fail('Invalid form data \'%s\': %s' % (data_set.data, errors))
|
||||||
|
|
||||||
def test_invalid_data(self, form_class=None, data_sets=None, form_kwargs=None):
|
def test_invalid_data(self, form_class=None, data_sets=None, form_kwargs=None):
|
||||||
if form_class is None:
|
if form_class is None:
|
||||||
form_class = self.form_class
|
form_class = self.form_class
|
||||||
if form_class is None:
|
if form_class is None:
|
||||||
return True
|
return
|
||||||
|
|
||||||
if data_sets is None:
|
if data_sets is None:
|
||||||
data_sets = self.invalid_data_sets
|
data_sets = self.invalid_data_sets
|
||||||
|
|
||||||
|
given_form_kwargs = form_kwargs
|
||||||
for data_set in data_sets:
|
for data_set in data_sets:
|
||||||
fk = {}
|
form_kwargs = {}
|
||||||
if form_kwargs is not None:
|
if given_form_kwargs is not None:
|
||||||
fk.update(form_kwargs)
|
form_kwargs.update(given_form_kwargs)
|
||||||
if data_set.form_kwargs is not None:
|
if data_set.form_kwargs is not None:
|
||||||
fk.update(data_set.form_kwargs)
|
form_kwargs.update(data_set.form_kwargs)
|
||||||
fk['data'] = data_set.data
|
form_kwargs['data'] = data_set.data
|
||||||
|
|
||||||
form = form_class(**fk)
|
form = form_class(**form_kwargs)
|
||||||
|
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
self.fail('Valid form data: \'%s\'' % data_set.data)
|
self.fail('Valid form data: \'%s\'' % data_set.data)
|
||||||
@@ -164,7 +166,7 @@ class FormsTestCase(TestCase):
|
|||||||
self.assertIn(code, error_codes)
|
self.assertIn(code, error_codes)
|
||||||
|
|
||||||
|
|
||||||
class Url(object):
|
class Url: # pylint: disable=too-few-public-methods
|
||||||
def __init__(self, location, name=None, func=None, **kwargs):
|
def __init__(self, location, name=None, func=None, **kwargs):
|
||||||
self.location = location
|
self.location = location
|
||||||
self.name = name
|
self.name = name
|
||||||
@@ -210,15 +212,15 @@ class UrlsTestCase(TestCase):
|
|||||||
'Getting url named \'{}\' resolve to wrong function'.format(url.name))
|
'Getting url named \'{}\' resolve to wrong function'.format(url.name))
|
||||||
|
|
||||||
|
|
||||||
class ValidatorTestMixin(object):
|
class ValidatorTestMixin:
|
||||||
def assertValid(self, validator, data):
|
def assertValid(self, validator, data): # pylint: disable=invalid-name
|
||||||
for val in data:
|
for val in data:
|
||||||
try:
|
try:
|
||||||
validator(val)
|
validator(val)
|
||||||
except ValidationError as e: # pragma: no cover
|
except ValidationError as e: # pragma: no cover
|
||||||
self.fail('%s: %s' % (val, e))
|
self.fail('%s: %s' % (val, e))
|
||||||
|
|
||||||
def assertInvalid(self, validator, data):
|
def assertInvalid(self, validator, data): # pylint: disable=invalid-name
|
||||||
for val in data:
|
for val in data:
|
||||||
try:
|
try:
|
||||||
validator(val)
|
validator(val)
|
||||||
@@ -234,7 +236,7 @@ class SeleniumTestCase(StaticLiveServerTestCase):
|
|||||||
window_height = 768
|
window_height = 768
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(SeleniumTestCase, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self._driver = None
|
self._driver = None
|
||||||
self._driver_options = webdriver.FirefoxOptions()
|
self._driver_options = webdriver.FirefoxOptions()
|
||||||
self.quit_selenium = None
|
self.quit_selenium = None
|
||||||
@@ -254,7 +256,7 @@ class SeleniumTestCase(StaticLiveServerTestCase):
|
|||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
if self.quit_selenium:
|
if self.quit_selenium:
|
||||||
self.selenium.quit()
|
self.selenium.quit()
|
||||||
super(SeleniumTestCase, self).tearDown()
|
super().tearDown()
|
||||||
|
|
||||||
def complete_url(self, location):
|
def complete_url(self, location):
|
||||||
base_url = self.live_server_url
|
base_url = self.live_server_url
|
||||||
@@ -264,8 +266,8 @@ class SeleniumTestCase(StaticLiveServerTestCase):
|
|||||||
return self.selenium.get(self.complete_url(location))
|
return self.selenium.get(self.complete_url(location))
|
||||||
|
|
||||||
def wait_on(self, driver, ec_name, ec_argument, timeout=30):
|
def wait_on(self, driver, ec_name, ec_argument, timeout=30):
|
||||||
ec = getattr(EC, ec_name)
|
expected_condition = getattr(ExpectedConditions, ec_name)
|
||||||
return WebDriverWait(driver, timeout).until(ec(ec_argument))
|
return WebDriverWait(driver, timeout).until(expected_condition(ec_argument))
|
||||||
|
|
||||||
def wait_on_presence(self, driver, locator, timeout=30):
|
def wait_on_presence(self, driver, locator, timeout=30):
|
||||||
ec_name = 'presence_of_element_located'
|
ec_name = 'presence_of_element_located'
|
||||||
@@ -281,7 +283,7 @@ class ScreenshotTestCase(SeleniumTestCase):
|
|||||||
locations = ()
|
locations = ()
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(ScreenshotTestCase, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
screenshot_base_dir = os.path.join('tmp', 'test-screenshots')
|
screenshot_base_dir = os.path.join('tmp', 'test-screenshots')
|
||||||
self.screenshot_path = screenshot_base_dir
|
self.screenshot_path = screenshot_base_dir
|
||||||
self.screenshot_sequences = {}
|
self.screenshot_sequences = {}
|
||||||
@@ -298,7 +300,7 @@ class ScreenshotTestCase(SeleniumTestCase):
|
|||||||
else:
|
else:
|
||||||
self.screenshot_sequences[sequence] = 1
|
self.screenshot_sequences[sequence] = 1
|
||||||
n = self.screenshot_sequences[sequence]
|
n = self.screenshot_sequences[sequence]
|
||||||
sequence = u'%s-%04d-' % (sequence, n)
|
sequence = '%s-%04d-' % (sequence, n)
|
||||||
if title is None:
|
if title is None:
|
||||||
location = self.selenium.current_url
|
location = self.selenium.current_url
|
||||||
if location.startswith(self.live_server_url):
|
if location.startswith(self.live_server_url):
|
||||||
@@ -308,7 +310,7 @@ class ScreenshotTestCase(SeleniumTestCase):
|
|||||||
location = 'root'
|
location = 'root'
|
||||||
title = location
|
title = location
|
||||||
|
|
||||||
base_name = u'{timestamp}-{prefix}{sequence}{title}.png'.format(
|
base_name = '{timestamp}-{prefix}{sequence}{title}.png'.format(
|
||||||
timestamp=datetime.datetime.now().strftime('%Y%m%d-%H%M%S.%f'),
|
timestamp=datetime.datetime.now().strftime('%Y%m%d-%H%M%S.%f'),
|
||||||
prefix=self.screenshot_prefix,
|
prefix=self.screenshot_prefix,
|
||||||
sequence=sequence,
|
sequence=sequence,
|
||||||
|
|||||||
@@ -48,8 +48,8 @@ class DefaultSettingTestCase(SimpleTestCase):
|
|||||||
|
|
||||||
re_validator = r'[a-z]'
|
re_validator = r'[a-z]'
|
||||||
|
|
||||||
valid_values = u'aocd'
|
valid_values = 'aocd'
|
||||||
invalid_values = u'Aö1.'
|
invalid_values = 'Aö1.'
|
||||||
|
|
||||||
setting = DefaultSetting(name, None, validator=re_validator)
|
setting = DefaultSetting(name, None, validator=re_validator)
|
||||||
for val in valid_values:
|
for val in valid_values:
|
||||||
@@ -89,7 +89,7 @@ class AppSettingsTestCase(SimpleTestCase):
|
|||||||
DefaultSetting('no_test_setting', ImproperlyConfigured),
|
DefaultSetting('no_test_setting', ImproperlyConfigured),
|
||||||
)
|
)
|
||||||
with self.assertRaisesRegex(ImproperlyConfigured, 'NO_TEST_SETTING must be defined in main.settings-dav_base'):
|
with self.assertRaisesRegex(ImproperlyConfigured, 'NO_TEST_SETTING must be defined in main.settings-dav_base'):
|
||||||
app_settings = AppSettings(self.app_name, default_settings)
|
_ = AppSettings(self.app_name, default_settings)
|
||||||
|
|
||||||
def test_improperlyconfigured_by_instance(self):
|
def test_improperlyconfigured_by_instance(self):
|
||||||
"""Test if mandatory but unset setting raise correct exception"""
|
"""Test if mandatory but unset setting raise correct exception"""
|
||||||
@@ -97,17 +97,17 @@ class AppSettingsTestCase(SimpleTestCase):
|
|||||||
DefaultSetting('no_test_setting', ImproperlyConfigured('Some Error Message')),
|
DefaultSetting('no_test_setting', ImproperlyConfigured('Some Error Message')),
|
||||||
)
|
)
|
||||||
with self.assertRaisesRegex(ImproperlyConfigured, 'Some Error Message'):
|
with self.assertRaisesRegex(ImproperlyConfigured, 'Some Error Message'):
|
||||||
app_settings = AppSettings(self.app_name, default_settings)
|
_ = AppSettings(self.app_name, default_settings)
|
||||||
|
|
||||||
def test_improperlyconfigured_by_func(self):
|
def test_improperlyconfigured_by_func(self):
|
||||||
"""Test if invalid setting raise correct exception"""
|
"""Test if invalid setting raise correct exception"""
|
||||||
def validator(value):
|
def validator(value): # pylint: disable=unused-argument
|
||||||
return False
|
return False
|
||||||
default_settings = (
|
default_settings = (
|
||||||
DefaultSetting('test_setting', 1, validator=validator),
|
DefaultSetting('test_setting', 1, validator=validator),
|
||||||
)
|
)
|
||||||
with self.assertRaises(ImproperlyConfigured):
|
with self.assertRaises(ImproperlyConfigured):
|
||||||
app_settings = AppSettings(self.app_name, default_settings)
|
_ = AppSettings(self.app_name, default_settings)
|
||||||
|
|
||||||
def test_improperlyconfigured_by_regex(self):
|
def test_improperlyconfigured_by_regex(self):
|
||||||
"""Test if invalid setting raise correct exception"""
|
"""Test if invalid setting raise correct exception"""
|
||||||
@@ -115,7 +115,7 @@ class AppSettingsTestCase(SimpleTestCase):
|
|||||||
DefaultSetting('test_setting', 1, validator=r'^[01]$'),
|
DefaultSetting('test_setting', 1, validator=r'^[01]$'),
|
||||||
)
|
)
|
||||||
with self.assertRaises(ImproperlyConfigured):
|
with self.assertRaises(ImproperlyConfigured):
|
||||||
app_settings = AppSettings(self.app_name, default_settings)
|
_ = AppSettings(self.app_name, default_settings)
|
||||||
|
|
||||||
def test_settings_from_file(self):
|
def test_settings_from_file(self):
|
||||||
"""Test if value from settings file eliminate exception"""
|
"""Test if value from settings file eliminate exception"""
|
||||||
@@ -145,7 +145,7 @@ class AppSettingsTestCase(SimpleTestCase):
|
|||||||
class AppConfigTestCase(SimpleTestCase):
|
class AppConfigTestCase(SimpleTestCase):
|
||||||
def test_init(self):
|
def test_init(self):
|
||||||
app_name = 'dav_base'
|
app_name = 'dav_base'
|
||||||
import dav_base as app_module
|
import dav_base as app_module # pylint: disable=import-outside-toplevel
|
||||||
|
|
||||||
test_default_settings = (
|
test_default_settings = (
|
||||||
DefaultSetting('test_setting', ImproperlyConfigured),
|
DefaultSetting('test_setting', ImproperlyConfigured),
|
||||||
@@ -154,7 +154,7 @@ class AppConfigTestCase(SimpleTestCase):
|
|||||||
|
|
||||||
class TestAppConfig(AppConfig):
|
class TestAppConfig(AppConfig):
|
||||||
name = 'not_dav_base'
|
name = 'not_dav_base'
|
||||||
verbose_name = u'DAV Base App'
|
verbose_name = 'DAV Base App'
|
||||||
default_settings = test_default_settings
|
default_settings = test_default_settings
|
||||||
|
|
||||||
app_config = TestAppConfig(app_name, app_module)
|
app_config = TestAppConfig(app_name, app_module)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ MAIL_TEMPLATE = 'dav_base/tests/mail.txt'
|
|||||||
|
|
||||||
class TestCase(EmailTestMixin, SimpleTestCase):
|
class TestCase(EmailTestMixin, SimpleTestCase):
|
||||||
def test_no_template_configured(self):
|
def test_no_template_configured(self):
|
||||||
class ConcreteMail(AbstractMail):
|
class ConcreteMail(AbstractMail): # pylint: disable=abstract-method disable=too-few-public-methods
|
||||||
pass
|
pass
|
||||||
|
|
||||||
email = ConcreteMail()
|
email = ConcreteMail()
|
||||||
@@ -18,7 +18,7 @@ class TestCase(EmailTestMixin, SimpleTestCase):
|
|||||||
email.send()
|
email.send()
|
||||||
|
|
||||||
def test_no_get_recipients_implemented(self):
|
def test_no_get_recipients_implemented(self):
|
||||||
class ConcreteMail(AbstractMail):
|
class ConcreteMail(AbstractMail): # pylint: disable=abstract-method disable=too-few-public-methods
|
||||||
_template_name = MAIL_TEMPLATE
|
_template_name = MAIL_TEMPLATE
|
||||||
|
|
||||||
email = ConcreteMail()
|
email = ConcreteMail()
|
||||||
@@ -28,7 +28,7 @@ class TestCase(EmailTestMixin, SimpleTestCase):
|
|||||||
def test_send(self):
|
def test_send(self):
|
||||||
recipient = 'root@localhost'
|
recipient = 'root@localhost'
|
||||||
|
|
||||||
class ConcreteMail(AbstractMail):
|
class ConcreteMail(AbstractMail): # pylint: disable=too-few-public-methods
|
||||||
_template_name = MAIL_TEMPLATE
|
_template_name = MAIL_TEMPLATE
|
||||||
|
|
||||||
def _get_recipients(self):
|
def _get_recipients(self):
|
||||||
@@ -42,5 +42,5 @@ class TestCase(EmailTestMixin, SimpleTestCase):
|
|||||||
|
|
||||||
self.assertSender(mail)
|
self.assertSender(mail)
|
||||||
self.assertRecipients(mail, [recipient])
|
self.assertRecipients(mail, [recipient])
|
||||||
self.assertSubject(mail, u'')
|
self.assertSubject(mail, '')
|
||||||
self.assertBody(mail, 'MAILBODY')
|
self.assertBody(mail, 'MAILBODY')
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from django.test import SimpleTestCase
|
|||||||
|
|
||||||
class TemplatesTestCase(SimpleTestCase):
|
class TemplatesTestCase(SimpleTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TemplatesTestCase, self).setUp()
|
super().setUp()
|
||||||
self.response_from_root_view = self.client.get('/')
|
self.response_from_root_view = self.client.get('/')
|
||||||
|
|
||||||
def test_template_usage(self):
|
def test_template_usage(self):
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ class TemplateTagsTestCase(SimpleTestCase):
|
|||||||
|
|
||||||
def test_include_if_exist(self):
|
def test_include_if_exist(self):
|
||||||
template_name = 'dav_base/tests/include_if_exist.html'
|
template_name = 'dav_base/tests/include_if_exist.html'
|
||||||
|
|
||||||
|
with self.settings(DEBUG=False):
|
||||||
template = get_template(template_name)
|
template = get_template(template_name)
|
||||||
|
|
||||||
content = template.render()
|
content = template.render()
|
||||||
@@ -36,9 +38,7 @@ class TemplateTagsTestCase(SimpleTestCase):
|
|||||||
--DEFAULT INCLUDED HTML--
|
--DEFAULT INCLUDED HTML--
|
||||||
--OPTIONAL INCLUDED HTML--
|
--OPTIONAL INCLUDED HTML--
|
||||||
--OPTIONAL INCLUDED HTML--
|
--OPTIONAL INCLUDED HTML--
|
||||||
--
|
|
||||||
----
|
----
|
||||||
--
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.assertEqual(content, expected_content)
|
self.assertEqual(content, expected_content)
|
||||||
|
|||||||
@@ -10,4 +10,4 @@ DAVNumberValidator = RegexValidator(r'^'
|
|||||||
r'(\*[0-9]{4}\*[0-9]{4})?'
|
r'(\*[0-9]{4}\*[0-9]{4})?'
|
||||||
r'([* ][0-9]{8})?'
|
r'([* ][0-9]{8})?'
|
||||||
r'$',
|
r'$',
|
||||||
_(u'Ungültiges Format.'))
|
_('Ungültiges Format.'))
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ class RootView(generic.TemplateView):
|
|||||||
template_name = 'dav_base/root.html'
|
template_name = 'dav_base/root.html'
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
c = super(RootView, self).get_context_data(**kwargs)
|
c = super().get_context_data(**kwargs)
|
||||||
root_urls = []
|
root_urls = []
|
||||||
for module_meta_obj in settings.MODULE_CONFIG.modules.values():
|
for module_meta_obj in settings.MODULE_CONFIG.modules.values():
|
||||||
root_url_name = 'root'
|
root_url_name = 'root'
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
default_app_config = 'dav_event_office.apps.AppConfig'
|
default_app_config = 'dav_event_office.apps.AppConfig' # pylint: disable=invalid-name
|
||||||
|
|||||||
@@ -5,5 +5,5 @@ DEFAULT_SETTINGS = ()
|
|||||||
|
|
||||||
class AppConfig(_AppConfig):
|
class AppConfig(_AppConfig):
|
||||||
name = 'dav_event_office'
|
name = 'dav_event_office'
|
||||||
verbose_name = u'DAV Touren- & Kursreferat'
|
verbose_name = 'DAV Touren- & Kursreferat'
|
||||||
default_settings = DEFAULT_SETTINGS
|
default_settings = DEFAULT_SETTINGS
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<p>
|
<p>
|
||||||
Hier sind alle Veranstaltungen gelistet.
|
Hier sind alle Veranstaltungen gelistet.
|
||||||
<span class="glyphicon glyphicon-question-sign" title="
|
<span class="glyphicon glyphicon-question-sign" title="
|
||||||
Zu jeder Veranstaltung ist eine Detail-Seite aufrufbar, über die Details und angemeldete Teilnehmer einsehbar sind.
|
Zu jeder Veranstaltung ist eine Detail-Seite aufrufbar, über die Details und angemeldete Teilnehmer*innen einsehbar sind.
|
||||||
"></span>
|
"></span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
@@ -18,14 +18,14 @@ Zu jeder Veranstaltung ist eine Detail-Seite aufrufbar, über die Details und an
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<div class="well">
|
<div class="well">
|
||||||
<p class="lead">{% trans 'Teilnehmer' %} <small><small>({% trans 'Zugang nur für Geschäftstelle' %})</small></small></p>
|
<p class="lead">{% trans 'Teilnehmer*innen' %} <small><small>({% trans 'Zugang nur für Geschäftstelle' %})</small></small></p>
|
||||||
<p>
|
<p>
|
||||||
Hier sind alle angemeldeten Teilnehmer gelistet.
|
Hier sind alle angemeldeten Teilnehmer*innen gelistet.
|
||||||
<span class="glyphicon glyphicon-question-sign" title="
|
<span class="glyphicon glyphicon-question-sign" title="
|
||||||
"></span>
|
"></span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<a class="btn btn-success" href="{% url 'dav_event_office:participant-list' %}">{% trans 'Teilnehmer' %}</a>
|
<a class="btn btn-success" href="{% url 'dav_event_office:participant-list' %}">{% trans 'Teilnehmer*innen' %}</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ from django.conf.urls import url
|
|||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
|
app_name = 'dav_event_office'
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^home$', views.HomeView.as_view(), name='root'),
|
url(r'^home$', views.HomeView.as_view(), name='root'),
|
||||||
url(r'^participants$', views.ParticipantListView.as_view(), name='participant-list'),
|
url(r'^participants$', views.ParticipantListView.as_view(), name='participant-list'),
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class EventListView(_EventListView):
|
|||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
if not DefaultWorkflow.has_global_permission(request.user, 'payment'):
|
if not DefaultWorkflow.has_global_permission(request.user, 'payment'):
|
||||||
raise PermissionDenied('payment')
|
raise PermissionDenied('payment')
|
||||||
return super(EventListView, self).dispatch(request, *args, **kwargs)
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class EventDetailView(_EventRegistrationsView):
|
class EventDetailView(_EventRegistrationsView):
|
||||||
@@ -34,7 +34,7 @@ class EventDetailView(_EventRegistrationsView):
|
|||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
if not DefaultWorkflow.has_global_permission(request.user, 'payment'):
|
if not DefaultWorkflow.has_global_permission(request.user, 'payment'):
|
||||||
raise PermissionDenied('payment')
|
raise PermissionDenied('payment')
|
||||||
return super(EventDetailView, self).dispatch(request, *args, **kwargs)
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class ParticipantListView(generic.ListView):
|
class ParticipantListView(generic.ListView):
|
||||||
@@ -66,4 +66,4 @@ class ParticipantListView(generic.ListView):
|
|||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
if not DefaultWorkflow.has_global_permission(request.user, 'payment'):
|
if not DefaultWorkflow.has_global_permission(request.user, 'payment'):
|
||||||
raise PermissionDenied('payment')
|
raise PermissionDenied('payment')
|
||||||
return super(ParticipantListView, self).dispatch(request, *args, **kwargs)
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
default_app_config = 'dav_events.apps.AppConfig'
|
default_app_config = 'dav_events.apps.AppConfig' # pylint: disable=invalid-name
|
||||||
|
|||||||
@@ -16,17 +16,17 @@ DEFAULT_SETTINGS = (
|
|||||||
DefaultSetting('groups_publisher_facebook', []),
|
DefaultSetting('groups_publisher_facebook', []),
|
||||||
DefaultSetting('groups_office', []),
|
DefaultSetting('groups_office', []),
|
||||||
DefaultSetting('forms_development_init', False),
|
DefaultSetting('forms_development_init', False),
|
||||||
DefaultSetting('form_initials', dict()),
|
DefaultSetting('form_initials', {}),
|
||||||
DefaultSetting('matrix_config', ImproperlyConfigured),
|
DefaultSetting('matrix_config', ImproperlyConfigured),
|
||||||
DefaultSetting('publish_before_begin_days', 10),
|
DefaultSetting('publish_before_begin_days', 10),
|
||||||
DefaultSetting('publish_before_deadline_days', 7),
|
DefaultSetting('publish_before_deadline_days', 7),
|
||||||
DefaultSetting('publish_issues', list()),
|
DefaultSetting('publish_issues', []),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class AppConfig(_AppConfig):
|
class AppConfig(_AppConfig):
|
||||||
name = 'dav_events'
|
name = 'dav_events'
|
||||||
verbose_name = u'DAV Touren & Kurse'
|
verbose_name = 'DAV Touren & Kurse'
|
||||||
default_settings = DEFAULT_SETTINGS
|
default_settings = DEFAULT_SETTINGS
|
||||||
|
|
||||||
def ready(self):
|
def ready(self):
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
import pytz
|
|
||||||
import re
|
import re
|
||||||
|
import pytz
|
||||||
from six import string_types
|
from six import string_types
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Iso8601Serializer(object):
|
class Iso8601Serializer:
|
||||||
marker = 'ISO8601'
|
marker = 'ISO8601'
|
||||||
separator = ':'
|
separator = ':'
|
||||||
|
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
|
|
||||||
class RegistrationResponseForm(forms.Form):
|
class RegistrationResponseForm(forms.Form):
|
||||||
apply_reduced_fee = forms.BooleanField(required=False,
|
apply_reduced_fee = forms.BooleanField(required=False,
|
||||||
label=_(u'Reduzierte Teilnahmegebühr'))
|
label=_('Reduzierte Teilnahmegebühr'))
|
||||||
|
|||||||
44
dav_events/migrations/0042_auto_20220607_1345.py
Normal file
44
dav_events/migrations/0042_auto_20220607_1345.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# Generated by Django 3.2.13 on 2022-06-07 11:45
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('dav_events', '0041_auto_20210107_1220'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='event',
|
||||||
|
name='id',
|
||||||
|
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='eventchange',
|
||||||
|
name='id',
|
||||||
|
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='eventflag',
|
||||||
|
name='id',
|
||||||
|
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='participant',
|
||||||
|
name='event',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='participants', to='dav_events.event'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='participant',
|
||||||
|
name='id',
|
||||||
|
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='trashedparticipant',
|
||||||
|
name='id',
|
||||||
|
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -10,10 +10,9 @@ import unicodedata
|
|||||||
from babel.dates import format_date
|
from babel.dates import format_date
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.template.loader import get_template
|
from django.template.loader import get_template
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
|
||||||
from django.utils.translation import get_language, ugettext_lazy as _
|
from django.utils.translation import get_language, ugettext_lazy as _
|
||||||
from django_countries.fields import Country, CountryField
|
from django_countries.fields import Country, CountryField
|
||||||
|
|
||||||
@@ -27,7 +26,6 @@ from .eventchange import EventChange
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class Event(models.Model):
|
class Event(models.Model):
|
||||||
# Metadata
|
# Metadata
|
||||||
owner = models.ForeignKey(settings.AUTH_USER_MODEL,
|
owner = models.ForeignKey(settings.AUTH_USER_MODEL,
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ from __future__ import unicode_literals
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
|
||||||
|
|
||||||
from . import get_ghost_user, get_system_user
|
from . import get_ghost_user, get_system_user
|
||||||
|
|
||||||
@@ -12,7 +11,6 @@ def get_system_user_id():
|
|||||||
return get_system_user().id
|
return get_system_user().id
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class EventChange(models.Model):
|
class EventChange(models.Model):
|
||||||
UPDATE = 'update'
|
UPDATE = 'update'
|
||||||
RAISE_FLAG = 'set_flag'
|
RAISE_FLAG = 'set_flag'
|
||||||
@@ -23,7 +21,7 @@ class EventChange(models.Model):
|
|||||||
(LOWER_FLAG, 'Lower Flag'),
|
(LOWER_FLAG, 'Lower Flag'),
|
||||||
)
|
)
|
||||||
|
|
||||||
event = models.ForeignKey('dav_events.Event', related_name='changes')
|
event = models.ForeignKey('dav_events.Event', related_name='changes', on_delete=models.CASCADE)
|
||||||
timestamp = models.DateTimeField(default=timezone.now)
|
timestamp = models.DateTimeField(default=timezone.now)
|
||||||
user = models.ForeignKey(settings.AUTH_USER_MODEL,
|
user = models.ForeignKey(settings.AUTH_USER_MODEL,
|
||||||
default=get_system_user_id,
|
default=get_system_user_id,
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ from __future__ import unicode_literals
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
|
||||||
|
|
||||||
from . import get_ghost_user, get_system_user
|
from . import get_ghost_user, get_system_user
|
||||||
|
|
||||||
@@ -11,9 +10,8 @@ def get_system_user_id():
|
|||||||
return get_system_user().id
|
return get_system_user().id
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class EventFlag(models.Model):
|
class EventFlag(models.Model):
|
||||||
event = models.ForeignKey('dav_events.Event', related_name='flags')
|
event = models.ForeignKey('dav_events.Event', related_name='flags', on_delete=models.CASCADE)
|
||||||
status = models.ForeignKey('dav_events.EventStatus',
|
status = models.ForeignKey('dav_events.EventStatus',
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name='+')
|
related_name='+')
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from ..validators import IdStringValidator
|
from ..validators import IdStringValidator
|
||||||
@@ -22,7 +21,6 @@ BOOTSTRAP_CONTEXT_CHOICES = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class EventStatus(models.Model):
|
class EventStatus(models.Model):
|
||||||
code = models.CharField(primary_key=True, max_length=254, validators=[IdStringValidator])
|
code = models.CharField(primary_key=True, max_length=254, validators=[IdStringValidator])
|
||||||
severity = models.IntegerField(unique=True)
|
severity = models.IntegerField(unique=True)
|
||||||
|
|||||||
@@ -3,10 +3,9 @@ from __future__ import unicode_literals
|
|||||||
import logging
|
import logging
|
||||||
import uuid
|
import uuid
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
|
||||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||||
|
|
||||||
from .event import Event
|
from .event import Event
|
||||||
@@ -14,7 +13,6 @@ from .event import Event
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class OneClickAction(models.Model):
|
class OneClickAction(models.Model):
|
||||||
COMMANDS = (
|
COMMANDS = (
|
||||||
('EVENT_LIST', 'login and go to event list (user id)'),
|
('EVENT_LIST', 'login and go to event list (user id)'),
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import datetime
|
|||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from dav_base.validators import DAVNumberValidator
|
from dav_base.validators import DAVNumberValidator
|
||||||
@@ -12,7 +11,6 @@ from dav_base.validators import DAVNumberValidator
|
|||||||
midnight = datetime.time(00, 00, 00)
|
midnight = datetime.time(00, 00, 00)
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class AbstractParticipant(models.Model):
|
class AbstractParticipant(models.Model):
|
||||||
personal_names = models.CharField(max_length=1024,
|
personal_names = models.CharField(max_length=1024,
|
||||||
verbose_name=_('Vorname(n)'))
|
verbose_name=_('Vorname(n)'))
|
||||||
@@ -158,9 +156,8 @@ class AbstractParticipant(models.Model):
|
|||||||
return timezone.make_aware(datetime.datetime.combine(purge_date, midnight))
|
return timezone.make_aware(datetime.datetime.combine(purge_date, midnight))
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class Participant(AbstractParticipant):
|
class Participant(AbstractParticipant):
|
||||||
event = models.ForeignKey('Event', related_name='participants')
|
event = models.ForeignKey('Event', related_name='participants', on_delete=models.PROTECT)
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
position = models.IntegerField(verbose_name='Listennummer')
|
position = models.IntegerField(verbose_name='Listennummer')
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from ..participant import AbstractParticipant
|
from ..participant import AbstractParticipant
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class TrashedParticipant(AbstractParticipant):
|
class TrashedParticipant(AbstractParticipant):
|
||||||
event = models.ForeignKey('Event', related_name='trashed_participants')
|
event = models.ForeignKey('Event', related_name='trashed_participants', on_delete=models.CASCADE)
|
||||||
created_at = models.DateTimeField()
|
created_at = models.DateTimeField()
|
||||||
trashed_at = models.DateTimeField(auto_now_add=True)
|
trashed_at = models.DateTimeField(auto_now_add=True)
|
||||||
position = models.IntegerField(verbose_name='Listennummer')
|
position = models.IntegerField(verbose_name='Listennummer')
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ Der folgende Link führt zur Veranstaltung:
|
|||||||
|
|
||||||
Veröffentlichung: ({% if planned_publication_date %}{{ planned_publication_date|date:'l, d. F Y' }}{% else %}sofort{% endif %})
|
Veröffentlichung: ({% if planned_publication_date %}{{ planned_publication_date|date:'l, d. F Y' }}{% else %}sofort{% endif %})
|
||||||
=================
|
=================
|
||||||
|
Titel: {{ number }} - {{ title }}
|
||||||
Veröffentlichung starten: {% if planned_publication_date %}{{ planned_publication_date|date:'d.m.Y' }} 00:00:00{% else %}--{% endif %}
|
Veröffentlichung starten: {% if planned_publication_date %}{{ planned_publication_date|date:'d.m.Y' }} 00:00:00{% else %}--{% endif %}
|
||||||
Veröffentlichung beenden: {{ day_after|date:'d.m.Y' }} 00:00:00
|
Veröffentlichung beenden: {{ day_after|date:'d.m.Y' }} 00:00:00
|
||||||
Erstellungsdatum: {{ first_day|date:'d.m.Y' }} 00:00:00
|
Erstellungsdatum: {{ first_day|date:'d.m.Y' }} 00:00:00
|
||||||
Titel: {{ number }} - {{ title }}
|
|
||||||
{% if internal_note %}
|
{% if internal_note %}
|
||||||
Bearbeitungshinweis:
|
Bearbeitungshinweis:
|
||||||
====================
|
====================
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ Du wirst dann per E-Mail auf dem laufenden gehalten.
|
|||||||
<p>
|
<p>
|
||||||
Hier kannst du deine eingetragenen Touren und Kurse verwalten.
|
Hier kannst du deine eingetragenen Touren und Kurse verwalten.
|
||||||
<span class="glyphicon glyphicon-question-sign" title="
|
<span class="glyphicon glyphicon-question-sign" title="
|
||||||
Als Tourenleiter kannst du hier die Anmeldungen und Teilnehmerlisten zu deinen Touren verwalten.
|
Als Tourenleiter*in kannst du hier die Anmeldungen und Teilnehmerlisten zu deinen Touren verwalten.
|
||||||
Tourenreferenten und Redakteure können hier Veranstaltungen freigeben und Programmlisten herunterladen.
|
Tourenreferenten und Redakteure können hier Veranstaltungen freigeben und Programmlisten herunterladen.
|
||||||
"></span>
|
"></span>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ register = template.Library()
|
|||||||
|
|
||||||
@register.simple_tag
|
@register.simple_tag
|
||||||
def render_event_status(event, show_void=True):
|
def render_event_status(event, show_void=True):
|
||||||
label_html = u'<span class="label label-{context}">{label}</span> '
|
label_html = '<span class="label label-{context}">{label}</span> '
|
||||||
|
|
||||||
html = u''
|
html = ''
|
||||||
|
|
||||||
status_list = event.workflow.get_status_list()
|
status_list = event.workflow.get_status_list()
|
||||||
if show_void and len(status_list) < 1:
|
if show_void and len(status_list) < 1:
|
||||||
@@ -39,29 +39,29 @@ def render_event_status(event, show_void=True):
|
|||||||
|
|
||||||
@register.simple_tag
|
@register.simple_tag
|
||||||
def render_event_changelog(event):
|
def render_event_changelog(event):
|
||||||
change_templ = u'<li class="list-group-item">\n' \
|
change_templ = '<li class="list-group-item">\n' \
|
||||||
u'\t<p class="list-group-item-heading">' \
|
'\t<p class="list-group-item-heading">' \
|
||||||
u'<span class="glyphicon glyphicon-{icon}"></span>' \
|
'<span class="glyphicon glyphicon-{icon}"></span>' \
|
||||||
u' {timestamp}' \
|
' {timestamp}' \
|
||||||
u' - ' \
|
' - ' \
|
||||||
u' {user}</p>\n' \
|
' {user}</p>\n' \
|
||||||
u'\t{content}\n' \
|
'\t{content}\n' \
|
||||||
u'</li>\n'
|
'</li>\n'
|
||||||
update_sub_templ = u'<li class="list-group-item">\n' \
|
update_sub_templ = '<li class="list-group-item">\n' \
|
||||||
u'\t{field}:{separator1}\n' \
|
'\t{field}:{separator1}\n' \
|
||||||
u'\t<span style="background-color: #ffe0e0;">{refer}</span>\n' \
|
'\t<span style="background-color: #ffe0e0;">{refer}</span>\n' \
|
||||||
u'\t{separator2}\n' \
|
'\t{separator2}\n' \
|
||||||
u'\t<span style="background-color: #e0ffe0;">{current}</span>\n' \
|
'\t<span style="background-color: #e0ffe0;">{current}</span>\n' \
|
||||||
u'</li>\n'
|
'</li>\n'
|
||||||
raise_flag_templ = u'<span class="text-success glyphicon glyphicon-plus"></span>' \
|
raise_flag_templ = '<span class="text-success glyphicon glyphicon-plus"></span>' \
|
||||||
u' <span class="label label-{bcontext}">{label}</span>' \
|
' <span class="label label-{bcontext}">{label}</span>' \
|
||||||
u' <span class="text-success glyphicon glyphicon-plus"></span>'
|
' <span class="text-success glyphicon glyphicon-plus"></span>'
|
||||||
lower_flag_templ = u'<span class="text-danger glyphicon glyphicon-minus"></span>' \
|
lower_flag_templ = '<span class="text-danger glyphicon glyphicon-minus"></span>' \
|
||||||
u' <del><span class="label label-{bcontext}">{label}</span></del>' \
|
' <del><span class="label label-{bcontext}">{label}</span></del>' \
|
||||||
u' <span class="text-danger glyphicon glyphicon-minus"></span>'
|
' <span class="text-danger glyphicon glyphicon-minus"></span>'
|
||||||
|
|
||||||
if event.changes.exists():
|
if event.changes.exists():
|
||||||
html = u'<ul class="list-group">\n'
|
html = '<ul class="list-group">\n'
|
||||||
|
|
||||||
for change in event.changes.all():
|
for change in event.changes.all():
|
||||||
|
|
||||||
@@ -70,8 +70,8 @@ def render_event_changelog(event):
|
|||||||
username = change.user
|
username = change.user
|
||||||
|
|
||||||
if change.operation == EventChange.UPDATE:
|
if change.operation == EventChange.UPDATE:
|
||||||
icon = u'pencil'
|
icon = 'pencil'
|
||||||
content_html = u'<ul class="list-group">'
|
content_html = '<ul class="list-group">'
|
||||||
subchanges = json.loads(change.content)
|
subchanges = json.loads(change.content)
|
||||||
for subchange in subchanges:
|
for subchange in subchanges:
|
||||||
field_label = event._meta.get_field(subchange['field']).verbose_name
|
field_label = event._meta.get_field(subchange['field']).verbose_name
|
||||||
@@ -80,33 +80,33 @@ def render_event_changelog(event):
|
|||||||
except TypeError:
|
except TypeError:
|
||||||
is_long_strings = False
|
is_long_strings = False
|
||||||
if is_long_strings:
|
if is_long_strings:
|
||||||
separator1 = u'<br />'
|
separator1 = '<br />'
|
||||||
separator2 = u'<br />'
|
separator2 = '<br />'
|
||||||
else:
|
else:
|
||||||
separator1 = u' '
|
separator1 = ' '
|
||||||
separator2 = u' -> '
|
separator2 = ' -> '
|
||||||
content_html += format_html(update_sub_templ,
|
content_html += format_html(update_sub_templ,
|
||||||
field=field_label,
|
field=field_label,
|
||||||
separator1=mark_safe(separator1),
|
separator1=mark_safe(separator1),
|
||||||
refer=subchange['refer'],
|
refer=subchange['refer'],
|
||||||
separator2=mark_safe(separator2),
|
separator2=mark_safe(separator2),
|
||||||
current=subchange['current'])
|
current=subchange['current'])
|
||||||
content_html += u'</ul>'
|
content_html += '</ul>'
|
||||||
elif change.operation == EventChange.RAISE_FLAG:
|
elif change.operation == EventChange.RAISE_FLAG:
|
||||||
icon = u'flag'
|
icon = 'flag'
|
||||||
status = get_or_create_event_status(change.content)
|
status = get_or_create_event_status(change.content)
|
||||||
content_html = format_html(raise_flag_templ,
|
content_html = format_html(raise_flag_templ,
|
||||||
bcontext=status.bootstrap_context or u'default',
|
bcontext=status.bootstrap_context or 'default',
|
||||||
label=status.label)
|
label=status.label)
|
||||||
elif change.operation == EventChange.LOWER_FLAG:
|
elif change.operation == EventChange.LOWER_FLAG:
|
||||||
icon = u'flag'
|
icon = 'flag'
|
||||||
status = get_or_create_event_status(change.content)
|
status = get_or_create_event_status(change.content)
|
||||||
content_html = format_html(lower_flag_templ,
|
content_html = format_html(lower_flag_templ,
|
||||||
bcontext=status.bootstrap_context or u'default',
|
bcontext=status.bootstrap_context or 'default',
|
||||||
label=status.label)
|
label=status.label)
|
||||||
else:
|
else:
|
||||||
icon = u'question-sign'
|
icon = 'question-sign'
|
||||||
content_html = format_html(u'{content}', content=change.content)
|
content_html = format_html('{content}', content=change.content)
|
||||||
|
|
||||||
html += format_html(change_templ,
|
html += format_html(change_templ,
|
||||||
icon=icon,
|
icon=icon,
|
||||||
@@ -114,7 +114,7 @@ def render_event_changelog(event):
|
|||||||
user=username,
|
user=username,
|
||||||
content=mark_safe(content_html))
|
content=mark_safe(content_html))
|
||||||
|
|
||||||
html += u'</ul>\n'
|
html += '</ul>\n'
|
||||||
else:
|
else:
|
||||||
html = _(u'Keine Einträge')
|
html = _('Keine Einträge')
|
||||||
return mark_safe(html)
|
return mark_safe(html)
|
||||||
|
|||||||
@@ -6,13 +6,12 @@ import os
|
|||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import Group
|
||||||
from django.db import models
|
|
||||||
|
|
||||||
from ..models.event import Event
|
from ..models.event import Event
|
||||||
from ..models.eventstatus import EventStatus
|
from ..models.eventstatus import EventStatus
|
||||||
|
|
||||||
|
|
||||||
class RoleMixin(object):
|
class RoleMixin:
|
||||||
def create_user_for_role(self, role_name, password, first_name, last_name):
|
def create_user_for_role(self, role_name, password, first_name, last_name):
|
||||||
group = Group(name=role_name)
|
group = Group(name=role_name)
|
||||||
group.save()
|
group.save()
|
||||||
@@ -31,7 +30,7 @@ class RoleMixin(object):
|
|||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
class EventMixin(object):
|
class EventMixin:
|
||||||
def get_status_label(self, status_code):
|
def get_status_label(self, status_code):
|
||||||
return EventStatus.objects.get(code=status_code).label
|
return EventStatus.objects.get(code=status_code).label
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import sys
|
|
||||||
import datetime
|
import datetime
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
@@ -10,7 +9,7 @@ class Iso8601SerializerTestCase(TestCase):
|
|||||||
date = start_date
|
date = start_date
|
||||||
while date <= end_date:
|
while date <= end_date:
|
||||||
text = '%04d-%02d-%02d' % (date.year, date.month, date.day)
|
text = '%04d-%02d-%02d' % (date.year, date.month, date.day)
|
||||||
yield (date, text)
|
yield date, text
|
||||||
date += datetime.timedelta(step)
|
date += datetime.timedelta(step)
|
||||||
|
|
||||||
def _gen_times(self, step_hours=1, step_minutes=1, step_seconds=1):
|
def _gen_times(self, step_hours=1, step_minutes=1, step_seconds=1):
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ Ausschreibung:
|
|||||||
==============
|
==============
|
||||||
{event_text}"""
|
{event_text}"""
|
||||||
|
|
||||||
EVENT_ACCEPTED_EMAIL_TEMPLATE=u"""Hallo {recipient_first_name},
|
EVENT_ACCEPTED_EMAIL_TEMPLATE = """Hallo {recipient_first_name},
|
||||||
|
|
||||||
deine Veranstaltung wurde von {editor_full_name} freigegeben.
|
deine Veranstaltung wurde von {editor_full_name} freigegeben.
|
||||||
Die Redaktion wurde informiert, um deine Veranstaltung zu veröffentlichen.
|
Die Redaktion wurde informiert, um deine Veranstaltung zu veröffentlichen.
|
||||||
@@ -113,10 +113,10 @@ Der folgende Link führt zur Veranstaltung:
|
|||||||
|
|
||||||
Veröffentlichung: ({planned_publication_date})
|
Veröffentlichung: ({planned_publication_date})
|
||||||
=================
|
=================
|
||||||
|
Titel: {number} - {title}
|
||||||
Veröffentlichung starten: {planned_publication_date_short} 00:00:00
|
Veröffentlichung starten: {planned_publication_date_short} 00:00:00
|
||||||
Veröffentlichung beenden: {day_after_short} 00:00:00
|
Veröffentlichung beenden: {day_after_short} 00:00:00
|
||||||
Erstellungsdatum: {first_day_short} 00:00:00
|
Erstellungsdatum: {first_day_short} 00:00:00
|
||||||
Titel: {number} - {title}
|
|
||||||
|
|
||||||
Bearbeitungshinweis:
|
Bearbeitungshinweis:
|
||||||
====================
|
====================
|
||||||
|
|||||||
@@ -117,15 +117,12 @@ class ActionTestCase(EmailTestMixin, RoleMixin, EventMixin, TestCase):
|
|||||||
' von %(user)s'
|
' von %(user)s'
|
||||||
' auf \'%(status)s\' gesetzt.') % {
|
' auf \'%(status)s\' gesetzt.') % {
|
||||||
'status': status_label,
|
'status': status_label,
|
||||||
'date': datetime.datetime.now().strftime('%d.%m.%Y %H:%M:%S'),
|
# 'date': datetime.datetime.now().strftime('%d.%m.%Y %H:%M:%S'),
|
||||||
|
'date': '\d{2}\.\d{2}\.\d{4} \d{2}:\d{2}:\d{2}',
|
||||||
'user': user.get_full_name(),
|
'user': user.get_full_name(),
|
||||||
})
|
})
|
||||||
html = message.replace('\'', ''')
|
html = message.replace('\'', ''')
|
||||||
# Sometimes this test fail, and we cannot see the tested content, so we create our own Exception
|
self.assertRegex(content, html)
|
||||||
try:
|
|
||||||
self.assertInHTML(html, content)
|
|
||||||
except AssertionError:
|
|
||||||
raise AssertionError('Not in HTML:\n{}\n-----\n{}\n'.format(html, content))
|
|
||||||
self.assertRegex(content, r'alert-success')
|
self.assertRegex(content, r'alert-success')
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|||||||
@@ -158,4 +158,3 @@ class EventsTestCase(EventMixin, TestCase):
|
|||||||
def test_event_create_view(self):
|
def test_event_create_view(self):
|
||||||
for data in self.gen_create_event_input():
|
for data in self.gen_create_event_input():
|
||||||
self.create_event_by_view(data)
|
self.create_event_by_view(data)
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ from django.conf.urls import url
|
|||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
|
app_name = 'dav_events'
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^home$', views.base.HomeView.as_view(), name='root'),
|
url(r'^home$', views.base.HomeView.as_view(), name='root'),
|
||||||
url(r'^$', views.events.EventListView.as_view(), name='list'),
|
url(r'^$', views.events.EventListView.as_view(), name='list'),
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class OneClickActionRunView(generic.DetailView):
|
|||||||
login(self.request, user)
|
login(self.request, user)
|
||||||
logger.info('Logged in via OneClickAction: %s', user.username)
|
logger.info('Logged in via OneClickAction: %s', user.username)
|
||||||
messages.success(self.request,
|
messages.success(self.request,
|
||||||
_(u'Benutzer angemeldet: %(username)s') % {'username': user.username})
|
_('Benutzer angemeldet: %(username)s') % {'username': user.username})
|
||||||
|
|
||||||
if 'location' in result:
|
if 'location' in result:
|
||||||
return HttpResponseRedirect(result['location'])
|
return HttpResponseRedirect(result['location'])
|
||||||
|
|||||||
@@ -754,14 +754,14 @@ class EventCreateView(EventPermissionMixin, generic.FormView):
|
|||||||
_(u'Du hast jemand anderen als Tourenleiter eingetragen.'),
|
_(u'Du hast jemand anderen als Tourenleiter eingetragen.'),
|
||||||
_(u'Warum machst du sowas?')
|
_(u'Warum machst du sowas?')
|
||||||
))
|
))
|
||||||
elif owner.has_usable_password():
|
elif owner.last_login is None:
|
||||||
next_url = reverse('dav_events:list')
|
|
||||||
else:
|
|
||||||
login(self.request, owner)
|
login(self.request, owner)
|
||||||
next_url = reverse('dav_auth:set_password')
|
next_url = reverse('dav_auth:set_password')
|
||||||
messages.success(self.request,
|
messages.success(self.request,
|
||||||
_(u'Neuen Benutzer angemeldet: %(username)s') % {'username': owner.username})
|
_(u'Neuen Benutzer angemeldet: %(username)s') % {'username': owner.username})
|
||||||
messages.warning(self.request, _(u'Bitte neues Passwort setzen!'))
|
messages.warning(self.request, _(u'Bitte neues Passwort setzen!'))
|
||||||
|
else:
|
||||||
|
next_url = reverse('dav_events:list')
|
||||||
return HttpResponseRedirect(next_url)
|
return HttpResponseRedirect(next_url)
|
||||||
|
|
||||||
def clean_session_data(self, session=None):
|
def clean_session_data(self, session=None):
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
default_app_config = 'dav_registration.apps.AppConfig'
|
default_app_config = 'dav_registration.apps.AppConfig' # pylint: disable=invalid-name
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ DEFAULT_SETTINGS = (
|
|||||||
|
|
||||||
class AppConfig(_AppConfig):
|
class AppConfig(_AppConfig):
|
||||||
name = 'dav_registration'
|
name = 'dav_registration'
|
||||||
verbose_name = u'DAV Kurs-Anmeldungen'
|
verbose_name = 'DAV Kurs-Anmeldungen'
|
||||||
default_settings = DEFAULT_SETTINGS
|
default_settings = DEFAULT_SETTINGS
|
||||||
|
|
||||||
def ready(self):
|
def ready(self):
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
from dav_base.emails import AbstractMail
|
from dav_base.emails import AbstractMail
|
||||||
|
|
||||||
|
|
||||||
class AbstractRegistrationMail(AbstractMail):
|
class AbstractRegistrationMail(AbstractMail): # pylint: disable=too-few-public-methods
|
||||||
def __init__(self, recipient, registration):
|
def __init__(self, recipient, registration):
|
||||||
self._recipient = recipient
|
self._recipient = recipient
|
||||||
self._registration = registration
|
self._registration = registration
|
||||||
@@ -13,45 +13,45 @@ class AbstractRegistrationMail(AbstractMail):
|
|||||||
subject_fmt = self._subject
|
subject_fmt = self._subject
|
||||||
|
|
||||||
if self._event.number:
|
if self._event.number:
|
||||||
subject_fmt = u'%s: %s' % (self._event.number, subject_fmt)
|
subject_fmt = '%s: %s' % (self._event.number, subject_fmt)
|
||||||
|
|
||||||
return super(AbstractRegistrationMail, self)._get_subject(subject_fmt=subject_fmt, **kwargs)
|
return super()._get_subject(subject_fmt=subject_fmt, **kwargs)
|
||||||
|
|
||||||
def _get_recipients(self):
|
def _get_recipients(self):
|
||||||
if hasattr(self._recipient, 'get_full_name') and hasattr(self._recipient, 'email'):
|
if hasattr(self._recipient, 'get_full_name') and hasattr(self._recipient, 'email'):
|
||||||
r = u'"{fullname}" <{email}>'.format(fullname=self._recipient.get_full_name(),
|
r = '"{fullname}" <{email}>'.format(fullname=self._recipient.get_full_name(),
|
||||||
email=self._recipient.email)
|
email=self._recipient.email)
|
||||||
else:
|
else:
|
||||||
r = self._recipient
|
r = self._recipient
|
||||||
return [r]
|
return [r]
|
||||||
|
|
||||||
def _get_context_data(self, extra_context=None):
|
def _get_context_data(self, extra_context=None):
|
||||||
context = super(AbstractRegistrationMail, self)._get_context_data(extra_context=extra_context)
|
context = super()._get_context_data(extra_context=extra_context)
|
||||||
context['recipient'] = self._recipient
|
context['recipient'] = self._recipient
|
||||||
context['registration'] = self._registration
|
context['registration'] = self._registration
|
||||||
context['event'] = self._event
|
context['event'] = self._event
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class InformTrainerRegistrationMail(AbstractRegistrationMail):
|
class InformTrainerRegistrationMail(AbstractRegistrationMail): # pylint: disable=too-few-public-methods
|
||||||
_subject = u'Anmeldung {full_name}'
|
_subject = 'Anmeldung {full_name}'
|
||||||
_template_name = 'dav_registration/emails/inform_trainer.txt'
|
_template_name = 'dav_registration/emails/inform_trainer.txt'
|
||||||
|
|
||||||
def _get_subject(self, subject_fmt=None, **kwargs):
|
def _get_subject(self, subject_fmt=None, **kwargs):
|
||||||
kwargs['full_name'] = self._registration.get_full_name()
|
kwargs['full_name'] = self._registration.get_full_name()
|
||||||
return super(InformTrainerRegistrationMail, self)._get_subject(subject_fmt=subject_fmt, **kwargs)
|
return super()._get_subject(subject_fmt=subject_fmt, **kwargs)
|
||||||
|
|
||||||
def _get_reply_to(self):
|
def _get_reply_to(self):
|
||||||
s = u'"{fullname}" <{email}>'.format(fullname=self._registration.get_full_name(),
|
reply_to = '"{fullname}" <{email}>'.format(fullname=self._registration.get_full_name(),
|
||||||
email=self._registration.email_address)
|
email=self._registration.email_address)
|
||||||
return [s]
|
return [reply_to]
|
||||||
|
|
||||||
|
|
||||||
class InformSelfRegistrationMail(AbstractRegistrationMail):
|
class InformSelfRegistrationMail(AbstractRegistrationMail): # pylint: disable=too-few-public-methods
|
||||||
_subject = u'Deine Anmeldung'
|
_subject = 'Deine Anmeldung'
|
||||||
_template_name = 'dav_registration/emails/inform_self.txt'
|
_template_name = 'dav_registration/emails/inform_self.txt'
|
||||||
|
|
||||||
def _get_reply_to(self):
|
def _get_reply_to(self):
|
||||||
s = u'"{fullname}" <{email}>'.format(fullname=self._event.owner.get_full_name(),
|
reply_to = '"{fullname}" <{email}>'.format(fullname=self._event.owner.get_full_name(),
|
||||||
email=self._event.owner.email)
|
email=self._event.owner.email)
|
||||||
return [s]
|
return [reply_to]
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class RegistrationForm(forms.ModelForm):
|
class RegistrationForm(forms.ModelForm):
|
||||||
not_dav_member = forms.BooleanField(required=False,
|
not_dav_member = forms.BooleanField(required=False,
|
||||||
label=_(u'Ich bin noch kein DAV Mitglied.'),
|
label=_('Ich bin noch kein DAV Mitglied.'),
|
||||||
help_text=u'%s<br />\n%s' % (
|
help_text='%s<br />\n%s' % (
|
||||||
_(u'Wenn du noch kein DAV Mitglied bist,'
|
_('Wenn du noch kein DAV Mitglied bist,'
|
||||||
u' oder deine Aufnahme noch in Arbeit ist,'
|
' oder deine Aufnahme noch in Arbeit ist,'
|
||||||
u' kreuze dieses Feld hier an.'),
|
' kreuze dieses Feld hier an.'),
|
||||||
_(u'Spätestens zu Veranstaltungsbeginn muss'
|
_('Spätestens zu Veranstaltungsbeginn muss'
|
||||||
u' jedoch eine Mitgliedschaft bestehen.')
|
' jedoch eine Mitgliedschaft bestehen.')
|
||||||
))
|
))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -30,7 +30,7 @@ class RegistrationForm(forms.ModelForm):
|
|||||||
'note': forms.Textarea(attrs={'rows': 5}),
|
'note': forms.Textarea(attrs={'rows': 5}),
|
||||||
}
|
}
|
||||||
labels = {
|
labels = {
|
||||||
'apply_reduced_fee': _(u'Ich bin noch keine 25 Jahre alt oder besitze einen "Karlsruher Pass".'),
|
'apply_reduced_fee': _('Ich bin noch keine 25 Jahre alt oder besitze einen "Karlsruher Pass".'),
|
||||||
}
|
}
|
||||||
|
|
||||||
def clean_year_of_birth(self):
|
def clean_year_of_birth(self):
|
||||||
@@ -40,16 +40,16 @@ class RegistrationForm(forms.ModelForm):
|
|||||||
val = self.cleaned_data.get('year_of_birth')
|
val = self.cleaned_data.get('year_of_birth')
|
||||||
if val > year_now:
|
if val > year_now:
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
ugettext(u'Dein Geburtsjahr liegt in der Zukunft?'
|
ugettext('Dein Geburtsjahr liegt in der Zukunft?'
|
||||||
u' Das finden wir gut,'
|
' Das finden wir gut,'
|
||||||
u' aber bitte melde dich besser mal per E-Mail bei uns.'),
|
' aber bitte melde dich besser mal per E-Mail bei uns.'),
|
||||||
code='to_young',
|
code='to_young',
|
||||||
)
|
)
|
||||||
elif val < (year_now - max_age):
|
elif val < (year_now - max_age):
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
ugettext(u'Du bist schon über %(max_age)d Jahre alt?'
|
ugettext('Du bist schon über %(max_age)d Jahre alt?'
|
||||||
u' Das finden wir gut,'
|
' Das finden wir gut,'
|
||||||
u' aber bitte melde dich besser mal per E-Mail bei uns.'),
|
' aber bitte melde dich besser mal per E-Mail bei uns.'),
|
||||||
params={'max_age': max_age},
|
params={'max_age': max_age},
|
||||||
code='to_old',
|
code='to_old',
|
||||||
)
|
)
|
||||||
@@ -63,8 +63,8 @@ class RegistrationForm(forms.ModelForm):
|
|||||||
need_experience = False
|
need_experience = False
|
||||||
if need_experience:
|
if need_experience:
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
ugettext(u'Die Tourenleiter*innen brauchen ein paar Angaben,'
|
ugettext('Die Tourenleiter*innen brauchen ein paar Angaben,'
|
||||||
u' was du bereits kannst oder wie fit du bist.'),
|
' was du bereits kannst oder wie fit du bist.'),
|
||||||
code='need_experience',
|
code='need_experience',
|
||||||
)
|
)
|
||||||
return val
|
return val
|
||||||
@@ -73,18 +73,18 @@ class RegistrationForm(forms.ModelForm):
|
|||||||
val = self.cleaned_data.get('privacy_policy_accepted')
|
val = self.cleaned_data.get('privacy_policy_accepted')
|
||||||
if not val and self.instance.privacy_policy:
|
if not val and self.instance.privacy_policy:
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
ugettext(u'Wir müssen deine Daten leider speichern können,'
|
ugettext('Wir müssen deine Daten leider speichern können,'
|
||||||
u' damit wir wissen, dass du teilnehmen möchtest.'),
|
' damit wir wissen, dass du teilnehmen möchtest.'),
|
||||||
code='privacy_policy_not_accepted',
|
code='privacy_policy_not_accepted',
|
||||||
)
|
)
|
||||||
return val
|
return val
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
super(RegistrationForm, self).clean()
|
super().clean()
|
||||||
dav_member = self.cleaned_data.get('dav_member')
|
dav_member = self.cleaned_data.get('dav_member')
|
||||||
dav_number = self.cleaned_data.get('dav_number')
|
dav_number = self.cleaned_data.get('dav_number')
|
||||||
if dav_member and not dav_number:
|
if dav_member and not dav_number:
|
||||||
error_msg = ugettext(u'Wenn du DAV Mitglied bist, brauchen wir deine Mitgliedsnummer.')
|
error_msg = ugettext('Wenn du DAV Mitglied bist, brauchen wir deine Mitgliedsnummer.')
|
||||||
self.add_error('not_dav_member', error_msg)
|
self.add_error('not_dav_member', error_msg)
|
||||||
raise forms.ValidationError(error_msg, code='dav_number_missing')
|
raise forms.ValidationError(error_msg, code='dav_number_missing')
|
||||||
return self.cleaned_data
|
return self.cleaned_data
|
||||||
|
|||||||
30
dav_registration/migrations/0011_auto_20220607_1345.py
Normal file
30
dav_registration/migrations/0011_auto_20220607_1345.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# Generated by Django 3.2.13 on 2022-06-07 11:45
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('dav_events', '0042_auto_20220607_1345'),
|
||||||
|
('dav_registration', '0010_registration_apply_reduced_fee'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='registration',
|
||||||
|
name='event',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='registrations', to='dav_events.event'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='registration',
|
||||||
|
name='id',
|
||||||
|
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='registrationstatus',
|
||||||
|
name='id',
|
||||||
|
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 3.2.13 on 2023-02-15 16:56
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('dav_registration', '0011_auto_20220607_1345'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='registrationstatus',
|
||||||
|
name='accepted',
|
||||||
|
field=models.BooleanField(blank=True, null=True, verbose_name='Zusage erteilt'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -6,7 +6,6 @@ from django.core.exceptions import ValidationError
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from dav_base.validators import DAVNumberValidator
|
from dav_base.validators import DAVNumberValidator
|
||||||
@@ -19,9 +18,8 @@ logger = logging.getLogger(__name__)
|
|||||||
midnight = datetime.time(00, 00, 00)
|
midnight = datetime.time(00, 00, 00)
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class Registration(models.Model):
|
class Registration(models.Model):
|
||||||
event = models.ForeignKey(Event, related_name='registrations')
|
event = models.ForeignKey(Event, related_name='registrations', on_delete=models.PROTECT)
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
personal_names = models.CharField(max_length=1024,
|
personal_names = models.CharField(max_length=1024,
|
||||||
@@ -184,7 +182,7 @@ Anmerkung:
|
|||||||
self.purge_at = self.__class__.calc_purge_at(self.event)
|
self.purge_at = self.__class__.calc_purge_at(self.event)
|
||||||
|
|
||||||
self.full_clean()
|
self.full_clean()
|
||||||
super(Registration, self).save(**kwargs)
|
super().save(**kwargs)
|
||||||
|
|
||||||
if creating:
|
if creating:
|
||||||
status = RegistrationStatus(registration=self)
|
status = RegistrationStatus(registration=self)
|
||||||
@@ -219,12 +217,11 @@ Anmerkung:
|
|||||||
return timezone.make_aware(datetime.datetime.combine(purge_date, midnight))
|
return timezone.make_aware(datetime.datetime.combine(purge_date, midnight))
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class RegistrationStatus(models.Model):
|
class RegistrationStatus(models.Model):
|
||||||
registration = models.OneToOneField(Registration, on_delete=models.CASCADE, related_name='status')
|
registration = models.OneToOneField(Registration, on_delete=models.CASCADE, related_name='status')
|
||||||
updated_at = models.DateTimeField(auto_now=True)
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
answered = models.BooleanField(_('Durch Tourleitung beantwortet'), default=False)
|
answered = models.BooleanField(_('Durch Tourleitung beantwortet'), default=False)
|
||||||
accepted = models.NullBooleanField(_('Zusage erteilt'))
|
accepted = models.BooleanField(_('Zusage erteilt'), null=True, blank=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('Anmeldungsstatus')
|
verbose_name = _('Anmeldungsstatus')
|
||||||
@@ -240,7 +237,7 @@ class RegistrationStatus(models.Model):
|
|||||||
|
|
||||||
def save(self, **kwargs):
|
def save(self, **kwargs):
|
||||||
self.full_clean()
|
self.full_clean()
|
||||||
super(RegistrationStatus, self).save(**kwargs)
|
super().save(**kwargs)
|
||||||
|
|
||||||
def set_accepted(self):
|
def set_accepted(self):
|
||||||
self.accepted = True
|
self.accepted = True
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from . import emails
|
|||||||
registration_created = Signal(providing_args=['registration'])
|
registration_created = Signal(providing_args=['registration'])
|
||||||
|
|
||||||
|
|
||||||
def send_emails_on_registration(sender, **kwargs):
|
def send_emails_on_registration(sender, **kwargs): # pylint: disable=unused-argument
|
||||||
registration = kwargs.get('registration')
|
registration = kwargs.get('registration')
|
||||||
|
|
||||||
# Inform the event owner (trainer)
|
# Inform the event owner (trainer)
|
||||||
@@ -14,7 +14,7 @@ def send_emails_on_registration(sender, **kwargs):
|
|||||||
email.send()
|
email.send()
|
||||||
|
|
||||||
# Inform the potential participant
|
# Inform the potential participant
|
||||||
recipient = u'"{fullname}" <{email}>'.format(fullname=registration.get_full_name(),
|
recipient = '"{fullname}" <{email}>'.format(fullname=registration.get_full_name(),
|
||||||
email=registration.email_address)
|
email=registration.email_address)
|
||||||
email = emails.InformSelfRegistrationMail(recipient=recipient, registration=registration)
|
email = emails.InformSelfRegistrationMail(recipient=recipient, registration=registration)
|
||||||
email.send()
|
email.send()
|
||||||
|
|||||||
@@ -12,5 +12,3 @@ def render_event_paragraphs(event):
|
|||||||
@register.simple_tag
|
@register.simple_tag
|
||||||
def render_event_facts(event):
|
def render_event_facts(event):
|
||||||
return render_to_string('dav_registration/event/facts.html', {'event': event})
|
return render_to_string('dav_registration/event/facts.html', {'event': event})
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from ..models import Registration
|
|||||||
THIS_YEAR = timezone.now().year
|
THIS_YEAR = timezone.now().year
|
||||||
|
|
||||||
|
|
||||||
class RegistrationMixin(object):
|
class RegistrationMixin: # pylint: disable=too-few-public-methods
|
||||||
def create_registration(self, data):
|
def create_registration(self, data):
|
||||||
r = Registration(**data)
|
r = Registration(**data)
|
||||||
r.save()
|
r.save()
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ Zeitpunkt der Datenlöschung: {purge_at}
|
|||||||
|
|
||||||
class EmailsTestCase(EmailTestMixin, EventMixin, RegistrationMixin, TestCase):
|
class EmailsTestCase(EmailTestMixin, EventMixin, RegistrationMixin, TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(EmailsTestCase, self).setUp()
|
super().setUp()
|
||||||
|
|
||||||
app_config = apps.get_app_config('dav_events')
|
app_config = apps.get_app_config('dav_events')
|
||||||
app_config.settings.enable_email_on_status_update = False
|
app_config.settings.enable_email_on_status_update = False
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from .generic import THIS_YEAR, RegistrationMixin
|
|||||||
|
|
||||||
class RegistrationTestCase(EventMixin, RegistrationMixin, TestCase):
|
class RegistrationTestCase(EventMixin, RegistrationMixin, TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(RegistrationTestCase, self).setUp()
|
super().setUp()
|
||||||
|
|
||||||
app_config = apps.get_app_config('dav_events')
|
app_config = apps.get_app_config('dav_events')
|
||||||
app_config.settings.enable_email_on_status_update = False
|
app_config.settings.enable_email_on_status_update = False
|
||||||
@@ -100,4 +100,3 @@ class RegistrationTestCase(EventMixin, RegistrationMixin, TestCase):
|
|||||||
'dav_member': False,
|
'dav_member': False,
|
||||||
}
|
}
|
||||||
self.create_registration(registration_data)
|
self.create_registration(registration_data)
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class UtilsTestCase(RegistrationMixin, EventMixin, TestCase):
|
|||||||
self.submit_event(event)
|
self.submit_event(event)
|
||||||
self.accept_event(event)
|
self.accept_event(event)
|
||||||
|
|
||||||
for i in range(0, registrations_per_event):
|
for _ in range(0, registrations_per_event):
|
||||||
d = registration_data
|
d = registration_data
|
||||||
d['event'] = event
|
d['event'] = event
|
||||||
self.create_registration(d)
|
self.create_registration(d)
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ from django.conf.urls import url
|
|||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
|
app_name = 'dav_registration'
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^$', views.RootView.as_view(), name='root'),
|
url(r'^$', views.RootView.as_view(), name='root'),
|
||||||
url(r'^finished', views.RegistrationSuccessView.as_view(), name='registered'),
|
url(r'^finished', views.RegistrationSuccessView.as_view(), name='registered'),
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class RootView(generic.RedirectView):
|
|||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
purge_registrations()
|
purge_registrations()
|
||||||
return super(RootView, self).get(request, *args, **kwargs)
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class EventListView(generic.ListView):
|
class EventListView(generic.ListView):
|
||||||
@@ -33,14 +33,14 @@ class EventListView(generic.ListView):
|
|||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
today = datetime.date.today()
|
today = datetime.date.today()
|
||||||
|
|
||||||
filter = Q(flags__status__code__in=('publishing', 'publishing_web', 'publishing_facebook',
|
filter_exp = Q(flags__status__code__in=('publishing', 'publishing_web', 'publishing_facebook',
|
||||||
'published', 'published_web', 'published_facebook'))
|
'published', 'published_web', 'published_facebook'))
|
||||||
filter &= Q(planned_publication_date__isnull=True) | Q(planned_publication_date__lte=today)
|
filter_exp &= Q(planned_publication_date__isnull=True) | Q(planned_publication_date__lte=today)
|
||||||
filter &= Q(first_day__gte=today)
|
filter_exp &= Q(first_day__gte=today)
|
||||||
# filter &= Q(registration_closed=False)
|
# filter_exp &= Q(registration_closed=False)
|
||||||
# filter &= Q(deadline__isnull=True) | Q(deadline__gte=today)
|
# filter_exp &= Q(deadline__isnull=True) | Q(deadline__gte=today)
|
||||||
|
|
||||||
qs = self.model.objects.filter(filter).order_by('first_day', 'number').distinct()
|
qs = self.model.objects.filter(filter_exp).order_by('first_day', 'number').distinct()
|
||||||
|
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
@@ -52,14 +52,14 @@ class EventDetailView(generic.DetailView):
|
|||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
today = datetime.date.today()
|
today = datetime.date.today()
|
||||||
|
|
||||||
filter = Q(flags__status__code__in=('publishing', 'publishing_web', 'publishing_facebook',
|
filter_exp = Q(flags__status__code__in=('publishing', 'publishing_web', 'publishing_facebook',
|
||||||
'published', 'published_web', 'published_facebook'))
|
'published', 'published_web', 'published_facebook'))
|
||||||
filter &= Q(planned_publication_date__isnull=True) | Q(planned_publication_date__lte=today)
|
filter_exp &= Q(planned_publication_date__isnull=True) | Q(planned_publication_date__lte=today)
|
||||||
filter &= Q(first_day__gte=today)
|
filter_exp &= Q(first_day__gte=today)
|
||||||
# filter &= Q(registration_closed=False)
|
# filter_exp &= Q(registration_closed=False)
|
||||||
# filter &= Q(deadline__isnull=True) | Q(deadline__gte=today)
|
# filter_exp &= Q(deadline__isnull=True) | Q(deadline__gte=today)
|
||||||
|
|
||||||
qs = self.model.objects.filter(filter).distinct()
|
qs = self.model.objects.filter(filter_exp).distinct()
|
||||||
|
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
@@ -74,72 +74,72 @@ class RegistrationView(generic.CreateView):
|
|||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
today = datetime.date.today()
|
today = datetime.date.today()
|
||||||
|
|
||||||
filter = Q(flags__status__code__in=('publishing', 'publishing_web', 'publishing_facebook',
|
filter_exp = Q(flags__status__code__in=('publishing', 'publishing_web', 'publishing_facebook',
|
||||||
'published', 'published_web', 'published_facebook'))
|
'published', 'published_web', 'published_facebook'))
|
||||||
filter &= Q(planned_publication_date__isnull=True) | Q(planned_publication_date__lte=today)
|
filter_exp &= Q(planned_publication_date__isnull=True) | Q(planned_publication_date__lte=today)
|
||||||
filter &= Q(first_day__gte=today)
|
filter_exp &= Q(first_day__gte=today)
|
||||||
filter &= Q(registration_closed=False)
|
filter_exp &= Q(registration_closed=False)
|
||||||
filter &= Q(deadline__isnull=True) | Q(deadline__gte=today)
|
filter_exp &= Q(deadline__isnull=True) | Q(deadline__gte=today)
|
||||||
|
|
||||||
qs = Event.objects.filter(filter).distinct()
|
qs = Event.objects.filter(filter_exp).distinct()
|
||||||
|
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
initials = super(RegistrationView, self).get_initial()
|
initials = super().get_initial()
|
||||||
return initials
|
return initials
|
||||||
|
|
||||||
def get_form(self, form_class=None):
|
def get_form(self, form_class=None):
|
||||||
form = super(RegistrationView, self).get_form(form_class)
|
form = super().get_form(form_class)
|
||||||
|
|
||||||
event = self.get_object()
|
event = self.get_object()
|
||||||
|
|
||||||
experience_label = form.fields['experience'].label
|
experience_label = form.fields['experience'].label
|
||||||
experience_help_text = form.fields['experience'].help_text
|
experience_help_text = form.fields['experience'].help_text
|
||||||
if event.sport == 'B':
|
if event.sport == 'B':
|
||||||
experience_label = _(u'Eigene Bergerfahrung')
|
experience_label = _('Eigene Bergerfahrung')
|
||||||
elif event.sport == 'K' and event.terrain == 'alpine':
|
elif event.sport == 'K' and event.terrain == 'alpine':
|
||||||
experience_label = _(u'Eigene Fels- und Bergerfahrung')
|
experience_label = _('Eigene Fels- und Bergerfahrung')
|
||||||
if event.level == 'beginner':
|
if event.level == 'beginner':
|
||||||
experience_help_text = u'%s<br /> %s<br /> %s' % (
|
experience_help_text = '%s<br /> %s<br /> %s' % (
|
||||||
_(u'Warst du schon mal im Gebirge klettern?'),
|
_('Warst du schon mal im Gebirge klettern?'),
|
||||||
_(u'In welchem Schwierigkeitsgrad hast du Spaß?'),
|
_('In welchem Schwierigkeitsgrad hast du Spaß?'),
|
||||||
_(u'Bist du schon mal vorgestiegen?')
|
_('Bist du schon mal vorgestiegen?')
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
experience_help_text = u'%s<br /> %s<br /> %s' % (
|
experience_help_text = '%s<br /> %s<br /> %s' % (
|
||||||
_(u'In welchen Klettergebieten/Touren warst du bisher unterwegs?'),
|
_('In welchen Klettergebieten/Touren warst du bisher unterwegs?'),
|
||||||
_(u'In welchem Schwierigkeitsgrad hast du im Vorstieg Spaß?'),
|
_('In welchem Schwierigkeitsgrad hast du im Vorstieg Spaß?'),
|
||||||
_(u'Wie waren die Touren abgesichert, in denen du dich noch wohlgefühlt hast?')
|
_('Wie waren die Touren abgesichert, in denen du dich noch wohlgefühlt hast?')
|
||||||
)
|
)
|
||||||
elif event.sport == 'K':
|
elif event.sport == 'K':
|
||||||
experience_label = _(u'Eigene Klettererfahrung')
|
experience_label = _('Eigene Klettererfahrung')
|
||||||
if event.level == 'beginner':
|
if event.level == 'beginner':
|
||||||
experience_help_text = u'%s<br /> %s<br /> %s' % (
|
experience_help_text = '%s<br /> %s<br /> %s' % (
|
||||||
_(u'Warst du schon mal am Fels klettern?'),
|
_('Warst du schon mal am Fels klettern?'),
|
||||||
_(u'In welchem Schwierigkeitsgrad hast du Spaß?'),
|
_('In welchem Schwierigkeitsgrad hast du Spaß?'),
|
||||||
_(u'Bist du schon mal vorgestiegen?')
|
_('Bist du schon mal vorgestiegen?')
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
experience_help_text = u'%s<br /> %s<br /> %s' % (
|
experience_help_text = '%s<br /> %s<br /> %s' % (
|
||||||
_(u'In welchen Klettergebieten warst du bisher unterwegs?'),
|
_('In welchen Klettergebieten warst du bisher unterwegs?'),
|
||||||
_(u'In welchem Schwierigkeitsgrad hast du im Vorstieg Spaß?'),
|
_('In welchem Schwierigkeitsgrad hast du im Vorstieg Spaß?'),
|
||||||
_(u'Wie waren die Touren abgesichert, in denen du dich noch wohlgefühlt hast?')
|
_('Wie waren die Touren abgesichert, in denen du dich noch wohlgefühlt hast?')
|
||||||
)
|
)
|
||||||
elif event.sport == 'M':
|
elif event.sport == 'M':
|
||||||
experience_label = _(u'Eigene MTB-Erfahrung')
|
experience_label = _('Eigene MTB-Erfahrung')
|
||||||
experience_help_text = u'%s' % (
|
experience_help_text = '%s' % (
|
||||||
_(u'Was für Touren bist du schon gefahren?')
|
_('Was für Touren bist du schon gefahren?')
|
||||||
)
|
)
|
||||||
if event.level != 'beginner':
|
if event.level != 'beginner':
|
||||||
experience_help_text += u'<br /> %s' % (
|
experience_help_text += '<br /> %s' % (
|
||||||
_(u'Single-Trail-Schwierigkeit (S0-S5) bei der du dich wohl fühlst?')
|
_('Single-Trail-Schwierigkeit (S0-S5) bei der du dich wohl fühlst?')
|
||||||
)
|
)
|
||||||
elif event.sport == 'S':
|
elif event.sport == 'S':
|
||||||
experience_label = _(u'Eigene Skitouren- und Bergerfahrung')
|
experience_label = _('Eigene Skitouren- und Bergerfahrung')
|
||||||
elif event.sport == 'W':
|
elif event.sport == 'W':
|
||||||
experience_help_text += u'<br /> %s' % (
|
experience_help_text += '<br /> %s' % (
|
||||||
_(u'Kann frei gelassen werden.')
|
_('Kann frei gelassen werden.')
|
||||||
)
|
)
|
||||||
|
|
||||||
form.fields['experience'].label = experience_label
|
form.fields['experience'].label = experience_label
|
||||||
@@ -150,7 +150,7 @@ class RegistrationView(generic.CreateView):
|
|||||||
return form
|
return form
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(RegistrationView, self).get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
event = self.get_object()
|
event = self.get_object()
|
||||||
context['event'] = event
|
context['event'] = event
|
||||||
context['privacy_policy'] = app_config.settings.privacy_policy
|
context['privacy_policy'] = app_config.settings.privacy_policy
|
||||||
@@ -158,16 +158,16 @@ class RegistrationView(generic.CreateView):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
r = super(RegistrationView, self).form_valid(form)
|
r = super().form_valid(form)
|
||||||
self.request.session['registration_id'] = form.instance.pk
|
self.request.session['registration_id'] = form.instance.pk
|
||||||
message = _(u'Deine Anmeldung wurde erfolgreich gespeichert.')
|
message = _('Deine Anmeldung wurde erfolgreich gespeichert.')
|
||||||
messages.success(self.request, message)
|
messages.success(self.request, message)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
if 'registration_id' in request.session:
|
if 'registration_id' in request.session:
|
||||||
del request.session['registration_id']
|
del request.session['registration_id']
|
||||||
return super(RegistrationView, self).post(request, *args, **kwargs)
|
return super().post(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class RegistrationSuccessView(generic.DetailView):
|
class RegistrationSuccessView(generic.DetailView):
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
default_app_config = 'dav_submission.apps.AppConfig'
|
default_app_config = 'dav_submission.apps.AppConfig' # pylint: disable=invalid-name
|
||||||
|
|||||||
@@ -19,5 +19,5 @@ DEFAULT_SETTINGS = (
|
|||||||
|
|
||||||
class AppConfig(_AppConfig):
|
class AppConfig(_AppConfig):
|
||||||
name = 'dav_submission'
|
name = 'dav_submission'
|
||||||
verbose_name = u'DAV Beitragsupload (150 Jahre DAV)'
|
verbose_name = 'DAV Beitragsupload (150 Jahre DAV)'
|
||||||
default_settings = DEFAULT_SETTINGS
|
default_settings = DEFAULT_SETTINGS
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ from dav_base.emails import AbstractMail
|
|||||||
app_config = apps.get_containing_app_config(__package__)
|
app_config = apps.get_containing_app_config(__package__)
|
||||||
|
|
||||||
|
|
||||||
class NewSubmissionMail(AbstractMail):
|
class NewSubmissionMail(AbstractMail): # pylint: disable=too-few-public-methods
|
||||||
_subject = u'Neuer Beitrag: {title}'
|
_subject = 'Neuer Beitrag: {title}'
|
||||||
_template_name = 'dav_submission/emails/new_submission.txt'
|
_template_name = 'dav_submission/emails/new_submission.txt'
|
||||||
|
|
||||||
def __init__(self, metadata):
|
def __init__(self, metadata):
|
||||||
@@ -15,18 +15,18 @@ class NewSubmissionMail(AbstractMail):
|
|||||||
|
|
||||||
def _get_subject(self, subject_fmt=None, **kwargs):
|
def _get_subject(self, subject_fmt=None, **kwargs):
|
||||||
kwargs['title'] = self._metadata['title']
|
kwargs['title'] = self._metadata['title']
|
||||||
return super(NewSubmissionMail, self)._get_subject(subject_fmt=subject_fmt, **kwargs)
|
return super()._get_subject(subject_fmt=subject_fmt, **kwargs)
|
||||||
|
|
||||||
def _get_reply_to(self):
|
def _get_reply_to(self):
|
||||||
s = u'"{fullname}" <{email}>'.format(fullname=self._metadata['name'],
|
reply_to = '"{fullname}" <{email}>'.format(fullname=self._metadata['name'],
|
||||||
email=self._metadata['email_address'])
|
email=self._metadata['email_address'])
|
||||||
return [s]
|
return [reply_to]
|
||||||
|
|
||||||
def _get_recipients(self):
|
def _get_recipients(self):
|
||||||
r = app_config.settings.notify_address
|
r = app_config.settings.notify_address
|
||||||
return [r]
|
return [r]
|
||||||
|
|
||||||
def _get_context_data(self, extra_context=None):
|
def _get_context_data(self, extra_context=None):
|
||||||
context = super(NewSubmissionMail, self)._get_context_data(extra_context=extra_context)
|
context = super()._get_context_data(extra_context=extra_context)
|
||||||
context['metadata'] = self._metadata
|
context['metadata'] = self._metadata
|
||||||
return context
|
return context
|
||||||
|
|||||||
@@ -8,58 +8,58 @@ app_config = apps.get_containing_app_config(__package__)
|
|||||||
|
|
||||||
class UploadForm(forms.Form):
|
class UploadForm(forms.Form):
|
||||||
name = forms.CharField(max_length=1024,
|
name = forms.CharField(max_length=1024,
|
||||||
label=_(u'Dein Name'),
|
label=_('Dein Name'),
|
||||||
help_text=_(u'Wenn wir wissen, wie du heißt, wird uns das echt weiter helfen'))
|
help_text=_('Wenn wir wissen, wie du heißt, wird uns das echt weiter helfen'))
|
||||||
email_address = forms.EmailField(label=_(u'Deine E-Mail-Adresse'),
|
email_address = forms.EmailField(label=_('Deine E-Mail-Adresse'),
|
||||||
help_text=_(u'Damit wir dich für Rückfragen kontaktieren können'))
|
help_text=_('Damit wir dich für Rückfragen kontaktieren können'))
|
||||||
|
|
||||||
group = forms.CharField(max_length=1024,
|
group = forms.CharField(max_length=1024,
|
||||||
required=False,
|
required=False,
|
||||||
label=_(u'DAV Gruppe'),
|
label=_('DAV Gruppe'),
|
||||||
help_text=_(u'Optional, falls du aktiv in einer der Sektionsgruppen bist'))
|
help_text=_('Optional, falls du aktiv in einer der Sektionsgruppen bist'))
|
||||||
|
|
||||||
title = forms.CharField(max_length=60,
|
title = forms.CharField(max_length=60,
|
||||||
label=_(u'Titel deines Beitrags / Stichwort'),
|
label=_('Titel deines Beitrags / Stichwort'),
|
||||||
help_text=u'%s<br />\n%s' % (
|
help_text='%s<br />\n%s' % (
|
||||||
_(u'Kommt zum Bild, falls es veröffentlicht wird'),
|
_('Kommt zum Bild, falls es veröffentlicht wird'),
|
||||||
_(u'Maximal 60 Zeichen')
|
_('Maximal 60 Zeichen')
|
||||||
))
|
))
|
||||||
|
|
||||||
description = forms.CharField(max_length=300,
|
description = forms.CharField(max_length=300,
|
||||||
label=_(u'Beschreibung'),
|
label=_('Beschreibung'),
|
||||||
help_text=u'%s<br />\n%s' % (
|
help_text='%s<br />\n%s' % (
|
||||||
_(u'Wo warst du? Was hast du gemacht? Worum ging es bei der Aktion?'),
|
_('Wo warst du? Was hast du gemacht? Worum ging es bei der Aktion?'),
|
||||||
_(u'Maximal 300 Zeichen')
|
_('Maximal 300 Zeichen')
|
||||||
),
|
),
|
||||||
widget=forms.Textarea(attrs={'rows': 2}))
|
widget=forms.Textarea(attrs={'rows': 2}))
|
||||||
|
|
||||||
files = forms.FileField(label=_(u'Dateien'),
|
files = forms.FileField(label=_('Dateien'),
|
||||||
help_text=_(u'Wenn du auf den Button klickst, kannst du mehrere Dateien auswählen'
|
help_text=_('Wenn du auf den Button klickst, kannst du mehrere Dateien auswählen'
|
||||||
u' (nötigenfalls Strg- oder Command-Taste benutzen)'),
|
' (nötigenfalls Strg- oder Command-Taste benutzen)'),
|
||||||
widget=forms.ClearableFileInput(attrs={'multiple': True}))
|
widget=forms.ClearableFileInput(attrs={'multiple': True}))
|
||||||
|
|
||||||
accepted = forms.BooleanField(required=False,
|
accepted = forms.BooleanField(required=False,
|
||||||
label=_(u'Ja, ich stimme den Teilnahmebedingungen zu!'))
|
label=_('Ja, ich stimme den Teilnahmebedingungen zu!'))
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(UploadForm, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.fields['title'].widget.attrs['placeholder'] = \
|
self.fields['title'].widget.attrs['placeholder'] = \
|
||||||
u'z.B. Nacktbesteigung der Nose' \
|
'z.B. Nacktbesteigung der Nose' \
|
||||||
u' oder Juma jümart Jung-Mann-Weg'[:self.fields['title'].max_length]
|
' oder Juma jümart Jung-Mann-Weg'[:self.fields['title'].max_length]
|
||||||
|
|
||||||
self.fields['group'].widget.attrs['placeholder'] = \
|
self.fields['group'].widget.attrs['placeholder'] = \
|
||||||
ugettext(u'Kann frei gelassen werden')[:self.fields['title'].max_length]
|
ugettext('Kann frei gelassen werden')[:self.fields['title'].max_length]
|
||||||
|
|
||||||
help_text = self.fields['files'].help_text
|
help_text = self.fields['files'].help_text
|
||||||
if app_config.settings.max_files:
|
if app_config.settings.max_files:
|
||||||
help_text += u'<br />\n%s' % (ugettext(u'Wähle bis zu %d Dateien aus')
|
help_text += '<br />\n%s' % (ugettext('Wähle bis zu %d Dateien aus')
|
||||||
% app_config.settings.max_files)
|
% app_config.settings.max_files)
|
||||||
if app_config.settings.max_file_size_mib:
|
if app_config.settings.max_file_size_mib:
|
||||||
help_text += u'<br />\n%s' % (ugettext(u'Einzelne Dateien dürfen maximal %d MiB groß sein')
|
help_text += '<br />\n%s' % (ugettext('Einzelne Dateien dürfen maximal %d MiB groß sein')
|
||||||
% app_config.settings.max_file_size_mib)
|
% app_config.settings.max_file_size_mib)
|
||||||
if app_config.settings.max_total_size_mib:
|
if app_config.settings.max_total_size_mib:
|
||||||
help_text += u'<br />\n%s' % (ugettext(u'Alle Dateien zusammen dürfen maximal %d MiB groß sein')
|
help_text += '<br />\n%s' % (ugettext('Alle Dateien zusammen dürfen maximal %d MiB groß sein')
|
||||||
% app_config.settings.max_total_size_mib)
|
% app_config.settings.max_total_size_mib)
|
||||||
self.fields['files'].help_text = help_text
|
self.fields['files'].help_text = help_text
|
||||||
|
|
||||||
@@ -77,35 +77,35 @@ class UploadForm(forms.Form):
|
|||||||
n_files = 0
|
n_files = 0
|
||||||
for file in files:
|
for file in files:
|
||||||
if file.name in not_allowed_file_names:
|
if file.name in not_allowed_file_names:
|
||||||
ve = forms.ValidationError(
|
e = forms.ValidationError(
|
||||||
ugettext(u'Dateiname nicht erlaubt: %s') % file.name,
|
ugettext('Dateiname nicht erlaubt: %s') % file.name,
|
||||||
code='filename_not_allowed',
|
code='filename_not_allowed',
|
||||||
)
|
)
|
||||||
validation_errors.append(ve)
|
validation_errors.append(e)
|
||||||
if max_file_size and file.size > max_file_size:
|
if max_file_size and file.size > max_file_size:
|
||||||
ve = forms.ValidationError(
|
e = forms.ValidationError(
|
||||||
ugettext(u'Die Datei ist insgesamt zu groß:'
|
ugettext('Die Datei ist insgesamt zu groß:'
|
||||||
u' %(name)s (> %(max_mib)s MiB)') % {'name': file.name, 'max_mib': max_file_size_mib},
|
' %(name)s (> %(max_mib)s MiB)') % {'name': file.name, 'max_mib': max_file_size_mib},
|
||||||
code='file_to_big',
|
code='file_to_big',
|
||||||
)
|
)
|
||||||
validation_errors.append(ve)
|
validation_errors.append(e)
|
||||||
size_total += file.size
|
size_total += file.size
|
||||||
n_files += 1
|
n_files += 1
|
||||||
|
|
||||||
max_total_size = max_total_size_mib * 1024 * 1024
|
max_total_size = max_total_size_mib * 1024 * 1024
|
||||||
if max_total_size and size_total > max_total_size:
|
if max_total_size and size_total > max_total_size:
|
||||||
ve = forms.ValidationError(
|
e = forms.ValidationError(
|
||||||
ugettext(u'Dein Beitrag ist zu groß (%s MiB)') % max_total_size_mib,
|
ugettext('Dein Beitrag ist zu groß (%s MiB)') % max_total_size_mib,
|
||||||
code='files_to_big',
|
code='files_to_big',
|
||||||
)
|
)
|
||||||
validation_errors.append(ve)
|
validation_errors.append(e)
|
||||||
|
|
||||||
if max_files and n_files > max_files:
|
if max_files and n_files > max_files:
|
||||||
ve = forms.ValidationError(
|
e = forms.ValidationError(
|
||||||
ugettext(u'Dein Beitrag enthält mehr als %d Dateien') % max_files,
|
ugettext('Dein Beitrag enthält mehr als %d Dateien') % max_files,
|
||||||
code='files_to_big',
|
code='files_to_big',
|
||||||
)
|
)
|
||||||
validation_errors.append(ve)
|
validation_errors.append(e)
|
||||||
|
|
||||||
if validation_errors:
|
if validation_errors:
|
||||||
raise forms.ValidationError(validation_errors)
|
raise forms.ValidationError(validation_errors)
|
||||||
@@ -116,9 +116,9 @@ class UploadForm(forms.Form):
|
|||||||
val = self.cleaned_data.get('accepted')
|
val = self.cleaned_data.get('accepted')
|
||||||
if not val:
|
if not val:
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
ugettext(u'Um deinen Beitrag hochladen zu können,'
|
ugettext('Um deinen Beitrag hochladen zu können,'
|
||||||
u' musst du den Teilnahmebedingungen zustimmen.'
|
' musst du den Teilnahmebedingungen zustimmen.'
|
||||||
u' Dazu musst du das Kästchen ankreuzen!'),
|
' Dazu musst du das Kästchen ankreuzen!'),
|
||||||
code='not_accepted',
|
code='not_accepted',
|
||||||
)
|
)
|
||||||
return val
|
return val
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ from django.conf.urls import url
|
|||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
|
app_name = 'dav_submission'
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^$', views.UploadView.as_view(), name='root'),
|
url(r'^$', views.UploadView.as_view(), name='root'),
|
||||||
url(r'^danke', views.SuccessView.as_view(), name='success'),
|
url(r'^danke', views.SuccessView.as_view(), name='success'),
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ import codecs
|
|||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import pytz
|
|
||||||
import re
|
import re
|
||||||
import urllib
|
import urllib
|
||||||
import zipfile
|
import zipfile
|
||||||
|
import pytz
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
@@ -90,7 +90,7 @@ class ListView(generic.ListView):
|
|||||||
permission_group = app_config.settings.download_group
|
permission_group = app_config.settings.download_group
|
||||||
if not request.user.groups.filter(name=permission_group).exists():
|
if not request.user.groups.filter(name=permission_group).exists():
|
||||||
raise PermissionDenied()
|
raise PermissionDenied()
|
||||||
return super(ListView, self).dispatch(request, *args, **kwargs)
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class DownloadView(generic.DetailView):
|
class DownloadView(generic.DetailView):
|
||||||
@@ -139,7 +139,7 @@ class DownloadView(generic.DetailView):
|
|||||||
permission_group = app_config.settings.download_group
|
permission_group = app_config.settings.download_group
|
||||||
if not request.user.groups.filter(name=permission_group).exists():
|
if not request.user.groups.filter(name=permission_group).exists():
|
||||||
raise PermissionDenied()
|
raise PermissionDenied()
|
||||||
return super(DownloadView, self).dispatch(request, *args, **kwargs)
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class UploadView(generic.edit.FormView):
|
class UploadView(generic.edit.FormView):
|
||||||
@@ -155,30 +155,30 @@ class UploadView(generic.edit.FormView):
|
|||||||
form_class = UploadForm
|
form_class = UploadForm
|
||||||
success_url = reverse_lazy('dav_submission:success')
|
success_url = reverse_lazy('dav_submission:success')
|
||||||
|
|
||||||
def _sanitize_filename(self, input):
|
def _sanitize_filename(self, filename):
|
||||||
max_length = None
|
max_length = None
|
||||||
discard_chars = u''
|
discard_chars = ''
|
||||||
replace_chars = {
|
replace_chars = {
|
||||||
u'ä': u'ae',
|
'ä': 'ae',
|
||||||
u'ö': u'oe',
|
'ö': 'oe',
|
||||||
u'ü': u'ue',
|
'ü': 'ue',
|
||||||
u'ß': u'ss',
|
'ß': 'ss',
|
||||||
u'Ä': u'Ae',
|
'Ä': 'Ae',
|
||||||
u'Ö': u'Oe',
|
'Ö': 'Oe',
|
||||||
u'Ü': u'Ue',
|
'Ü': 'Ue',
|
||||||
}
|
}
|
||||||
allowed_chars = (u'abcdefghijklmnopqrstuvwxyz'
|
allowed_chars = ('abcdefghijklmnopqrstuvwxyz'
|
||||||
u'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||||
u'0123456789'
|
'0123456789'
|
||||||
u'._-')
|
'._-')
|
||||||
non_allowed_substitute = u'_'
|
non_allowed_substitute = '_'
|
||||||
space_substitute = u'_'
|
space_substitute = '_'
|
||||||
|
|
||||||
if space_substitute is None:
|
if space_substitute is None:
|
||||||
space_substitute = non_allowed_substitute
|
space_substitute = non_allowed_substitute
|
||||||
|
|
||||||
r = ''
|
r = ''
|
||||||
for c in input:
|
for c in filename:
|
||||||
if c in discard_chars:
|
if c in discard_chars:
|
||||||
continue
|
continue
|
||||||
elif c in replace_chars:
|
elif c in replace_chars:
|
||||||
@@ -195,14 +195,14 @@ class UploadView(generic.edit.FormView):
|
|||||||
return r[:max_length]
|
return r[:max_length]
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
c = super(UploadView, self).get_context_data(**kwargs)
|
c = super().get_context_data(**kwargs)
|
||||||
c['show_upload_form'] = app_config.settings.enable_upload
|
c['show_upload_form'] = app_config.settings.enable_upload
|
||||||
return c
|
return c
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
base_path = app_config.settings.upload_path
|
base_path = app_config.settings.upload_path
|
||||||
|
|
||||||
subdir_format_str = u'{datetime}--{title}'
|
subdir_format_str = '{datetime}--{title}'
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
subdir_format_kwargs = {'datetime': now.strftime('%Y-%m-%d--%H%M%S'),
|
subdir_format_kwargs = {'datetime': now.strftime('%Y-%m-%d--%H%M%S'),
|
||||||
'date': now.strftime('%Y-%m-%d'),
|
'date': now.strftime('%Y-%m-%d'),
|
||||||
@@ -214,7 +214,7 @@ class UploadView(generic.edit.FormView):
|
|||||||
subdir_path = os.path.join(base_path, subdir_name)
|
subdir_path = os.path.join(base_path, subdir_name)
|
||||||
|
|
||||||
if os.path.isdir(subdir_path):
|
if os.path.isdir(subdir_path):
|
||||||
message = _(u'Es gibt bereits einen Beitrag mit dem Titel "%(title)s".') % subdir_format_kwargs
|
message = _('Es gibt bereits einen Beitrag mit dem Titel "%(title)s".') % subdir_format_kwargs
|
||||||
messages.error(self.request, message)
|
messages.error(self.request, message)
|
||||||
form.add_error('title', message)
|
form.add_error('title', message)
|
||||||
return self.render_to_response(self.get_context_data(form=form))
|
return self.render_to_response(self.get_context_data(form=form))
|
||||||
@@ -222,7 +222,7 @@ class UploadView(generic.edit.FormView):
|
|||||||
os.makedirs(subdir_path)
|
os.makedirs(subdir_path)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
metadata_format_str = u"""Absender: {name} <{email_address}>
|
metadata_format_str = """Absender: {name} <{email_address}>
|
||||||
Gruppe: {group}
|
Gruppe: {group}
|
||||||
Datum: {date} {time}
|
Datum: {date} {time}
|
||||||
Titel: {title}
|
Titel: {title}
|
||||||
@@ -246,10 +246,10 @@ Beschreibung:
|
|||||||
with codecs.open(metadata_file_path, 'w', encoding='utf-8') as metadata_file:
|
with codecs.open(metadata_file_path, 'w', encoding='utf-8') as metadata_file:
|
||||||
metadata_file.write(metadata)
|
metadata_file.write(metadata)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
message = _(u'Jetzt ist irgendwas schief gelaufen.')
|
message = _('Jetzt ist irgendwas schief gelaufen.')
|
||||||
messages.error(self.request, message)
|
messages.error(self.request, message)
|
||||||
logger.error('dav_submission.views.UploadView.form_valid(): Catched Exception #2: %s', str(e))
|
logger.error('dav_submission.views.UploadView.form_valid(): Catched Exception #2: %s', str(e))
|
||||||
return super(UploadView, self).form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
for input_file in form.files.getlist('files'):
|
for input_file in form.files.getlist('files'):
|
||||||
@@ -257,7 +257,7 @@ Beschreibung:
|
|||||||
file_path = os.path.join(subdir_path, file_name)
|
file_path = os.path.join(subdir_path, file_name)
|
||||||
|
|
||||||
if os.path.exists(file_path):
|
if os.path.exists(file_path):
|
||||||
message = _(u'Die Datei %(name)s haben wir bereits.') % {'name': input_file.name}
|
message = _('Die Datei %(name)s haben wir bereits.') % {'name': input_file.name}
|
||||||
messages.error(self.request, message)
|
messages.error(self.request, message)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -267,28 +267,28 @@ Beschreibung:
|
|||||||
|
|
||||||
size = os.path.getsize(file_path)
|
size = os.path.getsize(file_path)
|
||||||
if size > (1024 * 1024):
|
if size > (1024 * 1024):
|
||||||
size_str = u'%s MiB' % ('%.3f' % (size / 1024.0 / 1024.0)).rstrip('0').rstrip('.')
|
size_str = '%s MiB' % ('%.3f' % (size / 1024.0 / 1024.0)).rstrip('0').rstrip('.')
|
||||||
elif size > 1024:
|
elif size > 1024:
|
||||||
size_str = u'%s KiB' % ('%.3f' % (size / 1024.0)).rstrip('0').rstrip('.')
|
size_str = '%s KiB' % ('%.3f' % (size / 1024.0)).rstrip('0').rstrip('.')
|
||||||
else:
|
else:
|
||||||
size_str = u'%d Byte' % size
|
size_str = '%d Byte' % size
|
||||||
message = _(u'Datei erfolgreich hochgeladen: %(name)s (%(size)s)') % {'name': input_file.name,
|
message = _('Datei erfolgreich hochgeladen: %(name)s (%(size)s)') % {'name': input_file.name,
|
||||||
'size': size_str}
|
'size': size_str}
|
||||||
messages.success(self.request, message)
|
messages.success(self.request, message)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
message = _(u'Jetzt ist irgendwas schief gelaufen.')
|
message = _('Jetzt ist irgendwas schief gelaufen.')
|
||||||
messages.error(self.request, message)
|
messages.error(self.request, message)
|
||||||
logger.error('dav_submission.views.UploadView.form_valid(): Catched Exception #3: %s', str(e))
|
logger.error('dav_submission.views.UploadView.form_valid(): Catched Exception #3: %s', str(e))
|
||||||
return super(UploadView, self).form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
mail = NewSubmissionMail(metadata_format_kwargs)
|
mail = NewSubmissionMail(metadata_format_kwargs)
|
||||||
mail.send()
|
mail.send()
|
||||||
return super(UploadView, self).form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
if not app_config.settings.enable_upload:
|
if not app_config.settings.enable_upload:
|
||||||
raise PermissionDenied(_(u'Der Upload ist noch nicht freigeschaltet.'))
|
raise PermissionDenied(_('Der Upload ist noch nicht freigeschaltet.'))
|
||||||
return super(UploadView, self).post(request, *args, **kwargs)
|
return super().post(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class SuccessView(generic.TemplateView):
|
class SuccessView(generic.TemplateView):
|
||||||
|
|||||||
20
setup.py
20
setup.py
@@ -22,12 +22,7 @@ class SetupPythonEnvironment(MyCommand):
|
|||||||
def run(self):
|
def run(self):
|
||||||
python_bin = sys.executable if sys.executable else 'python'
|
python_bin = sys.executable if sys.executable else 'python'
|
||||||
python_ver = sys.version_info.major
|
python_ver = sys.version_info.major
|
||||||
if python_ver == 2:
|
if python_ver == 3:
|
||||||
path = os.path.join('env', 'python2')
|
|
||||||
symlink_path = os.path.join('env', 'python')
|
|
||||||
venv_module = 'virtualenv'
|
|
||||||
prompt = '(py2-dav) '
|
|
||||||
elif python_ver == 3:
|
|
||||||
path = os.path.join('env', 'python3')
|
path = os.path.join('env', 'python3')
|
||||||
symlink_path = os.path.join('env', 'python')
|
symlink_path = os.path.join('env', 'python')
|
||||||
venv_module = 'venv'
|
venv_module = 'venv'
|
||||||
@@ -97,7 +92,7 @@ class QuickSetup(MyCommand):
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='django-dav-events',
|
name='django-dav-events',
|
||||||
version='2.0',
|
version='2.1',
|
||||||
description='A django based web application project to organize DAV Events.',
|
description='A django based web application project to organize DAV Events.',
|
||||||
url='https://touren.alpenverein-karlsruhe.de',
|
url='https://touren.alpenverein-karlsruhe.de',
|
||||||
author='Jens Kleineheismann',
|
author='Jens Kleineheismann',
|
||||||
@@ -116,11 +111,12 @@ setup(
|
|||||||
},
|
},
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'babel',
|
'babel',
|
||||||
'django >= 1.11, < 2.0',
|
#'django >= 1.11, < 2.0',
|
||||||
'django-extensions',
|
'django >= 1.11, < 3.3',
|
||||||
'django-bootstrap3 < 12',
|
# 'django-extensions',
|
||||||
'django-countries < 6',
|
'django-bootstrap3',
|
||||||
'django-datetime-widget',
|
'django-countries',
|
||||||
|
'django-datetime-widget2',
|
||||||
'pytz',
|
'pytz',
|
||||||
'selenium',
|
'selenium',
|
||||||
'coverage',
|
'coverage',
|
||||||
|
|||||||
5
tests/__main__.py
Normal file
5
tests/__main__.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from test_suite import main
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
4
tests/settings.py
Normal file
4
tests/settings.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
BASE_DIR = Path(__file__).resolve().parent
|
||||||
|
|
||||||
|
SECRET_KEY = 'fake-key'
|
||||||
@@ -1,18 +1,19 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
|
||||||
import datetime
|
import datetime
|
||||||
import django
|
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
|
import django
|
||||||
from django.test.utils import get_runner
|
from django.test.utils import get_runner
|
||||||
|
|
||||||
#from dav_base.console_scripts.admin import DJANGO_MAIN_MODULE
|
|
||||||
DJANGO_MAIN_MODULE = 'main'
|
|
||||||
from dav_base.tests.utils import mkdtemp
|
from dav_base.tests.utils import mkdtemp
|
||||||
|
|
||||||
|
# from dav_base.console_scripts.admin import DJANGO_MAIN_MODULE
|
||||||
|
DJANGO_MAIN_MODULE = 'main'
|
||||||
|
|
||||||
class DjangoEnvironment(object):
|
|
||||||
|
class DjangoEnvironment:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _install_djangoproject(path, modules=None):
|
def _install_djangoproject(path, modules=None):
|
||||||
cmd = 'django-dav-admin setup "{}"'.format(path)
|
cmd = 'django-dav-admin setup "{}"'.format(path)
|
||||||
@@ -34,6 +35,8 @@ class DjangoEnvironment(object):
|
|||||||
else:
|
else:
|
||||||
self._enable_modules = []
|
self._enable_modules = []
|
||||||
|
|
||||||
|
self.settings = None
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
if self.path is None:
|
if self.path is None:
|
||||||
prefix = 'testrun-{datetime}-'.format(
|
prefix = 'testrun-{datetime}-'.format(
|
||||||
@@ -50,7 +53,7 @@ class DjangoEnvironment(object):
|
|||||||
os.environ['DJANGO_SETTINGS_MODULE'] = '{}.settings'.format(DJANGO_MAIN_MODULE)
|
os.environ['DJANGO_SETTINGS_MODULE'] = '{}.settings'.format(DJANGO_MAIN_MODULE)
|
||||||
django.setup()
|
django.setup()
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings # pylint: disable=import-outside-toplevel
|
||||||
self.settings = settings
|
self.settings = settings
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -63,7 +66,7 @@ class DjangoEnvironment(object):
|
|||||||
shutil.rmtree(self.path)
|
shutil.rmtree(self.path)
|
||||||
|
|
||||||
|
|
||||||
class TestSuite(object):
|
class TestSuite:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def run():
|
def run():
|
||||||
modules = ['dav_auth', 'dav_events', 'dav_registration', 'dav_event_office']
|
modules = ['dav_auth', 'dav_events', 'dav_registration', 'dav_event_office']
|
||||||
@@ -80,3 +83,12 @@ class TestSuite(object):
|
|||||||
|
|
||||||
def __call__(self):
|
def __call__(self):
|
||||||
sys.exit(self.run())
|
sys.exit(self.run())
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
test_suite = TestSuite()
|
||||||
|
test_suite()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|||||||
Reference in New Issue
Block a user