------------------------------------------------------------
revno: 6551
committer: Barry Warsaw <[EMAIL PROTECTED]>
branch nick: 3.0
timestamp: Sun 2007-09-16 12:08:24 -0400
message:
  Finish up the request hold conversion.  ListAdmin can go away though
  it hasn't yet.  SendSubscribeAck(), SendUnsubscribeAck(), and
  ApprovedDeleteMember() are all removed, though the latter is not yet
  completely eradicated.
modified:
  Mailman/Deliverer.py
  Mailman/MailList.py
  Mailman/app/membership.py
  Mailman/app/moderator.py
  Mailman/docs/requests.txt
  Mailman/templates/en/adminunsubscribeack.txt
  Mailman/templates/en/unsubauth.txt

=== modified file 'Mailman/Deliverer.py'
--- a/Mailman/Deliverer.py      2007-08-01 20:11:08 +0000
+++ b/Mailman/Deliverer.py      2007-09-16 16:08:24 +0000
@@ -37,53 +37,6 @@
 
 
 class Deliverer:
-    def SendSubscribeAck(self, name, password, digest, text=''):
-        pluser = self.getMemberLanguage(name)
-        if self.welcome_msg:
-            welcome = Utils.wrap(self.welcome_msg) + '\n'
-        else:
-            welcome = ''
-        if self.umbrella_list:
-            addr = self.GetMemberAdminEmail(name)
-            umbrella = Utils.wrap(_('''\
-Note: Since this is a list of mailing lists, administrative
-notices like the password reminder will be sent to
-your membership administrative address, %(addr)s.'''))
-        else:
-            umbrella = ''
-        # get the text from the template
-        text += Utils.maketext(
-            'subscribeack.txt',
-            {'real_name'   : self.real_name,
-             'host_name'   : self.host_name,
-             'welcome'     : welcome,
-             'umbrella'    : umbrella,
-             'emailaddr'   : self.GetListEmail(),
-             'listinfo_url': self.GetScriptURL('listinfo', absolute=True),
-             'optionsurl'  : self.GetOptionsURL(name, absolute=True),
-             'password'    : password,
-             'user'        : self.getMemberCPAddress(name),
-             }, lang=pluser, mlist=self)
-        if digest:
-            digmode = _(' (Digest mode)')
-        else:
-            digmode = ''
-        realname = self.real_name
-        msg = Message.UserNotification(
-            self.GetMemberAdminEmail(name), self.GetRequestEmail(),
-            _('Welcome to the "%(realname)s" mailing list%(digmode)s'),
-            text, pluser)
-        msg['X-No-Archive'] = 'yes'
-        msg.send(self, verp=config.VERP_PERSONALIZED_DELIVERIES)
-
-    def SendUnsubscribeAck(self, addr, lang):
-        realname = self.real_name
-        msg = Message.UserNotification(
-            self.GetMemberAdminEmail(addr), self.GetBouncesEmail(),
-            _('You have been unsubscribed from the %(realname)s mailing list'),
-            Utils.wrap(self.goodbye_msg), lang)
-        msg.send(self, verp=config.VERP_PERSONALIZED_DELIVERIES)
-
     def MailUserPassword(self, user):
         listfullname = self.fqdn_listname
         requestaddr = self.GetRequestEmail()

=== modified file 'Mailman/MailList.py'
--- a/Mailman/MailList.py       2007-09-16 11:15:53 +0000
+++ b/Mailman/MailList.py       2007-09-16 16:08:24 +0000
@@ -597,37 +597,6 @@
             raise Errors.MMNeedApproval, _(
                 'unsubscriptions require moderator approval')
 
