Merge branch 'release/0.8.0' into production

* release/0.8.0:
  add release version in changelog, update conf.py
  add german translation for new strings
  implement user database deletion
  improve table layout on hosting package detail page
  implement database user password change
  link from hostingpackages details to add_userdatabase
  implement setup of new user databases
  add combined method for creating databases with users
  performance optimizations for hosting package detail view
  move HostingPackageAndCustomerMixin to gvawebcore.views
This commit is contained in:
Jan Dittberner 2015-01-26 13:56:15 +01:00
commit 3b248f650c
20 changed files with 619 additions and 68 deletions

View file

@ -1,6 +1,14 @@
Changelog
=========
* :release:`0.8.0 <2015-01-26>`
* :feature:`-` implement deletion of user database and database users
* :feature:`-` implement password changes for database users
* :feature:`-` implement setup of user databases
* :support:`-` performance improvement for hosting package detail view
* :support:`-` move HostingPackageAndCustomerMixin from managemails.views to
gvawebcore.views
* :release:`0.7.0 <2015-01-25>`
* :feature:`-` implement mail address target editing
* :feature:`-` implement mail address deletion

View file

@ -9,3 +9,10 @@
.. automodule:: gvawebcore.forms
:members:
:py:mod:`views <gvawebcore.views>`
----------------------------------
.. automodule:: gvawebcore.views
:members:

View file

@ -17,6 +17,13 @@
.. automodule:: userdbs.apps
:py:mod:`forms <userdbs.forms>`
-------------------------------
.. automodule:: userdbs.forms
:members:
:py:mod:`models <userdbs.models>`
---------------------------------
@ -34,3 +41,17 @@
.. automodule:: userdbs.templatetags.userdb
:members:
:py:mod:`urls <userdbs.urls>`
-----------------------------
.. automodule:: userdbs.urls
:members:
:py:mod:`views <userdbs.views>`
-------------------------------
.. automodule:: userdbs.views
:members:

View file

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

View file

