On 8/20/19 1:43 AM, Ralf Hildebrandt wrote: > attached are two patches against mm2.1 which add sync_members > functionality to the GUI
I found some issues with this patch. - Entries which are already members are reported as added. - Entries with display names or mixed case addresses are not recognized as current members. I have attached a modified patch which seems better but needs more testing. -- Mark Sapiro <m...@msapiro.net> The highway is for gamblers, San Francisco Bay Area, California better use your sense - B. Dylan
=== modified file 'Mailman/Cgi/admin.py' --- Mailman/Cgi/admin.py 2018-07-15 03:18:00 +0000 +++ Mailman/Cgi/admin.py 2019-08-20 17:53:25 +0000 @@ -457,7 +457,7 @@ form = Form('%s/%s/%s' % (adminurl, category, subcat), encoding=encoding, mlist=mlist, contexts=AUTH_CONTEXTS) else: - form = Form('%s/%s' % (adminurl, category), + form = Form('%s/%s' % (adminurl, category), encoding=encoding, mlist=mlist, contexts=AUTH_CONTEXTS) # This holds the two columns of links linktable = Table(valign='top', width='100%') @@ -549,7 +549,7 @@ if category == 'members': # Figure out which subcategory we should display subcat = Utils.GetPathPieces()[-1] - if subcat not in ('list', 'add', 'remove', 'change'): + if subcat not in ('list', 'add', 'remove', 'change', 'sync'): subcat = 'list' # Add member category specific tables form.AddItem(membership_options(mlist, subcat, cgidata, doc, form)) @@ -910,6 +910,13 @@ container.AddItem(header) address_change(mlist, container) return container + if subcat == 'sync': + header.AddRow([Center(Header(2, _('Upload Membership List')))]) + header.AddCellInfo(header.GetCurrentRowIndex(), 0, colspan=2, + bgcolor=mm_cfg.WEB_HEADER_COLOR) + container.AddItem(header) + mass_sync(mlist, container) + return container # Otherwise... header.AddRow([Center(Header(2, _('Membership List')))]) header.AddCellInfo(header.GetCurrentRowIndex(), 0, colspan=2, @@ -1334,6 +1341,20 @@ +def mass_sync(mlist, container): + # MASS SYNC + table = Table(width='90%') + table.AddRow([Italic(_('Enter one address per line below...'))]) + table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) + table.AddRow([Center(TextArea(name='memberlist', + rows=10, cols='70%', wrap=None))]) + table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2) + table.AddRow([Italic(Label(_('...or specify a file to upload:'))), + FileUpload('memberlist_upload', cols='50')]) + container.AddItem(Center(table)) + + + def password_inputs(mlist): adminurl = mlist.GetScriptURL('admin', absolute=1) table = Table(cellspacing=3, cellpadding=4) @@ -1631,6 +1652,85 @@ msg.send(mlist) doc.AddItem(Header(3, _('Notification sent to %(schange_to)s.'))) doc.AddItem('<p>') + + # sync operation + memberlist = '' + memberlist += cgidata.getvalue('memberlist', '') + memberlist += cgidata.getvalue('memberlist_upload', '') + if memberlist: + current_members = {} + for addr in mlist.getMemberCPAddresses(mlist.getMembers()): + current_members[addr.lower()] = addr + # Now we know all the members. + entries = filter(None, [n.strip() for n in memberlist.splitlines()]) + lc_addresses = [parseaddr(x)[1] for x in entries if parseaddr(x)[1]] + subscribe_errors = [] + subscribe_success = [] + # First we add all the addresses that should be added to the list. + for entry in entries: + safeentry = Utils.websafe(entry) + fullname, address = parseaddr(entry) + if mlist.isMember(address): + continue + # Canonicalize the full name. + fullname = Utils.canonstr(fullname, mlist.preferred_language) + userdesc = UserDesc(address, fullname, + Utils.MakeRandomPassword(), + 0, mlist.preferred_language) + try: + # Add a member if not yet member. + mlist.ApprovedAddMember(userdesc, 0, 0, 0, + whence='admin sync members') + except Errors.MMBadEmailError: + if userdesc.address == '': + subscribe_errors.append((_('<blank line>'), + _('Bad/Invalid email address'))) + else: + subscribe_errors.append((safeentry, + _('Bad/Invalid email address'))) + except Errors.MMHostileAddress: + subscribe_errors.append( + (safeentry, _('Hostile address (illegal characters)'))) + except Errors.MembershipIsBanned, pattern: + subscribe_errors.append( + (safeentry, _('Banned address (matched %(pattern)s)'))) + else: + member = Utils.uncanonstr(formataddr((fullname, address))) + subscribe_success.append(Utils.websafe(member)) + + # Then we remove the addresses not in our list. + unsubscribe_errors = [] + unsubscribe_success = [] + + for entry in current_members: + # If an entry is not found in the uploaded "entries" list, then + # remove the member. + if not(entry in lc_addresses): + try: + mlist.ApprovedDeleteMember(entry, 0, 0) + except Errors.NotAMemberError: + # This can happen if the address is illegal (i.e. can't be + # parsed by email.Utils.parseaddr()) but for legacy + # reasons is in the database. Use a lower level remove to + # get rid of this member's entry + mlist.removeMember(entry) + else: + unsubscribe_success.append(Utils.websafe(entry)) + + if subscribe_success: + doc.AddItem(Header(5, _('Successfully subscribed:'))) + doc.AddItem(UnorderedList(*subscribe_success)) + doc.AddItem('<p>') + if subscribe_errors: + doc.AddItem(Header(5, _('Error subscribing:'))) + items = ['%s -- %s' % (x0, x1) for x0, x1 in subscribe_errors] + doc.AddItem(UnorderedList(*items)) + doc.AddItem('<p>') + if unsubscribe_success: + doc.AddItem(Header(5, _('Successfully unsubscribed:'))) + doc.AddItem(UnorderedList(*unsubscribe_success)) + doc.AddItem('<p>') + # See if this was a moderation bit operation if cgidata.has_key('allmodbit_btn'): val = safeint('allmodbit_val') === modified file 'Mailman/Gui/Membership.py' --- Mailman/Gui/Membership.py 2018-06-17 23:47:34 +0000 +++ Mailman/Gui/Membership.py 2019-08-20 14:45:27 +0000 @@ -31,5 +31,6 @@ ('add', _('Mass Subscription')), ('remove', _('Mass Removal')), ('change', _('Address Change')), + ('sync', _('Upload Membership List')), ] return None
signature.asc
Description: OpenPGP digital signature
------------------------------------------------------ Mailman-Users mailing list Mailman-Users@python.org https://mail.python.org/mailman/listinfo/mailman-users Mailman FAQ: http://wiki.list.org/x/AgA3 Security Policy: http://wiki.list.org/x/QIA9 Searchable Archives: http://www.mail-archive.com/mailman-users%40python.org/ Unsubscribe: https://mail.python.org/mailman/options/mailman-users/archive%40jab.org