Merge branch 'release/0.6.0' into production
* release/0.6.0: (32 commits) define version number, mark version in changelog plug users and hosting packages together implement CustomerHostingPackageDetails view introduce new settings for groups and upload server implement osusers.forms.ChangeOsUserPasswordForm refactor dashboard.views.UserDashboardView generate documentation for gvacommon.viewmixins implement viewmixins.StaffOrSelfLoginRequiredMixin create system user when creating a new hosting package fix some test issues incomplete create_hosting_package view fix issue with mailbox count calculation remove unused LogoutView and corresponding url pattern add docstrings to managemails.models add task stub for ldaptasks.tasks.set_ldap_user_password update changelog adapt documentation to changed module structure refactor osusers.tasks into fileservertasks and ldaptasks add a list of planned features and development ideas remove newline at EOF ...
This commit is contained in:
commit
b27dfd5d68
70 changed files with 3943 additions and 221 deletions
|
@ -3,3 +3,8 @@ gnuviechadmin
|
||||||
=============
|
=============
|
||||||
|
|
||||||
Customer center for gnuviech servers.
|
Customer center for gnuviech servers.
|
||||||
|
|
||||||
|
Gnuviechadmin is based on Django_ and Celery_
|
||||||
|
|
||||||
|
.. _Django: https://djangoproject.com/
|
||||||
|
.. _Celery: http://www.celeryproject.com/
|
||||||
|
|
|
@ -1,6 +1,21 @@
|
||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
* :release:`0.6.0 <2015-01-24>`
|
||||||
|
* :feature:`-` add frontend functionality to set an os users' sftp password
|
||||||
|
(needs gvaldap >= 0.4.0 on the LDAP side)
|
||||||
|
* :support:`-` remove unused dashboard.views.LogoutView and the corresponding
|
||||||
|
URL in dashboard.urls
|
||||||
|
* :feature:`-` add new task stub to set an ldap user's password
|
||||||
|
* :support:`-` refactor osusers.tasks, split into fileservertasks.tasks and
|
||||||
|
ldaptasks.tasks
|
||||||
|
* :feature:`-` show hosting package information on user dashboard
|
||||||
|
* :feature:`-` implement new hostingpackages app to provide hosting package
|
||||||
|
templates, hosting options and customer hosting packages as well as customer
|
||||||
|
specific hosting package options
|
||||||
|
* :feature:`-` add template tags for database icons and human readable names in
|
||||||
|
:py:mod:`userdbs.templatetags.userdb`
|
||||||
|
|
||||||
* :release:`0.5.2 <2015-01-18>`
|
* :release:`0.5.2 <2015-01-18>`
|
||||||
* :bug:`-` define proper allauth production settings with https and mandatory
|
* :bug:`-` define proper allauth production settings with https and mandatory
|
||||||
email verification
|
email verification
|
||||||
|
|
|
@ -9,16 +9,35 @@ administrators and customers.
|
||||||
|
|
||||||
.. _Django: https://www.djangoproject.com/
|
.. _Django: https://www.djangoproject.com/
|
||||||
|
|
||||||
|
Common code
|
||||||
|
===========
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
|
||||||
|
code/gvacommon
|
||||||
|
|
||||||
|
|
||||||
|
Celery task stubs
|
||||||
|
=================
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
|
||||||
|
code/fileservertasks
|
||||||
|
code/ldaptasks
|
||||||
|
code/mysqltasks
|
||||||
|
code/pgsqltasks
|
||||||
|
|
||||||
|
|
||||||
|
Django app code
|
||||||
|
===============
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
|
|
||||||
code/gnuviechadmin
|
code/gnuviechadmin
|
||||||
code/gvacommon
|
|
||||||
code/dashboard
|
code/dashboard
|
||||||
code/domains
|
code/domains
|
||||||
|
code/hostingpackages
|
||||||
code/managemails
|
code/managemails
|
||||||
code/mysqltasks
|
|
||||||
code/osusers
|
code/osusers
|
||||||
code/pgsqltasks
|
|
||||||
code/taskresults
|
code/taskresults
|
||||||
code/userdbs
|
code/userdbs
|
||||||
|
|
12
docs/code/fileservertasks.rst
Normal file
12
docs/code/fileservertasks.rst
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
:py:mod:`fileservertasks` app
|
||||||
|
=============================
|
||||||
|
|
||||||
|
.. automodule:: fileservertasks
|
||||||
|
|
||||||
|
|
||||||
|
:py:mod:`tasks <fileservertasks.tasks>`
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: fileservertasks.tasks
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
|
@ -13,3 +13,11 @@ provides some functionality that is common to all gnuviechadmin subprojects.
|
||||||
.. automodule:: gvacommon.celeryrouters
|
.. automodule:: gvacommon.celeryrouters
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
|
|
||||||
|
|
||||||
|
:py:mod:`viewmixins <gvacommon.viewmixins>`
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: gvacommon.viewmixins
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
|
38
docs/code/hostingpackages.rst
Normal file
38
docs/code/hostingpackages.rst
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
:py:mod:`hostingpackages` app
|
||||||
|
=============================
|
||||||
|
|
||||||
|
.. automodule:: hostingpackages
|
||||||
|
|
||||||
|
:py:mod:`admin <hostingpackages.admin>`
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: hostingpackages.admin
|
||||||
|
:members:
|
||||||
|
|
||||||
|
|
||||||
|
:py:mod:`apps <hostingpackages.apps>`
|
||||||
|
-------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: hostingpackages.apps
|
||||||
|
:members:
|
||||||
|
|
||||||
|
|
||||||
|
:py:mod:`models <hostingpackages.models>`
|
||||||
|
-----------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: hostingpackages.models
|
||||||
|
:members:
|
||||||
|
|
||||||
|
|
||||||
|
:py:mod:`views <hostingpackages.views>`
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: hostingpackages.views
|
||||||
|
:members:
|
||||||
|
|
||||||
|
|
||||||
|
:py:mod:`urls <hostingpackages.urls>`
|
||||||
|
-------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: hostingpackages.urls
|
||||||
|
:members:
|
12
docs/code/ldaptasks.rst
Normal file
12
docs/code/ldaptasks.rst
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
:py:mod:`ldaptasks` app
|
||||||
|
=======================
|
||||||
|
|
||||||
|
.. automodule:: ldaptasks
|
||||||
|
|
||||||
|
|
||||||
|
:py:mod:`tasks <ldaptasks.tasks>`
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ldaptasks.tasks
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
|
@ -9,9 +9,4 @@
|
||||||
|
|
||||||
.. automodule:: mysqltasks.tasks
|
.. automodule:: mysqltasks.tasks
|
||||||
:members:
|
:members:
|
||||||
|
:undoc-members:
|
||||||
.. autotask:: mysqltasks.tasks.create_mysql_database
|
|
||||||
.. autotask:: mysqltasks.tasks.create_mysql_user
|
|
||||||
.. autotask:: mysqltasks.tasks.delete_mysql_database
|
|
||||||
.. autotask:: mysqltasks.tasks.delete_mysql_user
|
|
||||||
.. autotask:: mysqltasks.tasks.set_mysql_userpassword
|
|
||||||
|
|
|
@ -18,6 +18,13 @@
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
|
||||||
|
:py:mod:`forms <osusers.forms>`
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
.. automodule:: osusers.forms
|
||||||
|
:members:
|
||||||
|
|
||||||
|
|
||||||
:py:mod:`models <osusers.models>`
|
:py:mod:`models <osusers.models>`
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
|
@ -25,21 +32,14 @@
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
|
||||||
:py:mod:`tasks <osusers.tasks>`
|
:py:mod:`urls <osusers.urls>`
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
.. automodule:: osusers.urls
|
||||||
|
|
||||||
|
|
||||||
|
:py:mod:`views <osusers.views>`
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
.. automodule:: osusers.tasks
|
.. automodule:: osusers.views
|
||||||
|
:members:
|
||||||
.. autotask:: osusers.tasks.add_ldap_user_to_group
|
|
||||||
.. autotask:: osusers.tasks.create_file_mailbox
|
|
||||||
.. autotask:: osusers.tasks.create_ldap_group
|
|
||||||
.. autotask:: osusers.tasks.create_ldap_user
|
|
||||||
.. autotask:: osusers.tasks.delete_file_mail_userdir
|
|
||||||
.. autotask:: osusers.tasks.delete_file_mailbox
|
|
||||||
.. autotask:: osusers.tasks.delete_file_sftp_userdir
|
|
||||||
.. autotask:: osusers.tasks.delete_ldap_group
|
|
||||||
.. autotask:: osusers.tasks.delete_ldap_group_if_empty
|
|
||||||
.. autotask:: osusers.tasks.delete_ldap_user
|
|
||||||
.. autotask:: osusers.tasks.remove_ldap_user_from_group
|
|
||||||
.. autotask:: osusers.tasks.setup_file_mail_userdir
|
|
||||||
.. autotask:: osusers.tasks.setup_file_sftp_userdir
|
|
||||||
|
|
|
@ -9,9 +9,5 @@
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
|
||||||
.. automodule:: pgsqltasks.tasks
|
.. automodule:: pgsqltasks.tasks
|
||||||
|
:members:
|
||||||
.. autotask:: pgsqltasks.tasks.create_pgsql_database
|
:undoc-members:
|
||||||
.. autotask:: pgsqltasks.tasks.create_pgsql_user
|
|
||||||
.. autotask:: pgsqltasks.tasks.delete_pgsql_database
|
|
||||||
.. autotask:: pgsqltasks.tasks.delete_pgsql_user
|
|
||||||
.. autotask:: pgsqltasks.tasks.set_pgsql_userpassword
|
|
||||||
|
|
|
@ -23,8 +23,14 @@
|
||||||
.. automodule:: userdbs.models
|
.. automodule:: userdbs.models
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
:py:mod:`templatetags <userdbs.templatetags>`
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
:py:mod:`views <userdbs.views>`
|
.. automodule:: userdbs.templatetags
|
||||||
-------------------------------
|
|
||||||
|
|
||||||
.. automodule:: userdbs.views
|
|
||||||
|
:py:mod:`userdb <userdbs.templatetags.userdb>`
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. automodule:: userdbs.templatetags.userdb
|
||||||
|
:members:
|
||||||
|
|
|
@ -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.5'
|
version = '0.6'
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
release = '0.5.2'
|
release = '0.6.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.
|
||||||
|
|
19
docs/ideas.rst
Normal file
19
docs/ideas.rst
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
Development ideas and planned features
|
||||||
|
======================================
|
||||||
|
|
||||||
|
* password reset for
|
||||||
|
|
||||||
|
- OS users
|
||||||
|
- mailboxes
|
||||||
|
|
||||||
|
* add ssh key management for sftp users
|
||||||
|
|
||||||
|
* link to phpmyadmin and phppgadmin
|
||||||
|
|
||||||
|
* link to webmail
|
||||||
|
|
||||||
|
* list mail domains
|
||||||
|
|
||||||
|
* CRUD for mailboxes
|
||||||
|
|
||||||
|
* CRUD for mail addresses
|
|
@ -25,6 +25,7 @@ Contents:
|
||||||
deploy
|
deploy
|
||||||
tests
|
tests
|
||||||
code
|
code
|
||||||
|
ideas
|
||||||
changelog
|
changelog
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ from django.conf.urls import patterns, url
|
||||||
|
|
||||||
from .views import (
|
from .views import (
|
||||||
IndexView,
|
IndexView,
|
||||||
LogoutView,
|
|
||||||
UserDashboardView,
|
UserDashboardView,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -14,6 +13,4 @@ urlpatterns = patterns(
|
||||||
url(r'^$', IndexView.as_view(), name='dashboard'),
|
url(r'^$', IndexView.as_view(), name='dashboard'),
|
||||||
url(r'^user/(?P<slug>[\w0-9@.+-_]+)/$',
|
url(r'^user/(?P<slug>[\w0-9@.+-_]+)/$',
|
||||||
UserDashboardView.as_view(), name='customer_dashboard'),
|
UserDashboardView.as_view(), name='customer_dashboard'),
|
||||||
url(r'^logout/',
|
|
||||||
LogoutView.as_view(), name='logout'),
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,16 +4,15 @@ This module defines the views for the gnuviechadmin customer dashboard.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.http import HttpResponseForbidden
|
|
||||||
from django.views.generic import (
|
from django.views.generic import (
|
||||||
DetailView,
|
DetailView,
|
||||||
TemplateView,
|
TemplateView,
|
||||||
)
|
)
|
||||||
from django.views.generic.base import RedirectView
|
from django.contrib.auth import get_user_model
|
||||||
from django.utils.translation import ugettext as _
|
|
||||||
from django.contrib.auth import get_user_model, logout
|
|
||||||
|
|
||||||
from braces.views import LoginRequiredMixin
|
from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin
|
||||||
|
|
||||||
|
from hostingpackages.models import CustomerHostingPackage
|
||||||
|
|
||||||
|
|
||||||
class IndexView(TemplateView):
|
class IndexView(TemplateView):
|
||||||
|
@ -24,7 +23,7 @@ class IndexView(TemplateView):
|
||||||
template_name = 'dashboard/index.html'
|
template_name = 'dashboard/index.html'
|
||||||
|
|
||||||
|
|
||||||
class UserDashboardView(LoginRequiredMixin, DetailView):
|
class UserDashboardView(StaffOrSelfLoginRequiredMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
This is the user dashboard view.
|
This is the user dashboard view.
|
||||||
|
|
||||||
|
@ -34,23 +33,16 @@ class UserDashboardView(LoginRequiredMixin, DetailView):
|
||||||
slug_field = 'username'
|
slug_field = 'username'
|
||||||
template_name = 'dashboard/user_dashboard.html'
|
template_name = 'dashboard/user_dashboard.html'
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
if (request.user.is_staff or request.user == self.get_object()):
|
context = super(UserDashboardView, self).get_context_data(**kwargs)
|
||||||
return super(UserDashboardView, self).dispatch(
|
context['hosting_packages'] = CustomerHostingPackage.objects.filter(
|
||||||
request, *args, **kwargs
|
customer=self.object
|
||||||
)
|
|
||||||
return HttpResponseForbidden(
|
|
||||||
_('You are not allowed to view this page.')
|
|
||||||
)
|
)
|
||||||
|
return context
|
||||||
|
|
||||||
class LogoutView(RedirectView):
|
def get_customer_object(self):
|
||||||
pattern_name = 'dashboard'
|
"""
|
||||||
|
Returns the customer object.
|
||||||
|
|
||||||
def get(self, *args, **kwargs):
|
"""
|
||||||
logout(self.request)
|
return self.get_object()
|
||||||
return super(LogoutView, self).get(*args, **kwargs)
|
|
||||||
|
|
||||||
def get_redirect_url(self, *args, **kwargs):
|
|
||||||
if 'next' in self.request.GET:
|
|
||||||
return self.request.GET['next']
|
|
||||||
return super(LogoutView, self).get_redirect_url(*args, **kwargs)
|
|
||||||
|
|
4
gnuviechadmin/fileservertasks/__init__.py
Normal file
4
gnuviechadmin/fileservertasks/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
"""
|
||||||
|
This module contains :py:mod:`fileservertasks.tasks`.
|
||||||
|
|
||||||
|
"""
|
94
gnuviechadmin/fileservertasks/tasks.py
Normal file
94
gnuviechadmin/fileservertasks/tasks.py
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
"""
|
||||||
|
This module defines task stubs for the tasks implemented in the gvafile Celery
|
||||||
|
worker.
|
||||||
|
|
||||||
|
"""
|
||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
from celery import shared_task
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task
|
||||||
|
def setup_file_sftp_userdir(username):
|
||||||
|
"""
|
||||||
|
This task creates the home directory for an SFTP user if it does not exist
|
||||||
|
yet.
|
||||||
|
|
||||||
|
:param str username: the user name
|
||||||
|
:raises Exception: if the SFTP directory of the user cannot be created
|
||||||
|
:return: the created directory name
|
||||||
|
:rtype: str
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task
|
||||||
|
def delete_file_sftp_userdir(username):
|
||||||
|
"""
|
||||||
|
This task recursively deletes the home directory of an SFTP user if it
|
||||||
|
does not exist yet.
|
||||||
|
|
||||||
|
:param str username: the user name
|
||||||
|
:raises Exception: if the SFTP directory of the user cannot be removed
|
||||||
|
:return: the removed directory name
|
||||||
|
:rtype: str
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task
|
||||||
|
def setup_file_mail_userdir(username):
|
||||||
|
"""
|
||||||
|
This task creates the mail base directory for a user if it does not exist
|
||||||
|
yet.
|
||||||
|
|
||||||
|
:param str username: the user name
|
||||||
|
:raises Exception: if the mail base directory for the user cannot be
|
||||||
|
created
|
||||||
|
:return: the created directory name
|
||||||
|
:rtype: str
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task
|
||||||
|
def delete_file_mail_userdir(username):
|
||||||
|
"""
|
||||||
|
This task recursively deletes the mail base directory for a user if it
|
||||||
|
does not exist yet.
|
||||||
|
|
||||||
|
:param str username: the user name
|
||||||
|
:raises Exception: if the mail base directory of the user cannot be removed
|
||||||
|
:return: the removed directory name
|
||||||
|
:rtype: str
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task
|
||||||
|
def create_file_mailbox(username, mailboxname):
|
||||||
|
"""
|
||||||
|
This task creates a new mailbox directory for the given user and mailbox
|
||||||
|
name.
|
||||||
|
|
||||||
|
:param str username: the user name
|
||||||
|
:param str mailboxname: the mailbox name
|
||||||
|
:raises GVAFileException: if the mailbox directory cannot be created
|
||||||
|
:return: the created mailbox directory name
|
||||||
|
:rtype: str
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task
|
||||||
|
def delete_file_mailbox(username, mailboxname):
|
||||||
|
"""
|
||||||
|
This task deletes the given mailbox of the given user.
|
||||||
|
|
||||||
|
:param str username: the user name
|
||||||
|
:param str mailboxname: the mailbox name
|
||||||
|
:raises GVAFileException: if the mailbox directory cannot be deleted
|
||||||
|
:return: the deleted mailbox directory name
|
||||||
|
:rtype: str
|
||||||
|
|
||||||
|
"""
|
|
@ -262,6 +262,7 @@ LOCAL_APPS = (
|
||||||
'osusers',
|
'osusers',
|
||||||
'managemails',
|
'managemails',
|
||||||
'userdbs',
|
'userdbs',
|
||||||
|
'hostingpackages',
|
||||||
)
|
)
|
||||||
|
|
||||||
# See: https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
|
# See: https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
|
||||||
|
@ -351,4 +352,8 @@ OSUSER_MINGID = int(get_env_variable('GVA_MIN_OS_GID'))
|
||||||
OSUSER_USERNAME_PREFIX = get_env_variable('GVA_OSUSER_PREFIX')
|
OSUSER_USERNAME_PREFIX = get_env_variable('GVA_OSUSER_PREFIX')
|
||||||
OSUSER_HOME_BASEPATH = get_env_variable('GVA_OSUSER_HOME_BASEPATH')
|
OSUSER_HOME_BASEPATH = get_env_variable('GVA_OSUSER_HOME_BASEPATH')
|
||||||
OSUSER_DEFAULT_SHELL = get_env_variable('GVA_OSUSER_DEFAULT_SHELL')
|
OSUSER_DEFAULT_SHELL = get_env_variable('GVA_OSUSER_DEFAULT_SHELL')
|
||||||
|
OSUSER_SFTP_GROUP = 'sftponly'
|
||||||
|
OSUSER_SSH_GROUP = 'sshusers'
|
||||||
|
OSUSER_DEFAULT_GROUPS = [OSUSER_SFTP_GROUP]
|
||||||
|
OSUSER_UPLOAD_SERVER = get_env_variable('GVA_OSUSER_UPLOADSERVER')
|
||||||
########## END CUSTOM APP CONFIGURATION
|
########## END CUSTOM APP CONFIGURATION
|
||||||
|
|
|
@ -3,15 +3,15 @@ from __future__ import absolute_import
|
||||||
from django.conf.urls import patterns, include, url
|
from django.conf.urls import patterns, include, url
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
import dashboard.urls
|
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
admin.autodiscover()
|
admin.autodiscover()
|
||||||
|
|
||||||
urlpatterns = patterns(
|
urlpatterns = patterns(
|
||||||
'',
|
'',
|
||||||
url(r'', include(dashboard.urls)),
|
url(r'', include('dashboard.urls')),
|
||||||
url(r'^accounts/', include('allauth.urls')),
|
url(r'^accounts/', include('allauth.urls')),
|
||||||
|
url(r'^hosting/', include('hostingpackages.urls')),
|
||||||
|
url(r'^osuser/', include('osusers.urls')),
|
||||||
url(r'^admin/', include(admin.site.urls)),
|
url(r'^admin/', include(admin.site.urls)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
42
gnuviechadmin/gvacommon/viewmixins.py
Normal file
42
gnuviechadmin/gvacommon/viewmixins.py
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
"""
|
||||||
|
This module defines mixins for gnuviechadmin views.
|
||||||
|
|
||||||
|
"""
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.http import HttpResponseForbidden
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
from braces.views import LoginRequiredMixin
|
||||||
|
|
||||||
|
|
||||||
|
class StaffOrSelfLoginRequiredMixin(LoginRequiredMixin):
|
||||||
|
"""
|
||||||
|
Mixin that makes sure that a user is logged in and matches the current
|
||||||
|
customer or is a staff user.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
if (
|
||||||
|
request.user.is_staff or
|
||||||
|
request.user == self.get_customer_object()
|
||||||
|
):
|
||||||
|
return super(StaffOrSelfLoginRequiredMixin, self).dispatch(
|
||||||
|
request, *args, **kwargs
|
||||||
|
)
|
||||||
|
return HttpResponseForbidden(
|
||||||
|
_('You are not allowed to view this page.')
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_customer_object(self):
|
||||||
|
"""
|
||||||
|
Views based on this mixin have to implement this method to return
|
||||||
|
the customer that must be an object of the same class as the
|
||||||
|
django.contrib.auth user type.
|
||||||
|
|
||||||
|
:return: customer
|
||||||
|
:rtype: settings.AUTH_USER_MODEL
|
||||||
|
|
||||||
|
"""
|
||||||
|
raise NotImplemented("subclass has to implement get_customer_object")
|
5
gnuviechadmin/hostingpackages/__init__.py
Normal file
5
gnuviechadmin/hostingpackages/__init__.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
"""
|
||||||
|
This app takes care of hosting packages.
|
||||||
|
|
||||||
|
"""
|
||||||
|
default_app_config = 'hostingpackages.apps.HostingPackagesAppConfig'
|
146
gnuviechadmin/hostingpackages/admin.py
Normal file
146
gnuviechadmin/hostingpackages/admin.py
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
"""
|
||||||
|
This module contains the admin site interface for hosting packages.
|
||||||
|
|
||||||
|
"""
|
||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
|
from django import forms
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from .models import (
|
||||||
|
CustomerHostingPackage,
|
||||||
|
CustomerDiskSpaceOption,
|
||||||
|
CustomerUserDatabaseOption,
|
||||||
|
CustomerMailboxOption,
|
||||||
|
DiskSpaceOption,
|
||||||
|
HostingPackageTemplate,
|
||||||
|
MailboxOption,
|
||||||
|
UserDatabaseOption,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomerHostingPackageCreateForm(forms.ModelForm):
|
||||||
|
"""
|
||||||
|
This is the form class for creating new customer hosting packages.
|
||||||
|
|
||||||
|
"""
|
||||||
|
class Meta:
|
||||||
|
model = CustomerHostingPackage
|
||||||
|
fields = ['customer', 'template', 'name']
|
||||||
|
|
||||||
|
def save(self, **kwargs):
|
||||||
|
"""
|
||||||
|
Save the customer hosting package.
|
||||||
|
|
||||||
|
:param kwargs: keyword arguments passed to create method
|
||||||
|
:return: customer hosting package instance
|
||||||
|
:rtype: :py:class:`hostingpackages.models.CustomerHostingPackage`
|
||||||
|
|
||||||
|
"""
|
||||||
|
hostinpackages = CustomerHostingPackage.objects.create_from_template(
|
||||||
|
customer=self.cleaned_data['customer'],
|
||||||
|
template=self.cleaned_data['template'],
|
||||||
|
name=self.cleaned_data['name'],
|
||||||
|
**kwargs)
|
||||||
|
return hostinpackages
|
||||||
|
|
||||||
|
def save_m2m(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CustomerDiskSpaceOptionInline(admin.TabularInline):
|
||||||
|
"""
|
||||||
|
This class implements the inline editor for customer hosting package disk
|
||||||
|
space options.
|
||||||
|
|
||||||
|
"""
|
||||||
|
model = CustomerDiskSpaceOption
|
||||||
|
extra = 0
|
||||||
|
|
||||||
|
|
||||||
|
class CustomerMailboxOptionInline(admin.TabularInline):
|
||||||
|
"""
|
||||||
|
This class implements the inline editor for customer hosting package
|
||||||
|
mailbox options.
|
||||||
|
|
||||||
|
"""
|
||||||
|
model = CustomerMailboxOption
|
||||||
|
extra = 0
|
||||||
|
|
||||||
|
|
||||||
|
class CustomerUserDatabaseOptionInline(admin.TabularInline):
|
||||||
|
"""
|
||||||
|
This class implements the inline editor for customer hosting package user
|
||||||
|
database options.
|
||||||
|
|
||||||
|
"""
|
||||||
|
model = CustomerUserDatabaseOption
|
||||||
|
extra = 0
|
||||||
|
|
||||||
|
|
||||||
|
class CustomerHostingPackageAdmin(admin.ModelAdmin):
|
||||||
|
"""
|
||||||
|
This class implements the admin interface for
|
||||||
|
:py:class:`CustomerHostingPackage`.
|
||||||
|
|
||||||
|
"""
|
||||||
|
add_form = CustomerHostingPackageCreateForm
|
||||||
|
add_fieldsets = (
|
||||||
|
(None, {
|
||||||
|
'fields': ('customer', 'template', 'name')
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
inlines = [
|
||||||
|
CustomerDiskSpaceOptionInline,
|
||||||
|
CustomerMailboxOptionInline,
|
||||||
|
CustomerUserDatabaseOptionInline,
|
||||||
|
]
|
||||||
|
list_display = ['name', 'customer', 'osuser']
|
||||||
|
|
||||||
|
def get_form(self, request, obj=None, **kwargs):
|
||||||
|
"""
|
||||||
|
Use special form for customer hosting package creation.
|
||||||
|
|
||||||
|
:param request: the current HTTP request
|
||||||
|
:param obj: either a :py:class:`CustomerHostingPackage
|
||||||
|
<hostingpackages.models.CustomerHostingPackage>` instance or None
|
||||||
|
for a new customer hosting package
|
||||||
|
:param kwargs: keyword arguments to be passed to
|
||||||
|
:py:meth:`django.contrib.admin.ModelAdmin.get_form`
|
||||||
|
:return: form instance
|
||||||
|
|
||||||
|
"""
|
||||||
|
defaults = {}
|
||||||
|
if obj is None:
|
||||||
|
defaults.update({
|
||||||
|
'form': self.add_form,
|
||||||
|
'fields': admin.options.flatten_fieldsets(self.add_fieldsets),
|
||||||
|
})
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return super(CustomerHostingPackageAdmin, self).get_form(
|
||||||
|
request, obj, **defaults)
|
||||||
|
|
||||||
|
def get_readonly_fields(self, request, obj=None):
|
||||||
|
"""
|
||||||
|
Make sure that customer and template are not editable for existing
|
||||||
|
customer hosting packages.
|
||||||
|
|
||||||
|
:param request: the current HTTP request
|
||||||
|
:param obj: either a :py:class:`CustomerHostingPackage
|
||||||
|
<hostingpackages.models.CustomerHostingPackage>` instance or None
|
||||||
|
for a new customer hosting package
|
||||||
|
:return: a list of fields
|
||||||
|
:rtype: list
|
||||||
|
|
||||||
|
"""
|
||||||
|
if obj:
|
||||||
|
return ['customer', 'template']
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.register(CustomerHostingPackage, CustomerHostingPackageAdmin)
|
||||||
|
admin.site.register(DiskSpaceOption)
|
||||||
|
admin.site.register(HostingPackageTemplate)
|
||||||
|
admin.site.register(MailboxOption)
|
||||||
|
admin.site.register(UserDatabaseOption)
|
17
gnuviechadmin/hostingpackages/apps.py
Normal file
17
gnuviechadmin/hostingpackages/apps.py
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
"""
|
||||||
|
This module contains the :py:class:`django.apps.AppConfig` instance for the
|
||||||
|
:py:mod:`hostingpackages` app.
|
||||||
|
|
||||||
|
"""
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
from django.apps import AppConfig
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class HostingPackagesAppConfig(AppConfig):
|
||||||
|
"""
|
||||||
|
AppConfig for the :py:mod:`hostingpackages` app.
|
||||||
|
|
||||||
|
"""
|
||||||
|
name = 'hostingpackages'
|
||||||
|
verbose_name = _('Hosting Packages and Options')
|
44
gnuviechadmin/hostingpackages/forms.py
Normal file
44
gnuviechadmin/hostingpackages/forms.py
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
"""
|
||||||
|
This module contains the form classes related to hosting packages.
|
||||||
|
|
||||||
|
"""
|
||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
|
from django import forms
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
from crispy_forms.helper import FormHelper
|
||||||
|
from crispy_forms.layout import (
|
||||||
|
Layout,
|
||||||
|
Submit,
|
||||||
|
)
|
||||||
|
|
||||||
|
from .models import CustomerHostingPackage
|
||||||
|
|
||||||
|
|
||||||
|
class CreateHostingPackageForm(forms.ModelForm):
|
||||||
|
"""
|
||||||
|
This form class is used for creating new customer hosting packages.
|
||||||
|
|
||||||
|
"""
|
||||||
|
class Meta:
|
||||||
|
model = CustomerHostingPackage
|
||||||
|
fields = ['template', 'name', 'description']
|
||||||
|
|
||||||
|
def __init__(self, instance, *args, **kwargs):
|
||||||
|
username = kwargs.pop('user')
|
||||||
|
super(CreateHostingPackageForm, self).__init__(
|
||||||
|
*args, **kwargs
|
||||||
|
)
|
||||||
|
self.fields['description'].widget.attrs['rows'] = 2
|
||||||
|
self.helper = FormHelper()
|
||||||
|
self.helper.form_action = reverse(
|
||||||
|
'create_hosting_package', kwargs={'user': username}
|
||||||
|
)
|
||||||
|
self.helper.layout = Layout(
|
||||||
|
'template',
|
||||||
|
'name',
|
||||||
|
'description',
|
||||||
|
Submit('submit', _('Add Hosting Package')),
|
||||||
|
)
|
192
gnuviechadmin/hostingpackages/locale/de/LC_MESSAGES/django.po
Normal file
192
gnuviechadmin/hostingpackages/locale/de/LC_MESSAGES/django.po
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
# 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: gnuviechadmin hostingpackages\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2015-01-18 13:36+0100\n"
|
||||||
|
"PO-Revision-Date: 2015-01-18 13:17+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"
|
||||||
|
|
||||||
|
#: apps.py:17
|
||||||
|
msgid "Hosting Packages and Options"
|
||||||
|
msgstr "Hostingpakete und -Optionen"
|
||||||
|
|
||||||
|
#: models.py:20
|
||||||
|
msgid "MiB"
|
||||||
|
msgstr "MiB"
|
||||||
|
|
||||||
|
#: models.py:21
|
||||||
|
msgid "GiB"
|
||||||
|
msgstr "GiB"
|
||||||
|
|
||||||
|
#: models.py:22
|
||||||
|
msgid "TiB"
|
||||||
|
msgstr "TiB"
|
||||||
|
|
||||||
|
#: models.py:28
|
||||||
|
msgid "description"
|
||||||
|
msgstr "Beschreibung"
|
||||||
|
|
||||||
|
#: models.py:29
|
||||||
|
msgid "mailbox count"
|
||||||
|
msgstr "Anzahl Postfächer"
|
||||||
|
|
||||||
|
#: models.py:31 models.py:58
|
||||||
|
msgid "disk space"
|
||||||
|
msgstr "Speicherplatz"
|
||||||
|
|
||||||
|
#: models.py:31
|
||||||
|
msgid "disk space for the hosting package"
|
||||||
|
msgstr "Speicherplatz für das Hostingpaket"
|
||||||
|
|
||||||
|
#: models.py:33 models.py:60
|
||||||
|
msgid "unit of disk space"
|
||||||
|
msgstr "Maßeinheit für den Speicherplatz"
|
||||||
|
|
||||||
|
#: models.py:43 models.py:192
|
||||||
|
msgid "name"
|
||||||
|
msgstr "Name"
|
||||||
|
|
||||||
|
#: models.py:46
|
||||||
|
msgid "Hosting package"
|
||||||
|
msgstr "Hostingpaket"
|
||||||
|
|
||||||
|
#: models.py:47
|
||||||
|
msgid "Hosting packages"
|
||||||
|
msgstr "Hostingpakete"
|
||||||
|
|
||||||
|
#: models.py:66
|
||||||
|
msgid "Disk space option"
|
||||||
|
msgstr "Speicherplatzoption"
|
||||||
|
|
||||||
|
#: models.py:67
|
||||||
|
msgid "Disk space options"
|
||||||
|
msgstr "Speicherplatzoptionen"
|
||||||
|
|
||||||
|
#: models.py:70
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Additional disk space {space} {unit}"
|
||||||
|
msgstr "Zusätzlicher Speicherplatz {space} {unit}"
|
||||||
|
|
||||||
|
#: models.py:84
|
||||||
|
msgid "number of databases"
|
||||||
|
msgstr "Anzahl von Datenbanken"
|
||||||
|
|
||||||
|
#: models.py:86
|
||||||
|
msgid "database type"
|
||||||
|
msgstr "Datenbanktyp"
|
||||||
|
|
||||||
|
#: models.py:92
|
||||||
|
msgid "Database option"
|
||||||
|
msgstr "Datenbankoption"
|
||||||
|
|
||||||
|
#: models.py:93
|
||||||
|
msgid "Database options"
|
||||||
|
msgstr "Datenbankoptionen"
|
||||||
|
|
||||||
|
#: models.py:97
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{type} database"
|
||||||
|
msgid_plural "{count} {type} databases"
|
||||||
|
msgstr[0] "{type}-Datenbank"
|
||||||
|
msgstr[1] "{count} {type}-Datenbanken"
|
||||||
|
|
||||||
|
#: models.py:120
|
||||||
|
msgid "number of mailboxes"
|
||||||
|
msgstr "Anzahl von Postfächern"
|
||||||
|
|
||||||
|
#: models.py:125
|
||||||
|
msgid "Mailbox option"
|
||||||
|
msgstr "Postfachoption"
|
||||||
|
|
||||||
|
#: models.py:126
|
||||||
|
msgid "Mailbox options"
|
||||||
|
msgstr "Postfachoptionen"
|
||||||
|
|
||||||
|
#: models.py:130
|
||||||
|
#, 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"
|
||||||
|
|
||||||
|
#: models.py:185
|
||||||
|
msgid "customer"
|
||||||
|
msgstr "Kunde"
|
||||||
|
|
||||||
|
#: models.py:187
|
||||||
|
msgid "hosting package template"
|
||||||
|
msgstr "Hostingpaketvorlage"
|
||||||
|
|
||||||
|
#: models.py:189
|
||||||
|
msgid "The hosting package template that this hosting package is based on"
|
||||||
|
msgstr "Die Hostingpaketvorlage, auf der dieses Hostingpaket aufgebaut ist"
|
||||||
|
|
||||||
|
#: models.py:194
|
||||||
|
msgid "Operating system user"
|
||||||
|
msgstr "Betriebssystemnutzer"
|
||||||
|
|
||||||
|
#: models.py:201
|
||||||
|
msgid "customer hosting package"
|
||||||
|
msgstr "Kundenhostingpaket"
|
||||||
|
|
||||||
|
#: models.py:202
|
||||||
|
msgid "customer hosting packages"
|
||||||
|
msgstr "Kundenhostingpakete"
|
||||||
|
|
||||||
|
#: models.py:211
|
||||||
|
msgid "hosting package"
|
||||||
|
msgstr "Hostingpaket"
|
||||||
|
|
||||||
|
#: models.py:214
|
||||||
|
msgid "customer hosting option"
|
||||||
|
msgstr "kundenspezifische Hostingoption"
|
||||||
|
|
||||||
|
#: models.py:215
|
||||||
|
msgid "customer hosting options"
|
||||||
|
msgstr "kundenspezifische Hostingoptionen"
|
||||||
|
|
||||||
|
#: models.py:227
|
||||||
|
msgid "disk space option template"
|
||||||
|
msgstr "Speicherplatzoptionsvorlage"
|
||||||
|
|
||||||
|
#: models.py:229
|
||||||
|
msgid "The disk space option template that this disk space option is based on"
|
||||||
|
msgstr ""
|
||||||
|
"Die Speicherplatzoptionsvorlage auf der diese Speicherplatzoption aufgebaut "
|
||||||
|
"ist"
|
||||||
|
|
||||||
|
#: models.py:243
|
||||||
|
msgid "user database option template"
|
||||||
|
msgstr "Nutzerdatenbankoptionsvorlage"
|
||||||
|
|
||||||
|
#: models.py:245
|
||||||
|
msgid "The user database option template that this database option is based on"
|
||||||
|
msgstr ""
|
||||||
|
"Die Nutzerdatenbankoptionsvorlage auf der diese Datenbankoption aufgebaut ist"
|
||||||
|
|
||||||
|
#: models.py:259
|
||||||
|
msgid "mailbox option template"
|
||||||
|
msgstr "Postfachoptionsvorlage"
|
||||||
|
|
||||||
|
#: models.py:261
|
||||||
|
msgid "The mailbox option template that this mailbox option is based on"
|
||||||
|
msgstr "Die Postfachoptionsvorlage auf der diese Postfachoption aufgebaut ist"
|
||||||
|
|
||||||
|
#~ msgid "Hosting option"
|
||||||
|
#~ msgstr "Hostingoption"
|
||||||
|
|
||||||
|
#~ msgid "Hosting options"
|
||||||
|
#~ msgstr "Hostingoptionen"
|
214
gnuviechadmin/hostingpackages/migrations/0001_initial.py
Normal file
214
gnuviechadmin/hostingpackages/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import models, migrations
|
||||||
|
import django.utils.timezone
|
||||||
|
from django.conf import settings
|
||||||
|
import model_utils.fields
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='CustomerHostingPackage',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||||
|
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, verbose_name='created', editable=False)),
|
||||||
|
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, verbose_name='modified', editable=False)),
|
||||||
|
('name', models.CharField(unique=True, max_length=128, verbose_name='name')),
|
||||||
|
('description', models.TextField(verbose_name='description', blank=True)),
|
||||||
|
('mailboxcount', models.PositiveIntegerField(verbose_name='mailbox count')),
|
||||||
|
('diskspace', models.PositiveIntegerField(help_text='disk space for the hosting package', verbose_name='disk space')),
|
||||||
|
('diskspace_unit', models.PositiveSmallIntegerField(verbose_name='unit of disk space', choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])),
|
||||||
|
('customer', models.ForeignKey(verbose_name='customer', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'customer hosting package',
|
||||||
|
'verbose_name_plural': 'customer hosting packages',
|
||||||
|
},
|
||||||
|
bases=(models.Model,),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='CustomerHostingPackageOption',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||||
|
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, verbose_name='created', editable=False)),
|
||||||
|
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, verbose_name='modified', editable=False)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'customer hosting option',
|
||||||
|
'verbose_name_plural': 'customer hosting options',
|
||||||
|
},
|
||||||
|
bases=(models.Model,),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='CustomerDiskSpaceOption',
|
||||||
|
fields=[
|
||||||
|
('customerhostingpackageoption_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='hostingpackages.CustomerHostingPackageOption')),
|
||||||
|
('diskspace', models.PositiveIntegerField(verbose_name='disk space')),
|
||||||
|
('diskspace_unit', models.PositiveSmallIntegerField(verbose_name='unit of disk space', choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['diskspace_unit', 'diskspace'],
|
||||||
|
'abstract': False,
|
||||||
|
'verbose_name': 'Disk space option',
|
||||||
|
'verbose_name_plural': 'Disk space options',
|
||||||
|
},
|
||||||
|
bases=('hostingpackages.customerhostingpackageoption', models.Model),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='CustomerMailboxOption',
|
||||||
|
fields=[
|
||||||
|
('customerhostingpackageoption_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='hostingpackages.CustomerHostingPackageOption')),
|
||||||
|
('number', models.PositiveIntegerField(unique=True, verbose_name='number of mailboxes')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['number'],
|
||||||
|
'abstract': False,
|
||||||
|
'verbose_name': 'Mailbox option',
|
||||||
|
'verbose_name_plural': 'Mailbox options',
|
||||||
|
},
|
||||||
|
bases=('hostingpackages.customerhostingpackageoption', models.Model),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='CustomerUserDatabaseOption',
|
||||||
|
fields=[
|
||||||
|
('customerhostingpackageoption_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='hostingpackages.CustomerHostingPackageOption')),
|
||||||
|
('number', models.PositiveIntegerField(default=1, verbose_name='number of databases')),
|
||||||
|
('db_type', models.PositiveSmallIntegerField(verbose_name='database type', choices=[(0, 'PostgreSQL'), (1, 'MySQL')])),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['db_type', 'number'],
|
||||||
|
'abstract': False,
|
||||||
|
'verbose_name': 'Database option',
|
||||||
|
'verbose_name_plural': 'Database options',
|
||||||
|
},
|
||||||
|
bases=('hostingpackages.customerhostingpackageoption', models.Model),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='HostingOption',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||||
|
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, verbose_name='created', editable=False)),
|
||||||
|
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, verbose_name='modified', editable=False)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Hosting option',
|
||||||
|
'verbose_name_plural': 'Hosting options',
|
||||||
|
},
|
||||||
|
bases=(models.Model,),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='DiskSpaceOption',
|
||||||
|
fields=[
|
||||||
|
('hostingoption_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='hostingpackages.HostingOption')),
|
||||||
|
('diskspace', models.PositiveIntegerField(verbose_name='disk space')),
|
||||||
|
('diskspace_unit', models.PositiveSmallIntegerField(verbose_name='unit of disk space', choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['diskspace_unit', 'diskspace'],
|
||||||
|
'abstract': False,
|
||||||
|
'verbose_name': 'Disk space option',
|
||||||
|
'verbose_name_plural': 'Disk space options',
|
||||||
|
},
|
||||||
|
bases=('hostingpackages.hostingoption', models.Model),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='HostingPackageTemplate',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||||
|
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, verbose_name='created', editable=False)),
|
||||||
|
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, verbose_name='modified', editable=False)),
|
||||||
|
('name', models.CharField(unique=True, max_length=128, verbose_name='name')),
|
||||||
|
('description', models.TextField(verbose_name='description', blank=True)),
|
||||||
|
('mailboxcount', models.PositiveIntegerField(verbose_name='mailbox count')),
|
||||||
|
('diskspace', models.PositiveIntegerField(help_text='disk space for the hosting package', verbose_name='disk space')),
|
||||||
|
('diskspace_unit', models.PositiveSmallIntegerField(verbose_name='unit of disk space', choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Hosting package',
|
||||||
|
'verbose_name_plural': 'Hosting packages',
|
||||||
|
},
|
||||||
|
bases=(models.Model,),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='MailboxOption',
|
||||||
|
fields=[
|
||||||
|
('hostingoption_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='hostingpackages.HostingOption')),
|
||||||
|
('number', models.PositiveIntegerField(unique=True, verbose_name='number of mailboxes')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['number'],
|
||||||
|
'abstract': False,
|
||||||
|
'verbose_name': 'Mailbox option',
|
||||||
|
'verbose_name_plural': 'Mailbox options',
|
||||||
|
},
|
||||||
|
bases=('hostingpackages.hostingoption', models.Model),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='UserDatabaseOption',
|
||||||
|
fields=[
|
||||||
|
('hostingoption_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='hostingpackages.HostingOption')),
|
||||||
|
('number', models.PositiveIntegerField(default=1, verbose_name='number of databases')),
|
||||||
|
('db_type', models.PositiveSmallIntegerField(verbose_name='database type', choices=[(0, 'PostgreSQL'), (1, 'MySQL')])),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['db_type', 'number'],
|
||||||
|
'abstract': False,
|
||||||
|
'verbose_name': 'Database option',
|
||||||
|
'verbose_name_plural': 'Database options',
|
||||||
|
},
|
||||||
|
bases=('hostingpackages.hostingoption', models.Model),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='userdatabaseoption',
|
||||||
|
unique_together=set([('number', 'db_type')]),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='diskspaceoption',
|
||||||
|
unique_together=set([('diskspace', 'diskspace_unit')]),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='customeruserdatabaseoption',
|
||||||
|
name='template',
|
||||||
|
field=models.ForeignKey(verbose_name='user database option template', to='hostingpackages.UserDatabaseOption', help_text='The user database option template that this hosting option is based on'),
|
||||||
|
preserve_default=True,
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='customeruserdatabaseoption',
|
||||||
|
unique_together=set([('number', 'db_type')]),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='customermailboxoption',
|
||||||
|
name='template',
|
||||||
|
field=models.ForeignKey(verbose_name='mailbox option template', to='hostingpackages.UserDatabaseOption', help_text='The mailbox option template that this hosting option is based on'),
|
||||||
|
preserve_default=True,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='customerhostingpackageoption',
|
||||||
|
name='hosting_package',
|
||||||
|
field=models.ForeignKey(verbose_name='hosting package', to='hostingpackages.CustomerHostingPackage'),
|
||||||
|
preserve_default=True,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='customerhostingpackage',
|
||||||
|
name='template',
|
||||||
|
field=models.ForeignKey(verbose_name='hosting package template', to='hostingpackages.HostingPackageTemplate', help_text='The hosting package template that this hosting package is based on.'),
|
||||||
|
preserve_default=True,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='customerdiskspaceoption',
|
||||||
|
name='template',
|
||||||
|
field=models.ForeignKey(verbose_name='disk space option template', to='hostingpackages.DiskSpaceOption', help_text='The disk space option template that this hosting option is based on'),
|
||||||
|
preserve_default=True,
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='customerdiskspaceoption',
|
||||||
|
unique_together=set([('diskspace', 'diskspace_unit')]),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,245 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import models, migrations
|
||||||
|
import django.utils.timezone
|
||||||
|
from django.conf import settings
|
||||||
|
import model_utils.fields
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
replaces = [('hostingpackages', '0001_initial'), ('hostingpackages', '0002_auto_20150118_1149'), ('hostingpackages', '0003_auto_20150118_1221'), ('hostingpackages', '0004_customerhostingpackage_osuser'), ('hostingpackages', '0005_auto_20150118_1303')]
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('osusers', '0004_auto_20150104_1751'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='CustomerHostingPackage',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||||
|
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, verbose_name='created', editable=False)),
|
||||||
|
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, verbose_name='modified', editable=False)),
|
||||||
|
('name', models.CharField(unique=True, max_length=128, verbose_name='name')),
|
||||||
|
('description', models.TextField(verbose_name='description', blank=True)),
|
||||||
|
('mailboxcount', models.PositiveIntegerField(verbose_name='mailbox count')),
|
||||||
|
('diskspace', models.PositiveIntegerField(help_text='disk space for the hosting package', verbose_name='disk space')),
|
||||||
|
('diskspace_unit', models.PositiveSmallIntegerField(verbose_name='unit of disk space', choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])),
|
||||||
|
('customer', models.ForeignKey(verbose_name='customer', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'customer hosting package',
|
||||||
|
'verbose_name_plural': 'customer hosting packages',
|
||||||
|
},
|
||||||
|
bases=(models.Model,),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='CustomerHostingPackageOption',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||||
|
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, verbose_name='created', editable=False)),
|
||||||
|
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, verbose_name='modified', editable=False)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'customer hosting option',
|
||||||
|
'verbose_name_plural': 'customer hosting options',
|
||||||
|
},
|
||||||
|
bases=(models.Model,),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='CustomerDiskSpaceOption',
|
||||||
|
fields=[
|
||||||
|
('customerhostingpackageoption_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='hostingpackages.CustomerHostingPackageOption')),
|
||||||
|
('diskspace', models.PositiveIntegerField(verbose_name='disk space')),
|
||||||
|
('diskspace_unit', models.PositiveSmallIntegerField(verbose_name='unit of disk space', choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['diskspace_unit', 'diskspace'],
|
||||||
|
'abstract': False,
|
||||||
|
'verbose_name': 'Disk space option',
|
||||||
|
'verbose_name_plural': 'Disk space options',
|
||||||
|
},
|
||||||
|
bases=('hostingpackages.customerhostingpackageoption', models.Model),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='CustomerMailboxOption',
|
||||||
|
fields=[
|
||||||
|
('customerhostingpackageoption_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='hostingpackages.CustomerHostingPackageOption')),
|
||||||
|
('number', models.PositiveIntegerField(unique=True, verbose_name='number of mailboxes')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['number'],
|
||||||
|
'abstract': False,
|
||||||
|
'verbose_name': 'Mailbox option',
|
||||||
|
'verbose_name_plural': 'Mailbox options',
|
||||||
|
},
|
||||||
|
bases=('hostingpackages.customerhostingpackageoption', models.Model),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='CustomerUserDatabaseOption',
|
||||||
|
fields=[
|
||||||
|
('customerhostingpackageoption_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='hostingpackages.CustomerHostingPackageOption')),
|
||||||
|
('number', models.PositiveIntegerField(default=1, verbose_name='number of databases')),
|
||||||
|
('db_type', models.PositiveSmallIntegerField(verbose_name='database type', choices=[(0, 'PostgreSQL'), (1, 'MySQL')])),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['db_type', 'number'],
|
||||||
|
'abstract': False,
|
||||||
|
'verbose_name': 'Database option',
|
||||||
|
'verbose_name_plural': 'Database options',
|
||||||
|
},
|
||||||
|
bases=('hostingpackages.customerhostingpackageoption', models.Model),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='HostingOption',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||||
|
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, verbose_name='created', editable=False)),
|
||||||
|
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, verbose_name='modified', editable=False)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Hosting option',
|
||||||
|
'verbose_name_plural': 'Hosting options',
|
||||||
|
},
|
||||||
|
bases=(models.Model,),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='DiskSpaceOption',
|
||||||
|
fields=[
|
||||||
|
('hostingoption_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='hostingpackages.HostingOption')),
|
||||||
|
('diskspace', models.PositiveIntegerField(verbose_name='disk space')),
|
||||||
|
('diskspace_unit', models.PositiveSmallIntegerField(verbose_name='unit of disk space', choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['diskspace_unit', 'diskspace'],
|
||||||
|
'abstract': False,
|
||||||
|
'verbose_name': 'Disk space option',
|
||||||
|
'verbose_name_plural': 'Disk space options',
|
||||||
|
},
|
||||||
|
bases=('hostingpackages.hostingoption', models.Model),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='HostingPackageTemplate',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||||
|
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, verbose_name='created', editable=False)),
|
||||||
|
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, verbose_name='modified', editable=False)),
|
||||||
|
('name', models.CharField(unique=True, max_length=128, verbose_name='name')),
|
||||||
|
('description', models.TextField(verbose_name='description', blank=True)),
|
||||||
|
('mailboxcount', models.PositiveIntegerField(verbose_name='mailbox count')),
|
||||||
|
('diskspace', models.PositiveIntegerField(help_text='disk space for the hosting package', verbose_name='disk space')),
|
||||||
|
('diskspace_unit', models.PositiveSmallIntegerField(verbose_name='unit of disk space', choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Hosting package',
|
||||||
|
'verbose_name_plural': 'Hosting packages',
|
||||||
|
},
|
||||||
|
bases=(models.Model,),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='MailboxOption',
|
||||||
|
fields=[
|
||||||
|
('hostingoption_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='hostingpackages.HostingOption')),
|
||||||
|
('number', models.PositiveIntegerField(unique=True, verbose_name='number of mailboxes')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['number'],
|
||||||
|
'abstract': False,
|
||||||
|
'verbose_name': 'Mailbox option',
|
||||||
|
'verbose_name_plural': 'Mailbox options',
|
||||||
|
},
|
||||||
|
bases=('hostingpackages.hostingoption', models.Model),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='UserDatabaseOption',
|
||||||
|
fields=[
|
||||||
|
('hostingoption_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='hostingpackages.HostingOption')),
|
||||||
|
('number', models.PositiveIntegerField(default=1, verbose_name='number of databases')),
|
||||||
|
('db_type', models.PositiveSmallIntegerField(verbose_name='database type', choices=[(0, 'PostgreSQL'), (1, 'MySQL')])),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['db_type', 'number'],
|
||||||
|
'abstract': False,
|
||||||
|
'verbose_name': 'Database option',
|
||||||
|
'verbose_name_plural': 'Database options',
|
||||||
|
},
|
||||||
|
bases=('hostingpackages.hostingoption', models.Model),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='userdatabaseoption',
|
||||||
|
unique_together=set([('number', 'db_type')]),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='diskspaceoption',
|
||||||
|
unique_together=set([('diskspace', 'diskspace_unit')]),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='customeruserdatabaseoption',
|
||||||
|
name='template',
|
||||||
|
field=models.ForeignKey(verbose_name='user database option template', to='hostingpackages.UserDatabaseOption', help_text='The user database option template that this hosting option is based on'),
|
||||||
|
preserve_default=True,
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='customeruserdatabaseoption',
|
||||||
|
unique_together=set([('number', 'db_type')]),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='customermailboxoption',
|
||||||
|
name='template',
|
||||||
|
field=models.ForeignKey(verbose_name='mailbox option template', to='hostingpackages.UserDatabaseOption', help_text='The mailbox option template that this mailbox option is based on'),
|
||||||
|
preserve_default=True,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='customerhostingpackageoption',
|
||||||
|
name='hosting_package',
|
||||||
|
field=models.ForeignKey(verbose_name='hosting package', to='hostingpackages.CustomerHostingPackage'),
|
||||||
|
preserve_default=True,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='customerhostingpackage',
|
||||||
|
name='template',
|
||||||
|
field=models.ForeignKey(verbose_name='hosting package template', to='hostingpackages.HostingPackageTemplate', help_text='The hosting package template that this hosting package is based on'),
|
||||||
|
preserve_default=True,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='customerdiskspaceoption',
|
||||||
|
name='template',
|
||||||
|
field=models.ForeignKey(verbose_name='disk space option template', to='hostingpackages.DiskSpaceOption', help_text='The disk space option template that this hosting option is based on'),
|
||||||
|
preserve_default=True,
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='customerdiskspaceoption',
|
||||||
|
unique_together=set([('diskspace', 'diskspace_unit')]),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='customerdiskspaceoption',
|
||||||
|
name='template',
|
||||||
|
field=models.ForeignKey(verbose_name='disk space option template', to='hostingpackages.DiskSpaceOption', help_text='The disk space option template that this disk space option is based on'),
|
||||||
|
preserve_default=True,
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='customeruserdatabaseoption',
|
||||||
|
name='template',
|
||||||
|
field=models.ForeignKey(verbose_name='user database option template', to='hostingpackages.UserDatabaseOption', help_text='The user database option template that this database option is based on'),
|
||||||
|
preserve_default=True,
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='customerhostingpackage',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(max_length=128, verbose_name='name'),
|
||||||
|
preserve_default=True,
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='customerhostingpackage',
|
||||||
|
unique_together=set([('customer', 'name')]),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='customerhostingpackage',
|
||||||
|
name='osuser',
|
||||||
|
field=models.OneToOneField(null=True, blank=True, to='osusers.User', verbose_name='Operating system user'),
|
||||||
|
preserve_default=True,
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,38 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import models, migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('hostingpackages', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='customerdiskspaceoption',
|
||||||
|
name='template',
|
||||||
|
field=models.ForeignKey(verbose_name='disk space option template', to='hostingpackages.DiskSpaceOption', help_text='The disk space option template that this disk space option is based on'),
|
||||||
|
preserve_default=True,
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='customerhostingpackage',
|
||||||
|
name='template',
|
||||||
|
field=models.ForeignKey(verbose_name='hosting package template', to='hostingpackages.HostingPackageTemplate', help_text='The hosting package template that this hosting package is based on'),
|
||||||
|
preserve_default=True,
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='customermailboxoption',
|
||||||
|
name='template',
|
||||||
|
field=models.ForeignKey(verbose_name='mailbox option template', to='hostingpackages.UserDatabaseOption', help_text='The mailbox option template that this mailbox option is based on'),
|
||||||
|
preserve_default=True,
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='customeruserdatabaseoption',
|
||||||
|
name='template',
|
||||||
|
field=models.ForeignKey(verbose_name='user database option template', to='hostingpackages.UserDatabaseOption', help_text='The user database option template that this database option is based on'),
|
||||||
|
preserve_default=True,
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import models, migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('hostingpackages', '0001_squashed_0005_auto_20150118_1303'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='hostingoption',
|
||||||
|
options={},
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,24 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import models, migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('hostingpackages', '0002_auto_20150118_1149'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='customerhostingpackage',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(max_length=128, verbose_name='name'),
|
||||||
|
preserve_default=True,
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='customerhostingpackage',
|
||||||
|
unique_together=set([('customer', 'name')]),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,20 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import models, migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('hostingpackages', '0002_auto_20150118_1319'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='customermailboxoption',
|
||||||
|
name='template',
|
||||||
|
field=models.ForeignKey(verbose_name='mailbox option template', to='hostingpackages.MailboxOption', help_text='The mailbox option template that this mailbox option is based on'),
|
||||||
|
preserve_default=True,
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,21 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import models, migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('osusers', '0004_auto_20150104_1751'),
|
||||||
|
('hostingpackages', '0003_auto_20150118_1221'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='customerhostingpackage',
|
||||||
|
name='osuser',
|
||||||
|
field=models.ForeignKey(verbose_name='Operating system user', blank=True, to='osusers.User', null=True),
|
||||||
|
preserve_default=True,
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,20 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import models, migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('hostingpackages', '0004_customerhostingpackage_osuser'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='customerhostingpackage',
|
||||||
|
name='osuser',
|
||||||
|
field=models.OneToOneField(null=True, blank=True, to='osusers.User', verbose_name='Operating system user'),
|
||||||
|
preserve_default=True,
|
||||||
|
),
|
||||||
|
]
|
0
gnuviechadmin/hostingpackages/migrations/__init__.py
Normal file
0
gnuviechadmin/hostingpackages/migrations/__init__.py
Normal file
389
gnuviechadmin/hostingpackages/models.py
Normal file
389
gnuviechadmin/hostingpackages/models.py
Normal file
|
@ -0,0 +1,389 @@
|
||||||
|
"""
|
||||||
|
This module contains the hosting package models.
|
||||||
|
|
||||||
|
"""
|
||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.db import transaction
|
||||||
|
from django.db import models
|
||||||
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
|
from django.utils.translation import ugettext_lazy as _, ungettext
|
||||||
|
|
||||||
|
from model_utils import Choices
|
||||||
|
from model_utils.models import TimeStampedModel
|
||||||
|
|
||||||
|
from managemails.models import Mailbox
|
||||||
|
from osusers.models import (
|
||||||
|
AdditionalGroup,
|
||||||
|
Group,
|
||||||
|
User as OsUser,
|
||||||
|
)
|
||||||
|
from userdbs.models import DB_TYPES
|
||||||
|
|
||||||
|
|
||||||
|
DISK_SPACE_UNITS = Choices(
|
||||||
|
(0, 'M', _('MiB')),
|
||||||
|
(1, 'G', _('GiB')),
|
||||||
|
(2, 'T', _('TiB')),
|
||||||
|
)
|
||||||
|
|
||||||
|
DISK_SPACE_FACTORS = (
|
||||||
|
(1, None, None),
|
||||||
|
(1024, 1, None),
|
||||||
|
(1024 * 1024, 1024, 1),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
|
class HostingPackageBase(TimeStampedModel):
|
||||||
|
description = models.TextField(_('description'), blank=True)
|
||||||
|
mailboxcount = models.PositiveIntegerField(_('mailbox count'))
|
||||||
|
diskspace = models.PositiveIntegerField(
|
||||||
|
_('disk space'), help_text=_('disk space for the hosting package'))
|
||||||
|
diskspace_unit = models.PositiveSmallIntegerField(
|
||||||
|
_('unit of disk space'), choices=DISK_SPACE_UNITS)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
class HostingPackageTemplate(HostingPackageBase):
|
||||||
|
name = models.CharField(_('name'), max_length=128, unique=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _('Hosting package')
|
||||||
|
verbose_name_plural = _('Hosting packages')
|
||||||
|
|
||||||
|
|
||||||
|
class HostingOption(TimeStampedModel):
|
||||||
|
"""
|
||||||
|
This is the base class for several types of hosting options.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
|
class DiskSpaceOptionBase(models.Model):
|
||||||
|
diskspace = models.PositiveIntegerField(_('disk space'))
|
||||||
|
diskspace_unit = models.PositiveSmallIntegerField(
|
||||||
|
_('unit of disk space'), choices=DISK_SPACE_UNITS)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
ordering = ['diskspace_unit', 'diskspace']
|
||||||
|
unique_together = ['diskspace', 'diskspace_unit']
|
||||||
|
verbose_name = _('Disk space option')
|
||||||
|
verbose_name_plural = _('Disk space options')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return _("Additional disk space {space} {unit}").format(
|
||||||
|
space=self.diskspace, unit=self.get_diskspace_unit_display())
|
||||||
|
|
||||||
|
class DiskSpaceOption(DiskSpaceOptionBase, HostingOption):
|
||||||
|
"""
|
||||||
|
This is a class for hosting options adding additional disk space to
|
||||||
|
existing hosting packages.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
|
class UserDatabaseOptionBase(models.Model):
|
||||||
|
number = models.PositiveIntegerField(
|
||||||
|
_('number of databases'), default=1)
|
||||||
|
db_type = models.PositiveSmallIntegerField(
|
||||||
|
_('database type'), choices=DB_TYPES)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
ordering = ['db_type', 'number']
|
||||||
|
unique_together = ['number', 'db_type']
|
||||||
|
verbose_name = _('Database option')
|
||||||
|
verbose_name_plural = _('Database options')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return ungettext(
|
||||||
|
'{type} database',
|
||||||
|
'{count} {type} databases',
|
||||||
|
self.number
|
||||||
|
).format(
|
||||||
|
type=self.get_db_type_display(), count=self.number
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class UserDatabaseOption(UserDatabaseOptionBase, HostingOption):
|
||||||
|
"""
|
||||||
|
This is a class for hosting options adding user databases to existing
|
||||||
|
hosting packages.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
|
class MailboxOptionBase(models.Model):
|
||||||
|
"""
|
||||||
|
Base class for mailbox options.
|
||||||
|
|
||||||
|
"""
|
||||||
|
number = models.PositiveIntegerField(
|
||||||
|
_('number of mailboxes'), unique=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
ordering = ['number']
|
||||||
|
verbose_name = _('Mailbox option')
|
||||||
|
verbose_name_plural = _('Mailbox options')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return ungettext(
|
||||||
|
'{count} additional mailbox',
|
||||||
|
'{count} additional mailboxes',
|
||||||
|
self.number
|
||||||
|
).format(
|
||||||
|
count=self.number
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MailboxOption(MailboxOptionBase, HostingOption):
|
||||||
|
"""
|
||||||
|
This is a class for hosting options adding more mailboxes to existing
|
||||||
|
hosting packages.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class CustomerHostingPackageManager(models.Manager):
|
||||||
|
"""
|
||||||
|
This is the default manager implementation for
|
||||||
|
:py:class:`CustomerHostingPackage`.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def create_from_template(self, customer, template, name, **kwargs):
|
||||||
|
"""
|
||||||
|
Use this method to create a new :py:class:`CustomerHostingPackage` from
|
||||||
|
a :py:class:`HostingPackageTemplate`.
|
||||||
|
|
||||||
|
The method copies data from the template to the new
|
||||||
|
:py:class:`CustomerHostingPackage` instance.
|
||||||
|
|
||||||
|
:param customer: a Django user representing a customer
|
||||||
|
:param template: a :py:class:`HostingPackageTemplate`
|
||||||
|
:param str name: the name of the hosting package there must only be
|
||||||
|
one hosting package of this name for each customer
|
||||||
|
:return: customer hosting package
|
||||||
|
:rtype: :py:class:`CustomerHostingPackage`
|
||||||
|
|
||||||
|
"""
|
||||||
|
package = CustomerHostingPackage(
|
||||||
|
customer=customer, template=template, name=name)
|
||||||
|
package.description = template.description
|
||||||
|
package.copy_template_attributes()
|
||||||
|
if 'commit' in kwargs and kwargs['commit'] is True:
|
||||||
|
package.save(**kwargs)
|
||||||
|
return package
|
||||||
|
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
|
class CustomerHostingPackage(HostingPackageBase):
|
||||||
|
"""
|
||||||
|
This class defines customer specific hosting packages.
|
||||||
|
|
||||||
|
"""
|
||||||
|
customer = models.ForeignKey(
|
||||||
|
settings.AUTH_USER_MODEL, verbose_name=_('customer'))
|
||||||
|
template = models.ForeignKey(
|
||||||
|
HostingPackageTemplate, verbose_name=_('hosting package template'),
|
||||||
|
help_text=_(
|
||||||
|
'The hosting package template that this hosting package is based'
|
||||||
|
' on'
|
||||||
|
))
|
||||||
|
name = models.CharField(_('name'), max_length=128)
|
||||||
|
osuser = models.OneToOneField(
|
||||||
|
OsUser, verbose_name=_('Operating system user'),
|
||||||
|
blank=True, null=True)
|
||||||
|
|
||||||
|
objects = CustomerHostingPackageManager()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = ['customer', 'name']
|
||||||
|
verbose_name = _('customer hosting package')
|
||||||
|
verbose_name_plural = _('customer hosting packages')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return _("{name} for {customer}").format(
|
||||||
|
name=self.name, customer=self.customer
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return reverse('hosting_package_details', kwargs={
|
||||||
|
'user': self.customer.username,
|
||||||
|
'pk': self.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
def copy_template_attributes(self):
|
||||||
|
"""
|
||||||
|
Copy the attributes of the hosting package's template to the package.
|
||||||
|
|
||||||
|
"""
|
||||||
|
for attrname in ('diskspace', 'diskspace_unit', 'mailboxcount'):
|
||||||
|
setattr(self, attrname, getattr(self.template, attrname))
|
||||||
|
|
||||||
|
def get_disk_space(self, unit=None):
|
||||||
|
"""
|
||||||
|
Get the total disk space reserved for this hosting package and all its
|
||||||
|
additional disk space options.
|
||||||
|
|
||||||
|
:return: disk space
|
||||||
|
:rtype: int
|
||||||
|
|
||||||
|
"""
|
||||||
|
diskspace = self.diskspace
|
||||||
|
min_unit = self.diskspace_unit
|
||||||
|
|
||||||
|
options = CustomerDiskSpaceOption.objects.filter(hosting_package=self)
|
||||||
|
for option in options:
|
||||||
|
if option.diskspace_unit == min_unit:
|
||||||
|
diskspace += option.disk_space
|
||||||
|
elif option.diskspace_unit > min_unit:
|
||||||
|
diskspace += (
|
||||||
|
DISK_SPACE_FACTORS[option.diskspace_unit][min_unit] *
|
||||||
|
option.diskspace)
|
||||||
|
else:
|
||||||
|
diskspace = (
|
||||||
|
DISK_SPACE_FACTORS[min_unit][option.diskspace_unit] *
|
||||||
|
diskspace) + option.diskspace
|
||||||
|
min_unit = option.diskspace_unit
|
||||||
|
if unit is None:
|
||||||
|
return DISK_SPACE_FACTORS[min_unit][0] * diskspace * 1024**2
|
||||||
|
if unit > min_unit:
|
||||||
|
return DISK_SPACE_FACTORS[unit][min_unit] * diskspace
|
||||||
|
return DISK_SPACE_FACTORS[min_unit][unit] * diskspace
|
||||||
|
|
||||||
|
def get_quota(self):
|
||||||
|
soft = 1024 * self.get_disk_space(DISK_SPACE_UNITS.M)
|
||||||
|
hard = soft * 105 / 100
|
||||||
|
return (soft, hard)
|
||||||
|
|
||||||
|
def get_used_mailboxes(self):
|
||||||
|
"""
|
||||||
|
Get the number of used mailboxes for this hosting package.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if self.osuser:
|
||||||
|
return Mailbox.objects.filter(osuser=self.osuser).count()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def get_mailboxes(self):
|
||||||
|
"""
|
||||||
|
Get the number of mailboxes provided by this hosting package and all
|
||||||
|
of its mailbox options.
|
||||||
|
|
||||||
|
"""
|
||||||
|
result = CustomerMailboxOption.objects.filter(
|
||||||
|
hosting_package=self
|
||||||
|
).aggregate(
|
||||||
|
mailbox_sum=models.Sum('number')
|
||||||
|
)
|
||||||
|
return self.mailboxcount + (result['mailbox_sum'] or 0)
|
||||||
|
|
||||||
|
def get_databases(self):
|
||||||
|
"""
|
||||||
|
Get the number of user databases provided by user database hosting
|
||||||
|
options for this hosting package.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return CustomerUserDatabaseOption.objects.values(
|
||||||
|
'db_type'
|
||||||
|
).filter(hosting_package=self).annotate(
|
||||||
|
number=models.Sum('number')
|
||||||
|
).all()
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Save the hosting package to the database.
|
||||||
|
|
||||||
|
If this is a new hosting package a new operating system user is
|
||||||
|
created and assigned to the hosting package.
|
||||||
|
|
||||||
|
:param args: positional arguments to be passed on to
|
||||||
|
:py:meth:`django.db.Model.save`
|
||||||
|
:param kwargs: keyword arguments to be passed on to
|
||||||
|
:py:meth:`django.db.Model.save`
|
||||||
|
:return: self
|
||||||
|
:rtype: :py:class:`CustomerHostingPackage`
|
||||||
|
|
||||||
|
"""
|
||||||
|
if self.pk is None:
|
||||||
|
self.copy_template_attributes()
|
||||||
|
self.osuser = OsUser.objects.create_user(self.customer)
|
||||||
|
for group in settings.OSUSER_DEFAULT_GROUPS:
|
||||||
|
AdditionalGroup.objects.create(
|
||||||
|
user=self.osuser, group=Group.objects.get(groupname=group)
|
||||||
|
)
|
||||||
|
return super(CustomerHostingPackage, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomerHostingPackageOption(TimeStampedModel):
|
||||||
|
"""
|
||||||
|
This class defines options for customer hosting packages.
|
||||||
|
|
||||||
|
"""
|
||||||
|
hosting_package = models.ForeignKey(
|
||||||
|
CustomerHostingPackage, verbose_name=_('hosting package'))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _('customer hosting option')
|
||||||
|
verbose_name_plural = _('customer hosting options')
|
||||||
|
|
||||||
|
|
||||||
|
class CustomerDiskSpaceOption(DiskSpaceOptionBase,
|
||||||
|
CustomerHostingPackageOption):
|
||||||
|
"""
|
||||||
|
This is a class for customer hosting package options adding additional disk
|
||||||
|
space to existing customer hosting package.
|
||||||
|
|
||||||
|
"""
|
||||||
|
template = models.ForeignKey(
|
||||||
|
DiskSpaceOption,
|
||||||
|
verbose_name=_('disk space option template'),
|
||||||
|
help_text=_(
|
||||||
|
'The disk space option template that this disk space option is'
|
||||||
|
' based on'
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
class CustomerUserDatabaseOption(UserDatabaseOptionBase,
|
||||||
|
CustomerHostingPackageOption):
|
||||||
|
"""
|
||||||
|
This is a class for customer hosting package options adding user databases
|
||||||
|
to existing customer hosting packages.
|
||||||
|
|
||||||
|
"""
|
||||||
|
template = models.ForeignKey(
|
||||||
|
UserDatabaseOption,
|
||||||
|
verbose_name=_('user database option template'),
|
||||||
|
help_text=_(
|
||||||
|
'The user database option template that this database option is'
|
||||||
|
' based on'
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
class CustomerMailboxOption(MailboxOptionBase,
|
||||||
|
CustomerHostingPackageOption):
|
||||||
|
"""
|
||||||
|
This is a class for customer hosting package options adding additional
|
||||||
|
mailboxes to existing customer hosting packages.
|
||||||
|
|
||||||
|
"""
|
||||||
|
template = models.ForeignKey(
|
||||||
|
MailboxOption,
|
||||||
|
verbose_name=_('mailbox option template'),
|
||||||
|
help_text=_(
|
||||||
|
'The mailbox option template that this mailbox option is based on'
|
||||||
|
))
|
0
gnuviechadmin/hostingpackages/tests/__init__.py
Normal file
0
gnuviechadmin/hostingpackages/tests/__init__.py
Normal file
31
gnuviechadmin/hostingpackages/tests/test_models.py
Normal file
31
gnuviechadmin/hostingpackages/tests/test_models.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
"""
|
||||||
|
Test for models.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from hostingpackages.models import (
|
||||||
|
DISK_SPACE_UNITS,
|
||||||
|
CustomerHostingPackage,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomerHostingPackageTest(TestCase):
|
||||||
|
def test_get_disk_space_bytes(self):
|
||||||
|
package = CustomerHostingPackage(
|
||||||
|
diskspace=10, diskspace_unit=DISK_SPACE_UNITS.G
|
||||||
|
)
|
||||||
|
self.assertEqual(package.get_disk_space(), 10 * 1024 * 1024**2)
|
||||||
|
|
||||||
|
def test_get_disk_space_mib(self):
|
||||||
|
package = CustomerHostingPackage(
|
||||||
|
diskspace=10, diskspace_unit=DISK_SPACE_UNITS.G
|
||||||
|
)
|
||||||
|
self.assertEqual(package.get_disk_space(DISK_SPACE_UNITS.M), 10 * 1024)
|
||||||
|
|
||||||
|
def test_get_quota(self):
|
||||||
|
package = CustomerHostingPackage(
|
||||||
|
diskspace=256, diskspace_unit=DISK_SPACE_UNITS.M
|
||||||
|
)
|
||||||
|
self.assertEqual(package.get_quota(), (262144, 275251))
|
22
gnuviechadmin/hostingpackages/urls.py
Normal file
22
gnuviechadmin/hostingpackages/urls.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
"""
|
||||||
|
This module defines the URL patterns for hosting package related views.
|
||||||
|
|
||||||
|
"""
|
||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
|
from django.conf.urls import patterns, url
|
||||||
|
|
||||||
|
from .views import (
|
||||||
|
CreateHostingPackage,
|
||||||
|
CustomerHostingPackageDetails,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
urlpatterns = patterns(
|
||||||
|
'',
|
||||||
|
url(r'^(?P<user>[\w0-9@.+-_]+)/create$', CreateHostingPackage.as_view(),
|
||||||
|
name='create_hosting_package'),
|
||||||
|
url(r'^(?P<user>[\w0-9@.+-_]+)/hostingpackage/(?P<pk>\d+)/$',
|
||||||
|
CustomerHostingPackageDetails.as_view(),
|
||||||
|
name='hosting_package_details'),
|
||||||
|
)
|
86
gnuviechadmin/hostingpackages/views.py
Normal file
86
gnuviechadmin/hostingpackages/views.py
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
"""
|
||||||
|
This module defines views related to hosting packages.
|
||||||
|
|
||||||
|
"""
|
||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.shortcuts import redirect
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
from django.views.generic import DetailView
|
||||||
|
from django.views.generic.edit import CreateView
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
|
from braces.views import (
|
||||||
|
LoginRequiredMixin,
|
||||||
|
StaffuserRequiredMixin,
|
||||||
|
)
|
||||||
|
|
||||||
|
from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin
|
||||||
|
|
||||||
|
from .forms import CreateHostingPackageForm
|
||||||
|
from .models import CustomerHostingPackage
|
||||||
|
|
||||||
|
|
||||||
|
class CreateHostingPackage(
|
||||||
|
LoginRequiredMixin, StaffuserRequiredMixin, CreateView
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Create a hosting package.
|
||||||
|
|
||||||
|
"""
|
||||||
|
model = CustomerHostingPackage
|
||||||
|
raise_exception = True
|
||||||
|
template_name_suffix = '_create'
|
||||||
|
form_class = CreateHostingPackageForm
|
||||||
|
|
||||||
|
def get_form_kwargs(self):
|
||||||
|
kwargs = super(CreateHostingPackage, self).get_form_kwargs()
|
||||||
|
kwargs.update(self.kwargs)
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
def get_customer_object(self):
|
||||||
|
return get_user_model().objects.get(username=self.kwargs['user'])
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(CreateHostingPackage, self).get_context_data(**kwargs)
|
||||||
|
context['customer'] = self.get_customer_object()
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse(
|
||||||
|
'customer_dashboard', kwargs={'slug': self.kwargs['user']})
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
hostingpackage = form.save(commit=False)
|
||||||
|
hostingpackage.customer = self.get_customer_object()
|
||||||
|
hostingpackage.save()
|
||||||
|
messages.success(
|
||||||
|
self.request,
|
||||||
|
_('Started setup of new hosting package {name}.').format(
|
||||||
|
name=hostingpackage.name)
|
||||||
|
)
|
||||||
|
return redirect(self.get_success_url())
|
||||||
|
|
||||||
|
|
||||||
|
class CustomerHostingPackageDetails(StaffOrSelfLoginRequiredMixin, DetailView):
|
||||||
|
"""
|
||||||
|
This view is for showing details of a customer hosting package.
|
||||||
|
|
||||||
|
"""
|
||||||
|
model = CustomerHostingPackage
|
||||||
|
context_object_name = 'hostingpackage'
|
||||||
|
|
||||||
|
def get_customer_object(self):
|
||||||
|
return get_user_model().objects.get(username=self.kwargs['user'])
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(CustomerHostingPackageDetails, self).get_context_data(
|
||||||
|
**kwargs)
|
||||||
|
context.update({
|
||||||
|
'customer': self.get_customer_object(),
|
||||||
|
'uploadserver': settings.OSUSER_UPLOAD_SERVER,
|
||||||
|
})
|
||||||
|
return context
|
4
gnuviechadmin/ldaptasks/__init__.py
Normal file
4
gnuviechadmin/ldaptasks/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
"""
|
||||||
|
This module contains :py:mod:`ldaptasks.tasks`.
|
||||||
|
|
||||||
|
"""
|
|
@ -1,6 +1,6 @@
|
||||||
"""
|
"""
|
||||||
This module defines task stubs for the tasks implemented on the Celery
|
This module defines task stubs for the tasks implemented in the gvaldap Celery
|
||||||
workers.
|
worker.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
@ -51,6 +51,20 @@ def create_ldap_user(username, uid, gid, gecos, homedir, shell, password):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task
|
||||||
|
def set_ldap_user_password(self, username, password):
|
||||||
|
"""
|
||||||
|
This task sets the password of an existing :py:class:`LDAP user
|
||||||
|
<ldapentities.models.LdapUser>`.
|
||||||
|
|
||||||
|
:param str username: the user name
|
||||||
|
:param str password: teh clear text password
|
||||||
|
:return: :py:const:`True` if the password has been set, :py:const:`False`
|
||||||
|
if the user does not exist.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def add_ldap_user_to_group(username, groupname):
|
def add_ldap_user_to_group(username, groupname):
|
||||||
"""
|
"""
|
||||||
|
@ -115,89 +129,3 @@ def delete_ldap_group(groupname):
|
||||||
:rtype: boolean
|
:rtype: boolean
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
|
||||||
def setup_file_sftp_userdir(username):
|
|
||||||
"""
|
|
||||||
This task creates the home directory for an SFTP user if it does not exist
|
|
||||||
yet.
|
|
||||||
|
|
||||||
:param str username: the user name
|
|
||||||
:raises Exception: if the SFTP directory of the user cannot be created
|
|
||||||
:return: the created directory name
|
|
||||||
:rtype: str
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
|
||||||
def delete_file_sftp_userdir(username):
|
|
||||||
"""
|
|
||||||
This task recursively deletes the home directory of an SFTP user if it
|
|
||||||
does not exist yet.
|
|
||||||
|
|
||||||
:param str username: the user name
|
|
||||||
:raises Exception: if the SFTP directory of the user cannot be removed
|
|
||||||
:return: the removed directory name
|
|
||||||
:rtype: str
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
|
||||||
def setup_file_mail_userdir(username):
|
|
||||||
"""
|
|
||||||
This task creates the mail base directory for a user if it does not exist
|
|
||||||
yet.
|
|
||||||
|
|
||||||
:param str username: the user name
|
|
||||||
:raises Exception: if the mail base directory for the user cannot be
|
|
||||||
created
|
|
||||||
:return: the created directory name
|
|
||||||
:rtype: str
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
|
||||||
def delete_file_mail_userdir(username):
|
|
||||||
"""
|
|
||||||
This task recursively deletes the mail base directory for a user if it
|
|
||||||
does not exist yet.
|
|
||||||
|
|
||||||
:param str username: the user name
|
|
||||||
:raises Exception: if the mail base directory of the user cannot be removed
|
|
||||||
:return: the removed directory name
|
|
||||||
:rtype: str
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
|
||||||
def create_file_mailbox(username, mailboxname):
|
|
||||||
"""
|
|
||||||
This task creates a new mailbox directory for the given user and mailbox
|
|
||||||
name.
|
|
||||||
|
|
||||||
:param str username: the user name
|
|
||||||
:param str mailboxname: the mailbox name
|
|
||||||
:raises GVAFileException: if the mailbox directory cannot be created
|
|
||||||
:return: the created mailbox directory name
|
|
||||||
:rtype: str
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
|
||||||
def delete_file_mailbox(username, mailboxname):
|
|
||||||
"""
|
|
||||||
This task deletes the given mailbox of the given user.
|
|
||||||
|
|
||||||
:param str username: the user name
|
|
||||||
:param str mailboxname: the mailbox name
|
|
||||||
:raises GVAFileException: if the mailbox directory cannot be deleted
|
|
||||||
:return: the deleted mailbox directory name
|
|
||||||
:rtype: str
|
|
||||||
|
|
||||||
"""
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
"""
|
||||||
|
This module defines the database models for mail handling.
|
||||||
|
|
||||||
|
"""
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
@ -7,7 +13,11 @@ from model_utils.models import TimeStampedModel
|
||||||
|
|
||||||
from domains.models import MailDomain
|
from domains.models import MailDomain
|
||||||
from osusers.models import User as OsUser
|
from osusers.models import User as OsUser
|
||||||
from osusers.tasks import create_file_mailbox, delete_file_mailbox
|
|
||||||
|
from fileservertasks.tasks import (
|
||||||
|
create_file_mailbox,
|
||||||
|
delete_file_mailbox,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ActivateAbleMixin(models.Model):
|
class ActivateAbleMixin(models.Model):
|
||||||
|
@ -22,8 +32,21 @@ class ActivateAbleMixin(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class MailboxManager(models.Manager):
|
class MailboxManager(models.Manager):
|
||||||
|
"""
|
||||||
|
This is the default manager class for :py:class:`Mailbox`.
|
||||||
|
"""
|
||||||
|
|
||||||
def get_next_mailbox_name(self, osuser):
|
def get_next_mailbox_name(self, osuser):
|
||||||
|
"""
|
||||||
|
Get the next available mailbox name for the given operating system
|
||||||
|
user.
|
||||||
|
|
||||||
|
:param osuser: a :py:class:`operating system user <osuser.models.User>`
|
||||||
|
instance
|
||||||
|
:return: name of the mailbox
|
||||||
|
:rtype: str
|
||||||
|
|
||||||
|
"""
|
||||||
count = 1
|
count = 1
|
||||||
mailboxformat = "{0}p{1:02d}"
|
mailboxformat = "{0}p{1:02d}"
|
||||||
mailboxname = mailboxformat.format(osuser.username, count)
|
mailboxname = mailboxformat.format(osuser.username, count)
|
||||||
|
@ -41,6 +64,10 @@ class MailboxManager(models.Manager):
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class Mailbox(ActivateAbleMixin, TimeStampedModel, models.Model):
|
class Mailbox(ActivateAbleMixin, TimeStampedModel, models.Model):
|
||||||
|
"""
|
||||||
|
This is the model class for a mailbox.
|
||||||
|
|
||||||
|
"""
|
||||||
osuser = models.ForeignKey(OsUser)
|
osuser = models.ForeignKey(OsUser)
|
||||||
username = models.CharField(max_length=128, unique=True)
|
username = models.CharField(max_length=128, unique=True)
|
||||||
password = models.CharField(max_length=255)
|
password = models.CharField(max_length=255)
|
||||||
|
@ -53,6 +80,12 @@ class Mailbox(ActivateAbleMixin, TimeStampedModel, models.Model):
|
||||||
verbose_name_plural = _('Mailboxes')
|
verbose_name_plural = _('Mailboxes')
|
||||||
|
|
||||||
def set_password(self, password):
|
def set_password(self, password):
|
||||||
|
"""
|
||||||
|
Set the hashed password for the mailbox.
|
||||||
|
|
||||||
|
:param str password: the clear text password
|
||||||
|
|
||||||
|
"""
|
||||||
self.password = sha512_crypt.encrypt(password)
|
self.password = sha512_crypt.encrypt(password)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
@ -69,6 +102,10 @@ class Mailbox(ActivateAbleMixin, TimeStampedModel, models.Model):
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class MailAddress(ActivateAbleMixin, TimeStampedModel, models.Model):
|
class MailAddress(ActivateAbleMixin, TimeStampedModel, models.Model):
|
||||||
|
"""
|
||||||
|
This is the model class for a mail address.
|
||||||
|
|
||||||
|
"""
|
||||||
localpart = models.CharField(max_length=128)
|
localpart = models.CharField(max_length=128)
|
||||||
domain = models.ForeignKey(MailDomain)
|
domain = models.ForeignKey(MailDomain)
|
||||||
|
|
||||||
|
@ -84,6 +121,10 @@ class MailAddress(ActivateAbleMixin, TimeStampedModel, models.Model):
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class MailAddressMailbox(TimeStampedModel, models.Model):
|
class MailAddressMailbox(TimeStampedModel, models.Model):
|
||||||
|
"""
|
||||||
|
This is the model class to assign a mail address to a mailbox.
|
||||||
|
|
||||||
|
"""
|
||||||
mailaddress = models.OneToOneField(MailAddress, primary_key=True)
|
mailaddress = models.OneToOneField(MailAddress, primary_key=True)
|
||||||
mailbox = models.ForeignKey(Mailbox)
|
mailbox = models.ForeignKey(Mailbox)
|
||||||
|
|
||||||
|
@ -95,6 +136,10 @@ class MailAddressMailbox(TimeStampedModel, models.Model):
|
||||||
|
|
||||||
|
|
||||||
class MailAddressForward(TimeStampedModel, models.Model):
|
class MailAddressForward(TimeStampedModel, models.Model):
|
||||||
|
"""
|
||||||
|
This is a model class to map mail addresses to forwarding addresses.
|
||||||
|
|
||||||
|
"""
|
||||||
mailaddress = models.ForeignKey(MailAddress)
|
mailaddress = models.ForeignKey(MailAddress)
|
||||||
target = models.EmailField(max_length=254)
|
target = models.EmailField(max_length=254)
|
||||||
|
|
||||||
|
|
|
@ -160,7 +160,7 @@ class MailBoxAdminTest(TestCase):
|
||||||
form = self.mbadmin.get_form(Mock)
|
form = self.mbadmin.get_form(Mock)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
form.Meta.fields,
|
form.Meta.fields,
|
||||||
['username', 'password1', 'password2']
|
['osuser', 'password1', 'password2']
|
||||||
)
|
)
|
||||||
|
|
||||||
@override_settings(
|
@override_settings(
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
"""
|
"""
|
||||||
This module defines Celery_ tasks to manage MySQL users and databases.
|
This module defines Celery tasks to manage MySQL users and databases.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
|
@ -6,6 +6,9 @@ from django import forms
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from .forms import (
|
||||||
|
PASSWORD_MISMATCH_ERROR
|
||||||
|
)
|
||||||
from .models import (
|
from .models import (
|
||||||
AdditionalGroup,
|
AdditionalGroup,
|
||||||
Group,
|
Group,
|
||||||
|
@ -13,11 +16,6 @@ from .models import (
|
||||||
User,
|
User,
|
||||||
)
|
)
|
||||||
|
|
||||||
PASSWORD_MISMATCH_ERROR = _("Passwords don't match")
|
|
||||||
"""
|
|
||||||
Error message for non matching passwords.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class AdditionalGroupInline(admin.TabularInline):
|
class AdditionalGroupInline(admin.TabularInline):
|
||||||
"""
|
"""
|
||||||
|
|
71
gnuviechadmin/osusers/forms.py
Normal file
71
gnuviechadmin/osusers/forms.py
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
"""
|
||||||
|
This module defines operating system user related forms.
|
||||||
|
|
||||||
|
"""
|
||||||
|
from __future__ 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 User
|
||||||
|
|
||||||
|
PASSWORD_MISMATCH_ERROR = _("Passwords don't match")
|
||||||
|
"""
|
||||||
|
Error message for non matching passwords.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class ChangeOsUserPasswordForm(forms.ModelForm):
|
||||||
|
"""
|
||||||
|
A form for setting an OS user's password.
|
||||||
|
|
||||||
|
"""
|
||||||
|
password1 = forms.CharField(
|
||||||
|
label=_('Password'), widget=forms.PasswordInput,
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
password2 = forms.CharField(
|
||||||
|
label=_('Password (again)'), widget=forms.PasswordInput,
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
fields = []
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.helper = FormHelper()
|
||||||
|
super(ChangeOsUserPasswordForm, self).__init__(*args, **kwargs)
|
||||||
|
self.helper.form_action = reverse(
|
||||||
|
'set_osuser_password', kwargs={'slug': self.instance.username})
|
||||||
|
self.helper.add_input(Submit('submit', _('Set password')))
|
||||||
|
|
||||||
|
def clean_password2(self):
|
||||||
|
"""
|
||||||
|
Check that the two password entries match.
|
||||||
|
|
||||||
|
:return: the validated password
|
||||||
|
:rtype: str or None
|
||||||
|
|
||||||
|
"""
|
||||||
|
password1 = self.cleaned_data.get('password1')
|
||||||
|
password2 = self.cleaned_data.get('password2')
|
||||||
|
if password1 and password2 and password1 != password2:
|
||||||
|
raise forms.ValidationError(PASSWORD_MISMATCH_ERROR)
|
||||||
|
return password2
|
||||||
|
|
||||||
|
def save(self, commit=True):
|
||||||
|
"""
|
||||||
|
Save the provided password in hashed format.
|
||||||
|
|
||||||
|
:param boolean commit: whether to save the created user
|
||||||
|
:return: user instance
|
||||||
|
:rtype: :py:class:`osusers.models.User`
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.instance.set_password(self.cleaned_data['password1'])
|
||||||
|
return super(ChangeOsUserPasswordForm, self).save(commit=commit)
|
|
@ -22,15 +22,19 @@ from passlib.utils import generate_password
|
||||||
|
|
||||||
from taskresults.models import TaskResult
|
from taskresults.models import TaskResult
|
||||||
|
|
||||||
from .tasks import (
|
from ldaptasks.tasks import (
|
||||||
add_ldap_user_to_group,
|
add_ldap_user_to_group,
|
||||||
create_ldap_group,
|
create_ldap_group,
|
||||||
create_ldap_user,
|
create_ldap_user,
|
||||||
delete_file_mail_userdir,
|
|
||||||
delete_file_sftp_userdir,
|
|
||||||
delete_ldap_group,
|
delete_ldap_group,
|
||||||
delete_ldap_user,
|
delete_ldap_user,
|
||||||
remove_ldap_user_from_group,
|
remove_ldap_user_from_group,
|
||||||
|
set_ldap_user_password,
|
||||||
|
)
|
||||||
|
|
||||||
|
from fileservertasks.tasks import (
|
||||||
|
delete_file_mail_userdir,
|
||||||
|
delete_file_sftp_userdir,
|
||||||
setup_file_mail_userdir,
|
setup_file_mail_userdir,
|
||||||
setup_file_sftp_userdir,
|
setup_file_sftp_userdir,
|
||||||
)
|
)
|
||||||
|
@ -242,15 +246,26 @@ class User(TimeStampedModel, models.Model):
|
||||||
"""
|
"""
|
||||||
if hasattr(self, 'shadow'):
|
if hasattr(self, 'shadow'):
|
||||||
self.shadow.set_password(password)
|
self.shadow.set_password(password)
|
||||||
|
success = set_ldap_user_password.delay(
|
||||||
|
self.username, password).get()
|
||||||
|
if success:
|
||||||
|
_LOGGER.info(
|
||||||
|
"successfully set LDAP password for %s", self.username)
|
||||||
|
else:
|
||||||
|
_LOGGER.error(
|
||||||
|
"setting the LDAP password for %s failed", self.username)
|
||||||
|
return success
|
||||||
else:
|
else:
|
||||||
self.shadow = Shadow.objects.create_shadow(
|
self.shadow = Shadow.objects.create_shadow(
|
||||||
user=self, password=password
|
user=self, password=password
|
||||||
)
|
)
|
||||||
dn = create_ldap_user.delay(
|
dn = create_ldap_user.delay(
|
||||||
self.username, self.uid, self.group.gid, self.gecos,
|
self.username, self.uid, self.group.gid, self.gecos,
|
||||||
self.homedir, self.shell, password
|
self.homedir, self.shell, password
|
||||||
).get()
|
).get()
|
||||||
logging.info("set LDAP password for %s", dn)
|
_LOGGER.info("set LDAP password for %s", dn)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
|
|
@ -6,17 +6,14 @@ from django.test.utils import override_settings
|
||||||
from mock import patch, Mock
|
from mock import patch, Mock
|
||||||
|
|
||||||
from osusers.models import (
|
from osusers.models import (
|
||||||
DeleteTaskResult,
|
|
||||||
Group,
|
Group,
|
||||||
User,
|
User,
|
||||||
)
|
)
|
||||||
from osusers.admin import (
|
from osusers.admin import (
|
||||||
DeleteTaskResultAdmin,
|
|
||||||
GroupAdmin,
|
GroupAdmin,
|
||||||
PASSWORD_MISMATCH_ERROR,
|
PASSWORD_MISMATCH_ERROR,
|
||||||
UserAdmin,
|
UserAdmin,
|
||||||
UserCreationForm,
|
UserCreationForm,
|
||||||
UserTaskResultInline,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -85,7 +82,7 @@ class UserAdminTest(TestCase):
|
||||||
form = self.uadmin.get_form(Mock(name='request'))
|
form = self.uadmin.get_form(Mock(name='request'))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
form.Meta.fields,
|
form.Meta.fields,
|
||||||
['password1', 'password2']
|
['customer', 'password1', 'password2']
|
||||||
)
|
)
|
||||||
|
|
||||||
@override_settings(
|
@override_settings(
|
||||||
|
@ -103,7 +100,7 @@ class UserAdminTest(TestCase):
|
||||||
|
|
||||||
def test_get_inline_instances_without_object(self):
|
def test_get_inline_instances_without_object(self):
|
||||||
inlines = self.uadmin.get_inline_instances(Mock(name='request'))
|
inlines = self.uadmin.get_inline_instances(Mock(name='request'))
|
||||||
self.assertEqual(inlines, [])
|
self.assertEqual(len(inlines), 2)
|
||||||
|
|
||||||
@override_settings(
|
@override_settings(
|
||||||
CELERY_ALWAYS_EAGER=True,
|
CELERY_ALWAYS_EAGER=True,
|
||||||
|
|
|
@ -12,12 +12,9 @@ from passlib.hash import sha512_crypt
|
||||||
from osusers.models import (
|
from osusers.models import (
|
||||||
CANNOT_USE_PRIMARY_GROUP_AS_ADDITIONAL,
|
CANNOT_USE_PRIMARY_GROUP_AS_ADDITIONAL,
|
||||||
AdditionalGroup,
|
AdditionalGroup,
|
||||||
DeleteTaskResult,
|
|
||||||
Group,
|
Group,
|
||||||
GroupTaskResult,
|
|
||||||
Shadow,
|
Shadow,
|
||||||
User,
|
User,
|
||||||
UserTaskResult,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -62,13 +59,7 @@ class AdditionalGroupTest(TestCaseWithCeleryTasks):
|
||||||
def test_delete(self):
|
def test_delete(self):
|
||||||
group2 = Group.objects.create(groupname='test2', gid=1001)
|
group2 = Group.objects.create(groupname='test2', gid=1001)
|
||||||
addgroup = AdditionalGroup.objects.create(user=self.user, group=group2)
|
addgroup = AdditionalGroup.objects.create(user=self.user, group=group2)
|
||||||
DeleteTaskResult.objects.all().delete()
|
|
||||||
addgroup.delete()
|
addgroup.delete()
|
||||||
taskres = DeleteTaskResult.objects.all()
|
|
||||||
self.assertTrue(len(taskres), 1)
|
|
||||||
self.assertEqual(taskres[0].task_name, 'remove_ldap_user_from_group')
|
|
||||||
self.assertEqual(taskres[0].modeltype, 'usergroup')
|
|
||||||
self.assertEqual(taskres[0].modelname, 'test (1000) in test2 (1001)')
|
|
||||||
self.assertEqual(len(AdditionalGroup.objects.all()), 0)
|
self.assertEqual(len(AdditionalGroup.objects.all()), 0)
|
||||||
|
|
||||||
def test___str__(self):
|
def test___str__(self):
|
||||||
|
@ -107,12 +98,6 @@ class GroupTest(TestCaseWithCeleryTasks):
|
||||||
group.delete()
|
group.delete()
|
||||||
self.assertEqual(len(Group.objects.all()), 0)
|
self.assertEqual(len(Group.objects.all()), 0)
|
||||||
self.assertEqual(len(GroupTaskResult.objects.all()), 0)
|
self.assertEqual(len(GroupTaskResult.objects.all()), 0)
|
||||||
taskres = DeleteTaskResult.objects.all()
|
|
||||||
self.assertEqual(len(taskres), 1)
|
|
||||||
self.assertEqual(taskres[0].task_name,
|
|
||||||
'delete_ldap_group_if_empty')
|
|
||||||
self.assertEqual(taskres[0].modeltype, 'group')
|
|
||||||
self.assertEqual(taskres[0].modelname, 'test')
|
|
||||||
|
|
||||||
|
|
||||||
class ShadowManagerTest(TestCaseWithCeleryTasks):
|
class ShadowManagerTest(TestCaseWithCeleryTasks):
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
from osusers.tasks import LdapRouter
|
|
||||||
|
|
||||||
|
|
||||||
class LdapRouterTest(TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
self.router = LdapRouter()
|
|
||||||
super(LdapRouterTest, self).setUp()
|
|
||||||
|
|
||||||
def test_ldap_tasks_are_routed_to_ldap_queue(self):
|
|
||||||
route = self.router.route_for_task(
|
|
||||||
'some_ldap_task')
|
|
||||||
self.assertEqual(
|
|
||||||
route,
|
|
||||||
{'exchange': 'ldap',
|
|
||||||
'exchange_type': 'direct',
|
|
||||||
'queue': 'ldap'})
|
|
||||||
|
|
||||||
def test_non_ldap_tasks_are_routed_to_default(self):
|
|
||||||
self.assertIsNone(
|
|
||||||
self.router.route_for_task('other'))
|
|
16
gnuviechadmin/osusers/urls.py
Normal file
16
gnuviechadmin/osusers/urls.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
"""
|
||||||
|
This module defines the URL patterns for operating system user related views.
|
||||||
|
|
||||||
|
"""
|
||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
|
from django.conf.urls import patterns, url
|
||||||
|
|
||||||
|
from .views import SetOsUserPassword
|
||||||
|
|
||||||
|
|
||||||
|
urlpatterns = patterns(
|
||||||
|
'',
|
||||||
|
url(r'^(?P<slug>[\w0-9@.+-_]+)/setpassword$', SetOsUserPassword.as_view(),
|
||||||
|
name='set_osuser_password'),
|
||||||
|
)
|
45
gnuviechadmin/osusers/views.py
Normal file
45
gnuviechadmin/osusers/views.py
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
"""
|
||||||
|
This module defines the views for gnuviechadmin operating system user handling.
|
||||||
|
|
||||||
|
"""
|
||||||
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
|
from django.shortcuts import redirect
|
||||||
|
from django.views.generic import UpdateView
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
from django.contrib import messages
|
||||||
|
|
||||||
|
from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin
|
||||||
|
|
||||||
|
from .forms import ChangeOsUserPasswordForm
|
||||||
|
from .models import User
|
||||||
|
|
||||||
|
|
||||||
|
class SetOsUserPassword(StaffOrSelfLoginRequiredMixin, UpdateView):
|
||||||
|
"""
|
||||||
|
This view is used for setting a new operating system user password.
|
||||||
|
|
||||||
|
"""
|
||||||
|
model = User
|
||||||
|
slug_field = 'username'
|
||||||
|
template_name_suffix = '_setpassword'
|
||||||
|
context_object_name = 'osuser'
|
||||||
|
form_class = ChangeOsUserPasswordForm
|
||||||
|
|
||||||
|
def get_customer_object(self):
|
||||||
|
return self.get_object().customer
|
||||||
|
|
||||||
|
def get_context_data(self, *args, **kwargs):
|
||||||
|
context = super(SetOsUserPassword, self).get_context_data(
|
||||||
|
*args, **kwargs)
|
||||||
|
context['customer'] = self.get_customer_object()
|
||||||
|
return context
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
osuser = form.save()
|
||||||
|
messages.success(
|
||||||
|
self.request,
|
||||||
|
_("New password for {username} has been set successfully.").format(
|
||||||
|
username=osuser.username
|
||||||
|
))
|
||||||
|
return redirect(osuser.customerhostingpackage)
|
|
@ -1,5 +1,5 @@
|
||||||
"""
|
"""
|
||||||
This module defines Celery_ tasks to manage PostgreSQL users and databases.
|
This module defines Celery tasks to manage PostgreSQL users and databases.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
318
gnuviechadmin/static/fonts/font-mfizz.css
Normal file
318
gnuviechadmin/static/fonts/font-mfizz.css
Normal file
|
@ -0,0 +1,318 @@
|
||||||
|
/*
|
||||||
|
* Font Mfizz v1.2
|
||||||
|
* Copyright 2013 Mfizz Inc, Joe Lauer
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Project: http://mfizz.com/oss/font-mfizz
|
||||||
|
*
|
||||||
|
* The font designed for technology and software geeks representing programming
|
||||||
|
* languages, operating systems, software engineering, and technology.
|
||||||
|
*
|
||||||
|
* Mfizz Inc
|
||||||
|
* Web: http://mfizz.com/
|
||||||
|
* Twitter: http://twitter.com/mfizz_inc
|
||||||
|
*
|
||||||
|
* Joe Lauer
|
||||||
|
* Web: http://lauer.bz/
|
||||||
|
* Twitter: http://twitter.com/jjlauer
|
||||||
|
*/
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "FontMfizz";
|
||||||
|
src: url("font-mfizz.eot");
|
||||||
|
src: url("font-mfizz.eot?#iefix") format("embedded-opentype"),
|
||||||
|
url("font-mfizz.woff") format("woff"),
|
||||||
|
url("font-mfizz.ttf") format("truetype"),
|
||||||
|
url("font-mfizz.svg#font-mfizz") format("svg");
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-microscope:before,
|
||||||
|
.icon-cplusplus:before,
|
||||||
|
.icon-wireless:before,
|
||||||
|
.icon-fire-alt:before,
|
||||||
|
.icon-mobile-device:before,
|
||||||
|
.icon-objc:before,
|
||||||
|
.icon-redhat:before,
|
||||||
|
.icon-freebsd:before,
|
||||||
|
.icon-heroku:before,
|
||||||
|
.icon-python:before,
|
||||||
|
.icon-java:before,
|
||||||
|
.icon-satellite:before,
|
||||||
|
.icon-debian:before,
|
||||||
|
.icon-grails:before,
|
||||||
|
.icon-c:before,
|
||||||
|
.icon-postgres:before,
|
||||||
|
.icon-database-alt2:before,
|
||||||
|
.icon-raspberrypi:before,
|
||||||
|
.icon-nginx:before,
|
||||||
|
.icon-ruby-on-rails:before,
|
||||||
|
.icon-redis:before,
|
||||||
|
.icon-scala:before,
|
||||||
|
.icon-gnome:before,
|
||||||
|
.icon-perl:before,
|
||||||
|
.icon-mysql:before,
|
||||||
|
.icon-fedora:before,
|
||||||
|
.icon-ghost:before,
|
||||||
|
.icon-google:before,
|
||||||
|
.icon-netbsd:before,
|
||||||
|
.icon-aws:before,
|
||||||
|
.icon-bomb:before,
|
||||||
|
.icon-looking:before,
|
||||||
|
.icon-ruby:before,
|
||||||
|
.icon-mysql-alt:before,
|
||||||
|
.icon-playframework-alt:before,
|
||||||
|
.icon-osx:before,
|
||||||
|
.icon-database:before,
|
||||||
|
.icon-database-alt:before,
|
||||||
|
.icon-shell:before,
|
||||||
|
.icon-script:before,
|
||||||
|
.icon-antenna:before,
|
||||||
|
.icon-coffee-bean:before,
|
||||||
|
.icon-scala-alt:before,
|
||||||
|
.icon-platter:before,
|
||||||
|
.icon-java-duke:before,
|
||||||
|
.icon-iphone:before,
|
||||||
|
.icon-script-alt:before,
|
||||||
|
.icon-google-alt:before,
|
||||||
|
.icon-haskell:before,
|
||||||
|
.icon-mariadb:before,
|
||||||
|
.icon-phone-retro:before,
|
||||||
|
.icon-phone-alt:before,
|
||||||
|
.icon-csharp:before,
|
||||||
|
.icon-php:before,
|
||||||
|
.icon-postgres-alt:before,
|
||||||
|
.icon-html:before,
|
||||||
|
.icon-mfizz:before,
|
||||||
|
.icon-apache:before,
|
||||||
|
.icon-hadoop:before,
|
||||||
|
.icon-ruby-on-rails-alt:before,
|
||||||
|
.icon-mobile-phone-broadcast:before,
|
||||||
|
.icon-css:before,
|
||||||
|
.icon-playframework:before,
|
||||||
|
.icon-clojure:before,
|
||||||
|
.icon-mobile-phone-alt:before,
|
||||||
|
.icon-suse:before,
|
||||||
|
.icon-java-bold:before,
|
||||||
|
.icon-nginx-alt:before,
|
||||||
|
.icon-nginx-alt2:before,
|
||||||
|
.icon-linux-mint:before,
|
||||||
|
.icon-dreamhost:before,
|
||||||
|
.icon-blackberry:before,
|
||||||
|
.icon-javascript:before,
|
||||||
|
.icon-ubuntu:before,
|
||||||
|
.icon-php-alt:before,
|
||||||
|
.icon-centos:before,
|
||||||
|
.icon-nodejs:before,
|
||||||
|
.icon-splatter:before,
|
||||||
|
.icon-3dprint:before,
|
||||||
|
.icon-line-graph:before,
|
||||||
|
.icon-cassandra:before,
|
||||||
|
.icon-solaris:before,
|
||||||
|
.icon-jetty:before,
|
||||||
|
.icon-tomcat:before,
|
||||||
|
.icon-oracle:before,
|
||||||
|
.icon-oracle-alt:before,
|
||||||
|
.icon-mssql:before,
|
||||||
|
.icon-google-developers:before,
|
||||||
|
.icon-google-code:before,
|
||||||
|
.icon-kde:before,
|
||||||
|
.icon-grails-alt:before {
|
||||||
|
font-family: "FontMfizz";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
font-variant: normal;
|
||||||
|
text-transform: none;
|
||||||
|
line-height: 1;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
display: inline-block;
|
||||||
|
text-decoration: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-microscope:before { content: "\f100"; }
|
||||||
|
.icon-cplusplus:before { content: "\f101"; }
|
||||||
|
.icon-wireless:before { content: "\f102"; }
|
||||||
|
.icon-fire-alt:before { content: "\f103"; }
|
||||||
|
.icon-mobile-device:before { content: "\f104"; }
|
||||||
|
.icon-objc:before { content: "\f105"; }
|
||||||
|
.icon-redhat:before { content: "\f106"; }
|
||||||
|
.icon-freebsd:before { content: "\f107"; }
|
||||||
|
.icon-heroku:before { content: "\f108"; }
|
||||||
|
.icon-python:before { content: "\f109"; }
|
||||||
|
.icon-java:before { content: "\f10a"; }
|
||||||
|
.icon-satellite:before { content: "\f10b"; }
|
||||||
|
.icon-debian:before { content: "\f10c"; }
|
||||||
|
.icon-grails:before { content: "\f10d"; }
|
||||||
|
.icon-c:before { content: "\f10e"; }
|
||||||
|
.icon-postgres:before { content: "\f10f"; }
|
||||||
|
.icon-database-alt2:before { content: "\f110"; }
|
||||||
|
.icon-raspberrypi:before { content: "\f111"; }
|
||||||
|
.icon-nginx:before { content: "\f112"; }
|
||||||
|
.icon-ruby-on-rails:before { content: "\f113"; }
|
||||||
|
.icon-redis:before { content: "\f114"; }
|
||||||
|
.icon-scala:before { content: "\f115"; }
|
||||||
|
.icon-gnome:before { content: "\f116"; }
|
||||||
|
.icon-perl:before { content: "\f117"; }
|
||||||
|
.icon-mysql:before { content: "\f118"; }
|
||||||
|
.icon-fedora:before { content: "\f119"; }
|
||||||
|
.icon-ghost:before { content: "\f11a"; }
|
||||||
|
.icon-google:before { content: "\f11b"; }
|
||||||
|
.icon-netbsd:before { content: "\f11c"; }
|
||||||
|
.icon-aws:before { content: "\f11d"; }
|
||||||
|
.icon-bomb:before { content: "\f11e"; }
|
||||||
|
.icon-looking:before { content: "\f11f"; }
|
||||||
|
.icon-ruby:before { content: "\f120"; }
|
||||||
|
.icon-mysql-alt:before { content: "\f121"; }
|
||||||
|
.icon-playframework-alt:before { content: "\f122"; }
|
||||||
|
.icon-osx:before { content: "\f123"; }
|
||||||
|
.icon-database:before { content: "\f124"; }
|
||||||
|
.icon-database-alt:before { content: "\f125"; }
|
||||||
|
.icon-shell:before { content: "\f126"; }
|
||||||
|
.icon-script:before { content: "\f127"; }
|
||||||
|
.icon-antenna:before { content: "\f128"; }
|
||||||
|
.icon-coffee-bean:before { content: "\f129"; }
|
||||||
|
.icon-scala-alt:before { content: "\f12a"; }
|
||||||
|
.icon-platter:before { content: "\f12b"; }
|
||||||
|
.icon-java-duke:before { content: "\f12c"; }
|
||||||
|
.icon-iphone:before { content: "\f12d"; }
|
||||||
|
.icon-script-alt:before { content: "\f12e"; }
|
||||||
|
.icon-google-alt:before { content: "\f12f"; }
|
||||||
|
.icon-haskell:before { content: "\f130"; }
|
||||||
|
.icon-mariadb:before { content: "\f131"; }
|
||||||
|
.icon-phone-retro:before { content: "\f132"; }
|
||||||
|
.icon-phone-alt:before { content: "\f133"; }
|
||||||
|
.icon-csharp:before { content: "\f134"; }
|
||||||
|
.icon-php:before { content: "\f135"; }
|
||||||
|
.icon-postgres-alt:before { content: "\f136"; }
|
||||||
|
.icon-html:before { content: "\f137"; }
|
||||||
|
.icon-mfizz:before { content: "\f138"; }
|
||||||
|
.icon-apache:before { content: "\f139"; }
|
||||||
|
.icon-hadoop:before { content: "\f13a"; }
|
||||||
|
.icon-ruby-on-rails-alt:before { content: "\f13b"; }
|
||||||
|
.icon-mobile-phone-broadcast:before { content: "\f13c"; }
|
||||||
|
.icon-css:before { content: "\f13d"; }
|
||||||
|
.icon-playframework:before { content: "\f13e"; }
|
||||||
|
.icon-clojure:before { content: "\f13f"; }
|
||||||
|
.icon-mobile-phone-alt:before { content: "\f140"; }
|
||||||
|
.icon-suse:before { content: "\f141"; }
|
||||||
|
.icon-java-bold:before { content: "\f142"; }
|
||||||
|
.icon-nginx-alt:before { content: "\f143"; }
|
||||||
|
.icon-nginx-alt2:before { content: "\f144"; }
|
||||||
|
.icon-linux-mint:before { content: "\f145"; }
|
||||||
|
.icon-dreamhost:before { content: "\f146"; }
|
||||||
|
.icon-blackberry:before { content: "\f147"; }
|
||||||
|
.icon-javascript:before { content: "\f148"; }
|
||||||
|
.icon-ubuntu:before { content: "\f149"; }
|
||||||
|
.icon-php-alt:before { content: "\f14a"; }
|
||||||
|
.icon-centos:before { content: "\f14b"; }
|
||||||
|
.icon-nodejs:before { content: "\f14c"; }
|
||||||
|
.icon-splatter:before { content: "\f14d"; }
|
||||||
|
.icon-3dprint:before { content: "\f14e"; }
|
||||||
|
.icon-line-graph:before { content: "\f14f"; }
|
||||||
|
.icon-cassandra:before { content: "\f150"; }
|
||||||
|
.icon-solaris:before { content: "\f151"; }
|
||||||
|
.icon-jetty:before { content: "\f152"; }
|
||||||
|
.icon-tomcat:before { content: "\f153"; }
|
||||||
|
.icon-oracle:before { content: "\f154"; }
|
||||||
|
.icon-oracle-alt:before { content: "\f155"; }
|
||||||
|
.icon-mssql:before { content: "\f156"; }
|
||||||
|
.icon-google-developers:before { content: "\f157"; }
|
||||||
|
.icon-google-code:before { content: "\f158"; }
|
||||||
|
.icon-kde:before { content: "\f159"; }
|
||||||
|
.icon-grails-alt:before { content: "\f15a"; }
|
||||||
|
|
||||||
|
/* These classes only added to fix FontFamily to display FontMfizz during debug/inspection */
|
||||||
|
.icon-osx,
|
||||||
|
.icon-bomb,
|
||||||
|
.icon-mobile-phone-broadcast,
|
||||||
|
.icon-objc,
|
||||||
|
.icon-nginx-alt2,
|
||||||
|
.icon-mysql,
|
||||||
|
.icon-phone-retro,
|
||||||
|
.icon-netbsd,
|
||||||
|
.icon-mobile-device,
|
||||||
|
.icon-ruby-on-rails,
|
||||||
|
.icon-phone-alt,
|
||||||
|
.icon-line-graph,
|
||||||
|
.icon-postgres,
|
||||||
|
.icon-playframework,
|
||||||
|
.icon-python,
|
||||||
|
.icon-ruby-on-rails-alt,
|
||||||
|
.icon-nginx,
|
||||||
|
.icon-database-alt2,
|
||||||
|
.icon-google-alt,
|
||||||
|
.icon-microscope,
|
||||||
|
.icon-blackberry,
|
||||||
|
.icon-dreamhost,
|
||||||
|
.icon-google,
|
||||||
|
.icon-centos,
|
||||||
|
.icon-kde,
|
||||||
|
.icon-csharp,
|
||||||
|
.icon-scala,
|
||||||
|
.icon-redis,
|
||||||
|
.icon-looking,
|
||||||
|
.icon-database-alt,
|
||||||
|
.icon-javascript,
|
||||||
|
.icon-postgres-alt,
|
||||||
|
.icon-linux-mint,
|
||||||
|
.icon-ubuntu,
|
||||||
|
.icon-apache,
|
||||||
|
.icon-script-alt,
|
||||||
|
.icon-mssql,
|
||||||
|
.icon-c,
|
||||||
|
.icon-gnome,
|
||||||
|
.icon-java-duke,
|
||||||
|
.icon-scala-alt,
|
||||||
|
.icon-clojure,
|
||||||
|
.icon-oracle-alt,
|
||||||
|
.icon-redhat,
|
||||||
|
.icon-haskell,
|
||||||
|
.icon-3dprint,
|
||||||
|
.icon-mariadb,
|
||||||
|
.icon-java,
|
||||||
|
.icon-script,
|
||||||
|
.icon-cplusplus,
|
||||||
|
.icon-jetty,
|
||||||
|
.icon-perl,
|
||||||
|
.icon-heroku,
|
||||||
|
.icon-nginx-alt,
|
||||||
|
.icon-iphone,
|
||||||
|
.icon-splatter,
|
||||||
|
.icon-shell,
|
||||||
|
.icon-mysql-alt,
|
||||||
|
.icon-wireless,
|
||||||
|
.icon-ruby,
|
||||||
|
.icon-playframework-alt,
|
||||||
|
.icon-raspberrypi,
|
||||||
|
.icon-suse,
|
||||||
|
.icon-nodejs,
|
||||||
|
.icon-java-bold,
|
||||||
|
.icon-google-developers,
|
||||||
|
.icon-mobile-phone-alt,
|
||||||
|
.icon-grails-alt,
|
||||||
|
.icon-coffee-bean,
|
||||||
|
.icon-cassandra,
|
||||||
|
.icon-google-code,
|
||||||
|
.icon-fedora,
|
||||||
|
.icon-antenna,
|
||||||
|
.icon-hadoop,
|
||||||
|
.icon-solaris,
|
||||||
|
.icon-html,
|
||||||
|
.icon-css,
|
||||||
|
.icon-satellite,
|
||||||
|
.icon-aws,
|
||||||
|
.icon-mfizz,
|
||||||
|
.icon-php,
|
||||||
|
.icon-debian,
|
||||||
|
.icon-ghost,
|
||||||
|
.icon-php-alt,
|
||||||
|
.icon-tomcat,
|
||||||
|
.icon-database,
|
||||||
|
.icon-grails,
|
||||||
|
.icon-freebsd,
|
||||||
|
.icon-oracle,
|
||||||
|
.icon-fire-alt,
|
||||||
|
.icon-platter{
|
||||||
|
font-family: "FontMfizz";
|
||||||
|
}
|
BIN
gnuviechadmin/static/fonts/font-mfizz.eot
Normal file
BIN
gnuviechadmin/static/fonts/font-mfizz.eot
Normal file
Binary file not shown.
1344
gnuviechadmin/static/fonts/font-mfizz.svg
Normal file
1344
gnuviechadmin/static/fonts/font-mfizz.svg
Normal file
File diff suppressed because it is too large
Load diff
After Width: | Height: | Size: 276 KiB |
BIN
gnuviechadmin/static/fonts/font-mfizz.ttf
Normal file
BIN
gnuviechadmin/static/fonts/font-mfizz.ttf
Normal file
Binary file not shown.
BIN
gnuviechadmin/static/fonts/font-mfizz.woff
Normal file
BIN
gnuviechadmin/static/fonts/font-mfizz.woff
Normal file
Binary file not shown.
|
@ -11,6 +11,7 @@
|
||||||
<!-- Le styles -->
|
<!-- Le styles -->
|
||||||
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
|
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
|
||||||
<link href="{% static 'css/font-awesome.min.css' %}" rel="stylesheet">
|
<link href="{% static 'css/font-awesome.min.css' %}" rel="stylesheet">
|
||||||
|
<link href="{% static 'fonts/font-mfizz.css' %}" rel="stylesheet">
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
|
padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
|
||||||
|
|
|
@ -3,10 +3,49 @@
|
||||||
{% block title %}{{ block.super }} - {% blocktrans with full_name=dashboard_user.get_full_name %}Dashboard for {{ full_name }}{% endblocktrans %}{% endblock title %}
|
{% block title %}{{ block.super }} - {% blocktrans with full_name=dashboard_user.get_full_name %}Dashboard for {{ full_name }}{% endblocktrans %}{% endblock title %}
|
||||||
{% block page_title %}{% blocktrans with full_name=dashboard_user.get_full_name %}Dashboard for {{ full_name }}{% endblocktrans %}{% endblock page_title %}
|
{% block page_title %}{% blocktrans with full_name=dashboard_user.get_full_name %}Dashboard for {{ full_name }}{% endblocktrans %}{% endblock page_title %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<p>Contract details</p>
|
<div class="row">
|
||||||
<ul>
|
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||||
<li>Domains</li>
|
<div class="panel panel-default">
|
||||||
<li>Email mailboxes</li>
|
<div class="panel-heading">{% trans "Hosting packages" %}</div>
|
||||||
<li>Email addresses</li>
|
<div class="panel-body">
|
||||||
</ul>
|
{% if hosting_packages %}
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "Name" %}</th>
|
||||||
|
<th>{% trans "Disk space" %}</th>
|
||||||
|
<th>{% trans "Mailboxes" %}</th>
|
||||||
|
<th>{% trans "Databases" %}</th>
|
||||||
|
<th>{% trans "Actions" %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for package in hosting_packages %}
|
||||||
|
<tr>
|
||||||
|
<th><a href="{{ package.get_absolute_url }}" title="{% blocktrans with packagename=package.name %}Show details for {{ packagename }}{% endblocktrans %}">{{ package.name }}</a></th>
|
||||||
|
<th>
|
||||||
|
{% with diskspace=package.get_disk_space %}
|
||||||
|
<span title="{% blocktrans %}The reserved disk space for your hosting package is {{ diskspace }} bytes.{% endblocktrans %}">{{ diskspace|filesizeformat }}</span>
|
||||||
|
{% endwith %}
|
||||||
|
</th>
|
||||||
|
<th>{% blocktrans with num=package.get_used_mailboxes total=package.get_mailboxes %}used {{ num }} of {{ total }}{% endblocktrans %}</th>
|
||||||
|
<th>{% for dbtype in package.get_databases %}
|
||||||
|
{{ dbtype.number }} {% include "userdbs/snippets/db_type.html" with db_type=dbtype.db_type %}
|
||||||
|
{% if not forloop.last %} / {% endif %}
|
||||||
|
{% endfor %}</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% else %}
|
||||||
|
<p class="text-info">{% if user == object %}{% trans "You have no hosting packages yet." %}{% else %}{% trans "This user has no hosting packages assigned yet." %}{% endif %}</p>
|
||||||
|
{% endif %}
|
||||||
|
{% if user.is_staff %}
|
||||||
|
<a href="{% url "create_hosting_package" user=object.username %}" class="btn btn-primary">{% trans "Add hosting package" %}</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|
1
gnuviechadmin/templates/hostingpackages/base.html
Normal file
1
gnuviechadmin/templates/hostingpackages/base.html
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{% extends "base.html" %}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{% extends "hostingpackages/base.html" %}
|
||||||
|
{% load i18n crispy_forms_tags %}
|
||||||
|
{% block title %}{{ block.super }} - {% blocktrans with full_name=customer.get_full_name %}Add hosting package for Customer {{ full_name }}{% endblocktrans %}{% endblock title %}
|
||||||
|
{% block page_title %}{% blocktrans with full_name=customer.get_full_name %}Add Hosting Package for Customer {{ full_name }}{% endblocktrans %}{% endblock page_title %}
|
||||||
|
{% block content %}
|
||||||
|
{% crispy form %}
|
||||||
|
{% endblock content %}
|
|
@ -0,0 +1,48 @@
|
||||||
|
{% extends "hostingpackages/base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block title %}{{ block.super }} - {% spaceless %}
|
||||||
|
{% if user == customer %}
|
||||||
|
{% blocktrans with package=hostingpackage.name %}Details for your Hosting Package {{ package }}{% endblocktrans %}
|
||||||
|
{% else %}
|
||||||
|
{% blocktrans with package=hostingpackage.name full_name=customer.get_full_name %}Details for Hosting Package {{ package }} of {{ full_name }}{% endblocktrans %}
|
||||||
|
{% endif %}
|
||||||
|
{% endspaceless %}{% endblock title %}
|
||||||
|
|
||||||
|
{% block page_title %}{% blocktrans with package=hostingpackage.name %}Details of Hosting Package {{ package }}{% endblocktrans %}{% endblock page_title %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-6 col-md-6 col-xs-12">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
{% trans "Hosting Package Information" %}<div class="pull-right"><a class="panel-title" href="#" title="{% trans "Edit Hosting Package Information" %}"><i class="glyphicon glyphicon-cog"></i></a></div>
|
||||||
|
</div>
|
||||||
|
<dl class="panel-body dl-horizontal">
|
||||||
|
<dt>{% trans "Name" %}</dt>
|
||||||
|
<dd>{{ hostingpackage.name }}</dd>
|
||||||
|
<dt>{% trans "Description" %}</dt>
|
||||||
|
<dd>{{ hostingpackage.description|default:"-" }}</dd>
|
||||||
|
<dt>{% trans "Disk space" %}</dt>
|
||||||
|
{% with diskspace=hostingpackage.get_disk_space %}
|
||||||
|
<dd title="{% blocktrans %}The reserved disk space for your hosting package is {{ diskspace }} bytes.{% endblocktrans %}">{{ diskspace|filesizeformat }}</dd>
|
||||||
|
{% endwith %}
|
||||||
|
<dt>{% trans "Mailboxes" %}</dt>
|
||||||
|
<dd>{% blocktrans with num=hostingpackage.get_used_mailboxes total=hostingpackage.get_mailboxes %}{{ num }} of {{ total }} in use{% endblocktrans %}</dd>
|
||||||
|
<dt>{% if hostingpackage.osuser.is_sftp_user %}{% trans "SFTP username" %}{% else %}{% trans "SSH/SFTP username" %}{% endif %}</dt>
|
||||||
|
<dd>{{ hostingpackage.osuser.username }}</dd>
|
||||||
|
<dt>{% trans "Upload server" %}</dt>
|
||||||
|
<dd>{{ uploadserver }}</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-6 col-md-6 col-xs-12">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">{% trans "Hosting Package Actions" %}</div>
|
||||||
|
<ul class="list-group">
|
||||||
|
<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>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock content %}
|
1
gnuviechadmin/templates/osusers/base.html
Normal file
1
gnuviechadmin/templates/osusers/base.html
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{% extends "base.html" %}
|
30
gnuviechadmin/templates/osusers/user_setpassword.html
Normal file
30
gnuviechadmin/templates/osusers/user_setpassword.html
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
{% extends "osusers/base.html" %}
|
||||||
|
{% load i18n crispy_forms_tags %}
|
||||||
|
{% block title %}{{ block.super }} - {% spaceless %}
|
||||||
|
{% if customer == user %}
|
||||||
|
{% blocktrans with osuser=osuser.username %}Set new password for user {{ osuser }}{% endblocktrans %}
|
||||||
|
{% else %}
|
||||||
|
{% blocktrans with osuser=osuser.username full_name=customer.get_full_name %}Set new password for user {{ osuser }} of customer {{ full_name }}{% endblocktrans %}
|
||||||
|
{% endif %}
|
||||||
|
{% endspaceless %}{% endblock title %}
|
||||||
|
|
||||||
|
{% block page_title %}{% spaceless %}
|
||||||
|
{% if customer == user %}
|
||||||
|
{% blocktrans with osuser=osuser.username %}Set new password for user {{ osuser }}{% endblocktrans %}
|
||||||
|
{% else %}
|
||||||
|
{% blocktrans with osuser=osuser.username full_name=customer.get_full_name %}Set new password for user {{ osuser }} of customer {{ full_name }}{% endblocktrans %}
|
||||||
|
{% endif %}
|
||||||
|
{% endspaceless %}{% endblock page_title %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% crispy form %}
|
||||||
|
{% endblock content %}
|
||||||
|
|
||||||
|
{% block extra_js %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('input[type=password]').val('');
|
||||||
|
$('input[type=password]').first().focus();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock extra_js %}
|
3
gnuviechadmin/templates/userdbs/snippets/db_type.html
Normal file
3
gnuviechadmin/templates/userdbs/snippets/db_type.html
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{# format database types #}
|
||||||
|
{% load userdb %}
|
||||||
|
<i class="{% db_type_icon_class %}" title="{% db_type_name %}"></i><span class="sr-only">{% db_type_name %}</span>
|
4
gnuviechadmin/userdbs/templatetags/__init__.py
Normal file
4
gnuviechadmin/userdbs/templatetags/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
"""
|
||||||
|
This module provides custom template tags for user databases.
|
||||||
|
|
||||||
|
"""
|
55
gnuviechadmin/userdbs/templatetags/userdb.py
Normal file
55
gnuviechadmin/userdbs/templatetags/userdb.py
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
"""
|
||||||
|
This is the template tag library for user databases.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django import template
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from userdbs.models import DB_TYPES
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
_TYPE_NAME_MAP = {
|
||||||
|
DB_TYPES.mysql: 'mysql',
|
||||||
|
DB_TYPES.pgsql: 'postgres',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def db_type_icon_class(context):
|
||||||
|
"""
|
||||||
|
This template tag derives the matching icon name for the numeric database
|
||||||
|
type stored in the context variable db_type.
|
||||||
|
|
||||||
|
The icon names used are those of `Font Mfizz
|
||||||
|
<http://mfizz.com/oss/font-mfizz>`_.
|
||||||
|
|
||||||
|
:param context: the template context
|
||||||
|
:return: icon name
|
||||||
|
:rtype: str
|
||||||
|
|
||||||
|
"""
|
||||||
|
db_type = context['db_type']
|
||||||
|
if db_type in _TYPE_NAME_MAP:
|
||||||
|
return 'icon-' + _TYPE_NAME_MAP[db_type]
|
||||||
|
return 'icon-database'
|
||||||
|
|
||||||
|
|
||||||
|
register.simple_tag(db_type_icon_class, takes_context=True)
|
||||||
|
|
||||||
|
|
||||||
|
def db_type_name(context):
|
||||||
|
"""
|
||||||
|
This template tag gets the human readable database type for the numeric
|
||||||
|
database type stored in the context variable db_type.
|
||||||
|
|
||||||
|
:param context: the template context
|
||||||
|
:return: human readable database type name
|
||||||
|
:rtype: str
|
||||||
|
|
||||||
|
"""
|
||||||
|
db_type = context['db_type']
|
||||||
|
return _(DB_TYPES[db_type])
|
||||||
|
|
||||||
|
|
||||||
|
register.simple_tag(db_type_name, takes_context=True)
|
|
@ -1,3 +0,0 @@
|
||||||
from django.shortcuts import render
|
|
||||||
|
|
||||||
# Create your views here.
|
|
Loading…
Reference in a new issue