------------------------------------------------------------
revno: 6642
committer: Barry Warsaw <[EMAIL PROTECTED]>
branch nick: 3.0
timestamp: Wed 2008-09-24 22:59:05 -0400
message:
  thread merge
removed:
  mailman/commands/cmd_join.py
added:
  mailman/app/notifications.py
  mailman/commands/docs/
  mailman/commands/docs/echo.txt
  mailman/commands/docs/end.txt
  mailman/commands/docs/join.txt
renamed:
  mailman/commands/cmd_end.py => mailman/commands/end.py
  mailman/commands/cmd_subscribe.py => mailman/commands/join.py
modified:
  mailman/app/membership.py
  mailman/app/moderator.py
  mailman/app/registrar.py
  mailman/bin/set_members.py
  mailman/bin/testall.py
  mailman/commands/__init__.py
  mailman/commands/echo.py
  mailman/docs/registration.txt
  mailman/initialize.py
  mailman/interfaces/command.py
  mailman/interfaces/registrar.py
  mailman/queue/command.py
  mailman/queue/docs/command.txt
  mailman/queue/docs/outgoing.txt
  mailman/commands/end.py
  mailman/commands/join.py
    ------------------------------------------------------------
    revno: 6638.2.7
    committer: Barry Warsaw <[EMAIL PROTECTED]>
    branch nick: 10-commands
    timestamp: Wed 2008-09-24 22:22:29 -0400
    message:
      Complete the 'join' command, including allowing it to both register and
      subscribe to a mailing list.
    modified:
      mailman/app/registrar.py
      mailman/commands/docs/join.txt
      mailman/commands/join.py
      mailman/docs/registration.txt
      mailman/interfaces/registrar.py
    ------------------------------------------------------------
    revno: 6638.2.6
    committer: Barry Warsaw <[EMAIL PROTECTED]>
    branch nick: 10-commands
    timestamp: Tue 2008-09-23 23:30:05 -0400
    message:
      Collect the initialization of adapters into a separate method.
      
      Flesh out the join method.
    modified:
      mailman/app/registrar.py
      mailman/bin/testall.py
      mailman/commands/docs/join.txt
      mailman/commands/join.py
      mailman/initialize.py
    ------------------------------------------------------------
    revno: 6638.2.5
    committer: Barry Warsaw <[EMAIL PROTECTED]>
    branch nick: 10-commands
    timestamp: Tue 2008-09-23 22:38:44 -0400
    message:
      thread merge
    modified:
      TODO.txt
      mailman/app/registrar.py
      mailman/archiving/pipermail.py
      mailman/docs/domains.txt
      mailman/docs/registration.txt
    ------------------------------------------------------------
    revno: 6638.2.4
    committer: Barry Warsaw <[EMAIL PROTECTED]>
    branch nick: 10-commands
    timestamp: Sun 2008-09-21 18:11:38 -0400
    message:
      thread merge
    added:
      mailman/docs/domains.txt
      mailman/domain.py
    modified:
      mailman/Cgi/create.py
      mailman/Defaults.py
      mailman/archiving/mhonarc.py
      mailman/archiving/pipermail.py
      mailman/archiving/prototype.py
      mailman/configuration.py
      mailman/interfaces/domain.py
      mailman/testing/testing.cfg.in
      mailman/tests/test_documentation.py
    ------------------------------------------------------------
    revno: 6638.2.3
    committer: Barry Warsaw <[EMAIL PROTECTED]>
    branch nick: 10-commands
    timestamp: Tue 2008-09-16 20:29:21 -0400
    message:
      thread merge
    modified:
      mailman/database/__init__.py
      mailman/interfaces/usermanager.py
    ------------------------------------------------------------
    revno: 6638.2.2
    committer: Barry Warsaw <[EMAIL PROTECTED]>
    branch nick: 10-commands
    timestamp: Mon 2008-08-25 20:59:39 -0400
    message:
      checkpointing
    modified:
      mailman/commands/docs/join.txt
      mailman/commands/join.py
    ------------------------------------------------------------
    revno: 6638.2.1
    committer: Barry Warsaw <[EMAIL PROTECTED]>
    branch nick: 10-commands
    timestamp: Tue 2008-08-12 23:33:48 -0400
    message:
      Merge in command refactoring branch.
      
      Begin to flesh out the tests for the join command.
      
      Refactor out notifications from the add_member() function.
    removed:
      mailman/commands/cmd_join.py
    added:
      mailman/app/notifications.py
      mailman/commands/docs/
      mailman/commands/docs/echo.txt
      mailman/commands/docs/end.txt
      mailman/commands/docs/join.txt
    renamed:
      mailman/commands/cmd_end.py => mailman/commands/end.py
      mailman/commands/cmd_subscribe.py => mailman/commands/join.py
    modified:
      mailman/app/membership.py
      mailman/app/moderator.py
      mailman/bin/set_members.py
      mailman/commands/__init__.py
      mailman/commands/echo.py
      mailman/interfaces/command.py
      mailman/queue/command.py
      mailman/queue/docs/command.txt
      mailman/queue/docs/outgoing.txt
      mailman/commands/end.py
      mailman/commands/join.py
    ------------------------------------------------------------
    revno: 6633.3.1
    committer: Barry Warsaw <[EMAIL PROTECTED]>
    branch nick: 3.0commands
    timestamp: Tue 2008-08-12 20:54:04 -0400
    message:
      Checkpointing new command infrastructure.
      - Add join command
      - Add echo command
      - Add end command
    removed:
      mailman/commands/cmd_join.py
    added:
      mailman/commands/docs/
      mailman/commands/docs/echo.txt
      mailman/commands/docs/end.txt
    renamed:
      mailman/commands/cmd_end.py => mailman/commands/end.py
      mailman/commands/cmd_subscribe.py => mailman/commands/join.py
    modified:
      mailman/app/membership.py
      mailman/commands/__init__.py
      mailman/commands/echo.py
      mailman/interfaces/command.py
      mailman/queue/command.py
      mailman/queue/docs/command.txt
      mailman/commands/end.py
      mailman/commands/join.py

=== modified file 'mailman/app/membership.py'
--- a/mailman/app/membership.py 2008-03-27 09:14:14 +0000
+++ b/mailman/app/membership.py 2008-08-13 03:33:48 +0000
@@ -23,10 +23,8 @@
 __all__ = [
     'add_member',
     'delete_member',
-    'send_goodbye_message',
-    'send_welcome_message',
     ]
-    
+
 
 from email.utils import formataddr
 
@@ -34,6 +32,7 @@
 from mailman import Message
 from mailman import Utils
 from mailman import i18n
