VMM: {alias,catchall}delete: Accept multiple destinations.
authorPascal Volk <user@localhost.localdomain.org>
Thu, 27 Sep 2012 19:15:09 +0000
changeset 618 d8736bb80bdc
parent 617 9eecf0160c39
child 619 4ec5c015b7aa
VMM: {alias,catchall}delete: Accept multiple destinations.
VirtualMailManager/alias.py
VirtualMailManager/catchall.py
VirtualMailManager/cli/clihelp.py
VirtualMailManager/cli/subcommands.py
VirtualMailManager/handler.py
man/de/man1/vmm.1
man/man1/vmm.1
--- a/VirtualMailManager/alias.py	Mon Sep 24 19:13:51 2012 +0000
+++ b/VirtualMailManager/alias.py	Thu Sep 27 19:15:09 2012 +0000
@@ -73,20 +73,21 @@
                                  'limit': limit, 'count_new': count_new},
                        ALIAS_EXCEEDS_EXPANSION_LIMIT)
 
-    def _delete(self, destination=None):
-        """Deletes a destination from the alias, if ``destination`` is
-        not ``None``.  If ``destination`` is None, the alias with all
+    def _delete(self, destinations=None):
+        """Deletes the *destinations* from the alias, if ``destinations``
+        is not ``None``.  If ``destinations`` is None, the alias with all
         its destination addresses will be deleted.
 
         """
         dbc = self._dbh.cursor()
-        if not destination:
+        if not destinations:
             dbc.execute('DELETE FROM alias WHERE gid = %s AND address = %s',
                         (self._gid, self._addr.localpart))
         else:
-            dbc.execute('DELETE FROM alias WHERE gid = %s AND address = %s '
-                        'AND destination = %s',
-                        (self._gid, self._addr.localpart, str(destination)))
+            dbc.executemany("DELETE FROM alias WHERE gid = %d AND address = "
+                            "'%s' AND destination = %%s" % (self._gid,
+                                                         self._addr.localpart),
+                            ((str(dest),) for dest in destinations))
         if dbc.rowcount > 0:
             self._dbh.commit()
         dbc.close()
@@ -135,18 +136,34 @@
         self._dests.extend(destinations)
         return destinations
 
-    def del_destination(self, destination):
-        """Deletes the specified ``destination`` address from the alias."""
-        assert isinstance(destination, EmailAddress)
+    def del_destinations(self, destinations, warnings=None):
+        """Delete the specified `EmailAddress`es of *destinations* from
+        the alias's destinations.
+
+        """
+        destinations = set(destinations)
+        assert destinations and \
+                all(isinstance(dest, EmailAddress) for dest in destinations)
+        if not warnings is None:
+            assert isinstance(warnings, list)
+        if self._addr in destinations:
+            destinations.remove(self._addr)
+            if not warnings is None:
+                warnings.append(self._addr)
         if not self._dests:
             raise AErr(_(u"The alias '%s' does not exist.") % self._addr,
                        NO_SUCH_ALIAS)
-        if not destination in self._dests:
-            raise AErr(_(u"The address '%(addr)s' is not a destination of "
-                         u"the alias '%(alias)s'.") % {'addr': destination,
-                       'alias': self._addr}, NO_SUCH_ALIAS)
-        self._delete(destination)
-        self._dests.remove(destination)
+        unknown = destinations.difference(set(self._dests))
+        if unknown:
+            destinations.intersection_update(set(self._dests))
+            if not warnings is None:
+                warnings.extend(unknown)
+        if not destinations:
+            raise AErr(_(u"No suitable destinations left to remove from alias "
+                         u"'%s'.") % self._addr, NO_SUCH_ALIAS)
+        self._delete(destinations)
+        for destination in destinations:
+            self._dests.remove(destination)
 
     def get_destinations(self):
         """Returns an iterator for all destinations of the alias."""
--- a/VirtualMailManager/catchall.py	Mon Sep 24 19:13:51 2012 +0000
+++ b/VirtualMailManager/catchall.py	Thu Sep 27 19:15:09 2012 +0000
@@ -84,18 +84,19 @@
                                  'limit': limit, 'count_new': count_new},
                        ALIAS_EXCEEDS_EXPANSION_LIMIT)
 
-    def _delete(self, destination=None):
-        """Deletes a destination from the catchall alias, if ``destination``
-        is not ``None``.  If ``destination`` is None, the catchall alias with
-        all its destination addresses will be deleted.
+    def _delete(self, destinations=None):
+        """Delete one ore multiple destinations from the catchall alias, if
+        ``destinations`` is not ``None``.  If ``destinations`` is None, the
+        catchall alias with all its destination addresses will be deleted.
 
         """
         dbc = self._dbh.cursor()