-    def ApprovedDeleteMember(self, name, whence=None,
-                             admin_notif=None, userack=None):
-        if userack is None:
-            userack = self.send_goodbye_msg
-        if admin_notif is None:
-            admin_notif = self.admin_notify_mchanges
-        # Delete a member, for which we know the approval has been made
-        fullname, emailaddr = parseaddr(name)
-        userlang = self.getMemberLanguage(emailaddr)
-        # Remove the member
-        self.removeMember(emailaddr)
-        # And send an acknowledgement to the user...
-        if userack:
-            self.SendUnsubscribeAck(emailaddr, userlang)
-        # ...and to the administrator
-        if admin_notif:
-            realname = self.real_name
-            subject = _('%(realname)s unsubscribe notification')
-            text = Utils.maketext(
-                'adminunsubscribeack.txt',
-                {'member'  : name,
-                 'listname': self.real_name,
-                 }, mlist=self)
-            msg = Message.OwnerNotification(self, subject, text)
-            msg.send(self)
-        if whence:
-            whence = "; %s" % whence
-        else:
-            whence = ""
-        slog.info('%s: deleted %s%s', self.internal_name(), name, whence)
-
     def ChangeMemberName(self, addr, name, globally):
         self.setMemberName(addr, name)
         if not globally:

=== modified file 'Mailman/app/membership.py'
--- a/Mailman/app/membership.py 2007-09-16 11:15:53 +0000
+++ b/Mailman/app/membership.py 2007-09-16 16:08:24 +0000
@@ -151,3 +151,43 @@
         text, language)
     msg['X-No-Archive'] = 'yes'
     msg.send(mlist, verp=config.VERP_PERSONALIZED_DELIVERIES)
+
+
+
+def delete_member(mlist, address, admin_notif=None, userack=None):
+    if userack is None:
+        userack = mlist.send_goodbye_msg
+    if admin_notif is None:
+        admin_notif = mlist.admin_notify_mchanges
+    # Delete a member, for which we know the approval has been made
+    member = mlist.members.get_member(address)
+    language = member.preferred_language
+    member.unsubscribe()
+    # And send an acknowledgement to the user...
+    if userack:
+        send_goodbye_message(mlist, address, language)
+    # ...and to the administrator.
+    if admin_notif:
+        user = config.db.user_manager.get_user(address)
+        realname = user.real_name
+        subject = _('$mlist.real_name unsubscription notification')
+        text = Utils.maketext(
+            'adminunsubscribeack.txt',
+            {'listname': mlist.real_name,
+             'member'  : formataddr((realname, address)),
+             }, mlist=mlist)
+        msg = Message.OwnerNotification(mlist, subject, text)
+        msg.send(mlist)
+
+
+
+def send_goodbye_message(mlist, address, language):
+    if mlist.goodbye_msg:
+        goodbye = Utils.wrap(mlist.goodbye_msg) + '\n'
+    else:
+        goodbye = ''
+    msg = Message.UserNotification(
+        address, mlist.bounces_address,
+        _('You have been unsubscribed from the $mlist.real_name mailing list'),
+        goodbye, language)
+    msg.send(mlist, verp=config.VERP_PERSONALIZED_DELIVERIES)

=== modified file 'Mailman/app/moderator.py'
--- a/Mailman/app/moderator.py  2007-09-16 11:15:53 +0000
+++ b/Mailman/app/moderator.py  2007-09-16 16:08:24 +0000
@@ -17,10 +17,13 @@
 
 """Application support for moderators."""
 
-from __future__ import with_statement
-
 __all__ = [
+    'handle_message',
+    'handle_subscription',
+    'handle_unsubscription',
     'hold_message',
+    'hold_subscription',
+    'hold_unsubscription',
     ]
 
 import logging
@@ -33,7 +36,7 @@
 from Mailman import Utils
 from Mailman import i18n
 from Mailman.Queue.sbcache import get_switchboard
-from Mailman.app.membership import add_member
+from Mailman.app.membership import add_member, delete_member
 from Mailman.configuration import config
 from Mailman.constants import Action, DeliveryMode
 from Mailman.interfaces import RequestType
@@ -188,9 +191,8 @@
               mlist.fqdn_listname, address)
     # Possibly notify the administrator in default list language
     if mlist.admin_immed_notify:
