Deferring OPML import if it takes > 20 seconds. Email user when complete. Also, hamburgers. This one's for @torrez.

This commit is contained in:
Samuel Clay 2012-07-20 19:43:28 -07:00
parent 9c479e5c43
commit d3b58e7cc9
18 changed files with 231 additions and 74 deletions

View file

@ -1,5 +1,6 @@
import datetime
import oauth2 as oauth
import mongoengine as mongo
from collections import defaultdict
from StringIO import StringIO
from xml.etree.ElementTree import Element, SubElement, Comment, tostring
@ -13,7 +14,7 @@ from apps.rss_feeds.models import Feed, DuplicateFeed, MStarredStory
from apps.reader.models import UserSubscription, UserSubscriptionFolders
from utils import json_functions as json, urlnorm
from utils import log as logging
from utils.feed_functions import timelimit
class OAuthToken(models.Model):
user = models.OneToOneField(User, null=True, blank=True)
@ -96,12 +97,17 @@ class OPMLImporter(Importer):
def __init__(self, opml_xml, user):
self.user = user
self.opml_xml = opml_xml
def try_processing(self):
folders = timelimit(20)(self.process)()
return folders
def process(self):
outline = opml.from_string(self.opml_xml)
self.clear_feeds()
outline = opml.from_string(self.opml_xml)
folders = self.process_outline(outline)
UserSubscriptionFolders.objects.create(user=self.user, folders=json.encode(folders))
return folders
def process_outline(self, outline):
@ -169,7 +175,30 @@ class OPMLImporter(Importer):
us.save()
folders.append(feed_db.pk)
return folders
def count_feeds_in_opml(self):
opml_count = len(opml.from_string(self.opml_xml))
sub_count = UserSubscription.objects.filter(user=self.user).count()
return opml_count + sub_count
class UploadedOPML(mongo.Document):
user_id = mongo.IntField()
opml_file = mongo.StringField()
upload_date = mongo.DateTimeField(default=datetime.datetime.now)
def __unicode__(self):
user = User.objects.get(pk=self.user_id)
return "%s: %s characters" % (user.username, len(self.opml_file))
meta = {
'collection': 'uploaded_opml',
'allow_inheritance': False,
'order': '-upload_date',
'indexes': ['user_id', '-upload_date'],
}
class GoogleReaderImporter(Importer):

21
apps/feed_import/tasks.py Normal file
View file

@ -0,0 +1,21 @@
from celery.task import Task
from django.contrib.auth.models import User
from apps.feed_import.models import UploadedOPML, OPMLImporter
from apps.reader.models import UserSubscription
from utils import log as logging
class ProcessOPML(Task):
def run(self, user_id):
user = User.objects.get(pk=user_id)
logging.user(user, "~FR~SBOPML upload (task) starting...")
opml = UploadedOPML.objects.filter(user_id=user_id).first()
opml_importer = OPMLImporter(opml.opml_file, user)
opml_importer.process()
feed_count = UserSubscription.objects.filter(user=user).count()
user.profile.send_upload_opml_finished_email(feed_count)
logging.user(user, "~FR~SBOPML upload (task): ~SK%s~SN~SB~FR feeds" % (feed_count))

View file

