Merge branch 'release/0.11.0' into production

* release/0.11.0:
  update docs version, add release to changelog
  fix tests
  add icons to top level navigation
  add contact_form link in top navigation
  add context processing for contact_form views
  define DEFAULT_FROM_EMAIL in production settings
  implement contact form
  add imprint as flatpage
  fix issues with changed URLs
  add new german translation strings
  add new view CustomerHostingPackageList
  configure local logging
  mark active menu item as active
  add links to webmail, phpmyadmin and phppgadmin
This commit is contained in:
Jan Dittberner 2015-02-01 20:15:22 +01:00
commit 11222c584d
30 changed files with 621 additions and 49 deletions

View file

@ -1,6 +1,14 @@
Changelog Changelog
========= =========
* :release:`0.11.0 <2015-02-01>`
* :feature:`-` add icons to top level navigation
* :feature:`-` add contact form
* :feature:`-` add imprint as flatpage
* :support:`-` mark active menu item as active via context_processor and
corresponding template markup
* :feature:`-` add links to webmail, phpmyadmin and phppgadmin
* :release:`0.10.0 <2015-02-01>` * :release:`0.10.0 <2015-02-01>`
* :support:`-` move taskresults tests to tasksresults.tests and fix them * :support:`-` move taskresults tests to tasksresults.tests and fix them
* :support:`-` cache result of get_hosting_package method of * :support:`-` cache result of get_hosting_package method of

View file

@ -36,6 +36,7 @@ Django app code
.. toctree:: .. toctree::
code/gnuviechadmin code/gnuviechadmin
code/contact_form
code/dashboard code/dashboard
code/domains code/domains
code/hostingpackages code/hostingpackages

View file

@ -0,0 +1,24 @@
:py:mod:`contact_form` app
==========================
.. automodule:: contact_form
:py:mod:`forms <contact_form.forms>`
------------------------------------
.. automodule:: contact_form.forms
:members:
:py:mod:`urls <contact_form.urls>`
----------------------------------
.. automodule:: contact_form.urls
:py:mod:`views <contact_form.views>`
------------------------------------
.. automodule:: contact_form.views
:members:

View file

@ -11,6 +11,13 @@ The project module :py:mod:`gnuviechadmin`
:members: :members:
:py:mod:`context_processors <gnuviechadmin.context_processors>`
---------------------------------------------------------------
.. automodule:: gnuviechadmin.context_processors
:members:
:py:mod:`urls <gnuviechadmin.urls>` :py:mod:`urls <gnuviechadmin.urls>`
----------------------------------- -----------------------------------

View file

@ -60,9 +60,9 @@ copyright = u'2014, 2015 Jan Dittberner'
# built documents. # built documents.
# #
# The short X.Y version. # The short X.Y version.
version = '0.10' version = '0.11'
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = '0.10.0' release = '0.11.0'
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.

View file

@ -1,5 +1,5 @@
[run] [run]
source = gnuviechadmin,managemails,osusers,domains,taskresults,gvawebcore,userdbs source = gnuviechadmin,contact_form,dashboard,domains,gvawebcore,managemails,osusers,taskresults,userdbs
[report] [report]
omit = */migrations/*,*/tests/*.py,*/tests.py,gnuviechadmin/settings/local.py,gnuviechadmin/settings/production.py omit = */migrations/*,*/tests/*.py,*/tests.py,gnuviechadmin/settings/local.py,gnuviechadmin/settings/production.py

View file

@ -0,0 +1,4 @@
"""
Contact form app.
"""

View file

@ -0,0 +1,73 @@
"""
This module contains the form class for the contact_form app.
"""
from __future__ import absolute_import, unicode_literals
from django import forms
from django.conf import settings
from django.core.mail import send_mail
from django.core.urlresolvers import reverse
from django.template import RequestContext
from django.template import loader
from django.utils.translation import ugettext as _
from django.contrib.sites.models import RequestSite
from django.contrib.sites.models import Site
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit
class ContactForm(forms.Form):
"""
This is the contact form class.
"""
name = forms.CharField(max_length=100, label=_('Your name'))
email = forms.EmailField(max_length=200, label=_('Your email address'))
body = forms.CharField(widget=forms.Textarea, label=_('Your message'))
subject_template_name = "contact_form/contact_form_subject.txt"
template_name = 'contact_form/contact_form.txt'
from_email = settings.DEFAULT_FROM_EMAIL
recipient_list = [mail_tuple[1] for mail_tuple in settings.MANAGERS]
def __init__(self, **kwargs):
self.request = kwargs.pop('request')
super(ContactForm, self).__init__(**kwargs)
self.helper = FormHelper()
self.helper.form_action = reverse('contact_form')
self.helper.add_input(Submit('submit', _('Send message')))
def get_context(self):
if not self.is_valid():
raise ValueError(
'Cannot generate context from invalid contact form')
if Site._meta.installed:
site = Site.objects.get_current()
else:
site = RequestSite(self.request)
return RequestContext(
self.request, dict(self.cleaned_data, site=site))
def message(self):
return loader.render_to_string(self.template_name, self.get_context())
def subject(self):
subject = loader.render_to_string(
self.subject_template_name, self.get_context())
return ''.join(subject.splitlines())
def save(self, fail_silently=False):
"""
Build and send the email.
"""
send_mail(
fail_silently=fail_silently,
from_email=self.from_email,
recipient_list=self.recipient_list,
subject=self.subject(),
message=self.message()
)

View file