+from mailman.app.notifications import send_goodbye_message
 from mailman.configuration import config
 from mailman.interfaces import AlreadySubscribedError, DeliveryMode, MemberRole
 
@@ -41,31 +40,30 @@
 
 
 
-def add_member(mlist, address, realname, password, delivery_mode, language,
-               ack=None, admin_notif=None, text=''):
+def add_member(mlist, address, realname, password, delivery_mode, language):
     """Add a member right now.
 
     The member's subscription must be approved by whatever policy the list
     enforces.
 
-    ack is a flag that specifies whether the user should get an
-    acknowledgement of their being subscribed.  Default is to use the
-    list's default flag value.
-
-    admin_notif is a flag that specifies whether the list owner should get
-    an acknowledgement of this subscription.  Default is to use the list's
-    default flag value.
+    :param mlist: the mailing list to add the member to
+    :type mlist: IMailingList
+    :param address: the address to subscribe
+    :type address: string
+    :param realname: the subscriber's full name
+    :type realname: string
+    :param password: the subscriber's password
+    :type password: string
+    :param delivery_mode: the delivery mode the subscriber has chosen
+    :type delivery_mode: DeliveryMode
+    :param language: the language that the subscriber is going to use
+    :type language: string
     """
-    # Set up default flag values
-    if ack is None:
-        ack = mlist.send_welcome_msg
-    if admin_notif is None:
-        admin_notif = mlist.admin_notify_mchanges
     # Let's be extra cautious.
     Utils.ValidateEmail(address)
     if mlist.members.get_member(address) is not None:
-        raise AlreadySubscribedError(mlist.fqdn_listname, address,
-                                     MemberRole.member)
+        raise AlreadySubscribedError(
+            mlist.fqdn_listname, address, MemberRole.member)
     # Check for banned address here too for admin mass subscribes and
     # confirmations.
     pattern = Utils.get_pattern(address, mlist.ban_list)
@@ -110,53 +108,6 @@
         member.preferences.delivery_mode = delivery_mode
 ##     mlist.setMemberOption(email, config.Moderate,
 ##                          mlist.default_member_moderation)
-    # Send notifications.
-    if ack:
-        send_welcome_message(mlist, address, language, delivery_mode, text)
-    if admin_notif:
-        with i18n.using_language(mlist.preferred_language):
-            subject = _('$mlist.real_name subscription notification')
-        if isinstance(realname, unicode):
-            realname = realname.encode(Utils.GetCharSet(language), 'replace')
-        text = Utils.maketext(
-            'adminsubscribeack.txt',
-            {'listname' : mlist.real_name,
-             'member'   : formataddr((realname, address)),
-             }, mlist=mlist)
-        msg = Message.OwnerNotification(mlist, subject, text)
-        msg.send(mlist)
-
-
-
-def send_welcome_message(mlist, address, language, delivery_mode, text=''):
-    if mlist.welcome_msg:
-        welcome = Utils.wrap(mlist.welcome_msg) + '\n'
-    else:
-        welcome = ''
-    # Find the IMember object which is subscribed to the mailing list, because
-    # from there, we can get the member's options url.
-    member = mlist.members.get_member(address)
-    options_url = member.options_url
-    # Get the text from the template.
-    text += Utils.maketext(
-        'subscribeack.txt', {
-            'real_name'         : mlist.real_name,
-            'posting_address'   : mlist.fqdn_listname,
-            'listinfo_url'      : mlist.script_url('listinfo'),
-            'optionsurl'        : options_url,
-            'request_address'   : mlist.request_address,
-            'welcome'           : welcome,
-            }, lang=language, mlist=mlist)
-    if delivery_mode is not DeliveryMode.regular:
-        digmode = _(' (Digest mode)')
-    else:
-        digmode = ''
-    msg = Message.UserNotification(
-        address, mlist.request_address,
-        _('Welcome to the "$mlist.real_name" mailing list${digmode}'),
-        text, language)
-    msg['X-No-Archive'] = 'yes'
-    msg.send(mlist, verp=config.VERP_PERSONALIZED_DELIVERIES)
 
 
 
@@ -184,16 +135,3 @@
              }, 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  2008-02-27 06:26:18 +0000
+++ b/mailman/app/moderator.py  2008-08-13 03:33:48 +0000
@@ -38,8 +38,11 @@
 from mailman import Utils
 from mailman import i18n
 from mailman.app.membership import add_member, delete_member
+from mailman.app.notifications import (
+    send_admin_subscription_notice, send_welcome_message)
 from mailman.configuration import config
 from mailman.interfaces import Action, DeliveryMode, RequestType
+from mailman.interfaces.member import AlreadySubscribedError
 from mailman.queue import Switchboard
 
 _ = i18n._
@@ -237,13 +240,21 @@
         delivery_mode = DeliveryMode(enum_value)
         address = data['address']
         realname = data['realname']
+        language = data['language']
+        password = data['password']
         try:
-            add_member(mlist, address, realname, data['password'],
-                       delivery_mode, data['language'])
-        except Errors.AlreadySubscribedError:
+            add_member(mlist, address, realname, password,
+                       delivery_mode, language)
+        except AlreadySubscribedError:
             # The address got subscribed in some other way after the original
             # request was made and accepted.
             pass
+        else:
+            if mlist.send_welcome_msg:
+                send_welcome_message(mlist, address, language, delivery_mode)
+            if mlist.admin_notify_mchanges:
+                send_admin_subscription_notice(
+                    mlist, address, realname, language)
         slog.info('%s: new %s, %s %s', mlist.fqdn_listname,
                   delivery_mode, formataddr((realname, address)),
                   'via admin approval')

