# HG changeset patch # User Pascal Volk # Date 1348773309 0 # Node ID d8736bb80bdcc9c3e9ee4135f17a017268a9f1d1 # Parent 9eecf0160c39b821292f79ac5f338f04370581a9 VMM: {alias,catchall}delete: Accept multiple destinations. diff -r 9eecf0160c39 -r d8736bb80bdc VirtualMailManager/alias.py --- 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.""" diff -r 9eecf0160c39 -r d8736bb80bdc VirtualMailManager/catchall.py --- 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.""" diff -r 9eecf0160c39 -r d8736bb80bdc VirtualMailManager/cli/clihelp.py --- 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 +s from the alias with the given
."""), +_(u"""When no 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
can be displayed with this subcommand."""),), @@ -222,8 +227,8 @@ aliases defined for the domain ."""),), # 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 s which +were specified explicitly."""),), } del _ diff -r 9eecf0160c39 -r d8736bb80bdc VirtualMailManager/cli/subcommands.py --- 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', diff -r 9eecf0160c39 -r d8736bb80bdc VirtualMailManager/handler.py --- 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(...)""" diff -r 9eecf0160c39 -r d8736bb80bdc man/de/man1/vmm.1 --- 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 diff -r 9eecf0160c39 -r d8736bb80bdc man/man1/vmm.1 --- 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