-        if not destination:
+        if not destinations:
             dbc.execute('DELETE FROM catchall WHERE gid = %s', (self._gid,))
         else:
-            dbc.execute('DELETE FROM catchall WHERE gid = %s '
-                        'AND destination = %s', (self._gid, str(destination)))
+            dbc.executemany('DELETE FROM catchall WHERE gid = %d AND '
+                            'destination = %%s' % self._gid,
+                            ((str(dest),) for dest in destinations))
         if dbc.rowcount > 0:
             self._dbh.commit()
         dbc.close()
@@ -139,20 +140,28 @@
         self._dests.extend(destinations)
         return destinations
 
-    def del_destination(self, destination):
-        """Deletes the specified ``destination`` address from the catchall
-        alias."""
-        assert isinstance(destination, EmailAddress)
+    def del_destinations(self, destinations, warnings=None):
+        """Deletes the specified ``destinations`` from the catchall alias."""
+        destinations = set(destinations)
+        assert destinations and \
+                all(isinstance(dest, EmailAddress) for dest in destinations)
+        if not warnings is None:
+            assert isinstance(warnings, list)
         if not self._dests:
             raise AErr(_(u"There are no catch-all aliases defined for "
                          u"domain '%s'.") % self._domain, NO_SUCH_ALIAS)
