diff --git a/modoboa/admin/forms/forward.py b/modoboa/admin/forms/forward.py index 5e2db8509..00334f14a 100644 --- a/modoboa/admin/forms/forward.py +++ b/modoboa/admin/forms/forward.py @@ -28,7 +28,7 @@ def get_recipients(self): recipients = [] rawdata = self.cleaned_data["dest"].strip() if not rawdata: - return + return recipients for rcpt in rawdata.split(","): local_part, domname = split_mailbox(rcpt) if not local_part or not domname: diff --git a/modoboa/admin/management/commands/export_domains.py b/modoboa/admin/management/commands/export_domains.py index 0f1114619..c4fc5edc6 100644 --- a/modoboa/admin/management/commands/export_domains.py +++ b/modoboa/admin/management/commands/export_domains.py @@ -6,11 +6,10 @@ from modoboa.core import load_core_settings from modoboa.core.extensions import exts_pool -from modoboa.core.management.commands import CloseConnectionMixin from ...models import Domain -class Command(BaseCommand, CloseConnectionMixin): +class Command(BaseCommand): help = 'Export domains and domain aliases to a csv' option_list = BaseCommand.option_list + ( diff --git a/modoboa/admin/management/commands/export_identities.py b/modoboa/admin/management/commands/export_identities.py index 95551647e..6d3bb1f46 100644 --- a/modoboa/admin/management/commands/export_identities.py +++ b/modoboa/admin/management/commands/export_identities.py @@ -7,11 +7,10 @@ from modoboa.core import load_core_settings from modoboa.core.models import User from modoboa.core.extensions import exts_pool -from modoboa.core.management.commands import CloseConnectionMixin from ...models import Alias -class Command(BaseCommand, CloseConnectionMixin): +class Command(BaseCommand): help = 'Export identities (mailbox and aliases) to a csv' option_list = BaseCommand.option_list + ( diff --git a/modoboa/admin/management/commands/handle_mailbox_operations.py b/modoboa/admin/management/commands/handle_mailbox_operations.py index 6ba7daa2a..c2a8dee7b 100644 --- a/modoboa/admin/management/commands/handle_mailbox_operations.py +++ b/modoboa/admin/management/commands/handle_mailbox_operations.py @@ -5,7 +5,6 @@ from django.core.management.base import BaseCommand -from modoboa.core.management.commands import CloseConnectionMixin from modoboa.lib import parameters from modoboa.lib.sysutils import exec_cmd from modoboa.lib.exceptions import InternalError @@ -18,7 +17,7 @@ class OperationError(Exception): pass -class Command(BaseCommand, CloseConnectionMixin): +class Command(BaseCommand): help = 'Handles rename and delete operations on mailboxes' option_list = BaseCommand.option_list + ( diff --git a/modoboa/admin/management/commands/import_domains.py b/modoboa/admin/management/commands/import_domains.py index a4669da14..aafbbe2ef 100644 --- a/modoboa/admin/management/commands/import_domains.py +++ b/modoboa/admin/management/commands/import_domains.py @@ -5,12 +5,11 @@ from modoboa.core import load_core_settings from modoboa.core.extensions import exts_pool -from modoboa.core.management.commands import CloseConnectionMixin from ._import import import_csv -class Command(BaseCommand, CloseConnectionMixin): +class Command(BaseCommand): args = 'csvfile' help = 'Import domains and domain aliases from a csv file' diff --git a/modoboa/admin/management/commands/import_identities.py b/modoboa/admin/management/commands/import_identities.py index 1cf710bd7..28487bc61 100644 --- a/modoboa/admin/management/commands/import_identities.py +++ b/modoboa/admin/management/commands/import_identities.py @@ -4,12 +4,11 @@ from modoboa.core import load_core_settings from modoboa.core.extensions import exts_pool -from modoboa.core.management.commands import CloseConnectionMixin from ._import import import_csv -class Command(BaseCommand, CloseConnectionMixin): +class Command(BaseCommand): args = 'csvfile' help = 'Import identities from a csv file' diff --git a/modoboa/admin/models/domain.py b/modoboa/admin/models/domain.py index 3021e7281..848e0cf06 100644 --- a/modoboa/admin/models/domain.py +++ b/modoboa/admin/models/domain.py @@ -22,8 +22,8 @@ def get_for_admin(self, admin): to fill ``ModelChoiceField`` objects. """ if admin.is_superuser: - return self.get_query_set() - return self.get_query_set().filter(owners__user=admin) + return self.get_queryset() + return self.get_queryset().filter(owners__user=admin) class Domain(AdminObject): diff --git a/modoboa/admin/models/mailbox.py b/modoboa/admin/models/mailbox.py index 275291fb5..a66dbaea2 100644 --- a/modoboa/admin/models/mailbox.py +++ b/modoboa/admin/models/mailbox.py @@ -66,7 +66,7 @@ def get_for_admin(self, admin, squery=None): qf = Q(pk__in=ids) & qf else: qf = Q(pk__in=ids) - return self.get_query_set().select_related().filter(qf) + return self.get_queryset().select_related().filter(qf) class Mailbox(AdminObject): diff --git a/modoboa/admin/tests/account.py b/modoboa/admin/tests/account.py index 1b82547e7..24397ba04 100644 --- a/modoboa/admin/tests/account.py +++ b/modoboa/admin/tests/account.py @@ -9,8 +9,10 @@ class AccountTestCase(ModoTestCase): - def setUp(self): - super(AccountTestCase, self).setUp() + @classmethod + def setUpTestData(cls): + """Create test data.""" + super(AccountTestCase, cls).setUpTestData() factories.populate_database() def test_crud(self): @@ -115,13 +117,15 @@ def test_master_user(self): class PermissionsTestCase(ModoTestCase): - def setUp(self): - super(PermissionsTestCase, self).setUp() + @classmethod + def setUpTestData(cls): + """Create test data.""" + super(PermissionsTestCase, cls).setUpTestData() factories.populate_database() - self.user = User.objects.get(username='user@test.com') - self.values = dict( - username=self.user.username, role="DomainAdmins", - is_active=self.user.is_active, email="user@test.com", + cls.user = User.objects.get(username='user@test.com') + cls.values = dict( + username=cls.user.username, role="DomainAdmins", + is_active=cls.user.is_active, email="user@test.com", quota_act=True ) diff --git a/modoboa/admin/tests/alias.py b/modoboa/admin/tests/alias.py index d6aab928f..5e5636dfb 100644 --- a/modoboa/admin/tests/alias.py +++ b/modoboa/admin/tests/alias.py @@ -13,8 +13,10 @@ class AliasTestCase(ModoTestCase): - def setUp(self): - super(AliasTestCase, self).setUp() + @classmethod + def setUpTestData(cls): + """Create test data.""" + super(AliasTestCase, cls).setUpTestData() factories.populate_database() def test_alias(self): diff --git a/modoboa/admin/tests/domain.py b/modoboa/admin/tests/domain.py index d175e61ba..bc909015f 100644 --- a/modoboa/admin/tests/domain.py +++ b/modoboa/admin/tests/domain.py @@ -16,8 +16,10 @@ class DomainTestCase(ModoTestCase): """Test case for Domain.""" - def setUp(self): - super(DomainTestCase, self).setUp() + @classmethod + def setUpTestData(cls): + """Create test data.""" + super(DomainTestCase, cls).setUpTestData() factories.populate_database() def test_create(self): diff --git a/modoboa/admin/tests/domain_alias.py b/modoboa/admin/tests/domain_alias.py index f99fa32c8..38c4d5ef5 100644 --- a/modoboa/admin/tests/domain_alias.py +++ b/modoboa/admin/tests/domain_alias.py @@ -8,10 +8,12 @@ class DomainAliasTestCase(ModoTestCase): - def setUp(self): - super(DomainAliasTestCase, self).setUp() + @classmethod + def setUpTestData(cls): + """Create test data.""" + super(DomainAliasTestCase, cls).setUpTestData() factories.populate_database() - self.dom = Domain.objects.get(name='test.com') + cls.dom = Domain.objects.get(name='test.com') def test_model(self): dom = Domain.objects.get(name="test.com") diff --git a/modoboa/admin/tests/export.py b/modoboa/admin/tests/export.py index f6d35d521..65801db61 100644 --- a/modoboa/admin/tests/export.py +++ b/modoboa/admin/tests/export.py @@ -11,14 +11,16 @@ class ExportTestCase(ModoTestCase): """Test case for export operations.""" - def setUp(self): - super(ExportTestCase, self).setUp() + @classmethod + def setUpTestData(cls): + """Create test data.""" + super(ExportTestCase, cls).setUpTestData() factories.populate_database() def __export_identities(self, idtfilter="", grpfilter=""): self.clt.get( - reverse("admin:_identity_list") - + "?grpfilter=%s&idtfilter=%s" % (grpfilter, idtfilter) + reverse("admin:_identity_list") + + "?grpfilter=%s&idtfilter=%s" % (grpfilter, idtfilter) ) return self.clt.post( reverse("admin:identity_export"), diff --git a/modoboa/admin/tests/import_.py b/modoboa/admin/tests/import_.py index efbb43650..5dacc52f1 100644 --- a/modoboa/admin/tests/import_.py +++ b/modoboa/admin/tests/import_.py @@ -11,8 +11,10 @@ class ImportTestCase(ModoTestCase): - def setUp(self): - super(ImportTestCase, self).setUp() + @classmethod + def setUpTestData(cls): + """Create test data.""" + super(ImportTestCase, cls).setUpTestData() factories.populate_database() def test_domains_import(self): @@ -180,7 +182,7 @@ def test_import_missing_quota(self): f = ContentFile(b""" account; user1@test.com; toto; User; One; True; SimpleUsers; user1@test.com """, name="identities.csv") - resp = self.clt.post( + self.clt.post( reverse("admin:identity_import"), {"sourcefile": f, "crypt_password": True} ) diff --git a/modoboa/admin/tests/password_schemes.py b/modoboa/admin/tests/password_schemes.py index 605d90c64..5d3fc5a71 100644 --- a/modoboa/admin/tests/password_schemes.py +++ b/modoboa/admin/tests/password_schemes.py @@ -9,11 +9,16 @@ class PasswordSchemesTestCase(ModoTestCase): - fixtures = ['initial_users.json'] + + @classmethod + def setUpTestData(cls): + """Create test data.""" + super(PasswordSchemesTestCase, cls).setUpTestData() + factories.populate_database() def setUp(self): + """Test initialization.""" super(PasswordSchemesTestCase, self).setUp() - factories.populate_database() load_core_settings() def _create_account(self): diff --git a/modoboa/admin/views/user.py b/modoboa/admin/views/user.py index 4c6517a99..5b8d51f70 100644 --- a/modoboa/admin/views/user.py +++ b/modoboa/admin/views/user.py @@ -1,3 +1,4 @@ +"""SimpleUsers views.""" from django.contrib.auth.decorators import login_required from django.utils.translation import ugettext as _ @@ -38,9 +39,9 @@ def forward(request, tplname="admin/forward.html"): ) form = ForwardForm() - if al is not None: + if al is not None and al.recipients: form.fields["dest"].initial = al.recipients - if al.aliasrecipient_set.filter(mailbox=mb.id).exists(): + if al.aliasrecipient_set.filter(r_mailbox=mb.pk).exists(): form.fields["keepcopies"].initial = True return render_to_json_response({ "content": _render_to_string(request, tplname, { diff --git a/modoboa/core/commands/templates/settings.py.tpl b/modoboa/core/commands/templates/settings.py.tpl index b119e253d..8a3e60f91 100644 --- a/modoboa/core/commands/templates/settings.py.tpl +++ b/modoboa/core/commands/templates/settings.py.tpl @@ -12,7 +12,6 @@ https://docs.djangoproject.com/en/1.6/ref/settings/ import os from logging.handlers import SysLogHandler -from django.conf import global_settings {% if devmode %} from modoboa.core.dev_settings import * {% endif %} @@ -29,14 +28,16 @@ SECRET_KEY = '{{ secret_key }}' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = {{ devmode }} -TEMPLATE_DEBUG = {{ devmode }} - ALLOWED_HOSTS = [ '{{ allowed_host }}' ] SITE_ID = 1 +# Security settings + +X_FRAME_OPTIONS = "DENY" + # Application definition INSTALLED_APPS = ( @@ -87,9 +88,27 @@ AUTHENTICATION_BACKENDS = ( 'modoboa.lib.authbackends.SimpleBackend', ) -TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + ( - 'modoboa.core.context_processors.top_notifications', -) +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.template.context_processors.i18n', + 'django.template.context_processors.media', + 'django.template.context_processors.static', + 'django.template.context_processors.tz', + 'django.contrib.messages.context_processors.messages', + 'modoboa.core.context_processors.top_notifications', + ], + 'debug': {{ devmode }}, + }, + }, +] ROOT_URLCONF = '{{ name }}.urls' diff --git a/modoboa/core/management/commands/__init__.py b/modoboa/core/management/commands/__init__.py index bcc7f3a46..e69de29bb 100644 --- a/modoboa/core/management/commands/__init__.py +++ b/modoboa/core/management/commands/__init__.py @@ -1,18 +0,0 @@ -""" -Extra classes for management commands. -""" - -from django.db import connection - - -class CloseConnectionMixin(object): - - """ - A simple mixin used to close all database connections after - command execution. - """ - - def execute(self, *args, **options): - """Execute will call handle so we can close the connection here.""" - super(CloseConnectionMixin, self).execute(*args, **options) - connection.close() diff --git a/modoboa/core/management/commands/cleanlogs.py b/modoboa/core/management/commands/cleanlogs.py index 26219dc38..7840615f1 100644 --- a/modoboa/core/management/commands/cleanlogs.py +++ b/modoboa/core/management/commands/cleanlogs.py @@ -10,10 +10,8 @@ from modoboa.core.models import Log from modoboa.lib import parameters -from . import CloseConnectionMixin - -class Command(BaseCommand, CloseConnectionMixin): +class Command(BaseCommand): args = '' help = 'Log table cleanup' diff --git a/modoboa/core/management/commands/load_initial_data.py b/modoboa/core/management/commands/load_initial_data.py index bc600ac31..1bf92aa23 100644 --- a/modoboa/core/management/commands/load_initial_data.py +++ b/modoboa/core/management/commands/load_initial_data.py @@ -18,10 +18,9 @@ from modoboa.lib.permissions import add_permissions_to_group from modoboa.limits.app_settings import load_limits_settings import modoboa.relaydomains.models as relay_models -from . import CloseConnectionMixin -class Command(BaseCommand, CloseConnectionMixin): +class Command(BaseCommand): """Command defintion.""" diff --git a/modoboa/core/management/commands/set_default_site.py b/modoboa/core/management/commands/set_default_site.py index 3aea3ea07..03f703267 100644 --- a/modoboa/core/management/commands/set_default_site.py +++ b/modoboa/core/management/commands/set_default_site.py @@ -9,10 +9,8 @@ from django.contrib.sites.models import Site -from . import CloseConnectionMixin - -class Command(BaseCommand, CloseConnectionMixin): +class Command(BaseCommand): """Management command to set the default site.""" diff --git a/modoboa/core/tests.py b/modoboa/core/tests.py index aaeca7d03..af5794290 100644 --- a/modoboa/core/tests.py +++ b/modoboa/core/tests.py @@ -8,9 +8,11 @@ class ProfileTestCase(ModoTestCase): - def setUp(self): - super(ProfileTestCase, self).setUp() - self.account = factories.UserFactory( + @classmethod + def setUpTestData(cls): + """Create test data.""" + super(ProfileTestCase, cls).setUpTestData() + cls.account = factories.UserFactory( username="user@test.com", groups=('SimpleUsers',) ) diff --git a/modoboa/lib/form_utils.py b/modoboa/lib/form_utils.py index 4c8748fea..edb92305b 100644 --- a/modoboa/lib/form_utils.py +++ b/modoboa/lib/form_utils.py @@ -9,7 +9,8 @@ from django.core.exceptions import ValidationError from django.forms import ChoiceField from django.forms.fields import CharField, Field -from django.forms.widgets import RadioSelect, RadioInput +from django.forms.widgets import RadioSelect +from django.forms.widgets import RadioChoiceInput from django.shortcuts import render from django.utils.encoding import force_unicode from django.utils.html import conditional_escape @@ -401,7 +402,7 @@ def clean(self, value): return super(DomainNameField, self).clean(value) -class CustomRadioInput(RadioInput): +class CustomRadioInput(RadioChoiceInput): def __unicode__(self): if 'id' in self.attrs: label_for = ' for="%s"' % self.attrs['id'] diff --git a/modoboa/lib/tests.py b/modoboa/lib/tests.py index f8a410c31..08b2d486e 100644 --- a/modoboa/lib/tests.py +++ b/modoboa/lib/tests.py @@ -9,14 +9,19 @@ from django.test.client import Client from modoboa.lib import parameters +from modoboa.core import models as core_models class ModoTestCase(TestCase): """All test cases must inherit from this one.""" - def setUp(self, username="admin", password="password"): + @classmethod + def setUpTestData(cls): + """Create a default user.""" management.call_command("load_initial_data") + + def setUp(self, username="admin", password="password"): self.clt = Client() self.assertEqual( self.clt.login(username=username, password=password), True) @@ -66,11 +71,16 @@ class ParameterTestCase(TestCase): """Simple test cases for ``modoboa.lib.parameters`` module. """ + @classmethod + def setUpTestData(cls): + super(ParameterTestCase, cls).setUpTestData() + cls.user = core_models.User.objects.create(username="tester") + def setUp(self): - from modoboa.core.models import User + """Initialize tests.""" + super(ParameterTestCase, self).setUp() parameters.register(TestParams, "Test") parameters.register(TestUserParams, "TestUser") - self.user = User.objects.create(username="tester") def test_register_form(self): self.assertIn("test", parameters._params['A']) diff --git a/modoboa/limits/tests.py b/modoboa/limits/tests.py index f797c2048..e4f4fec2d 100644 --- a/modoboa/limits/tests.py +++ b/modoboa/limits/tests.py @@ -14,8 +14,10 @@ class PermissionsTestCase(ModoTestCase): - def setUp(self): - super(PermissionsTestCase, self).setUp() + @classmethod + def setUpTestData(cls): + """Create test data.""" + super(PermissionsTestCase, cls).setUpTestData() populate_database() def test_domainadmin_deletes_reseller(self): @@ -41,13 +43,10 @@ def test_domainadmin_deletes_reseller(self): class ResourceTestCase(ModoTestCase): - def setUp(self): - """Custom setUp method. - - The 'limits' is manually loaded to ensure extra parameters - provided by 'postfix_relay_domains' are properly received. - """ - super(ResourceTestCase, self).setUp() + @classmethod + def setUpTestData(cls): + """Custom setUpTestData method.""" + super(ResourceTestCase, cls).setUpTestData() for tpl in LimitTemplates().templates: parameters.save_admin( "DEFLT_{0}".format(tpl[0].upper()), 2, @@ -115,11 +114,17 @@ def _check_limit(self, name, curvalue, maxvalue): class DomainAdminTestCase(ResourceTestCase): + @classmethod + def setUpTestData(cls): + """Create test data.""" + super(DomainAdminTestCase, cls).setUpTestData() + cls.user = User.objects.get(username='admin@test.com') + cls.user.limitspool.set_maxvalue('mailboxes_limit', 2) + cls.user.limitspool.set_maxvalue('mailbox_aliases_limit', 2) + def setUp(self): + """Test initialization.""" super(DomainAdminTestCase, self).setUp() - self.user = User.objects.get(username='admin@test.com') - self.user.limitspool.set_maxvalue('mailboxes_limit', 2) - self.user.limitspool.set_maxvalue('mailbox_aliases_limit', 2) self.clt.logout() self.clt.login(username='admin@test.com', password='toto') @@ -168,11 +173,17 @@ def test_aliases_limit_through_account_form(self): class ResellerTestCase(ResourceTestCase): - def setUp(self): - super(ResellerTestCase, self).setUp() - self.user = UserFactory.create( + @classmethod + def setUpTestData(cls): + """Create test data.""" + super(ResellerTestCase, cls).setUpTestData() + cls.user = UserFactory.create( username='reseller', groups=('Resellers',) ) + + def setUp(self): + """Test initialization.""" + super(ResellerTestCase, self).setUp() self.clt.logout() self.clt.login(username='reseller', password='toto') diff --git a/modoboa/relaydomains/models.py b/modoboa/relaydomains/models.py index 751328718..b0a33c908 100644 --- a/modoboa/relaydomains/models.py +++ b/modoboa/relaydomains/models.py @@ -11,19 +11,6 @@ from modoboa.lib.exceptions import BadRequest -class RelayDomainManager(Manager): - - def get_for_admin(self, admin): - """Return the relay domains belonging to this admin. - - The result is a ``QuerySet`` object, so this function can be used - to fill ``ModelChoiceField`` objects. - """ - if admin.is_superuser: - return self.get_query_set() - return self.get_query_set().filter(owners__user=admin) - - class ServiceManager(Manager): def load_from_master_cf(self): @@ -91,8 +78,6 @@ class RelayDomain(admin_models.AdminObject): default=False ) - objects = RelayDomainManager() - class Meta: ordering = ["domain__name"] db_table = "postfix_relay_domains_relaydomain" diff --git a/modoboa/relaydomains/tests.py b/modoboa/relaydomains/tests.py index e40654a45..288284ae9 100644 --- a/modoboa/relaydomains/tests.py +++ b/modoboa/relaydomains/tests.py @@ -57,14 +57,16 @@ def _check_limit(self, name, curvalue, maxvalue): class RelayDomainsTestCase(ModoTestCase, Operations): - def setUp(self): - super(RelayDomainsTestCase, self).setUp() + @classmethod + def setUpTestData(cls): + """Create test data.""" + super(RelayDomainsTestCase, cls).setUpTestData() admin_factories.populate_database() - self.rdom = RelayDomainFactory(domain__name='relaydomain.tld') + cls.rdom = RelayDomainFactory(domain__name='relaydomain.tld') admin_factories.DomainAliasFactory( - name='relaydomainalias.tld', target=self.rdom.domain) + name='relaydomainalias.tld', target=cls.rdom.domain) admin_factories.MailboxFactory( - domain=self.rdom.domain, address="local", + domain=cls.rdom.domain, address="local", user__username="local@relaydomain.tld", user__groups=("SimpleUsers", ) ) @@ -196,16 +198,22 @@ def test_alias_on_relaydomain(self): class LimitsTestCase(ModoTestCase, Operations): - def setUp(self): - super(LimitsTestCase, self).setUp() + @classmethod + def setUpTestData(cls): + """Create test data.""" + super(LimitsTestCase, cls).setUpTestData() for tpl in LimitTemplates().templates: parameters.save_admin( "DEFLT_{0}".format(tpl[0].upper()), 2, app="limits" ) - self.user = UserFactory.create( + cls.user = UserFactory.create( username='reseller', groups=('Resellers',) ) + + def setUp(self): + """Initialize test.""" + super(LimitsTestCase, self).setUp() self.clt.logout() self.clt.login(username='reseller', password='toto') diff --git a/requirements.txt b/requirements.txt index 9f59e5205..f37d2ca7a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -django>=1.7,<1.8 -django-reversion==1.8.5 +django>=1.8,<1.9 +django-reversion==1.9.3 dj-database-url pycrypto argparse