@ -10,6 +10,7 @@ urlpatterns = patterns(
'',
url(r'', include('dashboard.urls')),
url(r'^accounts/', include('allauth.urls')),
url(r'^database/', include('userdbs.urls')),
url(r'^domains/', include('domains.urls')),
url(r'^hosting/', include('hostingpackages.urls')),
url(r'^mail/', include('managemails.urls')),

View file

@ -0,0 +1,26 @@
"""
This module defines common view code to be used by multiple gnuviechadmin apps.
"""
from __future__ import absolute_import, unicode_literals
from django.shortcuts import get_object_or_404
from hostingpackages.models import CustomerHostingPackage
class HostingPackageAndCustomerMixin(object):
"""
Mixin for views that gets the hosting package instance from the URL
keyword argument 'package'.
"""
hosting_package_kwarg = 'package'
"""Keyword argument used to find the hosting package in the URL."""
def get_hosting_package(self):
return get_object_or_404(
CustomerHostingPackage,
pk=int(self.kwargs[self.hosting_package_kwarg]))
def get_customer_object(self):
return self.get_hosting_package().customer

View file

@ -114,6 +114,12 @@ class CustomerHostingPackageDetails(StaffOrSelfLoginRequiredMixin, DetailView):
context.update({
'customer': self.get_customer_object(),
'uploadserver': settings.OSUSER_UPLOAD_SERVER,
'databases': context['hostingpackage'].databases,
'osuser': context['hostingpackage'].osuser,
'hostingoptions':
context['hostingpackage'].get_hostingoptions(),
'domains': context['hostingpackage'].domains.all(),
'mailboxes': context['hostingpackage'].mailboxes,
})
return context

View file

@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gnuviechadmin\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-25 22:13+0100\n"
"PO-Revision-Date: 2015-01-25 22:20+0100\n"
"POT-Creation-Date: 2015-01-26 13:42+0100\n"
"PO-Revision-Date: 2015-01-26 13:51+0100\n"
"Last-Translator: Jan Dittberner <jan@dittberner.info>\n"
"Language-Team: Jan Dittberner <jan@dittberner.info>\n"
"Language: de\n"
@ -785,30 +785,38 @@ msgid "Add mailbox"
msgstr "Postfach hinzufügen"
#: templates/hostingpackages/customerhostingpackage_detail.html:168
msgid "Database type"
msgstr "Datenbanktyp"
#: templates/hostingpackages/customerhostingpackage_detail.html:168
msgid "Type"
msgstr "Typ"
#: templates/hostingpackages/customerhostingpackage_detail.html:169
msgid "Database name"
msgstr "Datenbankname"
#: templates/hostingpackages/customerhostingpackage_detail.html:170
#: templates/hostingpackages/customerhostingpackage_detail.html:169
msgid "Database user"
msgstr "Datenbanknutzer"
#: templates/hostingpackages/customerhostingpackage_detail.html:170
msgid "Database type"
msgstr "Datenbanktyp"
#: templates/hostingpackages/customerhostingpackage_detail.html:170
msgid "Type"
msgstr "Typ"
#: templates/hostingpackages/customerhostingpackage_detail.html:171
msgid "Database actions"
msgstr "Datenbankaktionen"
#: templates/hostingpackages/customerhostingpackage_detail.html:186
#: templates/hostingpackages/customerhostingpackage_detail.html:181
msgid "Set database user password"
msgstr "Datenbanknutzerpasswort setzen"
#: templates/hostingpackages/customerhostingpackage_detail.html:182
msgid "Delete database"
msgstr "Datenbank löschen"
#: templates/hostingpackages/customerhostingpackage_detail.html:189
msgid "There are no databases assigned to this hosting package yet."
msgstr "Diesem Hostingpaket sind noch keine Datenbanken zugeordnet."
#: templates/hostingpackages/customerhostingpackage_detail.html:189
#: templates/hostingpackages/customerhostingpackage_detail.html:192
msgid "Add database"
msgstr "Datenbank hinzufügen"
@ -839,10 +847,12 @@ msgid "Do you really want to delete the mail address %(mailaddress)s?"
msgstr "Wollen Sie die E-Mailadresse %(mailaddress)s wirklich löschen?"
#: templates/managemails/mailaddress_confirm_delete.html:28
#: templates/userdbs/userdatabase_confirm_delete.html:29
msgid "Yes, do it!"
msgstr "Ja, so soll es sein!"
#: templates/managemails/mailaddress_confirm_delete.html:29
#: templates/userdbs/userdatabase_confirm_delete.html:30
msgid "Cancel"
msgstr "Abbrechen"
@ -1021,6 +1031,93 @@ msgstr ""
"%(site_name)s zu nutzen. Als letzten Schritt füllen Sie bitte folgendes "
"Formular aus:"
#: templates/userdbs/databaseuser_setpassword.html:6
#, python-format
msgid "Set Database User Password for %(dbuser)s"
msgstr "Neues Datenbanknutzerpasswort für %(dbuser)s setzen"
#: templates/userdbs/databaseuser_setpassword.html:8
#, python-format
msgid "Set Database User Password for %(dbuser)s of Customer %(full_name)s"
msgstr ""
"Neues Datenbanknutzerpasswort für %(dbuser)s des Kunden %(full_name)s setzen."
#: templates/userdbs/databaseuser_setpassword.html:14
#, python-format
msgid "Set Database User Password <small>for %(dbuser)s</small>"
msgstr "Datenbanknutzerpasswort setzen <small>für %(dbuser)s</small>"
#: templates/userdbs/databaseuser_setpassword.html:16
#, python-format
msgid ""
"Set Database User Password <small>for %(dbuser)s of Customer %(full_name)s</"
"small>"
msgstr ""
"Neues Datenbanknutzerpasswort setzen <small>für %(dbuser)s des Kunden "
"%(full_name)s</small>"
#: templates/userdbs/databaseuser_setpassword.html:21
msgid "Please specify the new password for your database user."
msgstr "Bitte geben Sie das neue Passwort für Ihren Datenbanknutzer ein."
#: templates/userdbs/databaseuser_setpassword.html:21
msgid "Please specify the new password of the database user."
msgstr "Bitte geben Sie das neue Passwort für den Datenbanknutzer ein."
#: templates/userdbs/userdatabase_confirm_delete.html:6
#, python-format
msgid "Delete Database %(database)s"
msgstr "Datenbank %(database)s löschen"
#: templates/userdbs/userdatabase_confirm_delete.html:8
#, python-format
msgid "Delete Database %(database)s of customer %(full_name)s"
msgstr "Datenbank %(database)s des Kunden %(full_name)s löschen"
#: templates/userdbs/userdatabase_confirm_delete.html:14
#, python-format
msgid "Delete Database <small>%(database)s</small>"
msgstr "Datenbank löschen <small>%(database)s</small>"
#: templates/userdbs/userdatabase_confirm_delete.html:16
#, python-format
msgid "Delete Database <small>%(database)s of customer %(full_name)s</small>"
msgstr "Datenbank löschen <small>%(database)s des Kunden %(full_name)s</small>"
#: templates/userdbs/userdatabase_confirm_delete.html:23
#, python-format
msgid "Do you really want to delete the database %(database)s?"
msgstr "Wollen Sie die Datenbank %(database)s wirklich löschen?"
#: templates/userdbs/userdatabase_confirm_delete.html:26
msgid ""
"When you confirm the deletion the database will be removed from the database "
"server. <strong>All data in the database will be lost!</strong> If the "
"database user assigned to that database has no other databases assigned it "
"will be deleted too."
msgstr ""
"Wenn Sie die Löschung bestätigen, wird die Datenbank vom Datenbankserver "
"entfernt. <strong>Alle Daten in der Datenbank gehen verloren!</strong> Wenn "
"dem zu dieser Datenbank gehörigen Datenbanknutzer keine weiteren Datenbanken "
"zugeordnet sind, wird er ebenfalls gelöscht."
#: templates/userdbs/userdatabase_create.html:6
#: templates/userdbs/userdatabase_create.html:14
msgid "Add new Database"
msgstr "Neue Datenbank hinzufügen"
#: templates/userdbs/userdatabase_create.html:8
#: templates/userdbs/userdatabase_create.html:16
#, python-format
msgid "Add new Database for Customer %(full_name)s"
msgstr "Neue Datenbank für Kunde %(full_name)s anlegen"
#: templates/userdbs/userdatabase_create.html:21
msgid "Please enter a password for a new database user for your database."
msgstr ""
"Bitte geben Sie ein Passwort für den neuen Datenbanknutzer für Ihre "
"Datenbank ein."
#, fuzzy
#~| msgid "Password Reset"
#~ msgid "Password (again)"

View file

@ -15,8 +15,7 @@ from django.views.generic.edit import (
from django.contrib import messages
from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin
from hostingpackages.models import CustomerHostingPackage
from gvawebcore.views import HostingPackageAndCustomerMixin
from domains.models import MailDomain
from .forms import (
@ -33,25 +32,6 @@ from .models import (
)
class HostingPackageAndCustomerMixin(object):
"""
Mixin for views that gets the hosting package instance from the URL
keyword argument 'package'.
"""
hosting_package_kwarg = 'package'
"""Keyword argument used to find the hosting package in the URL."""
def get_hosting_package(self):
return get_object_or_404(
CustomerHostingPackage,
pk=int(self.kwargs[self.hosting_package_kwarg]))
def get_customer_object(self):
return self.get_hosting_package().customer
class CreateMailbox(
HostingPackageAndCustomerMixin, StaffOrSelfLoginRequiredMixin, CreateView
):

View file

@ -1 +1,10 @@
/*! project specific CSS goes here. */
table thead th.actions-column {
width: 5em;
}
table thead th.status-column {
width: 5em;
}
table thead th.name-column {
width: 15em;
}

View file

@ -38,8 +38,8 @@
<dt>{% trans "Mailboxes" %}</dt>
<dd>{% blocktrans with num=hostingpackage.used_mailbox_count total=hostingpackage.mailbox_count %}{{ num }} of {{ total }} in use{% endblocktrans %} <span class="glyphicon
glyphicon-info-sign" title="{% blocktrans with mailboxes=hostingpackage.mailboxcount %}The package provides {{ mailboxcount }} mailboxes the difference comes from mailbox options.{% endblocktrans %}"></span></dd>
<dt>{% if hostingpackage.osuser.is_sftp_user %}{% trans "SFTP username" %}{% else %}{% trans "SSH/SFTP username" %}{% endif %}</dt>
<dd>{{ hostingpackage.osuser.username }}</dd>
<dt>{% if osuser.is_sftp_user %}{% trans "SFTP username" %}{% else %}{% trans "SSH/SFTP username" %}{% endif %}</dt>
<dd>{{ osuser.username }}</dd>
<dt>{% trans "Upload server" %}</dt>
<dd>{{ uploadserver }}</dd>
</dl>
@ -48,9 +48,9 @@
<div class="col-lg-4 col-md-6 col-xs-12">
<div class="panel panel-default">
<div class="panel-heading">{% trans "Hosting Package Options" %}</div>
{% if hostingpackage.customerhostingpackageoption_set.exists %}
{% if hostingoptions %}
<ul class="list-group">
{% for opt in hostingpackage.get_hostingoptions %}
{% for opt in hostingoptions %}
<li class="list-group-item">{{ opt }}</li>
{% endfor %}
</ul>
@ -67,7 +67,7 @@
<div class="panel-heading">{% trans "Hosting Package Actions" %}</div>
<ul class="list-group">
<li class="list-group-item"><a href="#" title="{% trans "Edit Hosting Package Description" %}">{% trans "Edit description" %}</a></li>
<li class="list-group-item"><a href="{% url "set_osuser_password" slug=hostingpackage.osuser.username %}">{% if hostingpackage.osuser.is_sftp %}{% trans "Set SFTP password" %}{% else %}{% trans "Set SSH/SFTP password" %}{% endif %}</a></li>
<li class="list-group-item"><a href="{% url "set_osuser_password" slug=osuser.username %}">{% if osuser.is_sftp %}{% trans "Set SFTP password" %}{% else %}{% trans "Set SSH/SFTP password" %}{% endif %}</a></li>
</ul>
</div>
</div>
@ -76,18 +76,18 @@
<div class="col-lg-12 col-md-12 col-xs-12">
<div class="panel panel-default">
<div class="panel-heading">{% trans "Domains" %}</div>
{% if hostingpackage.domains %}
{% if domains %}
<table class="table table-condensed">
<thead>
<tr>
<th>{% trans "Domain name" %}</th>
<th class="name-column">{% trans "Domain name" %}</th>
<th>{% trans "Mail addresses" %}</th>
<th>{% trans "Websites" %}</th>
<th title="{% trans "Domain actions" %}"><span class="sr-only">{% trans "Actions" %}</span></th>
<th title="{% trans "Domain actions" %}" class="actions-column"><span class="sr-only">{% trans "Actions" %}</span></th>
</tr>
</thead>
<tbody>
{% for domain in hostingpackage.domains.all %}
{% for domain in domains %}
<tr>
<td>{{ domain.domain }}</td>
{% if domain.domain.maildomain %}
@ -126,18 +126,18 @@
<div class="col-lg-12 col-md-12 col-xs-12">
<div class="panel panel-default">
<div class="panel-heading">{% trans "E-Mail-Accounts" %}</div>
{% if hostingpackage.mailboxes %}
{% if mailboxes %}
<table class="table table-condensed">
<thead>
<tr>
<th>{% trans "Mailbox" %}</th>
<th class="name-column">{% trans "Mailbox" %}</th>
<th>{% trans "Mail addresses" %}</th>
<th>{% trans "Active" %}</th>
<th title="{% trans "Mailbox actions" %}"><span class="sr-only">{% trans "Actions" %}</span></th>
<th class="status-column">{% trans "Active" %}</th>
<th title="{% trans "Mailbox actions" %}" class="actions-column"><span class="sr-only">{% trans "Actions" %}</span></th>
</tr>
</thead>
<tbody>
{% for mailbox in hostingpackage.mailboxes %}
{% for mailbox in mailboxes %}
<tr>
<td>{{ mailbox.username }}</td>
<td>{{ mailbox.mailaddresses|join:", " }}</td>
@ -161,23 +161,26 @@
<div class="col-lg-12 col-md-12 col-xs-12">
<div class="panel panel-default">
<div class="panel-heading">{% trans "Databases" %}</div>
{% if hostingpackage.databases %}
{% if databases %}
<table class="table table-condensed">
<thead>
<tr>
<th class="name-column">{% trans "Database name" %}</th>
<th class="name-column">{% trans "Database user" %}</th>
<th title="{% trans "Database type" %}"><span class="sr-only">{% trans "Type" %}</span></th>
<th>{% trans "Database name" %}</th>
<th>{% trans "Database user" %}</th>
<th title="{% trans "Database actions" %}"><span class="sr-only">{% trans "Actions" %}</span></th>
<th title="{% trans "Database actions" %}" class="actions-column"><span class="sr-only">{% trans "Actions" %}</span></th>
</tr>
</thead>
<tbody>
{% for database in hostingpackage.databases %}
{% for database in databases %}
<tr>
<td>{% include "userdbs/snippets/db_type.html" with db_type=database.db_user.db_type %}</td>
<td>{{ database.db_name }}</td>
<td>{{ database.db_user.username }}</td>
<td></td>
<td>{{ database.db_user.name }}</td>
<td>{% include "userdbs/snippets/db_type.html" with db_type=database.db_user.db_type %}</td>
<td>
<a href="{% url 'change_dbuser_password' package=hostingpackage.id slug=database.db_user.name %}" title="{% trans "Set database user password" %}"><i class="fa fa-user-secret"></i><span class="sr-only"> {% trans "Set database user password" %}</span></a>
<a href="{% url 'delete_userdatabase' package=hostingpackage.id slug=database.db_name %}" title="{% trans "Delete database" %}"><i class="glyphicon glyphicon-trash"></i><span class="sr-only">{% trans "Delete database" %}</span></a>
</td>
</tr>
{% endfor %}
</tbody>
@ -186,7 +189,7 @@
<p class="panel-body text-info">{% trans "There are no databases assigned to this hosting package yet." %}</p>
{% endif %}
{% if hostingpackage.may_add_database %}
<p class="panel-body"><a href="#" class="btn btn-primary">{% trans "Add database" %}</a></p>
<p class="panel-body"><a href="{% url 'add_userdatabase' package=hostingpackage.id %}" class="btn btn-primary">{% trans "Add database" %}</a></p>
{% endif %}
</div>
</div>

View file

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

View file

@ -0,0 +1,31 @@
{% extends "userdbs/base.html" %}
{% load i18n crispy_forms_tags %}
{% block title %}{{ block.super }} - {% spaceless %}
{% if customer == user %}
{% blocktrans with dbuser=dbuser.name %}Set Database User Password for {{ dbuser }}{% endblocktrans %}
{% else %}
{% blocktrans with dbuser=dbuser.name full_name=customer.get_full_name %}Set Database User Password for {{ dbuser }} of Customer {{ full_name }}{% endblocktrans %}
{% endif %}
{% endspaceless %}{% endblock title %}
{% block page_title %}{% spaceless %}
{% if customer == user %}
{% blocktrans with dbuser=dbuser.name %}Set Database User Password <small>for {{ dbuser }}</small>{% endblocktrans %}
{% else %}
{% blocktrans with dbuser=dbuser.name full_name=customer.get_full_name %}Set Database User Password <small>for {{ dbuser }} of Customer {{ full_name }}</small>{% endblocktrans %}
{% endif %}
{% endspaceless %}{% endblock page_title %}
{% block content %}
<p>{% if customer == user %}{% trans "Please specify the new password for your database user." %}{% else %}{% trans "Please specify the new password of the database user." %}{% endif %}
{% crispy form %}
{% endblock content %}
{% block extra_js %}
<script type="text/javascript">
$(document).ready(function() {
$('input[type=password]').val('').first().focus();
});
</script>
{% endblock extra_js %}

View file

@ -0,0 +1,34 @@
{% extends "userdbs/base.html" %}
{% load i18n %}
{% block title %}{{ block.super }} - {% spaceless %}
{% if user == customer %}
{% blocktrans with database=database.db_name %}Delete Database {{ database }}{% endblocktrans %}
{% else %}
{% blocktrans with database=database.db_name full_name=customer.get_full_name %}Delete Database {{ database }} of customer {{ full_name }}{% endblocktrans %}
{% endif %}
{% endspaceless %}{% endblock title %}
{% block page_title %}{% spaceless %}
{% if user == customer %}
{% blocktrans with database=database.db_name %}Delete Database <small>{{ database }}</small>{% endblocktrans %}
{% else %}
{% blocktrans with database=database.db_name full_name=customer.get_full_name %}Delete Database <small>{{ database }} of customer {{ full_name }}</small>{% endblocktrans %}
{% endif %}
{% endspaceless %}{% endblock page_title %}
{% block content %}
<div class="panel panel-warning">
<div class="panel-heading">
{% blocktrans with database=database.db_name %}Do you really want to delete the database {{ database }}?{% endblocktrans %}
</div>
<div class="panel-body form">
<p>{% blocktrans %}When you confirm the deletion the database will be removed from the database server. <strong>All data in the database will be lost!</strong> If the database user assigned to that database has no other databases assigned it will be deleted too.{% endblocktrans %}</p>
<form action="{% url 'delete_userdatabase' package=hostingpackage.id slug=database.db_name %}" method="post">
{% csrf_token %}
<input class="btn btn-warning" type="submit" value="{% trans "Yes, do it!" %}" />
<a class="btn btn-default" href="{{ hostingpackage.get_absolute_url }}">{% trans "Cancel" %}</a>
</form>
</div>
</div>
{% endblock content %}

View file

@ -0,0 +1,31 @@
{% extends "userdbs/base.html" %}
{% load i18n crispy_forms_tags %}
{% block title %}{{ block.super }} - {% spaceless %}
{% if user == customer %}
{% blocktrans %}Add new Database{% endblocktrans %}
{% else %}
{% blocktrans with full_name=customer.get_full_name %}Add new Database for Customer {{ full_name }}{% endblocktrans %}
{% endif %}
{% endspaceless %}{% endblock title %}
{% block page_title %}{% spaceless %}
{% if user == customer %}
{% blocktrans %}Add new Database{% endblocktrans %}
{% else %}
{% blocktrans with full_name=customer.get_full_name %}Add new Database for Customer {{ full_name }}{% endblocktrans %}
{% endif %}
{% endspaceless %}{% endblock page_title %}
{% block content %}
<p>{% blocktrans %}Please enter a password for a new database user for your database.{% endblocktrans %}</p>
{% crispy form %}
{% endblock content %}
{% block extra_js %}
<script type="text/javascript">
$(document).ready(function() {
$('input[type=password]').val('').first().focus();
});
</script>
{% endblock %}

View file

@ -0,0 +1,92 @@
"""
This module defines form classes for user database editing.
"""
from __future__ import absolute_import, unicode_literals
from django import forms
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from crispy_forms.helper import FormHelper
from crispy_forms.layout import (
Submit,
)
from .models import (
DB_TYPES,
DatabaseUser,
UserDatabase,
)
from gvawebcore.forms import PasswordModelFormMixin
class AddUserDatabaseForm(forms.ModelForm, PasswordModelFormMixin):
"""
This form is used to create new user database instances.
"""
db_type = forms.TypedChoiceField(
label=_('Database type'),
choices=DB_TYPES,
widget=forms.RadioSelect,
coerce=int,
)
class Meta:
model = UserDatabase
fields = []
def __init__(self, *args, **kwargs):
self.hosting_package = kwargs.pop('hostingpackage')
self.available_dbtypes = kwargs.pop('dbtypes')
super(AddUserDatabaseForm, self).__init__(*args, **kwargs)
self.fields['db_type'].choices = self.available_dbtypes
if len(self.available_dbtypes) == 1:
self.fields['db_type'].initial = self.available_dbtypes[0][0]
self.fields['db_type'].widget = forms.HiddenInput()
self.helper = FormHelper()
self.helper.form_action = reverse(
'add_userdatabase', kwargs={'package': self.hosting_package.id})
self.helper.add_input(Submit('submit', _('Create database')))
def save(self, commit=True):
"""
Setup a new database with a new database user with the specified
password.
:param boolean commit: whether to save the created database
:return: database instance
:rtype: :py:class:`userdbs.models.UserDatabase`
"""
data = self.cleaned_data
self.instance = UserDatabase.objects.create_userdatabase_with_user(
data['db_type'], self.hosting_package.osuser,
password=data['password1'], commit=commit)
return super(AddUserDatabaseForm, self).save(commit)
class ChangeDatabaseUserPasswordForm(forms.ModelForm, PasswordModelFormMixin):
"""
This form is used to change the password of a database user.
"""
class Meta:
model = DatabaseUser
fields = []
def __init__(self, *args, **kwargs):
self.hosting_package = kwargs.pop('hostingpackage')
super(ChangeDatabaseUserPasswordForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_action = reverse(
'change_dbuser_password', kwargs={
'slug': self.instance.name,
'package': self.hosting_package.id,
})
self.helper.add_input(Submit('submit', _('Set password')))
def save(self, commit=True):
self.instance.set_password(self.cleaned_data['password1'])
return super(ChangeDatabaseUserPasswordForm, self).save()

View file

@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gnuviechadmin userdbs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-17 15:59+0100\n"
"PO-Revision-Date: 2015-01-17 16:00+0100\n"
"POT-Creation-Date: 2015-01-26 13:42+0100\n"
"PO-Revision-Date: 2015-01-26 13:44+0100\n"
"Last-Translator: Jan Dittberner <jan@dittberner.info>\n"
"Language-Team: Jan Dittberner <jan@dittberner.info>\n"
"Language: de\n"
@ -31,6 +31,18 @@ msgstr "Ausgewählte Nutzerdatenbanken löschen"
msgid "Database Users and their Databases"
msgstr "Datenbanknutzer und ihre Datenbanken"
#: userdbs/forms.py:30
msgid "Database type"
msgstr "Datenbanktyp"
#: userdbs/forms.py:51
msgid "Create database"
msgstr "Datenbank anlegen"
#: userdbs/forms.py:88
msgid "Set password"
msgstr "Passwort setzen"
#: userdbs/models.py:32
msgid "PostgreSQL"
msgstr "PostgreSQL"
@ -47,7 +59,7 @@ msgstr "Benutzername"
msgid "database type"
msgstr "Datenbanktyp"
#: userdbs/models.py:114 userdbs/models.py:230
#: userdbs/models.py:114 userdbs/models.py:250
msgid "database user"
msgstr "Datenbanknutzer"
@ -55,14 +67,29 @@ msgstr "Datenbanknutzer"
msgid "database users"
msgstr "Datenbanknutzer"
#: userdbs/models.py:229
#: userdbs/models.py:249
msgid "database name"
msgstr "Datenbankname"
#: userdbs/models.py:236
#: userdbs/models.py:256
msgid "user database"
msgstr "Nutzerdatenbank"
#: userdbs/models.py:237
#: userdbs/models.py:257
msgid "user specific database"
msgstr "nutzerspezifische Datenbank"
#: userdbs/views.py:63
#, python-brace-format
msgid "Successfully create new {type} database {dbname} for user {dbuser}"
msgstr ""
"Neue {type}-Datenbank {dbname} für Benutzer {dbuser} erfolgreich angelegt"
#: userdbs/views.py:100
#, python-brace-format
msgid "Successfully changed password of database user {dbuser}"
msgstr "Passwort des Datenbanknutzers {dbuser} wurde erfolgreich geändert."
#: userdbs/views.py:129
msgid "Database deleted"
msgstr "Datenbank gelöscht"

View file

@ -202,6 +202,26 @@ class UserDatabaseManager(models.Manager):
break
return nextname
@transaction.atomic
def create_userdatabase_with_user(
self, db_type, osuser, password=None, commit=True):
"""
Creates a new user database with a new user.
:param db_type: database type from :py:data:`DB_TYPES`
:param osuser: :py:class:`osusers.models.OsUser` instance
:param str password: the password of the new database user
:param boolean commit: whether the user and the database should be
persisted
:return: database instance
:rtype: :py:class:`UserDatabase`
"""
dbuser = DatabaseUser.objects.create_database_user(
osuser, db_type, password=password, commit=commit)
database = self.create_userdatabase(dbuser, commit=commit)
return database
@transaction.atomic
def create_userdatabase(self, db_user, db_name=None, commit=True):
"""
@ -267,10 +287,13 @@ class UserDatabase(TimeStampedModel, models.Model):
:py:meth:`django.db.models.Model.delete`
"""
if self.db_user.db_type == DB_TYPES.pgsql:
db_user = self.db_user
if db_user.db_type == DB_TYPES.pgsql:
delete_pgsql_database.delay(self.db_name).get()
elif self.db_user.db_type == DB_TYPES.mysql:
delete_mysql_database.delay(self.db_name, self.db_user.name).get()
elif db_user.db_type == DB_TYPES.mysql:
delete_mysql_database.delay(self.db_name, db_user.name).get()
else:
raise ValueError('Unknown database type %d' % self.db_type)
super(UserDatabase, self).delete(*args, **kwargs)
if not db_user.userdatabase_set.exists():
db_user.delete()

View file

@ -0,0 +1,23 @@
"""
This module defines the URL patterns for user database views.
"""
from __future__ import absolute_import, unicode_literals
from django.conf.urls import patterns, url
from .views import (
AddUserDatabase,
ChangeDatabaseUserPassword,
DeleteUserDatabase,
)
urlpatterns = patterns(
'',
url(r'^(?P<package>\d+)/create$',
AddUserDatabase.as_view(), name='add_userdatabase'),
url(r'^(?P<package>\d+)/(?P<slug>[\w0-9]+)/setpassword',
ChangeDatabaseUserPassword.as_view(), name='change_dbuser_password'),
url(r'^(?P<package>\d+)/(?P<slug>[\w0-9]+)/delete',
DeleteUserDatabase.as_view(), name='delete_userdatabase'),
)

View file

@ -0,0 +1,131 @@
"""
This module defines views for user database handling.
"""
from __future__ import absolute_import, unicode_literals
from django.shortcuts import redirect
from django.utils.translation import ugettext as _
from django.views.generic.edit import (
CreateView,
DeleteView,
UpdateView,
)
from django.contrib import messages
from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin
from gvawebcore.views import HostingPackageAndCustomerMixin
from .forms import (
AddUserDatabaseForm,
ChangeDatabaseUserPasswordForm,
)
from .models import (
DB_TYPES,
DatabaseUser,
UserDatabase,
)
class AddUserDatabase(
HostingPackageAndCustomerMixin, StaffOrSelfLoginRequiredMixin, CreateView
):
"""
This view is used to setup new user databases.
"""
model = UserDatabase
context_object_name = 'database'
template_name_suffix = '_create'
form_class = AddUserDatabaseForm
def _get_dbtypes(self, hostingpackage):
retval = []
db_options = hostingpackage.get_databases()
for opt in db_options:
dbs_of_type = UserDatabase.objects.filter(
db_user__osuser=hostingpackage.osuser,
db_user__db_type=opt['db_type']).count()
if dbs_of_type < opt['number']:
retval.append((opt['db_type'], DB_TYPES[opt['db_type']]))
return retval
def get_form_kwargs(self):
kwargs = super(AddUserDatabase, self).get_form_kwargs()
kwargs['hostingpackage'] = self.get_hosting_package()
kwargs['dbtypes'] = self._get_dbtypes(kwargs['hostingpackage'])
return kwargs
def form_valid(self, form):
userdatabase = form.save()
messages.success(
self.request,
_('Successfully create new {type} database {dbname} for user '
'{dbuser}').format(
type=userdatabase.db_user.db_type,
dbname=userdatabase.db_name, dbuser=userdatabase.db_user)
)
return redirect(self.get_hosting_package())
class ChangeDatabaseUserPassword(
HostingPackageAndCustomerMixin, StaffOrSelfLoginRequiredMixin, UpdateView
):
"""
This view is used to change a database user's password.
"""
model = DatabaseUser
slug_field = 'name'
context_object_name = 'dbuser'
template_name_suffix = '_setpassword'
form_class = ChangeDatabaseUserPasswordForm
def get_form_kwargs(self):
kwargs = super(ChangeDatabaseUserPassword, self).get_form_kwargs()
kwargs['hostingpackage'] = self.get_hosting_package()
return kwargs
def get_context_data(self, **kwargs):
context = super(ChangeDatabaseUserPassword, self).get_context_data(
**kwargs)
context['hostingpackage'] = self.get_hosting_package()
context['customer'] = self.get_customer_object()
return context
def form_valid(self, form):
db_user = form.save()
messages.success(
self.request,
_('Successfully changed password of database user {dbuser}'
).format(dbuser=db_user.name)
)
return redirect(self.get_hosting_package())
class DeleteUserDatabase(
HostingPackageAndCustomerMixin, StaffOrSelfLoginRequiredMixin, DeleteView
):
"""
This view is used to delete user databases and databases users if they have
no more databases assigned.
"""
model = UserDatabase
slug_field = 'db_name'
context_object_name = 'database'
def get_context_data(self, **kwargs):
context = super(DeleteUserDatabase, self).get_context_data(**kwargs)
context.update({
'hostingpackage': self.get_hosting_package(),
'customer': self.get_customer_object(),
})
return context
def get_success_url(self):
messages.success(
self.request,
_('Database deleted'),
)
return self.get_hosting_package().get_absolute_url()