diff --git a/modoboa/admin/app_settings.py b/modoboa/admin/app_settings.py index c7a0d1c3f..4418e3ffd 100644 --- a/modoboa/admin/app_settings.py +++ b/modoboa/admin/app_settings.py @@ -108,6 +108,14 @@ class AdminParametersForm(param_forms.AdminParametersForm): ) ) + create_alias_on_mbox_rename = YesNoField( + label=ugettext_lazy("Create an alias when a mailbox is renamed"), + initial=False, + help_text=ugettext_lazy( + "Create an alias using the old address when a mailbox is renamed." + ) + ) + # Visibility rules visibility_rules = { "valid_mxs": "enable_mx_checks=True", diff --git a/modoboa/admin/forms/account.py b/modoboa/admin/forms/account.py index 093529f32..19effd8ab 100644 --- a/modoboa/admin/forms/account.py +++ b/modoboa/admin/forms/account.py @@ -223,6 +223,11 @@ class AccountFormMail(forms.Form, DynamicForm): email = lib_fields.UTF8EmailField( label=ugettext_lazy("E-mail"), required=False) + create_alias_with_old_address = forms.BooleanField( + label=ugettext_lazy("Create an alias using the old address"), + required=False, + initial=False + ) quota = forms.IntegerField( label=ugettext_lazy("Quota"), required=False, @@ -277,7 +282,12 @@ class AccountFormMail(forms.Form, DynamicForm): self.fields["quota_act"].initial = self.mb.use_domain_quota if not self.mb.use_domain_quota and self.mb.quota: self.fields["quota"].initial = self.mb.quota + self.fields["create_alias_with_old_address"].initial = ( + param_tools.get_global_parameter( + "create_alias_on_mbox_rename") + ) else: + del self.fields["create_alias_with_old_address"] self.fields["quota_act"].initial = True if len(args) and isinstance(args[0], QueryDict): @@ -422,6 +432,8 @@ class AccountFormMail(forms.Form, DynamicForm): else: self.cleaned_data["use_domain_quota"] = ( self.cleaned_data["quota_act"]) + if self.cleaned_data.get("create_alias_with_old_address", False): + self.aliases.append(self.mb.full_address) self.mb.update_from_dict(user, self.cleaned_data) account.email = self.cleaned_data["email"] diff --git a/modoboa/admin/handlers.py b/modoboa/admin/handlers.py index 427990efa..41663b927 100644 --- a/modoboa/admin/handlers.py +++ b/modoboa/admin/handlers.py @@ -84,14 +84,20 @@ def manage_alias_for_mailbox(sender, instance, **kwargs): old_address = getattr(instance, "old_full_address", None) if old_address is None or old_address == instance.full_address: return + # Update old self alias alr = models.AliasRecipient.objects.get( alias__address=old_address, address=old_address, r_mailbox=instance, alias__internal=True) alr.address = instance.full_address alr.save() alr.alias.address = instance.full_address + alr.alias.domain = instance.domain alr.alias.save() + # Update AliasRecipient instances + instance.aliasrecipient_set.filter(alias__internal=False).update( + address=instance.full_address) + @receiver(signals.pre_delete, sender=models.Mailbox) def mailbox_deleted_handler(sender, **kwargs): diff --git a/modoboa/admin/static/admin/js/admin.js b/modoboa/admin/static/admin/js/admin.js index 5c33d4f1c..f3cd6cf26 100644 --- a/modoboa/admin/static/admin/js/admin.js +++ b/modoboa/admin/static/admin/js/admin.js @@ -437,13 +437,20 @@ Identities.prototype = { }, simpleuser_mode: function() { - $("#id_username").autocompleter({ + var $username = $('#id_username'); + var $email = $('#id_email'); + + $username.change(function (evt) { + $email.val($(this).val()); + $email.change(); + }); + $username.autocompleter({ from_character: "@", choices: $.proxy(this.get_domain_list, this) }); $("#id_email").addClass("disabled") .attr("readonly", "") - .autocompleter("unbind"); + .autocompleter("unbind"); this.toggle_master_user(); }, @@ -483,6 +490,18 @@ Identities.prototype = { } }, + /** + * Callback to watch for username change. + * + */ + emailChanged: function(event) { + var $target = get_target(event); + + if ($target.val() !== this.originalEmail) { + $('#id_create_alias_with_old_address').parents(".form-group").show(); + } + }, + generalform_init: function(notrigger) { $("#id_role").change($.proxy(function(e) { var $this = $(e.target); @@ -499,6 +518,7 @@ Identities.prototype = { this.togglePasswordInputs(!$this.prop('checked')); }, this)); this.toggle_master_user($("#id_role").val()); + $('#id_create_alias_with_old_address').parents('.form-group').hide(); if (notrigger !== undefined && notrigger) { return; } @@ -506,6 +526,10 @@ Identities.prototype = { }, mailform_init: function() { + var $email = $('#id_email'); + + this.originalEmail = $email.val(); + $email.change($.proxy(this.emailChanged, this)); $("#id_aliases").autocompleter({ from_character: "@", choices: $.proxy(this.get_domain_list, this) @@ -528,7 +552,7 @@ Identities.prototype = { return true; } }); - $("#id_email").autocompleter({ + $email.autocompleter({ from_character: "@", choices: $.proxy(this.get_domain_list, this) }); diff --git a/modoboa/admin/templates/admin/index.html b/modoboa/admin/templates/admin/index.html new file mode 100644 index 000000000..3f7c3e2a5 --- /dev/null +++ b/modoboa/admin/templates/admin/index.html @@ -0,0 +1,29 @@ +{% extends "fluid.html" %} + +{% load i18n lib_tags static %} +{% load render_bundle from webpack_loader %} + +{% block pagetitle %}{% trans "Admin" %}{% endblock %} + +{% block extra_css %} + {% if not debug %} + {% render_bundle 'app' 'css' 'ADMIN' %} + {% endif %} +{% endblock %} + +{% block extra_js %} + {% if not debug %} + {% render_bundle 'manifest' 'js' 'ADMIN' %} + {% render_bundle 'vendor' 'js' 'ADMIN' %} + {% render_bundle 'app' 'js' 'ADMIN' %} + {% else %} + {% render_bundle 'app' 'js' 'ADMIN' %} + {% endif %} +{% endblock %} + +{% block container_content %} + +
+
+ +{% endblock %} diff --git a/modoboa/admin/templates/admin/mailform.html b/modoboa/admin/templates/admin/mailform.html index c82ad0a6d..f903e8afd 100644 --- a/modoboa/admin/templates/admin/mailform.html +++ b/modoboa/admin/templates/admin/mailform.html @@ -1,6 +1,9 @@ {% load form_tags %} {% render_field form.email %} +{% if form.create_alias_with_old_address %} + {% render_field form.create_alias_with_old_address %} +{% endif %} {% include "common/generic_field.html" with field=form.quota help_display_mode="tooltip" label_width="col-sm-4" appended_text=_("MB") activator=True activator_value=form.quota_act.value deactivate_if_empty=False activator_label=_("Use domain default value") activator_size="col-sm-5" %} {% render_fields_group form "aliases" %} {% render_fields_group form "senderaddress" %} diff --git a/modoboa/admin/tests/test_account.py b/modoboa/admin/tests/test_account.py index 38cfb0964..e1b94a6ea 100644 --- a/modoboa/admin/tests/test_account.py +++ b/modoboa/admin/tests/test_account.py @@ -107,6 +107,78 @@ class AccountTestCase(ModoTestCase): alias__internal=True).exists() ) + def test_aliases_update_on_rename(self): + """Check if aliases are updated on mailbox rename.""" + account = User.objects.get(username="user@test.com") + values = { + "username": "new_user@test.com", + "first_name": account.first_name, + "last_name": account.last_name, + "role": account.role, + "quota_act": True, + "is_active": account.is_active, + "email": "new_user@test.com", + "language": "en", + "create_alias_with_old_address": False, + "aliases_1": "alias@test.com" + } + url = reverse("admin:account_change", args=[account.pk]) + self.ajax_post(url, values) + qset = account.mailbox.aliasrecipient_set.filter(alias__internal=False) + for alr in qset: + self.assertEqual(alr.address, values["email"]) + + def test_create_alias_on_rename(self): + """Check if alias is automatically created.""" + account = User.objects.get(username="user@test.com") + values = { + "username": "new_user@test.com", + "first_name": account.first_name, + "last_name": account.last_name, + "role": account.role, + "quota_act": True, + "is_active": account.is_active, + "email": "new_user@test.com", + "language": "en", + "create_alias_with_old_address": False + } + url = reverse("admin:account_change", args=[account.pk]) + # Rename while option is set to False -> no alias created + self.ajax_post(url, values) + self.assertFalse( + models.AliasRecipient.objects.filter( + address="new_user@test.com", alias__address="user@test.com", + alias__internal=False + ).exists() + ) + # Now rename while option set to True -> alias created + values.update({ + "username": "user@test.com", + "email": "user@test.com", + "create_alias_with_old_address": True} + ) + self.ajax_post(url, values) + self.assertTrue( + models.AliasRecipient.objects.filter( + address="user@test.com", alias__address="new_user@test.com", + alias__internal=False + ).exists() + ) + # Change domain while option set to True -> alias created + values.update({ + "username": "new_user@test2.com", + "email": "new_user@test2.com", + }) + self.ajax_post(url, values) + self.assertTrue( + models.AliasRecipient.objects.filter( + address="new_user@test2.com", + alias__address="user@test.com", + alias__domain__name="test.com", + alias__internal=False + ).exists() + ) + def test_password_constraints(self): """Check password constraints.""" values = { diff --git a/modoboa/static/js/autocompleter.js b/modoboa/static/js/autocompleter.js index 1467343b4..b28ba3b83 100644 --- a/modoboa/static/js/autocompleter.js +++ b/modoboa/static/js/autocompleter.js @@ -103,6 +103,7 @@ this.$element.val(curvalue.substr( 0, curvalue.indexOf(this.options.from_character) + 1 ) + $link.attr("name")); + this.$element.change(); } this.hide_menu(); }