summaryrefslogtreecommitdiff
path: root/src/mailman/commands
diff options
context:
space:
mode:
authorBarry Warsaw2009-08-09 10:49:35 -0400
committerBarry Warsaw2009-08-09 10:49:35 -0400
commit9cb2d844baf1ec47bed25fd48e5762ab641b5498 (patch)
tree071f4c18deec29c8c270bef8d8d3fd94b6cdaec8 /src/mailman/commands
parentc4a053b184f16156cafd984b01c055d70414c4b6 (diff)
downloadmailman-9cb2d844baf1ec47bed25fd48e5762ab641b5498.tar.gz
mailman-9cb2d844baf1ec47bed25fd48e5762ab641b5498.tar.zst
mailman-9cb2d844baf1ec47bed25fd48e5762ab641b5498.zip
A start on the 'mailman' subcommand layout, with the help of argparse. Right
now the only subcommand is 'lists' which displays all mailing lists like the old bin/list_lists command did (which is now removed). Remove bin/version since 'bin/mailman --version' does this for us. Simplify the calculation of the bin scripts; there will be many fewer of them. Extend i18n to use a class based structure. By default, all i18n strings are dedented after translation and substitution, which improves command line help. The class structure allows for overriding this behavior.
Diffstat (limited to 'src/mailman/commands')
-rw-r--r--src/mailman/commands/docs/lists.txt127
-rw-r--r--src/mailman/commands/lists.py102
2 files changed, 229 insertions, 0 deletions
diff --git a/src/mailman/commands/docs/lists.txt b/src/mailman/commands/docs/lists.txt
new file mode 100644
index 000000000..85fe7edc2
--- /dev/null
+++ b/src/mailman/commands/docs/lists.txt
@@ -0,0 +1,127 @@
+=========================
+Command line list display
+=========================
+
+A system administrator can display all the mailing lists via the command
+line. When there are no mailing lists, a helpful message is displayed.
+
+ >>> class FakeArgs:
+ ... advertised = False
+ ... bare = False
+ ... domains = None
+ ... full = False
+
+ >>> from mailman.commands.lists import Lists
+ >>> command = Lists()
+ >>> command.process(FakeArgs)
+ No matching mailing lists found
+
+When there are a few mailing lists, they are shown in alphabetical order by
+their fully qualified list names, with a description.
+
+ >>> from mailman.config import config
+ >>> from mailman.interfaces.domain import IDomainManager
+ >>> domain_mgr = IDomainManager(config)
+ >>> domain_mgr.add('example.net')
+ <Domain example.net...>
+
+ >>> from mailman.app.lifecycle import create_list
+ >>> mlist_1 = create_list('list-one@example.com')
+ >>> mlist_1.description = 'List One'
+
+ >>> mlist_2 = create_list('list-two@example.com')
+ >>> mlist_2.description = 'List Two'
+
+ >>> mlist_3 = create_list('list-one@example.net')
+ >>> mlist_3.description = 'List One in Example.Net'
+ >>> transaction.commit()
+
+ >>> command.process(FakeArgs)
+ 3 matching mailing lists found:
+ List-one - List One
+ List-one - List One in Example.Net
+ List-two - List Two
+
+
+Full names
+==========
+
+You can display the mailing lists' full names, i.e. their posting addresses,
+with the --full switch.
+
+ >>> FakeArgs.full = True
+ >>> command.process(FakeArgs)
+ 3 matching mailing lists found:
+ list-one@example.com - List One
+ list-one@example.net - List One in Example.Net
+ list-two@example.com - List Two
+
+
+Bare names
+==========
+
+You can print less verbose output, such that only the mailing list's name is
+shown, with the --bare option.
+
+ >>> FakeArgs.bare = True
+ >>> FakeArgs.full = False
+ >>> command.process(FakeArgs)
+ List-one
+ List-one
+ List-two
+
+--full and --bare can be combined.
+
+ >>> FakeArgs.full = True
+ >>> command.process(FakeArgs)
+ list-one@example.com
+ list-one@example.net
+ list-two@example.com
+
+
+Specific domain
+===============
+
+You can narrow the search down to a specific domain with the --domain option.
+A helpful message is displayed if no matching domains are given.
+
+ >>> FakeArgs.bare = False
+ >>> FakeArgs.domains = ['example.org']
+ >>> command.process(FakeArgs)
+ No matching mailing lists found
+
+But if a matching domain is given, only mailing lists in that domain are
+shown.
+
+ >>> FakeArgs.domains = ['example.net']
+ >>> command.process(FakeArgs)
+ 1 matching mailing lists found:
+ list-one@example.net - List One in Example.Net
+
+More than one --domain argument can be given; then all mailing lists in
+matching domains are shown.
+
+ >>> FakeArgs.domains = ['example.com', 'example.net']
+ >>> command.process(FakeArgs)
+ 3 matching mailing lists found:
+ list-one@example.com - List One
+ list-one@example.net - List One in Example.Net
+ list-two@example.com - List Two
+
+
+Advertised lists
+================
+
+Mailing lists can be 'advertised' meaning their existence is public
+knowledge. Non-advertised lists are considered private. Display through the
+command line can select on this attribute.
+
+ >>> FakeArgs.domains = []
+ >>> FakeArgs.advertised = True
+ >>> mlist_1.advertised = False
+ >>> transaction.commit()
+
+ >>> command.process(FakeArgs)
+ 2 matching mailing lists found:
+ list-one@example.net - List One in Example.Net
+ list-two@example.com - List Two
diff --git a/src/mailman/commands/lists.py b/src/mailman/commands/lists.py
new file mode 100644
index 000000000..6757099d9
--- /dev/null
+++ b/src/mailman/commands/lists.py
@@ -0,0 +1,102 @@
+# Copyright (C) 2009 by the Free Software Foundation, Inc.
+#
+# This file is part of GNU Mailman.
+#
+# GNU Mailman is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option)
+# any later version.
+#
+# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+# more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
+
+"""The 'lists' subcommand."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'Lists',
+ ]
+
+
+from zope.interface import implements
+
+from mailman.config import config
+from mailman.i18n import _
+from mailman.interfaces.command import ICLISubCommand
+
+
+
+class Lists:
+ """The `lists` subcommand."""
+
+ implements(ICLISubCommand)
+
+ def add(self, subparser):
+ """See `ICLISubCommand`."""
+ lists_parser = subparser.add_parser(
+ 'lists', help=_('List all mailing lists'))
+ lists_parser.add_argument(
+ '-a', '--advertised',
+ default=False, action='store_true',
+ help=_(
+ 'List only those mailing lists that are publicly advertised'))
+ lists_parser.add_argument(
+ '-b', '--bare',
+ default=False, action='store_true',
+ help=_('Show only the list name, with no description'))
+ lists_parser.add_argument(
+ '-d', '--domain',
+ action='append', help=_("""\
+ List only those mailing lists hosted on the given domain, which
+ must be the email host name. Multiple -d options may be given.
+ """))
+ lists_parser.add_argument(
+ '-f', '--full',
+ default=False, action='store_true',
+ help=_(
+ 'Show the full mailing list name (i.e. the posting address'))
+ lists_parser.set_defaults(func=self.process)
+
+ def process(self, args):
+ """See `ICLISubCommand`."""
+ mailing_lists = []
+ list_manager = config.db.list_manager
+ # Gather the matching mailing lists.
+ for fqdn_name in sorted(list_manager.names):
+ mlist = list_manager.get(fqdn_name)
+ if args.advertised and not mlist.advertised:
+ continue
+ if args.domains and mlist.host_name not in args.domains:
+ continue
+ mailing_lists.append(mlist)
+ # Maybe no mailing lists matched.
+ if len(mailing_lists) == 0:
+ if not args.bare:
+ print _('No matching mailing lists found')
+ return
+ if not args.bare:
+ count = len(mailing_lists)
+ print _('$count matching mailing lists found:')
+ # Calculate the longest mailing list name.
+ longest = len(
+ max(mlist.fqdn_listname for mlist in mailing_lists)
+ if args.full else
+ max(mlist.real_name for mlist in mailing_lists))
+ # Print it out.
+ for mlist in mailing_lists:
+ name = (mlist.fqdn_listname if args.full else mlist.real_name)
+ if args.bare:
+ print name
+ else:
+ description = (mlist.description
+ if mlist.description is not None
+ else _('[no description available]'))
+ print '{0:{2}} - {1:{3}}'.format(
+ name, description, longest, 77 - longest)