------------------------------------------------------------
revno: 1516
fixes bugs: https://launchpad.net/bugs/266609 https://launchpad.net/bugs/266754 
https://launchpad.net/bugs/418728 https://launchpad.net/bugs/266442
committer: Mark Sapiro <m...@msapiro.net>
branch nick: 2.1
timestamp: Thu 2015-01-22 16:09:03 -0800
message:
  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.
added:
  templates/en/adminaddrchgack.txt
modified:
  Mailman/Archiver/HyperArch.py
  Mailman/Cgi/confirm.py
  Mailman/Cgi/options.py
  Mailman/Commands/cmd_confirm.py
  Mailman/Defaults.py.in
  Mailman/Gui/Privacy.py
  Mailman/Gui/Topics.py
  Mailman/HTMLFormatter.py
  Mailman/Handlers/Moderate.py
  Mailman/Handlers/Tagger.py
  Mailman/MailList.py
  Mailman/Utils.py
  Mailman/Version.py
  Mailman/versions.py
  NEWS
  bin/newlist
  templates/en/admindbdetails.html


--
lp:mailman/2.1
https://code.launchpad.net/~mailman-coders/mailman/2.1

Your team Mailman Checkins is subscribed to branch lp:mailman/2.1.
To unsubscribe from this branch go to 
https://code.launchpad.net/~mailman-coders/mailman/2.1/+edit-subscription
=== modified file 'Mailman/Archiver/HyperArch.py'
--- Mailman/Archiver/HyperArch.py	2014-11-04 22:59:40 +0000
+++ Mailman/Archiver/HyperArch.py	2015-01-23 00:09:03 +0000
@@ -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 @@
             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 @@
             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)))

=== modified file 'Mailman/Cgi/confirm.py'
--- Mailman/Cgi/confirm.py	2014-03-22 03:47:45 +0000
+++ Mailman/Cgi/confirm.py	2015-01-23 00:09:03 +0000
@@ -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 @@
     %(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.''')
 

=== modified file 'Mailman/Cgi/options.py'
--- Mailman/Cgi/options.py	2014-11-08 01:19:05 +0000
+++ Mailman/Cgi/options.py	2015-01-23 00:09:03 +0000
@@ -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.htmlformat import *
 from Mailman.Logging.Syslog import syslog
 
+OR = '|'
 SLASH = '/'
 SETLANGUAGE = -1
 
@@ -176,6 +177,9 @@
         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 @@
                 # 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 @@
                 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 @@
                 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 @@
     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

=== modified file 'Mailman/Commands/cmd_confirm.py'
--- Mailman/Commands/cmd_confirm.py	2011-11-13 21:32:08 +0000
+++ Mailman/Commands/cmd_confirm.py	2015-01-23 00:09:03 +0000
@@ -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 @@
         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."""))

=== modified file 'Mailman/Defaults.py.in'
--- Mailman/Defaults.py.in	2015-01-11 05:22:36 +0000
+++ Mailman/Defaults.py.in	2015-01-23 00:09:03 +0000
@@ -1188,6 +1188,10 @@
 # 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

=== modified file 'Mailman/Gui/Privacy.py'
--- Mailman/Gui/Privacy.py	2014-05-03 03:26:03 +0000
+++ Mailman/Gui/Privacy.py	2015-01-23 00:09:03 +0000
@@ -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 @@
 
             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)"""),

=== modified file 'Mailman/Gui/Topics.py'
--- Mailman/Gui/Topics.py	2009-02-09 00:36:11 +0000
+++ Mailman/Gui/Topics.py	2015-01-23 00:09:03 +0000
@@ -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 @@
     True = 1
     False = 0
 
+OR = '|'
+
 
 
 class Topics(GUIBase):
@@ -126,10 +128,10 @@
             # 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

=== modified file 'Mailman/HTMLFormatter.py'
--- Mailman/HTMLFormatter.py	2010-09-09 15:16:57 +0000
+++ Mailman/HTMLFormatter.py	2015-01-23 00:09:03 +0000
@@ -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 @@
                 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, ')')

=== modified file 'Mailman/Handlers/Moderate.py'
--- Mailman/Handlers/Moderate.py	2014-06-26 21:52:28 +0000
+++ Mailman/Handlers/Moderate.py	2015-01-23 00:09:03 +0000
@@ -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 @@
               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."""))
 
 
 

=== modified file 'Mailman/Handlers/Tagger.py'
--- Mailman/Handlers/Tagger.py	2013-11-15 02:15:47 +0000
+++ Mailman/Handlers/Tagger.py	2015-01-23 00:09:03 +0000
@@ -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.Logging.Syslog import syslog
 from Mailman.Handlers.CookHeaders import change_header
 
+OR = '|'
 CRNL = '\r\n'
 EMPTYSTRING = ''
 NLTAB = '\n\t'
@@ -63,7 +64,8 @@
     # 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

=== modified file 'Mailman/MailList.py'
--- Mailman/MailList.py	2014-05-03 03:26:03 +0000
+++ Mailman/MailList.py	2015-01-23 00:09:03 +0000
@@ -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 @@
 _ = i18n._
 
 EMPTYSTRING = ''
+OR = '|'
 
 try:
     True, False
@@ -356,6 +357,7 @@
         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 @@
         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 @@
             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 @@
         # 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 @@
             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 @@
                     # 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 @@
         """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 @@
             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
 
 
 

=== modified file 'Mailman/Utils.py'
--- Mailman/Utils.py	2014-09-22 02:06:41 +0000
+++ Mailman/Utils.py	2015-01-23 00:09:03 +0000
@@ -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 @@
 
 EMPTYSTRING = ''
 UEMPTYSTRING = u''
+CR = '\r'
 NL = '\n'
 DOT = '.'
 IDENTCHARS = ascii_letters + digits + '_'
@@ -918,6 +919,61 @@
         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,

=== modified file 'Mailman/Version.py'
--- Mailman/Version.py	2014-10-17 05:09:49 +0000
+++ Mailman/Version.py	2015-01-23 00:09:03 +0000
@@ -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 @@
                (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

=== modified file 'Mailman/versions.py'
--- Mailman/versions.py	2014-10-17 05:09:49 +0000
+++ Mailman/versions.py	2015-01-23 00:09:03 +0000
@@ -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 @@
             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 @@
     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)

=== modified file 'NEWS'
--- NEWS	2015-01-11 05:22:36 +0000
+++ NEWS	2015-01-23 00:09:03 +0000
@@ -5,6 +5,61 @@
 
 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

=== modified file 'bin/newlist'
--- bin/newlist	2013-07-12 21:03:26 +0000
+++ bin/newlist	2015-01-23 00:09:03 +0000
@@ -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 @@
         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 @@
 '--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 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 @@
         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',

=== added file 'templates/en/adminaddrchgack.txt'
--- templates/en/adminaddrchgack.txt	1970-01-01 00:00:00 +0000
+++ templates/en/adminaddrchgack.txt	2015-01-23 00:09:03 +0000
@@ -0,0 +1,4 @@
+Address for member %(name)s has been successfully changed
+from %(oldaddr)s to %(newaddr)s for list %(listname)s.
+
+

=== modified file 'templates/en/admindbdetails.html'
--- templates/en/admindbdetails.html	2006-07-21 05:46:44 +0000
+++ templates/en/admindbdetails.html	2015-01-23 00:09:03 +0000
@@ -12,7 +12,7 @@
 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

_______________________________________________
Mailman-checkins mailing list
Mailman-checkins@python.org
Unsubscribe: 
https://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org

Reply via email to