diff --git a/apps/newsletters/models.py b/apps/newsletters/models.py index f11535a68..96c968c75 100644 --- a/apps/newsletters/models.py +++ b/apps/newsletters/models.py @@ -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') diff --git a/media/css/reader.css b/media/css/reader.css index 45e3c0a64..0a66882db 100644 --- a/media/css/reader.css +++ b/media/css/reader.css @@ -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 = */ /* ================= */ diff --git a/media/img/icons/circular/g_modal_mail.png b/media/img/icons/circular/g_modal_mail.png new file mode 100644 index 000000000..570331349 Binary files /dev/null and b/media/img/icons/circular/g_modal_mail.png differ diff --git a/media/img/icons/circular/menu_icn_mail.png b/media/img/icons/circular/menu_icn_mail.png index 5a76aef28..32f364779 100644 Binary files a/media/img/icons/circular/menu_icn_mail.png and b/media/img/icons/circular/menu_icn_mail.png differ diff --git a/media/img/reader/newsletters_folder.png b/media/img/reader/newsletters_folder.png new file mode 100644 index 000000000..8db1a348a Binary files /dev/null and b/media/img/reader/newsletters_folder.png differ diff --git a/media/img/reader/newsletters_gmail.png b/media/img/reader/newsletters_gmail.png new file mode 100644 index 000000000..6741e651c Binary files /dev/null and b/media/img/reader/newsletters_gmail.png differ diff --git a/media/js/newsblur/reader/reader.js b/media/js/newsblur/reader/reader.js index e71546755..1039cc8a9 100644 --- a/media/js/newsblur/reader/reader.js +++ b/media/js/newsblur/reader/reader.js @@ -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 & 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')) { diff --git a/media/js/newsblur/reader/reader_newsletters.js b/media/js/newsblur/reader/reader_newsletters.js new file mode 100644 index 000000000..416b229e5 --- /dev/null +++ b/media/js/newsblur/reader/reader_newsletters.js @@ -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 > 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(); + }); + } + +}); \ No newline at end of file diff --git a/media/js/newsblur/views/feed_list_view.js b/media/js/newsblur/views/feed_list_view.js index 23d759194..a7940bf3b 100644 --- a/media/js/newsblur/views/feed_list_view.js +++ b/media/js/newsblur/views/feed_list_view.js @@ -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') { diff --git a/templates/mail/email_first_newsletter.txt b/templates/mail/email_first_newsletter.txt new file mode 100644 index 000000000..2d2062b20 --- /dev/null +++ b/templates/mail/email_first_newsletter.txt @@ -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 %} \ No newline at end of file diff --git a/templates/mail/email_first_newsletter.xhtml b/templates/mail/email_first_newsletter.xhtml new file mode 100644 index 000000000..9a7b90633 --- /dev/null +++ b/templates/mail/email_first_newsletter.xhtml @@ -0,0 +1,10 @@ +{% extends "mail/email_base.xhtml" %} +{% load utils_tags %} + +{% 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 %} \ No newline at end of file diff --git a/templates/reader/manage_module.xhtml b/templates/reader/manage_module.xhtml index 1c1f1fd42..206a650f0 100644 --- a/templates/reader/manage_module.xhtml +++ b/templates/reader/manage_module.xhtml @@ -42,12 +42,18 @@ Import sites -
  • -
    -
    - Goodies & Apps -
    -
  • +
  • +
    +
    + Goodies & Apps +
    +
  • +
  • +
    +
    + Email Newsletters +
    +