mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-18 05:51:26 +02:00
Remove extensions.
This commit is contained in:
@@ -1,63 +0,0 @@
|
||||
# coding: utf-8
|
||||
|
||||
"""Custom settings."""
|
||||
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext_lazy
|
||||
|
||||
from modoboa.lib.form_utils import SeparatorField
|
||||
from modoboa.lib.parameters import AdminParametersForm
|
||||
|
||||
|
||||
class ParametersForm(AdminParametersForm):
|
||||
app = "postfix_autoreply"
|
||||
|
||||
general_sep = SeparatorField(label=ugettext_lazy("General"))
|
||||
|
||||
autoreplies_timeout = forms.IntegerField(
|
||||
label=ugettext_lazy("Automatic reply timeout"),
|
||||
initial=86400,
|
||||
help_text=ugettext_lazy(
|
||||
"Timeout in seconds between two auto-replies to the same recipient"
|
||||
),
|
||||
widget=forms.TextInput(attrs={"class": "form-control"})
|
||||
)
|
||||
|
||||
default_subject = forms.CharField(
|
||||
label=ugettext_lazy("Default subject"),
|
||||
initial=ugettext_lazy("I'm off"),
|
||||
help_text=ugettext_lazy(
|
||||
"Default subject used when an auto-reply message is created "
|
||||
"automatically"
|
||||
),
|
||||
widget=forms.TextInput(attrs={"class": "form-control"})
|
||||
)
|
||||
|
||||
default_content = forms.CharField(
|
||||
label=ugettext_lazy("Default content"),
|
||||
initial=ugettext_lazy(
|
||||
"""I'm currently off. I'll answer as soon as I come back.
|
||||
|
||||
Best regards,
|
||||
%(name)s
|
||||
"""),
|
||||
help_text=ugettext_lazy(
|
||||
"Default content used when an auto-reply message is created "
|
||||
"automatically. The '%(name)s' macro will be replaced by the "
|
||||
"user's full name."
|
||||
),
|
||||
widget=forms.widgets.Textarea(attrs={"class": "form-control"})
|
||||
)
|
||||
|
||||
def clean_default_content(self):
|
||||
"""Check if the provided value is valid.
|
||||
|
||||
Must be a valid format string which will be used with the %
|
||||
operator.
|
||||
"""
|
||||
tpl = self.cleaned_data["default_content"]
|
||||
try:
|
||||
test = tpl % {"name": "Antoine Nguyen"}
|
||||
except (KeyError, ValueError):
|
||||
raise forms.ValidationError(ugettext_lazy("Invalid syntax"))
|
||||
return tpl
|
||||
@@ -1,81 +0,0 @@
|
||||
# coding: utf-8
|
||||
|
||||
"""Custom forms."""
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
from django import forms
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
|
||||
from .models import ARmessage
|
||||
|
||||
|
||||
class ARmessageForm(forms.ModelForm):
|
||||
|
||||
fromdate = forms.DateTimeField(
|
||||
label=ugettext_lazy('From'),
|
||||
required=False,
|
||||
help_text=ugettext_lazy("Activate your auto reply from this date"),
|
||||
widget=forms.TextInput(
|
||||
attrs={'class': 'datefield form-control', 'readonly': 'readonly'}
|
||||
)
|
||||
)
|
||||
untildate = forms.DateTimeField(
|
||||
label=ugettext_lazy('Until'),
|
||||
required=False,
|
||||
help_text=ugettext_lazy("Activate your auto reply until this date"),
|
||||
widget=forms.TextInput(
|
||||
attrs={'class': 'datefield form-control', 'readonly': 'readonly'}
|
||||
)
|
||||
)
|
||||
subject = forms.CharField(
|
||||
widget=forms.TextInput(
|
||||
attrs={'class': 'form-control'}
|
||||
)
|
||||
)
|
||||
content = forms.CharField(
|
||||
widget=forms.Textarea(
|
||||
attrs={'class': 'form-control'}
|
||||
)
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = ARmessage
|
||||
fields = ('subject', 'content', 'enabled')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ARmessageForm, self).__init__(*args, **kwargs)
|
||||
self.fields = OrderedDict(
|
||||
(key, self.fields[key]) for key in
|
||||
['subject', 'content', 'fromdate', 'untildate', 'enabled']
|
||||
)
|
||||
if 'instance' in kwargs and kwargs['instance'] is not None:
|
||||
self.fields['fromdate'].initial = \
|
||||
kwargs['instance'].fromdate.replace(second=0, microsecond=0)
|
||||
self.fields['untildate'].initial = kwargs['instance'].untildate
|
||||
|
||||
def clean(self):
|
||||
"""Custom fields validaton.
|
||||
|
||||
We want to be sure that fromdate < untildate and that they are
|
||||
both in the future ONLY IF the autoreply is beeing activated.
|
||||
|
||||
"""
|
||||
super(ARmessageForm, self).clean()
|
||||
if not self.cleaned_data["enabled"]:
|
||||
return self.cleaned_data
|
||||
if self.cleaned_data["fromdate"] is not None:
|
||||
if self.cleaned_data["fromdate"] < timezone.now():
|
||||
self.add_error("fromdate", _("This date is over"))
|
||||
else:
|
||||
self.cleaned_data['fromdate'] = timezone.now()
|
||||
if self.cleaned_data["untildate"] is not None:
|
||||
untildate = self.cleaned_data["untildate"]
|
||||
if untildate < timezone.now():
|
||||
self.add_error("untildate", _("This date is over"))
|
||||
elif "fromdate" in self.cleaned_data and \
|
||||
untildate < self.cleaned_data["fromdate"]:
|
||||
self.add_error(
|
||||
"untildate", _("Must be greater than start date"))
|
||||
return self.cleaned_data
|
||||
@@ -1,151 +0,0 @@
|
||||
"""Event callbacks."""
|
||||
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import ugettext_lazy
|
||||
|
||||
from .models import ARmessage
|
||||
from modoboa.extensions.postfix_autoreply.models import Transport, Alias
|
||||
from modoboa.lib import events, parameters
|
||||
|
||||
|
||||
@events.observe("ExtraUprefsJS")
|
||||
def extra_js(user):
|
||||
return ["""function autoreply_cb() {
|
||||
$('.datefield').datetimepicker({
|
||||
format: 'YYYY-MM-DD hh:mm:ss',
|
||||
language: '%(lang)s'
|
||||
});
|
||||
}
|
||||
""" % {'lang': parameters.get_user(user, "LANG", app="core")}
|
||||
]
|
||||
|
||||
|
||||
@events.observe("UserMenuDisplay")
|
||||
def menu(target, user):
|
||||
if target != "uprefs_menu":
|
||||
return []
|
||||
if not user.mailbox_set.count():
|
||||
return []
|
||||
return [
|
||||
{"name": "autoreply",
|
||||
"class": "ajaxnav",
|
||||
"url": "autoreply/",
|
||||
"label": ugettext_lazy("Auto-reply message")}
|
||||
]
|
||||
|
||||
|
||||
@events.observe("DomainCreated")
|
||||
def onDomainCreated(user, domain):
|
||||
transport = Transport()
|
||||
transport.domain = "autoreply.%s" % domain.name
|
||||
transport.method = "autoreply:"
|
||||
transport.save()
|
||||
|
||||
|
||||
@events.observe("DomainModified")
|
||||
def onDomainModified(domain):
|
||||
if domain.oldname == domain.name:
|
||||
return
|
||||
Transport.objects.filter(domain="autoreply.%s" % domain.oldname) \
|
||||
.update(domain="autoreply.%s" % domain.name)
|
||||
for al in Alias.objects.filter(
|
||||
full_address__contains="@%s" % domain.oldname):
|
||||
new_address = al.full_address.replace(
|
||||
"@%s" % domain.oldname,
|
||||
"@%s" % domain.name)
|
||||
al.full_address = new_address
|
||||
al.autoreply_address = "%s@autoreply.%s" % (new_address, domain.name)
|
||||
al.save()
|
||||
|
||||
|
||||
@events.observe("DomainDeleted")
|
||||
def onDomainDeleted(domain):
|
||||
Transport.objects.filter(domain="autoreply.%s" % domain.name).delete()
|
||||
|
||||
|
||||
@events.observe("MailboxCreated")
|
||||
def onMailboxCreated(user, mailbox):
|
||||
alias = Alias()
|
||||
alias.full_address = mailbox.full_address
|
||||
alias.autoreply_address = \
|
||||
"%s@autoreply.%s" % (mailbox.full_address, mailbox.domain.name)
|
||||
alias.save()
|
||||
|
||||
|
||||
@events.observe("MailboxDeleted")
|
||||
def onMailboxDeleted(mailboxes):
|
||||
from modoboa.extensions.admin.models import Mailbox
|
||||
|
||||
if isinstance(mailboxes, Mailbox):
|
||||
mailboxes = [mailboxes]
|
||||
for mailbox in mailboxes:
|
||||
try:
|
||||
alias = Alias.objects.get(full_address=mailbox.full_address)
|
||||
except Alias.DoesNotExist:
|
||||
pass
|
||||
else:
|
||||
alias.delete()
|
||||
|
||||
|
||||
@events.observe("MailboxModified")
|
||||
def onMailboxModified(mailbox):
|
||||
if not hasattr(mailbox, 'old_full_address'):
|
||||
return
|
||||
if mailbox.full_address == mailbox.old_full_address:
|
||||
return
|
||||
alias = Alias.objects.get(full_address=mailbox.old_full_address)
|
||||
alias.full_address = mailbox.full_address
|
||||
alias.autoreply_address = \
|
||||
"%s@autoreply.%s" % (mailbox.full_address, mailbox.domain.name)
|
||||
alias.save()
|
||||
|
||||
|
||||
@events.observe("ExtraFormFields")
|
||||
def extra_mailform_fields(form_name, mailbox=None):
|
||||
"""Define extra fields to include in mail forms.
|
||||
|
||||
For now, only the auto-reply state can be modified.
|
||||
|
||||
:param str form_name: form name (must be 'mailform')
|
||||
:param Mailbox mailbox: mailbox
|
||||
"""
|
||||
from modoboa.lib.form_utils import YesNoField
|
||||
|
||||
if form_name != "mailform":
|
||||
return []
|
||||
status = False
|
||||
if mailbox is not None and mailbox.armessage_set.count():
|
||||
status = mailbox.armessage_set.all()[0].enabled
|
||||
return [
|
||||
('autoreply', YesNoField(
|
||||
label=ugettext_lazy("Enable auto-reply"),
|
||||
initial="yes" if status else "no",
|
||||
required=False,
|
||||
help_text=ugettext_lazy("Enable or disable Postfix auto-reply")
|
||||
))
|
||||
]
|
||||
|
||||
|
||||
@events.observe("SaveExtraFormFields")
|
||||
def save_extra_mailform_fields(form_name, mailbox, values):
|
||||
"""Set the auto-reply status for a mailbox.
|
||||
|
||||
If a corresponding auto-reply message exists, we update its
|
||||
status. Otherwise, we create a message using default values.
|
||||
|
||||
:param str form_name: form name (must be 'mailform')
|
||||
:param Mailbox mailbox: mailbox
|
||||
:param dict values: form values
|
||||
"""
|
||||
if form_name != 'mailform':
|
||||
return
|
||||
if mailbox.armessage_set.count():
|
||||
arm = mailbox.armessage_set.all()[0]
|
||||
else:
|
||||
arm = ARmessage(mbox=mailbox)
|
||||
arm.subject = parameters.get_admin("DEFAULT_SUBJECT")
|
||||
arm.content = parameters.get_admin("DEFAULT_CONTENT") \
|
||||
% {'name': mailbox.user.fullname}
|
||||
arm.fromdate = timezone.now()
|
||||
arm.enabled = True if values['autoreply'] == 'yes' else False
|
||||
arm.save()
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
@@ -1,68 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
import sys
|
||||
import datetime
|
||||
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.utils import timezone
|
||||
|
||||
from modoboa.core.management.commands import CloseConnectionMixin
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.email_utils import split_mailbox, sendmail_simple
|
||||
from modoboa.extensions.admin.models import Mailbox
|
||||
from modoboa.extensions.postfix_autoreply.models import ARmessage, ARhistoric
|
||||
|
||||
|
||||
def send_autoreply(sender, mailbox, armessage):
|
||||
if armessage.fromdate > timezone.now():
|
||||
return
|
||||
if armessage.untildate is not None \
|
||||
and armessage.untildate < timezone.now():
|
||||
armessage.enabled = False
|
||||
armessage.save()
|
||||
return
|
||||
|
||||
try:
|
||||
lastar = ARhistoric.objects.get(armessage=armessage.id, sender=sender)
|
||||
timeout = parameters.get_admin("AUTOREPLIES_TIMEOUT",
|
||||
app="postfix_autoreply")
|
||||
delta = datetime.timedelta(seconds=int(timeout))
|
||||
now = timezone.make_aware(datetime.datetime.now(),
|
||||
timezone.get_default_timezone())
|
||||
if lastar.last_sent + delta > now:
|
||||
sys.exit(0)
|
||||
except ARhistoric.DoesNotExist:
|
||||
lastar = ARhistoric()
|
||||
lastar.armessage = armessage
|
||||
lastar.sender = sender
|
||||
|
||||
sendmail_simple(mailbox.user.encoded_address, sender, armessage.subject,
|
||||
armessage.content.encode('utf-8'))
|
||||
|
||||
lastar.last_sent = datetime.datetime.now()
|
||||
lastar.save()
|
||||
|
||||
|
||||
class Command(BaseCommand, CloseConnectionMixin):
|
||||
args = '<sender> <recipient ...>'
|
||||
help = 'Send autoreply emails'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
if len(args) < 2:
|
||||
raise CommandError(
|
||||
"usage: ./manage.py autoreply <sender> <recipient ...>")
|
||||
|
||||
sender = args[0]
|
||||
for fulladdress in args[1:]:
|
||||
address, domain = split_mailbox(fulladdress)
|
||||
try:
|
||||
mbox = Mailbox.objects.get(address=address, domain__name=domain)
|
||||
except Mailbox.DoesNotExist:
|
||||
print "Unknown recipient %s" % (fulladdress)
|
||||
continue
|
||||
try:
|
||||
armessage = ARmessage.objects.get(mbox=mbox.id, enabled=True)
|
||||
except ARmessage.DoesNotExist:
|
||||
continue
|
||||
|
||||
send_autoreply(sender, mbox, armessage)
|
||||
@@ -1,73 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('admin', '__first__'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Alias',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('full_address', models.CharField(max_length=255)),
|
||||
('autoreply_address', models.CharField(max_length=255)),
|
||||
],
|
||||
options={
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ARhistoric',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('last_sent', models.DateTimeField(auto_now=True)),
|
||||
('sender', models.CharField(max_length=254)),
|
||||
],
|
||||
options={
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ARmessage',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('subject', models.CharField(help_text='The subject that will appear in sent emails', max_length=255, verbose_name='subject')),
|
||||
('content', models.TextField(help_text='The content that will appear in sent emails', verbose_name='content')),
|
||||
('enabled', models.BooleanField(default=False, help_text='Activate/Deactivate your auto reply', verbose_name='enabled')),
|
||||
('fromdate', models.DateTimeField(default=django.utils.timezone.now)),
|
||||
('untildate', models.DateTimeField(null=True, blank=True)),
|
||||
('mbox', models.ForeignKey(to='admin.Mailbox')),
|
||||
],
|
||||
options={
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Transport',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('domain', models.CharField(max_length=300)),
|
||||
('method', models.CharField(max_length=255)),
|
||||
],
|
||||
options={
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='arhistoric',
|
||||
name='armessage',
|
||||
field=models.ForeignKey(to='postfix_autoreply.ARmessage'),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='arhistoric',
|
||||
unique_together=set([('armessage', 'sender')]),
|
||||
),
|
||||
]
|
||||
@@ -1,43 +0,0 @@
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils import timezone
|
||||
|
||||
from modoboa.extensions.admin.models import Mailbox
|
||||
|
||||
|
||||
class Transport(models.Model):
|
||||
domain = models.CharField(max_length=300)
|
||||
method = models.CharField(max_length=255)
|
||||
|
||||
|
||||
class Alias(models.Model):
|
||||
full_address = models.CharField(max_length=255)
|
||||
autoreply_address = models.CharField(max_length=255)
|
||||
|
||||
|
||||
class ARmessage(models.Model):
|
||||
mbox = models.ForeignKey(Mailbox)
|
||||
subject = models.CharField(
|
||||
_('subject'), max_length=255,
|
||||
help_text=_("The subject that will appear in sent emails")
|
||||
)
|
||||
content = models.TextField(
|
||||
_('content'),
|
||||
help_text=_("The content that will appear in sent emails")
|
||||
)
|
||||
enabled = models.BooleanField(
|
||||
_('enabled'),
|
||||
help_text=_("Activate/Deactivate your auto reply"),
|
||||
default=False
|
||||
)
|
||||
fromdate = models.DateTimeField(default=timezone.now)
|
||||
untildate = models.DateTimeField(null=True, blank=True)
|
||||
|
||||
|
||||
class ARhistoric(models.Model):
|
||||
armessage = models.ForeignKey(ARmessage)
|
||||
last_sent = models.DateTimeField(auto_now=True)
|
||||
sender = models.CharField(max_length=254)
|
||||
|
||||
class Meta:
|
||||
unique_together = ("armessage", "sender")
|
||||
@@ -1,65 +0,0 @@
|
||||
# coding: utf-8
|
||||
"""
|
||||
Postfix auto-replies plugin.
|
||||
|
||||
This module provides a way to integrate Modoboa auto-reply
|
||||
functionality into Postfix.
|
||||
|
||||
"""
|
||||
|
||||
from django.utils.translation import ugettext_lazy
|
||||
|
||||
from modoboa.core.extensions import ModoExtension, exts_pool
|
||||
from modoboa.extensions.admin.models import Domain
|
||||
from modoboa.lib import events, parameters
|
||||
from . import general_callbacks
|
||||
from .models import Transport, Alias
|
||||
|
||||
|
||||
class PostfixAutoreply(ModoExtension):
|
||||
|
||||
"""
|
||||
Auto-reply (vacation) functionality using Postfix.
|
||||
|
||||
"""
|
||||
name = "postfix_autoreply"
|
||||
label = "Postfix autoreply"
|
||||
version = "1.0"
|
||||
description = ugettext_lazy(
|
||||
"Auto-reply (vacation) functionality using Postfix")
|
||||
|
||||
def load(self):
|
||||
from modoboa.extensions.postfix_autoreply.app_settings import (
|
||||
ParametersForm
|
||||
)
|
||||
parameters.register(
|
||||
ParametersForm, ugettext_lazy("Automatic replies"))
|
||||
|
||||
def load_initial_data(self):
|
||||
"""Create records for existing domains."""
|
||||
for dom in Domain.objects.all():
|
||||
try:
|
||||
Transport.objects.get(domain="autoreply.%s" % dom.name)
|
||||
except Transport.DoesNotExist:
|
||||
general_callbacks.onDomainCreated(None, dom)
|
||||
else:
|
||||
continue
|
||||
|
||||
for mb in dom.mailbox_set.all():
|
||||
try:
|
||||
Alias.objects.get(full_address=mb.full_address)
|
||||
except Alias.DoesNotExist:
|
||||
general_callbacks.onMailboxCreated(None, mb)
|
||||
|
||||
exts_pool.register_extension(PostfixAutoreply)
|
||||
|
||||
|
||||
@events.observe("ExtraUprefsRoutes")
|
||||
def extra_routes():
|
||||
from django.conf.urls import url
|
||||
|
||||
return [
|
||||
url(r'^user/autoreply/$',
|
||||
'modoboa.extensions.postfix_autoreply.views.autoreply',
|
||||
name="autoreply")
|
||||
]
|
||||
@@ -1,11 +0,0 @@
|
||||
{% load i18n form_tags %}{% load url from future %}
|
||||
<h2>{% trans "Auto-reply message" %} <small>{% trans "Define a message to automatically send when you are off" %}</small></h2>
|
||||
<hr>
|
||||
<form class="form-horizontal" method="POST"
|
||||
action="{% url 'autoreply' %}"
|
||||
id="uprefs_form">{% csrf_token %}
|
||||
{% render_form form %}
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary col-lg-offset-8 col-md-offset-8 col-sm-offset-7 col-lg-1 col-md-1 col-sm-2" id="update" name="update">{% trans 'Update' %}</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -1,125 +0,0 @@
|
||||
from django.core.urlresolvers import reverse
|
||||
from modoboa.core.models import User
|
||||
from modoboa.lib.tests import ModoTestCase
|
||||
from modoboa.extensions.admin import factories
|
||||
from modoboa.extensions.admin.models import (
|
||||
Domain
|
||||
)
|
||||
from modoboa.extensions.postfix_autoreply.models import (
|
||||
Transport, Alias, ARmessage
|
||||
)
|
||||
|
||||
|
||||
class EventsTestCase(ModoTestCase):
|
||||
fixtures = ['initial_users.json']
|
||||
|
||||
def setUp(self):
|
||||
super(EventsTestCase, self).setUp()
|
||||
factories.populate_database()
|
||||
|
||||
def test_domain_created_event(self):
|
||||
values = {
|
||||
"name": "domain.tld", "quota": 100, "create_dom_admin": "no",
|
||||
"stepid": 'step2'
|
||||
}
|
||||
self.ajax_post(
|
||||
reverse("admin:domain_add"), values
|
||||
)
|
||||
trans = Transport.objects.get(domain='autoreply.domain.tld')
|
||||
|
||||
def test_domain_deleted_event(self):
|
||||
dom = Domain.objects.get(name="test.com")
|
||||
trans = Transport.objects.get(domain='autoreply.test.com')
|
||||
self.ajax_post(
|
||||
reverse("admin:domain_delete", args=[dom.id]),
|
||||
{}
|
||||
)
|
||||
with self.assertRaises(Transport.DoesNotExist):
|
||||
Transport.objects.get(domain='autoreply.test.com')
|
||||
|
||||
def test_domain_modified_event(self):
|
||||
values = {
|
||||
"name": "test.fr", "quota": 100, "enabled": True
|
||||
}
|
||||
dom = Domain.objects.get(name="test.com")
|
||||
self.ajax_post(
|
||||
reverse("admin:domain_change", args=[dom.id]),
|
||||
values
|
||||
)
|
||||
trans = Transport.objects.get(domain='autoreply.test.fr')
|
||||
self.assertEqual(
|
||||
Alias.objects.filter(full_address__contains='@test.fr').count(), 2
|
||||
)
|
||||
for al in Alias.objects.filter(full_address__contains='@test.fr'):
|
||||
self.assertIn('autoreply.test.fr', al.autoreply_address)
|
||||
|
||||
def test_mailbox_created_event(self):
|
||||
values = {
|
||||
'username': "tester@test.com",
|
||||
'first_name': 'Tester', 'last_name': 'Toto',
|
||||
'password1': 'toto', 'password2': 'toto', 'role': 'SimpleUsers',
|
||||
'quota_act': True, 'is_active': True, 'email': 'tester@test.com',
|
||||
'stepid': 'step2', 'autoreply': 'no'
|
||||
}
|
||||
self.ajax_post(
|
||||
reverse("admin:account_add"), values
|
||||
)
|
||||
al = Alias.objects.get(full_address='tester@test.com')
|
||||
self.assertEqual(
|
||||
al.autoreply_address, 'tester@test.com@autoreply.test.com')
|
||||
arm = ARmessage.objects.get(mbox__address='tester')
|
||||
|
||||
def test_mailbox_deleted_event(self):
|
||||
account = User.objects.get(username="user@test.com")
|
||||
self.ajax_post(
|
||||
reverse("admin:account_delete", args=[account.id]),
|
||||
{}
|
||||
)
|
||||
with self.assertRaises(Alias.DoesNotExist):
|
||||
Alias.objects.get(full_address='user@test.com')
|
||||
with self.assertRaises(ARmessage.DoesNotExist):
|
||||
ARmessage.objects.get(
|
||||
mbox__address='user', mbox__domain__name='test.com')
|
||||
|
||||
def test_modify_mailbox_event(self):
|
||||
values = {
|
||||
'username': "leon@test.com",
|
||||
'first_name': 'Tester', 'last_name': 'Toto',
|
||||
'role': 'SimpleUsers', 'quota_act': True,
|
||||
'is_active': True, 'email': 'leon@test.com',
|
||||
'autoreply': 'no'
|
||||
}
|
||||
account = User.objects.get(username="user@test.com")
|
||||
self.ajax_post(
|
||||
reverse("admin:account_change", args=[account.id]),
|
||||
values
|
||||
)
|
||||
with self.assertRaises(Alias.DoesNotExist):
|
||||
Alias.objects.get(full_address='user@test.com')
|
||||
al = Alias.objects.get(full_address='leon@test.com')
|
||||
|
||||
|
||||
class FormTestCase(ModoTestCase):
|
||||
fixtures = ['initial_users.json']
|
||||
|
||||
def setUp(self):
|
||||
super(FormTestCase, self).setUp()
|
||||
factories.populate_database()
|
||||
|
||||
# def test_set_autoreply(self):
|
||||
# self.clt.get(
|
||||
# reverse('modoboa.core.views.user.index')
|
||||
# )
|
||||
# values = {
|
||||
# 'subject': 'test', 'content': "I'm off", "enabled": True
|
||||
# }
|
||||
# self.ajax_post(
|
||||
# reverse('modoboa.extensions.postfix_autoreply.views.autoreply'),
|
||||
# values
|
||||
# )
|
||||
# account = User.objects.get(username="user@test.com")
|
||||
# arm = ARmessage.objects.get(mbox=account.mailbox_set.all()[0])
|
||||
# self.assertEqual(arm.subject, 'test')
|
||||
# self.assertTrue(arm.enabled)
|
||||
# self.assertFalse(arm.untildate)
|
||||
# self.assertTrue(arm.fromdate)
|
||||
@@ -1,45 +0,0 @@
|
||||
# coding: utf-8
|
||||
from datetime import date
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.utils.translation import ugettext as _
|
||||
from modoboa.lib.web_utils import (
|
||||
render_to_json_response, _render_to_string
|
||||
)
|
||||
from modoboa.extensions.admin.lib import needs_mailbox
|
||||
from modoboa.extensions.admin.models import Mailbox
|
||||
from .forms import ARmessageForm
|
||||
from .models import ARmessage
|
||||
|
||||
|
||||
@login_required
|
||||
@needs_mailbox()
|
||||
def autoreply(request, tplname="postfix_autoreply/autoreply.html"):
|
||||
mb = Mailbox.objects.get(user=request.user.id)
|
||||
try:
|
||||
arm = ARmessage.objects.get(mbox=mb.id)
|
||||
except ARmessage.DoesNotExist:
|
||||
arm = None
|
||||
if request.method == "POST":
|
||||
if arm:
|
||||
form = ARmessageForm(request.POST, instance=arm)
|
||||
else:
|
||||
form = ARmessageForm(request.POST)
|
||||
if form.is_valid():
|
||||
arm = form.save(commit=False)
|
||||
arm.fromdate = form.cleaned_data["fromdate"]
|
||||
arm.untildate = form.cleaned_data["untildate"]
|
||||
arm.mbox = mb
|
||||
arm.save()
|
||||
return render_to_json_response(
|
||||
_("Auto reply message updated successfully.")
|
||||
)
|
||||
|
||||
return render_to_json_response(
|
||||
{"form_errors": form.errors}, status=400
|
||||
)
|
||||
|
||||
form = ARmessageForm(instance=arm)
|
||||
return render_to_json_response({
|
||||
"content": _render_to_string(request, tplname, {"form": form}),
|
||||
"onload_cb": "autoreply_cb"
|
||||
})
|
||||
@@ -1,35 +0,0 @@
|
||||
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("@{0}".format(rdomain.name))
|
||||
|
||||
|
||||
@events.observe("RelayDomainModified")
|
||||
def on_relay_domain_modified(rdomain):
|
||||
update_user_and_policy(
|
||||
"@{0}".format(rdomain.oldname), "@{0}".format(rdomain.name)
|
||||
)
|
||||
|
||||
|
||||
@events.observe("RelayDomainDeleted")
|
||||
def on_relay_domain_deleted(rdomain):
|
||||
delete_user_and_policy("@{0}".format(rdomain.name))
|
||||
|
||||
|
||||
@events.observe("RelayDomainAliasCreated")
|
||||
def on_relay_domain_alias_created(user, rdomainalias):
|
||||
create_user_and_use_policy(
|
||||
"@{0}".format(rdomainalias.name),
|
||||
"@{0}".format(rdomainalias.target.name)
|
||||
)
|
||||
|
||||
|
||||
@events.observe("RelayDomainAliasDeleted")
|
||||
def on_relay_domain_alias_deleted(rdomainalias):
|
||||
delete_user("@{0}".format(rdomainalias.name))
|
||||
@@ -1,25 +0,0 @@
|
||||
"""
|
||||
Relay domains settings definition.
|
||||
"""
|
||||
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext_lazy
|
||||
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.form_utils import SeparatorField
|
||||
|
||||
|
||||
class AdminParametersForm(parameters.AdminParametersForm):
|
||||
|
||||
"""Admin parameters definition."""
|
||||
|
||||
app = "postfix_relay_domains"
|
||||
|
||||
general_sep = SeparatorField(label=ugettext_lazy("General"))
|
||||
|
||||
master_cf_path = forms.CharField(
|
||||
label=ugettext_lazy("Postfix's master.cf path"),
|
||||
initial="/etc/postfix/master.cf",
|
||||
help_text=ugettext_lazy('Path to the master.cf configuration file'),
|
||||
widget=forms.TextInput(attrs={"class": "form-control"})
|
||||
)
|
||||
@@ -1,25 +0,0 @@
|
||||
import factory
|
||||
from modoboa.core.factories import PermissionFactory
|
||||
from . import models
|
||||
|
||||
|
||||
class ServiceFactory(factory.DjangoModelFactory):
|
||||
FACTORY_FOR = models.Service
|
||||
FACTORY_DJANGO_GET_OR_CREATE = ('name', )
|
||||
|
||||
name = 'dummy'
|
||||
|
||||
|
||||
class RelayDomainFactory(PermissionFactory):
|
||||
FACTORY_FOR = models.RelayDomain
|
||||
|
||||
target_host = 'external.host.tld'
|
||||
enabled = True
|
||||
verify_recipients = True
|
||||
service = factory.SubFactory(ServiceFactory)
|
||||
|
||||
|
||||
class RelayDomainAliasFactory(PermissionFactory):
|
||||
FACTORY_FOR = models.RelayDomainAlias
|
||||
|
||||
enabled = True
|
||||
@@ -1,184 +0,0 @@
|
||||
"""
|
||||
Postfix relay domains extension forms.
|
||||
"""
|
||||
from django import forms
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.http import QueryDict
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
|
||||
from .models import RelayDomain, RelayDomainAlias
|
||||
from modoboa.extensions.admin.models import Domain, DomainAlias
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib.form_utils import DynamicForm, DomainNameField, TabForms
|
||||
from modoboa.lib.web_utils import render_to_json_response
|
||||
|
||||
|
||||
class RelayDomainFormGeneral(forms.ModelForm, DynamicForm):
|
||||
aliases = DomainNameField(
|
||||
label=ugettext_lazy("Alias(es)"),
|
||||
required=False,
|
||||
help_text=ugettext_lazy(
|
||||
"Alias(es) of this relay domain. Indicate only one name per input"
|
||||
", press ENTER to add a new input."
|
||||
)
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = RelayDomain
|
||||
exclude = ['dates']
|
||||
widgets = {
|
||||
"service": forms.Select(attrs={"class": "form-control"})
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.oldname = None
|
||||
if "instance" in kwargs:
|
||||
self.oldname = kwargs["instance"].name
|
||||
super(RelayDomainFormGeneral, self).__init__(*args, **kwargs)
|
||||
self.field_widths = {
|
||||
"service": 3
|
||||
}
|
||||
if args and isinstance(args[0], QueryDict):
|
||||
self._load_from_qdict(args[0], "aliases", DomainNameField)
|
||||
elif 'instance' in kwargs:
|
||||
rd = kwargs['instance']
|
||||
for pos, rdalias in enumerate(rd.relaydomainalias_set.all()):
|
||||
name = "aliases_%d" % (pos + 1)
|
||||
self._create_field(forms.CharField, name, rdalias.name, 3)
|
||||
|
||||
def clean(self):
|
||||
"""Custom fields validaton.
|
||||
|
||||
We want to prevent duplicate names between domains, relay
|
||||
domains, domain aliases and relay domain aliases.
|
||||
|
||||
The validation way is not very smart...
|
||||
"""
|
||||
super(RelayDomainFormGeneral, self).clean()
|
||||
cleaned_data = self.cleaned_data
|
||||
for dtype, label in [(Domain, _('domain')),
|
||||
(DomainAlias, _('domain alias')),
|
||||
(RelayDomainAlias, _('relay domain alias'))]:
|
||||
try:
|
||||
dtype.objects.get(name=cleaned_data['name'])
|
||||
except dtype.DoesNotExist:
|
||||
pass
|
||||
else:
|
||||
self.add_error(
|
||||
"name", _("A %s with this name already exists") % label
|
||||
)
|
||||
break
|
||||
|
||||
for k in cleaned_data.keys():
|
||||
if not k.startswith("aliases"):
|
||||
continue
|
||||
if not cleaned_data[k]:
|
||||
del cleaned_data[k]
|
||||
continue
|
||||
for dtype, name in [(RelayDomain, _('relay domain')),
|
||||
(DomainAlias, _('domain alias')),
|
||||
(Domain, _('domain'))]:
|
||||
try:
|
||||
dtype.objects.get(name=cleaned_data[k])
|
||||
except dtype.DoesNotExist:
|
||||
pass
|
||||
else:
|
||||
self.add_error(
|
||||
k, _("A %s with this name already exists") % name)
|
||||
break
|
||||
|
||||
return cleaned_data
|
||||
|
||||
def save(self, user, commit=True, rdomalias_post_create=False):
|
||||
"""Custom save method.
|
||||
|
||||
As relay domain aliases are defined using the same form as
|
||||
relay domains, we need to save them manually.
|
||||
|
||||
:param ``User`` user: connected user
|
||||
"""
|
||||
rd = super(RelayDomainFormGeneral, self).save(commit=False)
|
||||
if commit:
|
||||
rd.oldname = self.oldname
|
||||
rd.save()
|
||||
aliases = []
|
||||
for k, v in self.cleaned_data.iteritems():
|
||||
if not k.startswith("aliases"):
|
||||
continue
|
||||
if v in ["", None]:
|
||||
continue
|
||||
aliases.append(v)
|
||||
for rdalias in rd.relaydomainalias_set.all():
|
||||
if rdalias.name not in aliases:
|
||||
rdalias.delete()
|
||||
else:
|
||||
aliases.remove(rdalias.name)
|
||||
if aliases:
|
||||
events.raiseEvent(
|
||||
"CanCreate", user, "relay_domain_aliases", len(aliases)
|
||||
)
|
||||
for alias in aliases:
|
||||
try:
|
||||
rd.relaydomainalias_set.get(name=alias)
|
||||
except RelayDomainAlias.DoesNotExist:
|
||||
pass
|
||||
else:
|
||||
continue
|
||||
al = RelayDomainAlias(
|
||||
name=alias, target=rd, enabled=rd.enabled
|
||||
)
|
||||
al.save(creator=user) \
|
||||
if rdomalias_post_create else al.save()
|
||||
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, request, *args, **kwargs):
|
||||
self.user = request.user
|
||||
self.forms = []
|
||||
if self.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 = [self.user]
|
||||
if "instances" in kwargs:
|
||||
cbargs += [kwargs["instances"]["general"]]
|
||||
self.forms += events.raiseQueryEvent("ExtraRelayDomainForm", *cbargs)
|
||||
super(RelayDomainForm, self).__init__(request, *args, **kwargs)
|
||||
|
||||
def extra_context(self, context):
|
||||
"""Additional content.
|
||||
"""
|
||||
rdom = self.instances["general"]
|
||||
context.update({
|
||||
'action': reverse(
|
||||
"postfix_relay_domains:relaydomain_change", args=[rdom.id]),
|
||||
'formid': 'rdomform',
|
||||
'title': rdom.name
|
||||
})
|
||||
|
||||
def save(self):
|
||||
"""Custom save method
|
||||
|
||||
As forms interact with each other, it is easier to make custom
|
||||
code to save them.
|
||||
"""
|
||||
self.forms[0]['instance'].save(
|
||||
self.request.user, rdomalias_post_create=True
|
||||
)
|
||||
for f in self.forms[1:]:
|
||||
f["instance"].save(self.request.user)
|
||||
|
||||
def done(self):
|
||||
events.raiseEvent('RelayDomainModified', self.instances["general"])
|
||||
return render_to_json_response(_('Relay domain modified'))
|
||||
@@ -1,154 +0,0 @@
|
||||
"""General event callbacks."""
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db.models import Q
|
||||
from django.template import Template, Context
|
||||
from django.utils.translation import ugettext_lazy
|
||||
|
||||
from modoboa.core.extensions import exts_pool
|
||||
from modoboa.lib import events
|
||||
from .models import RelayDomain, RelayDomainAlias
|
||||
|
||||
PERMISSIONS = {
|
||||
"Resellers": [
|
||||
("postfix_relay_domains", "relaydomain", "add_relaydomain"),
|
||||
("postfix_relay_domains", "relaydomain", "change_relaydomain"),
|
||||
("postfix_relay_domains", "relaydomain", "delete_relaydomain"),
|
||||
("postfix_relay_domains", "relaydomainalias", "add_relaydomainalias"),
|
||||
("postfix_relay_domains", "relaydomainalias",
|
||||
"change_relaydomainalias"),
|
||||
("postfix_relay_domains", "relaydomainalias",
|
||||
"delete_relaydomainalias"),
|
||||
("postfix_relay_domains", "service", "add_service"),
|
||||
("postfix_relay_domains", "service", "change_service"),
|
||||
("postfix_relay_domains", "service", "delete_service")
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@events.observe("GetExtraRolePermissions")
|
||||
def extra_permissions(rolename):
|
||||
"""Return extra permissions for Resellers."""
|
||||
if not exts_pool.is_extension_installed("modoboa.extensions.limits"):
|
||||
return []
|
||||
return PERMISSIONS.get(rolename, [])
|
||||
|
||||
|
||||
@events.observe('GetStaticContent')
|
||||
def static_content(caller, st_type, user):
|
||||
if caller != 'domains' or st_type != 'js':
|
||||
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": "fa fa-plus",
|
||||
"modal": True,
|
||||
"modalcb": "rdomain.domainform_cb",
|
||||
"url": reverse("postfix_relay_domains:relaydomain_add")
|
||||
}]
|
||||
|
||||
|
||||
@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(
|
||||
"postfix_relay_domains:relaydomain_change",
|
||||
args=[domain.id]
|
||||
),
|
||||
'modalcb': 'rdomain.editdomain_form_cb'
|
||||
}
|
||||
|
||||
|
||||
@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(
|
||||
"postfix_relay_domains:relaydomain_delete",
|
||||
args=[domain.id]
|
||||
),
|
||||
"title": ugettext_lazy("Delete %s?" % domain.name),
|
||||
"img": "fa fa-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('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 []
|
||||
@@ -1,14 +0,0 @@
|
||||
from django.db import transaction
|
||||
from .models import RelayDomain, RelayDomainAlias
|
||||
|
||||
|
||||
@transaction.commit_on_success
|
||||
def import_relaydomain(user, row, formopts):
|
||||
"""Specific code for relay domains import"""
|
||||
RelayDomain().from_csv(user, row)
|
||||
|
||||
|
||||
@transaction.commit_on_success
|
||||
def import_relaydomainalias(user, row, formopts):
|
||||
"""Specific code for relay domain aliases import"""
|
||||
RelayDomainAlias().from_csv(user, row)
|
||||
@@ -1,85 +0,0 @@
|
||||
"""Callbacks related to limits extension."""
|
||||
|
||||
from django.utils.translation import ugettext_lazy
|
||||
|
||||
from modoboa.extensions.limits.lib import inc_limit_usage, dec_limit_usage
|
||||
from modoboa.extensions.limits.models import LimitsPool, Limit
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib.permissions import get_object_owner
|
||||
from .models import RelayDomainAlias
|
||||
|
||||
|
||||
def create_new_limits():
|
||||
"""Create relay domains limits."""
|
||||
|
||||
new_limits = ["relay_domains_limit", "relay_domain_aliases_limit"]
|
||||
for pool in LimitsPool.objects.all():
|
||||
for lname in new_limits:
|
||||
Limit.objects.get_or_create(name=lname, pool=pool, maxvalue=0)
|
||||
|
||||
|
||||
@events.observe("InitialDataLoaded")
|
||||
def load_limits_dependant_data(name):
|
||||
"""Complete existing pools with new limits."""
|
||||
if name != "limits":
|
||||
return
|
||||
create_new_limits()
|
||||
|
||||
|
||||
@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": "col-md-1 form-control"})
|
||||
),
|
||||
'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": "col-md-1 form-control"})
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@events.observe('RelayDomainCreated')
|
||||
def inc_relaydomains_count(user, rdomain):
|
||||
inc_limit_usage(user, 'relay_domains_limit')
|
||||
|
||||
|
||||
@events.observe('RelayDomainDeleted')
|
||||
def dec_relaydomains_count(rdomain):
|
||||
owner = get_object_owner(rdomain)
|
||||
dec_limit_usage(owner, 'relay_domains_limit')
|
||||
for rdomalias in rdomain.relaydomainalias_set.all():
|
||||
dec_rdomaliases_count(rdomalias)
|
||||
|
||||
|
||||
@events.observe('RelayDomainAliasCreated')
|
||||
def inc_rdomaliases_count(user, rdomalias):
|
||||
inc_limit_usage(user, 'relay_domain_aliases_limit')
|
||||
|
||||
|
||||
@events.observe('RelayDomainAliasDeleted')
|
||||
def dec_rdomaliases_count(rdomainaliases):
|
||||
if isinstance(rdomainaliases, RelayDomainAlias):
|
||||
rdomainaliases = [rdomainaliases]
|
||||
for rdomainalias in rdomainaliases:
|
||||
owner = get_object_owner(rdomainalias)
|
||||
dec_limit_usage(owner, 'relay_domain_aliases_limit')
|
||||
@@ -1,59 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('admin', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='RelayDomain',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('name', models.CharField(help_text='The domain name', unique=True, max_length=100, verbose_name='name')),
|
||||
('target_host', models.CharField(help_text='Remote destination of this domain', max_length=255, verbose_name='target host')),
|
||||
('enabled', models.BooleanField(default=True, help_text='Check to activate this domain', verbose_name='enabled')),
|
||||
('verify_recipients', models.BooleanField(default=False, help_text='Check for valid recipients', verbose_name='verify recipients')),
|
||||
('dates', models.ForeignKey(to='admin.ObjectDates')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['name'],
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='RelayDomainAlias',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('name', models.CharField(help_text='The alias name', unique=True, max_length=100, verbose_name='name')),
|
||||
('enabled', models.BooleanField(default=True, help_text='Check to activate this alias', verbose_name='enabled')),
|
||||
('dates', models.ForeignKey(to='admin.ObjectDates')),
|
||||
('target', models.ForeignKey(verbose_name='target', to='postfix_relay_domains.RelayDomain', help_text='The relay domain this alias points to')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Service',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('name', models.CharField(help_text='The service name', unique=True, max_length=100, verbose_name='name')),
|
||||
],
|
||||
options={
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='relaydomain',
|
||||
name='service',
|
||||
field=models.ForeignKey(to='postfix_relay_domains.Service'),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
||||
@@ -1,210 +0,0 @@
|
||||
"""Relay domain related models."""
|
||||
|
||||
from django.contrib.contenttypes import generic
|
||||
from django.db import models
|
||||
from django.db.models.manager import Manager
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
|
||||
import reversion
|
||||
|
||||
from modoboa.core.models import ObjectAccess
|
||||
from modoboa.extensions.admin.models import AdminObject
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.exceptions import BadRequest, NotFound
|
||||
|
||||
|
||||
class RelayDomainManager(Manager):
|
||||
|
||||
def get_for_admin(self, admin):
|
||||
"""Return the relay domains belonging to this admin.
|
||||
|
||||
The result is a ``QuerySet`` object, so this function can be used
|
||||
to fill ``ModelChoiceField`` objects.
|
||||
"""
|
||||
if admin.is_superuser:
|
||||
return self.get_query_set()
|
||||
return self.get_query_set().filter(owners__user=admin)
|
||||
|
||||
|
||||
class ServiceManager(Manager):
|
||||
|
||||
def load_from_master_cf(self):
|
||||
"""Load services from the master.cf file.
|
||||
|
||||
Parse the configuration file to update the service table. New
|
||||
entries are saved and outdated ones (ie. present in the
|
||||
database but not in the file) are removed.
|
||||
"""
|
||||
with open(parameters.get_admin('MASTER_CF_PATH')) as fp:
|
||||
content = fp.read()
|
||||
services = []
|
||||
for line in content.split('\n'):
|
||||
if not line or line.startswith('#'):
|
||||
continue
|
||||
parts = line.strip().split()
|
||||
if len(parts) != 8:
|
||||
continue
|
||||
if parts[7] != 'smtp':
|
||||
continue
|
||||
srv, created = self.get_or_create(name=parts[0])
|
||||
services.append(parts[0])
|
||||
to_delete = []
|
||||
for service in self.all():
|
||||
if service.name not in services:
|
||||
to_delete.append(service.name)
|
||||
Service.objects.filter(name__in=to_delete).delete()
|
||||
|
||||
|
||||
class Service(models.Model):
|
||||
|
||||
"""Postfix service.
|
||||
"""
|
||||
name = models.CharField(
|
||||
ugettext_lazy('name'), max_length=100, unique=True,
|
||||
help_text=ugettext_lazy('The service name')
|
||||
)
|
||||
|
||||
objects = ServiceManager()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class RelayDomain(AdminObject):
|
||||
|
||||
"""Relay domain.
|
||||
|
||||
A relay domain differs from a usual domaine because its final
|
||||
destination is not reached yet. It must be accepted by the MTA but
|
||||
it will then be transfered to another one.
|
||||
"""
|
||||
name = models.CharField(
|
||||
ugettext_lazy('name'), max_length=100, unique=True,
|
||||
help_text=ugettext_lazy('The domain name')
|
||||
)
|
||||
target_host = models.CharField(
|
||||
ugettext_lazy('target host'), max_length=255,
|
||||
help_text=ugettext_lazy('Remote destination of this domain')
|
||||
)
|
||||
service = models.ForeignKey(Service)
|
||||
enabled = models.BooleanField(
|
||||
ugettext_lazy('enabled'),
|
||||
help_text=ugettext_lazy('Check to activate this domain'),
|
||||
default=True
|
||||
)
|
||||
verify_recipients = models.BooleanField(
|
||||
ugettext_lazy('verify recipients'),
|
||||
help_text=ugettext_lazy('Check for valid recipients'),
|
||||
default=False
|
||||
)
|
||||
|
||||
owners = generic.GenericRelation(ObjectAccess)
|
||||
|
||||
objects = RelayDomainManager()
|
||||
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
|
||||
@property
|
||||
def tags(self):
|
||||
return [
|
||||
{"name": "relaydomain", "label": _("Relay Domain"), "type": "dom"},
|
||||
{"name": self.service.name, "label": "%s:" % self.service.name,
|
||||
"type": "srv", "color": "info"}
|
||||
]
|
||||
|
||||
@property
|
||||
def aliases(self):
|
||||
return self.relaydomainalias_set
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def to_csv(self, csvwriter):
|
||||
"""Export this relay domain to CSV.
|
||||
|
||||
:param csvwriter:
|
||||
"""
|
||||
csvwriter.writerow(
|
||||
["relaydomain", self.name, self.target_host,
|
||||
self.service.name, self.enabled, self.verify_recipients]
|
||||
)
|
||||
for rdalias in self.relaydomainalias_set.all():
|
||||
rdalias.to_csv(csvwriter)
|
||||
|
||||
def from_csv(self, user, row):
|
||||
"""Import a relay domain from CSV.
|
||||
|
||||
:param user: user importing the relay domain
|
||||
:param str row: relay domain definition
|
||||
"""
|
||||
if len(row) != 6:
|
||||
raise BadRequest(_("Invalid line"))
|
||||
self.name = row[1].strip()
|
||||
self.target_host = row[2].strip()
|
||||
self.service, created = Service.objects.get_or_create(
|
||||
name=row[3].strip())
|
||||
self.enabled = (row[4].strip() == 'True')
|
||||
self.verify_recipients = (row[5].strip() == 'True')
|
||||
self.save(creator=user)
|
||||
|
||||
def post_create(self, creator):
|
||||
"""Post creation actions.
|
||||
|
||||
:param ``User`` creator: user whos created this relay domain
|
||||
"""
|
||||
super(RelayDomain, self).post_create(creator)
|
||||
for rdomalias in self.relaydomainalias_set.all():
|
||||
rdomalias.post_create(creator)
|
||||
|
||||
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")
|
||||
)
|
||||
target = models.ForeignKey(
|
||||
RelayDomain, verbose_name=ugettext_lazy('target'),
|
||||
help_text=ugettext_lazy("The relay domain this alias points to")
|
||||
)
|
||||
enabled = models.BooleanField(
|
||||
ugettext_lazy('enabled'),
|
||||
help_text=ugettext_lazy("Check to activate this alias"),
|
||||
default=True
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def to_csv(self, csvwriter):
|
||||
"""Export this relay domain alias to CSV.
|
||||
|
||||
:param csvwriter:
|
||||
"""
|
||||
csvwriter.writerow(
|
||||
['relaydomainalias', self.name, self.target.name, self.enabled]
|
||||
)
|
||||
|
||||
def from_csv(self, user, row):
|
||||
"""Import a relay domain alias from CSV.
|
||||
|
||||
:param user: user importing the relay domain alias
|
||||
:param str row: relay domain alias definition
|
||||
"""
|
||||
if len(row) != 4:
|
||||
raise BadRequest(_("Invalid line"))
|
||||
self.name = row[1].strip()
|
||||
try:
|
||||
self.target = RelayDomain.objects.get(name=row[2].strip())
|
||||
except RelayDomain.DoesNotExist:
|
||||
raise NotFound(_("Relay domain %s does not exist" % row[2].strip()))
|
||||
self.enabled = (row[3].strip() == 'True')
|
||||
self.save(creator=user)
|
||||
|
||||
reversion.register(RelayDomainAlias)
|
||||
@@ -1,55 +0,0 @@
|
||||
# coding: utf-8
|
||||
|
||||
"""Declare postfix_relay_domains extension and register it."""
|
||||
|
||||
from django.utils.translation import ugettext_lazy
|
||||
|
||||
from modoboa.core.extensions import ModoExtension, exts_pool
|
||||
from modoboa.lib import events, parameters
|
||||
|
||||
|
||||
EXTENSION_EVENTS = [
|
||||
"RelayDomainCreated",
|
||||
"RelayDomainDeleted",
|
||||
"RelayDomainModified",
|
||||
"RelayDomainAliasCreated",
|
||||
"RelayDomainAliasDeleted",
|
||||
"ExtraRelayDomainForm",
|
||||
"FillRelayDomainInstances"
|
||||
]
|
||||
|
||||
|
||||
class PostfixRelayDomains(ModoExtension):
|
||||
|
||||
"""Extension declaration."""
|
||||
|
||||
name = "postfix_relay_domains"
|
||||
label = "Postfix relay domains"
|
||||
version = "1.0"
|
||||
description = ugettext_lazy("Relay domains support for Postfix")
|
||||
|
||||
def load(self):
|
||||
from .app_settings import AdminParametersForm
|
||||
|
||||
parameters.register(
|
||||
AdminParametersForm, ugettext_lazy("Relay domains")
|
||||
)
|
||||
events.declare(EXTENSION_EVENTS)
|
||||
from modoboa.extensions.postfix_relay_domains import general_callbacks
|
||||
if exts_pool.is_extension_installed("modoboa.extensions.limits"):
|
||||
import limits_callbacks
|
||||
if exts_pool.is_extension_installed("modoboa.extensions.amavis"):
|
||||
import amavis_callbacks
|
||||
|
||||
def load_initial_data(self):
|
||||
"""Create extension data."""
|
||||
from .models import Service
|
||||
for service_name in ['relay', 'smtp']:
|
||||
Service.objects.get_or_create(name=service_name)
|
||||
|
||||
if not exts_pool.is_extension_installed("modoboa.extensions.limits"):
|
||||
return
|
||||
from .limits_callbacks import create_new_limits
|
||||
create_new_limits()
|
||||
|
||||
exts_pool.register_extension(PostfixRelayDomains)
|
||||
-100
@@ -1,100 +0,0 @@
|
||||
/**
|
||||
* @module relay_domains
|
||||
* @desc Utility class to manage relay domains.
|
||||
*/
|
||||
|
||||
var RelayDomains = function(options) {
|
||||
this.initialize(options);
|
||||
};
|
||||
|
||||
RelayDomains.prototype = {
|
||||
constructor: RelayDomains,
|
||||
|
||||
defaults: {
|
||||
},
|
||||
|
||||
initialize: function(options) {
|
||||
this.options = $.extend({}, this.defaults, options);
|
||||
admin.register_tag_handler("srv", this.srv_tag_handler);
|
||||
$(document).bind('domain_listing_refresh', this.listing_refresh_cb);
|
||||
},
|
||||
|
||||
listing_refresh_cb: function() {
|
||||
$("a[name=delrelaydomain]").confirm({
|
||||
question: function() { return this.$element.attr('title'); },
|
||||
method: "DELETE",
|
||||
success_cb: $.proxy(admin.reload_listing, admin)
|
||||
});
|
||||
},
|
||||
|
||||
launch_scan: function(e) {
|
||||
var $link = get_target(e);
|
||||
|
||||
e.preventDefault();
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: $link.attr("href"),
|
||||
global: false
|
||||
}).done(function(data) {
|
||||
var $widget = $('#id_service');
|
||||
|
||||
$widget.empty();
|
||||
$.each(data, function(label, id) {
|
||||
$widget.append($('<option />', {
|
||||
value: id,
|
||||
html: label
|
||||
}));
|
||||
});
|
||||
$('.modal-body').prepend(
|
||||
build_success_alert(gettext('Services updated'))
|
||||
);
|
||||
}).fail(function(jqxhr) {
|
||||
var result = $.parseJSON(jqxhr.responseText);
|
||||
$('.modal-body').prepend(
|
||||
build_error_alert(result[0])
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
domainform_cb: function() {
|
||||
$('#btnscan').click(this.launch_scan);
|
||||
$('input:text:visible:first').focus();
|
||||
$("#original_aliases").dynamic_input({
|
||||
input_added: function($row) {
|
||||
$row.find("label").html("");
|
||||
},
|
||||
input_removed: function($input) {
|
||||
$input.parents(".form-group").remove();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
$(".submit").on('click', $.proxy(function(e) {
|
||||
simple_ajax_form_post(e, {
|
||||
formid: "rdomform",
|
||||
reload_on_success: false,
|
||||
success_cb: $.proxy(admin.reload_listing, admin)
|
||||
});
|
||||
}, this));
|
||||
},
|
||||
|
||||
editdomain_form_cb: function() {
|
||||
this.domainform_cb();
|
||||
$(document).trigger('domform_init');
|
||||
},
|
||||
|
||||
srv_tag_handler: function(tag, $link) {
|
||||
if (this.navobj.getparam(tag + "filter") === undefined && $link.hasClass(tag)) {
|
||||
var text = $link.attr("name");
|
||||
this.navobj
|
||||
.setparam("domfilter", "relaydomain")
|
||||
.setparam(tag + "filter", text)
|
||||
.update();
|
||||
if ($("a[name=dom]").length === 0) {
|
||||
$("#taglist").append(this.make_tag("relaydomain", "dom"));
|
||||
}
|
||||
$("#taglist").append(this.make_tag(text, tag));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
{% extends "common/generic_modal_form.html" %}{% block formbody %}
|
||||
{% include "postfix_relay_domains/relaydomain_form.html" %}
|
||||
{% endblock %}
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
{% load form_tags %}
|
||||
{% render_field form.name %}
|
||||
{% render_field form.target_host %}
|
||||
{% include "postfix_relay_domains/service_field.html" with field=form.service label_width="col-sm-4" %}
|
||||
{% render_fields_group form "aliases" %}
|
||||
{% render_field form.enabled %}
|
||||
{% render_field form.verify_recipients %}
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
{% extends "common/generic_field.html" %}{% load i18n %}
|
||||
{% block extra_controls %}
|
||||
<a id="btnscan" class="btn btn-success" style="margin-top: 2px" href="{% url 'postfix_relay_domains:service_scan' %}">{% trans "Scan" %}</a>
|
||||
{% endblock %}
|
||||
@@ -1,209 +0,0 @@
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from modoboa.core.factories import UserFactory
|
||||
from modoboa.extensions.admin import factories
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.tests import ModoTestCase
|
||||
from .factories import RelayDomainFactory, RelayDomainAliasFactory
|
||||
from .models import RelayDomain, RelayDomainAlias, Service
|
||||
|
||||
|
||||
class Operations(object):
|
||||
|
||||
def _create_relay_domain(self, name, status=200):
|
||||
srv, created = Service.objects.get_or_create(name='dummy')
|
||||
values = {
|
||||
'name': name, 'target_host': 'external.host.tld',
|
||||
'service': srv.id, 'enabled': True
|
||||
}
|
||||
return self.ajax_post(
|
||||
reverse("postfix_relay_domains:relaydomain_add"),
|
||||
values, status
|
||||
)
|
||||
|
||||
def _relay_domain_alias_operation(self, optype, rdomain, name, status=200):
|
||||
rdom = RelayDomain.objects.get(name=rdomain)
|
||||
values = {
|
||||
'name': rdom.name, 'target_host': rdom.target_host,
|
||||
'service': rdom.service.id
|
||||
}
|
||||
aliases = [alias.name for alias in rdom.relaydomainalias_set.all()]
|
||||
if optype == 'add':
|
||||
aliases.append(name)
|
||||
else:
|
||||
aliases.remove(name)
|
||||
for cpt, alias in enumerate(aliases):
|
||||
fname = 'aliases' if not cpt else 'aliases_%d' % cpt
|
||||
values[fname] = alias
|
||||
return self.ajax_post(
|
||||
reverse("postfix_relay_domains:relaydomain_change",
|
||||
args=[rdom.id]),
|
||||
values, status
|
||||
)
|
||||
|
||||
def _check_limit(self, name, curvalue, maxvalue):
|
||||
l = self.user.limitspool.get_limit('%s_limit' % name)
|
||||
self.assertEqual(l.curvalue, curvalue)
|
||||
self.assertEqual(l.maxvalue, maxvalue)
|
||||
|
||||
|
||||
class RelayDomainsTestCase(ModoTestCase, Operations):
|
||||
fixtures = ['initial_users.json']
|
||||
|
||||
def setUp(self):
|
||||
super(RelayDomainsTestCase, self).setUp()
|
||||
factories.populate_database()
|
||||
self.rdom = RelayDomainFactory(name='relaydomain.tld')
|
||||
RelayDomainAliasFactory(name='relaydomainalias.tld', target=self.rdom)
|
||||
|
||||
def test_create_relaydomain(self):
|
||||
"""Test the creation of a relay domain.
|
||||
|
||||
We also check that unique constraints are respected: domain,
|
||||
relay domain alias.
|
||||
|
||||
FIXME: add a check for domain alias.
|
||||
"""
|
||||
self._create_relay_domain('relaydomain1.tld')
|
||||
rdom = RelayDomain.objects.get(name='relaydomain1.tld')
|
||||
self.assertEqual(rdom.target_host, 'external.host.tld')
|
||||
self.assertEqual(rdom.service.name, 'dummy')
|
||||
self.assertEqual(rdom.enabled, True)
|
||||
self.assertEqual(rdom.verify_recipients, False)
|
||||
|
||||
resp = self._create_relay_domain('test.com', 400)
|
||||
self.assertEqual(resp['form_errors']['name'][0],
|
||||
'A domain with this name already exists')
|
||||
resp = self._create_relay_domain('relaydomainalias.tld', 400)
|
||||
self.assertEqual(
|
||||
resp['form_errors']['name'][0],
|
||||
'A relay domain alias with this name already exists'
|
||||
)
|
||||
|
||||
def test_create_relaydomainalias(self):
|
||||
"""Test the creation of a relay domain alias.
|
||||
|
||||
We also check that unique constraints are respected: domain,
|
||||
relay domain.
|
||||
|
||||
FIXME: add a check for domain alias.
|
||||
"""
|
||||
self._relay_domain_alias_operation(
|
||||
'add', self.rdom.name, 'relaydomainalias1.tld'
|
||||
)
|
||||
resp = self._relay_domain_alias_operation(
|
||||
'add', self.rdom.name, 'test.com', 400
|
||||
)
|
||||
self.assertEqual(
|
||||
resp['form_errors']['aliases_2'][0],
|
||||
'A domain with this name already exists'
|
||||
)
|
||||
resp = self._relay_domain_alias_operation(
|
||||
'add', self.rdom.name, self.rdom.name, 400
|
||||
)
|
||||
self.assertEqual(
|
||||
resp['form_errors']['aliases_2'][0],
|
||||
'A relay domain with this name already exists'
|
||||
)
|
||||
|
||||
def test_edit_relaydomain(self):
|
||||
"""Test the modification of a relay domain.
|
||||
|
||||
Rename 'relaydomain.tld' domain to 'relaydomain.org'
|
||||
"""
|
||||
values = {
|
||||
'name': 'relaydomain.org', 'target_host': self.rdom.target_host,
|
||||
'service': self.rdom.service.id
|
||||
}
|
||||
self.ajax_post(
|
||||
reverse('postfix_relay_domains:relaydomain_change',
|
||||
args=[self.rdom.id]),
|
||||
values
|
||||
)
|
||||
RelayDomain.objects.get(name='relaydomain.org')
|
||||
|
||||
def test_edit_relaydomainalias(self):
|
||||
"""Test the modification of a relay domain alias.
|
||||
|
||||
Rename 'relaydomainalias.tld' domain to 'relaydomainalias.net'
|
||||
"""
|
||||
values = {
|
||||
'name': 'relaydomain.org', 'target_host': self.rdom.target_host,
|
||||
'service': self.rdom.service.id, 'aliases': 'relaydomainalias.net'
|
||||
}
|
||||
self.ajax_post(
|
||||
reverse('postfix_relay_domains:relaydomain_change',
|
||||
args=[self.rdom.id]),
|
||||
values
|
||||
)
|
||||
RelayDomainAlias.objects.get(name='relaydomainalias.net')
|
||||
with self.assertRaises(RelayDomainAlias.DoesNotExist):
|
||||
RelayDomainAlias.objects.get(name='relaydomainalias.tld')
|
||||
|
||||
def test_delete_relaydomain(self):
|
||||
"""Test the removal of a relay domain
|
||||
"""
|
||||
self.ajax_post(
|
||||
reverse("postfix_relay_domains:relaydomain_delete",
|
||||
args=[self.rdom.id]),
|
||||
{}
|
||||
)
|
||||
with self.assertRaises(RelayDomain.DoesNotExist):
|
||||
RelayDomain.objects.get(name='relaydomain.tld')
|
||||
|
||||
def test_delete_relaydomainalias(self):
|
||||
"""Test the remove of a relay domain alias
|
||||
"""
|
||||
self._relay_domain_alias_operation(
|
||||
'del', self.rdom.name, 'relaydomainalias.tld'
|
||||
)
|
||||
with self.assertRaises(RelayDomain.DoesNotExist):
|
||||
RelayDomain.objects.get(name='relaydomainalias.tld')
|
||||
|
||||
|
||||
class LimitsTestCase(ModoTestCase, Operations):
|
||||
|
||||
def setUp(self):
|
||||
super(LimitsTestCase, self).setUp()
|
||||
from modoboa.extensions.limits.models import LimitTemplates
|
||||
|
||||
for tpl in LimitTemplates().templates:
|
||||
parameters.save_admin('DEFLT_%s' % tpl[0].upper(), 2, app='limits')
|
||||
self.user = UserFactory.create(
|
||||
username='reseller', groups=('Resellers',)
|
||||
)
|
||||
self.clt.logout()
|
||||
self.clt.login(username='reseller', password='toto')
|
||||
|
||||
def test_relay_domains_limit(self):
|
||||
self._create_relay_domain('relaydomain1.tld')
|
||||
self._check_limit('relay_domains', 1, 2)
|
||||
self._create_relay_domain('relaydomain2.tld')
|
||||
self._check_limit('relay_domains', 2, 2)
|
||||
resp = self._create_relay_domain('relaydomain3.tld', 403)
|
||||
self._check_limit('relay_domains', 2, 2)
|
||||
self.ajax_post(
|
||||
reverse('postfix_relay_domains:relaydomain_delete',
|
||||
args=[RelayDomain.objects.get(name='relaydomain2.tld').id]),
|
||||
{}
|
||||
)
|
||||
self._check_limit('relay_domains', 1, 2)
|
||||
|
||||
def test_relay_domain_aliases_limit(self):
|
||||
self._create_relay_domain('relaydomain1.tld')
|
||||
self._relay_domain_alias_operation(
|
||||
'add', 'relaydomain1.tld', 'relay_domain_alias1.tld'
|
||||
)
|
||||
self._check_limit('relay_domain_aliases', 1, 2)
|
||||
self._relay_domain_alias_operation(
|
||||
'add', 'relaydomain1.tld', 'relay_domain_alias2.tld'
|
||||
)
|
||||
self._check_limit('relay_domain_aliases', 2, 2)
|
||||
self._relay_domain_alias_operation(
|
||||
'add', 'relaydomain1.tld', 'relay_domain_alias3.tld', 403
|
||||
)
|
||||
self._check_limit('relay_domain_aliases', 2, 2)
|
||||
self._relay_domain_alias_operation(
|
||||
'delete', 'relaydomain1.tld', 'relay_domain_alias2.tld'
|
||||
)
|
||||
self._check_limit('relay_domain_aliases', 1, 2)
|
||||
@@ -1,13 +0,0 @@
|
||||
from django.conf.urls import patterns, url
|
||||
|
||||
urlpatterns = patterns(
|
||||
'modoboa.extensions.postfix_relay_domains.views',
|
||||
|
||||
url(r'^relaydomains/new/', 'create', name="relaydomain_add"),
|
||||
url(r'^relaydomains/(?P<rdom_id>\d+)/edit/$', 'edit',
|
||||
name='relaydomain_change'),
|
||||
url(r'^relaydomains/(?P<rdom_id>\d+)/delete/$', 'delete',
|
||||
name="relaydomain_delete"),
|
||||
url(r'^relaydomains/scan_services/$', 'scan_for_services',
|
||||
name="service_scan"),
|
||||
)
|
||||
@@ -1,75 +0,0 @@
|
||||
import reversion
|
||||
from django.shortcuts import render
|
||||
from django.db import transaction
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.views.decorators.http import require_http_methods
|
||||
from django.contrib.auth.decorators import (
|
||||
login_required, permission_required
|
||||
)
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib.exceptions import PermDeniedException
|
||||
from modoboa.lib.web_utils import render_to_json_response
|
||||
from .models import RelayDomain, Service
|
||||
from .forms import RelayDomainForm, RelayDomainFormGeneral
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required("postfix_relay_domains.add_relaydomain")
|
||||
@transaction.commit_on_success
|
||||
@reversion.create_revision()
|
||||
def create(request, tplname="postfix_relay_domains/new_relaydomain_form.html"):
|
||||
events.raiseEvent("CanCreate", request.user, "relay_domains")
|
||||
if request.method == 'POST':
|
||||
form = RelayDomainFormGeneral(request.POST)
|
||||
if form.is_valid():
|
||||
rdom = form.save(request.user)
|
||||
rdom.post_create(request.user)
|
||||
return render_to_json_response(_("Relay domain created"))
|
||||
return render_to_json_response(
|
||||
{'form_errors': form.errors}, status=400
|
||||
)
|
||||
|
||||
ctx = {"title": _("New relay domain"),
|
||||
"action_label": _("Create"),
|
||||
"action_classes": "submit",
|
||||
"action": reverse("postfix_relay_domains:relaydomain_add"),
|
||||
"formid": "rdomform",
|
||||
"form": RelayDomainFormGeneral()}
|
||||
return render(request, tplname, ctx)
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required("postfix_relay_domains.change_relaydomain")
|
||||
@reversion.create_revision()
|
||||
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)
|
||||
return RelayDomainForm(request, instances=instances).process()
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required("postfix_relay_domains.delete_relaydomain")
|
||||
def delete(request, rdom_id):
|
||||
rdom = RelayDomain.objects.get(pk=rdom_id)
|
||||
if not request.user.can_access(rdom):
|
||||
raise PermDeniedException
|
||||
rdom.delete()
|
||||
return render_to_json_response(_('Relay domain deleted'))
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required("postfix_relay_domains.add_service")
|
||||
@require_http_methods(["POST"])
|
||||
def scan_for_services(request):
|
||||
try:
|
||||
Service.objects.load_from_master_cf()
|
||||
except IOError as e:
|
||||
return render_to_json_response([str(e)], status=500)
|
||||
|
||||
return render_to_json_response(
|
||||
dict((srv.name, srv.id) for srv in Service.objects.all())
|
||||
)
|
||||
Reference in New Issue
Block a user