Merge branch 'newsletters'

* newsletters:
  Sending email for the first time users get a newsletter.
  Finishing up the newsletters dialog. Perhaps an intro email on first newsletter?
  Stubbing in email newsletters dialog. Needs email and setup instructions.
This commit is contained in:
Samuel Clay 2016-06-30 11:23:24 -07:00
commit f5a56f3ffd
12 changed files with 212 additions and 19 deletions

View file

@ -5,12 +5,14 @@ from cgi import escape
from django.db import models
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.core.mail import EmailMultiAlternatives
from django.core.urlresolvers import reverse
from django.conf import settings
from django.template.loader import render_to_string
from django.utils.html import linebreaks
from apps.rss_feeds.models import Feed, MStory, MFetchHistory
from apps.reader.models import UserSubscription, UserSubscriptionFolders
from apps.profile.models import Profile
from apps.profile.models import Profile, MSentEmail
from utils import log as logging
from utils.story_functions import linkify
from utils.scrubber import Scrubber
@ -18,12 +20,12 @@ from utils.scrubber import Scrubber
class EmailNewsletter:
def receive_newsletter(self, params):
user = self.user_from_email(params['recipient'])
user = self._user_from_email(params['recipient'])
if not user:
return
sender_name, sender_username, sender_domain = self.split_sender(params['from'])
feed_address = self.feed_address(user, "%s@%s" % (sender_username, sender_domain))
sender_name, sender_username, sender_domain = self._split_sender(params['from'])
feed_address = self._feed_address(user, "%s@%s" % (sender_username, sender_domain))
usf = UserSubscriptionFolders.objects.get(user=user)
usf.add_folder('', 'Newsletters')
@ -40,6 +42,7 @@ class EmailNewsletter:
logging.user(user, "~FCCreating newsletter feed: ~SB%s" % (feed))
r = redis.Redis(connection_pool=settings.REDIS_PUBSUB_POOL)
r.publish(user.username, 'reload:%s' % feed.pk)
self._check_if_first_newsletter(user)
if feed.feed_title != sender_name:
feed.feed_title = sender_name
@ -61,7 +64,7 @@ class EmailNewsletter:
plain_story_content = self.get_content(params, force_plain=True)
if len(plain_story_content) > len(story_content):
story_content = plain_story_content
story_content = self.clean_content(story_content)
story_content = self._clean_content(story_content)
story_params = {
"story_feed_id": feed.pk,
"story_date": datetime.datetime.fromtimestamp(int(params['timestamp'])),
@ -83,15 +86,54 @@ class EmailNewsletter:
usersub.needs_unread_recalc = True
usersub.save()
self.publish_to_subscribers(feed)
self._publish_to_subscribers(feed)
MFetchHistory.add(feed_id=feed.pk, fetch_type='push')
logging.user(user, "~FCNewsletter feed story: ~SB%s~SN / ~SB%s" % (story.story_title, feed))
return story
def _check_if_first_newsletter(self, user, force=False):
if not user.email:
return
subs = UserSubscription.objects.filter(user=user)
found_newsletter = False
for sub in subs:
if sub.feed.is_newsletter:
found_newsletter = True
break
if not found_newsletter and not force:
return
params = dict(receiver_user_id=user.pk, email_type='first_newsletter')
try:
sent_email = MSentEmail.objects.get(**params)
if not force:
# Return if email already sent
return
except MSentEmail.DoesNotExist:
sent_email = MSentEmail.objects.create(**params)
text = render_to_string('mail/email_first_newsletter.txt', {})
html = render_to_string('mail/email_first_newsletter.xhtml', {})
subject = "Your email newsletters are now being sent to NewsBlur"
msg = EmailMultiAlternatives(subject, text,
from_email='NewsBlur <%s>' % settings.HELLO_EMAIL,
to=['%s <%s>' % (user, user.email)])
msg.attach_alternative(html, "text/html")
msg.send(fail_silently=True)
logging.user(user, "~BB~FM~SBSending first newsletter email to: %s" % user.email)
<<<<<<< HEAD
def _user_from_email(self, email):
tokens = re.search('(\w+)\+(\w+)@newsletters.newsblur.com', email)
=======
def user_from_email(self, email):
tokens = re.search('(\w+)[\+\-\.](\w+)@newsletters.newsblur.com', email)
>>>>>>> master
if not tokens:
return
@ -106,10 +148,10 @@ class EmailNewsletter:
return profile.user
def feed_address(self, user, sender):
def _feed_address(self, user, sender):
return 'newsletter:%s:%s' % (user.pk, sender)
def split_sender(self, sender):
def _split_sender(self, sender):
tokens = re.search('(.*?) <(.*?)@(.*?)>', sender)
if not tokens:
@ -121,7 +163,7 @@ class EmailNewsletter:
return sender_name, sender_username, sender_domain
def get_content(self, params, force_plain=False):
def _get_content(self, params, force_plain=False):
if 'body-enriched' in params and not force_plain:
return params['body-enriched']
if 'body-html' in params and not force_plain:
@ -131,13 +173,13 @@ class EmailNewsletter:
if 'body-plain' in params:
return linkify(linebreaks(params['body-plain']))
def clean_content(self, content):
def _clean_content(self, content):
scrubber = Scrubber()
content = scrubber.scrub(content)
content = content.replace('!important', '')
return content
def publish_to_subscribers(self, feed):
def _publish_to_subscribers(self, feed):
try:
r = redis.Redis(connection_pool=settings.REDIS_PUBSUB_POOL)
listeners_count = r.publish(str(feed.pk), 'story:new')

View file

@ -124,7 +124,7 @@ a img {
right: 0;
z-index: 1;
overflow: hidden;
width: 180px;
width: 188px;
margin: 0 24px 32px 0;
}
@ -6554,7 +6554,7 @@ form.opml_import_form input {
/* ================== */
.NB-modules-center {
margin: 24px 204px 0 0px;
margin: 24px 212px 0 0px;
padding: 0 24px;
max-width: 500px;
float: left;
@ -7267,6 +7267,10 @@ form.opml_import_form input {
background: transparent url('/media/embed/icons/circular/menu_icn_goodies.png') no-repeat 0 1px;
background-size: 18px;
}
.NB-menu-manage .NB-menu-manage-newsletters .NB-menu-manage-image {
background: transparent url('/media/embed/icons/circular/menu_icn_mail.png') no-repeat 0 0;
background-size: 18px;
}
.NB-menu-manage .NB-menu-manage-import .NB-menu-manage-image {
background: transparent url('/media/embed/icons/circular/nav_icn_global.png') no-repeat 2px 2px;
background-size: 14px;
@ -9263,6 +9267,35 @@ form.opml_import_form input {
height: 96px;
}
/* ===================== */
/* = Newsletters Modal = */
/* ===================== */
.NB-modal-newsletters fieldset {
margin: 32px 0;
}
.NB-modal-newsletters .NB-modal-title .NB-icon {
background: transparent url('/media/embed/icons/circular/g_modal_mail.png');
background-size: 28px;
}
.NB-modal-newsletters .NB-newsletters-email {
margin: 0 0 0 24px;
font-size: 18px;
width: 80%;
}
.NB-modal-newsletters p {
margin-left: 24px;
font-size: 14px;
}
.NB-modal-newsletters .NB-newsletters-gmail {
width: 700px;
margin: 0 auto;
display: block;
padding: 12px;
border: 1px solid #F0F0F0;
}
/* ================= */
/* = Goodies Modal = */
/* ================= */

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 865 B

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 KiB

View file

@ -3009,6 +3009,10 @@
NEWSBLUR.goodies = new NEWSBLUR.ReaderGoodies();
},
open_newsletters_modal: function() {
NEWSBLUR.newsletters = new NEWSBLUR.ReaderNewsletters();
},
open_preferences_modal: function() {
NEWSBLUR.preferences = new NEWSBLUR.ReaderPreferences();
},
@ -3164,6 +3168,10 @@
$.make('div', { className: 'NB-menu-manage-image' }),
$.make('div', { className: 'NB-menu-manage-title' }, 'Goodies &amp; Mobile Apps')
]),
$.make('li', { className: 'NB-menu-item NB-menu-manage-newsletters' }, [
$.make('div', { className: 'NB-menu-manage-image' }),
$.make('div', { className: 'NB-menu-manage-title' }, 'Email Newsletters')
]),
$.make('li', { className: 'NB-menu-item NB-menu-manage-import' }, [
$.make('div', { className: 'NB-menu-manage-image' }),
$.make('div', { className: 'NB-menu-manage-title' }, 'Import or upload sites')
@ -5888,6 +5896,14 @@
});
}
});
$.targetIs(e, { tagSelector: '.NB-menu-manage-newsletters' }, function($t, $p){
e.preventDefault();
if (!$t.hasClass('NB-disabled')) {
$.modal.close(function() {
self.open_newsletters_modal();
});
}
});
$.targetIs(e, { tagSelector: '.NB-menu-manage-import' }, function($t, $p){
e.preventDefault();
if (!$t.hasClass('NB-disabled')) {

View file

@ -0,0 +1,75 @@
NEWSBLUR.ReaderNewsletters = function(options) {
var defaults = {
'width': 800
};
this.options = $.extend({}, defaults, options);
this.model = NEWSBLUR.assets;
this.runner();
};
NEWSBLUR.ReaderNewsletters.prototype = new NEWSBLUR.Modal;
NEWSBLUR.ReaderNewsletters.prototype.constructor = NEWSBLUR.ReaderNewsletters;
_.extend(NEWSBLUR.ReaderNewsletters.prototype, {
runner: function() {
this.make_modal();
this.open_modal(_.bind(function() {
$('.NB-newsletters-email').click();
}, this));
this.$modal.bind('click', $.rescope(this.handle_click, this));
},
make_modal: function() {
var self = this;
var email = NEWSBLUR.Globals.username + "-" + NEWSBLUR.Globals.secret_token + "@newsletters.newsblur.com";
this.$modal = $.make('div', { className: 'NB-modal-newsletters NB-modal' }, [
$.make('h2', { className: 'NB-modal-title' }, [
$.make('div', { className: 'NB-icon' }),
'Email Newsletters',
$.make('div', { className: 'NB-icon-dropdown' })
]),
$.make('fieldset', [
$.make('legend', 'Forwarding email address')
]),
$.make('div', { className: 'NB-newsletters-group' }, [
$.make('input', { type: 'text', value: email, className: 'NB-newsletters-email' })
]),
$.make('fieldset', [
$.make('legend', 'Setup instructions')
]),
$.make('div', { className: 'NB-newsletters-group' }, [
$.make('p', 'To read your email newsletters in NewsBlur, forward your newsletters to your custom email address shown above.'),
$.make('p', [
'In Gmail, go to ',
$.make('b', 'Settings &gt; Forwarding'),
' and click on ',
$.make('b', 'Add a forwarding address'),
'. Add your custom NewsBlur email address.'
]),
$.make('p', 'Gmail will walk you through confirming the email address. You\'ll want to come back to NewsBlur and look for the confirmation email under the "Newsletters" folder.'),
$.make('p', 'Next, create a filter with all of your newsletters so that they forward to the custom address on NewsBlur.'),
$.make('img', { src: NEWSBLUR.Globals.MEDIA_URL + "/img/reader/newsletters_gmail.png", className: 'NB-newsletters-gmail' })
])
]);
},
// ===========
// = Actions =
// ===========
handle_click: function(elem, e) {
var self = this;
$.targetIs(e, { tagSelector: '.NB-newsletters-email' }, function($t, $p) {
e.preventDefault();
$t.select();
});
}
});

View file

@ -218,6 +218,8 @@ NEWSBLUR.Views.FeedList = Backbone.View.extend({
NEWSBLUR.reader.open_account_modal({'animate_email': true});
} else if (next == 'goodies') {
NEWSBLUR.reader.open_goodies_modal();
} else if (next == 'newsletters') {
NEWSBLUR.reader.open_newsletters_modal();
} else if (next == 'friends') {
NEWSBLUR.reader.open_friends_modal();
} else if (next == 'account') {

View file

@ -0,0 +1,9 @@
{% extends "mail/email_base.txt" %}
{% block body %}Your email newsletters are now being sent to NewsBlur
You have just received your first email newsletter in NewsBlur. You can find your newsletters under the Newsletters folder.
Feel free to rename newsletters to better fit their name. Sometimes newsletters come in as names when they should be the name of the organization. Easy to change!
You can also move and delete newsletters just as if they were independent feeds. Training evne works on newsletters, so you can hide the newsletters you don't want to read while highlighting the ones you do.{% endblock body %}

View file

@ -0,0 +1,10 @@
{% extends "mail/email_base.xhtml" %}
{% load utils_tags %}
{% block body %}
<p style="font-size: 24px; color:#555555; margin-top: 18px;margin-bottom: 10px;padding-top:6px;">Your email newsletters are now being sent to NewsBlur</p>
<img src="http://{% current_domain %}/media/img/reader/newsletters_folder.png" style="width:239px;height:407px;border:1px solid #E0E0E0;margin: 0 24px 24px 0;float: left;" alt="">
<p style="line-height: 20px;">You have just received your first email newsletter in NewsBlur. You can find your newsletters under the <b>Newsletters</b> folder.</p>
<p style="line-height: 20px;">Feel free to rename newsletters to better fit their name. Sometimes newsletters come in as names when they should be the name of the organization. Easy to change!</p>
<p style="line-height: 20px;">You can also move and delete newsletters just as if they were independent feeds. Training evne works on newsletters, so you can hide the newsletters you don't want to read while highlighting the ones you do.</p>
{% endblock %}

View file

@ -42,12 +42,18 @@
Import sites
</div>
</li>
<li class="NB-menu-item NB-menu-manage-goodies">
<div class="NB-menu-manage-image"></div>
<div class="NB-menu-manage-title">
Goodies &amp; Apps
</div>
</li>
<li class="NB-menu-item NB-menu-manage-goodies">
<div class="NB-menu-manage-image"></div>
<div class="NB-menu-manage-title">
Goodies &amp; Apps
</div>
</li>
<li class="NB-menu-item NB-menu-manage-newsletters">
<div class="NB-menu-manage-image"></div>
<div class="NB-menu-manage-title">
Email Newsletters
</div>
</li>
<li class="NB-menu-item NB-menu-manage-tutorial">
<div class="NB-menu-manage-image"></div>
<div class="NB-menu-manage-title">