Permalink
Browse files

Generic transport editor. (#1309)

Generic transport editor.

see #1301

* Fixed unit test.

* Added unit tests.

* Fixed unit test.

* Added transport module to test list.

* Moved relay transport backend to appropriate app.

see #1301

* Added missing file.
  • Loading branch information...
tonioo committed Nov 27, 2017
1 parent 1129c94 commit 98eea782a080a3cdfea5abea7d288ff3d49595c6
Showing with 1,620 additions and 610 deletions.
  1. +3 −0 .gitignore
  2. +2 −1 .travis.yml
  3. +12 −0 .tx/config
  4. +8 −0 modoboa/admin/constants.py
  5. +8 −14 modoboa/admin/forms/domain.py
  6. +19 −0 modoboa/admin/models/base.py
  7. +6 −27 modoboa/admin/models/domain.py
  8. +2 −0 modoboa/admin/models/mailbox.py
  9. +2 −2 modoboa/admin/signals.py
  10. +1 −1 modoboa/admin/tests/test_repair.py
  11. +4 −5 modoboa/admin/views/domain.py
  12. +1 −0 modoboa/core/commands/templates/settings.py.tpl
  13. +4 −8 modoboa/core/management/commands/load_initial_data.py
  14. +1 −2 modoboa/core/tests/test_views.py
  15. +1 −1 modoboa/limits/forms.py
  16. +0 −24 modoboa/relaydomains/app_settings.py
  17. +1 −10 modoboa/relaydomains/apps.py
  18. +0 −14 modoboa/relaydomains/constants.py
  19. +29 −38 modoboa/relaydomains/forms.py
  20. +45 −82 modoboa/relaydomains/handlers.py
  21. +23 −2 modoboa/relaydomains/lib.py
  22. +23 −0 modoboa/relaydomains/migrations/0007_recipientaccess.py
  23. +52 −0 modoboa/relaydomains/migrations/0008_auto_20171123_1653.py
  24. +29 −0 modoboa/relaydomains/migrations/0009_auto_20171124_1508.py
  25. +4 −131 modoboa/relaydomains/models.py
  26. +12 −58 modoboa/relaydomains/postfix_maps.py
  27. +0 −70 modoboa/relaydomains/static/relaydomains/js/relay_domains.js
  28. +3 −4 modoboa/relaydomains/templates/relaydomains/relaydomain_form.html
  29. +89 −65 modoboa/relaydomains/tests.py
  30. +29 −0 modoboa/relaydomains/transport.py
  31. +0 −13 modoboa/relaydomains/urls.py
  32. +0 −26 modoboa/relaydomains/views.py
  33. +9 −0 modoboa/static/css/custom.css
  34. +2 −2 modoboa/static/js/global.js
  35. +9 −6 modoboa/templates/twocols.html
  36. +1 −0 modoboa/transport/__init__.py
  37. +3 −0 modoboa/transport/admin.py
  38. +11 −0 modoboa/transport/apps.py
  39. +105 −0 modoboa/transport/backends.py
  40. +5 −0 modoboa/transport/constants.py
  41. +20 −0 modoboa/transport/factories.py
  42. +94 −0 modoboa/transport/forms.py
  43. +31 −0 modoboa/transport/handlers.py
  44. +124 −0 modoboa/transport/locale/en/LC_MESSAGES/django.po
  45. +22 −0 modoboa/transport/locale/en/LC_MESSAGES/djangojs.po
  46. +124 −0 modoboa/transport/locale/fr/LC_MESSAGES/django.po
  47. +22 −0 modoboa/transport/locale/fr/LC_MESSAGES/djangojs.po
  48. +34 −0 modoboa/transport/migrations/0001_initial.py
  49. 0 modoboa/transport/migrations/__init__.py
  50. +32 −0 modoboa/transport/models.py
  51. +24 −0 modoboa/transport/postfix_maps.py
  52. +49 −0 modoboa/transport/serializers.py
  53. +43 −0 modoboa/transport/static/transport/js/transport.js
  54. +14 −0 modoboa/transport/templates/transport/_transport_form.html
  55. +5 −0 modoboa/transport/templates/transport/_transport_headers.html
  56. +66 −0 modoboa/transport/templates/transport/transport_list.html
  57. +163 −0 modoboa/transport/tests.py
  58. +17 −0 modoboa/transport/urls.py
  59. +11 −0 modoboa/transport/urls_api.py
  60. +142 −0 modoboa/transport/views.py
  61. +18 −0 modoboa/transport/viewsets.py
  62. +2 −2 modoboa/urls.py
  63. +1 −0 modoboa/urls_api.py
  64. +3 −2 requirements.txt
  65. +1 −0 test_project/test_project/settings.py
View
@@ -12,3 +12,6 @@ DEV
modoboa.iml
.tox
modoboa_test
.coverage
build/
dist/
View
@@ -4,6 +4,7 @@ cache: pip
python:
- "2.7"
- "3.4"
- "3.6"
addons:
apt:
packages:
@@ -43,7 +44,7 @@ before_script:
script:
- python ./tests.py
- cd test_project
- coverage run --source ../modoboa manage.py test modoboa.core modoboa.lib modoboa.admin modoboa.limits modoboa.relaydomains
- coverage run --source ../modoboa manage.py test modoboa.core modoboa.lib modoboa.admin modoboa.limits modoboa.transport modoboa.relaydomains
after_success:
- codecov
View
@@ -42,3 +42,15 @@ file_filter = modoboa/admin/locale/<lang>/LC_MESSAGES/djangojs.po
source_file = modoboa/admin/locale/en/LC_MESSAGES/djangojs.po
source_lang = en
type = PO
[modoboa.transport-djangopo]
file_filter = modoboa/transport/locale/<lang>/LC_MESSAGES/django.po
source_file = modoboa/transport/locale/en/LC_MESSAGES/django.po
source_lang = en
type = PO
[modoboa.transport-djangojspo]
file_filter = modoboa/transport/locale/<lang>/LC_MESSAGES/djangojs.po
source_file = modoboa/transport/locale/en/LC_MESSAGES/djangojs.po
source_lang = en
type = PO
@@ -2,6 +2,9 @@
from __future__ import unicode_literals
from django.utils.translation import ugettext_lazy as _
DNSBL_PROVIDERS = [
"aspews.ext.sorbs.net",
"b.barracudacentral.org",
@@ -91,3 +94,8 @@
"localhost",
"test"
]
DOMAIN_TYPES = [
("domain", _("Domain")),
("relaydomain", _("Relay domain")),
]
@@ -20,6 +20,7 @@
from modoboa.lib.web_utils import render_to_json_response
from modoboa.parameters import tools as param_tools
from .. import constants
from .. import lib
from .. import signals
@@ -28,11 +29,6 @@
)
DOMAIN_TYPES = [
("domain", _("Domain")),
]
class DomainFormGeneral(forms.ModelForm, DynamicForm):
"""A form to create/edit a domain."""
@@ -63,12 +59,7 @@ def __init__(self, user, *args, **kwargs):
self.fields["quota"].initial = params["default_domain_quota"]
self.fields["default_mailbox_quota"].initial = (
params["default_mailbox_quota"])
extra_domain_types = reduce(
lambda a, b: a + b,
[result[1] for result in signals.extra_domain_types.send(
sender=self.__class__)]
)
self.fields["type"].choices = DOMAIN_TYPES + extra_domain_types
self.fields["type"].choices = constants.DOMAIN_TYPES
self.field_widths = {
"quota": 3,
"default_mailbox_quota": 3
@@ -382,10 +373,13 @@ def save(self):
first_form = self.forms[0]["instance"]
options = {}
if isinstance(first_form, DomainFormGeneral):
options["domalias_post_create"] = True
first_form.save(self.request.user, **options)
domain = first_form.save(
self.request.user, domalias_post_create=True)
options.update({"domain": domain})
else:
first_form.save(self.request.user)
for f in self.forms[1:]:
f["instance"].save(self.request.user)
f["instance"].save(self.request.user, **options)
def done(self):
return render_to_json_response(_("Domain modified"))
@@ -5,11 +5,27 @@
from django.db import models
from django.utils import timezone
from django.contrib.contenttypes.fields import GenericRelation
from modoboa.core import models as core_models
from modoboa.lib.permissions import (
grant_access_to_object, ungrant_access_to_object
)
class AdminObjectManager(models.Manager):
def get_for_admin(self, admin):
"""Return the objects belonging to this admin
The result is a ``QuerySet`` object, so this function can be used
to fill ``ModelChoiceField`` objects.
"""
if admin.is_superuser:
return self.get_queryset()
return self.get_queryset().filter(owners__user=admin)
class AdminObject(models.Model):
"""Abstract model to support dates.
@@ -20,8 +36,11 @@ class AdminObject(models.Model):
creation = models.DateTimeField(default=timezone.now)
last_modification = models.DateTimeField(auto_now=True)
owners = GenericRelation(core_models.ObjectAccess)
_objectname = None
objects = AdminObjectManager()
class Meta:
abstract = True
@@ -3,42 +3,24 @@
from __future__ import unicode_literals
import datetime
from functools import reduce
from django.db import models
from django.db.models.manager import Manager
from django.utils import timezone
from django.utils.encoding import (
python_2_unicode_compatible, smart_text, force_text
)
from django.utils.functional import cached_property
from django.utils.translation import ugettext as _, ugettext_lazy
from django.contrib.contenttypes.fields import GenericRelation
from reversion import revisions as reversion
from modoboa.core import signals as core_signals
from modoboa.core.models import User, ObjectAccess
from modoboa.core.models import User
from modoboa.lib.exceptions import BadRequest, Conflict
from modoboa.parameters import tools as param_tools
from .base import AdminObject
from .. import constants
from .. import signals
class DomainManager(Manager):
def get_for_admin(self, admin):
"""Return the domains belonging to this admin
The result is a ``QuerySet`` object, so this function can be used
to fill ``ModelChoiceField`` objects.
"""
if admin.is_superuser:
return self.get_queryset()
return self.get_queryset().filter(owners__user=admin)
@python_2_unicode_compatible
@@ -67,15 +49,12 @@ class Domain(AdminObject):
help_text=ugettext_lazy("Check to activate this domain"),
default=True
)
owners = GenericRelation(ObjectAccess)
type = models.CharField(default="domain", max_length=20)
enable_dns_checks = models.BooleanField(
ugettext_lazy("Enable DNS checks"), default=True,
help_text=ugettext_lazy("Check to enable DNS checks for this domain")
)
objects = DomainManager()
class Meta:
permissions = (
("view_domain", "View domain"),
@@ -111,11 +90,11 @@ def identities_count(self):
@property
def tags(self):
if self.type == "domain":
return [{"name": "domain", "label": _("Domain"), "type": "dom"}]
results = signals.get_domain_tags.send(
sender=self.__class__, domain=self)
return reduce(lambda a, b: a + b, [result[1] for result in results])
label = ""
for dt in constants.DOMAIN_TYPES:
if self.type == dt[0]:
label = dt[1]
return [{"name": self.type, "label": label, "type": "dom"}]
@property
def admins(self):
@@ -345,6 +345,7 @@ def save(self, *args, **kwargs):
username=self.full_address)
super(Mailbox, self).save(*args, **kwargs)
reversion.register(Mailbox)
@@ -365,6 +366,7 @@ def __str__(self):
"""Return address."""
return smart_text(self.address)
reversion.register(SenderAddress)
View
@@ -21,12 +21,12 @@
extra_domain_menu_entries = django.dispatch.Signal(providing_args=["user"])
extra_domain_qset_filters = django.dispatch.Signal(
providing_args=["domfilter", "extrafilters"])
extra_domain_types = django.dispatch.Signal()
# extra_domain_types = django.dispatch.Signal()
extra_domain_wizard_steps = django.dispatch.Signal()
get_account_form_instances = django.dispatch.Signal(
providing_args=["user", "account"])
get_domain_form_instances = django.dispatch.Signal(
providing_args=["user", "domain"])
get_domain_tags = django.dispatch.Signal(providing_args=["domain"])
# get_domain_tags = django.dispatch.Signal(providing_args=["domain"])
import_object = django.dispatch.Signal(providing_args=["objtype"])
use_external_recipients = django.dispatch.Signal(providing_args=["recipients"])
@@ -57,7 +57,7 @@ def test_management_command_with_no_alias(self):
"""Check that problem is fixed."""
count, detail = models.Alias.objects.filter(
address="user@test.com", internal=True).delete()
self.assertEqual(count, 2)
self.assertEqual(count, 3)
ret = management.call_command("modo", "repair", "--quiet")
assert ret is None
self.assertTrue(
@@ -45,11 +45,10 @@ def index(request):
)
def _domains(request):
sort_order, sort_dir = get_sort_order(request.GET, "name")
extra_filters = reduce(
lambda a, b: a + b,
[result[1] for result in
signals.extra_domain_filters.send(sender="_domains")]
)
extra_filters = signals.extra_domain_filters.send(sender="_domains")
if extra_filters:
extra_filters = reduce(
lambda a, b: a + b, [result[1] for result in extra_filters])
filters = dict(
(flt, request.GET.get(flt, None))
for flt in ["domfilter", "searchquery"] + extra_filters
@@ -63,6 +63,7 @@ MODOBOA_APPS = (
'modoboa.core',
'modoboa.lib',
'modoboa.admin',
'modoboa.transport',
'modoboa.relaydomains',
'modoboa.limits',
'modoboa.parameters',
@@ -14,7 +14,6 @@
from modoboa.lib.cryptutils import random_key
from modoboa.lib.permissions import add_permissions_to_group
import modoboa.relaydomains.models as relay_models
from ... import constants
from ... import extensions
@@ -58,18 +57,15 @@ def handle(self, *args, **options):
lc.parameters.set_value("secret_key", random_key())
lc.save()
for service_name in ["relay", "smtp"]:
relay_models.Service.objects.get_or_create(name=service_name)
groups = list(constants.PERMISSIONS.keys())
for groupname in groups:
group, created = Group.objects.get_or_create(name=groupname)
results = signals.extra_role_permissions.send(
sender=self.__class__, role=groupname)
permissions = (
constants.PERMISSIONS.get(groupname, []) +
reduce(lambda a, b: a + b, [result[1] for result in results])
)
permissions = constants.PERMISSIONS.get(groupname, [])
if results:
permissions += reduce(
lambda a, b: a + b, [result[1] for result in results])
if not permissions:
continue
add_permissions_to_group(group, permissions)
@@ -120,7 +120,6 @@ class SettingsTestCase(ModoTestCase):
"core-ldap_bind_password": "",
"limits-deflt_domain_domain_aliases_limit": "0",
"core-secret_key": ":?j3]QPWo!.'_c4n",
"relaydomains-master_cf_path": "/etc/postfix/master.cf",
"limits-deflt_domain_domain_admins_limit": "0",
"limits-enable_admin_limits": "True",
"core-ldap_bind_dn": "",
@@ -166,7 +165,7 @@ def test_get_settings(self):
"""Test settings display."""
url = reverse("core:parameters")
response = self.ajax_get(url)
for app in ["core", "admin", "limits", "relaydomains"]:
for app in ["core", "admin", "limits"]:
self.assertIn('data-app="{}"'.format(app), response["content"])
def test_save_settings(self):
View
@@ -92,7 +92,7 @@ def clean(self):
self.add_error(lname, _("Invalid limit"))
return cleaned_data
def save(self, user):
def save(self, user, **kwargs):
"""Set limits."""
for name, ltpl in utils.get_domain_limit_templates():
fieldname = "{}_limit".format(name)

This file was deleted.

Oops, something went wrong.
@@ -3,15 +3,6 @@
from __future__ import unicode_literals
from django.apps import AppConfig
from django.utils.translation import ugettext as _
def load_relaydomains_settings():
"""Load application settings."""
from modoboa.parameters import tools as param_tools
from .app_settings import AdminParametersForm
param_tools.registry.add("global", AdminParametersForm, _("Relay domains"))
class RelayDomainsConfig(AppConfig):
@@ -22,5 +13,5 @@ class RelayDomainsConfig(AppConfig):
verbose_name = "Modoboa relay domains"
def ready(self):
load_relaydomains_settings()
from . import handlers
from . import transport
Oops, something went wrong.

0 comments on commit 98eea78

Please sign in to comment.