diff options
author | Mark Sapiro <mark@msapiro.net> | 2015-01-22 16:09:03 -0800 |
---|---|---|
committer | Mark Sapiro <mark@msapiro.net> | 2015-01-22 16:09:03 -0800 |
commit | 4758a0d904a12d6be21972fa432ad89ed9c1a768 (patch) | |
tree | 85d88b27697dd72a55d6470d9e88487bf29568c9 | |
parent | ac22662b811ac9bcf58cf001c8fd5ad21e757c8b (diff) | |
download | mailman2-4758a0d904a12d6be21972fa432ad89ed9c1a768.tar.gz mailman2-4758a0d904a12d6be21972fa432ad89ed9c1a768.tar.xz mailman2-4758a0d904a12d6be21972fa432ad89ed9c1a768.zip |
A number of changes from the unofficial 2.2 branch have been backported to
the 2.1 branch for release with 2.1.19. The 2.2 branch is now no different
from the 2.1 branch and will no longer be maintained.
-rw-r--r-- | Mailman/Archiver/HyperArch.py | 6 | ||||
-rw-r--r-- | Mailman/Cgi/confirm.py | 7 | ||||
-rw-r--r-- | Mailman/Cgi/options.py | 32 | ||||
-rw-r--r-- | Mailman/Commands/cmd_confirm.py | 8 | ||||
-rwxr-xr-x | Mailman/Defaults.py.in | 4 | ||||
-rw-r--r-- | Mailman/Gui/Privacy.py | 11 | ||||
-rw-r--r-- | Mailman/Gui/Topics.py | 10 | ||||
-rw-r--r-- | Mailman/HTMLFormatter.py | 5 | ||||
-rw-r--r-- | Mailman/Handlers/Moderate.py | 9 | ||||
-rw-r--r-- | Mailman/Handlers/Tagger.py | 6 | ||||
-rwxr-xr-x | Mailman/MailList.py | 81 | ||||
-rw-r--r-- | Mailman/Utils.py | 58 | ||||
-rw-r--r-- | Mailman/Version.py | 4 | ||||
-rwxr-xr-x | Mailman/versions.py | 13 | ||||
-rwxr-xr-x | NEWS | 55 | ||||
-rwxr-xr-x | bin/newlist | 28 | ||||
-rw-r--r-- | templates/en/adminaddrchgack.txt | 4 | ||||
-rw-r--r-- | templates/en/admindbdetails.html | 2 |
18 files changed, 279 insertions, 64 deletions
diff --git a/Mailman/Archiver/HyperArch.py b/Mailman/Archiver/HyperArch.py index 9b1df75a..0c0e3356 100644 --- a/Mailman/Archiver/HyperArch.py +++ b/Mailman/Archiver/HyperArch.py @@ -1,4 +1,4 @@ -# Copyright (C) 1998-2014 by the Free Software Foundation, Inc. +# Copyright (C) 1998-2015 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -509,7 +509,7 @@ class Article(pipermail.Article): subject = self._get_subject_enc(self.prev) prev = ('<LINK REL="Previous" HREF="%s">' % (url_quote(self.prev.filename))) - prev_wsubj = ('<LI>' + _('Previous message:') + + prev_wsubj = ('<LI>' + _('Previous message (by thread):') + ' <A HREF="%s">%s\n</A></li>' % (url_quote(self.prev.filename), self.quote(subject))) @@ -531,7 +531,7 @@ class Article(pipermail.Article): subject = self._get_subject_enc(self.next) next = ('<LINK REL="Next" HREF="%s">' % (url_quote(self.next.filename))) - next_wsubj = ('<LI>' + _('Next message:') + + next_wsubj = ('<LI>' + _('Next message (by thread):') + ' <A HREF="%s">%s\n</A></li>' % (url_quote(self.next.filename), self.quote(subject))) diff --git a/Mailman/Cgi/confirm.py b/Mailman/Cgi/confirm.py index bb529318..97297e10 100644 --- a/Mailman/Cgi/confirm.py +++ b/Mailman/Cgi/confirm.py @@ -1,4 +1,4 @@ -# Copyright (C) 2001-2014 by the Free Software Foundation, Inc. +# Copyright (C) 2001-2015 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -99,8 +99,9 @@ def main(): %(safecookie)s. <p>Note that confirmation strings expire approximately - %(days)s days after the initial subscription request. If your - confirmation has expired, please try to re-submit your subscription. + %(days)s days after the initial request. They also expire if the + request has already been handled in some way. If your confirmation + has expired, please try to re-submit your request. Otherwise, <a href="%(confirmurl)s">re-enter</a> your confirmation string.''') diff --git a/Mailman/Cgi/options.py b/Mailman/Cgi/options.py index 69ac52a9..74f186d7 100644 --- a/Mailman/Cgi/options.py +++ b/Mailman/Cgi/options.py @@ -1,4 +1,4 @@ -# Copyright (C) 1998-2014 by the Free Software Foundation, Inc. +# Copyright (C) 1998-2015 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -33,6 +33,7 @@ from Mailman import i18n from Mailman.htmlformat import * from Mailman.Logging.Syslog import syslog +OR = '|' SLASH = '/' SETLANGUAGE = -1 @@ -176,6 +177,9 @@ def main(): return # Are we processing an unsubscription request from the login screen? + msgc = _('If you are a list member, a confirmation email has been sent.') + msga = _("""If you are a list member, your unsubscription request has been + forwarded to the list administrator for approval.""") if cgidata.has_key('login-unsub'): # Because they can't supply a password for unsubscribing, we'll need # to do the confirmation dance. @@ -187,14 +191,11 @@ def main(): # be held. Otherwise, send a confirmation. if mlist.unsubscribe_policy: mlist.HoldUnsubscription(user) - doc.addError(_("""Your unsubscription request has been - forwarded to the list administrator for approval."""), - tag='') + doc.addError(msga, tag='') else: ip = os.environ.get('REMOTE_ADDR') mlist.ConfirmUnsubscription(user, userlang, remote=ip) - doc.addError(_('The confirmation email has been sent.'), - tag='') + doc.addError(msgc, tag='') mlist.Save() finally: mlist.Unlock() @@ -207,19 +208,21 @@ def main(): syslog('mischief', 'Unsub attempt of non-member w/ private rosters: %s', user) - doc.addError(_('The confirmation email has been sent.'), - tag='') + if mlist.unsubscribe_policy: + doc.addError(msga, tag='') + else: + doc.addError(msgc, tag='') loginpage(mlist, doc, user, language) print doc.Format() return # Are we processing a password reminder from the login screen? + msg = _("""If you are a list member, + your password has been emailed to you.""") if cgidata.has_key('login-remind'): if mlist.isMember(user): mlist.MailUserPassword(user) - doc.addError( - _('A reminder of your password has been emailed to you.'), - tag='') + doc.addError(msg, tag='') else: # Not a member if mlist.private_roster == 0: @@ -229,9 +232,7 @@ def main(): syslog('mischief', 'Reminder attempt of non-member w/ private rosters: %s', user) - doc.addError( - _('A reminder of your password has been emailed to you.'), - tag='') + doc.addError(msg, tag='') loginpage(mlist, doc, user, language) print doc.Format() return @@ -1068,7 +1069,8 @@ def topic_details(mlist, doc, user, cpuser, userlang, varhelp): table.AddRow([Bold(Label(_('Name:'))), Utils.websafe(name)]) table.AddRow([Bold(Label(_('Pattern (as regexp):'))), - '<pre>' + Utils.websafe(pattern) + '</pre>']) + '<pre>' + Utils.websafe(OR.join(pattern.splitlines())) + + '</pre>']) table.AddRow([Bold(Label(_('Description:'))), Utils.websafe(description)]) # Make colors look nice diff --git a/Mailman/Commands/cmd_confirm.py b/Mailman/Commands/cmd_confirm.py index a3accf64..379d23c2 100644 --- a/Mailman/Commands/cmd_confirm.py +++ b/Mailman/Commands/cmd_confirm.py @@ -1,4 +1,4 @@ -# Copyright (C) 2002-2011 by the Free Software Foundation, Inc. +# Copyright (C) 2002-2015 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -49,9 +49,9 @@ def process(res, args): days = int(mm_cfg.PENDING_REQUEST_LIFE / mm_cfg.days(1) + 0.5) res.results.append(_("""\ Invalid confirmation string. Note that confirmation strings expire -approximately %(days)s days after the initial subscription request. If your -confirmation has expired, please try to re-submit your original request or -message.""")) +approximately %(days)s days after the initial request. They also expire if +the request has already been handled in some way. If your confirmation has +expired, please try to re-submit your original request or message.""")) except Errors.MMNeedApproval: res.results.append(_("""\ Your request has been forwarded to the list moderator for approval.""")) diff --git a/Mailman/Defaults.py.in b/Mailman/Defaults.py.in index bec3e441..8a5e6b0e 100755 --- a/Mailman/Defaults.py.in +++ b/Mailman/Defaults.py.in @@ -1188,6 +1188,10 @@ DEFAULT_SUBSCRIBE_POLICY = 1 # Does this site allow completely unchecked subscriptions? ALLOW_OPEN_SUBSCRIBE = No +# This is the default list of addresses and regular expressions (beginning +# with ^) that are exempt from approval if SUBSCRIBE_POLICY is 2 or 3. +DEFAULT_SUBSCRIBE_AUTO_APPROVAL = [] + # The default policy for unsubscriptions. 0 (unmoderated unsubscribes) is # highly recommended! # 0 - unmoderated unsubscribes diff --git a/Mailman/Gui/Privacy.py b/Mailman/Gui/Privacy.py index 7f1e12f3..3c32bf50 100644 --- a/Mailman/Gui/Privacy.py +++ b/Mailman/Gui/Privacy.py @@ -1,4 +1,4 @@ -# Copyright (C) 2001-2014 by the Free Software Foundation, Inc. +# Copyright (C) 2001-2015 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -113,6 +113,15 @@ class Privacy(GUIBase): sub_cfentry, + ('subscribe_auto_approval', mm_cfg.EmailListEx, (10, WIDTH), 1, + _("""List of addresses (or regexps) whose subscriptions do not + require approval."""), + + _("""When subscription requires approval, addresses in this list + are allowed to subscribe without administrator approval. Add + addresses one per line. You may begin a line with a ^ character + to designate a (case insensitive) regular expression match.""")), + ('unsubscribe_policy', mm_cfg.Radio, (_('No'), _('Yes')), 0, _("""Is the list moderator's approval required for unsubscription requests? (<em>No</em> is recommended)"""), diff --git a/Mailman/Gui/Topics.py b/Mailman/Gui/Topics.py index 96f9b421..ec60dbda 100644 --- a/Mailman/Gui/Topics.py +++ b/Mailman/Gui/Topics.py @@ -1,4 +1,4 @@ -# Copyright (C) 2001-2009 by the Free Software Foundation, Inc. +# Copyright (C) 2001-2015 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -29,6 +29,8 @@ except NameError: True = 1 False = 0 +OR = '|' + class Topics(GUIBase): @@ -126,10 +128,10 @@ class Topics(GUIBase): # Make sure the pattern was a legal regular expression name = Utils.websafe(name) try: - # Tagger compiles in verbose mode so we do too. - re.compile(pattern, re.VERBOSE) + orpattern = OR.join(pattern.splitlines()) + re.compile(orpattern) except (re.error, TypeError): - safepattern = Utils.websafe(pattern) + safepattern = Utils.websafe(orpattern) doc.addError(_("""The topic pattern '%(safepattern)s' is not a legal regular expression. It will be discarded.""")) continue diff --git a/Mailman/HTMLFormatter.py b/Mailman/HTMLFormatter.py index dad51e74..df22e5f2 100644 --- a/Mailman/HTMLFormatter.py +++ b/Mailman/HTMLFormatter.py @@ -1,4 +1,4 @@ -# Copyright (C) 1998-2010 by the Free Software Foundation, Inc. +# Copyright (C) 1998-2015 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -90,6 +90,9 @@ class HTMLFormatter: showing = Utils.ObscureEmail(person, for_text=1) else: showing = person + realname = Utils.uncanonstr(self.getMemberName(person), lang) + if realname: + showing += " (%s)" % Utils.websafe(realname) got = Link(url, showing) if self.getDeliveryStatus(person) <> MemberAdaptor.ENABLED: got = Italic('(', got, ')') diff --git a/Mailman/Handlers/Moderate.py b/Mailman/Handlers/Moderate.py index 4400d086..225ee37f 100644 --- a/Mailman/Handlers/Moderate.py +++ b/Mailman/Handlers/Moderate.py @@ -1,4 +1,4 @@ -# Copyright (C) 2001-2014 by the Free Software Foundation, Inc. +# Copyright (C) 2001-2015 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -162,9 +162,10 @@ def do_reject(mlist): Utils.wrap(_(mlist.nonmember_rejection_notice)) else: raise Errors.RejectMessage, Utils.wrap(_("""\ -You are not allowed to post to this mailing list, and your message has been -automatically rejected. If you think that your messages are being rejected in -error, contact the mailing list owner at %(listowner)s.""")) +Your message has been rejected, probably because you are not subscribed to the +mailing list and the list's policy is to prohibit non-members from posting to +it. If you think that your messages are being rejected in error, contact the +mailing list owner at %(listowner)s.""")) diff --git a/Mailman/Handlers/Tagger.py b/Mailman/Handlers/Tagger.py index cb90bfc4..ed9a7e71 100644 --- a/Mailman/Handlers/Tagger.py +++ b/Mailman/Handlers/Tagger.py @@ -1,4 +1,4 @@ -# Copyright (C) 2001-2013 by the Free Software Foundation, Inc. +# Copyright (C) 2001-2015 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -29,6 +29,7 @@ from Mailman import Utils from Mailman.Logging.Syslog import syslog from Mailman.Handlers.CookHeaders import change_header +OR = '|' CRNL = '\r\n' EMPTYSTRING = '' NLTAB = '\n\t' @@ -63,7 +64,8 @@ def process(mlist, msg, msgdata): # added to the specific topics bucket. hits = {} for name, pattern, desc, emptyflag in mlist.topics: - cre = re.compile(pattern, re.IGNORECASE | re.VERBOSE) + pattern = OR.join(pattern.splitlines()) + cre = re.compile(pattern, re.IGNORECASE) for line in matchlines: if cre.search(line): hits[name] = 1 diff --git a/Mailman/MailList.py b/Mailman/MailList.py index 0ebf8ab8..dca8c8f5 100755 --- a/Mailman/MailList.py +++ b/Mailman/MailList.py @@ -1,4 +1,4 @@ -# Copyright (C) 1998-2014 by the Free Software Foundation, Inc. +# Copyright (C) 1998-2015 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -74,6 +74,7 @@ from Mailman.Logging.Syslog import syslog _ = i18n._ EMPTYSTRING = '' +OR = '|' try: True, False @@ -356,6 +357,7 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin, self.welcome_msg = '' self.goodbye_msg = '' self.subscribe_policy = mm_cfg.DEFAULT_SUBSCRIBE_POLICY + self.subscribe_auto_approval = mm_cfg.DEFAULT_SUBSCRIBE_AUTO_APPROVAL self.unsubscribe_policy = mm_cfg.DEFAULT_UNSUBSCRIBE_POLICY self.private_roster = mm_cfg.DEFAULT_PRIVATE_ROSTER self.obscure_addresses = mm_cfg.DEFAULT_OBSCURE_ADDRESSES @@ -773,10 +775,11 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin, goodtopics = [] for name, pattern, desc, emptyflag in self.topics: try: - re.compile(pattern) + orpattern = OR.join(pattern.splitlines()) + re.compile(orpattern) except (re.error, TypeError): syslog('error', 'Bad topic pattern "%s" for list: %s', - pattern, self.internal_name()) + orpattern, self.internal_name()) else: goodtopics.append((name, pattern, desc, emptyflag)) self.topics = goodtopics @@ -941,6 +944,9 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin, syslog('subscribe', '%s: pending %s %s', self.internal_name(), who, by) raise Errors.MMSubscribeNeedsConfirmation + elif self.HasAutoApprovedSender(email): + # no approval necessary: + self.ApprovedAddMember(userdesc) else: # Subscription approval is required. Add this entry to the admin # requests database. BAW: this should probably take a userdesc @@ -1164,12 +1170,14 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin, # CP address of a member, then if the old address yields a different # CP address, we can simply remove the old address, otherwise we can # do nothing. + cpoldaddr = self.getMemberCPAddress(oldaddr) if self.isMember(newaddr) and (self.getMemberCPAddress(newaddr) == newaddr): - if self.getMemberCPAddress(oldaddr) <> newaddr: + if cpoldaddr <> newaddr: self.removeMember(oldaddr) else: self.changeMemberAddress(oldaddr, newaddr) + self.log_and_notify_admin(cpoldaddr, newaddr) # If globally is true, then we also include every list for which # oldaddr is a member. if not globally: @@ -1189,16 +1197,46 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin, mlist.Lock() try: # Same logic as above, re newaddr is already a member + cpoldaddr = mlist.getMemberCPAddress(oldaddr) if mlist.isMember(newaddr) and ( mlist.getMemberCPAddress(newaddr) == newaddr): - if mlist.getMemberCPAddress(oldaddr) <> newaddr: + if cpoldaddr <> newaddr: mlist.removeMember(oldaddr) else: mlist.changeMemberAddress(oldaddr, newaddr) + mlist.log_and_notify_admin(cpoldaddr, newaddr) mlist.Save() finally: mlist.Unlock() + def log_and_notify_admin(self, oldaddr, newaddr): + """Log member address change and notify admin if requested.""" + syslog('subscribe', '%s: changed member address from %s to %s', + self.internal_name(), oldaddr, newaddr) + if self.admin_notify_mchanges: + lang = self.preferred_language + otrans = i18n.get_translation() + i18n.set_language(lang) + try: + realname = self.real_name + subject = _('%(realname)s address change notification') + finally: + i18n.set_translation(otrans) + name = self.getMemberName(newaddr) + if name is None: + name = '' + if isinstance(name, UnicodeType): + name = name.encode(Utils.GetCharSet(lang), 'replace') + text = Utils.maketext( + 'adminaddrchgack.txt', + {'name' : name, + 'oldaddr' : oldaddr, + 'newaddr' : newaddr, + 'listname': self.real_name, + }, mlist=self) + msg = Message.OwnerNotification(self, subject, text) + msg.send(self) + # # Confirmation processing @@ -1242,7 +1280,8 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin, # list administrators. self.SendHostileSubscriptionNotice(invitation, addr) raise Errors.HostileSubscriptionError - elif self.subscribe_policy in (2, 3): + elif self.subscribe_policy in (2, 3) and \ + not self.HasAutoApprovedSender(addr): self.HoldSubscription(addr, fullname, password, digest, lang) name = self.real_name raise Errors.MMNeedApproval, _( @@ -1521,13 +1560,30 @@ bad regexp in bounce_matching_header line: %s """Returns matched entry in ban_list if email matches. Otherwise returns None. """ - ban = False - for pattern in self.ban_list: + return self.GetPattern(email, self.ban_list) + + def HasAutoApprovedSender(self, sender): + """Returns True and logs if sender matches address or pattern + in subscribe_auto_approval. Otherwise returns False. + """ + auto_approve = False + if self.GetPattern(sender, self.subscribe_auto_approval): + auto_approve = True + syslog('vette', '%s: auto approved subscribe from %s', + self.internal_name(), sender) + return auto_approve + + def GetPattern(self, email, pattern_list): + """Returns matched entry in pattern_list if email matches. + Otherwise returns None. + """ + matched = None + for pattern in pattern_list: if pattern.startswith('^'): # This is a regular expression match try: if re.search(pattern, email, re.IGNORECASE): - ban = True + matched = pattern break except re.error: # BAW: we should probably remove this pattern @@ -1535,12 +1591,9 @@ bad regexp in bounce_matching_header line: %s else: # Do the comparison case insensitively if pattern.lower() == email.lower(): - ban = True + matched = pattern break - if ban: - return pattern - else: - return None + return matched diff --git a/Mailman/Utils.py b/Mailman/Utils.py index 1a08c119..0cb9f122 100644 --- a/Mailman/Utils.py +++ b/Mailman/Utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 1998-2014 by the Free Software Foundation, Inc. +# Copyright (C) 1998-2015 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -80,6 +80,7 @@ except ImportError: EMPTYSTRING = '' UEMPTYSTRING = u'' +CR = '\r' NL = '\n' DOT = '.' IDENTCHARS = ascii_letters + digits + '_' @@ -918,6 +919,61 @@ def oneline(s, cset): return EMPTYSTRING.join(s.splitlines()) +def strip_verbose_pattern(pattern): + # Remove white space and comments from a verbose pattern and return a + # non-verbose, equivalent pattern. Replace CR and NL in the result + # with '\\r' and '\\n' respectively to avoid multi-line results. + if not isinstance(pattern, str): + return pattern + newpattern = '' + i = 0 + inclass = False + skiptoeol = False + copynext = False + while i < len(pattern): + c = pattern[i] + if copynext: + if c == NL: + newpattern += '\\n' + elif c == CR: + newpattern += '\\r' + else: + newpattern += c + copynext = False + elif skiptoeol: + if c == NL: + skiptoeol = False + elif c == '#' and not inclass: + skiptoeol = True + elif c == '[' and not inclass: + inclass = True + newpattern += c + copynext = True + elif c == ']' and inclass: + inclass = False + newpattern += c + elif re.search('\s', c): + if inclass: + if c == NL: + newpattern += '\\n' + elif c == CR: + newpattern += '\\r' + else: + newpattern += c + elif c == '\\' and not inclass: + newpattern += c + copynext = True + else: + if c == NL: + newpattern += '\\n' + elif c == CR: + newpattern += '\\r' + else: + newpattern += c + i += 1 + return newpattern + + # Patterns and functions to flag possible XSS attacks in HTML. # This list is compiled from information at http://ha.ckers.org/xss.html, # http://www.quirksmode.org/js/events_compinfo.html, diff --git a/Mailman/Version.py b/Mailman/Version.py index 66eed767..93616e5f 100644 --- a/Mailman/Version.py +++ b/Mailman/Version.py @@ -1,4 +1,4 @@ -# Copyright (C) 1998-2014 by the Free Software Foundation, Inc. +# Copyright (C) 1998-2015 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -37,7 +37,7 @@ HEX_VERSION = ((MAJOR_REV << 24) | (MINOR_REV << 16) | (MICRO_REV << 8) | (REL_LEVEL << 4) | (REL_SERIAL << 0)) # config.pck schema version number -DATA_FILE_VERSION = 105 +DATA_FILE_VERSION = 106 # qfile/*.db schema version number QFILE_SCHEMA_VERSION = 3 diff --git a/Mailman/versions.py b/Mailman/versions.py index 9006ec0c..d0960e0d 100755 --- a/Mailman/versions.py +++ b/Mailman/versions.py @@ -1,4 +1,4 @@ -# Copyright (C) 1998-2014 by the Free Software Foundation, Inc. +# Copyright (C) 1998-2015 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -313,6 +313,15 @@ def UpdateOldVars(l, stored_state): pass else: l.digest_members[k] = 0 + # + # Convert pre 2.2 topics regexps which were compiled in verbose mode + # to a non-verbose equivalent. + # + if stored_state['data_version'] < 106 and stored_state.has_key('topics'): + l.topics = [] + for name, pattern, description, emptyflag in stored_state['topics']: + pattern = Utils.strip_verbose_pattern(pattern) + l.topics.append((name, pattern, description, emptyflag)) # from_is_list was called author_is_list in 2.1.16rc2 (only). PreferStored('author_is_list', 'from_is_list', mm_cfg.DEFAULT_FROM_IS_LIST) @@ -352,6 +361,8 @@ def NewVars(l): add_only_if_missing('personalize', 0) add_only_if_missing('first_strip_reply_to', mm_cfg.DEFAULT_FIRST_STRIP_REPLY_TO) + add_only_if_missing('subscribe_auto_approval', + mm_cfg.DEFAULT_SUBSCRIBE_AUTO_APPROVAL) add_only_if_missing('unsubscribe_policy', mm_cfg.DEFAULT_UNSUBSCRIBE_POLICY) add_only_if_missing('send_goodbye_msg', mm_cfg.DEFAULT_SEND_GOODBYE_MSG) @@ -5,6 +5,61 @@ Copyright (C) 1998-2014 by the Free Software Foundation, Inc. Here is a history of user visible changes to Mailman. +2.2 Branch Backports (released in conjunction with 2.1.19) + + The following New Features and Bug Fixes have been in an "unofficial, + never to be released" Mailman 2.2 branch for several years. Until now, + they were never implemented on the official 2.1 branch because of their + i18n impacts. Given that there have been a number of i18n impacting + changes due to DMARC mitigations in the last few releases, it has been + decided to backport these as well. + + All of these changes have been running in production on several lists + for years without problems other than untranslated strings, so they should + be reasonably "bug free". + + New Features + + - There is a new list attribute 'subscribe_auto_approval' which is a list + of email addresses and regular expressions matching email addresses + whose subscriptions are exempt from admin approval. (LP: #266609) + + - Confirmed member change of address is logged in the 'subscribe' log, + and if admin_notify_mchanges is true, a notice is sent to the list + owner using a new adminaddrchgack.txt template. + + - Added an 'automate' option to bin/newlist to send the notice to the + admin without the prompt. + + - The processing of Topics regular expressions has changed. Previously the + Topics regexp was compiled in verbose mode but not documented as such + which caused some confusion. Also, the documentation indicated that + topic keywords could be entered one per line, but these entries were not + handled properly. Topics regexps are now compiled in non-verbose mode + and multi-line entries are 'ored'. Existing Topics regexps will be + converted when the list is updated so they will continue to work. + + - Added real name display to the web roster. (LP: #266754) + + + Bug fixes and other patches + + - Changed the response to an invalid confirmation to be more generic. + Not all confirmations are subscription requests. + + - Changed the default nonmember_rejection_notice to be more user friendly. + (LP: #418728) + + - Added "If you are a list member" qualification to some messages from the + options login page. (LP: #266442) + + - Changed the 'Approve' wording in the admindbdetails.html template to + 'Accept/Approve' for better agreement with the button labels. + + - Added '(by thread)' to the previous and next message links in the + archive to emphasize that even if you got to the message from a + subject, date or author index, previous and next are still by thread. + 2.1.19 (xx-xxx-xxxx) New Features diff --git a/bin/newlist b/bin/newlist index 940ca9f4..f80595c8 100755 --- a/bin/newlist +++ b/bin/newlist @@ -1,6 +1,6 @@ #! @PYTHON@ # -# Copyright (C) 1998-2010 by the Free Software Foundation, Inc. +# Copyright (C) 1998-2015 by the Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -41,6 +41,13 @@ Options: their list has been created. This option suppresses the prompt and notification. + -a/--automate + This option suppresses the prompt prior to administrator notification + but still sends the notification. It can be used to make newlist + totally non-interactive but still send the notification, assuming + listname, listadmin-addr and admin-password are all specified on the + command line. + -h/--help Print this help text and exit. @@ -84,8 +91,9 @@ where www.mydom.ain is used for `urlhost' but it will also be used for '--urlhost' and '--emailhost' have precedence to this notation. If you spell the list name as just `mylist', then the email hostname will be -taken from DEFAULT_EMAIL_HOST and the url will be taken from DEFAULT_URL (as -defined in your Defaults.py file or overridden by settings in mm_cfg.py). +taken from DEFAULT_EMAIL_HOST and the url will be taken from DEFAULT_URL_HOST +interpolated into DEFAULT_URL_PATTERN (as defined in your Defaults.py file or +overridden by settings in mm_cfg.py). Note that listnames are forced to lowercase. """ @@ -123,21 +131,24 @@ def usage(code, msg=''): def main(): try: - opts, args = getopt.getopt(sys.argv[1:], 'hql:u:e:', - ['help', 'quiet', 'language=', + opts, args = getopt.getopt(sys.argv[1:], 'hqal:u:e:', + ['help', 'quiet', 'automate', 'language=', 'urlhost=', 'emailhost=']) except getopt.error, msg: usage(1, msg) lang = mm_cfg.DEFAULT_SERVER_LANGUAGE - quiet = 0 + quiet = False + automate = False urlhost = None emailhost = None for opt, arg in opts: if opt in ('-h', '--help'): usage(0) if opt in ('-q', '--quiet'): - quiet = 1 + quiet = True + if opt in ('-a', '--automate'): + automate = True if opt in ('-l', '--language'): lang = arg if opt in ('-u', '--urlhost'): @@ -228,9 +239,10 @@ def main(): sys.modules[modname].create(mlist) # And send the notice to the list owner - if not quiet: + if not quiet and not automate: print _('Hit enter to notify %(listname)s owner...'), sys.stdin.readline() + if not quiet: siteowner = Utils.get_site_email(mlist.host_name, 'owner') text = Utils.maketext( 'newlist.txt', diff --git a/templates/en/adminaddrchgack.txt b/templates/en/adminaddrchgack.txt new file mode 100644 index 00000000..a24dd3d9 --- /dev/null +++ b/templates/en/adminaddrchgack.txt @@ -0,0 +1,4 @@ +Address for member %(name)s has been successfully changed +from %(oldaddr)s to %(newaddr)s for list %(listname)s. + + diff --git a/templates/en/admindbdetails.html b/templates/en/admindbdetails.html index a6b7eb96..4c19fc49 100644 --- a/templates/en/admindbdetails.html +++ b/templates/en/admindbdetails.html @@ -12,7 +12,7 @@ excerpt of the message body. taken now for this pending administrative request, but for held postings, you can still forward or preserve the message (see below). -<li><b>Approve</b> -- Approve the message, sending it on to the list. +<li><b>Accept/Approve</b> -- Accept the message, sending it on to the list. For membership requests, approve the change in membership status. <li><b>Reject</b> -- Reject the message, sending a rejection notice to |