summaryrefslogtreecommitdiff
path: root/service
diff options
context:
space:
mode:
authorAlexandre Pretto <anunes@thoughtworks.com>2015-01-08 18:48:48 -0200
committerPixpoa pairing <pixpoapairing@pixelated-project.org>2015-01-08 18:53:40 -0200
commitb06b794de750d972a9dff9165e203815b79c5b62 (patch)
treeb34414d27dff67ce94c7c15f419f25b9dcfae0d1 /service
parent266b2f10f848902b31e0dfd05696dc2f5618bf2e (diff)
#157: mail api to return htmlBody and textPlainBody so the UI doesnt have to parse the multipart mails anymore
Diffstat (limited to 'service')
-rw-r--r--service/pixelated/adapter/model/mail.py54
-rw-r--r--service/pixelated/adapter/search/__init__.py2
-rw-r--r--service/test/unit/adapter/mail_test.py35
3 files changed, 53 insertions, 38 deletions
diff --git a/service/pixelated/adapter/model/mail.py b/service/pixelated/adapter/model/mail.py
index 4b128d8c..98533a74 100644
--- a/service/pixelated/adapter/model/mail.py
+++ b/service/pixelated/adapter/model/mail.py
@@ -25,7 +25,6 @@ import pixelated.support.date
from email.MIMEMultipart import MIMEMultipart
from pycryptopp.hash import sha256
import re
-import base64
from pixelated.support.functional import compact
@@ -65,7 +64,13 @@ class Mail(object):
mime = MIMEMultipart()
for key, value in self.headers.items():
mime[str(key)] = str(value)
- mime.attach(MIMEText(self.body, 'plain', self._charset()))
+
+ try:
+ body_to_use = self.body
+ except AttributeError:
+ body_to_use = self.text_plain_body
+
+ mime.attach(MIMEText(body_to_use, 'plain', self._charset()))
self._mime = mime
return mime
@@ -193,6 +198,7 @@ class InputMail(Mail):
class PixelatedMail(Mail):
+
@staticmethod
def from_soledad(fdoc, hdoc, bdoc, parts=None, soledad_querier=None):
mail = PixelatedMail()
@@ -205,24 +211,33 @@ class PixelatedMail(Mail):
mail._mime = None
return mail
+ def _decode_part(self, part):
+ encoding = part['headers'].get('Content-Transfer-Encoding', '')
+
+ decoding_map = {
+ 'quoted-printable': lambda content: unicode(content.decode('quopri')),
+ 'base64': lambda content: content.decode('base64').decode('utf-8')
+ }
+ if encoding:
+ return decoding_map[encoding](part['content'])
+ return part['content']
+
@property
- def body(self):
- if self.parts and len(self.parts['alternatives']) > 1:
- body = ''
- for alternative in self.parts['alternatives']:
- body += '--' + self.boundary + '\n'
- for header, value in alternative['headers'].items():
- body += '%s: %s\n' % (header, value)
- body += '\n'
- body += alternative['content']
- body += '\n'
- body += '--' + self.boundary + '--'
- return body
+ def alternatives(self):
+ return self.parts.get('alternatives')
+
+ @property
+ def text_plain_body(self):
+ if self.parts and len(self.alternatives) == 1:
+ return self._decode_part(self.alternatives[0])
else:
- if self.parts and self.parts['alternatives'][0]['headers'].get('Content-Transfer-Encoding', '') == 'base64':
- return unicode(base64.b64decode(self.parts['alternatives'][0]['content']), 'utf-8')
- else:
- return self.bdoc.content['raw']
+ return self.bdoc.content['raw'] # plain
+
+ @property
+ def html_body(self):
+ if self.parts and len(self.alternatives) > 1:
+ html_part = [e for e in self.alternatives if re.match('text/html', e['headers']['Content-Type'])][0]
+ return self._decode_part(html_part)
@property
def headers(self):
@@ -352,7 +367,8 @@ class PixelatedMail(Mail):
'tags': list(self.tags),
'status': list(self.status),
'security_casing': self.security_casing,
- 'body': self.body,
+ 'textPlainBody': self.text_plain_body,
+ 'htmlBody': self.html_body,
'mailbox': self.mailbox_name.lower(),
'attachments': self.parts['attachments'] if self.parts else []}
dict_mail['replying'] = {'single': None, 'all': {'to-field': [], 'cc-field': []}}
diff --git a/service/pixelated/adapter/search/__init__.py b/service/pixelated/adapter/search/__init__.py
index bbefc487..a5eb4ef2 100644
--- a/service/pixelated/adapter/search/__init__.py
+++ b/service/pixelated/adapter/search/__init__.py
@@ -128,7 +128,7 @@ class SearchEngine(object):
'cc': u','.join(header.get('cc', [''])),
'bcc': u','.join(header.get('bcc', [''])),
'tag': u','.join(unique(tags)),
- 'body': unicode(mdict['body']),
+ 'body': unicode(mdict['textPlainBody']),
'ident': unicode(mdict['ident']),
'flags': unicode(','.join(unique(mail.flags))),
'raw': unicode(mail.raw)
diff --git a/service/test/unit/adapter/mail_test.py b/service/test/unit/adapter/mail_test.py
index e0b7a498..c35bdb62 100644
--- a/service/test/unit/adapter/mail_test.py
+++ b/service/test/unit/adapter/mail_test.py
@@ -95,7 +95,10 @@ class TestPixelatedMail(unittest.TestCase):
_dict = mail.as_dict()
- self.assertEquals(_dict, {'body': 'body',
+ self.maxDiff = None
+
+ self.assertEquals(_dict, {'htmlBody': None,
+ 'textPlainBody': 'body',
'header': {
'date': dateparser.parse(hdoc.content['date']).isoformat(),
'from': 'someone@pixelated.org',
@@ -143,21 +146,20 @@ class TestPixelatedMail(unittest.TestCase):
parts['alternatives'].append({'content': 'blablabla', 'headers': {'Content-Type': 'text/plain'}})
parts['alternatives'].append({'content': '<p>blablabla</p>', 'headers': {'Content-Type': 'text/html'}})
- mail = PixelatedMail.from_soledad(None, None, None, parts=parts, soledad_querier=None)
+ mail = PixelatedMail.from_soledad(None, None, self._create_bdoc(raw='blablabla'), parts=parts, soledad_querier=None)
- self.assertRegexpMatches(mail.body, '^--' + mail.boundary + '\n.*')
- self.assertRegexpMatches(mail.body, '\nContent-Type: text/html\n\n<p>blablabla</p>\n')
- self.assertRegexpMatches(mail.body, '\nContent-Type: text/plain\n\nblablabla\n')
- self.assertRegexpMatches(mail.body, '.*--' + mail.boundary + '--$')
+ self.assertRegexpMatches(mail.html_body, '^<p>blablabla</p>$')
+ self.assertRegexpMatches(mail.text_plain_body, '^blablabla$')
def test_percent_character_is_allowed_on_body(self):
parts = {'alternatives': [], 'attachments': []}
parts['alternatives'].append({'content': '100% happy with percentage symbol', 'headers': {'Content-Type': 'text/plain'}})
parts['alternatives'].append({'content': '<p>100% happy with percentage symbol</p>', 'headers': {'Content-Type': 'text/html'}})
- mail = PixelatedMail.from_soledad(None, None, None, parts=parts, soledad_querier=None)
+ mail = PixelatedMail.from_soledad(None, None, self._create_bdoc(raw="100% happy with percentage symbol"), parts=parts, soledad_querier=None)
- self.assertRegexpMatches(mail.body, '([\s\S]*100%){2}')
+ self.assertRegexpMatches(mail.text_plain_body, '([\s\S]*100%)')
+ self.assertRegexpMatches(mail.html_body, '([\s\S]*100%)')
def test_clean_line_breaks_on_address_headers(self):
many_recipients = 'One <one@mail.com>,\nTwo <two@mail.com>, Normal <normal@mail.com>,\nalone@mail.com'
@@ -175,25 +177,22 @@ class TestPixelatedMail(unittest.TestCase):
self.assertNotIn(',', address)
self.assertEquals(4, len(mail.headers[header_label]))
- def test_content_type_is_read_from_headers_for_plain_mail_when_converted_to_raw(self):
- fdoc, hdoc, bdoc = test_helper.leap_mail(flags=['\\Recent'], body=u'some umlaut \xc3', extra_headers={'Content-Type': 'text/plain; charset=ISO-8859-1'})
- hdoc.content['headers']['Subject'] = 'The subject'
- hdoc.content['headers']['From'] = 'me@pixelated.org'
- mail = PixelatedMail.from_soledad(fdoc, hdoc, bdoc, soledad_querier=self.querier)
-
- mail.raw
-
def test_that_body_understands_base64(self):
body = u'bl\xe1'
- encoded_body = unicode(base64.b64encode(body.encode('utf-8')))
+ encoded_body = unicode(body.encode('utf-8').encode('base64'))
fdoc, hdoc, bdoc = test_helper.leap_mail()
parts = {'alternatives': []}
parts['alternatives'].append({'content': encoded_body, 'headers': {'Content-Transfer-Encoding': 'base64'}})
mail = PixelatedMail.from_soledad(fdoc, hdoc, bdoc, soledad_querier=self.querier, parts=parts)
- self.assertEquals(body, mail.body)
+ self.assertEquals(body, mail.text_plain_body)
+ def _create_bdoc(self, raw):
+ class FakeBDoc:
+ def __init__(self, raw):
+ self.content = {'raw': raw}
+ return FakeBDoc(raw)
class InputMailTest(unittest.TestCase):
mail_dict = lambda x: {