=== added file 'mailman/app/notifications.py'
--- a/mailman/app/notifications.py      1970-01-01 00:00:00 +0000
+++ b/mailman/app/notifications.py      2008-08-13 03:33:48 +0000
@@ -0,0 +1,134 @@
+# Copyright (C) 2007-2008 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
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+"""Sending notifications."""
+
+from __future__ import with_statement
+
+__metaclass__ = type
+__all__ = [
+    'send_admin_subscription_notice',
+    'send_goodbye_message',
+    'send_welcome_message',
+    ]
+
+
+from email.utils import formataddr
+
+from mailman import Message
+from mailman import Utils
+from mailman import i18n
+from mailman.configuration import config
+from mailman.interfaces.member import DeliveryMode
+
+_ = i18n._
+
+
+
+def send_welcome_message(mlist, address, language, delivery_mode, text=''):
+    """Send a welcome message to a subscriber.
+
+    Prepending to the standard welcome message template is the mailing list's
+    welcome message, if there is one.
+
+    :param mlist: the mailing list
+    :type mlist: IMailingList
+    :param address: The address to respond to
+    :type address: string
+    :param language: the language of the response
+    :type language: string
+    :param delivery_mode: the type of delivery the subscriber is getting
+    :type delivery_mode: DeliveryMode
+    """
+    if mlist.welcome_msg:
+        welcome = Utils.wrap(mlist.welcome_msg) + '\n'
+    else:
+        welcome = ''
+    # Find the IMember object which is subscribed to the mailing list, because
+    # from there, we can get the member's options url.
+    member = mlist.members.get_member(address)
+    options_url = member.options_url
+    # Get the text from the template.
+    text += Utils.maketext(
+        'subscribeack.txt', {
+            'real_name'         : mlist.real_name,
+            'posting_address'   : mlist.fqdn_listname,
+            'listinfo_url'      : mlist.script_url('listinfo'),
+            'optionsurl'        : options_url,
+            'request_address'   : mlist.request_address,
+            'welcome'           : welcome,
+            }, lang=language, mlist=mlist)
+    if delivery_mode is not DeliveryMode.regular:
+        digmode = _(' (Digest mode)')
+    else:
+        digmode = ''
+    msg = Message.UserNotification(
+        address, mlist.request_address,
+        _('Welcome to the "$mlist.real_name" mailing list${digmode}'),
+        text, language)
+    msg['X-No-Archive'] = 'yes'
+    msg.send(mlist, verp=config.VERP_PERSONALIZED_DELIVERIES)
+
+
+
+def send_goodbye_message(mlist, address, language):
+    """Send a goodbye message to a subscriber.
+
+    Prepending to the standard goodbye message template is the mailing list's
+    goodbye message, if there is one.
+
+    :param mlist: the mailing list
+    :type mlist: IMailingList
+    :param address: The address to respond to
+    :type address: string
+    :param language: the language of the response
+    :type language: string
+    """
+    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)
+
+
+
+def send_admin_subscription_notice(mlist, address, full_name, language):
+    """Send the list administrators a subscription notice.
+
+    :param mlist: the mailing list
+    :type mlist: IMailingList
+    :param address: the address being subscribed
+    :type address: string
+    :param full_name: the name of the subscriber
+    :type full_name: string
+    :param language: the language of the address's realname
+    :type language: string
+    """
+    with i18n.using_language(mlist.preferred_language):
+        subject = _('$mlist.real_name subscription notification')
+    full_name = full_name.encode(Utils.GetCharSet(language), 'replace')
+    text = Utils.maketext(
+        'adminsubscribeack.txt',
+        {'listname' : mlist.real_name,
+         'member'   : formataddr((full_name, address)),
+         }, mlist=mlist)
+    msg = Message.OwnerNotification(mlist, subject, text)
+    msg.send(mlist)

=== modified file 'mailman/app/registrar.py'
--- a/mailman/app/registrar.py  2008-09-24 02:26:43 +0000
+++ b/mailman/app/registrar.py  2008-09-25 02:22:29 +0000
@@ -20,6 +20,7 @@
 __metaclass__ = type
 __all__ = [
     'Registrar',
+    'adapt_domain_to_registrar',
     ]
 
 
@@ -27,13 +28,13 @@
 import pkg_resources
 
 from zope.interface import implements
-from zope.interface.interface import adapter_hooks
 
 from mailman.Message import UserNotification
 from mailman.Utils import ValidateEmail
 from mailman.configuration import config
 from mailman.i18n import _
 from mailman.interfaces import IDomain, IPendable, IRegistrar
+from mailman.interfaces.member import MemberRole
 
 
 
@@ -49,29 +50,18 @@
     def __init__(self, context):
         self._context = context
 
-    def register(self, address, real_name=None):
+    def register(self, address, real_name=None, mlist=None):
         """See `IUserRegistrar`."""
         # First, do validation on the email address.  If the address is
         # invalid, it will raise an exception, otherwise it just returns.
         ValidateEmail(address)
-        # Check to see if there is already a verified IAddress in the database
-        # matching this address.  If so, there's nothing to do.
-        usermgr = config.db.user_manager
-        addr = usermgr.get_address(address)
-        if addr and addr.verified_on:
-            # Before returning, see if this address is linked to a user.  If
-            # not, create one and link it now since no future verification
-            # will be done.
-            user = usermgr.get_user(address)
-            if user is None:
-                user = usermgr.create_user()
-                user.real_name = (real_name if real_name else addr.real_name)
-                user.link(addr)
-            return None
-        # Calculate the token for this confirmation record.
-        pendable = PendableRegistration(type=PendableRegistration.PEND_KEY,
-                                        address=address,
-                                        real_name=real_name)
+        # Create a pendable for the registration.
+        pendable = PendableRegistration(
+            type=PendableRegistration.PEND_KEY,
+            address=address,
+            real_name=real_name)
+        if mlist is not None:
+            pendable['list_name'] = mlist.fqdn_listname
         token = config.db.pendings.add(pendable)
         # Set up some local variables for translation interpolation.
         domain = IDomain(self._context)
@@ -100,8 +90,8 @@
         missing = object()
         address = pendable.get('address', missing)
         real_name = pendable.get('real_name', missing)
-        if (pendable.get('type') <> PendableRegistration.PEND_KEY or
-            address is missing or real_name is missing):
+        list_name = pendable.get('list_name', missing)
+        if pendable.get('type') != PendableRegistration.PEND_KEY:
             # It seems like it would be very difficult to accurately guess
             # tokens, or brute force an attack on the SHA1 hash, so we'll just
             # throw the pendable away in that case.  It's possible we'll need
@@ -112,8 +102,10 @@
         # and an IUser linked to this IAddress.  See if any of these objects
         # currently exist in our database.
         usermgr = config.db.user_manager
-        addr = usermgr.get_address(address)
-        user = usermgr.get_user(address)
+        addr = (usermgr.get_address(address)
+                if address is not missing else None)
+        user = (usermgr.get_user(address)
+                if address is not missing else None)
         # If there is neither an address nor a user matching the confirmed
         # record, then create the user, which will in turn create the address
         # and link the two together
@@ -138,6 +130,13 @@
             # do is verify the address.
             pass
         addr.verified_on = datetime.datetime.now()
+        # If this registration is tied to a mailing list, subscribe the person
+        # to the list right now.
+        list_name = pendable.get('list_name')
+        if list_name is not None:
+            mlist = config.db.list_manager.get(list_name)
+            if mlist:
+                addr.subscribe(mlist, MemberRole.member)
         return True
 
     def discard(self, token):
@@ -159,5 +158,3 @@
     return (Registrar(obj)
             if IDomain.providedBy(obj) and iface is IRegistrar
             else None)
-
-adapter_hooks.append(adapt_domain_to_registrar)

=== modified file 'mailman/bin/set_members.py'
--- a/mailman/bin/set_members.py        2008-03-31 18:12:04 +0000
+++ b/mailman/bin/set_members.py        2008-08-13 03:33:48 +0000
@@ -25,6 +25,8 @@
 from mailman import i18n
 from mailman import passwords
 from mailman.app.membership import add_member
+from mailman.app.notifications import (
+    send_admin_subscription_notice, send_welcome_message)
 from mailman.configuration import config
 from mailman.initialize import initialize
 from mailman.interfaces import DeliveryMode
@@ -176,6 +178,10 @@
             add_member(mlist, address, real_name, password, delivery_mode,
                        mlist.preferred_language, send_welcome_msg,
                        admin_notify)
+            if send_welcome_msg:
+                send_welcome_message(mlist, address, language, delivery_mode)
+            if admin_notify:
+                send_admin_subscription_notice(mlist, address, real_name)
 
     config.db.flush()
 

=== modified file 'mailman/bin/testall.py'
--- a/mailman/bin/testall.py    2008-07-10 02:35:44 +0000
+++ b/mailman/bin/testall.py    2008-09-24 03:30:05 +0000
@@ -34,7 +34,7 @@
 from mailman import Defaults
 from mailman.configuration import config
 from mailman.i18n import _
-from mailman.initialize import initialize_1, initialize_2
+from mailman.initialize import initialize_1, initialize_2, initialize_3
 from mailman.testing.helpers import SMTPServer
 from mailman.version import MAILMAN_VERSION
 
@@ -261,6 +261,7 @@
 
         # With -vvv, turn on engine debugging.
         initialize_2(parser.options.verbosity > 3)
+        initialize_3()
 
         # Run the tests.  XXX I'm not sure if basedir can be converted to
         # pkg_resources.

=== modified file 'mailman/commands/__init__.py'
--- a/mailman/commands/__init__.py      2008-04-23 02:34:45 +0000
+++ b/mailman/commands/__init__.py      2008-08-13 03:33:48 +0000
@@ -17,4 +17,6 @@
 
 __all__ = [
     'echo',
+    'end',
+    'join',
     ]

=== removed file 'mailman/commands/cmd_join.py'
--- a/mailman/commands/cmd_join.py      2008-02-27 06:26:18 +0000
+++ b/mailman/commands/cmd_join.py      1970-01-01 00:00:00 +0000
@@ -1,20 +0,0 @@
-# Copyright (C) 2002-2008 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
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 
USA.
-
-"""The `join' command is synonymous with `subscribe'.
-"""
-
-from mailman.Commands.cmd_subscribe import process

=== added directory 'mailman/commands/docs'
=== added file 'mailman/commands/docs/echo.txt'
--- a/mailman/commands/docs/echo.txt    1970-01-01 00:00:00 +0000
+++ b/mailman/commands/docs/echo.txt    2008-08-13 00:54:04 +0000
@@ -0,0 +1,31 @@
+The 'echo' command
+==================
+
+The mail command 'echo' simply replies with the original command and arguments
+to the sender.
+
+    >>> from mailman.configuration import config
+    >>> command = config.commands['echo']
+    >>> command.name
+    'echo'
+    >>> command.argument_description
+    '[args]'
+    >>> command.description
+    u'Echo an acknowledgement.  Arguments are return unchanged.'
+
+The original message is ignored, but the results receive the echoed command.
+
+    >>> from mailman.app.lifecycle import create_list
+    >>> mlist = create_list(u'[EMAIL PROTECTED]')
+
+    >>> from mailman.queue.command import Results
+    >>> results = Results()
+
+    >>> from mailman.Message import Message
+    >>> print command.process(mlist, Message(), {}, ('foo', 'bar'), results)
+    ContinueProcessing.yes
+    >>> print unicode(results)
+    The results of your email command are provided below.
+    <BLANKLINE>
+    echo foo bar
+    <BLANKLINE>

=== added file 'mailman/commands/docs/end.txt'
--- a/mailman/commands/docs/end.txt     1970-01-01 00:00:00 +0000
+++ b/mailman/commands/docs/end.txt     2008-08-13 00:54:04 +0000
@@ -0,0 +1,38 @@
+The 'end' command
+=================
+
+The mail command processor recognized an 'end' command which tells it to stop
+processing email messages.
+
+    >>> from mailman.configuration import config
+    >>> command = config.commands['end']
+    >>> command.name
+    'end'
+    >>> command.description
+    u'Stop processing commands.'
+
+The 'end' command takes no arguments.
+
+    >>> command.argument_description
+    ''
+
+The command itself is fairly simple; it just stops command processing, and the
+message isn't even looked at.
+
+    >>> from mailman.app.lifecycle import create_list
+    >>> mlist = create_list(u'[EMAIL PROTECTED]')
+    >>> from mailman.Message import Message
+    >>> print command.process(mlist, Message(), {}, (), None)
+    ContinueProcessing.no
+
+The 'stop' command is a synonym for 'end'.
+
+    >>> command = config.commands['stop']
+    >>> command.name
+    'stop'
+    >>> command.description
+    u'Stop processing commands.'
+    >>> command.argument_description
+    ''
+    >>> print command.process(mlist, Message(), {}, (), None)
+    ContinueProcessing.no

