Merge branch 'master' into 5.1

* master:
  Maybe I shouldn't be coding right now. That errant 'else' keyword looked fine in word wrap.
  Python's naming on the try..except..else block is almost as bad as for..else. I blame Python for this stupidity.
  Lifetime premium accounts should see status when on Renewal dialog.
  Adding auto-canceling of stripe/paypal subscription when renewing with different paypal/stripe provider.
  Splitting intelligence trainer counts into + and -
  Adding alt attributes to images in emails to lower spam score.
  Fix somewhat long-standing error logging bug that spits out a ton of data on feeds that don't work with requests lib.
  Fix on-upgrade crash.
  Fixing feed finder to allow feeds with <html> tags, just not those right at the beginning.
This commit is contained in:
Samuel Clay 2016-03-20 19:32:58 -07:00
commit b54a583c9d
9 changed files with 82 additions and 30 deletions

View file

@ -162,7 +162,7 @@ class Profile(models.Model):
def activate_premium(self, never_expire=False):
from apps.profile.tasks import EmailNewPremium
EmailNewPremium.delay(user_id=self.user.pk)
self.is_premium = True
@ -357,9 +357,10 @@ class Profile(models.Model):
stripe_cancel = self.cancel_premium_stripe()
return paypal_cancel or stripe_cancel
def cancel_premium_paypal(self):
def cancel_premium_paypal(self, second_most_recent_only=False):
transactions = PayPalIPN.objects.filter(custom=self.user.username,
txn_type='subscr_signup')
txn_type='subscr_signup').order_by('-subscr_date')
if not transactions:
return
@ -371,14 +372,24 @@ class Profile(models.Model):
'API_CA_CERTS': False,
}
paypal = PayPalInterface(**paypal_opts)
transaction = transactions[0]
if second_most_recent_only:
# Check if user has an active subscription. If so, cancel it because a new one came in.
if len(transactions) > 1:
transaction = transactions[1]
else:
return False
else:
transaction = transactions[0]
profileid = transaction.subscr_id
try:
paypal.manage_recurring_payments_profile_status(profileid=profileid, action='Cancel')
except PayPalAPIResponseError:
logging.user(self.user, "~FRUser ~SBalready~SN canceled Paypal subscription")
logging.user(self.user, "~FRUser ~SBalready~SN canceled Paypal subscription: %s" % profileid)
else:
logging.user(self.user, "~FRCanceling Paypal subscription")
if second_most_recent_only:
logging.user(self.user, "~FRCanceling ~BR~FWsecond-oldest~SB~FR Paypal subscription: %s" % profileid)
else:
logging.user(self.user, "~FRCanceling Paypal subscription: %s" % profileid)
return True
@ -883,6 +894,8 @@ def paypal_signup(sender, **kwargs):
except:
pass
user.profile.activate_premium()
user.profile.cancel_premium_stripe()
user.profile.cancel_premium_paypal(second_most_recent_only=True)
subscription_signup.connect(paypal_signup)
def paypal_payment_history_sync(sender, **kwargs):
@ -931,6 +944,7 @@ def stripe_signup(sender, full_json, **kwargs):
profile = Profile.objects.get(stripe_id=stripe_id)
logging.user(profile.user, "~BC~SB~FBStripe subscription signup")
profile.activate_premium()
profile.cancel_premium_paypal()
except Profile.DoesNotExist:
return {"code": -1, "message": "User doesn't exist."}
zebra_webhook_customer_subscription_created.connect(stripe_signup)

View file

