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:
Antoine Nguyen
2013-11-01 16:48:04 +01:00
parent a11e69074d
commit 22c811f3da
17 changed files with 576 additions and 358 deletions
+4 -129
View File
@@ -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
+12 -11
View File
@@ -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
+69
View File
@@ -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")
@@ -67,6 +67,7 @@ RelayDomains.prototype = {
success_cb: $.proxy(admin.reload_listing, admin)
});
}, this));
$(document).trigger('domform_init');
},
srv_tag_handler: function(tag, $link) {
@@ -0,0 +1,3 @@
{% extends "common/generic_modal_form.html" %}{% block formbody %}
{% include "postfix_relay_domains/relaydomain_form.html" %}
{% endblock %}
@@ -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)
+15
View File
@@ -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
+9 -22
View File
@@ -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"]
+14
View File
@@ -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