=== added file 'mailman/commands/docs/join.txt'
--- a/mailman/commands/docs/join.txt    1970-01-01 00:00:00 +0000
+++ b/mailman/commands/docs/join.txt    2008-09-25 02:22:29 +0000
@@ -0,0 +1,172 @@
+The 'join' command
+==================
+
+The mail command 'join' subscribes an email address to the mailing list.
+'subscribe' is an alias for 'join'.
+
+    >>> from mailman.configuration import config
+    >>> command = config.commands['join']
+    >>> print command.name
+    join
+    >>> print command.description
+    Join this mailing list.  You will be asked to confirm your subscription
+    request and you may be issued a provisional password.
+    <BLANKLINE>
+    By using the 'digest' option, you can specify whether you want digest
+    delivery or not.  If not specified, the mailing list's default will be
+    used.  You can also subscribe an alternative address by using the
+    'address' option.  For example:
+    <BLANKLINE>
+        join [EMAIL PROTECTED]
+    <BLANKLINE>
+    >>> print command.argument_description
+    [digest=<yes|no>] [address=<address>]
+
+
+No address to join
+------------------
+
+    >>> from mailman.Message import Message
+    >>> from mailman.app.lifecycle import create_list
+    >>> from mailman.queue.command import Results
+    >>> mlist = create_list(u'[EMAIL PROTECTED]')
+
+When no address argument is given, the message's From address will be used.
+If that's missing though, then an error is returned.
+
+    >>> results = Results()
+    >>> print command.process(mlist, Message(), {}, (), results)
+    ContinueProcessing.no
+    >>> print unicode(results)
+    The results of your email command are provided below.
+    <BLANKLINE>
+    join: No valid address found to subscribe
+    <BLANKLINE>
+
+The 'subscribe' command is an alias.
+
+    >>> subscribe = config.commands['subscribe']
+    >>> print subscribe.name
+    subscribe
+    >>> results = Results()
+    >>> print subscribe.process(mlist, Message(), {}, (), results)
+    ContinueProcessing.no
+    >>> print unicode(results)
+    The results of your email command are provided below.
+    <BLANKLINE>
+    subscribe: No valid address found to subscribe
+    <BLANKLINE>
+
+
+Joining the sender
+------------------
+
+When the message has a From field, that address will be subscribed.
+
+    >>> msg = message_from_string("""\
+    ... From: Anne Person <[EMAIL PROTECTED]>
+    ...
+    ... """)
+    >>> results = Results()
+    >>> print command.process(mlist, msg, {}, (), results)
+    ContinueProcessing.yes
+    >>> print unicode(results)
+    The results of your email command are provided below.
+    <BLANKLINE>
+    Confirmation email sent to Anne Person <[EMAIL PROTECTED]>
+    <BLANKLINE>
+
+Anne is not yet a member because she must confirm her subscription request
+first.
+
+    >>> print config.db.user_manager.get_user(u'[EMAIL PROTECTED]')
+    None
+
+Mailman has sent her the confirmation message.
+
+    >>> from mailman.queue import Switchboard
+    >>> virginq = Switchboard(config.VIRGINQUEUE_DIR)
+    >>> qmsg, qdata = virginq.dequeue(virginq.files[0])
+    >>> print qmsg.as_string()
+    MIME-Version: 1.0
+    ...
+    Subject: confirm ...
+    From: [EMAIL PROTECTED]
+    To: [EMAIL PROTECTED]
+    ...
+    <BLANKLINE>
+    Email Address Registration Confirmation
+    <BLANKLINE>
+    Hello, this is the GNU Mailman server at example.com.
+    <BLANKLINE>
+    We have received a registration request for the email address
+    <BLANKLINE>
+        [EMAIL PROTECTED]
+    <BLANKLINE>
+    Before you can start using GNU Mailman at this site, you must first
+    confirm that this is your email address.  You can do this by replying to
+    this message, keeping the Subject header intact.  Or you can visit this
+    web page
+    <BLANKLINE>
+        http://www.example.com/confirm/...
+    <BLANKLINE>
+    If you do not wish to register this email address simply disregard this
+    message.  If you think you are being maliciously subscribed to the list, or
+    have any other questions, you may contact
+    <BLANKLINE>
+        [EMAIL PROTECTED]
+    <BLANKLINE>
+
+Once Anne confirms her registration, she will be made a member of the mailing
+list.
+
+    >>> token = str(qmsg['subject']).split()[1].strip()
+    >>> from mailman.interfaces.registrar import IRegistrar
+    >>> registrar = IRegistrar(config.domains['example.com'])
+    >>> registrar.confirm(token)
+    True
+
+    >>> user = config.db.user_manager.get_user(u'[EMAIL PROTECTED]')
+    >>> print user.real_name
+    Anne Person
+    >>> list(user.addresses)
+    [<Address: Anne Person <[EMAIL PROTECTED]> [verified] at ...>]
+
+Anne is also now a member of the mailing list.
+
+    >>> mlist.members.get_member(u'[EMAIL PROTECTED]')
+    <Member: Anne Person <[EMAIL PROTECTED]>
+             on [EMAIL PROTECTED] as MemberRole.member>
+
+
+Joining a second list
+---------------------
+
+    >>> mlist_2 = create_list(u'[EMAIL PROTECTED]')
+    >>> msg = message_from_string("""\
+    ... From: Anne Person <[EMAIL PROTECTED]>
+    ...
+    ... """)
+    >>> print command.process(mlist_2, msg, {}, (), Results())
+    ContinueProcessing.yes
+
+Anne of course, is still registered.
+
+    >>> print config.db.user_manager.get_user(u'[EMAIL PROTECTED]')
+    <User "Anne Person" at ...>
+
+But she is not a member of the mailing list.
+
+    >>> print mlist_2.members.get_member(u'[EMAIL PROTECTED]')
+    None
+
+One Anne confirms this subscription, she becomes a member of the mailing list.
+
+    >>> qmsg, qdata = virginq.dequeue(virginq.files[0])
+    >>> token = str(qmsg['subject']).split()[1].strip()
+    >>> registrar.confirm(token)
+    True
+
+    >>> print mlist_2.members.get_member(u'[EMAIL PROTECTED]')
+    <Member: Anne Person <[EMAIL PROTECTED]>
+             on [EMAIL PROTECTED] as MemberRole.member>

=== modified file 'mailman/commands/echo.py'
--- a/mailman/commands/echo.py  2008-04-26 05:39:14 +0000
+++ b/mailman/commands/echo.py  2008-08-13 00:54:04 +0000
@@ -25,7 +25,7 @@
 from zope.interface import implements
 
 from mailman.i18n import _
-from mailman.interfaces import IEmailCommand
+from mailman.interfaces import ContinueProcessing, IEmailCommand
 
 
 SPACE = ' '
@@ -44,4 +44,4 @@
     def process(self, mlist, msg, msgdata, arguments, results):
         """See `IEmailCommand`."""
         print >> results, 'echo', SPACE.join(arguments)
-        return True
+        return ContinueProcessing.yes

=== renamed file 'mailman/commands/cmd_end.py' => 'mailman/commands/end.py'
--- a/mailman/commands/cmd_end.py       2008-02-27 06:26:18 +0000
+++ b/mailman/commands/end.py   2008-08-13 00:54:04 +0000
@@ -14,20 +14,37 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 
USA.
 
-"""
-    end
-        Stop processing commands.  Use this if your mail program automatically
-        adds a signature file.
-"""
+"""The email commands 'end' and 'stop'."""
+
+__metaclass__ = type
+__all__ = [
+    'End',
+    'Stop',
+    ]
+
+
+from zope.interface import implements
 
 from mailman.i18n import _