-        realname = mlist.real_name
         subject = _(
-            'New subscription request to list $realname from $address')
+            'New subscription request to list $mlist.real_name from $address')
         text = Utils.maketext(
             'subauth.txt',
             {'username'   : address,
@@ -241,54 +243,62 @@
         raise AssertionError('Unexpected action: %s' % action)
     # Delete the request from the database.
     requestdb.delete_request(id)
-    return
 
 
 
-def HoldUnsubscription(self, addr):
-    # Assure the database is open for writing
-    self._opendb()
-    # Get the next unique id
-    id = self._next_id
-    # All we need to do is save the unsubscribing address
-    self._db[id] = (UNSUBSCRIPTION, addr)
+def hold_unsubscription(mlist, address):
+    data = dict(address=address)
+    requestsdb = config.db.requests.get_list_requests(mlist)
+    request_id = requestsdb.hold_request(
+        RequestType.unsubscription, address, data)
     vlog.info('%s: held unsubscription request from %s',
-              self.internal_name(), addr)
+              mlist.fqdn_listname, address)
     # Possibly notify the administrator of the hold
-    if self.admin_immed_notify:
-        realname = self.real_name
+    if mlist.admin_immed_notify:
         subject = _(
-            'New unsubscription request from %(realname)s by %(addr)s')
+            'New unsubscription request from $mlist.real_name by $address')
         text = Utils.maketext(
             'unsubauth.txt',
-            {'username'   : addr,
-             'listname'   : self.internal_name(),
-             'hostname'   : self.host_name,
-             'admindb_url': self.GetScriptURL('admindb', absolute=1),
-             }, mlist=self)
+            {'address'   : address,
+             'listname'   : mlist.fqdn_listname,
+             'admindb_url': mlist.script_url('admindb'),
+             }, mlist=mlist)
         # This message should appear to come from the <list>-owner so as
         # to avoid any useless bounce processing.
-        owneraddr = self.GetOwnerEmail()
-        msg = Message.UserNotification(owneraddr, owneraddr, subject, text,
-                                       self.preferred_language)
-        msg.send(self, **{'tomoderators': 1})
-
-def _handleunsubscription(self, record, value, comment):
-    addr = record
-    if value == config.DEFER:
-        return DEFER
-    elif value == config.DISCARD:
+        msg = Message.UserNotification(
+            mlist.owner_address, mlist.owner_address,
+            subject, text, mlist.preferred_language)
+        msg.send(mlist, tomoderators=True)
+    return request_id
+
+
+
+def handle_unsubscription(mlist, id, action, comment=None):
+    requestdb = config.db.requests.get_list_requests(mlist)
+    key, data = requestdb.get_request(id)
+    address = data['address']
+    if action is Action.defer:
+        # Nothing to do.
+        return
+    elif action is Action.discard:
+        # Nothing to do except delete the request from the database.
         pass
-    elif value == config.REJECT:
-        self._refuse(_('Unsubscription request'), addr, comment)
-    else:
-        assert value == config.UNSUBSCRIBE
+    elif action is Action.reject:
+        key, data = requestdb.get_request(id)
+        _refuse(mlist, _('Unsubscription request'), address,
+                comment or _('[No reason given]'))
+    elif action is Action.accept:
+        key, data = requestdb.get_request(id)
         try:
-            self.ApprovedDeleteMember(addr)
+            delete_member(mlist, address)
         except Errors.NotAMemberError:
-            # User has already been unsubscribed
+            # User has already been unsubscribed.
             pass
-    return REMOVE
+        slog.info('%s: deleted %s', mlist.fqdn_listname, address)
+    else:
+        raise AssertionError('Unexpected action: %s' % action)
+    # Delete the request from the database.
+    requestdb.delete_request(id)
 
 
 
@@ -324,41 +334,3 @@
     msg = Message.UserNotification(recip, mlist.bounces_address,
                                    subject, text, lang)
     msg.send(mlist)