@ -0,0 +1,36 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: contact_form\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-01 19:02+0100\n"
"PO-Revision-Date: 2015-02-01 19:03+0100\n"
"Last-Translator: Jan Dittberner <jan@dittberner.info>\n"
"Language-Team: Jan Dittberner <jan@dittberner.info>\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 1.6.10\n"
"X-Poedit-SourceCharset: UTF-8\n"
#: forms.py:27
msgid "Your name"
msgstr "Ihr Name"
#: forms.py:28
msgid "Your email address"
msgstr "Ihre E-Mailadresse"
#: forms.py:29
msgid "Your message"
msgstr "Ihre Nachricht"
#: forms.py:41
msgid "Send message"
msgstr "Nachricht senden"

View file

@ -0,0 +1,19 @@
"""
URL patterns for the contact_form views.
"""
from __future__ import absolute_import, unicode_literals
from django.conf.urls import patterns, url
from .views import (
ContactFormView,
ContactSuccessView,
)
urlpatterns = patterns(
'',
url(r'^$', ContactFormView.as_view(), name='contact_form'),
url(r'^success/$', ContactSuccessView.as_view(), name='contact_success'),
)

View file

@ -0,0 +1,50 @@
"""
This module defines the views of the contact_form app.
"""
from __future__ import absolute_import, unicode_literals
from django.shortcuts import redirect
from django.core.urlresolvers import reverse_lazy
from django.views.generic import (
FormView,
TemplateView,
)
from .forms import ContactForm
class ContactFormView(FormView):
"""
This is the contact form view.
"""
form_class = ContactForm
template_name = 'contact_form/contact_form.html'
success_url = reverse_lazy('contact_success')
def get_form_kwargs(self, **kwargs):
kwargs = super(ContactFormView, self).get_form_kwargs(**kwargs)
kwargs['request'] = self.request
return kwargs
def get_initial(self):
initial = super(ContactFormView, self).get_initial()
currentuser = self.request.user
if currentuser.is_authenticated():
initial['name'] = (
currentuser.get_full_name() or currentuser.username)
initial['email'] = currentuser.email
return initial
def form_valid(self, form):
form.save(False)
return redirect(self.get_success_url())
class ContactSuccessView(TemplateView):
"""
This view is shown after successful contact form sending.
"""
template_name = 'contact_form/contact_success.html'

View file

@ -0,0 +1,56 @@
"""
This module provides context processor implementations for gnuviechadmin.
"""
from __future__ import absolute_import, unicode_literals
import logging
from django.conf import settings
_LOGGER = logging.getLogger(__name__)
def navigation(request):
"""
Add navigation items to the request context.
:param request: Django :py:class:`HttpRequest <django.http.HttpRequest>`
:return: new context items
:rtype: dict
"""
if request.is_ajax():
return {}
context = {
'webmail_url': settings.GVA_LINK_WEBMAIL,
'phpmyadmin_url': settings.GVA_LINK_PHPMYADMIN,
'phppgadmin_url': settings.GVA_LINK_PHPPGADMIN,
'active_item': 'dashboard',
}
if request.resolver_match:
viewfunc = request.resolver_match.func
viewmodule = viewfunc.__module__
if viewmodule == 'contact_form.views':
context['active_item'] = 'contact'
elif viewmodule in (
'hostingpackages.views', 'osusers.views', 'userdbs.views',
'managemails.views', 'websites.views', 'domains.views',
):
context['active_item'] = 'hostingpackage'
elif viewmodule in (
'allauth.account.views', 'allauth.socialaccount.views'
):
context['active_item'] = 'account'
elif (
viewmodule == 'django.contrib.flatpages.views' and
request.path.endswith('/impressum/')
):
context['active_item'] = 'imprint'
else:
_LOGGER.debug(
'no special handling for view %s in module %s, fallback to '
'default active menu item %s',
viewfunc.__name__, viewmodule, context['active_item'])
return context

View file