-
-
-
-def gethelp(mlist):
-    return _(__doc__)
-
-
-
-def process(res, args):
-    return 1 # STOP
+from mailman.interfaces import ContinueProcessing, IEmailCommand
+
+
+
+class End:
+    """The email 'end' command."""
+    implements(IEmailCommand)
+
+    name = 'end'
+    argument_description = ''
+    description = _('Stop processing commands.')
+
+    def process(self, mlist, msg, msgdata, arguments, results):
+        """See `IEmailCommand`."""
+        # Ignore all arguments.
+        return ContinueProcessing.no
+
+
+class Stop(End):
+    """The email 'stop' command (an alias for 'end')."""
+
+    name = 'stop'

=== renamed file 'mailman/commands/cmd_subscribe.py' => 
'mailman/commands/join.py'
--- a/mailman/commands/cmd_subscribe.py 2008-02-27 06:26:18 +0000
+++ b/mailman/commands/join.py  2008-09-25 02:22:29 +0000
@@ -14,120 +14,114 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 
USA.
 
-"""
-    subscribe [password] [digest|nodigest] [address=<address>]
-        Subscribe to this mailing list.  Your password must be given to
-        unsubscribe or change your options, but if you omit the password, one
-        will be generated for you.  You may be periodically reminded of your
-        password.
-
-        The next argument may be either: `nodigest' or `digest' (no quotes!).
-        If you wish to subscribe an address other than the address you sent
-        this request from, you may specify `address=<address>' (no brackets
-        around the email address, and no quotes!)
-"""
-
-from email.Utils import parseaddr
-from email.Header import decode_header, make_header
-
-from mailman import Utils
-from mailman import Errors
-from mailman.UserDesc import UserDesc
+"""The email commands 'join' and 'subscribe'."""
+
+__metaclass__ = type
+__all__ = [
+    'Join',
+    'Subscribe',
+    ]
+
+
+from email.header import decode_header, make_header
+from email.utils import formataddr, parseaddr
+from zope.interface import implements
+
+from mailman.Utils import MakeRandomPassword
+from mailman.configuration import config
 from mailman.i18n import _
-
-STOP = 1
-
-
-
-def gethelp(mlist):
-    return _(__doc__)
-
-
-
-def process(res, args):
-    mlist = res.mlist
-    digest = None
-    password = None
-    address = None
-    realname = None
-    # Parse the args
-    argnum = 0
-    for arg in args:
-        if arg.startswith('address='):
-            address = arg[8:]
-        elif argnum == 0:
-            password = arg
-        elif argnum == 1:
-            if arg.lower() not in ('digest', 'nodigest'):
-                res.results.append(_('Bad digest specifier: %(arg)s'))
-                return STOP
-            if arg.lower() == 'digest':
-                digest = 1
-            else:
-                digest = 0
-        else:
-            res.results.append(_('Usage:'))
-            res.results.append(gethelp(mlist))
-            return STOP
-        argnum += 1
-    # Fill in empty defaults
-    if digest is None:
-        digest = mlist.digest_is_default
-    if password is None:
-        password = Utils.MakeRandomPassword()
-    if address is None:
-        realname, address = parseaddr(res.msg['from'])
-        if not address:
-            # Fall back to the sender address
-            address = res.msg.get_sender()
-        if not address:
-            res.results.append(_('No valid address found to subscribe'))
-            return STOP
-        # Watch for encoded names
-        try:
-            h = make_header(decode_header(realname))
-            # BAW: in Python 2.2, use just unicode(h)
-            realname = h.__unicode__()
-        except UnicodeError:
-            realname = u''
-        # Coerce to byte string if uh contains only ascii
-        try:
-            realname = realname.encode('us-ascii')
-        except UnicodeError:
-            pass
-    # Create the UserDesc record and do a non-approved subscription
-    listowner = mlist.GetOwnerEmail()
-    userdesc = UserDesc(address, realname, password, digest)
-    remote = res.msg.get_sender()
-    try:
-        mlist.AddMember(userdesc, remote)
-    except Errors.MembershipIsBanned:
-        res.results.append(_("""\
-The email address you supplied is banned from this mailing list.
-If you think this restriction is erroneous, please contact the list
-owners at %(listowner)s."""))
-        return STOP
-    except Errors.InvalidEmailAddress:
-        res.results.append(_("""\
-Mailman won't accept the given email address as a valid address."""))
-        return STOP
-    except Errors.MMAlreadyAMember:
-        res.results.append(_('You are already subscribed!'))
-        return STOP
-    except Errors.MMCantDigestError:
-        res.results.append(
-            _('No one can subscribe to the digest of this list!'))
-        return STOP
-    except Errors.MMMustDigestError:
-        res.results.append(_('This list only supports digest subscriptions!'))
-        return STOP
-    except Errors.MMSubscribeNeedsConfirmation:
-        # We don't need to respond /and/ send a confirmation message.
-        res.respond = 0
-    except Errors.MMNeedApproval:
-        res.results.append(_("""\
-Your subscription request has been forwarded to the list administrator
-at %(listowner)s for review."""))
-    else:
-        # Everything is a-ok
-        res.results.append(_('Subscription request succeeded.'))
+from mailman.interfaces import (
+    ContinueProcessing, DeliveryMode, IEmailCommand)
+from mailman.interfaces.registrar import IRegistrar
+
+
+
+class Join:
+    """The email 'join' command."""
+    implements(IEmailCommand)
+
+    name = 'join'
+    argument_description = '[digest=<yes|no>] [address=<address>]'
+    description = _("""\
+Join this mailing list.  You will be asked to confirm your subscription
+request and you may be issued a provisional password.
+
+By using the 'digest' option, you can specify whether you want digest delivery
+or not.  If not specified, the mailing list's default will be used.  You can
+also subscribe an alternative address by using the 'address' option.  For
+example:
+
+    join [EMAIL PROTECTED]
+""")
+
+    def process(self, mlist, msg, msgdata, arguments, results):
+        """See `IEmailCommand`."""
+        # Parse the arguments.
+        address, delivery_mode = self._parse_arguments(arguments)
+        if address is None:
+            real_name, address = parseaddr(msg['from'])
+        # Address could be None or the empty string.
+        if not address:
+            address = msg.get_sender()
+        if not address:
+            print >> results, _(
+                '$self.name: No valid address found to subscribe')
+            return ContinueProcessing.no
+        domain = config.domains[mlist.host_name]
+        registrar = IRegistrar(domain)
+        registrar.register(address, real_name, mlist)
+        person = formataddr((real_name, address))
+        print >> results, _('Confirmation email sent to $person')
+        return ContinueProcessing.yes
+
+    def _parse_arguments(self, arguments):
+        """Parse command arguments.
+
+        :param arguments: The sequences of arguments as given to the
+            `process()` method.
+        :return: address, delivery_mode
+        """
+        address = None
+        delivery_mode = None
+        for argument in arguments:
+            parts = argument.split('=', 1)
+            if parts[0].lower() == 'digest':
+                if digest is not None:
+                    print >> results, self.name, \
+                          _('duplicate argument: $argument')
+                    return ContinueProcessing.no
+                if len(parts) == 0:
+                    # We treat just plain 'digest' as 'digest=yes'.  We don't
+                    # yet support the other types of digest delivery.
+                    delivery_mode = DeliveryMode.mime_digests
+                else:
+                    if parts[1].lower() == 'yes':
+                        delivery_mode = DeliveryMode.mime_digests
+                    elif parts[1].lower() == 'no':
+                        delivery_mode = DeliveryMode.regular
+                    else:
+                        print >> results, self.name, \
+                              _('bad argument: $argument')
+                        return ContinueProcessing.no
+            elif parts[0].lower() == 'address':
+                if address is not None:
+                    print >> results, self.name, \
+                          _('duplicate argument $argument')
+                    return ContinueProcessing.no
+                if len(parts) == 0:
+                    print >> results, self.name, \
+                          _('missing argument value: $argument')
+                    return ContinueProcessing.no
+                if len(parts) > 1:
+                    print >> results, self.name, \
+                          _('too many argument values: $argument')
+                    return ContinueProcessing.no
+                address = parts[1]
+        return address, delivery_mode
+
+
+
+class Subscribe(Join):
+    """The email 'subscribe' command (an alias for 'join')."""
+
+    name = 'subscribe'

