Refactor: split code into several django apps (we call them modules).
This commit is contained in:
1 parent
c23dc33d4e
commit
0d5a8c65e3
81 files changed
+739
-332
No files matched your search
@@ -0,0 +1 @@
|
||||
default_app_config = 'dav_auth.apps.AppConfig'
|
||||
@@ -0,0 +1,12 @@
|
||||
from dav_base.config.apps import AppConfig as _AppConfig, DefaultSetting
|
||||
|
||||
DEFAULT_SETTINGS = (
|
||||
DefaultSetting('login_redirect_url', 'root'),
|
||||
DefaultSetting('logout_redirect_url', 'root'),
|
||||
)
|
||||
|
||||
|
||||
class AppConfig(_AppConfig):
|
||||
name = 'dav_auth'
|
||||
verbose_name = u'DAV Benutzerverwaltung'
|
||||
default_settings = DEFAULT_SETTINGS
|
||||
@@ -0,0 +1,2 @@
|
||||
# LOGIN_REDIRECT_URL = 'root'
|
||||
# LOGOUT_REDIRECT_URL = 'root'
|
||||
@@ -0,0 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from dav_base.emails import AbstractMail
|
||||
|
||||
|
||||
class PasswordSetEmail(AbstractMail):
|
||||
_subject = u'Zugangsdaten'
|
||||
_template_name = 'dav_auth/emails/password_set.txt'
|
||||
|
||||
def __init__(self, user, password):
|
||||
self._user = user
|
||||
self._password = password
|
||||
|
||||
def _get_recipients(self):
|
||||
r = u'{fullname} <{email}>'.format(fullname=self._user.get_full_name(),
|
||||
email=self._user.email)
|
||||
return [r]
|
||||
|
||||
def _get_context_data(self, extra_context=None):
|
||||
context = super(PasswordSetEmail, self)._get_context_data(extra_context=extra_context)
|
||||
context.update({
|
||||
'fullname': self._user.get_full_name(),
|
||||
'username': self._user.username,
|
||||
'password': self._password
|
||||
})
|
||||
return context
|
||||
File renamed without changes.
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"url_prefix": "auth"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{% extends "dav_base/base.html" %}
|
||||
+1
-1
@@ -3,4 +3,4 @@ Hallo {{ fullname }},
|
||||
Benutzername: {{ username }}
|
||||
Passwort: {{ password }}
|
||||
|
||||
URL: {{ base_url }}{% url 'dav_events:home' %}
|
||||
URL: {{ base_url }}{% url 'root' %}
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
{% extends "dav_events/base.html" %}
|
||||
{% extends "dav_auth/base.html" %}
|
||||
{% load bootstrap3 %}
|
||||
{% load i18n %}
|
||||
|
||||
@@ -15,13 +15,13 @@
|
||||
<form action="" method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form %}
|
||||
<div class="pull-right"><a href="{% url 'dav_events:reset_password' %}">{% trans 'Passwort vergessen?' %}</a></div>
|
||||
<div class="pull-right"><a href="{% url 'dav_auth:reset_password' %}">{% trans 'Passwort vergessen?' %}</a></div>
|
||||
{% buttons %}
|
||||
<button type="submit" class="btn btn-success">
|
||||
{% bootstrap_icon 'log-in' %} 
|
||||
{% trans 'Login' %}
|
||||
</button>
|
||||
<a class="btn btn-danger" href="{% url 'dav_events:home' %}">
|
||||
<a class="btn btn-danger" href="{% url 'root' %}">
|
||||
{% bootstrap_icon 'remove' %} 
|
||||
{% trans 'Abbrechen' %}
|
||||
</a>
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
{% extends "dav_events/base.html" %}
|
||||
{% extends "dav_auth/base.html" %}
|
||||
{% load bootstrap3 %}
|
||||
{% load i18n %}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
{% bootstrap_icon 'saved' %} 
|
||||
{% trans 'Neues Passwort per E-Mail zusenden' %}
|
||||
</button>
|
||||
<a class="btn btn-danger" href="{% url 'dav_events:home' %}">
|
||||
<a class="btn btn-danger" href="{% url 'root' %}">
|
||||
{% bootstrap_icon 'remove' %} 
|
||||
{% trans 'Abbrechen' %}
|
||||
</a>
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
{% extends "dav_events/base.html" %}
|
||||
{% extends "dav_auth/base.html" %}
|
||||
{% load bootstrap3 %}
|
||||
{% load i18n %}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
{% bootstrap_icon 'saved' %} 
|
||||
{% trans 'Neues Passwort setzen' %}
|
||||
</button>
|
||||
<a class="btn btn-danger" href="{% url 'dav_events:home' %}">
|
||||
<a class="btn btn-danger" href="{% url 'root' %}">
|
||||
{% bootstrap_icon 'remove' %} 
|
||||
{% trans 'Abbrechen' %}
|
||||
</a>
|
||||
+3
-3
@@ -7,12 +7,12 @@
|
||||
{{ user }} <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-right" aria-labelledby="user_dropdown_button">
|
||||
<li><a href="{% url 'dav_events:set_password' %}">{% trans 'Passwort ändern' %}</a></li>
|
||||
<li><a href="{% url 'dav_events:logout' %}">{% trans 'Logout' %}</a></li>
|
||||
<li><a href="{% url 'dav_auth:set_password' %}">{% trans 'Passwort ändern' %}</a></li>
|
||||
<li><a href="{% url 'dav_auth:logout' %}">{% trans 'Logout' %}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% else %}
|
||||
<a class="btn btn-default btn-sm" href="{% url 'dav_events:login' %}">
|
||||
<a class="btn btn-default btn-sm" href="{% url 'dav_auth:login' %}">
|
||||
{% bootstrap_icon 'log-in' %}
|
||||
{% trans 'Login' %}
|
||||
</a>
|
||||
@@ -0,0 +1,10 @@
|
||||
from django.conf.urls import url
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^login$', views.LoginView.as_view(), name='login'),
|
||||
url(r'^logout$', views.LogoutView.as_view(), name='logout'),
|
||||
url(r'^password$', views.SetPasswordView.as_view(), name='set_password'),
|
||||
url(r'^password/reset$', views.ResetPasswordView.as_view(), name='reset_password'),
|
||||
]
|
||||
@@ -1,24 +1,28 @@
|
||||
import logging
|
||||
from django.apps import apps
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth import views as auth_views, get_user_model
|
||||
from django.shortcuts import resolve_url
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.views import generic
|
||||
|
||||
from .. import emails
|
||||
from .. import forms
|
||||
from . import emails
|
||||
from . import forms
|
||||
|
||||
app_config = apps.get_containing_app_config(__package__)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LoginView(auth_views.LoginView):
|
||||
form_class = forms.auth.LoginForm
|
||||
next_page = reverse_lazy('dav_events:event_list')
|
||||
template_name = 'dav_events/auth/login_form.html'
|
||||
form_class = forms.LoginForm
|
||||
template_name = 'dav_auth/forms/login.html'
|
||||
|
||||
def get_success_url(self):
|
||||
url = self.get_redirect_url()
|
||||
return url or self.next_page
|
||||
def get_redirect_url(self):
|
||||
url = super(LoginView, self).get_redirect_url()
|
||||
if not url and app_config.settings.login_redirect_url:
|
||||
url = resolve_url(app_config.settings.login_redirect_url)
|
||||
return url
|
||||
|
||||
def form_valid(self, form):
|
||||
r = super(LoginView, self).form_valid(form)
|
||||
@@ -27,7 +31,11 @@ class LoginView(auth_views.LoginView):
|
||||
|
||||
|
||||
class LogoutView(auth_views.LogoutView):
|
||||
next_page = reverse_lazy('dav_events:home')
|
||||
def get_next_page(self):
|
||||
url = super(LogoutView, self).get_next_page()
|
||||
if not url and app_config.settings.logout_redirect_url:
|
||||
url = resolve_url(app_config.settings.logout_redirect_url)
|
||||
return url
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
r = super(LogoutView, self).dispatch(request, *args, **kwargs)
|
||||
@@ -36,9 +44,11 @@ class LogoutView(auth_views.LogoutView):
|
||||
|
||||
|
||||
class SetPasswordView(auth_views.PasswordChangeView):
|
||||
form_class = forms.auth.SetPasswordForm
|
||||
template_name = 'dav_events/auth/set_password_form.html'
|
||||
success_url = reverse_lazy('dav_events:event_list')
|
||||
form_class = forms.SetPasswordForm
|
||||
template_name = 'dav_auth/forms/set_password.html'
|
||||
|
||||
def get_success_url(self):
|
||||
return resolve_url(app_config.settings.login_redirect_url)
|
||||
|
||||
def form_valid(self, form):
|
||||
r = super(SetPasswordView, self).form_valid(form)
|
||||
@@ -50,9 +60,9 @@ class SetPasswordView(auth_views.PasswordChangeView):
|
||||
|
||||
|
||||
class ResetPasswordView(generic.FormView):
|
||||
form_class = forms.auth.ResetPasswordForm
|
||||
template_name = 'dav_events/auth/reset_password_form.html'
|
||||
success_url = reverse_lazy('dav_events:login')
|
||||
form_class = forms.ResetPasswordForm
|
||||
template_name = 'dav_auth/forms/reset_password.html'
|
||||
success_url = reverse_lazy('dav_auth:login')
|
||||
|
||||
def form_valid(self, form):
|
||||
username = form.cleaned_data.get('username')
|
||||
@@ -0,0 +1 @@
|
||||
default_app_config = 'dav_base.apps.AppConfig'
|
||||
File renamed without changes.
@@ -0,0 +1,13 @@
|
||||
from .config.apps import AppConfig as _AppConfig, DefaultSetting
|
||||
|
||||
DEFAULT_SETTINGS = (
|
||||
DefaultSetting('email_sender', None),
|
||||
DefaultSetting('email_base_url', None),
|
||||
DefaultSetting('email_subject_prefix', ''),
|
||||
)
|
||||
|
||||
|
||||
class AppConfig(_AppConfig):
|
||||
name = 'dav_base'
|
||||
verbose_name = u'DAV Base App'
|
||||
default_settings = DEFAULT_SETTINGS
|
||||
@@ -0,0 +1,2 @@
|
||||
from . import apps
|
||||
from . import modules
|
||||
@@ -0,0 +1,71 @@
|
||||
import importlib
|
||||
import logging
|
||||
import re
|
||||
from django.apps import AppConfig as _AppConfig
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DefaultSetting(object):
|
||||
def __init__(self, name, value, key_name=None, validator=None):
|
||||
self.name = name
|
||||
self.value = value
|
||||
if key_name is None:
|
||||
self.key_name = self.name.upper()
|
||||
else:
|
||||
self.key_name = key_name
|
||||
self.validator = validator
|
||||
|
||||
def validate(self, value):
|
||||
if hasattr(self, 'validator') and self.validator is not None:
|
||||
if callable(self.validator):
|
||||
if not self.validator(value):
|
||||
raise ImproperlyConfigured("Validator callback {clb} returned False".format(clb=self.validator))
|
||||
else:
|
||||
if not re.search(self.validator, value):
|
||||
raise ImproperlyConfigured("Does not match /{re}/".format(re=self.validator))
|
||||
|
||||
|
||||
class AppSettings(object):
|
||||
def __init__(self, app_name, defaults):
|
||||
settings_name = 'main.settings-' + app_name
|
||||
|
||||
try:
|
||||
settings_module = importlib.import_module(settings_name)
|
||||
except ImportError:
|
||||
settings_module = None
|
||||
|
||||
for default in defaults:
|
||||
if hasattr(settings_module, default.key_name):
|
||||
value = getattr(settings_module, default.key_name)
|
||||
try:
|
||||
default.validate(value)
|
||||
except ImproperlyConfigured as e:
|
||||
msg = 'Invalid value of {key} in {module}: {cause}'.format(key=default.key_name,
|
||||
module=settings_name,
|
||||
cause=e.message)
|
||||
raise ImproperlyConfigured(msg)
|
||||
setattr(self, default.name, value)
|
||||
elif isinstance(default.value, ImproperlyConfigured):
|
||||
raise default.value
|
||||
else:
|
||||
try:
|
||||
is_impconf = issubclass(default.value, ImproperlyConfigured)
|
||||
except TypeError:
|
||||
is_impconf = False
|
||||
|
||||
if is_impconf:
|
||||
msg = '{key} must be defined in {module}'.format(key=default.key_name,
|
||||
module=settings_name)
|
||||
raise default.value(msg)
|
||||
else:
|
||||
setattr(self, default.name, default.value)
|
||||
|
||||
|
||||
class AppConfig(_AppConfig):
|
||||
default_settings = ()
|
||||
|
||||
def __init__(self, app_name, app_module):
|
||||
super(AppConfig, self).__init__(app_name, app_module)
|
||||
self.settings = AppSettings(app_name, self.default_settings)
|
||||
@@ -0,0 +1,132 @@
|
||||
import json
|
||||
import os
|
||||
import pkg_resources
|
||||
|
||||
from django.conf import settings
|
||||
from django.conf.urls import url as django_conf_url, include
|
||||
|
||||
DJANGO_MAIN_MODULE = 'main'
|
||||
MODULE_CONFIG_FILE_NAME = 'module_config.json'
|
||||
|
||||
|
||||
class ModuleConfigError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ModuleMeta(object):
|
||||
_json_file = 'module.json'
|
||||
_root_url_name = 'root'
|
||||
|
||||
def __init__(self, package_name):
|
||||
self._package_name = package_name
|
||||
self._additional_apps = []
|
||||
self._url_prefix = None
|
||||
self._load_from_package()
|
||||
|
||||
def __str__(self):
|
||||
t = '- {}'.format(self._package_name)
|
||||
return t
|
||||
|
||||
@property
|
||||
def app(self):
|
||||
return self._package_name
|
||||
|
||||
@property
|
||||
def additional_apps(self):
|
||||
return self._additional_apps
|
||||
|
||||
@property
|
||||
def url_conf_pattern(self):
|
||||
url_pattern = '^'
|
||||
if self._url_prefix is None:
|
||||
url_pattern += self._package_name
|
||||
else:
|
||||
url_pattern += self._url_prefix
|
||||
url_pattern += '/'
|
||||
url_conf = self._package_name + '.urls'
|
||||
return django_conf_url(url_pattern, include(url_conf, self.url_namespace))
|
||||
|
||||
@property
|
||||
def url_namespace(self):
|
||||
return self._package_name.replace('.', '_')
|
||||
|
||||
def _load_from_package(self):
|
||||
package_name = self._package_name
|
||||
json_text = pkg_resources.resource_string(package_name, self._json_file)
|
||||
meta_dict = json.loads(json_text)
|
||||
meta_dict['package'] = package_name
|
||||
self.load_from_dict(meta_dict)
|
||||
|
||||
def load_from_dict(self, meta_dict):
|
||||
self._package_name = meta_dict.get('package', None)
|
||||
self._additional_apps = meta_dict.get('additional_apps', [])
|
||||
self._url_prefix = meta_dict.get('url_prefix', None)
|
||||
|
||||
def dump_as_dict(self):
|
||||
d = {
|
||||
'package': self._package_name,
|
||||
}
|
||||
if self._additional_apps:
|
||||
d['additional_apps'] = self._additional_apps
|
||||
if self._url_prefix:
|
||||
d['url_prefix'] = self._url_prefix
|
||||
return d
|
||||
|
||||
|
||||
class ModuleConfig(object):
|
||||
_lazy_load = True
|
||||
|
||||
def __init__(self, config_file_path=None, django_base_dir=None):
|
||||
if config_file_path is None:
|
||||
if django_base_dir is None:
|
||||
django_base_dir = settings.BASE_DIR
|
||||
config_file_path = os.path.join(django_base_dir, DJANGO_MAIN_MODULE, MODULE_CONFIG_FILE_NAME)
|
||||
self._config_file_path = config_file_path
|
||||
|
||||
self._modules = dict()
|
||||
|
||||
self._loaded = False
|
||||
if not self._lazy_load:
|
||||
self._load()
|
||||
|
||||
def _lazy_init(self):
|
||||
if not self._loaded:
|
||||
self._load()
|
||||
|
||||
def _load(self):
|
||||
path = self._config_file_path
|
||||
|
||||
self._modules = dict()
|
||||
|
||||
with open(path, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
if 'modules' in data:
|
||||
for meta_dict in data['modules']:
|
||||
module_name = meta_dict['package']
|
||||
self._modules[module_name] = ModuleMeta(module_name)
|
||||
self._modules[module_name].load_from_dict(meta_dict)
|
||||
|
||||
self._loaded = True
|
||||
|
||||
@property
|
||||
def modules(self):
|
||||
self._lazy_init()
|
||||
return self._modules
|
||||
|
||||
def save(self):
|
||||
path = self._config_file_path
|
||||
|
||||
if os.path.exists(path):
|
||||
self._lazy_init()
|
||||
else:
|
||||
self._loaded = True
|
||||
|
||||
data = {
|
||||
'modules': [],
|
||||
}
|
||||
for meta_obj in self._modules.values():
|
||||
data['modules'].append(meta_obj.dump_as_dict())
|
||||
|
||||
with open(path, 'w') as f:
|
||||
json.dump(data, f, indent=4)
|
||||
Whitespace-only changes.
@@ -4,13 +4,15 @@ import pkg_resources
|
||||
import posix
|
||||
import sys
|
||||
|
||||
from dav_base.config.modules import DJANGO_MAIN_MODULE, ModuleConfig
|
||||
|
||||
VERSION = '0.1'
|
||||
|
||||
|
||||
class AdminCommand(object):
|
||||
def _setup_argparser(self):
|
||||
kwargs = {
|
||||
'description': 'Tool to manage the DAV Events django project installation.',
|
||||
'description': 'Tool to manage the DAV django project installation.',
|
||||
'epilog': 'Off Belay.',
|
||||
}
|
||||
parser = argparse.ArgumentParser(**kwargs)
|
||||
@@ -36,30 +38,31 @@ class AdminCommand(object):
|
||||
return self._argparser.parse_args(argv)
|
||||
|
||||
def _subcmd_setup(self, cmd_args):
|
||||
django_project_name = 'main'
|
||||
django_project_path = cmd_args.path
|
||||
django_main_module = DJANGO_MAIN_MODULE
|
||||
django_base_dir = cmd_args.path
|
||||
|
||||
sys.stdout.write('Setup installation in \'{path}\'...\n'.format(path=django_project_path))
|
||||
sys.stdout.write('Setup installation in \'{path}\'...\n'.format(path=django_base_dir))
|
||||
|
||||
if os.path.exists(django_project_path):
|
||||
if not os.path.isdir(django_project_path):
|
||||
sys.stderr.write('{path}: Not a directory.\n'.format(path=django_project_path))
|
||||
if os.path.exists(django_base_dir):
|
||||
if not os.path.isdir(django_base_dir):
|
||||
sys.stderr.write('{path}: Not a directory.\n'.format(path=django_base_dir))
|
||||
return posix.EX_USAGE
|
||||
else:
|
||||
os.makedirs(django_project_path)
|
||||
os.makedirs(django_base_dir)
|
||||
|
||||
sys.stdout.write('Creating django project...\n')
|
||||
django_cmd = 'django-admin startproject {name} "{path}"'.format(name=django_project_name,
|
||||
path=django_project_path)
|
||||
django_cmd = 'django-admin startproject {name} "{path}"'.format(name=django_main_module,
|
||||
path=django_base_dir)
|
||||
exitval = os.system(django_cmd)
|
||||
if exitval != posix.EX_OK:
|
||||
return exitval
|
||||
|
||||
sys.stdout.write('Creating directories...\n')
|
||||
dirs = [
|
||||
os.path.join(django_project_path, 'var', 'db'),
|
||||
os.path.join(django_project_path, 'var', 'log'),
|
||||
os.path.join(django_project_path, 'var', 'www', 'static'),
|
||||
os.path.join(django_base_dir, 'common', 'templates'),
|
||||
os.path.join(django_base_dir, 'var', 'db'),
|
||||
os.path.join(django_base_dir, 'var', 'log'),
|
||||
os.path.join(django_base_dir, 'var', 'www', 'static'),
|
||||
]
|
||||
|
||||
for d in dirs:
|
||||
@@ -68,18 +71,21 @@ class AdminCommand(object):
|
||||
|
||||
sys.stdout.write('Configure django project...\n')
|
||||
|
||||
input_file = os.path.join('Resources', 'django.main.additional_settings.py')
|
||||
output_file = os.path.join(django_project_path, django_project_name, 'settings.py')
|
||||
config = ModuleConfig(django_base_dir=django_base_dir)
|
||||
config.save()
|
||||
|
||||
input_file = os.path.join('django_project_config', 'additional_settings.py')
|
||||
output_file = os.path.join(django_base_dir, django_main_module, 'settings.py')
|
||||
with open(output_file, 'a') as f:
|
||||
f.write(pkg_resources.resource_string(__package__, input_file))
|
||||
|
||||
input_file = os.path.join('Resources', 'django.main.urls.py')
|
||||
output_file = os.path.join(django_project_path, django_project_name, 'urls.py')
|
||||
input_file = os.path.join('django_project_config', 'urls.py')
|
||||
output_file = os.path.join(django_base_dir, django_main_module, 'urls.py')
|
||||
with open(output_file, 'w') as f:
|
||||
f.write(pkg_resources.resource_string(__package__, input_file))
|
||||
|
||||
input_file = os.path.join('Resources', 'django.main.settings-dav_events.py')
|
||||
output_file = os.path.join(django_project_path, django_project_name, 'settings-dav_events.py')
|
||||
input_file = os.path.join('django_project_config', 'settings-dav_base.py')
|
||||
output_file = os.path.join(django_base_dir, django_main_module, 'settings-dav_base.py')
|
||||
with open(output_file, 'w') as f:
|
||||
f.write(pkg_resources.resource_string(__package__, input_file))
|
||||
|
||||
+20
-3
@@ -1,20 +1,33 @@
|
||||
|
||||
#
|
||||
# Additional settings for django-dav-events
|
||||
# Additional settings for django-dav
|
||||
#
|
||||
|
||||
BASE_VAR_DIR = os.path.join(BASE_DIR, 'var')
|
||||
BASE_SHARE_DIR = os.path.join(BASE_DIR, 'common')
|
||||
|
||||
# Get modules config
|
||||
from dav_base.config.modules import ModuleConfig
|
||||
MODULE_CONFIG = ModuleConfig()
|
||||
|
||||
INSTALLED_APPS += [
|
||||
'bootstrap3',
|
||||
'datetimewidget',
|
||||
'django_countries',
|
||||
'django_extensions',
|
||||
# Our main app
|
||||
'dav_events',
|
||||
'dav_base',
|
||||
]
|
||||
|
||||
# Add apps from our modules
|
||||
for module_meta_obj in MODULE_CONFIG.modules.values():
|
||||
if module_meta_obj.app:
|
||||
INSTALLED_APPS.append(module_meta_obj.app)
|
||||
if module_meta_obj.additional_apps:
|
||||
for app in module_meta_obj.additional_apps:
|
||||
INSTALLED_APPS.append(app)
|
||||
|
||||
# Add a template engine without html auto escape for rendering plain text templates.
|
||||
TEMPLATES += [
|
||||
{
|
||||
'NAME': 'PLAINTEXT',
|
||||
@@ -30,6 +43,10 @@ TEMPLATES += [
|
||||
},
|
||||
]
|
||||
|
||||
# Add our local templates directory to the template engine configurations.
|
||||
for config in TEMPLATES:
|
||||
config['DIRS'].append(os.path.join(BASE_SHARE_DIR, 'templates'))
|
||||
|
||||
DATABASES['default'] = {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': os.path.join(BASE_VAR_DIR, 'db', 'devel.sqlite3'),
|
||||
@@ -39,7 +56,7 @@ STATIC_ROOT = os.path.join(BASE_VAR_DIR, 'www', 'static')
|
||||
|
||||
LANGUAGE_CODE = 'de'
|
||||
|
||||
LOGIN_URL = 'dav_events:login'
|
||||
LOGIN_URL = 'dav_auth:login'
|
||||
|
||||
BOOTSTRAP3 = {
|
||||
'set_placeholder': False,
|
||||
@@ -0,0 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# E-Mails
|
||||
EMAIL_SENDER = 'DAV heinzel <heinzel@alpenverein-karlsruhe.de>'
|
||||
EMAIL_BASE_URL = 'http://localhost:8000'
|
||||
EMAIL_SUBJECT_PREFIX = u'[DAV heinzel]'
|
||||
@@ -0,0 +1,12 @@
|
||||
from django.conf import settings
|
||||
from django.conf.urls import url, include
|
||||
|
||||
urlpatterns = []
|
||||
|
||||
for module_meta_obj in settings.MODULE_CONFIG.modules.values():
|
||||
if module_meta_obj.url_conf_pattern:
|
||||
urlpatterns.append(module_meta_obj.url_conf_pattern)
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^', include('dav_base.urls'))
|
||||
]
|
||||
@@ -0,0 +1,59 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import logging
|
||||
from django.apps import apps
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.core.mail import EmailMessage
|
||||
from django.template.loader import get_template
|
||||
|
||||
app_config = apps.get_containing_app_config(__package__)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AbstractMail(object):
|
||||
_subject = u''
|
||||
_template_name = None
|
||||
|
||||
def _get_sender(self):
|
||||
#app_config = apps.get_containing_app_config(__package__)
|
||||
return app_config.settings.email_sender
|
||||
|
||||
def _get_subject(self, subject_fmt=None, **kwargs):
|
||||
if subject_fmt is None:
|
||||
subject_fmt = self._subject
|
||||
#app_config = apps.get_containing_app_config(__package__)
|
||||
if app_config.settings.email_subject_prefix:
|
||||
subject_fmt = u'%s %s' % (app_config.settings.email_subject_prefix, subject_fmt)
|
||||
subject = subject_fmt.format(**kwargs)
|
||||
return subject
|
||||
|
||||
def _get_template(self):
|
||||
if not self._template_name:
|
||||
raise ImproperlyConfigured('%s._template_name ist not set.', self.__class__.__name__)
|
||||
return get_template(self._template_name, using='PLAINTEXT')
|
||||
|
||||
def _get_context_data(self, extra_context=None):
|
||||
#app_config = apps.get_containing_app_config(__package__)
|
||||
context = {
|
||||
'base_url': app_config.settings.email_base_url,
|
||||
}
|
||||
if extra_context:
|
||||
context.update(extra_context)
|
||||
return context
|
||||
|
||||
def _get_body(self, context=None):
|
||||
template = self._get_template()
|
||||
context = self._get_context_data(extra_context=context)
|
||||
return template.render(context)
|
||||
|
||||
def _get_recipients(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def send(self):
|
||||
subject = self._get_subject()
|
||||
body = self._get_body()
|
||||
sender = self._get_sender()
|
||||
recipients = self._get_recipients()
|
||||
|
||||
email = EmailMessage(subject=subject, body=body, from_email=sender, to=recipients)
|
||||
logger.info(u'Send %s to %s', self.__class__.__name__, recipients)
|
||||
email.send()
|
||||
Whitespace-only changes.
Whitespace-only changes.
@@ -0,0 +1,18 @@
|
||||
from django.conf import settings
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Disable a modular app from django-dav installation.'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('module')
|
||||
|
||||
def handle(self, *args, **options):
|
||||
module_name = options['module']
|
||||
|
||||
config = settings.MODULE_CONFIG
|
||||
del config.modules[module_name]
|
||||
config.save()
|
||||
|
||||
self.stdout.write(self.style.SUCCESS('Module \'{}\' disabled.'.format(module_name)))
|
||||
@@ -0,0 +1,37 @@
|
||||
import os
|
||||
import pkg_resources
|
||||
from django.conf import settings
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
|
||||
from dav_base.config.modules import DJANGO_MAIN_MODULE, ModuleMeta
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Enable a modular app in django-dav installation.'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('module', help='the name of the module')
|
||||
|
||||
def handle(self, *args, **options):
|
||||
django_base_dir = settings.BASE_DIR
|
||||
django_main_module = DJANGO_MAIN_MODULE
|
||||
module_name = options['module']
|
||||
|
||||
config = settings.MODULE_CONFIG
|
||||
|
||||
if module_name in config.modules.keys():
|
||||
raise CommandError('Module \'{}\' is already enabled'.format(module_name))
|
||||
|
||||
settings_file_name = 'settings-{}.py'.format(module_name)
|
||||
input_file = os.path.join('django_project_config', settings_file_name)
|
||||
if pkg_resources.resource_exists(module_name, input_file):
|
||||
output_file = os.path.join(django_base_dir, django_main_module, settings_file_name)
|
||||
if not os.path.exists(output_file):
|
||||
with open(output_file, 'w') as f:
|
||||
f.write(pkg_resources.resource_string(module_name, input_file))
|
||||
|
||||
module_meta_obj = ModuleMeta(module_name)
|
||||
config.modules[module_name] = module_meta_obj
|
||||
config.save()
|
||||
|
||||
self.stdout.write(self.style.SUCCESS('Module \'{}\' enabled.'.format(module_name)))
|
||||
@@ -0,0 +1,17 @@
|
||||
from django.conf import settings
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'List enabled modular apps in django-dav installation.'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
pass
|
||||
|
||||
def handle(self, *args, **options):
|
||||
paragraphs = []
|
||||
config = settings.MODULE_CONFIG
|
||||
for obj in config.modules.values():
|
||||
paragraphs.append(str(obj))
|
||||
output = '\n'.join(paragraphs)
|
||||
self.stdout.write(output)
|
||||
Whitespace-only changes.
Whitespace-only changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,4 +1,4 @@
|
||||
{% extends "error_base.html" %}
|
||||
{% extends "dav_base/error_base.html" %}
|
||||
|
||||
{% block error-code %}400{% endblock %}
|
||||
{% block error-title %}Bad Request{% endblock %}
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "error_base.html" %}
|
||||
{% extends "dav_base/error_base.html" %}
|
||||
|
||||
{% block error-code %}403{% endblock %}
|
||||
{% block error-title %}Forbidden{% endblock %}
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "error_base.html" %}
|
||||
{% extends "dav_base/error_base.html" %}
|
||||
|
||||
{% block error-code %}404{% endblock %}
|
||||
{% block error-title %}Not Found{% endblock %}
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "error_base.html" %}
|
||||
{% extends "dav_base/error_base.html" %}
|
||||
|
||||
{% block error-code %}500{% endblock %}
|
||||
{% block error-title %}Internal Server Error{% endblock %}
|
||||
@@ -0,0 +1,72 @@
|
||||
<!DOCTYPE html>
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load bootstrap3 %}
|
||||
{% load dav_base %}
|
||||
<html lang="{{ LANGUAGE_CODE|default:'de' }}">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
{% block head-media %}
|
||||
<link type="image/x-icon" href="{% static 'dav_base/img/dav-favicon.ico' %}" rel="shortcut icon" />
|
||||
<link type="text/css" href="{% static 'dav_base/bootstrap/css/bootstrap.min.css' %}" rel="stylesheet" />
|
||||
<link type="text/css" href="{% static 'dav_base/css/dataTables.bootstrap.min.css' %}" rel="stylesheet" />
|
||||
<link type="text/css" href="{% static 'dav_base/css/local.css' %}" rel="stylesheet" />
|
||||
|
||||
<script type="text/javascript" src="{% static 'dav_base/js/jquery.min.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'dav_base/js/jquery.dataTables.min.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'dav_base/bootstrap/js/bootstrap.min.js' %}"></script>
|
||||
|
||||
{{ form.media }}
|
||||
{% endblock head-media %}
|
||||
|
||||
{% block head-additional %}
|
||||
{% endblock head-additional %}
|
||||
|
||||
<title>
|
||||
{% block head-title %}Alpenverein Karlsruhe{% endblock head-title %}
|
||||
</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
<div id="page-header">
|
||||
<h2>
|
||||
<a href="{% url 'root' %}">
|
||||
<img width="217" height="30" src="{% static 'dav_base/img/brand.png' %}" />{% block project-name %}{% include_if_exist 'project_name.html' %}{% endblock project-name %}
|
||||
</a>
|
||||
</h2>
|
||||
<div id="login-widget">{% include_if_exist 'dav_auth/includes/login_widget.html' %}</div>
|
||||
</div>
|
||||
|
||||
<div id="messages">
|
||||
{% block messages %}
|
||||
<div class="container-fluid">
|
||||
{% bootstrap_messages %}
|
||||
</div>
|
||||
{% endblock messages %}
|
||||
</div>
|
||||
|
||||
<div id="page-body">
|
||||
{% block modals %}
|
||||
{% endblock modals %}
|
||||
{% block page-body %}
|
||||
<div class="container-fluid">
|
||||
{% block page-container-fluid %}
|
||||
{% endblock page-container-fluid %}
|
||||
</div>
|
||||
<div class="container">
|
||||
{% block page-container %}
|
||||
{% endblock page-container %}
|
||||
</div>
|
||||
{% endblock page-body %}
|
||||
</div>
|
||||
|
||||
<div id="page-footer">
|
||||
<div class="signum">{% block signum %}<a href="mailto:heinzel@alpenverein-karlsruhe.de">heinzel@alpenverein-karlsruhe.de</a>{% endblock signum %}</div>
|
||||
<a href="http://alpenverein-karlsruhe.de" target="_blank">© Sektion Karlsruhe im Deutschen Alpenverein (DAV) e.V.</a>  • 
|
||||
<a href="http://alpenverein-karlsruhe.de/impressum">{% trans 'Impressum' %}</a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "dav_events/base.html" %}
|
||||
{% extends "dav_base/base.html" %}
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% block head-title %}{% block error-code %}{% endblock %} {% block error-title %}Error{% endblock %} - {{ block.super }}{% endblock head-title %}
|
||||
@@ -0,0 +1,27 @@
|
||||
{% extends "dav_base/base.html" %}
|
||||
{% load bootstrap3 %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block page-container %}
|
||||
<div class="jumbotron">
|
||||
<h1>Hallo,</h1>
|
||||
<p>
|
||||
du bist auf dem Entwicklungs- und Testserver der
|
||||
<a href="http://alpenverein-karlsruhe.de">Sektion Karlsruhe des Deutschen Alpenvereins (DAV) e.V.</a> gelandet.
|
||||
</p>
|
||||
<p>
|
||||
Wenn du Fragen hast, kannst du dich an
|
||||
<a href="mailto:heinzel@alpenverein-karlsruhe.de">heinzel@alpenverein-karlsruhe.de</a> wenden.
|
||||
</p>
|
||||
<p> </p>
|
||||
{% if root_urls %}
|
||||
<h3>Module:</h3>
|
||||
<div class="list-group">
|
||||
{% for root_url in root_urls %}
|
||||
<a class="list-group-item list-group-item-warning"
|
||||
href="{% url root_url.1 %}"><span class="glyphicon glyphicon-arrow-right"></span> {{ root_url.0 }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock page-container %}
|
||||
Whitespace-only changes.
Whitespace-only changes.
@@ -0,0 +1,53 @@
|
||||
from django import template
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.tag('include_if_exist')
|
||||
def do_include_if_exist(parser, token):
|
||||
"""
|
||||
Used to include a template, that maybe does not exist.
|
||||
|
||||
include_if_exist support an optional keyword 'default', which must be followed by the name of a default template.
|
||||
The default template will be included, if the first template does not exist.
|
||||
If also the default template does not exist, the behaviour is the same as for the original (builtin) include tag.
|
||||
|
||||
If no default template is given and the first template does not exist an empty string will be returned.
|
||||
"""
|
||||
bits = token.split_contents()
|
||||
if len(bits) < 2:
|
||||
raise template.TemplateSyntaxError("%r tag takes at least two arguments:"
|
||||
" the name of the template to be included" % bits[0])
|
||||
|
||||
try:
|
||||
pos = bits.index('default')
|
||||
del bits[pos]
|
||||
default_template = bits[pos]
|
||||
del bits[pos]
|
||||
token = template.base.Token(token.token_type, ' '.join(bits))
|
||||
token2 = template.base.Token(token.token_type, bits[0] + ' ' + default_template + ' '.join(bits[2:]))
|
||||
default_node = template.loader_tags.do_include(parser, token2)
|
||||
except ValueError:
|
||||
default_node = template.defaulttags.CommentNode()
|
||||
except IndexError:
|
||||
raise template.TemplateSyntaxError("'default' keyword in %r tag requires another arguments:"
|
||||
" the name of the default template" % bits[0])
|
||||
|
||||
try:
|
||||
include_node = template.loader_tags.do_include(parser, token)
|
||||
except template.TemplateDoesNotExist:
|
||||
return default_node
|
||||
|
||||
orig_render = include_node.render
|
||||
orig_template = include_node.template
|
||||
|
||||
def wrapped_render(context, *args, **kwargs):
|
||||
try:
|
||||
t = orig_template.resolve(context)
|
||||
if not callable(getattr(t, 'render', None)):
|
||||
t = context.template.engine.find_template(t)
|
||||
return orig_render(context, *args, **kwargs)
|
||||
except template.TemplateDoesNotExist:
|
||||
return default_node.render(context, *args, **kwargs)
|
||||
include_node.render = wrapped_render
|
||||
return include_node
|
||||
@@ -0,0 +1,9 @@
|
||||
from django.conf.urls import url
|
||||
from django.contrib import admin
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.RootView.as_view(), name='root'),
|
||||
url(r'^djangoadmin/', admin.site.urls),
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
from django.conf import settings
|
||||
from django.urls import reverse, NoReverseMatch
|
||||
from django.views import generic
|
||||
|
||||
|
||||
class RootView(generic.TemplateView):
|
||||
template_name = 'dav_base/root.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
c = super(RootView, self).get_context_data(**kwargs)
|
||||
root_urls = []
|
||||
for module_meta_obj in settings.MODULE_CONFIG.modules.values():
|
||||
root_url_name = 'root'
|
||||
if module_meta_obj.url_namespace:
|
||||
root_url_name = '%s:%s' % (module_meta_obj.url_namespace, root_url_name)
|
||||
try:
|
||||
reverse(root_url_name)
|
||||
root_urls.append((module_meta_obj.app, root_url_name))
|
||||
except NoReverseMatch:
|
||||
pass
|
||||
|
||||
c['root_urls'] = root_urls
|
||||
return c
|
||||
+2
-4
@@ -1,13 +1,11 @@
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
||||
from dav_base.config.apps import AppConfig as _AppConfig, DefaultSetting
|
||||
|
||||
from . import signals
|
||||
from .config import AppConfig as _AppConfig, DefaultSetting
|
||||
|
||||
DEFAULT_SETTINGS = (
|
||||
DefaultSetting('enable_email_notifications', False),
|
||||
DefaultSetting('email_sender', None),
|
||||
DefaultSetting('email_base_url', None),
|
||||
DefaultSetting('email_subject_prefix', ''),
|
||||
DefaultSetting('group_manage_all', None),
|
||||
DefaultSetting('group_manage_w', None),
|
||||
DefaultSetting('group_manage_s', None),
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import importlib
|
||||
import logging
|
||||
import re
|
||||
from django.apps import AppConfig as _AppConfig
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -84,67 +81,3 @@ class FieldInitial(object):
|
||||
return result
|
||||
|
||||
return None
|
||||
|
||||
|
||||
class DefaultSetting(object):
|
||||
def __init__(self, name, value, key_name=None, validator=None):
|
||||
self.name = name
|
||||
self.value = value
|
||||
if key_name is None:
|
||||
self.key_name = self.name.upper()
|
||||
else:
|
||||
self.key_name = key_name
|
||||
self.validator = validator
|
||||
|
||||
def validate(self, value):
|
||||
if hasattr(self, 'validator') and self.validator is not None:
|
||||
if callable(self.validator):
|
||||
if not self.validator(value):
|
||||
raise ImproperlyConfigured("Validator callback {clb} returned False".format(clb=self.validator))
|
||||
else:
|
||||
if not re.search(self.validator, value):
|
||||
raise ImproperlyConfigured("Does not match /{re}/".format(re=self.validator))
|
||||
|
||||
|
||||
class AppSettings(object):
|
||||
def __init__(self, app_name, defaults):
|
||||
settings_name = 'main.settings-' + app_name
|
||||
|
||||
try:
|
||||
settings_module = importlib.import_module(settings_name)
|
||||
except ImportError:
|
||||
settings_module = None
|
||||
|
||||
for default in defaults:
|
||||
if hasattr(settings_module, default.key_name):
|
||||
value = getattr(settings_module, default.key_name)
|
||||
try:
|
||||
default.validate(value)
|
||||
except ImproperlyConfigured as e:
|
||||
msg = 'Invalid value of {key} in {module}: {cause}'.format(key=default.key_name,
|
||||
module=settings_name,
|
||||
cause=e.message)
|
||||
raise ImproperlyConfigured(msg)
|
||||
setattr(self, default.name, value)
|
||||
elif isinstance(default.value, ImproperlyConfigured):
|
||||
raise default.value
|
||||
else:
|
||||
try:
|
||||
is_impconf = issubclass(default.value, ImproperlyConfigured)
|
||||
except TypeError:
|
||||
is_impconf = False
|
||||
|
||||
if is_impconf:
|
||||
msg = '{key} must be defined in {module}'.format(key=default.key_name,
|
||||
module=settings_name)
|
||||
raise default.value(msg)
|
||||
else:
|
||||
setattr(self, default.name, default.value)
|
||||
|
||||
|
||||
class AppConfig(_AppConfig):
|
||||
default_settings = ()
|
||||
|
||||
def __init__(self, app_name, app_module):
|
||||
super(AppConfig, self).__init__(app_name, app_module)
|
||||
self.settings = AppSettings(app_name, self.default_settings)
|
||||
@@ -1,7 +0,0 @@
|
||||
from django.conf.urls import url, include
|
||||
from django.contrib import admin
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^', include('dav_events.urls', namespace='dav_events')),
|
||||
url(r'^admin/', admin.site.urls),
|
||||
]
|
||||
-3
@@ -4,9 +4,6 @@ from dav_events.config import FieldInitial
|
||||
|
||||
# E-Mails
|
||||
ENABLE_EMAIL_NOTIFICATIONS = False
|
||||
EMAIL_SENDER = 'DAV Veranstaltungsheinzel <heinzel@alpenverein-karlsruhe.de>'
|
||||
EMAIL_BASE_URL = 'http://localhost:8000'
|
||||
EMAIL_SUBJECT_PREFIX = u'[DAV Veranstaltungen]'
|
||||
|
||||
# Authorization Roles / Groups
|
||||
GROUP_MANAGE_ALL = 'Tourenreferenten'
|
||||
+1
-80
@@ -1,61 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import logging
|
||||
from django.apps import apps
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.core.mail import EmailMessage
|
||||
from django.template.loader import get_template
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AbstractMail(object):
|
||||
_subject = u''
|
||||
_template_name = None
|
||||
|
||||
def _get_sender(self):
|
||||
app_config = apps.get_containing_app_config(__package__)
|
||||
return app_config.settings.email_sender
|
||||
|
||||
def _get_subject(self, subject_fmt=None, **kwargs):
|
||||
if subject_fmt is None:
|
||||
subject_fmt = self._subject
|
||||
app_config = apps.get_containing_app_config(__package__)
|
||||
if app_config.settings.email_subject_prefix:
|
||||
subject_fmt = u'%s %s' % (app_config.settings.email_subject_prefix, subject_fmt)
|
||||
subject = subject_fmt.format(**kwargs)
|
||||
return subject
|
||||
|
||||
def _get_template(self):
|
||||
if not self._template_name:
|
||||
raise ImproperlyConfigured('%s._template_name ist not set.', self.__class__.__name__)
|
||||
return get_template(self._template_name, using='PLAINTEXT')
|
||||
|
||||
def _get_context_data(self, extra_context=None):
|
||||
app_config = apps.get_containing_app_config(__package__)
|
||||
context = {
|
||||
'base_url': app_config.settings.email_base_url,
|
||||
}
|
||||
if extra_context:
|
||||
context.update(extra_context)
|
||||
return context
|
||||
|
||||
def _get_body(self, context=None):
|
||||
template = self._get_template()
|
||||
context = self._get_context_data(extra_context=context)
|
||||
return template.render(context)
|
||||
|
||||
def _get_recipients(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def send(self):
|
||||
subject = self._get_subject()
|
||||
body = self._get_body()
|
||||
sender = self._get_sender()
|
||||
recipients = self._get_recipients()
|
||||
|
||||
emo = EmailMessage(subject=subject, body=body, from_email=sender, to=recipients)
|
||||
logger.info(u'Send %s to %s', self.__class__.__name__, recipients)
|
||||
emo.send()
|
||||
from dav_base.emails import AbstractMail
|
||||
|
||||
|
||||
class AbstractEventMail(AbstractMail):
|
||||
@@ -149,26 +93,3 @@ class EventToPublishMail(AbstractEventMail):
|
||||
context['editor'] = self._editor
|
||||
context['confirm_publication_url'] = self._confirm_publication_action.get_absolute_url()
|
||||
return context
|
||||
|
||||
|
||||
class PasswordSetEmail(AbstractMail):
|
||||
_subject = u'Zugangsdaten'
|
||||
_template_name = 'dav_events/emails/password_set.txt'
|
||||
|
||||
def __init__(self, user, password):
|
||||
self._user = user
|
||||
self._password = password
|
||||
|
||||
def _get_recipients(self):
|
||||
r = u'{fullname} <{email}>'.format(fullname=self._user.get_full_name(),
|
||||
email=self._user.email)
|
||||
return [r]
|
||||
|
||||
def _get_context_data(self, extra_context=None):
|
||||
context = super(PasswordSetEmail, self)._get_context_data(extra_context=extra_context)
|
||||
context.update({
|
||||
'fullname': self._user.get_full_name(),
|
||||
'username': self._user.username,
|
||||
'password': self._password
|
||||
})
|
||||
return context
|
||||
@@ -1,3 +1,2 @@
|
||||
from . import generic
|
||||
from . import auth
|
||||
from . import events
|
||||
@@ -254,7 +254,7 @@ class Event(models.Model):
|
||||
date=self.get_formated_date())
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('dav_events:event_detail', kwargs={'pk': self.pk})
|
||||
return reverse('dav_events:detail', kwargs={'pk': self.pk})
|
||||
|
||||
def save(self, implicit_update=False, **kwargs):
|
||||
creating = False
|
||||
|
||||
@@ -132,7 +132,7 @@ class OneClickAction(models.Model):
|
||||
try:
|
||||
user_id = self.parameters
|
||||
user = get_user_model().objects.get(id=user_id)
|
||||
url = reverse('dav_events:event_list')
|
||||
url = reverse('dav_events:list')
|
||||
result['location'] = url
|
||||
result['login'] = user
|
||||
except Exception as e:
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"url_prefix": "events"
|
||||
}
|
||||
@@ -1,71 +1,3 @@
|
||||
<!DOCTYPE html>
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load bootstrap3 %}
|
||||
<html lang="{{ LANGUAGE_CODE|default:'de' }}">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
{% block head-media %}
|
||||
<link type="image/x-icon" href="{% static 'dav_events/img/dav-favicon.ico' %}" rel="shortcut icon" />
|
||||
<link type="text/css" href="{% static 'dav_events/bootstrap/css/bootstrap.min.css' %}" rel="stylesheet" />
|
||||
<link type="text/css" href="{% static 'dav_events/css/dataTables.bootstrap.min.css' %}" rel="stylesheet" />
|
||||
<link type="text/css" href="{% static 'dav_events/css/local.css' %}" rel="stylesheet" />
|
||||
{% extends "dav_base/base.html" %}
|
||||
|
||||
<script type="text/javascript" src="{% static 'dav_events/js/jquery.min.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'dav_events/js/jquery.dataTables.min.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'dav_events/bootstrap/js/bootstrap.min.js' %}"></script>
|
||||
|
||||
{{ form.media }}
|
||||
{% endblock head-media %}
|
||||
|
||||
{% block head-additional %}
|
||||
{% endblock head-additional %}
|
||||
|
||||
<title>
|
||||
{% block head-title %}Veranstaltungsheinzel - Alpenverein Karlsruhe{% endblock head-title %}
|
||||
</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
<div id="page-header">
|
||||
<h2>
|
||||
<a href="{% url 'dav_events:home' %}">
|
||||
<img width="217" height="30" src="{% static 'dav_events/img/brand.png' %}" />Veranstaltungsheinzel
|
||||
</a>
|
||||
</h2>
|
||||
<div id="login-widget">{% include 'dav_events/includes/login_widget.html' %}</div>
|
||||
</div>
|
||||
|
||||
<div id="messages">
|
||||
{% block messages %}
|
||||
<div class="container-fluid">
|
||||
{% bootstrap_messages %}
|
||||
</div>
|
||||
{% endblock messages %}
|
||||
</div>
|
||||
|
||||
<div id="page-body">
|
||||
{% block modals %}
|
||||
{% endblock modals %}
|
||||
{% block page-body %}
|
||||
<div class="container-fluid">
|
||||
{% block page-container-fluid %}
|
||||
{% endblock page-container-fluid %}
|
||||
</div>
|
||||
<div class="container">
|
||||
{% block page-container %}
|
||||
{% endblock page-container %}
|
||||
</div>
|
||||
{% endblock page-body %}
|
||||
</div>
|
||||
|
||||
<div id="page-footer">
|
||||
<div class="signum"><a href="mailto:heinzel@alpenverein-karlsruhe.de">heinzel@alpenverein-karlsruhe.de</a></div>
|
||||
<a href="http://alpenverein-karlsruhe.de" target="_blank">© Sektion Karlsruhe im Deutschen Alpenverein (DAV) e.V.</a>  • 
|
||||
<a href="http://alpenverein-karlsruhe.de/impressum">{% trans 'Impressum' %}</a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
{% block head-title %}Touren und Kurse - {{ block.super }}{% endblock %}
|
||||
@@ -23,7 +23,7 @@
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a class="btn btn-success" href="{% url 'dav_events:event_confirmstatus' event.pk 'submitted' %}">
|
||||
<a class="btn btn-success" href="{% url 'dav_events:confirmstatus' event.pk 'submitted' %}">
|
||||
{% bootstrap_icon 'ok' %} 
|
||||
{% trans 'Ja, alles klar!' %}
|
||||
</a>
|
||||
@@ -51,7 +51,7 @@
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a class="btn btn-success" href="{% url 'dav_events:event_confirmstatus' event.pk 'accepted' %}">
|
||||
<a class="btn btn-success" href="{% url 'dav_events:confirmstatus' event.pk 'accepted' %}">
|
||||
{% bootstrap_icon 'ok' %} 
|
||||
{% trans 'Ja, passt schon!' %}
|
||||
</a>
|
||||
@@ -84,7 +84,7 @@
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a class="btn btn-success" href="{% if event.planned_publication_date %}{% url 'dav_events:event_confirmstatus' event.pk 'publishing' %}{% else %}{% url 'dav_events:event_confirmstatus' event.pk 'published' %}{% endif %}">
|
||||
<a class="btn btn-success" href="{% if event.planned_publication_date %}{% url 'dav_events:confirmstatus' event.pk 'publishing' %}{% else %}{% url 'dav_events:confirmstatus' event.pk 'published' %}{% endif %}">
|
||||
{% bootstrap_icon 'ok' %} 
|
||||
{% trans 'Ja' %}
|
||||
</a>
|
||||
@@ -135,7 +135,7 @@
|
||||
</a>
|
||||
{% endif %}
|
||||
<a class="btn btn-primary"
|
||||
href="{% url 'dav_events:event_create' %}?copy={{ event.pk }}"
|
||||
href="{% url 'dav_events:create' %}?copy={{ event.pk }}"
|
||||
title="{% trans 'Diese Veranstaltung als Vorlage für eine neue Veranstaltung benutzen' %}">
|
||||
{% bootstrap_icon 'duplicate' %} 
|
||||
{% trans 'Kopieren' %}
|
||||
@@ -144,15 +144,15 @@
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li>
|
||||
<a class="btn"
|
||||
href="{% url 'dav_events:event_list' %}">{% trans 'Veranstaltungsliste' %}</a>
|
||||
href="{% url 'dav_events:list' %}">{% trans 'Veranstaltungsliste' %}</a>
|
||||
</li>
|
||||
<li class="active">
|
||||
<a class="btn"
|
||||
href="{% url 'dav_events:event_detail' event.pk %}">{% trans 'Details' %}</a>
|
||||
href="{% url 'dav_events:detail' event.pk %}">{% trans 'Details' %}</a>
|
||||
</li>
|
||||
<li class="{% if not has_permission_update %}disabled{% endif %}">
|
||||
<a class="btn {% if has_permission_update %}btn-warning{% else %}disabled{% endif %}"
|
||||
href="{% url 'dav_events:event_update' event.pk %}">{% trans 'Ändern' %}</a>
|
||||
href="{% url 'dav_events:update' event.pk %}">{% trans 'Ändern' %}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -6,18 +6,18 @@
|
||||
<div class="action-tabs">
|
||||
<div class="pull-right">
|
||||
<a class="btn btn-primary"
|
||||
href="{% url 'dav_events:event_create' %}">
|
||||
href="{% url 'dav_events:create' %}">
|
||||
{% bootstrap_icon 'plus' %}
|
||||
{% trans 'Neue Veranstaltung anlegen' %}
|
||||
</a>
|
||||
</div>
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li class="active">
|
||||
<a href="{% url 'dav_events:event_list' %}">{% trans 'Veranstaltungsliste' %}</a>
|
||||
<a href="{% url 'dav_events:list' %}">{% trans 'Veranstaltungsliste' %}</a>
|
||||
</li>
|
||||
<li class="{% if not has_permission_export %}disabled{% endif %}">
|
||||
<a class="btn {% if not has_permission_export %}disabled{% endif %}"
|
||||
href="{% url 'dav_events:event_list_export' %}">{% trans 'Veranstaltungsliste herunterladen' %}</a>
|
||||
href="{% url 'dav_events:list_export' %}">{% trans 'Veranstaltungsliste herunterladen' %}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -45,11 +45,11 @@
|
||||
<tr>
|
||||
<td data-order="{{ event.get_number|slice:':1' }}{{ event.get_number|slice:'-2:' }}{{ event.get_number|slice:'1:-2'|cut:'/' }}"
|
||||
data-search="{{ event.get_number }} ({{ event.get_sport_display }})">
|
||||
<a href="{% url 'dav_events:event_detail' event.pk %}">{{ event.get_number }}</a><br />
|
||||
<a href="{% url 'dav_events:detail' event.pk %}">{{ event.get_number }}</a><br />
|
||||
<small>({{ event.get_sport_display }})</small>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{% url 'dav_events:event_detail' event.pk %}">{{ event.title }}</a>
|
||||
<a href="{% url 'dav_events:detail' event.pk %}">{{ event.title }}</a>
|
||||
</td>
|
||||
<td>
|
||||
{{ event.get_trainer_full_name }}
|
||||
|
||||
@@ -6,17 +6,17 @@
|
||||
<div class="action-tabs">
|
||||
<div class="pull-right">
|
||||
<a class="btn btn-primary"
|
||||
href="{% url 'dav_events:event_create' %}">
|
||||
href="{% url 'dav_events:create' %}">
|
||||
{% bootstrap_icon 'plus' %}
|
||||
{% trans 'Neue Veranstaltung anlegen' %}
|
||||
</a>
|
||||
</div>
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li>
|
||||
<a href="{% url 'dav_events:event_list' %}">{% trans 'Veranstaltungsliste' %}</a>
|
||||
<a href="{% url 'dav_events:list' %}">{% trans 'Veranstaltungsliste' %}</a>
|
||||
</li>
|
||||
<li class="active">
|
||||
<a href="{% url 'dav_events:event_list_export' %}">{% trans 'Veranstaltungsliste herunterladen' %}</a>
|
||||
<a href="{% url 'dav_events:list_export' %}">{% trans 'Veranstaltungsliste herunterladen' %}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -29,7 +29,7 @@
|
||||
{% bootstrap_icon 'download-alt' %} 
|
||||
{% trans 'Herunterladen' %}
|
||||
</button>
|
||||
<a class="btn btn-danger" href="{% url 'dav_events:event_list' %}">
|
||||
<a class="btn btn-danger" href="{% url 'dav_events:list' %}">
|
||||
{% bootstrap_icon 'remove' %} 
|
||||
{% trans 'Abbrechen' %}
|
||||
</a>
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
<div class="action-tabs">
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li>
|
||||
<a href="{% url 'dav_events:event_list' %}">{% trans 'Veranstaltungsliste' %}</a>
|
||||
<a href="{% url 'dav_events:list' %}">{% trans 'Veranstaltungsliste' %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url 'dav_events:event_detail' event.pk %}">{% trans 'Details' %}</a>
|
||||
<a href="{% url 'dav_events:detail' event.pk %}">{% trans 'Details' %}</a>
|
||||
</li>
|
||||
<li class="active">
|
||||
<a href="{% url 'dav_events:event_update' event.pk %}">{% trans 'Ändern' %}</a>
|
||||
<a href="{% url 'dav_events:update' event.pk %}">{% trans 'Ändern' %}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -50,7 +50,7 @@
|
||||
{% bootstrap_icon 'hdd' %} 
|
||||
{% trans 'Speichern' %}
|
||||
</button>
|
||||
<a class="btn btn-danger" href="{% url 'dav_events:event_detail' event.pk %}">
|
||||
<a class="btn btn-danger" href="{% url 'dav_events:detail' event.pk %}">
|
||||
{% bootstrap_icon 'remove' %} 
|
||||
{% trans 'Abbrechen' %}
|
||||
</a>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
Du wirst dann per E-Mail auf dem laufenden gehalten.
|
||||
</p>
|
||||
<p>
|
||||
<a class="btn btn-primary" href="{% url 'dav_events:event_create' %}">Los geht's!</a>
|
||||
<a class="btn btn-primary" href="{% url 'dav_events:create' %}">Los geht's!</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="well">
|
||||
@@ -30,7 +30,7 @@
|
||||
Tourenreferenten und Redakteure können hier Veranstaltungen freigeben und Programmlisten herunterladen.
|
||||
</p>
|
||||
<p>
|
||||
<a class="btn btn-primary" href="{% url 'dav_events:event_list' %}">Weiter</a>
|
||||
<a class="btn btn-primary" href="{% url 'dav_events:list' %}">Weiter</a>
|
||||
</p>
|
||||
</div>
|
||||
{% endblock page-container-fluid %}
|
||||
+8
-12
@@ -3,18 +3,14 @@ from django.conf.urls import url
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.base.HomeView.as_view(), name='home'),
|
||||
url(r'^user/login$', views.auth.LoginView.as_view(), name='login'),
|
||||
url(r'^user/logout$', views.auth.LogoutView.as_view(), name='logout'),
|
||||
url(r'^user/password$', views.auth.SetPasswordView.as_view(), name='set_password'),
|
||||
url(r'^user/password/reset$', views.auth.ResetPasswordView.as_view(), name='reset_password'),
|
||||
url(r'^events$', views.events.EventListView.as_view(), name='event_list'),
|
||||
url(r'^events/export$', views.events.EventListExportView.as_view(), name='event_list_export'),
|
||||
url(r'^events/create$', views.events.EventCreateView.as_view(), name='event_create'),
|
||||
url(r'^events/(?P<pk>\d+)/confirm/(?P<status>[a-z0-9][a-z0-9]*)',
|
||||
views.events.EventConfirmStatusView.as_view(), name='event_confirmstatus'),
|
||||
url(r'^events/(?P<pk>\d+)/edit', views.events.EventUpdateView.as_view(), name='event_update'),
|
||||
url(r'^events/(?P<pk>\d+)/', views.events.EventDetailView.as_view(), name='event_detail'),
|
||||
url(r'^home$', views.base.HomeView.as_view(), name='root'),
|
||||
url(r'^$', views.events.EventListView.as_view(), name='list'),
|
||||
url(r'^export$', views.events.EventListExportView.as_view(), name='list_export'),
|
||||
url(r'^create$', views.events.EventCreateView.as_view(), name='create'),
|
||||
url(r'^(?P<pk>\d+)/confirm/(?P<status>[a-z0-9][a-z0-9]*)',
|
||||
views.events.EventConfirmStatusView.as_view(), name='confirmstatus'),
|
||||
url(r'^(?P<pk>\d+)/edit', views.events.EventUpdateView.as_view(), name='update'),
|
||||
url(r'^(?P<pk>\d+)/', views.events.EventDetailView.as_view(), name='detail'),
|
||||
url(r'^action/(?P<pk>[a-fA-F0-9]{8}-([a-fA-F0-9]{4}-){3}[a-fA-F0-9]{12})/',
|
||||
views.actions.OneClickActionRunView.as_view(), name='action_run'),
|
||||
]
|
||||
@@ -1,4 +1,3 @@
|
||||
from . import base
|
||||
from . import auth
|
||||
from . import events
|
||||
from . import actions
|
||||
@@ -20,7 +20,6 @@ from .. import models
|
||||
from ..utils import has_role
|
||||
from ..workflow import workflow
|
||||
|
||||
app_config = apps.get_containing_app_config(__package__)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -259,7 +258,7 @@ class EventCreateView(EventPermissionMixin, generic.FormView):
|
||||
form_class = forms.events.EventCreateForm
|
||||
template_dir = os.path.join('dav_events', 'event_create')
|
||||
default_template_name = 'default.html'
|
||||
abort_url = reverse_lazy('dav_events:home')
|
||||
abort_url = reverse_lazy('dav_events:root')
|
||||
|
||||
def get_template_names(self):
|
||||
form = self.get_form()
|
||||
@@ -317,7 +316,7 @@ class EventCreateView(EventPermissionMixin, generic.FormView):
|
||||
owner = event.owner
|
||||
self.clean_session_data()
|
||||
if self.request.user.is_authenticated:
|
||||
next_url = reverse('dav_events:event_list')
|
||||
next_url = reverse('dav_events:list')
|
||||
if self.request.user != event.owner:
|
||||
messages.warning(self.request,
|
||||
u'%s %s' % (
|
||||
@@ -325,10 +324,10 @@ class EventCreateView(EventPermissionMixin, generic.FormView):
|
||||
_(u'Warum machst du sowas?')
|
||||
))
|
||||
elif owner.has_usable_password():
|
||||
next_url = reverse('dav_events:event_list')
|
||||
next_url = reverse('dav_events:list')
|
||||
else:
|
||||
login(self.request, owner)
|
||||
next_url = reverse('dav_events:set_password')
|
||||
next_url = reverse('dav_auth:set_password')
|
||||
messages.success(self.request,
|
||||
_(u'Neuen Benutzer angemeldet: %(username)s') % {'username': owner.username})
|
||||
messages.warning(self.request, _(u'Bitte neues Passwort setzen!'))
|
||||
|
||||
@@ -4,6 +4,7 @@ from django.apps import apps
|
||||
from django.utils import timezone
|
||||
|
||||
from . import emails
|
||||
from .utils import get_users_by_role
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -143,7 +144,6 @@ class BasicWorkflow(object):
|
||||
recipients = [event.owner]
|
||||
if event.is_flagged('submitted'):
|
||||
# If the event is already submitted, add managers to the recipients.
|
||||
from .utils import get_users_by_role
|
||||
recipients += get_users_by_role('manage_all')
|
||||
recipients += get_users_by_role('manage_{}'.format(event.sport.lower()))
|
||||
if event.is_flagged('accepted'):
|
||||
@@ -174,7 +174,6 @@ class BasicWorkflow(object):
|
||||
# Inform managers that they have to accept the event.
|
||||
# Also create OneClickActions for all of them and add the link to the mail,
|
||||
# so they can accept the event with a click into the mail.
|
||||
from .utils import get_users_by_role
|
||||
recipients = get_users_by_role('manage_all')
|
||||
recipients += get_users_by_role('manage_{}'.format(event.sport.lower()))
|
||||
OneClickAction = app_config.get_model('OneClickAction')
|
||||
@@ -195,7 +194,6 @@ class BasicWorkflow(object):
|
||||
# Inform publishers that they have to publish the event.
|
||||
# Also create OneClickActions for all of them and add the link to the mail,
|
||||
# so they can confirm the publication with a click into the mail.
|
||||
from .utils import get_users_by_role
|
||||
recipients = get_users_by_role('publish_incremental')
|
||||
OneClickAction = app_config.get_model('OneClickAction')
|
||||
for recipient in recipients:
|
||||
|
||||
@@ -45,7 +45,7 @@ if sys.version_info.major != 2:
|
||||
|
||||
setup(
|
||||
name='django-dav-events',
|
||||
version='0.2',
|
||||
version='1.0',
|
||||
description='A django based web application project to submit DAV Events.',
|
||||
url='https://www.heinzelwelt.de',
|
||||
maintainer='Jens Kleineheismann',
|
||||
@@ -57,7 +57,7 @@ setup(
|
||||
include_package_data=True,
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'django-dav-events-admin = dav_events.console_scripts.admin:main',
|
||||
'django-dav-admin = dav_base.console_scripts.admin:main',
|
||||
],
|
||||
},
|
||||
install_requires=[
|
||||
|
||||
Reference in new issue
Block a user