@ -171,6 +171,8 @@ TEMPLATE_CONTEXT_PROCESSORS = (
# allauth specific context processors # allauth specific context processors
'allauth.account.context_processors.account', 'allauth.account.context_processors.account',
'allauth.socialaccount.context_processors.socialaccount', 'allauth.socialaccount.context_processors.socialaccount',
# custom context processors
'gnuviechadmin.context_processors.navigation',
) )
# See: https://docs.djangoproject.com/en/dev/ref/settings/#template-loaders # See: https://docs.djangoproject.com/en/dev/ref/settings/#template-loaders
@ -237,7 +239,9 @@ DJANGO_APPS = (
# Admin panel and documentation: # Admin panel and documentation:
'django.contrib.admin', 'django.contrib.admin',
# 'django.contrib.admindocs',
# Flatpages for about page
'django.contrib.flatpages',
'crispy_forms', 'crispy_forms',
) )
@ -304,6 +308,15 @@ CRISPY_TEMPLATE_PACK = 'bootstrap3'
LOGGING = { LOGGING = {
'version': 1, 'version': 1,
'disable_existing_loggers': False, 'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '%(levelname)s %(asctime)s %(name)s '
'%(module)s:%(lineno)d %(process)d %(thread)d %(message)s',
},
'simple': {
'format': '%(levelname)s %(name)s:%(lineno)d %(message)s',
},
},
'filters': { 'filters': {
'require_debug_false': { 'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse' '()': 'django.utils.log.RequireDebugFalse'
@ -359,4 +372,8 @@ OSUSER_SFTP_GROUP = 'sftponly'
OSUSER_SSH_GROUP = 'sshusers' OSUSER_SSH_GROUP = 'sshusers'
OSUSER_DEFAULT_GROUPS = [OSUSER_SFTP_GROUP] OSUSER_DEFAULT_GROUPS = [OSUSER_SFTP_GROUP]
OSUSER_UPLOAD_SERVER = get_env_variable('GVA_OSUSER_UPLOADSERVER') OSUSER_UPLOAD_SERVER = get_env_variable('GVA_OSUSER_UPLOADSERVER')
GVA_LINK_WEBMAIL = get_env_variable('GVA_WEBMAIL_URL')
GVA_LINK_PHPMYADMIN = get_env_variable('GVA_PHPMYADMIN_URL')
GVA_LINK_PHPPGADMIN = get_env_variable('GVA_PHPPGADMIN_URL')
########## END CUSTOM APP CONFIGURATION ########## END CUSTOM APP CONFIGURATION

View file

@ -42,6 +42,38 @@ MIDDLEWARE_CLASSES += (
'debug_toolbar.middleware.DebugToolbarMiddleware', 'debug_toolbar.middleware.DebugToolbarMiddleware',
) )
LOGGING['handlers'].update({
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'simple',
}
})
LOGGING['loggers'].update({
'gnuviechadmin': {
'handlers': ['console'], 'level': 'DEBUG', 'propagate': True,},
'dashboard': {
'handlers': ['console'], 'level': 'DEBUG', 'propagate': True,},
'domains': {
'handlers': ['console'], 'level': 'DEBUG', 'propagate': True,},
'gvacommon': {
'handlers': ['console'], 'level': 'DEBUG', 'propagate': True,},
'gvawebcore': {
'handlers': ['console'], 'level': 'DEBUG', 'propagate': True,},
'hostingpackages': {
'handlers': ['console'], 'level': 'DEBUG', 'propagate': True,},
'managemails': {
'handlers': ['console'], 'level': 'DEBUG', 'propagate': True,},
'osusers': {
'handlers': ['console'], 'level': 'DEBUG', 'propagate': True,},
'taskresults': {
'handlers': ['console'], 'level': 'DEBUG', 'propagate': True,},
'userdbs': {
'handlers': ['console'], 'level': 'DEBUG', 'propagate': True,},
'websites': {
'handlers': ['console'], 'level': 'DEBUG', 'propagate': True,},
})
DEBUG_TOOLBAR_PATCH_SETTINGS = False DEBUG_TOOLBAR_PATCH_SETTINGS = False
# http://django-debug-toolbar.readthedocs.org/en/latest/installation.html # http://django-debug-toolbar.readthedocs.org/en/latest/installation.html

View file

@ -18,6 +18,9 @@ EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
# See: https://docs.djangoproject.com/en/dev/ref/settings/#email-subject-prefix # See: https://docs.djangoproject.com/en/dev/ref/settings/#email-subject-prefix
EMAIL_SUBJECT_PREFIX = '[%s] ' % SITE_NAME EMAIL_SUBJECT_PREFIX = '[%s] ' % SITE_NAME
# See: https://docs.djangoproject.com/en/dev/ref/settings/#default-from-email
DEFAULT_FROM_EMAIL = get_env_variable('GVA_SITE_ADMINMAIL')
# See: https://docs.djangoproject.com/en/dev/ref/settings/#server-email # See: https://docs.djangoproject.com/en/dev/ref/settings/#server-email
SERVER_EMAIL = get_env_variable('GVA_SITE_ADMINMAIL') SERVER_EMAIL = get_env_variable('GVA_SITE_ADMINMAIL')
########## END EMAIL CONFIGURATION ########## END EMAIL CONFIGURATION

View file

@ -17,6 +17,11 @@ urlpatterns = patterns(
url(r'^mail/', include('managemails.urls')), url(r'^mail/', include('managemails.urls')),
url(r'^osuser/', include('osusers.urls')), url(r'^osuser/', include('osusers.urls')),
url(r'^admin/', include(admin.site.urls)), url(r'^admin/', include(admin.site.urls)),
url(r'^contact/', include('contact_form.urls')),
)
urlpatterns += patterns(
'django.contrib.flatpages.views',
url(r'^impressum/$', 'flatpage', {'url': '/impressum/'}, name='imprint'),
) )
# Uncomment the next line to serve media files in dev. # Uncomment the next line to serve media files in dev.

View file

