Composing messages
==================

The ``composer`` module provides
``collective.singing.interfaces.IComposer`` implementations for
assembling mails.

  >>> import collective.dancing.composer

Setup
-----

Let's make an ISubscription and an IChannel implementation for use in
this test:

  >>> from zope import interface, component
  >>> import collective.singing.interfaces
  >>> import collective.singing.message
  >>> from collective.dancing.tests import decodeMessageAsString

  >>> class Channel(object):
  ...     def __init__(self, title, url):
  ...         self.title = title
  ...         self.url = url
  ...         self.queue = collective.singing.message.MessageQueues()
  ...     def absolute_url(self):
  ...         return self.url

  >>> class Subscription(object):
  ...     def __init__(self, channel):
  ...         self.channel = channel
  ...         self.composer_data = {}

HTMLComposer
------------

The ``HTMLComposer`` creates messages of type
``email.Message.Message``.  It expects the subscription's
``IComposerData`` to have an 'email' entry:

  >>> HTMLComposer = collective.dancing.composer.HTMLComposer
  >>> HTMLComposer.schema.names()
  ['email']

The HTMLComposer implements both the ``IComposer`` and the
``IComposerBasedSecret`` interfaces:

  >>> from zope.interface import verify
  >>> verify.verifyClass(collective.singing.interfaces.IComposer,
  ...                    HTMLComposer)
  True
  >>> verify.verifyClass(collective.singing.interfaces.IComposerBasedSecret,
  ...                    HTMLComposer)
  True

That is, we can use it to retrieve a subscription's secret:

  >>> channel = Channel('News for nerds', 'http://my.chann.el')
  >>> subscription = Subscription(channel)
  >>> subscription.composer_data['email'] = u'daniel@domain.tld'
  >>> secret = HTMLComposer.secret(subscription.composer_data)
  >>> isinstance(secret, str)
  True
  >>> subscription.secret = secret

  >>> composer = HTMLComposer()
  
  Lets also try to add some style e.g. font-family for p tags  
  >>> composer.stylesheet = """
  ...     p{font-family:Arial,'Lucida Grande',
  ...     Verdana,Lucida,Helvetica,sans-serif;}
  ...     """

  Test _from_address
  >>> from Products.CMFCore.interfaces import IPropertiesTool
  >>> properties = component.getUtility(IPropertiesTool)
  >>> old_name = properties.email_from_name
  >>> properties.email_from_name = u'Ko\u010dka'.encode('utf8')
  >>> composer._from_address
  '=?utf-8?b?S2/EjWth?= <>'
  >>> properties.email_from_name = old_name

  Rendering

  >>> items = [
  ...     'My bonnie lies over the ocean',
  ...     'My bonnie lies over the sea',
  ...     'My bonnie lies over the ocean',
  ...     'Oh bring back my bonnie to me',
  ...     'Er ... if the cab fare is less than $2.00',] #$ tests bug #300720
  
  >>> items.append('<p>A paragraph with font-family style attribute</p>') \
  ... # tests bug #438003

  >>> msg = composer.render(subscription, items=zip(items, items))
  >>> msg # doctest: +ELLIPSIS
  <collective.singing.message.Message object ...>
  >>> msg.payload # doctest: +ELLIPSIS
  <email.MIMEMultipart.MIMEMultipart instance ...>
  >>> print decodeMessageAsString(msg.payload) # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
  Content-Type: multipart/mixed;...
  Subject: Plone site: News for nerds...
  From: Site Administrator <>...
  To: daniel@domain.tld...
  ...My bonnie lies over the ocean...My bonnie lies over the sea...<p...style...Arial...>A paragraph...

  >>> msg = composer.render_confirmation(subscription) # doctest: +ELLIPSIS
  >>> msg # doctest: +ELLIPSIS
  <collective.singing.message.Message object ...>
  >>> print decodeMessageAsString(msg.payload) # doctest: +ELLIPSIS
  Content-Type: multipart/mixed;...
  Subject: Confirm your subscription with News for nerds...
  From: Site Administrator <>...
  To: daniel@domain.tld...
  ...To confirm your subscription with News for nerds, please click here...

IMailer
-------

The ``composer`` module defines an ``IMailer`` utility that's used to
send out mails.  Let's make sure that it's registered properly, and
that it has the right settings:

  >>> from zope import component
  >>> from zope.sendmail.interfaces import IMailer
  >>> smtp = component.getUtility(IMailer, 'plone.smtp')
  >>> cfg = smtp._fetch_settings()
  >>> cfg['username'], cfg['password'], cfg['hostname'], cfg['port']
  (None, None, 'localhost', 25)


Formatters
==========

HTML formatters
---------------

  >>> from collective.dancing import composer

The CMFDublinCoreHTMLFormatter renders objects providing the CMF
Dublin Core interface:

  >>> from zope.publisher.browser import TestRequest
  >>> request = TestRequest()
  
  >>> print composer.CMFDublinCoreHTMLFormatter(portal['front-page'], request)() \
  ... # doctest: +NORMALIZE_WHITESPACE
  <div>
    <h2><a href="http://nohost/plone/front-page">Welcome to Plone</a></h2>
    <p>Congratulations! You have successfully installed Plone.</p>
  </div>

The PloneCallHTMLFormatter assumes that the item passed to it is a
Plone object that returns its Plone view when called:

  >>> html = composer.PloneCallHTMLFormatter(portal['front-page'], request)()
  >>> '<h1 class="documentFirstHeading">' in html
  True
  >>> 'Welcome to Plone' in html
  True
  >>> 'Congratulations! You have successfully installed Plone' in html
  True


