Commit eb41bebb authored by Barry Warsaw's avatar Barry Warsaw

* Mailing lists get multiple chains and pipelines. For example, normal

   postings go through the `posting_chain` while messages to owners to through
   `owners_chain`.  The default `built-in` chain is renamed to
   `default-posting-chain` while the `built-in` pipeline is renamed
   `default-posting-pipeline`.

 * Schema changes:
   - start_chain      -> posting_chain
   - pipeline         -> posting_pipeline
parent 4ca5055e
......@@ -2,9 +2,9 @@
Chains
======
When a new message comes into the system, Mailman uses a set of rule chains to
decide whether the message gets posted to the list, rejected, discarded, or
held for moderator approval.
When a new message is posted to a mailing list, Mailman uses a set of rule
chains to decide whether the message gets accepted for posting, rejected,
discarded, or held for moderator approval.
There are a number of built-in chains available that act as end-points in the
processing of messages.
......@@ -285,13 +285,13 @@ Run-time chains are made up of links where each link associates both a rule
and a `jump`. The rule is really a rule name, which is looked up when
needed. The jump names a chain which is jumped to if the rule matches.
There is one built-in run-time chain, called appropriately `built-in`. This
is the default chain to use when no other input chain is defined for a mailing
list. It runs through the default rules.
There is one built-in posting chain. This is the default chain to use when no
other input chain is defined for a mailing list. It runs through the default
rules.
>>> chain = config.chains['built-in']
>>> chain = config.chains['default-posting-chain']
>>> print chain.name
built-in
default-posting-chain
>>> print chain.description
The built-in moderation chain.
......
......@@ -9,8 +9,8 @@ handlers, each of which is applied in turn. Unlike rules and chains, there is
no way to stop a pipeline from processing the message once it's started.
>>> mlist = create_list('[email protected]')
>>> print mlist.pipeline
built-in
>>> print mlist.posting_pipeline
default-posting-pipeline
>>> from mailman.core.pipelines import process
......@@ -28,7 +28,7 @@ Messages hit the pipeline after they've been accepted for posting.
... First post!
... """)
>>> msgdata = {}
>>> process(mlist, msg, msgdata, mlist.pipeline)
>>> process(mlist, msg, msgdata, mlist.posting_pipeline)
The message has been modified with additional headers, footer decorations,
etc.
......
......@@ -44,7 +44,7 @@ class BuiltInChain:
implements(IChain)
name = 'built-in'
name = 'default-posting-chain'
description = _('The built-in moderation chain.')
_link_descriptions = (
......
......@@ -73,7 +73,7 @@ built-in chain. No rules hit and so the message is accepted.
>>> from mailman.core.chains import process
>>> from mailman.testing.helpers import event_subscribers
>>> with event_subscribers(on_chain):
... process(mlist, msg, {}, 'built-in')
... process(mlist, msg, {}, 'default-posting-chain')
<mailman.chains.accept.AcceptNotification ...>
<mailman.chains.accept.AcceptChain ...>
Subject: aardvark
......@@ -108,7 +108,7 @@ moderator approval.
... """)
>>> with event_subscribers(on_chain):
... process(mlist, msg, {}, 'built-in')
... process(mlist, msg, {}, 'default-posting-chain')
<mailman.chains.hold.HoldNotification ...>
<mailman.chains.hold.HoldChain ...>
Subject: badger
......@@ -133,7 +133,7 @@ The list's member moderation action can also be set to `discard`...
... """)
>>> with event_subscribers(on_chain):
... process(mlist, msg, {}, 'built-in')
... process(mlist, msg, {}, 'default-posting-chain')
<mailman.chains.discard.DiscardNotification ...>
<mailman.chains.discard.DiscardChain ...>
Subject: cougar
......@@ -157,7 +157,7 @@ The list's member moderation action can also be set to `discard`...
... """)
>>> with event_subscribers(on_chain):
... process(mlist, msg, {}, 'built-in')
... process(mlist, msg, {}, 'default-posting-chain')
<mailman.chains.reject.RejectNotification ...>
<mailman.chains.reject.RejectChain ...>
Subject: dingo
......@@ -196,7 +196,7 @@ moderator approval.
... """)
>>> with event_subscribers(on_chain):
... process(mlist, msg, {}, 'built-in')
... process(mlist, msg, {}, 'default-posting-chain')
<mailman.chains.hold.HoldNotification ...>
<mailman.chains.hold.HoldChain ...>
Subject: elephant
......
......@@ -35,7 +35,7 @@ from mailman.interfaces.chain import LinkAction, IChain
def process(mlist, msg, msgdata, start_chain='built-in'):
def process(mlist, msg, msgdata, start_chain='default-posting-chain'):
"""Process the message through a chain.
:param mlist: the IMailingList for this message.
......
......@@ -79,7 +79,7 @@ class BasePipeline:
class BuiltInPipeline(BasePipeline):
"""The built-in pipeline."""
name = 'built-in'
name = 'default-posting-pipeline'
description = _('The built-in pipeline.')
_default_handlers = (
......
......@@ -37,7 +37,7 @@ from mailman.testing.layers import ConfigLayer
class TestBuiltinPipeline(unittest.TestCase):
"""Test various aspects of the built-in pipeline."""
"""Test various aspects of the built-in postings pipeline."""
layer = ConfigLayer
......@@ -57,6 +57,7 @@ Subject: a test
testing
""")
msgdata = {}
process(self._mlist, msg, msgdata, pipeline_name='built-in')
process(self._mlist, msg, msgdata,
pipeline_name='default-posting-pipeline')
self.assertEqual(msg['list-id'], '<test.example.com>')
self.assertEqual(msg['list-post'], '<mailto:[email protected]>')
......@@ -84,8 +84,9 @@ CREATE TABLE mailinglist (
nonmember_rejection_notice TEXT,
obscure_addresses BOOLEAN,
personalize INTEGER,
pipeline TEXT,
post_id INTEGER,
posting_chain TEXT,
posting_pipeline TEXT,
preferred_language TEXT,
private_roster BOOLEAN,
real_name TEXT,
......@@ -98,7 +99,6 @@ CREATE TABLE mailinglist (
send_goodbye_message BOOLEAN,
send_reminders BOOLEAN,
send_welcome_message BOOLEAN,
start_chain TEXT,
subject_prefix TEXT,
subscribe_auto_approval BYTEA,
subscribe_policy INTEGER,
......
......@@ -180,8 +180,9 @@ CREATE TABLE mailinglist (
nonmember_rejection_notice TEXT,
obscure_addresses BOOLEAN,
personalize INTEGER,
pipeline TEXT,
post_id INTEGER,
posting_chain TEXT,
posting_pipeline TEXT,
preferred_language TEXT,
private_roster BOOLEAN,
real_name TEXT,
......@@ -194,7 +195,6 @@ CREATE TABLE mailinglist (
send_goodbye_message BOOLEAN,
send_reminders BOOLEAN,
send_welcome_message BOOLEAN,
start_chain TEXT,
subject_prefix TEXT,
subscribe_auto_approval BLOB,
subscribe_policy INTEGER,
......
......@@ -31,6 +31,11 @@ Architecture
E.g. mailman:///[email protected]/it/welcome.txt
* $user_password is no longer supported as a placeholder in headers and
footers.
* Mailing lists get multiple chains and pipelines. For example, normal
postings go through the `posting_chain` while messages to owners to through
`owners_chain`. The default `built-in` chain is renamed to
`default-posting-chain` while the `built-in` pipeline is renamed
`default-posting-pipeline`.
Database
--------
......@@ -43,6 +48,8 @@ Database
- msg_footer -> footer_uri
- digest_header -> digest_header_uri
- digest_footer -> digest_footer_uri
- start_chain -> posting_chain
- pipeline -> posting_pipeline
REST
----
......
......@@ -383,11 +383,20 @@ class IMailingList(Interface):
# Processing.
pipeline = Attribute(
"""The name of this mailing list's processing pipeline.
posting_chain = Attribute(
"""This mailing list's moderation chain.
When messages are posted to a mailing list, it first goes through a
moderation chain to determine whether the message will be accepted.
This attribute names a chain for postings, which must exist.
""")
posting_pipeline = Attribute(
"""This mailing list's posting pipeline.
Every mailing list has a processing pipeline that messages flow
through once they've been accepted.
through once they've been accepted for posting to the mailing list.
This attribute names a pipeline for postings, which must exist.
""")
data_path = Attribute(
......
......@@ -168,8 +168,9 @@ class MailingList(Model):
nonmember_rejection_notice = Unicode()
obscure_addresses = Bool()
personalize = Enum(Personalization)
pipeline = Unicode()
post_id = Int()
posting_chain = Unicode()
posting_pipeline = Unicode()
_preferred_language = Unicode(name='preferred_language')
private_roster = Bool()
real_name = Unicode()
......@@ -182,7 +183,6 @@ class MailingList(Model):
send_goodbye_message = Bool()
send_reminders = Bool()
send_welcome_message = Bool()
start_chain = Unicode()
subject_prefix = Unicode()
subscribe_auto_approval = Pickle()
subscribe_policy = Int()
......
......@@ -192,9 +192,9 @@ ATTRIBUTES = dict(
next_digest_number=GetterSetter(None),
no_reply_address=GetterSetter(None),
owner_address=GetterSetter(None),
pipeline=GetterSetter(pipeline_validator),
post_id=GetterSetter(None),
posting_address=GetterSetter(None),
posting_pipeline=GetterSetter(pipeline_validator),
real_name=GetterSetter(unicode),
reply_goes_to_list=GetterSetter(enum_validator(ReplyToMunging)),
request_address=GetterSetter(None),
......
......@@ -51,9 +51,9 @@ All readable attributes for a list are available on a sub-resource.
next_digest_number: 1
no_reply_address: [email protected]
owner_address: [email protected]
pipeline: built-in
post_id: 1
posting_address: [email protected]
posting_pipeline: default-posting-pipeline
real_name: Test-one
reply_goes_to_list: no_munging
request_address: [email protected]
......@@ -93,7 +93,7 @@ all the writable attributes in one request.
... include_rfc2369_headers=False,
... include_list_post_header=False,
... digest_size_threshold=10.5,
... pipeline='virgin',
... posting_pipeline='virgin',
... filter_content=True,
... convert_html_to_plaintext=True,
... collapse_alternatives=False,
......@@ -141,8 +141,7 @@ These values are changed permanently.
include_list_post_header: False
include_rfc2369_headers: False
...
pipeline: virgin
...
posting_pipeline: virgin
real_name: Fnords
reply_goes_to_list: point_to_list
...
......@@ -174,7 +173,7 @@ must be included. It is an error to leave one or more out...
... include_rfc2369_headers=False,
... include_list_post_header=False,
... digest_size_threshold=10.5,
... pipeline='virgin',
... posting_pipeline='virgin',
... filter_content=True,
... convert_html_to_plaintext=True,
... collapse_alternatives=False,
......@@ -214,7 +213,7 @@ must be included. It is an error to leave one or more out...
... include_rfc2369_headers=False,
... include_list_post_header=False,
... digest_size_threshold=10.5,
... pipeline='virgin',
... posting_pipeline='virgin',
... filter_content=True,
... convert_html_to_plaintext=True,
... collapse_alternatives=False,
......@@ -247,7 +246,7 @@ It is also an error to spell an attribute value incorrectly...
... include_rfc2369_headers=False,
... include_list_post_header=False,
... digest_size_threshold=10.5,
... pipeline='virgin',
... posting_pipeline='virgin',
... filter_content=True,
... convert_html_to_plaintext=True,
... collapse_alternatives=False,
......@@ -279,7 +278,7 @@ It is also an error to spell an attribute value incorrectly...
... include_rfc2369_headers=False,
... include_list_post_header=False,
... digest_size_threshold=10.5,
... pipeline='dummy',
... posting_pipeline='dummy',
... filter_content=True,
... convert_html_to_plaintext=True,
... collapse_alternatives=False,
......@@ -287,7 +286,7 @@ It is also an error to spell an attribute value incorrectly...
... 'PUT')
Traceback (most recent call last):
...
HTTPError: HTTP Error 400: Cannot convert parameters: pipeline
HTTPError: HTTP Error 400: Cannot convert parameters: posting_pipeline
...or to name an invalid auto-response enumeration value.
......@@ -311,7 +310,7 @@ It is also an error to spell an attribute value incorrectly...
... include_rfc2369_headers=False,
... include_list_post_header=False,
... digest_size_threshold=10.5,
... pipeline='virgin',
... posting_pipeline='virgin',
... filter_content=True,
... convert_html_to_plaintext=True,
... collapse_alternatives=False,
......
......@@ -7,14 +7,13 @@ message. It can either be accepted for delivery, rejected (i.e. bounced),
held for moderator approval, or discarded.
The runner operates by processing chains on a message/metadata pair in the
context of a mailing list. Each mailing list may have a 'start chain' where
processing begins, with a global default. This chain is processed with the
message eventually ending up in one of the four disposition states described
above.
context of a mailing list. Each mailing list has a default chain for messages
posted to the mailing list. This chain is processed with the message
eventually ending up in one of the four disposition states described above.
>>> mlist = create_list('[email protected]')
>>> print mlist.start_chain
built-in
>>> print mlist.posting_chain
default-posting-chain
Sender addresses
......@@ -107,16 +106,17 @@ Inject the message into the incoming queue and run until the queue is empty.
>>> inject_message(mlist, msg)
>>> incoming.run()
There are no messages left in the incoming queue.
>>> get_queue_messages('in')
[]
Now the message is in the pipeline queue.
>>> pipeline_queue = config.switchboards['pipeline']
>>> len(pipeline_queue.files)
>>> messages = get_queue_messages('pipeline')
>>> len(messages)
1
>>> incoming_queue = config.switchboards['in']
>>> len(incoming_queue.files)
0
>>> item = get_queue_messages('pipeline')[0]
>>> print item.msg.as_string()
>>> print messages[0].msg.as_string()
From: [email protected]
To: [email protected]
Subject: My first post
......@@ -128,7 +128,7 @@ Now the message is in the pipeline queue.
<BLANKLINE>
First post!
<BLANKLINE>
>>> dump_msgdata(item.msgdata)
>>> dump_msgdata(messages[0].msgdata)
_parsemsg : False
envsender : [email protected]
...
......@@ -186,7 +186,7 @@ new chain and set it as the mailing list's start chain.
... return test_chain
>>> test_chain = make_chain('always-discard', 'discard')
>>> mlist.start_chain = test_chain.name
>>> mlist.posting_chain = test_chain.name
>>> msg.replace_header('message-id', '<second>')
>>> with event_subscribers(on_chain):
......@@ -204,7 +204,6 @@ new chain and set it as the mailing list's start chain.
The virgin queue needs to be cleared out due to artifacts from the
previous tests above.
>>> virgin_queue = config.switchboards['virgin']
>>> ignore = get_queue_messages('virgin')
......@@ -216,7 +215,7 @@ the original sender. Again, the built-in chain doesn't support this so we'll
just create a new chain that does.
>>> test_chain = make_chain('always-reject', 'reject')
>>> mlist.start_chain = test_chain.name
>>> mlist.posting_chain = test_chain.name
>>> msg.replace_header('message-id', '<third>')
>>> with event_subscribers(on_chain):
......@@ -231,10 +230,10 @@ just create a new chain that does.
The rejection message is sitting in the virgin queue waiting to be delivered
to the original sender.
>>> len(virgin_queue.files)
>>> messages = get_queue_messages('virgin')
>>> len(messages)
1
>>> item = get_queue_messages('virgin')[0]
>>> print item.msg.as_string()
>>> print messages[0].msg.as_string()
Subject: My first post
From: [email protected]
To: [email protected]
......
......@@ -61,6 +61,6 @@ class IncomingRunner(Runner):
pass
config.db.commit()
# Process the message through the mailing list's start chain.
process(mlist, msg, msgdata, mlist.start_chain)
process(mlist, msg, msgdata, mlist.posting_chain)
# Do not keep this message queued.
return False
......@@ -30,6 +30,6 @@ from mailman.core.runner import Runner
class PipelineRunner(Runner):
def _dispose(self, mlist, msg, msgdata):
# Process the message through the mailing list's pipeline.
process(mlist, msg, msgdata, mlist.pipeline)
process(mlist, msg, msgdata, mlist.posting_pipeline)
# Do not keep this message queued.
return False
......@@ -211,11 +211,12 @@ from: .*@uplinkpro.com
# is that they will get all messages, and they will not have an entry
# in this dictionary.
mlist.topics_userinterest = {}
# The processing chain that messages coming into this list get
# The processing chain that messages posted to this mailing list get
# processed by.
mlist.start_chain = 'built-in'
# The default pipeline to send accepted messages through.
mlist.pipeline = 'built-in'
mlist.posting_chain = 'default-posting-chain'
# The default pipeline to send accepted messages through to the
# mailing list's members.
mlist.posting_pipeline = 'default-posting-pipeline'
def match(self, mailing_list, styles):
"""See `IStyle`."""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment