summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Fadeev <evgeny.fadeev@gmail.com>2012-09-20 11:49:19 -0400
committerEvgeny Fadeev <evgeny.fadeev@gmail.com>2012-09-20 11:49:19 -0400
commit735639fd94be645f8dc13287ef5af0e9f8338f5f (patch)
treed3b50feb9993ddf2e33758550d564bfc0b6480ed
parent7681d6d236524608b58a96fc846fab0f6fff864f (diff)
parente7c932c927ce384de6762d0a4ba33e57ba2aceab (diff)
downloadaskbot-735639fd94be645f8dc13287ef5af0e9f8338f5f.tar.gz
askbot-735639fd94be645f8dc13287ef5af0e9f8338f5f.tar.bz2
askbot-735639fd94be645f8dc13287ef5af0e9f8338f5f.zip
Merge branch 'adolfo-master' into group-messaging
-rw-r--r--askbot/context.py20
-rw-r--r--askbot/mail/__init__.py30
-rw-r--r--askbot/skins/default/templates/email/base_mail.html183
-rw-r--r--askbot/templates/email/welcome_lamson_off.html10
-rw-r--r--askbot/templates/email/welcome_lamson_on.html11
-rw-r--r--askbot/tests/__init__.py1
-rw-r--r--askbot/tests/email_parsing_tests.py25
-rw-r--r--askbot/tests/reply_by_email_tests.py50
8 files changed, 304 insertions, 26 deletions
diff --git a/askbot/context.py b/askbot/context.py
index 1828ae11..48c1cf3b 100644
--- a/askbot/context.py
+++ b/askbot/context.py
@@ -59,14 +59,22 @@ def application_settings(request):
}
if askbot_settings.GROUPS_ENABLED:
- groups = models.Group.objects.exclude_personal()
+
+ def _get_group_url(group):
+ group_slug = slugify(group['name'])
+ return reverse('users_by_group',
+ kwargs={'group_id': group['id'],
+ 'group_slug': group_slug})
+
+
+ global_group = models.tag.get_global_group()
+ groups = models.Group.objects.exclude_personal().exclude(id=global_group.id).order_by('name')
groups = groups.values('id', 'name')
- group_list = []
+ group_list = [{'link': _get_group_url({'name': global_group.name,
+ 'id': global_group.id}),
+ 'name': global_group.name},]
for group in groups:
- group_slug = slugify(group['name'])
- link = reverse('users_by_group',
- kwargs={'group_id': group['id'],
- 'group_slug': group_slug})
+ link = _get_group_url(group)
group_list.append({'name': group['name'], 'link': link})
context['group_list'] = simplejson.dumps(group_list)
diff --git a/askbot/mail/__init__.py b/askbot/mail/__init__.py
index 01ffb6f0..739b147e 100644
--- a/askbot/mail/__init__.py
+++ b/askbot/mail/__init__.py
@@ -11,11 +11,14 @@ from django.forms import ValidationError
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import string_concat
from django.template import Context
+from django.utils.html import strip_tags
from askbot import exceptions
from askbot import const
from askbot.conf import settings as askbot_settings
from askbot.utils import url_utils
from askbot.utils.file_utils import store_file
+
+from bs4 import BeautifulSoup
#todo: maybe send_mail functions belong to models
#or the future API
def prefix_the_subject_line(subject):
@@ -77,6 +80,19 @@ def thread_headers(post, orig_post, update):
return headers
+def clean_html_email(email_body):
+ '''needs more clenup might not work for other email templates
+ that does not use table layout'''
+
+ remove_linejump = lambda s: s.replace('\n', '')
+
+ soup = BeautifulSoup(email_body)
+ table_tds = soup.find('body')
+ phrases = map(lambda s: s.strip(),
+ filter(bool, table_tds.get_text().split('\n')))
+
+ return '\n\n'.join(phrases)
+
def send_mail(
subject_line = None,
body_text = None,
@@ -103,14 +119,14 @@ def send_mail(
try:
assert(subject_line is not None)
subject_line = prefix_the_subject_line(subject_line)
- msg = mail.EmailMessage(
- subject_line,
- body_text,
+ msg = mail.EmailMultiAlternatives(
+ subject_line,
+ clean_html_email(body_text),
from_email,
recipient_list,
headers = headers
)
- msg.content_subtype = 'html'
+ msg.attach_alternative(body_text, "text/html")
msg.send()
if related_object is not None:
assert(activity_type is not None)
@@ -237,7 +253,7 @@ def bounce_email(
headers = {}
if reply_to:
headers['Reply-To'] = reply_to
-
+
send_mail(
recipient_list = (email,),
subject_line = 'Re: ' + subject,
@@ -294,7 +310,7 @@ def process_parts(parts, reply_code = None):
"""Process parts will upload the attachments and parse out the
body, if body is multipart. Secondly - links to attachments
will be added to the body of the question.
- Returns ready to post body of the message and the list
+ Returns ready to post body of the message and the list
of uploaded files.
"""
body_markdown = ''
@@ -312,7 +328,7 @@ def process_parts(parts, reply_code = None):
stored_files.append(stored_file)
body_markdown += markdown
- #if the response separator is present -
+ #if the response separator is present -
#split the body with it, and discard the "so and so wrote:" part
if reply_code:
signature = extract_user_signature(body_markdown, reply_code)
diff --git a/askbot/skins/default/templates/email/base_mail.html b/askbot/skins/default/templates/email/base_mail.html
new file mode 100644
index 00000000..dfd252f2
--- /dev/null
+++ b/askbot/skins/default/templates/email/base_mail.html
@@ -0,0 +1,183 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
+ <title>{% block title%}{%endblock%}</title>
+ <style type="text/css">
+ /* Based on The MailChimp Reset INLINE: Yes. */
+ /* Client-specific Styles */
+ #outlook a {padding:0;} /* Force Outlook to provide a "view in browser" menu link. */
+ body{width:100% !important; -webkit-text-size-adjust:100%; -ms-text-size-adjust:100%; margin:0; padding:0; font-family:arial, sans-serif;font-size:13px;}
+ /* Prevent Webkit and Windows Mobile platforms from changing default font sizes.*/
+ .ExternalClass {width:100%;} /* Force Hotmail to display emails at full width */
+ .ExternalClass, .ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td, .ExternalClass div {line-height: 100%;}
+ /* Forces Hotmail to display normal line spacing. More on that: http://www.emailonacid.com/forum/viewthread/43/ */
+ #backgroundTable {margin:0; padding:0; width:100% !important; line-height: 100% !important;}
+ /* End reset */
+
+ /* Some sensible defaults for images
+ Bring inline: Yes. */
+ img {outline:none; text-decoration:none; -ms-interpolation-mode: bicubic;}
+ a img {border:none;}
+ .image_fix {display:block;}
+
+ /* Yahoo paragraph fix
+ Bring inline: Yes. */
+ p {margin: 1em 0;}
+
+ /* Hotmail header color reset
+ Bring inline: Yes. */
+ h1, h2, h3, h4, h5, h6 {color: black !important;}
+
+ h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {color: blue !important;}
+
+ h1 a:active, h2 a:active, h3 a:active, h4 a:active, h5 a:active, h6 a:active {
+ color: red !important; /* Preferably not the same color as the normal header link color. There is limited support for psuedo classes in email clients, this was added just for good measure. */
+ }
+
+ h1 a:visited, h2 a:visited, h3 a:visited, h4 a:visited, h5 a:visited, h6 a:visited {
+ color: purple !important; /* Preferably not the same color as the normal header link color. There is limited support for psuedo classes in email clients, this was added just for good measure. */
+ }
+
+ /* Outlook 07, 10 Padding issue fix
+ Bring inline: No.*/
+ table td {border-collapse: collapse;}
+
+ /* Remove spacing around Outlook 07, 10 tables
+ Bring inline: Yes */
+ table { border-collapse:collapse; mso-table-lspace:0pt; mso-table-rspace:0pt; }
+
+ /* Styling your links has become much simpler with the new Yahoo. In fact, it falls in line with the main credo of styling in email and make sure to bring your styles inline. Your link colors will be uniform across clients when brought inline.
+ Bring inline: Yes. */
+ a {color: orange;}
+
+
+ /***************************************************
+ ****************************************************
+ MOBILE TARGETING
+ ****************************************************
+ ***************************************************/
+ @media only screen and (max-device-width: 480px) {
+ /* Part one of controlling phone number linking for mobile. */
+ a[href^="tel"], a[href^="sms"] {
+ text-decoration: none;
+ color: blue; /* or whatever your want */
+ pointer-events: none;
+ cursor: default;
+ }
+
+ .mobile_link a[href^="tel"], .mobile_link a[href^="sms"] {
+ text-decoration: default;
+ color: orange !important;
+ pointer-events: auto;
+ cursor: default;
+ }
+
+ }
+
+ /* More Specific Targeting */
+
+ @media only screen and (min-device-width: 768px) and (max-device-width: 1024px) {
+ /* You guessed it, ipad (tablets, smaller screens, etc) */
+ /* repeating for the ipad */
+ a[href^="tel"], a[href^="sms"] {
+ text-decoration: none;
+ color: blue; /* or whatever your want */
+ pointer-events: none;
+ cursor: default;
+ }
+
+ .mobile_link a[href^="tel"], .mobile_link a[href^="sms"] {
+ text-decoration: default;
+ color: orange !important;
+ pointer-events: auto;
+ cursor: default;
+ }
+ }
+
+ @media only screen and (-webkit-min-device-pixel-ratio: 2) {
+ /* Put your iPhone 4g styles in here */
+ }
+
+ /* Android targeting */
+ @media only screen and (-webkit-device-pixel-ratio:.75){
+ /* Put CSS for low density (ldpi) Android layouts in here */
+ }
+ @media only screen and (-webkit-device-pixel-ratio:1){
+ /* Put CSS for medium density (mdpi) Android layouts in here */
+ }
+ @media only screen and (-webkit-device-pixel-ratio:1.5){
+ /* Put CSS for high density (hdpi) Android layouts in here */
+ }
+ /* end Android targeting */
+ </style>
+
+ <!-- Targeting Windows Mobile -->
+ <!--[if IEMobile 7]>
+ <style type="text/css">
+
+ </style>
+ <![endif]-->
+
+ <!-- ***********************************************
+ ****************************************************
+ END MOBILE TARGETING
+ ****************************************************
+ ************************************************ -->
+
+ <!--[if gte mso 9]>
+ <style>
+ /* Target Outlook 2007 and 2010 */
+ </style>
+ <![endif]-->
+</head>
+<body>
+<table cellpadding="0" cellspacing="0" border="0" id="backgroundTable">
+ <tr>
+ <td>
+ <table border="0" align="center" cellspacing="0" cellpadding="0" style="background-color:#E7E8E8;">
+ <tr height="20">
+ <td valign="top">
+ </td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <table border="0" align="center" cellspacing="0" cellpadding="0" style="background-color: #fff;" width=80%>
+ <tr>
+ <td valign="top">
+ <table border="0" align="center" cellspacing="0" cellpadding="0" width=80%>
+ <tr>
+ <td valign="top">
+ <h1>{%block headline%}{%endblock%}</h1>
+ </td>
+ </tr>
+ <tr>
+ <td valign="top">
+ {%block content%}
+ {%endblock%}
+ </td>
+ </tr>
+ <tr>
+ <td valign="top" class="footer">
+ <hr>
+ {%block footer%}
+ {%endblock%}
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <tr height="20">
+ <td valign="top">
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+</body>
+</html>
diff --git a/askbot/templates/email/welcome_lamson_off.html b/askbot/templates/email/welcome_lamson_off.html
index d1b9854d..c5c9384c 100644
--- a/askbot/templates/email/welcome_lamson_off.html
+++ b/askbot/templates/email/welcome_lamson_off.html
@@ -1,11 +1,15 @@
+{% extends "email/base_mail.html" %}
{% import "email/macros.html" as macros %}
{# site_name - is short name of the site, email_code - address portion
of the reply email used for this message, we scan to the last appearance
of the email code to detect the response signature that will appear under #}
-<p style="{{ macros.heading_style() }}">
- {% trans %}Welcome to {{ site_name }}!{% endtrans %}
-</p>
+{%block title%} {% trans %}Welcome to {{ site_name }}!{% endtrans %} {%endblock%}
+{%block headline%} {% trans %}Welcome to {{ site_name }}!{% endtrans %} {%endblock%}
+{%block content%}
<p>
{% trans %}We look forward to your Questions!{% endtrans %}
</p>
+{%endblock%}
+{%block footer %}
{% include "email/footer.html" %}
+{%endblock%}
diff --git a/askbot/templates/email/welcome_lamson_on.html b/askbot/templates/email/welcome_lamson_on.html
index 0efa7096..3e47f44f 100644
--- a/askbot/templates/email/welcome_lamson_on.html
+++ b/askbot/templates/email/welcome_lamson_on.html
@@ -1,15 +1,20 @@
+{% extends "email/base_mail.html"%}
{% import "email/macros.html" as macros %}
+{%block title%}{% trans %}Welcome to {{ site_name }}!{% endtrans %}{% endblock %}
+{%block headline%}{% trans %}Welcome to {{ site_name }}!{% endtrans %}{% endblock %}
+
+{%block content %}
{# site_name - is short name of the site, email_code - address portion
of the reply email used for this message, we scan to the last appearance
of the email code to detect the response signature that will appear under #}
-<p style="{{ macros.heading_style() }}">
- {% trans %}Welcome to {{ site_name }}!{% endtrans %}
-</p>
<p>
{% trans %}Important: <em>Please reply</em> to this message, without editing it. We need this to determine your email signature and that the email address is valid and was typed correctly.{% endtrans %}
</p>
<p>
{% trans %}Until we receive the response from you, you will not be able ask or answer questions on {{ site_name }} by email.{% endtrans %}
</p>
+{% endblock %}
+{%block footer %}
{% include "email/footer.html" %}
<p style="{{ macros.fine_print_style() }}">{{ email_code }}</p>{# important #}
+{% endblock %}
diff --git a/askbot/tests/__init__.py b/askbot/tests/__init__.py
index 4cecf930..8a018ea1 100644
--- a/askbot/tests/__init__.py
+++ b/askbot/tests/__init__.py
@@ -15,6 +15,7 @@ from askbot.tests.markup_test import *
from askbot.tests.post_model_tests import *
from askbot.tests.thread_model_tests import *
from askbot.tests.reply_by_email_tests import *
+from askbot.tests.email_parsing_tests import *
from askbot.tests.widget_tests import *
from askbot.tests.category_tree_tests import CategoryTreeTests
from askbot.tests.user_model_tests import UserModelTests
diff --git a/askbot/tests/email_parsing_tests.py b/askbot/tests/email_parsing_tests.py
new file mode 100644
index 00000000..905bff0a
--- /dev/null
+++ b/askbot/tests/email_parsing_tests.py
@@ -0,0 +1,25 @@
+from django.conf import settings as django_settings
+from askbot.skins.loaders import get_template
+from django.template import Context
+from askbot import mail
+from askbot import models
+from askbot.tests import utils
+
+class EmailParseTests(utils.AskbotTestCase):
+
+ def setUp(self):
+ self.template_name = 'email/welcome_lamson_on.html'
+ self.context = {'site_name': 'askbot.com',
+ 'email_code': 'DwFwndQty'}
+ template = get_template(self.template_name)
+ self.rendered_template = template.render(Context(self.context))
+ self.expected_output = 'Welcome to askbot.com!\n\nImportant: Please reply to this message, without editing it. We need this to determine your email signature and that the email address is valid and was typed correctly.\n\nUntil we receive the response from you, you will not be able ask or answer questions on askbot.com by email.\n\nSincerely,askbot.com Administrator\n\nDwFwndQty'
+
+ def test_clean_email_body(self):
+ cleaned_body = mail.clean_html_email(self.rendered_template)
+ print "EXPECTED BODY"
+ print self.expected_output
+ print '=================================================='
+ print cleaned_body
+ print "CLEANED BODY"
+ self.assertEquals(cleaned_body, self.expected_output)
diff --git a/askbot/tests/reply_by_email_tests.py b/askbot/tests/reply_by_email_tests.py
index 698662fc..9248fc50 100644
--- a/askbot/tests/reply_by_email_tests.py
+++ b/askbot/tests/reply_by_email_tests.py
@@ -11,9 +11,9 @@ TEST_CONTENT = 'Test content'
TEST_LONG_CONTENT = 'Test content' * 10
class MockPart(object):
- def __init__(self, body):
+ def __init__(self, body, content_type='text/plain'):
self.body = body
- self.content_encoding = {'Content-Type':('text/plain',)}
+ self.content_encoding = {'Content-Type':(content_type,)}
class MockMessage(dict):
@@ -34,13 +34,19 @@ class MockMessage(dict):
self._body = content
self._part = MockPart(content)
+ self._alternatives = []
def body(self):
return self._body
+ def attach_alternative(self, content, content_type):
+ assert content is not None
+ assert content_type is not None
+ self._alternatives.append(MockPart(content, content_type))
+
def walk(self):
"""todo: add real file attachment"""
- return [self._part]
+ return [self._part] + self._alternatives
class ReplyAddressModelTests(AskbotTestCase):
@@ -115,7 +121,7 @@ class ReplyAddressModelTests(AskbotTestCase):
self.assertEquals(post.post_type, "comment")
self.assertEquals(post.text, TEST_CONTENT)
self.assertEquals(self.answer.comments.count(), 2)
-
+
def test_create_question_comment_reply(self):
result = ReplyAddress.objects.create_new(
@@ -136,10 +142,11 @@ class ReplyAddressModelTests(AskbotTestCase):
self.assertEquals(post.text, TEST_LONG_CONTENT)
class EmailSignatureDetectionTests(AskbotTestCase):
-
+
def setUp(self):
self.u1 = self.create_user('user1', status = 'a')
self.u2 = self.create_user('user2', status = 'a')
+ self.u3 = self.create_user('user3', status = 'a')
def test_detect_signature_in_response(self):
question = self.post_question(user = self.u1)
@@ -162,7 +169,7 @@ class EmailSignatureDetectionTests(AskbotTestCase):
)
PROCESS(msg, address = reply_token.address)
- signature = self.reload_object(self.u2).email_signature
+ signature = self.reload_object(self.u2).email_signature
self.assertEqual(signature, 'Yours Truly')
def test_detect_signature_in_welcome_response(self):
@@ -184,5 +191,34 @@ class EmailSignatureDetectionTests(AskbotTestCase):
address = reply_token.address
)
- signature = self.reload_object(self.u2).email_signature
+ signature = self.reload_object(self.u2).email_signature
+ self.assertEqual(signature, 'Yours Truly')
+
+ def test_detect_signature_in_html_welcome_response(self):
+ reply_token = ReplyAddress.objects.create_new(
+ user = self.u3,
+ reply_action = 'validate_email'
+ )
+ self.u3.email_signature = ''
+ self.u3.save()
+ signature = 'Yours Truly'
+
+ msg = MockMessage(
+ 'some text',
+ self.u3.email,
+ signature = signature,
+ response_code = reply_token.address
+ )
+
+ html_message = '<b>some text</b>' + signature
+
+ msg.attach_alternative(html_message, 'text/html')
+ VALIDATE_EMAIL(
+ msg,
+ address = reply_token.address
+ )
+
+ signature = self.reload_object(self.u3).email_signature
self.assertEqual(signature, 'Yours Truly')
+
+