mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-18 05:51:26 +02:00
Merge pull request #1333 from fyfe/upgrade-django
Upgrade Django (Django-1.10 no longer supported)
This commit is contained in:
+20
-13
@@ -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
|
||||
|
||||
@@ -1 +1 @@
|
||||
django-auth-ldap>=1.2.13
|
||||
django-auth-ldap>=1.3.0
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 _
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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'))
|
||||
)
|
||||
|
||||
@@ -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)()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 _
|
||||
|
||||
@@ -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 _
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 }}': {
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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={
|
||||
},
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"'(.*?)'", "<strong>\g<1></strong>", message)
|
||||
return re.sub(r"'(.*?)'", r"<strong>\g<1></strong>", message)
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 class='radio-inline' %s>%s %s</label>"
|
||||
% (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)
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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={
|
||||
},
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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"]:
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{% with id=widget.attrs.id %}<div{% if id %} id="{{ id }}"{% endif %}{% if widget.attrs.class %} class="{{ widget.attrs.class }}"{% endif %}>
|
||||
{% for group, options, index in widget.optgroups %}
|
||||
{% for option in options %}
|
||||
{% include option.template_name with widget=option %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</div>{% endwith %}
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
{% if wrap_label %}<label{% if widget.attrs.id %} for="{{ widget.attrs.id }}"{% endif %}{% if widget.attrs.id %} for="{{ widget.attrs.id }}"{% endif %} class="radio-inline">{% endif %}{% include "django/forms/widgets/input.html" %}{% if wrap_label %} {{ widget.label }}</label>{% endif %}
|
||||
@@ -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 _
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import json
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.urls import reverse
|
||||
|
||||
from modoboa.lib.tests import ModoAPITestCase, ModoTestCase
|
||||
|
||||
|
||||
@@ -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<pk>\d+)/$", views.TransportUpdateView.as_view(),
|
||||
url(r"^update/(?P<pk>\d+)/$", views.TransportUpdateView.as_view(),
|
||||
name="transport_update"),
|
||||
url("^delete/(?P<pk>\d+)/$", views.TransportDeleteView.as_view(),
|
||||
url(r"^delete/(?P<pk>\d+)/$", views.TransportDeleteView.as_view(),
|
||||
name="transport_delete"),
|
||||
]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Transport views."""
|
||||
|
||||
from django import http
|
||||
from django.core.urlresolvers import reverse, reverse_lazy
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
from django.views import generic
|
||||
|
||||
|
||||
+6
-6
@@ -1,23 +1,23 @@
|
||||
django>=1.10,<=1.10.99
|
||||
django>=1.11.8,<=1.11.99
|
||||
django-braces==1.11.0
|
||||
django-ckeditor==5.2.1
|
||||
django-reversion==1.10.2
|
||||
django-ckeditor==5.2.2
|
||||
django-reversion==2.0.12
|
||||
django-subcommand2==0.1.1
|
||||
django-versionfield2>=0.5.0
|
||||
django-xforwardedfor-middleware
|
||||
|
||||
dj-database-url
|
||||
|
||||
coreapi==2.3.1
|
||||
coreapi==2.3.3
|
||||
coreapi-cli==1.0.6
|
||||
djangorestframework==3.7.1
|
||||
djangorestframework==3.7.3
|
||||
|
||||
argparse
|
||||
# bcrypt requires libffi-dev
|
||||
bcrypt==3.1.4
|
||||
dnspython==1.15.0
|
||||
feedparser==5.2.1
|
||||
gevent==1.2.1
|
||||
gevent==1.2.2
|
||||
ipaddress
|
||||
jsonfield==2.0.2
|
||||
passlib==1.7.1
|
||||
|
||||
+15
-3
@@ -7,7 +7,19 @@ import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_project.settings")
|
||||
|
||||
from django.core.management import execute_from_command_line
|
||||
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError:
|
||||
# The above import may fail for some other reason. Ensure that the
|
||||
# issue is really that Django is missing to avoid masking other
|
||||
# exceptions on Python 2.
|
||||
try:
|
||||
import django # noqa
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and "
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?"
|
||||
)
|
||||
raise
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
@@ -1,26 +1,29 @@
|
||||
"""
|
||||
Django settings for test_project 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/
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
import os
|
||||
from logging.handlers import SysLogHandler
|
||||
import os
|
||||
|
||||
from modoboa.test_settings import * # NOQA
|
||||
from modoboa.test_settings import * # noqa
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
|
||||
|
||||
# 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 = '!8o(-dbbl3e+*bh7nx-^xysdt)1gso*%@4ze4-9_9o+i&t--u_'
|
||||
@@ -37,6 +40,10 @@ ALLOWED_HOSTS = [
|
||||
|
||||
SITE_ID = 1
|
||||
|
||||
# Security settings
|
||||
|
||||
X_FRAME_OPTIONS = "SAMEORIGIN"
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = (
|
||||
@@ -47,8 +54,10 @@ INSTALLED_APPS = (
|
||||
'django.contrib.sites',
|
||||
'django.contrib.staticfiles',
|
||||
'reversion',
|
||||
'ckeditor',
|
||||
'ckeditor_uploader',
|
||||
'rest_framework',
|
||||
'rest_framework.authtoken'
|
||||
'rest_framework.authtoken',
|
||||
)
|
||||
|
||||
# A dedicated place to register Modoboa applications
|
||||
@@ -59,17 +68,18 @@ MODOBOA_APPS = (
|
||||
'modoboa.core',
|
||||
'modoboa.lib',
|
||||
'modoboa.admin',
|
||||
'modoboa.limits',
|
||||
'modoboa.transport',
|
||||
'modoboa.relaydomains',
|
||||
'modoboa.limits',
|
||||
'modoboa.parameters',
|
||||
# Modoboa extensions here.
|
||||
)
|
||||
|
||||
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',
|
||||
@@ -85,10 +95,88 @@ MIDDLEWARE_CLASSES = (
|
||||
)
|
||||
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
# 'modoboa.lib.authbackends.LDAPBackend',
|
||||
# 'modoboa.lib.authbackends.SMTPBackend',
|
||||
'django.contrib.auth.backends.ModelBackend',
|
||||
)
|
||||
|
||||
# Password validation rules
|
||||
# SMTP authentication
|
||||
# AUTH_SMTP_SERVER_ADDRESS = 'localhost'
|
||||
# AUTH_SMTP_SERVER_PORT = 25
|
||||
# AUTH_SMTP_SECURED_MODE = None # 'ssl' or 'starttls' are accepted
|
||||
|
||||
|
||||
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': TEMPLATE_DEBUG,
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'test_project.urls'
|
||||
|
||||
WSGI_APPLICATION = 'test_project.wsgi.application'
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/1.11/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/1.11/howto/static-files/
|
||||
|
||||
STATIC_URL = '/sitestatic/'
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, 'sitestatic')
|
||||
STATICFILES_DIRS = (
|
||||
os.path.join(BASE_DIR, '..', 'modoboa', 'bower_components'),
|
||||
)
|
||||
|
||||
MEDIA_URL = '/media/'
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
||||
|
||||
# Rest framework settings
|
||||
|
||||
REST_FRAMEWORK = {
|
||||
'DEFAULT_AUTHENTICATION_CLASSES': (
|
||||
'rest_framework.authentication.TokenAuthentication',
|
||||
'rest_framework.authentication.SessionAuthentication',
|
||||
),
|
||||
}
|
||||
|
||||
# Modoboa settings
|
||||
# MODOBOA_CUSTOM_LOGO = os.path.join(MEDIA_URL, "custom_logo.png")
|
||||
|
||||
# DOVECOT_LOOKUP_PATH = ('/path/to/dovecot', )
|
||||
|
||||
MODOBOA_API_URL = 'https://api.modoboa.org/1/'
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
@@ -113,77 +201,38 @@ AUTH_PASSWORD_VALIDATORS = [
|
||||
},
|
||||
]
|
||||
|
||||
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': False,
|
||||
},
|
||||
# CKeditor
|
||||
|
||||
CKEDITOR_UPLOAD_PATH = "uploads/"
|
||||
|
||||
CKEDITOR_IMAGE_BACKEND = "pillow"
|
||||
|
||||
CKEDITOR_RESTRICT_BY_USER = True
|
||||
|
||||
CKEDITOR_BROWSE_SHOW_DIRS = True
|
||||
|
||||
CKEDITOR_ALLOW_NONIMAGE_FILES = False
|
||||
|
||||
CKEDITOR_CONFIGS = {
|
||||
'default': {
|
||||
'allowedContent': True,
|
||||
'toolbar': 'Modoboa',
|
||||
'width': None,
|
||||
'toolbar_Modoboa': [
|
||||
['Bold', 'Italic', 'Underline'],
|
||||
['JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock'],
|
||||
['BidiLtr', 'BidiRtl', 'Language'],
|
||||
['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent'],
|
||||
['Undo', 'Redo'],
|
||||
['Link', 'Unlink', 'Anchor', '-', 'Smiley'],
|
||||
['TextColor', 'BGColor', '-', 'Source'],
|
||||
['Font', 'FontSize'],
|
||||
['Image', ],
|
||||
['SpellChecker']
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'test_project.urls'
|
||||
|
||||
WSGI_APPLICATION = 'test_project.wsgi.application'
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/1.6/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = False
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/1.6/howto/static-files/
|
||||
|
||||
STATIC_URL = '/sitestatic/'
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, 'sitestatic')
|
||||
STATICFILES_DIRS = (
|
||||
os.path.join(BASE_DIR, '..', 'modoboa', 'bower_components'),
|
||||
)
|
||||
|
||||
MEDIA_URL = '/media/'
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
||||
|
||||
# Rest framework settings
|
||||
|
||||
REST_FRAMEWORK = {
|
||||
'DEFAULT_AUTHENTICATION_CLASSES': (
|
||||
'rest_framework.authentication.TokenAuthentication',
|
||||
'rest_framework.authentication.SessionAuthentication',
|
||||
),
|
||||
'DEFAULT_PERMISSION_CLASSES': (
|
||||
'rest_framework.permissions.IsAuthenticated',
|
||||
)
|
||||
}
|
||||
|
||||
# Modoboa settings
|
||||
# MODOBOA_CUSTOM_LOGO = os.path.join(MEDIA_URL, "custom_logo.png")
|
||||
|
||||
# DOVECOT_LOOKUP_PATH = ('/path/to/dovecot', )
|
||||
|
||||
MODOBOA_API_URL = 'http://api.modoboa.org/1/'
|
||||
|
||||
# Logging configuration
|
||||
|
||||
LOGGING = {
|
||||
@@ -216,3 +265,5 @@ LOGGING = {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Load settings from extensions
|
||||
|
||||
@@ -3,5 +3,5 @@ from __future__ import unicode_literals
|
||||
from django.conf.urls import include, url
|
||||
|
||||
urlpatterns = [
|
||||
url(r'', include('modoboa.urls')),
|
||||
url(r"", include("modoboa.urls")),
|
||||
]
|
||||
|
||||
@@ -4,13 +4,15 @@ WSGI config for test_project project.
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/
|
||||
https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_project.settings")
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_project.settings")
|
||||
|
||||
application = get_wsgi_application()
|
||||
|
||||
Reference in New Issue
Block a user