List styles are a way to name and apply a canned collection of attribute settings. Every style has a name, which must be unique within the context of a specific style manager. There is usually only one global style manager.
Styles also have a priority, which allows you to specify the order in which multiple styles will be applied. A style has a match function which is used to determine whether the style should be applied to a particular mailing list or not. And finally, application of a style to a mailing list can really modify the mailing list any way it wants.
Let’s start with a vanilla mailing list and a default style manager.
>>> from mailman.interfaces.listmanager import IListManager
>>> from zope.component import getUtility
>>> mlist = getUtility(IListManager).create('_xtest@example.com')
>>> from mailman.styles.manager import StyleManager
>>> style_manager = StyleManager()
>>> style_manager.populate()
>>> sorted(style.name for style in style_manager.styles)
['default']
There is a default style which implements the legacy application of list defaults from previous versions of Mailman. This style only matching a mailing list when no other styles match, and it has the lowest priority. The low priority means that it is matched last and if it matches, it is applied last.
>>> default_style = style_manager.get('default')
>>> default_style.name
'default'
>>> default_style.priority
0
>>> sorted(style.name for style in style_manager.styles)
['default']
Given a mailing list, you can ask the style manager to find all the styles that match the list. The registered styles will be sorted by decreasing priority and each style’s match() method will be called in turn. The sorted list of matching styles will be returned – but not applied – by the style manager’s lookup() method.
>>> [style.name for style in style_manager.lookup(mlist)]
['default']
New styles must implement the IStyle interface.
>>> from zope.interface import implements
>>> from mailman.interfaces.styles import IStyle
>>> class TestStyle(object):
... implements(IStyle)
... name = 'test'
... priority = 10
... def apply(self, mailing_list):
... # Just does something very simple.
... mailing_list.msg_footer = 'test footer'
... def match(self, mailing_list, styles):
... # Applies to any test list
... if 'test' in mailing_list.fqdn_listname:
... styles.append(self)
You can register a new style with the style manager.
>>> style_manager.register(TestStyle())
And now if you lookup matching styles, you should find only the new test style. This is because the default style only gets applied when no other styles match the mailing list.
>>> sorted(style.name for style in style_manager.lookup(mlist))
[u'test']
>>> for style in style_manager.lookup(mlist):
... style.apply(mlist)
>>> print mlist.msg_footer
test footer
When multiple styles match a particular mailing list, they are applied in descending order of priority. In other words, a priority zero style would be applied last.
>>> class AnotherTestStyle(TestStyle):
... name = 'another'
... priority = 5
... # Use the base class's match() method.
... def apply(self, mailing_list):
... mailing_list.msg_footer = 'another footer'
>>> mlist.msg_footer = ''
>>> mlist.msg_footer
u''
>>> style_manager.register(AnotherTestStyle())
>>> for style in style_manager.lookup(mlist):
... style.apply(mlist)
>>> print mlist.msg_footer
another footer
You can change the priority of a style, and if you reapply the styles, they will take effect in the new priority order.
>>> style_1 = style_manager.get('test')
>>> style_1.priority = 5
>>> style_2 = style_manager.get('another')
>>> style_2.priority = 10
>>> for style in style_manager.lookup(mlist):
... style.apply(mlist)
>>> print mlist.msg_footer
test footer
You can unregister a style, making it unavailable in the future.
>>> style_manager.unregister(style_2)
>>> sorted(style.name for style in style_manager.lookup(mlist))
[u'test']
If you register a style with the same name as an already registered style, you get an exception.
>>> style_manager.register(TestStyle())
Traceback (most recent call last):
...
DuplicateStyleError: test
If you try to register an object that isn’t a style, you get an exception.
>>> style_manager.register(object())
Traceback (most recent call last):
...
DoesNotImplement: An object does not implement interface
<InterfaceClass mailman.interfaces.styles.IStyle>
If you try to unregister a style that isn’t registered, you get an exception.
>>> style_manager.unregister(style_2)
Traceback (most recent call last):
...
KeyError: u'another'