diff --git a/.travis.yml b/.travis.yml index 066b49c97..ee3f38ff9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,19 @@ -sudo: false language: python -cache: pip python: - "2.7" - "3.4" + - "3.5" - "3.6" + +env: + global: + - PYTHONWARNINGS="default,ignore::PendingDeprecationWarning,ignore::ResourceWarning" + matrix: + - DB="POSTGRESQL" + - DB="MYSQL" + +sudo: false + addons: apt: packages: @@ -12,30 +21,28 @@ addons: - postfix - slapd -env: - - DB="POSTGRESQL" - - DB="MYSQL" +cache: pip services: - - mysql - postgres + - mysql before_install: - pip install codecov - - if [[ $DB = 'POSTGRESQL' ]]; then pip install -q psycopg2; fi - - if [[ $DB = 'MYSQL' ]]; then pip install -q mysqlclient; fi + - if [[ $DB = "POSTGRESQL" ]]; then pip install -q psycopg2; fi + - if [[ $DB = "MYSQL" ]]; then pip install -q mysqlclient; fi install: - pip install -q -r requirements.txt - pip install -q -r ldap-requirements.txt - pip install -q -r test-requirements.txt - - pip install -e . + - python setup.py -q develop before_script: - - if [[ $DB = 'POSTGRESQL' ]]; then psql -c 'create database modoboa_test;' -U postgres; fi - - if [[ $DB = 'MYSQL' ]]; then mysql -e "create database IF NOT EXISTS modoboa_test;" -uroot; fi - - if [[ $DB = 'MYSQL' ]]; then mysql -e "CREATE USER 'modoboa'@'localhost' IDENTIFIED BY 'modoboa'" -uroot; fi - - if [[ $DB = 'MYSQL' ]]; then mysql -e "GRANT ALL PRIVILEGES ON * . * TO 'modoboa'@'localhost';" -uroot; fi + - if [[ $DB = "POSTGRESQL" ]]; then psql -c "CREATE DATABASE modoboa_test;" -U postgres; fi + - if [[ $DB = "MYSQL" ]]; then mysql -e "CREATE DATABASE IF NOT EXISTS modoboa_test;" -uroot; fi + - if [[ $DB = "MYSQL" ]]; then mysql -e "CREATE USER 'modoboa'@'localhost' IDENTIFIED BY 'modoboa'" -uroot; fi + - if [[ $DB = "MYSQL" ]]; then mysql -e "GRANT ALL PRIVILEGES ON * . * TO 'modoboa'@'localhost';" -uroot; fi - mkdir /tmp/slapd - slapd -f test_data/slapd.conf -h ldap://localhost:3389 & - sleep 3 diff --git a/ldap-requirements.txt b/ldap-requirements.txt index 35eaac975..40145847b 100644 --- a/ldap-requirements.txt +++ b/ldap-requirements.txt @@ -1 +1 @@ -django-auth-ldap>=1.2.13 +django-auth-ldap>=1.3.0 diff --git a/modoboa/admin/forms/account.py b/modoboa/admin/forms/account.py index 19effd8ab..e160b6530 100644 --- a/modoboa/admin/forms/account.py +++ b/modoboa/admin/forms/account.py @@ -6,7 +6,7 @@ from collections import OrderedDict from functools import reduce from django import forms -from django.core.urlresolvers import reverse +from django.urls import reverse from django.http import QueryDict from django.utils.translation import ugettext as _, ugettext_lazy @@ -607,8 +607,6 @@ class AccountWizard(WizardForm): }) def done(self): - from modoboa.lib.web_utils import render_to_json_response - account = self.first_step.form.save() account.post_create(self.request.user) mailform = self.steps[1].form diff --git a/modoboa/admin/forms/domain.py b/modoboa/admin/forms/domain.py index 23e2e9343..558b6e89f 100644 --- a/modoboa/admin/forms/domain.py +++ b/modoboa/admin/forms/domain.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals from functools import reduce from django import forms -from django.core.urlresolvers import reverse +from django.urls import reverse from django.http import QueryDict from django.utils.translation import ugettext as _, ugettext_lazy from django.utils.encoding import force_text diff --git a/modoboa/admin/handlers.py b/modoboa/admin/handlers.py index c51e01b27..da360c6d8 100644 --- a/modoboa/admin/handlers.py +++ b/modoboa/admin/handlers.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals -from django.core.urlresolvers import reverse +from django.urls import reverse from django.db.models import signals from django.dispatch import receiver from django.utils.translation import ugettext as _ diff --git a/modoboa/admin/migrations/0001_initial.py b/modoboa/admin/migrations/0001_initial.py index ec5eb96bb..9be649089 100644 --- a/modoboa/admin/migrations/0001_initial.py +++ b/modoboa/admin/migrations/0001_initial.py @@ -31,8 +31,8 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('address', models.EmailField(max_length=75)), - ('alias', models.ForeignKey(to='admin.Alias')), - ('r_alias', models.ForeignKey(related_name='alias_recipient_aliases', blank=True, to='admin.Alias', null=True)), + ('alias', models.ForeignKey(to='admin.Alias', on_delete=models.CASCADE)), + ('r_alias', models.ForeignKey(related_name='alias_recipient_aliases', blank=True, to='admin.Alias', null=True, on_delete=models.CASCADE)), ], options={ 'db_table': 'modoboa_admin_aliasrecipient', @@ -85,7 +85,7 @@ class Migration(migrations.Migration): ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('type', models.CharField(max_length=20, choices=[(b'rename', b'rename'), (b'delete', b'delete')])), ('argument', models.TextField()), - ('mailbox', models.ForeignKey(blank=True, to='admin.Mailbox', null=True)), + ('mailbox', models.ForeignKey(blank=True, to='admin.Mailbox', null=True, on_delete=models.CASCADE)), ], options={ }, @@ -116,43 +116,43 @@ class Migration(migrations.Migration): migrations.AddField( model_name='mailbox', name='dates', - field=models.ForeignKey(to='admin.ObjectDates'), + field=models.ForeignKey(to='admin.ObjectDates', on_delete=models.CASCADE), preserve_default=True, ), migrations.AddField( model_name='mailbox', name='domain', - field=models.ForeignKey(to='admin.Domain'), + field=models.ForeignKey(to='admin.Domain', on_delete=models.CASCADE), preserve_default=True, ), migrations.AddField( model_name='mailbox', name='user', - field=models.OneToOneField(to=settings.AUTH_USER_MODEL), + field=models.OneToOneField(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE), preserve_default=True, ), migrations.AddField( model_name='domainalias', name='dates', - field=models.ForeignKey(to='admin.ObjectDates'), + field=models.ForeignKey(to='admin.ObjectDates', on_delete=models.CASCADE), preserve_default=True, ), migrations.AddField( model_name='domainalias', name='target', - field=models.ForeignKey(verbose_name='target', to='admin.Domain', help_text='The domain this alias points to'), + field=models.ForeignKey(verbose_name='target', to='admin.Domain', help_text='The domain this alias points to', on_delete=models.CASCADE), preserve_default=True, ), migrations.AddField( model_name='domain', name='dates', - field=models.ForeignKey(to='admin.ObjectDates'), + field=models.ForeignKey(to='admin.ObjectDates', on_delete=models.CASCADE), preserve_default=True, ), migrations.AddField( model_name='aliasrecipient', name='r_mailbox', - field=models.ForeignKey(blank=True, to='admin.Mailbox', null=True), + field=models.ForeignKey(blank=True, to='admin.Mailbox', null=True, on_delete=models.CASCADE), preserve_default=True, ), migrations.AlterUniqueTogether( @@ -162,13 +162,13 @@ class Migration(migrations.Migration): migrations.AddField( model_name='alias', name='dates', - field=models.ForeignKey(to='admin.ObjectDates'), + field=models.ForeignKey(to='admin.ObjectDates', on_delete=models.CASCADE), preserve_default=True, ), migrations.AddField( model_name='alias', name='domain', - field=models.ForeignKey(to='admin.Domain', null=True), + field=models.ForeignKey(to='admin.Domain', null=True, on_delete=models.CASCADE), preserve_default=True, ), migrations.AlterUniqueTogether( diff --git a/modoboa/admin/models/alias.py b/modoboa/admin/models/alias.py index 59b796725..225ed9e78 100644 --- a/modoboa/admin/models/alias.py +++ b/modoboa/admin/models/alias.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import hashlib import random -from django.core.urlresolvers import reverse +from django.urls import reverse from django.db import models from django.utils.encoding import ( python_2_unicode_compatible, smart_text, force_str, force_bytes @@ -37,7 +37,7 @@ class Alias(AdminObject): "The alias address." ) ) - domain = models.ForeignKey(Domain, null=True) + domain = models.ForeignKey(Domain, null=True, on_delete=models.CASCADE) enabled = models.BooleanField( ugettext_lazy("enabled"), help_text=ugettext_lazy("Check to activate this alias"), @@ -210,13 +210,15 @@ class AliasRecipient(models.Model): """An alias recipient.""" address = models.EmailField() - alias = models.ForeignKey(Alias) + alias = models.ForeignKey(Alias, on_delete=models.CASCADE) # if recipient is a local mailbox - r_mailbox = models.ForeignKey(Mailbox, blank=True, null=True) + r_mailbox = models.ForeignKey(Mailbox, blank=True, null=True, + on_delete=models.CASCADE) # if recipient is a local alias r_alias = models.ForeignKey( - Alias, related_name="alias_recipient_aliases", blank=True, null=True) + Alias, related_name="alias_recipient_aliases", blank=True, null=True, + on_delete=models.CASCADE) class Meta: app_label = "admin" diff --git a/modoboa/admin/models/domain_alias.py b/modoboa/admin/models/domain_alias.py index c9047886d..51ccfba69 100644 --- a/modoboa/admin/models/domain_alias.py +++ b/modoboa/admin/models/domain_alias.py @@ -40,7 +40,8 @@ class DomainAlias(AdminObject): help_text=ugettext_lazy("The alias name")) target = models.ForeignKey( Domain, verbose_name=ugettext_lazy('target'), - help_text=ugettext_lazy("The domain this alias points to") + help_text=ugettext_lazy("The domain this alias points to"), + on_delete=models.CASCADE ) enabled = models.BooleanField( ugettext_lazy('enabled'), diff --git a/modoboa/admin/models/mailbox.py b/modoboa/admin/models/mailbox.py index 1cf18320d..790cf9fbb 100644 --- a/modoboa/admin/models/mailbox.py +++ b/modoboa/admin/models/mailbox.py @@ -100,8 +100,8 @@ class Mailbox(AdminObject): ) quota = models.PositiveIntegerField(default=0) use_domain_quota = models.BooleanField(default=False) - domain = models.ForeignKey(Domain) - user = models.OneToOneField(User) + domain = models.ForeignKey(Domain, on_delete=models.CASCADE) + user = models.OneToOneField(User, on_delete=models.CASCADE) objects = MailboxManager() @@ -354,7 +354,7 @@ class SenderAddress(models.Model): """Extra sender address for Mailbox.""" address = models.EmailField() - mailbox = models.ForeignKey(Mailbox) + mailbox = models.ForeignKey(Mailbox, on_delete=models.CASCADE) class Meta: app_label = "admin" @@ -373,7 +373,8 @@ reversion.register(SenderAddress) class MailboxOperation(models.Model): """An operation on a mailbox.""" - mailbox = models.ForeignKey(Mailbox, blank=True, null=True) + mailbox = models.ForeignKey(Mailbox, blank=True, null=True, + on_delete=models.CASCADE) type = models.CharField( max_length=20, choices=(('rename', 'rename'), ('delete', 'delete')) ) diff --git a/modoboa/admin/models/mxrecord.py b/modoboa/admin/models/mxrecord.py index c769da392..4a4f71e5b 100644 --- a/modoboa/admin/models/mxrecord.py +++ b/modoboa/admin/models/mxrecord.py @@ -37,14 +37,14 @@ class MXRecordManager(models.Manager): if records.exists(): for record in records: yield record - raise StopIteration() + return self.get_queryset().filter(domain=domain).delete() delta = datetime.timedelta(seconds=ttl) domain_mxs = lib.get_domain_mx_list(domain.name) if len(domain_mxs) == 0: - raise StopIteration + return for mx_addr, mx_ip_addr in domain_mxs: record = self.get_queryset().create( domain=domain, @@ -58,7 +58,7 @@ class MXRecordManager(models.Manager): class MXRecord(models.Model): """A model used to store MX records for Domain.""" - domain = models.ForeignKey("admin.Domain") + domain = models.ForeignKey("admin.Domain", on_delete=models.CASCADE) name = models.CharField(max_length=254) address = models.GenericIPAddressField() managed = models.BooleanField(default=False) @@ -86,9 +86,9 @@ class DNSBLQuerySet(models.QuerySet): class DNSBLResult(models.Model): """Store a DNSBL query result.""" - domain = models.ForeignKey("admin.Domain") + domain = models.ForeignKey("admin.Domain", on_delete=models.CASCADE) provider = models.CharField(max_length=254, db_index=True) - mx = models.ForeignKey(MXRecord) + mx = models.ForeignKey(MXRecord, on_delete=models.CASCADE) status = models.CharField(max_length=45, blank=True, db_index=True) objects = models.Manager.from_queryset(DNSBLQuerySet)() diff --git a/modoboa/admin/templatetags/admin_tags.py b/modoboa/admin/templatetags/admin_tags.py index 750ce39c3..202088157 100644 --- a/modoboa/admin/templatetags/admin_tags.py +++ b/modoboa/admin/templatetags/admin_tags.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals from functools import reduce from django import template -from django.core.urlresolvers import reverse +from django.urls import reverse from django.template.loader import render_to_string from django.utils.safestring import mark_safe from django.utils.translation import ugettext as _, ugettext_lazy diff --git a/modoboa/admin/tests/test_account.py b/modoboa/admin/tests/test_account.py index acf1baf9d..3f6f2144d 100644 --- a/modoboa/admin/tests/test_account.py +++ b/modoboa/admin/tests/test_account.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals from unittest import skipIf -from django.core.urlresolvers import reverse +from django.urls import reverse from django.test import override_settings from modoboa.core import factories as core_factories diff --git a/modoboa/admin/tests/test_alias.py b/modoboa/admin/tests/test_alias.py index 046b85275..3df90621d 100644 --- a/modoboa/admin/tests/test_alias.py +++ b/modoboa/admin/tests/test_alias.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals """Admin test cases.""" -from django.core.urlresolvers import reverse +from django.urls import reverse from modoboa.core.models import User from modoboa.lib.tests import ModoTestCase diff --git a/modoboa/admin/tests/test_api.py b/modoboa/admin/tests/test_api.py index 6b0c0fbca..0a4cfbf59 100644 --- a/modoboa/admin/tests/test_api.py +++ b/modoboa/admin/tests/test_api.py @@ -9,7 +9,7 @@ import json import dns.resolver from mock import patch -from django.core.urlresolvers import reverse +from django.urls import reverse from rest_framework.authtoken.models import Token diff --git a/modoboa/admin/tests/test_domain.py b/modoboa/admin/tests/test_domain.py index b3eeabfeb..553a861a8 100644 --- a/modoboa/admin/tests/test_domain.py +++ b/modoboa/admin/tests/test_domain.py @@ -7,7 +7,7 @@ from __future__ import unicode_literals import dns.resolver from mock import patch -from django.core.urlresolvers import reverse +from django.urls import reverse from modoboa.core import factories as core_factories from modoboa.core.models import User diff --git a/modoboa/admin/tests/test_domain_alias.py b/modoboa/admin/tests/test_domain_alias.py index 76f92fb16..5dd667702 100644 --- a/modoboa/admin/tests/test_domain_alias.py +++ b/modoboa/admin/tests/test_domain_alias.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals -from django.core.urlresolvers import reverse +from django.urls import reverse from modoboa.lib.tests import ModoTestCase diff --git a/modoboa/admin/tests/test_export.py b/modoboa/admin/tests/test_export.py index f7ba5177f..c48e3486d 100644 --- a/modoboa/admin/tests/test_export.py +++ b/modoboa/admin/tests/test_export.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals -from django.core.urlresolvers import reverse +from django.urls import reverse from django.utils.encoding import force_text from modoboa.lib.tests import ModoTestCase diff --git a/modoboa/admin/tests/test_import_.py b/modoboa/admin/tests/test_import_.py index b40445f77..370c2087c 100644 --- a/modoboa/admin/tests/test_import_.py +++ b/modoboa/admin/tests/test_import_.py @@ -6,7 +6,7 @@ from mock import patch import dns.resolver from django.core.files.base import ContentFile -from django.core.urlresolvers import reverse +from django.urls import reverse from modoboa.core import factories as core_factories from modoboa.core.models import User diff --git a/modoboa/admin/tests/test_mailbox_operations.py b/modoboa/admin/tests/test_mailbox_operations.py index 2f9a719ee..81c02de97 100644 --- a/modoboa/admin/tests/test_mailbox_operations.py +++ b/modoboa/admin/tests/test_mailbox_operations.py @@ -9,7 +9,7 @@ import tempfile import mock from django.core.management import call_command -from django.core.urlresolvers import reverse +from django.urls import reverse from django.test import override_settings from modoboa.lib.tests import ModoTestCase diff --git a/modoboa/admin/tests/test_mx.py b/modoboa/admin/tests/test_mx.py index 2e2a39b10..53d2a94c6 100644 --- a/modoboa/admin/tests/test_mx.py +++ b/modoboa/admin/tests/test_mx.py @@ -6,7 +6,7 @@ from mock import patch from django.core import mail from django.core import management -from django.core.urlresolvers import reverse +from django.urls import reverse from django.test import override_settings from modoboa.core import factories as core_factories diff --git a/modoboa/admin/tests/test_password_schemes.py b/modoboa/admin/tests/test_password_schemes.py index 160d1e0b2..277d202b9 100644 --- a/modoboa/admin/tests/test_password_schemes.py +++ b/modoboa/admin/tests/test_password_schemes.py @@ -1,6 +1,6 @@ from __future__ import unicode_literals -from django.core.urlresolvers import reverse +from django.urls import reverse from modoboa.core.models import User from modoboa.lib.tests import ModoTestCase diff --git a/modoboa/admin/tests/test_user.py b/modoboa/admin/tests/test_user.py index 2eccfc900..1c55760ff 100644 --- a/modoboa/admin/tests/test_user.py +++ b/modoboa/admin/tests/test_user.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals -from django.core.urlresolvers import reverse +from django.urls import reverse from modoboa.core.models import User from modoboa.lib.tests import ModoTestCase diff --git a/modoboa/admin/views/alias.py b/modoboa/admin/views/alias.py index 20b1c4635..a7c97f58b 100644 --- a/modoboa/admin/views/alias.py +++ b/modoboa/admin/views/alias.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals -from django.core.urlresolvers import reverse +from django.urls import reverse from django.db import IntegrityError from django.shortcuts import render from django.utils.translation import ugettext as _, ungettext diff --git a/modoboa/admin/views/domain.py b/modoboa/admin/views/domain.py index 2fd0f777e..4618b294a 100644 --- a/modoboa/admin/views/domain.py +++ b/modoboa/admin/views/domain.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals from functools import reduce -from django.core.urlresolvers import reverse +from django.urls import reverse from django.db.models import Sum from django.http import HttpResponseRedirect from django.shortcuts import render diff --git a/modoboa/admin/views/export.py b/modoboa/admin/views/export.py index 89e6dabb0..f916ea8c5 100644 --- a/modoboa/admin/views/export.py +++ b/modoboa/admin/views/export.py @@ -9,7 +9,7 @@ from rfc6266 import build_header from django.contrib.auth.decorators import ( login_required, permission_required, user_passes_test ) -from django.core.urlresolvers import reverse +from django.urls import reverse from django.http import HttpResponse from django.shortcuts import render from django.utils.translation import ugettext as _ diff --git a/modoboa/admin/views/import_.py b/modoboa/admin/views/import_.py index 44921471c..1291b41ee 100644 --- a/modoboa/admin/views/import_.py +++ b/modoboa/admin/views/import_.py @@ -9,7 +9,7 @@ from io import StringIO from django.contrib.auth.decorators import ( login_required, permission_required, user_passes_test ) -from django.core.urlresolvers import reverse +from django.urls import reverse from django.db import transaction from django.shortcuts import render from django.utils.translation import ugettext as _ diff --git a/modoboa/core/app_settings.py b/modoboa/core/app_settings.py index ee7e699f6..0f69de5ad 100644 --- a/modoboa/core/app_settings.py +++ b/modoboa/core/app_settings.py @@ -11,7 +11,7 @@ from django.contrib.auth import password_validation from modoboa.lib import fields as lib_fields from modoboa.lib.cryptutils import random_key from modoboa.lib.form_utils import ( - YesNoField, SeparatorField, InlineRadioSelect + YesNoField, SeparatorField, HorizontalRadioSelect ) from modoboa.parameters import forms as param_forms from modoboa.parameters import tools as param_tools @@ -44,7 +44,7 @@ class GeneralParametersForm(param_forms.AdminParametersForm): ('ldap', "LDAP")], initial="local", help_text=ugettext_lazy("The backend used for authentication"), - widget=InlineRadioSelect + widget=HorizontalRadioSelect() ) password_scheme = forms.ChoiceField( diff --git a/modoboa/core/commands/deploy.py b/modoboa/core/commands/deploy.py index e63ca890a..84c539a8e 100644 --- a/modoboa/core/commands/deploy.py +++ b/modoboa/core/commands/deploy.py @@ -28,7 +28,7 @@ from modoboa.core.commands import Command from modoboa.lib.api_client import ModoAPIClient if sys.version_info < (3, 0, 0): - input = raw_input + input = raw_input DBCONN_TPL = """ '{{ conn_name }}': { diff --git a/modoboa/core/commands/templates/settings.py.tpl b/modoboa/core/commands/templates/settings.py.tpl index c61596c2e..af59cce7a 100644 --- a/modoboa/core/commands/templates/settings.py.tpl +++ b/modoboa/core/commands/templates/settings.py.tpl @@ -1,26 +1,31 @@ """ Django settings for {{ name }} project. +Generated by 'django-admin startproject' using Django 1.11.8. + For more information on this file, see -https://docs.djangoproject.com/en/1.6/topics/settings/ +https://docs.djangoproject.com/en/1.11/topics/settings/ For the full list of settings and their values, see -https://docs.djangoproject.com/en/1.6/ref/settings/ +https://docs.djangoproject.com/en/1.11/ref/settings/ """ -# Build paths inside the project like this: os.path.join(BASE_DIR, ...) -import os -from logging.handlers import SysLogHandler +from __future__ import unicode_literals +from logging.handlers import SysLogHandler +import os {% if devmode %} -from modoboa.core.dev_settings import * + +from modoboa.core.dev_settings import * # noqa {% endif %} + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.realpath(os.path.dirname(os.path.dirname(__file__))) # Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/1.6/howto/deployment/checklist/ +# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = '{{ secret_key }}' @@ -29,7 +34,7 @@ SECRET_KEY = '{{ secret_key }}' DEBUG = {{ devmode }} ALLOWED_HOSTS = [ - '{{ allowed_host }}' + '{{ allowed_host }}', ] SITE_ID = 1 @@ -76,7 +81,7 @@ INSTALLED_APPS += MODOBOA_APPS AUTH_USER_MODEL = 'core.User' -MIDDLEWARE_CLASSES = ( +MIDDLEWARE = ( 'x_forwarded_for.middleware.XForwardedForMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', @@ -131,14 +136,14 @@ WSGI_APPLICATION = '{{ name }}.wsgi.application' # Database -# https://docs.djangoproject.com/en/1.6/ref/settings/#databases +# https://docs.djangoproject.com/en/1.11/ref/settings/#databases DATABASES = { {% for conn in db_connections.values %}{{ conn|safe }}{% endfor %} } # Internationalization -# https://docs.djangoproject.com/en/1.6/topics/i18n/ +# https://docs.djangoproject.com/en/1.11/topics/i18n/ LANGUAGE_CODE = '{{ lang }}' @@ -150,9 +155,8 @@ USE_L10N = True USE_TZ = True - # Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/1.6/howto/static-files/ +# https://docs.djangoproject.com/en/1.11/howto/static-files/ STATIC_URL = '/sitestatic/' STATIC_ROOT = os.path.join(BASE_DIR, 'sitestatic') @@ -173,13 +177,14 @@ REST_FRAMEWORK = { } # Modoboa settings -#MODOBOA_CUSTOM_LOGO = os.path.join(MEDIA_URL, "custom_logo.png") +# MODOBOA_CUSTOM_LOGO = os.path.join(MEDIA_URL, "custom_logo.png") -#DOVECOT_LOOKUP_PATH = ('/path/to/dovecot', ) +# DOVECOT_LOOKUP_PATH = ('/path/to/dovecot', ) MODOBOA_API_URL = 'https://api.modoboa.org/1/' -# Password validation rules +# Password validation +# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { @@ -276,5 +281,5 @@ try: from {{ extension }} import settings as {{ extension }}_settings {{ extension }}_settings.apply(globals()) except AttributeError: - from {{ extension }}.settings import * + from {{ extension }}.settings import * # noqa {% endfor %} diff --git a/modoboa/core/handlers.py b/modoboa/core/handlers.py index c146dc89f..d5983626e 100644 --- a/modoboa/core/handlers.py +++ b/modoboa/core/handlers.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import logging -from django.core.urlresolvers import reverse +from django.urls import reverse from django.db.models import signals from django.dispatch import receiver from django.utils.encoding import smart_text @@ -13,6 +13,7 @@ from django.utils.translation import ugettext as _ from django.contrib.sites import models as sites_models from reversion import revisions as reversion +from reversion.models import Version from modoboa.lib import exceptions from modoboa.lib import permissions @@ -32,14 +33,12 @@ def post_revision_commit(sender, **kwargs): django-reversion for that. """ - from modoboa.lib.signals import get_request - current_user = get_request().user.username logger = logging.getLogger("modoboa.admin") for version in kwargs["versions"]: if version.object is None: continue - prev_revisions = reversion.get_for_object(version.object) + prev_revisions = Version.objects.get_for_object(version.object) if prev_revisions.count() == 1: action = _("added") level = "info" @@ -60,11 +59,9 @@ def log_object_removal(sender, instance, **kwargs): We want to know who was responsible for an object deletion. """ - from reversion.models import Version - if not reversion.is_registered(sender): return - del_list = reversion.get_deleted(sender) + del_list = Version.objects.get_deleted(sender) try: version = del_list.get(object_id=instance.id) except Version.DoesNotExist: diff --git a/modoboa/core/middleware.py b/modoboa/core/middleware.py index f8f66b403..df8f6939a 100644 --- a/modoboa/core/middleware.py +++ b/modoboa/core/middleware.py @@ -2,10 +2,12 @@ from __future__ import unicode_literals +from django.utils.deprecation import MiddlewareMixin + from . import models -class LocalConfigMiddleware(object): +class LocalConfigMiddleware(MiddlewareMixin): """A middleware to inject LocalConfig into request.""" def process_request(self, request): diff --git a/modoboa/core/migrations/0001_initial.py b/modoboa/core/migrations/0001_initial.py index 3824558f1..1be0eae38 100644 --- a/modoboa/core/migrations/0001_initial.py +++ b/modoboa/core/migrations/0001_initial.py @@ -67,8 +67,8 @@ class Migration(migrations.Migration): ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('object_id', models.PositiveIntegerField()), ('is_owner', models.BooleanField(default=False)), - ('content_type', models.ForeignKey(to='contenttypes.ContentType')), - ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), + ('content_type', models.ForeignKey(to='contenttypes.ContentType', on_delete=models.CASCADE)), + ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), ], options={ }, diff --git a/modoboa/core/models.py b/modoboa/core/models.py index 0d2b203f4..4750f645e 100644 --- a/modoboa/core/models.py +++ b/modoboa/core/models.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals from email.header import Header import re -from django.core.urlresolvers import reverse +from django.urls import reverse from django.db import models from django.utils.encoding import ( python_2_unicode_compatible, smart_bytes, smart_text, force_str @@ -407,8 +407,8 @@ def populate_callback(user, group="SimpleUsers"): @python_2_unicode_compatible class ObjectAccess(models.Model): - user = models.ForeignKey(User) - content_type = models.ForeignKey(ContentType) + user = models.ForeignKey(User, on_delete=models.CASCADE) + content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) object_id = models.PositiveIntegerField() content_object = GenericForeignKey('content_type', 'object_id') is_owner = models.BooleanField(default=False) @@ -435,7 +435,7 @@ class LocalConfig(models.Model): """Store instance configuration here.""" api_pk = models.PositiveIntegerField(null=True) - site = models.ForeignKey("sites.Site") + site = models.ForeignKey("sites.Site", on_delete=models.CASCADE) # API results cache api_versions = jsonfield.JSONField() diff --git a/modoboa/core/password_hashers/advanced.py b/modoboa/core/password_hashers/advanced.py index ad27ae865..0f526221c 100644 --- a/modoboa/core/password_hashers/advanced.py +++ b/modoboa/core/password_hashers/advanced.py @@ -34,7 +34,8 @@ class BLFCRYPTHasher(PasswordHasher): # rounds = parameters.get_global_parameter("rounds_number") # To get around this, I use the default of 12. rounds = 12 - return bcrypt.encrypt(clearvalue, rounds=rounds) + bcrypt.using(rounds=rounds) + return bcrypt.hash(clearvalue) def verify(self, clearvalue, hashed_value): return bcrypt.verify(clearvalue, hashed_value) @@ -55,7 +56,7 @@ class MD5CRYPTHasher(PasswordHasher): return pwhash def _encrypt(self, clearvalue, salt=None): - return md5_crypt.encrypt(clearvalue) + return md5_crypt.hash(clearvalue) def verify(self, clearvalue, hashed_value): return md5_crypt.verify(clearvalue, hashed_value) @@ -78,7 +79,8 @@ class SHA256CRYPTHasher(PasswordHasher): def _encrypt(self, clearvalue, salt=None): rounds = param_tools.get_global_parameter("rounds_number") - return sha256_crypt.encrypt(clearvalue, rounds=rounds) + sha256_crypt.using(rounds=rounds) + return sha256_crypt.hash(clearvalue) def verify(self, clearvalue, hashed_value): return sha256_crypt.verify(clearvalue, hashed_value) @@ -101,7 +103,8 @@ class SHA512CRYPTHasher(PasswordHasher): def _encrypt(self, clearvalue, salt=None): rounds = param_tools.get_global_parameter("rounds_number") - return sha512_crypt.encrypt(clearvalue, rounds=rounds) + sha512_crypt.using(rounds=rounds) + return sha512_crypt.hash(clearvalue) def verify(self, clearvalue, hashed_value): return sha512_crypt.verify(clearvalue, hashed_value) diff --git a/modoboa/core/templatetags/core_tags.py b/modoboa/core/templatetags/core_tags.py index 44d0735a4..25ef503af 100644 --- a/modoboa/core/templatetags/core_tags.py +++ b/modoboa/core/templatetags/core_tags.py @@ -10,7 +10,7 @@ import re from django import template from django.conf import settings -from django.core.urlresolvers import reverse +from django.urls import reverse from django.template.loader import render_to_string from django.utils import timezone from django.utils.encoding import smart_text @@ -158,7 +158,7 @@ def colorize_level(level): @register.filter def tohtml(message): """Simple tag to format a text using HTML.""" - return re.sub(r"'(.*?)'", "\g<1>", message) + return re.sub(r"'(.*?)'", r"\g<1>", message) @register.simple_tag diff --git a/modoboa/core/tests/test_authentication.py b/modoboa/core/tests/test_authentication.py index 22df9d964..08c7fb4dc 100644 --- a/modoboa/core/tests/test_authentication.py +++ b/modoboa/core/tests/test_authentication.py @@ -10,7 +10,7 @@ from unittest import skipIf from mock import patch from django.core import mail -from django.core.urlresolvers import reverse +from django.urls import reverse from django.test import override_settings from modoboa.lib.tests import ModoTestCase diff --git a/modoboa/core/tests/test_core.py b/modoboa/core/tests/test_core.py index 6ca505c39..5efa811c5 100644 --- a/modoboa/core/tests/test_core.py +++ b/modoboa/core/tests/test_core.py @@ -8,7 +8,7 @@ from dateutil.relativedelta import relativedelta from six import StringIO from django.core import management -from django.core.urlresolvers import reverse +from django.urls import reverse from django.test import TestCase from django.utils import timezone diff --git a/modoboa/core/tests/test_ldap.py b/modoboa/core/tests/test_ldap.py index d7b166594..2de9be271 100644 --- a/modoboa/core/tests/test_ldap.py +++ b/modoboa/core/tests/test_ldap.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals from unittest import skipIf -from django.core.urlresolvers import reverse +from django.urls import reverse from django.test import override_settings from django.utils.functional import cached_property diff --git a/modoboa/core/tests/test_views.py b/modoboa/core/tests/test_views.py index 8e06bdfce..0c433c16b 100644 --- a/modoboa/core/tests/test_views.py +++ b/modoboa/core/tests/test_views.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals from testfixtures import compare from django import forms -from django.core.urlresolvers import reverse +from django.urls import reverse from modoboa.lib.tests import ModoTestCase from modoboa.parameters import forms as param_forms diff --git a/modoboa/core/views/auth.py b/modoboa/core/views/auth.py index 2ca28c56a..577263144 100644 --- a/modoboa/core/views/auth.py +++ b/modoboa/core/views/auth.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import logging -from django.core.urlresolvers import reverse +from django.urls import reverse from django.http import HttpResponse, HttpResponseRedirect from django.template.loader import render_to_string from django.utils import translation diff --git a/modoboa/core/views/base.py b/modoboa/core/views/base.py index 8d4634bf3..7b6291bac 100644 --- a/modoboa/core/views/base.py +++ b/modoboa/core/views/base.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals -from django.core.urlresolvers import reverse +from django.urls import reverse from django.utils.http import is_safe_url from django.views import generic diff --git a/modoboa/lib/authbackends.py b/modoboa/lib/authbackends.py index 995c24272..b46db0855 100644 --- a/modoboa/lib/authbackends.py +++ b/modoboa/lib/authbackends.py @@ -29,9 +29,9 @@ class SMTPBackend(object): smtp.login(username, password) except smtplib.SMTPException: return None - return self.get_or_create_user(username) + return self.get_or_build_user(username) - def get_or_create_user(self, username): + def get_or_build_user(self, username): """Get a user or create it the first time. .. note:: @@ -71,7 +71,7 @@ try: self.global_params = dict( param_tools.get_global_parameters("core")) - def get_or_create_user(self, username, ldap_user): + def get_or_build_user(self, username, ldap_user): """ This must return a (User, created) 2-tuple for the given LDAP user. username is the Django-friendly username of diff --git a/modoboa/lib/form_utils.py b/modoboa/lib/form_utils.py index 9aa259ff7..74abb7a37 100644 --- a/modoboa/lib/form_utils.py +++ b/modoboa/lib/form_utils.py @@ -11,12 +11,8 @@ import re from django.forms import TypedChoiceField from django.forms.fields import Field from django.forms.widgets import RadioSelect -from django.forms.widgets import RadioChoiceInput from django.shortcuts import render -from django.utils.encoding import ( - python_2_unicode_compatible, force_text, force_str) -from django.utils.html import conditional_escape -from django.utils.safestring import mark_safe +from django.utils.encoding import force_str from django.utils.translation import ugettext as _, ugettext_lazy from modoboa.lib.exceptions import BadRequest @@ -198,6 +194,9 @@ class DynamicForm(object): """ + fields = {} + data = {} + def _create_field(self, typ, name, value=None, pos=None): """Create a new form field. """ @@ -371,41 +370,9 @@ class TabForms(object): # -@python_2_unicode_compatible -class CustomRadioInput(RadioChoiceInput): - """Custom radio input.""" - - def __str__(self): - if "id" in self.attrs: - label_for = ' for="%s"' % self.attrs["id"] - else: - label_for = "" - choice_label = conditional_escape(force_text(self.choice_label)) - return mark_safe( - "" - % (label_for, self.tag(), choice_label) - ) - - -class InlineRadioRenderer(RadioSelect.renderer): - """Custom inline radio renderer.""" - - def __iter__(self): - for i, choice in enumerate(self.choices): - yield CustomRadioInput( - self.name, self.value, self.attrs.copy(), choice, i - ) - - def render(self): - return mark_safe( - u"\n".join([u"%s\n" % force_text(w) for w in self]) - ) - - -class InlineRadioSelect(RadioSelect): - """Custom inline radio widget.""" - - renderer = InlineRadioRenderer +class HorizontalRadioSelect(RadioSelect): + template_name = "common/horizontal_select.html" + option_template_name = "common/horizontal_select_option.html" class SeparatorField(Field): @@ -426,7 +393,7 @@ class YesNoField(TypedChoiceField): (True, ugettext_lazy("Yes")), (False, ugettext_lazy("No")) ), - "widget": InlineRadioSelect, + "widget": HorizontalRadioSelect(), "coerce": lambda x: x == "True" }) super(YesNoField, self).__init__(*args, **kwargs) diff --git a/modoboa/lib/middleware.py b/modoboa/lib/middleware.py index 6fd7c77f2..684726d65 100644 --- a/modoboa/lib/middleware.py +++ b/modoboa/lib/middleware.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals from django.http import HttpResponseRedirect from django.utils.encoding import smart_text +from django.utils.deprecation import MiddlewareMixin from modoboa.lib.exceptions import ModoboaException from modoboa.lib import signals as lib_signals @@ -14,7 +15,7 @@ from modoboa.lib.web_utils import ( ) -class AjaxLoginRedirect(object): +class AjaxLoginRedirect(MiddlewareMixin): def process_response(self, request, response): if request.is_ajax(): @@ -23,7 +24,7 @@ class AjaxLoginRedirect(object): return response -class CommonExceptionCatcher(object): +class CommonExceptionCatcher(MiddlewareMixin): """Modoboa exceptions catcher.""" def process_exception(self, request, exception): @@ -44,7 +45,7 @@ class CommonExceptionCatcher(object): ) -class RequestCatcherMiddleware(object): +class RequestCatcherMiddleware(MiddlewareMixin): """Simple middleware to store the current request.""" def process_request(self, request): diff --git a/modoboa/lib/migrations/0001_initial.py b/modoboa/lib/migrations/0001_initial.py index 02453b798..6f5cd6f93 100644 --- a/modoboa/lib/migrations/0001_initial.py +++ b/modoboa/lib/migrations/0001_initial.py @@ -29,7 +29,7 @@ class Migration(migrations.Migration): ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('name', models.CharField(max_length=255)), ('value', models.CharField(max_length=255)), - ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), + ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), ], options={ }, diff --git a/modoboa/lib/permissions.py b/modoboa/lib/permissions.py index 72a14621e..73cc4439c 100644 --- a/modoboa/lib/permissions.py +++ b/modoboa/lib/permissions.py @@ -51,8 +51,6 @@ def grant_access_to_object(user, obj, is_owner=False): :param obj: an admin. object (Domain, Mailbox, ...) :param is_owner: the user is the unique object's owner """ - from modoboa.core.models import User - ct = ContentType.objects.get_for_model(obj) entry, created = ObjectAccess.objects.get_or_create( user=user, content_type=ct, object_id=obj.id) diff --git a/modoboa/lib/validators.py b/modoboa/lib/validators.py index c440eb805..bd5e29918 100644 --- a/modoboa/lib/validators.py +++ b/modoboa/lib/validators.py @@ -34,6 +34,7 @@ class HostnameValidator(object): if not self.regex.match(value): raise ValidationError(self.message, self.code) + validate_hostname = HostnameValidator() @@ -42,15 +43,16 @@ class UTF8EmailValidator(EmailValidator): """Validator for addresses using non-ASCII characters.""" # unicode letters range (must be a unicode string, not a raw string) - ul = u"\u00a1-\uffff" - ascii_set = u"-!#$%&'*+/=?^_`{}|~0-9A-Z" + ul = "\u00a1-\uffff" + ascii_set = "-!#$%&'*+/=?^_`{}|~0-9A-Z" user_regex_raw = ( # dot-atom - r"^[" + ascii_set + ul + "]+(\.[" + ascii_set + ul + "]+)*\Z" + r"^[" + ascii_set + ul + r"]+(\.[" + ascii_set + ul + r"]+)*\Z" # quoted-string r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"\Z)' ) - user_regex = re.compile("(" + user_regex_raw, re.IGNORECASE) + user_regex = re.compile(r"(" + user_regex_raw, re.IGNORECASE) + validate_utf8_email = UTF8EmailValidator() @@ -62,4 +64,5 @@ class UTF8AndEmptyUserEmailValidator(UTF8EmailValidator): user_regex = re.compile( r"(^$|" + UTF8EmailValidator.user_regex_raw, re.IGNORECASE) + validate_utf8_and_empty_user_email = UTF8AndEmptyUserEmailValidator() diff --git a/modoboa/lib/web_utils.py b/modoboa/lib/web_utils.py index 555a1ebf4..2596c94de 100644 --- a/modoboa/lib/web_utils.py +++ b/modoboa/lib/web_utils.py @@ -114,9 +114,9 @@ def size2integer(value): :param value: the string to convert :return: the corresponding integer value """ - m = re.match("(\d+)\s*(\w+)", value) + m = re.match(r"(\d+)\s*(\w+)", value) if m is None: - if re.match("\d+", value): + if re.match(r"\d+", value): return int(value) return 0 if m.group(2)[0] in ["K", "k"]: diff --git a/modoboa/limits/migrations/0001_initial.py b/modoboa/limits/migrations/0001_initial.py index 468235c9b..2c1d96230 100644 --- a/modoboa/limits/migrations/0001_initial.py +++ b/modoboa/limits/migrations/0001_initial.py @@ -29,7 +29,7 @@ class Migration(migrations.Migration): name='LimitsPool', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('user', models.OneToOneField(to=settings.AUTH_USER_MODEL)), + ('user', models.OneToOneField(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), ], options={ 'db_table': 'limits_limitspool', @@ -39,7 +39,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='limit', name='pool', - field=models.ForeignKey(to='limits.LimitsPool'), + field=models.ForeignKey(to='limits.LimitsPool', on_delete=models.CASCADE), preserve_default=True, ), migrations.AlterUniqueTogether( diff --git a/modoboa/limits/migrations/0003_auto_20160413_1046.py b/modoboa/limits/migrations/0003_auto_20160413_1046.py index 438479931..625a90568 100644 --- a/modoboa/limits/migrations/0003_auto_20160413_1046.py +++ b/modoboa/limits/migrations/0003_auto_20160413_1046.py @@ -58,8 +58,8 @@ class Migration(migrations.Migration): ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('name', models.CharField(max_length=254)), ('max_value', models.IntegerField(default=-2)), - ('content_type', models.ForeignKey(to='contenttypes.ContentType')), - ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), + ('content_type', models.ForeignKey(to='contenttypes.ContentType', on_delete=models.CASCADE)), + ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), ], ), migrations.AlterUniqueTogether( diff --git a/modoboa/limits/migrations/0005_auto_20160415_1654.py b/modoboa/limits/migrations/0005_auto_20160415_1654.py index ab97e7807..9b05b9f08 100644 --- a/modoboa/limits/migrations/0005_auto_20160415_1654.py +++ b/modoboa/limits/migrations/0005_auto_20160415_1654.py @@ -33,7 +33,7 @@ class Migration(migrations.Migration): ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('name', models.CharField(max_length=254)), ('max_value', models.IntegerField(default=0)), - ('domain', models.ForeignKey(to='admin.Domain')), + ('domain', models.ForeignKey(to='admin.Domain', on_delete=models.CASCADE)), ], bases=(modoboa.limits.models.ObjectLimitMixin, models.Model), ), diff --git a/modoboa/limits/models.py b/modoboa/limits/models.py index 0797a7557..114a933c6 100644 --- a/modoboa/limits/models.py +++ b/modoboa/limits/models.py @@ -69,9 +69,10 @@ class ObjectLimitMixin(object): class UserObjectLimit(ObjectLimitMixin, models.Model): """Object level limit.""" - user = models.ForeignKey(settings.AUTH_USER_MODEL) + user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) name = models.CharField(max_length=254) - content_type = models.ForeignKey("contenttypes.ContentType") + content_type = models.ForeignKey("contenttypes.ContentType", + on_delete=models.CASCADE) max_value = models.IntegerField(default=0) class Meta(object): @@ -132,7 +133,7 @@ class UserObjectLimit(ObjectLimitMixin, models.Model): class DomainObjectLimit(ObjectLimitMixin, models.Model): """Per-domain limits on object creation.""" - domain = models.ForeignKey("admin.Domain") + domain = models.ForeignKey("admin.Domain", on_delete=models.CASCADE) name = models.CharField(max_length=254) max_value = models.IntegerField(default=0) diff --git a/modoboa/limits/tests/test_api.py b/modoboa/limits/tests/test_api.py index ecfbe9d8f..4707c244b 100644 --- a/modoboa/limits/tests/test_api.py +++ b/modoboa/limits/tests/test_api.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals from testfixtures import compare -from django.core.urlresolvers import reverse +from django.urls import reverse from rest_framework.authtoken.models import Token diff --git a/modoboa/limits/tests/test_domain_limits.py b/modoboa/limits/tests/test_domain_limits.py index 3d9f44f99..92f6c8749 100644 --- a/modoboa/limits/tests/test_domain_limits.py +++ b/modoboa/limits/tests/test_domain_limits.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals -from django.core.urlresolvers import reverse +from django.urls import reverse from modoboa.admin import factories as admin_factories from modoboa.admin.models import Domain diff --git a/modoboa/limits/tests/test_import.py b/modoboa/limits/tests/test_import.py index dc743b438..1b61fd0c5 100644 --- a/modoboa/limits/tests/test_import.py +++ b/modoboa/limits/tests/test_import.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals from django.core.files.base import ContentFile -from django.core.urlresolvers import reverse +from django.urls import reverse from modoboa.admin import factories as admin_factories from modoboa.admin import models as admin_models diff --git a/modoboa/limits/tests/test_user_limits.py b/modoboa/limits/tests/test_user_limits.py index 2d87d0a72..39168a24b 100644 --- a/modoboa/limits/tests/test_user_limits.py +++ b/modoboa/limits/tests/test_user_limits.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals -from django.core.urlresolvers import reverse +from django.urls import reverse from modoboa.admin.factories import populate_database from modoboa.admin.models import Alias, Domain diff --git a/modoboa/relaydomains/migrations/0001_initial.py b/modoboa/relaydomains/migrations/0001_initial.py index 0f6bff791..84cf66c6a 100644 --- a/modoboa/relaydomains/migrations/0001_initial.py +++ b/modoboa/relaydomains/migrations/0001_initial.py @@ -17,8 +17,8 @@ class Migration(migrations.Migration): ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('target_host', models.CharField(help_text='Remote destination of this domain', max_length=255, verbose_name='target host')), ('verify_recipients', models.BooleanField(default=False, help_text='Check for valid recipients', verbose_name='verify recipients')), - ('dates', models.ForeignKey(to='admin.ObjectDates')), - ('domain', models.OneToOneField(null=True, to='admin.Domain')), + ('dates', models.ForeignKey(to='admin.ObjectDates', on_delete=models.CASCADE)), + ('domain', models.OneToOneField(null=True, to='admin.Domain', on_delete=models.CASCADE)), ], options={ 'ordering': ['domain__name'], @@ -40,7 +40,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='relaydomain', name='service', - field=models.ForeignKey(to='relaydomains.Service'), + field=models.ForeignKey(to='relaydomains.Service', on_delete=models.CASCADE), preserve_default=True, ), ] diff --git a/modoboa/relaydomains/tests.py b/modoboa/relaydomains/tests.py index e071fee22..86c1e4fef 100644 --- a/modoboa/relaydomains/tests.py +++ b/modoboa/relaydomains/tests.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals from django.core.files.base import ContentFile -from django.core.urlresolvers import reverse +from django.urls import reverse from django.test import TestCase from modoboa.admin import factories as admin_factories diff --git a/modoboa/templates/common/horizontal_select.html b/modoboa/templates/common/horizontal_select.html new file mode 100644 index 000000000..d7145f734 --- /dev/null +++ b/modoboa/templates/common/horizontal_select.html @@ -0,0 +1,7 @@ +{% with id=widget.attrs.id %}
{% endwith %} \ No newline at end of file diff --git a/modoboa/templates/common/horizontal_select_option.html b/modoboa/templates/common/horizontal_select_option.html new file mode 100644 index 000000000..80f603bf9 --- /dev/null +++ b/modoboa/templates/common/horizontal_select_option.html @@ -0,0 +1,2 @@ + +{% if wrap_label %}{% endif %} \ No newline at end of file diff --git a/modoboa/transport/handlers.py b/modoboa/transport/handlers.py index 91a581022..aae87601a 100644 --- a/modoboa/transport/handlers.py +++ b/modoboa/transport/handlers.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals -from django.core.urlresolvers import reverse +from django.urls import reverse from django.dispatch import receiver from django.utils.translation import ugettext as _ diff --git a/modoboa/transport/tests.py b/modoboa/transport/tests.py index 052128fd6..5a35dd917 100644 --- a/modoboa/transport/tests.py +++ b/modoboa/transport/tests.py @@ -2,7 +2,7 @@ import json -from django.core.urlresolvers import reverse +from django.urls import reverse from modoboa.lib.tests import ModoAPITestCase, ModoTestCase diff --git a/modoboa/transport/urls.py b/modoboa/transport/urls.py index 9a7319ac0..18b430451 100644 --- a/modoboa/transport/urls.py +++ b/modoboa/transport/urls.py @@ -8,10 +8,10 @@ from . import views urlpatterns = [ url(r"^$", views.TransportListView.as_view(), name="transport_list"), - url("^create/$", views.TransportCreateView.as_view(), + url(r"^create/$", views.TransportCreateView.as_view(), name="transport_create"), - url("^update/(?P