-        if not destination in self._dests:
-            raise AErr(_(u"The address '%(addr)s' is not a destination of "
-                         u"the catch-all alias for domain '%(domain)s'.")
-                       % {'addr': destination, 'domain': self._domain},
+        unknown = destinations.difference(set(self._dests))
+        if unknown:
+            destinations.intersection_update(set(self._dests))
+            if not warnings is None:
+                warnings.extend(unknown)
+        if not destinations:
+            raise AErr(_(u"No suitable destinations left to remove from the "
+                         u"catch-all alias of domain '%s'.") % self._domain,
                        NO_SUCH_ALIAS)
-        self._delete(destination)
-        self._dests.remove(destination)
+        self._delete(destinations)
+        for destination in destinations:
+            self._dests.remove(destination)
 
     def get_destinations(self):
         """Returns an iterator for all destinations of the catchall alias."""
--- a/VirtualMailManager/cli/clihelp.py	Mon Sep 24 19:13:51 2012 +0000
+++ b/VirtualMailManager/cli/clihelp.py	Thu Sep 27 19:15:09 2012 +0000
@@ -193,6 +193,11 @@
 will be replaced by the local part, the domain, or the email address with '@'
 replaced by '=' respectively. In combination with alias domains, this enables
 domain-specific destinations."""),),
+    # TP: description of subcommand aliasdelete
+    'aliasdelete': (_(u"""This subcommand is used to delete one or multiple
+<destination>s from the alias with the given <address>."""),
+_(u"""When no <destination> address was specified the alias with all its
+destinations will be deleted."""),),
     # TP: description of subcommand aliasinfo
     'aliasinfo': (_(u"""Information about the alias with the given <address>
 can be displayed with this subcommand."""),),
@@ -222,8 +227,8 @@
 aliases defined for the domain <fqdn>."""),),
     # TP: description of subcommand catchalldelete
     'catchalldelete': (_(u"""With this subcommand, catch-all aliases defined
-for a domain can be removed, either all of them, or a single one if specified
-explicitly."""),),
+for a domain can be removed, either all of them, or those <destination>s which
+were specified explicitly."""),),
 }
 
 del _
--- a/VirtualMailManager/cli/subcommands.py	Mon Sep 24 19:13:51 2012 +0000
+++ b/VirtualMailManager/cli/subcommands.py	Thu Sep 27 19:15:09 2012 +0000
@@ -133,7 +133,7 @@
     elif ctx.argc < 4:
         ctx.hdlr.alias_delete(ctx.args[2].lower())
     else:
-        ctx.hdlr.alias_delete(ctx.args[2].lower(), ctx.args[3])
+        ctx.hdlr.alias_delete(ctx.args[2].lower(), ctx.args[3:])
 
 
 def alias_info(ctx):
@@ -220,7 +220,7 @@
     elif ctx.argc < 4:
         ctx.hdlr.catchall_delete(ctx.args[2].lower())
     else:
-        ctx.hdlr.catchall_delete(ctx.args[2].lower(), ctx.args[3])
+        ctx.hdlr.catchall_delete(ctx.args[2].lower(), ctx.args[3:])
 
 
 def catchall_info(ctx):
@@ -826,7 +826,7 @@
                     _(u'create a new alias e-mail address with one or more '
                       u'destinations')),
     'aliasdelete': cmd('aliasdelete', 'ad', alias_delete,
-                       'address [destination]',
+                       'address [destination ...]',
                        _(u'delete the specified alias e-mail address or one '
                          u'of its destinations')),
     'aliasinfo': cmd('aliasinfo', 'ai', alias_info, 'address',
@@ -848,7 +848,7 @@
                        _(u'add one or more catch-all destinations for a '
                          u'domain')),
     'catchalldelete': cmd('catchalldelete', 'cad', catchall_delete,
-                       'fqdn [destination]',
+                       'fqdn [destination ...]',
                        _(u'delete the specified catch-all destination or all '
                          u'of a domain\'s destinations')),
     'catchallinfo': cmd('catchallinfo', 'cai', catchall_info, 'fqdn',
--- a/VirtualMailManager/handler.py	Mon Sep 24 19:13:51 2012 +0000
+++ b/VirtualMailManager/handler.py	Thu Sep 27 19:15:09 2012 +0000
@@ -711,16 +711,27 @@
             raise VMMError(_(u"The alias '%s' does not exist.") %
                            alias.address, NO_SUCH_ALIAS)
 
-    def alias_delete(self, aliasaddress, targetaddress=None):
+    def alias_delete(self, aliasaddress, targetaddresses=None):
         """Deletes the `Alias` *aliasaddress* with all its destinations from
-        the database. If *targetaddress* is not ``None``, only this
-        destination will be removed from the alias."""
+        the database. If *targetaddresses* is not ``None``, only the given
+        destinations will be removed from the alias."""
         alias = self._get_alias(aliasaddress)
-        if targetaddress is None:
+        error = None
+        if targetaddresses is None:
             alias.delete()
         else:
-            alias.del_destination(DestinationEmailAddress(targetaddress,
-                                                          self._dbh))
+            destinations = [DestinationEmailAddress(addr, self._dbh)
+                            for addr in targetaddresses]
+            warnings = []
+            try:
+                alias.del_destinations(destinations, warnings)
+            except VMMError, err:
+                error = err
+            if warnings:
+                self._warnings.append(_('Ignored destination addresses:'))
+                self._warnings.extend(('  * %s' % w for w in warnings))
+            if error:
+                raise error
 
     def catchall_add(self, domain, *targetaddresses):
         """Creates a new `CatchallAlias` entry for the given *domain* with
@@ -744,16 +755,27 @@
         instances) for the `CatchallAlias` with the given *domain*."""
         return self._get_catchall(domain).get_destinations()
 