@ -419,10 +419,14 @@ def payment_history(request):
"read_story_count": RUserStory.read_story_count(user.pk),
"feed_opens": UserSubscription.objects.filter(user=user).aggregate(sum=Sum('feed_opens'))['sum'],
"training": {
'title': MClassifierTitle.objects.filter(user_id=user.pk).count(),
'tag': MClassifierTag.objects.filter(user_id=user.pk).count(),
'author': MClassifierAuthor.objects.filter(user_id=user.pk).count(),
'feed': MClassifierFeed.objects.filter(user_id=user.pk).count(),
'title_ps': MClassifierTitle.objects.filter(user_id=user.pk, score__gt=0).count(),
'title_ng': MClassifierTitle.objects.filter(user_id=user.pk, score__lt=0).count(),
'tag_ps': MClassifierTag.objects.filter(user_id=user.pk, score__gt=0).count(),
'tag_ng': MClassifierTag.objects.filter(user_id=user.pk, score__lt=0).count(),
'author_ps': MClassifierAuthor.objects.filter(user_id=user.pk, score__gt=0).count(),
'author_ng': MClassifierAuthor.objects.filter(user_id=user.pk, score__lt=0).count(),
'feed_ps': MClassifierFeed.objects.filter(user_id=user.pk, score__gt=0).count(),
'feed_ng': MClassifierFeed.objects.filter(user_id=user.pk, score__lt=0).count(),
}
}

View file