@ -14,9 +14,12 @@ from django.contrib.auth import login as login_user
from django.shortcuts import render_to_response
from apps.reader.forms import SignupForm
from apps.reader.models import UserSubscription
from apps.feed_import.models import OAuthToken, OPMLImporter, OPMLExporter, GoogleReaderImporter
from apps.feed_import.models import OAuthToken, GoogleReaderImporter
from apps.feed_import.models import OPMLImporter, OPMLExporter, UploadedOPML
from apps.feed_import.tasks import ProcessOPML
from utils import json_functions as json
from utils.user_functions import ajax_login_required, get_user
from utils.feed_functions import TimeoutError
@ajax_login_required
@ -31,11 +34,24 @@ def opml_upload(request):
logging.user(request, "~FR~SBOPML upload starting...")
file = request.FILES['file']
xml_opml = file.read()
UploadedOPML.objects.create(user_id=request.user.pk, opml_file=xml_opml)
opml_importer = OPMLImporter(xml_opml, request.user)
folders = opml_importer.process()
feeds = UserSubscription.objects.filter(user=request.user).values()
payload = dict(folders=folders, feeds=feeds)
logging.user(request, "~FR~SBOPML Upload: ~SK%s~SN~SB~FR feeds" % (len(feeds)))
try:
folders = opml_importer.try_processing()
except TimeoutError:
folders = None
ProcessOPML.delay(request.user.pk)
feed_count = opml_importer.count_feeds_in_opml()
logging.user(request, "~FR~SBOPML pload took too long, found %s feeds. Tasking..." % feed_count)
payload = dict(folders=folders, delayed=True, feed_count=feed_count)
code = 2
message = ""
if folders:
feeds = UserSubscription.objects.filter(user=request.user).values()
payload = dict(folders=folders, feeds=feeds)
logging.user(request, "~FR~SBOPML Upload: ~SK%s~SN~SB~FR feeds" % (len(feeds)))
request.session['import_from_google_reader'] = False
else:

View file