=== modified file 'mailman/docs/registration.txt'
--- a/mailman/docs/registration.txt     2008-09-24 02:26:43 +0000
+++ b/mailman/docs/registration.txt     2008-09-25 02:22:29 +0000
@@ -242,27 +242,12 @@
     >>> usermgr.get_address(u'[EMAIL PROTECTED]')
     <Address: [EMAIL PROTECTED] [verified] at ...>
 
-If an address being registered has already been verified, linked or not to a
-user, then registration sends no confirmation.
-
-    >>> print registrar.register(u'[EMAIL PROTECTED]')
-    None
-    >>> len(switchboard.files)
-    0
-
-But if the already verified address is not linked to a user, then a user is
-created now and they are linked, with no confirmation necessary.
-
-    >>> address = usermgr.create_address(u'[EMAIL PROTECTED]', u'Dave Person')
-    >>> address.verified_on = datetime.now()
-    >>> print usermgr.get_user(u'[EMAIL PROTECTED]')
-    None
-    >>> print registrar.register(u'[EMAIL PROTECTED]')
-    None
-    >>> len(switchboard.files)
-    0
-    >>> usermgr.get_user(u'[EMAIL PROTECTED]')
-    <User "Dave Person" at ...>
+Even if the address being registered has already been verified, the
+registration sends a confirmation.
+
+    >>> token = registrar.register(u'[EMAIL PROTECTED]')
+    >>> token is not None
+    True
 
 
 Discarding
@@ -290,9 +275,12 @@
 different except that the new address will still need to be verified before it
 can be used.
 
-    >>> dperson = usermgr.get_user(u'[EMAIL PROTECTED]')
+    >>> dperson = usermgr.create_user(u'[EMAIL PROTECTED]', u'Dave Person')
     >>> dperson
     <User "Dave Person" at ...>
+    >>> address = usermgr.get_address(u'[EMAIL PROTECTED]')
+    >>> address.verified_on = datetime.now()
+
     >>> from operator import attrgetter
     >>> sorted((addr for addr in dperson.addresses), key=attrgetter('address'))
     [<Address: Dave Person <[EMAIL PROTECTED]> [verified] at ...>]
@@ -339,12 +327,27 @@
     >>> print pendingdb.confirm(token)
     None
 
-If somehow the pending registration event doesn't have an address in its
-record, you will also get None back, and the record will be removed.
-
-    >>> pendable = SimplePendable(type='registration', foo='bar')
-    >>> token = pendingdb.add(pendable)
+
+Registration and subscription
+-----------------------------
+
+Fred registers with Mailman at the same time that he subscribes to a mailing
+list.
+
+    >>> from mailman.app.lifecycle import create_list
+    >>> mlist = create_list(u'[EMAIL PROTECTED]')
+    >>> token = registrar.register(
+    ...     u'[EMAIL PROTECTED]', 'Fred Person', mlist)
+
+Before confirmation, Fred is not a member of the mailing list.
+
+    >>> print mlist.members.get_member(u'[EMAIL PROTECTED]')
+    None
+
+But after confirmation, he is.
+
     >>> registrar.confirm(token)
-    False
-    >>> print pendingdb.confirm(token)
-    None
+    True
+    >>> print mlist.members.get_member(u'[EMAIL PROTECTED]')
+    <Member: Fred Person <[EMAIL PROTECTED]>
+             on [EMAIL PROTECTED] as MemberRole.member>

=== modified file 'mailman/initialize.py'
--- a/mailman/initialize.py     2008-07-05 15:06:37 +0000
+++ b/mailman/initialize.py     2008-09-24 03:30:05 +0000
@@ -26,6 +26,7 @@
 
 import os
 
+from zope.interface.interface import adapter_hooks
 from zope.interface.verify import verifyObject
 
 import mailman.configuration
@@ -42,6 +43,17 @@
 # code will just call initialize().
 
 def initialize_1(config_path, propagate_logs):
+    """First initialization step.
+
+    * The configuration system
+    * Run-time directories
+    * The logging subsystem
+
+    :param config_path: The path to the configuration file.
+    :type config_path: string
+    :param propagate_logs: Should the log output propagate to stderr?
+    :type propagate_logs: boolean
+    """
     # By default, set the umask so that only owner and group can read and
     # write our files.  Specifically we must have g+rw and we probably want
     # o-rwx although I think in most cases it doesn't hurt if other can read
@@ -56,6 +68,17 @@
 
 
 def initialize_2(debug=False):
+    """Second initialization step.
+
+    * Archivers
+    * Rules
+    * Chains
+    * Pipelines
+    * Commands
+
+    :param debug: Should the database layer be put in debug mode?
+    :type debug: boolean
+    """
     database_plugin = get_plugin('mailman.database')
     # Instantiate the database plugin, ensure that it's of the right type, and
     # initialize it.  Then stash the object on our configuration object.
@@ -77,6 +100,17 @@
     initialize_commands()
 
 
