diff --git a/frontend/src/components/parameters/ParametersForm.vue b/frontend/src/components/parameters/ParametersForm.vue index d5cc3dbc3..cb7a3577b 100644 --- a/frontend/src/components/parameters/ParametersForm.vue +++ b/frontend/src/components/parameters/ParametersForm.vue @@ -18,18 +18,11 @@ > - - - + + @@ -69,20 +71,30 @@ export default { } }, created () { - var app = this.$route.params.app - api.getParametersStructure(app).then(response => { - this.structure = response.data - }) - api.getParametersForApplication(app).then(response => { - this.parameters = response.data - }) + this.loadParams(this.$route.params.app) + }, + watch: { + '$route' (to, from) { + this.loadParams(to.params.app) + } }, methods: { + loadParams (app) { + api.getParametersStructure(app).then(response => { + this.structure = response.data + }) + api.getParametersForApplication(app).then(response => { + this.parameters = response.data + }) + }, display (element) { if (element.display === '') { return true } var [field, value] = element.display.split('=') + if (value === 'true' || value === 'false') { + value = Boolean(value) + } return this.parameters[field] === value }, save () { diff --git a/modoboa/admin/app_settings.py b/modoboa/admin/app_settings.py index cd1afa3e7..46f9d0801 100644 --- a/modoboa/admin/app_settings.py +++ b/modoboa/admin/app_settings.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals +import collections import os from django import forms @@ -14,6 +15,7 @@ from django.utils.translation import ugettext as _, ugettext_lazy from modoboa.lib.form_utils import SeparatorField, YesNoField from modoboa.lib.sysutils import exec_cmd from modoboa.parameters import forms as param_forms + from . import constants @@ -227,9 +229,121 @@ class AdminParametersForm(param_forms.AdminParametersForm): return cleaned_data +GLOBAL_PARAMETERS_STRUCT = collections.OrderedDict([ + ("domains", { + "label": ugettext_lazy("Domains"), + "params": collections.OrderedDict([ + ("enable_mx_checks", { + "label": ugettext_lazy("Enable MX checks"), + "help_text": ugettext_lazy( + "Check that every domain has a valid MX record" + ) + }), + ("valid_mxs", { + "label": ugettext_lazy("Valid MXs"), + "display": "enable_mx_checks=true", + "help_text": ugettext_lazy( + "A list of IP or network address every MX record should " + "match. A warning will be sent if a record does not " + "respect it." + ), + }), + ("domains_must_have_authorized_mx", { + "label": ugettext_lazy("New domains must use authorized MX(s)"), + "display": "enable_mx_checks=true", + "help_text": ugettext_lazy( + "Prevent the creation of a new domain if its MX record " + "does not use one of the defined addresses." + ) + }), + ("enable_dnsbl_checks", { + "label": ugettext_lazy("Enable DNSBL checks"), + "help_text": ugettext_lazy( + "Check every domain against major DNSBL providers" + ) + }), + ("dkim_keys_storage_dir", { + "label": ugettext_lazy("DKIM keys storage directory"), + "help_text": ugettext_lazy( + "Absolute path of the directory where DKIM private keys " + "will be stored. Make sure this directory belongs to root " + "user and is not readable by the outside world." + ), + }), + ("dkim_default_key_length", { + "label": ugettext_lazy("Default DKIM key length"), + "help_text": ugettext_lazy( + "Default length in bits for newly generated DKIM keys." + ) + }), + ]) + }), + ("mailboxes", { + "label": ugettext_lazy("Mailboxes"), + "params": collections.OrderedDict([ + ("handle_mailboxes", { + "label": ugettext_lazy("Handle mailboxes on filesystem"), + "help_text": ugettext_lazy( + "Rename or remove mailboxes on the filesystem when they get" + " renamed or removed within Modoboa" + ) + }), + ("mailboxes_owner", { + "label": ugettext_lazy("Mailboxes owner"), + "display": "handle_mailboxes=true", + "help_text": ugettext_lazy( + "The UNIX account who owns mailboxes on the filesystem" + ) + }), + ("default_domain_quota", { + "label": ugettext_lazy("Default domain quota"), + "help_text": ugettext_lazy( + "Default quota (in MB) applied to freshly created domains " + "with no value specified. A value of 0 means no quota." + ) + }), + ("default_mailbox_quota", { + "label": ugettext_lazy("Default mailbox quota"), + "help_text": ugettext_lazy( + "Default mailbox quota (in MB) applied to freshly created " + "domains with no value specified. A value of 0 means no " + "quota." + ) + }), + ("auto_account_removal", { + "label": ugettext_lazy("Automatic account removal"), + "help_text": ugettext_lazy( + "When a mailbox is removed, also remove the associated " + "account") + }), + ("auto_create_domain_and_mailbox", { + "label": ugettext_lazy("Automatic domain/mailbox creation"), + "help_text": ugettext_lazy( + "Create a domain and a mailbox when an account is " + "automatically created." + ) + }), + ("create_alias_on_mbox_rename", { + "label": ugettext_lazy( + "Create an alias when a mailbox is renamed"), + "help_text": ugettext_lazy( + "Create an alias using the old address when a mailbox is " + "renamed." + ) + }) + ]) + }), +]) + + def load_admin_settings(): """Load admin settings.""" from modoboa.parameters import tools as param_tools + from . import serializers param_tools.registry.add( "global", AdminParametersForm, ugettext_lazy("Administration")) + param_tools.registry.add2( + "global", "admin", ugettext_lazy("Administration"), + GLOBAL_PARAMETERS_STRUCT, + serializers.AdminGlobalParametersSerializer) diff --git a/modoboa/admin/serializers.py b/modoboa/admin/serializers.py index bb98f6d4c..52e764d20 100644 --- a/modoboa/admin/serializers.py +++ b/modoboa/admin/serializers.py @@ -22,7 +22,8 @@ from modoboa.lib import ( permissions ) from modoboa.parameters import tools as param_tools -from . import lib, models + +from . import constants, lib, models class DomainSerializer(serializers.ModelSerializer): @@ -443,3 +444,25 @@ class SenderAddressSerializer(serializers.ModelSerializer): raise serializers.ValidationError( _("You don't have access to this mailbox.")) return value + + +class AdminGlobalParametersSerializer(serializers.Serializer): + """A serializer for global parameters.""" + + # Domain settings + enable_mx_checks = serializers.BooleanField(default=True) + valid_mxs = serializers.CharField(allow_blank=True) + domains_must_have_authorized_mx = serializers.BooleanField(default=False) + enable_dnsbl_checks = serializers.BooleanField(default=True) + dkim_keys_storage_dir = serializers.CharField(allow_blank=True) + dkim_default_key_length = serializers.ChoiceField( + default=2048, choices=constants.DKIM_KEY_LENGTHS) + + # Mailboxes settings + handle_mailboxes = serializers.BooleanField(default=False) + mailboxes_owner = serializers.CharField(default="vmail") + default_domain_quota = serializers.IntegerField(default=0) + default_mailbox_quota = serializers.IntegerField(default=0) + auto_account_removal = serializers.BooleanField(default=False) + auto_create_domain_and_mailbox = serializers.BooleanField(default=True) + create_alias_on_mbox_rename = serializers.BooleanField(default=False) diff --git a/modoboa/core/app_settings.py b/modoboa/core/app_settings.py index 7e3fdb59b..494e58ec2 100644 --- a/modoboa/core/app_settings.py +++ b/modoboa/core/app_settings.py @@ -595,4 +595,71 @@ GLOBAL_PARAMETERS_STRUCT = collections.OrderedDict([ }), ]) }), + ("notifications", { + "label": ugettext_lazy("Notifications"), + "params": collections.OrderedDict([ + ("sender_address", { + "label": ugettext_lazy("Sender address"), + "help_text": ugettext_lazy( + "Email address used to send notifications.") + }) + ]) + }), + ("api", { + "label": ugettext_lazy("Public API"), + "params": collections.OrderedDict([ + ("enable_api_communication", { + "label": ugettext_lazy("Enable communication"), + "help_text": ugettext_lazy( + "Automatically checks if a newer version is available") + }), + ("check_new_versions", { + "label": ugettext_lazy("Check new versions"), + "display": "enable_api_communication=true", + "help_text": ugettext_lazy( + "Automatically checks if a newer version is available") + }), + ("send_statistics", { + "label": ugettext_lazy("Send statistics"), + "display": "enable_api_communication=true", + "help_text": ugettext_lazy( + "Send statistics to Modoboa public API " + "(counters and used extensions)") + }) + ]) + }), + ("misc", { + "label": ugettext_lazy("Miscellaneous"), + "params": collections.OrderedDict([ + ("inactive_account_threshold", { + "label": ugettext_lazy("Inactive account threshold"), + "help_text": ugettext_lazy( + "An account with a last login date greater than this " + "threshold (in days) will be considered as inactive" + ) + }), + ("top_notifications_check_interval", { + "label": ugettext_lazy("Top notifications check interval"), + "help_text": ugettext_lazy( + "Interval between two top notification checks (in seconds)" + ), + }), + ("log_maximum_age", { + "label": ugettext_lazy("Maximum log record age"), + "help_text": ugettext_lazy( + "The maximum age in days of a log record"), + }), + ("items_per_page", { + "label": ugettext_lazy("Items per page"), + "help_text": ugettext_lazy("Number of displayed items per page") + }), + ("default_top_redirection", { + "label": ugettext_lazy("Default top redirection"), + "help_text": ugettext_lazy( + "The default redirection used when no application is " + "specified" + ), + }) + ]) + }) ]) diff --git a/modoboa/limits/app_settings.py b/modoboa/limits/app_settings.py index 070a38f23..6e6f8e74a 100644 --- a/modoboa/limits/app_settings.py +++ b/modoboa/limits/app_settings.py @@ -4,6 +4,8 @@ from __future__ import unicode_literals +import collections + from django import forms from django.utils.translation import ugettext_lazy as _ @@ -149,3 +151,109 @@ class ParametersForm(param_forms.AdminParametersForm): "deflt_domain_domain_aliases_limit": "enable_domain_limits=True", "deflt_domain_domain_admins_limit": "enable_domain_limits=True", } + + +GLOBAL_PARAMETERS_STRUCT = collections.OrderedDict([ + ("per_admin_limits", { + "label": _("Default per-admin limits"), + "params": collections.OrderedDict([ + ("enable_admin_limits", { + "label": _("Enable per-admin limits"), + "help_text": _("Enable or disable per-admin limits") + }), + ("deflt_user_domain_admins_limit", { + "label": _("Domain admins"), + "display": "enable_admin_limits=true", + "help_text": _( + "Maximum number of allowed domain administrators for a new " + "administrator. (0 to deny any creation, -1 to allow " + "unlimited creations)" + ), + }), + ("deflt_user_domains_limit", { + "label": _("Domains"), + "display": "enable_admin_limits=true", + "help_text": _( + "Maximum number of allowed domains for a new administrator." + " (0 to deny any creation, -1 to allow unlimited creations)" + ), + }), + ("deflt_user_domain_aliases_limit", { + "label": _("Domain aliases"), + "display": "enable_admin_limits=true", + "help_text": _( + "Maximum number of allowed domain aliases for a new " + "administrator. (0 to deny any creation, -1 to allow " + "unlimited creations)" + ), + }), + ("deflt_user_mailboxes_limit", { + "label": _("Mailboxes"), + "display": "enable_admin_limits=true", + "help_text": _( + "Maximum number of allowed mailboxes for a new " + "administrator. (0 to deny any creation, -1 to allow " + "unlimited creations)" + ), + }), + ("deflt_user_mailbox_aliases_limit", { + "label": _("Mailbox aliases"), + "display": "enable_admin_limits=true", + "help_text": _( + "Maximum number of allowed aliases for a new administrator." + " (0 to deny any creation, -1 to allow unlimited creations)" + ), + }), + ("deflt_user_quota_limit", { + "label": _("Quota"), + "help_text": _( + "The quota a reseller will be allowed to share between the " + "domains he creates. (0 means no quota)" + ), + }) + ]) + }), + ("per_domain_limits", { + "label": _("Default per-domain limits"), + "params": collections.OrderedDict([ + ("enable_domain_limits", { + "label": _("Enable per-domain limits"), + "help_text": _("Enable or disable per-domain limits") + }), + ("deflt_domain_domain_admins_limit", { + "label": _("Domain admins"), + "display": "enable_domain_limits=true", + "help_text": _( + "Maximum number of allowed domain administrators for a new " + "domain. (0 to deny any creation, -1 to allow unlimited " + "creations)" + ), + }), + ("deflt_domain_domain_aliases_limit", { + "label": _("Domain aliases"), + "display": "enable_domain_limits=true", + "help_text": _( + "Maximum number of allowed domain aliases for a new " + "domain. (0 to deny any creation, -1 to allow " + "unlimited creations)" + ), + }), + ("deflt_domain_mailboxes_limit", { + "label": _("Mailboxes"), + "display": "enable_domain_limits=true", + "help_text": _( + "Maximum number of allowed mailboxes for a new domain. " + "(0 to deny any creation, -1 to allow unlimited creations)" + ), + }), + ("deflt_domain_mailbox_aliases_limit", { + "label": _("Mailbox aliases"), + "display": "enable_domain_limits=true", + "help_text": _( + "Maximum number of allowed aliases for a new domain. " + "(0 to deny any creation, -1 to allow unlimited creations)" + ), + }) + ]) + }) +]) diff --git a/modoboa/limits/apps.py b/modoboa/limits/apps.py index 7f066caf0..07c14facc 100644 --- a/modoboa/limits/apps.py +++ b/modoboa/limits/apps.py @@ -11,9 +11,14 @@ from django.utils.translation import ugettext as _ def load_limits_settings(): """Load settings.""" from modoboa.parameters import tools as param_tools - from .app_settings import ParametersForm + from . import app_settings + from . import serializers - param_tools.registry.add("global", ParametersForm, _("Limits")) + param_tools.registry.add("global", app_settings.ParametersForm, _("Limits")) + param_tools.registry.add2( + "global", "limits", _("Limits"), app_settings.GLOBAL_PARAMETERS_STRUCT, + serializers.LimitsGlobalParemetersSerializer + ) class LimitsConfig(AppConfig): diff --git a/modoboa/limits/serializers.py b/modoboa/limits/serializers.py index 9fa0cbd00..ba0aa9ae8 100644 --- a/modoboa/limits/serializers.py +++ b/modoboa/limits/serializers.py @@ -36,3 +36,23 @@ class ResourcesSerializer(serializers.Serializer): limit.max_value = max_value limit.save(update_fields=["max_value"]) return instance + + +class LimitsGlobalParemetersSerializer(serializers.Serializer): + """A serializer for global parameters.""" + + # Per-admin limits + enable_admin_limits = serializers.BooleanField(default=True) + deflt_user_domain_admins_limit = serializers.IntegerField(default=0) + deflt_user_domains_limit = serializers.IntegerField(default=0) + deflt_user_domain_aliases_limit = serializers.IntegerField(default=0) + deflt_user_mailboxes_limit = serializers.IntegerField(default=0) + deflt_user_mailbox_aliases_limit = serializers.IntegerField(default=0) + deflt_user_quota_limit = serializers.IntegerField(default=0) + + # Per-domain limits + enable_domain_limits = serializers.BooleanField(default=False) + deflt_domain_domain_admins_limit = serializers.IntegerField(default=0) + deflt_domain_domain_aliases_limit = serializers.IntegerField(default=0) + deflt_domain_mailboxes_limit = serializers.IntegerField(default=0) + deflt_domain_mailbox_aliases_limit = serializers.IntegerField(default=0) diff --git a/modoboa/parameters/serializers.py b/modoboa/parameters/serializers.py deleted file mode 100644 index 6594776f3..000000000 --- a/modoboa/parameters/serializers.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Parameters serializers.""" - -from rest_framework import serializers - -from . import tools - - -class ParametersSerializer(serializers.Serializer): - """Parameters serializer.""" - - def __init__(self, *args, **kwargs): - """Generate serializer.""" - app = kwargs.pop("app") - super(ParametersSerializer, self).__init__(*args, **kwargs) - for name, field in tools.registry.get_fields("global", app): - field.source = None - self.fields[name] = field diff --git a/modoboa/urls.py b/modoboa/urls.py index 650816886..672bf0310 100644 --- a/modoboa/urls.py +++ b/modoboa/urls.py @@ -28,7 +28,7 @@ urlpatterns = [ name="ckeditor_browse"), url("", include("modoboa.core.urls", namespace="core")), url("^user/forward/", forward, name="user_forward"), - url("admin/", include("modoboa.admin.urls", namespace="admin")), + url("^admin/", include("modoboa.admin.urls", namespace="admin")), # No namespace url(r'^accounts/password_reset/$', core_views.password_reset, name="password_reset"),