@ -214,6 +214,23 @@ NewsBlur""" % {'user': self.user.username, 'feeds': subs.count()}
logging.user(self.user, "~BB~FM~SBSending email for social beta: %s" % self.user.email)
def send_upload_opml_finished_email(self, feed_count):
if not self.user.email:
print "Please provide an email address."
return
user = self.user
text = render_to_string('mail/email_upload_opml_finished.txt', locals())
html = render_to_string('mail/email_upload_opml_finished.xhtml', locals())
subject = "Your OPML upload is complete. Get going with 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()
logging.user(self.user, "~BB~FM~SBSending email for OPML upload: %s" % self.user.email)
def autologin_url(self, next=None):
return reverse('autologin', kwargs={
'username': self.user.username,

2
fabfile.py vendored
View file

@ -374,7 +374,7 @@ def setup_psycopg():
def setup_python():
# sudo('easy_install -U pip')
sudo('easy_install -U fabric django==1.3.1 readline pyflakes iconv celery django-celery django-celery-with-redis django-compress South django-extensions pymongo==2.2.0 stripe BeautifulSoup pyyaml nltk lxml oauth2 pytz boto seacucumber django_ses mongoengine redis requests')
sudo('easy_install -U fabric django==1.3.1 readline pyflakes iconv celery django-celery django-celery-with-redis django-compress South django-extensions pymongo==2.2.0 stripe BeautifulSoup pyyaml nltk lxml oauth2 pytz boto seacucumber django_ses mongoengine redis requests psutil')
put('config/pystartup.py', '.pystartup')
# with cd(os.path.join(env.NEWSBLUR_PATH, 'vendor/cjson')):

View file

@ -5872,6 +5872,13 @@ form.opml_import_form input {
min-height: 234px;
}
.NB-modal-intro .NB-intro-import-delayed {
color: #808080;
margin: 12px 0 0;
font-size: 12px;
text-align: center;
display: none;
}
.NB-modal-intro .NB-intro-import {
width: 300px;
float: left;
@ -6337,16 +6344,15 @@ form.opml_import_form input {
}
.NB-modal-feedchooser .NB-modal-subtitle {
font-weight: normal;
font-size: 12px;
color: #606060;
text-shadow: 1px 1px 0 #F0F0F0;
width: 715px;
font-weight: normal;
font-size: 12px;
color: #606060;
text-shadow: 1px 1px 0 #F0F0F0;
width: 715px;
}
.NB-modal-feedchooser .NB-modal-subtitle b {
/* margin: 0 0 4px 0;*/
padding-right: 8px;
color: #303030;
color: #303030;
}
.NB-modal-feedchooser .NB-feedchooser-subtitle-type-prefix {
@ -6354,19 +6360,33 @@ form.opml_import_form input {
}
.NB-modal-feedchooser .NB-feedchooser-type {
float: left;
width: 345px;
margin: 0 12px 0 0;
padding: 0 12px 0 0;
float: left;
width: 345px;
margin: 0 52px 0 26px;
padding: 0;
}
.NB-modal-feedchooser .NB-feedchooser-type.NB-last {
margin: 0 0 0 0;
padding: 0 0 0 24px;
margin-right: 0;
border-left: 1px solid #B0B0B0;
margin: 0 0 0 0;
padding: 0 0 0 52px;
margin-right: 0;
border-left: 1px solid #B0B0B0;
}
.NB-modal-feedchooser .NB-feedchooser-porpoise {
border-radius: 16px;
line-height: 48px;
color: #808080;
font-size: 18px;
padding: 8px 2px;
background-color: white;
position: absolute;
margin-top: -24px;
width: 30px;
text-align: center;
top: 50%;
left: -17px
}
.NB-modal-feedchooser .NB-feedchooser-info {
overflow: hidden;
}
@ -6510,22 +6530,20 @@ form.opml_import_form input {
position: absolute;
width: 40px;
height: 40px;
top: 0;
top: -10px;
left: -40px;
display: none;
}
.NB-modal-feedchooser .NB-feedchooser-dollar-value.NB-selected.NB-1 .NB-feedchooser-dollar-image {
top: -8px;
background: transparent url('/media/embed/reader/hamburgers.png') no-repeat 0 0;
display: block;
}
.NB-modal-feedchooser .NB-feedchooser-dollar-value.NB-selected.NB-2 .NB-feedchooser-dollar-image {
top: -8px;
background: transparent url('/media/embed/reader/hamburgers.png') no-repeat 0 -36px;
display: block;
}
.NB-modal-feedchooser .NB-feedchooser-dollar-value.NB-selected.NB-3 .NB-feedchooser-dollar-image {
top: -10px;
background: transparent url('/media/embed/reader/hamburgers.png') no-repeat 0 -72px;
display: block;
}
.NB-modal-feedchooser .NB-feedchooser-dollar-month {

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -774,11 +774,14 @@
this.hide_progress_bar();
},
open_dialog_after_feeds_loaded: function() {
open_dialog_after_feeds_loaded: function(options) {
options = options || {};
if (!NEWSBLUR.Globals.is_authenticated) return;
if (!NEWSBLUR.assets.folders.length || !NEWSBLUR.assets.preference('has_setup_feeds')) {
if (NEWSBLUR.assets.preference('has_setup_feeds')) {
if (options.delayed_import || this.flags.delayed_import) {
this.setup_ftux_add_feed_callout("Check your email...");
} else if (NEWSBLUR.assets.preference('has_setup_feeds')) {
this.setup_ftux_add_feed_callout();
} else if (!NEWSBLUR.intro || !NEWSBLUR.intro.flags.open) {
_.defer(_.bind(this.open_intro_modal, this), 100);
@ -3774,11 +3777,11 @@
// = FTUX =
// ========
setup_ftux_add_feed_callout: function() {
setup_ftux_add_feed_callout: function(message) {
var self = this;
if (this.flags['bouncing_callout']) return;
$('.NB-callout-ftux .NB-callout-text').text('First things first...');
$('.NB-callout-ftux .NB-callout-text').text(message || 'First things first...');
$('.NB-callout-ftux').corner('5px');
$('.NB-callout-ftux').css({
'opacity': 0,

View file

@ -60,7 +60,9 @@ NEWSBLUR.ReaderFeedchooser.prototype = {
return false;
})
]),
$.make('div', { className: 'NB-feedchooser-type NB-last'}, [
$.make('div', { className: 'NB-feedchooser-type NB-last', style: 'position: relative'}, [
$.make('div', { className: 'NB-feedchooser-porpoise' }, 'OR'),
$.make('div', { className: 'NB-feedchooser-info'}, [
$.make('div', { className: 'NB-feedchooser-info-type' }, [
$.make('span', { className: 'NB-feedchooser-subtitle-type-prefix' }, 'Super-Mega'),
@ -105,21 +107,30 @@ NEWSBLUR.ReaderFeedchooser.prototype = {
$.make('div', { className: 'NB-feedchooser-dollar' }, [
$.make('div', { className: 'NB-feedchooser-dollar-value NB-1' }, [
$.make('div', { className: 'NB-feedchooser-dollar-month' }, [
$.make('div', { className: 'NB-feedchooser-dollar-image' }),
$.make('div', { className: 'NB-feedchooser-dollar-image' }, [
$.make('img', { src: NEWSBLUR.Globals.MEDIA_URL + '/img/reader/hamburger_s.png' })
]),
'$12/year'
]),
$.make('div', { className: 'NB-feedchooser-dollar-year' }, '($1/month)')
]),
$.make('div', { className: 'NB-feedchooser-dollar-value NB-2' }, [
$.make('div', { className: 'NB-feedchooser-dollar-month' }, [
$.make('div', { className: 'NB-feedchooser-dollar-image' }),
$.make('div', { className: 'NB-feedchooser-dollar-image' }, [
$.make('img', { src: NEWSBLUR.Globals.MEDIA_URL + '/img/reader/hamburger_s.png', style: "position: absolute; left: -26px;top: 1px" }),
$.make('img', { src: NEWSBLUR.Globals.MEDIA_URL + '/img/reader/hamburger_m.png', style: "position: absolute; left: 0px;top: 2px" })
]),
'$24/year'
]),
$.make('div', { className: 'NB-feedchooser-dollar-year' }, '($2/month)')
]),
$.make('div', { className: 'NB-feedchooser-dollar-value NB-3' }, [
$.make('div', { className: 'NB-feedchooser-dollar-month' }, [
$.make('div', { className: 'NB-feedchooser-dollar-image' }),
$.make('div', { className: 'NB-feedchooser-dollar-image' }, [
$.make('img', { src: NEWSBLUR.Globals.MEDIA_URL + '/img/reader/hamburger_s.png', style: "position: absolute; left: -58px;top: 0" }),
$.make('img', { src: NEWSBLUR.Globals.MEDIA_URL + '/img/reader/hamburger_m.png', style: "position: absolute; left: -31px;top: 2px" }),
$.make('img', { src: NEWSBLUR.Globals.MEDIA_URL + '/img/reader/hamburger_l.png', style: "position: absolute; left: 0; top: 0" })
]),
'$36/year'
]),
$.make('div', { className: 'NB-feedchooser-dollar-year' }, '($3/month)')
@ -205,8 +216,8 @@ NEWSBLUR.ReaderFeedchooser.prototype = {
open_modal: function() {
var self = this;
this.$modal.modal({
'minWidth': 780,
'maxWidth': 780,
'minWidth': 860,
'maxWidth': 860,
'overlayClose': true,
'onOpen': function (dialog) {
dialog.overlay.fadeIn(200, function () {

View file

@ -10,6 +10,7 @@ NEWSBLUR.ReaderIntro = function(options) {
'twitter': {},
'facebook': {}
};
this.flags = {};
this.autofollow = true;
this.page_number = this.options.page_number;
@ -79,6 +80,11 @@ _.extend(NEWSBLUR.ReaderIntro.prototype, {
$.make('h4'),
$.make('div', { className: 'NB-intro-import-restart NB-modal-submit-grey NB-modal-submit-button' }, [
'&laquo; Restart and re-import your sites'
]),
$.make('div', { className: 'NB-intro-import-delayed' }, [
'There are too many sites to process...',
$.make('br'),
'You will be emailed within a minute or three.'
])
])
]),
@ -303,9 +309,9 @@ _.extend(NEWSBLUR.ReaderIntro.prototype, {
} else if (page_number > page_count) {
NEWSBLUR.assets.preference('has_setup_feeds', true);
NEWSBLUR.reader.check_hide_getting_started();
this.close(function() {
NEWSBLUR.reader.open_dialog_after_feeds_loaded();
});
this.close(_.bind(function() {
NEWSBLUR.reader.open_dialog_after_feeds_loaded({delayed_import: this.flags.delayed_import});
}, this));
return;
} else if (page_number == 1) {
$('.NB-tutorial-next-page-text', this.$modal).text("Let's Get Started ");
@ -345,6 +351,7 @@ _.extend(NEWSBLUR.ReaderIntro.prototype, {
if (NEWSBLUR.assets.feeds.size() && !this.options.force_import) {
page = 2;
$('.NB-intro-imports-sites', this.$modal).addClass('active');
$('.NB-intro-import-delayed', this.$modal).hide();
} else {
page = 0;
$('.NB-intro-imports-start', this.$modal).addClass('active');
@ -360,11 +367,12 @@ _.extend(NEWSBLUR.ReaderIntro.prototype, {
this.count_feeds();
},
count_feeds: function() {
var feed_count = NEWSBLUR.assets.feeds.size();
count_feeds: function(fake_feed_count) {
var feed_count = fake_feed_count || NEWSBLUR.assets.feeds.size();
$(".NB-intro-imports-sites h4", this.$modal).text([
'You are subscribed to ',
(fake_feed_count && 'at least '),
Inflector.pluralize(' site', feed_count, true),
'.'
].join(""));
@ -464,10 +472,16 @@ _.extend(NEWSBLUR.ReaderIntro.prototype, {
var params = {
url: NEWSBLUR.URLs['opml-upload'],
type: 'POST',
dataType: 'json',
success: function (data, status) {
NEWSBLUR.assets.load_feeds(function() {
$loading.removeClass('NB-active');
self.advance_import_carousel(2);
if (data.payload.delayed) {
NEWSBLUR.reader.flags.delayed_import = true;
self.count_feeds(data.payload.feed_count);
$('.NB-intro-import-delayed', self.$modal).show();
}
});
NEWSBLUR.reader.load_recommended_feed();
},

View file

@ -310,7 +310,10 @@ BROKER_URL = "redis://db01:6379/0"
CELERY_REDIS_HOST = "db01"
CELERYD_PREFETCH_MULTIPLIER = 1
CELERY_IMPORTS = ("apps.rss_feeds.tasks", "apps.social.tasks", "apps.reader.tasks",)
CELERY_IMPORTS = ("apps.rss_feeds.tasks",
"apps.social.tasks",
"apps.reader.tasks",
"apps.feed_import.tasks",)
CELERYD_CONCURRENCY = 4
CELERY_IGNORE_RESULT = True
CELERY_ACKS_LATE = True # Retry if task fails

View file

@ -1,3 +1,5 @@
{% load utils_tags %}
{% block body %}{% endblock body %}
- Samuel & Roy
@ -18,10 +20,10 @@ Stay up to date and in touch with us, yr. developers, in a few different ways:
There's plenty of ways to use NewsBlur beyond the website:
* Download the free iPhone App: http://www.newsblur.com/iphone/
* Download the free iPhone App: http://{% current_domain %}/iphone/
* Download the Android App on the Android Market: https://market.android.com/details?id=bitwrit.Blar
* Download browser extensions for Safari, Firefox, and Chrome: http://www.newsblur.com{{ user.profile.autologin_url }}?next=goodies
* Download browser extensions for Safari, Firefox, and Chrome: http://{% current_domain %}{{ user.profile.autologin_url }}?next=goodies
-----------------------------------------------------------------------------
Don't want to be notified about anything NewsBlur related? Opt-out of emails from NewsBlur: http://www.newsblur.com{{ user.profile.autologin_url }}?next=optout
Don't want to be notified about anything NewsBlur related? Opt-out of emails from NewsBlur: http://{% current_domain %}{{ user.profile.autologin_url }}?next=optout

View file

@ -1,3 +1,5 @@
{% load utils_tags %}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
@ -12,11 +14,11 @@
<table cellpadding="0" cellspacing="0" border="0" bgcolor="#FFFFFF" align="left" width="644" style="padding: 20px; margin-top: 24px;align:center;border: 1px solid #BABABA;font-family:'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:16px;color:#333;">
<tr>
<td align="center">
<a href="http://www.newsblur.com{{ user.profile.autologin_url }}"><img alt="NewsBlur" src="http://www.newsblur.com/media/img/logo_newsblur_blur.png" title="NewsBlur" style="width:312px;height:55px;border: none;"></a>
<a href="http://{% current_domain %}{{ user.profile.autologin_url }}"><img alt="NewsBlur" src="http://{% current_domain %}/media/img/logo_newsblur_blur.png" title="NewsBlur" style="width:312px;height:55px;border: none;"></a>
</td>
</tr>
<tr>
<td>
<td style="padding-top: 12px;">
{% block body %}{% endblock %}
@ -32,23 +34,23 @@
<p style="line-height:20px;">Stay up to date and in touch with us, yr. developers, in a few different ways:</p>
<p style="line-height: 20px;">
<ul style="list-style: none;">
<li style="line-height:22px;"><a href="http://twitter.com/newsblur/" style="text-decoration:none"><img src="http://www.newsblur.com/media/img/reader/twitter.png" style="width:16px;height:16px;vertical-align:top;padding-top:3px;"> Follow @newsblur on Twitter</a>.</li>
<li style="line-height:22px;"><a href="http://github.com/samuelclay/" style="text-decoration:none"><img src="http://www.newsblur.com/media/img/reader/github_icon.png" style="width:16px;height:16px;vertical-align:top;padding-top:3px;"> Follow @samuelclay on GitHub</a>.</li>
<li style="line-height:22px;"><a href="http://twitter.com/newsblur/" style="text-decoration:none"><img src="http://{% current_domain %}/media/img/reader/twitter.png" style="width:16px;height:16px;vertical-align:top;padding-top:3px;"> Follow @newsblur on Twitter</a>.</li>
<li style="line-height:22px;"><a href="http://github.com/samuelclay/" style="text-decoration:none"><img src="http://{% current_domain %}/media/img/reader/github_icon.png" style="width:16px;height:16px;vertical-align:top;padding-top:3px;"> Follow @samuelclay on GitHub</a>.</li>
</ul>
</p>
<p style="line-height: 20px;">{% block resources_header %}To get the most out of NewsBlur, here are a few resources:{% endblock resources_header %}</p>
<p style="line-height: 20px;">
<ul style="list-style: none;">
<li style="line-height:22px;"><a href="http://blog.newsblur.com" style="text-decoration:none"><img src="http://www.newsblur.com/media/img/favicon.png" style="width:16px;height:16px;vertical-align:top;padding-top:3px;"> Read the NewsBlur Blog</a>.</li>
<li style="line-height:22px;"><a href="http://getsatisfaction.com/newsblur/" style="text-decoration:none"><img src="http://www.newsblur.com/media/img/reader/getsatisfaction.png" style="width:16px;height:16px;vertical-align:top;padding-top:3px;"> Get support on NewsBlur's Get Satisfaction</a>.</li>
<li style="line-height:22px;"><a href="http://blog.newsblur.com" style="text-decoration:none"><img src="http://{% current_domain %}/media/img/favicon.png" style="width:16px;height:16px;vertical-align:top;padding-top:3px;"> Read the NewsBlur Blog</a>.</li>
<li style="line-height:22px;"><a href="http://getsatisfaction.com/newsblur/" style="text-decoration:none"><img src="http://{% current_domain %}/media/img/reader/getsatisfaction.png" style="width:16px;height:16px;vertical-align:top;padding-top:3px;"> Get support on NewsBlur's Get Satisfaction</a>.</li>
</ul>
</p>
<p style="line-height: 20px;">There's plenty of ways to use NewsBlur beyond the web:</p>
<p style="line-height: 20px;">
<ul style="list-style: none;">
<li style="line-height:22px;"><a href="http://www.newsblur.com/iphone/" style="text-decoration:none"><img src="http://www.newsblur.com/media/img/reader/iphone_icon.png" style="width:16px;height:16px;vertical-align:top;padding-top:3px;"> Download the free iPhone App</a>.</li>
<li style="line-height:22px;"><a href="https://market.android.com/details?id=bitwrit.Blar" style="text-decoration:none"><img src="http://www.newsblur.com/media/img/reader/android_icon.png" style="width:16px;height:16px;vertical-align:top;padding-top:3px;"> Download the Android App on the Android Market</a>.</li>
<li style="line-height:22px;"><a href="http://www.newsblur.com{{ user.profile.autologin_url }}?next=goodies" style="text-decoration:none"><img src="http://www.newsblur.com/media/img/icons/silk/package_green.png" style="width:16px;height:16px;vertical-align:top;padding-top:3px;"> Download browser extensions for Safari, Firefox, and Chrome</a>.</li>
<li style="line-height:22px;"><a href="http://{% current_domain %}/iphone/" style="text-decoration:none"><img src="http://{% current_domain %}/media/img/reader/iphone_icon.png" style="width:16px;height:16px;vertical-align:top;padding-top:3px;"> Download the free iPhone App</a>.</li>
<li style="line-height:22px;"><a href="https://market.android.com/details?id=bitwrit.Blar" style="text-decoration:none"><img src="http://{% current_domain %}/media/img/reader/android_icon.png" style="width:16px;height:16px;vertical-align:top;padding-top:3px;"> Download the Android App on the Android Market</a>.</li>
<li style="line-height:22px;"><a href="http://{% current_domain %}{{ user.profile.autologin_url }}?next=goodies" style="text-decoration:none"><img src="http://{% current_domain %}/media/img/icons/silk/package_green.png" style="width:16px;height:16px;vertical-align:top;padding-top:3px;"> Download browser extensions for Safari, Firefox, and Chrome</a>.</li>
</ul>
</p>
</td>
@ -62,7 +64,7 @@
<tr>
<td>
<p style="font-size:12px;">
Don't want to be notified about anything NewsBlur related? <a href="http://www.newsblur.com{{ user.profile.autologin_url }}?next=optout" style="text-decoration:none;">Opt-out of emails from NewsBlur</a>.
Don't want to be notified about anything NewsBlur related? <a href="http://{% current_domain %}{{ user.profile.autologin_url }}?next=optout" style="text-decoration:none;">Opt-out of emails from NewsBlur</a>.
</p>
</td>
</tr>
@ -71,7 +73,7 @@
</tr>
<tr>
<td align="center" style="padding-top: 12px;">
<a href="http://www.newsblur.com{{ user.profile.autologin_url }}"><img alt="NewsBlur" src="http://www.newsblur.com/media/img/logo_newsblur.png" title="NewsBlur" style="width:312px;height:55px;border: none;"></a>
<a href="http://{% current_domain %}{{ user.profile.autologin_url }}"><img alt="NewsBlur" src="http://{% current_domain %}/media/img/logo_newsblur.png" title="NewsBlur" style="width:312px;height:55px;border: none;"></a>
</td>
</tr>
</table>

View file

@ -0,0 +1,7 @@
{% extends "mail/email_base.txt" %}
{% load utils_tags %}
{% block body %}Good news! NewsBlur has finished importing your OPML file. You are now subscribed to <b>{{ feed_count }}</b> site{{ feed_count|pluralize }}.
Head over to NewsBlur and get reading: http://{% current_domain %}{% endblock body %}

View file

@ -0,0 +1,15 @@
{% extends "mail/email_base.xhtml" %}
{% load utils_tags %}
{% block body %}
<p style="margin-top: 12px;">
Good news! NewsBlur has finished importing your OPML file. You are now
subscribed to <b>{{ feed_count }}</b> site{{ feed_count|pluralize }}.
</p>
<p>
Head over to <a href="http://{% current_domain %}">NewsBlur</a> and get reading.
</p>
{% endblock %}

View file

@ -27,19 +27,18 @@ def timelimit(timeout):
self.result = function(*args, **kw)
except:
self.error = sys.exc_info()
if not settings.DEBUG and not settings.TEST_DEBUG:
c = Dispatch()
c.join(timeout)
if c.isAlive():
raise TimeoutError, 'took too long'
if c.error:
tb = ''.join(traceback.format_exception(c.error[0], c.error[1], c.error[2]))
logging.debug(tb)
mail_admins('Error in timeout: %s' % c.error[0], tb)
raise c.error[0], c.error[1]
return c.result
else:
return function(*args, **kw)
c = Dispatch()
c.join(timeout)
if c.isAlive():
raise TimeoutError, 'took too long'
if not settings.DEBUG and not settings.TEST_DEBUG and c.error:
tb = ''.join(traceback.format_exception(c.error[0], c.error[1], c.error[2]))
logging.debug(tb)
mail_admins('Error in timeout: %s' % c.error[0], tb)
raise c.error[0], c.error[1]
return c.result
# else:
# return function(*args, **kw)
return _2
return _1