Compare commits

...

24 commits
0.13.0 ... main

Author SHA1 Message Date
Jan Dittberner 11c04df074 Add flatpage URL for /issues/
Jira has been decommissioned and replaced with a flatpage
2024-02-11 13:12:54 +01:00
Jan Dittberner 7c57b4cc94 Update docker setup 2024-02-11 13:12:26 +01:00
Jan Dittberner 78e06dd2b9 Update dependencies
- updated minimum version of several dependencies in pyproject.toml
- update of django-allauth required to add
  allauth.account.middleware.AccountMiddleware to MIDDLEWARE setting
2024-01-13 13:38:03 +01:00
Jan Dittberner 90795375e7 Update dependencies 2024-01-13 13:22:50 +01:00
Jan Dittberner c1b226e5a1 Update dependency versions 2023-08-03 09:24:42 +02:00
Jan Dittberner 5e527ef10d Release 0.15.1 2023-07-23 11:24:46 +02:00
Jan Dittberner 3b6d50a62a Remove stale disk usage stats 2023-07-23 11:24:10 +02:00
Jan Dittberner 4b74f5d04b Release 0.15.0 2023-07-23 10:28:00 +02:00
Jan Dittberner ec6a9a7cc1 Add disk usage details for mail and web
Addresses #10
2023-07-23 10:26:43 +02:00
Jan Dittberner f21987158b Release 0.14.4 2023-07-22 22:35:49 +02:00
Jan Dittberner 2e7dca529a Add customer to disk space detail view 2023-07-22 22:34:44 +02:00
Jan Dittberner 1d4f070867 Release 0.14.3 2023-07-22 22:25:36 +02:00
Jan Dittberner d1494af0a1 Fix permission check on disk space detail view 2023-07-22 22:25:08 +02:00
Jan Dittberner 69638b6b6d Release 0.14.2 2023-07-22 21:57:40 +02:00
Jan Dittberner ee561a5127 Fix division by zero 2023-07-22 21:56:53 +02:00
Jan Dittberner b69ecbfa2d Prepare release 0.14.1 2023-07-22 20:46:47 +02:00
Jan Dittberner bf7b02d5b5 Fix squashed migration 2023-07-22 20:46:03 +02:00
Jan Dittberner 22945f72bf Prepare release
- update changelog
- update dependencies
- bump version
2023-07-22 20:12:00 +02:00
Jan Dittberner d0fe915612 Update german translation 2023-07-22 20:07:01 +02:00
Jan Dittberner cb62bd63e2 Add disk usage statistics
- add model CustomerPackageDiskUsage for hosting package disk usage
  statistics
- add REST API endpoint for submittings statistics for disk, mysql and
  pgsql usage
- add disk usage information to hosting package detail view
- add separate hosting package disk usage statistic view
2023-07-22 19:43:10 +02:00
Jan Dittberner affb49a971 Update to Django 4.2
- fix deprecation warnings
- update dependencies
2023-07-08 19:21:02 +02:00
Jan Dittberner 8aadae1c83 Fix Django4 deprecation warnings 2023-07-08 18:34:12 +02:00
Jan Dittberner 30ffdf1751 Update dependencies 2023-07-08 17:01:49 +02:00
Jan Dittberner aed8e97dbc Fix Poetry deprecation warning 2023-07-08 16:59:04 +02:00
33 changed files with 2043 additions and 1476 deletions

View file

@ -1,8 +1,8 @@
ARG DEBIAN_RELEASE=buster
ARG DEBIAN_RELEASE=bookworm
FROM debian:$DEBIAN_RELEASE AS builder
ARG GVAAPP=gva
ARG POETRY_VERSION=1.3.1
ARG POETRY_VERSION=1.7.1
ENV LC_ALL=C.UTF-8
ENV LANG=C.UTF-8
@ -26,7 +26,7 @@ WORKDIR /srv/$GVAAPP
COPY poetry.lock pyproject.toml /srv/$GVAAPP/
RUN /root/.local/bin/poetry install --only=main
RUN /root/.local/bin/poetry install --only=main --no-root
FROM debian:$DEBIAN_RELEASE
LABEL maintainer="Jan Dittberner <jan@dittberner.info>"

View file

@ -19,7 +19,7 @@ services:
volumes:
- "redis_data:/var/lib/redis"
gva:
image: gnuviech/gva:buster
image: gnuviech/gva:bookworm
build:
context: .
args:
@ -67,7 +67,7 @@ services:
volumes:
- "../gvaldap/gvaldap:/srv/gvaldap/gvaldap"
file:
image: gnuviech/gvafile:buster
image: gnuviech/gvafile:bookworm
build:
context: ../gvafile
args:

View file

@ -1,6 +1,27 @@
Changelog
=========
* :release:`0.15.1 <2023-07-23>
* :bug:`-` remove stale disk usage stats older than 30 minutes
* :release:`0.15.0 <2023-07-23>
* :feature:`10` add disk usage details for mail and web
* :release:`0.14.4 <2023-07-22>`
* :bug:`-` add customer to disk space detail view
* :release:`0.14.3 <2023-07-22>`
* :bug:`-` fix missing permission check on disk space detail view
* :release:`0.14.2 <2023-07-22>`
* :bug:`-` fix division by zero for hosting packages without disk space allocation
* :release:`0.14.1 <2023-07-22>`
* :bug:`-` fix squashed migration for disk space statistics
* :release:`0.14.0 <2023-07-22>`
* :feature:`-` add disk space statistics
* :release:`0.13.0 <2023-05-08>`
* :feature:`-` add REST API to retrieve and set user information as admin
* :feature:`-` add support model for offline account reset codes in new help

View file

@ -20,46 +20,49 @@ import django
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath(os.path.join('..', 'gnuviechadmin')))
sys.path.insert(0, os.path.abspath(os.path.join("..", "gnuviechadmin")))
os.environ['DJANGO_SETTINGS_MODULE'] = 'gnuviechadmin.settings'
os.environ['GVA_SITE_ADMINMAIL'] = 'admin@gva.example.org'
os.environ["DJANGO_SETTINGS_MODULE"] = "gnuviechadmin.settings"
os.environ["GVA_SITE_ADMINMAIL"] = "admin@gva.example.org"
django.setup()
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = [
'releases', 'sphinx.ext.autodoc', 'celery.contrib.sphinx',
'sphinxcontrib.blockdiag']
"releases",
"sphinx.ext.autodoc",
"celery.contrib.sphinx",
"sphinxcontrib.blockdiag",
]
# configuration for releases extension
releases_issue_uri = 'https://git.dittberner.info/gnuviech/gva/issues/%s'
releases_release_uri = 'https://git.dittberner.info/gnuviech/gva/src/tag/%s'
releases_issue_uri = "https://git.dittberner.info/gnuviech/gva/issues/%s"
releases_release_uri = "https://git.dittberner.info/gnuviech/gva/src/tag/%s"
# configuration for blockdiag extension
blockdiag_fontpath = '/usr/share/fonts/truetype/dejavu/'
blockdiag_fontpath = "/usr/share/fonts/truetype/dejavu/"
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]
# The suffix of source filenames.
source_suffix = '.rst'
source_suffix = ".rst"
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
master_doc = "index"
# General information about the project.
project = u'gnuviechadmin'
copyright = u'2014-2020, Jan Dittberner'
project = "gnuviechadmin"
copyright = "2014-2023, Jan Dittberner"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@ -69,121 +72,121 @@ copyright = u'2014-2020, Jan Dittberner'
from gnuviechadmin import __version__ as release
# The short X.Y version.
version = ".".join(release.split('.')[:2])
version = ".".join(release.split(".")[:2])
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
exclude_patterns = ["_build"]
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'alabaster'
html_theme = "alabaster"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_static_path = ["_static"]
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'gnuviechadmindoc'
htmlhelp_basename = "gnuviechadmindoc"
# -- Options for LaTeX output --------------------------------------------------
@ -191,10 +194,8 @@ htmlhelp_basename = 'gnuviechadmindoc'
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
@ -202,29 +203,34 @@ latex_elements = {
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'gnuviechadmin.tex', u'gnuviechadmin Documentation',
u'Jan Dittberner', 'manual'),
(
"index",
"gnuviechadmin.tex",
"gnuviechadmin Documentation",
"Jan Dittberner",
"manual",
),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# latex_domain_indices = True
# -- Options for manual page output --------------------------------------------
@ -232,12 +238,11 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'gnuviechadmin', u'gnuviechadmin Documentation',
[u'Jan Dittberner'], 1)
("index", "gnuviechadmin", "gnuviechadmin Documentation", ["Jan Dittberner"], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# man_show_urls = False
# -- Options for Texinfo output ------------------------------------------------
@ -246,16 +251,22 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'gnuviechadmin', u'gnuviechadmin Documentation',
u'Jan Dittberner', 'gnuviechadmin', 'Customer center for gnuviech servers.',
'Miscellaneous'),
(
"index",
"gnuviechadmin",
"gnuviechadmin Documentation",
"Jan Dittberner",
"gnuviechadmin",
"Customer center for gnuviech servers.",
"Miscellaneous",
),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# texinfo_show_urls = 'footnote'

View file

@ -7,6 +7,7 @@ from __future__ import absolute_import, unicode_literals
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit
from django import forms
from django.apps import apps
from django.conf import settings
from django.contrib.sites.models import Site
from django.contrib.sites.requests import RequestSite
@ -41,7 +42,7 @@ class ContactForm(forms.Form):
def get_context(self):
if not self.is_valid():
raise ValueError("Cannot generate context from invalid contact form")
if Site._meta.installed:
if apps.is_installed("django.contrib.sites"):
site = Site.objects.get_current()
else:
site = RequestSite(self.request)

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: contact_form\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-04-22 13:01+0200\n"
"POT-Creation-Date: 2023-07-22 19:45+0200\n"
"PO-Revision-Date: 2023-04-22 13:01+0200\n"
"Last-Translator: Jan Dittberner <jan@dittberner.info>\n"
"Language-Team: Jan Dittberner <jan@dittberner.info>\n"
@ -19,19 +19,19 @@ msgstr ""
"X-Generator: Poedit 3.2.2\n"
"X-Poedit-SourceCharset: UTF-8\n"
#: contact_form/forms.py:25
#: contact_form/forms.py:26
msgid "Your name"
msgstr "Ihr Name"
#: contact_form/forms.py:26
#: contact_form/forms.py:27
msgid "Your email address"
msgstr "Ihre E-Mail-Adresse"
#: contact_form/forms.py:27
#: contact_form/forms.py:28
msgid "Your message"
msgstr "Ihre Nachricht"
#: contact_form/forms.py:39
#: contact_form/forms.py:40
msgid "Send message"
msgstr "Nachricht senden"

View file

@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gnuviechadmin dashboard\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-04-16 22:07+0200\n"
"PO-Revision-Date: 2023-04-16 18:31+0200\n"
"POT-Creation-Date: 2023-07-22 19:45+0200\n"
"PO-Revision-Date: 2023-07-22 19:46+0200\n"
"Last-Translator: Jan Dittberner <jan@dittberner.info>\n"
"Language-Team: Jan Dittberner <jan@dittberner.info>\n"
"Language: de\n"
@ -19,25 +19,6 @@ msgstr ""
"X-Generator: Poedit 3.2.2\n"
"X-Poedit-SourceCharset: UTF-8\n"
#: dashboard/templates/dashboard/index.html:3
msgid "Welcome"
msgstr "Willkommen"
#: dashboard/templates/dashboard/index.html:4
msgid "Welcome to our customer self service"
msgstr "Willkommen in unserem Selbstservice-System"
#: dashboard/templates/dashboard/index.html:7
#, python-format
msgid ""
"Hello %(full_name)s,<br/> You can visit your <a "
"href=\"%(dashboard_url)s\">Dashboard</a> to view and modify your hosting "
"options."
msgstr ""
"Hallo %(full_name)s,<br /> Sie können Ihre <a "
"href=\"%(dashboard_url)s\">Startseite</a> besuchen, um Ihre "
"Hostingeinstellungen anzusehen und zu bearbeiten."
#: dashboard/templates/dashboard/user_dashboard.html:3
#: dashboard/templates/dashboard/user_dashboard.html:6
#, python-format
@ -53,46 +34,26 @@ msgid "Name"
msgstr "Name"
#: dashboard/templates/dashboard/user_dashboard.html:18
msgid "Disk space"
msgstr "Speicherplatz"
msgid "Setup date"
msgstr "Einrichtungsdatum"
#: dashboard/templates/dashboard/user_dashboard.html:19
msgid "Mailboxes"
msgstr "Postfächer"
#: dashboard/templates/dashboard/user_dashboard.html:20
msgid "Databases"
msgstr "Datenbanken"
#: dashboard/templates/dashboard/user_dashboard.html:21
msgid "Actions"
msgstr "Aktionen"
#: dashboard/templates/dashboard/user_dashboard.html:28
#: dashboard/templates/dashboard/user_dashboard.html:26
#, python-format
msgid "Show details for %(packagename)s"
msgstr "Details für %(packagename)s anzeigen"
#: dashboard/templates/dashboard/user_dashboard.html:34
#, python-format
msgid ""
"The reserved disk space for your hosting package is %(diskspace)s bytes."
msgstr ""
"Der für Ihr Hostingpaket reservierte Speicherplatz sind %(diskspace)s Bytes."
#: dashboard/templates/dashboard/user_dashboard.html:40
#, python-format
msgid "used %(num)s of %(total)s"
msgstr "%(num)s von %(total)s genutzt"
#: dashboard/templates/dashboard/user_dashboard.html:55
#: dashboard/templates/dashboard/user_dashboard.html:38
msgid "You have no hosting packages yet."
msgstr "Sie haben noch keine Hostingpakete."
#: dashboard/templates/dashboard/user_dashboard.html:56
#: dashboard/templates/dashboard/user_dashboard.html:39
msgid "This user has no hosting packages assigned yet."
msgstr "Diesem Benutzer sind noch keine Hostingpakete zugewiesen."
#: dashboard/templates/dashboard/user_dashboard.html:60
#: dashboard/templates/dashboard/user_dashboard.html:43
msgid "Add hosting package"
msgstr "Hostingpaket anlegen"

View file

@ -1,4 +1,4 @@
# import celery_app to initialize it
from gnuviechadmin.celery import app as celery_app # NOQA
__version__ = "0.13.0"
__version__ = "0.15.1"

View file

