see #951
This commit is contained in:
Antoine Nguyen
2016-10-28 15:48:08 +02:00
parent 3fb1c13f18
commit d1b59cc312
14 changed files with 208 additions and 80 deletions
+1 -1
View File
@@ -38,7 +38,7 @@ before_script:
script:
- python ./tests.py
- cd test_project
- coverage run --source ../modoboa manage.py test modoboa.core modoboa.lib modoboa.admin.tests modoboa.limits modoboa.relaydomains
- coverage run --source ../modoboa manage.py test modoboa.core modoboa.admin.tests modoboa.limits modoboa.relaydomains
after_success:
- codecov
+1 -8
View File
@@ -15,14 +15,7 @@ from . import constants
def enabled_applications():
"""Return the list of installed extensions.
We check if the table exists before trying to fetch activated
extensions because the admin module is always imported by Django,
even before the database exists (example: the first ``syncdb``).
:return: a list
"""
"""Return the list of installed extensions."""
from modoboa.core.extensions import exts_pool
result = [("user", "user")]
+3 -1
View File
@@ -4,6 +4,8 @@
* @constructor
* @param {Object} options - instance options
*/
/* global TwocolsNav */
var Settings = function(options) {
TwocolsNav.call(this, options);
};
@@ -40,7 +42,7 @@ Settings.prototype = {
* Navigation callback: extensions.
*/
extensions_callback: function(data) {
this.select_left_menu();
this.select_left_menu();
if (data.content) {
$('#' + this.options.divid).html(data.content);
}
+2 -3
View File
@@ -1,8 +1,7 @@
{% load i18n lib_tags %}
<h2>{% trans "Parameters" %} <small>{% trans "Configure Modoboa" %}</small></h2><hr />
<form id="settings_form" method="POST" action="{% url 'core:parameter_save' %}" class="form-horizontal">{% csrf_token %}
<form id="parameters_form" method="POST" action="{% url 'core:parameters' %}" class="form-horizontal">{% csrf_token %}
{% include "parameters/base_form.html" %}
<hr>
<button type="submit" class="btn btn-primary col-xs-offset-4 col-xs-4" id="update" name="update">{% trans 'Save' %}</button>
<button type="submit" class="btn btn-primary col-xs-offset-4 col-xs-4" name="update">{% trans 'Save' %}</button>
</form>
@@ -3,11 +3,11 @@
<hr>
{% if forms %}
<form class="form-horizontal" method="POST" action="{% url 'core:user_preferences' %}"
id="uprefs_form">
id="parameters_form">
{% csrf_token %}
{% include "parameters/base_form.html" %}
<hr>
<button type="submit" class="btn btn-primary col-xs-offset-4 col-xs-4" id="update" name="update">{% trans 'Save' %}</button>
<button type="submit" class="btn btn-primary col-xs-offset-4 col-xs-4" name="update">{% trans 'Save' %}</button>
</form>
{% else %}
<div class="alert alert-info">
-38
View File
@@ -57,44 +57,6 @@ class ChangeDefaultAdminTestCase(TestCase):
self.client.login(username="modoadmin", password="password"))
class DashboardTestCase(ModoTestCase):
"""Dashboard tests."""
@classmethod
def setUpTestData(cls):
"""Create some data."""
super(DashboardTestCase, cls).setUpTestData()
cls.dadmin = factories.UserFactory(
username="admin@test.com", groups=("DomainAdmins",)
)
cls.user = factories.UserFactory(
username="user@test.com", groups=("SimpleUsers",)
)
def test_access(self):
"""Load dashboard."""
url = reverse("core:dashboard")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertIn("Latest news", response.content)
self.client.logout()
self.client.login(username=self.dadmin.username, password="toto")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertIn("Latest news", response.content)
self.client.logout()
self.client.login(username=self.user.username, password="toto")
response = self.client.get(url)
self.assertEqual(response.status_code, 302)
def test_root_dispatch(self):
"""Check root dispatching."""
url = reverse("core:root")
response = self.client.get(url)
self.assertEqual(response.status_code, 302)
self.assertTrue(response.url.endswith(reverse("core:dashboard")))
@skipIf(NO_LDAP, 'No ldap module installed')
class LDAPTestCaseMixin(object):
"""Set of methods used to test LDAP features."""
+162
View File
@@ -0,0 +1,162 @@
"""Core views test cases."""
from testfixtures import compare
from django import forms
from django.core.urlresolvers import reverse
from modoboa.lib.tests import ModoTestCase
from modoboa.parameters import forms as param_forms
from modoboa.parameters import tools as param_tools
from .. import factories
from .. import models
class DashboardTestCase(ModoTestCase):
"""Dashboard tests."""
@classmethod
def setUpTestData(cls):
"""Create some data."""
super(DashboardTestCase, cls).setUpTestData()
cls.dadmin = factories.UserFactory(
username="admin@test.com", groups=("DomainAdmins",)
)
cls.user = factories.UserFactory(
username="user@test.com", groups=("SimpleUsers",)
)
def test_access(self):
"""Load dashboard."""
url = reverse("core:dashboard")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertIn("Latest news", response.content)
self.client.logout()
self.client.login(username=self.dadmin.username, password="toto")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertIn("Latest news", response.content)
self.client.logout()
self.client.login(username=self.user.username, password="toto")
response = self.client.get(url)
self.assertEqual(response.status_code, 302)
def test_root_dispatch(self):
"""Check root dispatching."""
url = reverse("core:root")
response = self.client.get(url)
self.assertEqual(response.status_code, 302)
self.assertTrue(response.url.endswith(reverse("core:dashboard")))
class SettingsTestCase(ModoTestCase):
"""Settings tests."""
settings_sample = {
"core-check_new_versions": "True",
"core-log_maximum_age": "365",
"core-ldap_auth_method": "searchbind",
"limits-deflt_domain_mailbox_aliases_limit": "0",
"core-send_statistics": "True",
"core-default_top_redirection": "user",
"core-sender_address": "noreply@modoboa.org",
"admin-auto_account_removal": "False",
"core-ldap_server_port": "389",
"core-rounds_number": "70000",
"core-ldap_bind_password": "",
"limits-deflt_domain_domain_aliases_limit": "0",
"core-secret_key": ":?j3]QPWo!.'_c4n",
"relaydomains-master_cf_path": "/etc/postfix/master.cf",
"limits-deflt_domain_domain_admins_limit": "0",
"limits-enable_admin_limits": "True",
"core-ldap_bind_dn": "",
"core-enable_api_communication": "True",
"limits-deflt_user_domain_admins_limit": "0",
"limits-enable_domain_limits": "False",
"csrfmiddlewaretoken": "SGgMVZsA4TPqoiV786TMST6xgOlhAf4F",
"limits-deflt_user_mailboxes_limit": "0",
"core-password_scheme": "sha512crypt",
"core-items_per_page": "30",
"limits-deflt_user_mailbox_aliases_limit": "0",
"limits-deflt_domain_mailboxes_limit": "0",
"core-ldap_is_active_directory": "False",
"core-ldap_group_type": "posixgroup",
"limits-deflt_user_domain_aliases_limit": "0",
"core-top_notifications_check_interval": "30",
"core-ldap_password_attribute": "userPassword",
"admin-auto_create_domain_and_mailbox": "True",
"admin-enable_dnsbl_checks": "True",
"admin-default_domain_quota": "0",
"core-ldap_server_address": "localhost",
"core-authentication_type": "local",
"core-ldap_admin_groups": "",
"core-ldap_groups_search_base": "",
"admin-enable_mx_checks": "True",
"core-ldap_search_base": "",
"admin-valid_mxs": "",
"limits-deflt_user_domains_limit": "0",
"core-ldap_search_filter": "(mail=%(user)s)",
"core-ldap_secured": "False",
"core-ldap_user_dn_template": ""
}
def test_get_settings(self):
"""Test settings display."""
url = reverse("core:parameters")
response = self.ajax_get(url)
for app in ["core", "admin", "limits", "relaydomains"]:
self.assertIn('data-app="{}"'.format(app), response["content"])
def test_save_settings(self):
"""Test settings save."""
url = reverse("core:parameters")
response = self.client.post(url, self.settings_sample, format="json")
self.assertEqual(response.status_code, 200)
self.settings_sample["core-rounds_number"] = ""
response = self.client.post(url, self.settings_sample, format="json")
self.assertEqual(response.status_code, 400)
compare(response.json(), {
"form_errors": {"rounds_number": ["This field is required."]},
"prefix": "core"
})
class UserSettings(param_forms.UserParametersForm):
"""Stupid user settings form."""
app = "core"
test = forms.CharField()
class PreferencesTestCase(ModoTestCase):
"""Test user preferences."""
@classmethod
def setUpClass(cls):
"""Register user form."""
super(PreferencesTestCase, cls).setUpClass()
param_tools.registry.add("user", UserSettings, "Test")
@classmethod
def tearDownClass(cls):
"""Remove user class."""
super(PreferencesTestCase, cls).tearDownClass()
del param_tools.registry._registry["user"]["core"]
def test_get_preferences(self):
"""Test preferences display."""
url = reverse("core:user_preferences")
self.ajax_get(url)
def test_save_preferences(self):
"""Test preferences save."""
url = reverse("core:user_preferences")
response = self.client.post(url, {"core-test": "toto"}, format="json")
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), "Preferences saved")
admin = models.User.objects.get(username="admin")
self.assertEqual(admin.parameters.get_value("test"), "toto")
+2 -4
View File
@@ -17,10 +17,8 @@ urlpatterns = patterns(
url(r'^core/$', 'modoboa.core.views.admin.viewsettings',
name="index"),
url(r'^core/parameters/$', 'modoboa.core.views.admin.viewparameters',
name="parameter_listl"),
url(r'^core/parameters/save/$', 'modoboa.core.views.admin.saveparameters',
name="parameter_save"),
url(r'^core/parameters/$', 'modoboa.core.views.admin.parameters',
name="parameters"),
url(r'^core/info/$', 'modoboa.core.views.admin.information',
name="information"),
url(r'^core/logs/$', 'modoboa.core.views.admin.logs', name="log_list"),
+16 -20
View File
@@ -28,7 +28,22 @@ def viewsettings(request, tplname='core/settings_header.html'):
@login_required
@user_passes_test(lambda u: u.is_superuser)
def viewparameters(request, tplname='core/parameters.html'):
def parameters(request, tplname="core/parameters.html"):
"""View to display and save global parameters."""
if request.method == "POST":
forms = param_tools.registry.get_forms(
"global", request.POST, localconfig=request.localconfig)
for formdef in forms:
form = formdef["form"]
if form.is_valid():
form.save()
form.to_django_settings()
continue
return render_to_json_response(
{"form_errors": form.errors, "prefix": form.app}, status=400
)
request.localconfig.save()
return render_to_json_response(_("Parameters saved"))
return render_to_json_response({
"left_selection": "parameters",
"content": _render_to_string(request, tplname, {
@@ -38,25 +53,6 @@ def viewparameters(request, tplname='core/parameters.html'):
})
@login_required
@user_passes_test(lambda u: u.is_superuser)
def saveparameters(request):
"""Save all parameters."""
forms = param_tools.registry.get_forms(
"global", request.POST, localconfig=request.localconfig)
for formdef in forms:
form = formdef["form"]
if form.is_valid():
form.save()
form.to_django_settings()
continue
return render_to_json_response(
{'form_errors': form.errors, 'prefix': form.app}, status=400
)
request.localconfig.save()
return render_to_json_response(_("Parameters saved"))
@login_required
@user_passes_test(lambda u: u.is_superuser)
def information(request, tplname="core/information.html"):
+1 -2
View File
@@ -232,9 +232,8 @@ class DynamicForm(object):
class TabForms(object):
"""
Simple forms container
Simple forms container.
This class tries to encapsulate multiple forms that will be
displayed using tabs. It is different from a classical formset
@@ -1,7 +1,7 @@
{% load form_tags %}
<div class="tabbable">
<ul class="nav nav-tabs">
{% for form in forms %}<li{% if forloop.first %} class="active"{% endif %}><a href="#{{ forloop.counter }}" data-toggle="tab">{{ form.label }}</a></li>
{% for form in forms %}<li{% if forloop.first %} class="active"{% endif %}><a href="#{{ forloop.counter }}" data-app="{{ form.app }}" data-toggle="tab">{{ form.label }}</a></li>
{% endfor %}
</ul>
<div class="tab-content">{% for form in forms %}
+1
View File
@@ -63,6 +63,7 @@ class Registry(object):
if not data["formclass"].has_access(**kwargs):
continue
result.append({
"app": app,
"label": data["label"],
"form": data["formclass"](*args, **kwargs)
})
+14
View File
@@ -3,6 +3,9 @@
*
* @constructor
*/
/* global Listing */
var TwocolsNav = function(options) {
Listing.call(this, options);
};
@@ -33,6 +36,17 @@ TwocolsNav.prototype = {
success_cb: $.proxy(this.save_cb, this)
});
}, this));
$(document).on('submit', '#parameters_form', function(evt) {
simple_ajax_form_post(evt, {
formid: $(this).attr('id'),
modal: false,
reload_on_success: false,
success_cb: $.proxy(this.save_cb, this),
error_cb: function(data) {
$('a[data-app="' + data.prefix + '"]').tab('show');
}
});
});
},
update_content: function(data) {
+2
View File
@@ -60,6 +60,7 @@ MODOBOA_APPS = (
'modoboa.admin',
'modoboa.limits',
'modoboa.relaydomains',
'modoboa.parameters',
)
INSTALLED_APPS += MODOBOA_APPS
@@ -75,6 +76,7 @@ MIDDLEWARE_CLASSES = (
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'modoboa.core.middleware.LocalConfigMiddleware',
'modoboa.lib.middleware.AjaxLoginRedirect',
'modoboa.lib.middleware.CommonExceptionCatcher',
'modoboa.lib.middleware.RequestCatcherMiddleware',