Merge branch 'feature/add_hosting_options'

* feature/add_hosting_options:
  add german translation for new strings
  add feature description and bugfixes to changelog
  implement adding options to hosting packages
  implement hosting package option choice view
This commit is contained in:
Jan Dittberner 2015-01-25 15:53:19 +01:00
commit 3271690841
12 changed files with 370 additions and 43 deletions

View file

@ -1,6 +1,12 @@
Changelog
=========
* :feature:`-` implement adding options to hosting packages
* :bug:`-` fix disk space calculation in
hostingpackages.models.CustomerHostingPackage
* :bug:`-` fix unique constraints on
hostingpackages.models.CustomerDiskSpaceOption and
hostingpackages.models.CustomerDatabaseOption
* :feature:`-` implement password change functionality for mailboxes
* :feature:`-` implement creation of new mailboxes for hosting packages
* :support:`-` move common form code to new module gvawebcore.forms

View file

@ -14,7 +14,12 @@ from crispy_forms.layout import (
Submit,
)
from .models import CustomerHostingPackage
from .models import (
CustomerDiskSpaceOption,
CustomerHostingPackage,
CustomerMailboxOption,
CustomerUserDatabaseOption,
)
class CreateCustomerHostingPackageForm(forms.ModelForm):
@ -68,3 +73,79 @@ class CreateHostingPackageForm(forms.ModelForm):
'description',
Submit('submit', _('Add Hosting Package')),
)
class AddDiskspaceOptionForm(forms.ModelForm):
class Meta:
model = CustomerDiskSpaceOption
fields = ['diskspace', 'diskspace_unit']
def __init__(self, *args, **kwargs):
self.hostingpackage = kwargs.pop('hostingpackage')
self.option_template = kwargs.pop('option_template')
super(AddDiskspaceOptionForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_action = reverse(
'add_hosting_option',
kwargs={
'package': self.hostingpackage.id,
'type': 'diskspace',
'optionid': self.option_template.id,
})
self.helper.add_input(Submit('submit', _('Add disk space option')))
def save(self, commit=True):
self.instance.hosting_package = self.hostingpackage
self.instance.template = self.option_template
return super(AddDiskspaceOptionForm, self).save(commit=True)
class AddMailboxOptionForm(forms.ModelForm):
class Meta:
model = CustomerMailboxOption
fields = ['number']
def __init__(self, *args, **kwargs):
self.hostingpackage = kwargs.pop('hostingpackage')
self.option_template = kwargs.pop('option_template')
super(AddMailboxOptionForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_action = reverse(
'add_hosting_option',
kwargs={
'package': self.hostingpackage.id,
'type': 'mailboxes',
'optionid': self.option_template.id,
})
self.helper.add_input(Submit('submit', _('Add mailbox option')))
def save(self, commit=True):
self.instance.hosting_package = self.hostingpackage
self.instance.template = self.option_template
return super(AddMailboxOptionForm, self).save(commit=True)
class AddUserDatabaseOptionForm(forms.ModelForm):
class Meta:
model = CustomerUserDatabaseOption
fields = ['number']
def __init__(self, *args, **kwargs):
self.hostingpackage = kwargs.pop('hostingpackage')
self.option_template = kwargs.pop('option_template')
super(AddUserDatabaseOptionForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_action = reverse(
'add_hosting_option',
kwargs={
'package': self.hostingpackage.id,
'type': 'databases',
'optionid': self.option_template.id,
})
self.helper.add_input(Submit('submit', _('Add database option')))
def save(self, commit=True):
self.instance.hosting_package = self.hostingpackage
self.instance.template = self.option_template
self.instance.db_type = self.option_template.db_type
return super(AddUserDatabaseOptionForm, self).save(commit=True)

View file

@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gnuviechadmin hostingpackages\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-25 00:46+0100\n"
"PO-Revision-Date: 2015-01-25 00:55+0100\n"
"POT-Creation-Date: 2015-01-25 15:46+0100\n"
"PO-Revision-Date: 2015-01-25 15:49+0100\n"
"Last-Translator: Jan Dittberner <jan@dittberner.info>\n"
"Language-Team: Jan Dittberner <jan@dittberner.info>\n"
"Language: de\n"
@ -23,10 +23,22 @@ msgstr ""
msgid "Hosting Packages and Options"
msgstr "Hostingpakete und -Optionen"
#: hostingpackages/forms.py:44 hostingpackages/forms.py:69
#: hostingpackages/forms.py:49 hostingpackages/forms.py:74
msgid "Add Hosting Package"
msgstr "Hostingpaket anlegen"
#: hostingpackages/forms.py:95
msgid "Add disk space option"
msgstr "Speicherplatzoption hinzufügen"
#: hostingpackages/forms.py:120
msgid "Add mailbox option"
msgstr "Postfachoption hinzufügen"
#: hostingpackages/forms.py:145
msgid "Add database option"
msgstr "Datenbankoption hinzufügen"
#: hostingpackages/models.py:31
msgid "MiB"
msgstr "MiB"
@ -59,7 +71,7 @@ msgstr "Speicherplatz für das Hostingpaket"
msgid "unit of disk space"
msgstr "Maßeinheit für den Speicherplatz"
#: hostingpackages/models.py:60 hostingpackages/models.py:211
#: hostingpackages/models.py:60 hostingpackages/models.py:213
msgid "name"
msgstr "Name"
@ -71,24 +83,24 @@ msgstr "Hostingpaket"
msgid "Hosting packages"
msgstr "Hostingpakete"
#: hostingpackages/models.py:84
#: hostingpackages/models.py:83
msgid "Disk space option"
msgstr "Speicherplatzoption"
#: hostingpackages/models.py:85
#: hostingpackages/models.py:84
msgid "Disk space options"
msgstr "Speicherplatzoptionen"
#: hostingpackages/models.py:88
#: hostingpackages/models.py:87
#, python-brace-format
msgid "Additional disk space {space} {unit}"
msgstr "Zusätzlicher Speicherplatz {space} {unit}"
#: hostingpackages/models.py:103
#: hostingpackages/models.py:104
msgid "number of databases"
msgstr "Anzahl von Datenbanken"
#: hostingpackages/models.py:105
#: hostingpackages/models.py:106
msgid "database type"
msgstr "Datenbanktyp"
@ -107,101 +119,118 @@ msgid_plural "{count} {type} databases"
msgstr[0] "{type}-Datenbank"
msgstr[1] "{count} {type}-Datenbanken"
#: hostingpackages/models.py:139
#: hostingpackages/models.py:141
msgid "number of mailboxes"
msgstr "Anzahl von Postfächern"
#: hostingpackages/models.py:144
#: hostingpackages/models.py:146
msgid "Mailbox option"
msgstr "Postfachoption"
#: hostingpackages/models.py:145
#: hostingpackages/models.py:147
msgid "Mailbox options"
msgstr "Postfachoptionen"
#: hostingpackages/models.py:149
#: hostingpackages/models.py:151
#, python-brace-format
msgid "{count} additional mailbox"
msgid_plural "{count} additional mailboxes"
msgstr[0] "{count} zusätzliches Postfach"
msgstr[1] "{count} zusätzliche Postfächer"
#: hostingpackages/models.py:204
#: hostingpackages/models.py:206
msgid "customer"
msgstr "Kunde"
#: hostingpackages/models.py:206
#: hostingpackages/models.py:208
msgid "hosting package template"
msgstr "Hostingpaketvorlage"
#: hostingpackages/models.py:208
#: hostingpackages/models.py:210
msgid "The hosting package template that this hosting package is based on"
msgstr "Die Hostingpaketvorlage, auf der dieses Hostingpaket aufgebaut ist"
#: hostingpackages/models.py:213
#: hostingpackages/models.py:215
msgid "Operating system user"
msgstr "Betriebssystemnutzer"
#: hostingpackages/models.py:220
#: hostingpackages/models.py:222
msgid "customer hosting package"
msgstr "Kundenhostingpaket"
#: hostingpackages/models.py:221
#: hostingpackages/models.py:223
msgid "customer hosting packages"
msgstr "Kundenhostingpakete"
#: hostingpackages/models.py:224
#: hostingpackages/models.py:226
#, python-brace-format
msgid "{name} for {customer}"
msgstr "{name} für {customer}"
#: hostingpackages/models.py:403 hostingpackages/models.py:425
#: hostingpackages/models.py:405 hostingpackages/models.py:427
msgid "hosting package"
msgstr "Hostingpaket"
#: hostingpackages/models.py:406
#: hostingpackages/models.py:408
msgid "hosting domain"
msgstr "Hostingdomain"
#: hostingpackages/models.py:428
#: hostingpackages/models.py:430
msgid "customer hosting option"
msgstr "kundenspezifische Hostingoption"
#: hostingpackages/models.py:429
#: hostingpackages/models.py:431
msgid "customer hosting options"
msgstr "kundenspezifische Hostingoptionen"
#: hostingpackages/models.py:441
#: hostingpackages/models.py:443
msgid "disk space option template"
msgstr "Speicherplatzoptionsvorlage"
#: hostingpackages/models.py:443
#: hostingpackages/models.py:445
msgid "The disk space option template that this disk space option is based on"
msgstr ""
"Die Speicherplatzoptionsvorlage auf der diese Speicherplatzoption aufgebaut "
"ist"
#: hostingpackages/models.py:457
#: hostingpackages/models.py:459
msgid "user database option template"
msgstr "Nutzerdatenbankoptionsvorlage"
#: hostingpackages/models.py:459
#: hostingpackages/models.py:461
msgid "The user database option template that this database option is based on"
msgstr ""
"Die Nutzerdatenbankoptionsvorlage auf der diese Datenbankoption aufgebaut ist"
#: hostingpackages/models.py:473
#: hostingpackages/models.py:475
msgid "mailbox option template"
msgstr "Postfachoptionsvorlage"
#: hostingpackages/models.py:475
#: hostingpackages/models.py:477
msgid "The mailbox option template that this mailbox option is based on"
msgstr "Die Postfachoptionsvorlage auf der diese Postfachoption aufgebaut ist"
#: hostingpackages/views.py:48 hostingpackages/views.py:81
#: hostingpackages/views.py:60 hostingpackages/views.py:93
#, python-brace-format
msgid "Started setup of new hosting package {name}."
msgstr "Einrichtung des Hostingpakets {name} wurde gestartet."
#: hostingpackages/views.py:146
msgid "Disk space"
msgstr "Speicherplatz"
#: hostingpackages/views.py:149
msgid "Mailboxes"
msgstr "Postfächer"
#: hostingpackages/views.py:152
msgid "Databases"
msgstr "Datenbanken"
#: hostingpackages/views.py:222
#, python-brace-format
msgid "Successfully added option {option} to hosting package {package}."
msgstr "Option {option} erfolgreich zum Hostingpaket {package} hinzugefügt."
#~ msgid "Hosting options"
#~ msgstr "Hostingoptionen"

View file

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('hostingpackages', '0004_customerhostingpackagedomain'),
]
operations = [
migrations.AlterModelOptions(
name='diskspaceoption',
options={},
),
migrations.AlterUniqueTogether(
name='customerdiskspaceoption',
unique_together=set([]),
),
]

View file

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('hostingpackages', '0005_auto_20150125_1508'),
]
operations = [
migrations.AlterModelOptions(
name='userdatabaseoption',
options={},
),
migrations.AlterUniqueTogether(
name='customeruserdatabaseoption',
unique_together=set([]),
),
]

View file

@ -80,7 +80,6 @@ class DiskSpaceOptionBase(models.Model):
class Meta:
abstract = True
ordering = ['diskspace_unit', 'diskspace']
unique_together = ['diskspace', 'diskspace_unit']
verbose_name = _('Disk space option')
verbose_name_plural = _('Disk space options')
@ -95,6 +94,8 @@ class DiskSpaceOption(DiskSpaceOptionBase, HostingOption):
existing hosting packages.
"""
class Meta:
unique_together = ['diskspace', 'diskspace_unit']
@python_2_unicode_compatible
@ -107,7 +108,6 @@ class UserDatabaseOptionBase(models.Model):
class Meta:
abstract = True
ordering = ['db_type', 'number']
unique_together = ['number', 'db_type']
verbose_name = _('Database option')
verbose_name_plural = _('Database options')
@ -127,6 +127,8 @@ class UserDatabaseOption(UserDatabaseOptionBase, HostingOption):
hosting packages.
"""
class Meta:
unique_together = ['number', 'db_type']
@python_2_unicode_compatible
@ -267,7 +269,7 @@ class CustomerHostingPackage(HostingPackageBase):
options = CustomerDiskSpaceOption.objects.filter(hosting_package=self)
for option in options:
if option.diskspace_unit == min_unit:
diskspace += option.disk_space
diskspace += option.diskspace
elif option.diskspace_unit > min_unit:
diskspace += (
DISK_SPACE_FACTORS[option.diskspace_unit][min_unit] *

View file

@ -7,10 +7,12 @@ from __future__ import absolute_import, unicode_literals
from django.conf.urls import patterns, url
from .views import (
AddHostingOption,
AllCustomerHostingPackageList,
CreateHostingPackage,
CreateCustomerHostingPackage,
CreateHostingPackage,
CustomerHostingPackageDetails,
HostingOptionChoices,
)
@ -25,6 +27,9 @@ urlpatterns = patterns(
CustomerHostingPackageDetails.as_view(),
name='hosting_package_details'),
url(r'^allpackages/',
AllCustomerHostingPackageList.as_view(),
name='all_hosting_packages'),
AllCustomerHostingPackageList.as_view(), name='all_hosting_packages'),
url(r'^(?P<pk>\d+)/option-choices$',
HostingOptionChoices.as_view(), name='hosting_option_choices'),
url(r'^(?P<package>\d+)/add-option/(?P<type>\w+)/(?P<optionid>\d+)$',
AddHostingOption.as_view(), name='add_hosting_option'),
)

