diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 6b6d7915e..d2dbb2acb 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -13,11 +13,31 @@ + + {{ notification }} + + + diff --git a/frontend/src/components/domains/DomainAliasForm.vue b/frontend/src/components/domains/DomainAliasForm.vue index 3fd3d60f1..5e1c4d955 100644 --- a/frontend/src/components/domains/DomainAliasForm.vue +++ b/frontend/src/components/domains/DomainAliasForm.vue @@ -4,20 +4,39 @@ {{ title }} - - - + + + + + + + + + @@ -41,6 +60,7 @@ - diff --git a/frontend/src/components/tools/ChoiceField.vue b/frontend/src/components/tools/ChoiceField.vue index b37d96e24..00f6457f3 100644 --- a/frontend/src/components/tools/ChoiceField.vue +++ b/frontend/src/components/tools/ChoiceField.vue @@ -5,7 +5,7 @@
{{ choice.icon }} {{ choice.label }} @@ -16,20 +16,39 @@ @@ -44,5 +63,9 @@ export default { &--selected { border-color: #046BF8 !important; } + + &--disabled { + cursor: unset; + } } diff --git a/frontend/src/filters/index.js b/frontend/src/filters/index.js new file mode 100644 index 000000000..108b12c48 --- /dev/null +++ b/frontend/src/filters/index.js @@ -0,0 +1,7 @@ +import Vue from 'vue' +import { translate } from 'vue-gettext' +const { gettext: $gettext } = translate + +Vue.filter('yesno', function (value) { + return (value) ? $gettext('yes') : $gettext('no') +}) diff --git a/frontend/src/main.js b/frontend/src/main.js index ed0d8331c..2217a53b7 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -3,6 +3,7 @@ import './plugins/axios' import './plugins/vee-validate' import './plugins/vue-cookie' import './plugins/vue-gettext' +import './filters' import App from './App.vue' import router from './router' import store from './store' @@ -10,6 +11,8 @@ import vuetify from './plugins/vuetify' Vue.config.productionTip = false +export const bus = new Vue() + new Vue({ router, store, diff --git a/frontend/src/views/Domains.vue b/frontend/src/views/Domains.vue index 712ab76fd..f80d0ce3b 100644 --- a/frontend/src/views/Domains.vue +++ b/frontend/src/views/Domains.vue @@ -30,6 +30,7 @@ diff --git a/modoboa/admin/api.py b/modoboa/admin/api.py index 29c4845de..c3877ffc0 100644 --- a/modoboa/admin/api.py +++ b/modoboa/admin/api.py @@ -38,12 +38,16 @@ class DomainViewSet(viewsets.ModelViewSet): """Domain viewset.""" permission_classes = [IsAuthenticated, DjangoModelPermissions, ] - serializer_class = serializers.DomainSerializer def get_queryset(self): """Filter queryset based on current user.""" return models.Domain.objects.get_for_admin(self.request.user) + def get_serializer_class(self, *args, **kwargs): + if self.request.version == "v2" and self.action == "create": + return serializers.DomainSerializerV2 + return serializers.DomainSerializer + def perform_destroy(self, instance): """Add custom args to delete call.""" instance.delete(self.request.user) diff --git a/modoboa/admin/serializers.py b/modoboa/admin/serializers.py index e78b0f82e..9deeeb0b6 100644 --- a/modoboa/admin/serializers.py +++ b/modoboa/admin/serializers.py @@ -110,6 +110,85 @@ class DomainSerializer(serializers.ModelSerializer): return domain +class CreateDomainAdminSerializer(serializers.Serializer): + """Sub serializer for domain administrator creation.""" + + username = serializers.CharField() + with_random_password = serializers.BooleanField(default=False) + with_mailbox = serializers.BooleanField(default=False) + with_aliases = serializers.BooleanField(default=False) + + +class DomainSerializerV2(DomainSerializer): + """Domain serializer for v2 API.""" + + domain_admin = CreateDomainAdminSerializer(required=False) + + class Meta(DomainSerializer.Meta): + fields = DomainSerializer.Meta.fields + ( + "domain_admin", + ) + + def create(self, validated_data): + """Create administrator and other stuff if needed.""" + domain_admin = validated_data.pop("domain_admin", None) + domain = super().create(validated_data) + if not domain_admin: + return domain + + # 1. Create a domain administrator + username = "%s@%s" % (domain_admin["username"], domain.name) + try: + da = core_models.User.objects.get(username=username) + except core_models.User.DoesNotExist: + pass + else: + raise lib_exceptions.Conflict( + _("User '%s' already exists") % username) + user = self.context["request"].user + core_signals.can_create_object.send( + self.__class__, context=user, klass=models.Mailbox) + da = core_models.User(username=username, email=username, is_active=True) + if domain_admin["with_random_password"]: + password = lib.make_password() + else: + password = param_tools.get_global_parameter( + "default_password", app="core") + da.set_password(password) + da.save() + da.role = "DomainAdmins" + da.post_create(user) + + # 2. Create mailbox if needed + if domain_admin["with_mailbox"]: + dom_admin_username = domain_admin["username"] + mb = models.Mailbox( + address=dom_admin_username, domain=domain, + user=da, use_domain_quota=True + ) + mb.set_quota( + override_rules=user.has_perm("admin.change_domain")) + mb.save(creator=user) + + # 3. Create aliases if needed + condition = ( + domain.type == "domain" and + domain_admin["with_aliases"] and + dom_admin_username != "postmaster" + ) + if condition: + core_signals.can_create_object.send( + self.__class__, context=user, klass=models.Alias) + address = u"postmaster@{}".format(domain.name) + alias = models.Alias.objects.create( + address=address, domain=domain, enabled=True) + alias.set_recipients([mb.full_address]) + alias.post_create(user) + + domain.add_admin(da) + return domain + + class DomainAliasSerializer(FlexFieldsModelSerializer): """Base DomainAlias serializer."""