@ -12,6 +12,7 @@ from .views import (
CreateCustomerHostingPackage, CreateCustomerHostingPackage,
CreateHostingPackage, CreateHostingPackage,
CustomerHostingPackageDetails, CustomerHostingPackageDetails,
CustomerHostingPackageList,
HostingOptionChoices, HostingOptionChoices,
) )
@ -20,14 +21,16 @@ urlpatterns = patterns(
'', '',
url(r'^create$', CreateHostingPackage.as_view(), url(r'^create$', CreateHostingPackage.as_view(),
name='create_hosting_package'), name='create_hosting_package'),
url(r'^(?P<user>[\w0-9@.+-_]+)/create$',
CreateCustomerHostingPackage.as_view(),
name='create_customer_hosting_package'),
url(r'^(?P<user>[\w0-9@.+-_]+)/hostingpackage/(?P<pk>\d+)/$',
CustomerHostingPackageDetails.as_view(),
name='hosting_package_details'),
url(r'^allpackages/', url(r'^allpackages/',
AllCustomerHostingPackageList.as_view(), name='all_hosting_packages'), AllCustomerHostingPackageList.as_view(), name='all_hosting_packages'),
url(r'^(?P<user>[-\w0-9@.+_]+)/$',
CustomerHostingPackageList.as_view(), name='hosting_packages'),
url(r'^(?P<user>[-\w0-9@.+_]+)/create$',
CreateCustomerHostingPackage.as_view(),
name='create_customer_hosting_package'),
url(r'^(?P<user>[-\w0-9@.+_]+)/(?P<pk>\d+)/$',
CustomerHostingPackageDetails.as_view(),
name='hosting_package_details'),
url(r'^(?P<pk>\d+)/option-choices$', url(r'^(?P<pk>\d+)/option-choices$',
HostingOptionChoices.as_view(), name='hosting_option_choices'), HostingOptionChoices.as_view(), name='hosting_option_choices'),
url(r'^(?P<package>\d+)/add-option/(?P<type>\w+)/(?P<optionid>\d+)$', url(r'^(?P<package>\d+)/add-option/(?P<type>\w+)/(?P<optionid>\d+)$',

View file

@ -103,10 +103,13 @@ class CustomerHostingPackageDetails(StaffOrSelfLoginRequiredMixin, DetailView):
""" """
model = CustomerHostingPackage model = CustomerHostingPackage
context_object_name = 'hostingpackage' context_object_name = 'hostingpackage'
customer = None
def get_customer_object(self): def get_customer_object(self):
return get_object_or_404( if self.customer is None:
self.customer = get_object_or_404(
get_user_model(), username=self.kwargs['user']) get_user_model(), username=self.kwargs['user'])
return self.customer
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(CustomerHostingPackageDetails, self).get_context_data( context = super(CustomerHostingPackageDetails, self).get_context_data(
@ -128,10 +131,39 @@ class CustomerHostingPackageDetails(StaffOrSelfLoginRequiredMixin, DetailView):
class AllCustomerHostingPackageList( class AllCustomerHostingPackageList(
LoginRequiredMixin, StaffuserRequiredMixin, ListView LoginRequiredMixin, StaffuserRequiredMixin, ListView
): ):
"""
This view is used for showing a list of all hosting packages.
"""
model = CustomerHostingPackage model = CustomerHostingPackage
template_name_suffix = '_admin_list' template_name_suffix = '_admin_list'
class CustomerHostingPackageList(StaffOrSelfLoginRequiredMixin, ListView):
"""
This view is used for showing a list of a customer's hosting packages.
"""
model = CustomerHostingPackage
customer = None
def get_customer_object(self):
if self.customer is None:
self.customer = get_object_or_404(
get_user_model(), username=self.kwargs['user'])
return self.customer
def get_context_data(self, **kwargs):
context = super(CustomerHostingPackageList, self).get_context_data(
**kwargs)
context['customer'] = self.get_customer_object()
return context
def get_queryset(self):
return super(CustomerHostingPackageList, self).get_queryset().filter(
customer__username=self.kwargs['user'])
class HostingOptionChoices( class HostingOptionChoices(
LoginRequiredMixin, StaffuserRequiredMixin, DetailView LoginRequiredMixin, StaffuserRequiredMixin, DetailView
): ):

View file

@ -7,8 +7,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: gnuviechadmin\n" "Project-Id-Version: gnuviechadmin\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-01 02:12+0100\n" "POT-Creation-Date: 2015-02-01 19:04+0100\n"
"PO-Revision-Date: 2015-02-01 02:29+0100\n" "PO-Revision-Date: 2015-02-01 19:04+0100\n"
"Last-Translator: Jan Dittberner <jan@dittberner.info>\n" "Last-Translator: Jan Dittberner <jan@dittberner.info>\n"
"Language-Team: Jan Dittberner <jan@dittberner.info>\n" "Language-Team: Jan Dittberner <jan@dittberner.info>\n"
"Language: de\n" "Language: de\n"
@ -181,7 +181,7 @@ msgstr ""
"Mailadresse des Benutzers %(user_display)s ist." "Mailadresse des Benutzers %(user_display)s ist."
#: templates/account/login.html:4 templates/account/login.html.py:5 #: templates/account/login.html:4 templates/account/login.html.py:5
#: templates/account/login.html:29 templates/base.html:67 #: templates/account/login.html:29 templates/base.html:82
#: templates/registration/login.html:4 #: templates/registration/login.html:4
msgid "Sign In" msgid "Sign In"
msgstr "Anmelden" msgstr "Anmelden"
@ -347,7 +347,7 @@ msgid "Signup"
msgstr "Registrieren" msgstr "Registrieren"
#: templates/account/signup.html:5 templates/account/signup.html.py:15 #: templates/account/signup.html:5 templates/account/signup.html.py:15
#: templates/base.html:68 templates/socialaccount/signup.html:5 #: templates/base.html:83 templates/socialaccount/signup.html:5
#: templates/socialaccount/signup.html:15 #: templates/socialaccount/signup.html:15
msgid "Sign Up" msgid "Sign Up"
msgstr "Registrieren" msgstr "Registrieren"
@ -425,37 +425,86 @@ msgstr ""
"<strong>Hinweis:</strong> Sie können Ihre <a href=\"%(email_url)s\">E-" "<strong>Hinweis:</strong> Sie können Ihre <a href=\"%(email_url)s\">E-"
"Mailadresse noch ändern</a>." "Mailadresse noch ändern</a>."
#: templates/base.html:48 #: templates/base.html:44
msgid "Dashboard" msgid "Dashboard"
msgstr "Dashboard" msgstr "Dashboard"
#: templates/base.html:50 #: templates/base.html:50 templates/base.html.py:57
msgid "Hosting"
msgstr "Hosting"
#: templates/base.html:52
#: templates/hostingpackages/customerhostingpackage_list.html:5
#: templates/hostingpackages/customerhostingpackage_list.html:13
msgid "Your hosting packages"
msgstr "Ihre Hostingpakete"
#: templates/base.html:53
#: templates/hostingpackages/customerhostingpackage_admin_list.html:3 #: templates/hostingpackages/customerhostingpackage_admin_list.html:3
#: templates/hostingpackages/customerhostingpackage_admin_list.html:4 #: templates/hostingpackages/customerhostingpackage_admin_list.html:4
msgid "All hosting packages" msgid "All hosting packages"
msgstr "Alle Hostingpakete" msgstr "Alle Hostingpakete"
#: templates/base.html:58 #: templates/base.html:60
msgid "Links"
msgstr "Links"
#: templates/base.html:62
msgid "Web based mail system"
msgstr "Webbasiertes E-Mailsystem"
#: templates/base.html:62
msgid "Webmail"
msgstr "Webmail"
#: templates/base.html:63
msgid "phpMyAdmin - MySQL database administration tool"
msgstr "phpMyAdmin - MySQL-Datenbankverwaltungswerkzeug"
#: templates/base.html:63
msgid "phpMyAdmin"
msgstr "phpMyAdmin"
#: templates/base.html:64
msgid "phpPgAdmin - PostgreSQL database administration tool"
msgstr "phpPgAdmin - PostgreSQL-Datenbankverwaltungswerkzeug"
#: templates/base.html:64
msgid "phpPgAdmin"
msgstr "phpPgAdmin"
#: templates/base.html:67
msgid "Imprint"
msgstr "Impressum"
#: templates/base.html:68 templates/contact_form/contact_form.html:4
#: templates/contact_form/contact_form.html:5
#: templates/contact_form/contact_success.html:4
#: templates/contact_form/contact_success.html:5
msgid "Contact"
msgstr "Kontakt"
#: templates/base.html:73
msgid "My Account" msgid "My Account"
msgstr "Mein Konto" msgstr "Mein Konto"
#: templates/base.html:60 #: templates/base.html:75
msgid "Admin site" msgid "Admin site"
msgstr "Adminsite" msgstr "Adminsite"
#: templates/base.html:61 #: templates/base.html:76
msgid "Change Email" msgid "Change Email"
msgstr "E-Mail ändern" msgstr "E-Mail ändern"
#: templates/base.html:62 #: templates/base.html:77
msgid "Social Accounts" msgid "Social Accounts"
msgstr "Konten in sozialen Netzwerken" msgstr "Konten in sozialen Netzwerken"
#: templates/base.html:63 #: templates/base.html:78
msgid "Logout" msgid "Logout"
msgstr "Abmelden" msgstr "Abmelden"
#: templates/base.html:74 #: templates/base.html:89
#, python-format #, python-format
msgid "" msgid ""
"Signed in as <a href=\"%(profile_url)s\" class=\"navbar-link\" title=\"My " "Signed in as <a href=\"%(profile_url)s\" class=\"navbar-link\" title=\"My "
@ -464,10 +513,14 @@ msgstr ""
"Angemeldet als <a href=\"%(profile_url)s\" class=\"navbar-link\" title=" "Angemeldet als <a href=\"%(profile_url)s\" class=\"navbar-link\" title="
"\"Mein Profil\">%(user_display)s</a>" "\"Mein Profil\">%(user_display)s</a>"
#: templates/base.html:87 #: templates/base.html:102
msgid "Close" msgid "Close"
msgstr "Schließen" msgstr "Schließen"
#: templates/contact_form/contact_success.html:8
msgid "Your message has been sent successfully."
msgstr "Ihre Nachricht wurde erfolgreich übermittelt."
#: templates/dashboard/index.html:3 #: templates/dashboard/index.html:3
msgid "Welcome" msgid "Welcome"
msgstr "Willkommen" msgstr "Willkommen"
@ -514,6 +567,7 @@ msgstr "Hostingpakete"
#: templates/dashboard/user_dashboard.html:15 #: templates/dashboard/user_dashboard.html:15
#: templates/hostingpackages/customerhostingpackage_admin_list.html:11 #: templates/hostingpackages/customerhostingpackage_admin_list.html:11
#: templates/hostingpackages/customerhostingpackage_detail.html:27 #: templates/hostingpackages/customerhostingpackage_detail.html:27
#: templates/hostingpackages/customerhostingpackage_list.html:24
msgid "Name" msgid "Name"
msgstr "Name" msgstr "Name"
@ -567,6 +621,7 @@ msgstr "Diesem Benutzer sind noch keine Hostingpakete zugewiesen."
#: templates/dashboard/user_dashboard.html:45 #: templates/dashboard/user_dashboard.html:45
#: templates/hostingpackages/customerhostingpackage_admin_list.html:30 #: templates/hostingpackages/customerhostingpackage_admin_list.html:30
#: templates/hostingpackages/customerhostingpackage_list.html:41
msgid "Add hosting package" msgid "Add hosting package"
msgstr "Hostingpaket anlegen" msgstr "Hostingpaket anlegen"
@ -589,6 +644,7 @@ msgid "Customer"
msgstr "Kunde" msgstr "Kunde"
#: templates/hostingpackages/customerhostingpackage_admin_list.html:13 #: templates/hostingpackages/customerhostingpackage_admin_list.html:13
#: templates/hostingpackages/customerhostingpackage_list.html:25
msgid "Setup date" msgid "Setup date"
msgstr "Einrichtungsdatum" msgstr "Einrichtungsdatum"
@ -852,6 +908,24 @@ msgstr "Diesem Hostingpaket sind noch keine Datenbanken zugeordnet."
msgid "Add database" msgid "Add database"
msgstr "Datenbank hinzufügen" msgstr "Datenbank hinzufügen"
#: templates/hostingpackages/customerhostingpackage_list.html:7
#, python-format
msgid "Hosting Packages of %(customer)s"
msgstr "Hostingpakete des Kunden %(customer)s"
#: templates/hostingpackages/customerhostingpackage_list.html:15
#, python-format
msgid "Hosting Packages <small>of %(customer)s</small>"
msgstr "Hostingpakete <small>des Kunden %(customer)s</small>"
#: templates/hostingpackages/customerhostingpackage_list.html:38
msgid "You have no hosting packages setup yet."
msgstr "Es wurden noch keine Hostingpakete für Sie eingerichtet."
#: templates/hostingpackages/customerhostingpackage_list.html:38
msgid "There are no hosting packages setup for this customer yet."
msgstr "Es wurden noch keine Hostingpakete für diesen Kunden eingerichtet."
#: templates/hostingpackages/customerhostingpackage_option_choices.html:4 #: templates/hostingpackages/customerhostingpackage_option_choices.html:4
#: templates/hostingpackages/customerhostingpackage_option_choices.html:6 #: templates/hostingpackages/customerhostingpackage_option_choices.html:6
#, python-format #, python-format

View file

@ -6,6 +6,7 @@ from django.utils.html import format_html
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.contrib.admin import AdminSite from django.contrib.admin import AdminSite
from django.contrib.auth import get_user_model
from mock import Mock from mock import Mock
@ -24,6 +25,8 @@ from managemails.models import (
Mailbox, Mailbox,
) )
Customer = get_user_model()
class ReadOnlyPasswordHashWidgetTest(TestCase): class ReadOnlyPasswordHashWidgetTest(TestCase):
def test_render(self): def test_render(self):
@ -52,7 +55,13 @@ class ReadOnlyPasswordHashFieldTest(TestCase):
self.assertFalse(field._has_changed('new', 'old')) self.assertFalse(field._has_changed('new', 'old'))
class MailboxCreationFormTest(TestCase): class CustomerTestCase(TestCase):
def setUp(self):
super(CustomerTestCase, self).setUp()
self.customer = Customer.objects.create(username='test')
class MailboxCreationFormTest(CustomerTestCase):
def test_clean_password2_same(self): def test_clean_password2_same(self):
form = MailboxCreationForm() form = MailboxCreationForm()
form.cleaned_data = {'password1': 'secret', 'password2': 'secret'} form.cleaned_data = {'password1': 'secret', 'password2': 'secret'}
@ -76,7 +85,7 @@ class MailboxCreationFormTest(TestCase):
BROKER_BACKEND='memory' BROKER_BACKEND='memory'
) )
def test_save_commit(self): def test_save_commit(self):
user = User.objects.create_user() user = User.objects.create_user(customer=self.customer)
form = MailboxCreationForm(data={ form = MailboxCreationForm(data={
'osuser': user.uid, 'osuser': user.uid,
'password1': 'secret', 'password1': 'secret',
@ -93,7 +102,7 @@ class MailboxCreationFormTest(TestCase):
BROKER_BACKEND='memory' BROKER_BACKEND='memory'
) )
def test_save_no_commit(self): def test_save_no_commit(self):
user = User.objects.create_user() user = User.objects.create_user(customer=self.customer)
form = MailboxCreationForm(data={ form = MailboxCreationForm(data={
'osuser': user.uid, 'osuser': user.uid,
'password1': 'secret', 'password1': 'secret',
@ -105,14 +114,16 @@ class MailboxCreationFormTest(TestCase):
len(Mailbox.objects.filter(osuser=user)), 0) len(Mailbox.objects.filter(osuser=user)), 0)
class MailboxChangeFormTest(TestCase): class MailboxChangeFormTest(CustomerTestCase):
@override_settings( @override_settings(
CELERY_ALWAYS_EAGER=True, CELERY_ALWAYS_EAGER=True,
CELERY_CACHE_BACKEND='memory', CELERY_CACHE_BACKEND='memory',
BROKER_BACKEND='memory' BROKER_BACKEND='memory'
) )
def test_clean_password(self): def test_clean_password(self):
mailbox = Mailbox(username='test', osuser=User.objects.create_user()) mailbox = Mailbox(
username='test',
osuser=User.objects.create_user(customer=self.customer))
mailbox.set_password('test') mailbox.set_password('test')
mailbox.save() mailbox.save()
form = MailboxChangeForm(instance=mailbox, data={'password': 'blub'}) form = MailboxChangeForm(instance=mailbox, data={'password': 'blub'})
@ -133,8 +144,9 @@ class ActivationChangeMixinTest(TestCase):
querysetmock.update.called_with(active=False) querysetmock.update.called_with(active=False)
class MailBoxAdminTest(TestCase): class MailBoxAdminTest(CustomerTestCase):
def setUp(self): def setUp(self):
super(MailBoxAdminTest, self).setUp()
site = AdminSite() site = AdminSite()
self.mbadmin = MailboxAdmin(Mailbox, site) self.mbadmin = MailboxAdmin(Mailbox, site)
@ -149,7 +161,9 @@ class MailBoxAdminTest(TestCase):
BROKER_BACKEND='memory' BROKER_BACKEND='memory'
) )
def test_get_fieldsets_with_object(self): def test_get_fieldsets_with_object(self):
mailbox = Mailbox(username='test', osuser=User.objects.create_user()) mailbox = Mailbox(
username='test',
osuser=User.objects.create_user(customer=self.customer))
mailbox.set_password('test') mailbox.set_password('test')
mailbox.save() mailbox.save()
self.assertEqual( self.assertEqual(
@ -169,13 +183,15 @@ class MailBoxAdminTest(TestCase):
BROKER_BACKEND='memory' BROKER_BACKEND='memory'
) )
def test_get_form_with_object(self): def test_get_form_with_object(self):
mailbox = Mailbox(username='test', osuser=User.objects.create_user()) mailbox = Mailbox(
username='test',
osuser=User.objects.create_user(customer=self.customer))
mailbox.set_password('test') mailbox.set_password('test')
mailbox.save() mailbox.save()
form = self.mbadmin.get_form(Mock, mailbox) form = self.mbadmin.get_form(Mock, mailbox)
self.assertEqual( self.assertEqual(
form.Meta.fields, form.Meta.fields,
['username', 'password', 'osuser', 'active'] ['osuser', 'username', 'password', 'active']
) )
def test_admin_for_mailbox(self): def test_admin_for_mailbox(self):

View file

@ -1,5 +1,6 @@
from django.test import TestCase from django.test import TestCase
from django.test.utils import override_settings from django.test.utils import override_settings
from django.contrib.auth import get_user_model
from passlib.hash import sha512_crypt from passlib.hash import sha512_crypt
@ -11,6 +12,8 @@ from managemails.models import (
Mailbox, Mailbox,
) )
Customer = get_user_model()
@override_settings( @override_settings(
CELERY_ALWAYS_EAGER=True, CELERY_ALWAYS_EAGER=True,
@ -18,14 +21,18 @@ from managemails.models import (
BROKER_BACKEND='memory' BROKER_BACKEND='memory'
) )
class MailboxTest(TestCase): class MailboxTest(TestCase):
def setUp(self):
super(MailboxTest, self).setUp()
self.customer = Customer.objects.create_user('test')
def test_set_password(self): def test_set_password(self):
user = User.objects.create_user() user = User.objects.create_user(self.customer)
mb = Mailbox.objects.create(username='test', osuser=user) mb = Mailbox.objects.create(username='test', osuser=user)
mb.set_password('test') mb.set_password('test')
self.assertTrue(sha512_crypt.verify('test', mb.password)) self.assertTrue(sha512_crypt.verify('test', mb.password))
def test___str__(self): def test___str__(self):
user = User.objects.create_user() user = User.objects.create_user(self.customer)
mb = Mailbox.objects.create(username='test', osuser=user) mb = Mailbox.objects.create(username='test', osuser=user)
mb.set_password('test') mb.set_password('test')
self.assertEqual(str(mb), 'test') self.assertEqual(str(mb), 'test')

View file

@ -41,31 +41,46 @@
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
</button> </button>
<a class="navbar-brand" href="{% url "dashboard" %}">gnuviechadmin</a> <a class="navbar-brand" href="{% if user.is_authenticated %}{% url 'customer_dashboard' slug=user.username %}{% else %}{% url 'dashboard' %}{% endif %}" title="{% trans "Dashboard" %}">gnuviechadmin</a>
</div> </div>
<div class="collapse navbar-collapse"> <div class="collapse navbar-collapse">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<li class="active"><a href="{% if user.is_authenticated %}{% url 'customer_dashboard' slug=user.username %}{% else %}{% url 'dashboard' %}{% endif %}">{% trans "Dashboard" %}</a></li>
{% if user.is_staff %} {% if user.is_staff %}
<li><a href="{% url 'all_hosting_packages' %}">{% trans "All hosting packages" %}</a></li> <li class="dropdown{% if active_item == 'hostingpackage' %} active{% endif %}">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><i class="fa fa-server"></i> {% trans "Hosting" %} <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="{% url 'hosting_packages' user=user.username %}"><i class="fa fa-cube"></i> {% trans "Your hosting packages" %}</a></li>
<li><a href="{% url 'all_hosting_packages' %}"><i class="fa fa-cubes"></i> {% trans "All hosting packages" %}</a></li>
</ul>
</li>
{% elif user.is_authenticated %}
<li{% if active_item == 'hostingpackage' %} class="active"{% endif %}><a href="{% url 'hosting_packages' user=user.username %}"><i class="fa fa-server"></i> {% trans "Hosting" %}</a></li>
{% endif %} {% endif %}
<li><a href="#about">About</a></li> <li class="dropdown">
<li><a href="#contact">Contact</a></li> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><i class="glyphicon glyphicon-link"></i> {% trans "Links" %} <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="{{ webmail_url }}" title="{% trans "Web based mail system" %}"><i class="fa fa-envelope"></i> {% trans "Webmail" %}</a></li>
<li><a href="{{ phpmyadmin_url }}" title="{% trans "phpMyAdmin - MySQL database administration tool" %}"><i class="icon-mysql"></i> {% trans "phpMyAdmin" %}</a></li>
<li><a href="{{ phppgadmin_url }}" title="{% trans "phpPgAdmin - PostgreSQL database administration tool" %}"><i class="icon-postgres"></i> {% trans "phpPgAdmin" %}</a></li>
</ul>
</li>
<li{% if active_item == 'imprint' %} class="active"{% endif %}><a href="{% url 'imprint' %}"><i class="fa fa-info"></i> {% trans "Imprint" %}</a></li>
<li{% if active_item == 'contact' %} class="active"{% endif %}><a href="{% url 'contact_form' %}"><i class="fa fa-paper-plane"></i> {% trans "Contact" %}</a></li>
</ul> </ul>
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
{% if user.is_authenticated %} {% if user.is_authenticated %}
<li class="dropdown"> <li class="dropdown{% if active_item == 'account' %} active{% endif %}">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{% trans "My Account" %} <span class="caret"></span></a> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><i class="fa fa-user"></i> {% trans "My Account" %} <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu"> <ul class="dropdown-menu" role="menu">
{% if user.is_staff %}<li><a href="{% url 'admin:index' %}">{% trans "Admin site" %}</a></li>{% endif %} {% if user.is_staff %}<li><a href="{% url 'admin:index' %}"><i class="fa fa-wrench"></i> {% trans "Admin site" %}</a></li>{% endif %}
<li><a href="{% url 'account_email' %}">{% trans "Change Email" %}</a></li> <li><a href="{% url 'account_email' %}"><i class="fa fa-at"></i> {% trans "Change Email" %}</a></li>
<li><a href="{% url 'socialaccount_connections' %}">{% trans "Social Accounts" %}</a></li> <li><a href="{% url 'socialaccount_connections' %}"><i class="fa fa-users"></i> {% trans "Social Accounts" %}</a></li>
<li><a href="{% url 'account_logout' %}">{% trans "Logout" %}</a></li> <li><a href="{% url 'account_logout' %}"><i class="fa fa-sign-out"></i> {% trans "Logout" %}</a></li>
</ul> </ul>
</li> </li>
{% else %} {% else %}
<li><a href="{% url 'account_login' %}?next={{ request.path }}">{% trans "Sign In" %}</a></li> <li><a href="{% url 'account_login' %}?next={{ request.path }}"><i class="fa fa-sign-in"></i> {% trans "Sign In" %}</a></li>
<li><a href="{% url 'account_signup' %}">{% trans "Sign Up" %}</a></li> <li><a href="{% url 'account_signup' %}"><i class="fa fa-user-plus"></i> {% trans "Sign Up" %}</a></li>
{% endif %} {% endif %}
</ul> </ul>
{% if user.is_authenticated %} {% if user.is_authenticated %}

View file

@ -0,0 +1 @@
{% extends "base.html" %}

View file

@ -0,0 +1,21 @@
{% extends "contact_form/base.html" %}
{% load i18n crispy_forms_tags %}
{% block title %}{{ block.super }} - {% trans "Contact" %}{% endblock title %}
{% block page_title %}{% trans "Contact" %}{% endblock page_title %}
{% block content %}
{% crispy form %}
{% endblock %}
{% block extra_js %}
<script type="text/javascript">
$(document).ready(function() {
if ($('input[type=text]').first().val() != '') {
$('textarea').first().focus();
} else {
$('input[type=text]').first().focus();
}
});
</script>
{% endblock extra_js %}

View file

@ -0,0 +1,5 @@
User {{ name }} <{{ email }}> from IP address {{ request.META.REMOTE_ADDR }}
sent the following message via the contact form at
{{ site }}{% url 'contact_form' %}:
{{ body }}

View file

@ -0,0 +1 @@
[{{ site.name }}] message from {{ name }} via contact form

View file

@ -0,0 +1,9 @@
{% extends "contact_form/base.html" %}
{% load i18n %}
{% block title %}{{ block.super }} - {% trans "Contact" %}{% endblock title %}
{% block page_title %}{% trans "Contact" %}{% endblock page_title %}
{% block content %}
<p class="text-success">{% trans "Your message has been sent successfully." %}</p>
{% endblock %}

View file

@ -0,0 +1 @@
{% extends "base.html" %}

View file

@ -0,0 +1,9 @@
{% extends "flatpages/base.html" %}
{% block title %}{{ block.super }} - {{ flatpage.title }}{% endblock title %}
{% block page_title %}{{ flatpage.title }}{% endblock page_title %}
{% block content %}
{{ flatpage.content }}
{% endblock content %}

View file

@ -0,0 +1,43 @@
{% extends "hostingpackages/base.html" %}
{% load i18n %}
{% block title %}{{ block.super }} - {% spaceless %}
{% if user == customer %}
{% trans "Your hosting packages" %}
{% else %}
{% blocktrans with customer=customer.get_full_name %}Hosting Packages of {{ customer }}{% endblocktrans %}
{% endif %}
{% endspaceless %}{% endblock title %}
{% block page_title %}{% spaceless %}
{% if user == customer %}
{% trans "Your hosting packages" %}
{% else %}
{% blocktrans with customer=customer.get_full_name %}Hosting Packages <small>of {{ customer }}</small>{% endblocktrans %}
{% endif %}
{% endspaceless %}{% endblock page_title %}
{% block content %}
{% if customerhostingpackage_list %}
<table class="table table-condensed">
<thead>
<tr>
<th>{% trans "Name" %}</th>
<th>{% trans "Setup date" %}</th>
</tr>
</thead>
<tbody>
{% for package in customerhostingpackage_list %}
<tr>
<td><a href="{{ package.get_absolute_url }}">{{ package.name }}</a></td>
<td>{{ package.created }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p class="text-info">{% if user == customer %}{% trans "You have no hosting packages setup yet." %}{% else %}{% trans "There are no hosting packages setup for this customer yet." %}{% endif %}</p>
{% endif %}
{% if user.is_staff %}
<p><a href="{% url 'create_customer_hosting_package' user=customer.username %}" class="btn btn-primary">{% trans "Add hosting package" %}</a></p>
{% endif %}
{% endblock content %}