-
-
-
-def readMessage(path):
-    # For backwards compatibility, we must be able to read either a flat text
-    # file or a pickle.
-    ext = os.path.splitext(path)[1]
-    with open(path) as fp:
-        if ext == '.txt':
-            msg = email.message_from_file(fp, Message.Message)
-        else:
-            assert ext == '.pck'
-            msg = cPickle.load(fp)
-    return msg
-
-
-
-def handle_request(mlist, id, value,
-                   comment=None, preserve=None, forward=None, addr=None):
-    requestsdb = config.db.get_list_requests(mlist)
-    key, data = requestsdb.get_record(id)
-
-    self._opendb()
-    rtype, data = self._db[id]
-    if rtype == HELDMSG:
-        status = self._handlepost(data, value, comment, preserve,
-                                  forward, addr)
-    elif rtype == UNSUBSCRIPTION:
-        status = self._handleunsubscription(data, value, comment)
-    else:
-        assert rtype == SUBSCRIPTION
-        status = self._handlesubscription(data, value, comment)
-    if status <> DEFER:
-        # BAW: Held message ids are linked to Pending cookies, allowing
-        # the user to cancel their post before the moderator has approved
-        # it.  We should probably remove the cookie associated with this
-        # id, but we have no way currently of correlating them. :(
-        del self._db[id]

=== modified file 'Mailman/docs/requests.txt'
--- a/Mailman/docs/requests.txt 2007-09-16 11:15:53 +0000
+++ b/Mailman/docs/requests.txt 2007-09-16 16:08:24 +0000
@@ -548,8 +548,8 @@
     ('recips', ['[EMAIL PROTECTED]']),
     ('reduced_list_headers', True), ('version', 3)]
 
-Or the subscription can be approved/accepted.  This subscribes the member to
-the mailing list.
+The subscription can also be accepted.  This subscribes the address to the
+mailing list.
 
     >>> mlist.send_welcome_msg = True
     >>> id_4 = moderator.hold_subscription(mlist,
@@ -711,28 +711,189 @@
     >>> mlist.admin_immed_notify = False
     >>> flush()
     >>> from Mailman.constants import MemberRole
-    >>> user_1 = config.db.user_manager.create_user('[EMAIL PROTECTED]')
+    >>> user_1 = config.db.user_manager.create_user('[EMAIL PROTECTED]')
     >>> flush()
     >>> address_1 = list(user_1.addresses)[0]
     >>> address_1.subscribe(mlist, MemberRole.member)
-    <Member: [EMAIL PROTECTED] on [EMAIL PROTECTED] as MemberRole.member>
-    >>> user_2 = config.db.user_manager.create_user('[EMAIL PROTECTED]')
+    <Member: [EMAIL PROTECTED] on [EMAIL PROTECTED] as MemberRole.member>
+    >>> user_2 = config.db.user_manager.create_user('[EMAIL PROTECTED]')
     >>> flush()
     >>> address_2 = list(user_2.addresses)[0]
     >>> address_2.subscribe(mlist, MemberRole.member)
-    <Member: [EMAIL PROTECTED] on [EMAIL PROTECTED] as MemberRole.member>
-    >>> flush()
-    >>> id_5 = moderator.hold_unsubscription(mlist, '[EMAIL PROTECTED]')
-    >>> flush()
-    >>> requests.get_request(id_5) is not None)
+    <Member: [EMAIL PROTECTED] on [EMAIL PROTECTED] as MemberRole.member>
+    >>> flush()
+    >>> id_5 = moderator.hold_unsubscription(mlist, '[EMAIL PROTECTED]')
+    >>> flush()
+    >>> requests.get_request(id_5) is not None
     True
     >>> virginq.files
     []
     >>> mlist.admin_immed_notify = True