-    def catchall_delete(self, domain, targetaddress=None):
+    def catchall_delete(self, domain, targetaddresses=None):
         """Deletes the `CatchallAlias` for domain *domain* with all its
-        destinations from the database. If *targetaddress* is not ``None``,
-        only this destination will be removed from the alias."""
+        destinations from the database.  If *targetaddresses* is not
+        ``None``,  only those destinations will be removed from the alias."""
         catchall = self._get_catchall(domain)
-        if targetaddress is None:
+        error = None
+        if targetaddresses is None:
             catchall.delete()
         else:
-            catchall.del_destination(DestinationEmailAddress(targetaddress,
-                                                             self._dbh))
+            destinations = [DestinationEmailAddress(addr, self._dbh)
+                            for addr in targetaddresses]
+            warnings = []
+            try:
+                catchall.del_destinations(destinations, warnings)
+            except VMMError, err:
+                error = err
+            if warnings:
+                self._warnings.append(_('Ignored destination addresses:'))
+                self._warnings.extend(('  * %s' % w for w in warnings))
+            if error:
+                raise error
 
     def user_info(self, emailaddress, details=None):
         """Wrapper around Account.get_info(...)"""
--- a/man/de/man1/vmm.1	Mon Sep 24 19:13:51 2012 +0000
+++ b/man/de/man1/vmm.1	Thu Sep 27 19:15:09 2012 +0000
@@ -1,4 +1,4 @@
-.TH "VMM" "1" "2012-08-12" "vmm 0.6" "vmm"
+.TH "VMM" "1" "2012-09-27" "vmm 0.6" "vmm"
 .SH NAME
 vmm \- Kommandozeilenprogramm zur Verwaltung von E\-Mail\-Domains/\-Konten
 und \-Aliase.
@@ -853,15 +853,15 @@
 .\" ------------------------------------
 .SS aliasdelete (ad)
 .BI "vmm aliasdelete" " address"
-.RI [ destination ]
+.RI [ destination " ...]"
 .PP
 Verwenden Sie diesen Unterbefehl um den Alias mit der angegebenen Adresse
 zu löschen.
 .PP
-Wurde eine optionale
+Wurden eine oder mehrere optionale
 .I destination
-angegeben, so wird nur diese
-.I destination
+Adressen angegeben, so werden nur diese
+.IR destination s
 vom angegebenen Alias entfernt.
 .PP
 Beispiel:
@@ -972,11 +972,13 @@
 .\" ------------------------------------
 .SS catchalldelete (cad)
 .BI "vmm catchalldelete " fqdn
-.RI [ destination ]
+.RI [ destination " ...]"
 .PP
 Mit diesem Unterbefehl werden Catch\-all Aliase einer Domain wieder
-gelöscht, entweder nur das angegebene Alias, oder alle, wenn keines
-angegeben wurde.
+gelöscht,
+entweder nur das/die angegebene(n) Alias(e), oder alle, wenn keine
+.I destination
+Adresse angegeben wurde.
 .PP
 Beispiel:
 .PP
--- a/man/man1/vmm.1	Mon Sep 24 19:13:51 2012 +0000
+++ b/man/man1/vmm.1	Thu Sep 27 19:15:09 2012 +0000
@@ -1,4 +1,4 @@
-.TH "VMM" "1" "2012-09-02" "vmm 0.6" "vmm"
+.TH "VMM" "1" "2012-09-27" "vmm 0.6" "vmm"
 .SH NAME
 vmm \- command line tool to manage email domains/accounts/aliases
 .\" -----------------------------------------------------------------------
@@ -821,15 +821,16 @@
 .\" ------------------------------------
 .SS aliasdelete (ad)
 .BI "vmm aliasdelete" " address"
-.RI [ destination ]
+.RI [ destination " ...]"
 .PP
-Use this subcommand to delete the alias with the given
+This subcommand is used to delete one or multiple
+.IR  destination s
+from the alias with the given
 .IR address .
 .PP
-If the optional
+When no
 .I destination
-address is given, only this
-destination will be removed from the alias.
+address was specified the alias with all its destinations will be deleted.
 .PP
 Example:
 .PP
@@ -935,10 +936,12 @@
 .\" ------------------------------------
 .SS catchalldelete (cad)
 .BI "vmm catchalldelete " fqdn
-.RI [ destination ]
+.RI [ destination " ...]"
 .PP
 With this subcommand, catch\-all aliases defined for a domain can be
-removed, either all of them, or a single one if specified explicitly.
+removed, either all of them, or those
+.IR destination s
+which were specified explicitly.
 .PP
 Example:
 .PP