View file

@ -5,13 +5,17 @@ This module defines views related to hosting packages.
from __future__ import absolute_import, unicode_literals
from django.conf import settings
from django.http import Http404
from django.shortcuts import redirect, get_object_or_404
from django.utils.translation import ugettext as _
from django.views.generic import (
DetailView,
ListView,
)
from django.views.generic.edit import CreateView
from django.views.generic.edit import (
CreateView,
FormView,
)
from django.contrib import messages
from django.contrib.auth import get_user_model
@ -23,10 +27,18 @@ from braces.views import (
from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin
from .forms import (
AddDiskspaceOptionForm,
AddMailboxOptionForm,
AddUserDatabaseOptionForm,
CreateCustomerHostingPackageForm,
CreateHostingPackageForm,
)
from .models import CustomerHostingPackage
from .models import (
CustomerHostingPackage,
DiskSpaceOption,
MailboxOption,
UserDatabaseOption,
)
class CreateHostingPackage(
@ -111,3 +123,104 @@ class AllCustomerHostingPackageList(
):
model = CustomerHostingPackage
template_name_suffix = '_admin_list'
class HostingOptionChoices(
LoginRequiredMixin, StaffuserRequiredMixin, DetailView
):
"""
This view displays choices of hosting options for a customer hosting
package.
"""
model = CustomerHostingPackage
context_object_name = 'hostingpackage'
template_name_suffix = '_option_choices'
def get_context_data(self, **kwargs):
context = super(HostingOptionChoices, self).get_context_data(
**kwargs)
context.update({
'customer': self.get_object().customer,
'hosting_options': (
(_('Disk space'),
[(option, 'diskspace') for option in
DiskSpaceOption.objects.all()]),
(_('Mailboxes'),
[(option, 'mailboxes') for option in
MailboxOption.objects.all()]),
(_('Databases'),
[(option, 'databases') for option in
UserDatabaseOption.objects.all()]),
),
})
return context
class AddHostingOption(
LoginRequiredMixin, StaffuserRequiredMixin, FormView
):
template_name = 'hostingpackages/add_hosting_option.html'
def get_form_class(self):
optiontype = self.kwargs['type']
if optiontype == 'diskspace':
return AddDiskspaceOptionForm
elif optiontype == 'mailboxes':
return AddMailboxOptionForm
elif optiontype == 'databases':
return AddUserDatabaseOptionForm
raise Http404()
def get_hosting_package(self):
return get_object_or_404(
CustomerHostingPackage, pk=int(self.kwargs['package']))
def get_option_template(self):
optiontype = self.kwargs['type']
optionid = int(self.kwargs['optionid'])
if optiontype == 'diskspace':
return get_object_or_404(DiskSpaceOption, pk=optionid)
elif optiontype == 'mailboxes':
return get_object_or_404(MailboxOption, pk=optionid)
elif optiontype == 'databases':
return get_object_or_404(UserDatabaseOption, pk=optionid)
raise Http404()
def get_form_kwargs(self):
kwargs = super(AddHostingOption, self).get_form_kwargs()
kwargs['hostingpackage'] = self.get_hosting_package()
kwargs['option_template'] = self.get_option_template()
return kwargs
def get_initial(self):
initial = super(AddHostingOption, self).get_initial()
template = self.get_option_template()
if type(template) == DiskSpaceOption:
initial.update({
'diskspace': template.diskspace,
'diskspace_unit': template.diskspace_unit,
})
elif type(template) == MailboxOption:
initial['number'] = template.number
elif type(template) == UserDatabaseOption:
initial['number'] = template.number
else:
raise Http404()
return initial
def get_context_data(self, **kwargs):
context = super(AddHostingOption, self).get_context_data(**kwargs)
context['option_template'] = self.get_option_template()
return context
def form_valid(self, form):
option = form.save()
hosting_package = self.get_hosting_package()
messages.success(
self.request,
_("Successfully added option {option} to hosting package "
"{package}.").format(
option=option, package=hosting_package.name)
)
return redirect(hosting_package)

View file

@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gnuviechadmin\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-25 12:46+0100\n"
"PO-Revision-Date: 2015-01-25 12:49+0100\n"
"POT-Creation-Date: 2015-01-25 15:47+0100\n"
"PO-Revision-Date: 2015-01-25 15:50+0100\n"
"Last-Translator: Jan Dittberner <jan@dittberner.info>\n"
"Language-Team: Jan Dittberner <jan@dittberner.info>\n"
"Language: de\n"
@ -572,6 +572,13 @@ msgid "Add Domain to Hosting Package %(package)s of Customer %(full_name)s"
msgstr ""
"Domain zum Hostingpaket %(package)s des Kunden %(full_name)s hinzufügen"
#: templates/hostingpackages/add_hosting_option.html:3
#: templates/hostingpackages/add_hosting_option.html:4
#, python-format
msgid "Add Option to Hosting Package %(package)s of Customer %(full_name)s"
msgstr ""
"Option zum Hostingpaket %(package)s des Kunden %(full_name)s hinzufügen"
#: templates/hostingpackages/customerhostingpackage_admin_list.html:12
msgid "Customer"
msgstr "Kunde"
@ -793,6 +800,15 @@ msgstr "Diesem Hostingpaket sind noch keine Datenbanken zugeordnet."
msgid "Add database"
msgstr "Datenbank hinzufügen"
#: templates/hostingpackages/customerhostingpackage_option_choices.html:4
#: templates/hostingpackages/customerhostingpackage_option_choices.html:6
#, python-format
msgid ""
"Choose new Option for Hosting Package %(package)s of Customer %(full_name)s"
msgstr ""
"Wählen Sie eine neue Option für das Hostingpaket %(package)s des Kunden "
"%(full_name)s"
#: templates/managemails/mailbox_create.html:6
#: templates/managemails/mailbox_create.html:15
#, python-format

View file

@ -0,0 +1,8 @@
{% extends "hostingpackages/base.html" %}
{% load i18n crispy_forms_tags %}
{% block title %}{{ block.super }} - {% blocktrans with package=hostingpackage.name full_name=customer.get_full_name %}Add Option to Hosting Package {{ package }} of Customer {{ full_name }}{% endblocktrans %}{% endblock title %}
{% block page_title %}{% blocktrans with package=hostingpackage.name full_name=customer.get_full_name %}Add Option to Hosting Package {{ package }} of Customer {{ full_name }}{% endblocktrans %}{% endblock page_title %}
{% block content %}
{% crispy form %}
{% endblock content %}

View file

@ -58,7 +58,7 @@
<p class="panel-body text-info">{% trans "No options booked" %}</p>
{% endif %}
{% if user.is_staff %}
<p class="panel-body"><a class="btn btn-primary" href="#" title="{% trans "Add another hosting option" %}">{% trans "Add option" %}</a></p>
<p class="panel-body"><a class="btn btn-primary" href="{% url 'hosting_option_choices' pk=hostingpackage.id %}" title="{% trans "Add another hosting option" %}">{% trans "Add option" %}</a></p>
{% endif %}
</div>
</div>

View file

@ -0,0 +1,23 @@
{% extends "hostingpackages/base.html"%}
{% load i18n %}
{% block title %}{{ block.super }} - {% blocktrans with package=hostingpackage.name full_name=customer.get_full_name %}Choose new Option for Hosting Package {{ package }} of Customer {{ full_name }}{% endblocktrans %}{% endblock title %}
{% block page_title %}{% blocktrans with package=hostingpackage.name full_name=customer.get_full_name %}Choose new Option for Hosting Package {{ package }} of Customer {{ full_name }}{% endblocktrans %}{% endblock page_title %}
{% block content %}
<div class="row">
{% for label, items in hosting_options %}
<div class="col-lg-4 col-md-4 col-xs-12">
<div class="panel panel-default">
<div class="panel-heading">{{ label }}</div>
<ul class="list-group">
{% for item, option_type in items %}
<li class="list-group-item"><a href="{% url 'add_hosting_option' package=hostingpackage.id type=option_type optionid=item.id %}"><i class="glyphicon glyphicon-plus-sign"></i> {{ item }}</a></li>
{% endfor %}
</ul>
</div>
</div>
{% endfor %}
</div>
{% endblock %}