-    >>> id_6 = moderator.hold_unsubscription(mlist, '[EMAIL PROTECTED]')
-    >>> flush()
-    >>> qmsg, qdata = dequeue()
-    >>> print qmsg.as_string()
-    XXX
-    >>> sorted(qdata.items())
-    XXX
+    >>> id_6 = moderator.hold_unsubscription(mlist, '[EMAIL PROTECTED]')
+    >>> flush()
+    >>> qmsg, qdata = dequeue()
+    >>> print qmsg.as_string()
+    MIME-Version: 1.0
+    Content-Type: text/plain; charset="us-ascii"
+    Content-Transfer-Encoding: 7bit
+    Subject: New unsubscription request from A Test List by [EMAIL PROTECTED]
+    From: [EMAIL PROTECTED]
+    To: [EMAIL PROTECTED]
+    Message-ID: ...
+    Date: ...
+    Precedence: bulk
+    <BLANKLINE>
+    Your authorization is required for a mailing list unsubscription
+    request approval:
+    <BLANKLINE>
+        By:   [EMAIL PROTECTED]
+        From: [EMAIL PROTECTED]
+    <BLANKLINE>
+    At your convenience, visit:
+    <BLANKLINE>
+        http://www.example.com/admindb/[EMAIL PROTECTED]
+    <BLANKLINE>
+    to process the request.
+    <BLANKLINE>
+    >>> sorted(qdata.items())
+    [('_parsemsg', False),
+     ('listname', '[EMAIL PROTECTED]'), ('nodecorate', True),
+     ('received_time', ...),
+     ('recips', ['[EMAIL PROTECTED]']),
+     ('reduced_list_headers', True), ('tomoderators', True), ('version', 3)]
+
+There are now two addresses with held unsubscription requests.  As above, one
+of the actions we can take is to defer to the decision.
+
+    >>> moderator.handle_unsubscription(mlist, id_5, Action.defer)
+    >>> flush()
+    >>> requests.get_request(id_5) is not None
+    True
+
+The held unsubscription can also be discarded, and the member will remain
+subscribed.
+
+    >>> moderator.handle_unsubscription(mlist, id_5, Action.discard)
+    >>> flush()
+    >>> print requests.get_request(id_5)
+    None
+    >>> mlist.members.get_member('[EMAIL PROTECTED]')
+    <Member: [EMAIL PROTECTED] on [EMAIL PROTECTED] as MemberRole.member>
+
+The request can be rejected, in which case a message is sent to the member,
+and the person remains a member of the mailing list.
+
+    >>> moderator.handle_unsubscription(mlist, id_6, Action.reject,
+    ...     'This list is a prison.')
+    >>> flush()
+    >>> print requests.get_request(id_6)
+    None
+    >>> qmsg, qdata = dequeue()
+    >>> print qmsg.as_string()
+    MIME-Version: 1.0
+    Content-Type: text/plain; charset="us-ascii"
+    Content-Transfer-Encoding: 7bit
+    Subject: Request to mailing list "A Test List" rejected
+    From: [EMAIL PROTECTED]
+    To: [EMAIL PROTECTED]
+    Message-ID: ...
+    Date: ...
+    Precedence: bulk
+    <BLANKLINE>
+    Your request to the [EMAIL PROTECTED] mailing list
+    <BLANKLINE>
+        Unsubscription request
+    <BLANKLINE>
+    has been rejected by the list moderator.  The moderator gave the
+    following reason for rejecting your request:
+    <BLANKLINE>
+    "This list is a prison."
+    <BLANKLINE>
+    Any questions or comments should be directed to the list administrator
+    at:
+    <BLANKLINE>
+        [EMAIL PROTECTED]
+    <BLANKLINE>
+    >>> sorted(qdata.items())
+    [('_parsemsg', False),
+     ('listname', '[EMAIL PROTECTED]'),
+     ('nodecorate', True), ('received_time', ...),
+     ('recips', ['[EMAIL PROTECTED]']),
+     ('reduced_list_headers', True), ('version', 3)]
+    >>> mlist.members.get_member('[EMAIL PROTECTED]')
+    <Member: [EMAIL PROTECTED] on [EMAIL PROTECTED] as MemberRole.member>
+
+The unsubscription request can also be accepted.  This removes the member from
+the mailing list.
+
+    >>> mlist.send_goodbye_msg = True
+    >>> mlist.goodbye_msg = 'So long!'
+    >>> mlist.admin_immed_notify = False
+    >>> flush()
+    >>> id_7 = moderator.hold_unsubscription(mlist, '[EMAIL PROTECTED]')
+    >>> flush()
+    >>> moderator.handle_unsubscription(mlist, id_7, Action.accept)
+    >>> flush()
+    >>> print mlist.members.get_member('[EMAIL PROTECTED]')
+    None
+
+There are now two messages in the virgin queue, one to the member who was just
+unsubscribed and another to the moderators informing them of this membership
+change.
+
+    >>> qmsg_1, qdata_1 = dequeue(expected_count=2)
+    >>> qmsg_2, qdata_2 = dequeue()
+    >>> if '[EMAIL PROTECTED]' in qdata_1['recips']:
+    ...     # The first message is the goodbye message
+    ...     goodbye_qmsg = qmsg_1
+    ...     goodbye_qdata = qdata_1
+    ...     admin_qmsg = qmsg_2
+    ...     admin_qdata = qdata_2
+    ... else:
+    ...     goodbye_qmsg = qmsg_2
+    ...     goodbye_qdata = qdata_2
+    ...     admin_qmsg = qmsg_1
+    ...     admin_qdata = qdata_1
+
+The goodbye message...
+
+    >>> print goodbye_qmsg.as_string()
+    MIME-Version: 1.0
+    Content-Type: text/plain; charset="us-ascii"
+    Content-Transfer-Encoding: 7bit
+    Subject: You have been unsubscribed from the A Test List mailing list
+    From: [EMAIL PROTECTED]
+    To: [EMAIL PROTECTED]
+    Message-ID: ...
+    Date: ...
+    Precedence: bulk
+    <BLANKLINE>
+    So long!
+    <BLANKLINE>
+    >>> sorted(goodbye_qdata.items())
+    [('_parsemsg', False),
+     ('listname', '[EMAIL PROTECTED]'),
+     ('nodecorate', True), ('received_time', ...),
+     ('recips', ['[EMAIL PROTECTED]']),
+     ('reduced_list_headers', True), ('verp', False), ('version', 3)]
+
+...and the admin message.
+
+    >>> print admin_qmsg.as_string()
+    MIME-Version: 1.0
+    Content-Type: text/plain; charset="us-ascii"
+    Content-Transfer-Encoding: 7bit
+    Subject: A Test List unsubscription notification
+    From: [EMAIL PROTECTED]
+    To: [EMAIL PROTECTED]
+    Message-ID: ...
+    Date: ...
+    Precedence: bulk
+    <BLANKLINE>
+    [EMAIL PROTECTED] has been removed from A Test List.
+    <BLANKLINE>
+    >>> sorted(admin_qdata.items())
+    [('_parsemsg', False), ('envsender', '[EMAIL PROTECTED]'),
+     ('listname', '[EMAIL PROTECTED]'),
+     ('nodecorate', True), ('received_time', ...),
+     ('recips', []), ('reduced_list_headers', True), ('version', 3)]

