diff --git a/README.md b/README.md index 112013f4d..cf0200692 100755 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ command will automatically do this for you, but Mac OS X needs to have it instal Not the easiest to get installed. If you are running Mac OS X, you have a few options: - * Use the [Superpack by Chris Fonnesbeck](http://stronginference.com/scipy-superpack/) + * Use the [Superpack by Chris Fonnesbeck](http://fonnesbeck.github.com/ScipySuperpack/) * Use MacPorts: `sudo port install py26-numpy py26-scipy` * Install from source (grueling): [http://www.scipy.org/Download](http://www.scipy.org/Download) diff --git a/apps/reader/managers.py b/apps/reader/managers.py index 5a5d88a99..5e33702d2 100644 --- a/apps/reader/managers.py +++ b/apps/reader/managers.py @@ -20,5 +20,5 @@ class UserSubscriptionManager(models.Manager): kwargs['feed'] = feed elif 'feed__pk' in kwargs: kwargs['feed__pk'] = feed.pk - logging.debug(" ---> [%s] ~BRFound dupe UserSubscription: ~SB%s (%s)" % (kwargs['user'].username, feed, feed_id)) + logging.debug(" ---> [%s] ~BRFound dupe UserSubscription: ~SB%s (%s)" % (getattr(kwargs.get('user'), 'username'), feed, feed_id)) return super(UserSubscriptionManager, self).get(*args, **kwargs) \ No newline at end of file diff --git a/apps/reader/views.py b/apps/reader/views.py index d8e7e465e..752bf4443 100644 --- a/apps/reader/views.py +++ b/apps/reader/views.py @@ -1,5 +1,6 @@ import datetime import time +import boto from django.shortcuts import render_to_response, get_object_or_404 from django.contrib.auth.decorators import login_required from django.template import RequestContext @@ -561,7 +562,7 @@ def load_river_stories(request): try: mstories = [story.value for story in mstories if story and story.value] except OperationFailure, e: - raise e + return dict(error=str(e), code=-1) mstories = sorted(mstories, cmp=lambda x, y: cmp(story_score(y, days_to_keep_unreads), story_score(x, days_to_keep_unreads))) @@ -927,7 +928,7 @@ def add_feature(request): @json.json_view def load_features(request): user = get_user(request) - page = int(request.REQUEST.get('page', 0)) + page = max(int(request.REQUEST.get('page', 0)), 0) logging.user(request, "~FBBrowse features: ~SBPage #%s" % (page+1)) features = Feature.objects.all()[page*3:(page+1)*3+1].values() features = [{ @@ -1065,8 +1066,14 @@ def mark_story_as_starred(request): if k is not None and v is not None]) now = datetime.datetime.now() story_values = dict(user_id=request.user.pk, starred_date=now, **story_db) - MStarredStory.objects.create(**story_values) - logging.user(request, "~FCStarring: ~SB%s" % (story[0].story_title[:50])) + starred_story, created = MStarredStory.objects.get_or_create( + story_guid=story_values.pop('story_guid'), + user_id=story_values.pop('user_id'), + defaults=story_values) + if created: + logging.user(request, "~FCStarring: ~SB%s" % (story[0].story_title[:50])) + else: + logging.user(request, "~FC~BRAlready stared:~SN~FC ~SB%s" % (story[0].story_title[:50])) else: code = -1 @@ -1077,7 +1084,7 @@ def mark_story_as_starred(request): def mark_story_as_unstarred(request): code = 1 story_id = request.POST['story_id'] - + starred_story = MStarredStory.objects(user_id=request.user.pk, story_guid=story_id) if starred_story: logging.user(request, "~FCUnstarring: ~SB%s" % (starred_story[0].story_title[:50])) @@ -1124,7 +1131,11 @@ def send_story_email(request): cc=['%s <%s>' % (from_name, from_email)], headers={'Reply-To': '%s <%s>' % (from_name, from_email)}) msg.attach_alternative(html, "text/html") - msg.send() + try: + msg.send() + except boto.ses.connection.ResponseError, e: + code = -1 + message = "Email error: %s" % str(e) logging.user(request, '~BMSharing story by email: ~FY~SB%s~SN~BM~FY/~SB%s' % (story['story_title'][:50], feed.feed_title[:50])) diff --git a/assets.yml b/assets.yml index 44c6d38f0..7d1caf395 100644 --- a/assets.yml +++ b/assets.yml @@ -73,19 +73,19 @@ javascripts: - media/js/newsblur/reader_tutorial.js - media/js/newsblur/about.js - media/js/newsblur/faq.js - # mobile: - # - media/js/jquery-1.7.js - # - media/js/mobile/jquery.mobile-1.0b1.js - # - media/js/jquery.ajaxmanager.3.js - # - media/js/underscore.js - # - media/js/underscore.string.js - # - media/js/inflector.js - # - media/js/jquery.json.js - # - media/js/jquery.easing.js - # - media/js/jquery.newsblur.js - # - media/js/newsblur/reader_utils.js - # - media/js/newsblur/assetmodel.js - # - media/js/mobile/newsblur/mobile_workspace.js + mobile: + - media/js/jquery-1.7.1.js + - media/js/mobile/jquery.mobile-1.0b1.js + - media/js/jquery.ajaxmanager.3.js + - media/js/underscore.js + - media/js/underscore.string.js + - media/js/inflector.js + - media/js/jquery.json.js + - media/js/jquery.easing.js + - media/js/jquery.newsblur.js + - media/js/newsblur/reader_utils.js + - media/js/newsblur/assetmodel.js + - media/js/mobile/newsblur/mobile_workspace.js paypal: - media/js/newsblur/paypal_return.js bookmarklet: @@ -104,6 +104,9 @@ stylesheets: - media/css/jquery-ui/jquery.theme.css - media/css/jquery.tipsy.css - media/css/*.css + mobile: + - media/css/mobile/jquery.mobile-1.0b1.css + - media/css/mobile/mobile.css bookmarklet: - media/css/bookmarklet/reset.css - media/css/modals.css \ No newline at end of file diff --git a/config/nginx.newsblur.conf b/config/nginx.newsblur.conf index 492bb1073..bfbbbfef6 100644 --- a/config/nginx.newsblur.conf +++ b/config/nginx.newsblur.conf @@ -1,24 +1,31 @@ server { server_name newsblur.com; - rewrite ^(.*) http://www.newsblur.com$1 permanent; + rewrite ^(.*) https://www.newsblur.com$1 permanent; } upstream app_server { server 127.0.0.1:8000 fail_timeout=10 max_fails=3 ; - server app02.newsblur.com:80 fail_timeout=10 max_fails=3; - # server db01.newsblur.com:80 fail_timeout=10 max_fails=3 down; + server app02.newsblur.com:80 fail_timeout=10 max_fails=3 down; } -# limit_req_zone $remote_addr zone=one:10m rate=200r/m; -# limit_req zone=one burst=200 nodelay; +server { + listen 80; + server_name www.newsblur.com; + rewrite ^ https://$server_name$request_uri? permanent; +} server { - listen 80 default; + listen 443; + + ssl on; + ssl_certificate /home/sclay/newsblur/config/certificates/newsblur_cert.crt; + ssl_certificate_key /home/sclay/newsblur/config/certificates/newsblur_cert.crt; + client_max_body_size 4M; server_name www.newsblur.com; if ($host = 'newsblur.com') { - rewrite ^/(.*)$ http://www.newsblur.com/$1 permanent; + rewrite ^/(.*)$ https://www.newsblur.com/$1 permanent; } error_page 503 @maintenance; diff --git a/fabfile.py b/fabfile.py index dd3e604cb..5240890fc 100644 --- a/fabfile.py +++ b/fabfile.py @@ -149,15 +149,15 @@ def kill_celery(): def compress_assets(): local('jammit -c assets.yml --base-url http://www.newsblur.com --output static') - local('tar -czf static.tar static/*') + local('tar -czf static.tgz static/*') def transfer_assets(): - put('static.tar', '%s/static/' % env.NEWSBLUR_PATH) - run('tar -xzf static/static.tar') - run('rm -f static/static.tar') + put('static.tgz', '%s/static/' % env.NEWSBLUR_PATH) + run('tar -xzf static/static.tgz') + run('rm -f static/static.tgz') def cleanup_assets(): - local('rm -f static.tar') + local('rm -f static.tgz') # =========== # = Backups = diff --git a/media/js/newsblur/assetmodel.js b/media/js/newsblur/assetmodel.js index 53d634097..8ee938851 100644 --- a/media/js/newsblur/assetmodel.js +++ b/media/js/newsblur/assetmodel.js @@ -87,8 +87,10 @@ NEWSBLUR.AssetModel.Reader.prototype = { }, success: function(o) { // NEWSBLUR.log(['make_request 1', o]); - - if ($.isFunction(callback)) { + + if (o && o.code < 0 && error_callback) { + error_callback(o); + } else if ($.isFunction(callback)) { callback(o); } }, diff --git a/media/js/newsblur/reader.js b/media/js/newsblur/reader.js index 835399bb7..0ed5edfe0 100644 --- a/media/js/newsblur/reader.js +++ b/media/js/newsblur/reader.js @@ -2022,7 +2022,7 @@ $('.task_view_page', this.$s.$taskbar).addClass('NB-disabled'); var explicit_view_setting = this.model.view_setting(this.active_feed); - if (!explicit_view_setting) { + if (!explicit_view_setting || explicit_view_setting == 'page') { explicit_view_setting = 'feed'; } this.set_correct_story_view_for_feed(this.active_feed, explicit_view_setting); @@ -2542,7 +2542,8 @@ }, 400); }); - if (this.model.preference('folder_counts') || !$feed.is(':visible')) { + if (this.model.preference('folder_counts') || + $feed.parents('li.folder').filter('.NB-folder-collapsed').length) { var $folder_title = $feed.closest('li.folder:visible').children('.folder_title'); var $children = $folder_title.closest('li.folder').children('ul.folder, .feed'); this.show_collapsed_folder_count($folder_title, $children); @@ -5504,8 +5505,9 @@ $module.addClass('NB-loading'); - if (direction == -1 && !this.counts['feature_page']) { + if (direction == -1 && this.counts['feature_page'] <= 0) { $module.removeClass('NB-loading'); + this.counts['feature_page'] = 0; return; } if (direction == 1 && this.flags['features_last_page']) { diff --git a/media/js/newsblur/reader_send_email.js b/media/js/newsblur/reader_send_email.js index 442545e82..ada12f17e 100644 --- a/media/js/newsblur/reader_send_email.js +++ b/media/js/newsblur/reader_send_email.js @@ -122,8 +122,14 @@ NEWSBLUR.ReaderSendEmail.prototype = _.extend({}, NEWSBLUR.Modal.prototype, { error: function(data) { var $error = $('.NB-modal-error', this.$modal); + var $save = $('input[type=submit]', this.$modal); $error.show(); - $error.text("There was a issue on the backend with sending your email. Sorry about this! It has been noted and will be fixed soon. You should probably send this manually now."); + if (!data) { + $error.text("There was a issue on the backend with sending your email. Sorry about this! It has been noted and will be fixed soon. You should probably send this manually now."); + } else { + $('.NB-error', this.$modal).html(data.message).fadeIn(500); + } + $save.removeClass('NB-disabled').val('Send this story'); $('.NB-modal-loading', this.$modal).removeClass('NB-active'); }, diff --git a/utils/feed_fetcher.py b/utils/feed_fetcher.py index cc7f1100f..aa81a6ead 100644 --- a/utils/feed_fetcher.py +++ b/utils/feed_fetcher.py @@ -11,7 +11,6 @@ from utils import feedparser from utils.story_functions import pre_process_story from utils import log as logging from utils.feed_functions import timelimit, TimeoutError, mail_feed_error_to_admin, utf8encode -from utils.story_functions import bunch import time import datetime import traceback @@ -96,7 +95,7 @@ class ProcessFeed: def refresh_feed(self): self.feed = Feed.objects.using('default').get(pk=self.feed_id) - def process(self, first_run=True): + def process(self): """ Downloads and parses a feed. """ self.refresh_feed() @@ -109,7 +108,6 @@ class ProcessFeed: # logging.debug(u' ---> [%d] Processing %s' % (self.feed.id, self.feed.feed_title)) - self.feed.fetched_once = True self.feed.last_update = datetime.datetime.utcnow() if hasattr(self.fpf, 'status'): @@ -131,8 +129,9 @@ class ProcessFeed: if self.fpf.status in (302, 301): if not self.fpf.href.endswith('feedburner.com/atom.xml'): self.feed.feed_address = self.fpf.href - if first_run: + if not self.feed.fetched_once: self.feed.has_feed_exception = True + self.feed.fetched_once = True self.feed.schedule_feed_fetch_immediately() if not self.fpf.entries: self.feed.save() @@ -140,8 +139,10 @@ class ProcessFeed: return FEED_ERRHTTP, ret_values if self.fpf.status >= 400: - logging.debug(" ---> [%-30s] HTTP Status code: %s. Checking address..." % (unicode(self.feed)[:30], self.fpf.status)) - fixed_feed = self.feed.check_feed_link_for_feed_address() + logging.debug(" ---> [%-30s] HTTP Status code: %s.%s Checking address..." % (unicode(self.feed)[:30], self.fpf.status, ' Not' if self.feed.fetched_once else '')) + fixed_feed = None + if not self.feed.fetched_once: + fixed_feed = self.feed.check_feed_link_for_feed_address() if not fixed_feed: self.feed.save_feed_history(self.fpf.status, "HTTP Error") else: @@ -153,7 +154,9 @@ class ProcessFeed: if self.fpf.bozo and isinstance(self.fpf.bozo_exception, feedparser.NonXMLContentType): logging.debug(" ---> [%-30s] Feed is Non-XML. %s entries.%s Checking address..." % (unicode(self.feed)[:30], len(self.fpf.entries), ' Not' if self.fpf.entries else '')) if not self.fpf.entries: - fixed_feed = self.feed.check_feed_link_for_feed_address() + fixed_feed = None + if not self.feed.fetched_once: + fixed_feed = self.feed.check_feed_link_for_feed_address() if not fixed_feed: self.feed.save_feed_history(502, 'Non-xml feed', self.fpf.bozo_exception) else: @@ -164,7 +167,9 @@ class ProcessFeed: elif self.fpf.bozo and isinstance(self.fpf.bozo_exception, xml.sax._exceptions.SAXException): logging.debug(" ---> [%-30s] Feed has SAX/XML parsing issues. %s entries.%s Checking address..." % (unicode(self.feed)[:30], len(self.fpf.entries), ' Not' if self.fpf.entries else '')) if not self.fpf.entries: - fixed_feed = self.feed.check_feed_link_for_feed_address() + fixed_feed = None + if not self.feed.fetched_once: + fixed_feed = self.feed.check_feed_link_for_feed_address() if not fixed_feed: self.feed.save_feed_history(503, 'SAX Exception', self.fpf.bozo_exception) else: