mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-18 05:51:26 +02:00
wip: relay domains support.
* Integration with the amavis frontend * Per relay domain policies * One policy shared between a relay domain and its aliases * One 'users' record per object * Extra: better event handlers management see #491
This commit is contained in:
@@ -8,9 +8,7 @@ Provides:
|
||||
* Per-domain settings
|
||||
|
||||
"""
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.template import Template, Context
|
||||
from django.utils.translation import ugettext_lazy
|
||||
from modoboa.lib import events, parameters
|
||||
from modoboa.core.extensions import ModoExtension, exts_pool
|
||||
|
||||
@@ -41,136 +39,13 @@ class Amavis(ModoExtension):
|
||||
|
||||
def load(self):
|
||||
from .app_settings import ParametersForm, UserSettings
|
||||
|
||||
parameters.register(ParametersForm, "Amavis")
|
||||
parameters.register(UserSettings, ugettext_lazy("Quarantine"))
|
||||
from modoboa.extensions.amavis import general_callbacks
|
||||
|
||||
def destroy(self):
|
||||
events.unregister("UserMenuDisplay", menu)
|
||||
events.unregister_extension()
|
||||
parameters.unregister()
|
||||
|
||||
exts_pool.register_extension(Amavis)
|
||||
|
||||
|
||||
@events.observe("UserMenuDisplay")
|
||||
def menu(target, user):
|
||||
if target == "top_menu":
|
||||
return [
|
||||
{"name": "quarantine",
|
||||
"label": _("Quarantine"),
|
||||
"url": reverse('modoboa.extensions.amavis.views.index')}
|
||||
]
|
||||
return []
|
||||
|
||||
|
||||
@events.observe("DomainCreated")
|
||||
def on_domain_created(user, domain):
|
||||
from .models import Users, Policy
|
||||
p = Policy.objects.create(policy_name=domain.name)
|
||||
Users.objects.create(email="@%s" % domain.name, fullname=domain.name,
|
||||
priority=7, policy=p)
|
||||
|
||||
|
||||
@events.observe("DomainModified")
|
||||
def on_domain_modified(domain):
|
||||
if domain.oldname != domain.name:
|
||||
from .models import Users
|
||||
u = Users.objects.get(email="@%s" % domain.oldname)
|
||||
u.email = "@%s" % domain.name
|
||||
u.fullname = domain.name
|
||||
u.policy.policy_name = domain.name
|
||||
u.policy.save()
|
||||
u.save()
|
||||
|
||||
|
||||
@events.observe("DomainDeleted")
|
||||
def on_domain_deleted(domain):
|
||||
from .models import Users
|
||||
|
||||
try:
|
||||
u = Users.objects.get(email="@%s" % domain.name)
|
||||
except Users.DoesNotExist:
|
||||
return
|
||||
u.policy.delete()
|
||||
u.delete()
|
||||
|
||||
|
||||
@events.observe("GetStaticContent")
|
||||
def extra_static_content(caller, user):
|
||||
if user.group == "SimpleUsers":
|
||||
return []
|
||||
|
||||
if caller == 'top' and parameters.get_admin("USER_CAN_RELEASE") == 'no':
|
||||
tpl = Template("""<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
var poller = new Poller("{{ url }}", {
|
||||
interval: {{ interval }},
|
||||
success_cb: function(data) {
|
||||
var $link = $("#nbrequests");
|
||||
if (data.requests > 0) {
|
||||
$link.html(data.requests + " " + "{{ text }}");
|
||||
$link.parent().removeClass('hidden');
|
||||
} else {
|
||||
$link.parent().addClass('hidden');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
""")
|
||||
return [tpl.render(
|
||||
Context({
|
||||
'url': reverse("modoboa.extensions.amavis.views.nbrequests"),
|
||||
'interval': int(parameters.get_admin("CHECK_REQUESTS_INTERVAL")) * 1000,
|
||||
'text': _("pending requests"),
|
||||
})
|
||||
)]
|
||||
|
||||
if caller == 'domains':
|
||||
tpl = Template("""<script type="text/javascript">
|
||||
$(document).bind('domform_init', function() {
|
||||
activate_widget.call($('#id_spam_subject_tag2_act'));
|
||||
});
|
||||
</script>
|
||||
""")
|
||||
|
||||
return [tpl.render(Context({}))]
|
||||
return []
|
||||
|
||||
|
||||
@events.observe("TopNotifications")
|
||||
def display_requests(user):
|
||||
from .sql_listing import get_wrapper
|
||||
|
||||
if parameters.get_admin("USER_CAN_RELEASE") == "yes" \
|
||||
or user.group == "SimpleUsers":
|
||||
return []
|
||||
nbrequests = get_wrapper().get_pending_requests(user)
|
||||
|
||||
url = reverse("modoboa.extensions.amavis.views.index")
|
||||
url += "#listing/?viewrequests=1"
|
||||
tpl = Template('<div class="btn-group {{ css }}"><a id="nbrequests" href="{{ url }}" class="btn btn-danger">{{ label }}</a></div>')
|
||||
css = "hidden" if nbrequests == 0 else ""
|
||||
return [tpl.render(Context(dict(
|
||||
label=_("%d pending requests" % nbrequests), url=url, css=css
|
||||
)))]
|
||||
|
||||
|
||||
@events.observe("ExtraDomainForm")
|
||||
def extra_domain_form(user, domain):
|
||||
from .forms import DomainPolicyForm
|
||||
|
||||
if not user.has_perm("admin.view_domains"):
|
||||
return []
|
||||
return [
|
||||
dict(
|
||||
id="amavis", title=_("Content filter"), cls=DomainPolicyForm,
|
||||
formtpl="amavis/domain_content_filter.html"
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
@events.observe("FillDomainInstances")
|
||||
def fill_domain_instances(user, domain, instances):
|
||||
if not user.has_perm("admin.view_domains"):
|
||||
return
|
||||
instances["amavis"] = domain
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# coding: utf-8
|
||||
from django import forms
|
||||
from models import Policy, Users
|
||||
from modoboa.lib.formutils import InlineRadioSelect
|
||||
from modoboa.extensions.amavis.models import Policy, Users
|
||||
|
||||
|
||||
class DomainPolicyForm(forms.ModelForm):
|
||||
@@ -34,20 +34,21 @@ class DomainPolicyForm(forms.ModelForm):
|
||||
self.fields[f].required = False
|
||||
|
||||
def save(self, user, commit=True):
|
||||
p = super(DomainPolicyForm, self).save(commit=False)
|
||||
for f in ['bypass_spam_checks', 'bypass_virus_checks', 'bypass_banned_checks']:
|
||||
if getattr(p, f) == '':
|
||||
setattr(p, f, None)
|
||||
policy = super(DomainPolicyForm, self).save(commit=False)
|
||||
for field in ['bypass_spam_checks', 'bypass_virus_checks',
|
||||
'bypass_banned_checks']:
|
||||
if getattr(policy, field) == '':
|
||||
setattr(policy, field, None)
|
||||
|
||||
if self.cleaned_data['spam_subject_tag2_act']:
|
||||
p.spam_subject_tag2 = None
|
||||
policy.spam_subject_tag2 = None
|
||||
|
||||
if commit:
|
||||
p.save()
|
||||
policy.save()
|
||||
try:
|
||||
u = Users.objects.get(policy=p)
|
||||
u = Users.objects.get(fullname=policy.policy_name)
|
||||
except Users.DoesNotExist:
|
||||
u = Users.objects.get(email="@%s" % self.domain.name)
|
||||
u.policy = p
|
||||
p.save()
|
||||
return p
|
||||
u.policy = policy
|
||||
policy.save()
|
||||
return policy
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.template import Template, Context
|
||||
|
||||
from modoboa.lib import events, parameters
|
||||
from modoboa.extensions.amavis.lib import (
|
||||
create_user_and_policy, update_user_and_policy, delete_user_and_policy
|
||||
)
|
||||
|
||||
|
||||
@events.observe("UserMenuDisplay")
|
||||
def menu(target, user):
|
||||
if target == "top_menu":
|
||||
return [
|
||||
{"name": "quarantine",
|
||||
"label": _("Quarantine"),
|
||||
"url": reverse('modoboa.extensions.amavis.views.index')}
|
||||
]
|
||||
return []
|
||||
|
||||
|
||||
@events.observe("DomainCreated")
|
||||
def on_domain_created(user, domain):
|
||||
create_user_and_policy(domain.name)
|
||||
|
||||
|
||||
@events.observe("DomainModified")
|
||||
def on_domain_modified(domain):
|
||||
update_user_and_policy(domain.oldname, domain.name)
|
||||
|
||||
|
||||
@events.observe("DomainDeleted")
|
||||
def on_domain_deleted(domain):
|
||||
delete_user_and_policy(domain.name)
|
||||
|
||||
|
||||
@events.observe("GetStaticContent")
|
||||
def extra_static_content(caller, user):
|
||||
if user.group == "SimpleUsers":
|
||||
return []
|
||||
|
||||
if caller == 'top' and parameters.get_admin("USER_CAN_RELEASE") == 'no':
|
||||
tpl = Template("""<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
var poller = new Poller("{{ url }}", {
|
||||
interval: {{ interval }},
|
||||
success_cb: function(data) {
|
||||
var $link = $("#nbrequests");
|
||||
if (data.requests > 0) {
|
||||
$link.html(data.requests + " " + "{{ text }}");
|
||||
$link.parent().removeClass('hidden');
|
||||
} else {
|
||||
$link.parent().addClass('hidden');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
""")
|
||||
return [tpl.render(
|
||||
Context({
|
||||
'url': reverse("modoboa.extensions.amavis.views.nbrequests"),
|
||||
'interval': int(parameters.get_admin("CHECK_REQUESTS_INTERVAL")) * 1000,
|
||||
'text': _("pending requests"),
|
||||
})
|
||||
)]
|
||||
|
||||
if caller == 'domains':
|
||||
tpl = Template("""<script type="text/javascript">
|
||||
$(document).bind('domform_init', function() {
|
||||
activate_widget.call($('#id_spam_subject_tag2_act'));
|
||||
});
|
||||
</script>
|
||||
""")
|
||||
|
||||
return [tpl.render(Context({}))]
|
||||
return []
|
||||
|
||||
|
||||
@events.observe("TopNotifications")
|
||||
def display_requests(user):
|
||||
from .sql_listing import get_wrapper
|
||||
|
||||
if parameters.get_admin("USER_CAN_RELEASE") == "yes" \
|
||||
or user.group == "SimpleUsers":
|
||||
return []
|
||||
nbrequests = get_wrapper().get_pending_requests(user)
|
||||
|
||||
url = reverse("modoboa.extensions.amavis.views.index")
|
||||
url += "#listing/?viewrequests=1"
|
||||
tpl = Template('<div class="btn-group {{ css }}"><a id="nbrequests" href="{{ url }}" class="btn btn-danger">{{ label }}</a></div>')
|
||||
css = "hidden" if nbrequests == 0 else ""
|
||||
return [tpl.render(Context(dict(
|
||||
label=_("%d pending requests" % nbrequests), url=url, css=css
|
||||
)))]
|
||||
|
||||
|
||||
def send_amavis_form():
|
||||
"""
|
||||
"""
|
||||
from .forms import DomainPolicyForm
|
||||
return [{
|
||||
'id': 'amavis', 'title': _("Content filter"), 'cls': DomainPolicyForm,
|
||||
'formtpl': 'amavis/domain_content_filter.html'
|
||||
}]
|
||||
|
||||
|
||||
@events.observe("ExtraDomainForm")
|
||||
def extra_domain_form(user, domain):
|
||||
if not user.has_perm("admin.view_domains"):
|
||||
return []
|
||||
return send_amavis_form()
|
||||
|
||||
|
||||
@events.observe('ExtraRelayDomainForm')
|
||||
def extra_relaydomain_form(user, rdomain):
|
||||
if not user.has_perm("postfix_relay_domains.add_relaydomain"):
|
||||
return []
|
||||
return send_amavis_form()
|
||||
|
||||
|
||||
@events.observe("FillDomainInstances")
|
||||
def fill_domain_instances(user, domain, instances):
|
||||
if not user.has_perm("admin.view_domains"):
|
||||
return
|
||||
instances["amavis"] = domain
|
||||
|
||||
|
||||
@events.observe("FillRelayDomainInstances")
|
||||
def fill_relaydomain_instances(user, rdomain, instances):
|
||||
if not user.has_perm("postfix_relay_domains.add_relaydomain"):
|
||||
return
|
||||
instances["amavis"] = rdomain
|
||||
@@ -8,6 +8,7 @@ from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext as _
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.exceptions import InternalError
|
||||
from modoboa.extensions.amavis.models import Users, Policy
|
||||
|
||||
|
||||
def selfservice(ssfunc=None):
|
||||
@@ -76,3 +77,71 @@ recipient=%s
|
||||
if re.search("250 [\d\.]+ Ok", answer):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def create_user_and_policy(name):
|
||||
"""Create records.
|
||||
|
||||
Create two records (a user and a policy) using :keyword:`name` as
|
||||
an identifier.
|
||||
|
||||
:param str name: name
|
||||
"""
|
||||
policy = Policy.objects.create(policy_name=name)
|
||||
Users.objects.create(
|
||||
email="@%s" % name, fullname=name,
|
||||
priority=7, policy=policy
|
||||
)
|
||||
|
||||
|
||||
def create_user_and_use_policy(name, policy_name):
|
||||
"""Create a *users* record and use an existing policy.
|
||||
|
||||
:param str name: user record name
|
||||
:param str policy_name: policy name
|
||||
"""
|
||||
policy = Policy.objects.get(policy_name=policy_name)
|
||||
Users.objects.create(
|
||||
email="@%s" % name, fullname=name,
|
||||
priority=7, policy=policy
|
||||
)
|
||||
|
||||
|
||||
def update_user_and_policy(oldname, newname):
|
||||
"""Update records.
|
||||
|
||||
:param str oldname: old name
|
||||
:param str newname: new name
|
||||
"""
|
||||
if oldname == newname:
|
||||
return
|
||||
u = Users.objects.get(email="@%s" % oldname)
|
||||
u.email = "@%s" % newname
|
||||
u.fullname = newname
|
||||
u.policy.policy_name = newname
|
||||
u.policy.save()
|
||||
u.save()
|
||||
|
||||
|
||||
def delete_user_and_policy(name):
|
||||
"""Delete records.
|
||||
|
||||
:param str name: identifier
|
||||
"""
|
||||
try:
|
||||
u = Users.objects.get(email="@%s" % name)
|
||||
except Users.DoesNotExist:
|
||||
return
|
||||
u.policy.delete()
|
||||
u.delete()
|
||||
|
||||
|
||||
def delete_user(name):
|
||||
"""Delete a *users* record.
|
||||
|
||||
:param str name: user record name
|
||||
"""
|
||||
try:
|
||||
Users.objects.get(email="@%s" % name).delete()
|
||||
except Users.DoesNotExist:
|
||||
pass
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
# coding: utf-8
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.contrib.auth.models import Group, Permission
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.template import Template, Context
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy
|
||||
from django.db.models import Q
|
||||
from modoboa.lib import events, parameters
|
||||
from modoboa.core.extensions import ModoExtension, exts_pool
|
||||
from .models import RelayDomain, RelayDomainAlias, Service
|
||||
@@ -14,16 +10,64 @@ extension_events = [
|
||||
"RelayDomainCreated",
|
||||
"RelayDomainDeleted",
|
||||
"RelayDomainAliasCreated",
|
||||
"RelayDomainAliasDeleted"
|
||||
"RelayDomainAliasDeleted",
|
||||
"ExtraRelayDomainForm",
|
||||
"FillRelayDomainInstances"
|
||||
]
|
||||
|
||||
|
||||
def init_limits_dependant_features():
|
||||
"""Populate limits tables.
|
||||
|
||||
Update the pool of every defined user (to add new limits) and
|
||||
update the *Resellers* group with new permissions.
|
||||
"""
|
||||
from modoboa.core.models import User
|
||||
from modoboa.extensions.limits import controls
|
||||
|
||||
for u in User.objects.all():
|
||||
controls.create_pool(u)
|
||||
grp = Group.objects.get(name='Resellers')
|
||||
for model in [RelayDomain, RelayDomainAlias, Service]:
|
||||
ct = ContentType.objects.get_for_model(model)
|
||||
name = model.__name__.lower()
|
||||
for action in ['add', 'change', 'delete']:
|
||||
grp.permissions.add(
|
||||
Permission.objects.get(
|
||||
content_type=ct, codename='%s_%s' % (action, name)
|
||||
)
|
||||
)
|
||||
grp.save()
|
||||
|
||||
|
||||
def init_amavis_dependant_features():
|
||||
"""Populate amavis database.
|
||||
|
||||
We create records for *users* and *policy* tables for each defined
|
||||
relay domain or relay domain alias.
|
||||
"""
|
||||
from modoboa.extensions.amavis.models import Users
|
||||
from modoboa.extensions.amavis.lib import (
|
||||
create_user_and_policy, create_user_and_use_policy
|
||||
)
|
||||
|
||||
for rdom in RelayDomain.objects.all():
|
||||
try:
|
||||
Users.objects.get(email="@%s" % rdom.name)
|
||||
except Users.DoesNotExist:
|
||||
create_user_and_policy(rdom.name)
|
||||
for rdomalias in rdom.relaydomainalias_set.all():
|
||||
try:
|
||||
Users.objects.get(email='@%s' % rdomalias.name)
|
||||
except Users.DoesNotExist:
|
||||
create_user_and_use_policy(rdomalias.name, rdom.name)
|
||||
|
||||
|
||||
class PostfixRelayDomains(ModoExtension):
|
||||
name = "postfix_relay_domains"
|
||||
label = "Postfix relay domains"
|
||||
version = "1.0"
|
||||
description = ugettext_lazy("Relay domains support for Postfix")
|
||||
depends_on = ['limits']
|
||||
|
||||
def init(self):
|
||||
"""Initialisation method.
|
||||
@@ -33,26 +77,8 @@ class PostfixRelayDomains(ModoExtension):
|
||||
"""
|
||||
for service_name in ['relay', 'smtp']:
|
||||
Service.objects.get_or_create(name=service_name)
|
||||
if not exts_pool.is_extension_enabled('limits'):
|
||||
return
|
||||
|
||||
from modoboa.core.models import User
|
||||
from modoboa.extensions.limits import controls
|
||||
|
||||
for u in User.objects.all():
|
||||
controls.create_pool(u)
|
||||
|
||||
grp = Group.objects.get(name='Resellers')
|
||||
for model in [RelayDomain, RelayDomainAlias, Service]:
|
||||
ct = ContentType.objects.get_for_model(model)
|
||||
name = model.__name__.lower()
|
||||
for action in ['add', 'change', 'delete']:
|
||||
grp.permissions.add(
|
||||
Permission.objects.get(
|
||||
content_type=ct, codename='%s_%s' % (action, name)
|
||||
)
|
||||
)
|
||||
grp.save()
|
||||
if exts_pool.is_extension_enabled('limits'):
|
||||
init_limits_dependant_features()
|
||||
|
||||
def load(self):
|
||||
from .app_settings import AdminParametersForm
|
||||
@@ -61,163 +87,14 @@ class PostfixRelayDomains(ModoExtension):
|
||||
AdminParametersForm, ugettext_lazy("Relay domains")
|
||||
)
|
||||
events.declare(extension_events)
|
||||
if not exts_pool.is_extension_enabled('limits'):
|
||||
return
|
||||
import limits_controls
|
||||
from modoboa.extensions.postfix_relay_domains import general_callbacks
|
||||
if exts_pool.is_extension_enabled('limits'):
|
||||
import limits_callbacks
|
||||
if exts_pool.is_extension_enabled('amavis'):
|
||||
import amavis_callbacks
|
||||
|
||||
def destroy(self):
|
||||
events.unregister('GetExtraDomainEntries', extra_domain_entries)
|
||||
events.unregister('GetExtraParameters', extra_parameters)
|
||||
events.unregister_extension()
|
||||
parameters.unregister()
|
||||
|
||||
exts_pool.register_extension(PostfixRelayDomains)
|
||||
|
||||
|
||||
@events.observe('GetStaticContent')
|
||||
def static_content(caller, user):
|
||||
if caller != 'domains':
|
||||
return []
|
||||
|
||||
t = Template("""<script src="{{ STATIC_URL }}postfix_relay_domains/js/relay_domains.js" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
var rdomain;
|
||||
$(document).ready(function() {
|
||||
rdomain = new RelayDomains({});
|
||||
});
|
||||
</script>
|
||||
""")
|
||||
return [t.render(Context({'STATIC_URL': settings.STATIC_URL}))]
|
||||
|
||||
|
||||
@events.observe('ExtraDomainFilters')
|
||||
def extra_domain_filters():
|
||||
return ['srvfilter']
|
||||
|
||||
|
||||
@events.observe('ExtraDomainMenuEntries')
|
||||
def extra_domain_menu_entries(user):
|
||||
return [
|
||||
{"name": "newrelaydomain",
|
||||
"label": ugettext_lazy("Add relay domain"),
|
||||
"img": "icon-plus",
|
||||
"modal": True,
|
||||
"modalcb": "rdomain.domainform_cb",
|
||||
"url": reverse(
|
||||
"modoboa.extensions.postfix_relay_domains.views.create"
|
||||
)},
|
||||
]
|
||||
|
||||
|
||||
@events.observe('ExtraDomainEntries')
|
||||
def extra_domain_entries(user, domfilter, searchquery, **extrafilters):
|
||||
if domfilter is not None and domfilter and domfilter != 'relaydomain':
|
||||
return []
|
||||
relay_domains = RelayDomain.objects.get_for_admin(user)
|
||||
if searchquery is not None:
|
||||
q = Q(name__contains=searchquery)
|
||||
q |= Q(relaydomainalias__name__contains=searchquery)
|
||||
relay_domains = relay_domains.filter(q).distinct()
|
||||
if 'srvfilter' in extrafilters and extrafilters['srvfilter']:
|
||||
relay_domains = relay_domains.filter(
|
||||
Q(service__name=extrafilters['srvfilter'])
|
||||
)
|
||||
return relay_domains
|
||||
|
||||
|
||||
@events.observe('GetDomainModifyLink')
|
||||
def rdomain_modify_link(domain):
|
||||
if not isinstance(domain, RelayDomain):
|
||||
return {}
|
||||
return {
|
||||
'url': reverse(
|
||||
'modoboa.extensions.postfix_relay_domains.views.edit',
|
||||
args=[domain.id]
|
||||
),
|
||||
'modalcb': 'rdomain.domainform_cb'
|
||||
}
|
||||
|
||||
|
||||
@events.observe('GetDomainAliasQuerySet')
|
||||
def get_da_query_set(domain):
|
||||
if domain.__class__.__name__ != 'RelayDomain':
|
||||
return []
|
||||
return [domain.relaydomainalias_set]
|
||||
|
||||
|
||||
@events.observe('GetDomainActions')
|
||||
def rdomain_actions(user, domain):
|
||||
if not isinstance(domain, RelayDomain):
|
||||
return []
|
||||
if not user.has_perm("postfix_relay_domains.delete_relaydomain"):
|
||||
return []
|
||||
return [{
|
||||
"name": "delrelaydomain",
|
||||
"url": reverse("modoboa.extensions.postfix_relay_domains.views.delete",
|
||||
args=[domain.id]),
|
||||
"title": ugettext_lazy("Delete %s?" % domain.name),
|
||||
"img": "icon-trash"
|
||||
}]
|
||||
|
||||
|
||||
@events.observe('CheckDomainName')
|
||||
def check_domain_name():
|
||||
return [
|
||||
(RelayDomain, ugettext_lazy('relay domain')),
|
||||
(RelayDomainAlias, ugettext_lazy('relay domain alias'))
|
||||
]
|
||||
|
||||
|
||||
@events.observe('GetExtraLimitTemplates')
|
||||
def extra_limit_templates():
|
||||
return [
|
||||
('relay_domains_limit', ugettext_lazy('Relay domains'),
|
||||
ugettext_lazy('Maximum number of relay domains this user can create'),
|
||||
'Resellers'),
|
||||
("relay_domain_aliases_limit", ugettext_lazy("Relay domain aliases"),
|
||||
ugettext_lazy('Maximum number of relay domain aliases this user can create'),
|
||||
'Resellers'),
|
||||
]
|
||||
|
||||
|
||||
@events.observe('GetExtraParameters')
|
||||
def extra_parameters(app, level):
|
||||
from django import forms
|
||||
|
||||
if app != 'limits' or level != 'A':
|
||||
return {}
|
||||
return {
|
||||
'deflt_relay_domains_limit': forms.IntegerField(
|
||||
label=ugettext_lazy("Relay domains"),
|
||||
initial=0,
|
||||
help_text=ugettext_lazy(
|
||||
"Maximum number of allowed relay domains for a new administrator"
|
||||
),
|
||||
widget=forms.widgets.TextInput(attrs={"class": "span1"})
|
||||
),
|
||||
'deflt_relay_domain_aliases_limit': forms.IntegerField(
|
||||
label=ugettext_lazy("Relay domain aliases"),
|
||||
initial=0,
|
||||
help_text=ugettext_lazy(
|
||||
"Maximum number of allowed relay domain aliases for a new administrator"
|
||||
),
|
||||
widget=forms.widgets.TextInput(attrs={"class": "span1"})
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@events.observe('ExtraDomainImportHelp')
|
||||
def extra_domain_import_help():
|
||||
return [ugettext_lazy("""
|
||||
<li><em>relaydomain; name; target host; service; enabled; verify recipients</em></li>
|
||||
<li><em>relaydomainalias; name; target; enabled</em></li>
|
||||
""")]
|
||||
|
||||
|
||||
@events.observe('ImportObject')
|
||||
def get_import_func(objtype):
|
||||
from .lib import import_relaydomain, import_relaydomainalias
|
||||
|
||||
if objtype == 'relaydomain':
|
||||
return [import_relaydomain]
|
||||
if objtype == 'relaydomainalias':
|
||||
return [import_relaydomainalias]
|
||||
return []
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
from modoboa.lib import events
|
||||
from modoboa.extensions.amavis.lib import (
|
||||
create_user_and_policy, update_user_and_policy, delete_user_and_policy,
|
||||
create_user_and_use_policy, delete_user
|
||||
)
|
||||
|
||||
|
||||
@events.observe("RelayDomainCreated")
|
||||
def on_relay_domain_created(user, rdomain):
|
||||
create_user_and_policy(rdomain.name)
|
||||
|
||||
|
||||
@events.observe("RelayDomainModified")
|
||||
def on_relay_domain_modified(rdomain):
|
||||
update_user_and_policy(rdomain.oldname, rdomain.name)
|
||||
|
||||
|
||||
@events.observe("RelayDomainDeleted")
|
||||
def on_relay_domain_deleted(rdomain):
|
||||
delete_user_and_policy(rdomain.name)
|
||||
|
||||
|
||||
@events.observe("RelayDomainAliasCreated")
|
||||
def on_relay_domain_alias_created(user, rdomainalias):
|
||||
create_user_and_use_policy(rdomainalias.name, rdomainalias.target.name)
|
||||
|
||||
|
||||
@events.observe("RelayDomainAliasDeleted")
|
||||
def on_relay_domain_alias_deleted(rdomainalias):
|
||||
delete_user(rdomainalias.name)
|
||||
@@ -2,12 +2,12 @@ from django import forms
|
||||
from django.http import QueryDict
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib.formutils import DynamicForm, DomainNameField
|
||||
from modoboa.lib.formutils import DynamicForm, DomainNameField, TabForms
|
||||
from modoboa.extensions.admin.models import Domain, DomainAlias
|
||||
from .models import RelayDomain, RelayDomainAlias
|
||||
|
||||
|
||||
class RelayDomainForm(forms.ModelForm, DynamicForm):
|
||||
class RelayDomainFormGeneral(forms.ModelForm, DynamicForm):
|
||||
aliases = DomainNameField(
|
||||
label=ugettext_lazy("Alias(es)"),
|
||||
required=False,
|
||||
@@ -25,7 +25,7 @@ class RelayDomainForm(forms.ModelForm, DynamicForm):
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(RelayDomainForm, self).__init__(*args, **kwargs)
|
||||
super(RelayDomainFormGeneral, self).__init__(*args, **kwargs)
|
||||
if args and isinstance(args[0], QueryDict):
|
||||
self._load_from_qdict(args[0], "aliases", DomainNameField)
|
||||
elif 'instance' in kwargs:
|
||||
@@ -42,7 +42,7 @@ class RelayDomainForm(forms.ModelForm, DynamicForm):
|
||||
|
||||
The validation way is not very smart...
|
||||
"""
|
||||
super(RelayDomainForm, self).clean()
|
||||
super(RelayDomainFormGeneral, self).clean()
|
||||
if self._errors:
|
||||
raise forms.ValidationError(self._errors)
|
||||
cleaned_data = self.cleaned_data
|
||||
@@ -90,7 +90,7 @@ class RelayDomainForm(forms.ModelForm, DynamicForm):
|
||||
|
||||
:param ``User`` user: connected user
|
||||
"""
|
||||
rd = super(RelayDomainForm, self).save(commit=False)
|
||||
rd = super(RelayDomainFormGeneral, self).save(commit=False)
|
||||
if commit:
|
||||
rd.save()
|
||||
aliases = []
|
||||
@@ -121,3 +121,35 @@ class RelayDomainForm(forms.ModelForm, DynamicForm):
|
||||
)
|
||||
al.save(creator=user)
|
||||
return rd
|
||||
|
||||
|
||||
class RelayDomainForm(TabForms):
|
||||
"""Specific edition form for relay domains.
|
||||
|
||||
We use a *tabs* compatible form because extensions can add their
|
||||
own tab. (ex: amavis)
|
||||
"""
|
||||
def __init__(self, user, *args, **kwargs):
|
||||
self.user = user
|
||||
self.forms = []
|
||||
if user.has_perm("postfix_relay_domains.change_relaydomain"):
|
||||
self.forms.append({
|
||||
'id': 'general', 'title': _("General"),
|
||||
'formtpl': 'postfix_relay_domains/relaydomain_form.html',
|
||||
'cls': RelayDomainFormGeneral, 'mandatory': True
|
||||
})
|
||||
|
||||
cbargs = [user]
|
||||
if "instances" in kwargs:
|
||||
cbargs += [kwargs["instances"]["general"]]
|
||||
self.forms += events.raiseQueryEvent("ExtraRelayDomainForm", *cbargs)
|
||||
super(RelayDomainForm, self).__init__(*args, **kwargs)
|
||||
|
||||
def save(self, user):
|
||||
"""Custom save method
|
||||
|
||||
As forms interact with each other, it is easier to make custom
|
||||
code to save them.
|
||||
"""
|
||||
for f in self.forms:
|
||||
f["instance"].save(user)
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.template import Template, Context
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy
|
||||
from django.db.models import Q
|
||||
from modoboa.lib import events
|
||||
from .models import RelayDomain, RelayDomainAlias
|
||||
|
||||
|
||||
@events.observe('GetStaticContent')
|
||||
def static_content(caller, user):
|
||||
if caller != 'domains':
|
||||
return []
|
||||
|
||||
t = Template("""<script src="{{ STATIC_URL }}postfix_relay_domains/js/relay_domains.js" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
var rdomain;
|
||||
$(document).ready(function() {
|
||||
rdomain = new RelayDomains({});
|
||||
});
|
||||
</script>
|
||||
""")
|
||||
return [t.render(Context({'STATIC_URL': settings.STATIC_URL}))]
|
||||
|
||||
|
||||
@events.observe('ExtraDomainFilters')
|
||||
def extra_domain_filters():
|
||||
return ['srvfilter']
|
||||
|
||||
|
||||
@events.observe('ExtraDomainMenuEntries')
|
||||
def extra_domain_menu_entries(user):
|
||||
return [
|
||||
{"name": "newrelaydomain",
|
||||
"label": ugettext_lazy("Add relay domain"),
|
||||
"img": "icon-plus",
|
||||
"modal": True,
|
||||
"modalcb": "rdomain.domainform_cb",
|
||||
"url": reverse(
|
||||
"modoboa.extensions.postfix_relay_domains.views.create"
|
||||
)},
|
||||
]
|
||||
|
||||
|
||||
@events.observe('ExtraDomainEntries')
|
||||
def extra_domain_entries(user, domfilter, searchquery, **extrafilters):
|
||||
if domfilter is not None and domfilter and domfilter != 'relaydomain':
|
||||
return []
|
||||
relay_domains = RelayDomain.objects.get_for_admin(user)
|
||||
if searchquery is not None:
|
||||
q = Q(name__contains=searchquery)
|
||||
q |= Q(relaydomainalias__name__contains=searchquery)
|
||||
relay_domains = relay_domains.filter(q).distinct()
|
||||
if 'srvfilter' in extrafilters and extrafilters['srvfilter']:
|
||||
relay_domains = relay_domains.filter(
|
||||
Q(service__name=extrafilters['srvfilter'])
|
||||
)
|
||||
return relay_domains
|
||||
|
||||
|
||||
@events.observe('GetDomainModifyLink')
|
||||
def rdomain_modify_link(domain):
|
||||
if not isinstance(domain, RelayDomain):
|
||||
return {}
|
||||
return {
|
||||
'url': reverse(
|
||||
'modoboa.extensions.postfix_relay_domains.views.edit',
|
||||
args=[domain.id]
|
||||
),
|
||||
'modalcb': 'rdomain.domainform_cb'
|
||||
}
|
||||
|
||||
|
||||
@events.observe('GetDomainAliasQuerySet')
|
||||
def get_da_query_set(domain):
|
||||
if domain.__class__.__name__ != 'RelayDomain':
|
||||
return []
|
||||
return [domain.relaydomainalias_set]
|
||||
|
||||
|
||||
@events.observe('GetDomainActions')
|
||||
def rdomain_actions(user, domain):
|
||||
if not isinstance(domain, RelayDomain):
|
||||
return []
|
||||
if not user.has_perm("postfix_relay_domains.delete_relaydomain"):
|
||||
return []
|
||||
return [{
|
||||
"name": "delrelaydomain",
|
||||
"url": reverse("modoboa.extensions.postfix_relay_domains.views.delete",
|
||||
args=[domain.id]),
|
||||
"title": ugettext_lazy("Delete %s?" % domain.name),
|
||||
"img": "icon-trash"
|
||||
}]
|
||||
|
||||
|
||||
@events.observe('CheckDomainName')
|
||||
def check_domain_name():
|
||||
return [
|
||||
(RelayDomain, ugettext_lazy('relay domain')),
|
||||
(RelayDomainAlias, ugettext_lazy('relay domain alias'))
|
||||
]
|
||||
|
||||
|
||||
@events.observe('GetExtraLimitTemplates')
|
||||
def extra_limit_templates():
|
||||
return [
|
||||
('relay_domains_limit', ugettext_lazy('Relay domains'),
|
||||
ugettext_lazy('Maximum number of relay domains this user can create'),
|
||||
'Resellers'),
|
||||
("relay_domain_aliases_limit", ugettext_lazy("Relay domain aliases"),
|
||||
ugettext_lazy('Maximum number of relay domain aliases this user can create'),
|
||||
'Resellers'),
|
||||
]
|
||||
|
||||
|
||||
@events.observe('GetExtraParameters')
|
||||
def extra_parameters(app, level):
|
||||
from django import forms
|
||||
|
||||
if app != 'limits' or level != 'A':
|
||||
return {}
|
||||
return {
|
||||
'deflt_relay_domains_limit': forms.IntegerField(
|
||||
label=ugettext_lazy("Relay domains"),
|
||||
initial=0,
|
||||
help_text=ugettext_lazy(
|
||||
"Maximum number of allowed relay domains for a new administrator"
|
||||
),
|
||||
widget=forms.widgets.TextInput(attrs={"class": "span1"})
|
||||
),
|
||||
'deflt_relay_domain_aliases_limit': forms.IntegerField(
|
||||
label=ugettext_lazy("Relay domain aliases"),
|
||||
initial=0,
|
||||
help_text=ugettext_lazy(
|
||||
"Maximum number of allowed relay domain aliases for a new administrator"
|
||||
),
|
||||
widget=forms.widgets.TextInput(attrs={"class": "span1"})
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@events.observe('ExtraDomainImportHelp')
|
||||
def extra_domain_import_help():
|
||||
return [ugettext_lazy("""
|
||||
<li><em>relaydomain; name; target host; service; enabled; verify recipients</em></li>
|
||||
<li><em>relaydomainalias; name; target; enabled</em></li>
|
||||
""")]
|
||||
|
||||
|
||||
@events.observe('ImportObject')
|
||||
def get_import_func(objtype):
|
||||
from .lib import import_relaydomain, import_relaydomainalias
|
||||
|
||||
if objtype == 'relaydomain':
|
||||
return [import_relaydomain]
|
||||
if objtype == 'relaydomainalias':
|
||||
return [import_relaydomainalias]
|
||||
return []
|
||||
|
||||
|
||||
@events.observe('ExtEnabled')
|
||||
def extension_enabled(extension):
|
||||
"""ExtEnabled event listener.
|
||||
|
||||
Usefull when *limits* or *amavis* extensions are activated after
|
||||
*postfix_relay_domains*.
|
||||
|
||||
:param extension: enabled extension
|
||||
"""
|
||||
from modoboa.extensions.postfix_relay_domains import (
|
||||
init_limits_dependant_features, init_amavis_dependant_features
|
||||
)
|
||||
if extension.name == 'limits':
|
||||
init_limits_dependant_features()
|
||||
if extension.name == 'amavis':
|
||||
init_amavis_dependant_features()
|
||||
@@ -139,6 +139,9 @@ reversion.register(RelayDomain)
|
||||
|
||||
|
||||
class RelayDomainAlias(AdminObject):
|
||||
"""Relay domain alias.
|
||||
|
||||
"""
|
||||
name = models.CharField(
|
||||
ugettext_lazy("name"), max_length=100, unique=True,
|
||||
help_text=ugettext_lazy("The alias name")
|
||||
|
||||
+1
@@ -67,6 +67,7 @@ RelayDomains.prototype = {
|
||||
success_cb: $.proxy(admin.reload_listing, admin)
|
||||
});
|
||||
}, this));
|
||||
$(document).trigger('domform_init');
|
||||
},
|
||||
|
||||
srv_tag_handler: function(tag, $link) {
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{% extends "common/generic_modal_form.html" %}{% block formbody %}
|
||||
{% include "postfix_relay_domains/relaydomain_form.html" %}
|
||||
{% endblock %}
|
||||
+1
-2
@@ -1,8 +1,7 @@
|
||||
{% extends "common/generic_modal_form.html" %}{% load lib_tags %}{% block formbody %}
|
||||
{% load lib_tags %}
|
||||
{% render_field form.name %}
|
||||
{% render_field form.target_host %}
|
||||
{% include "postfix_relay_domains/service_field.html" with field=form.service %}
|
||||
{% render_fields_group form "aliases" %}
|
||||
{% render_field form.enabled %}
|
||||
{% render_field form.verify_recipients %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -10,16 +10,16 @@ from modoboa.lib import events
|
||||
from modoboa.lib.exceptions import PermDeniedException
|
||||
from modoboa.lib.webutils import render_to_json_response
|
||||
from .models import RelayDomain, Service
|
||||
from .forms import RelayDomainForm
|
||||
from .forms import RelayDomainForm, RelayDomainFormGeneral
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required("postfix_relay_domains.add_relaydomain")
|
||||
@transaction.commit_on_success
|
||||
def create(request, tplname="postfix_relay_domains/relaydomain_form.html"):
|
||||
def create(request, tplname="postfix_relay_domains/new_relaydomain_form.html"):
|
||||
events.raiseEvent("CanCreate", request.user, "relay_domains")
|
||||
if request.method == 'POST':
|
||||
form = RelayDomainForm(request.POST)
|
||||
form = RelayDomainFormGeneral(request.POST)
|
||||
if form.is_valid():
|
||||
rdom = form.save(request.user)
|
||||
rdom.post_create(request.user)
|
||||
@@ -35,18 +35,21 @@ def create(request, tplname="postfix_relay_domains/relaydomain_form.html"):
|
||||
"action_classes": "submit",
|
||||
"action": reverse(create),
|
||||
"formid": "rdomform",
|
||||
"form": RelayDomainForm()}
|
||||
"form": RelayDomainFormGeneral()}
|
||||
return render(request, tplname, ctx)
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required("postfix_relay_domains.change_relaydomain")
|
||||
def edit(request, rdom_id, tplname='postfix_relay_domains/relaydomain_form.html'):
|
||||
def edit(request, rdom_id, tplname='common/tabforms.html'):
|
||||
rdom = RelayDomain.objects.get(pk=rdom_id)
|
||||
if not request.user.can_access(rdom):
|
||||
raise PermDeniedException
|
||||
instances = {'general': rdom}
|
||||
events.raiseEvent("FillRelayDomainInstances", request.user, rdom, instances)
|
||||
if request.method == 'POST':
|
||||
form = RelayDomainForm(request.POST, instance=rdom)
|
||||
rdom.oldname = rdom.name
|
||||
form = RelayDomainForm(request.user, request.POST, instances=instances)
|
||||
if form.is_valid():
|
||||
form.save(request.user)
|
||||
events.raiseEvent('RelayDomainModified', rdom)
|
||||
@@ -61,7 +64,7 @@ def edit(request, rdom_id, tplname='postfix_relay_domains/relaydomain_form.html'
|
||||
'title': rdom.name,
|
||||
'action_label': _("Update"),
|
||||
'action_classes': "submit",
|
||||
'form': RelayDomainForm(instance=rdom)
|
||||
'tabs': RelayDomainForm(request.user, instances=instances)
|
||||
}
|
||||
return render(request, tplname, ctx)
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ from functools import wraps
|
||||
import inspect
|
||||
import re
|
||||
from django.conf import settings
|
||||
from modoboa.lib.sysutils import guess_extension_name
|
||||
|
||||
events = []
|
||||
callbacks = {}
|
||||
@@ -117,6 +118,20 @@ def unregister(event, callback):
|
||||
pass
|
||||
|
||||
|
||||
def unregister_extension(extension=None):
|
||||
"""Unregister all callbacks declared by an extension.
|
||||
|
||||
:param str extension: extension full name (ie. module name)
|
||||
"""
|
||||
if extension is None:
|
||||
extension = guess_extension_name()
|
||||
print extension
|
||||
for evt, values in callbacks.items():
|
||||
for name in values.keys():
|
||||
if extension in name:
|
||||
del callbacks[evt][name]
|
||||
|
||||
|
||||
def raiseEvent(event, *args, **kwargs):
|
||||
"""Raise a specific event
|
||||
|
||||
|
||||
@@ -8,11 +8,10 @@ will be available and modifiable directly from the web interface.
|
||||
|
||||
Only super users will be able to access this part of the web interface.
|
||||
"""
|
||||
import inspect
|
||||
import re
|
||||
from django import forms
|
||||
from . import events
|
||||
from exceptions import ModoboaException
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib.sysutils import guess_extension_name
|
||||
from modoboa.lib.exceptions import ModoboaException
|
||||
|
||||
_params = {'A': {}, 'U': {}}
|
||||
|
||||
@@ -185,7 +184,7 @@ def unregister(app=None):
|
||||
:param app: the application's name (string)
|
||||
"""
|
||||
if app is None:
|
||||
app = __guess_extension()
|
||||
app = guess_extension_name()
|
||||
for lvlparams in _params.values():
|
||||
if app in lvlparams:
|
||||
del lvlparams[app]
|
||||
@@ -198,23 +197,11 @@ def __is_defined(app, level, name):
|
||||
raise NotDefined(app, name)
|
||||
|
||||
|
||||
def __guess_extension():
|
||||
"""Tries to guess the application's name by inspecting the stack
|
||||
|
||||
:return: a string or None
|
||||
"""
|
||||
modname = inspect.getmodule(inspect.stack()[2][0]).__name__
|
||||
m = re.match("(?:modoboa\.)?(?:extensions\.)?([^\.$]+)", modname)
|
||||
if m:
|
||||
return m.group(1)
|
||||
return None
|
||||
|
||||
|
||||
def save_admin(name, value, app=None):
|
||||
from .models import Parameter
|
||||
|
||||
if app is None:
|
||||
app = __guess_extension()
|
||||
app = guess_extension_name()
|
||||
__is_defined(app, 'A', name)
|
||||
fullname = "%s.%s" % (app, name)
|
||||
try:
|
||||
@@ -231,7 +218,7 @@ def save_user(user, name, value, app=None):
|
||||
from .models import UserParameter
|
||||
|
||||
if app is None:
|
||||
app = __guess_extension()
|
||||
app = guess_extension_name()
|
||||
__is_defined(app, 'U', name)
|
||||
fullname = "%s.%s" % (app, name)
|
||||
try:
|
||||
@@ -256,7 +243,7 @@ def get_admin(name, app=None, raise_error=True):
|
||||
from .models import Parameter
|
||||
|
||||
if app is None:
|
||||
app = __guess_extension()
|
||||
app = guess_extension_name()
|
||||
try:
|
||||
__is_defined(app, "A", name)
|
||||
except NotDefined:
|
||||
@@ -283,7 +270,7 @@ def get_user(user, name, app=None, raise_error=True):
|
||||
from .models import UserParameter
|
||||
|
||||
if app is None:
|
||||
app = __guess_extension()
|
||||
app = guess_extension_name()
|
||||
try:
|
||||
__is_defined(app, "U", name)
|
||||
except NotDefined:
|
||||
@@ -349,7 +336,7 @@ def get_parameter_form(level, name, app=None):
|
||||
:return: a form class
|
||||
"""
|
||||
if app is None:
|
||||
app = __guess_extension()
|
||||
app = guess_extension_name()
|
||||
__is_defined(app, level, name)
|
||||
return _params[level][app]["form"]
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ This module extra functions/shortcuts to communicate with the system
|
||||
(executing commands, etc.)
|
||||
"""
|
||||
import subprocess
|
||||
import inspect
|
||||
import re
|
||||
|
||||
|
||||
def exec_cmd(cmd, sudo_user=None, **kwargs):
|
||||
@@ -24,3 +26,15 @@ def exec_cmd(cmd, sudo_user=None, **kwargs):
|
||||
stderr=subprocess.STDOUT, **kwargs)
|
||||
output = p.communicate()[0]
|
||||
return p.returncode, output
|
||||
|
||||
|
||||
def guess_extension_name():
|
||||
"""Tries to guess the application's name by inspecting the stack.
|
||||
|
||||
:return: a string or None
|
||||
"""
|
||||
modname = inspect.getmodule(inspect.stack()[2][0]).__name__
|
||||
match = re.match(r"(?:modoboa\.)?(?:extensions\.)?([^\.$]+)", modname)
|
||||
if match is not None:
|
||||
return match.group(1)
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user