~barry/mailman/events-and-web

« back to all changes in this revision

Viewing changes to src/mailman/bin/set_members.py

  • Committer: Barry Warsaw
  • Date: 2012-04-22 21:33:33 UTC
  • mfrom: (7150.1.11 transactions)
  • Revision ID: barry@list.org-20120422213333-3skjqsjktooesgsl
Several non-functional improvements to the code base.

Reduce the explicit use of the config.db global by introducing two new
helpers:
 - A new transaction() context manager which will commit the transaction on
   successful exit, otherwise it will abort the transaction
 - A new dbconnection decorator which calls the decorated method with the
   Storm store object as the first argument (after self).  This can be used
   instead of config.db.store.

By reducing the explicit use of this global, we have a better chance of
refactoring it away in the future.  Still TODO: runner.py and lmtp.py.

Be explicit about the `store` attribute on the IDatabase interface.

More consistent use of __future__ imports.

Remove an obsolete command line script.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2012 by the Free Software Foundation, Inc.
2
 
#
3
 
# This file is part of GNU Mailman.
4
 
#
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.
9
 
#
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.
14
 
#
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/>.
17
 
 
18
 
import csv
19
 
import optparse
20
 
 
21
 
from zope.component import getUtility
22
 
 
23
 
from mailman import Utils
24
 
from mailman import passwords
25
 
from mailman.app.membership import add_member
26
 
from mailman.app.notifications import (
27
 
    send_admin_subscription_notice, send_welcome_message)
28
 
from mailman.configuration import config
29
 
from mailman.core.i18n import _
30
 
from mailman.initialize import initialize
31
 
from mailman.interfaces.members import DeliveryMode
32
 
from mailman.interfaces.usermanager import IUserManager
33
 
from mailman.version import MAILMAN_VERSION
34
 
 
35
 
 
36
 
DELIVERY_MODES = {
37
 
    'regular':  DeliveryMode.regular,
38
 
    'plain':    DeliveryMode.plaintext_digests,
39
 
    'mime':     DeliveryMode.mime_digests,
40
 
    }
41
 
 
42
 
 
43
 
 
44
 
def parseargs():
45
 
    parser = optparse.OptionParser(version=MAILMAN_VERSION,
46
 
                                   usage=_("""\
47
 
%prog [options] csv-file
48
 
 
49
 
Set the membership of a mailing list to that described in a CSV file.  Each
50
 
row of the CSV file has the following format.  Only the address column is
51
 
required.
52
 
 
53
 
    - email address
54
 
    - full name (default: the empty string)
55
 
    - delivery mode (default: regular delivery) [1]
56
 
 
57
 
[1] The delivery mode is a case insensitive string of the following values:
58
 
 
59
 
    regular     - regular, i.e. immediate delivery
60
 
    mime        - MIME digest delivery
61
 
    plain       - plain text (RFC 1153) digest delivery
62
 
 
63
 
Any address not included in the CSV file is removed from the list membership.
64
 
"""))
65
 
    parser.add_option('-l', '--listname',
66
 
                      type='string', help=_("""\
67
 
Mailng list to set the membership for."""))
68
 
    parser.add_option('-w', '--welcome-msg',
69
 
                      type='string', metavar='<y|n>', help=_("""\
70
 
Set whether or not to send the list members a welcome message, overriding
71
 
whatever the list's 'send_welcome_msg' setting is."""))
72
 
    parser.add_option('-a', '--admin-notify',
73
 
                      type='string', metavar='<y|n>', help=_("""\
74
 
Set whether or not to send the list administrators a notification on the
75
 
success/failure of these subscriptions, overriding whatever the list's
76
 
'admin_notify_mchanges' setting is."""))
77
 
    parser.add_option('-v', '--verbose', action='store_true',
78
 
                      help=_('Increase verbosity'))
79
 
    parser.add_option('-C', '--config',
80
 
                      help=_('Alternative configuration file to use'))
81
 
    opts, args = parser.parse_args()
82
 
    if opts.welcome_msg is not None:
83
 
        ch = opts.welcome_msg[0].lower()
84
 
        if ch == 'y':
85
 
            opts.welcome_msg = True
86
 
        elif ch == 'n':
