diff --git a/modoboa/extensions/amavis/__init__.py b/modoboa/extensions/amavis/__init__.py
index 69e63e056..5e47ac577 100644
--- a/modoboa/extensions/amavis/__init__.py
+++ b/modoboa/extensions/amavis/__init__.py
@@ -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("""
-""")
- 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("""
-""")
-
- 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('
')
- 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
diff --git a/modoboa/extensions/amavis/forms.py b/modoboa/extensions/amavis/forms.py
index 721b3c5a1..1f9a270ad 100644
--- a/modoboa/extensions/amavis/forms.py
+++ b/modoboa/extensions/amavis/forms.py
@@ -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
diff --git a/modoboa/extensions/amavis/general_callbacks.py b/modoboa/extensions/amavis/general_callbacks.py
new file mode 100644
index 000000000..29366aa3a
--- /dev/null
+++ b/modoboa/extensions/amavis/general_callbacks.py
@@ -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("""
+""")
+ 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("""
+""")
+
+ 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('')
+ 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
diff --git a/modoboa/extensions/amavis/lib.py b/modoboa/extensions/amavis/lib.py
index 56d8312df..f33e77249 100644
--- a/modoboa/extensions/amavis/lib.py
+++ b/modoboa/extensions/amavis/lib.py
@@ -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
diff --git a/modoboa/extensions/postfix_relay_domains/__init__.py b/modoboa/extensions/postfix_relay_domains/__init__.py
index da9bf2ea9..10aba023a 100644
--- a/modoboa/extensions/postfix_relay_domains/__init__.py
+++ b/modoboa/extensions/postfix_relay_domains/__init__.py
@@ -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("""
-
-""")
- 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("""
-relaydomain; name; target host; service; enabled; verify recipients
-relaydomainalias; name; target; enabled
-""")]
-
-
-@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 []
diff --git a/modoboa/extensions/postfix_relay_domains/amavis_callbacks.py b/modoboa/extensions/postfix_relay_domains/amavis_callbacks.py
new file mode 100644
index 000000000..c43052af3
--- /dev/null
+++ b/modoboa/extensions/postfix_relay_domains/amavis_callbacks.py
@@ -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)
diff --git a/modoboa/extensions/postfix_relay_domains/forms.py b/modoboa/extensions/postfix_relay_domains/forms.py
index 767caeb3a..37a49d646 100644
--- a/modoboa/extensions/postfix_relay_domains/forms.py
+++ b/modoboa/extensions/postfix_relay_domains/forms.py
@@ -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)
diff --git a/modoboa/extensions/postfix_relay_domains/general_callbacks.py b/modoboa/extensions/postfix_relay_domains/general_callbacks.py
new file mode 100644
index 000000000..d7fa11bc6
--- /dev/null
+++ b/modoboa/extensions/postfix_relay_domains/general_callbacks.py
@@ -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("""
+
+""")
+ 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("""
+relaydomain; name; target host; service; enabled; verify recipients
+relaydomainalias; name; target; enabled
+""")]
+
+
+@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()
diff --git a/modoboa/extensions/postfix_relay_domains/limits_controls.py b/modoboa/extensions/postfix_relay_domains/limits_callbacks.py
similarity index 100%
rename from modoboa/extensions/postfix_relay_domains/limits_controls.py
rename to modoboa/extensions/postfix_relay_domains/limits_callbacks.py
diff --git a/modoboa/extensions/postfix_relay_domains/models.py b/modoboa/extensions/postfix_relay_domains/models.py
index 560dfb0c5..1c45cbffb 100644
--- a/modoboa/extensions/postfix_relay_domains/models.py
+++ b/modoboa/extensions/postfix_relay_domains/models.py
@@ -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")
diff --git a/modoboa/extensions/postfix_relay_domains/static/postfix_relay_domains/js/relay_domains.js b/modoboa/extensions/postfix_relay_domains/static/postfix_relay_domains/js/relay_domains.js
index fc454ab39..d30e10f4b 100644
--- a/modoboa/extensions/postfix_relay_domains/static/postfix_relay_domains/js/relay_domains.js
+++ b/modoboa/extensions/postfix_relay_domains/static/postfix_relay_domains/js/relay_domains.js
@@ -67,6 +67,7 @@ RelayDomains.prototype = {
success_cb: $.proxy(admin.reload_listing, admin)
});
}, this));
+ $(document).trigger('domform_init');
},
srv_tag_handler: function(tag, $link) {
diff --git a/modoboa/extensions/postfix_relay_domains/templates/postfix_relay_domains/new_relaydomain_form.html b/modoboa/extensions/postfix_relay_domains/templates/postfix_relay_domains/new_relaydomain_form.html
new file mode 100644
index 000000000..8a349d160
--- /dev/null
+++ b/modoboa/extensions/postfix_relay_domains/templates/postfix_relay_domains/new_relaydomain_form.html
@@ -0,0 +1,3 @@
+{% extends "common/generic_modal_form.html" %}{% block formbody %}
+{% include "postfix_relay_domains/relaydomain_form.html" %}
+{% endblock %}
diff --git a/modoboa/extensions/postfix_relay_domains/templates/postfix_relay_domains/relaydomain_form.html b/modoboa/extensions/postfix_relay_domains/templates/postfix_relay_domains/relaydomain_form.html
index 99edce36b..2c0e8d150 100644
--- a/modoboa/extensions/postfix_relay_domains/templates/postfix_relay_domains/relaydomain_form.html
+++ b/modoboa/extensions/postfix_relay_domains/templates/postfix_relay_domains/relaydomain_form.html
@@ -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 %}
diff --git a/modoboa/extensions/postfix_relay_domains/views.py b/modoboa/extensions/postfix_relay_domains/views.py
index 5eded6ca3..e59657891 100644
--- a/modoboa/extensions/postfix_relay_domains/views.py
+++ b/modoboa/extensions/postfix_relay_domains/views.py
@@ -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)
diff --git a/modoboa/lib/events.py b/modoboa/lib/events.py
index b2e778bbb..cf565e370 100644
--- a/modoboa/lib/events.py
+++ b/modoboa/lib/events.py
@@ -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
diff --git a/modoboa/lib/parameters.py b/modoboa/lib/parameters.py
index 61d3372d7..a5c7dfa89 100644
--- a/modoboa/lib/parameters.py
+++ b/modoboa/lib/parameters.py
@@ -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"]
diff --git a/modoboa/lib/sysutils.py b/modoboa/lib/sysutils.py
index 237f9ea64..02af77148 100644
--- a/modoboa/lib/sysutils.py
+++ b/modoboa/lib/sysutils.py
@@ -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