@ -53,7 +53,13 @@ public class FeedUtils {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... arg) {
dbHelper.clearStorySession();
try {
dbHelper.clearStorySession();
} catch (Exception e) {
; // TODO: this can evade DB-ready gating and crash. figure out how to
// defer this call until the DB-ready broadcast is received, as this
// can mask important errors
}
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

View file

@ -7818,6 +7818,12 @@ form.opml_import_form input {
.NB-modal-admin .NB-admin-training-counts span.NB-grey {
color: #D0D0D0;
}
.NB-modal-admin .NB-admin-training-counts span.NB-green {
color: #2B8B19;
}
.NB-modal-admin .NB-admin-training-counts span.NB-red {
color: #761113;
}
/* ===================== */
/* = Email Story Modal = */

View file

@ -87,7 +87,8 @@ _.extend(NEWSBLUR.ReaderUserAdmin.prototype, {
$actions.append($.make('div', { className: "NB-modal-submit-button NB-modal-submit-green NB-admin-action-history", style: "float: left" }, "Update History"));
$actions.append($.make('div', { className: "NB-modal-submit-button NB-modal-submit-green NB-admin-action-opml", style: "float: left" }, "OPML"));
var training = data.statistics.training;
$statistics.append($.make('dl', [
$.make('dt', 'Created:'),
$.make('dd', data.statistics.created_date),
@ -109,10 +110,30 @@ _.extend(NEWSBLUR.ReaderUserAdmin.prototype, {
$.make('dd', Inflector.commas(data.statistics.read_story_count)),
$.make('dt', 'Training:'),
$.make('dd', { className: 'NB-admin-training-counts' }, [
$.make('span', { className: data.statistics.training.title ? '' : 'NB-grey' }, 'Title: ' + data.statistics.training.title),
$.make('span', { className: data.statistics.training.author ? '' : 'NB-grey' }, 'Author: ' + data.statistics.training.author),
$.make('span', { className: data.statistics.training.tag ? '' : 'NB-grey' }, 'Tag: ' + data.statistics.training.tag),
$.make('span', { className: data.statistics.training.feed ? '' : 'NB-grey' }, 'Feed: ' + data.statistics.training.feed)
$.make('span', { className: training.title_ps || training.title_ng ? '' : 'NB-grey' }, [
'Title: ',
(training.title_ps && $.make('span', { className: 'NB-green' }, training.title_ps)),
'-',
(training.title_ng && $.make('span', { className: 'NB-red' }, training.title_ng))
]),
$.make('span', { className: training.author_ps || training.author_ng ? '' : 'NB-grey' }, [
'Author: ',
(training.author_ps && $.make('span', { className: 'NB-green' }, training.author_ps)),
'-',
(training.author_ng && $.make('span', { className: 'NB-red' }, training.author_ng))
]),
$.make('span', { className: training.tag_ps || training.tag_ng ? '' : 'NB-grey' }, [
'Tag: ',
(training.tag_ps && $.make('span', { className: 'NB-green' }, training.tag_ps)),
'-',
(training.tag_ng && $.make('span', { className: 'NB-red' }, training.tag_ng))
]),
$.make('span', { className: training.feed_ps || training.feed_ng ? '' : 'NB-grey' }, [
'Feed: ',
(training.feed_ps && $.make('span', { className: 'NB-green' }, training.feed_ps)),
'-',
(training.feed_ng && $.make('span', { className: 'NB-red' }, training.feed_ng))
])
])
]));
$(window).resize();

View file

@ -77,7 +77,8 @@ _.extend(NEWSBLUR.ReaderFeedchooser.prototype, {
$.make('b', { style: 'display: block; margin: 8px 0' }, [
$.make('span', { className: 'NB-raquo' }, '&raquo;'),
' ',
NEWSBLUR.Globals.premium_expire && NEWSBLUR.utils.format_date(NEWSBLUR.Globals.premium_expire)
NEWSBLUR.Globals.premium_expire && NEWSBLUR.utils.format_date(NEWSBLUR.Globals.premium_expire),
(!NEWSBLUR.Globals.premium_expire && $.make('b', "Never gonna expire. Congrats!"))
]),
'You can change your payment method and card details. ',
(NEWSBLUR.Globals.premium_expire < new Date) ?

View file

@ -36,23 +36,23 @@
<p style="line-height:20px;">Stay up to date and in touch with 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://{% current_domain %}/media/img/icons/circular/story_share_twitter_active.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/welcome/github_favicon.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/icons/circular/story_share_twitter_active.png" style="width:16px;height:16px;vertical-align:top;padding-top:3px;" alt=""> 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/welcome/github_favicon.png" style="width:16px;height:16px;vertical-align:top;padding-top:3px;" alt=""> 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://{% current_domain %}/media/img/favicon_32.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>
<li style="line-height:22px;"><a href="http://blog.newsblur.com" style="text-decoration:none"><img src="http://{% current_domain %}/media/img/favicon_32.png" style="width:16px;height:16px;vertical-align:top;padding-top:3px;" alt=""> 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;" alt=""> Get support on NewsBlur's Get Satisfaction</a></li>
</ul>
</p>
<p style="line-height: 20px;">There are 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://{% current_domain %}/ios/" 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 iPad/iPhone App</a></li>
<li style="line-height:22px;"><a href="http://{% current_domain %}/android/" 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 free Android App</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>
<li style="line-height:22px;"><a href="http://{% current_domain %}/ios/" 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;" alt=""> Download the free iPad/iPhone App</a></li>
<li style="line-height:22px;"><a href="http://{% current_domain %}/android/" 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;" alt=""> Download the free Android App</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;" alt=""> Download browser extensions for Safari, Firefox, and Chrome</a></li>
</ul>
</p>
</td>

View file

@ -130,7 +130,7 @@ class FetchFeed:
self.fpf = feedparser.parse(smart_unicode(raw_feed.content),
response_headers=response_headers)
except Exception, e:
logging.debug(" ---> [%-30s] ~FRFeed failed to fetch with request, trying feedparser: %s" % (self.feed.title[:30], e))
logging.debug(" ---> [%-30s] ~FRFeed failed to fetch with request, trying feedparser: %s" % (self.feed.title[:30], unicode(e)[:100]))
if not self.fpf:
try:

View file

@ -49,7 +49,7 @@ class FeedFinder(object):
def is_feed_data(self, text):
data = text.lower()
if data.count("<html"):
if data and data[:100].count("<html"):
return False
return data.count("<rss")+data.count("<rdf")+data.count("<feed")
@ -75,17 +75,17 @@ def find_feeds(url, check_all=False, user_agent=None):
url = coerce_url(url)
# Download the requested URL.
text = finder.get_feed(url)
if text is None:
feed_text = finder.get_feed(url)
if feed_text is None:
return []
# Check if it is already a feed.
if finder.is_feed_data(text):
if finder.is_feed_data(feed_text):
return [url]
# Look for <link> tags.
logging.info("Looking for <link> tags.")
tree = BeautifulSoup(text)
tree = BeautifulSoup(feed_text)
links = []
for link in tree.findAll("link"):
if link.get("type") in ["application/rss+xml",