Move a module.
[mailman.git] / src / mailman / commands / cli_lists.py
blob212dba8af401a1661dd8a573e4540b6affdf2340
1 # Copyright (C) 2009 by the Free Software Foundation, Inc.
3 # This file is part of GNU Mailman.
5 # GNU Mailman is free software: you can redistribute it and/or modify it under
6 # the terms of the GNU General Public License as published by the Free
7 # Software Foundation, either version 3 of the License, or (at your option)
8 # any later version.
10 # GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
11 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 # more details.
15 # You should have received a copy of the GNU General Public License along with
16 # GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
18 """The 'lists' subcommand."""
20 from __future__ import absolute_import, unicode_literals
22 __metaclass__ = type
23 __all__ = [
24 'Create',
25 'Lists',
26 'Remove',
30 from zope.component import getUtility
31 from zope.interface import implements
33 from mailman.Utils import maketext
34 from mailman.app.lifecycle import create_list, remove_list
35 from mailman.config import config
36 from mailman.core.constants import system_preferences
37 from mailman.core.errors import InvalidEmailAddress
38 from mailman.email.message import UserNotification
39 from mailman.i18n import _, using_language
40 from mailman.interfaces.command import ICLISubCommand
41 from mailman.interfaces.domain import (
42 BadDomainSpecificationError, IDomainManager)
43 from mailman.interfaces.listmanager import IListManager, ListAlreadyExistsError
47 class Lists:
48 """List all mailing lists"""
50 implements(ICLISubCommand)
52 name = 'lists'
54 def add(self, parser, command_parser):
55 """See `ICLISubCommand`."""
56 command_parser.add_argument(
57 '-a', '--advertised',
58 default=False, action='store_true',
59 help=_(
60 'List only those mailing lists that are publicly advertised'))
61 command_parser.add_argument(
62 '-b', '--bare',
63 default=False, action='store_true',
64 help=_('Show only the list name, with no description'))
65 command_parser.add_argument(
66 '-d', '--domain',
67 action='append', help=_("""\
68 List only those mailing lists hosted on the given domain, which
69 must be the email host name. Multiple -d options may be given.
70 """))
71 command_parser.add_argument(
72 '-f', '--full',
73 default=False, action='store_true',
74 help=_(
75 'Show the full mailing list name (i.e. the posting address'))
77 def process(self, args):
78 """See `ICLISubCommand`."""
79 mailing_lists = []
80 list_manager = getUtility(IListManager)
81 # Gather the matching mailing lists.
82 for fqdn_name in sorted(list_manager.names):
83 mlist = list_manager.get(fqdn_name)
84 if args.advertised and not mlist.advertised:
85 continue
86 domains = getattr(args, 'domains', None)
87 if domains and mlist.host_name not in domains:
88 continue
89 mailing_lists.append(mlist)
90 # Maybe no mailing lists matched.
91 if len(mailing_lists) == 0:
92 if not args.bare:
93 print _('No matching mailing lists found')
94 return
95 if not args.bare:
96 count = len(mailing_lists)
97 print _('$count matching mailing lists found:')
98 # Calculate the longest mailing list name.
99 longest = len(
100 max(mlist.fqdn_listname for mlist in mailing_lists)
101 if args.full else
102 max(mlist.real_name for mlist in mailing_lists))
103 # Print it out.
104 for mlist in mailing_lists:
105 name = (mlist.fqdn_listname if args.full else mlist.real_name)
106 if args.bare:
107 print name
108 else:
109 description = (mlist.description
110 if mlist.description is not None
111 else _('[no description available]'))
112 print '{0:{2}} - {1:{3}}'.format(
113 name, description, longest, 77 - longest)
117 class Create:
118 """Create a mailing list"""
120 implements(ICLISubCommand)
122 name = 'create'
124 def add(self, parser, command_parser):
125 """See `ICLISubCommand`."""
126 self.parser = parser
127 command_parser.add_argument(
128 '--language',
129 type='unicode', metavar='CODE', help=_("""\
130 Set the list's preferred language to CODE, which must be a
131 registered two letter language code."""))
132 command_parser.add_argument(
133 '-o', '--owner',
134 type='unicode', action='append', default=[],
135 dest='owners', metavar='OWNER', help=_("""\
136 Specify a listowner email address. If the address is not
137 currently registered with Mailman, the address is registered and
138 linked to a user. Mailman will send a confirmation message to the
139 address, but it will also send a list creation notice to the
140 address. More than one owner can be specified."""))
141 command_parser.add_argument(
142 '-n', '--notify',
143 default=False, action='store_true',
144 help=_("""\
145 Notify the list owner by email that their mailing list has been
146 created."""))
147 command_parser.add_argument(
148 '-q', '--quiet',
149 default=False, action='store_true',
150 help=_('Print less output.'))
151 command_parser.add_argument(
152 '-d', '--domain',
153 default=False, action='store_true',
154 help=_("""\
155 Register the mailing list's domain if not yet registered."""))
156 # Required positional argument.
157 command_parser.add_argument(
158 'listname', metavar='LISTNAME', nargs=1,
159 help=_("""\
160 The 'fully qualified list name', i.e. the posting address of the
161 mailing list. It must be a valid email address and the domain
162 must be registered with Mailman. List names are forced to lower
163 case."""))
165 def process(self, args):
166 """See `ICLISubCommand`."""
167 language_code = (args.language
168 if args.language is not None
169 else system_preferences.preferred_language.code)
170 # Make sure that the selected language code is known.
171 if language_code not in config.languages.codes:
172 self.parser.error(_('Invalid language code: $language_code'))
173 return
174 assert len(args.listname) == 1, (
175 'Unexpected positional arguments: %s' % args.listname)
176 # Check to see if the domain exists or not.
177 fqdn_listname = args.listname[0]
178 listname, at, domain = fqdn_listname.partition('@')
179 domain_mgr = IDomainManager(config)
180 if domain_mgr.get(domain) is None and args.domain:
181 domain_mgr.add(domain)
182 try:
183 mlist = create_list(fqdn_listname, args.owners)
184 except InvalidEmailAddress:
185 self.parser.error(_('Illegal list name: $fqdn_listname'))
186 return
187 except ListAlreadyExistsError:
188 self.parser.error(_('List already exists: $fqdn_listname'))
189 return
190 except BadDomainSpecificationError, domain:
191 self.parser.error(_('Undefined domain: $domain'))
192 return
193 # Find the language associated with the code, then set the mailing
194 # list's preferred language to that. The changes then must be
195 # committed to the database.
196 mlist.preferred_language = config.languages[language_code]
197 config.db.commit()
198 # Do the notification.
199 if not args.quiet:
200 print _('Created mailing list: $mlist.fqdn_listname')
201 if args.notify:
202 d = dict(
203 listname = mlist.fqdn_listname,
204 admin_url = mlist.script_url('admin'),
205 listinfo_url = mlist.script_url('listinfo'),
206 requestaddr = mlist.request_address,
207 siteowner = mlist.no_reply_address,
209 text = maketext('newlist.txt', d, mlist=mlist)
210 # Set the I18N language to the list's preferred language so the
211 # header will match the template language. Stashing and restoring
212 # the old translation context is just (healthy? :) paranoia.
213 with using_language(mlist.preferred_language.code):
214 msg = UserNotification(
215 args.owners, mlist.no_reply_address,
216 _('Your new mailing list: $fqdn_listname'),
217 text, mlist.preferred_language)
218 msg.send(mlist)
222 class Remove:
223 """Remove a mailing list"""
225 implements(ICLISubCommand)
227 name = 'remove'
229 def add(self, parser, command_parser):
230 """See `ICLISubCommand`."""
231 command_parser.add_argument(
232 '-a', '--archives',
233 default=False, action='store_true',
234 help=_("""\
235 Remove the list's archives too, or if the list has already been deleted,
236 remove any residual archives."""))
237 command_parser.add_argument(
238 '-q', '--quiet',
239 default=False, action='store_true',
240 help=_('Suppress status messages'))
241 # Required positional argument.
242 command_parser.add_argument(
243 'listname', metavar='LISTNAME', nargs=1,
244 help=_("""\
245 The 'fully qualified list name', i.e. the posting address of the
246 mailing list."""))
248 def process(self, args):
249 """See `ICLISubCommand`."""
250 def log(message):
251 if not args.quiet:
252 print message
253 assert len(args.listname) == 1, (
254 'Unexpected positional arguments: %s' % args.listname)
255 fqdn_listname = args.listname[0]
256 mlist = getUtility(IListManager).get(fqdn_listname)
257 if mlist is None:
258 if args.archives:
259 log(_('No such list: $fqdn_listname; '
260 'removing residual archives.'))
261 else:
262 log(_('No such list: $fqdn_listname'))
263 return
264 else:
265 log(_('Removed list: $fqdn_listname'))
266 if not args.archives:
267 log(_('Not removing archives. Reinvoke with -a to remove them.'))
268 remove_list(fqdn_listname, mlist, args.archives)
269 config.db.commit()