@ -76,9 +76,6 @@ SITES_SITE_NAME = get_env_variable("GVA_SITE_NAME")
# See: https://docs.djangoproject.com/en/dev/ref/settings/#use-i18n
USE_I18N = True
# See: https://docs.djangoproject.com/en/dev/ref/settings/#use-l10n
USE_L10N = True
# See: https://docs.djangoproject.com/en/dev/ref/settings/#use-tz
USE_TZ = True
# ######### END GENERAL CONFIGURATION
@ -164,6 +161,7 @@ MIDDLEWARE = [
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"allauth.account.middleware.AccountMiddleware",
"django.middleware.locale.LocaleMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
# uncomment next line to enable translation to browser locale

View file

@ -38,6 +38,7 @@ urlpatterns = [
path("contact/", include("contact_form.urls")),
path("impressum/", views.flatpage, {"url": "/impressum/"}, name="imprint"),
path("datenschutz/", views.flatpage, {"url": "/datenschutz/"}, name="privacy"),
path("issues/", views.flatpage, {"url": "/issues/"}, name="support"),
]
# Uncomment the next line to serve media files in dev.

View file

@ -1,7 +1,7 @@
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _
from help.models import HelpUser

View file

@ -2,7 +2,7 @@ import uuid
from django.conf import settings
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _
class HelpUser(models.Model):

View file

@ -12,6 +12,7 @@ from .models import (
CustomerHostingPackage,
CustomerHostingPackageDomain,
CustomerMailboxOption,
CustomerPackageDiskUsage,
CustomerUserDatabaseOption,
DiskSpaceOption,
HostingPackageTemplate,
@ -95,6 +96,18 @@ class CustomerHostingPackageDomainInline(admin.TabularInline):
extra = 0
class CustomerPackageDiskUsageInline(admin.TabularInline):
model = CustomerPackageDiskUsage
ordering = ["-used_kb", "source", "item"]
fields = ["source", "item", "used_kb"]
readonly_fields = ["source", "item", "used_kb"]
extra = 0
can_delete = False
def has_add_permission(self, request, obj):
return False
class CustomerHostingPackageAdmin(admin.ModelAdmin):
"""
This class implements the admin interface for
@ -110,6 +123,7 @@ class CustomerHostingPackageAdmin(admin.ModelAdmin):
CustomerMailboxOptionInline,
CustomerUserDatabaseOptionInline,
CustomerHostingPackageDomainInline,
CustomerPackageDiskUsageInline,
]
list_display = ["name", "customer", "osuser"]

View file

@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gnuviechadmin hostingpackages\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-04-22 13:14+0200\n"
"PO-Revision-Date: 2023-04-22 13:15+0200\n"
"POT-Creation-Date: 2023-07-23 10:23+0200\n"
"PO-Revision-Date: 2023-07-23 10:24+0200\n"
"Last-Translator: Jan Dittberner <jan@dittberner.info>\n"
"Language-Team: Jan Dittberner <jan@dittberner.info>\n"
"Language: de\n"
@ -39,177 +39,228 @@ msgstr "Postfachoption hinzufügen"
msgid "Add database option"
msgstr "Datenbankoption hinzufügen"
#: hostingpackages/models.py:21
#: hostingpackages/models.py:22
msgid "MiB"
msgstr "MiB"
#: hostingpackages/models.py:21
#: hostingpackages/models.py:22
msgid "GiB"
msgstr "GiB"
#: hostingpackages/models.py:21
#: hostingpackages/models.py:22
msgid "TiB"
msgstr "TiB"
#: hostingpackages/models.py:27
#: hostingpackages/models.py:28
msgid "description"
msgstr "Beschreibung"
#: hostingpackages/models.py:28
#: hostingpackages/models.py:29
msgid "mailbox count"
msgstr "Anzahl Postfächer"
#: hostingpackages/models.py:30 hostingpackages/models.py:59
#: hostingpackages/models.py:31 hostingpackages/models.py:60
msgid "disk space"
msgstr "Speicherplatz"
#: hostingpackages/models.py:30
#: hostingpackages/models.py:31
msgid "disk space for the hosting package"
msgstr "Speicherplatz für das Hostingpaket"
#: hostingpackages/models.py:33 hostingpackages/models.py:61
#: hostingpackages/models.py:34 hostingpackages/models.py:62
msgid "unit of disk space"
msgstr "Maßeinheit für den Speicherplatz"
#: hostingpackages/models.py:44 hostingpackages/models.py:192
#: hostingpackages/models.py:45 hostingpackages/models.py:193
msgid "name"
msgstr "Name"
#: hostingpackages/models.py:47
#: hostingpackages/models.py:48
msgid "Hosting package"
msgstr "Hostingpaket"
#: hostingpackages/models.py:48
#: hostingpackages/models.py:49
msgid "Hosting packages"
msgstr "Hostingpakete"
#: hostingpackages/models.py:67
#: hostingpackages/models.py:68
msgid "Disk space option"
msgstr "Speicherplatzoption"
#: hostingpackages/models.py:68
#: hostingpackages/models.py:69
msgid "Disk space options"
msgstr "Speicherplatzoptionen"
#: hostingpackages/models.py:71
#: hostingpackages/models.py:72
#, python-brace-format
msgid "Additional disk space {space} {unit}"
msgstr "Zusätzlicher Speicherplatz {space} {unit}"
#: hostingpackages/models.py:88
#: hostingpackages/models.py:89
msgid "number of databases"
msgstr "Anzahl von Datenbanken"
#: hostingpackages/models.py:89
#: hostingpackages/models.py:90
msgid "database type"
msgstr "Datenbanktyp"
#: hostingpackages/models.py:94
#: hostingpackages/models.py:95
msgid "Database option"
msgstr "Datenbankoption"
#: hostingpackages/models.py:95
#: hostingpackages/models.py:96
msgid "Database options"
msgstr "Datenbankoptionen"
#: hostingpackages/models.py:99
#: hostingpackages/models.py:100
#, python-brace-format
msgid "{type} database"
msgid_plural "{count} {type} databases"
msgstr[0] "{type}-Datenbank"
msgstr[1] "{count} {type}-Datenbanken"
#: hostingpackages/models.py:120
#: hostingpackages/models.py:121
msgid "number of mailboxes"
msgstr "Anzahl von Postfächern"
#: hostingpackages/models.py:125
#: hostingpackages/models.py:126
msgid "Mailbox option"
msgstr "Postfachoption"
#: hostingpackages/models.py:126
#: hostingpackages/models.py:127
msgid "Mailbox options"
msgstr "Postfachoptionen"
#: hostingpackages/models.py:130
#: hostingpackages/models.py:131
#, python-brace-format
msgid "{count} additional mailbox"
msgid_plural "{count} additional mailboxes"
msgstr[0] "{count} zusätzliches Postfach"
msgstr[1] "{count} zusätzliche Postfächer"
#: hostingpackages/models.py:182
#: hostingpackages/models.py:183
msgid "customer"
msgstr "Kunde"
#: hostingpackages/models.py:186
#: hostingpackages/models.py:187
msgid "hosting package template"
msgstr "Hostingpaketvorlage"
#: hostingpackages/models.py:188
#: hostingpackages/models.py:189
msgid "The hosting package template that this hosting package is based on"
msgstr "Die Hostingpaketvorlage, auf der dieses Hostingpaket aufgebaut ist"
#: hostingpackages/models.py:195
#: hostingpackages/models.py:196
msgid "Operating system user"
msgstr "Betriebssystemnutzer"
#: hostingpackages/models.py:205
#: hostingpackages/models.py:206
msgid "customer hosting package"
msgstr "Kundenhostingpaket"
#: hostingpackages/models.py:206
#: hostingpackages/models.py:207
msgid "customer hosting packages"
msgstr "Kundenhostingpakete"
#: hostingpackages/models.py:209
#: hostingpackages/models.py:210
#, python-brace-format
msgid "{name} for {customer}"
msgstr "{name} für {customer}"
#: hostingpackages/models.py:388 hostingpackages/models.py:415
#: hostingpackages/models.py:421 hostingpackages/models.py:448
#: hostingpackages/models.py:513
msgid "hosting package"
msgstr "Hostingpaket"
#: hostingpackages/models.py:393
#: hostingpackages/models.py:426
msgid "hosting domain"
msgstr "Hostingdomain"
#: hostingpackages/models.py:420
#: hostingpackages/models.py:453
msgid "customer hosting option"
msgstr "kundenspezifische Hostingoption"
#: hostingpackages/models.py:421
#: hostingpackages/models.py:454
msgid "customer hosting options"
msgstr "kundenspezifische Hostingoptionen"
#: hostingpackages/models.py:433
#: hostingpackages/models.py:466
msgid "disk space option template"
msgstr "Speicherplatzoptionsvorlage"
#: hostingpackages/models.py:435
#: hostingpackages/models.py:468
msgid "The disk space option template that this disk space option is based on"
msgstr ""
"Die Speicherplatzoptionsvorlage auf der diese Speicherplatzoption aufgebaut "
"ist"
#: hostingpackages/models.py:450
#: hostingpackages/models.py:483
msgid "user database option template"
msgstr "Nutzerdatenbankoptionsvorlage"
#: hostingpackages/models.py:452
#: hostingpackages/models.py:485
msgid "The user database option template that this database option is based on"
msgstr ""
"Die Nutzerdatenbankoptionsvorlage auf der diese Datenbankoption aufgebaut ist"
#: hostingpackages/models.py:467
#: hostingpackages/models.py:500
msgid "mailbox option template"
msgstr "Postfachoptionsvorlage"
#: hostingpackages/models.py:468
#: hostingpackages/models.py:501
msgid "The mailbox option template that this mailbox option is based on"
msgstr "Die Postfachoptionsvorlage auf der diese Postfachoption aufgebaut ist"
#: hostingpackages/models.py:514
msgid "The hosting package"
msgstr "Das Hostingpaket"
#: hostingpackages/models.py:518
msgid "data source"
msgstr "Datenquelle"
#: hostingpackages/models.py:520
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:202
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:42
msgid "Mailbox"
msgstr "Postfach"
#: hostingpackages/models.py:521
msgid "Website"
msgstr "Webauftritt"
#: hostingpackages/models.py:522
msgid "MariaDB database"
msgstr "MariaDB-Datenbank"
#: hostingpackages/models.py:523
msgid "PostgreSQL database"
msgstr "PostgreSQL-Datenbank"
#: hostingpackages/models.py:526
msgid "data item"
msgstr "Dateneinheit"
#: hostingpackages/models.py:528
msgid "space used in KiB"
msgstr "genutzter Platz in KiB"
#: hostingpackages/models.py:532
msgid "mail address"
msgstr "E-Mailadresse"
#: hostingpackages/models.py:533
msgid "Assigned mail address"
msgstr "Zugeordnete E-Mailadresse"
#: hostingpackages/models.py:539
msgid "website"
msgstr "Webauftritt"
#: hostingpackages/models.py:540
msgid "Assigned web site"
msgstr "Zugeordneter Webauftritt"
#: hostingpackages/templates/hostingpackages/add_hosting_option.html:4
#: hostingpackages/templates/hostingpackages/add_hosting_option.html:7
#, python-format
@ -224,7 +275,6 @@ msgstr "Alle Hostingpakete"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:11
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:36
#: hostingpackages/templates/hostingpackages/customerhostingpackage_list.html:26
msgid "Name"
msgstr "Name"
@ -237,16 +287,45 @@ msgid "OS User"
msgstr "OS-Nutzer"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:14
#: hostingpackages/templates/hostingpackages/customerhostingpackage_list.html:27
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:40
#: hostingpackages/views.py:184
msgid "Disk space"
msgstr "Speicherplatz"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:15
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:54
#: hostingpackages/views.py:191
msgid "Mailboxes"
msgstr "Postfächer"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:16
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:239
#: hostingpackages/views.py:198
msgid "Databases"
msgstr "Datenbanken"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:17
msgid "Setup date"
msgstr "Einrichtungsdatum"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:31
#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:28
#, python-format
msgid ""
"The reserved disk space for your hosting package is %(diskspace)s bytes."
msgstr ""
"Der für Ihr Hostingpaket reservierte Speicherplatz beträgt %(diskspace)s "
"Bytes."
#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:34
#, python-format
msgid "used %(num)s of %(total)s"
msgstr "used %(num)s of %(total)s"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:48
msgid "No hosting packages have been setup yet."
msgstr "Es wurden noch keine Hostingpakete eingerichtet."
#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:34
#: hostingpackages/templates/hostingpackages/customerhostingpackage_list.html:46
#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:51
msgid "Add hosting package"
msgstr "Hostingpaket anlegen"
@ -287,38 +366,45 @@ msgstr "Informationen zum Hostingpaket ändern"
msgid "Description"
msgstr "Beschreibung"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:40
#: hostingpackages/views.py:199
msgid "Disk space"
msgstr "Speicherplatz"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:43
#, python-format
msgid "The reserved disk space for your hosting package is %(diskspace)s bytes"
msgid ""
"You use %(used_space)s of the reserved disk space of %(disk_space)s for your "
"hosting package"
msgstr ""
"Der für Ihr Hostingpaket reservierte Speicherplatz beträgt %(diskspace)s "
"Bytes"
"Sie nutzen aktuell %(used_space)s des reservierten Speicherplatzes von "
"%(disk_space)s für Ihr Hostingpaket"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:45
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:28
#, python-format
msgid "%(used_space)s of %(disk_space)s (%(space_level_percent)s%%)"
msgstr "%(used_space)s von %(disk_space)s (%(space_level_percent)s%%)"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:47
msgid "Disk usage details"
msgstr "Details zur Speicherplatznutzung"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:47
msgid "Details"
msgstr "Details"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:49
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:32
#, python-format
msgid ""
"The package contributes %(humanbytes)s (%(packagespace)s bytes) the "
"difference comes from disk space options"
"The package contributes %(package_space)s the difference comes from disk "
"space options"
msgstr ""
"Das Paket trägt %(humanbytes)s (%(packagespace)s Bytes) zur Gesamtgröße bei, "
"der Unterschied ergibt sich aus Speicherplatzoptionen"
"Das Paket trägt %(package_space)s zur Gesamtgröße bei, der Unterschied "
"ergibt sich aus Speicherplatzoptionen"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:52
#: hostingpackages/views.py:206
msgid "Mailboxes"
msgstr "Postfächer"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:54
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:56
#, python-format
msgid "%(num)s of %(total)s in use"
msgstr "%(num)s von %(total)s genutzt"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:57
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:59
#, python-format
msgid ""
"The package provides %(mailboxcount)s mailboxes the difference comes from "
@ -327,234 +413,269 @@ msgstr ""
"Das Paket bietet %(mailboxcount)s Postfächer, der Unterschied ergibt sich "
"durch die Postfachoptionen."
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:59
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:61
msgid "SFTP username"
msgstr "SFTP-Benutzername"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:60
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:62
msgid "SSH/SFTP username"
msgstr "SSH/SFTP-Benutzername"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:63
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:65
#, python-format
msgid "There is an SSH public key set for this user."
msgid_plural "There are %(counter)s SSH public keys set for this user."
msgstr[0] "Es wurde ein SSH-Schlüssel für diesen Nutzer hinterlegt."
msgstr[1] "Es wurden %(counter)s SSH-Schlüssel für diesen Nutzer hinterlegt."
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:65
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:67
msgid "Upload server"
msgstr "Uploadserver"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:73
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:75
msgid "Hosting Package Options"
msgstr "Hostingpaketoptionen"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:81
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:83
msgid "No options booked"
msgstr "Keine Optionen gebucht"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:87
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:89
msgid "Add another hosting option"
msgstr "Eine weitere Hostingoption hinzufügen"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:87
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:89
msgid "Add option"
msgstr "Option hinzufügen"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:94
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:96
msgid "Hosting Package Actions"
msgstr "Aktionen zum Hostingpaket"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:98
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:100
msgid "Edit Hosting Package Description"
msgstr "Beschreibung des Hostingpakets bearbeiten"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:98
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:100
msgid "Edit description"
msgstr "Beschreibung bearbeiten"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:101
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:103
msgid "Set SFTP password"
msgstr "SFTP-Passwort setzen"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:102
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:104
msgid "Set SSH/SFTP password"
msgstr "SSH/SFTP-Passwort setzen"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:105
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:107
msgid "Add an SSH public key that can be used as an alternative for password"
msgstr ""
"Einen SSH-Schlüssel, der als Alternative zum Passwort genutzt werden kann, "
"hinzufügen"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:107
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:109
msgid "Add SSH public key"
msgstr "SSH-Schlüssel hinzufügen"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:116
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:118
msgid "Domains"
msgstr "Domains"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:121
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:123
msgid "Domain name"
msgstr "Domainname"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:122
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:201
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:124
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:203
msgid "Mail addresses"
msgstr "E-Mailadressen"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:123
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:125
msgid "Websites"
msgstr "Webauftritte"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:124
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:126
msgid "Domain actions"
msgstr "Domainaktionen"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:125
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:204
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:247
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:127
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:206
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:249
msgid "Actions"
msgstr "Aktionen"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:137
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:139
msgid "Edit mail address targets"
msgstr "E-Mailadressziele bearbeiten"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:139
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:141
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:143
msgid "Delete mail address"
msgstr "E-Mailadresse löschen"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:146
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:161
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:148
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:163
msgid "None"
msgstr "Keine"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:154
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:156
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:158
msgid "Delete website"
msgstr "Webauftritt löschen"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:167
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:169
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:171
msgid "Add mail address"
msgstr "E-Mailadresse hinzufügen"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:174
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:175
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:176
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:177
msgid "Add website"
msgstr "Webauftritt anlegen"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:183
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:185
msgid "There are no domains assigned to this hosting package yet."
msgstr "Diesem Paket sind noch keine Domains zugeordnet."
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:187
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:189
msgid "Add domain"
msgstr "Domain hinzufügen"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:195
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:197
msgid "E-Mail-Accounts"
msgstr "E-Mail-Konten"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:200
msgid "Mailbox"
msgstr "Postfach"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:202
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:214
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:204
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:216
msgid "Active"
msgstr "Aktiv"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:203
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:205
msgid "Mailbox actions"
msgstr "Postfachaktionen"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:215
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:217
msgid "inactive"
msgstr "inaktiv"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:218
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:219
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:220
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:221
msgid "Set mailbox password"
msgstr "Postfachpasswort setzen"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:225
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:227
msgid "There are no mailboxes assigned to this hosting package yet."
msgstr "Diesem Hostingpaket sind noch keine Postfächer zugeordnet."
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:230
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:232
msgid "Add mailbox"
msgstr "Postfach hinzufügen"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:237
#: hostingpackages/views.py:213
msgid "Databases"
msgstr "Datenbanken"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:242
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:244
msgid "Database name"
msgstr "Datenbankname"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:243
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:245
msgid "Database user"
msgstr "Datenbanknutzer"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:244
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:246
msgid "Database type"
msgstr "Datenbanktyp"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:245
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:247
msgid "Type"
msgstr "Typ"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:246
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:248
msgid "Database actions"
msgstr "Datenbankaktionen"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:258
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:260
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:262
msgid "Set database user password"
msgstr "Datenbanknutzerpasswort setzen"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:262
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:263
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:264
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:265
msgid "Delete database"
msgstr "Datenbank löschen"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:270
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:272
msgid "There are no databases assigned to this hosting package yet."
msgstr "Diesem Hostingpaket sind noch keine Datenbanken zugeordnet."
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:275
#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:277
msgid "Add database"
msgstr "Datenbank hinzufügen"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_list.html:5
#: hostingpackages/templates/hostingpackages/customerhostingpackage_list.html:14
msgid "Your hosting packages"
msgstr "Ihre Hostingpakete"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_list.html:7
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:6
#, python-format
msgid "Hosting Packages of %(customer)s"
msgstr "Hostingpakete des Kunden %(customer)s"
msgid "Disk usage of your Hosting Package %(package)s"
msgstr "Speicherplatznutzung Ihres Hostingpakets %(package)s"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_list.html:16
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:10
#, python-format
msgid "Hosting Packages <small>of %(customer)s</small>"
msgstr "Hostingpakete <small>des Kunden %(customer)s</small>"
msgid "Disk usage of Hosting Package %(package)s of %(full_name)s"
msgstr "Speicherplatznutzung des Hostingpakets %(package)s von %(full_name)s"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_list.html:41
msgid "You have no hosting packages setup yet."
msgstr "Es wurden noch keine Hostingpakete für Sie eingerichtet."
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:17
#, python-format
msgid ""
"Disk usage of Hosting Package <a href=\"%(package_url)s\">%(package)s</a>"
msgstr ""
"Speicherplatznutzung des Hostingpakets <a "
"href=\"%(package_url)s\">%(package)s</a>"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_list.html:42
msgid "There are no hosting packages setup for this customer yet."
msgstr "Es wurden noch keine Hostingpakete für diesen Kunden eingerichtet."
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:23
#, python-format
msgid ""
"You use %(used_space)s of the reserved disk space of %(disk_space)s for your "
"hosting package."
msgstr ""
"Sie nutzen %(used_space)s des reservierten Speicherplatzes von "
"%(disk_space)s für Ihr Hostingpaket."
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:36
msgid "Breakdown by usage"
msgstr "Aufgliederung nach Nutzung"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:38
msgid "Mailbox usage"
msgstr "Postfachnutzung"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:43
msgid "Primary email address"
msgstr "Primäre E-Mailadresse"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:44
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:64
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:83
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:102
msgid "Used space"
msgstr "Genutzter Speicherplatz"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:59
msgid "Website usage"
msgstr "Nutzung für Webauftritte"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:63
msgid "Website / Directory"
msgstr "Webauftritt / Verzeichnis"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:78
msgid "MySQL/MariaDB database usage"
msgstr "MySQL/MariaDB-Datenbanknutzung"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:82
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:101
msgid "Database"
msgstr "Datenbank"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_disk_usage_details.html:97
msgid "PostgreSQL database usage"
msgstr "PostgreSQL-Datenbanknutzung"
#: hostingpackages/templates/hostingpackages/customerhostingpackage_option_choices.html:5
#: hostingpackages/templates/hostingpackages/customerhostingpackage_option_choices.html:8
@ -565,12 +686,12 @@ msgstr ""
"Wählen Sie eine neue Option für das Hostingpaket %(package)s des Kunden "
"%(full_name)s"
#: hostingpackages/views.py:49 hostingpackages/views.py:83
#: hostingpackages/views.py:63 hostingpackages/views.py:97
#, python-brace-format
msgid "Started setup of new hosting package {name}."
msgstr "Einrichtung des Hostingpakets {name} wurde gestartet."
#: hostingpackages/views.py:287
#: hostingpackages/views.py:272
#, python-brace-format
msgid "Successfully added option {option} to hosting package {package}."
msgstr "Option {option} erfolgreich zum Hostingpaket {package} hinzugefügt."

View file

@ -0,0 +1,74 @@
# Generated by Django 4.2.3 on 2023-07-22 17:31
import django.utils.timezone
import model_utils.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("hostingpackages", "0006_auto_20150125_1510"),
]
operations = [
migrations.CreateModel(
name="CustomerPackageDiskUsage",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"created",
model_utils.fields.AutoCreatedField(
default=django.utils.timezone.now,
editable=False,
verbose_name="created",
),
),
(
"modified",
model_utils.fields.AutoLastModifiedField(
default=django.utils.timezone.now,
editable=False,
verbose_name="modified",
),
),
(
"source",
models.CharField(
choices=[
("disk", "disk"),
("mysql", "mysql"),
("pgsql", "pgsql"),
],
verbose_name="data source",
),
),
("item", models.CharField(verbose_name="data item")),
(
"used_kb",
models.PositiveBigIntegerField(
default=0, verbose_name="space used in KiB"
),
),
(
"package",
models.ForeignKey(
help_text="The hosting package",
on_delete=django.db.models.deletion.CASCADE,
to="hostingpackages.customerhostingpackage",
verbose_name="hosting package",
),
),
],
options={
"unique_together": {("package", "source", "item")},
},
),
]

View file

@ -0,0 +1,53 @@
# Generated by Django 4.2.3 on 2023-07-23 07:24
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("managemails", "0004_auto_20150125_1825"),
("websites", "0001_initial"),
("hostingpackages", "0007_add_disk_usage_table"),
]
operations = [
migrations.AddField(
model_name="customerpackagediskusage",
name="email_address",
field=models.ForeignKey(
help_text="Assigned mail address",
null=True,
on_delete=django.db.models.deletion.CASCADE,
to="managemails.mailaddress",
verbose_name="mail address",
),
),
migrations.AddField(
model_name="customerpackagediskusage",
name="website",
field=models.ForeignKey(
help_text="Assigned web site",
null=True,
on_delete=django.db.models.deletion.CASCADE,
to="websites.website",
verbose_name="website",
),
),
migrations.RunSQL(
"DELETE FROM hostingpackages_customerpackagediskusage WHERE source='disk'"
),
migrations.AlterField(
model_name="customerpackagediskusage",
name="source",
field=models.CharField(
choices=[
("mail", "Mailbox"),
("web", "Website"),
("mysql", "MariaDB database"),
("pgsql", "PostgreSQL database"),
],
verbose_name="data source",
),
),
]

View file

@ -269,6 +269,33 @@ class CustomerHostingPackage(HostingPackageBase):
return DISK_SPACE_FACTORS[unit][min_unit] * diskspace
return DISK_SPACE_FACTORS[min_unit][unit] * diskspace
disk_space = property(get_disk_space)
def get_used_disk_space_sum(self, unit=None):
"""
Get the used disk space of this hosting package from submitted disk space statistics.
:param unit: value from :py:data:`DISK_SPACE_UNITS` or :py:const:`None`
:return: disk space in unit or bytes (if parameter unit is :py:const:`None`)
:rtype: int
"""
sum = 0
for usage in self.customerpackagediskusage_set.all():
sum += usage.size_in_bytes
if unit is None:
return sum
return DISK_SPACE_FACTORS[0][unit] * sum
used_disk_space_sum = property(get_used_disk_space_sum)
def get_space_level(self):
if not self.diskspace:
return 100.0
return self.used_disk_space_sum / self.disk_space * 100.0
space_level = property(get_space_level)
def get_package_space(self, unit=None):
"""
Get the total disk space reserved for this package without looking at
@ -474,3 +501,53 @@ class CustomerMailboxOption(MailboxOptionBase, CustomerHostingPackageOption):
help_text=_("The mailbox option template that this mailbox option is based on"),
on_delete=models.CASCADE,
)
class CustomerPackageDiskUsage(TimeStampedModel):
"""
This class represents disk usage statistics for a customer hosting package.
"""
package = models.ForeignKey(
CustomerHostingPackage,
verbose_name=_("hosting package"),
help_text=_("The hosting package"),
on_delete=models.CASCADE,
)
source = models.CharField(
verbose_name=_("data source"),
choices=(
("mail", _("Mailbox")),
("web", _("Website")),
("mysql", _("MariaDB database")),
("pgsql", _("PostgreSQL database")),
),
)
item = models.CharField(verbose_name=_("data item"))
used_kb = models.PositiveBigIntegerField(
verbose_name=_("space used in KiB"), default=0
)
email_address = models.ForeignKey(
"managemails.MailAddress",
verbose_name=_("mail address"),
help_text=_("Assigned mail address"),
on_delete=models.CASCADE,
null=True,
)
website = models.ForeignKey(
"websites.Website",
verbose_name=_("website"),
help_text=_("Assigned web site"),
on_delete=models.CASCADE,
null=True,
)
class Meta:
unique_together = ("package", "source", "item")
def __str__(self):
return "%s %s = %d KiB" % (self.source, self.item, self.used_kb)
@property
def size_in_bytes(self):
return self.used_kb * 1024

View file

@ -0,0 +1,7 @@
from rest_framework import serializers
from hostingpackages.models import CustomerPackageDiskUsage
class DiskUsageSerializer(serializers.Serializer):
user = serializers.CharField()

View file

@ -38,14 +38,16 @@
<dt>{% translate "Description" %}</dt>
<dd>{{ hostingpackage.description|default:"-" }}</dd>
<dt>{% translate "Disk space" %}</dt>
{% with diskspace=hostingpackage.get_disk_space packagespace=hostingpackage.get_package_space %}
{% with used_space=hostingpackage.get_used_disk_space_sum|filesizeformat disk_space=hostingpackage.get_disk_space|filesizeformat package_space=hostingpackage.get_package_space|filesizeformat space_level=hostingpackage.space_level %}
<dd>
<span title="{% blocktranslate trimmed %}
The reserved disk space for your hosting package is {{ diskspace }} bytes
{% endblocktranslate %}">{{ diskspace|filesizeformat }}</span>
You use {{ used_space }} of the reserved disk space of {{ disk_space }} for your hosting package
{% endblocktranslate %}" class="text-{% if space_level > 90.0 %}danger{% elif space_level > 80.0 %}warning{% else %}success{% endif %}">{% blocktranslate with space_level_percent=space_level|floatformat:1 trimmed%}
{{ used_space }} of {{ disk_space }} ({{ space_level_percent }}%)
{% endblocktranslate %} <a title="{% translate "Disk usage details" %}" href="{% url "disk_usage_details" user=hostingpackage.customer.username package=hostingpackage.id %}">{% translate "Details" %}</a></span>
<i class="bi-info-circle"
title="{% blocktranslate with humanbytes=packagespace|filesizeformat trimmed %}
The package contributes {{ humanbytes }} ({{ packagespace }} bytes) the difference comes from disk space options
title="{% blocktranslate trimmed %}
The package contributes {{ package_space }} the difference comes from disk space options
{% endblocktranslate %}"></i>
</dd>
{% endwith %}

View file

@ -0,0 +1,117 @@
{% extends "hostingpackages/base.html" %}
{% load i18n %}
{% block title %}{{ block.super }} - {% spaceless %}
{% if user == customer %}
{% blocktranslate with package=hostingpackage.name trimmed %}
Disk usage of your Hosting Package {{ package }}
{% endblocktranslate %}
{% else %}
{% blocktranslate with package=hostingpackage.name full_name=customer.get_full_name trimmed %}
Disk usage of Hosting Package {{ package }} of {{ full_name }}
{% endblocktranslate %}
{% endif %}
{% endspaceless %}{% endblock title %}
{% block page_title %}
{% blocktranslate with package=hostingpackage.name package_url=hostingpackage.get_absolute_url trimmed %}
Disk usage of Hosting Package <a href="{{ package_url }}">{{ package }}</a>
{% endblocktranslate %}{% endblock page_title %}
{% block content %}
{% with used_space=hostingpackage.get_used_disk_space_sum|filesizeformat disk_space=hostingpackage.get_disk_space|filesizeformat package_space=hostingpackage.get_package_space|filesizeformat space_level=hostingpackage.space_level %}
<p>{% blocktranslate trimmed %}
You use {{ used_space }} of the reserved disk space of {{ disk_space }} for your hosting package.
{% endblocktranslate %}</p>
<p class="lead"><span
class="text-{% if space_level > 90.0 %}danger{% elif space_level > 80.0 %}warning{% else %}success{% endif %}">
{% blocktranslate with space_level_percent=space_level|floatformat:1 trimmed %}
{{ used_space }} of {{ disk_space }} ({{ space_level_percent }}%)
{% endblocktranslate %}</span>
<i class="bi-info-circle"
title="{% blocktranslate trimmed %}
The package contributes {{ package_space }} the difference comes from disk space options
{% endblocktranslate %}"></i>
</p>
<h2>{% trans "Breakdown by usage" %}</h2>
{% if mail_usage %}
<h3>{% trans "Mailbox usage" %}</h3>
<table class="table table-condensed table-striped">
<thead>
<tr>
<th>{% translate "Mailbox" %}</th>
<th>{% translate "Primary email address" %}</th>
<th class="text-end">{% translate "Used space" %}</th>
</tr>
</thead>
<tbody>
{% for line in mail_usage %}
<tr>
<td>{{ line.item }}</td>
<td>{% if line.email_address %}{{ line.email_address }}{% else %}-{% endif %}</td>
<td class="text-end">{{ line.size_in_bytes|filesizeformat }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% if web_usage %}
<h3>{% trans "Website usage" %}</h3>
<table class="table table-condensed table-striped">
<thead>
<tr>
<th>{% translate "Website / Directory" %}</th>
<th class="text-end">{% translate "Used space" %}</th>
</tr>
</thead>
<tbody>
{% for line in web_usage %}
<tr>
<td>{{ line.item }}</td>
<td class="text-end">{{ line.size_in_bytes|filesizeformat }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% if mysql_usage %}
<h3>{% trans "MySQL/MariaDB database usage" %}</h3>
<table class="table table-condensed table-striped">
<thead>
<tr>
<th>{% translate "Database" %}</th>
<th class="text-end">{% translate "Used space" %}</th>
</tr>
</thead>
<tbody>
{% for line in mysql_usage %}
<tr>
<td>{{ line.item }}</td>
<td class="text-end">{{ line.size_in_bytes|filesizeformat }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% if pgsql_usage %}
<h3>{% trans "PostgreSQL database usage" %}</h3>
<table class="table table-condensed table-striped">
<thead>
<tr>
<th>{% translate "Database" %}</th>
<th class="text-end">{% translate "Used space" %}</th>
</tr>
</thead>
<tbody>
{% for line in pgsql_usage %}
<tr>
<td>{{ line.item }}</td>
<td class="text-end">{{ line.size_in_bytes|filesizeformat }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% endwith %}
{% endblock content %}

View file

@ -16,37 +16,54 @@ User = get_user_model()
class CustomerHostingPackageTest(TestCase):
def test_get_disk_space_bytes(self):
package = CustomerHostingPackage(
diskspace=10, diskspace_unit=DISK_SPACE_UNITS.G
def setUp(self) -> None:
self.user = User.objects.create(username="test")
self.template = HostingPackageTemplate.objects.create(
mailboxcount=10,
diskspace=100,
diskspace_unit=DISK_SPACE_UNITS.M,
description="Test package 1 - Description",
name="Test package 1",
)
self.assertEqual(package.get_disk_space(), 10 * 1024**3)
def test_get_disk_space_mib(self):
package = CustomerHostingPackage(
diskspace=10, diskspace_unit=DISK_SPACE_UNITS.G
@override_settings(OSUSER_DEFAULT_GROUPS=[])
def test_get_disk_space_bytes(self):
self.template.diskspace = 10
self.template.diskspace_unit = DISK_SPACE_UNITS.G
self.template.save()
package = CustomerHostingPackage.objects.create_from_template(
customer=self.user, template=self.template, name="customer",
)
package.save()
self.assertEqual(package.get_disk_space(), 10 * 1024 ** 3)
@override_settings(OSUSER_DEFAULT_GROUPS=[])
def test_get_disk_space_mib(self):
self.template.diskspace = 10
self.template.diskspace_unit = DISK_SPACE_UNITS.G
self.template.save()
package = CustomerHostingPackage.objects.create_from_template(
customer=self.user, template=self.template, name="customer",
)
package.save()
self.assertEqual(package.get_disk_space(DISK_SPACE_UNITS.M), 10 * 1024)
@override_settings(OSUSER_DEFAULT_GROUPS=[])
def test_get_quota(self):
package = CustomerHostingPackage(
diskspace=256, diskspace_unit=DISK_SPACE_UNITS.M
self.template.diskspace = 256
self.template.diskspace_unit = DISK_SPACE_UNITS.M
self.template.save()
package = CustomerHostingPackage.objects.create_from_template(
customer=self.user, template=self.template, name="customer",
)
package.save()
self.assertEqual(package.get_quota(), (262144, 275251))
@override_settings(OSUSER_DEFAULT_GROUPS=["testgroup"])
def test_additional_group_not_defined(self):
user = User.objects.create(username="test")
template = HostingPackageTemplate.objects.create(
description="Test package 1 - Description",
mailboxcount=10,
diskspace=100,
diskspace_unit=DISK_SPACE_UNITS.M,
name="Test package 1",
)
with self.assertRaises(ImproperlyConfigured) as ctx:
package = CustomerHostingPackage.objects.create_from_template(
customer=user, template=template, name="Test customer package"
customer=self.user, template=self.template, name="Test customer package",
)
package.save()
self.assertIn("testgroup", str(ctx.exception))

View file

@ -4,7 +4,7 @@ This module defines the URL patterns for hosting package related views.
"""
from __future__ import absolute_import
from django.urls import re_path
from django.urls import path, re_path
from .views import (
AddHostingOption,
@ -12,7 +12,9 @@ from .views import (
CreateCustomerHostingPackage,
CreateHostingPackage,
CustomerHostingPackageDetails,
CustomerHostingPackageDiskUsageDetails,
HostingOptionChoices,
UploadCustomerPackageDiskUsage,
)
urlpatterns = [
@ -42,4 +44,14 @@ urlpatterns = [
AddHostingOption.as_view(),
name="add_hosting_option",
),
path(
"<str:user>/<int:package>/disk-usage/",
CustomerHostingPackageDiskUsageDetails.as_view(),
name="disk_usage_details",
),
path(
"upload-disk-usage/",
UploadCustomerPackageDiskUsage.as_view(),
name="upload_disk_usage",
),
]

View file

@ -4,17 +4,29 @@ This module defines views related to hosting packages.
"""
from __future__ import absolute_import
import http
import logging
from datetime import timedelta
from django.conf import settings
from django.contrib import messages
from django.contrib.auth import get_user_model
from django.contrib.auth.mixins import PermissionRequiredMixin, UserPassesTestMixin
from django.http import Http404
from django.shortcuts import get_object_or_404, redirect
from django.utils import timezone
from django.utils.translation import gettext as _
from django.views.generic import DetailView, ListView
from django.views.generic.edit import CreateView, FormView
import rest_framework.request
from rest_framework.permissions import BasePermission
from rest_framework.response import Response
from rest_framework.views import APIView
from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin
from managemails.models import Mailbox
from .forms import (
AddDiskspaceOptionForm,
AddMailboxOptionForm,
@ -24,10 +36,14 @@ from .forms import (
)
from .models import (
CustomerHostingPackage,
CustomerPackageDiskUsage,
DiskSpaceOption,
MailboxOption,
UserDatabaseOption,
)
from .serializers import DiskUsageSerializer
logger = logging.getLogger(__name__)
class CreateHostingPackage(PermissionRequiredMixin, CreateView):
@ -259,3 +275,121 @@ class AddHostingOption(StaffUserRequiredMixin, FormView):
).format(option=option, package=hosting_package.name),
)
return redirect(hosting_package)
class HasDiskUsageUploadPermission(BasePermission):
def has_permission(self, request, view):
return (
request.user.has_perm("hostingpackages.add_customerpackagediskusage")
and request.method == "POST"
)
class UploadCustomerPackageDiskUsage(APIView):
permission_classes = [HasDiskUsageUploadPermission]
allowed_methods = ("POST",)
serializer = DiskUsageSerializer(many=True)
def post(self, request: rest_framework.request.Request, format=None):
if request.content_type != "application/json":
return Response("Unacceptable", status=http.HTTPStatus.BAD_REQUEST)
submitted_sources = set()
for row in request.data:
user = row["user"]
for key in row:
if key == "user":
continue
else:
submitted_sources.add(key)
for item, size in row[key].items():
try:
package = CustomerHostingPackage.objects.get(
osuser__username=user
)
(
metric,
created,
) = CustomerPackageDiskUsage.objects.get_or_create(
package=package,
source=key,
item=item,
)
metric.used_kb = size
if key == "mail":
try:
ma_mb = package.mailboxes.get(
username=item
).mailaddressmailbox_set.first()
if ma_mb:
metric.email_address_id = ma_mb.mailaddress_id
except Mailbox.DoesNotExist:
logger.warning("mail box %s does not exist", item)
metric.save()
except CustomerHostingPackage.DoesNotExist:
logger.warning(
"hosting package for user %s does not exist", user
)
if submitted_sources:
CustomerPackageDiskUsage.objects.filter(
source__in=submitted_sources,
modified__lt=timezone.now() - timedelta(minutes=30),
).delete()
logger.info("usage data submitted by %s", request.user)
return Response("Accepted", status=http.HTTPStatus.ACCEPTED)
class CustomerHostingPackageDiskUsageDetails(StaffOrSelfLoginRequiredMixin, DetailView):
template_name_suffix = "_disk_usage_details"
model = CustomerHostingPackage
pk_url_kwarg = "package"
context_object_name = "hostingpackage"
customer = None
def get_customer_object(self):
if self.customer is None:
self.customer = get_object_or_404(
get_user_model(), username=self.kwargs["user"]
)
return self.customer
def get_queryset(self, queryset=None):
return super().get_queryset().prefetch_related("customerpackagediskusage_set")
def get_context_data(self, **kwargs):
context_data = super().get_context_data(**kwargs)
mail_usage, web_usage, mysql_usage, pgsql_usage = [], [], [], []
for usage in self.get_object().customerpackagediskusage_set.order_by(
"-used_kb"
):
if usage.used_kb <= 0:
continue
if usage.source == "mail":
mail_usage.append(usage)
elif usage.source == "web":
web_usage.append(usage)
elif usage.source == "mysql":
mysql_usage.append(usage)
elif usage.source == "pgsql":
pgsql_usage.append(usage)
context_data.update(
{
"customer": self.get_customer_object(),
"mail_usage": mail_usage,
"web_usage": web_usage,
"mysql_usage": mysql_usage,
"pgsql_usage": pgsql_usage,
}
)
return context_data

View file

@ -1,5 +1,5 @@
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _
class InvoiceConfig(AppConfig):

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: gnuviechadmin invoice\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-04-23 14:35+0200\n"
"POT-Creation-Date: 2023-07-22 19:45+0200\n"
"PO-Revision-Date: 2023-04-23 14:35+0200\n"
"Last-Translator: \n"
"Language-Team: Jan Dittberner <jan@dittberner.info>\n"
@ -19,51 +19,51 @@ msgstr ""
"X-Generator: Poedit 3.2.2\n"
"X-Poedit-SourceCharset: UTF-8\n"
#: invoice/apps.py:8
#: invoices/apps.py:8
msgid "Invoices"
msgstr "Rechnungen"
#: invoice/models.py:26
#: invoices/models.py:26
msgid "customer"
msgstr "Kunde"
#: invoice/models.py:33
#: invoices/models.py:33
msgid "invoice number"
msgstr "Rechnungsnummer"
#: invoice/models.py:35
#: invoices/models.py:35
msgid "invoice date"
msgstr "Rechnungsdatum"
#: invoice/models.py:37
#: invoices/models.py:37
msgid "amount"
msgstr "Betrag"
#: invoice/models.py:40
#: invoices/models.py:40
msgid "currency"
msgstr "Währung"
#: invoice/models.py:42
#: invoices/models.py:42
msgid "due date"
msgstr "Fälligkeit"
#: invoice/models.py:44
#: invoices/models.py:44
msgid "payment date"
msgstr "Zahlungsdatum"
#: invoice/models.py:47
#: invoices/models.py:47
msgid "payment variant"
msgstr "Zahlungsart"
#: invoice/models.py:51
#: invoices/models.py:51
msgid "invoice"
msgstr "Rechnung"
#: invoice/models.py:52
#: invoices/models.py:52
msgid "invoices"
msgstr "Rechnungen"
#: invoice/models.py:56
#: invoices/models.py:56
#, python-brace-format
msgid "Invoice {0}"
msgstr "Rechnung {0}"

View file

@ -3,7 +3,7 @@ from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.validators import FileExtensionValidator
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _
CURRENCIES = [(1, "EUR")]

View file

@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gnuviechadmin\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-04-22 13:01+0200\n"
"PO-Revision-Date: 2023-04-22 12:58+0200\n"
"POT-Creation-Date: 2023-07-22 19:45+0200\n"
"PO-Revision-Date: 2023-07-22 19:56+0200\n"
"Last-Translator: Jan Dittberner <jan@dittberner.info>\n"
"Language-Team: Jan Dittberner <jan@dittberner.info>\n"
"Language: de\n"
@ -44,7 +44,7 @@ msgstr "Die folgenden E-Mailadressen sind Ihrem Konto zugeordnet:"
msgid "Email address"
msgstr "E-Mailadresse"
#: templates/account/email.html:15 templates/account/email.html:25
#: templates/account/email.html:15 templates/account/email.html:26
msgid "Verified"
msgstr "Geprüft"
@ -52,31 +52,40 @@ msgstr "Geprüft"
msgid "Primary"
msgstr "Primär"
#: templates/account/email.html:27
#: templates/account/email.html:17
msgid "Selection"
msgstr "Auswahl"
#: templates/account/email.html:28
msgid "Unverified"
msgstr "Unbestätigt"
#: templates/account/email.html:32
#: templates/account/email.html:33
msgid "This is the current primary Email address"
msgstr "Dies ist die aktuelle primäre E-Mailadresse"
#: templates/account/email.html:43
#, python-format
msgid "Select %(address)s"
msgstr "%(address)s auswählen"
#: templates/account/email.html:54
msgid "Make Primary"
msgstr "Als primär definieren"
#: templates/account/email.html:44
#: templates/account/email.html:57
msgid "Re-send Verification"
msgstr "Prüf-E-Mail noch einmal verschicken"
#: templates/account/email.html:45 templates/socialaccount/connections.html:42
#: templates/account/email.html:58 templates/socialaccount/connections.html:42
msgid "Remove"
msgstr "Entfernen"
#: templates/account/email.html:49
#: templates/account/email.html:63
msgid "Warning:"
msgstr "Warnung:"
#: templates/account/email.html:49
#: templates/account/email.html:63
msgid ""
"You currently do not have any e-mail address set up. You should really add "
"an e-mail address so you can receive notifications, reset your password, etc."
@ -85,15 +94,15 @@ msgstr ""
"Mailadresse, um Benachrichtigungen zu erhalten, Ihr Passwort zurückzusetzen, "
"etc."
#: templates/account/email.html:52
#: templates/account/email.html:67
msgid "Add E-mail Address"
msgstr "E-Mailadresse hinzufügen"
#: templates/account/email.html:57
#: templates/account/email.html:72
msgid "Add E-mail"
msgstr "E-Mail hinzufügen"
#: templates/account/email.html:64
#: templates/account/email.html:79
msgid "Do you really want to remove the selected e-mail address?"
msgstr "Wollen Sie die augewählte E-Mailadresse wirklich entfernen?"
@ -181,7 +190,7 @@ msgstr ""
"Mailadresse des Benutzers %(user_display)s ist."
#: templates/account/login.html:4 templates/account/login.html:5
#: templates/account/login.html:18 templates/base.html:124
#: templates/account/login.html:18 templates/base.html:112
#: templates/registration/login.html:4 templates/socialaccount/login.html:4
msgid "Sign In"
msgstr "Anmelden"
@ -399,67 +408,59 @@ msgstr ""
"<strong>Hinweis:</strong> Sie können Ihre <a href=\"%(email_url)s\">E-"
"Mailadresse noch ändern</a>."
#: templates/base.html:32
#: templates/base.html:26
msgid "Dashboard"
msgstr "Dashboard"
#: templates/base.html:34
#: templates/base.html:28
msgid "Toggle navigation"
msgstr "Navigation umschalten"
#: templates/base.html:42 templates/base.html:53
#: templates/base.html:36
msgid "Hosting"
msgstr "Hosting"
#: templates/base.html:45
msgid "Your hosting packages"
msgstr "Ihre Hostingpakete"
#: templates/base.html:47
msgid "All hosting packages"
msgstr "Alle Hostingpakete"
#: templates/base.html:57
#: templates/base.html:42
msgid "Links"
msgstr "Links"
#: templates/base.html:60
#: templates/base.html:45
msgid "Web based mail system"
msgstr "Webbasiertes E-Mailsystem"
#: templates/base.html:61
#: templates/base.html:46
msgid "Webmail"
msgstr "Webmail"
#: templates/base.html:63
#: templates/base.html:48
msgid "phpMyAdmin - MySQL database administration tool"
msgstr "phpMyAdmin - MySQL-Datenbankverwaltungswerkzeug"
#: templates/base.html:64
#: templates/base.html:49
msgid "phpMyAdmin"
msgstr "phpMyAdmin"
#: templates/base.html:66
#: templates/base.html:51
msgid "phpPgAdmin - PostgreSQL database administration tool"
msgstr "phpPgAdmin - PostgreSQL-Datenbankverwaltungswerkzeug"
#: templates/base.html:67
#: templates/base.html:52
msgid "phpPgAdmin"
msgstr "phpPgAdmin"
#: templates/base.html:71
#: templates/base.html:57
msgid "Imprint"
msgstr "Impressum"
#: templates/base.html:74
#: templates/base.html:61
msgid "Privacy policy"
msgstr "Datenschutz"
#: templates/base.html:78
#: templates/base.html:65
msgid "Contact"
msgstr "Kontakt"
#: templates/base.html:86
#: templates/base.html:73
#, python-format
msgid ""
"Signed in as <a href=\"%(profile_url)s\" class=\"navbar-link\" title=\"My "
@ -474,7 +475,7 @@ msgstr ""
"class=\"navbar-link\"><i class=\"bi-sign-stop\" title=\"Übernahme "
"beenden\"></i></a>"
#: templates/base.html:93
#: templates/base.html:81
#, python-format
msgid ""
"Signed in as <a href=\"%(profile_url)s\" class=\"navbar-link\" title=\"My "
@ -483,31 +484,31 @@ msgstr ""
"Angemeldet als <a href=\"%(profile_url)s\" class=\"navbar-link\" "
"title=\"Mein Profil\">%(user_display)s</a>"
#: templates/base.html:103
#: templates/base.html:91
msgid "My Account"
msgstr "Mein Konto"
#: templates/base.html:107
#: templates/base.html:95
msgid "Impersonate user"
msgstr "Als Nutzer agieren"
#: templates/base.html:111
#: templates/base.html:99
msgid "Admin site"
msgstr "Adminsite"
#: templates/base.html:113
#: templates/base.html:101
msgid "Change Email"
msgstr "E-Mail ändern"
#: templates/base.html:116
#: templates/base.html:104
msgid "Social Accounts"
msgstr "Konten in sozialen Netzwerken"
#: templates/base.html:118
#: templates/base.html:106
msgid "Logout"
msgstr "Abmelden"
#: templates/base.html:139
#: templates/base.html:127
msgid "Close"
msgstr "Schließen"
@ -527,6 +528,12 @@ msgstr "Django Impersonate - Nutzerliste"
msgid "User List - Page %(page_number)s"
msgstr "Nutzerliste - Seite %(page_number)s"
#: templates/impersonate/list_users.html:14
#: templates/impersonate/search_users.html:24
#, python-format
msgid "Impersonate %(user)s"
msgstr "Als Nutzer %(user)s agieren"
#: templates/impersonate/list_users.html:21
msgid "Search users"
msgstr "Nutzer suchen"

View file

@ -140,7 +140,7 @@ class MailAddressTest(TestCaseWithCeleryTasks):
def get_target(maf):
return maf.target
self.assertQuerysetEqual(
self.assertQuerySetEqual(
ma.mailaddressforward_set.all(), ["test2@example.org"], get_target
)
@ -152,7 +152,7 @@ class MailAddressTest(TestCaseWithCeleryTasks):
def get_target(maf):
return maf.target
self.assertQuerysetEqual(
self.assertQuerySetEqual(
ma.mailaddressforward_set.all(), ["test2@example.org"], get_target
)
@ -165,7 +165,7 @@ class MailAddressTest(TestCaseWithCeleryTasks):
def get_target(maf):
return maf.target
self.assertQuerysetEqual(
self.assertQuerySetEqual(
ma.mailaddressforward_set.all(), ["test3@example.org"], get_target
)
@ -178,7 +178,7 @@ class MailAddressTest(TestCaseWithCeleryTasks):
def get_target(maf):
return maf.target
self.assertQuerysetEqual(
self.assertQuerySetEqual(
ma.mailaddressforward_set.all(),
["test2@example.org", "test3@example.org"],
get_target,
@ -198,7 +198,7 @@ class MailAddressTest(TestCaseWithCeleryTasks):
def get_target(maf):
return maf.target
self.assertQuerysetEqual(
self.assertQuerySetEqual(
ma.mailaddressforward_set.all(), ["test2@example.org"], get_target
)
@ -213,7 +213,6 @@ class MailAddressTest(TestCaseWithCeleryTasks):
md = MailDomain.objects.create(domain="example.org")
ma = MailAddress(localpart="test", domain=md)
mafwds = ma.set_forward_addresses(["test2@example.org"], commit=False)
self.assertEqual(ma.mailaddressforward_set.count(), 0)
self.assertEqual(mafwds[0].target, "test2@example.org")
@ -242,14 +241,14 @@ class MailboxManagerTest(TestCaseWithCeleryTasks):
md = MailDomain.objects.create(domain="example.org")
address = MailAddress.objects.create(localpart="test", domain=md)
mailboxes = Mailbox.objects.unused_or_own(address, self.user)
self.assertQuerysetEqual(mailboxes, [])
self.assertQuerySetEqual(mailboxes, [])
def test_unused_or_own_unassigned(self):
md = MailDomain.objects.create(domain="example.org")
address = MailAddress.objects.create(localpart="test", domain=md)
mailboxes = [Mailbox.objects.create_mailbox(self.user) for _ in range(2)]
assignable = Mailbox.objects.unused_or_own(address, self.user)
self.assertQuerysetEqual(
self.assertQuerySetEqual(
assignable, [repr(mb) for mb in mailboxes], transform=repr
)
@ -259,7 +258,7 @@ class MailboxManagerTest(TestCaseWithCeleryTasks):
mailboxes = [Mailbox.objects.create_mailbox(self.user) for _ in range(2)]
address.set_mailbox(mailboxes[0])
assignable = Mailbox.objects.unused_or_own(address, self.user)
self.assertQuerysetEqual(
self.assertQuerySetEqual(
assignable, [repr(mb) for mb in mailboxes], transform=repr
)
@ -270,16 +269,16 @@ class MailboxManagerTest(TestCaseWithCeleryTasks):
mailboxes = [Mailbox.objects.create_mailbox(self.user) for _ in range(2)]
address2.set_mailbox(mailboxes[0])
assignable = Mailbox.objects.unused_or_own(address, self.user)
self.assertQuerysetEqual(assignable, [repr(mailboxes[1])], transform=repr)
self.assertQuerySetEqual(assignable, [repr(mailboxes[1])], transform=repr)
def test_unused_fresh(self):
mailboxes = Mailbox.objects.unused(self.user)
self.assertQuerysetEqual(mailboxes, [])
self.assertQuerySetEqual(mailboxes, [])
def test_unused_unassigned(self):
mailbox = Mailbox.objects.create_mailbox(self.user)
mailboxes = Mailbox.objects.unused(self.user)
self.assertQuerysetEqual(mailboxes, [repr(mailbox)], transform=repr)
self.assertQuerySetEqual(mailboxes, [repr(mailbox)], transform=repr)
def test_unused_assigned(self):
md = MailDomain.objects.create(domain="example.org")
@ -287,7 +286,7 @@ class MailboxManagerTest(TestCaseWithCeleryTasks):
mailboxes = [Mailbox.objects.create_mailbox(self.user) for _ in range(2)]
address.set_mailbox(mailboxes[0])
assignable = Mailbox.objects.unused(self.user)
self.assertQuerysetEqual(assignable, [repr(mailboxes[1])], transform=repr)
self.assertQuerySetEqual(assignable, [repr(mailboxes[1])], transform=repr)
def test_create_mailbox_no_password(self):
mailbox = Mailbox.objects.create_mailbox(self.user)

View file

@ -166,7 +166,7 @@ class DeleteSshPublicKeyTest(HostingPackageAwareTestMixin, TestCase):
kwargs={"package": str(self.package.pk), "pk": str(self.sshkey.pk)},
)
queryset = view.get_queryset()
self.assertQuerysetEqual(queryset, [repr(self.sshkey)], transform=repr)
self.assertQuerySetEqual(queryset, [repr(self.sshkey)], transform=repr)
def test_get_context_data(self):
self.client.login(username=TEST_USER, password=TEST_PASSWORD)
@ -234,7 +234,7 @@ class EditSshPublicKeyCommentTest(HostingPackageAwareTestMixin, TransactionTestC
kwargs={"package": str(self.package.pk), "pk": str(self.sshkey.pk)},
)
queryset = view.get_queryset()
self.assertQuerysetEqual(queryset, [repr(self.sshkey)], transform=repr)
self.assertQuerySetEqual(queryset, [repr(self.sshkey)], transform=repr)
def test_get_form_kwargs(self):
self.client.login(username=TEST_USER, password=TEST_PASSWORD)

View file

@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gnuviechadmin taskresults\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-04-16 22:07+0200\n"
"PO-Revision-Date: 2023-04-16 18:21+0200\n"
"POT-Creation-Date: 2023-07-22 19:45+0200\n"
"PO-Revision-Date: 2023-07-22 19:57+0200\n"
"Last-Translator: Jan Dittberner <jan@dittberner.info>\n"
"Language-Team: Jan Dittberner <jan@dittberner.info>\n"
"Language: de\n"
@ -19,38 +19,38 @@ msgstr ""
"X-Generator: Poedit 3.2.2\n"
"X-Poedit-SourceCharset: UTF-8\n"
#: taskresults/models.py:23
#: taskresults/models.py:24
msgid "Task id"
msgstr "Task-Id"
#: taskresults/models.py:24
#: taskresults/models.py:25
msgid "Task signature"
msgstr "Tasksignatur"
#: taskresults/models.py:25
#: taskresults/models.py:26
msgid "Task creator"
msgstr "Taskersteller"
#: taskresults/models.py:26
#: taskresults/models.py:27
msgid "Task notes"
msgstr "Tasknotizen"
#: taskresults/models.py:27 taskresults/models.py:34
#: taskresults/models.py:28 taskresults/models.py:35
msgid "Task result"
msgstr "Taskergebnis"
#: taskresults/models.py:29
#: taskresults/models.py:30
msgid "Task state"
msgstr "Taskstatus"
#: taskresults/models.py:35
#: taskresults/models.py:36
msgid "Task results"
msgstr "Taskergebnisse"
#: taskresults/models.py:42
#: taskresults/models.py:43
msgid "yes"
msgstr "ja"
#: taskresults/models.py:42
#: taskresults/models.py:43
msgid "no"
msgstr "nein"

View file

@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: websites gnuviechadmin app\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-04-16 22:07+0200\n"
"PO-Revision-Date: 2023-04-16 18:21+0200\n"
"POT-Creation-Date: 2023-07-22 19:45+0200\n"
"PO-Revision-Date: 2023-07-22 19:57+0200\n"
"Last-Translator: Jan Dittberner <jan@dittberner.info>\n"
"Language-Team: Jan Dittberner <jan@dittberner.info>\n"
"Language: de\n"
@ -31,27 +31,27 @@ msgstr "Webauftritt anlegen"
msgid "There is already a website for this subdomain"
msgstr "Es gibt bereits einen Webauftritt mit dieser Subdomain"
#: websites/models.py:32
#: websites/models.py:20
msgid "sub domain"
msgstr "Subdomain"
#: websites/models.py:34
#: websites/models.py:22
msgid "operating system user"
msgstr "Betriebssystemnutzer"
#: websites/models.py:36
#: websites/models.py:24
msgid "domain"
msgstr "Domain"
#: websites/models.py:37
#: websites/models.py:25
msgid "wildcard"
msgstr "Wildcard"
#: websites/models.py:41
#: websites/models.py:29
msgid "website"
msgstr "Webauftritt"
#: websites/models.py:42
#: websites/models.py:30
msgid "websites"
msgstr "Webauftritte"

2076
poetry.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,30 +1,31 @@
[tool.poetry]
name = "gva"
version = "0.13.0"
version = "0.15.1"
description = "gnuviechadmin web interface"
authors = ["Jan Dittberner <jan@dittberner.info>"]
license = "AGPL-3+"
readme = "README.md"
readme = "README.rst"
[tool.poetry.dependencies]
python = "^3.7"
django = "<4"
python = "^3.8"
django = "^4.2"
psycopg2-binary = "^2.9"
celery = "^5.2.7"
django-allauth = "^0.52.0"
django-allauth = "^0.60.0"
django-crispy-forms = "^2.0"
django-debug-toolbar = "^3.8"
django-debug-toolbar = "^4.2"
django-model-utils = "^4.1"
gvacommon = {version = "^0.6.0", source = "gnuviech"}
gvacommon = {version = "^0.7.0", source = "gnuviech"}
passlib = "^1.7.4"
redis = "^4.5.1"
redis = "^5.0.1"
requests-oauthlib = "^1.3.1"
django-impersonate = "^1.9.1"
djangorestframework = "^3.14.0"
markdown = "^3.4.3"
django-filter = "^23.1"
crispy-bootstrap5 = "^0.7"
crispy-bootstrap5 = "^2023.10"
python-magic = "^0.4.27"
isort = "^5.12.0"
[tool.poetry.group.dev.dependencies]
@ -34,16 +35,21 @@ releases = "^2.0.0"
sphinxcontrib-blockdiag = "^3.0.0"
pylama = "^8.4.1"
black = {extras = ["d"], version = "^23.3.0"}
isort = "<5"
[[tool.poetry.source]]
name = "gnuviech"
url = "https://pypi.gnuviech-server.de/simple"
default = false
secondary = false
priority = "explicit"
[tool.isort]
profile = "black"
known_django = ["django","model_utils"]
known_drf = ["rest_framework"]
known_celery = ["celery"]
sections = ["FUTURE", "STDLIB", "DJANGO", "DRF", "CELERY", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"]
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"