+def initialize_3():
+    """Third initialization step.
+
+    * Adapters
+    """
+    from mailman.app.registrar import adapt_domain_to_registrar
+    adapter_hooks.append(adapt_domain_to_registrar)
+
+
+
 def initialize(config_path=None, propagate_logs=False):
     initialize_1(config_path, propagate_logs)
     initialize_2()
+    initialize_3()

=== modified file 'mailman/interfaces/command.py'
--- a/mailman/interfaces/command.py     2008-04-23 02:34:45 +0000
+++ b/mailman/interfaces/command.py     2008-08-13 00:54:04 +0000
@@ -17,10 +17,18 @@
 
 """Interfaces defining email commands."""
 
+from munepy import Enum
 from zope.interface import Interface, Attribute
 
 
 
+class ContinueProcessing(Enum):
+    """Should `IEmailCommand.process()` continue or not."""
+    no = 0
+    yes = 1
+
+
+
 class IEmailResults(Interface):
     """The email command results object."""
 
@@ -45,6 +53,6 @@
         :param msgdata: The message metadata.
         :param arguments: The command arguments tuple.
         :param results: An IEmailResults object for these commands.
-        :return: True if further processing should be taken of the email
-            commands in this message.
+        :return: A `ContinueProcessing` enum specifying whether to continue
+            processing or not.
         """

=== modified file 'mailman/interfaces/registrar.py'
--- a/mailman/interfaces/registrar.py   2008-02-08 04:01:48 +0000
+++ b/mailman/interfaces/registrar.py   2008-09-25 02:22:29 +0000
@@ -34,7 +34,7 @@
     syntax checking, or confirmation, while this interface does.
     """
 
-    def register(address, real_name=None):
+    def register(address, real_name=None, mlist=None):
         """Register the email address, requesting verification.
 
         No IAddress or IUser is created during this step, but after successful

=== modified file 'mailman/queue/command.py'
--- a/mailman/queue/command.py  2008-04-26 05:57:53 +0000
+++ b/mailman/queue/command.py  2008-08-13 00:54:04 +0000
@@ -44,7 +44,7 @@
 from mailman.app.replybot import autorespond_to_sender
 from mailman.configuration import config
 from mailman.i18n import _
-from mailman.interfaces import IEmailResults
+from mailman.interfaces import ContinueProcessing, IEmailResults
 from mailman.queue import Runner
 
 NL = '\n'
@@ -179,7 +179,12 @@
             if command is None:
                 print >> results, _('No such command: $command_name')
             else:
-                command.process(mlist, msg, msgdata, arguments, results)
+                status = command.process(
+                    mlist, msg, msgdata, arguments, results)
+                assert status in ContinueProcessing, (
+                    'Invalid status: %s' % status)
+                if status == ContinueProcessing.no:
+                    break
         # All done, send the response.
         if len(finder.command_lines) > 0:
             print >> results, _('\n- Unprocessed:')

=== modified file 'mailman/queue/docs/command.txt'
--- a/mailman/queue/docs/command.txt    2008-07-03 19:32:25 +0000
+++ b/mailman/queue/docs/command.txt    2008-08-13 03:33:48 +0000
@@ -104,3 +104,68 @@
     <BLANKLINE>
     - Done.
     <BLANKLINE>
+
+
+Stopping command processing
+---------------------------
+
+The 'end' command stops email processing, so that nothing following is looked
+at by the command queue.
+
+    >>> msg = message_from_string("""\
+    ... From: [EMAIL PROTECTED]
+    ... To: [EMAIL PROTECTED]
+    ... Message-ID: <caribou>
+    ...
+    ... echo foo bar
+    ... end ignored
+    ... echo baz qux
+    ... """)
+
+    >>> inject_message(mlist, msg, qdir=config.CMDQUEUE_DIR)
+    >>> command.run()
+    >>> len(virgin_queue.files)
+    1
+    >>> item = get_queue_messages(virgin_queue)[0]
+    >>> print item.msg.as_string()
+    Subject: The results of your email commands
+    ...
+    <BLANKLINE>
+    - Results:
+    echo foo bar
+    <BLANKLINE>
+    - Unprocessed:
+    echo baz qux
+    <BLANKLINE>
+    - Done.
+    <BLANKLINE>
+
+The 'stop' command is an alias for 'end'.
+
+    >>> msg = message_from_string("""\
+    ... From: [EMAIL PROTECTED]
+    ... To: [EMAIL PROTECTED]
+    ... Message-ID: <caribou>
+    ...
+    ... echo foo bar
+    ... stop ignored
+    ... echo baz qux
+    ... """)
+
+    >>> inject_message(mlist, msg, qdir=config.CMDQUEUE_DIR)
+    >>> command.run()
+    >>> len(virgin_queue.files)
+    1
+    >>> item = get_queue_messages(virgin_queue)[0]
+    >>> print item.msg.as_string()
+    Subject: The results of your email commands
+    ...
+    <BLANKLINE>
+    - Results:
+    echo foo bar
+    <BLANKLINE>
+    - Unprocessed:
+    echo baz qux
+    <BLANKLINE>
+    - Done.
+    <BLANKLINE>

=== modified file 'mailman/queue/docs/outgoing.txt'
--- a/mailman/queue/docs/outgoing.txt   2008-07-06 14:27:05 +0000
+++ b/mailman/queue/docs/outgoing.txt   2008-08-13 03:33:48 +0000
@@ -17,14 +17,11 @@
     >>> from mailman.app.membership import add_member
     >>> from mailman.interfaces import DeliveryMode
     >>> add_member(mlist, u'[EMAIL PROTECTED]', u'Anne Person',
-    ...            u'password', DeliveryMode.regular, u'en',
-    ...            ack=False, admin_notif=False)
+    ...            u'password', DeliveryMode.regular, u'en')
     >>> add_member(mlist, u'[EMAIL PROTECTED]', u'Bart Person',
-    ...            u'password', DeliveryMode.regular, u'en',
-    ...            ack=False, admin_notif=False)
+    ...            u'password', DeliveryMode.regular, u'en')
     >>> add_member(mlist, u'[EMAIL PROTECTED]', u'Cris Person',
-    ...            u'password', DeliveryMode.regular, u'en',
-    ...            ack=False, admin_notif=False)
+    ...            u'password', DeliveryMode.regular, u'en')
 
 By setting the mailing list to personalize messages, each recipient will get a
 unique copy of the message, with certain headers tailored for that recipient.



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

You are receiving this branch notification because you are subscribed to it.
_______________________________________________
Mailman-checkins mailing list
[email protected]
Unsubscribe: 
http://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org

Reply via email to