87
 
            opts.welcome_msg = False
88
 
        else:
89
 
            parser.error(_('Illegal value for -w: $opts.welcome_msg'))
90
 
    if opts.admin_notify is not None:
91
 
        ch = opts.admin_notify[0].lower()
92
 
        if ch == 'y':
93
 
            opts.admin_notify = True
94
 
        elif ch == 'n':
95
 
            opts.admin_notify = False
96
 
        else:
97
 
            parser.error(_('Illegal value for -a: $opts.admin_notify'))
98
 
    return parser, opts, args
99
 
 
100
 
 
101
 
 
102
 
def parse_file(filename):
103
 
    members = {}
104
 
    with open(filename) as fp:
105
 
        for row in csv.reader(fp):
106
 
            if len(row) == 0:
107
 
                continue
108
 
            elif len(row) == 1:
109
 
                address = row[0]
110
 
                real_name = None
111
 
                delivery_mode = DeliveryMode.regular
112
 
            elif len(row) == 2:
113
 
                address, real_name = row
114
 
                delivery_mode = DeliveryMode.regular
115
 
            else:
116
 
                # Ignore extra columns
117
 
                address, real_name = row[0:2]
118
 
                delivery_mode = DELIVERY_MODES.get(row[2].lower())
119
 
                if delivery_mode is None:
120
 
                    delivery_mode = DeliveryMode.regular
121
 
            members[address] = real_name, delivery_mode
122
 
    return members
123
 
 
124
 
 
125
 
 
126
 
def main():
127
 
    parser, opts, args = parseargs()
128
 
    initialize(opts.config)
129
 
 
130
 
    mlist = config.db.list_manager.get(opts.listname)
131
 
    if mlist is None:
132
 
        parser.error(_('No such list: $opts.listname'))
133
 
 
134
 
    # Set up defaults.
135
 
    if opts.welcome_msg is None:
136
 
        send_welcome_msg = mlist.send_welcome_msg
137
 
    else:
138
 
        send_welcome_msg = opts.welcome_msg
139
 
    if opts.admin_notify is None:
140
 
        admin_notify = mlist.admin_notify_mchanges
141
 
    else:
142
 
        admin_notify = opts.admin_notify
143
 
 
144
 
    # Parse the csv files.
145
 
    member_data = {}
146
 
    for filename in args:
147
 
        member_data.update(parse_file(filename))
148
 
 
149
 
    future_members = set(member_data)
150
 
    current_members = set(obj.address for obj in mlist.members.addresses)
151
 
    add_members = future_members - current_members
152
 
    delete_members = current_members - future_members
153
 
    change_members = current_members & future_members
154
 
    
155
 
    with _.using(mlist.preferred_language):
156
 
        # Start by removing all the delete members.
157
 
        for address in delete_members:
158
 
            print _('deleting address: $address')
159
 
            member = mlist.members.get_member(address)
160
 
            member.unsubscribe()
161
 
        # For all members that are in both lists, update their full name and
162
 
        # delivery mode.
163
 
        for address in change_members:
164
 
            print _('updating address: $address')
165
 
            real_name, delivery_mode = member_data[address]
166
 
            member = mlist.members.get_member(address)
167
 
            member.preferences.delivery_mode = delivery_mode
168
 
            user = getUtility(IUserManager).get_user(address)
169
 
            user.real_name = real_name
170
 
        for address in add_members:
171
 
            print _('adding address: $address')
172
 
            real_name, delivery_mode = member_data[address]
173
 
            password = passwords.make_secret(
174
 
                Utils.MakeRandomPassword(),
175
 
                passwords.lookup_scheme(config.PASSWORD_SCHEME))
176
 
            add_member(mlist, address, real_name, password, delivery_mode,
177
 
                       mlist.preferred_language, send_welcome_msg,
178
 
                       admin_notify)
179
 
            if send_welcome_msg:
180
 
                send_welcome_message(mlist, address, language, delivery_mode)
181
 
            if admin_notify:
182
 
                send_admin_subscription_notice(mlist, address, real_name)
183
 
 
184
 
    config.db.flush()
185
 
 
186
 
 
187
 
 
188
 
if __name__ == '__main__':
189
 
    main()