=== modified file 'Mailman/templates/en/adminunsubscribeack.txt'
--- a/Mailman/templates/en/adminunsubscribeack.txt      2001-05-18 21:28:54 
+0000
+++ b/Mailman/templates/en/adminunsubscribeack.txt      2007-09-16 16:08:24 
+0000
@@ -1,2 +1,1 @@
 %(member)s has been removed from %(listname)s.
-

=== modified file 'Mailman/templates/en/unsubauth.txt'
--- a/Mailman/templates/en/unsubauth.txt        2001-10-21 06:41:57 +0000
+++ b/Mailman/templates/en/unsubauth.txt        2007-09-16 16:08:24 +0000
@@ -1,8 +1,8 @@
 Your authorization is required for a mailing list unsubscription
 request approval:
 
-    By:   %(username)s
-    From: %(listname)[EMAIL PROTECTED](hostname)s
+    By:   %(address)s
+    From: %(listname)s
 
 At your convenience, visit:
 



--

https://code.launchpad.net/~mailman-coders/mailman/3.0

You are receiving this branch notification because you are subscribed to it.
To unsubscribe from this branch go to 
https://code.launchpad.net/~mailman-coders/mailman/3.0/+subscription/mailman-checkins.
_______________________________________________
Mailman-checkins mailing list
Mailman-checkins@python.org
Unsubscribe: 
http://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org

Reply via email to